Add new priority queues

This commit is contained in:
Dean 2025-06-14 17:39:52 -07:00
parent 7c6189e812
commit 5f098545f5
12 changed files with 244 additions and 126 deletions

View File

@ -79,6 +79,7 @@ import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves"; import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves";
import type { Localizable } from "#app/@types/locales"; import type { Localizable } from "#app/@types/locales";
import { applyAbAttrs } from "./apply-ab-attrs"; import { applyAbAttrs } from "./apply-ab-attrs";
import { MovePriorityModifier } from "#enums/move-priority-modifier";
export class Ability implements Localizable { export class Ability implements Localizable {
public id: AbilityId; public id: AbilityId;
@ -4978,6 +4979,31 @@ export class ChangeMovePriorityAbAttr extends AbAttr {
} }
} }
export class ChangeMovePriorityModifierAbAttr extends AbAttr {
private newModifier: MovePriorityModifier;
private moveFunc: (pokemon: Pokemon, move: Move) => boolean;
constructor(moveFunc: (pokemon: Pokemon, move: Move) => boolean, newModifier: MovePriorityModifier) {
super(false);
this.newModifier = newModifier;
this.moveFunc = moveFunc;
}
override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean {
return this.moveFunc(pokemon, args[0] as Move);
}
override apply(
_pokemon: Pokemon,
_passive: boolean,
_simulated: boolean,
_cancelled: BooleanHolder | null,
args: any[],
): void {
(args[1] as NumberHolder).value = this.newModifier;
}
}
export class IgnoreContactAbAttr extends AbAttr {} export class IgnoreContactAbAttr extends AbAttr {}
export class PreWeatherEffectAbAttr extends AbAttr { export class PreWeatherEffectAbAttr extends AbAttr {
@ -7219,7 +7245,12 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null;
const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL;
return ( return (
!simulated && !bypassSpeed.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove !simulated &&
!bypassSpeed.value &&
pokemon.randBattleSeedInt(100) < this.chance &&
isCommandFight &&
isDamageMove &&
pokemon.canAddTag(BattlerTagType.BYPASS_SPEED)
); );
} }
@ -7228,17 +7259,16 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
* @param {Pokemon} _pokemon {@linkcode Pokemon} the Pokemon applying this ability * @param {Pokemon} _pokemon {@linkcode Pokemon} the Pokemon applying this ability
* @param {boolean} _passive N/A * @param {boolean} _passive N/A
* @param {BooleanHolder} _cancelled N/A * @param {BooleanHolder} _cancelled N/A
* @param {any[]} args [0] {@linkcode BooleanHolder} set to true when the ability activated * @param {any[]} _args N/A
*/ */
override apply( override apply(
_pokemon: Pokemon, pokemon: Pokemon,
_passive: boolean, _passive: boolean,
_simulated: boolean, _simulated: boolean,
_cancelled: BooleanHolder, _cancelled: BooleanHolder,
args: any[], _args: any[],
): void { ): void {
const bypassSpeed = args[0] as BooleanHolder; pokemon.addTag(BattlerTagType.BYPASS_SPEED);
bypassSpeed.value = true;
} }
getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string {
@ -7270,7 +7300,6 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr {
/** /**
* @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment
* @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not
*/ */
override apply( override apply(
_pokemon: Pokemon, _pokemon: Pokemon,
@ -7280,9 +7309,7 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr {
args: any[], args: any[],
): void { ): void {
const bypassSpeed = args[0] as BooleanHolder; const bypassSpeed = args[0] as BooleanHolder;
const canCheckHeldItems = args[1] as BooleanHolder;
bypassSpeed.value = false; bypassSpeed.value = false;
canCheckHeldItems.value = false;
} }
} }
@ -7814,6 +7841,7 @@ const AbilityAttrs = Object.freeze({
BlockStatusDamageAbAttr, BlockStatusDamageAbAttr,
BlockOneHitKOAbAttr, BlockOneHitKOAbAttr,
ChangeMovePriorityAbAttr, ChangeMovePriorityAbAttr,
ChangeMovePriorityModifierAbAttr,
IgnoreContactAbAttr, IgnoreContactAbAttr,
PreWeatherEffectAbAttr, PreWeatherEffectAbAttr,
PreWeatherDamageAbAttr, PreWeatherDamageAbAttr,
@ -8230,7 +8258,7 @@ export function initAbilities() {
.attr(AlwaysHitAbAttr) .attr(AlwaysHitAbAttr)
.attr(DoubleBattleChanceAbAttr), .attr(DoubleBattleChanceAbAttr),
new Ability(AbilityId.STALL, 4) new Ability(AbilityId.STALL, 4)
.attr(ChangeMovePriorityAbAttr, (_pokemon, _move: Move) => true, -0.2), .attr(ChangeMovePriorityModifierAbAttr, (_pokemon, _move: Move) => true, MovePriorityModifier.LAST_IN_BRACKET),
new Ability(AbilityId.TECHNICIAN, 4) new Ability(AbilityId.TECHNICIAN, 4)
.attr(MovePowerBoostAbAttr, (user, target, move) => { .attr(MovePowerBoostAbAttr, (user, target, move) => {
const power = new NumberHolder(move.power); const power = new NumberHolder(move.power);
@ -8957,7 +8985,7 @@ export function initAbilities() {
.attr(TypeImmunityHealAbAttr, PokemonType.GROUND) .attr(TypeImmunityHealAbAttr, PokemonType.GROUND)
.ignorable(), .ignorable(),
new Ability(AbilityId.MYCELIUM_MIGHT, 9) new Ability(AbilityId.MYCELIUM_MIGHT, 9)
.attr(ChangeMovePriorityAbAttr, (_pokemon, move) => move.category === MoveCategory.STATUS, -0.2) .attr(ChangeMovePriorityModifierAbAttr, (_pokemon, move) => move.category === MoveCategory.STATUS, MovePriorityModifier.LAST_IN_BRACKET)
.attr(PreventBypassSpeedChanceAbAttr, (_pokemon, move) => move.category === MoveCategory.STATUS) .attr(PreventBypassSpeedChanceAbAttr, (_pokemon, move) => move.category === MoveCategory.STATUS)
.attr(MoveAbilityBypassAbAttr, (_pokemon, move: Move) => move.category === MoveCategory.STATUS), .attr(MoveAbilityBypassAbAttr, (_pokemon, move: Move) => move.category === MoveCategory.STATUS),
new Ability(AbilityId.MINDS_EYE, 9) new Ability(AbilityId.MINDS_EYE, 9)

View File

@ -3421,6 +3421,23 @@ export class GrudgeTag extends BattlerTag {
} }
} }
/**
* Tag to allow the affected Pokemon's move to go first in its priority bracket.
* Used for {@link https://bulbapedia.bulbagarden.net/wiki/Quick_Draw_(Ability) Quick Draw}
* and {@link https://bulbapedia.bulbagarden.net/wiki/Quick_Claw Quick Claw}.
*/
export class BypassSpeedTag extends BattlerTag {
constructor() {
super(BattlerTagType.BYPASS_SPEED, BattlerTagLapseType.TURN_END, 1);
}
override canAdd(pokemon: Pokemon): boolean {
const cancelled = new BooleanHolder(false);
applyAbAttrs("PreventBypassSpeedChanceAbAttr", pokemon, null, false, cancelled);
return !cancelled.value;
}
}
/** /**
* Tag used to heal the user of Psycho Shift of its status effect if Psycho Shift succeeds in transferring its status effect to the target Pokemon * Tag used to heal the user of Psycho Shift of its status effect if Psycho Shift succeeds in transferring its status effect to the target Pokemon
*/ */
@ -3668,6 +3685,8 @@ export function getBattlerTag(
return new PsychoShiftTag(); return new PsychoShiftTag();
case BattlerTagType.MAGIC_COAT: case BattlerTagType.MAGIC_COAT:
return new MagicCoatTag(); return new MagicCoatTag();
case BattlerTagType.BYPASS_SPEED:
return new BypassSpeedTag();
case BattlerTagType.NONE: case BattlerTagType.NONE:
default: default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -100,6 +100,7 @@ import { SelectBiomePhase } from "#app/phases/select-biome-phase";
import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types"; import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types";
import { applyMoveAttrs } from "./apply-attrs"; import { applyMoveAttrs } from "./apply-attrs";
import { frenzyMissFunc, getMoveTargets } from "./move-utils"; import { frenzyMissFunc, getMoveTargets } from "./move-utils";
import { MovePriorityModifier } from "#enums/move-priority-modifier";
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
@ -867,6 +868,13 @@ export default abstract class Move implements Localizable {
return priority.value; return priority.value;
} }
getPriorityModifier(user: Pokemon, simulated = true): MovePriorityModifier {
const modifierHolder = new NumberHolder(MovePriorityModifier.NORMAL);
applyAbAttrs("ChangeMovePriorityModifierAbAttr", user, null, simulated, this, modifierHolder);
modifierHolder.value = user.getTag(BattlerTagType.BYPASS_SPEED) ? MovePriorityModifier.FIRST_IN_BRACKET : modifierHolder.value;
return modifierHolder.value;
}
/** /**
* Calculate the [Expected Power](https://en.wikipedia.org/wiki/Expected_value) per turn * Calculate the [Expected Power](https://en.wikipedia.org/wiki/Expected_value) per turn
* of this move, taking into account multi hit moves, accuracy, and the number of turns it * of this move, taking into account multi hit moves, accuracy, and the number of turns it

View File

@ -1,97 +0,0 @@
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;
}

View File

@ -95,4 +95,5 @@ export enum BattlerTagType {
ENDURE_TOKEN = "ENDURE_TOKEN", ENDURE_TOKEN = "ENDURE_TOKEN",
POWDER = "POWDER", POWDER = "POWDER",
MAGIC_COAT = "MAGIC_COAT", MAGIC_COAT = "MAGIC_COAT",
BYPASS_SPEED = "BYPASS_SPEED"
} }

View File

@ -0,0 +1,5 @@
export enum MovePriorityModifier {
LAST_IN_BRACKET = 0,
NORMAL,
FIRST_IN_BRACKET,
}

View File

@ -12,7 +12,6 @@ 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";
import type { VoucherType } from "#app/system/voucher"; import type { VoucherType } from "#app/system/voucher";
import { Command } from "#enums/command";
import { addTextObject, TextStyle } from "#app/ui/text"; import { addTextObject, TextStyle } from "#app/ui/text";
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common"; import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
@ -1588,30 +1587,16 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier {
return new BypassSpeedChanceModifier(this.type, this.pokemonId, this.stackCount); return new BypassSpeedChanceModifier(this.type, this.pokemonId, this.stackCount);
} }
/**
* Checks if {@linkcode BypassSpeedChanceModifier} should be applied
* @param pokemon the {@linkcode Pokemon} that holds the item
* @param doBypassSpeed {@linkcode BooleanHolder} that is `true` if speed should be bypassed
* @returns `true` if {@linkcode BypassSpeedChanceModifier} should be applied
*/
override shouldApply(pokemon?: Pokemon, doBypassSpeed?: BooleanHolder): boolean {
return super.shouldApply(pokemon, doBypassSpeed) && !!doBypassSpeed;
}
/** /**
* Applies {@linkcode BypassSpeedChanceModifier} * Applies {@linkcode BypassSpeedChanceModifier}
* @param pokemon the {@linkcode Pokemon} that holds the item * @param pokemon the {@linkcode Pokemon} that holds the item
* @param doBypassSpeed {@linkcode BooleanHolder} that is `true` if speed should be bypassed
* @returns `true` if {@linkcode BypassSpeedChanceModifier} has been applied * @returns `true` if {@linkcode BypassSpeedChanceModifier} has been applied
*/ */
override apply(pokemon: Pokemon, doBypassSpeed: BooleanHolder): boolean { override apply(pokemon: Pokemon): boolean {
if (!doBypassSpeed.value && pokemon.randBattleSeedInt(10) < this.getStackCount()) { if (pokemon.randBattleSeedInt(10) < this.getStackCount() && pokemon.addTag(BattlerTagType.BYPASS_SPEED)) {
doBypassSpeed.value = true;
const isCommandFight =
globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT;
const hasQuickClaw = this.type.is("PokemonHeldItemModifierType") && this.type.id === "QUICK_CLAW"; const hasQuickClaw = this.type.is("PokemonHeldItemModifierType") && this.type.id === "QUICK_CLAW";
if (isCommandFight && hasQuickClaw) { if (hasQuickClaw) {
globalScene.phaseManager.queueMessage( globalScene.phaseManager.queueMessage(
i18next.t("modifier:bypassSpeedChanceApply", { i18next.t("modifier:bypassSpeedChanceApply", {
pokemonName: getPokemonNameWithAffix(pokemon), pokemonName: getPokemonNameWithAffix(pokemon),

View File

@ -0,0 +1,28 @@
import type { MovePhase } from "#app/phases/move-phase";
import { PokemonPhasePriorityQueue } from "#app/queues/pokemon-phase-priority-queue";
export class MovePhasePriorityQueue extends PokemonPhasePriorityQueue<MovePhase> {
public override reorder(): void {
super.reorder();
this.sortPostSpeed();
}
private sortPostSpeed(): void {
this.queue.sort((a: MovePhase, b: MovePhase) => {
const priority = [a, b].map(movePhase => {
const move = movePhase.move.getMove();
return move.getPriority(movePhase.pokemon, true);
});
const priorityModifiers = [a, b].map(movePhase =>
movePhase.move.getMove().getPriorityModifier(movePhase.pokemon),
);
if (priority[0] === priority[1] && priorityModifiers[0] !== priorityModifiers[1]) {
return priorityModifiers[0] - priorityModifiers[1];
}
return priority[0] - priority[1];
});
}
}

View File

@ -0,0 +1,43 @@
import type { Phase } from "#app/phase";
/**
* 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<T extends Phase> {
protected queue: T[] = [];
/**
* 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(): T | undefined {
this.reorder();
return this.queue.shift();
}
/**
* Adds a phase to the queue
* @param phase The phase to add
*/
public push(phase: T): void {
this.queue.push(phase);
}
/**
* Removes all phases from the queue
*/
public clear(): void {
this.queue.splice(0, this.queue.length);
}
public isEmpty(): boolean {
return !this.queue.length;
}
}

View File

@ -0,0 +1,10 @@
import type { PartyMemberPokemonPhase } from "#app/phases/party-member-pokemon-phase";
import type { PokemonPhase } from "#app/phases/pokemon-phase";
import { PhasePriorityQueue } from "#app/queues/phase-priority-queue";
import { sortInSpeedOrder } from "#app/utils/speed-order";
export class PokemonPhasePriorityQueue<T extends PokemonPhase | PartyMemberPokemonPhase> extends PhasePriorityQueue<T> {
public override reorder(): void {
this.queue = sortInSpeedOrder(this.queue);
}
}

View File

@ -0,0 +1,42 @@
import { globalScene } from "#app/global-scene";
import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase";
import { PostSummonActivateAbilityPhase } from "#app/phases/post-summon-activate-ability-phase";
import type { PostSummonPhase } from "#app/phases/post-summon-phase";
import { PokemonPhasePriorityQueue } from "#app/queues/pokemon-phase-priority-queue";
/**
* 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 PokemonPhasePriorityQueue<PostSummonPhase> {
public override reorder(): void {
super.reorder();
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
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("PostSummonPhase"),
"ActivatePriorityQueuePhase",
(p: ActivatePriorityQueuePhase) => p.getType() === "PostSummonPhase",
);
});
}
}

46
src/utils/speed-order.ts Normal file
View File

@ -0,0 +1,46 @@
import Pokemon from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import { BooleanHolder, randSeedShuffle } from "#app/utils/common";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Stat } from "#enums/stat";
export interface hasPokemon {
getPokemon(): Pokemon;
}
export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] {
pokemonList = shuffle(pokemonList);
sortBySpeed(pokemonList);
return pokemonList;
}
/** Randomly shuffles the queue. */
function shuffle<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] {
// This is seeded with the current turn to prevent an inconsistency where it
// was varying based on how long since you last reloaded
globalScene.executeWithSeedOffset(
() => {
pokemonList = randSeedShuffle(pokemonList);
},
globalScene.currentBattle.turn * 1000 + pokemonList.length,
globalScene.waveSeed,
);
return pokemonList;
}
function sortBySpeed<T extends Pokemon | hasPokemon>(pokemonList: T[]): void {
pokemonList.sort((a, b) => {
const [aSpeed, bSpeed] = [a, b].map(pkmn =>
pkmn instanceof Pokemon ? pkmn.getEffectiveStat(Stat.SPD) : pkmn.getPokemon().getEffectiveStat(Stat.SPD),
);
return bSpeed - aSpeed;
});
/** 'true' if Trick Room is on the field. */
const speedReversed = new BooleanHolder(false);
globalScene.arena.applyTags(ArenaTagType.TRICK_ROOM, false, speedReversed);
if (speedReversed.value) {
pokemonList.reverse();
}
}