Merge remote-tracking branch 'upstream/beta' into intim-tests
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.5 KiB |
@ -1,11 +1,27 @@
|
|||||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
import type { AbAttr } from "#app/data/abilities/ability";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { BattleStat } from "#enums/stat";
|
import type { BattleStat } from "#enums/stat";
|
||||||
|
import type { AbAttrConstructorMap } from "#app/data/abilities/ability";
|
||||||
|
|
||||||
export type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => void;
|
// Intentionally re-export all types from the ability attributes module
|
||||||
export type AbAttrSuccessFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean;
|
export type * from "#app/data/abilities/ability";
|
||||||
|
|
||||||
|
export type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean, ...args: any[]) => void;
|
||||||
|
export type AbAttrSuccessFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean, ...args: any[]) => boolean;
|
||||||
export type AbAttrCondition = (pokemon: Pokemon) => boolean;
|
export type AbAttrCondition = (pokemon: Pokemon) => boolean;
|
||||||
export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
|
export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
|
||||||
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
|
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
|
||||||
export type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean;
|
export type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all ability attribute class names as strings
|
||||||
|
*/
|
||||||
|
export type AbAttrString = keyof AbAttrConstructorMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of ability attribute class names to an instance of the class.
|
||||||
|
*/
|
||||||
|
export type AbAttrMap = {
|
||||||
|
[K in keyof AbAttrConstructorMap]: InstanceType<AbAttrConstructorMap[K]>;
|
||||||
|
};
|
||||||
|
32
src/@types/modifier-types.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Re-exports of all the types defined in the modifier module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { ModifierConstructorMap } from "#app/modifier/modifier";
|
||||||
|
import type { ModifierType, WeightedModifierType } from "#app/modifier/modifier-type";
|
||||||
|
export type ModifierTypeFunc = () => ModifierType;
|
||||||
|
export type WeightedModifierTypeWeightFunc = (party: Pokemon[], rerollCount?: number) => number;
|
||||||
|
|
||||||
|
export type { ModifierConstructorMap } from "#app/modifier/modifier";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of modifier names to their respective instance types
|
||||||
|
*/
|
||||||
|
export type ModifierInstanceMap = {
|
||||||
|
[K in keyof ModifierConstructorMap]: InstanceType<ModifierConstructorMap[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all modifier constructors.
|
||||||
|
*/
|
||||||
|
export type ModifierClass = ModifierConstructorMap[keyof ModifierConstructorMap];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all modifier names as strings.
|
||||||
|
*/
|
||||||
|
export type ModifierString = keyof ModifierConstructorMap;
|
||||||
|
|
||||||
|
export type ModifierPool = {
|
||||||
|
[tier: string]: WeightedModifierType[];
|
||||||
|
}
|
@ -58,23 +58,15 @@ import {
|
|||||||
getEnemyModifierTypesForWave,
|
getEnemyModifierTypesForWave,
|
||||||
getLuckString,
|
getLuckString,
|
||||||
getLuckTextTint,
|
getLuckTextTint,
|
||||||
getModifierPoolForType,
|
|
||||||
getModifierType,
|
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
ModifierPoolType,
|
|
||||||
modifierTypes,
|
|
||||||
PokemonHeldItemModifierType,
|
PokemonHeldItemModifierType,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { getModifierType } from "./utils/modifier-utils";
|
||||||
|
import { modifierTypes } from "./data/data-lists";
|
||||||
|
import { getModifierPoolForType } from "./utils/modifier-utils";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import AbilityBar from "#app/ui/ability-bar";
|
import AbilityBar from "#app/ui/ability-bar";
|
||||||
import {
|
import { applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs } from "./data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
applyPostBattleInitAbAttrs,
|
|
||||||
applyPostItemLostAbAttrs,
|
|
||||||
BlockItemTheftAbAttr,
|
|
||||||
DoubleBattleChanceAbAttr,
|
|
||||||
PostBattleInitAbAttr,
|
|
||||||
PostItemLostAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { allAbilities } from "./data/data-lists";
|
import { allAbilities } from "./data/data-lists";
|
||||||
import type { FixedBattleConfig } from "#app/battle";
|
import type { FixedBattleConfig } from "#app/battle";
|
||||||
import Battle from "#app/battle";
|
import Battle from "#app/battle";
|
||||||
@ -145,14 +137,13 @@ import { LoadingScene } from "#app/loading-scene";
|
|||||||
import type { MovePhase } from "#app/phases/move-phase";
|
import type { MovePhase } from "#app/phases/move-phase";
|
||||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
import { allMysteryEncounters, mysteryEncountersByBiome } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import {
|
import {
|
||||||
allMysteryEncounters,
|
|
||||||
ANTI_VARIANCE_WEIGHT_MODIFIER,
|
ANTI_VARIANCE_WEIGHT_MODIFIER,
|
||||||
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
||||||
BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT,
|
BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT,
|
||||||
MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT,
|
MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT,
|
||||||
mysteryEncountersByBiome,
|
} from "./constants";
|
||||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
|
||||||
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
@ -1264,7 +1255,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
||||||
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
||||||
for (const p of playerField) {
|
for (const p of playerField) {
|
||||||
applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance);
|
applyAbAttrs("DoubleBattleChanceAbAttr", p, null, false, doubleChance);
|
||||||
}
|
}
|
||||||
return Math.max(doubleChance.value, 1);
|
return Math.max(doubleChance.value, 1);
|
||||||
}
|
}
|
||||||
@ -1469,7 +1460,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
for (const pokemon of this.getPlayerParty()) {
|
for (const pokemon of this.getPlayerParty()) {
|
||||||
pokemon.resetBattleAndWaveData();
|
pokemon.resetBattleAndWaveData();
|
||||||
pokemon.resetTera();
|
pokemon.resetTera();
|
||||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||||
if (
|
if (
|
||||||
pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|
pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|
||||||
(this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
|
(this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
|
||||||
@ -1644,6 +1635,9 @@ export default class BattleScene extends SceneBase {
|
|||||||
case SpeciesId.TATSUGIRI:
|
case SpeciesId.TATSUGIRI:
|
||||||
case SpeciesId.PALDEA_TAUROS:
|
case SpeciesId.PALDEA_TAUROS:
|
||||||
return randSeedInt(species.forms.length);
|
return randSeedInt(species.forms.length);
|
||||||
|
case SpeciesId.MAUSHOLD:
|
||||||
|
case SpeciesId.DUDUNSPARCE:
|
||||||
|
return !randSeedInt(4) ? 1 : 0;
|
||||||
case SpeciesId.PIKACHU:
|
case SpeciesId.PIKACHU:
|
||||||
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
|
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
|
||||||
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30
|
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30
|
||||||
@ -2745,7 +2739,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||||
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
|
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
@ -2785,13 +2779,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
if (target.isPlayer()) {
|
if (target.isPlayer()) {
|
||||||
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
||||||
if (source && itemLost) {
|
if (source && itemLost) {
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant);
|
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant);
|
||||||
if (source && itemLost) {
|
if (source && itemLost) {
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2814,7 +2808,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||||
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
|
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
import Trainer from "./field/trainer";
|
import Trainer from "./field/trainer";
|
||||||
import { TrainerVariant } from "#enums/trainer-variant";
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type { GameMode } from "./game-mode";
|
import type { GameMode } from "./game-mode";
|
||||||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
import { MoneyMultiplierModifier, type PokemonHeldItemModifier } from "./modifier/modifier";
|
||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
@ -30,7 +30,7 @@ import i18next from "#app/plugins/i18n";
|
|||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
@ -173,7 +173,7 @@ export default class Battle {
|
|||||||
this.postBattleLoot.push(
|
this.postBattleLoot.push(
|
||||||
...globalScene
|
...globalScene
|
||||||
.findModifiers(
|
.findModifiers(
|
||||||
m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable,
|
m => m.is("PokemonHeldItemModifier") && m.pokemonId === enemyPokemon.id && m.isTransferable,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.map(i => {
|
.map(i => {
|
||||||
|
@ -54,3 +54,43 @@ export const defaultStarterSpecies: SpeciesId[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
||||||
|
*/
|
||||||
|
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The divisor for determining ME spawns, defines the "maximum" weight required for a spawn
|
||||||
|
* If spawn_weight === MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, 100% chance to spawn a ME
|
||||||
|
*/
|
||||||
|
export const MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT = 256;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks.
|
||||||
|
* These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
||||||
|
*/
|
||||||
|
export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the target average for total ME spawns in a single Classic run.
|
||||||
|
* Used by anti-variance mechanic to check whether a run is above or below the target on a given wave.
|
||||||
|
*/
|
||||||
|
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will increase/decrease the chance of spawning a ME based on the current run's total MEs encountered vs AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
||||||
|
* Example:
|
||||||
|
* AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 17 (expects avg 1 ME every 10 floors)
|
||||||
|
* ANTI_VARIANCE_WEIGHT_MODIFIER = 15
|
||||||
|
*
|
||||||
|
* On wave 20, if 1 ME has been encountered, the difference from expected average is 0 MEs.
|
||||||
|
* So anti-variance adds 0/256 to the spawn weight check for ME spawn.
|
||||||
|
*
|
||||||
|
* On wave 20, if 0 MEs have been encountered, the difference from expected average is 1 ME.
|
||||||
|
* So anti-variance adds 15/256 to the spawn weight check for ME spawn.
|
||||||
|
*
|
||||||
|
* On wave 20, if 2 MEs have been encountered, the difference from expected average is -1 ME.
|
||||||
|
* So anti-variance adds -15/256 to the spawn weight check for ME spawn.
|
||||||
|
*/
|
||||||
|
export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15;
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import type { AbAttrCondition } from "#app/@types/ability-types";
|
|
||||||
import type Pokemon from "#app/field/pokemon";
|
|
||||||
import type { BooleanHolder } from "#app/utils/common";
|
|
||||||
|
|
||||||
export abstract class AbAttr {
|
|
||||||
public showAbility: boolean;
|
|
||||||
private extraCondition: AbAttrCondition;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param showAbility - Whether to show this ability as a flyout during battle; default `true`.
|
|
||||||
* Should be kept in parity with mainline where possible.
|
|
||||||
*/
|
|
||||||
constructor(showAbility = true) {
|
|
||||||
this.showAbility = showAbility;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies ability effects without checking conditions
|
|
||||||
* @param _pokemon - The pokemon to apply this ability to
|
|
||||||
* @param _passive - Whether or not the ability is a passive
|
|
||||||
* @param _simulated - Whether the call is simulated
|
|
||||||
* @param _args - Extra args passed to the function. Handled by child classes.
|
|
||||||
* @see {@linkcode canApply}
|
|
||||||
*/
|
|
||||||
apply(
|
|
||||||
_pokemon: Pokemon,
|
|
||||||
_passive: boolean,
|
|
||||||
_simulated: boolean,
|
|
||||||
_cancelled: BooleanHolder | null,
|
|
||||||
_args: any[],
|
|
||||||
): void {}
|
|
||||||
|
|
||||||
getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCondition(): AbAttrCondition | null {
|
|
||||||
return this.extraCondition || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCondition(condition: AbAttrCondition): AbAttr {
|
|
||||||
this.extraCondition = condition;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a boolean describing whether the ability can be applied under current conditions
|
|
||||||
* @param _pokemon - The pokemon to apply this ability to
|
|
||||||
* @param _passive - Whether or not the ability is a passive
|
|
||||||
* @param _simulated - Whether the call is simulated
|
|
||||||
* @param _args - Extra args passed to the function. Handled by child classes.
|
|
||||||
* @returns `true` if the ability can be applied, `false` otherwise
|
|
||||||
* @see {@linkcode apply}
|
|
||||||
*/
|
|
||||||
canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
import { AbilityId } from "#enums/ability-id";
|
|
||||||
import type { AbAttrCondition } from "#app/@types/ability-types";
|
|
||||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import type { Localizable } from "#app/@types/locales";
|
|
||||||
import type { Constructor } from "#app/utils/common";
|
|
||||||
|
|
||||||
export class Ability implements Localizable {
|
|
||||||
public id: AbilityId;
|
|
||||||
|
|
||||||
private nameAppend: string;
|
|
||||||
public name: string;
|
|
||||||
public description: string;
|
|
||||||
public generation: number;
|
|
||||||
public isBypassFaint: boolean;
|
|
||||||
public isIgnorable: boolean;
|
|
||||||
public isSuppressable = true;
|
|
||||||
public isCopiable = true;
|
|
||||||
public isReplaceable = true;
|
|
||||||
public attrs: AbAttr[];
|
|
||||||
public conditions: AbAttrCondition[];
|
|
||||||
|
|
||||||
constructor(id: AbilityId, generation: number) {
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
this.nameAppend = "";
|
|
||||||
this.generation = generation;
|
|
||||||
this.attrs = [];
|
|
||||||
this.conditions = [];
|
|
||||||
|
|
||||||
this.isSuppressable = true;
|
|
||||||
this.isCopiable = true;
|
|
||||||
this.isReplaceable = true;
|
|
||||||
|
|
||||||
this.localize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isSwappable(): boolean {
|
|
||||||
return this.isCopiable && this.isReplaceable;
|
|
||||||
}
|
|
||||||
localize(): void {
|
|
||||||
const i18nKey = AbilityId[this.id]
|
|
||||||
.split("_")
|
|
||||||
.filter(f => f)
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("") as string;
|
|
||||||
|
|
||||||
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : "";
|
|
||||||
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all ability attributes that match `attrType`
|
|
||||||
* @param attrType any attribute that extends {@linkcode AbAttr}
|
|
||||||
* @returns Array of attributes that match `attrType`, Empty Array if none match.
|
|
||||||
*/
|
|
||||||
getAttrs<T extends AbAttr>(attrType: Constructor<T>): T[] {
|
|
||||||
return this.attrs.filter((a): a is T => a instanceof attrType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an ability has an attribute that matches `attrType`
|
|
||||||
* @param attrType any attribute that extends {@linkcode AbAttr}
|
|
||||||
* @returns true if the ability has attribute `attrType`
|
|
||||||
*/
|
|
||||||
hasAttr<T extends AbAttr>(attrType: Constructor<T>): boolean {
|
|
||||||
return this.attrs.some(attr => attr instanceof attrType);
|
|
||||||
}
|
|
||||||
|
|
||||||
attr<T extends Constructor<AbAttr>>(AttrType: T, ...args: ConstructorParameters<T>): Ability {
|
|
||||||
const attr = new AttrType(...args);
|
|
||||||
this.attrs.push(attr);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
conditionalAttr<T extends Constructor<AbAttr>>(
|
|
||||||
condition: AbAttrCondition,
|
|
||||||
AttrType: T,
|
|
||||||
...args: ConstructorParameters<T>
|
|
||||||
): Ability {
|
|
||||||
const attr = new AttrType(...args);
|
|
||||||
attr.addCondition(condition);
|
|
||||||
this.attrs.push(attr);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bypassFaint(): Ability {
|
|
||||||
this.isBypassFaint = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ignorable(): Ability {
|
|
||||||
this.isIgnorable = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsuppressable(): Ability {
|
|
||||||
this.isSuppressable = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
uncopiable(): Ability {
|
|
||||||
this.isCopiable = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unreplaceable(): Ability {
|
|
||||||
this.isReplaceable = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
condition(condition: AbAttrCondition): Ability {
|
|
||||||
this.conditions.push(condition);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
partial(): this {
|
|
||||||
this.nameAppend += " (P)";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unimplemented(): this {
|
|
||||||
this.nameAppend += " (N)";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case.
|
|
||||||
* @returns the ability
|
|
||||||
*/
|
|
||||||
edgeCase(): this {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
832
src/data/abilities/apply-ab-attrs.ts
Normal file
@ -0,0 +1,832 @@
|
|||||||
|
import type { AbAttrApplyFunc, AbAttrMap, AbAttrString, AbAttrSuccessFunc } from "#app/@types/ability-types";
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { BooleanHolder, NumberHolder } from "#app/utils/common";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import type { HitResult } from "#enums/hit-result";
|
||||||
|
import type { BattleStat, Stat } from "#enums/stat";
|
||||||
|
import type { StatusEffect } from "#enums/status-effect";
|
||||||
|
import type { WeatherType } from "#enums/weather-type";
|
||||||
|
import type { BattlerTag } from "../battler-tags";
|
||||||
|
import type Move from "../moves/move";
|
||||||
|
import type { PokemonMove } from "../moves/pokemon-move";
|
||||||
|
import type { TerrainType } from "../terrain";
|
||||||
|
import type { Weather } from "../weather";
|
||||||
|
import type {
|
||||||
|
PostBattleInitAbAttr,
|
||||||
|
PreDefendAbAttr,
|
||||||
|
PostDefendAbAttr,
|
||||||
|
PostMoveUsedAbAttr,
|
||||||
|
StatMultiplierAbAttr,
|
||||||
|
AllyStatMultiplierAbAttr,
|
||||||
|
PostSetStatusAbAttr,
|
||||||
|
PostDamageAbAttr,
|
||||||
|
FieldMultiplyStatAbAttr,
|
||||||
|
PreAttackAbAttr,
|
||||||
|
ExecutedMoveAbAttr,
|
||||||
|
PostAttackAbAttr,
|
||||||
|
PostKnockOutAbAttr,
|
||||||
|
PostVictoryAbAttr,
|
||||||
|
PostSummonAbAttr,
|
||||||
|
PreSummonAbAttr,
|
||||||
|
PreSwitchOutAbAttr,
|
||||||
|
PreLeaveFieldAbAttr,
|
||||||
|
PreStatStageChangeAbAttr,
|
||||||
|
PostStatStageChangeAbAttr,
|
||||||
|
PreSetStatusAbAttr,
|
||||||
|
PreApplyBattlerTagAbAttr,
|
||||||
|
PreWeatherEffectAbAttr,
|
||||||
|
PreWeatherDamageAbAttr,
|
||||||
|
PostTurnAbAttr,
|
||||||
|
PostWeatherChangeAbAttr,
|
||||||
|
PostWeatherLapseAbAttr,
|
||||||
|
PostTerrainChangeAbAttr,
|
||||||
|
CheckTrappedAbAttr,
|
||||||
|
PostBattleAbAttr,
|
||||||
|
PostFaintAbAttr,
|
||||||
|
PostItemLostAbAttr,
|
||||||
|
} from "./ability";
|
||||||
|
|
||||||
|
function applySingleAbAttrs<T extends AbAttrString>(
|
||||||
|
pokemon: Pokemon,
|
||||||
|
passive: boolean,
|
||||||
|
attrType: T,
|
||||||
|
applyFunc: AbAttrApplyFunc<AbAttrMap[T]>,
|
||||||
|
successFunc: AbAttrSuccessFunc<AbAttrMap[T]>,
|
||||||
|
args: any[],
|
||||||
|
gainedMidTurn = false,
|
||||||
|
simulated = false,
|
||||||
|
messages: string[] = [],
|
||||||
|
) {
|
||||||
|
if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
|
||||||
|
if (
|
||||||
|
gainedMidTurn &&
|
||||||
|
ability.getAttrs(attrType).some(attr => {
|
||||||
|
attr.is("PostSummonAbAttr") && !attr.shouldActivateOnGain();
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr of ability.getAttrs(attrType)) {
|
||||||
|
const condition = attr.getCondition();
|
||||||
|
let abShown = false;
|
||||||
|
if ((condition && !condition(pokemon)) || !successFunc(attr, passive)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScene.phaseManager.setPhaseQueueSplice();
|
||||||
|
|
||||||
|
if (attr.showAbility && !simulated) {
|
||||||
|
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true);
|
||||||
|
abShown = true;
|
||||||
|
}
|
||||||
|
const message = attr.getTriggerMessage(pokemon, ability.name, args);
|
||||||
|
if (message) {
|
||||||
|
if (!simulated) {
|
||||||
|
globalScene.phaseManager.queueMessage(message);
|
||||||
|
}
|
||||||
|
messages.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFunc(attr, passive);
|
||||||
|
|
||||||
|
if (abShown) {
|
||||||
|
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!simulated) {
|
||||||
|
pokemon.waveData.abilitiesApplied.add(ability.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAbAttrsInternal<T extends AbAttrString>(
|
||||||
|
attrType: T,
|
||||||
|
pokemon: Pokemon | null,
|
||||||
|
applyFunc: AbAttrApplyFunc<AbAttrMap[T]>,
|
||||||
|
successFunc: AbAttrSuccessFunc<AbAttrMap[T]>,
|
||||||
|
args: any[],
|
||||||
|
simulated = false,
|
||||||
|
messages: string[] = [],
|
||||||
|
gainedMidTurn = false,
|
||||||
|
) {
|
||||||
|
for (const passive of [false, true]) {
|
||||||
|
if (pokemon) {
|
||||||
|
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages);
|
||||||
|
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyAbAttrs<T extends AbAttrString>(
|
||||||
|
attrType: T,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
cancelled: BooleanHolder | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal<T>(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
// @ts-expect-error: TODO: fix the error on `cancelled`
|
||||||
|
(attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args),
|
||||||
|
(attr, passive) => attr.canApply(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Improve the type signatures of the following methods / refactor the apply methods
|
||||||
|
|
||||||
|
export function applyPostBattleInitAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostBattleInitAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostBattleInitAbAttr).applyPostBattleInit(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostBattleInitAbAttr).canApplyPostBattleInit(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreDefendAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreDefendAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
attacker: Pokemon,
|
||||||
|
move: Move | null,
|
||||||
|
cancelled: BooleanHolder | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreDefendAbAttr).applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreDefendAbAttr).canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostDefendAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostDefendAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
attacker: Pokemon,
|
||||||
|
move: Move,
|
||||||
|
hitResult: HitResult | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostDefendAbAttr).applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostDefendAbAttr).canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostMoveUsedAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostMoveUsedAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
move: PokemonMove,
|
||||||
|
source: Pokemon,
|
||||||
|
targets: BattlerIndex[],
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, _passive) => (attr as PostMoveUsedAbAttr).applyPostMoveUsed(pokemon, move, source, targets, simulated, args),
|
||||||
|
(attr, _passive) =>
|
||||||
|
(attr as PostMoveUsedAbAttr).canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends StatMultiplierAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stat: BattleStat,
|
||||||
|
statValue: NumberHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as StatMultiplierAbAttr).applyStatStage(pokemon, passive, simulated, stat, statValue, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as StatMultiplierAbAttr).canApplyStatStage(pokemon, passive, simulated, stat, statValue, args),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies an ally's Stat multiplier attribute
|
||||||
|
* @param attrType - {@linkcode AllyStatMultiplierAbAttr} should always be AllyStatMultiplierAbAttr for the time being
|
||||||
|
* @param pokemon - The {@linkcode Pokemon} with the ability
|
||||||
|
* @param stat - The type of the checked {@linkcode Stat}
|
||||||
|
* @param statValue - {@linkcode NumberHolder} containing the value of the checked stat
|
||||||
|
* @param checkedPokemon - The {@linkcode Pokemon} with the checked stat
|
||||||
|
* @param ignoreAbility - Whether or not the ability should be ignored by the pokemon or its move.
|
||||||
|
* @param args - unused
|
||||||
|
*/
|
||||||
|
export function applyAllyStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends AllyStatMultiplierAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stat: BattleStat,
|
||||||
|
statValue: NumberHolder,
|
||||||
|
simulated = false,
|
||||||
|
checkedPokemon: Pokemon,
|
||||||
|
ignoreAbility: boolean,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as AllyStatMultiplierAbAttr).applyAllyStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
ignoreAbility,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as AllyStatMultiplierAbAttr).canApplyAllyStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
ignoreAbility,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostSetStatusAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostSetStatusAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
effect: StatusEffect,
|
||||||
|
sourcePokemon?: Pokemon | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostSetStatusAbAttr).applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostSetStatusAbAttr).canApplyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostDamageAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostDamageAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
damage: number,
|
||||||
|
_passive: boolean,
|
||||||
|
simulated = false,
|
||||||
|
args: any[],
|
||||||
|
source?: Pokemon,
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostDamageAbAttr).applyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||||
|
(attr, passive) => (attr as PostDamageAbAttr).canApplyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Applies a field Stat multiplier attribute
|
||||||
|
* @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being
|
||||||
|
* @param pokemon {@linkcode Pokemon} the Pokemon applying this ability
|
||||||
|
* @param stat {@linkcode Stat} the type of the checked stat
|
||||||
|
* @param statValue {@linkcode NumberHolder} the value of the checked stat
|
||||||
|
* @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat
|
||||||
|
* @param hasApplied {@linkcode BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat
|
||||||
|
* @param args unused
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function applyFieldStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends FieldMultiplyStatAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stat: Stat,
|
||||||
|
statValue: NumberHolder,
|
||||||
|
checkedPokemon: Pokemon,
|
||||||
|
hasApplied: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as FieldMultiplyStatAbAttr).applyFieldStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
hasApplied,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as FieldMultiplyStatAbAttr).canApplyFieldStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
hasApplied,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreAttackAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreAttackAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
defender: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreAttackAbAttr).applyPreAttack(pokemon, passive, simulated, defender, move, args),
|
||||||
|
(attr, passive) => (attr as PreAttackAbAttr).canApplyPreAttack(pokemon, passive, simulated, defender, move, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyExecutedMoveAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends ExecutedMoveAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
attr => (attr as ExecutedMoveAbAttr).applyExecutedMove(pokemon, simulated),
|
||||||
|
attr => (attr as ExecutedMoveAbAttr).canApplyExecutedMove(pokemon, simulated),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostAttackAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostAttackAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
defender: Pokemon,
|
||||||
|
move: Move,
|
||||||
|
hitResult: HitResult | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostAttackAbAttr).applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostAttackAbAttr).canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostKnockOutAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostKnockOutAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
knockedOut: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostKnockOutAbAttr).applyPostKnockOut(pokemon, passive, simulated, knockedOut, args),
|
||||||
|
(attr, passive) => (attr as PostKnockOutAbAttr).canApplyPostKnockOut(pokemon, passive, simulated, knockedOut, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostVictoryAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostVictoryAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostVictoryAbAttr).applyPostVictory(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostVictoryAbAttr).canApplyPostVictory(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostSummonAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostSummonAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
passive = false,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
attrType,
|
||||||
|
(attr, passive) => (attr as PostSummonAbAttr).applyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostSummonAbAttr).canApplyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
false,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreSummonAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreSummonAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreSummonAbAttr).applyPreSummon(pokemon, passive, args),
|
||||||
|
(attr, passive) => (attr as PreSummonAbAttr).canApplyPreSummon(pokemon, passive, args),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreSwitchOutAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreSwitchOutAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreSwitchOutAbAttr).applyPreSwitchOut(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PreSwitchOutAbAttr).canApplyPreSwitchOut(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreLeaveFieldAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreLeaveFieldAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreLeaveFieldAbAttr).applyPreLeaveField(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PreLeaveFieldAbAttr).canApplyPreLeaveField(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreStatStageChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreStatStageChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon | null,
|
||||||
|
stat: BattleStat,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreStatStageChangeAbAttr).applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreStatStageChangeAbAttr).canApplyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostStatStageChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostStatStageChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stats: BattleStat[],
|
||||||
|
stages: number,
|
||||||
|
selfTarget: boolean,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, _passive) =>
|
||||||
|
(attr as PostStatStageChangeAbAttr).applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args),
|
||||||
|
(attr, _passive) =>
|
||||||
|
(attr as PostStatStageChangeAbAttr).canApplyPostStatStageChange(
|
||||||
|
pokemon,
|
||||||
|
simulated,
|
||||||
|
stats,
|
||||||
|
stages,
|
||||||
|
selfTarget,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreSetStatusAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreSetStatusAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
effect: StatusEffect | undefined,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreSetStatusAbAttr).applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreSetStatusAbAttr).canApplyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreApplyBattlerTagAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreApplyBattlerTagAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
tag: BattlerTag,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreApplyBattlerTagAbAttr).applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreApplyBattlerTagAbAttr).canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreWeatherEffectAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreWeatherEffectAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
weather: Weather | null,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreWeatherDamageAbAttr).applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreWeatherDamageAbAttr).canApplyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostTurnAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostTurnAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostTurnAbAttr).applyPostTurn(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostTurnAbAttr).canApplyPostTurn(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostWeatherChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostWeatherChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
weather: WeatherType,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherChangeAbAttr).applyPostWeatherChange(pokemon, passive, simulated, weather, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherChangeAbAttr).canApplyPostWeatherChange(pokemon, passive, simulated, weather, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostWeatherLapseAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostWeatherLapseAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
weather: Weather | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherLapseAbAttr).applyPostWeatherLapse(pokemon, passive, simulated, weather, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherLapseAbAttr).canApplyPostWeatherLapse(pokemon, passive, simulated, weather, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostTerrainChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostTerrainChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
terrain: TerrainType,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostTerrainChangeAbAttr).applyPostTerrainChange(pokemon, passive, simulated, terrain, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostTerrainChangeAbAttr).canApplyPostTerrainChange(pokemon, passive, simulated, terrain, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyCheckTrappedAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends CheckTrappedAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
trapped: BooleanHolder,
|
||||||
|
otherPokemon: Pokemon,
|
||||||
|
messages: string[],
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as CheckTrappedAbAttr).applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as CheckTrappedAbAttr).canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
messages,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostBattleAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostBattleAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostBattleAbAttr).applyPostBattle(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostBattleAbAttr).canApplyPostBattle(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostFaintAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostFaintAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
attacker?: Pokemon,
|
||||||
|
move?: Move,
|
||||||
|
hitResult?: HitResult,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostFaintAbAttr).applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostFaintAbAttr).canApplyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostItemLostAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostItemLostAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, _passive) => (attr as PostItemLostAbAttr).applyPostItemLost(pokemon, simulated, args),
|
||||||
|
(attr, _passive) => (attr as PostItemLostAbAttr).canApplyPostItemLost(pokemon, simulated, args),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies abilities when they become active mid-turn (ability switch)
|
||||||
|
*
|
||||||
|
* Ignores passives as they don't change and shouldn't be reapplied when main abilities change
|
||||||
|
*/
|
||||||
|
export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void {
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
"PostSummonAbAttr",
|
||||||
|
(attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather)
|
||||||
|
*/
|
||||||
|
export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void {
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
"PreLeaveFieldAbAttr",
|
||||||
|
(attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [...args, true]),
|
||||||
|
(attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [...args, true]),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
"IllusionBreakAbAttr",
|
||||||
|
(attr, passive) => attr.apply(pokemon, passive, simulated, null, args),
|
||||||
|
(attr, passive) => attr.canApply(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
@ -10,15 +10,7 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { HitResult } from "#enums/hit-result";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "./abilities/apply-ab-attrs";
|
||||||
BlockNonDirectDamageAbAttr,
|
|
||||||
InfiltratorAbAttr,
|
|
||||||
PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr,
|
|
||||||
ProtectStatAbAttr,
|
|
||||||
applyAbAttrs,
|
|
||||||
applyOnGainAbAttrs,
|
|
||||||
applyOnLoseAbAttrs,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CommonBattleAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
import { CommonAnim } from "#enums/move-anims-common";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
@ -144,7 +136,7 @@ export class MistTag extends ArenaTag {
|
|||||||
if (attacker) {
|
if (attacker) {
|
||||||
const bypassed = new BooleanHolder(false);
|
const bypassed = new BooleanHolder(false);
|
||||||
// TODO: Allow this to be simulated
|
// TODO: Allow this to be simulated
|
||||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||||
if (bypassed.value) {
|
if (bypassed.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -209,7 +201,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
|||||||
): boolean {
|
): boolean {
|
||||||
if (this.weakenedCategories.includes(moveCategory)) {
|
if (this.weakenedCategories.includes(moveCategory)) {
|
||||||
const bypassed = new BooleanHolder(false);
|
const bypassed = new BooleanHolder(false);
|
||||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||||
if (bypassed.value) {
|
if (bypassed.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -765,7 +757,7 @@ class SpikesTag extends ArenaTrapTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
if (simulated || cancelled.value) {
|
if (simulated || cancelled.value) {
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
}
|
}
|
||||||
@ -953,7 +945,7 @@ class StealthRockTag extends ArenaTrapTag {
|
|||||||
|
|
||||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1010,7 +1002,7 @@ class StickyWebTag extends ArenaTrapTag {
|
|||||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||||
if (pokemon.isGrounded()) {
|
if (pokemon.isGrounded()) {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (simulated) {
|
if (simulated) {
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
@ -1444,8 +1436,8 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
|||||||
// Could have a custom message that plays when a specific pokemon's NG ends? This entire thing exists due to passives after all
|
// Could have a custom message that plays when a specific pokemon's NG ends? This entire thing exists due to passives after all
|
||||||
const setter = globalScene
|
const setter = globalScene
|
||||||
.getField()
|
.getField()
|
||||||
.filter(p => p?.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false))[0];
|
.filter(p => p?.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0];
|
||||||
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr));
|
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1457,7 +1449,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
|||||||
|
|
||||||
for (const pokemon of globalScene.getField(true)) {
|
for (const pokemon of globalScene.getField(true)) {
|
||||||
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
||||||
if (pokemon && !pokemon.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false)) {
|
if (pokemon && !pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) {
|
||||||
[true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive));
|
[true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import { MoveId } from "#enums/move-id";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier, SpeciesStatBoosterModifier, TempExtraModifierModifier } from "#app/modifier/modifier";
|
|
||||||
import type { SpeciesStatBoosterModifierType } from "#app/modifier/modifier-type";
|
import type { SpeciesStatBoosterModifierType } from "#app/modifier/modifier-type";
|
||||||
import { speciesStarterCosts } from "./starters";
|
import { speciesStarterCosts } from "./starters";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -275,9 +274,9 @@ class MoveTypeEvolutionCondition extends SpeciesEvolutionCondition {
|
|||||||
class TreasureEvolutionCondition extends SpeciesEvolutionCondition {
|
class TreasureEvolutionCondition extends SpeciesEvolutionCondition {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(p => p.evoCounter
|
super(p => p.evoCounter
|
||||||
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
|
+ p.getHeldItems().filter(m => m.is("DamageMoneyRewardModifier")).length
|
||||||
+ globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier
|
+ globalScene.findModifiers(m => m.is("MoneyMultiplierModifier")
|
||||||
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9);
|
|| m.is("ExtraModifierModifier") || m.is("TempExtraModifierModifier")).length > 9);
|
||||||
this.description = i18next.t("pokemonEvolutions:treasure");
|
this.description = i18next.t("pokemonEvolutions:treasure");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1794,8 +1793,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
|||||||
],
|
],
|
||||||
[SpeciesId.CLAMPERL]: [
|
[SpeciesId.CLAMPERL]: [
|
||||||
// TODO: Change the SpeciesEvolutionConditions here to use a bespoke HeldItemEvolutionCondition after the modifier rework
|
// TODO: Change the SpeciesEvolutionConditions here to use a bespoke HeldItemEvolutionCondition after the modifier rework
|
||||||
new SpeciesEvolution(SpeciesId.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.getHeldItems().some(m => m instanceof SpeciesStatBoosterModifier && (m.type as SpeciesStatBoosterModifierType).key === "DEEP_SEA_TOOTH")), SpeciesWildEvolutionDelay.VERY_LONG),
|
new SpeciesEvolution(SpeciesId.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.getHeldItems().some(m => m.is("SpeciesStatBoosterModifier") && (m.type as SpeciesStatBoosterModifierType).key === "DEEP_SEA_TOOTH")), SpeciesWildEvolutionDelay.VERY_LONG),
|
||||||
new SpeciesEvolution(SpeciesId.GOREBYSS, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.getHeldItems().some(m => m instanceof SpeciesStatBoosterModifier && (m.type as SpeciesStatBoosterModifierType).key === "DEEP_SEA_SCALE")), SpeciesWildEvolutionDelay.VERY_LONG)
|
new SpeciesEvolution(SpeciesId.GOREBYSS, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.getHeldItems().some(m => m.is("SpeciesStatBoosterModifier") && (m.type as SpeciesStatBoosterModifierType).key === "DEEP_SEA_SCALE")), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||||
],
|
],
|
||||||
[SpeciesId.BOLDORE]: [
|
[SpeciesId.BOLDORE]: [
|
||||||
new SpeciesEvolution(SpeciesId.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
|
new SpeciesEvolution(SpeciesId.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { allMoves } from "./data-lists";
|
import { allMoves } from "./data-lists";
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common";
|
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName, coerceArray } from "../utils/common";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SubstituteTag } from "./battler-tags";
|
import { SubstituteTag } from "./battler-tags";
|
||||||
@ -10,6 +10,7 @@ import { isNullOrUndefined } from "../utils/common";
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
|
||||||
export class AnimConfig {
|
export class AnimConfig {
|
||||||
public id: number;
|
public id: number;
|
||||||
@ -519,7 +520,7 @@ function logMissingMoveAnim(move: MoveId, ...optionalParams: any[]) {
|
|||||||
* @param encounterAnim one or more animations to fetch
|
* @param encounterAnim one or more animations to fetch
|
||||||
*/
|
*/
|
||||||
export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
|
export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
|
||||||
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim];
|
const anims = coerceArray(encounterAnim);
|
||||||
const encounterAnimNames = getEnumKeys(EncounterAnim);
|
const encounterAnimNames = getEnumKeys(EncounterAnim);
|
||||||
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
|
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
|
||||||
for (const anim of anims) {
|
for (const anim of anims) {
|
||||||
@ -770,7 +771,7 @@ export abstract class BattleAnim {
|
|||||||
const user = !isOppAnim ? this.user : this.target;
|
const user = !isOppAnim ? this.user : this.target;
|
||||||
const target = !isOppAnim ? this.target : this.user;
|
const target = !isOppAnim ? this.target : this.user;
|
||||||
|
|
||||||
const targetSubstitute = onSubstitute && user !== target ? target!.getTag(SubstituteTag) : null;
|
const targetSubstitute = onSubstitute && user !== target ? target!.getTag(BattlerTagType.SUBSTITUTE) : null;
|
||||||
|
|
||||||
const userInitialX = user!.x; // TODO: is this bang correct?
|
const userInitialX = user!.x; // TODO: is this bang correct?
|
||||||
const userInitialY = user!.y; // TODO: is this bang correct?
|
const userInitialY = user!.y; // TODO: is this bang correct?
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import {
|
import { applyAbAttrs } from "./abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
BlockNonDirectDamageAbAttr,
|
|
||||||
FlinchEffectAbAttr,
|
|
||||||
ProtectStatAbAttr,
|
|
||||||
ConditionalUserFieldProtectStatAbAttr,
|
|
||||||
ReverseDrainAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { allAbilities } from "./data-lists";
|
import { allAbilities } from "./data-lists";
|
||||||
import { CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims";
|
||||||
import { ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
import { ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||||
@ -28,7 +21,7 @@ import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
|||||||
import type { MovePhase } from "#app/phases/move-phase";
|
import type { MovePhase } from "#app/phases/move-phase";
|
||||||
import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
|
import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import { BooleanHolder, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common";
|
import { BooleanHolder, coerceArray, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
@ -57,7 +50,7 @@ export class BattlerTag {
|
|||||||
isBatonPassable = false,
|
isBatonPassable = false,
|
||||||
) {
|
) {
|
||||||
this.tagType = tagType;
|
this.tagType = tagType;
|
||||||
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [lapseType];
|
this.lapseTypes = coerceArray(lapseType);
|
||||||
this.turnCount = turnCount;
|
this.turnCount = turnCount;
|
||||||
this.sourceMove = sourceMove!; // TODO: is this bang correct?
|
this.sourceMove = sourceMove!; // TODO: is this bang correct?
|
||||||
this.sourceId = sourceId;
|
this.sourceId = sourceId;
|
||||||
@ -648,7 +641,7 @@ export class FlinchedTag extends BattlerTag {
|
|||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
applyAbAttrs(FlinchEffectAbAttr, pokemon, null);
|
applyAbAttrs("FlinchEffectAbAttr", pokemon, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -942,7 +935,7 @@ export class SeedTag extends BattlerTag {
|
|||||||
const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex);
|
const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex);
|
||||||
if (source) {
|
if (source) {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
@ -953,7 +946,7 @@ export class SeedTag extends BattlerTag {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||||
const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false);
|
const reverseDrain = pokemon.hasAbilityWithAttr("ReverseDrainAbAttr", false);
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"PokemonHealPhase",
|
"PokemonHealPhase",
|
||||||
source.getBattlerIndex(),
|
source.getBattlerIndex(),
|
||||||
@ -1026,7 +1019,7 @@ export class PowderTag extends BattlerTag {
|
|||||||
globalScene.phaseManager.unshiftNew("CommonAnimPhase", idx, idx, CommonAnim.POWDER);
|
globalScene.phaseManager.unshiftNew("CommonAnimPhase", idx, idx, CommonAnim.POWDER);
|
||||||
|
|
||||||
const cancelDamage = new BooleanHolder(false);
|
const cancelDamage = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelDamage);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelDamage);
|
||||||
if (!cancelDamage.value) {
|
if (!cancelDamage.value) {
|
||||||
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||||
}
|
}
|
||||||
@ -1079,7 +1072,7 @@ export class NightmareTag extends BattlerTag {
|
|||||||
phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE); // TODO: Update animation type
|
phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE); // TODO: Update animation type
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||||
@ -1438,7 +1431,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
|
|||||||
phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, this.commonAnim);
|
phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, this.commonAnim);
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||||
@ -1681,7 +1674,7 @@ export class ContactDamageProtectedTag extends ContactProtectedTag {
|
|||||||
*/
|
*/
|
||||||
override onContact(attacker: Pokemon, user: Pokemon): void {
|
override onContact(attacker: Pokemon, user: Pokemon): void {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), {
|
attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), {
|
||||||
result: HitResult.INDIRECT,
|
result: HitResult.INDIRECT,
|
||||||
@ -2277,7 +2270,7 @@ export class SaltCuredTag extends BattlerTag {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
const pokemonSteelOrWater = pokemon.isOfType(PokemonType.STEEL) || pokemon.isOfType(PokemonType.WATER);
|
const pokemonSteelOrWater = pokemon.isOfType(PokemonType.STEEL) || pokemon.isOfType(PokemonType.WATER);
|
||||||
@ -2331,7 +2324,7 @@ export class CursedTag extends BattlerTag {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||||
@ -2666,7 +2659,7 @@ export class GulpMissileTag extends BattlerTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", attacker, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), { result: HitResult.INDIRECT });
|
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), { result: HitResult.INDIRECT });
|
||||||
@ -3056,8 +3049,8 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
|
|||||||
|
|
||||||
if (lapseType === BattlerTagLapseType.CUSTOM) {
|
if (lapseType === BattlerTagLapseType.CUSTOM) {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled);
|
||||||
applyAbAttrs(ConditionalUserFieldProtectStatAbAttr, pokemon, cancelled, false, pokemon);
|
applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", pokemon, cancelled, false, pokemon);
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
if (pokemon.mysteryEncounterBattleEffects) {
|
if (pokemon.mysteryEncounterBattleEffects) {
|
||||||
pokemon.mysteryEncounterBattleEffects(pokemon);
|
pokemon.mysteryEncounterBattleEffects(pokemon);
|
||||||
|
@ -3,7 +3,7 @@ import type Pokemon from "../field/pokemon";
|
|||||||
import { HitResult } from "#enums/hit-result";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { getStatusEffectHealText } from "./status-effect";
|
import { getStatusEffectHealText } from "./status-effect";
|
||||||
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
||||||
import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./abilities/ability";
|
import { applyAbAttrs } from "./abilities/apply-ab-attrs";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
@ -38,25 +38,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
|||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
||||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6;
|
return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6;
|
||||||
};
|
};
|
||||||
case BerryType.LANSAT:
|
case BerryType.LANSAT:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
||||||
};
|
};
|
||||||
case BerryType.STARF:
|
case BerryType.STARF:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < 0.25;
|
return pokemon.getHpRatio() < 0.25;
|
||||||
};
|
};
|
||||||
case BerryType.LEPPA:
|
case BerryType.LEPPA:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return !!pokemon.getMoveset().find(m => !m.getPpRatio());
|
return !!pokemon.getMoveset().find(m => !m.getPpRatio());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
case BerryType.ENIGMA:
|
case BerryType.ENIGMA:
|
||||||
{
|
{
|
||||||
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, hpHealed);
|
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, hpHealed);
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"PokemonHealPhase",
|
"PokemonHealPhase",
|
||||||
consumer.getBattlerIndex(),
|
consumer.getBattlerIndex(),
|
||||||
@ -105,7 +105,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
// Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc.
|
// Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc.
|
||||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||||
const statStages = new NumberHolder(1);
|
const statStages = new NumberHolder(1);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, statStages);
|
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, statStages);
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"StatStageChangePhase",
|
"StatStageChangePhase",
|
||||||
consumer.getBattlerIndex(),
|
consumer.getBattlerIndex(),
|
||||||
@ -126,7 +126,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
{
|
{
|
||||||
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
||||||
const stages = new NumberHolder(2);
|
const stages = new NumberHolder(2);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, stages);
|
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, stages);
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"StatStageChangePhase",
|
"StatStageChangePhase",
|
||||||
consumer.getBattlerIndex(),
|
consumer.getBattlerIndex(),
|
||||||
|
@ -21,7 +21,7 @@ import { TrainerType } from "#enums/trainer-type";
|
|||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import { TypeColor, TypeShadow } from "#enums/color";
|
import { TypeColor, TypeShadow } from "#enums/color";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { pokemonFormChanges } from "./pokemon-forms";
|
import { pokemonFormChanges } from "./pokemon-forms";
|
||||||
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import type { Ability } from "./abilities/ability-class";
|
import type { ModifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import type { Ability } from "./abilities/ability";
|
||||||
import type Move from "./moves/move";
|
import type Move from "./moves/move";
|
||||||
|
|
||||||
export const allAbilities: Ability[] = [];
|
export const allAbilities: Ability[] = [];
|
||||||
export const allMoves: Move[] = [];
|
export const allMoves: Move[] = [];
|
||||||
|
|
||||||
|
// TODO: Figure out what this is used for and provide an appropriate tsdoc comment
|
||||||
|
export const modifierTypes = {} as ModifierTypes;
|
||||||
|
@ -1723,49 +1723,6 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const doubleBattleDialogue = {
|
|
||||||
blue_red_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:blue_red_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:blue_red_double.victory.1"],
|
|
||||||
},
|
|
||||||
red_blue_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:red_blue_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:red_blue_double.victory.1"],
|
|
||||||
},
|
|
||||||
tate_liza_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:tate_liza_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:tate_liza_double.victory.1"],
|
|
||||||
},
|
|
||||||
liza_tate_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:liza_tate_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:liza_tate_double.victory.1"],
|
|
||||||
},
|
|
||||||
wallace_steven_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:wallace_steven_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:wallace_steven_double.victory.1"],
|
|
||||||
},
|
|
||||||
steven_wallace_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:steven_wallace_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:steven_wallace_double.victory.1"],
|
|
||||||
},
|
|
||||||
alder_iris_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:alder_iris_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:alder_iris_double.victory.1"],
|
|
||||||
},
|
|
||||||
iris_alder_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:iris_alder_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:iris_alder_double.victory.1"],
|
|
||||||
},
|
|
||||||
marnie_piers_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:marnie_piers_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:marnie_piers_double.victory.1"],
|
|
||||||
},
|
|
||||||
piers_marnie_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:piers_marnie_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:piers_marnie_double.victory.1"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const battleSpecDialogue = {
|
export const battleSpecDialogue = {
|
||||||
[BattleSpec.FINAL_BOSS]: {
|
[BattleSpec.FINAL_BOSS]: {
|
||||||
encounter: "battleSpecDialogue:encounter",
|
encounter: "battleSpecDialogue:encounter",
|
||||||
|
44
src/data/double-battle-dialogue.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// TODO: Move this back into `dialogue.ts` after finding a suitable way to remove the circular dependencies
|
||||||
|
// that caused this to be moved out in the first place
|
||||||
|
export const doubleBattleDialogue = {
|
||||||
|
blue_red_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:blue_red_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:blue_red_double.victory.1"],
|
||||||
|
},
|
||||||
|
red_blue_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:red_blue_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:red_blue_double.victory.1"],
|
||||||
|
},
|
||||||
|
tate_liza_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:tate_liza_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:tate_liza_double.victory.1"],
|
||||||
|
},
|
||||||
|
liza_tate_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:liza_tate_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:liza_tate_double.victory.1"],
|
||||||
|
},
|
||||||
|
wallace_steven_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:wallace_steven_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:wallace_steven_double.victory.1"],
|
||||||
|
},
|
||||||
|
steven_wallace_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:steven_wallace_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:steven_wallace_double.victory.1"],
|
||||||
|
},
|
||||||
|
alder_iris_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:alder_iris_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:alder_iris_double.victory.1"],
|
||||||
|
},
|
||||||
|
iris_alder_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:iris_alder_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:iris_alder_double.victory.1"],
|
||||||
|
},
|
||||||
|
marnie_piers_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:marnie_piers_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:marnie_piers_double.victory.1"],
|
||||||
|
},
|
||||||
|
piers_marnie_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:piers_marnie_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:piers_marnie_double.victory.1"],
|
||||||
|
},
|
||||||
|
};
|
@ -33,38 +33,12 @@ import type { ArenaTrapTag } from "../arena-tag";
|
|||||||
import { WeakenMoveTypeTag } from "../arena-tag";
|
import { WeakenMoveTypeTag } from "../arena-tag";
|
||||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import {
|
import {
|
||||||
AllyMoveCategoryPowerBoostAbAttr,
|
|
||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
applyPostAttackAbAttrs,
|
applyPostAttackAbAttrs,
|
||||||
applyPostItemLostAbAttrs,
|
applyPostItemLostAbAttrs,
|
||||||
applyPreAttackAbAttrs,
|
applyPreAttackAbAttrs,
|
||||||
applyPreDefendAbAttrs,
|
applyPreDefendAbAttrs
|
||||||
BlockItemTheftAbAttr,
|
} from "../abilities/apply-ab-attrs";
|
||||||
BlockNonDirectDamageAbAttr,
|
|
||||||
BlockOneHitKOAbAttr,
|
|
||||||
BlockRecoilDamageAttr,
|
|
||||||
ChangeMovePriorityAbAttr,
|
|
||||||
ConfusionOnStatusEffectAbAttr,
|
|
||||||
FieldMoveTypePowerBoostAbAttr,
|
|
||||||
FieldPreventExplosiveMovesAbAttr,
|
|
||||||
ForceSwitchOutImmunityAbAttr,
|
|
||||||
HealFromBerryUseAbAttr,
|
|
||||||
IgnoreContactAbAttr,
|
|
||||||
IgnoreMoveEffectsAbAttr,
|
|
||||||
IgnoreProtectOnContactAbAttr,
|
|
||||||
InfiltratorAbAttr,
|
|
||||||
MaxMultiHitAbAttr,
|
|
||||||
MoveAbilityBypassAbAttr,
|
|
||||||
MoveEffectChanceMultiplierAbAttr,
|
|
||||||
MoveTypeChangeAbAttr,
|
|
||||||
PostDamageForceSwitchAbAttr,
|
|
||||||
PostItemLostAbAttr,
|
|
||||||
ReflectStatusMoveAbAttr,
|
|
||||||
ReverseDrainAbAttr,
|
|
||||||
UserFieldMoveTypePowerBoostAbAttr,
|
|
||||||
VariableMovePowerAbAttr,
|
|
||||||
WonderSkinAbAttr,
|
|
||||||
} from "../abilities/ability";
|
|
||||||
import { allAbilities, allMoves } from "../data-lists";
|
import { allAbilities, allMoves } from "../data-lists";
|
||||||
import {
|
import {
|
||||||
AttackTypeBoosterModifier,
|
AttackTypeBoosterModifier,
|
||||||
@ -77,7 +51,7 @@ import {
|
|||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { TerrainType } from "../terrain";
|
import { TerrainType } from "../terrain";
|
||||||
import { ModifierPoolType } from "#app/modifier/modifier-type";
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { Command } from "#enums/command";
|
import { Command } from "#enums/command";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { Localizable } from "#app/@types/locales";
|
import type { Localizable } from "#app/@types/locales";
|
||||||
@ -377,7 +351,7 @@ export default abstract class Move implements Localizable {
|
|||||||
|
|
||||||
const bypassed = new BooleanHolder(false);
|
const bypassed = new BooleanHolder(false);
|
||||||
// TODO: Allow this to be simulated
|
// TODO: Allow this to be simulated
|
||||||
applyAbAttrs(InfiltratorAbAttr, user, null, false, bypassed);
|
applyAbAttrs("InfiltratorAbAttr", user, null, false, bypassed);
|
||||||
|
|
||||||
return !bypassed.value
|
return !bypassed.value
|
||||||
&& !this.hasFlag(MoveFlags.SOUND_BASED)
|
&& !this.hasFlag(MoveFlags.SOUND_BASED)
|
||||||
@ -668,14 +642,14 @@ export default abstract class Move implements Localizable {
|
|||||||
// special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact
|
// special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
case MoveFlags.MAKES_CONTACT:
|
case MoveFlags.MAKES_CONTACT:
|
||||||
if (user.hasAbilityWithAttr(IgnoreContactAbAttr) || this.hitsSubstitute(user, target)) {
|
if (user.hasAbilityWithAttr("IgnoreContactAbAttr") || this.hitsSubstitute(user, target)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MoveFlags.IGNORE_ABILITIES:
|
case MoveFlags.IGNORE_ABILITIES:
|
||||||
if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) {
|
if (user.hasAbilityWithAttr("MoveAbilityBypassAbAttr")) {
|
||||||
const abilityEffectsIgnored = new BooleanHolder(false);
|
const abilityEffectsIgnored = new BooleanHolder(false);
|
||||||
applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this);
|
applyAbAttrs("MoveAbilityBypassAbAttr", user, abilityEffectsIgnored, false, this);
|
||||||
if (abilityEffectsIgnored.value) {
|
if (abilityEffectsIgnored.value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -684,7 +658,7 @@ export default abstract class Move implements Localizable {
|
|||||||
}
|
}
|
||||||
return this.hasFlag(MoveFlags.IGNORE_ABILITIES) && !isFollowUp;
|
return this.hasFlag(MoveFlags.IGNORE_ABILITIES) && !isFollowUp;
|
||||||
case MoveFlags.IGNORE_PROTECT:
|
case MoveFlags.IGNORE_PROTECT:
|
||||||
if (user.hasAbilityWithAttr(IgnoreProtectOnContactAbAttr)
|
if (user.hasAbilityWithAttr("IgnoreProtectOnContactAbAttr")
|
||||||
&& this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user })) {
|
&& this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user })) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -695,7 +669,7 @@ export default abstract class Move implements Localizable {
|
|||||||
target?.getTag(SemiInvulnerableTag) ||
|
target?.getTag(SemiInvulnerableTag) ||
|
||||||
!(target?.getTag(BattlerTagType.MAGIC_COAT) ||
|
!(target?.getTag(BattlerTagType.MAGIC_COAT) ||
|
||||||
(!this.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) &&
|
(!this.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) &&
|
||||||
target?.hasAbilityWithAttr(ReflectStatusMoveAbAttr)))
|
target?.hasAbilityWithAttr("ReflectStatusMoveAbAttr")))
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -792,7 +766,7 @@ export default abstract class Move implements Localizable {
|
|||||||
const moveAccuracy = new NumberHolder(this.accuracy);
|
const moveAccuracy = new NumberHolder(this.accuracy);
|
||||||
|
|
||||||
applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy);
|
applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy);
|
||||||
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy);
|
applyPreDefendAbAttrs("WonderSkinAbAttr", target, user, this, { value: false }, simulated, moveAccuracy);
|
||||||
|
|
||||||
if (moveAccuracy.value === -1) {
|
if (moveAccuracy.value === -1) {
|
||||||
return moveAccuracy.value;
|
return moveAccuracy.value;
|
||||||
@ -835,25 +809,25 @@ export default abstract class Move implements Localizable {
|
|||||||
const typeChangeMovePowerMultiplier = new NumberHolder(1);
|
const typeChangeMovePowerMultiplier = new NumberHolder(1);
|
||||||
const typeChangeHolder = new NumberHolder(this.type);
|
const typeChangeHolder = new NumberHolder(this.type);
|
||||||
|
|
||||||
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
|
applyPreAttackAbAttrs("MoveTypeChangeAbAttr", source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
|
||||||
|
|
||||||
const sourceTeraType = source.getTeraType();
|
const sourceTeraType = source.getTeraType();
|
||||||
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||||
power.value = 60;
|
power.value = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power);
|
applyPreAttackAbAttrs("VariableMovePowerAbAttr", source, target, this, simulated, power);
|
||||||
const ally = source.getAlly();
|
const ally = source.getAlly();
|
||||||
if (!isNullOrUndefined(ally)) {
|
if (!isNullOrUndefined(ally)) {
|
||||||
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, ally, target, this, simulated, power);
|
applyPreAttackAbAttrs("AllyMoveCategoryPowerBoostAbAttr", ally, target, this, simulated, power);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldAuras = new Set(
|
const fieldAuras = new Set(
|
||||||
globalScene.getField(true)
|
globalScene.getField(true)
|
||||||
.map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr).filter(attr => {
|
.map((p) => p.getAbilityAttrs("FieldMoveTypePowerBoostAbAttr").filter(attr => {
|
||||||
const condition = attr.getCondition();
|
const condition = attr.getCondition();
|
||||||
return (!condition || condition(p));
|
return (!condition || condition(p));
|
||||||
}) as FieldMoveTypePowerBoostAbAttr[])
|
}))
|
||||||
.flat(),
|
.flat(),
|
||||||
);
|
);
|
||||||
for (const aura of fieldAuras) {
|
for (const aura of fieldAuras) {
|
||||||
@ -861,7 +835,7 @@ export default abstract class Move implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||||
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power));
|
alliedField.forEach(p => applyPreAttackAbAttrs("UserFieldMoveTypePowerBoostAbAttr", p, target, this, simulated, power));
|
||||||
|
|
||||||
power.value *= typeChangeMovePowerMultiplier.value;
|
power.value *= typeChangeMovePowerMultiplier.value;
|
||||||
|
|
||||||
@ -888,7 +862,7 @@ export default abstract class Move implements Localizable {
|
|||||||
const priority = new NumberHolder(this.priority);
|
const priority = new NumberHolder(this.priority);
|
||||||
|
|
||||||
applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority);
|
applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority);
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, simulated, this, priority);
|
applyAbAttrs("ChangeMovePriorityAbAttr", user, null, simulated, this, priority);
|
||||||
|
|
||||||
return priority.value;
|
return priority.value;
|
||||||
}
|
}
|
||||||
@ -1340,7 +1314,7 @@ export class MoveEffectAttr extends MoveAttr {
|
|||||||
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number {
|
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number {
|
||||||
const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance);
|
const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, !showAbility, moveChance, move);
|
applyAbAttrs("MoveEffectChanceMultiplierAbAttr", user, null, !showAbility, moveChance, move);
|
||||||
|
|
||||||
if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) {
|
if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) {
|
||||||
const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
@ -1348,7 +1322,7 @@ export class MoveEffectAttr extends MoveAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!selfEffect) {
|
if (!selfEffect) {
|
||||||
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, !showAbility, moveChance);
|
applyPreDefendAbAttrs("IgnoreMoveEffectsAbAttr", target, user, null, null, !showAbility, moveChance);
|
||||||
}
|
}
|
||||||
return moveChance.value;
|
return moveChance.value;
|
||||||
}
|
}
|
||||||
@ -1726,8 +1700,8 @@ export class RecoilAttr extends MoveEffectAttr {
|
|||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
if (!this.unblockable) {
|
if (!this.unblockable) {
|
||||||
applyAbAttrs(BlockRecoilDamageAttr, user, cancelled);
|
applyAbAttrs("BlockRecoilDamageAttr", user, cancelled);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
@ -1860,7 +1834,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr {
|
|||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
// Check to see if the Pokemon has an ability that blocks non-direct damage
|
// Check to see if the Pokemon has an ability that blocks non-direct damage
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true });
|
user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message
|
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message
|
||||||
@ -2059,7 +2033,7 @@ export class FlameBurstAttr extends MoveEffectAttr {
|
|||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (!isNullOrUndefined(targetAlly)) {
|
if (!isNullOrUndefined(targetAlly)) {
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, targetAlly, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", targetAlly, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value || !targetAlly || targetAlly.switchOutStatus) {
|
if (cancelled.value || !targetAlly || targetAlly.switchOutStatus) {
|
||||||
@ -2289,7 +2263,7 @@ export class HitHealAttr extends MoveEffectAttr {
|
|||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
let healAmount = 0;
|
let healAmount = 0;
|
||||||
let message = "";
|
let message = "";
|
||||||
const reverseDrain = target.hasAbilityWithAttr(ReverseDrainAbAttr, false);
|
const reverseDrain = target.hasAbilityWithAttr("ReverseDrainAbAttr", false);
|
||||||
if (this.healStat !== null) {
|
if (this.healStat !== null) {
|
||||||
// Strength Sap formula
|
// Strength Sap formula
|
||||||
healAmount = target.getEffectiveStat(this.healStat);
|
healAmount = target.getEffectiveStat(this.healStat);
|
||||||
@ -2300,7 +2274,7 @@ export class HitHealAttr extends MoveEffectAttr {
|
|||||||
message = i18next.t("battle:regainHealth", { pokemonName: getPokemonNameWithAffix(user) });
|
message = i18next.t("battle:regainHealth", { pokemonName: getPokemonNameWithAffix(user) });
|
||||||
}
|
}
|
||||||
if (reverseDrain) {
|
if (reverseDrain) {
|
||||||
if (user.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
|
if (user.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")) {
|
||||||
healAmount = 0;
|
healAmount = 0;
|
||||||
message = "";
|
message = "";
|
||||||
} else {
|
} else {
|
||||||
@ -2430,7 +2404,7 @@ export class MultiHitAttr extends MoveAttr {
|
|||||||
{
|
{
|
||||||
const rand = user.randBattleSeedInt(20);
|
const rand = user.randBattleSeedInt(20);
|
||||||
const hitValue = new NumberHolder(rand);
|
const hitValue = new NumberHolder(rand);
|
||||||
applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue);
|
applyAbAttrs("MaxMultiHitAbAttr", user, null, false, hitValue);
|
||||||
if (hitValue.value >= 13) {
|
if (hitValue.value >= 13) {
|
||||||
return 2;
|
return 2;
|
||||||
} else if (hitValue.value >= 6) {
|
} else if (hitValue.value >= 6) {
|
||||||
@ -2538,7 +2512,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0))
|
if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||||
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus, quiet)) {
|
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus, quiet)) {
|
||||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
|
applyPostAttackAbAttrs("ConfusionOnStatusEffectAbAttr", user, target, move, null, false, this.effect);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2694,7 +2668,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
|
|||||||
// Check for abilities that block item theft
|
// Check for abilities that block item theft
|
||||||
// TODO: This should not trigger if the target would faint beforehand
|
// TODO: This should not trigger if the target would faint beforehand
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockItemTheftAbAttr, target, cancelled);
|
applyAbAttrs("BlockItemTheftAbAttr", target, cancelled);
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
@ -2811,8 +2785,8 @@ export class EatBerryAttr extends MoveEffectAttr {
|
|||||||
protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) {
|
protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) {
|
||||||
// consumer eats berry, owner triggers unburden and similar effects
|
// consumer eats berry, owner triggers unburden and similar effects
|
||||||
getBerryEffectFunc(this.chosenBerry.berryType)(consumer);
|
getBerryEffectFunc(this.chosenBerry.berryType)(consumer);
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", berryOwner, false);
|
||||||
applyAbAttrs(HealFromBerryUseAbAttr, consumer, new BooleanHolder(false));
|
applyAbAttrs("HealFromBerryUseAbAttr", consumer, new BooleanHolder(false));
|
||||||
consumer.recordEatenBerry(this.chosenBerry.berryType, updateHarvest);
|
consumer.recordEatenBerry(this.chosenBerry.berryType, updateHarvest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2837,7 +2811,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
|||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
// check for abilities that block item theft
|
// check for abilities that block item theft
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockItemTheftAbAttr, target, cancelled);
|
applyAbAttrs("BlockItemTheftAbAttr", target, cancelled);
|
||||||
if (cancelled.value === true) {
|
if (cancelled.value === true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2851,7 +2825,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
|||||||
|
|
||||||
// pick a random berry and eat it
|
// pick a random berry and eat it
|
||||||
this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)];
|
this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)];
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", target, false);
|
||||||
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name });
|
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name });
|
||||||
globalScene.phaseManager.queueMessage(message);
|
globalScene.phaseManager.queueMessage(message);
|
||||||
this.reduceBerryModifier(target);
|
this.reduceBerryModifier(target);
|
||||||
@ -2892,7 +2866,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
|
|||||||
|
|
||||||
// Special edge case for shield dust blocking Sparkling Aria curing burn
|
// Special edge case for shield dust blocking Sparkling Aria curing burn
|
||||||
const moveTargets = getMoveTargets(user, move.id);
|
const moveTargets = getMoveTargets(user, move.id);
|
||||||
if (target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && move.id === MoveId.SPARKLING_ARIA && moveTargets.targets.length === 1) {
|
if (target.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && move.id === MoveId.SPARKLING_ARIA && moveTargets.targets.length === 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3042,7 +3016,7 @@ export class OneHitKOAttr extends MoveAttr {
|
|||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user, target, move) => {
|
return (user, target, move) => {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockOneHitKOAbAttr, target, cancelled);
|
applyAbAttrs("BlockOneHitKOAbAttr", target, cancelled);
|
||||||
return !cancelled.value && user.level >= target.level;
|
return !cancelled.value && user.level >= target.level;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -5442,7 +5416,7 @@ export class NoEffectAttr extends MoveAttr {
|
|||||||
|
|
||||||
const crashDamageFunc = (user: Pokemon, move: Move) => {
|
const crashDamageFunc = (user: Pokemon, move: Move) => {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -6302,7 +6276,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
||||||
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
||||||
*/
|
*/
|
||||||
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr)
|
if (target.getAbility().hasAttr("PostDamageForceSwitchAbAttr")
|
||||||
&& [ MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN ].includes(move.id)
|
&& [ MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN ].includes(move.id)
|
||||||
) {
|
) {
|
||||||
if (this.hpDroppedBelowHalf(target)) {
|
if (this.hpDroppedBelowHalf(target)) {
|
||||||
@ -6391,7 +6365,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
||||||
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
||||||
*/
|
*/
|
||||||
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr)
|
if (target.getAbility().hasAttr("PostDamageForceSwitchAbAttr")
|
||||||
&& [ MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN ].includes(move.id)
|
&& [ MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN ].includes(move.id)
|
||||||
) {
|
) {
|
||||||
if (this.hpDroppedBelowHalf(target)) {
|
if (this.hpDroppedBelowHalf(target)) {
|
||||||
@ -6434,7 +6408,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
|
|
||||||
getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined {
|
getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined {
|
||||||
const blockedByAbility = new BooleanHolder(false);
|
const blockedByAbility = new BooleanHolder(false);
|
||||||
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
|
applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility);
|
||||||
if (blockedByAbility.value) {
|
if (blockedByAbility.value) {
|
||||||
return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) });
|
return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) });
|
||||||
}
|
}
|
||||||
@ -6474,7 +6448,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const blockedByAbility = new BooleanHolder(false);
|
const blockedByAbility = new BooleanHolder(false);
|
||||||
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
|
applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility);
|
||||||
if (blockedByAbility.value) {
|
if (blockedByAbility.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -7981,7 +7955,7 @@ const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScen
|
|||||||
|
|
||||||
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
|
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
globalScene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled));
|
globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", p, cancelled));
|
||||||
// Queue a message if an ability prevented usage of the move
|
// Queue a message if an ability prevented usage of the move
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }));
|
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }));
|
||||||
|
@ -19,8 +19,8 @@ import i18next from "i18next";
|
|||||||
import type { IEggOptions } from "#app/data/egg";
|
import type { IEggOptions } from "#app/data/egg";
|
||||||
import { EggSourceType } from "#enums/egg-source-types";
|
import { EggSourceType } from "#enums/egg-source-types";
|
||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
|
@ -10,7 +10,7 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { EnemyPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
setEncounterExp,
|
setEncounterExp,
|
||||||
updatePlayerMoney,
|
updatePlayerMoney,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import { ModifierPoolType, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
@ -38,7 +38,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import {
|
import {
|
||||||
AttackTypeBoosterModifier,
|
AttackTypeBoosterModifier,
|
||||||
@ -50,7 +50,7 @@ import {
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
|
@ -11,9 +11,10 @@ import {
|
|||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { ModifierPoolType, modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -38,7 +39,6 @@ import i18next from "i18next";
|
|||||||
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { Ability } from "#app/data/abilities/ability-class";
|
|
||||||
import { BerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
@ -49,6 +49,7 @@ import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
|||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/clowningAround";
|
const namespace = "mysteryEncounters/clowningAround";
|
||||||
@ -139,7 +140,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
|
|
||||||
// Generate random ability for Blacephalon from pool
|
// Generate random ability for Blacephalon from pool
|
||||||
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
|
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
|
||||||
encounter.setDialogueToken("ability", new Ability(ability, 3).name);
|
encounter.setDialogueToken("ability", allAbilities[ability].name);
|
||||||
encounter.misc = { ability };
|
encounter.misc = { ability };
|
||||||
|
|
||||||
// Decide the random types for Blacephalon. They should not be the same.
|
// Decide the random types for Blacephalon. They should not be the same.
|
||||||
|
@ -26,7 +26,7 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { EnemyPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
@ -3,7 +3,7 @@ import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
|||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
PreserveBerryModifier,
|
PreserveBerryModifier,
|
||||||
} from "#app/modifier/modifier";
|
} from "#app/modifier/modifier";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { randSeedItem } from "#app/utils/common";
|
import { randSeedItem } from "#app/utils/common";
|
||||||
|
@ -2,8 +2,8 @@ import {
|
|||||||
leaveEncounterWithoutBattle,
|
leaveEncounterWithoutBattle,
|
||||||
setEncounterRewards,
|
setEncounterRewards,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
|
import type { ModifierTypeFunc } from "#app/@types/modifier-types";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
generateModifierType,
|
generateModifierType,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -45,8 +45,8 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
|||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { Ability } from "#app/data/abilities/ability-class";
|
|
||||||
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/fieryFallout";
|
const namespace = "mysteryEncounters/fieryFallout";
|
||||||
@ -246,7 +246,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
|||||||
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
||||||
// Burn applied
|
// Burn applied
|
||||||
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
||||||
encounter.setDialogueToken("abilityName", new Ability(AbilityId.HEATPROOF, 3).name);
|
encounter.setDialogueToken("abilityName", allAbilities[AbilityId.HEATPROOF].name);
|
||||||
queueEncounterMessage(`${namespace}:option.2.target_burned`);
|
queueEncounterMessage(`${namespace}:option.2.target_burned`);
|
||||||
|
|
||||||
// Also permanently change the burned Pokemon's ability to Heatproof
|
// Also permanently change the burned Pokemon's ability to Heatproof
|
||||||
|
@ -9,13 +9,13 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import {
|
import {
|
||||||
getPlayerModifierTypeOptions,
|
getPlayerModifierTypeOptions,
|
||||||
ModifierPoolType,
|
|
||||||
regenerateModifierPoolThresholds,
|
regenerateModifierPoolThresholds,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
@ -26,7 +26,7 @@ import { PlayerGender } from "#enums/player-gender";
|
|||||||
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
||||||
import { addPokeballOpenParticles } from "#app/field/anims";
|
import { addPokeballOpenParticles } from "#app/field/anims";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
@ -4,14 +4,14 @@ import {
|
|||||||
setEncounterRewards,
|
setEncounterRewards,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { MusicPreference } from "#app/system/settings/settings";
|
import { MusicPreference } from "#app/system/settings/settings";
|
||||||
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import {
|
import {
|
||||||
getPlayerModifierTypeOptions,
|
getPlayerModifierTypeOptions,
|
||||||
ModifierPoolType,
|
|
||||||
regenerateModifierPoolThresholds,
|
regenerateModifierPoolThresholds,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
@ -7,8 +7,8 @@ import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
|||||||
import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate";
|
import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -23,7 +23,8 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
|
|||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import { getBiomeKey } from "#app/field/arena";
|
import { getBiomeKey } from "#app/field/arena";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getPartyLuckValue, modifierTypes } from "#app/modifier/modifier-type";
|
import { getPartyLuckValue } from "#app/modifier/modifier-type";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
@ -26,7 +26,7 @@ import { EggSourceType } from "#enums/egg-source-types";
|
|||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
generateModifierType,
|
generateModifierType,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -24,11 +24,11 @@ import { PokemonType } from "#enums/pokemon-type";
|
|||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability";
|
import { applyPostBattleInitAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
|||||||
|
|
||||||
// Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects
|
// Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects
|
||||||
pokemon.resetBattleAndWaveData();
|
pokemon.resetBattleAndWaveData();
|
||||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene.phaseManager.unshiftNew("ShowTrainerPhase");
|
globalScene.phaseManager.unshiftNew("ShowTrainerPhase");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Ability } from "#app/data/abilities/ability-class";
|
import type { Ability } from "#app/data/abilities/ability";
|
||||||
import { allAbilities } from "#app/data/data-lists";
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import {
|
import {
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -21,7 +21,7 @@ import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#app
|
|||||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
|
@ -25,7 +25,7 @@ import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from
|
|||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import {
|
import {
|
||||||
doPokemonTransformationSequence,
|
doPokemonTransformationSequence,
|
||||||
@ -34,7 +34,7 @@ import {
|
|||||||
import { getLevelTotalExp } from "#app/data/exp";
|
import { getLevelTotalExp } from "#app/data/exp";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
@ -11,7 +11,7 @@ import { WeatherType } from "#enums/weather-type";
|
|||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
||||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { AbilityId } from "#enums/ability-id";
|
import type { AbilityId } from "#enums/ability-id";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
@ -272,7 +272,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
||||||
super();
|
super();
|
||||||
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay];
|
this.requiredTimeOfDay = coerceArray(timeOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -294,7 +294,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
constructor(weather: WeatherType | WeatherType[]) {
|
constructor(weather: WeatherType | WeatherType[]) {
|
||||||
super();
|
super();
|
||||||
this.requiredWeather = Array.isArray(weather) ? weather : [weather];
|
this.requiredWeather = coerceArray(weather);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -360,7 +360,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
|||||||
constructor(heldItem: string | string[], minNumberOfItems = 1) {
|
constructor(heldItem: string | string[], minNumberOfItems = 1) {
|
||||||
super();
|
super();
|
||||||
this.minNumberOfItems = minNumberOfItems;
|
this.minNumberOfItems = minNumberOfItems;
|
||||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
this.requiredHeldItemModifiers = coerceArray(heldItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -426,7 +426,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredSpecies = Array.isArray(species) ? species : [species];
|
this.requiredSpecies = coerceArray(species);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -466,7 +466,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredNature = Array.isArray(nature) ? nature : [nature];
|
this.requiredNature = coerceArray(nature);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -504,7 +504,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeFainted = excludeFainted;
|
this.excludeFainted = excludeFainted;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredType = Array.isArray(type) ? type : [type];
|
this.requiredType = coerceArray(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -558,7 +558,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredMoves = Array.isArray(moves) ? moves : [moves];
|
this.requiredMoves = coerceArray(moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -609,7 +609,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove];
|
this.requiredMoves = coerceArray(learnableMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -665,7 +665,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities];
|
this.requiredAbilities = coerceArray(abilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -710,7 +710,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect];
|
this.requiredStatusEffect = coerceArray(statusEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -785,7 +785,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem];
|
this.requiredFormChangeItem = coerceArray(formChangeItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -843,7 +843,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems];
|
this.requiredEvolutionItem = coerceArray(evolutionItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -908,7 +908,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
this.requiredHeldItemModifiers = coerceArray(heldItem);
|
||||||
this.requireTransferable = requireTransferable;
|
this.requireTransferable = requireTransferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,7 +972,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
|
this.requiredHeldItemTypes = coerceArray(heldItemTypes);
|
||||||
this.requireTransferable = requireTransferable;
|
this.requireTransferable = requireTransferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters";
|
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/constants";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encoun
|
|||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type { PokemonMove } from "../moves/pokemon-move";
|
import type { PokemonMove } from "../moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common";
|
import { capitalizeFirstLetter, coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
||||||
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
|
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
|
||||||
@ -717,7 +717,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
withAnimations(
|
withAnimations(
|
||||||
...encounterAnimations: EncounterAnim[]
|
...encounterAnimations: EncounterAnim[]
|
||||||
): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
|
): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
|
||||||
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations];
|
const animations = coerceArray(encounterAnimations);
|
||||||
return Object.assign(this, { encounterAnimations: animations });
|
return Object.assign(this, { encounterAnimations: animations });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,7 +729,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
withDisallowedGameModes(
|
withDisallowedGameModes(
|
||||||
...disallowedGameModes: GameModes[]
|
...disallowedGameModes: GameModes[]
|
||||||
): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
|
): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
|
||||||
const gameModes = Array.isArray(disallowedGameModes) ? disallowedGameModes : [disallowedGameModes];
|
const gameModes = coerceArray(disallowedGameModes);
|
||||||
return Object.assign(this, { disallowedGameModes: gameModes });
|
return Object.assign(this, { disallowedGameModes: gameModes });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -741,7 +741,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
withDisallowedChallenges(
|
withDisallowedChallenges(
|
||||||
...disallowedChallenges: Challenges[]
|
...disallowedChallenges: Challenges[]
|
||||||
): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
|
): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
|
||||||
const challenges = Array.isArray(disallowedChallenges) ? disallowedChallenges : [disallowedChallenges];
|
const challenges = coerceArray(disallowedChallenges);
|
||||||
return Object.assign(this, { disallowedChallenges: challenges });
|
return Object.assign(this, { disallowedChallenges: challenges });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,42 +34,6 @@ import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encount
|
|||||||
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
||||||
import { getBiomeName } from "#app/data/balance/biomes";
|
import { getBiomeName } from "#app/data/balance/biomes";
|
||||||
|
|
||||||
/**
|
|
||||||
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
|
||||||
*/
|
|
||||||
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3;
|
|
||||||
/**
|
|
||||||
* The divisor for determining ME spawns, defines the "maximum" weight required for a spawn
|
|
||||||
* If spawn_weight === MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, 100% chance to spawn a ME
|
|
||||||
*/
|
|
||||||
export const MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT = 256;
|
|
||||||
/**
|
|
||||||
* When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks.
|
|
||||||
* These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
|
||||||
*/
|
|
||||||
export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 3;
|
|
||||||
/**
|
|
||||||
* Specifies the target average for total ME spawns in a single Classic run.
|
|
||||||
* Used by anti-variance mechanic to check whether a run is above or below the target on a given wave.
|
|
||||||
*/
|
|
||||||
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12;
|
|
||||||
/**
|
|
||||||
* Will increase/decrease the chance of spawning a ME based on the current run's total MEs encountered vs AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
|
||||||
* Example:
|
|
||||||
* AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 17 (expects avg 1 ME every 10 floors)
|
|
||||||
* ANTI_VARIANCE_WEIGHT_MODIFIER = 15
|
|
||||||
*
|
|
||||||
* On wave 20, if 1 ME has been encountered, the difference from expected average is 0 MEs.
|
|
||||||
* So anti-variance adds 0/256 to the spawn weight check for ME spawn.
|
|
||||||
*
|
|
||||||
* On wave 20, if 0 MEs have been encountered, the difference from expected average is 1 ME.
|
|
||||||
* So anti-variance adds 15/256 to the spawn weight check for ME spawn.
|
|
||||||
*
|
|
||||||
* On wave 20, if 2 MEs have been encountered, the difference from expected average is -1 ME.
|
|
||||||
* So anti-variance adds -15/256 to the spawn weight check for ME spawn.
|
|
||||||
*/
|
|
||||||
export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15;
|
|
||||||
|
|
||||||
export const EXTREME_ENCOUNTER_BIOMES = [
|
export const EXTREME_ENCOUNTER_BIOMES = [
|
||||||
BiomeId.SEA,
|
BiomeId.SEA,
|
||||||
BiomeId.SEABED,
|
BiomeId.SEABED,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||||
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
|
|
||||||
constructor(requiredMoves: MoveId | MoveId[], options: CanLearnMoveRequirementOptions = {}) {
|
constructor(requiredMoves: MoveId | MoveId[], options: CanLearnMoveRequirementOptions = {}) {
|
||||||
super();
|
super();
|
||||||
this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves];
|
this.requiredMoves = coerceArray(requiredMoves);
|
||||||
|
|
||||||
this.excludeLevelMoves = options.excludeLevelMoves ?? false;
|
this.excludeLevelMoves = options.excludeLevelMoves ?? false;
|
||||||
this.excludeTmMoves = options.excludeTmMoves ?? false;
|
this.excludeTmMoves = options.excludeTmMoves ?? false;
|
||||||
|
@ -3,10 +3,7 @@ import { BattlerIndex } from "#enums/battler-index";
|
|||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
||||||
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import {
|
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/constants";
|
||||||
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
|
||||||
WEIGHT_INCREMENT_ON_SPAWN_MISS,
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type { AiType } from "#enums/ai-type";
|
import type { AiType } from "#enums/ai-type";
|
||||||
@ -17,18 +14,18 @@ import { FieldPosition } from "#enums/field-position";
|
|||||||
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
|
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
|
||||||
import {
|
import {
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
ModifierPoolType,
|
|
||||||
ModifierTypeGenerator,
|
ModifierTypeGenerator,
|
||||||
ModifierTypeOption,
|
ModifierTypeOption,
|
||||||
modifierTypes,
|
|
||||||
regenerateModifierPoolThresholds,
|
regenerateModifierPoolThresholds,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
||||||
import { PartyUiMode } from "#app/ui/party-ui-handler";
|
import { PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedInt, randomString, randSeedItem, coerceArray } from "#app/utils/common";
|
||||||
import type { BattlerTagType } from "#enums/battler-tag-type";
|
import type { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import type { TrainerType } from "#enums/trainer-type";
|
import type { TrainerType } from "#enums/trainer-type";
|
||||||
@ -452,7 +449,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
|||||||
* @param moves
|
* @param moves
|
||||||
*/
|
*/
|
||||||
export function loadCustomMovesForEncounter(moves: MoveId | MoveId[]) {
|
export function loadCustomMovesForEncounter(moves: MoveId | MoveId[]) {
|
||||||
moves = Array.isArray(moves) ? moves : [moves];
|
moves = coerceArray(moves);
|
||||||
return Promise.all(moves.map(move => initMoveAnim(move))).then(() => loadMoveAnimAssets(moves));
|
return Promise.all(moves.map(move => initMoveAnim(move))).then(() => loadMoveAnimAssets(moves));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,7 +792,7 @@ export function setEncounterRewards(
|
|||||||
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
||||||
*/
|
*/
|
||||||
export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex = true) {
|
export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex = true) {
|
||||||
const participantIds = Array.isArray(participantId) ? participantId : [participantId];
|
const participantIds = coerceArray(participantId);
|
||||||
|
|
||||||
globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
|
globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
|
||||||
globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds));
|
globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds));
|
||||||
|
@ -29,7 +29,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import type { PermanentStat } from "#enums/stat";
|
import type { PermanentStat } from "#enums/stat";
|
||||||
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
||||||
|
97
src/data/phase-priority-queue.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { Phase } from "#app/phase";
|
||||||
|
import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase";
|
||||||
|
import type { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||||
|
import { PostSummonActivateAbilityPhase } from "#app/phases/post-summon-activate-ability-phase";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
import { TrickRoomTag } from "#app/data/arena-tag";
|
||||||
|
import { DynamicPhaseType } from "#enums/dynamic-phase-type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a list of {@linkcode Phase}s
|
||||||
|
*
|
||||||
|
* Dynamically updates ordering to always pop the highest "priority", based on implementation of {@linkcode reorder}
|
||||||
|
*/
|
||||||
|
export abstract class PhasePriorityQueue {
|
||||||
|
protected abstract queue: Phase[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the elements in the queue
|
||||||
|
*/
|
||||||
|
public abstract reorder(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@linkcode reorder} and shifts the queue
|
||||||
|
* @returns The front element of the queue after sorting
|
||||||
|
*/
|
||||||
|
public pop(): Phase | undefined {
|
||||||
|
this.reorder();
|
||||||
|
return this.queue.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a phase to the queue
|
||||||
|
* @param phase The phase to add
|
||||||
|
*/
|
||||||
|
public push(phase: Phase): void {
|
||||||
|
this.queue.push(phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all phases from the queue
|
||||||
|
*/
|
||||||
|
public clear(): void {
|
||||||
|
this.queue.splice(0, this.queue.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority Queue for {@linkcode PostSummonPhase} and {@linkcode PostSummonActivateAbilityPhase}
|
||||||
|
*
|
||||||
|
* Orders phases first by ability priority, then by the {@linkcode Pokemon}'s effective speed
|
||||||
|
*/
|
||||||
|
export class PostSummonPhasePriorityQueue extends PhasePriorityQueue {
|
||||||
|
protected override queue: PostSummonPhase[] = [];
|
||||||
|
|
||||||
|
public override reorder(): void {
|
||||||
|
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
|
||||||
|
if (phaseA.getPriority() === phaseB.getPriority()) {
|
||||||
|
return (
|
||||||
|
(phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD)) *
|
||||||
|
(isTrickRoom() ? -1 : 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return phaseB.getPriority() - phaseA.getPriority();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override push(phase: PostSummonPhase): void {
|
||||||
|
super.push(phase);
|
||||||
|
this.queueAbilityPhase(phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues all necessary {@linkcode PostSummonActivateAbilityPhase}s for each pushed {@linkcode PostSummonPhase}
|
||||||
|
* @param phase The {@linkcode PostSummonPhase} that was pushed onto the queue
|
||||||
|
*/
|
||||||
|
private queueAbilityPhase(phase: PostSummonPhase): void {
|
||||||
|
const phasePokemon = phase.getPokemon();
|
||||||
|
|
||||||
|
phasePokemon.getAbilityPriorities().forEach((priority, idx) => {
|
||||||
|
this.queue.push(new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority, !!idx));
|
||||||
|
globalScene.phaseManager.appendToPhase(
|
||||||
|
new ActivatePriorityQueuePhase(DynamicPhaseType.POST_SUMMON),
|
||||||
|
"ActivatePriorityQueuePhase",
|
||||||
|
(p: ActivatePriorityQueuePhase) => p.getType() === DynamicPhaseType.POST_SUMMON,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTrickRoom(): boolean {
|
||||||
|
const speedReversed = new BooleanHolder(false);
|
||||||
|
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
|
||||||
|
return speedReversed.value;
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier";
|
|
||||||
import { NumberHolder } from "#app/utils/common";
|
import { NumberHolder } from "#app/utils/common";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -94,7 +93,7 @@ export function getCriticalCaptureChance(modifiedCatchRate: number): number {
|
|||||||
}
|
}
|
||||||
const dexCount = globalScene.gameData.getSpeciesCount(d => !!d.caughtAttr);
|
const dexCount = globalScene.gameData.getSpeciesCount(d => !!d.caughtAttr);
|
||||||
const catchingCharmMultiplier = new NumberHolder(1);
|
const catchingCharmMultiplier = new NumberHolder(1);
|
||||||
globalScene.findModifier(m => m instanceof CriticalCatchChanceBoosterModifier)?.apply(catchingCharmMultiplier);
|
globalScene.findModifier(m => m.is("CriticalCatchChanceBoosterModifier"))?.apply(catchingCharmMultiplier);
|
||||||
const dexMultiplier =
|
const dexMultiplier =
|
||||||
globalScene.gameMode.isDaily || dexCount > 800
|
globalScene.gameMode.isDaily || dexCount > 800
|
||||||
? 2.5
|
? 2.5
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { Constructor } from "#app/utils/common";
|
import { coerceArray, type Constructor } from "#app/utils/common";
|
||||||
import type { TimeOfDay } from "#enums/time-of-day";
|
import type { TimeOfDay } from "#enums/time-of-day";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
@ -125,10 +125,7 @@ export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigg
|
|||||||
|
|
||||||
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
||||||
super();
|
super();
|
||||||
if (!Array.isArray(statusEffects)) {
|
this.statusEffects = coerceArray(statusEffects);
|
||||||
statusEffects = [statusEffects];
|
|
||||||
}
|
|
||||||
this.statusEffects = statusEffects;
|
|
||||||
this.invert = invert;
|
this.invert = invert;
|
||||||
// this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
|
// this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "../data-lists";
|
||||||
import { PokemonMove } from "../moves/pokemon-move";
|
import { PokemonMove } from "../moves/pokemon-move";
|
||||||
import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt, randSeedIntRange } from "#app/utils/common";
|
import {
|
||||||
|
toReadableString,
|
||||||
|
isNullOrUndefined,
|
||||||
|
randSeedItem,
|
||||||
|
randSeedInt,
|
||||||
|
coerceArray,
|
||||||
|
randSeedIntRange,
|
||||||
|
} from "#app/utils/common";
|
||||||
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { tmSpecies } from "#app/data/balance/tms";
|
import { tmSpecies } from "#app/data/balance/tms";
|
||||||
import { doubleBattleDialogue } from "#app/data/dialogue";
|
import { doubleBattleDialogue } from "../double-battle-dialogue";
|
||||||
import { TrainerVariant } from "#enums/trainer-variant";
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
|
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -37,7 +44,7 @@ import { timedEventManager } from "#app/global-event-manager";
|
|||||||
// Type imports
|
// Type imports
|
||||||
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
|
import type { ModifierTypeFunc } from "#app/@types/modifier-types";
|
||||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import type { EvilTeam } from "./evil-admin-trainer-pools";
|
import type { EvilTeam } from "./evil-admin-trainer-pools";
|
||||||
import type {
|
import type {
|
||||||
@ -554,10 +561,7 @@ export class TrainerConfig {
|
|||||||
this.speciesPools = evilAdminTrainerPools[poolName];
|
this.speciesPools = evilAdminTrainerPools[poolName];
|
||||||
|
|
||||||
signatureSpecies.forEach((speciesPool, s) => {
|
signatureSpecies.forEach((speciesPool, s) => {
|
||||||
if (!Array.isArray(speciesPool)) {
|
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
|
||||||
speciesPool = [speciesPool];
|
|
||||||
}
|
|
||||||
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");
|
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");
|
||||||
@ -620,10 +624,7 @@ export class TrainerConfig {
|
|||||||
this.setPartyTemplates(trainerPartyTemplates.RIVAL_5);
|
this.setPartyTemplates(trainerPartyTemplates.RIVAL_5);
|
||||||
}
|
}
|
||||||
signatureSpecies.forEach((speciesPool, s) => {
|
signatureSpecies.forEach((speciesPool, s) => {
|
||||||
if (!Array.isArray(speciesPool)) {
|
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
|
||||||
speciesPool = [speciesPool];
|
|
||||||
}
|
|
||||||
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
|
|
||||||
});
|
});
|
||||||
if (!isNullOrUndefined(specialtyType)) {
|
if (!isNullOrUndefined(specialtyType)) {
|
||||||
this.setSpeciesFilter(p => p.isOfType(specialtyType));
|
this.setSpeciesFilter(p => p.isOfType(specialtyType));
|
||||||
@ -668,12 +669,8 @@ export class TrainerConfig {
|
|||||||
|
|
||||||
// Set up party members with their corresponding species.
|
// Set up party members with their corresponding species.
|
||||||
signatureSpecies.forEach((speciesPool, s) => {
|
signatureSpecies.forEach((speciesPool, s) => {
|
||||||
// Ensure speciesPool is an array.
|
|
||||||
if (!Array.isArray(speciesPool)) {
|
|
||||||
speciesPool = [speciesPool];
|
|
||||||
}
|
|
||||||
// Set a function to get a random party member from the species pool.
|
// Set a function to get a random party member from the species pool.
|
||||||
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
|
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// If specialty type is provided, set species filter and specialty type.
|
// If specialty type is provided, set species filter and specialty type.
|
||||||
@ -729,12 +726,8 @@ export class TrainerConfig {
|
|||||||
|
|
||||||
// Set up party members with their corresponding species.
|
// Set up party members with their corresponding species.
|
||||||
signatureSpecies.forEach((speciesPool, s) => {
|
signatureSpecies.forEach((speciesPool, s) => {
|
||||||
// Ensure speciesPool is an array.
|
|
||||||
if (!Array.isArray(speciesPool)) {
|
|
||||||
speciesPool = [speciesPool];
|
|
||||||
}
|
|
||||||
// Set a function to get a random party member from the species pool.
|
// Set a function to get a random party member from the species pool.
|
||||||
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
|
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set species filter and specialty type if provided, otherwise filter by base total.
|
// Set species filter and specialty type if provided, otherwise filter by base total.
|
||||||
|
@ -5,12 +5,12 @@ import type Pokemon from "../field/pokemon";
|
|||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import type Move from "./moves/move";
|
import type Move from "./moves/move";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { SuppressWeatherEffectAbAttr } from "./abilities/ability";
|
|
||||||
import { TerrainType, getTerrainName } from "./terrain";
|
import { TerrainType, getTerrainName } from "./terrain";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { Arena } from "#app/field/arena";
|
import type { Arena } from "#app/field/arena";
|
||||||
import { timedEventManager } from "#app/global-event-manager";
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
import type { SuppressWeatherEffectAbAttr } from "./abilities/ability";
|
||||||
|
|
||||||
export class Weather {
|
export class Weather {
|
||||||
public weatherType: WeatherType;
|
public weatherType: WeatherType;
|
||||||
@ -108,10 +108,10 @@ export class Weather {
|
|||||||
for (const pokemon of field) {
|
for (const pokemon of field) {
|
||||||
let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon
|
let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon
|
||||||
.getAbility()
|
.getAbility()
|
||||||
.getAttrs(SuppressWeatherEffectAbAttr)[0];
|
.getAttrs("SuppressWeatherEffectAbAttr")[0];
|
||||||
if (!suppressWeatherEffectAbAttr) {
|
if (!suppressWeatherEffectAbAttr) {
|
||||||
suppressWeatherEffectAbAttr = pokemon.hasPassive()
|
suppressWeatherEffectAbAttr = pokemon.hasPassive()
|
||||||
? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]
|
? pokemon.getPassiveAbility().getAttrs("SuppressWeatherEffectAbAttr")[0]
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) {
|
if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) {
|
||||||
|
13
src/enums/confirm-ui-mode.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// biome-ignore lint/correctness/noUnusedImports: Used in tsdoc
|
||||||
|
import type ConfirmUiHandler from "#app/ui/confirm-ui-handler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by {@linkcode ConfirmUiHandler} to determine whether the cursor should start on Yes or No
|
||||||
|
*/
|
||||||
|
export const ConfirmUiMode = Object.freeze({
|
||||||
|
/** Start cursor on Yes */
|
||||||
|
DEFAULT_YES: 1,
|
||||||
|
/** Start cursor on No */
|
||||||
|
DEFAULT_NO: 2
|
||||||
|
});
|
||||||
|
export type ConfirmUiMode = typeof ConfirmUiMode[keyof typeof ConfirmUiMode];
|
6
src/enums/dynamic-phase-type.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Enum representation of the phase types held by implementations of {@linkcode PhasePriorityQueue}
|
||||||
|
*/
|
||||||
|
export enum DynamicPhaseType {
|
||||||
|
POST_SUMMON
|
||||||
|
}
|
7
src/enums/modifier-pool-type.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum ModifierPoolType {
|
||||||
|
PLAYER,
|
||||||
|
WILD,
|
||||||
|
TRAINER,
|
||||||
|
ENEMY_BUFF,
|
||||||
|
DAILY_STARTER
|
||||||
|
}
|
7
src/enums/unlockables.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
export enum Unlockables {
|
||||||
|
ENDLESS_MODE,
|
||||||
|
MINI_BLACK_HOLE,
|
||||||
|
SPLICED_ENDLESS_MODE,
|
||||||
|
EVIOLITE
|
||||||
|
}
|
@ -24,10 +24,7 @@ import {
|
|||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
applyPostTerrainChangeAbAttrs,
|
applyPostTerrainChangeAbAttrs,
|
||||||
applyPostWeatherChangeAbAttrs,
|
applyPostWeatherChangeAbAttrs,
|
||||||
PostTerrainChangeAbAttr,
|
} from "#app/data/abilities/apply-ab-attrs";
|
||||||
PostWeatherChangeAbAttr,
|
|
||||||
TerrainEventTypeChangeAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena";
|
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena";
|
||||||
@ -374,7 +371,7 @@ export class Arena {
|
|||||||
pokemon.findAndRemoveTags(
|
pokemon.findAndRemoveTags(
|
||||||
t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather),
|
t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather),
|
||||||
);
|
);
|
||||||
applyPostWeatherChangeAbAttrs(PostWeatherChangeAbAttr, pokemon, weather);
|
applyPostWeatherChangeAbAttrs("PostWeatherChangeAbAttr", pokemon, weather);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -463,8 +460,8 @@ export class Arena {
|
|||||||
pokemon.findAndRemoveTags(
|
pokemon.findAndRemoveTags(
|
||||||
t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain),
|
t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain),
|
||||||
);
|
);
|
||||||
applyPostTerrainChangeAbAttrs(PostTerrainChangeAbAttr, pokemon, terrain);
|
applyPostTerrainChangeAbAttrs("PostTerrainChangeAbAttr", pokemon, terrain);
|
||||||
applyAbAttrs(TerrainEventTypeChangeAbAttr, pokemon, null, false);
|
applyAbAttrs("TerrainEventTypeChangeAbAttr", pokemon, null, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import Pokemon from "./pokemon";
|
import Pokemon from "./pokemon";
|
||||||
import { fixedInt, randInt } from "#app/utils/common";
|
import { fixedInt, coerceArray, randInt } from "#app/utils/common";
|
||||||
|
|
||||||
export default class PokemonSpriteSparkleHandler {
|
export default class PokemonSpriteSparkleHandler {
|
||||||
private sprites: Set<Phaser.GameObjects.Sprite>;
|
private sprites: Set<Phaser.GameObjects.Sprite>;
|
||||||
@ -57,9 +57,7 @@ export default class PokemonSpriteSparkleHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
|
add(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
|
||||||
if (!Array.isArray(sprites)) {
|
sprites = coerceArray(sprites);
|
||||||
sprites = [sprites];
|
|
||||||
}
|
|
||||||
for (const s of sprites) {
|
for (const s of sprites) {
|
||||||
if (this.sprites.has(s)) {
|
if (this.sprites.has(s)) {
|
||||||
continue;
|
continue;
|
||||||
@ -69,9 +67,7 @@ export default class PokemonSpriteSparkleHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remove(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
|
remove(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
|
||||||
if (!Array.isArray(sprites)) {
|
sprites = coerceArray(sprites);
|
||||||
sprites = [sprites];
|
|
||||||
}
|
|
||||||
for (const s of sprites) {
|
for (const s of sprites) {
|
||||||
this.sprites.delete(s);
|
this.sprites.delete(s);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
type nil,
|
type nil,
|
||||||
type Constructor,
|
type Constructor,
|
||||||
randSeedIntRange,
|
randSeedIntRange,
|
||||||
|
coerceArray,
|
||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import type { TypeDamageMultiplier } from "#app/data/type";
|
import type { TypeDamageMultiplier } from "#app/data/type";
|
||||||
import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
|
import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
|
||||||
@ -111,61 +112,23 @@ import { WeatherType } from "#enums/weather-type";
|
|||||||
import { NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
import { NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
|
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
|
||||||
import type { Ability } from "#app/data/abilities/ability-class";
|
import type { Ability } from "#app/data/abilities/ability";
|
||||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
|
||||||
import {
|
import {
|
||||||
StatMultiplierAbAttr,
|
|
||||||
BlockCritAbAttr,
|
|
||||||
BonusCritAbAttr,
|
|
||||||
BypassBurnDamageReductionAbAttr,
|
|
||||||
FieldPriorityMoveImmunityAbAttr,
|
|
||||||
IgnoreOpponentStatStagesAbAttr,
|
|
||||||
MoveImmunityAbAttr,
|
|
||||||
PreDefendFullHpEndureAbAttr,
|
|
||||||
ReceivedMoveDamageMultiplierAbAttr,
|
|
||||||
StabBoostAbAttr,
|
|
||||||
StatusEffectImmunityAbAttr,
|
|
||||||
TypeImmunityAbAttr,
|
|
||||||
WeightMultiplierAbAttr,
|
|
||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
applyStatMultiplierAbAttrs,
|
applyStatMultiplierAbAttrs,
|
||||||
applyPreApplyBattlerTagAbAttrs,
|
applyPreApplyBattlerTagAbAttrs,
|
||||||
applyPreAttackAbAttrs,
|
applyPreAttackAbAttrs,
|
||||||
applyPreDefendAbAttrs,
|
applyPreDefendAbAttrs,
|
||||||
applyPreSetStatusAbAttrs,
|
applyPreSetStatusAbAttrs,
|
||||||
NoFusionAbilityAbAttr,
|
|
||||||
MultCritAbAttr,
|
|
||||||
IgnoreTypeImmunityAbAttr,
|
|
||||||
DamageBoostAbAttr,
|
|
||||||
IgnoreTypeStatusEffectImmunityAbAttr,
|
|
||||||
ConditionalCritAbAttr,
|
|
||||||
applyFieldStatMultiplierAbAttrs,
|
applyFieldStatMultiplierAbAttrs,
|
||||||
FieldMultiplyStatAbAttr,
|
|
||||||
AddSecondStrikeAbAttr,
|
|
||||||
UserFieldStatusEffectImmunityAbAttr,
|
|
||||||
UserFieldBattlerTagImmunityAbAttr,
|
|
||||||
BattlerTagImmunityAbAttr,
|
|
||||||
MoveTypeChangeAbAttr,
|
|
||||||
FullHpResistTypeAbAttr,
|
|
||||||
applyCheckTrappedAbAttrs,
|
applyCheckTrappedAbAttrs,
|
||||||
CheckTrappedAbAttr,
|
|
||||||
InfiltratorAbAttr,
|
|
||||||
AlliedFieldDamageReductionAbAttr,
|
|
||||||
PostDamageAbAttr,
|
|
||||||
applyPostDamageAbAttrs,
|
applyPostDamageAbAttrs,
|
||||||
CommanderAbAttr,
|
|
||||||
applyPostItemLostAbAttrs,
|
applyPostItemLostAbAttrs,
|
||||||
PostItemLostAbAttr,
|
|
||||||
applyOnGainAbAttrs,
|
applyOnGainAbAttrs,
|
||||||
PreLeaveFieldAbAttr,
|
|
||||||
applyPreLeaveFieldAbAttrs,
|
applyPreLeaveFieldAbAttrs,
|
||||||
applyOnLoseAbAttrs,
|
applyOnLoseAbAttrs,
|
||||||
PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr,
|
|
||||||
applyAllyStatMultiplierAbAttrs,
|
applyAllyStatMultiplierAbAttrs,
|
||||||
AllyStatMultiplierAbAttr,
|
} from "#app/data/abilities/apply-ab-attrs";
|
||||||
MoveAbilityBypassAbAttr,
|
|
||||||
PreSummonAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { allAbilities } from "#app/data/data-lists";
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
@ -192,7 +155,7 @@ import type { TrainerSlot } from "#enums/trainer-slot";
|
|||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { applyChallenges } from "#app/data/challenge";
|
import { applyChallenges } from "#app/data/challenge";
|
||||||
import { ChallengeType } from "#enums/challenge-type";
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
@ -229,6 +192,7 @@ import { HitResult } from "#enums/hit-result";
|
|||||||
import { AiType } from "#enums/ai-type";
|
import { AiType } from "#enums/ai-type";
|
||||||
import type { MoveResult } from "#enums/move-result";
|
import type { MoveResult } from "#enums/move-result";
|
||||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types";
|
||||||
|
|
||||||
/** Base typeclass for damage parameter methods, used for DRY */
|
/** Base typeclass for damage parameter methods, used for DRY */
|
||||||
type damageParams = {
|
type damageParams = {
|
||||||
@ -1403,7 +1367,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyMoveAttrs("HighCritAttr", source, this, move, critStage);
|
applyMoveAttrs("HighCritAttr", source, this, move, critStage);
|
||||||
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
||||||
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||||
applyAbAttrs(BonusCritAbAttr, source, null, false, critStage);
|
applyAbAttrs("BonusCritAbAttr", source, null, false, critStage);
|
||||||
const critBoostTag = source.getTag(CritBoostTag);
|
const critBoostTag = source.getTag(CritBoostTag);
|
||||||
if (critBoostTag) {
|
if (critBoostTag) {
|
||||||
if (critBoostTag instanceof DragonCheerTag) {
|
if (critBoostTag instanceof DragonCheerTag) {
|
||||||
@ -1464,19 +1428,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
// The Ruin abilities here are never ignored, but they reveal themselves on summon anyway
|
// The Ruin abilities here are never ignored, but they reveal themselves on summon anyway
|
||||||
const fieldApplied = new BooleanHolder(false);
|
const fieldApplied = new BooleanHolder(false);
|
||||||
for (const pokemon of globalScene.getField(true)) {
|
for (const pokemon of globalScene.getField(true)) {
|
||||||
applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied, simulated);
|
applyFieldStatMultiplierAbAttrs(
|
||||||
|
"FieldMultiplyStatAbAttr",
|
||||||
|
pokemon,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
this,
|
||||||
|
fieldApplied,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
if (fieldApplied.value) {
|
if (fieldApplied.value) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ignoreAbility) {
|
if (!ignoreAbility) {
|
||||||
applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue, simulated);
|
applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, stat, statValue, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ally = this.getAlly();
|
const ally = this.getAlly();
|
||||||
if (!isNullOrUndefined(ally)) {
|
if (!isNullOrUndefined(ally)) {
|
||||||
applyAllyStatMultiplierAbAttrs(
|
applyAllyStatMultiplierAbAttrs(
|
||||||
AllyStatMultiplierAbAttr,
|
"AllyStatMultiplierAbAttr",
|
||||||
ally,
|
ally,
|
||||||
stat,
|
stat,
|
||||||
statValue,
|
statValue,
|
||||||
@ -1803,9 +1775,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
let overrideArray: MoveId | Array<MoveId> = this.isPlayer()
|
let overrideArray: MoveId | Array<MoveId> = this.isPlayer()
|
||||||
? Overrides.MOVESET_OVERRIDE
|
? Overrides.MOVESET_OVERRIDE
|
||||||
: Overrides.OPP_MOVESET_OVERRIDE;
|
: Overrides.OPP_MOVESET_OVERRIDE;
|
||||||
if (!Array.isArray(overrideArray)) {
|
overrideArray = coerceArray(overrideArray);
|
||||||
overrideArray = [overrideArray];
|
|
||||||
}
|
|
||||||
if (overrideArray.length > 0) {
|
if (overrideArray.length > 0) {
|
||||||
if (!this.isPlayer()) {
|
if (!this.isPlayer()) {
|
||||||
this.moveset = [];
|
this.moveset = [];
|
||||||
@ -2059,15 +2029,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param ignoreOverride - Whether to ignore ability changing effects; Default `false`
|
* @param ignoreOverride - Whether to ignore ability changing effects; Default `false`
|
||||||
* @returns An array of all the ability attributes on this ability.
|
* @returns An array of all the ability attributes on this ability.
|
||||||
*/
|
*/
|
||||||
public getAbilityAttrs<T extends AbAttr = AbAttr>(
|
public getAbilityAttrs<T extends AbAttrString>(attrType: T, canApply = true, ignoreOverride = false): AbAttrMap[T][] {
|
||||||
attrType: { new (...args: any[]): T },
|
const abilityAttrs: AbAttrMap[T][] = [];
|
||||||
canApply = true,
|
|
||||||
ignoreOverride = false,
|
|
||||||
): T[] {
|
|
||||||
const abilityAttrs: T[] = [];
|
|
||||||
|
|
||||||
if (!canApply || this.canApplyAbility()) {
|
if (!canApply || this.canApplyAbility()) {
|
||||||
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs<T>(attrType));
|
abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canApply || this.canApplyAbility(true)) {
|
if (!canApply || this.canApplyAbility(true)) {
|
||||||
@ -2152,7 +2118,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ability = !passive ? this.getAbility() : this.getPassiveAbility();
|
const ability = !passive ? this.getAbility() : this.getPassiveAbility();
|
||||||
if (this.isFusion() && ability.hasAttr(NoFusionAbilityAbAttr)) {
|
if (this.isFusion() && ability.hasAttr("NoFusionAbilityAbAttr")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const arena = globalScene?.arena;
|
const arena = globalScene?.arena;
|
||||||
@ -2163,10 +2129,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
|
const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
|
||||||
const suppressOffField = ability.hasAttr(PreSummonAbAttr);
|
const suppressOffField = ability.hasAttr("PreSummonAbAttr");
|
||||||
if ((this.isOnField() || suppressOffField) && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) {
|
if ((this.isOnField() || suppressOffField) && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) {
|
||||||
const thisAbilitySuppressing = ability.hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr);
|
const thisAbilitySuppressing = ability.hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr");
|
||||||
const hasSuppressingAbility = this.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false);
|
const hasSuppressingAbility = this.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false);
|
||||||
// Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas
|
// Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas
|
||||||
// (Balance decided that the other ability of a neutralizing gas pokemon should not be neutralized)
|
// (Balance decided that the other ability of a neutralizing gas pokemon should not be neutralized)
|
||||||
// If the ability itself is neutralizing gas, don't suppress it (handled through arena tag)
|
// If the ability itself is neutralizing gas, don't suppress it (handled through arena tag)
|
||||||
@ -2207,13 +2173,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param ignoreOverride Whether to ignore ability changing effects; default `false`
|
* @param ignoreOverride Whether to ignore ability changing effects; default `false`
|
||||||
* @returns `true` if an ability with the given {@linkcode AbAttr} is present and active
|
* @returns `true` if an ability with the given {@linkcode AbAttr} is present and active
|
||||||
*/
|
*/
|
||||||
public hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply = true, ignoreOverride = false): boolean {
|
public hasAbilityWithAttr(attrType: AbAttrString, canApply = true, ignoreOverride = false): boolean {
|
||||||
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) {
|
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType);
|
return this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAbilityPriorities(): [number, number] {
|
||||||
|
return [this.getAbility().postSummonPriority, this.getPassiveAbility().postSummonPriority];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the weight of the Pokemon with subtractive modifiers (Autotomize) happening first
|
* Gets the weight of the Pokemon with subtractive modifiers (Autotomize) happening first
|
||||||
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
|
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
|
||||||
@ -2229,7 +2199,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const weight = new NumberHolder(this.species.weight - weightRemoved);
|
const weight = new NumberHolder(this.species.weight - weightRemoved);
|
||||||
|
|
||||||
// This will trigger the ability overlay so only call this function when necessary
|
// This will trigger the ability overlay so only call this function when necessary
|
||||||
applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight);
|
applyAbAttrs("WeightMultiplierAbAttr", this, null, false, weight);
|
||||||
return Math.max(minWeight, weight.value);
|
return Math.max(minWeight, weight.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2300,7 +2270,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false);
|
const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false);
|
||||||
|
|
||||||
for (const opponent of opposingField) {
|
for (const opponent of opposingField) {
|
||||||
applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated);
|
applyCheckTrappedAbAttrs("CheckTrappedAbAttr", opponent, trappedByAbility, this, trappedAbMessages, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
@ -2322,7 +2292,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const moveTypeHolder = new NumberHolder(move.type);
|
const moveTypeHolder = new NumberHolder(move.type);
|
||||||
|
|
||||||
applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder);
|
applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder);
|
||||||
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder);
|
applyPreAttackAbAttrs("MoveTypeChangeAbAttr", this, null, move, simulated, moveTypeHolder);
|
||||||
|
|
||||||
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
|
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
|
||||||
// then bypass the check for ion deluge and electrify
|
// then bypass the check for ion deluge and electrify
|
||||||
@ -2387,16 +2357,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
const cancelledHolder = cancelled ?? new BooleanHolder(false);
|
const cancelledHolder = cancelled ?? new BooleanHolder(false);
|
||||||
if (!ignoreAbility) {
|
if (!ignoreAbility) {
|
||||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier);
|
applyPreDefendAbAttrs("TypeImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier);
|
||||||
|
|
||||||
if (!cancelledHolder.value) {
|
if (!cancelledHolder.value) {
|
||||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier);
|
applyPreDefendAbAttrs("MoveImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cancelledHolder.value) {
|
if (!cancelledHolder.value) {
|
||||||
const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||||
defendingSidePlayField.forEach(p =>
|
defendingSidePlayField.forEach(p =>
|
||||||
applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelledHolder),
|
applyPreDefendAbAttrs("FieldPriorityMoveImmunityAbAttr", p, source, move, cancelledHolder),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2411,7 +2381,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
// Apply Tera Shell's effect to attacks after all immunities are accounted for
|
// Apply Tera Shell's effect to attacks after all immunities are accounted for
|
||||||
if (!ignoreAbility && move.category !== MoveCategory.STATUS) {
|
if (!ignoreAbility && move.category !== MoveCategory.STATUS) {
|
||||||
applyPreDefendAbAttrs(FullHpResistTypeAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier);
|
applyPreDefendAbAttrs("FullHpResistTypeAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move.category === MoveCategory.STATUS && move.hitsSubstitute(source, this)) {
|
if (move.category === MoveCategory.STATUS && move.hitsSubstitute(source, this)) {
|
||||||
@ -2463,8 +2433,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
if (source) {
|
if (source) {
|
||||||
const ignoreImmunity = new BooleanHolder(false);
|
const ignoreImmunity = new BooleanHolder(false);
|
||||||
if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) {
|
if (source.isActive(true) && source.hasAbilityWithAttr("IgnoreTypeImmunityAbAttr")) {
|
||||||
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, simulated, moveType, defType);
|
applyAbAttrs("IgnoreTypeImmunityAbAttr", source, ignoreImmunity, simulated, moveType, defType);
|
||||||
}
|
}
|
||||||
if (ignoreImmunity.value) {
|
if (ignoreImmunity.value) {
|
||||||
if (multiplier.value === 0) {
|
if (multiplier.value === 0) {
|
||||||
@ -3415,7 +3385,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ignoreOppAbility) {
|
if (!ignoreOppAbility) {
|
||||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage);
|
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", opponent, null, simulated, stat, ignoreStatStage);
|
||||||
}
|
}
|
||||||
if (move) {
|
if (move) {
|
||||||
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage);
|
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage);
|
||||||
@ -3454,8 +3424,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const ignoreAccStatStage = new BooleanHolder(false);
|
const ignoreAccStatStage = new BooleanHolder(false);
|
||||||
const ignoreEvaStatStage = new BooleanHolder(false);
|
const ignoreEvaStatStage = new BooleanHolder(false);
|
||||||
|
|
||||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, target, null, false, Stat.ACC, ignoreAccStatStage);
|
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", target, null, false, Stat.ACC, ignoreAccStatStage);
|
||||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, this, null, false, Stat.EVA, ignoreEvaStatStage);
|
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", this, null, false, Stat.EVA, ignoreEvaStatStage);
|
||||||
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, target, sourceMove, ignoreEvaStatStage);
|
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, target, sourceMove, ignoreEvaStatStage);
|
||||||
|
|
||||||
globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage);
|
globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage);
|
||||||
@ -3475,16 +3445,33 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
: 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6));
|
: 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, Stat.ACC, accuracyMultiplier, false, sourceMove);
|
applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, Stat.ACC, accuracyMultiplier, false, sourceMove);
|
||||||
|
|
||||||
const evasionMultiplier = new NumberHolder(1);
|
const evasionMultiplier = new NumberHolder(1);
|
||||||
applyStatMultiplierAbAttrs(StatMultiplierAbAttr, target, Stat.EVA, evasionMultiplier);
|
applyStatMultiplierAbAttrs("StatMultiplierAbAttr", target, Stat.EVA, evasionMultiplier);
|
||||||
|
|
||||||
const ally = this.getAlly();
|
const ally = this.getAlly();
|
||||||
if (!isNullOrUndefined(ally)) {
|
if (!isNullOrUndefined(ally)) {
|
||||||
const ignore = this.hasAbilityWithAttr(MoveAbilityBypassAbAttr) || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES);
|
const ignore =
|
||||||
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.ACC, accuracyMultiplier, false, this, ignore);
|
this.hasAbilityWithAttr("MoveAbilityBypassAbAttr") || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES);
|
||||||
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.EVA, evasionMultiplier, false, this, ignore);
|
applyAllyStatMultiplierAbAttrs(
|
||||||
|
"AllyStatMultiplierAbAttr",
|
||||||
|
ally,
|
||||||
|
Stat.ACC,
|
||||||
|
accuracyMultiplier,
|
||||||
|
false,
|
||||||
|
this,
|
||||||
|
ignore,
|
||||||
|
);
|
||||||
|
applyAllyStatMultiplierAbAttrs(
|
||||||
|
"AllyStatMultiplierAbAttr",
|
||||||
|
ally,
|
||||||
|
Stat.EVA,
|
||||||
|
evasionMultiplier,
|
||||||
|
false,
|
||||||
|
this,
|
||||||
|
ignore,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return accuracyMultiplier.value / evasionMultiplier.value;
|
return accuracyMultiplier.value / evasionMultiplier.value;
|
||||||
@ -3599,7 +3586,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier);
|
applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier);
|
||||||
|
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
|
applyAbAttrs("StabBoostAbAttr", source, null, simulated, stabMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.isTerastallized && sourceTeraType === moveType && moveType !== PokemonType.STELLAR) {
|
if (source.isTerastallized && sourceTeraType === moveType && moveType !== PokemonType.STELLAR) {
|
||||||
@ -3748,7 +3735,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
);
|
);
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
applyPreAttackAbAttrs(
|
applyPreAttackAbAttrs(
|
||||||
AddSecondStrikeAbAttr,
|
"AddSecondStrikeAbAttr",
|
||||||
source,
|
source,
|
||||||
this,
|
this,
|
||||||
move,
|
move,
|
||||||
@ -3766,7 +3753,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/** The damage multiplier when the given move critically hits */
|
/** The damage multiplier when the given move critically hits */
|
||||||
const criticalMultiplier = new NumberHolder(isCritical ? 1.5 : 1);
|
const criticalMultiplier = new NumberHolder(isCritical ? 1.5 : 1);
|
||||||
applyAbAttrs(MultCritAbAttr, source, null, simulated, criticalMultiplier);
|
applyAbAttrs("MultCritAbAttr", source, null, simulated, criticalMultiplier);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A multiplier for random damage spread in the range [0.85, 1]
|
* A multiplier for random damage spread in the range [0.85, 1]
|
||||||
@ -3787,7 +3774,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
) {
|
) {
|
||||||
const burnDamageReductionCancelled = new BooleanHolder(false);
|
const burnDamageReductionCancelled = new BooleanHolder(false);
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, simulated);
|
applyAbAttrs("BypassBurnDamageReductionAbAttr", source, burnDamageReductionCancelled, simulated);
|
||||||
}
|
}
|
||||||
if (!burnDamageReductionCancelled.value) {
|
if (!burnDamageReductionCancelled.value) {
|
||||||
burnMultiplier = 0.5;
|
burnMultiplier = 0.5;
|
||||||
@ -3851,7 +3838,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
|
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damage);
|
applyPreAttackAbAttrs("DamageBoostAbAttr", source, this, move, simulated, damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apply the enemy's Damage and Resistance tokens */
|
/** Apply the enemy's Damage and Resistance tokens */
|
||||||
@ -3864,12 +3851,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
|
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
|
||||||
if (!ignoreAbility) {
|
if (!ignoreAbility) {
|
||||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damage);
|
applyPreDefendAbAttrs("ReceivedMoveDamageMultiplierAbAttr", this, source, move, cancelled, simulated, damage);
|
||||||
|
|
||||||
const ally = this.getAlly();
|
const ally = this.getAlly();
|
||||||
/** Additionally apply friend guard damage reduction if ally has it. */
|
/** Additionally apply friend guard damage reduction if ally has it. */
|
||||||
if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) {
|
if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) {
|
||||||
applyPreDefendAbAttrs(AlliedFieldDamageReductionAbAttr, ally, source, move, cancelled, simulated, damage);
|
applyPreDefendAbAttrs("AlliedFieldDamageReductionAbAttr", ally, source, move, cancelled, simulated, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3877,7 +3864,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage);
|
applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage);
|
||||||
|
|
||||||
if (this.isFullHp() && !ignoreAbility) {
|
if (this.isFullHp() && !ignoreAbility) {
|
||||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
|
applyPreDefendAbAttrs("PreDefendFullHpEndureAbAttr", this, source, move, cancelled, false, damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug message for when damage is applied (i.e. not simulated)
|
// debug message for when damage is applied (i.e. not simulated)
|
||||||
@ -3919,13 +3906,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
isCritical.value = true;
|
isCritical.value = true;
|
||||||
}
|
}
|
||||||
applyMoveAttrs("CritOnlyAttr", source, this, move, isCritical);
|
applyMoveAttrs("CritOnlyAttr", source, this, move, isCritical);
|
||||||
applyAbAttrs(ConditionalCritAbAttr, source, null, simulated, isCritical, this, move);
|
applyAbAttrs("ConditionalCritAbAttr", source, null, simulated, isCritical, this, move);
|
||||||
if (!isCritical.value) {
|
if (!isCritical.value) {
|
||||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||||
isCritical.value = critChance === 1 || !globalScene.randBattleSeedInt(critChance);
|
isCritical.value = critChance === 1 || !globalScene.randBattleSeedInt(critChance);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAbAttrs(BlockCritAbAttr, this, null, simulated, isCritical);
|
applyAbAttrs("BlockCritAbAttr", this, null, simulated, isCritical);
|
||||||
|
|
||||||
return isCritical.value;
|
return isCritical.value;
|
||||||
}
|
}
|
||||||
@ -4032,7 +4019,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr
|
* Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr
|
||||||
*/
|
*/
|
||||||
if (!source || source.turnData.hitCount <= 1) {
|
if (!source || source.turnData.hitCount <= 1) {
|
||||||
applyPostDamageAbAttrs(PostDamageAbAttr, this, damage, this.hasPassive(), false, [], source);
|
applyPostDamageAbAttrs("PostDamageAbAttr", this, damage, this.hasPassive(), false, [], source);
|
||||||
}
|
}
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
@ -4080,11 +4067,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const stubTag = new BattlerTag(tagType, 0, 0);
|
const stubTag = new BattlerTag(tagType, 0, 0);
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, stubTag, cancelled, true);
|
applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, stubTag, cancelled, true);
|
||||||
|
|
||||||
const userField = this.getAlliedField();
|
const userField = this.getAlliedField();
|
||||||
userField.forEach(pokemon =>
|
userField.forEach(pokemon =>
|
||||||
applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, stubTag, cancelled, true, this),
|
applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, stubTag, cancelled, true, this),
|
||||||
);
|
);
|
||||||
|
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
@ -4100,13 +4087,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct?
|
const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct?
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, newTag, cancelled);
|
applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, newTag, cancelled);
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.getAlliedField()) {
|
for (const pokemon of this.getAlliedField()) {
|
||||||
applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, newTag, cancelled, false, this);
|
applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, newTag, cancelled, false, this);
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -4124,6 +4111,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
/**@overload */
|
/**@overload */
|
||||||
getTag(tagType: BattlerTagType.GRUDGE): GrudgeTag | nil;
|
getTag(tagType: BattlerTagType.GRUDGE): GrudgeTag | nil;
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
getTag(tagType: BattlerTagType.SUBSTITUTE): SubstituteTag | undefined;
|
||||||
|
|
||||||
/** @overload */
|
/** @overload */
|
||||||
getTag(tagType: BattlerTagType): BattlerTag | undefined;
|
getTag(tagType: BattlerTagType): BattlerTag | undefined;
|
||||||
|
|
||||||
@ -4626,7 +4616,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
// Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity
|
// Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity
|
||||||
const cancelImmunity = new BooleanHolder(false);
|
const cancelImmunity = new BooleanHolder(false);
|
||||||
if (sourcePokemon) {
|
if (sourcePokemon) {
|
||||||
applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, false, effect, defType);
|
applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", sourcePokemon, cancelImmunity, false, effect, defType);
|
||||||
if (cancelImmunity.value) {
|
if (cancelImmunity.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -4675,14 +4665,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyPreSetStatusAbAttrs(StatusEffectImmunityAbAttr, this, effect, cancelled, quiet);
|
applyPreSetStatusAbAttrs("StatusEffectImmunityAbAttr", this, effect, cancelled, quiet);
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.getAlliedField()) {
|
for (const pokemon of this.getAlliedField()) {
|
||||||
applyPreSetStatusAbAttrs(
|
applyPreSetStatusAbAttrs(
|
||||||
UserFieldStatusEffectImmunityAbAttr,
|
"UserFieldStatusEffectImmunityAbAttr",
|
||||||
pokemon,
|
pokemon,
|
||||||
effect,
|
effect,
|
||||||
cancelled,
|
cancelled,
|
||||||
@ -4839,7 +4829,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) {
|
if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) {
|
||||||
const bypassed = new BooleanHolder(false);
|
const bypassed = new BooleanHolder(false);
|
||||||
if (attacker) {
|
if (attacker) {
|
||||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||||
}
|
}
|
||||||
return !bypassed.value;
|
return !bypassed.value;
|
||||||
}
|
}
|
||||||
@ -4863,7 +4853,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
// If this Pokemon has Commander and Dondozo as an active ally, hide this Pokemon's sprite.
|
// If this Pokemon has Commander and Dondozo as an active ally, hide this Pokemon's sprite.
|
||||||
if (
|
if (
|
||||||
this.hasAbilityWithAttr(CommanderAbAttr) &&
|
this.hasAbilityWithAttr("CommanderAbAttr") &&
|
||||||
globalScene.currentBattle.double &&
|
globalScene.currentBattle.double &&
|
||||||
this.getAlly()?.species.speciesId === SpeciesId.DONDOZO
|
this.getAlly()?.species.speciesId === SpeciesId.DONDOZO
|
||||||
) {
|
) {
|
||||||
@ -5388,7 +5378,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.hideInfo();
|
this.hideInfo();
|
||||||
}
|
}
|
||||||
// Trigger abilities that activate upon leaving the field
|
// Trigger abilities that activate upon leaving the field
|
||||||
applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, this);
|
applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", this);
|
||||||
this.setSwitchOutStatus(true);
|
this.setSwitchOutStatus(true);
|
||||||
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true);
|
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true);
|
||||||
globalScene.field.remove(this, destroy);
|
globalScene.field.remove(this, destroy);
|
||||||
@ -5448,7 +5438,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
globalScene.removeModifier(heldItem, this.isEnemy());
|
globalScene.removeModifier(heldItem, this.isEnemy());
|
||||||
}
|
}
|
||||||
if (forBattle) {
|
if (forBattle) {
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, this, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -21,6 +21,8 @@ import { initVouchers } from "#app/system/voucher";
|
|||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { timedEventManager } from "./global-event-manager";
|
import { timedEventManager } from "./global-event-manager";
|
||||||
|
import { initModifierPools } from "./modifier/init-modifier-pools";
|
||||||
|
import { initModifierTypes } from "./modifier/modifier-type";
|
||||||
|
|
||||||
export class LoadingScene extends SceneBase {
|
export class LoadingScene extends SceneBase {
|
||||||
public static readonly KEY = "loading";
|
public static readonly KEY = "loading";
|
||||||
@ -363,6 +365,9 @@ export class LoadingScene extends SceneBase {
|
|||||||
|
|
||||||
this.loadLoadingScreen();
|
this.loadLoadingScreen();
|
||||||
|
|
||||||
|
initModifierTypes();
|
||||||
|
initModifierPools();
|
||||||
|
|
||||||
initAchievements();
|
initAchievements();
|
||||||
initVouchers();
|
initVouchers();
|
||||||
initStatsKeys();
|
initStatsKeys();
|
||||||
|
854
src/modifier/init-modifier-pools.ts
Normal file
@ -0,0 +1,854 @@
|
|||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import {
|
||||||
|
dailyStarterModifierPool,
|
||||||
|
enemyBuffModifierPool,
|
||||||
|
modifierPool,
|
||||||
|
trainerModifierPool,
|
||||||
|
wildModifierPool,
|
||||||
|
} from "#app/modifier/modifier-pools";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { DoubleBattleChanceBoosterModifier, SpeciesCritBoosterModifier, TurnStatusEffectModifier } from "./modifier";
|
||||||
|
import { WeightedModifierType } from "./modifier-type";
|
||||||
|
import { ModifierTier } from "../enums/modifier-tier";
|
||||||
|
import type { WeightedModifierTypeWeightFunc } from "#app/@types/modifier-types";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
|
import { PokeballType } from "#enums/pokeball";
|
||||||
|
import { BerryModifier } from "./modifier";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
|
import { Unlockables } from "#enums/unlockables";
|
||||||
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
||||||
|
// biome-ignore lint/correctness/noUnusedImports: This is used in a tsdoc comment
|
||||||
|
import type { initModifierTypes } from "./modifier-type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the wild modifier pool
|
||||||
|
*/
|
||||||
|
function initWildModifierPool() {
|
||||||
|
wildModifierPool[ModifierTier.COMMON] = [new WeightedModifierType(modifierTypes.BERRY, 1)].map(m => {
|
||||||
|
m.setTier(ModifierTier.COMMON);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
wildModifierPool[ModifierTier.GREAT] = [new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1)].map(m => {
|
||||||
|
m.setTier(ModifierTier.GREAT);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
wildModifierPool[ModifierTier.ULTRA] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.WHITE_HERB, 0),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ULTRA);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
wildModifierPool[ModifierTier.ROGUE] = [new WeightedModifierType(modifierTypes.LUCKY_EGG, 4)].map(m => {
|
||||||
|
m.setTier(ModifierTier.ROGUE);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
wildModifierPool[ModifierTier.MASTER] = [new WeightedModifierType(modifierTypes.GOLDEN_EGG, 1)].map(m => {
|
||||||
|
m.setTier(ModifierTier.MASTER);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the common modifier pool
|
||||||
|
*/
|
||||||
|
function initCommonModifierPool() {
|
||||||
|
modifierPool[ModifierTier.COMMON] = [
|
||||||
|
new WeightedModifierType(modifierTypes.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), 6),
|
||||||
|
new WeightedModifierType(modifierTypes.RARE_CANDY, 2),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.POTION,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(p => p.getInverseHp() >= 10 && p.getHpRatio() <= 0.875 && !p.isFainted()).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount * 3;
|
||||||
|
},
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.SUPER_POTION,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(p => p.getInverseHp() >= 25 && p.getHpRatio() <= 0.75 && !p.isFainted()).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount;
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.ETHER,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
p.hp &&
|
||||||
|
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
|
||||||
|
p
|
||||||
|
.getMoveset()
|
||||||
|
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
|
||||||
|
.length,
|
||||||
|
).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount * 3;
|
||||||
|
},
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MAX_ETHER,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
p.hp &&
|
||||||
|
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
|
||||||
|
p
|
||||||
|
.getMoveset()
|
||||||
|
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
|
||||||
|
.length,
|
||||||
|
).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount;
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.LURE, lureWeightFunc(10, 2)),
|
||||||
|
new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.BERRY, 2),
|
||||||
|
new WeightedModifierType(modifierTypes.TM_COMMON, 2),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.COMMON);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the Great modifier pool
|
||||||
|
*/
|
||||||
|
function initGreatModifierPool() {
|
||||||
|
modifierPool[ModifierTier.GREAT] = [
|
||||||
|
new WeightedModifierType(modifierTypes.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6),
|
||||||
|
new WeightedModifierType(modifierTypes.PP_UP, 2),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.FULL_HEAL,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const statusEffectPartyMemberCount = Math.min(
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
p.hp &&
|
||||||
|
!!p.status &&
|
||||||
|
!p.getHeldItems().some(i => {
|
||||||
|
if (i instanceof TurnStatusEffectModifier) {
|
||||||
|
return (i as TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return statusEffectPartyMemberCount * 6;
|
||||||
|
},
|
||||||
|
18,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.REVIVE,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3);
|
||||||
|
return faintedPartyMemberCount * 9;
|
||||||
|
},
|
||||||
|
27,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MAX_REVIVE,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3);
|
||||||
|
return faintedPartyMemberCount * 3;
|
||||||
|
},
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.SACRED_ASH,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
return party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2) ? 1 : 0;
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.HYPER_POTION,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.625 && !p.isFainted()).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount * 3;
|
||||||
|
},
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MAX_POTION,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount;
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.FULL_RESTORE,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const statusEffectPartyMemberCount = Math.min(
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
p.hp &&
|
||||||
|
!!p.status &&
|
||||||
|
!p.getHeldItems().some(i => {
|
||||||
|
if (i instanceof TurnStatusEffectModifier) {
|
||||||
|
return (i as TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
const thresholdPartyMemberCount = Math.floor(
|
||||||
|
(Math.min(party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length, 3) +
|
||||||
|
statusEffectPartyMemberCount) /
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount;
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.ELIXIR,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
p.hp &&
|
||||||
|
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
|
||||||
|
p
|
||||||
|
.getMoveset()
|
||||||
|
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
|
||||||
|
.length,
|
||||||
|
).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount * 3;
|
||||||
|
},
|
||||||
|
9,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MAX_ELIXIR,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const thresholdPartyMemberCount = Math.min(
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
p.hp &&
|
||||||
|
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
|
||||||
|
p
|
||||||
|
.getMoveset()
|
||||||
|
.filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
|
||||||
|
.length,
|
||||||
|
).length,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
return thresholdPartyMemberCount;
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.DIRE_HIT, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(15, 4)),
|
||||||
|
new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)),
|
||||||
|
new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 4),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.EVOLUTION_ITEM,
|
||||||
|
() => {
|
||||||
|
return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8);
|
||||||
|
},
|
||||||
|
8,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MAP,
|
||||||
|
() => (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0),
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.SOOTHE_BELL, 2),
|
||||||
|
new WeightedModifierType(modifierTypes.TM_GREAT, 3),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MEMORY_MUSHROOM,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
if (!party.find(p => p.getLearnableLevelMoves().length)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const highestPartyLevel = party
|
||||||
|
.map(p => p.level)
|
||||||
|
.reduce((highestLevel: number, level: number) => Math.max(highestLevel, level), 1);
|
||||||
|
return Math.min(Math.ceil(highestPartyLevel / 20), 4);
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.TERA_SHARD, (party: Pokemon[]) =>
|
||||||
|
party.filter(
|
||||||
|
p =>
|
||||||
|
!(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)),
|
||||||
|
).length > 0
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.DNA_SPLICERS,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
if (party.filter(p => !p.fusionSpecies).length > 1) {
|
||||||
|
if (globalScene.gameMode.isSplicedOnly) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
if (globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.VOUCHER,
|
||||||
|
(_party: Pokemon[], rerollCount: number) => (!globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0),
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.GREAT);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the Ultra modifier pool
|
||||||
|
*/
|
||||||
|
function initUltraModifierPool() {
|
||||||
|
modifierPool[ModifierTier.ULTRA] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15),
|
||||||
|
new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(30, 4)),
|
||||||
|
new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)),
|
||||||
|
new WeightedModifierType(modifierTypes.PP_MAX, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.MINT, 4),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.RARE_EVOLUTION_ITEM,
|
||||||
|
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32),
|
||||||
|
32,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.FORM_CHANGE_ITEM,
|
||||||
|
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6,
|
||||||
|
24,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)),
|
||||||
|
new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => {
|
||||||
|
const { gameMode, gameData } = globalScene;
|
||||||
|
if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) {
|
||||||
|
return party.some(p => {
|
||||||
|
// Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd
|
||||||
|
if (
|
||||||
|
!p.isMax() &&
|
||||||
|
(p.getSpeciesForm(true).speciesId in pokemonEvolutions ||
|
||||||
|
(p.isFusion() && p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))
|
||||||
|
) {
|
||||||
|
// Check if Pokemon is already holding an Eviolite
|
||||||
|
return !p.getHeldItems().some(i => i.type.id === "EVIOLITE");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
? 10
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}),
|
||||||
|
new WeightedModifierType(modifierTypes.RARE_SPECIES_STAT_BOOSTER, 12),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.LEEK,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
const checkedSpecies = [SpeciesId.FARFETCHD, SpeciesId.GALAR_FARFETCHD, SpeciesId.SIRFETCHD];
|
||||||
|
// If a party member doesn't already have a Leek and is one of the relevant species, Leek can appear
|
||||||
|
return party.some(
|
||||||
|
p =>
|
||||||
|
!p.getHeldItems().some(i => i instanceof SpeciesCritBoosterModifier) &&
|
||||||
|
(checkedSpecies.includes(p.getSpeciesForm(true).speciesId) ||
|
||||||
|
(p.isFusion() && checkedSpecies.includes(p.getFusionSpeciesForm(true).speciesId))),
|
||||||
|
)
|
||||||
|
? 12
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
12,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.TOXIC_ORB,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
return party.some(p => {
|
||||||
|
const isHoldingOrb = p.getHeldItems().some(i => i.type.id === "FLAME_ORB" || i.type.id === "TOXIC_ORB");
|
||||||
|
|
||||||
|
if (!isHoldingOrb) {
|
||||||
|
const moveset = p
|
||||||
|
.getMoveset(true)
|
||||||
|
.filter(m => !isNullOrUndefined(m))
|
||||||
|
.map(m => m.moveId);
|
||||||
|
const canSetStatus = p.canSetStatus(StatusEffect.TOXIC, true, true, null, true);
|
||||||
|
|
||||||
|
// Moves that take advantage of obtaining the actual status effect
|
||||||
|
const hasStatusMoves = [MoveId.FACADE, MoveId.PSYCHO_SHIFT].some(m => moveset.includes(m));
|
||||||
|
// Moves that take advantage of being able to give the target a status orb
|
||||||
|
// TODO: Take moves (Trick, Fling, Switcheroo) from comment when they are implemented
|
||||||
|
const hasItemMoves = [
|
||||||
|
/* MoveId.TRICK, MoveId.FLING, MoveId.SWITCHEROO */
|
||||||
|
].some(m => moveset.includes(m));
|
||||||
|
|
||||||
|
if (canSetStatus) {
|
||||||
|
// Abilities that take advantage of obtaining the actual status effect, separated based on specificity to the orb
|
||||||
|
const hasGeneralAbility = [
|
||||||
|
AbilityId.QUICK_FEET,
|
||||||
|
AbilityId.GUTS,
|
||||||
|
AbilityId.MARVEL_SCALE,
|
||||||
|
AbilityId.MAGIC_GUARD,
|
||||||
|
].some(a => p.hasAbility(a, false, true));
|
||||||
|
const hasSpecificAbility = [AbilityId.TOXIC_BOOST, AbilityId.POISON_HEAL].some(a =>
|
||||||
|
p.hasAbility(a, false, true),
|
||||||
|
);
|
||||||
|
const hasOppositeAbility = [AbilityId.FLARE_BOOST].some(a => p.hasAbility(a, false, true));
|
||||||
|
|
||||||
|
return hasSpecificAbility || (hasGeneralAbility && !hasOppositeAbility) || hasStatusMoves;
|
||||||
|
}
|
||||||
|
return hasItemMoves;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
? 10
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.FLAME_ORB,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
return party.some(p => {
|
||||||
|
const isHoldingOrb = p.getHeldItems().some(i => i.type.id === "FLAME_ORB" || i.type.id === "TOXIC_ORB");
|
||||||
|
|
||||||
|
if (!isHoldingOrb) {
|
||||||
|
const moveset = p
|
||||||
|
.getMoveset(true)
|
||||||
|
.filter(m => !isNullOrUndefined(m))
|
||||||
|
.map(m => m.moveId);
|
||||||
|
const canSetStatus = p.canSetStatus(StatusEffect.BURN, true, true, null, true);
|
||||||
|
|
||||||
|
// Moves that take advantage of obtaining the actual status effect
|
||||||
|
const hasStatusMoves = [MoveId.FACADE, MoveId.PSYCHO_SHIFT].some(m => moveset.includes(m));
|
||||||
|
// Moves that take advantage of being able to give the target a status orb
|
||||||
|
// TODO: Take moves (Trick, Fling, Switcheroo) from comment when they are implemented
|
||||||
|
const hasItemMoves = [
|
||||||
|
/* MoveId.TRICK, MoveId.FLING, MoveId.SWITCHEROO */
|
||||||
|
].some(m => moveset.includes(m));
|
||||||
|
|
||||||
|
if (canSetStatus) {
|
||||||
|
// Abilities that take advantage of obtaining the actual status effect, separated based on specificity to the orb
|
||||||
|
const hasGeneralAbility = [
|
||||||
|
AbilityId.QUICK_FEET,
|
||||||
|
AbilityId.GUTS,
|
||||||
|
AbilityId.MARVEL_SCALE,
|
||||||
|
AbilityId.MAGIC_GUARD,
|
||||||
|
].some(a => p.hasAbility(a, false, true));
|
||||||
|
const hasSpecificAbility = [AbilityId.FLARE_BOOST].some(a => p.hasAbility(a, false, true));
|
||||||
|
const hasOppositeAbility = [AbilityId.TOXIC_BOOST, AbilityId.POISON_HEAL].some(a =>
|
||||||
|
p.hasAbility(a, false, true),
|
||||||
|
);
|
||||||
|
|
||||||
|
return hasSpecificAbility || (hasGeneralAbility && !hasOppositeAbility) || hasStatusMoves;
|
||||||
|
}
|
||||||
|
return hasItemMoves;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
? 10
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MYSTICAL_ROCK,
|
||||||
|
(party: Pokemon[]) => {
|
||||||
|
return party.some(p => {
|
||||||
|
let isHoldingMax = false;
|
||||||
|
for (const i of p.getHeldItems()) {
|
||||||
|
if (i.type.id === "MYSTICAL_ROCK") {
|
||||||
|
isHoldingMax = i.getStackCount() === i.getMaxStackCount();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isHoldingMax) {
|
||||||
|
const moveset = p.getMoveset(true).map(m => m.moveId);
|
||||||
|
|
||||||
|
const hasAbility = [
|
||||||
|
AbilityId.DROUGHT,
|
||||||
|
AbilityId.ORICHALCUM_PULSE,
|
||||||
|
AbilityId.DRIZZLE,
|
||||||
|
AbilityId.SAND_STREAM,
|
||||||
|
AbilityId.SAND_SPIT,
|
||||||
|
AbilityId.SNOW_WARNING,
|
||||||
|
AbilityId.ELECTRIC_SURGE,
|
||||||
|
AbilityId.HADRON_ENGINE,
|
||||||
|
AbilityId.PSYCHIC_SURGE,
|
||||||
|
AbilityId.GRASSY_SURGE,
|
||||||
|
AbilityId.SEED_SOWER,
|
||||||
|
AbilityId.MISTY_SURGE,
|
||||||
|
].some(a => p.hasAbility(a, false, true));
|
||||||
|
|
||||||
|
const hasMoves = [
|
||||||
|
MoveId.SUNNY_DAY,
|
||||||
|
MoveId.RAIN_DANCE,
|
||||||
|
MoveId.SANDSTORM,
|
||||||
|
MoveId.SNOWSCAPE,
|
||||||
|
MoveId.HAIL,
|
||||||
|
MoveId.CHILLY_RECEPTION,
|
||||||
|
MoveId.ELECTRIC_TERRAIN,
|
||||||
|
MoveId.PSYCHIC_TERRAIN,
|
||||||
|
MoveId.GRASSY_TERRAIN,
|
||||||
|
MoveId.MISTY_TERRAIN,
|
||||||
|
].some(m => moveset.includes(m));
|
||||||
|
|
||||||
|
return hasAbility || hasMoves;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
? 10
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.CANDY_JAR, skipInLastClassicWaveOrDefault(5)),
|
||||||
|
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 9),
|
||||||
|
new WeightedModifierType(modifierTypes.TM_ULTRA, 11),
|
||||||
|
new WeightedModifierType(modifierTypes.RARER_CANDY, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)),
|
||||||
|
new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)),
|
||||||
|
new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
|
||||||
|
new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.TERA_ORB,
|
||||||
|
() =>
|
||||||
|
!globalScene.gameMode.isClassic
|
||||||
|
? Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4)
|
||||||
|
: 0,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(modifierTypes.QUICK_CLAW, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.WIDE_LENS, 7),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ULTRA);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRogueModifierPool() {
|
||||||
|
modifierPool[ModifierTier.ROGUE] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16),
|
||||||
|
new WeightedModifierType(modifierTypes.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)),
|
||||||
|
new WeightedModifierType(modifierTypes.LEFTOVERS, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.SHELL_BELL, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.BERRY_POUCH, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.GRIP_CLAW, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.SCOPE_LENS, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.BATON, 2),
|
||||||
|
new WeightedModifierType(modifierTypes.SOUL_DEW, 7),
|
||||||
|
new WeightedModifierType(modifierTypes.CATCHING_CHARM, () => (!globalScene.gameMode.isClassic ? 4 : 0), 4),
|
||||||
|
new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)),
|
||||||
|
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.LOCK_CAPSULE, () => (globalScene.gameMode.isClassic ? 0 : 3)),
|
||||||
|
new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.RARE_FORM_CHANGE_ITEM,
|
||||||
|
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6,
|
||||||
|
24,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MEGA_BRACELET,
|
||||||
|
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9,
|
||||||
|
36,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.DYNAMAX_BAND,
|
||||||
|
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9,
|
||||||
|
36,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.VOUCHER_PLUS,
|
||||||
|
(_party: Pokemon[], rerollCount: number) =>
|
||||||
|
!globalScene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0,
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ROGUE);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the Master modifier pool
|
||||||
|
*/
|
||||||
|
function initMasterModifierPool() {
|
||||||
|
modifierPool[ModifierTier.MASTER] = [
|
||||||
|
new WeightedModifierType(modifierTypes.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), 24),
|
||||||
|
new WeightedModifierType(modifierTypes.SHINY_CHARM, 14),
|
||||||
|
new WeightedModifierType(modifierTypes.HEALING_CHARM, 18),
|
||||||
|
new WeightedModifierType(modifierTypes.MULTI_LENS, 18),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.VOUCHER_PREMIUM,
|
||||||
|
(_party: Pokemon[], rerollCount: number) =>
|
||||||
|
!globalScene.gameMode.isDaily && !globalScene.gameMode.isEndless && !globalScene.gameMode.isSplicedOnly
|
||||||
|
? Math.max(5 - rerollCount * 2, 0)
|
||||||
|
: 0,
|
||||||
|
5,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.DNA_SPLICERS,
|
||||||
|
(party: Pokemon[]) =>
|
||||||
|
!(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) &&
|
||||||
|
!globalScene.gameMode.isSplicedOnly &&
|
||||||
|
party.filter(p => !p.fusionSpecies).length > 1
|
||||||
|
? 24
|
||||||
|
: 0,
|
||||||
|
24,
|
||||||
|
),
|
||||||
|
new WeightedModifierType(
|
||||||
|
modifierTypes.MINI_BLACK_HOLE,
|
||||||
|
() =>
|
||||||
|
globalScene.gameMode.isDaily ||
|
||||||
|
(!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.MASTER);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTrainerModifierPool() {
|
||||||
|
trainerModifierPool[ModifierTier.COMMON] = [
|
||||||
|
new WeightedModifierType(modifierTypes.BERRY, 8),
|
||||||
|
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.COMMON);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
trainerModifierPool[ModifierTier.GREAT] = [new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3)].map(m => {
|
||||||
|
m.setTier(ModifierTier.GREAT);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
trainerModifierPool[ModifierTier.ULTRA] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.WHITE_HERB, 0),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ULTRA);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
trainerModifierPool[ModifierTier.ROGUE] = [
|
||||||
|
new WeightedModifierType(modifierTypes.FOCUS_BAND, 2),
|
||||||
|
new WeightedModifierType(modifierTypes.LUCKY_EGG, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.QUICK_CLAW, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.GRIP_CLAW, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.WIDE_LENS, 1),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ROGUE);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
trainerModifierPool[ModifierTier.MASTER] = [
|
||||||
|
new WeightedModifierType(modifierTypes.KINGS_ROCK, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.LEFTOVERS, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.SHELL_BELL, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.SCOPE_LENS, 1),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.MASTER);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the enemy buff modifier pool
|
||||||
|
*/
|
||||||
|
function initEnemyBuffModifierPool() {
|
||||||
|
enemyBuffModifierPool[ModifierTier.COMMON] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 9),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 9),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_ATTACK_POISON_CHANCE, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_ATTACK_PARALYZE_CHANCE, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_ATTACK_BURN_CHANCE, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 9),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 1),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.COMMON);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
enemyBuffModifierPool[ModifierTier.GREAT] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 1),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.GREAT);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
enemyBuffModifierPool[ModifierTier.ULTRA] = [
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_HEAL, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 10),
|
||||||
|
new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 5),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ULTRA);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
enemyBuffModifierPool[ModifierTier.ROGUE] = [].map((m: WeightedModifierType) => {
|
||||||
|
m.setTier(ModifierTier.ROGUE);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
enemyBuffModifierPool[ModifierTier.MASTER] = [].map((m: WeightedModifierType) => {
|
||||||
|
m.setTier(ModifierTier.MASTER);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the daily starter modifier pool
|
||||||
|
*/
|
||||||
|
function initDailyStarterModifierPool() {
|
||||||
|
dailyStarterModifierPool[ModifierTier.COMMON] = [
|
||||||
|
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.BERRY, 3),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.COMMON);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
dailyStarterModifierPool[ModifierTier.GREAT] = [new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 5)].map(
|
||||||
|
m => {
|
||||||
|
m.setTier(ModifierTier.GREAT);
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
dailyStarterModifierPool[ModifierTier.ULTRA] = [
|
||||||
|
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.SOOTHE_BELL, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.SOUL_DEW, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, 1),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ULTRA);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
dailyStarterModifierPool[ModifierTier.ROGUE] = [
|
||||||
|
new WeightedModifierType(modifierTypes.GRIP_CLAW, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.BATON, 2),
|
||||||
|
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5),
|
||||||
|
new WeightedModifierType(modifierTypes.QUICK_CLAW, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.ROGUE);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
dailyStarterModifierPool[ModifierTier.MASTER] = [
|
||||||
|
new WeightedModifierType(modifierTypes.LEFTOVERS, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.SHELL_BELL, 1),
|
||||||
|
].map(m => {
|
||||||
|
m.setTier(ModifierTier.MASTER);
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize {@linkcode modifierPool} with the initial set of modifier types.
|
||||||
|
* {@linkcode initModifierTypes} MUST be called before this function.
|
||||||
|
*/
|
||||||
|
export function initModifierPools() {
|
||||||
|
// The modifier pools the player chooses from during modifier selection
|
||||||
|
initCommonModifierPool();
|
||||||
|
initGreatModifierPool();
|
||||||
|
initUltraModifierPool();
|
||||||
|
initRogueModifierPool();
|
||||||
|
initMasterModifierPool();
|
||||||
|
|
||||||
|
// Modifier pools for specific scenarios
|
||||||
|
initWildModifierPool();
|
||||||
|
initTrainerModifierPool();
|
||||||
|
initEnemyBuffModifierPool();
|
||||||
|
initDailyStarterModifierPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High order function that returns a WeightedModifierTypeWeightFunc that will only be applied on
|
||||||
|
* classic and skip an ModifierType if current wave is greater or equal to the one passed down
|
||||||
|
* @param wave - Wave where we should stop showing the modifier
|
||||||
|
* @param defaultWeight - ModifierType default weight
|
||||||
|
* @returns A WeightedModifierTypeWeightFunc
|
||||||
|
*/
|
||||||
|
function skipInClassicAfterWave(wave: number, defaultWeight: number): WeightedModifierTypeWeightFunc {
|
||||||
|
return () => {
|
||||||
|
const gameMode = globalScene.gameMode;
|
||||||
|
const currentWave = globalScene.currentBattle.waveIndex;
|
||||||
|
return gameMode.isClassic && currentWave >= wave ? 0 : defaultWeight;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High order function that returns a WeightedModifierTypeWeightFunc that will only be applied on
|
||||||
|
* classic and it will skip a ModifierType if it is the last wave pull.
|
||||||
|
* @param defaultWeight ModifierType default weight
|
||||||
|
* @returns A WeightedModifierTypeWeightFunc
|
||||||
|
*/
|
||||||
|
function skipInLastClassicWaveOrDefault(defaultWeight: number): WeightedModifierTypeWeightFunc {
|
||||||
|
return skipInClassicAfterWave(199, defaultWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High order function that returns a WeightedModifierTypeWeightFunc to ensure Lures don't spawn on Classic 199
|
||||||
|
* or if the lure still has over 60% of its duration left
|
||||||
|
* @param maxBattles The max battles the lure type in question lasts. 10 for green, 15 for Super, 30 for Max
|
||||||
|
* @param weight The desired weight for the lure when it does spawn
|
||||||
|
* @returns A WeightedModifierTypeWeightFunc
|
||||||
|
*/
|
||||||
|
function lureWeightFunc(maxBattles: number, weight: number): WeightedModifierTypeWeightFunc {
|
||||||
|
return () => {
|
||||||
|
const lures = globalScene.getModifiers(DoubleBattleChanceBoosterModifier);
|
||||||
|
return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199) &&
|
||||||
|
(lures.length === 0 ||
|
||||||
|
lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0)
|
||||||
|
? weight
|
||||||
|
: 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to check if the player has max of a given ball type in Classic
|
||||||
|
* @param ballType The {@linkcode PokeballType} being checked
|
||||||
|
* @returns boolean: true if the player has the maximum of a given ball type
|
||||||
|
*/
|
||||||
|
function hasMaximumBalls(ballType: PokeballType): boolean {
|
||||||
|
return globalScene.gameMode.isClassic && globalScene.pokeballCounts[ballType] >= MAX_PER_TYPE_POKEBALLS;
|
||||||
|
}
|
16
src/modifier/modifier-pools.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Contains modifier pools for different contexts in the game.
|
||||||
|
* Can be safely imported without worrying about circular dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ModifierPool } from "#app/@types/modifier-types";
|
||||||
|
|
||||||
|
export const modifierPool: ModifierPool = {};
|
||||||
|
|
||||||
|
export const wildModifierPool: ModifierPool = {};
|
||||||
|
|
||||||
|
export const trainerModifierPool: ModifierPool = {};
|
||||||
|
|
||||||
|
export const enemyBuffModifierPool: ModifierPool = {};
|
||||||
|
|
||||||
|
export const dailyStarterModifierPool: ModifierPool = {};
|
@ -1,12 +1,13 @@
|
|||||||
import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
|
import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
|
||||||
import { getLevelTotalExp } from "#app/data/exp";
|
import { getLevelTotalExp } from "#app/data/exp";
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves, modifierTypes } from "#app/data/data-lists";
|
||||||
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
||||||
import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import type { FormChangeItem } from "#enums/form-change-item";
|
import type { FormChangeItem } from "#enums/form-change-item";
|
||||||
import { getStatusEffectHealText } from "#app/data/status-effect";
|
import { getStatusEffectHealText } from "#app/data/status-effect";
|
||||||
import Pokemon, { type PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { LearnMoveType } from "#enums/learn-move-type";
|
import { LearnMoveType } from "#enums/learn-move-type";
|
||||||
@ -24,33 +25,26 @@ import { type PermanentStat, type TempBattleStat, BATTLE_STATS, Stat, TEMP_BATTL
|
|||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import type { PokemonType } from "#enums/pokemon-type";
|
import type { PokemonType } from "#enums/pokemon-type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import {
|
import type {
|
||||||
type DoubleBattleChanceBoosterModifierType,
|
DoubleBattleChanceBoosterModifierType,
|
||||||
type EvolutionItemModifierType,
|
EvolutionItemModifierType,
|
||||||
type FormChangeItemModifierType,
|
FormChangeItemModifierType,
|
||||||
type ModifierOverride,
|
ModifierOverride,
|
||||||
type ModifierType,
|
ModifierType,
|
||||||
type PokemonBaseStatTotalModifierType,
|
PokemonBaseStatTotalModifierType,
|
||||||
type PokemonExpBoosterModifierType,
|
PokemonExpBoosterModifierType,
|
||||||
type PokemonFriendshipBoosterModifierType,
|
PokemonFriendshipBoosterModifierType,
|
||||||
type PokemonMoveAccuracyBoosterModifierType,
|
PokemonMoveAccuracyBoosterModifierType,
|
||||||
type PokemonMultiHitModifierType,
|
PokemonMultiHitModifierType,
|
||||||
type TerastallizeModifierType,
|
TerastallizeModifierType,
|
||||||
type TmModifierType,
|
TmModifierType,
|
||||||
getModifierType,
|
|
||||||
ModifierTypeGenerator,
|
|
||||||
modifierTypes,
|
|
||||||
PokemonHeldItemModifierType,
|
|
||||||
} from "./modifier-type";
|
} from "./modifier-type";
|
||||||
|
import { getModifierType } from "#app/utils/modifier-utils";
|
||||||
import { Color, ShadowColor } from "#enums/color";
|
import { Color, ShadowColor } from "#enums/color";
|
||||||
import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters";
|
import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters";
|
||||||
import {
|
import { applyAbAttrs, applyPostItemLostAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
applyPostItemLostAbAttrs,
|
|
||||||
CommanderAbAttr,
|
|
||||||
PostItemLostAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { ModifierInstanceMap, ModifierString } from "#app/@types/modifier-types";
|
||||||
|
|
||||||
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||||
|
|
||||||
@ -164,6 +158,23 @@ export abstract class Modifier {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this modifier is of the given class
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Used to avoid requiring the caller to have imported the specific modifier class, avoiding circular dependencies.
|
||||||
|
*
|
||||||
|
* @param modifier - The modifier to check against
|
||||||
|
* @returns Whether the modiifer is an instance of the given type
|
||||||
|
*/
|
||||||
|
public is<T extends ModifierString>(modifier: T): this is ModifierInstanceMap[T] {
|
||||||
|
const targetModifier = ModifierClassMap[modifier];
|
||||||
|
if (!targetModifier) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this instanceof targetModifier;
|
||||||
|
}
|
||||||
|
|
||||||
match(_modifier: Modifier): boolean {
|
match(_modifier: Modifier): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -188,6 +199,11 @@ export abstract class PersistentModifier extends Modifier {
|
|||||||
public stackCount: number;
|
public stackCount: number;
|
||||||
public virtualStackCount: number;
|
public virtualStackCount: number;
|
||||||
|
|
||||||
|
/** This field does not exist at runtime and must not be used.
|
||||||
|
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||||
|
*/
|
||||||
|
private declare _: never;
|
||||||
|
|
||||||
constructor(type: ModifierType, stackCount = 1) {
|
constructor(type: ModifierType, stackCount = 1) {
|
||||||
super(type);
|
super(type);
|
||||||
this.stackCount = stackCount;
|
this.stackCount = stackCount;
|
||||||
@ -1593,7 +1609,7 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier {
|
|||||||
doBypassSpeed.value = true;
|
doBypassSpeed.value = true;
|
||||||
const isCommandFight =
|
const isCommandFight =
|
||||||
globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT;
|
globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT;
|
||||||
const hasQuickClaw = this.type instanceof PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW";
|
const hasQuickClaw = this.type.is("PokemonHeldItemModifierType") && this.type.id === "QUICK_CLAW";
|
||||||
|
|
||||||
if (isCommandFight && hasQuickClaw) {
|
if (isCommandFight && hasQuickClaw) {
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
@ -1877,7 +1893,7 @@ export class BerryModifier extends PokemonHeldItemModifier {
|
|||||||
|
|
||||||
// munch the berry and trigger unburden-like effects
|
// munch the berry and trigger unburden-like effects
|
||||||
getBerryEffectFunc(this.berryType)(pokemon);
|
getBerryEffectFunc(this.berryType)(pokemon);
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", pokemon, false);
|
||||||
|
|
||||||
// Update berry eaten trackers for Belch, Harvest, Cud Chew, etc.
|
// Update berry eaten trackers for Belch, Harvest, Cud Chew, etc.
|
||||||
// Don't recover it if we proc berry pouch (no item duplication)
|
// Don't recover it if we proc berry pouch (no item duplication)
|
||||||
@ -1965,7 +1981,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
|
|||||||
// Reapply Commander on the Pokemon's side of the field, if applicable
|
// Reapply Commander on the Pokemon's side of the field, if applicable
|
||||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||||
for (const p of field) {
|
for (const p of field) {
|
||||||
applyAbAttrs(CommanderAbAttr, p, null, false);
|
applyAbAttrs("CommanderAbAttr", p, null, false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3215,7 +3231,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
|||||||
* @returns the opponents of the source {@linkcode Pokemon}
|
* @returns the opponents of the source {@linkcode Pokemon}
|
||||||
*/
|
*/
|
||||||
getTargets(pokemon?: Pokemon, ..._args: unknown[]): Pokemon[] {
|
getTargets(pokemon?: Pokemon, ..._args: unknown[]): Pokemon[] {
|
||||||
return pokemon instanceof Pokemon ? pokemon.getOpponents() : [];
|
return pokemon?.getOpponents?.() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3787,7 +3803,7 @@ export function overrideModifiers(isPlayer = true): void {
|
|||||||
const modifierFunc = modifierTypes[item.name];
|
const modifierFunc = modifierTypes[item.name];
|
||||||
let modifierType: ModifierType | null = modifierFunc();
|
let modifierType: ModifierType | null = modifierFunc();
|
||||||
|
|
||||||
if (modifierType instanceof ModifierTypeGenerator) {
|
if (modifierType.is("ModifierTypeGenerator")) {
|
||||||
const pregenArgs = "type" in item && item.type !== null ? [item.type] : undefined;
|
const pregenArgs = "type" in item && item.type !== null ? [item.type] : undefined;
|
||||||
modifierType = modifierType.generateType([], pregenArgs);
|
modifierType = modifierType.generateType([], pregenArgs);
|
||||||
}
|
}
|
||||||
@ -3829,7 +3845,7 @@ export function overrideHeldItems(pokemon: Pokemon, isPlayer = true): void {
|
|||||||
let modifierType: ModifierType | null = modifierFunc();
|
let modifierType: ModifierType | null = modifierFunc();
|
||||||
const qty = item.count || 1;
|
const qty = item.count || 1;
|
||||||
|
|
||||||
if (modifierType instanceof ModifierTypeGenerator) {
|
if (modifierType.is("ModifierTypeGenerator")) {
|
||||||
const pregenArgs = "type" in item && item.type !== null ? [item.type] : undefined;
|
const pregenArgs = "type" in item && item.type !== null ? [item.type] : undefined;
|
||||||
modifierType = modifierType.generateType([], pregenArgs);
|
modifierType = modifierType.generateType([], pregenArgs);
|
||||||
}
|
}
|
||||||
@ -3847,3 +3863,102 @@ export function overrideHeldItems(pokemon: Pokemon, isPlayer = true): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private map from modifier strings to their constructors.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Used for {@linkcode Modifier.is} to check if a modifier is of a certain type without
|
||||||
|
* requiring modifier types to be imported in every file.
|
||||||
|
*/
|
||||||
|
const ModifierClassMap = Object.freeze({
|
||||||
|
PersistentModifier,
|
||||||
|
ConsumableModifier,
|
||||||
|
AddPokeballModifier,
|
||||||
|
AddVoucherModifier,
|
||||||
|
LapsingPersistentModifier,
|
||||||
|
DoubleBattleChanceBoosterModifier,
|
||||||
|
TempStatStageBoosterModifier,
|
||||||
|
TempCritBoosterModifier,
|
||||||
|
MapModifier,
|
||||||
|
MegaEvolutionAccessModifier,
|
||||||
|
GigantamaxAccessModifier,
|
||||||
|
TerastallizeAccessModifier,
|
||||||
|
PokemonHeldItemModifier,
|
||||||
|
LapsingPokemonHeldItemModifier,
|
||||||
|
BaseStatModifier,
|
||||||
|
EvoTrackerModifier,
|
||||||
|
PokemonBaseStatTotalModifier,
|
||||||
|
PokemonBaseStatFlatModifier,
|
||||||
|
PokemonIncrementingStatModifier,
|
||||||
|
StatBoosterModifier,
|
||||||
|
SpeciesStatBoosterModifier,
|
||||||
|
CritBoosterModifier,
|
||||||
|
SpeciesCritBoosterModifier,
|
||||||
|
AttackTypeBoosterModifier,
|
||||||
|
SurviveDamageModifier,
|
||||||
|
BypassSpeedChanceModifier,
|
||||||
|
FlinchChanceModifier,
|
||||||
|
TurnHealModifier,
|
||||||
|
TurnStatusEffectModifier,
|
||||||
|
HitHealModifier,
|
||||||
|
LevelIncrementBoosterModifier,
|
||||||
|
BerryModifier,
|
||||||
|
PreserveBerryModifier,
|
||||||
|
PokemonInstantReviveModifier,
|
||||||
|
ResetNegativeStatStageModifier,
|
||||||
|
FieldEffectModifier,
|
||||||
|
ConsumablePokemonModifier,
|
||||||
|
TerrastalizeModifier,
|
||||||
|
PokemonHpRestoreModifier,
|
||||||
|
PokemonStatusHealModifier,
|
||||||
|
ConsumablePokemonMoveModifier,
|
||||||
|
PokemonPpRestoreModifier,
|
||||||
|
PokemonAllMovePpRestoreModifier,
|
||||||
|
PokemonPpUpModifier,
|
||||||
|
PokemonNatureChangeModifier,
|
||||||
|
PokemonLevelIncrementModifier,
|
||||||
|
TmModifier,
|
||||||
|
RememberMoveModifier,
|
||||||
|
EvolutionItemModifier,
|
||||||
|
FusePokemonModifier,
|
||||||
|
MultipleParticipantExpBonusModifier,
|
||||||
|
HealingBoosterModifier,
|
||||||
|
ExpBoosterModifier,
|
||||||
|
PokemonExpBoosterModifier,
|
||||||
|
ExpShareModifier,
|
||||||
|
ExpBalanceModifier,
|
||||||
|
PokemonFriendshipBoosterModifier,
|
||||||
|
PokemonNatureWeightModifier,
|
||||||
|
PokemonMoveAccuracyBoosterModifier,
|
||||||
|
PokemonMultiHitModifier,
|
||||||
|
PokemonFormChangeItemModifier,
|
||||||
|
MoneyRewardModifier,
|
||||||
|
DamageMoneyRewardModifier,
|
||||||
|
MoneyInterestModifier,
|
||||||
|
HiddenAbilityRateBoosterModifier,
|
||||||
|
ShinyRateBoosterModifier,
|
||||||
|
CriticalCatchChanceBoosterModifier,
|
||||||
|
LockModifierTiersModifier,
|
||||||
|
HealShopCostModifier,
|
||||||
|
BoostBugSpawnModifier,
|
||||||
|
SwitchEffectTransferModifier,
|
||||||
|
HeldItemTransferModifier,
|
||||||
|
TurnHeldItemTransferModifier,
|
||||||
|
ContactHeldItemTransferChanceModifier,
|
||||||
|
IvScannerModifier,
|
||||||
|
ExtraModifierModifier,
|
||||||
|
TempExtraModifierModifier,
|
||||||
|
EnemyPersistentModifier,
|
||||||
|
EnemyDamageMultiplierModifier,
|
||||||
|
EnemyDamageBoosterModifier,
|
||||||
|
EnemyDamageReducerModifier,
|
||||||
|
EnemyTurnHealModifier,
|
||||||
|
EnemyAttackStatusEffectChanceModifier,
|
||||||
|
EnemyStatusEffectHealChanceModifier,
|
||||||
|
EnemyEndureChanceModifier,
|
||||||
|
EnemyFusionChanceModifier,
|
||||||
|
MoneyMultiplierModifier,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ModifierConstructorMap = typeof ModifierClassMap;
|
||||||
|
@ -4,7 +4,7 @@ import { Gender } from "#app/data/gender";
|
|||||||
import { FormChangeItem } from "#enums/form-change-item";
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
import { type ModifierOverride } from "#app/modifier/modifier-type";
|
import { type ModifierOverride } from "#app/modifier/modifier-type";
|
||||||
import { Variant } from "#app/sprites/variant";
|
import { Variant } from "#app/sprites/variant";
|
||||||
import { Unlockables } from "#app/system/unlockables";
|
import { Unlockables } from "#enums/unlockables";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
@ -2,6 +2,7 @@ import type { Phase } from "#app/phase";
|
|||||||
import type { default as Pokemon } from "#app/field/pokemon";
|
import type { default as Pokemon } from "#app/field/pokemon";
|
||||||
import type { PhaseMap, PhaseString } from "./@types/phase-types";
|
import type { PhaseMap, PhaseString } from "./@types/phase-types";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase";
|
||||||
import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase";
|
import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase";
|
||||||
import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase";
|
import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase";
|
||||||
import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
|
import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
|
||||||
@ -11,7 +12,9 @@ import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase";
|
|||||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||||
|
import { coerceArray, type Constructor } from "#app/utils/common";
|
||||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||||
|
import type { DynamicPhaseType } from "#enums/dynamic-phase-type";
|
||||||
import { EggHatchPhase } from "#app/phases/egg-hatch-phase";
|
import { EggHatchPhase } from "#app/phases/egg-hatch-phase";
|
||||||
import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
||||||
import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
|
import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
|
||||||
@ -55,6 +58,7 @@ import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
|||||||
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
||||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||||
|
import { type PhasePriorityQueue, PostSummonPhasePriorityQueue } from "#app/data/phase-priority-queue";
|
||||||
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
||||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||||
import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
|
import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
|
||||||
@ -111,6 +115,7 @@ import { WeatherEffectPhase } from "#app/phases/weather-effect-phase";
|
|||||||
* This allows for easy creation of new phases without needing to import each phase individually.
|
* This allows for easy creation of new phases without needing to import each phase individually.
|
||||||
*/
|
*/
|
||||||
const PHASES = Object.freeze({
|
const PHASES = Object.freeze({
|
||||||
|
ActivatePriorityQueuePhase,
|
||||||
AddEnemyBuffModifierPhase,
|
AddEnemyBuffModifierPhase,
|
||||||
AttemptCapturePhase,
|
AttemptCapturePhase,
|
||||||
AttemptRunPhase,
|
AttemptRunPhase,
|
||||||
@ -222,9 +227,19 @@ export class PhaseManager {
|
|||||||
private phaseQueuePrependSpliceIndex = -1;
|
private phaseQueuePrependSpliceIndex = -1;
|
||||||
private nextCommandPhaseQueue: Phase[] = [];
|
private nextCommandPhaseQueue: Phase[] = [];
|
||||||
|
|
||||||
|
/** Storage for {@linkcode PhasePriorityQueue}s which hold phases whose order dynamically changes */
|
||||||
|
private dynamicPhaseQueues: PhasePriorityQueue[];
|
||||||
|
/** Parallel array to {@linkcode dynamicPhaseQueues} - matches phase types to their queues */
|
||||||
|
private dynamicPhaseTypes: Constructor<Phase>[];
|
||||||
|
|
||||||
private currentPhase: Phase | null = null;
|
private currentPhase: Phase | null = null;
|
||||||
private standbyPhase: Phase | null = null;
|
private standbyPhase: Phase | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.dynamicPhaseQueues = [new PostSummonPhasePriorityQueue()];
|
||||||
|
this.dynamicPhaseTypes = [PostSummonPhase];
|
||||||
|
}
|
||||||
|
|
||||||
/* Phase Functions */
|
/* Phase Functions */
|
||||||
getCurrentPhase(): Phase | null {
|
getCurrentPhase(): Phase | null {
|
||||||
return this.currentPhase;
|
return this.currentPhase;
|
||||||
@ -254,7 +269,11 @@ export class PhaseManager {
|
|||||||
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
|
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
|
||||||
*/
|
*/
|
||||||
pushPhase(phase: Phase, defer = false): void {
|
pushPhase(phase: Phase, defer = false): void {
|
||||||
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
if (this.getDynamicPhaseType(phase) !== undefined) {
|
||||||
|
this.pushDynamicPhase(phase);
|
||||||
|
} else {
|
||||||
|
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -283,6 +302,7 @@ export class PhaseManager {
|
|||||||
for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) {
|
for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) {
|
||||||
queue.splice(0, queue.length);
|
queue.splice(0, queue.length);
|
||||||
}
|
}
|
||||||
|
this.dynamicPhaseQueues.forEach(queue => queue.clear());
|
||||||
this.currentPhase = null;
|
this.currentPhase = null;
|
||||||
this.standbyPhase = null;
|
this.standbyPhase = null;
|
||||||
this.clearPhaseQueueSplice();
|
this.clearPhaseQueueSplice();
|
||||||
@ -333,8 +353,9 @@ export class PhaseManager {
|
|||||||
|
|
||||||
this.currentPhase = this.phaseQueue.shift() ?? null;
|
this.currentPhase = this.phaseQueue.shift() ?? null;
|
||||||
|
|
||||||
|
const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
|
||||||
// Check if there are any conditional phases queued
|
// Check if there are any conditional phases queued
|
||||||
if (this.conditionalQueue?.length) {
|
while (this.conditionalQueue?.length) {
|
||||||
// Retrieve the first conditional phase from the queue
|
// Retrieve the first conditional phase from the queue
|
||||||
const conditionalPhase = this.conditionalQueue.shift();
|
const conditionalPhase = this.conditionalQueue.shift();
|
||||||
// Evaluate the condition associated with the phase
|
// Evaluate the condition associated with the phase
|
||||||
@ -343,11 +364,12 @@ export class PhaseManager {
|
|||||||
this.pushPhase(conditionalPhase[1]);
|
this.pushPhase(conditionalPhase[1]);
|
||||||
} else if (conditionalPhase) {
|
} else if (conditionalPhase) {
|
||||||
// If the condition is not met, re-add the phase back to the front of the conditional queue
|
// If the condition is not met, re-add the phase back to the front of the conditional queue
|
||||||
this.conditionalQueue.unshift(conditionalPhase);
|
unactivatedConditionalPhases.push(conditionalPhase);
|
||||||
} else {
|
} else {
|
||||||
console.warn("condition phase is undefined/null!", conditionalPhase);
|
console.warn("condition phase is undefined/null!", conditionalPhase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.conditionalQueue.push(...unactivatedConditionalPhases);
|
||||||
|
|
||||||
if (this.currentPhase) {
|
if (this.currentPhase) {
|
||||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
||||||
@ -416,9 +438,7 @@ export class PhaseManager {
|
|||||||
* @returns boolean if a targetPhase was found and added
|
* @returns boolean if a targetPhase was found and added
|
||||||
*/
|
*/
|
||||||
prependToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean {
|
prependToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean {
|
||||||
if (!Array.isArray(phase)) {
|
phase = coerceArray(phase);
|
||||||
phase = [phase];
|
|
||||||
}
|
|
||||||
const target = PHASES[targetPhase];
|
const target = PHASES[targetPhase];
|
||||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target);
|
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target);
|
||||||
|
|
||||||
@ -431,17 +451,16 @@ export class PhaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()}
|
* Tries 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 phase {@linkcode Phase} the phase(s) to be added
|
||||||
* @param targetPhase - The phase to search for in phaseQueue
|
* @param targetPhase {@linkcode Phase} the type of phase to search for in {@linkcode phaseQueue}
|
||||||
|
* @param condition Condition the target phase must meet to be appended to
|
||||||
* @returns `true` if a `targetPhase` was found to append to
|
* @returns `true` if a `targetPhase` was found to append to
|
||||||
*/
|
*/
|
||||||
appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean {
|
appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString, condition?: (p: Phase) => boolean): boolean {
|
||||||
if (!Array.isArray(phase)) {
|
phase = coerceArray(phase);
|
||||||
phase = [phase];
|
|
||||||
}
|
|
||||||
const target = PHASES[targetPhase];
|
const target = PHASES[targetPhase];
|
||||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target);
|
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target && (!condition || condition(ph)));
|
||||||
|
|
||||||
if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
|
if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
|
||||||
this.phaseQueue.splice(targetIndex + 1, 0, ...phase);
|
this.phaseQueue.splice(targetIndex + 1, 0, ...phase);
|
||||||
@ -451,6 +470,68 @@ export class PhaseManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a phase and returns the matching {@linkcode DynamicPhaseType}, or undefined if it does not match one
|
||||||
|
* @param phase The phase to check
|
||||||
|
* @returns The corresponding {@linkcode DynamicPhaseType} or `undefined`
|
||||||
|
*/
|
||||||
|
public getDynamicPhaseType(phase: Phase | null): DynamicPhaseType | undefined {
|
||||||
|
let phaseType: DynamicPhaseType | undefined;
|
||||||
|
this.dynamicPhaseTypes.forEach((cls, index) => {
|
||||||
|
if (phase instanceof cls) {
|
||||||
|
phaseType = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return phaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a phase onto its corresponding dynamic queue and marks the activation point in {@linkcode phaseQueue}
|
||||||
|
*
|
||||||
|
* The {@linkcode ActivatePriorityQueuePhase} will run the top phase in the dynamic queue (not necessarily {@linkcode phase})
|
||||||
|
* @param phase The phase to push
|
||||||
|
*/
|
||||||
|
public pushDynamicPhase(phase: Phase): void {
|
||||||
|
const type = this.getDynamicPhaseType(phase);
|
||||||
|
if (type === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pushPhase(new ActivatePriorityQueuePhase(type));
|
||||||
|
this.dynamicPhaseQueues[type].push(phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unshifts the top phase from the corresponding dynamic queue onto {@linkcode phaseQueue}
|
||||||
|
* @param type {@linkcode DynamicPhaseType} The type of dynamic phase to start
|
||||||
|
*/
|
||||||
|
public startDynamicPhaseType(type: DynamicPhaseType): void {
|
||||||
|
const phase = this.dynamicPhaseQueues[type].pop();
|
||||||
|
if (phase) {
|
||||||
|
this.unshiftPhase(phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unshifts an {@linkcode ActivatePriorityQueuePhase} for {@linkcode phase}, then pushes {@linkcode phase} to its dynamic queue
|
||||||
|
*
|
||||||
|
* This is the same as {@linkcode pushDynamicPhase}, except the activation phase is unshifted
|
||||||
|
*
|
||||||
|
* {@linkcode phase} is not guaranteed to be the next phase from the queue to run (if the queue is not empty)
|
||||||
|
* @param phase The phase to add
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public startDynamicPhase(phase: Phase): void {
|
||||||
|
const type = this.getDynamicPhaseType(phase);
|
||||||
|
if (type === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unshiftPhase(new ActivatePriorityQueuePhase(type));
|
||||||
|
this.dynamicPhaseQueues[type].push(phase);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
|
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
|
||||||
* @param message - string for MessagePhase
|
* @param message - string for MessagePhase
|
||||||
@ -578,4 +659,11 @@ export class PhaseManager {
|
|||||||
): boolean {
|
): boolean {
|
||||||
return this.appendToPhase(this.create(phase, ...args), targetPhase);
|
return this.appendToPhase(this.create(phase, ...args), targetPhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public startNewDynamicPhase<T extends PhaseString>(
|
||||||
|
phase: T,
|
||||||
|
...args: ConstructorParameters<PhaseConstructorMap[T]>
|
||||||
|
): void {
|
||||||
|
this.startDynamicPhase(this.create(phase, ...args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
23
src/phases/activate-priority-queue-phase.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { DynamicPhaseType } from "#enums/dynamic-phase-type";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { Phase } from "#app/phase";
|
||||||
|
|
||||||
|
export class ActivatePriorityQueuePhase extends Phase {
|
||||||
|
public readonly phaseName = "ActivatePriorityQueuePhase";
|
||||||
|
private type: DynamicPhaseType;
|
||||||
|
|
||||||
|
constructor(type: DynamicPhaseType) {
|
||||||
|
super();
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
override start() {
|
||||||
|
super.start();
|
||||||
|
globalScene.phaseManager.startDynamicPhaseType(this.type);
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getType(): DynamicPhaseType {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import {
|
import {
|
||||||
regenerateModifierPoolThresholds,
|
regenerateModifierPoolThresholds,
|
||||||
ModifierPoolType,
|
|
||||||
getEnemyBuffModifierForWave,
|
getEnemyBuffModifierForWave,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { EnemyPersistentModifier } from "#app/modifier/modifier";
|
import { EnemyPersistentModifier } from "#app/modifier/modifier";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { applyAbAttrs, applyPreLeaveFieldAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
applyPreLeaveFieldAbAttrs,
|
|
||||||
PreLeaveFieldAbAttr,
|
|
||||||
RunSuccessAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
|
||||||
@ -30,10 +25,10 @@ export class AttemptRunPhase extends PokemonPhase {
|
|||||||
|
|
||||||
this.attemptRunAway(playerField, enemyField, escapeChance);
|
this.attemptRunAway(playerField, enemyField, escapeChance);
|
||||||
|
|
||||||
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
|
applyAbAttrs("RunSuccessAbAttr", playerPokemon, null, false, escapeChance);
|
||||||
|
|
||||||
if (playerPokemon.randBattleSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
|
if (playerPokemon.randBattleSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
|
||||||
enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, enemyPokemon));
|
enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", enemyPokemon));
|
||||||
|
|
||||||
globalScene.playSound("se/flee");
|
globalScene.playSound("se/flee");
|
||||||
globalScene.phaseManager.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
|
globalScene.phaseManager.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/abilities/ability";
|
import { applyPostBattleAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ export class BattleEndPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
|
for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
|
||||||
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon, false, this.isVictory);
|
applyPostBattleAbAttrs("PostBattleAbAttr", pokemon, false, this.isVictory);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalScene.currentBattle.moneyScattered) {
|
if (globalScene.currentBattle.moneyScattered) {
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
PreventBerryUseAbAttr,
|
|
||||||
HealFromBerryUseAbAttr,
|
|
||||||
RepeatBerryNextTurnAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { CommonAnim } from "#enums/move-anims-common";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { BerryUsedEvent } from "#app/events/battle-scene";
|
import { BerryUsedEvent } from "#app/events/battle-scene";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
@ -25,7 +20,7 @@ export class BerryPhase extends FieldPhase {
|
|||||||
|
|
||||||
this.executeForAll(pokemon => {
|
this.executeForAll(pokemon => {
|
||||||
this.eatBerries(pokemon);
|
this.eatBerries(pokemon);
|
||||||
applyAbAttrs(RepeatBerryNextTurnAbAttr, pokemon, null);
|
applyAbAttrs("RepeatBerryNextTurnAbAttr", pokemon, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
@ -47,7 +42,7 @@ export class BerryPhase extends FieldPhase {
|
|||||||
|
|
||||||
// TODO: If both opponents on field have unnerve, which one displays its message?
|
// TODO: If both opponents on field have unnerve, which one displays its message?
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
pokemon.getOpponents().forEach(opp => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
|
pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", opp, cancelled));
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("abilityTriggers:preventBerryUse", {
|
i18next.t("abilityTriggers:preventBerryUse", {
|
||||||
@ -75,6 +70,6 @@ export class BerryPhase extends FieldPhase {
|
|||||||
globalScene.updateModifiers(pokemon.isPlayer());
|
globalScene.updateModifiers(pokemon.isPlayer());
|
||||||
|
|
||||||
// AbilityId.CHEEK_POUCH only works once per round of nom noms
|
// AbilityId.CHEEK_POUCH only works once per round of nom noms
|
||||||
applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new BooleanHolder(false));
|
applyAbAttrs("HealFromBerryUseAbAttr", pokemon, new BooleanHolder(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,7 @@ import { BattlerIndex } from "#enums/battler-index";
|
|||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
import {
|
import { applyAbAttrs, applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
SyncEncounterNatureAbAttr,
|
|
||||||
applyPreSummonAbAttrs,
|
|
||||||
PreSummonAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
|
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
|
||||||
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
@ -20,7 +15,8 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { FieldPosition } from "#enums/field-position";
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
||||||
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { BattlePhase } from "#app/phases/battle-phase";
|
import { BattlePhase } from "#app/phases/battle-phase";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
@ -34,7 +30,7 @@ import { PlayerGender } from "#enums/player-gender";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/constants";
|
||||||
import { getNatureName } from "#app/data/nature";
|
import { getNatureName } from "#app/data/nature";
|
||||||
|
|
||||||
export class EncounterPhase extends BattlePhase {
|
export class EncounterPhase extends BattlePhase {
|
||||||
@ -132,7 +128,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
.slice(0, !battle.double ? 1 : 2)
|
.slice(0, !battle.double ? 1 : 2)
|
||||||
.reverse()
|
.reverse()
|
||||||
.forEach(playerPokemon => {
|
.forEach(playerPokemon => {
|
||||||
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]);
|
applyAbAttrs("SyncEncounterNatureAbAttr", playerPokemon, null, false, battle.enemyParty[e]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +249,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
if (e < (battle.double ? 2 : 1)) {
|
if (e < (battle.double ? 2 : 1)) {
|
||||||
if (battle.battleType === BattleType.WILD) {
|
if (battle.battleType === BattleType.WILD) {
|
||||||
for (const pokemon of globalScene.getField()) {
|
for (const pokemon of globalScene.getField()) {
|
||||||
applyPreSummonAbAttrs(PreSummonAbAttr, pokemon, []);
|
applyPreSummonAbAttrs("PreSummonAbAttr", pokemon, []);
|
||||||
}
|
}
|
||||||
globalScene.field.add(enemyPokemon);
|
globalScene.field.add(enemyPokemon);
|
||||||
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
|
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
|
||||||
|
@ -5,10 +5,7 @@ import {
|
|||||||
applyPostFaintAbAttrs,
|
applyPostFaintAbAttrs,
|
||||||
applyPostKnockOutAbAttrs,
|
applyPostKnockOutAbAttrs,
|
||||||
applyPostVictoryAbAttrs,
|
applyPostVictoryAbAttrs,
|
||||||
PostFaintAbAttr,
|
} from "#app/data/abilities/apply-ab-attrs";
|
||||||
PostKnockOutAbAttr,
|
|
||||||
PostVictoryAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
@ -123,7 +120,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
if (pokemon.turnData.attacksReceived?.length) {
|
if (pokemon.turnData.attacksReceived?.length) {
|
||||||
const lastAttack = pokemon.turnData.attacksReceived[0];
|
const lastAttack = pokemon.turnData.attacksReceived[0];
|
||||||
applyPostFaintAbAttrs(
|
applyPostFaintAbAttrs(
|
||||||
PostFaintAbAttr,
|
"PostFaintAbAttr",
|
||||||
pokemon,
|
pokemon,
|
||||||
globalScene.getPokemonById(lastAttack.sourceId)!,
|
globalScene.getPokemonById(lastAttack.sourceId)!,
|
||||||
new PokemonMove(lastAttack.move).getMove(),
|
new PokemonMove(lastAttack.move).getMove(),
|
||||||
@ -131,18 +128,18 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
); // TODO: is this bang correct?
|
); // TODO: is this bang correct?
|
||||||
} else {
|
} else {
|
||||||
//If killed by indirect damage, apply post-faint abilities without providing a last move
|
//If killed by indirect damage, apply post-faint abilities without providing a last move
|
||||||
applyPostFaintAbAttrs(PostFaintAbAttr, pokemon);
|
applyPostFaintAbAttrs("PostFaintAbAttr", pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alivePlayField = globalScene.getField(true);
|
const alivePlayField = globalScene.getField(true);
|
||||||
for (const p of alivePlayField) {
|
for (const p of alivePlayField) {
|
||||||
applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon);
|
applyPostKnockOutAbAttrs("PostKnockOutAbAttr", p, pokemon);
|
||||||
}
|
}
|
||||||
if (pokemon.turnData.attacksReceived?.length) {
|
if (pokemon.turnData.attacksReceived?.length) {
|
||||||
const defeatSource = this.source;
|
const defeatSource = this.source;
|
||||||
|
|
||||||
if (defeatSource?.isOnField()) {
|
if (defeatSource?.isOnField()) {
|
||||||
applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource);
|
applyPostVictoryAbAttrs("PostVictoryAbAttr", defeatSource);
|
||||||
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
||||||
const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr");
|
const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr");
|
||||||
if (pvattrs.length) {
|
if (pvattrs.length) {
|
||||||
|
@ -7,11 +7,11 @@ import type PokemonSpecies from "#app/data/pokemon-species";
|
|||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { BattlePhase } from "#app/phases/battle-phase";
|
import { BattlePhase } from "#app/phases/battle-phase";
|
||||||
import type { EndCardPhase } from "#app/phases/end-card-phase";
|
import type { EndCardPhase } from "#app/phases/end-card-phase";
|
||||||
import { achvs, ChallengeAchv } from "#app/system/achv";
|
import { achvs, ChallengeAchv } from "#app/system/achv";
|
||||||
import { Unlockables } from "#app/system/unlockables";
|
import { Unlockables } from "#enums/unlockables";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { isLocal, isLocalServerConnected } from "#app/utils/common";
|
import { isLocal, isLocalServerConnected } from "#app/utils/common";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
|
@ -12,6 +12,7 @@ import { UiMode } from "#enums/ui-mode";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
|
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import { ConfirmUiMode } from "#enums/confirm-ui-mode";
|
||||||
import { LearnMoveType } from "#enums/learn-move-type";
|
import { LearnMoveType } from "#enums/learn-move-type";
|
||||||
|
|
||||||
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
||||||
@ -163,6 +164,10 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
|||||||
globalScene.ui.setMode(this.messageMode);
|
globalScene.ui.setMode(this.messageMode);
|
||||||
this.replaceMoveCheck(move, pokemon);
|
this.replaceMoveCheck(move, pokemon);
|
||||||
},
|
},
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
ConfirmUiMode.DEFAULT_NO,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { ModifierType, ModifierTypeFunc } from "#app/modifier/modifier-type";
|
import type { ModifierType } from "#app/modifier/modifier-type";
|
||||||
import { getModifierType } from "#app/modifier/modifier-type";
|
import type { ModifierTypeFunc } from "#app/@types/modifier-types";
|
||||||
|
import { getModifierType } from "#app/utils/modifier-utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
|
|
||||||
|
@ -1,21 +1,12 @@
|
|||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import {
|
import {
|
||||||
AddSecondStrikeAbAttr,
|
|
||||||
AlwaysHitAbAttr,
|
|
||||||
applyExecutedMoveAbAttrs,
|
applyExecutedMoveAbAttrs,
|
||||||
applyPostAttackAbAttrs,
|
applyPostAttackAbAttrs,
|
||||||
applyPostDamageAbAttrs,
|
applyPostDamageAbAttrs,
|
||||||
applyPostDefendAbAttrs,
|
applyPostDefendAbAttrs,
|
||||||
applyPreAttackAbAttrs,
|
applyPreAttackAbAttrs,
|
||||||
ExecutedMoveAbAttr,
|
} from "#app/data/abilities/apply-ab-attrs";
|
||||||
IgnoreMoveEffectsAbAttr,
|
|
||||||
MaxMultiHitAbAttr,
|
|
||||||
PostAttackAbAttr,
|
|
||||||
PostDamageAbAttr,
|
|
||||||
PostDefendAbAttr,
|
|
||||||
ReflectStatusMoveAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { ConditionalProtectTag } from "#app/data/arena-tag";
|
import { ConditionalProtectTag } from "#app/data/arena-tag";
|
||||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { MoveAnim } from "#app/data/battle-anims";
|
import { MoveAnim } from "#app/data/battle-anims";
|
||||||
@ -179,7 +170,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
globalScene.phaseManager.create(
|
globalScene.phaseManager.create(
|
||||||
"ShowAbilityPhase",
|
"ShowAbilityPhase",
|
||||||
target.getBattlerIndex(),
|
target.getBattlerIndex(),
|
||||||
target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr),
|
target.getPassiveAbility().hasAttr("ReflectStatusMoveAbAttr"),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.queuedPhases.push(globalScene.phaseManager.create("HideAbilityPhase"));
|
this.queuedPhases.push(globalScene.phaseManager.create("HideAbilityPhase"));
|
||||||
@ -317,7 +308,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
// Assume single target for multi hit
|
// Assume single target for multi hit
|
||||||
applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount);
|
applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount);
|
||||||
// If Parental Bond is applicable, add another hit
|
// If Parental Bond is applicable, add another hit
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, hitCount, null);
|
applyPreAttackAbAttrs("AddSecondStrikeAbAttr", user, null, move, false, hitCount, null);
|
||||||
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
||||||
globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount);
|
globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount);
|
||||||
// Set the user's relevant turnData fields to reflect the final hit count
|
// Set the user's relevant turnData fields to reflect the final hit count
|
||||||
@ -370,7 +361,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
// Add to the move history entry
|
// Add to the move history entry
|
||||||
if (this.firstHit) {
|
if (this.firstHit) {
|
||||||
user.pushMoveHistory(this.moveHistoryEntry);
|
user.pushMoveHistory(this.moveHistoryEntry);
|
||||||
applyExecutedMoveAbAttrs(ExecutedMoveAbAttr, user);
|
applyExecutedMoveAbAttrs("ExecutedMoveAbAttr", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -434,7 +425,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @returns a `Promise` intended to be passed into a `then()` call.
|
* @returns a `Promise` intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void {
|
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void {
|
||||||
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult);
|
applyPostDefendAbAttrs("PostDefendAbAttr", target, user, this.move, hitResult);
|
||||||
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +441,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !this.move.hitsSubstitute(user, target)) {
|
if (
|
||||||
|
dealsDamage &&
|
||||||
|
!target.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") &&
|
||||||
|
!this.move.hitsSubstitute(user, target)
|
||||||
|
) {
|
||||||
const flinched = new BooleanHolder(false);
|
const flinched = new BooleanHolder(false);
|
||||||
globalScene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
globalScene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
||||||
if (flinched.value) {
|
if (flinched.value) {
|
||||||
@ -580,7 +575,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
// Strikes after the first in a multi-strike move are guaranteed to hit,
|
// Strikes after the first in a multi-strike move are guaranteed to hit,
|
||||||
// unless the move is flagged to check all hits and the user does not have Skill Link.
|
// unless the move is flagged to check all hits and the user does not have Skill Link.
|
||||||
if (user.turnData.hitsLeft < user.turnData.hitCount) {
|
if (user.turnData.hitsLeft < user.turnData.hitCount) {
|
||||||
if (!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr(MaxMultiHitAbAttr)) {
|
if (!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr("MaxMultiHitAbAttr")) {
|
||||||
return [HitCheckResult.HIT, effectiveness];
|
return [HitCheckResult.HIT, effectiveness];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -626,7 +621,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) {
|
if (user.hasAbilityWithAttr("AlwaysHitAbAttr") || target.hasAbilityWithAttr("AlwaysHitAbAttr")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.move.hasAttr("ToxicAccuracyAttr") && user.isOfType(PokemonType.POISON)) {
|
if (this.move.hasAttr("ToxicAccuracyAttr") && user.isOfType(PokemonType.POISON)) {
|
||||||
@ -789,7 +784,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
// Multi-hit check for Wimp Out/Emergency Exit
|
// Multi-hit check for Wimp Out/Emergency Exit
|
||||||
if (user.turnData.hitCount > 1) {
|
if (user.turnData.hitCount > 1) {
|
||||||
applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user);
|
applyPostDamageAbAttrs("PostDamageAbAttr", target, 0, target.hasPassive(), false, [], user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -983,7 +978,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false);
|
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false);
|
||||||
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
|
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
|
||||||
this.applyOnGetHitAbEffects(user, target, hitResult);
|
this.applyOnGetHitAbEffects(user, target, hitResult);
|
||||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move, hitResult);
|
applyPostAttackAbAttrs("PostAttackAbAttr", user, target, this.move, hitResult);
|
||||||
|
|
||||||
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
||||||
if (!user.isPlayer() && this.move.is("AttackMove")) {
|
if (!user.isPlayer() && this.move.is("AttackMove")) {
|
||||||
|
@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/abilities/ability";
|
import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
|
||||||
export class MoveEndPhase extends PokemonPhase {
|
export class MoveEndPhase extends PokemonPhase {
|
||||||
@ -30,7 +30,7 @@ export class MoveEndPhase extends PokemonPhase {
|
|||||||
// Remove effects which were set on a Pokemon which removes them on summon (i.e. via Mold Breaker)
|
// Remove effects which were set on a Pokemon which removes them on summon (i.e. via Mold Breaker)
|
||||||
for (const target of this.targets) {
|
for (const target of this.targets) {
|
||||||
if (target) {
|
if (target) {
|
||||||
applyPostSummonAbAttrs(PostSummonRemoveEffectAbAttr, target);
|
applyPostSummonAbAttrs("PostSummonRemoveEffectAbAttr", target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import {
|
import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
applyPostMoveUsedAbAttrs,
|
|
||||||
applyPreAttackAbAttrs,
|
|
||||||
BlockRedirectAbAttr,
|
|
||||||
IncreasePpAbAttr,
|
|
||||||
PokemonTypeChangeAbAttr,
|
|
||||||
PostMoveUsedAbAttr,
|
|
||||||
RedirectMoveAbAttr,
|
|
||||||
ReduceStatusEffectDurationAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import type { DelayedAttackTag } from "#app/data/arena-tag";
|
import type { DelayedAttackTag } from "#app/data/arena-tag";
|
||||||
import { CommonAnim } from "#enums/move-anims-common";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { CenterOfAttentionTag } from "#app/data/battler-tags";
|
import { CenterOfAttentionTag } from "#app/data/battler-tags";
|
||||||
@ -228,7 +218,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove());
|
applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove());
|
||||||
const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0);
|
const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0);
|
||||||
applyAbAttrs(
|
applyAbAttrs(
|
||||||
ReduceStatusEffectDurationAbAttr,
|
"ReduceStatusEffectDurationAbAttr",
|
||||||
this.pokemon,
|
this.pokemon,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
@ -396,7 +386,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
*/
|
*/
|
||||||
if (success) {
|
if (success) {
|
||||||
const move = this.move.getMove();
|
const move = this.move.getMove();
|
||||||
applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, move);
|
applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, move);
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"MoveEffectPhase",
|
"MoveEffectPhase",
|
||||||
this.pokemon.getBattlerIndex(),
|
this.pokemon.getBattlerIndex(),
|
||||||
@ -407,7 +397,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if ([MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)) {
|
if ([MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)) {
|
||||||
applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove());
|
applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pokemon.pushMoveHistory({
|
this.pokemon.pushMoveHistory({
|
||||||
@ -437,7 +427,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
// Note that the `!this.followUp` check here prevents an infinite Dancer loop.
|
// Note that the `!this.followUp` check here prevents an infinite Dancer loop.
|
||||||
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) {
|
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) {
|
||||||
globalScene.getField(true).forEach(pokemon => {
|
globalScene.getField(true).forEach(pokemon => {
|
||||||
applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets);
|
applyPostMoveUsedAbAttrs("PostMoveUsedAbAttr", pokemon, this.move, this.pokemon, this.targets);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,7 +439,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
|
|
||||||
if (move.applyConditions(this.pokemon, targets[0], move)) {
|
if (move.applyConditions(this.pokemon, targets[0], move)) {
|
||||||
// Protean and Libero apply on the charging turn of charge moves
|
// Protean and Libero apply on the charging turn of charge moves
|
||||||
applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove());
|
applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove());
|
||||||
|
|
||||||
this.showMoveText();
|
this.showMoveText();
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
@ -498,7 +488,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
public getPpIncreaseFromPressure(targets: Pokemon[]): number {
|
public getPpIncreaseFromPressure(targets: Pokemon[]): number {
|
||||||
const foesWithPressure = this.pokemon
|
const foesWithPressure = this.pokemon
|
||||||
.getOpponents()
|
.getOpponents()
|
||||||
.filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr(IncreasePpAbAttr));
|
.filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr("IncreasePpAbAttr"));
|
||||||
return foesWithPressure.length;
|
return foesWithPressure.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +506,9 @@ export class MovePhase extends BattlePhase {
|
|||||||
globalScene
|
globalScene
|
||||||
.getField(true)
|
.getField(true)
|
||||||
.filter(p => p !== this.pokemon)
|
.filter(p => p !== this.pokemon)
|
||||||
.forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget, this.pokemon));
|
.forEach(p =>
|
||||||
|
applyAbAttrs("RedirectMoveAbAttr", p, null, false, this.move.moveId, redirectTarget, this.pokemon),
|
||||||
|
);
|
||||||
|
|
||||||
/** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */
|
/** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */
|
||||||
let redirectedByAbility = currentTarget !== redirectTarget.value;
|
let redirectedByAbility = currentTarget !== redirectTarget.value;
|
||||||
@ -545,17 +537,17 @@ export class MovePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) {
|
if (this.pokemon.hasAbilityWithAttr("BlockRedirectAbAttr")) {
|
||||||
redirectTarget.value = currentTarget;
|
redirectTarget.value = currentTarget;
|
||||||
// TODO: Ability displays should be handled by the ability
|
// TODO: Ability displays should be handled by the ability
|
||||||
globalScene.phaseManager.queueAbilityDisplay(
|
globalScene.phaseManager.queueAbilityDisplay(
|
||||||
this.pokemon,
|
this.pokemon,
|
||||||
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
|
this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
globalScene.phaseManager.queueAbilityDisplay(
|
globalScene.phaseManager.queueAbilityDisplay(
|
||||||
this.pokemon,
|
this.pokemon,
|
||||||
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
|
this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { applyAbAttrs, PostBiomeChangeAbAttr } from "#app/data/abilities/ability";
|
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { getRandomWeatherType } from "#app/data/weather";
|
import { getRandomWeatherType } from "#app/data/weather";
|
||||||
import { NextEncounterPhase } from "./next-encounter-phase";
|
import { NextEncounterPhase } from "./next-encounter-phase";
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase {
|
|||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
pokemon.resetBattleAndWaveData();
|
pokemon.resetBattleAndWaveData();
|
||||||
if (pokemon.isOnField()) {
|
if (pokemon.isOnField()) {
|
||||||
applyAbAttrs(PostBiomeChangeAbAttr, pokemon, null);
|
applyAbAttrs("PostBiomeChangeAbAttr", pokemon, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilities/ability";
|
import { applyPostSetStatusAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
|
|
||||||
export class ObtainStatusEffectPhase extends PokemonPhase {
|
export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||||
@ -53,7 +53,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
|||||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true);
|
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true);
|
||||||
// If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards
|
// If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards
|
||||||
globalScene.arena.setIgnoreAbilities(false);
|
globalScene.arena.setIgnoreAbilities(false);
|
||||||
applyPostSetStatusAbAttrs(PostSetStatusAbAttr, pokemon, this.statusEffect, this.sourcePokemon);
|
applyPostSetStatusAbAttrs("PostSetStatusAbAttr", pokemon, this.statusEffect, this.sourcePokemon);
|
||||||
}
|
}
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
|
27
src/phases/post-summon-activate-ability-phase.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
|
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to {@linkcode PostSummonPhase} which applies abilities
|
||||||
|
*/
|
||||||
|
export class PostSummonActivateAbilityPhase extends PostSummonPhase {
|
||||||
|
private priority: number;
|
||||||
|
private passive: boolean;
|
||||||
|
|
||||||
|
constructor(battlerIndex: BattlerIndex, priority: number, passive: boolean) {
|
||||||
|
super(battlerIndex);
|
||||||
|
this.priority = priority;
|
||||||
|
this.passive = passive;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
applyPostSummonAbAttrs("PostSummonAbAttr", this.getPokemon(), this.passive, false);
|
||||||
|
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override getPriority() {
|
||||||
|
return this.priority;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { applyAbAttrs, applyPostSummonAbAttrs, CommanderAbAttr, PostSummonAbAttr } from "#app/data/abilities/ability";
|
|
||||||
import { ArenaTrapTag } from "#app/data/arena-tag";
|
import { ArenaTrapTag } from "#app/data/arena-tag";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags";
|
import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
|
|
||||||
export class PostSummonPhase extends PokemonPhase {
|
export class PostSummonPhase extends PokemonPhase {
|
||||||
public readonly phaseName = "PostSummonPhase";
|
public readonly phaseName = "PostSummonPhase";
|
||||||
@ -26,12 +26,15 @@ export class PostSummonPhase extends PokemonPhase {
|
|||||||
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPostSummonAbAttrs(PostSummonAbAttr, pokemon);
|
|
||||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||||
for (const p of field) {
|
for (const p of field) {
|
||||||
applyAbAttrs(CommanderAbAttr, p, null, false);
|
applyAbAttrs("CommanderAbAttr", p, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getPriority() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import { applyAbAttrs, applyPostDamageAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
applyPostDamageAbAttrs,
|
|
||||||
BlockNonDirectDamageAbAttr,
|
|
||||||
BlockStatusDamageAbAttr,
|
|
||||||
PostDamageAbAttr,
|
|
||||||
ReduceBurnDamageAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { CommonBattleAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
import { CommonAnim } from "#enums/move-anims-common";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
||||||
@ -29,8 +22,8 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||||||
if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn() && !pokemon.switchOutStatus) {
|
if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn() && !pokemon.switchOutStatus) {
|
||||||
pokemon.status.incrementTurn();
|
pokemon.status.incrementTurn();
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
applyAbAttrs(BlockStatusDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockStatusDamageAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
@ -46,14 +39,14 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||||||
break;
|
break;
|
||||||
case StatusEffect.BURN:
|
case StatusEffect.BURN:
|
||||||
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
|
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
|
||||||
applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, false, damage);
|
applyAbAttrs("ReduceBurnDamageAbAttr", pokemon, null, false, damage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (damage.value) {
|
if (damage.value) {
|
||||||
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
||||||
globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
||||||
pokemon.updateInfo();
|
pokemon.updateInfo();
|
||||||
applyPostDamageAbAttrs(PostDamageAbAttr, pokemon, damage.value, pokemon.hasPassive(), false, []);
|
applyPostDamageAbAttrs("PostDamageAbAttr", pokemon, damage.value, pokemon.hasPassive(), false, []);
|
||||||
}
|
}
|
||||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end());
|
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end());
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,12 +12,7 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
import type { MovePhase } from "./move-phase";
|
import type { MovePhase } from "./move-phase";
|
||||||
import {
|
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
ClearTerrainAbAttr,
|
|
||||||
ClearWeatherAbAttr,
|
|
||||||
PostTeraFormChangeStatChangeAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
|
|
||||||
export class QuietFormChangePhase extends BattlePhase {
|
export class QuietFormChangePhase extends BattlePhase {
|
||||||
public readonly phaseName = "QuietFormChangePhase";
|
public readonly phaseName = "QuietFormChangePhase";
|
||||||
@ -185,9 +180,9 @@ export class QuietFormChangePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) {
|
if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) {
|
||||||
applyAbAttrs(PostTeraFormChangeStatChangeAbAttr, this.pokemon, null);
|
applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", this.pokemon, null);
|
||||||
applyAbAttrs(ClearWeatherAbAttr, this.pokemon, null);
|
applyAbAttrs("ClearWeatherAbAttr", this.pokemon, null);
|
||||||
applyAbAttrs(ClearTerrainAbAttr, this.pokemon, null);
|
applyAbAttrs("ClearTerrainAbAttr", this.pokemon, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.end();
|
super.end();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
|
import type { ModifierTypeFunc } from "#app/@types/modifier-types";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { ModifierRewardPhase } from "./modifier-reward-phase";
|
import { ModifierRewardPhase } from "./modifier-reward-phase";
|
||||||
|