mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 09:02:47 +02:00
Merge remote-tracking branch 'upstream/beta' into more-tests
This commit is contained in:
commit
4e9655e129
@ -31,7 +31,6 @@
|
||||
"src/overrides.ts",
|
||||
// TODO: these files are too big and complex, ignore them until their respective refactors
|
||||
"src/data/moves/move.ts",
|
||||
"src/data/abilities/ability.ts",
|
||||
|
||||
// this file is just too big:
|
||||
"src/data/balance/tms.ts"
|
||||
|
@ -1,172 +0,0 @@
|
||||
/**
|
||||
* This script creates a test boilerplate file in the appropriate
|
||||
* directory based on the type selected.
|
||||
* @example npm run create-test
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import inquirer from "inquirer";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
// Get the directory name of the current module file
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const typeChoices = ["Move", "Ability", "Item", "Mystery Encounter"];
|
||||
|
||||
/**
|
||||
* Prompts the user to select a type via list.
|
||||
* @returns {Promise<{selectedOption: string}>} the selected type
|
||||
*/
|
||||
async function promptTestType() {
|
||||
const typeAnswer = await inquirer.prompt([
|
||||
{
|
||||
type: "list",
|
||||
name: "selectedOption",
|
||||
message: "What type of test would you like to create:",
|
||||
choices: [...typeChoices, "EXIT"],
|
||||
},
|
||||
]);
|
||||
|
||||
if (typeAnswer.selectedOption === "EXIT") {
|
||||
console.log("Exiting...");
|
||||
return process.exit();
|
||||
}
|
||||
if (!typeChoices.includes(typeAnswer.selectedOption)) {
|
||||
console.error(`Please provide a valid type (${typeChoices.join(", ")})!`);
|
||||
return await promptTestType();
|
||||
}
|
||||
|
||||
return typeAnswer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to provide a file name.
|
||||
* @param {string} selectedType
|
||||
* @returns {Promise<{userInput: string}>} the selected file name
|
||||
*/
|
||||
async function promptFileName(selectedType) {
|
||||
const fileNameAnswer = await inquirer.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "userInput",
|
||||
message: `Please provide the name of the ${selectedType}:`,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) {
|
||||
console.error("Please provide a valid file name!");
|
||||
return await promptFileName(selectedType);
|
||||
}
|
||||
|
||||
return fileNameAnswer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the interactive create-test "CLI"
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function runInteractive() {
|
||||
const typeAnswer = await promptTestType();
|
||||
const fileNameAnswer = await promptFileName(typeAnswer.selectedOption);
|
||||
|
||||
const type = typeAnswer.selectedOption.toLowerCase();
|
||||
// Convert fileName from kebab-case or camelCase to snake_case
|
||||
const fileName = fileNameAnswer.userInput
|
||||
.replace(/-+/g, "_") // Convert kebab-case (dashes) to underscores
|
||||
.replace(/([a-z])([A-Z])/g, "$1_$2") // Convert camelCase to snake_case
|
||||
.replace(/\s+/g, "_") // Replace spaces with underscores
|
||||
.toLowerCase(); // Ensure all lowercase
|
||||
// Format the description for the test case
|
||||
|
||||
const formattedName = fileName.replace(/_/g, " ").replace(/\b\w/g, char => char.toUpperCase());
|
||||
// Determine the directory based on the type
|
||||
let dir;
|
||||
let description;
|
||||
switch (type) {
|
||||
case "move":
|
||||
dir = path.join(__dirname, "test", "moves");
|
||||
description = `Moves - ${formattedName}`;
|
||||
break;
|
||||
case "ability":
|
||||
dir = path.join(__dirname, "test", "abilities");
|
||||
description = `Abilities - ${formattedName}`;
|
||||
break;
|
||||
case "item":
|
||||
dir = path.join(__dirname, "test", "items");
|
||||
description = `Items - ${formattedName}`;
|
||||
break;
|
||||
case "mystery encounter":
|
||||
dir = path.join(__dirname, "test", "mystery-encounter", "encounters");
|
||||
description = `Mystery Encounter - ${formattedName}`;
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid type. Please use one of the following: ${typeChoices.join(", ")}.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Define the content template
|
||||
const content = `import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("${description}", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should do X", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
`;
|
||||
|
||||
// Ensure the directory exists
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create the file with the given name
|
||||
const filePath = path.join(dir, `${fileName}.test.ts`);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
console.error(`File "${fileName}.test.ts" already exists.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Write the template content to the file
|
||||
fs.writeFileSync(filePath, content, "utf8");
|
||||
|
||||
console.log(`File created at: ${filePath}`);
|
||||
}
|
||||
|
||||
runInteractive();
|
@ -145,6 +145,5 @@
|
||||
</div>
|
||||
<script type="module" src="./src/main.ts"></script>
|
||||
<script src="./src/touch-controls.ts" type="module"></script>
|
||||
<script src="./src/debug.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
@ -13,6 +13,7 @@
|
||||
"test:cov": "vitest run --coverage --no-isolate",
|
||||
"test:watch": "vitest watch --coverage --no-isolate",
|
||||
"test:silent": "vitest run --silent --no-isolate",
|
||||
"test:create": "node scripts/create-test/create-test.js",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"eslint": "eslint --fix .",
|
||||
"eslint-ci": "eslint .",
|
||||
@ -21,7 +22,6 @@
|
||||
"docs": "typedoc",
|
||||
"depcruise": "depcruise src",
|
||||
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
|
||||
"create-test": "node ./create-test-boilerplate.js",
|
||||
"postinstall": "npx lefthook install && npx lefthook run post-merge",
|
||||
"update-version:patch": "npm version patch --force --no-git-tag-version",
|
||||
"update-version:minor": "npm version minor --force --no-git-tag-version",
|
||||
|
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);
|
||||
});
|
||||
});
|
@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Dex entry for a single Pokemon Species
|
||||
*/
|
||||
export interface DexData {
|
||||
[key: number]: DexEntry;
|
||||
}
|
||||
|
||||
export interface DexEntry {
|
||||
seenAttr: bigint;
|
||||
caughtAttr: bigint;
|
||||
@ -10,7 +11,3 @@ export interface DexEntry {
|
||||
hatchedCount: number;
|
||||
ivs: number[];
|
||||
}
|
||||
|
||||
export interface DexData {
|
||||
[key: number]: DexEntry;
|
||||
}
|
@ -2,9 +2,6 @@ export interface Localizable {
|
||||
localize(): void;
|
||||
}
|
||||
|
||||
export interface TranslationEntries {
|
||||
[key: string]: string | { [key: string]: string };
|
||||
}
|
||||
export interface SimpleTranslationEntries {
|
||||
[key: string]: string;
|
||||
}
|
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;
|
@ -1,286 +1,25 @@
|
||||
import type { MoveAnim } from "#app/data/battle-anims";
|
||||
import type { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase";
|
||||
import type { AttemptCapturePhase } from "#app/phases/attempt-capture-phase";
|
||||
import type { AttemptRunPhase } from "#app/phases/attempt-run-phase";
|
||||
import type { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import type { BerryPhase } from "#app/phases/berry-phase";
|
||||
import type { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase";
|
||||
import type { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||
import type { CommandPhase } from "#app/phases/command-phase";
|
||||
import type { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
import type { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import type { EggHatchPhase } from "#app/phases/egg-hatch-phase";
|
||||
import type { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
||||
import type { EggSummaryPhase } from "#app/phases/egg-summary-phase";
|
||||
import type { EncounterPhase } from "#app/phases/encounter-phase";
|
||||
import type { EndCardPhase } from "#app/phases/end-card-phase";
|
||||
import type { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
|
||||
import type { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||
import type { EvolutionPhase } from "#app/phases/evolution-phase";
|
||||
import type { ExpPhase } from "#app/phases/exp-phase";
|
||||
import type { FaintPhase } from "#app/phases/faint-phase";
|
||||
import type { FormChangePhase } from "#app/phases/form-change-phase";
|
||||
import type { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase";
|
||||
import type { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import type { HideAbilityPhase } from "#app/phases/hide-ability-phase";
|
||||
import type { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase";
|
||||
import type { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import type { LevelCapPhase } from "#app/phases/level-cap-phase";
|
||||
import type { LevelUpPhase } from "#app/phases/level-up-phase";
|
||||
import type { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase";
|
||||
import type { LoginPhase } from "#app/phases/login-phase";
|
||||
import type { MessagePhase } from "#app/phases/message-phase";
|
||||
import type { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import type { MoneyRewardPhase } from "#app/phases/money-reward-phase";
|
||||
import type { MoveAnimPhase } from "#app/phases/move-anim-phase";
|
||||
import type { MoveChargePhase } from "#app/phases/move-charge-phase";
|
||||
import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import type { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import type { MoveHeaderPhase } from "#app/phases/move-header-phase";
|
||||
import type { MovePhase } from "#app/phases/move-phase";
|
||||
import type {
|
||||
MysteryEncounterPhase,
|
||||
MysteryEncounterOptionSelectedPhase,
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterRewardsPhase,
|
||||
PostMysteryEncounterPhase,
|
||||
MysteryEncounterBattleStartCleanupPhase,
|
||||
} from "#app/phases/mystery-encounter-phases";
|
||||
import type { NewBattlePhase } from "#app/phases/new-battle-phase";
|
||||
import type { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
|
||||
import type { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
||||
import type { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
||||
import type { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||
import type { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import type { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
||||
import type { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import type { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
|
||||
import type { PostGameOverPhase } from "#app/phases/post-game-over-phase";
|
||||
import type { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||
import type { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
|
||||
import type { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
||||
import type { ReloadSessionPhase } from "#app/phases/reload-session-phase";
|
||||
import type { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
||||
import type { ReturnPhase } from "#app/phases/return-phase";
|
||||
import type { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase";
|
||||
import type { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase";
|
||||
import type { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||
import type { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||
import type { SelectChallengePhase } from "#app/phases/select-challenge-phase";
|
||||
import type { SelectGenderPhase } from "#app/phases/select-gender-phase";
|
||||
import type { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import type { SelectStarterPhase } from "#app/phases/select-starter-phase";
|
||||
import type { SelectTargetPhase } from "#app/phases/select-target-phase";
|
||||
import type { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
|
||||
import type { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import type { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
import type { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
||||
import type { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import type { SummonMissingPhase } from "#app/phases/summon-missing-phase";
|
||||
import type { SummonPhase } from "#app/phases/summon-phase";
|
||||
import type { SwitchBiomePhase } from "#app/phases/switch-biome-phase";
|
||||
import type { SwitchPhase } from "#app/phases/switch-phase";
|
||||
import type { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||
import type { TeraPhase } from "#app/phases/tera-phase";
|
||||
import type { TitlePhase } from "#app/phases/title-phase";
|
||||
import type { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||
import type { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
|
||||
import type { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import type { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import type { TurnStartPhase } from "#app/phases/turn-start-phase";
|
||||
import type { UnavailablePhase } from "#app/phases/unavailable-phase";
|
||||
import type { UnlockPhase } from "#app/phases/unlock-phase";
|
||||
import type { VictoryPhase } from "#app/phases/victory-phase";
|
||||
import type { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
|
||||
import type { PhaseConstructorMap } from "#app/phase-manager";
|
||||
|
||||
export type PhaseClass =
|
||||
| typeof AddEnemyBuffModifierPhase
|
||||
| typeof AttemptCapturePhase
|
||||
| typeof AttemptRunPhase
|
||||
| typeof BattleEndPhase
|
||||
| typeof BerryPhase
|
||||
| typeof CheckStatusEffectPhase
|
||||
| typeof CheckSwitchPhase
|
||||
| typeof CommandPhase
|
||||
| typeof CommonAnimPhase
|
||||
| typeof DamageAnimPhase
|
||||
| typeof EggHatchPhase
|
||||
| typeof EggLapsePhase
|
||||
| typeof EggSummaryPhase
|
||||
| typeof EncounterPhase
|
||||
| typeof EndCardPhase
|
||||
| typeof EndEvolutionPhase
|
||||
| typeof EnemyCommandPhase
|
||||
| typeof EvolutionPhase
|
||||
| typeof FormChangePhase
|
||||
| typeof ExpPhase
|
||||
| typeof FaintPhase
|
||||
| typeof FormChangePhase
|
||||
| typeof GameOverPhase
|
||||
| typeof GameOverModifierRewardPhase
|
||||
| typeof HideAbilityPhase
|
||||
| typeof HidePartyExpBarPhase
|
||||
| typeof LearnMovePhase
|
||||
| typeof LevelUpPhase
|
||||
| typeof LevelCapPhase
|
||||
| typeof LoadMoveAnimPhase
|
||||
| typeof LoginPhase
|
||||
| typeof MessagePhase
|
||||
| typeof ModifierRewardPhase
|
||||
| typeof MoneyRewardPhase
|
||||
| typeof MoveAnimPhase
|
||||
| typeof MoveChargePhase
|
||||
| typeof MoveEffectPhase
|
||||
| typeof MoveEndPhase
|
||||
| typeof MoveHeaderPhase
|
||||
| typeof MovePhase
|
||||
| typeof MysteryEncounterPhase
|
||||
| typeof MysteryEncounterOptionSelectedPhase
|
||||
| typeof MysteryEncounterBattlePhase
|
||||
| typeof MysteryEncounterRewardsPhase
|
||||
| typeof MysteryEncounterBattleStartCleanupPhase
|
||||
| typeof MysteryEncounterRewardsPhase
|
||||
| typeof PostMysteryEncounterPhase
|
||||
| typeof NewBattlePhase
|
||||
| typeof NewBiomeEncounterPhase
|
||||
| typeof NextEncounterPhase
|
||||
| typeof ObtainStatusEffectPhase
|
||||
| typeof PartyExpPhase
|
||||
| typeof PartyHealPhase
|
||||
| typeof PokemonAnimPhase
|
||||
| typeof PokemonHealPhase
|
||||
| typeof PokemonTransformPhase
|
||||
| typeof PostGameOverPhase
|
||||
| typeof PostSummonPhase
|
||||
| typeof PostTurnStatusEffectPhase
|
||||
| typeof QuietFormChangePhase
|
||||
| typeof ReloadSessionPhase
|
||||
| typeof ResetStatusPhase
|
||||
| typeof ReturnPhase
|
||||
| typeof RevivalBlessingPhase
|
||||
| typeof RibbonModifierRewardPhase
|
||||
| typeof ScanIvsPhase
|
||||
| typeof SelectBiomePhase
|
||||
| typeof SelectChallengePhase
|
||||
| typeof SelectGenderPhase
|
||||
| typeof SelectModifierPhase
|
||||
| typeof SelectStarterPhase
|
||||
| typeof SelectTargetPhase
|
||||
| typeof ShinySparklePhase
|
||||
| typeof ShowAbilityPhase
|
||||
| typeof ShowTrainerPhase
|
||||
| typeof ShowPartyExpBarPhase
|
||||
| typeof StatStageChangePhase
|
||||
| typeof SummonMissingPhase
|
||||
| typeof SummonPhase
|
||||
| typeof SwitchBiomePhase
|
||||
| typeof SwitchPhase
|
||||
| typeof SwitchSummonPhase
|
||||
| typeof TeraPhase
|
||||
| typeof TitlePhase
|
||||
| typeof ToggleDoublePositionPhase
|
||||
| typeof TrainerVictoryPhase
|
||||
| typeof TurnEndPhase
|
||||
| typeof TurnInitPhase
|
||||
| typeof TurnStartPhase
|
||||
| typeof UnavailablePhase
|
||||
| typeof UnlockPhase
|
||||
| typeof VictoryPhase
|
||||
| typeof WeatherEffectPhase;
|
||||
// Intentionally export the types of everything in phase-manager, as this file is meant to be
|
||||
// the centralized place for type definitions for the phase system.
|
||||
export type * from "#app/phase-manager";
|
||||
|
||||
/** Typescript map used to map a string phase to the actual phase type */
|
||||
// This file includes helpful types for the phase system.
|
||||
// It intentionally imports the phase constructor map from the phase manager (and re-exports it)
|
||||
|
||||
/**
|
||||
* Map of phase names to constructors for said phase
|
||||
*/
|
||||
export type PhaseMap = {
|
||||
AddEnemyBuffModifierPhase: AddEnemyBuffModifierPhase;
|
||||
AttemptCapturePhase: AttemptCapturePhase;
|
||||
AttemptRunPhase: AttemptRunPhase;
|
||||
BattleEndPhase: BattleEndPhase;
|
||||
BerryPhase: BerryPhase;
|
||||
CheckStatusEffectPhase: CheckStatusEffectPhase;
|
||||
CheckSwitchPhase: CheckSwitchPhase;
|
||||
CommandPhase: CommandPhase;
|
||||
CommonAnimPhase: CommonAnimPhase;
|
||||
DamageAnimPhase: DamageAnimPhase;
|
||||
EggHatchPhase: EggHatchPhase;
|
||||
EggLapsePhase: EggLapsePhase;
|
||||
EggSummaryPhase: EggSummaryPhase;
|
||||
EncounterPhase: EncounterPhase;
|
||||
EndCardPhase: EndCardPhase;
|
||||
EndEvolutionPhase: EndEvolutionPhase;
|
||||
EnemyCommandPhase: EnemyCommandPhase;
|
||||
EvolutionPhase: EvolutionPhase;
|
||||
ExpPhase: ExpPhase;
|
||||
FaintPhase: FaintPhase;
|
||||
FormChangePhase: FormChangePhase;
|
||||
GameOverPhase: GameOverPhase;
|
||||
GameOverModifierRewardPhase: GameOverModifierRewardPhase;
|
||||
HideAbilityPhase: HideAbilityPhase;
|
||||
HidePartyExpBarPhase: HidePartyExpBarPhase;
|
||||
LearnMovePhase: LearnMovePhase;
|
||||
LevelCapPhase: LevelCapPhase;
|
||||
LevelUpPhase: LevelUpPhase;
|
||||
LoadMoveAnimPhase: LoadMoveAnimPhase;
|
||||
LoginPhase: LoginPhase;
|
||||
MessagePhase: MessagePhase;
|
||||
ModifierRewardPhase: ModifierRewardPhase;
|
||||
MoneyRewardPhase: MoneyRewardPhase;
|
||||
MoveAnimPhase: MoveAnimPhase<MoveAnim>;
|
||||
MoveChargePhase: MoveChargePhase;
|
||||
MoveEffectPhase: MoveEffectPhase;
|
||||
MoveEndPhase: MoveEndPhase;
|
||||
MoveHeaderPhase: MoveHeaderPhase;
|
||||
MovePhase: MovePhase;
|
||||
MysteryEncounterPhase: MysteryEncounterPhase;
|
||||
MysteryEncounterOptionSelectedPhase: MysteryEncounterOptionSelectedPhase;
|
||||
MysteryEncounterBattlePhase: MysteryEncounterBattlePhase;
|
||||
MysteryEncounterBattleStartCleanupPhase: MysteryEncounterBattleStartCleanupPhase;
|
||||
MysteryEncounterRewardsPhase: MysteryEncounterRewardsPhase;
|
||||
PostMysteryEncounterPhase: PostMysteryEncounterPhase;
|
||||
NewBattlePhase: NewBattlePhase;
|
||||
NewBiomeEncounterPhase: NewBiomeEncounterPhase;
|
||||
NextEncounterPhase: NextEncounterPhase;
|
||||
ObtainStatusEffectPhase: ObtainStatusEffectPhase;
|
||||
PartyExpPhase: PartyExpPhase;
|
||||
PartyHealPhase: PartyHealPhase;
|
||||
PokemonAnimPhase: PokemonAnimPhase;
|
||||
PokemonHealPhase: PokemonHealPhase;
|
||||
PokemonTransformPhase: PokemonTransformPhase;
|
||||
PostGameOverPhase: PostGameOverPhase;
|
||||
PostSummonPhase: PostSummonPhase;
|
||||
PostTurnStatusEffectPhase: PostTurnStatusEffectPhase;
|
||||
QuietFormChangePhase: QuietFormChangePhase;
|
||||
ReloadSessionPhase: ReloadSessionPhase;
|
||||
ResetStatusPhase: ResetStatusPhase;
|
||||
ReturnPhase: ReturnPhase;
|
||||
RevivalBlessingPhase: RevivalBlessingPhase;
|
||||
RibbonModifierRewardPhase: RibbonModifierRewardPhase;
|
||||
ScanIvsPhase: ScanIvsPhase;
|
||||
SelectBiomePhase: SelectBiomePhase;
|
||||
SelectChallengePhase: SelectChallengePhase;
|
||||
SelectGenderPhase: SelectGenderPhase;
|
||||
SelectModifierPhase: SelectModifierPhase;
|
||||
SelectStarterPhase: SelectStarterPhase;
|
||||
SelectTargetPhase: SelectTargetPhase;
|
||||
ShinySparklePhase: ShinySparklePhase;
|
||||
ShowAbilityPhase: ShowAbilityPhase;
|
||||
ShowPartyExpBarPhase: ShowPartyExpBarPhase;
|
||||
ShowTrainerPhase: ShowTrainerPhase;
|
||||
StatStageChangePhase: StatStageChangePhase;
|
||||
SummonMissingPhase: SummonMissingPhase;
|
||||
SummonPhase: SummonPhase;
|
||||
SwitchBiomePhase: SwitchBiomePhase;
|
||||
SwitchPhase: SwitchPhase;
|
||||
SwitchSummonPhase: SwitchSummonPhase;
|
||||
TeraPhase: TeraPhase;
|
||||
TitlePhase: TitlePhase;
|
||||
ToggleDoublePositionPhase: ToggleDoublePositionPhase;
|
||||
TrainerVictoryPhase: TrainerVictoryPhase;
|
||||
TurnEndPhase: TurnEndPhase;
|
||||
TurnInitPhase: TurnInitPhase;
|
||||
TurnStartPhase: TurnStartPhase;
|
||||
UnavailablePhase: UnavailablePhase;
|
||||
UnlockPhase: UnlockPhase;
|
||||
VictoryPhase: VictoryPhase;
|
||||
WeatherEffectPhase: WeatherEffectPhase;
|
||||
[K in keyof PhaseConstructorMap]: InstanceType<PhaseConstructorMap[K]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Union type of all phase constructors.
|
||||
*/
|
||||
export type PhaseClass = PhaseConstructorMap[keyof PhaseConstructorMap];
|
||||
|
||||
/**
|
||||
* Union type of all phase names as strings.
|
||||
*/
|
||||
export type PhaseString = keyof PhaseMap;
|
||||
|
@ -2,8 +2,8 @@ import type { EnemyPokemon } from "#app/field/pokemon";
|
||||
import type { PersistentModifier } from "#app/modifier/modifier";
|
||||
import type { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import type { TrainerConfig } from "./trainer-config";
|
||||
import type { TrainerPartyTemplate } from "./TrainerPartyTemplate";
|
||||
import type { TrainerConfig } from "../data/trainers/trainer-config";
|
||||
import type { TrainerPartyTemplate } from "../data/trainers/TrainerPartyTemplate";
|
||||
|
||||
export type PartyTemplateFunc = () => TrainerPartyTemplate;
|
||||
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
|
@ -80,13 +80,15 @@ import type { FixedBattleConfig } from "#app/battle";
|
||||
import Battle from "#app/battle";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import type { GameMode } from "#app/game-mode";
|
||||
import { GameModes, getGameMode } from "#app/game-mode";
|
||||
import { getGameMode } from "#app/game-mode";
|
||||
import { GameModes } from "#enums/game-modes";
|
||||
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
||||
import SpritePipeline from "#app/pipelines/sprite";
|
||||
import PartyExpBar from "#app/ui/party-exp-bar";
|
||||
import type { TrainerSlot } from "./enums/trainer-slot";
|
||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
||||
import Trainer from "#app/field/trainer";
|
||||
import { TrainerVariant } from "#enums/trainer-variant";
|
||||
import type TrainerData from "#app/system/trainer-data";
|
||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
@ -101,14 +103,12 @@ import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
||||
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type { SpeciesFormChange, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
|
||||
import {
|
||||
FormChangeItem,
|
||||
pokemonFormChanges,
|
||||
SpeciesFormChangeManualTrigger,
|
||||
SpeciesFormChangeTimeOfDayTrigger,
|
||||
} from "#app/data/pokemon-forms";
|
||||
import { FormChangePhase } from "#app/phases/form-change-phase";
|
||||
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||
import type { SpeciesFormChangeTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import { SpeciesFormChangeTimeOfDayTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||
import { SpeciesFormChangeManualTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||
import { FormChangeItem } from "#enums/form-change-item";
|
||||
import { getTypeRgb } from "#app/data/type";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
||||
@ -120,7 +120,7 @@ import { SceneBase } from "#app/scene-base";
|
||||
import CandyBar from "#app/ui/candy-bar";
|
||||
import type { Variant } from "#app/sprites/variant";
|
||||
import { variantData, clearVariantData } from "#app/sprites/variant";
|
||||
import type { Localizable } from "#app/interfaces/locales";
|
||||
import type { Localizable } from "#app/@types/locales";
|
||||
import Overrides from "#app/overrides";
|
||||
import { InputsController } from "#app/inputs-controller";
|
||||
import { UiInputs } from "#app/ui-inputs";
|
||||
@ -142,20 +142,7 @@ import i18next from "i18next";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||
import { LoadingScene } from "#app/loading-scene";
|
||||
import { LevelCapPhase } from "#app/phases/level-cap-phase";
|
||||
import { LoginPhase } from "#app/phases/login-phase";
|
||||
import { MessagePhase } from "#app/phases/message-phase";
|
||||
import type { MovePhase } from "#app/phases/move-phase";
|
||||
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
|
||||
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
||||
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
||||
import { ReturnPhase } from "#app/phases/return-phase";
|
||||
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
||||
import { SummonPhase } from "#app/phases/summon-phase";
|
||||
import { TitlePhase } from "#app/phases/title-phase";
|
||||
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import {
|
||||
@ -169,22 +156,19 @@ import {
|
||||
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import { ExpPhase } from "#app/phases/exp-phase";
|
||||
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { initGlobalScene } from "#app/global-scene";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
|
||||
import { expSpriteKeys } from "./sprites/sprite-keys";
|
||||
import { hasExpSprite } from "./sprites/sprite-utils";
|
||||
import { timedEventManager } from "./global-event-manager";
|
||||
import { starterColors } from "./global-vars/starter-colors";
|
||||
import { startingWave } from "./starting-wave";
|
||||
import { PhaseManager } from "./phase-manager";
|
||||
|
||||
const DEBUG_RNG = false;
|
||||
|
||||
@ -297,18 +281,8 @@ export default class BattleScene extends SceneBase {
|
||||
public gameData: GameData;
|
||||
public sessionSlotId: number;
|
||||
|
||||
/** PhaseQueue: dequeue/remove the first element to get the next phase */
|
||||
public phaseQueue: Phase[];
|
||||
public conditionalQueue: Array<[() => boolean, Phase]>;
|
||||
/** PhaseQueuePrepend: is a temp storage of what will be added to PhaseQueue */
|
||||
private phaseQueuePrepend: Phase[];
|
||||
|
||||
/** overrides default of inserting phases to end of phaseQueuePrepend array, useful or inserting Phases "out of order" */
|
||||
private phaseQueuePrependSpliceIndex: number;
|
||||
private nextCommandPhaseQueue: Phase[];
|
||||
|
||||
private currentPhase: Phase | null;
|
||||
private standbyPhase: Phase | null;
|
||||
/** Manager for the phases active in the battle scene */
|
||||
public readonly phaseManager: PhaseManager;
|
||||
public field: Phaser.GameObjects.Container;
|
||||
public fieldUI: Phaser.GameObjects.Container;
|
||||
public charSprite: CharSprite;
|
||||
@ -396,11 +370,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
constructor() {
|
||||
super("battle");
|
||||
this.phaseQueue = [];
|
||||
this.phaseQueuePrepend = [];
|
||||
this.conditionalQueue = [];
|
||||
this.phaseQueuePrependSpliceIndex = -1;
|
||||
this.nextCommandPhaseQueue = [];
|
||||
this.phaseManager = new PhaseManager();
|
||||
this.eventManager = new TimedEventManager();
|
||||
this.updateGameInfo();
|
||||
initGlobalScene(this);
|
||||
@ -716,10 +686,10 @@ export default class BattleScene extends SceneBase {
|
||||
).then(() => loadMoveAnimAssets(defaultMoves, true)),
|
||||
this.initStarterColors(),
|
||||
]).then(() => {
|
||||
this.pushPhase(new LoginPhase());
|
||||
this.pushPhase(new TitlePhase());
|
||||
this.phaseManager.pushNew("LoginPhase");
|
||||
this.phaseManager.pushNew("TitlePhase");
|
||||
|
||||
this.shiftPhase();
|
||||
this.phaseManager.shiftPhase();
|
||||
});
|
||||
}
|
||||
|
||||
@ -811,6 +781,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add a `getPartyOnSide` function for getting the party of a pokemon
|
||||
public getPlayerParty(): PlayerPokemon[] {
|
||||
return this.party;
|
||||
}
|
||||
@ -898,7 +869,7 @@ export default class BattleScene extends SceneBase {
|
||||
if (allyPokemon?.isActive(true)) {
|
||||
let targetingMovePhase: MovePhase;
|
||||
do {
|
||||
targetingMovePhase = this.findPhase(
|
||||
targetingMovePhase = this.phaseManager.findPhase(
|
||||
mp =>
|
||||
mp.is("MovePhase") &&
|
||||
mp.targets.length === 1 &&
|
||||
@ -1276,7 +1247,7 @@ export default class BattleScene extends SceneBase {
|
||||
duration: 250,
|
||||
ease: "Sine.easeInOut",
|
||||
onComplete: () => {
|
||||
this.clearPhaseQueue();
|
||||
this.phaseManager.clearPhaseQueue();
|
||||
|
||||
this.ui.freeUIData();
|
||||
this.uiContainer.remove(this.ui, true);
|
||||
@ -1449,7 +1420,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (lastBattle?.double && !newDouble) {
|
||||
this.tryRemovePhase((p: Phase) => p.is("SwitchPhase"));
|
||||
this.phaseManager.tryRemovePhase((p: Phase) => p.is("SwitchPhase"));
|
||||
for (const p of this.getPlayerField()) {
|
||||
p.lapseTag(BattlerTagType.COMMANDED);
|
||||
}
|
||||
@ -1491,7 +1462,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
playerField.forEach((pokemon, p) => {
|
||||
if (pokemon.isOnField()) {
|
||||
this.pushPhase(new ReturnPhase(p));
|
||||
this.phaseManager.pushNew("ReturnPhase", p);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1508,7 +1479,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (!this.trainer.visible) {
|
||||
this.pushPhase(new ShowTrainerPhase());
|
||||
this.phaseManager.pushNew("ShowTrainerPhase");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1517,13 +1488,13 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
|
||||
this.pushPhase(new NextEncounterPhase());
|
||||
this.phaseManager.pushNew("NextEncounterPhase");
|
||||
} else {
|
||||
this.pushPhase(new NewBiomeEncounterPhase());
|
||||
this.phaseManager.pushNew("NewBiomeEncounterPhase");
|
||||
|
||||
const newMaxExpLevel = this.getMaxExpLevel();
|
||||
if (newMaxExpLevel > maxExpLevel) {
|
||||
this.pushPhase(new LevelCapPhase());
|
||||
this.phaseManager.pushNew("LevelCapPhase");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1587,7 +1558,9 @@ export default class BattleScene extends SceneBase {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(this.getCurrentPhase()?.phaseName ?? "");
|
||||
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(
|
||||
this.phaseManager.getCurrentPhase()?.phaseName ?? "",
|
||||
);
|
||||
|
||||
if (
|
||||
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.
|
||||
@ -2614,286 +2587,6 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
}
|
||||
|
||||
/* Phase Functions */
|
||||
getCurrentPhase(): Phase | null {
|
||||
return this.currentPhase;
|
||||
}
|
||||
|
||||
getStandbyPhase(): Phase | null {
|
||||
return this.standbyPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met.
|
||||
*
|
||||
* This method allows deferring the execution of a phase until certain conditions are met, which is useful for handling
|
||||
* situations like abilities and entry hazards that depend on specific game states.
|
||||
*
|
||||
* @param {Phase} phase - The phase to be added to the conditional queue.
|
||||
* @param {() => boolean} condition - A function that returns a boolean indicating whether the phase should be executed.
|
||||
*
|
||||
*/
|
||||
pushConditionalPhase(phase: Phase, condition: () => boolean): void {
|
||||
this.conditionalQueue.push([condition, phase]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false
|
||||
* @param phase {@linkcode Phase} the phase to add
|
||||
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
|
||||
*/
|
||||
pushPhase(phase: Phase, defer = false): void {
|
||||
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Phase(s) to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex
|
||||
* @param phases {@linkcode Phase} the phase(s) to add
|
||||
*/
|
||||
unshiftPhase(...phases: Phase[]): void {
|
||||
if (this.phaseQueuePrependSpliceIndex === -1) {
|
||||
this.phaseQueuePrepend.push(...phases);
|
||||
} else {
|
||||
this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, ...phases);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the phaseQueue
|
||||
*/
|
||||
clearPhaseQueue(): void {
|
||||
this.phaseQueue.splice(0, this.phaseQueue.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all phase-related stuff, including all phase queues, the current and standby phases, and a splice index
|
||||
*/
|
||||
clearAllPhases(): void {
|
||||
for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) {
|
||||
queue.splice(0, queue.length);
|
||||
}
|
||||
this.currentPhase = null;
|
||||
this.standbyPhase = null;
|
||||
this.clearPhaseQueueSplice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by function unshiftPhase(), sets index to start inserting at current length instead of the end of the array, useful if phaseQueuePrepend gets longer with Phases
|
||||
*/
|
||||
setPhaseQueueSplice(): void {
|
||||
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets phaseQueuePrependSpliceIndex to -1, implies that calls to unshiftPhase will insert at end of phaseQueuePrepend
|
||||
*/
|
||||
clearPhaseQueueSplice(): void {
|
||||
this.phaseQueuePrependSpliceIndex = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called by each Phase implementations "end()" by default
|
||||
* We dump everything from phaseQueuePrepend to the start of of phaseQueue
|
||||
* then removes first Phase and starts it
|
||||
*/
|
||||
shiftPhase(): void {
|
||||
if (this.standbyPhase) {
|
||||
this.currentPhase = this.standbyPhase;
|
||||
this.standbyPhase = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.phaseQueuePrependSpliceIndex > -1) {
|
||||
this.clearPhaseQueueSplice();
|
||||
}
|
||||
if (this.phaseQueuePrepend.length) {
|
||||
while (this.phaseQueuePrepend.length) {
|
||||
const poppedPhase = this.phaseQueuePrepend.pop();
|
||||
if (poppedPhase) {
|
||||
this.phaseQueue.unshift(poppedPhase);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this.phaseQueue.length) {
|
||||
this.populatePhaseQueue();
|
||||
// Clear the conditionalQueue if there are no phases left in the phaseQueue
|
||||
this.conditionalQueue = [];
|
||||
}
|
||||
|
||||
this.currentPhase = this.phaseQueue.shift() ?? null;
|
||||
|
||||
// Check if there are any conditional phases queued
|
||||
if (this.conditionalQueue?.length) {
|
||||
// Retrieve the first conditional phase from the queue
|
||||
const conditionalPhase = this.conditionalQueue.shift();
|
||||
// Evaluate the condition associated with the phase
|
||||
if (conditionalPhase?.[0]()) {
|
||||
// If the condition is met, add the phase to the phase queue
|
||||
this.pushPhase(conditionalPhase[1]);
|
||||
} else if (conditionalPhase) {
|
||||
// If the condition is not met, re-add the phase back to the front of the conditional queue
|
||||
this.conditionalQueue.unshift(conditionalPhase);
|
||||
} else {
|
||||
console.warn("condition phase is undefined/null!", conditionalPhase);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentPhase) {
|
||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
||||
this.currentPhase.start();
|
||||
}
|
||||
}
|
||||
|
||||
overridePhase(phase: Phase): boolean {
|
||||
if (this.standbyPhase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.standbyPhase = this.currentPhase;
|
||||
this.currentPhase = phase;
|
||||
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
|
||||
phase.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific {@linkcode Phase} in the phase queue.
|
||||
*
|
||||
* @param phaseFilter filter function to use to find the wanted phase
|
||||
* @returns the found phase or undefined if none found
|
||||
*/
|
||||
findPhase<P extends Phase = Phase>(phaseFilter: (phase: P) => boolean): P | undefined {
|
||||
return this.phaseQueue.find(phaseFilter) as P;
|
||||
}
|
||||
|
||||
tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean {
|
||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueue[phaseIndex] = phase;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
tryRemovePhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueue.splice(phaseIndex, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will search for a specific phase in {@linkcode phaseQueuePrepend} via filter, and remove the first result if a match is found.
|
||||
* @param phaseFilter filter function
|
||||
*/
|
||||
tryRemoveUnshiftedPhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
||||
const phaseIndex = this.phaseQueuePrepend.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueuePrepend.splice(phaseIndex, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase()
|
||||
* @param phase {@linkcode Phase} the phase to be added
|
||||
* @param targetPhase {@linkcode Phase} the type of phase to search for in phaseQueue
|
||||
* @returns boolean if a targetPhase was found and added
|
||||
*/
|
||||
prependToPhase(phase: Phase | Phase[], targetPhase: Constructor<Phase>): boolean {
|
||||
if (!Array.isArray(phase)) {
|
||||
phase = [phase];
|
||||
}
|
||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase);
|
||||
|
||||
if (targetIndex !== -1) {
|
||||
this.phaseQueue.splice(targetIndex, 0, ...phase);
|
||||
return true;
|
||||
}
|
||||
this.unshiftPhase(...phase);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()}
|
||||
* @param phase {@linkcode Phase} the phase(s) to be added
|
||||
* @param targetPhase {@linkcode Phase} the type of phase to search for in {@linkcode phaseQueue}
|
||||
* @returns `true` if a `targetPhase` was found to append to
|
||||
*/
|
||||
appendToPhase(phase: Phase | Phase[], targetPhase: Constructor<Phase>): boolean {
|
||||
if (!Array.isArray(phase)) {
|
||||
phase = [phase];
|
||||
}
|
||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase);
|
||||
|
||||
if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
|
||||
this.phaseQueue.splice(targetIndex + 1, 0, ...phase);
|
||||
return true;
|
||||
}
|
||||
this.unshiftPhase(...phase);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
|
||||
* @param message string for MessagePhase
|
||||
* @param callbackDelay optional param for MessagePhase constructor
|
||||
* @param prompt optional param for MessagePhase constructor
|
||||
* @param promptDelay optional param for MessagePhase constructor
|
||||
* @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue
|
||||
*/
|
||||
queueMessage(
|
||||
message: string,
|
||||
callbackDelay?: number | null,
|
||||
prompt?: boolean | null,
|
||||
promptDelay?: number | null,
|
||||
defer?: boolean | null,
|
||||
) {
|
||||
const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay);
|
||||
if (!defer) {
|
||||
// adds to the end of PhaseQueuePrepend
|
||||
this.unshiftPhase(phase);
|
||||
} else {
|
||||
//remember that pushPhase adds it to nextCommandPhaseQueue
|
||||
this.pushPhase(phase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues an ability bar flyout phase
|
||||
* @param pokemon The pokemon who has the ability
|
||||
* @param passive Whether the ability is a passive
|
||||
* @param show Whether to show or hide the bar
|
||||
*/
|
||||
public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void {
|
||||
this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase());
|
||||
this.clearPhaseQueueSplice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the ability bar if it is currently visible
|
||||
*/
|
||||
public hideAbilityBar(): void {
|
||||
if (this.abilityBar.isVisible()) {
|
||||
this.unshiftPhase(new HideAbilityPhase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
|
||||
*/
|
||||
populatePhaseQueue(): void {
|
||||
if (this.nextCommandPhaseQueue.length) {
|
||||
this.phaseQueue.push(...this.nextCommandPhaseQueue);
|
||||
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
|
||||
}
|
||||
this.phaseQueue.push(new TurnInitPhase());
|
||||
}
|
||||
|
||||
addMoney(amount: number): void {
|
||||
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
|
||||
this.updateMoneyText();
|
||||
@ -2941,7 +2634,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
} else if (!virtual) {
|
||||
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
|
||||
this.queueMessage(
|
||||
this.phaseManager.queueMessage(
|
||||
i18next.t("battle:itemStackFull", {
|
||||
fullItemName: modifier.type.name,
|
||||
itemName: defaultModifierType.name,
|
||||
@ -3086,9 +2779,9 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
const removeOld = itemModifier.stackCount === 0;
|
||||
|
||||
if (!removeOld || !source || this.removeModifier(itemModifier, !source.isPlayer())) {
|
||||
if (!removeOld || !source || this.removeModifier(itemModifier, source.isEnemy())) {
|
||||
const addModifier = () => {
|
||||
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {
|
||||
if (!matchingModifier || this.removeModifier(matchingModifier, target.isEnemy())) {
|
||||
if (target.isPlayer()) {
|
||||
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
||||
if (source && itemLost) {
|
||||
@ -3492,17 +3185,17 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
if (matchingFormChange) {
|
||||
let phase: Phase;
|
||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
|
||||
phase = new FormChangePhase(pokemon, matchingFormChange, modal);
|
||||
if (pokemon.isPlayer() && !matchingFormChange.quiet) {
|
||||
phase = this.phaseManager.create("FormChangePhase", pokemon, matchingFormChange, modal);
|
||||
} else {
|
||||
phase = new QuietFormChangePhase(pokemon, matchingFormChange);
|
||||
phase = this.phaseManager.create("QuietFormChangePhase", pokemon, matchingFormChange);
|
||||
}
|
||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
|
||||
this.overridePhase(phase);
|
||||
if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) {
|
||||
this.phaseManager.overridePhase(phase);
|
||||
} else if (delayed) {
|
||||
this.pushPhase(phase);
|
||||
this.phaseManager.pushPhase(phase);
|
||||
} else {
|
||||
this.unshiftPhase(phase);
|
||||
this.phaseManager.unshiftPhase(phase);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3517,11 +3210,12 @@ export default class BattleScene extends SceneBase {
|
||||
fieldAssets?: Phaser.GameObjects.Sprite[],
|
||||
delayed = false,
|
||||
): boolean {
|
||||
const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets);
|
||||
const phaseManager = this.phaseManager;
|
||||
const phase: Phase = phaseManager.create("PokemonAnimPhase", battleAnimType, pokemon, fieldAssets);
|
||||
if (delayed) {
|
||||
this.pushPhase(phase);
|
||||
phaseManager.pushPhase(phase);
|
||||
} else {
|
||||
this.unshiftPhase(phase);
|
||||
phaseManager.unshiftPhase(phase);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3595,7 +3289,7 @@ export default class BattleScene extends SceneBase {
|
||||
activePokemon = activePokemon.concat(this.getEnemyParty());
|
||||
for (const p of activePokemon) {
|
||||
keys.push(p.getSpriteKey(true));
|
||||
if (p instanceof PlayerPokemon) {
|
||||
if (p.isPlayer()) {
|
||||
keys.push(p.getBattleSpriteKey(true, true));
|
||||
}
|
||||
keys.push(p.species.getCryKey(p.formIndex));
|
||||
@ -3611,7 +3305,7 @@ export default class BattleScene extends SceneBase {
|
||||
* @param pokemon The (enemy) pokemon
|
||||
*/
|
||||
initFinalBossPhaseTwo(pokemon: Pokemon): void {
|
||||
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
|
||||
if (pokemon.isEnemy() && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
|
||||
this.fadeOutBgm(fixedInt(2000), false);
|
||||
this.ui.showDialogue(
|
||||
battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin,
|
||||
@ -3629,19 +3323,19 @@ export default class BattleScene extends SceneBase {
|
||||
this.currentBattle.double = true;
|
||||
const availablePartyMembers = this.getPlayerParty().filter(p => p.isAllowedInBattle());
|
||||
if (availablePartyMembers.length > 1) {
|
||||
this.pushPhase(new ToggleDoublePositionPhase(true));
|
||||
this.phaseManager.pushNew("ToggleDoublePositionPhase", true);
|
||||
if (!availablePartyMembers[1].isOnField()) {
|
||||
this.pushPhase(new SummonPhase(1));
|
||||
this.phaseManager.pushNew("SummonPhase", 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.shiftPhase();
|
||||
this.phaseManager.shiftPhase();
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.shiftPhase();
|
||||
this.phaseManager.shiftPhase();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3753,10 +3447,10 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
if (exp) {
|
||||
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
|
||||
this.unshiftPhase(
|
||||
this.phaseManager.unshiftPhase(
|
||||
expPartyMembers[pm].isOnField()
|
||||
? new ExpPhase(partyMemberIndex, exp)
|
||||
: new ShowPartyExpBarPhase(partyMemberIndex, exp),
|
||||
? this.phaseManager.create("ExpPhase", partyMemberIndex, exp)
|
||||
: this.phaseManager.create("ShowPartyExpBarPhase", partyMemberIndex, exp),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { Command } from "./ui/command-ui-handler";
|
||||
import type { Command } from "#enums/command";
|
||||
import {
|
||||
randomString,
|
||||
getEnumValues,
|
||||
@ -8,8 +8,10 @@ import {
|
||||
shiftCharCodes,
|
||||
randSeedItem,
|
||||
randInt,
|
||||
randSeedFloat,
|
||||
} from "#app/utils/common";
|
||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
||||
import Trainer from "./field/trainer";
|
||||
import { TrainerVariant } from "#enums/trainer-variant";
|
||||
import type { GameMode } from "./game-mode";
|
||||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||
import type { PokeballType } from "#enums/pokeball";
|
||||
@ -32,14 +34,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||
|
||||
export enum BattlerIndex {
|
||||
ATTACKER = -1,
|
||||
PLAYER,
|
||||
PLAYER_2,
|
||||
ENEMY,
|
||||
ENEMY_2,
|
||||
}
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
|
||||
export interface TurnCommand {
|
||||
command: Command;
|
||||
@ -150,7 +145,7 @@ export default class Battle {
|
||||
randSeedGaussForLevel(value: number): number {
|
||||
let rand = 0;
|
||||
for (let i = value; i > 0; i--) {
|
||||
rand += Phaser.Math.RND.realInRange(0, 1);
|
||||
rand += randSeedFloat();
|
||||
}
|
||||
return rand / value;
|
||||
}
|
||||
@ -205,7 +200,7 @@ export default class Battle {
|
||||
const message = i18next.t("battle:moneyPickedUp", {
|
||||
moneyAmount: formattedMoneyAmount,
|
||||
});
|
||||
globalScene.queueMessage(message, undefined, true);
|
||||
globalScene.phaseManager.queueMessage(message, undefined, true);
|
||||
|
||||
globalScene.currentBattle.moneyScattered = 0;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/** The maximum size of the player's party */
|
||||
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
||||
|
||||
@ -17,3 +19,38 @@ export const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180
|
||||
|
||||
/** The raw percentage power boost for type boost items*/
|
||||
export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
|
||||
|
||||
/**
|
||||
* The default species that a new player can choose from
|
||||
*/
|
||||
export const defaultStarterSpecies: SpeciesId[] = [
|
||||
SpeciesId.BULBASAUR,
|
||||
SpeciesId.CHARMANDER,
|
||||
SpeciesId.SQUIRTLE,
|
||||
SpeciesId.CHIKORITA,
|
||||
SpeciesId.CYNDAQUIL,
|
||||
SpeciesId.TOTODILE,
|
||||
SpeciesId.TREECKO,
|
||||
SpeciesId.TORCHIC,
|
||||
SpeciesId.MUDKIP,
|
||||
SpeciesId.TURTWIG,
|
||||
SpeciesId.CHIMCHAR,
|
||||
SpeciesId.PIPLUP,
|
||||
SpeciesId.SNIVY,
|
||||
SpeciesId.TEPIG,
|
||||
SpeciesId.OSHAWOTT,
|
||||
SpeciesId.CHESPIN,
|
||||
SpeciesId.FENNEKIN,
|
||||
SpeciesId.FROAKIE,
|
||||
SpeciesId.ROWLET,
|
||||
SpeciesId.LITTEN,
|
||||
SpeciesId.POPPLIO,
|
||||
SpeciesId.GROOKEY,
|
||||
SpeciesId.SCORBUNNY,
|
||||
SpeciesId.SOBBLE,
|
||||
SpeciesId.SPRIGATITO,
|
||||
SpeciesId.FUECOCO,
|
||||
SpeciesId.QUAXLY,
|
||||
];
|
||||
|
||||
export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||
|
@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id";
|
||||
import type { AbAttrCondition } from "#app/@types/ability-types";
|
||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
||||
import i18next from "i18next";
|
||||
import type { Localizable } from "#app/interfaces/locales";
|
||||
import type { Localizable } from "#app/@types/locales";
|
||||
import type { Constructor } from "#app/utils/common";
|
||||
|
||||
export class Ability implements Localizable {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,9 +7,9 @@ import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { HitResult } from "#app/field/pokemon";
|
||||
import { HitResult } from "#enums/hit-result";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import {
|
||||
BlockNonDirectDamageAbAttr,
|
||||
InfiltratorAbAttr,
|
||||
@ -20,22 +20,14 @@ import {
|
||||
applyOnLoseAbAttrs,
|
||||
} from "#app/data/abilities/ability";
|
||||
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 { AbilityId } from "#enums/ability-id";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
|
||||
export enum ArenaTagSide {
|
||||
BOTH,
|
||||
PLAYER,
|
||||
ENEMY,
|
||||
}
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
|
||||
export abstract class ArenaTag {
|
||||
constructor(
|
||||
@ -54,7 +46,7 @@ export abstract class ArenaTag {
|
||||
|
||||
onRemove(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
{ moveName: this.getMoveName() },
|
||||
@ -126,7 +118,7 @@ export class MistTag extends ArenaTag {
|
||||
const source = globalScene.getPokemonById(this.sourceId);
|
||||
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:mistOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -161,7 +153,7 @@ export class MistTag extends ArenaTag {
|
||||
cancelled.value = true;
|
||||
|
||||
if (!simulated) {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -239,7 +231,7 @@ class ReflectTag extends WeakenMoveScreenTag {
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -259,7 +251,7 @@ class LightScreenTag extends WeakenMoveScreenTag {
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -282,7 +274,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag {
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -318,7 +310,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
{ moveName: super.getMoveName() },
|
||||
@ -355,7 +347,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
isProtected.value = true;
|
||||
if (!simulated) {
|
||||
new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:conditionalProtectApply", {
|
||||
moveName: super.getMoveName(),
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(defender),
|
||||
@ -381,7 +373,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
*/
|
||||
const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
|
||||
const move = allMoves[moveId];
|
||||
const effectPhase = globalScene.getCurrentPhase();
|
||||
const effectPhase = globalScene.phaseManager.getCurrentPhase();
|
||||
|
||||
if (effectPhase?.is("MoveEffectPhase")) {
|
||||
const attacker = effectPhase.getUserPokemon();
|
||||
@ -458,7 +450,7 @@ class MatBlockTag extends ConditionalProtectTag {
|
||||
if (this.sourceId) {
|
||||
const source = globalScene.getPokemonById(this.sourceId);
|
||||
if (source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:matBlockOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -517,7 +509,7 @@ export class NoCritTag extends ArenaTag {
|
||||
|
||||
/** Queues a message upon adding this effect to the field */
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
|
||||
moveName: this.getMoveName(),
|
||||
}),
|
||||
@ -527,7 +519,7 @@ export class NoCritTag extends ArenaTag {
|
||||
/** Queues a message upon removing this effect from the field */
|
||||
onRemove(_arena: Arena): void {
|
||||
const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:noCritOnRemove", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
|
||||
moveName: this.getMoveName(),
|
||||
@ -567,8 +559,8 @@ class WishTag extends ArenaTag {
|
||||
onRemove(_arena: Arena): void {
|
||||
const target = globalScene.getField()[this.battlerIndex];
|
||||
if (target?.isActive(true)) {
|
||||
globalScene.queueMessage(this.triggerMessage);
|
||||
globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false));
|
||||
globalScene.phaseManager.queueMessage(this.triggerMessage);
|
||||
globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), this.healHp, null, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -621,11 +613,11 @@ class MudSportTag extends WeakenMoveTypeTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,11 +631,11 @@ class WaterSportTag extends WeakenMoveTypeTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,7 +651,7 @@ export class IonDelugeTag extends ArenaTag {
|
||||
|
||||
/** Queues an on-add message */
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {} // Removes default on-remove message
|
||||
@ -758,7 +750,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:spikesOnAdd", {
|
||||
moveName: this.getMoveName(),
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
@ -781,7 +773,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||
const damageHpRatio = 1 / (10 - 2 * this.layers);
|
||||
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:spikesActivateTrap", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
@ -811,7 +803,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:toxicSpikesOnAdd", {
|
||||
moveName: this.getMoveName(),
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
@ -834,7 +826,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
if (pokemon.isOfType(PokemonType.POISON)) {
|
||||
this.neutralized = true;
|
||||
if (globalScene.arena.removeTag(this.tagType)) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:toxicSpikesActivateTrapPoison", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
moveName: this.getMoveName(),
|
||||
@ -891,8 +883,13 @@ export class DelayedAttackTag extends ArenaTag {
|
||||
const ret = super.lapse(arena);
|
||||
|
||||
if (!ret) {
|
||||
globalScene.unshiftPhase(
|
||||
new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], false, true),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"MoveEffectPhase",
|
||||
this.sourceId!,
|
||||
[this.targetIndex],
|
||||
allMoves[this.sourceMove!],
|
||||
false,
|
||||
true,
|
||||
); // TODO: are those bangs correct?
|
||||
}
|
||||
|
||||
@ -917,7 +914,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stealthRockOnAdd", {
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
}),
|
||||
@ -971,7 +968,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
}
|
||||
|
||||
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stealthRockActivateTrap", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
@ -1001,7 +998,7 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
super.onAdd(arena);
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stickyWebOnAdd", {
|
||||
moveName: this.getMoveName(),
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
@ -1020,25 +1017,24 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
}
|
||||
|
||||
if (!cancelled.value) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stickyWebActivateTrap", {
|
||||
pokemonName: pokemon.getNameToRender(),
|
||||
}),
|
||||
);
|
||||
const stages = new NumberHolder(-1);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
false,
|
||||
[Stat.SPD],
|
||||
stages.value,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
false,
|
||||
[Stat.SPD],
|
||||
stages.value,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@ -1074,7 +1070,7 @@ export class TrickRoomTag extends ArenaTag {
|
||||
onAdd(_arena: Arena): void {
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:trickRoomOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -1083,7 +1079,7 @@ export class TrickRoomTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1098,7 +1094,7 @@ export class GravityTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
globalScene.getField(true).forEach(pokemon => {
|
||||
if (pokemon !== null) {
|
||||
pokemon.removeTag(BattlerTagType.FLOATING);
|
||||
@ -1111,7 +1107,7 @@ export class GravityTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1127,7 +1123,7 @@ class TailwindTag extends ArenaTag {
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1136,31 +1132,33 @@ class TailwindTag extends ArenaTag {
|
||||
|
||||
const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
|
||||
const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
|
||||
const phaseManager = globalScene.phaseManager;
|
||||
|
||||
for (const pokemon of party) {
|
||||
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
||||
if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
||||
pokemon.addTag(BattlerTagType.CHARGED);
|
||||
globalScene.queueMessage(
|
||||
phaseManager.queueMessage(
|
||||
i18next.t("abilityTriggers:windPowerCharged", {
|
||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||
moveName: this.getMoveName(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Raise attack by one stage if party member has WIND_RIDER ability
|
||||
// TODO: Ability displays should be handled by the ability
|
||||
if (pokemon.hasAbility(AbilityId.WIND_RIDER)) {
|
||||
globalScene.queueAbilityDisplay(pokemon, false, true);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true));
|
||||
globalScene.queueAbilityDisplay(pokemon, false, false);
|
||||
phaseManager.queueAbilityDisplay(pokemon, false, true);
|
||||
phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true);
|
||||
phaseManager.queueAbilityDisplay(pokemon, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1179,11 +1177,11 @@ class HappyHourTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1193,7 +1191,7 @@ class SafeguardTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1201,7 +1199,7 @@ class SafeguardTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1237,7 +1235,7 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId);
|
||||
}
|
||||
});
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("battlerTags:imprisonOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -1294,7 +1292,7 @@ class FireGrassPledgeTag extends ArenaTag {
|
||||
|
||||
override onAdd(_arena: Arena): void {
|
||||
// "A sea of fire enveloped your/the opposing team!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1309,14 +1307,17 @@ class FireGrassPledgeTag extends ArenaTag {
|
||||
.filter(pokemon => !pokemon.isOfType(PokemonType.FIRE) && !pokemon.switchOutStatus)
|
||||
.forEach(pokemon => {
|
||||
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:fireGrassPledgeLapse", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
);
|
||||
// TODO: Replace this with a proper animation
|
||||
globalScene.unshiftPhase(
|
||||
new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"CommonAnimPhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
pokemon.getBattlerIndex(),
|
||||
CommonAnim.MAGMA_STORM,
|
||||
);
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
});
|
||||
@ -1339,7 +1340,7 @@ class WaterFirePledgeTag extends ArenaTag {
|
||||
|
||||
override onAdd(_arena: Arena): void {
|
||||
// "A rainbow appeared in the sky on your/the opposing team's side!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1373,7 +1374,7 @@ class GrassWaterPledgeTag extends ArenaTag {
|
||||
|
||||
override onAdd(_arena: Arena): void {
|
||||
// "A swamp enveloped your/the opposing team!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1394,7 +1395,7 @@ export class FairyLockTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1451,7 +1452,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
public override onRemove(_arena: Arena, quiet = false) {
|
||||
this.beingRemoved = true;
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
|
||||
}
|
||||
|
||||
for (const pokemon of globalScene.getField(true)) {
|
||||
@ -1472,7 +1473,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
|
||||
private playActivationMessage(pokemon: Pokemon | null) {
|
||||
if (pokemon) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:neutralizingGasOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
|
@ -5988,6 +5988,7 @@ export const tmSpecies: TmSpecies = {
|
||||
SpeciesId.FEZANDIPITI,
|
||||
SpeciesId.ARCHALUDON,
|
||||
SpeciesId.IRON_CROWN,
|
||||
SpeciesId.TERAPAGOS,
|
||||
SpeciesId.ALOLA_RATICATE,
|
||||
SpeciesId.ALOLA_RAICHU,
|
||||
SpeciesId.ALOLA_SANDSLASH,
|
||||
@ -16248,6 +16249,7 @@ export const tmSpecies: TmSpecies = {
|
||||
SpeciesId.CALYREX,
|
||||
SpeciesId.SANDY_SHOCKS,
|
||||
SpeciesId.IRON_JUGULIS,
|
||||
SpeciesId.TERAPAGOS,
|
||||
SpeciesId.ALOLA_DUGTRIO,
|
||||
SpeciesId.GALAR_SLOWPOKE,
|
||||
SpeciesId.GALAR_SLOWBRO,
|
||||
@ -39466,6 +39468,8 @@ export const tmSpecies: TmSpecies = {
|
||||
SpeciesId.FARFETCHD,
|
||||
SpeciesId.DODUO,
|
||||
SpeciesId.DODRIO,
|
||||
SpeciesId.DEWGONG,
|
||||
SpeciesId.GRIMER,
|
||||
SpeciesId.MUK,
|
||||
SpeciesId.GASTLY,
|
||||
SpeciesId.HAUNTER,
|
||||
@ -39477,6 +39481,7 @@ export const tmSpecies: TmSpecies = {
|
||||
SpeciesId.CUBONE,
|
||||
SpeciesId.MAROWAK,
|
||||
SpeciesId.HITMONLEE,
|
||||
SpeciesId.HITMONCHAN,
|
||||
SpeciesId.LICKITUNG,
|
||||
SpeciesId.TANGELA,
|
||||
SpeciesId.GOLDEEN,
|
||||
@ -48806,6 +48811,7 @@ export const tmSpecies: TmSpecies = {
|
||||
SpeciesId.GARGANACL,
|
||||
SpeciesId.GLIMMET,
|
||||
SpeciesId.GLIMMORA,
|
||||
SpeciesId.TERAPAGOS,
|
||||
SpeciesId.ALOLA_GEODUDE,
|
||||
SpeciesId.ALOLA_GRAVELER,
|
||||
SpeciesId.ALOLA_GOLEM,
|
||||
@ -53077,6 +53083,7 @@ export const tmSpecies: TmSpecies = {
|
||||
SpeciesId.MIRAIDON,
|
||||
SpeciesId.ARCHALUDON,
|
||||
SpeciesId.IRON_CROWN,
|
||||
SpeciesId.TERAPAGOS,
|
||||
[
|
||||
SpeciesId.WORMADAM,
|
||||
"trash",
|
||||
|
@ -1,111 +1,15 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove } from "./moves/move";
|
||||
import { allMoves } from "./data-lists";
|
||||
import { MoveFlags } from "#enums/MoveFlags";
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common";
|
||||
import type { BattlerIndex } from "../battle";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SubstituteTag } from "./battler-tags";
|
||||
import { isNullOrUndefined } from "../utils/common";
|
||||
import Phaser from "phaser";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
|
||||
export enum AnimFrameTarget {
|
||||
USER,
|
||||
TARGET,
|
||||
GRAPHIC,
|
||||
}
|
||||
|
||||
enum AnimFocus {
|
||||
TARGET = 1,
|
||||
USER,
|
||||
USER_TARGET,
|
||||
SCREEN,
|
||||
}
|
||||
|
||||
enum AnimBlendType {
|
||||
NORMAL,
|
||||
ADD,
|
||||
SUBTRACT,
|
||||
}
|
||||
|
||||
export enum ChargeAnim {
|
||||
FLY_CHARGING = 1000,
|
||||
BOUNCE_CHARGING,
|
||||
DIG_CHARGING,
|
||||
FUTURE_SIGHT_CHARGING,
|
||||
DIVE_CHARGING,
|
||||
SOLAR_BEAM_CHARGING,
|
||||
SHADOW_FORCE_CHARGING,
|
||||
SKULL_BASH_CHARGING,
|
||||
FREEZE_SHOCK_CHARGING,
|
||||
SKY_DROP_CHARGING,
|
||||
SKY_ATTACK_CHARGING,
|
||||
ICE_BURN_CHARGING,
|
||||
DOOM_DESIRE_CHARGING,
|
||||
RAZOR_WIND_CHARGING,
|
||||
PHANTOM_FORCE_CHARGING,
|
||||
GEOMANCY_CHARGING,
|
||||
SHADOW_BLADE_CHARGING,
|
||||
SOLAR_BLADE_CHARGING,
|
||||
BEAK_BLAST_CHARGING,
|
||||
METEOR_BEAM_CHARGING,
|
||||
ELECTRO_SHOT_CHARGING,
|
||||
}
|
||||
|
||||
export enum CommonAnim {
|
||||
USE_ITEM = 2000,
|
||||
HEALTH_UP,
|
||||
TERASTALLIZE,
|
||||
POISON = 2010,
|
||||
TOXIC,
|
||||
PARALYSIS,
|
||||
SLEEP,
|
||||
FROZEN,
|
||||
BURN,
|
||||
CONFUSION,
|
||||
ATTRACT,
|
||||
BIND,
|
||||
WRAP,
|
||||
CURSE_NO_GHOST,
|
||||
LEECH_SEED,
|
||||
FIRE_SPIN,
|
||||
PROTECT,
|
||||
COVET,
|
||||
WHIRLPOOL,
|
||||
BIDE,
|
||||
SAND_TOMB,
|
||||
QUICK_GUARD,
|
||||
WIDE_GUARD,
|
||||
CURSE,
|
||||
MAGMA_STORM,
|
||||
CLAMP,
|
||||
SNAP_TRAP,
|
||||
THUNDER_CAGE,
|
||||
INFESTATION,
|
||||
ORDER_UP_CURLY,
|
||||
ORDER_UP_DROOPY,
|
||||
ORDER_UP_STRETCHY,
|
||||
RAGING_BULL_FIRE,
|
||||
RAGING_BULL_WATER,
|
||||
SALT_CURE,
|
||||
POWDER,
|
||||
SUNNY = 2100,
|
||||
RAIN,
|
||||
SANDSTORM,
|
||||
HAIL,
|
||||
SNOW,
|
||||
WIND,
|
||||
HEAVY_RAIN,
|
||||
HARSH_SUN,
|
||||
STRONG_WINDS,
|
||||
MISTY_TERRAIN = 2110,
|
||||
ELECTRIC_TERRAIN,
|
||||
GRASSY_TERRAIN,
|
||||
PSYCHIC_TERRAIN,
|
||||
LOCK_ON = 2120,
|
||||
}
|
||||
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||
|
||||
export class AnimConfig {
|
||||
public id: number;
|
||||
@ -531,7 +435,7 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
||||
if (moveAnims.get(move) !== null) {
|
||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||
? allMoves[move]
|
||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
||||
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||
if (chargeAnimSource && chargeAnims.get(chargeAnimSource.chargeAnim) === null) {
|
||||
return;
|
||||
}
|
||||
@ -542,12 +446,11 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
||||
}
|
||||
} else {
|
||||
moveAnims.set(move, null);
|
||||
const defaultMoveAnim =
|
||||
allMoves[move] instanceof AttackMove
|
||||
? MoveId.TACKLE
|
||||
: allMoves[move] instanceof SelfStatusMove
|
||||
? MoveId.FOCUS_ENERGY
|
||||
: MoveId.TAIL_WHIP;
|
||||
const defaultMoveAnim = allMoves[move].is("AttackMove")
|
||||
? MoveId.TACKLE
|
||||
: allMoves[move].is("SelfStatusMove")
|
||||
? MoveId.FOCUS_ENERGY
|
||||
: MoveId.TAIL_WHIP;
|
||||
|
||||
const fetchAnimAndResolve = (move: MoveId) => {
|
||||
globalScene
|
||||
@ -570,7 +473,7 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
||||
}
|
||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||
? allMoves[move]
|
||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
||||
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||
if (chargeAnimSource) {
|
||||
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
||||
} else {
|
||||
@ -703,7 +606,7 @@ export function loadMoveAnimAssets(moveIds: MoveId[], startLoad?: boolean): Prom
|
||||
for (const moveId of moveIds) {
|
||||
const chargeAnimSource = allMoves[moveId].isChargingMove()
|
||||
? allMoves[moveId]
|
||||
: (allMoves[moveId].getAttrs(DelayedAttackAttr)[0] ?? allMoves[moveId].getAttrs(BeakBlastHeaderAttr)[0]);
|
||||
: (allMoves[moveId].getAttrs("DelayedAttackAttr")[0] ?? allMoves[moveId].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||
if (chargeAnimSource) {
|
||||
const moveChargeAnims = chargeAnims.get(chargeAnimSource.chargeAnim);
|
||||
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct?
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,13 @@
|
||||
import { getPokemonNameWithAffix } from "../messages";
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import { HitResult } from "../field/pokemon";
|
||||
import { HitResult } from "#enums/hit-result";
|
||||
import { getStatusEffectHealText } from "./status-effect";
|
||||
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
||||
import {
|
||||
DoubleBerryEffectAbAttr,
|
||||
ReduceBerryUseThresholdAbAttr,
|
||||
applyAbAttrs,
|
||||
} from "./abilities/ability";
|
||||
import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./abilities/ability";
|
||||
import i18next from "i18next";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat, type BattleStat } from "#app/enums/stat";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
export function getBerryName(berryType: BerryType): string {
|
||||
@ -79,23 +73,22 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
{
|
||||
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, hpHealed);
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
consumer.getBattlerIndex(),
|
||||
hpHealed.value,
|
||||
i18next.t("battle:hpHealBerry", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||
berryName: getBerryName(berryType),
|
||||
}),
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
consumer.getBattlerIndex(),
|
||||
hpHealed.value,
|
||||
i18next.t("battle:hpHealBerry", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||
berryName: getBerryName(berryType),
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case BerryType.LUM:
|
||||
{
|
||||
if (consumer.status) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
getStatusEffectHealText(consumer.status.effect, getPokemonNameWithAffix(consumer)),
|
||||
);
|
||||
}
|
||||
@ -113,8 +106,12 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
const statStages = new NumberHolder(1);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, statStages);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(consumer.getBattlerIndex(), true, [stat], statStages.value),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
consumer.getBattlerIndex(),
|
||||
true,
|
||||
[stat],
|
||||
statStages.value,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@ -130,8 +127,12 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
||||
const stages = new NumberHolder(2);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, stages);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(consumer.getBattlerIndex(), true, [randStat], stages.value),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
consumer.getBattlerIndex(),
|
||||
true,
|
||||
[randStat],
|
||||
stages.value,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@ -144,7 +145,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
|
||||
if (ppRestoreMove) {
|
||||
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("battle:ppHealBerry", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||
moveName: ppRestoreMove.getName(),
|
||||
|
@ -2,17 +2,18 @@ import { BooleanHolder, type NumberHolder, randSeedItem } from "#app/utils/commo
|
||||
import { deepCopy } from "#app/utils/data";
|
||||
import i18next from "i18next";
|
||||
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
||||
import { defaultStarterSpecies } from "#app/system/game-data";
|
||||
import { defaultStarterSpecies } from "#app/constants";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "./moves/pokemon-move";
|
||||
import type { FixedBattleConfig } from "#app/battle";
|
||||
import { getRandomTrainerFunc } from "#app/battle";
|
||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
||||
import Trainer from "#app/field/trainer";
|
||||
import { TrainerVariant } from "#enums/trainer-variant";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
@ -24,93 +25,12 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { pokemonFormChanges } from "./pokemon-forms";
|
||||
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
||||
import { ChallengeType } from "#enums/challenge-type";
|
||||
import type { MoveSourceType } from "#enums/move-source-type";
|
||||
|
||||
/** A constant for the default max cost of the starting party before a run */
|
||||
const DEFAULT_PARTY_MAX_COST = 10;
|
||||
|
||||
/**
|
||||
* An enum for all the challenge types. The parameter entries on these describe the
|
||||
* parameters to use when calling the applyChallenges function.
|
||||
*/
|
||||
export enum ChallengeType {
|
||||
/**
|
||||
* Challenges which modify what starters you can choose
|
||||
* @see {@link Challenge.applyStarterChoice}
|
||||
*/
|
||||
STARTER_CHOICE,
|
||||
/**
|
||||
* Challenges which modify how many starter points you have
|
||||
* @see {@link Challenge.applyStarterPoints}
|
||||
*/
|
||||
STARTER_POINTS,
|
||||
/**
|
||||
* Challenges which modify how many starter points you have
|
||||
* @see {@link Challenge.applyStarterPointCost}
|
||||
*/
|
||||
STARTER_COST,
|
||||
/**
|
||||
* Challenges which modify your starters in some way
|
||||
* @see {@link Challenge.applyStarterModify}
|
||||
*/
|
||||
STARTER_MODIFY,
|
||||
/**
|
||||
* Challenges which limit which pokemon you can have in battle.
|
||||
* @see {@link Challenge.applyPokemonInBattle}
|
||||
*/
|
||||
POKEMON_IN_BATTLE,
|
||||
/**
|
||||
* Adds or modifies the fixed battles in a run
|
||||
* @see {@link Challenge.applyFixedBattle}
|
||||
*/
|
||||
FIXED_BATTLES,
|
||||
/**
|
||||
* Modifies the effectiveness of Type matchups in battle
|
||||
* @see {@linkcode Challenge.applyTypeEffectiveness}
|
||||
*/
|
||||
TYPE_EFFECTIVENESS,
|
||||
/**
|
||||
* Modifies what level the AI pokemon are. UNIMPLEMENTED.
|
||||
*/
|
||||
AI_LEVEL,
|
||||
/**
|
||||
* Modifies how many move slots the AI has. UNIMPLEMENTED.
|
||||
*/
|
||||
AI_MOVE_SLOTS,
|
||||
/**
|
||||
* Modifies if a pokemon has its passive. UNIMPLEMENTED.
|
||||
*/
|
||||
PASSIVE_ACCESS,
|
||||
/**
|
||||
* Modifies the game mode settings in some way. UNIMPLEMENTED.
|
||||
*/
|
||||
GAME_MODE_MODIFY,
|
||||
/**
|
||||
* Modifies what level AI pokemon can access a move. UNIMPLEMENTED.
|
||||
*/
|
||||
MOVE_ACCESS,
|
||||
/**
|
||||
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
|
||||
*/
|
||||
MOVE_WEIGHT,
|
||||
/**
|
||||
* Modifies what the pokemon stats for Flip Stat Mode.
|
||||
*/
|
||||
FLIP_STAT,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for challenge types that modify movesets, these denote the various sources of moves for pokemon.
|
||||
*/
|
||||
export enum MoveSourceType {
|
||||
LEVEL_UP, // Currently unimplemented for move access
|
||||
RELEARNER, // Relearner moves currently unimplemented
|
||||
COMMON_TM,
|
||||
GREAT_TM,
|
||||
ULTRA_TM,
|
||||
COMMON_EGG,
|
||||
RARE_EGG,
|
||||
}
|
||||
|
||||
/**
|
||||
* A challenge object. Exists only to serve as a base class.
|
||||
*/
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { DexEntry, StarterDataEntry } from "#app/system/game-data";
|
||||
import type { StarterDataEntry } from "#app/system/game-data";
|
||||
import type { DexEntry } from "#app/@types/dex-data";
|
||||
|
||||
/**
|
||||
* Stores data associated with a specific egg and the hatched pokemon
|
||||
|
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 { NumberHolder, isNullOrUndefined } from "#app/utils/common";
|
||||
import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { applyMoveAttrs } from "./apply-attrs";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
|
||||
/**
|
||||
* Return whether the move targets the field
|
||||
@ -18,3 +27,88 @@ export function isFieldTargeted(move: Move): boolean {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: MoveTarget): MoveTargetSet {
|
||||
const variableTarget = new NumberHolder(0);
|
||||
user.getOpponents(false).forEach(p => applyMoveAttrs("VariableTargetAttr", user, p, allMoves[move], variableTarget));
|
||||
|
||||
let moveTarget: MoveTarget | undefined;
|
||||
if (allMoves[move].hasAttr("VariableTargetAttr")) {
|
||||
moveTarget = variableTarget.value;
|
||||
} else if (replaceTarget !== undefined) {
|
||||
moveTarget = replaceTarget;
|
||||
} else if (move) {
|
||||
moveTarget = allMoves[move].moveTarget;
|
||||
} else if (move === undefined) {
|
||||
moveTarget = MoveTarget.NEAR_ENEMY;
|
||||
}
|
||||
const opponents = user.getOpponents(false);
|
||||
|
||||
let set: Pokemon[] = [];
|
||||
let multiple = false;
|
||||
const ally: Pokemon | undefined = user.getAlly();
|
||||
|
||||
switch (moveTarget) {
|
||||
case MoveTarget.USER:
|
||||
case MoveTarget.PARTY:
|
||||
set = [user];
|
||||
break;
|
||||
case MoveTarget.NEAR_OTHER:
|
||||
case MoveTarget.OTHER:
|
||||
case MoveTarget.ALL_NEAR_OTHERS:
|
||||
case MoveTarget.ALL_OTHERS:
|
||||
set = !isNullOrUndefined(ally) ? opponents.concat([ally]) : opponents;
|
||||
multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS;
|
||||
break;
|
||||
case MoveTarget.NEAR_ENEMY:
|
||||
case MoveTarget.ALL_NEAR_ENEMIES:
|
||||
case MoveTarget.ALL_ENEMIES:
|
||||
case MoveTarget.ENEMY_SIDE:
|
||||
set = opponents;
|
||||
multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
|
||||
break;
|
||||
case MoveTarget.RANDOM_NEAR_ENEMY:
|
||||
set = [opponents[user.randBattleSeedInt(opponents.length)]];
|
||||
break;
|
||||
case MoveTarget.ATTACKER:
|
||||
return { targets: [-1 as BattlerIndex], multiple: false };
|
||||
case MoveTarget.NEAR_ALLY:
|
||||
case MoveTarget.ALLY:
|
||||
set = !isNullOrUndefined(ally) ? [ally] : [];
|
||||
break;
|
||||
case MoveTarget.USER_OR_NEAR_ALLY:
|
||||
case MoveTarget.USER_AND_ALLIES:
|
||||
case MoveTarget.USER_SIDE:
|
||||
set = !isNullOrUndefined(ally) ? [user, ally] : [user];
|
||||
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
|
||||
break;
|
||||
case MoveTarget.ALL:
|
||||
case MoveTarget.BOTH_SIDES:
|
||||
set = (!isNullOrUndefined(ally) ? [user, ally] : [user]).concat(opponents);
|
||||
multiple = true;
|
||||
break;
|
||||
case MoveTarget.CURSE:
|
||||
{
|
||||
const extraTargets = !isNullOrUndefined(ally) ? [ally] : [];
|
||||
set = user.getTypes(true).includes(PokemonType.GHOST) ? opponents.concat(extraTargets) : [user];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
targets: set
|
||||
.filter(p => p?.isActive(true))
|
||||
.map(p => p.getBattlerIndex())
|
||||
.filter(t => t !== undefined),
|
||||
multiple,
|
||||
};
|
||||
}
|
||||
|
||||
export const frenzyMissFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => {
|
||||
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) {
|
||||
user.getMoveQueue().shift();
|
||||
}
|
||||
user.removeTag(BattlerTagType.FRENZY); // FRENZY tag should be disrupted on miss/no effect
|
||||
|
||||
return true;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
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);
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ import i18next from "i18next";
|
||||
import type { IEggOptions } from "#app/data/egg";
|
||||
import { EggSourceType } from "#enums/egg-source-types";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
@ -182,7 +181,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Full heal party
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||
|
||||
const eggOptions: IEggOptions = {
|
||||
pulled: false,
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
transitionMysteryEncounterIntroVisuals,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
@ -25,7 +26,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { randInt } from "#app/utils/common";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import {
|
||||
applyModifierTypeToPlayerPokemon,
|
||||
catchPokemon,
|
||||
@ -33,9 +34,8 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import type { BerryType } from "#enums/berry-type";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import i18next from "i18next";
|
||||
|
||||
@ -237,8 +237,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
|
@ -22,7 +22,6 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import i18next from "i18next";
|
||||
|
||||
@ -137,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
||||
})
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player a Shiny Charm
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.SHINY_CHARM);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build(),
|
||||
|
@ -35,7 +35,6 @@ import { BerryModifier } from "#app/modifier/modifier";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
@ -237,8 +236,12 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
||||
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
};
|
||||
setEncounterRewards(
|
||||
|
@ -24,9 +24,8 @@ import { TrainerType } from "#enums/trainer-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
@ -766,8 +765,10 @@ function doBugTypeMoveTutor(): Promise<void> {
|
||||
|
||||
// Option select complete, handle if they are learning a move
|
||||
if (result && result.selectedOptionIndex < moveOptions.length) {
|
||||
globalScene.unshiftPhase(
|
||||
new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LearnMovePhase",
|
||||
result.selectedPokemonIndex,
|
||||
moveOptions[result.selectedOptionIndex].moveId,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,11 @@ import { UiMode } from "#enums/ui-mode";
|
||||
import i18next from "i18next";
|
||||
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { Ability } from "#app/data/abilities/ability-class";
|
||||
import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
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 { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -23,11 +23,10 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
@ -176,13 +175,12 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF],
|
||||
1,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -245,8 +243,10 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||
globalScene.unshiftPhase(
|
||||
new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), MoveId.REVELATION_DANCE),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LearnMovePhase",
|
||||
globalScene.getPlayerParty().indexOf(pokemon),
|
||||
MoveId.REVELATION_DANCE,
|
||||
);
|
||||
|
||||
// Play animation again to "learn" the dance
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
@ -165,7 +164,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player 5 Rogue Balls
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.ROGUE_BALL);
|
||||
|
||||
// Start encounter with random legendary (7-10 starter strength) that has level additive
|
||||
// If this is a mono-type challenge, always ensure the required type is filtered for
|
||||
|
@ -29,7 +29,6 @@ import {
|
||||
} from "#app/modifier/modifier";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { randSeedItem } from "#app/utils/common";
|
||||
@ -65,10 +64,10 @@ const doEventReward = () => {
|
||||
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
|
||||
});
|
||||
if (candidates.length > 0) {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)]));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes[randSeedItem(candidates)]);
|
||||
} else {
|
||||
// At max stacks, give a Voucher instead
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.VOUCHER);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -181,7 +180,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.AMULET_COIN);
|
||||
doEventReward();
|
||||
}
|
||||
|
||||
@ -266,7 +265,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.CANDY_JAR);
|
||||
doEventReward();
|
||||
}
|
||||
} else {
|
||||
@ -288,7 +287,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.BERRY_POUCH);
|
||||
doEventReward();
|
||||
}
|
||||
}
|
||||
@ -372,7 +371,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.HEALING_CHARM);
|
||||
doEventReward();
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,8 @@ import {
|
||||
setEncounterExp,
|
||||
setEncounterRewards,
|
||||
} 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 type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
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 { Gender } from "#app/data/gender";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
@ -44,7 +44,6 @@ import { EncounterAnim } from "#enums/encounter-anims";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Ability } from "#app/data/abilities/ability-class";
|
||||
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
@ -92,8 +91,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
gender: Gender.MALE,
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.SPDEF, Stat.SPD],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -103,8 +106,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
gender: Gender.FEMALE,
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.SPDEF, Stat.SPD],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
|
@ -32,7 +32,6 @@ import PokemonData from "#app/system/pokemon-data";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
@ -76,7 +75,13 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||
// Randomly boost 1 stat 2 stages
|
||||
// Cannot boost Spd, Acc, or Evasion
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2));
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[randSeedInt(4, 1)],
|
||||
2,
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -13,7 +13,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { FieldPosition } from "#app/field/pokemon";
|
||||
import { FieldPosition } from "#enums/field-position";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
@ -25,9 +25,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
||||
import { addPokeballOpenParticles } from "#app/field/anims";
|
||||
import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
|
||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
@ -411,13 +409,13 @@ function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise<void> {
|
||||
pokemon.resetSummonData();
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
if (pokemon.isShiny()) {
|
||||
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
|
||||
globalScene.phaseManager.unshiftNew("ShinySparklePhase", pokemon.getBattlerIndex());
|
||||
}
|
||||
|
||||
pokemon.resetTurnData();
|
||||
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||
globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex()));
|
||||
globalScene.phaseManager.pushNew("PostSummonPhase", pokemon.getBattlerIndex());
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
@ -33,7 +33,8 @@ import {
|
||||
} from "#app/utils/common";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import {
|
||||
HiddenAbilityRateBoosterModifier,
|
||||
|
@ -11,7 +11,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
|
||||
const OPTION_1_REQUIRED_MOVE = MoveId.SURF;
|
||||
const OPTION_2_REQUIRED_MOVE = MoveId.FLY;
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
@ -189,8 +188,8 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
const allowedPokemon = globalScene.getPokemonAllowedInBattle();
|
||||
if (allowedPokemon.length === 0) {
|
||||
// If there are no longer any legal pokemon in the party, game over.
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.unshiftPhase(new GameOverPhase());
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||
} else {
|
||||
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
|
@ -29,8 +29,6 @@ import { getEncounterText, showEncounterText } from "#app/data/mystery-encounter
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||
import { SummonPhase } from "#app/phases/summon-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||
|
||||
@ -276,7 +274,7 @@ async function summonSafariPokemon() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Message pokemon remaining
|
||||
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
|
||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true);
|
||||
globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true);
|
||||
|
||||
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
|
||||
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
||||
@ -325,7 +323,7 @@ async function summonSafariPokemon() {
|
||||
encounter.misc.pokemon = pokemon;
|
||||
encounter.misc.safariPokemonRemaining -= 1;
|
||||
|
||||
globalScene.unshiftPhase(new SummonPhase(0, false));
|
||||
globalScene.phaseManager.unshiftNew("SummonPhase", 0, false);
|
||||
|
||||
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
||||
|
||||
@ -336,7 +334,7 @@ async function summonSafariPokemon() {
|
||||
|
||||
const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier);
|
||||
if (ivScannerModifier) {
|
||||
globalScene.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex()));
|
||||
globalScene.phaseManager.pushNew("ScanIvsPhase", pokemon.getBattlerIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,7 +557,7 @@ async function doEndTurn(cursorIndex: number) {
|
||||
leaveEncounterWithoutBattle(true);
|
||||
}
|
||||
} else {
|
||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||
globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||
initSubsequentOptionSelect({
|
||||
overrideOptions: safariZoneGameOptions,
|
||||
startingCursorIndex: cursorIndex,
|
||||
|
@ -21,12 +21,12 @@ import {
|
||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { AiType, PokemonMove } from "#app/field/pokemon";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { AiType } from "#enums/ai-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
@ -155,7 +155,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
async () => {
|
||||
// Fall asleep waiting for Snorlax
|
||||
// Full heal party
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||
queueEncounterMessage(`${namespace}:option.2.rest_result`);
|
||||
leaveEncounterWithoutBattle();
|
||||
},
|
||||
|
@ -27,7 +27,6 @@ import { getPartyLuckValue, modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import {
|
||||
@ -227,7 +226,13 @@ async function doBiomeTransitionDialogueAndBattleInit() {
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:boss_enraged`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -658,8 +658,8 @@ function onGameOver() {
|
||||
globalScene.playBgm(globalScene.arena.bgm);
|
||||
|
||||
// Clear any leftover battle phases
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||
|
||||
// Return enemy Pokemon
|
||||
const pokemon = globalScene.getEnemyPokemon();
|
||||
|
@ -17,17 +17,16 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
@ -116,8 +115,12 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.DEF, Stat.SPDEF],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
|
@ -23,13 +23,10 @@ import { Nature } from "#enums/nature";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms";
|
||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability";
|
||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
||||
import { ReturnPhase } from "#app/phases/return-phase";
|
||||
import i18next from "i18next";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
@ -143,7 +140,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter
|
||||
},
|
||||
async () => {
|
||||
// Refuse the challenge, they full heal the party and give the player a Rarer Candy
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY],
|
||||
fillRemaining: false,
|
||||
@ -209,7 +206,7 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
||||
for (const pokemon of playerField) {
|
||||
pokemon.lapseTag(BattlerTagType.COMMANDED);
|
||||
}
|
||||
playerField.forEach((_, p) => globalScene.unshiftPhase(new ReturnPhase(p)));
|
||||
playerField.forEach((_, p) => globalScene.phaseManager.unshiftNew("ReturnPhase", p));
|
||||
|
||||
for (const pokemon of globalScene.getPlayerParty()) {
|
||||
// Only trigger form change when Eiscue is in Noice form
|
||||
@ -227,7 +224,7 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||
}
|
||||
|
||||
globalScene.unshiftPhase(new ShowTrainerPhase());
|
||||
globalScene.phaseManager.unshiftNew("ShowTrainerPhase");
|
||||
// Hide the trainer and init next battle
|
||||
const trainer = globalScene.currentBattle.trainer;
|
||||
// Unassign previous trainer from battle so it isn't destroyed before animation completes
|
||||
|
@ -12,7 +12,7 @@ import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { AbilityAttr } from "#app/system/game-data";
|
||||
import { AbilityAttr } from "#enums/ability-attr";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
||||
@ -25,7 +25,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import i18next from "i18next";
|
||||
import { getStatKey } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
@ -24,8 +24,8 @@ import i18next from "#app/plugins/i18n";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -29,13 +29,11 @@ import {
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { SelfStatusMove } from "#app/data/moves/move";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
@ -103,8 +101,12 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -172,7 +174,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
// Check what type of move the egg move is to determine target
|
||||
const pokemonMove = new PokemonMove(eggMove);
|
||||
const move = pokemonMove.getMove();
|
||||
const target = move instanceof SelfStatusMove ? BattlerIndex.ENEMY : BattlerIndex.PLAYER;
|
||||
const target = move.is("SelfStatusMove") ? BattlerIndex.ENEMY : BattlerIndex.PLAYER;
|
||||
|
||||
encounter.startOfBattleEffects.push({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
|
@ -16,7 +16,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
@ -39,7 +39,7 @@ import { PlayerGender } from "#enums/player-gender";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
|
@ -2,7 +2,9 @@ import { globalScene } from "#app/global-scene";
|
||||
import { allAbilities } from "../data-lists";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
||||
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import { SpeciesFormChangeItemTrigger } from "../pokemon-forms/form-change-triggers";
|
||||
import { FormChangeItem } from "#enums/form-change-item";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { PokemonMove } from "../moves/pokemon-move";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
@ -20,11 +21,11 @@ import {
|
||||
StatusEffectRequirement,
|
||||
WaveRangeRequirement,
|
||||
} from "./mystery-encounter-requirements";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import type { GameModes } from "#app/game-mode";
|
||||
import type { GameModes } from "#enums/game-modes";
|
||||
import type { EncounterAnim } from "#enums/encounter-anims";
|
||||
import type { Challenges } from "#enums/challenges";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
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 { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
@ -51,7 +51,7 @@ function getTextWithDialogueTokens(keyOrString: string): string | null {
|
||||
*/
|
||||
export function queueEncounterMessage(contentKey: string): void {
|
||||
const text: string | null = getEncounterText(contentKey);
|
||||
globalScene.queueMessage(text ?? "", null, true);
|
||||
globalScene.phaseManager.queueMessage(text ?? "", null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type Battle from "#app/battle";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
||||
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
@ -8,9 +8,12 @@ import {
|
||||
WEIGHT_INCREMENT_ON_SPAWN_MISS,
|
||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
||||
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 { 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 {
|
||||
getPartyLuckValue,
|
||||
@ -20,12 +23,6 @@ import {
|
||||
modifierTypes,
|
||||
regenerateModifierPoolThresholds,
|
||||
} from "#app/modifier/modifier-type";
|
||||
import {
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterBattleStartCleanupPhase,
|
||||
MysteryEncounterPhase,
|
||||
MysteryEncounterRewardsPhase,
|
||||
} from "#app/phases/mystery-encounter-phases";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
||||
@ -36,7 +33,8 @@ import type { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import type { TrainerType } from "#enums/trainer-type";
|
||||
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 { Nature } from "#enums/nature";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
@ -50,14 +48,7 @@ import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import type { IEggOptions } from "#app/data/egg";
|
||||
import { Egg } from "#app/data/egg";
|
||||
import type { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
||||
import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import type { Variant } from "#app/sprites/variant";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
@ -428,7 +419,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
||||
console.log("Moveset:", moveset);
|
||||
});
|
||||
|
||||
globalScene.pushPhase(new MysteryEncounterBattlePhase(partyConfig.disableSwitch));
|
||||
globalScene.phaseManager.pushNew("MysteryEncounterBattlePhase", partyConfig.disableSwitch);
|
||||
|
||||
await Promise.all(loadEnemyAssets);
|
||||
battle.enemyParty.forEach((enemyPokemon_2, e_1) => {
|
||||
@ -480,7 +471,7 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes
|
||||
}
|
||||
if (showMessage) {
|
||||
if (changeValue < 0) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("mysteryEncounterMessages:paid_money", {
|
||||
amount: -changeValue,
|
||||
}),
|
||||
@ -488,7 +479,7 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("mysteryEncounterMessages:receive_money", {
|
||||
amount: changeValue,
|
||||
}),
|
||||
@ -767,9 +758,9 @@ export function setEncounterRewards(
|
||||
}
|
||||
|
||||
if (customShopRewards) {
|
||||
globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards));
|
||||
globalScene.phaseManager.unshiftNew("SelectModifierPhase", 0, undefined, customShopRewards);
|
||||
} else {
|
||||
globalScene.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase"));
|
||||
globalScene.phaseManager.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase"));
|
||||
}
|
||||
|
||||
if (eggRewards) {
|
||||
@ -807,7 +798,7 @@ export function setEncounterExp(participantId: number | number[], baseExpValue:
|
||||
const participantIds = Array.isArray(participantId) ? participantId : [participantId];
|
||||
|
||||
globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
|
||||
globalScene.unshiftPhase(new PartyExpPhase(baseExpValue, useWaveIndex, new Set(participantIds)));
|
||||
globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds));
|
||||
|
||||
return true;
|
||||
};
|
||||
@ -829,7 +820,7 @@ export class OptionSelectSettings {
|
||||
* @param optionSelectSettings
|
||||
*/
|
||||
export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSettings) {
|
||||
globalScene.pushPhase(new MysteryEncounterPhase(optionSelectSettings));
|
||||
globalScene.phaseManager.pushNew("MysteryEncounterPhase", optionSelectSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -843,8 +834,8 @@ export function leaveEncounterWithoutBattle(
|
||||
encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE,
|
||||
) {
|
||||
globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode;
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||
handleMysteryEncounterVictory(addHealPhase);
|
||||
}
|
||||
|
||||
@ -857,8 +848,8 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu
|
||||
const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle());
|
||||
|
||||
if (allowedPkm.length === 0) {
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.unshiftPhase(new GameOverPhase());
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -869,8 +860,8 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu
|
||||
return;
|
||||
}
|
||||
if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) {
|
||||
globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
|
||||
globalScene.pushPhase(new EggLapsePhase());
|
||||
globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase);
|
||||
globalScene.phaseManager.pushNew("EggLapsePhase");
|
||||
} else if (
|
||||
!globalScene
|
||||
.getEnemyParty()
|
||||
@ -878,15 +869,15 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu
|
||||
encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true),
|
||||
)
|
||||
) {
|
||||
globalScene.pushPhase(new BattleEndPhase(true));
|
||||
globalScene.phaseManager.pushNew("BattleEndPhase", true);
|
||||
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||
globalScene.pushPhase(new TrainerVictoryPhase());
|
||||
globalScene.phaseManager.pushNew("TrainerVictoryPhase");
|
||||
}
|
||||
if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) {
|
||||
globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
|
||||
globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase);
|
||||
if (!encounter.doContinueEncounter) {
|
||||
// Only lapse eggs once for multi-battle encounters
|
||||
globalScene.pushPhase(new EggLapsePhase());
|
||||
globalScene.phaseManager.pushNew("EggLapsePhase");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -900,8 +891,8 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo
|
||||
const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle());
|
||||
|
||||
if (allowedPkm.length === 0) {
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.unshiftPhase(new GameOverPhase());
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -912,14 +903,14 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo
|
||||
return;
|
||||
}
|
||||
if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
|
||||
globalScene.pushPhase(new BattleEndPhase(false));
|
||||
globalScene.phaseManager.pushNew("BattleEndPhase", false);
|
||||
}
|
||||
|
||||
globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
|
||||
globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase);
|
||||
|
||||
if (!encounter.doContinueEncounter) {
|
||||
// Only lapse eggs once for multi-battle encounters
|
||||
globalScene.pushPhase(new EggLapsePhase());
|
||||
globalScene.phaseManager.pushNew("EggLapsePhase");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1004,12 +995,19 @@ export function handleMysteryEncounterBattleStartEffects() {
|
||||
} else {
|
||||
source = globalScene.getEnemyField()[0];
|
||||
}
|
||||
// @ts-ignore: source cannot be undefined
|
||||
globalScene.pushPhase(new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp));
|
||||
globalScene.phaseManager.pushNew(
|
||||
"MovePhase",
|
||||
// @ts-expect-error: source is guaranteed to be defined
|
||||
source,
|
||||
effect.targets,
|
||||
effect.move,
|
||||
effect.followUp,
|
||||
effect.ignorePp,
|
||||
);
|
||||
});
|
||||
|
||||
// Pseudo turn end phase to reset flinch states, Endure, etc.
|
||||
globalScene.pushPhase(new MysteryEncounterBattleStartCleanupPhase());
|
||||
globalScene.phaseManager.pushNew("MysteryEncounterBattleStartCleanupPhase");
|
||||
|
||||
encounter.startOfBattleEffectsComplete = true;
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import type { PermanentStat } from "#enums/stat";
|
||||
import { VictoryPhase } from "#app/phases/victory-phase";
|
||||
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import type { AbilityId } from "#enums/ability-id";
|
||||
@ -675,7 +674,7 @@ export async function catchPokemon(
|
||||
if (!globalScene.getEnemyParty().some(p => p.id === pokemon.id)) {
|
||||
globalScene.getEnemyParty().push(pokemon);
|
||||
}
|
||||
globalScene.unshiftPhase(new VictoryPhase(pokemon.id, true));
|
||||
globalScene.phaseManager.unshiftNew("VictoryPhase", pokemon.id, true);
|
||||
globalScene.pokemonInfoContainer.hide();
|
||||
if (pokeball) {
|
||||
removePb(pokeball);
|
||||
|
@ -1,139 +1,30 @@
|
||||
import { PokemonFormChangeItemModifier } from "../modifier/modifier";
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { allMoves } from "./data-lists";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
import type { Constructor, nil } from "#app/utils/common";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-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 { Challenges } from "#app/enums/challenges";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
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, // TODO: Find a potential use for this
|
||||
}
|
||||
import { FormChangeItem } from "#enums/form-change-item";
|
||||
import {
|
||||
MeloettaFormChangePostMoveTrigger,
|
||||
SpeciesDefaultFormMatchTrigger,
|
||||
SpeciesFormChangeAbilityTrigger,
|
||||
SpeciesFormChangeActiveTrigger,
|
||||
SpeciesFormChangeCompoundTrigger,
|
||||
SpeciesFormChangeItemTrigger,
|
||||
SpeciesFormChangeLapseTeraTrigger,
|
||||
SpeciesFormChangeManualTrigger,
|
||||
SpeciesFormChangeMoveLearnedTrigger,
|
||||
SpeciesFormChangePreMoveTrigger,
|
||||
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||
SpeciesFormChangeTeraTrigger,
|
||||
type SpeciesFormChangeTrigger,
|
||||
SpeciesFormChangeWeatherTrigger,
|
||||
} from "./pokemon-forms/form-change-triggers";
|
||||
|
||||
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
|
||||
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.
|
||||
* 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 });
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import type { Localizable } from "#app/interfaces/locales";
|
||||
import type { Localizable } from "#app/@types/locales";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
@ -7,8 +7,16 @@ import i18next from "i18next";
|
||||
import type { AnySound } from "#app/battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { GameMode } from "#app/game-mode";
|
||||
import { DexAttr, type StarterMoveset } from "#app/system/game-data";
|
||||
import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } from "#app/utils/common";
|
||||
import type { StarterMoveset } from "#app/system/game-data";
|
||||
import { DexAttr } from "#enums/dex-attr";
|
||||
import {
|
||||
isNullOrUndefined,
|
||||
capitalizeString,
|
||||
randSeedInt,
|
||||
randSeedGauss,
|
||||
randSeedItem,
|
||||
randSeedFloat,
|
||||
} from "#app/utils/common";
|
||||
import { uncatchableSpecies } from "#app/data/balance/biomes";
|
||||
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
||||
import { GrowthRate } from "#app/data/exp";
|
||||
@ -750,7 +758,7 @@ export abstract class PokemonSpeciesForm {
|
||||
let paletteColors: Map<number, number> = new Map();
|
||||
|
||||
const originalRandom = Math.random;
|
||||
Math.random = Phaser.Math.RND.frac;
|
||||
Math.random = randSeedFloat;
|
||||
|
||||
globalScene.executeWithSeedOffset(
|
||||
() => {
|
||||
@ -773,6 +781,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
readonly mythical: boolean;
|
||||
readonly species: string;
|
||||
readonly growthRate: GrowthRate;
|
||||
/** The chance (as a decimal) for this Species to be male, or `null` for genderless species */
|
||||
readonly malePercent: number | null;
|
||||
readonly genderDiffs: boolean;
|
||||
readonly canChangeForm: boolean;
|
||||
@ -889,7 +898,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
return Gender.GENDERLESS;
|
||||
}
|
||||
|
||||
if (Phaser.Math.RND.realInRange(0, 1) <= this.malePercent) {
|
||||
if (randSeedFloat() <= this.malePercent) {
|
||||
return Gender.MALE;
|
||||
}
|
||||
return Gender.FEMALE;
|
||||
@ -1138,7 +1147,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
||||
}
|
||||
}
|
||||
|
||||
if (noEvolutionChance === 1 || Phaser.Math.RND.realInRange(0, 1) < noEvolutionChance) {
|
||||
if (noEvolutionChance === 1 || randSeedFloat() <= noEvolutionChance) {
|
||||
return this.speciesId;
|
||||
}
|
||||
|
||||
@ -2022,9 +2031,9 @@ export function initSpecies() {
|
||||
new PokemonForm("Normal", "", PokemonType.GROUND, null, 3.5, 950, AbilityId.DROUGHT, AbilityId.NONE, AbilityId.NONE, 670, 100, 150, 140, 100, 90, 90, 3, 0, 335, false, null, true),
|
||||
new PokemonForm("Primal", "primal", PokemonType.GROUND, PokemonType.FIRE, 5, 999.7, AbilityId.DESOLATE_LAND, AbilityId.NONE, AbilityId.NONE, 770, 100, 180, 160, 150, 90, 90, 3, 0, 335),
|
||||
),
|
||||
new PokemonSpecies(SpeciesId.RAYQUAZA, 3, false, true, false, "Sky High Pokémon", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 45, 0, 340, GrowthRate.SLOW, null, false, true,
|
||||
new PokemonForm("Normal", "", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 45, 0, 340, false, null, true),
|
||||
new PokemonForm("Mega", SpeciesFormKey.MEGA, PokemonType.DRAGON, PokemonType.FLYING, 10.8, 392, AbilityId.DELTA_STREAM, AbilityId.NONE, AbilityId.NONE, 780, 105, 180, 100, 180, 100, 115, 45, 0, 340),
|
||||
new PokemonSpecies(SpeciesId.RAYQUAZA, 3, false, true, false, "Sky High Pokémon", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 3, 0, 340, GrowthRate.SLOW, null, false, true,
|
||||
new PokemonForm("Normal", "", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 3, 0, 340, false, null, true),
|
||||
new PokemonForm("Mega", SpeciesFormKey.MEGA, PokemonType.DRAGON, PokemonType.FLYING, 10.8, 392, AbilityId.DELTA_STREAM, AbilityId.NONE, AbilityId.NONE, 780, 105, 180, 100, 180, 100, 115, 3, 0, 340),
|
||||
),
|
||||
new PokemonSpecies(SpeciesId.JIRACHI, 3, false, false, true, "Wish Pokémon", PokemonType.STEEL, PokemonType.PSYCHIC, 0.3, 1.1, AbilityId.SERENE_GRACE, AbilityId.NONE, AbilityId.NONE, 600, 100, 100, 100, 100, 100, 100, 3, 100, 300, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.DEOXYS, 3, false, false, true, "DNA Pokémon", PokemonType.PSYCHIC, null, 1.7, 60.8, AbilityId.PRESSURE, AbilityId.NONE, AbilityId.NONE, 600, 50, 150, 50, 150, 50, 150, 3, 0, 300, GrowthRate.SLOW, null, false, true,
|
||||
@ -2270,10 +2279,10 @@ export function initSpecies() {
|
||||
new PokemonSpecies(SpeciesId.WHIMSICOTT, 5, false, false, false, "Windveiled Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 0.7, 6.6, AbilityId.PRANKSTER, AbilityId.INFILTRATOR, AbilityId.CHLOROPHYLL, 480, 60, 67, 85, 77, 75, 116, 75, 50, 168, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.PETILIL, 5, false, false, false, "Bulb Pokémon", PokemonType.GRASS, null, 0.5, 6.6, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 280, 45, 35, 50, 70, 50, 30, 190, 50, 56, GrowthRate.MEDIUM_FAST, 0, false),
|
||||
new PokemonSpecies(SpeciesId.LILLIGANT, 5, false, false, false, "Flowering Pokémon", PokemonType.GRASS, null, 1.1, 16.3, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 480, 70, 60, 75, 110, 75, 90, 75, 50, 168, GrowthRate.MEDIUM_FAST, 0, false),
|
||||
new PokemonSpecies(SpeciesId.BASCULIN, 5, false, false, false, "Hostile Pokémon", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||
new PokemonForm("Red-Striped Form", "red-striped", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true),
|
||||
new PokemonForm("Blue-Striped Form", "blue-striped", PokemonType.WATER, null, 1, 18, AbilityId.ROCK_HEAD, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true),
|
||||
new PokemonForm("White-Striped Form", "white-striped", PokemonType.WATER, null, 1, 18, AbilityId.RATTLED, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true),
|
||||
new PokemonSpecies(SpeciesId.BASCULIN, 5, false, false, false, "Hostile Pokémon", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||
new PokemonForm("Red-Striped Form", "red-striped", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true),
|
||||
new PokemonForm("Blue-Striped Form", "blue-striped", PokemonType.WATER, null, 1, 18, AbilityId.ROCK_HEAD, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true),
|
||||
new PokemonForm("White-Striped Form", "white-striped", PokemonType.WATER, null, 1, 18, AbilityId.RATTLED, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true),
|
||||
),
|
||||
new PokemonSpecies(SpeciesId.SANDILE, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 0.7, 15.2, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 292, 50, 72, 35, 35, 35, 65, 180, 50, 58, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||
new PokemonSpecies(SpeciesId.KROKOROK, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 1, 33.4, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 351, 60, 82, 45, 45, 45, 74, 90, 50, 123, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||
@ -2740,10 +2749,10 @@ export function initSpecies() {
|
||||
new PokemonSpecies(SpeciesId.TAPU_LELE, 7, true, false, false, "Land Spirit Pokémon", PokemonType.PSYCHIC, PokemonType.FAIRY, 1.2, 18.6, AbilityId.PSYCHIC_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 85, 75, 130, 115, 95, 3, 50, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.TAPU_BULU, 7, true, false, false, "Land Spirit Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 1.9, 45.5, AbilityId.GRASSY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 130, 115, 85, 95, 75, 3, 50, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.TAPU_FINI, 7, true, false, false, "Land Spirit Pokémon", PokemonType.WATER, PokemonType.FAIRY, 1.3, 21.2, AbilityId.MISTY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 75, 115, 95, 130, 85, 3, 50, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, AbilityId.UNAWARE, AbilityId.NONE, AbilityId.NONE, 200, 43, 29, 31, 29, 31, 37, 45, 0, 40, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, AbilityId.STURDY, AbilityId.NONE, AbilityId.NONE, 400, 43, 29, 131, 29, 131, 37, 45, 0, 140, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, AbilityId.FULL_METAL_BODY, AbilityId.NONE, AbilityId.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, AbilityId.SHADOW_SHIELD, AbilityId.NONE, AbilityId.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, AbilityId.UNAWARE, AbilityId.NONE, AbilityId.NONE, 200, 43, 29, 31, 29, 31, 37, 3, 0, 40, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, AbilityId.STURDY, AbilityId.NONE, AbilityId.NONE, 400, 43, 29, 131, 29, 131, 37, 3, 0, 140, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, AbilityId.FULL_METAL_BODY, AbilityId.NONE, AbilityId.NONE, 680, 137, 137, 107, 113, 89, 97, 3, 0, 340, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, AbilityId.SHADOW_SHIELD, AbilityId.NONE, AbilityId.NONE, 680, 137, 113, 89, 137, 107, 97, 3, 0, 340, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.NIHILEGO, 7, true, false, false, "Parasite Pokémon", PokemonType.ROCK, PokemonType.POISON, 1.2, 55.5, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 109, 53, 47, 127, 131, 103, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.BUZZWOLE, 7, true, false, false, "Swollen Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 2.4, 333.6, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 107, 139, 139, 53, 53, 79, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.PHEROMOSA, 7, true, false, false, "Lissome Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 1.8, 25, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 71, 137, 37, 137, 37, 151, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||
@ -2751,11 +2760,11 @@ export function initSpecies() {
|
||||
new PokemonSpecies(SpeciesId.CELESTEELA, 7, true, false, false, "Launch Pokémon", PokemonType.STEEL, PokemonType.FLYING, 9.2, 999.9, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 97, 101, 103, 107, 101, 61, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.KARTANA, 7, true, false, false, "Drawn Sword Pokémon", PokemonType.GRASS, PokemonType.STEEL, 0.3, 0.1, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 59, 181, 131, 59, 31, 109, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.GUZZLORD, 7, true, false, false, "Junkivore Pokémon", PokemonType.DARK, PokemonType.DRAGON, 5.5, 888, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 223, 101, 53, 97, 53, 43, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(SpeciesId.NECROZMA, 7, false, true, false, "Prism Pokémon", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 255, 0, 300, GrowthRate.SLOW, null, false, false,
|
||||
new PokemonForm("Normal", "", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 255, 0, 300, false, null, true),
|
||||
new PokemonForm("Dusk Mane", "dusk-mane", PokemonType.PSYCHIC, PokemonType.STEEL, 3.8, 460, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 157, 127, 113, 109, 77, 255, 0, 340),
|
||||
new PokemonForm("Dawn Wings", "dawn-wings", PokemonType.PSYCHIC, PokemonType.GHOST, 4.2, 350, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 113, 109, 157, 127, 77, 255, 0, 340),
|
||||
new PokemonForm("Ultra", "ultra", PokemonType.PSYCHIC, PokemonType.DRAGON, 7.5, 230, AbilityId.NEUROFORCE, AbilityId.NONE, AbilityId.NONE, 754, 97, 167, 97, 167, 97, 129, 255, 0, 377),
|
||||
new PokemonSpecies(SpeciesId.NECROZMA, 7, false, true, false, "Prism Pokémon", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 3, 0, 300, GrowthRate.SLOW, null, false, false,
|
||||
new PokemonForm("Normal", "", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 3, 0, 300, false, null, true),
|
||||
new PokemonForm("Dusk Mane", "dusk-mane", PokemonType.PSYCHIC, PokemonType.STEEL, 3.8, 460, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 157, 127, 113, 109, 77, 3, 0, 340),
|
||||
new PokemonForm("Dawn Wings", "dawn-wings", PokemonType.PSYCHIC, PokemonType.GHOST, 4.2, 350, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 113, 109, 157, 127, 77, 3, 0, 340),
|
||||
new PokemonForm("Ultra", "ultra", PokemonType.PSYCHIC, PokemonType.DRAGON, 7.5, 230, AbilityId.NEUROFORCE, AbilityId.NONE, AbilityId.NONE, 754, 97, 167, 97, 167, 97, 129, 3, 0, 377),
|
||||
),
|
||||
new PokemonSpecies(SpeciesId.MAGEARNA, 7, false, false, true, "Artificial Pokémon", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, GrowthRate.SLOW, null, false, false,
|
||||
new PokemonForm("Normal", "", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, false, null, true),
|
||||
@ -2964,15 +2973,15 @@ export function initSpecies() {
|
||||
new PokemonForm("Ice", "ice", PokemonType.PSYCHIC, PokemonType.ICE, 2.4, 809.1, AbilityId.AS_ONE_GLASTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 165, 150, 85, 130, 50, 3, 100, 340),
|
||||
new PokemonForm("Shadow", "shadow", PokemonType.PSYCHIC, PokemonType.GHOST, 2.4, 53.6, AbilityId.AS_ONE_SPECTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 85, 80, 165, 100, 150, 3, 100, 340),
|
||||
),
|
||||
new PokemonSpecies(SpeciesId.WYRDEER, 8, false, false, false, "Big Horn Pokémon", PokemonType.NORMAL, PokemonType.PSYCHIC, 1.8, 95.1, AbilityId.INTIMIDATE, AbilityId.FRISK, AbilityId.SAP_SIPPER, 525, 103, 105, 72, 105, 75, 65, 135, 50, 263, GrowthRate.SLOW, 50, false),
|
||||
new PokemonSpecies(SpeciesId.KLEAVOR, 8, false, false, false, "Axe Pokémon", PokemonType.BUG, PokemonType.ROCK, 1.8, 89, AbilityId.SWARM, AbilityId.SHEER_FORCE, AbilityId.SHARPNESS, 500, 70, 135, 95, 45, 70, 85, 115, 50, 175, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.URSALUNA, 8, false, false, false, "Peat Pokémon", PokemonType.GROUND, PokemonType.NORMAL, 2.4, 290, AbilityId.GUTS, AbilityId.BULLETPROOF, AbilityId.UNNERVE, 550, 130, 140, 105, 45, 80, 50, 75, 50, 275, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.BASCULEGION, 8, false, false, false, "Big Fish Pokémon", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 135, 50, 265, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||
new PokemonForm("Male", "male", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 135, 50, 265, false, "", true),
|
||||
new PokemonForm("Female", "female", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 92, 65, 100, 75, 78, 135, 50, 265, false, null, true),
|
||||
new PokemonSpecies(SpeciesId.WYRDEER, 8, false, false, false, "Big Horn Pokémon", PokemonType.NORMAL, PokemonType.PSYCHIC, 1.8, 95.1, AbilityId.INTIMIDATE, AbilityId.FRISK, AbilityId.SAP_SIPPER, 525, 103, 105, 72, 105, 75, 65, 45, 50, 263, GrowthRate.SLOW, 50, false),
|
||||
new PokemonSpecies(SpeciesId.KLEAVOR, 8, false, false, false, "Axe Pokémon", PokemonType.BUG, PokemonType.ROCK, 1.8, 89, AbilityId.SWARM, AbilityId.SHEER_FORCE, AbilityId.SHARPNESS, 500, 70, 135, 95, 45, 70, 85, 15, 50, 175, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.URSALUNA, 8, false, false, false, "Peat Pokémon", PokemonType.GROUND, PokemonType.NORMAL, 2.4, 290, AbilityId.GUTS, AbilityId.BULLETPROOF, AbilityId.UNNERVE, 550, 130, 140, 105, 45, 80, 50, 20, 50, 275, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.BASCULEGION, 8, false, false, false, "Big Fish Pokémon", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 45, 50, 265, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||
new PokemonForm("Male", "male", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 45, 50, 265, false, "", true),
|
||||
new PokemonForm("Female", "female", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 92, 65, 100, 75, 78, 45, 50, 265, false, null, true),
|
||||
),
|
||||
new PokemonSpecies(SpeciesId.SNEASLER, 8, false, false, false, "Free Climb Pokémon", PokemonType.FIGHTING, PokemonType.POISON, 1.3, 43, AbilityId.PRESSURE, AbilityId.UNBURDEN, AbilityId.POISON_TOUCH, 510, 80, 130, 60, 40, 80, 120, 135, 50, 102, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||
new PokemonSpecies(SpeciesId.OVERQWIL, 8, false, false, false, "Pin Cluster Pokémon", PokemonType.DARK, PokemonType.POISON, 2.5, 60.5, AbilityId.POISON_POINT, AbilityId.SWIFT_SWIM, AbilityId.INTIMIDATE, 510, 85, 115, 95, 65, 65, 85, 135, 50, 179, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.SNEASLER, 8, false, false, false, "Free Climb Pokémon", PokemonType.FIGHTING, PokemonType.POISON, 1.3, 43, AbilityId.PRESSURE, AbilityId.UNBURDEN, AbilityId.POISON_TOUCH, 510, 80, 130, 60, 40, 80, 120, 20, 50, 102, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||
new PokemonSpecies(SpeciesId.OVERQWIL, 8, false, false, false, "Pin Cluster Pokémon", PokemonType.DARK, PokemonType.POISON, 2.5, 60.5, AbilityId.POISON_POINT, AbilityId.SWIFT_SWIM, AbilityId.INTIMIDATE, 510, 85, 115, 95, 65, 65, 85, 45, 50, 179, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(SpeciesId.ENAMORUS, 8, true, false, false, "Love-Hate Pokémon", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, GrowthRate.SLOW, 0, false, true,
|
||||
new PokemonForm("Incarnate Forme", "incarnate", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, false, null, true),
|
||||
new PokemonForm("Therian Forme", "therian", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 580, 74, 115, 110, 135, 100, 46, 3, 50, 116),
|
||||
|
@ -1,8 +1,7 @@
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import type Move from "./moves/move";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { ProtectAttr } from "./moves/move";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import i18next from "i18next";
|
||||
|
||||
export enum TerrainType {
|
||||
@ -55,7 +54,7 @@ export class Terrain {
|
||||
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
||||
switch (this.terrainType) {
|
||||
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
|
||||
return (
|
||||
move.getPriority(user) > 0 &&
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { startingWave } from "#app/starting-wave";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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";
|
||||
|
||||
export class TrainerPartyTemplate {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { TrainerTierPools } from "#app/data/trainers/typedefs";
|
||||
import type { TrainerTierPools } from "#app/@types/trainer-funcs";
|
||||
import { TrainerPoolTier } from "#enums/trainer-pool-tier";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { tmSpecies } from "#app/data/balance/tms";
|
||||
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 i18next from "i18next";
|
||||
import { Gender } from "#app/data/gender";
|
||||
@ -48,7 +48,7 @@ import type {
|
||||
TrainerTierPools,
|
||||
TrainerConfigs,
|
||||
PartyMemberFuncs,
|
||||
} from "./typedefs";
|
||||
} from "../../@types/trainer-funcs";
|
||||
|
||||
/** Minimum BST for Pokemon generated onto the Elite Four's teams */
|
||||
const ELITE_FOUR_MINIMUM_BST = 460;
|
||||
|
@ -4,7 +4,6 @@ import { getPokemonNameWithAffix } from "../messages";
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import type Move from "./moves/move";
|
||||
import { AttackMove } from "./moves/move";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { SuppressWeatherEffectAbAttr } from "./abilities/ability";
|
||||
import { TerrainType, getTerrainName } from "./terrain";
|
||||
@ -95,9 +94,9 @@ export class Weather {
|
||||
|
||||
switch (this.weatherType) {
|
||||
case WeatherType.HARSH_SUN:
|
||||
return move instanceof AttackMove && moveType === PokemonType.WATER;
|
||||
return move.is("AttackMove") && moveType === PokemonType.WATER;
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
return move instanceof AttackMove && moveType === PokemonType.FIRE;
|
||||
return move.is("AttackMove") && moveType === PokemonType.FIRE;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
17
src/debug.js
17
src/debug.js
@ -1,17 +0,0 @@
|
||||
export function getData() {
|
||||
const dataStr = localStorage.getItem("data");
|
||||
if (!dataStr) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(atob(dataStr), (k, v) =>
|
||||
k.endsWith("Attr") && !["natureAttr", "abilityAttr", "passiveAttr"].includes(k) ? BigInt(v) : v,
|
||||
);
|
||||
}
|
||||
|
||||
export function getSession() {
|
||||
const sessionStr = localStorage.getItem("sessionData");
|
||||
if (!sessionStr) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(atob(sessionStr));
|
||||
}
|
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 { TerrainType } from "#app/data/terrain";
|
||||
import type { WeatherType } from "#enums/weather-type";
|
||||
|
@ -12,12 +12,13 @@ import {
|
||||
getLegendaryWeatherContinuesMessage,
|
||||
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 Move from "#app/data/moves/move";
|
||||
import type { ArenaTag } from "#app/data/arena-tag";
|
||||
import { ArenaTagSide, ArenaTrapTag, getArenaTag } from "#app/data/arena-tag";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import { ArenaTrapTag, getArenaTag } from "#app/data/arena-tag";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { Terrain, TerrainType } from "#app/data/terrain";
|
||||
import {
|
||||
applyAbAttrs,
|
||||
@ -37,8 +38,10 @@ import { SpeciesId } from "#enums/species-id";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms";
|
||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
import {
|
||||
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||
SpeciesFormChangeWeatherTrigger,
|
||||
} from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { FieldEffectModifier } from "#app/modifier/modifier";
|
||||
|
||||
@ -297,8 +300,8 @@ export class Arena {
|
||||
*/
|
||||
trySetWeatherOverride(weather: WeatherType): boolean {
|
||||
this.weather = new Weather(weather, 0);
|
||||
globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
||||
globalScene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
|
||||
globalScene.phaseManager.unshiftNew("CommonAnimPhase", undefined, undefined, CommonAnim.SUNNY + (weather - 1));
|
||||
globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -328,10 +331,14 @@ export class Arena {
|
||||
this.weather?.isImmutable() &&
|
||||
![WeatherType.HARSH_SUN, WeatherType.HEAVY_RAIN, WeatherType.STRONG_WINDS, WeatherType.NONE].includes(weather)
|
||||
) {
|
||||
globalScene.unshiftPhase(
|
||||
new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (oldWeatherType - 1), true),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"CommonAnimPhase",
|
||||
undefined,
|
||||
undefined,
|
||||
CommonAnim.SUNNY + (oldWeatherType - 1),
|
||||
true,
|
||||
);
|
||||
globalScene.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!);
|
||||
globalScene.phaseManager.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -348,10 +355,16 @@ export class Arena {
|
||||
); // TODO: is this bang correct?
|
||||
|
||||
if (this.weather) {
|
||||
globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1), true));
|
||||
globalScene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"CommonAnimPhase",
|
||||
undefined,
|
||||
undefined,
|
||||
CommonAnim.SUNNY + (weather - 1),
|
||||
true,
|
||||
);
|
||||
globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
|
||||
} else {
|
||||
globalScene.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct?
|
||||
globalScene.phaseManager.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct?
|
||||
}
|
||||
|
||||
globalScene
|
||||
@ -431,11 +444,16 @@ export class Arena {
|
||||
|
||||
if (this.terrain) {
|
||||
if (!ignoreAnim) {
|
||||
globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1)));
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"CommonAnimPhase",
|
||||
undefined,
|
||||
undefined,
|
||||
CommonAnim.MISTY_TERRAIN + (terrain - 1),
|
||||
);
|
||||
}
|
||||
globalScene.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct?
|
||||
globalScene.phaseManager.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct?
|
||||
} else {
|
||||
globalScene.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct?
|
||||
globalScene.phaseManager.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct?
|
||||
}
|
||||
|
||||
globalScene
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { TextStyle, addTextObject } from "../ui/text";
|
||||
import type { DamageResult } 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 type { BattlerIndex } from "../battle";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
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 { PlayerBattleInfo } from "#app/ui/battle-info/player-battle-info";
|
||||
import type Move from "#app/data/moves/move";
|
||||
import {
|
||||
HighCritAttr,
|
||||
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 { getMoveTargets } from "#app/data/moves/move-utils";
|
||||
import { applyMoveAttrs } from "#app/data/moves/apply-attrs";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
@ -65,6 +37,7 @@ import {
|
||||
rgbToHsv,
|
||||
deltaRgb,
|
||||
isBetween,
|
||||
randSeedFloat,
|
||||
type nil,
|
||||
type Constructor,
|
||||
randSeedIntRange,
|
||||
@ -115,7 +88,6 @@ import {
|
||||
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms";
|
||||
import {
|
||||
BattlerTag,
|
||||
BattlerTagLapseType,
|
||||
EncoreTag,
|
||||
GroundedTag,
|
||||
HighestStatBoostTag,
|
||||
@ -134,8 +106,10 @@ import {
|
||||
loadBattlerTag,
|
||||
type GrudgeTag,
|
||||
} from "../data/battler-tags";
|
||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-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 { Ability } from "#app/data/abilities/ability-class";
|
||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
||||
@ -194,7 +168,7 @@ import {
|
||||
} from "#app/data/abilities/ability";
|
||||
import { allAbilities } from "#app/data/data-lists";
|
||||
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 type { PartyOption } from "#app/ui/party-ui-handler";
|
||||
import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler";
|
||||
@ -203,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 { achvs } from "#app/system/achv";
|
||||
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 { getNatureStatMultiplier } from "#app/data/nature";
|
||||
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||
@ -212,14 +186,15 @@ import {
|
||||
SpeciesFormChangeLapseTeraTrigger,
|
||||
SpeciesFormChangeMoveLearnedTrigger,
|
||||
SpeciesFormChangePostMoveTrigger,
|
||||
} from "#app/data/pokemon-forms";
|
||||
} from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { TerrainType } from "#app/data/terrain";
|
||||
import type { TrainerSlot } from "#enums/trainer-slot";
|
||||
import Overrides from "#app/overrides";
|
||||
import i18next from "i18next";
|
||||
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
||||
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 { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
@ -229,13 +204,6 @@ import { BiomeId } from "#enums/biome-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { FaintPhase } from "#app/phases/faint-phase";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { PokemonAnimType } from "#enums/pokemon-anim-type";
|
||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||
@ -255,22 +223,12 @@ import { doShinySparkleAnim } from "#app/field/anims";
|
||||
import { MoveFlags } from "#enums/MoveFlags";
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
|
||||
import { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
||||
|
||||
export enum LearnMoveSituation {
|
||||
MISC,
|
||||
LEVEL_UP,
|
||||
RELEARN,
|
||||
EVOLUTION,
|
||||
EVOLUTION_FUSED, // If fusionSpecies has Evolved
|
||||
EVOLUTION_FUSED_BASE, // If fusion's base species has Evolved
|
||||
}
|
||||
|
||||
export enum FieldPosition {
|
||||
CENTER,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
}
|
||||
import { FieldPosition } from "#enums/field-position";
|
||||
import { LearnMoveSituation } from "#enums/learn-move-situation";
|
||||
import { HitResult } from "#enums/hit-result";
|
||||
import { AiType } from "#enums/ai-type";
|
||||
import type { MoveResult } from "#enums/move-result";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
|
||||
/** Base typeclass for damage parameter methods, used for DRY */
|
||||
type damageParams = {
|
||||
@ -503,7 +461,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
if (level > 1) {
|
||||
const fused = new BooleanHolder(globalScene.gameMode.isSplicedOnly);
|
||||
if (!fused.value && !this.isPlayer() && !this.hasTrainer()) {
|
||||
if (!fused.value && this.isEnemy() && !this.hasTrainer()) {
|
||||
globalScene.applyModifier(EnemyFusionChanceModifier, false, fused);
|
||||
}
|
||||
|
||||
@ -788,7 +746,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract isPlayer(): boolean;
|
||||
abstract isPlayer(): this is PlayerPokemon;
|
||||
|
||||
abstract isEnemy(): this is EnemyPokemon;
|
||||
|
||||
abstract hasTrainer(): boolean;
|
||||
|
||||
@ -1296,7 +1256,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return false;
|
||||
}
|
||||
// During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus"
|
||||
const currentPhase = globalScene.getCurrentPhase();
|
||||
const currentPhase = globalScene.phaseManager.getCurrentPhase();
|
||||
return !(currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this);
|
||||
}
|
||||
|
||||
@ -1440,7 +1400,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*/
|
||||
getCritStage(source: Pokemon, move: Move): number {
|
||||
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(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||
applyAbAttrs(BonusCritAbAttr, source, null, false, critStage);
|
||||
@ -1466,7 +1426,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*/
|
||||
getMoveCategory(target: Pokemon, move: Move): MoveCategory {
|
||||
const moveCategory = new NumberHolder(move.category);
|
||||
applyMoveAttrs(VariableMoveCategoryAttr, this, target, move, moveCategory);
|
||||
applyMoveAttrs("VariableMoveCategoryAttr", this, target, move, moveCategory);
|
||||
return moveCategory.value;
|
||||
}
|
||||
|
||||
@ -2050,7 +2010,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (Overrides.ABILITY_OVERRIDE && this.isPlayer()) {
|
||||
return allAbilities[Overrides.ABILITY_OVERRIDE];
|
||||
}
|
||||
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
|
||||
if (Overrides.OPP_ABILITY_OVERRIDE && this.isEnemy()) {
|
||||
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
|
||||
}
|
||||
if (this.isFusion()) {
|
||||
@ -2080,7 +2040,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) {
|
||||
return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE];
|
||||
}
|
||||
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) {
|
||||
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && this.isEnemy()) {
|
||||
return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE];
|
||||
}
|
||||
if (!isNullOrUndefined(this.customPokemonData.passive) && this.customPokemonData.passive !== -1) {
|
||||
@ -2152,7 +2112,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
// returns override if valid for current case
|
||||
if (
|
||||
(Overrides.HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isPlayer()) ||
|
||||
(Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && !this.isPlayer())
|
||||
(Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isEnemy())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -2160,7 +2120,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
((Overrides.PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) &&
|
||||
this.isPlayer()) ||
|
||||
((Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE) &&
|
||||
!this.isPlayer())
|
||||
this.isEnemy())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -2169,7 +2129,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const { currentBattle, gameMode } = globalScene;
|
||||
const waveIndex = currentBattle?.waveIndex;
|
||||
if (
|
||||
this instanceof EnemyPokemon &&
|
||||
this.isEnemy() &&
|
||||
(currentBattle?.battleSpec === BattleSpec.FINAL_BOSS ||
|
||||
gameMode.isEndlessMinorBoss(waveIndex) ||
|
||||
gameMode.isEndlessMajorBoss(waveIndex))
|
||||
@ -2376,7 +2336,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
public getMoveType(move: Move, simulated = true): PokemonType {
|
||||
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);
|
||||
|
||||
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
|
||||
@ -2420,18 +2380,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return this.turnData?.moveEffectiveness;
|
||||
}
|
||||
|
||||
if (move.hasAttr(TypelessAttr)) {
|
||||
if (move.hasAttr("TypelessAttr")) {
|
||||
return 1;
|
||||
}
|
||||
const moveType = source.getMoveType(move);
|
||||
|
||||
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)
|
||||
: 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))) {
|
||||
typeMultiplier.value = 0;
|
||||
}
|
||||
@ -2458,7 +2418,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
const immuneTags = this.findTags(tag => tag instanceof TypeImmuneTag && tag.immuneType === moveType);
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@ -2514,7 +2474,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType));
|
||||
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||
if (move) {
|
||||
applyMoveAttrs(VariableMoveTypeChartAttr, null, this, move, multiplier, defType);
|
||||
applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defType);
|
||||
}
|
||||
if (source) {
|
||||
const ignoreImmunity = new BooleanHolder(false);
|
||||
@ -2550,7 +2510,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
) {
|
||||
multiplier /= 2;
|
||||
if (!simulated) {
|
||||
globalScene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
||||
}
|
||||
}
|
||||
return multiplier as TypeDamageMultiplier;
|
||||
@ -2994,9 +2954,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
let fusionOverride: PokemonSpecies | undefined = undefined;
|
||||
|
||||
if (forStarter && this instanceof PlayerPokemon && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) {
|
||||
if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) {
|
||||
fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE);
|
||||
} else if (this instanceof EnemyPokemon && Overrides.OPP_FUSION_SPECIES_OVERRIDE) {
|
||||
} else if (this.isEnemy() && Overrides.OPP_FUSION_SPECIES_OVERRIDE) {
|
||||
fusionOverride = getPokemonSpecies(Overrides.OPP_FUSION_SPECIES_OVERRIDE);
|
||||
}
|
||||
|
||||
@ -3150,24 +3110,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
// Bosses never get self ko moves or Pain Split
|
||||
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
|
||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
|
||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr("SacrificialAttrOnHit"));
|
||||
if (this.hasTrainer()) {
|
||||
// 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
|
||||
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
|
||||
movePool = movePool.map(m => [
|
||||
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
|
||||
movePool = movePool.map(m => [
|
||||
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),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -3234,7 +3196,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
!this.moveset.some(
|
||||
mo =>
|
||||
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 => {
|
||||
@ -3263,7 +3225,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
!this.moveset.some(
|
||||
mo =>
|
||||
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
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -3307,7 +3269,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.battleInfo.setX(this.battleInfo.x + (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198));
|
||||
this.battleInfo.setVisible(true);
|
||||
if (this.isPlayer()) {
|
||||
this.battleInfo.expMaskRect.x += 150;
|
||||
// TODO: How do you get this to not require a private property access?
|
||||
this["battleInfo"].expMaskRect.x += 150;
|
||||
}
|
||||
globalScene.tweens.add({
|
||||
targets: [this.battleInfo, this.battleInfo.expMaskRect],
|
||||
@ -3328,7 +3291,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
ease: "Cubic.easeIn",
|
||||
onComplete: () => {
|
||||
if (this.isPlayer()) {
|
||||
this.battleInfo.expMaskRect.x -= 150;
|
||||
// TODO: How do you get this to not require a private property access?
|
||||
this["battleInfo"].expMaskRect.x -= 150;
|
||||
}
|
||||
this.battleInfo.setVisible(false);
|
||||
this.battleInfo.setX(this.battleInfo.x - (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198));
|
||||
@ -3423,7 +3387,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @returns An array of Pokémon on the allied field.
|
||||
*/
|
||||
getAlliedField(): Pokemon[] {
|
||||
return this instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||
return this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3469,7 +3433,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage);
|
||||
}
|
||||
if (move) {
|
||||
applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, opponent, move, ignoreStatStage);
|
||||
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3494,7 +3458,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @returns The calculated accuracy multiplier.
|
||||
*/
|
||||
getAccuracyMultiplier(target: Pokemon, sourceMove: Move): number {
|
||||
const isOhko = sourceMove.hasAttr(OneHitKOAccuracyAttr);
|
||||
const isOhko = sourceMove.hasAttr("OneHitKOAccuracyAttr");
|
||||
if (isOhko) {
|
||||
return 1;
|
||||
}
|
||||
@ -3507,7 +3471,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, target, null, false, Stat.ACC, ignoreAccStatStage);
|
||||
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);
|
||||
|
||||
@ -3590,7 +3554,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
simulated,
|
||||
),
|
||||
);
|
||||
applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk);
|
||||
applyMoveAttrs("VariableAtkAttr", source, this, move, sourceAtk);
|
||||
|
||||
/**
|
||||
* This Pokemon's defensive stat for the given move's category.
|
||||
@ -3608,7 +3572,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
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
|
||||
@ -3635,7 +3599,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*/
|
||||
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 (move.hasAttr(TypelessAttr)) {
|
||||
if (move.hasAttr("TypelessAttr")) {
|
||||
return 1;
|
||||
}
|
||||
const sourceTypes = source.getTypes();
|
||||
@ -3647,7 +3611,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
stabMultiplier.value += 0.5;
|
||||
}
|
||||
|
||||
applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier);
|
||||
applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier);
|
||||
|
||||
if (!ignoreSourceAbility) {
|
||||
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
|
||||
@ -3696,7 +3660,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
|
||||
const variableCategory = new NumberHolder(move.category);
|
||||
applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory);
|
||||
applyMoveAttrs("VariableMoveCategoryAttr", source, this, move, variableCategory);
|
||||
const moveCategory = variableCategory.value as MoveCategory;
|
||||
|
||||
/** The move's type after type-changing effects are applied */
|
||||
@ -3721,7 +3685,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const arenaAttackTypeMultiplier = new NumberHolder(
|
||||
globalScene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()),
|
||||
);
|
||||
applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier);
|
||||
applyMoveAttrs("IgnoreWeatherTypeDebuffAttr", source, this, move, arenaAttackTypeMultiplier);
|
||||
|
||||
const isTypeImmune = typeMultiplier * arenaAttackTypeMultiplier.value === 0;
|
||||
|
||||
@ -3735,7 +3699,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
// If the attack deals fixed damage, return a result with that much damage
|
||||
const fixedDamage = new NumberHolder(0);
|
||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
||||
applyMoveAttrs("FixedDamageAttr", source, this, move, fixedDamage);
|
||||
if (fixedDamage.value) {
|
||||
const multiLensMultiplier = new NumberHolder(1);
|
||||
globalScene.applyModifiers(
|
||||
@ -3757,7 +3721,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
|
||||
const isOneHitKo = new BooleanHolder(false);
|
||||
applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo);
|
||||
applyMoveAttrs("OneHitKOAttr", source, this, move, isOneHitKo);
|
||||
if (isOneHitKo.value) {
|
||||
return {
|
||||
cancelled: false,
|
||||
@ -3834,7 +3798,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
isPhysical &&
|
||||
source.status &&
|
||||
source.status.effect === StatusEffect.BURN &&
|
||||
!move.hasAttr(BypassBurnDamageReductionAttr)
|
||||
!move.hasAttr("BypassBurnDamageReductionAttr")
|
||||
) {
|
||||
const burnDamageReductionCancelled = new BooleanHolder(false);
|
||||
if (!ignoreSourceAbility) {
|
||||
@ -3868,7 +3832,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*/
|
||||
const hitsTagMultiplier = new NumberHolder(1);
|
||||
move
|
||||
.getAttrs(HitsTagAttr)
|
||||
.getAttrs("HitsTagAttr")
|
||||
.filter(hta => hta.doubleDamage)
|
||||
.forEach(hta => {
|
||||
if (this.getTag(hta.tagType)) {
|
||||
@ -3925,7 +3889,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.
|
||||
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
||||
applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage);
|
||||
|
||||
if (this.isFullHp() && !ignoreAbility) {
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
|
||||
@ -3961,7 +3925,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
getCriticalHitResult(source: Pokemon, move: Move, simulated = true): boolean {
|
||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
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;
|
||||
}
|
||||
const isCritical = new BooleanHolder(false);
|
||||
@ -3969,7 +3933,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (source.getTag(BattlerTagType.ALWAYS_CRIT)) {
|
||||
isCritical.value = true;
|
||||
}
|
||||
applyMoveAttrs(CritOnlyAttr, source, this, move, isCritical);
|
||||
applyMoveAttrs("CritOnlyAttr", source, this, move, isCritical);
|
||||
applyAbAttrs(ConditionalCritAbAttr, source, null, simulated, isCritical, this, move);
|
||||
if (!isCritical.value) {
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||
@ -4022,8 +3986,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*
|
||||
* Once the MoveEffectPhase is over (and calls it's .end() function, shiftPhase() will reset the PhaseQueueSplice via clearPhaseQueueSplice() )
|
||||
*/
|
||||
globalScene.setPhaseQueueSplice();
|
||||
globalScene.unshiftPhase(new FaintPhase(this.getBattlerIndex(), preventEndure));
|
||||
globalScene.phaseManager.setPhaseQueueSplice();
|
||||
globalScene.phaseManager.unshiftNew("FaintPhase", this.getBattlerIndex(), preventEndure);
|
||||
this.destroySubstitute();
|
||||
this.lapseTag(BattlerTagType.COMMANDED);
|
||||
}
|
||||
@ -4059,8 +4023,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
} = {},
|
||||
): number {
|
||||
const isIndirectDamage = [HitResult.INDIRECT, HitResult.INDIRECT_KO].includes(result);
|
||||
const damagePhase = new DamageAnimPhase(this.getBattlerIndex(), damage, result as DamageResult, isCritical);
|
||||
globalScene.unshiftPhase(damagePhase);
|
||||
const damagePhase = globalScene.phaseManager.create(
|
||||
"DamageAnimPhase",
|
||||
this.getBattlerIndex(),
|
||||
damage,
|
||||
result as DamageResult,
|
||||
isCritical,
|
||||
);
|
||||
globalScene.phaseManager.unshiftPhase(damagePhase);
|
||||
if (this.switchOutStatus && source) {
|
||||
damage = 0;
|
||||
}
|
||||
@ -4275,7 +4245,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
// Copy all stat stages
|
||||
for (const s of BATTLE_STATS) {
|
||||
const sourceStage = source.getStatStage(s);
|
||||
if (this instanceof PlayerPokemon && sourceStage === 6) {
|
||||
if (this.isPlayer() && sourceStage === 6) {
|
||||
globalScene.validateAchv(achvs.TRANSFER_MAX_STAT_STAGE);
|
||||
}
|
||||
this.setStatStage(s, sourceStage);
|
||||
@ -4626,7 +4596,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
: i18next.t("abilityTriggers:moveImmunity", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(this),
|
||||
});
|
||||
globalScene.queueMessage(message);
|
||||
globalScene.phaseManager.queueMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4746,7 +4716,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
if (sourcePokemon && sourcePokemon !== this && this.isSafeguarded(sourcePokemon)) {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) }));
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) }),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -4775,7 +4747,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* cancel the attack's subsequent hits.
|
||||
*/
|
||||
if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) {
|
||||
const currentPhase = globalScene.getCurrentPhase();
|
||||
const currentPhase = globalScene.phaseManager.getCurrentPhase();
|
||||
if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
|
||||
this.turnData.hitCount = 1;
|
||||
this.turnData.hitsLeft = 1;
|
||||
@ -4786,8 +4758,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (overrideStatus) {
|
||||
this.resetStatus(false);
|
||||
}
|
||||
globalScene.unshiftPhase(
|
||||
new ObtainStatusEffectPhase(this.getBattlerIndex(), effect, turnsRemaining, sourceText, sourcePokemon),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"ObtainStatusEffectPhase",
|
||||
this.getBattlerIndex(),
|
||||
effect,
|
||||
turnsRemaining,
|
||||
sourceText,
|
||||
sourcePokemon,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@ -4836,7 +4813,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
if (asPhase) {
|
||||
globalScene.unshiftPhase(new ResetStatusPhase(this, confusion, reloadAssets));
|
||||
globalScene.phaseManager.unshiftNew("ResetStatusPhase", this, confusion, reloadAssets);
|
||||
} else {
|
||||
this.clearStatus(confusion, reloadAssets);
|
||||
}
|
||||
@ -5234,7 +5211,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
let fusionPaletteColors: Map<number, number>;
|
||||
|
||||
const originalRandom = Math.random;
|
||||
Math.random = () => Phaser.Math.RND.realInRange(0, 1);
|
||||
Math.random = () => randSeedFloat();
|
||||
|
||||
globalScene.executeWithSeedOffset(
|
||||
() => {
|
||||
@ -5483,7 +5460,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
heldItem.stackCount--;
|
||||
if (heldItem.stackCount <= 0) {
|
||||
globalScene.removeModifier(heldItem, !this.isPlayer());
|
||||
globalScene.removeModifier(heldItem, this.isEnemy());
|
||||
}
|
||||
if (forBattle) {
|
||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, this, false);
|
||||
@ -5556,15 +5533,19 @@ export class PlayerPokemon extends Pokemon {
|
||||
this.battleInfo.initInfo(this);
|
||||
}
|
||||
|
||||
isPlayer(): boolean {
|
||||
override isPlayer(): this is PlayerPokemon {
|
||||
return true;
|
||||
}
|
||||
|
||||
hasTrainer(): boolean {
|
||||
override isEnemy(): this is EnemyPokemon {
|
||||
return false;
|
||||
}
|
||||
|
||||
override hasTrainer(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
isBoss(): boolean {
|
||||
override isBoss(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5639,9 +5620,13 @@ export class PlayerPokemon extends Pokemon {
|
||||
this.getFieldIndex(),
|
||||
(slotIndex: number, _option: PartyOption) => {
|
||||
if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) {
|
||||
globalScene.prependToPhase(
|
||||
new SwitchSummonPhase(switchType, this.getFieldIndex(), slotIndex, false),
|
||||
MoveEndPhase,
|
||||
globalScene.phaseManager.prependNewToPhase(
|
||||
"MoveEndPhase",
|
||||
"SwitchSummonPhase",
|
||||
switchType,
|
||||
this.getFieldIndex(),
|
||||
slotIndex,
|
||||
false,
|
||||
);
|
||||
}
|
||||
globalScene.ui.setMode(UiMode.MESSAGE).then(resolve);
|
||||
@ -6004,7 +5989,9 @@ export class PlayerPokemon extends Pokemon {
|
||||
const newPartyMemberIndex = globalScene.getPlayerParty().indexOf(this);
|
||||
pokemon
|
||||
.getMoveset(true)
|
||||
.map((m: PokemonMove) => globalScene.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id)));
|
||||
.map((m: PokemonMove) =>
|
||||
globalScene.phaseManager.unshiftNew("LearnMovePhase", newPartyMemberIndex, m.getMove().id),
|
||||
);
|
||||
pokemon.destroy();
|
||||
this.updateFusionPalette();
|
||||
}
|
||||
@ -6267,7 +6254,7 @@ export class EnemyPokemon extends Pokemon {
|
||||
.targets.map(ind => fieldPokemon[ind])
|
||||
.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
|
||||
const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
const isCritical = move.hasAttr("CritOnlyAttr") || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
|
||||
return (
|
||||
move.category !== MoveCategory.STATUS &&
|
||||
@ -6336,7 +6323,7 @@ export class EnemyPokemon extends Pokemon {
|
||||
![MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id)
|
||||
) {
|
||||
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
|
||||
* the move's type effectiveness against the target and whether the move is a STAB move.
|
||||
@ -6457,7 +6444,7 @@ export class EnemyPokemon extends Pokemon {
|
||||
if (!sortedBenefitScores.length) {
|
||||
// Set target to BattlerIndex.ATTACKER when using a counter move
|
||||
// This is the same as when the player does so
|
||||
if (move.hasAttr(CounterDamageAttr)) {
|
||||
if (move.hasAttr("CounterDamageAttr")) {
|
||||
return [BattlerIndex.ATTACKER];
|
||||
}
|
||||
|
||||
@ -6509,15 +6496,19 @@ export class EnemyPokemon extends Pokemon {
|
||||
return [sortedBenefitScores[targetIndex][0]];
|
||||
}
|
||||
|
||||
isPlayer() {
|
||||
override isPlayer(): this is PlayerPokemon {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasTrainer(): boolean {
|
||||
override isEnemy(): this is EnemyPokemon {
|
||||
return true;
|
||||
}
|
||||
|
||||
override hasTrainer(): boolean {
|
||||
return !!this.trainerSlot;
|
||||
}
|
||||
|
||||
isBoss(): boolean {
|
||||
override isBoss(): boolean {
|
||||
return !!this.bossSegments;
|
||||
}
|
||||
|
||||
@ -6642,8 +6633,14 @@ export class EnemyPokemon extends Pokemon {
|
||||
stages++;
|
||||
}
|
||||
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(this.getBattlerIndex(), true, [boostedStat!], stages, true, true),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
this.getBattlerIndex(),
|
||||
true,
|
||||
[boostedStat!],
|
||||
stages,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
this.bossSegmentIndex--;
|
||||
}
|
||||
@ -6930,36 +6927,6 @@ export class PokemonTurnData {
|
||||
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 =
|
||||
| HitResult.EFFECTIVE
|
||||
| HitResult.SUPER_EFFECTIVE
|
||||
@ -6978,91 +6945,3 @@ export interface DamageCalculationResult {
|
||||
/** The damage dealt by the move */
|
||||
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 { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils/common";
|
||||
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 i18next from "i18next";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { signatureSpecies } from "#app/data/balance/signature-species";
|
||||
|
||||
export enum TrainerVariant {
|
||||
DEFAULT,
|
||||
FEMALE,
|
||||
DOUBLE,
|
||||
}
|
||||
import { TrainerVariant } from "#enums/trainer-variant";
|
||||
|
||||
export default class Trainer extends Phaser.GameObjects.Container {
|
||||
public config: TrainerConfig;
|
||||
|
@ -2,7 +2,8 @@ import i18next from "i18next";
|
||||
import type { FixedBattleConfigs } from "./battle";
|
||||
import { classicFixedBattles, FixedBattleConfig } from "./battle";
|
||||
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 { allSpecies } from "./data/pokemon-species";
|
||||
import type { Arena } from "./field/arena";
|
||||
@ -14,14 +15,7 @@ import { Challenges } from "./enums/challenges";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { getDailyStartingBiome } from "./data/daily-run";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES } from "./constants";
|
||||
|
||||
export enum GameModes {
|
||||
CLASSIC,
|
||||
ENDLESS,
|
||||
SPLICED_ENDLESS,
|
||||
DAILY,
|
||||
CHALLENGE,
|
||||
}
|
||||
import { GameModes } from "#enums/game-modes";
|
||||
|
||||
interface GameModeConfig {
|
||||
isClassic?: boolean;
|
||||
|
@ -1,4 +1,3 @@
|
||||
export const starterColors: StarterColors = {};
|
||||
interface StarterColors {
|
||||
export const starterColors: {
|
||||
[key: string]: [string, string];
|
||||
}
|
||||
} = {};
|
||||
|
@ -16,7 +16,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio
|
||||
|
||||
switch (globalScene.currentBattle.battleSpec) {
|
||||
case BattleSpec.DEFAULT:
|
||||
return !pokemon.isPlayer()
|
||||
return pokemon.isEnemy()
|
||||
? pokemon.hasTrainer()
|
||||
? i18next.t("battle:foePokemonWithAffix", {
|
||||
pokemonName: pokemon.getNameToRender(useIllusion),
|
||||
@ -26,7 +26,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio
|
||||
})
|
||||
: pokemon.getNameToRender(useIllusion);
|
||||
case BattleSpec.FINAL_BOSS:
|
||||
return !pokemon.isPlayer()
|
||||
return pokemon.isEnemy()
|
||||
? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) })
|
||||
: pokemon.getNameToRender(useIllusion);
|
||||
default:
|
||||
|
@ -2,19 +2,16 @@ import { globalScene } from "#app/global-scene";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms";
|
||||
import { getBerryEffectDescription, getBerryName } from "#app/data/berry";
|
||||
import { AttackMove } from "#app/data/moves/move";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { getNatureName, getNatureStatMultiplier } from "#app/data/nature";
|
||||
import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
||||
import {
|
||||
FormChangeItem,
|
||||
pokemonFormChanges,
|
||||
SpeciesFormChangeCondition,
|
||||
SpeciesFormChangeItemTrigger,
|
||||
} from "#app/data/pokemon-forms";
|
||||
import { pokemonFormChanges, SpeciesFormChangeCondition } from "#app/data/pokemon-forms";
|
||||
import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { FormChangeItem } from "#enums/form-change-item";
|
||||
import { getStatusEffectDescriptor } from "#app/data/status-effect";
|
||||
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 { getPokemonNameWithAffix } from "#app/messages";
|
||||
import {
|
||||
@ -1339,7 +1336,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
p
|
||||
.getMoveset()
|
||||
.map(m => m.getMove())
|
||||
.filter(m => m instanceof AttackMove)
|
||||
.filter(m => m.is("AttackMove"))
|
||||
.map(m => m.type),
|
||||
);
|
||||
if (!attackMoveTypes.length) {
|
||||
|
@ -3,19 +3,17 @@ import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
|
||||
import { getLevelTotalExp } from "#app/data/exp";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
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 Pokemon, { type PlayerPokemon } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import Overrides from "#app/overrides";
|
||||
import { EvolutionPhase } from "#app/phases/evolution-phase";
|
||||
import { LearnMovePhase, LearnMoveType } from "#app/phases/learn-move-phase";
|
||||
import { LevelUpPhase } from "#app/phases/level-up-phase";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { LearnMoveType } from "#enums/learn-move-type";
|
||||
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 { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils/common";
|
||||
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
@ -1548,7 +1546,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
|
||||
if (!surviveDamage.value && pokemon.randBattleSeedInt(10) < this.getStackCount()) {
|
||||
surviveDamage.value = true;
|
||||
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("modifier:surviveDamageApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
@ -1598,7 +1596,7 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier {
|
||||
const hasQuickClaw = this.type instanceof PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW";
|
||||
|
||||
if (isCommandFight && hasQuickClaw) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("modifier:bypassSpeedChanceApply", {
|
||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||
itemName: i18next.t("modifierType:ModifierType.QUICK_CLAW.name"),
|
||||
@ -1684,16 +1682,15 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
|
||||
*/
|
||||
override apply(pokemon: Pokemon): boolean {
|
||||
if (!pokemon.isFullHp()) {
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount,
|
||||
i18next.t("modifier:turnHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
}),
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount,
|
||||
i18next.t("modifier:turnHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@ -1782,16 +1779,15 @@ export class HitHealModifier extends PokemonHeldItemModifier {
|
||||
override apply(pokemon: Pokemon): boolean {
|
||||
if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) {
|
||||
// TODO: this shouldn't be undefined AFAIK
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount,
|
||||
i18next.t("modifier:hitHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
}),
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount,
|
||||
i18next.t("modifier:hitHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1950,18 +1946,17 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
|
||||
*/
|
||||
override apply(pokemon: Pokemon): boolean {
|
||||
// Restore the Pokemon to half HP
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.getMaxHp() / 2),
|
||||
i18next.t("modifier:pokemonInstantReviveApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
}),
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.getMaxHp() / 2),
|
||||
i18next.t("modifier:pokemonInstantReviveApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
}),
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
|
||||
// Remove the Pokemon's FAINT status
|
||||
@ -2012,7 +2007,7 @@ export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier {
|
||||
}
|
||||
|
||||
if (statRestored) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("modifier:resetNegativeStatStageApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
@ -2323,12 +2318,11 @@ export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier {
|
||||
|
||||
playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY);
|
||||
|
||||
globalScene.unshiftPhase(
|
||||
new LevelUpPhase(
|
||||
globalScene.getPlayerParty().indexOf(playerPokemon),
|
||||
playerPokemon.level - levelCount.value,
|
||||
playerPokemon.level,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LevelUpPhase",
|
||||
globalScene.getPlayerParty().indexOf(playerPokemon),
|
||||
playerPokemon.level - levelCount.value,
|
||||
playerPokemon.level,
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -2344,8 +2338,11 @@ export class TmModifier extends ConsumablePokemonModifier {
|
||||
* @returns always `true`
|
||||
*/
|
||||
override apply(playerPokemon: PlayerPokemon): boolean {
|
||||
globalScene.unshiftPhase(
|
||||
new LearnMovePhase(globalScene.getPlayerParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LearnMovePhase",
|
||||
globalScene.getPlayerParty().indexOf(playerPokemon),
|
||||
this.type.moveId,
|
||||
LearnMoveType.TM,
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -2367,13 +2364,12 @@ export class RememberMoveModifier extends ConsumablePokemonModifier {
|
||||
* @returns always `true`
|
||||
*/
|
||||
override apply(playerPokemon: PlayerPokemon, cost?: number): boolean {
|
||||
globalScene.unshiftPhase(
|
||||
new LearnMovePhase(
|
||||
globalScene.getPlayerParty().indexOf(playerPokemon),
|
||||
playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex],
|
||||
LearnMoveType.MEMORY,
|
||||
cost,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LearnMovePhase",
|
||||
globalScene.getPlayerParty().indexOf(playerPokemon),
|
||||
playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex],
|
||||
LearnMoveType.MEMORY,
|
||||
cost,
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -2410,7 +2406,7 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier {
|
||||
}
|
||||
|
||||
if (matchingEvolution) {
|
||||
globalScene.unshiftPhase(new EvolutionPhase(playerPokemon, matchingEvolution, playerPokemon.level - 1));
|
||||
globalScene.phaseManager.unshiftNew("EvolutionPhase", playerPokemon, matchingEvolution, playerPokemon.level - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3008,7 +3004,7 @@ export class MoneyInterestModifier extends PersistentModifier {
|
||||
moneyAmount: formattedMoneyAmount,
|
||||
typeName: this.type.name,
|
||||
});
|
||||
globalScene.queueMessage(message, undefined, true);
|
||||
globalScene.phaseManager.queueMessage(message, undefined, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3262,7 +3258,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
||||
}
|
||||
|
||||
for (const mt of transferredModifierTypes) {
|
||||
globalScene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
|
||||
globalScene.phaseManager.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
|
||||
}
|
||||
|
||||
return !!transferredModifierTypes.length;
|
||||
@ -3349,7 +3345,7 @@ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModif
|
||||
}
|
||||
|
||||
getTransferredItemCount(): number {
|
||||
return Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount() ? 1 : 0;
|
||||
return randSeedFloat() <= this.chance * this.getStackCount() ? 1 : 0;
|
||||
}
|
||||
|
||||
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierType): string {
|
||||
@ -3572,19 +3568,18 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier {
|
||||
*/
|
||||
override apply(enemyPokemon: Pokemon): boolean {
|
||||
if (!enemyPokemon.isFullHp()) {
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
enemyPokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1),
|
||||
i18next.t("modifier:enemyTurnHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon),
|
||||
}),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
enemyPokemon.getBattlerIndex(),
|
||||
Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1),
|
||||
i18next.t("modifier:enemyTurnHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon),
|
||||
}),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@ -3627,7 +3622,7 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi
|
||||
* @returns `true` if the {@linkcode Pokemon} was affected
|
||||
*/
|
||||
override apply(enemyPokemon: Pokemon): boolean {
|
||||
if (Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) {
|
||||
if (randSeedFloat() <= this.chance * this.getStackCount()) {
|
||||
return enemyPokemon.trySetStatus(this.effect, true);
|
||||
}
|
||||
|
||||
@ -3662,21 +3657,21 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies {@linkcode EnemyStatusEffectHealChanceModifier}
|
||||
* @param enemyPokemon The {@linkcode Pokemon} to heal
|
||||
* Applies {@linkcode EnemyStatusEffectHealChanceModifier} to randomly heal status.
|
||||
* @param enemyPokemon - The {@linkcode Pokemon} to heal
|
||||
* @returns `true` if the {@linkcode Pokemon} was healed
|
||||
*/
|
||||
override apply(enemyPokemon: Pokemon): boolean {
|
||||
if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) {
|
||||
globalScene.queueMessage(
|
||||
getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)),
|
||||
);
|
||||
enemyPokemon.resetStatus();
|
||||
enemyPokemon.updateInfo();
|
||||
return true;
|
||||
if (!enemyPokemon.status || randSeedFloat() > this.chance * this.getStackCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
globalScene.phaseManager.queueMessage(
|
||||
getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)),
|
||||
);
|
||||
enemyPokemon.resetStatus();
|
||||
enemyPokemon.updateInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): number {
|
||||
@ -3755,7 +3750,7 @@ export class EnemyFusionChanceModifier extends EnemyPersistentModifier {
|
||||
* @returns `true` if the {@linkcode EnemyPokemon} is a fusion
|
||||
*/
|
||||
override apply(isFusion: BooleanHolder): boolean {
|
||||
if (Phaser.Math.RND.realInRange(0, 1) >= this.chance * this.getStackCount()) {
|
||||
if (randSeedFloat() > this.chance * this.getStackCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { type PokeballCounts } from "#app/battle-scene";
|
||||
import { EvolutionItem } from "#app/data/balance/pokemon-evolutions";
|
||||
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 { Variant } from "#app/sprites/variant";
|
||||
import { Unlockables } from "#app/system/unlockables";
|
||||
|
581
src/phase-manager.ts
Normal file
581
src/phase-manager.ts
Normal file
@ -0,0 +1,581 @@
|
||||
import type { Phase } from "#app/phase";
|
||||
import type { default as Pokemon } from "#app/field/pokemon";
|
||||
import type { PhaseMap, PhaseString } from "./@types/phase-types";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase";
|
||||
import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase";
|
||||
import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { BerryPhase } from "#app/phases/berry-phase";
|
||||
import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase";
|
||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { EggHatchPhase } from "#app/phases/egg-hatch-phase";
|
||||
import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
||||
import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
|
||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||
import { EndCardPhase } from "#app/phases/end-card-phase";
|
||||
import { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
|
||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||
import { EvolutionPhase } from "#app/phases/evolution-phase";
|
||||
import { ExpPhase } from "#app/phases/exp-phase";
|
||||
import { FaintPhase } from "#app/phases/faint-phase";
|
||||
import { FormChangePhase } from "#app/phases/form-change-phase";
|
||||
import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase";
|
||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
|
||||
import { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { LevelCapPhase } from "#app/phases/level-cap-phase";
|
||||
import { LevelUpPhase } from "#app/phases/level-up-phase";
|
||||
import { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase";
|
||||
import { LoginPhase } from "#app/phases/login-phase";
|
||||
import { MessagePhase } from "#app/phases/message-phase";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import { MoneyRewardPhase } from "#app/phases/money-reward-phase";
|
||||
import { MoveAnimPhase } from "#app/phases/move-anim-phase";
|
||||
import { MoveChargePhase } from "#app/phases/move-charge-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { MoveHeaderPhase } from "#app/phases/move-header-phase";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
import {
|
||||
MysteryEncounterPhase,
|
||||
MysteryEncounterOptionSelectedPhase,
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterRewardsPhase,
|
||||
PostMysteryEncounterPhase,
|
||||
MysteryEncounterBattleStartCleanupPhase,
|
||||
} from "#app/phases/mystery-encounter-phases";
|
||||
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
||||
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
|
||||
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
||||
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
|
||||
import { PostGameOverPhase } from "#app/phases/post-game-over-phase";
|
||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||
import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
|
||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
||||
import { ReloadSessionPhase } from "#app/phases/reload-session-phase";
|
||||
import { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
||||
import { ReturnPhase } from "#app/phases/return-phase";
|
||||
import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase";
|
||||
import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase";
|
||||
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||
import { SelectChallengePhase } from "#app/phases/select-challenge-phase";
|
||||
import { SelectGenderPhase } from "#app/phases/select-gender-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import { SelectStarterPhase } from "#app/phases/select-starter-phase";
|
||||
import { SelectTargetPhase } from "#app/phases/select-target-phase";
|
||||
import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { SummonMissingPhase } from "#app/phases/summon-missing-phase";
|
||||
import { SummonPhase } from "#app/phases/summon-phase";
|
||||
import { SwitchBiomePhase } from "#app/phases/switch-biome-phase";
|
||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||
import { TeraPhase } from "#app/phases/tera-phase";
|
||||
import { TitlePhase } from "#app/phases/title-phase";
|
||||
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||
import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
||||
import { UnavailablePhase } from "#app/phases/unavailable-phase";
|
||||
import { UnlockPhase } from "#app/phases/unlock-phase";
|
||||
import { VictoryPhase } from "#app/phases/victory-phase";
|
||||
import { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
|
||||
|
||||
/**
|
||||
* Manager for phases used by battle scene.
|
||||
*
|
||||
* *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Object that holds all of the phase constructors.
|
||||
* This is used to create new phases dynamically using the `newPhase` method in the `PhaseManager`.
|
||||
*
|
||||
* @remarks
|
||||
* The keys of this object are the names of the phases, and the values are the constructors of the phases.
|
||||
* This allows for easy creation of new phases without needing to import each phase individually.
|
||||
*/
|
||||
const PHASES = Object.freeze({
|
||||
AddEnemyBuffModifierPhase,
|
||||
AttemptCapturePhase,
|
||||
AttemptRunPhase,
|
||||
BattleEndPhase,
|
||||
BerryPhase,
|
||||
CheckStatusEffectPhase,
|
||||
CheckSwitchPhase,
|
||||
CommandPhase,
|
||||
CommonAnimPhase,
|
||||
DamageAnimPhase,
|
||||
EggHatchPhase,
|
||||
EggLapsePhase,
|
||||
EggSummaryPhase,
|
||||
EncounterPhase,
|
||||
EndCardPhase,
|
||||
EndEvolutionPhase,
|
||||
EnemyCommandPhase,
|
||||
EvolutionPhase,
|
||||
ExpPhase,
|
||||
FaintPhase,
|
||||
FormChangePhase,
|
||||
GameOverPhase,
|
||||
GameOverModifierRewardPhase,
|
||||
HideAbilityPhase,
|
||||
HidePartyExpBarPhase,
|
||||
LearnMovePhase,
|
||||
LevelCapPhase,
|
||||
LevelUpPhase,
|
||||
LoadMoveAnimPhase,
|
||||
LoginPhase,
|
||||
MessagePhase,
|
||||
ModifierRewardPhase,
|
||||
MoneyRewardPhase,
|
||||
MoveAnimPhase,
|
||||
MoveChargePhase,
|
||||
MoveEffectPhase,
|
||||
MoveEndPhase,
|
||||
MoveHeaderPhase,
|
||||
MovePhase,
|
||||
MysteryEncounterPhase,
|
||||
MysteryEncounterOptionSelectedPhase,
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterBattleStartCleanupPhase,
|
||||
MysteryEncounterRewardsPhase,
|
||||
PostMysteryEncounterPhase,
|
||||
NewBattlePhase,
|
||||
NewBiomeEncounterPhase,
|
||||
NextEncounterPhase,
|
||||
ObtainStatusEffectPhase,
|
||||
PartyExpPhase,
|
||||
PartyHealPhase,
|
||||
PokemonAnimPhase,
|
||||
PokemonHealPhase,
|
||||
PokemonTransformPhase,
|
||||
PostGameOverPhase,
|
||||
PostSummonPhase,
|
||||
PostTurnStatusEffectPhase,
|
||||
QuietFormChangePhase,
|
||||
ReloadSessionPhase,
|
||||
ResetStatusPhase,
|
||||
ReturnPhase,
|
||||
RevivalBlessingPhase,
|
||||
RibbonModifierRewardPhase,
|
||||
ScanIvsPhase,
|
||||
SelectBiomePhase,
|
||||
SelectChallengePhase,
|
||||
SelectGenderPhase,
|
||||
SelectModifierPhase,
|
||||
SelectStarterPhase,
|
||||
SelectTargetPhase,
|
||||
ShinySparklePhase,
|
||||
ShowAbilityPhase,
|
||||
ShowPartyExpBarPhase,
|
||||
ShowTrainerPhase,
|
||||
StatStageChangePhase,
|
||||
SummonMissingPhase,
|
||||
SummonPhase,
|
||||
SwitchBiomePhase,
|
||||
SwitchPhase,
|
||||
SwitchSummonPhase,
|
||||
TeraPhase,
|
||||
TitlePhase,
|
||||
ToggleDoublePositionPhase,
|
||||
TrainerVictoryPhase,
|
||||
TurnEndPhase,
|
||||
TurnInitPhase,
|
||||
TurnStartPhase,
|
||||
UnavailablePhase,
|
||||
UnlockPhase,
|
||||
VictoryPhase,
|
||||
WeatherEffectPhase,
|
||||
});
|
||||
|
||||
// This type export cannot be moved to `@types`, as `Phases` is intentionally private to this file
|
||||
/** Maps Phase strings to their constructors */
|
||||
export type PhaseConstructorMap = typeof PHASES;
|
||||
|
||||
/**
|
||||
* PhaseManager is responsible for managing the phases in the battle scene
|
||||
*/
|
||||
export class PhaseManager {
|
||||
/** PhaseQueue: dequeue/remove the first element to get the next phase */
|
||||
public phaseQueue: Phase[] = [];
|
||||
public conditionalQueue: Array<[() => boolean, Phase]> = [];
|
||||
/** PhaseQueuePrepend: is a temp storage of what will be added to PhaseQueue */
|
||||
private phaseQueuePrepend: Phase[] = [];
|
||||
|
||||
/** overrides default of inserting phases to end of phaseQueuePrepend array. Useful for inserting Phases "out of order" */
|
||||
private phaseQueuePrependSpliceIndex = -1;
|
||||
private nextCommandPhaseQueue: Phase[] = [];
|
||||
|
||||
private currentPhase: Phase | null = null;
|
||||
private standbyPhase: Phase | null = null;
|
||||
|
||||
/* Phase Functions */
|
||||
getCurrentPhase(): Phase | null {
|
||||
return this.currentPhase;
|
||||
}
|
||||
|
||||
getStandbyPhase(): Phase | null {
|
||||
return this.standbyPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met.
|
||||
*
|
||||
* This method allows deferring the execution of a phase until certain conditions are met, which is useful for handling
|
||||
* situations like abilities and entry hazards that depend on specific game states.
|
||||
*
|
||||
* @param phase - The phase to be added to the conditional queue.
|
||||
* @param condition - A function that returns a boolean indicating whether the phase should be executed.
|
||||
*
|
||||
*/
|
||||
pushConditionalPhase(phase: Phase, condition: () => boolean): void {
|
||||
this.conditionalQueue.push([condition, phase]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false
|
||||
* @param phase {@linkcode Phase} the phase to add
|
||||
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
|
||||
*/
|
||||
pushPhase(phase: Phase, defer = false): void {
|
||||
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Phase(s) to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex
|
||||
* @param phases {@linkcode Phase} the phase(s) to add
|
||||
*/
|
||||
unshiftPhase(...phases: Phase[]): void {
|
||||
if (this.phaseQueuePrependSpliceIndex === -1) {
|
||||
this.phaseQueuePrepend.push(...phases);
|
||||
} else {
|
||||
this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, ...phases);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the phaseQueue
|
||||
*/
|
||||
clearPhaseQueue(): void {
|
||||
this.phaseQueue.splice(0, this.phaseQueue.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all phase-related stuff, including all phase queues, the current and standby phases, and a splice index
|
||||
*/
|
||||
clearAllPhases(): void {
|
||||
for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) {
|
||||
queue.splice(0, queue.length);
|
||||
}
|
||||
this.currentPhase = null;
|
||||
this.standbyPhase = null;
|
||||
this.clearPhaseQueueSplice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by function unshiftPhase(), sets index to start inserting at current length instead of the end of the array, useful if phaseQueuePrepend gets longer with Phases
|
||||
*/
|
||||
setPhaseQueueSplice(): void {
|
||||
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets phaseQueuePrependSpliceIndex to -1, implies that calls to unshiftPhase will insert at end of phaseQueuePrepend
|
||||
*/
|
||||
clearPhaseQueueSplice(): void {
|
||||
this.phaseQueuePrependSpliceIndex = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called by each Phase implementations "end()" by default
|
||||
* We dump everything from phaseQueuePrepend to the start of of phaseQueue
|
||||
* then removes first Phase and starts it
|
||||
*/
|
||||
shiftPhase(): void {
|
||||
if (this.standbyPhase) {
|
||||
this.currentPhase = this.standbyPhase;
|
||||
this.standbyPhase = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.phaseQueuePrependSpliceIndex > -1) {
|
||||
this.clearPhaseQueueSplice();
|
||||
}
|
||||
if (this.phaseQueuePrepend.length) {
|
||||
while (this.phaseQueuePrepend.length) {
|
||||
const poppedPhase = this.phaseQueuePrepend.pop();
|
||||
if (poppedPhase) {
|
||||
this.phaseQueue.unshift(poppedPhase);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this.phaseQueue.length) {
|
||||
this.populatePhaseQueue();
|
||||
// Clear the conditionalQueue if there are no phases left in the phaseQueue
|
||||
this.conditionalQueue = [];
|
||||
}
|
||||
|
||||
this.currentPhase = this.phaseQueue.shift() ?? null;
|
||||
|
||||
// Check if there are any conditional phases queued
|
||||
if (this.conditionalQueue?.length) {
|
||||
// Retrieve the first conditional phase from the queue
|
||||
const conditionalPhase = this.conditionalQueue.shift();
|
||||
// Evaluate the condition associated with the phase
|
||||
if (conditionalPhase?.[0]()) {
|
||||
// If the condition is met, add the phase to the phase queue
|
||||
this.pushPhase(conditionalPhase[1]);
|
||||
} else if (conditionalPhase) {
|
||||
// If the condition is not met, re-add the phase back to the front of the conditional queue
|
||||
this.conditionalQueue.unshift(conditionalPhase);
|
||||
} else {
|
||||
console.warn("condition phase is undefined/null!", conditionalPhase);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentPhase) {
|
||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
||||
this.currentPhase.start();
|
||||
}
|
||||
}
|
||||
|
||||
overridePhase(phase: Phase): boolean {
|
||||
if (this.standbyPhase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.standbyPhase = this.currentPhase;
|
||||
this.currentPhase = phase;
|
||||
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
|
||||
phase.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific {@linkcode Phase} in the phase queue.
|
||||
*
|
||||
* @param phaseFilter filter function to use to find the wanted phase
|
||||
* @returns the found phase or undefined if none found
|
||||
*/
|
||||
findPhase<P extends Phase = Phase>(phaseFilter: (phase: P) => boolean): P | undefined {
|
||||
return this.phaseQueue.find(phaseFilter) as P;
|
||||
}
|
||||
|
||||
tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean {
|
||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueue[phaseIndex] = phase;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
tryRemovePhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueue.splice(phaseIndex, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will search for a specific phase in {@linkcode phaseQueuePrepend} via filter, and remove the first result if a match is found.
|
||||
* @param phaseFilter filter function
|
||||
*/
|
||||
tryRemoveUnshiftedPhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
||||
const phaseIndex = this.phaseQueuePrepend.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueuePrepend.splice(phaseIndex, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase()
|
||||
* @param phase - The phase to be added
|
||||
* @param targetPhase - The phase to search for in phaseQueue
|
||||
* @returns boolean if a targetPhase was found and added
|
||||
*/
|
||||
prependToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean {
|
||||
if (!Array.isArray(phase)) {
|
||||
phase = [phase];
|
||||
}
|
||||
const target = PHASES[targetPhase];
|
||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target);
|
||||
|
||||
if (targetIndex !== -1) {
|
||||
this.phaseQueue.splice(targetIndex, 0, ...phase);
|
||||
return true;
|
||||
}
|
||||
this.unshiftPhase(...phase);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()}
|
||||
* @param phase - The phase(s) to be added
|
||||
* @param targetPhase - The phase to search for in phaseQueue
|
||||
* @returns `true` if a `targetPhase` was found to append to
|
||||
*/
|
||||
appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean {
|
||||
if (!Array.isArray(phase)) {
|
||||
phase = [phase];
|
||||
}
|
||||
const target = PHASES[targetPhase];
|
||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target);
|
||||
|
||||
if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
|
||||
this.phaseQueue.splice(targetIndex + 1, 0, ...phase);
|
||||
return true;
|
||||
}
|
||||
this.unshiftPhase(...phase);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
|
||||
* @param message - string for MessagePhase
|
||||
* @param callbackDelay - optional param for MessagePhase constructor
|
||||
* @param prompt - optional param for MessagePhase constructor
|
||||
* @param promptDelay - optional param for MessagePhase constructor
|
||||
* @param defer - Whether to allow the phase to be deferred
|
||||
*
|
||||
* @see {@linkcode MessagePhase} for more details on the parameters
|
||||
*/
|
||||
queueMessage(
|
||||
message: string,
|
||||
callbackDelay?: number | null,
|
||||
prompt?: boolean | null,
|
||||
promptDelay?: number | null,
|
||||
defer?: boolean | null,
|
||||
) {
|
||||
const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay);
|
||||
if (!defer) {
|
||||
// adds to the end of PhaseQueuePrepend
|
||||
this.unshiftPhase(phase);
|
||||
} else {
|
||||
//remember that pushPhase adds it to nextCommandPhaseQueue
|
||||
this.pushPhase(phase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues an ability bar flyout phase
|
||||
* @param pokemon The pokemon who has the ability
|
||||
* @param passive Whether the ability is a passive
|
||||
* @param show Whether to show or hide the bar
|
||||
*/
|
||||
public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void {
|
||||
this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase());
|
||||
this.clearPhaseQueueSplice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the ability bar if it is currently visible
|
||||
*/
|
||||
public hideAbilityBar(): void {
|
||||
if (globalScene.abilityBar.isVisible()) {
|
||||
this.unshiftPhase(new HideAbilityPhase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
|
||||
*/
|
||||
private populatePhaseQueue(): void {
|
||||
if (this.nextCommandPhaseQueue.length) {
|
||||
this.phaseQueue.push(...this.nextCommandPhaseQueue);
|
||||
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
|
||||
}
|
||||
this.phaseQueue.push(new TurnInitPhase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically create the named phase from the provided arguments
|
||||
*
|
||||
* @remarks
|
||||
* Used to avoid importing each phase individually, allowing for dynamic creation of phases.
|
||||
* @param phase - The name of the phase to create.
|
||||
* @param args - The arguments to pass to the phase constructor.
|
||||
* @returns The requested phase instance
|
||||
*/
|
||||
public create<T extends PhaseString>(phase: T, ...args: ConstructorParameters<PhaseConstructorMap[T]>): PhaseMap[T] {
|
||||
const PhaseClass = PHASES[phase];
|
||||
|
||||
if (!PhaseClass) {
|
||||
throw new Error(`Phase ${phase} does not exist in PhaseMap.`);
|
||||
}
|
||||
|
||||
// @ts-expect-error: Typescript does not support narrowing the type of operands in generic methods (see https://stackoverflow.com/a/72891234)
|
||||
return new PhaseClass(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new phase and immediately push it to the phase queue. Equivalent to calling {@linkcode create} followed by {@linkcode pushPhase}.
|
||||
* @param phase - The name of the phase to create
|
||||
* @param args - The arguments to pass to the phase constructor
|
||||
*/
|
||||
public pushNew<T extends PhaseString>(phase: T, ...args: ConstructorParameters<PhaseConstructorMap[T]>): void {
|
||||
this.pushPhase(this.create(phase, ...args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new phase and immediately unshift it to the phase queue. Equivalent to calling {@linkcode create} followed by {@linkcode unshiftPhase}.
|
||||
* @param phase - The name of the phase to create
|
||||
* @param args - The arguments to pass to the phase constructor
|
||||
*/
|
||||
public unshiftNew<T extends PhaseString>(phase: T, ...args: ConstructorParameters<PhaseConstructorMap[T]>): void {
|
||||
this.unshiftPhase(this.create(phase, ...args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new phase and immediately prepend it to an existing phase in the phase queue.
|
||||
* Equivalent to calling {@linkcode create} followed by {@linkcode prependToPhase}.
|
||||
* @param targetPhase - The phase to search for in phaseQueue
|
||||
* @param phase - The name of the phase to create
|
||||
* @param args - The arguments to pass to the phase constructor
|
||||
* @returns `true` if a `targetPhase` was found to prepend to
|
||||
*/
|
||||
public prependNewToPhase<T extends PhaseString>(
|
||||
targetPhase: PhaseString,
|
||||
phase: T,
|
||||
...args: ConstructorParameters<PhaseConstructorMap[T]>
|
||||
): boolean {
|
||||
return this.prependToPhase(this.create(phase, ...args), targetPhase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new phase and immediately append it to an existing phase the phase queue.
|
||||
* Equivalent to calling {@linkcode create} followed by {@linkcode appendToPhase}.
|
||||
* @param targetPhase - The phase to search for in phaseQueue
|
||||
* @param phase - The name of the phase to create
|
||||
* @param args - The arguments to pass to the phase constructor
|
||||
* @returns `true` if a `targetPhase` was found to append to
|
||||
*/
|
||||
public appendNewToPhase<T extends PhaseString>(
|
||||
targetPhase: PhaseString,
|
||||
phase: T,
|
||||
...args: ConstructorParameters<PhaseConstructorMap[T]>
|
||||
): boolean {
|
||||
return this.appendToPhase(this.create(phase, ...args), targetPhase);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ export abstract class Phase {
|
||||
start() {}
|
||||
|
||||
end() {
|
||||
globalScene.shiftPhase();
|
||||
globalScene.phaseManager.shiftPhase();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user