Added overloads to modifier functions, cleaned up existing uses theroef

This commit is contained in:
Bertie690 2025-06-05 23:02:55 -04:00
parent 8cf8c854ce
commit e089fd28f2
24 changed files with 126 additions and 139 deletions

View File

@ -0,0 +1,4 @@
import type { Modifier } from "../modifier/modifier";
export type ModifierPredicate = (modifier: Modifier) => boolean;
export type ModifierIdentityPredicate<T extends Modifier> = (modifier: Modifier) => modifier is T;

View File

@ -19,12 +19,8 @@ import {
type Constructor, type Constructor,
} from "#app/utils/common"; } from "#app/utils/common";
import { deepMergeSpriteData } from "#app/utils/data"; import { deepMergeSpriteData } from "#app/utils/data";
import type { import type { Modifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
Modifier, import type { ModifierIdentityPredicate, ModifierPredicate } from "./@types/modifier-predicate";
ModifierIdentityPredicate,
ModifierPredicate,
TurnHeldItemTransferModifier,
} from "./modifier/modifier";
import { import {
ConsumableModifier, ConsumableModifier,
ConsumablePokemonModifier, ConsumablePokemonModifier,
@ -2125,9 +2121,10 @@ export default class BattleScene extends SceneBase {
enemy.getSpeciesForm().getBaseExp() * enemy.getSpeciesForm().getBaseExp() *
(enemy.level / this.getMaxExpLevel()) * (enemy.level / this.getMaxExpLevel()) *
((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8); ((enemy.ivs.reduce((iv: number, total: number) => (total += iv), 0) / 93) * 0.2 + 0.8);
this.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemy.id, false).map( this.findModifiers(
m => (scoreIncrease *= (m as PokemonHeldItemModifier).getScoreMultiplier()), (m): m is PokemonHeldItemModifier => m instanceof PokemonHeldItemModifier && m.pokemonId === enemy.id,
); false,
).forEach(m => (scoreIncrease *= m.getScoreMultiplier()));
if (enemy.isBoss()) { if (enemy.isBoss()) {
scoreIncrease *= Math.sqrt(enemy.bossSegments); scoreIncrease *= Math.sqrt(enemy.bossSegments);
} }
@ -3070,9 +3067,10 @@ export default class BattleScene extends SceneBase {
const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier; const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier;
newItemModifier.pokemonId = target.id; newItemModifier.pokemonId = target.id;
const matchingModifier = this.findModifier( const matchingModifier = this.findModifier(
m => m instanceof PokemonHeldItemModifier && m.matchType(itemModifier) && m.pokemonId === target.id, (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier && m.matchType(itemModifier) && m.pokemonId === target.id,
target.isPlayer(), target.isPlayer(),
) as PokemonHeldItemModifier; );
if (matchingModifier) { if (matchingModifier) {
const maxStackCount = matchingModifier.getMaxStackCount(); const maxStackCount = matchingModifier.getMaxStackCount();
@ -3137,9 +3135,10 @@ export default class BattleScene extends SceneBase {
} }
const matchingModifier = this.findModifier( const matchingModifier = this.findModifier(
m => m instanceof PokemonHeldItemModifier && m.matchType(mod) && m.pokemonId === target.id, (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier && m.matchType(mod) && m.pokemonId === target.id,
target.isPlayer(), target.isPlayer(),
) as PokemonHeldItemModifier; );
if (matchingModifier) { if (matchingModifier) {
const maxStackCount = matchingModifier.getMaxStackCount(); const maxStackCount = matchingModifier.getMaxStackCount();
@ -3365,28 +3364,29 @@ export default class BattleScene extends SceneBase {
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier} * @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true` * @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @returns the list of all modifiers that matched `modifierType`. * @returns the list of all modifiers that matched `modifierType`.
* @deprecated - use `findModifiers`
*/ */
getModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player = true): T[] { getModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player = true): T[] {
return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType); return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType);
} }
findModifiers<T extends PersistentModifier>(modifierFilter: ModifierIdentityPredicate<T>, isPlayer?: boolean): T[];
findModifiers(modifierFilter: ModifierPredicate, isPlayer?: boolean): PersistentModifier[];
/** /**
* Get all of the modifiers that pass the `modifierFilter` function * Get all of the modifiers that pass the `modifierFilter` function
* @param modifierFilter - The function used to filter a target's modifiers * @param modifierFilter - The function used to filter a target's modifiers
* @param isPlayer - Whether to search the player (`true`) or the enemy (`false`) party; default `true`. * @param isPlayer - Whether to search the player (`true`) or the enemy (`false`) party; default `true`.
* @returns - An array containing all modifiers that passed the `modifierFilter` function. * @returns - An array containing all modifiers that passed the `modifierFilter` function.
*/ */
findModifiers(modifierFilter: ModifierPredicate, isPlayer?: boolean): PersistentModifier[];
findModifiers<T extends PersistentModifier>(modifierFilter: ModifierIdentityPredicate<T>, isPlayer?: boolean): T[];
findModifiers(modifierFilter: ModifierPredicate, isPlayer = true): PersistentModifier[] { findModifiers(modifierFilter: ModifierPredicate, isPlayer = true): PersistentModifier[] {
return (isPlayer ? this.modifiers : this.enemyModifiers).filter(modifierFilter); return (isPlayer ? this.modifiers : this.enemyModifiers).filter(modifierFilter);
} }
findModifier(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier | undefined;
findModifier<T extends PersistentModifier>( findModifier<T extends PersistentModifier>(
modifierFilter: ModifierIdentityPredicate<T>, modifierFilter: ModifierIdentityPredicate<T>,
player?: boolean, player?: boolean,
): T | undefined; ): T | undefined;
findModifier(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier | undefined;
/** /**
* Get the first modifier that passes the `modifierFilter` function. * Get the first modifier that passes the `modifierFilter` function.
* @param modifierFilter - The function used to filter a target's modifiers. * @param modifierFilter - The function used to filter a target's modifiers.
@ -3503,13 +3503,10 @@ export default class BattleScene extends SceneBase {
let matchingFormChange: SpeciesFormChange | null; let matchingFormChange: SpeciesFormChange | null;
if (pokemon.species.speciesId === SpeciesId.NECROZMA && matchingFormChangeOpts.length > 1) { if (pokemon.species.speciesId === SpeciesId.NECROZMA && matchingFormChangeOpts.length > 1) {
// Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves. // Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves.
const formChangeItemModifiers = ( const formChangeItemModifiers = this.findModifiers(
this.findModifiers( (m): m is PokemonFormChangeItemModifier =>
m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id, m instanceof PokemonFormChangeItemModifier && m.active && m.pokemonId === pokemon.id,
) as PokemonFormChangeItemModifier[] ).map(m => m.formChangeItem);
)
.filter(m => m.active)
.map(m => m.formChangeItem);
matchingFormChange = formChangeItemModifiers.includes(FormChangeItem.N_LUNARIZER) matchingFormChange = formChangeItemModifiers.includes(FormChangeItem.N_LUNARIZER)
? matchingFormChangeOpts[0] ? matchingFormChangeOpts[0]
@ -3691,13 +3688,13 @@ export default class BattleScene extends SceneBase {
): void { ): void {
const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds; const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds;
const party = this.getPlayerParty(); const party = this.getPlayerParty();
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier);
const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier; const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier);
const multipleParticipantExpBonusModifier = this.findModifier( const multipleParticipantExpBonusModifier = this.findModifier(
m => m instanceof MultipleParticipantExpBonusModifier, m => m instanceof MultipleParticipantExpBonusModifier,
) as MultipleParticipantExpBonusModifier; );
const nonFaintedPartyMembers = party.filter(p => p.hp); const nonFaintedPartyMembers = party.filter(p => p.hp > 0);
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.getMaxExpLevel()); const expPartyMembers = party.filter(p => p.hp > 0 && p.level < this.getMaxExpLevel());
const partyMemberExp: number[] = []; const partyMemberExp: number[] = [];
// EXP value calculation is based off Pokemon.getExpValue // EXP value calculation is based off Pokemon.getExpValue
if (useWaveIndexMultiplier) { if (useWaveIndexMultiplier) {

View File

@ -178,14 +178,14 @@ 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 instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable,
false, false,
) )
.map(i => { .map(i => {
const ret = i as PokemonHeldItemModifier; // @ts-ignore - this is awful to fix/change
//@ts-ignore - this is awful to fix/change i.pokemonId = null;
ret.pokemonId = null; return i;
return ret;
}), }),
); );
} }

View File

@ -1777,8 +1777,10 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
} }
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier return globalScene.findModifiers((m): m is PokemonHeldItemModifier =>
&& m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; m instanceof PokemonHeldItemModifier
&& m.pokemonId === target.id, target.isPlayer()
);
} }
} }
@ -1904,8 +1906,10 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
} }
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier return globalScene.findModifiers((m): m is PokemonHeldItemModifier =>
&& m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; m instanceof PokemonHeldItemModifier
&& m.pokemonId === target.id, target.isPlayer()
);
} }
} }

View File

@ -815,7 +815,7 @@ export default class Move implements Localizable {
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier); applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
const sourceTeraType = source.getTeraType(); const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.hasModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
power.value = 60; power.value = 60;
} }
@ -2570,8 +2570,10 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
} }
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier return globalScene.findModifiers((m): m is PokemonHeldItemModifier =>
&& m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; m instanceof PokemonHeldItemModifier
&& m.pokemonId === target.id, target.isPlayer()
);
} }
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
@ -2652,8 +2654,10 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
} }
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier return globalScene.findModifiers((m): m is PokemonHeldItemModifier =>
&& m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; m instanceof PokemonHeldItemModifier
&& m.pokemonId === target.id, target.isPlayer()
);
} }
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
@ -2712,8 +2716,10 @@ export class EatBerryAttr extends MoveEffectAttr {
} }
getTargetHeldBerries(target: Pokemon): BerryModifier[] { getTargetHeldBerries(target: Pokemon): BerryModifier[] {
return globalScene.findModifiers(m => m instanceof BerryModifier return globalScene.findModifiers((m): m is BerryModifier =>
&& (m as BerryModifier).pokemonId === target.id, target.isPlayer()) as BerryModifier[]; m instanceof BerryModifier
&& m.pokemonId === target.id, target.isPlayer()
);
} }
reduceBerryModifier(target: Pokemon) { reduceBerryModifier(target: Pokemon) {

View File

@ -191,7 +191,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
globalScene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3"); globalScene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
// Get all player berry items, remove from party, and store reference // Get all player berry items, remove from party, and store reference
const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier);
// Sort berries by party member ID to more easily re-add later if necessary // Sort berries by party member ID to more easily re-add later if necessary
const berryItemsMap = new Map<number, BerryModifier[]>(); const berryItemsMap = new Map<number, BerryModifier[]>();
@ -256,7 +256,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
// Remove the berries from the party // Remove the berries from the party
// Session has been safely saved at this point, so data won't be lost // Session has been safely saved at this point, so data won't be lost
const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier);
berryItems.forEach(berryMod => { berryItems.forEach(berryMod => {
globalScene.removeModifier(berryMod); globalScene.removeModifier(berryMod);
}); });

View File

@ -314,12 +314,10 @@ function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
// Will try to apply to prioritized pokemon first, then do normal application method if it fails // Will try to apply to prioritized pokemon first, then do normal application method if it fails
if (prioritizedPokemon) { if (prioritizedPokemon) {
const heldBerriesOfType = globalScene.findModifier( const heldBerriesOfType = globalScene.findModifier(
m => (m): m is BerryModifier =>
m instanceof BerryModifier && m instanceof BerryModifier && m.pokemonId === prioritizedPokemon.id && m.berryType === berryType,
m.pokemonId === prioritizedPokemon.id &&
(m as BerryModifier).berryType === berryType,
true, true,
) as BerryModifier; );
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) { if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
applyModifierTypeToPlayerPokemon(prioritizedPokemon, berry); applyModifierTypeToPlayerPokemon(prioritizedPokemon, berry);
@ -330,9 +328,10 @@ function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
// Iterate over the party until berry was successfully given // Iterate over the party until berry was successfully given
for (const pokemon of party) { for (const pokemon of party) {
const heldBerriesOfType = globalScene.findModifier( const heldBerriesOfType = globalScene.findModifier(
m => m instanceof BerryModifier && m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, (m): m is BerryModifier =>
m instanceof BerryModifier && m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType,
true, true,
) as BerryModifier; );
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) { if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
applyModifierTypeToPlayerPokemon(pokemon, berry); applyModifierTypeToPlayerPokemon(pokemon, berry);

View File

@ -367,10 +367,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
const modifierOptions: ModifierTypeOption[] = [generateModifierTypeOption(modifierTypes.MASTER_BALL)!]; const modifierOptions: ModifierTypeOption[] = [generateModifierTypeOption(modifierTypes.MASTER_BALL)!];
const specialOptions: ModifierTypeOption[] = []; const specialOptions: ModifierTypeOption[] = [];
if (!globalScene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) { if (!globalScene.hasModifier(m => m instanceof MegaEvolutionAccessModifier)) {
modifierOptions.push(generateModifierTypeOption(modifierTypes.MEGA_BRACELET)!); modifierOptions.push(generateModifierTypeOption(modifierTypes.MEGA_BRACELET)!);
} }
if (!globalScene.findModifier(m => m instanceof GigantamaxAccessModifier)) { if (!globalScene.hasModifier(m => m instanceof GigantamaxAccessModifier)) {
modifierOptions.push(generateModifierTypeOption(modifierTypes.DYNAMAX_BAND)!); modifierOptions.push(generateModifierTypeOption(modifierTypes.DYNAMAX_BAND)!);
} }
const nonRareEvolutionModifier = generateModifierTypeOption(modifierTypes.EVOLUTION_ITEM); const nonRareEvolutionModifier = generateModifierTypeOption(modifierTypes.EVOLUTION_ITEM);

View File

@ -166,7 +166,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
.withOptionPhase(async () => { .withOptionPhase(async () => {
// Give the player an Amulet Coin // Give the player an Amulet Coin
// Check if the player has max stacks of that item already // Check if the player has max stacks of that item already
const existing = globalScene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier; const existing = globalScene.findModifier(m => m instanceof MoneyMultiplierModifier);
if (existing && existing.getStackCount() >= existing.getMaxStackCount()) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
@ -247,9 +247,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
// Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed // Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed
if (modifier instanceof BerryModifier) { if (modifier instanceof BerryModifier) {
// Check if the player has max stacks of that Candy Jar already // Check if the player has max stacks of that Candy Jar already
const existing = globalScene.findModifier( const existing = globalScene.findModifier(m => m instanceof LevelIncrementBoosterModifier);
m => m instanceof LevelIncrementBoosterModifier,
) as LevelIncrementBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount()) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
@ -271,7 +269,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
} }
} else { } else {
// Check if the player has max stacks of that Berry Pouch already // Check if the player has max stacks of that Berry Pouch already
const existing = globalScene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier; const existing = globalScene.findModifier(m => m instanceof PreserveBerryModifier);
if (existing && existing.getStackCount() >= existing.getMaxStackCount()) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
@ -357,7 +355,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check if the player has max stacks of Healing Charm already // Check if the player has max stacks of Healing Charm already
const existing = globalScene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier; const existing = globalScene.findModifier(m => m instanceof HealingBoosterModifier);
if (existing && existing.getStackCount() >= existing.getMaxStackCount()) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead

View File

@ -17,7 +17,8 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#app/modifier/modifier"; import { PokemonHeldItemModifier, TurnHealModifier } from "#app/modifier/modifier";
import type { HitHealModifier } from "#app/modifier/modifier";
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";
@ -231,12 +232,10 @@ async function tryApplyDigRewardItems() {
// Iterate over the party until an item was successfully given // Iterate over the party until an item was successfully given
// First leftovers // First leftovers
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = globalScene.findModifiers( const existingLeftovers = globalScene.findModifier(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id, (m): m is TurnHealModifier => m instanceof TurnHealModifier && m.pokemonId === pokemon.id,
true, true,
) as PokemonHeldItemModifier[]; );
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) { if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(pokemon, leftovers); await applyModifierTypeToPlayerPokemon(pokemon, leftovers);
break; break;
@ -245,12 +244,10 @@ async function tryApplyDigRewardItems() {
// Second leftovers // Second leftovers
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = globalScene.findModifiers( const existingLeftovers = globalScene.findModifier(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id, (m): m is TurnHealModifier => m instanceof TurnHealModifier && m.pokemonId === pokemon.id,
true, true,
) as PokemonHeldItemModifier[]; );
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) { if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(pokemon, leftovers); await applyModifierTypeToPlayerPokemon(pokemon, leftovers);
break; break;
@ -270,12 +267,10 @@ async function tryApplyDigRewardItems() {
// Only Shell bell // Only Shell bell
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = globalScene.findModifiers( const existingShellBell = globalScene.findModifier(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id, (m): m is HitHealModifier => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id,
true, true,
) as PokemonHeldItemModifier[]; );
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) { if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(pokemon, shellBell); await applyModifierTypeToPlayerPokemon(pokemon, shellBell);
break; break;

View File

@ -204,9 +204,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
// Remove 4 random berries from player's party // Remove 4 random berries from player's party
// Get all player berry items, remove from party, and store reference // Get all player berry items, remove from party, and store reference
const berryItems: BerryModifier[] = globalScene.findModifiers( const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier);
m => m instanceof BerryModifier,
) as BerryModifier[];
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
const index = randSeedInt(berryItems.length); const index = randSeedInt(berryItems.length);
const randBerry = berryItems[index]; const randBerry = berryItems[index];

View File

@ -377,10 +377,8 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
let modifierCount = 0; let modifierCount = 0;
for (const modifier of this.requiredHeldItemModifiers) { for (const modifier of this.requiredHeldItemModifiers) {
const matchingMods = globalScene.findModifiers(m => m.constructor.name === modifier); const matchingMods = globalScene.findModifiers(m => m.constructor.name === modifier);
if (matchingMods?.length > 0) { for (const matchingMod of matchingMods) {
for (const matchingMod of matchingMods) { modifierCount += matchingMod.stackCount;
modifierCount += matchingMod.stackCount;
}
} }
} }

View File

@ -404,12 +404,12 @@ export async function applyModifierTypeToPlayerPokemon(
// Check if the Pokemon has max stacks of that item already // Check if the Pokemon has max stacks of that item already
const modifier = modType.newModifier(pokemon); const modifier = modType.newModifier(pokemon);
const existing = globalScene.findModifier( const existing = globalScene.findModifier(
m => (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier && m instanceof PokemonHeldItemModifier &&
m.type.id === modType.id && m.type.id === modType.id &&
m.pokemonId === pokemon.id && m.pokemonId === pokemon.id &&
m.matchType(modifier), m.matchType(modifier),
) as PokemonHeldItemModifier; );
// At max stacks // At max stacks
if (existing && existing.getStackCount() >= existing.getMaxStackCount()) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {

View File

@ -1206,14 +1206,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.setScale(this.getSpriteScale()); this.setScale(this.getSpriteScale());
} }
getHeldItems(): PokemonHeldItemModifier[] { /**
* Return all of this Pokemon's held modifiers.
* @param transferrableOnly - Whether to only consider transferrable held items; default `false`
* @returns An array of all {@linkcode PokemonHeldItemModifier}s held by this Pokemon.
*/
getHeldItems(transferrableOnly = this.getFusionIconAtlasKey): PokemonHeldItemModifier[] {
if (!globalScene) { if (!globalScene) {
return []; return [];
} }
return globalScene.findModifiers( return globalScene.findModifiers(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === this.id, (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier && m.pokemonId === this.id && !(transferrableOnly && !m.isTransferable()),
this.isPlayer(), this.isPlayer(),
) as PokemonHeldItemModifier[]; );
} }
updateScale(): void { updateScale(): void {
@ -5872,10 +5878,7 @@ export class PlayerPokemon extends Pokemon {
globalScene.getPlayerParty().push(newPokemon); globalScene.getPlayerParty().push(newPokemon);
newPokemon.evolve(!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution), evoSpecies); newPokemon.evolve(!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution), evoSpecies);
const modifiers = globalScene.findModifiers( const modifiers = this.getHeldItems();
m => m instanceof PokemonHeldItemModifier && m.pokemonId === this.id,
true,
) as PokemonHeldItemModifier[];
modifiers.forEach(m => { modifiers.forEach(m => {
const clonedModifier = m.clone() as PokemonHeldItemModifier; const clonedModifier = m.clone() as PokemonHeldItemModifier;
clonedModifier.pokemonId = newPokemon.id; clonedModifier.pokemonId = newPokemon.id;
@ -5993,11 +5996,7 @@ export class PlayerPokemon extends Pokemon {
} }
// combine the two mons' held items // combine the two mons' held items
const fusedPartyMemberHeldModifiers = globalScene.findModifiers( for (const modifier of pokemon.getHeldItems()) {
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id,
true,
) as PokemonHeldItemModifier[];
for (const modifier of fusedPartyMemberHeldModifiers) {
globalScene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true, false); globalScene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true, false);
} }
globalScene.updateModifiers(true, true); globalScene.updateModifiers(true, true);

View File

@ -394,8 +394,9 @@ export class PokemonHeldItemModifierType extends PokemonModifierType {
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
const dummyModifier = this.newModifier(pokemon); const dummyModifier = this.newModifier(pokemon);
const matchingModifier = globalScene.findModifier( const matchingModifier = globalScene.findModifier(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(dummyModifier), (m): m is PokemonHeldItemModifier =>
) as PokemonHeldItemModifier; m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(dummyModifier),
);
const maxStackCount = dummyModifier.getMaxStackCount(); const maxStackCount = dummyModifier.getMaxStackCount();
if (!maxStackCount) { if (!maxStackCount) {
return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", { return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", {

View File

@ -54,9 +54,6 @@ import {
} from "#app/data/abilities/ability"; } from "#app/data/abilities/ability";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
export type ModifierPredicate = (modifier: Modifier) => boolean;
export type ModifierIdentityPredicate<T extends Modifier> = (modifier: Modifier) => modifier is T;
const iconOverflowIndex = 24; const iconOverflowIndex = 24;
export const modifierSortFunc = (a: Modifier, b: Modifier): number => { export const modifierSortFunc = (a: Modifier, b: Modifier): number => {
@ -3253,10 +3250,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
} }
const transferredModifierTypes: ModifierType[] = []; const transferredModifierTypes: ModifierType[] = [];
const itemModifiers = globalScene.findModifiers( const itemModifiers = targetPokemon.getHeldItems();
m => m instanceof PokemonHeldItemModifier && m.pokemonId === targetPokemon.id && m.isTransferable,
targetPokemon.isPlayer(),
) as PokemonHeldItemModifier[];
for (let i = 0; i < transferredItemCount; i++) { for (let i = 0; i < transferredItemCount; i++) {
if (!itemModifiers.length) { if (!itemModifiers.length) {

View File

@ -82,8 +82,9 @@ export class BattleEndPhase extends BattlePhase {
} }
const lapsingModifiers = globalScene.findModifiers( const lapsingModifiers = globalScene.findModifiers(
m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier, (m): m is LapsingPersistentModifier | LapsingPokemonHeldItemModifier =>
) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[]; m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier,
);
for (const m of lapsingModifiers) { for (const m of lapsingModifiers) {
const args: any[] = []; const args: any[] = [];
if (m instanceof LapsingPokemonHeldItemModifier) { if (m instanceof LapsingPokemonHeldItemModifier) {

View File

@ -40,7 +40,7 @@ export class SelectBiomePhase extends BattlePhase {
.filter(b => !Array.isArray(b) || !randSeedInt(b[1])) .filter(b => !Array.isArray(b) || !randSeedInt(b[1]))
.map(b => (!Array.isArray(b) ? b : b[0])); .map(b => (!Array.isArray(b) ? b : b[0]));
if (biomes.length > 1 && globalScene.findModifier(m => m instanceof MapModifier)) { if (biomes.length > 1 && globalScene.hasModifier(m => m instanceof MapModifier)) {
const biomeSelectItems = biomes.map(b => { const biomeSelectItems = biomes.map(b => {
const ret: OptionSelectItem = { const ret: OptionSelectItem = {
label: getBiomeName(b), label: getBiomeName(b),

View File

@ -15,12 +15,7 @@ import {
getPlayerModifierTypeOptions, getPlayerModifierTypeOptions,
} from "#app/modifier/modifier-type"; } from "#app/modifier/modifier-type";
import type { Modifier } from "#app/modifier/modifier"; import type { Modifier } from "#app/modifier/modifier";
import { import { ExtraModifierModifier, HealShopCostModifier, TempExtraModifierModifier } from "#app/modifier/modifier";
ExtraModifierModifier,
HealShopCostModifier,
PokemonHeldItemModifier,
TempExtraModifierModifier,
} from "#app/modifier/modifier";
import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
import { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler";
import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler";
@ -150,12 +145,7 @@ export class SelectModifierPhase extends BattlePhase {
fromSlotIndex !== toSlotIndex && fromSlotIndex !== toSlotIndex &&
itemIndex > -1 itemIndex > -1
) { ) {
const itemModifiers = globalScene.findModifiers( const itemModifiers = party[toSlotIndex].getHeldItems(true);
m =>
m instanceof PokemonHeldItemModifier &&
m.isTransferable &&
m.pokemonId === party[fromSlotIndex].id,
) as PokemonHeldItemModifier[];
const itemModifier = itemModifiers[itemIndex]; const itemModifier = itemModifiers[itemIndex];
globalScene.tryTransferHeldItemModifier( globalScene.tryTransferHeldItemModifier(
itemModifier, itemModifier,

View File

@ -138,7 +138,6 @@ export class SwitchSummonPhase extends SummonPhase {
return; return;
} }
if (this.switchType === SwitchType.BATON_PASS) { if (this.switchType === SwitchType.BATON_PASS) {
// If switching via baton pass, update opposing tags coming from the prior pokemon // If switching via baton pass, update opposing tags coming from the prior pokemon
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) => (this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) =>
@ -147,17 +146,17 @@ export class SwitchSummonPhase extends SummonPhase {
// If the recipient pokemon lacks a baton, give our baton to it during the swap // If the recipient pokemon lacks a baton, give our baton to it during the swap
if ( if (
!globalScene.findModifier( !globalScene.hasModifier(
m => m =>
m instanceof SwitchEffectTransferModifier && m instanceof SwitchEffectTransferModifier &&
(m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id, (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id,
) )
) { ) {
const batonPassModifier = globalScene.findModifier( const batonPassModifier = globalScene.findModifier(
m => (m): m is SwitchEffectTransferModifier =>
m instanceof SwitchEffectTransferModifier && m instanceof SwitchEffectTransferModifier &&
(m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id, (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id,
) as SwitchEffectTransferModifier; );
if (batonPassModifier) { if (batonPassModifier) {
globalScene.tryTransferHeldItemModifier( globalScene.tryTransferHeldItemModifier(

View File

@ -225,8 +225,9 @@ export default class PartyUiHandler extends MessageUiHandler {
public static FilterItemMaxStacks = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => { public static FilterItemMaxStacks = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => {
const matchingModifier = globalScene.findModifier( const matchingModifier = globalScene.findModifier(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(modifier), (m): m is PokemonHeldItemModifier =>
) as PokemonHeldItemModifier; m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(modifier),
);
if (matchingModifier && matchingModifier.stackCount === matchingModifier.getMaxStackCount()) { if (matchingModifier && matchingModifier.stackCount === matchingModifier.getMaxStackCount()) {
return i18next.t("partyUiHandler:tooManyItems", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); return i18next.t("partyUiHandler:tooManyItems", { pokemonName: getPokemonNameWithAffix(pokemon, false) });
} }
@ -552,11 +553,11 @@ export default class PartyUiHandler extends MessageUiHandler {
// this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon `p` // this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon `p`
// this returns `undefined` if the new pokemon doesn't have the item at all, otherwise it returns the `pokemonHeldItemModifier` for that item // this returns `undefined` if the new pokemon doesn't have the item at all, otherwise it returns the `pokemonHeldItemModifier` for that item
const matchingModifier = globalScene.findModifier( const matchingModifier = globalScene.findModifier(
m => (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier && m instanceof PokemonHeldItemModifier &&
m.pokemonId === newPokemon.id && m.pokemonId === newPokemon.id &&
m.matchType(this.getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor]), m.matchType(this.getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor]),
) as PokemonHeldItemModifier; );
const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us
if (p !== this.transferCursor) { if (p !== this.transferCursor) {
// this skips adding the able/not able labels on the pokemon doing the transfer // this skips adding the able/not able labels on the pokemon doing the transfer
@ -1161,9 +1162,9 @@ export default class PartyUiHandler extends MessageUiHandler {
} }
private allowBatonModifierSwitch(): boolean { private allowBatonModifierSwitch(): boolean {
return !!( return (
this.partyUiMode !== PartyUiMode.FAINT_SWITCH && this.partyUiMode !== PartyUiMode.FAINT_SWITCH &&
globalScene.findModifier( globalScene.hasModifier(
m => m =>
m instanceof SwitchEffectTransferModifier && m.pokemonId === globalScene.getPlayerField()[this.fieldIndex].id, m instanceof SwitchEffectTransferModifier && m.pokemonId === globalScene.getPlayerField()[this.fieldIndex].id,
) )

View File

@ -19,7 +19,9 @@ describe("Abilities - Harvest", () => {
let game: GameManager; let game: GameManager;
const getPlayerBerries = () => const getPlayerBerries = () =>
game.scene.getModifiers(BerryModifier, true).filter(b => b.pokemonId === game.scene.getPlayerPokemon()?.id); game.scene.findModifiers(
(b): b is BerryModifier => b instanceof BerryModifier && b.pokemonId === game.scene.getPlayerPokemon()!.id,
);
/** Check whether the player's Modifiers contains the specified berries and nothing else. */ /** Check whether the player's Modifiers contains the specified berries and nothing else. */
function expectBerriesContaining(...berries: ModifierOverride[]): void { function expectBerriesContaining(...berries: ModifierOverride[]): void {

View File

@ -64,8 +64,8 @@ describe("Items - Dire Hit", () => {
await game.phaseInterceptor.to(BattleEndPhase); await game.phaseInterceptor.to(BattleEndPhase);
const modifier = game.scene.findModifier(m => m instanceof TempCritBoosterModifier) as TempCritBoosterModifier; const modifier = game.scene.findModifier(m => m instanceof TempCritBoosterModifier);
expect(modifier.getBattleCount()).toBe(4); expect(modifier?.getBattleCount()).toBe(4);
// Forced DIRE_HIT to spawn in the first slot with override // Forced DIRE_HIT to spawn in the first slot with override
game.onNextPrompt( game.onNextPrompt(

View File

@ -157,9 +157,10 @@ describe("Absolute Avarice - Mystery Encounter", () => {
for (const partyPokemon of scene.getPlayerParty()) { for (const partyPokemon of scene.getPlayerParty()) {
const pokemonId = partyPokemon.id; const pokemonId = partyPokemon.id;
const pokemonItems = scene.findModifiers( const pokemonItems = scene.findModifiers(
m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId, (m): m is PokemonHeldItemModifier =>
m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId,
true, true,
) as PokemonHeldItemModifier[]; );
const revSeed = pokemonItems.find( const revSeed = pokemonItems.find(
i => i.type.name === i18next.t("modifierType:ModifierType.REVIVER_SEED.name"), i => i.type.name === i18next.t("modifierType:ModifierType.REVIVER_SEED.name"),
); );