Merge branch 'beta' into pokerouge

This commit is contained in:
damocleas 2025-08-07 21:22:47 -04:00 committed by GitHub
commit aa240bfdf5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 122 additions and 121 deletions

@ -1 +1 @@
Subproject commit fa35780fed762017c89d1e9ece8a2779dff56c4d Subproject commit ab2716d5440c25f73986664aa3f3131821c3c392

View File

@ -16,7 +16,7 @@ export type * from "#moves/move";
* Map of move subclass names to their respective classes. * Map of move subclass names to their respective classes.
* Does not include the ChargeMove subclasses. For that, use `ChargingMoveClassMap`. * Does not include the ChargeMove subclasses. For that, use `ChargingMoveClassMap`.
* *
* @privateremarks * @privateRemarks
* The `never` field (`declare private _: never`) in some classes is necessary * The `never` field (`declare private _: never`) in some classes is necessary
* to ensure typescript does not improperly narrow a failed `is` guard to `never`. * to ensure typescript does not improperly narrow a failed `is` guard to `never`.
* *

View File

@ -242,8 +242,8 @@ export class BattleScene extends SceneBase {
public battleStyle: BattleStyle = BattleStyle.SWITCH; public battleStyle: BattleStyle = BattleStyle.SWITCH;
/** /**
* Defines whether or not to show type effectiveness hints * Defines whether or not to show type effectiveness hints
* - true: No hints * - true: Show hints for moves
* - false: Show hints for moves * - false: No hints
*/ */
public typeHints = false; public typeHints = false;

View File

@ -1284,7 +1284,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
/** /**
* Set stat stages when the user gets hit by a critical hit * Set stat stages when the user gets hit by a critical hit
* *
* @privateremarks * @privateRemarks
* It is the responsibility of the caller to ensure that this ability attribute is only applied * It is the responsibility of the caller to ensure that this ability attribute is only applied
* when the user has been hit by a critical hit; such an event is not checked here. * when the user has been hit by a critical hit; such an event is not checked here.
* *
@ -2043,7 +2043,7 @@ export class AllyStatMultiplierAbAttr extends AbAttr {
/** /**
* @param stat - The stat being modified * @param stat - The stat being modified
* @param multipler - The multiplier to apply to the stat * @param multiplier - The multiplier to apply to the stat
* @param ignorable - Whether the multiplier can be ignored by mold breaker-like moves and abilities * @param ignorable - Whether the multiplier can be ignored by mold breaker-like moves and abilities
*/ */
constructor(stat: BattleStat, multiplier: number, ignorable = true) { constructor(stat: BattleStat, multiplier: number, ignorable = true) {
@ -6444,23 +6444,23 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
public override canApply({ pokemon, source, damage }: PostDamageAbAttrParams): boolean { public override canApply({ pokemon, source, damage }: PostDamageAbAttrParams): boolean {
const moveHistory = pokemon.getMoveHistory(); const moveHistory = pokemon.getMoveHistory();
// Will not activate when the Pokémon's HP is lowered by cutting its own HP // Will not activate when the Pokémon's HP is lowered by cutting its own HP
const fordbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT]; const forbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT];
if (moveHistory.length > 0) { if (moveHistory.length > 0) {
const lastMoveUsed = moveHistory[moveHistory.length - 1]; const lastMoveUsed = moveHistory[moveHistory.length - 1];
if (fordbiddenAttackingMoves.includes(lastMoveUsed.move)) { if (forbiddenAttackingMoves.includes(lastMoveUsed.move)) {
return false; return false;
} }
} }
// Dragon Tail and Circle Throw switch out Pokémon before the Ability activates. // Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.
const fordbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW]; const forbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW];
if (source) { if (source) {
const enemyMoveHistory = source.getMoveHistory(); const enemyMoveHistory = source.getMoveHistory();
if (enemyMoveHistory.length > 0) { if (enemyMoveHistory.length > 0) {
const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1]; const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop. // Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop.
if ( if (
fordbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || forbiddenDefendingMoves.includes(enemyLastMoveUsed.move) ||
(enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER)
) { ) {
return false; return false;

View File

@ -46,7 +46,7 @@ import {
} from "#mystery-encounters/mystery-encounter-requirements"; } from "#mystery-encounters/mystery-encounter-requirements";
import { getRandomPartyMemberFunc, trainerConfigs } from "#trainers/trainer-config"; import { getRandomPartyMemberFunc, trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { MoveInfoOverlay } from "#ui/move-info-overlay"; import { MoveInfoOverlay } from "#ui/move-info-overlay";
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#utils/common"; import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -45,7 +45,7 @@ import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import { trainerConfigs } from "#trainers/trainer-config"; import { trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template";
import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
import { randSeedInt, randSeedShuffle } from "#utils/common"; import { randSeedInt, randSeedShuffle } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils"; import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -37,7 +37,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou
import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements";
import { DANCING_MOVES } from "#mystery-encounters/requirement-groups"; import { DANCING_MOVES } from "#mystery-encounters/requirement-groups";
import { PokemonData } from "#system/pokemon-data"; import { PokemonData } from "#system/pokemon-data";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { getPokemonSpecies } from "#utils/pokemon-utils"; import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -33,7 +33,7 @@ import {
MoneyRequirement, MoneyRequirement,
} from "#mystery-encounters/mystery-encounter-requirements"; } from "#mystery-encounters/mystery-encounter-requirements";
import i18next from "#plugins/i18n"; import i18next from "#plugins/i18n";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { randSeedItem } from "#utils/common"; import { randSeedItem } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils"; import { getPokemonSpecies } from "#utils/pokemon-utils";

View File

@ -18,7 +18,7 @@ import {
import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import i18next from "i18next"; import i18next from "i18next";
/** i18n namespace for the encounter */ /** i18n namespace for the encounter */

View File

@ -42,7 +42,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou
import { PartySizeRequirement } from "#mystery-encounters/mystery-encounter-requirements"; import { PartySizeRequirement } from "#mystery-encounters/mystery-encounter-requirements";
import { PokemonData } from "#system/pokemon-data"; import { PokemonData } from "#system/pokemon-data";
import { MusicPreference } from "#system/settings"; import { MusicPreference } from "#system/settings";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { isNullOrUndefined, NumberHolder, randInt, randSeedInt, randSeedItem, randSeedShuffle } from "#utils/common"; import { isNullOrUndefined, NumberHolder, randInt, randSeedInt, randSeedItem, randSeedShuffle } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils"; import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -27,7 +27,7 @@ import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import { PokemonData } from "#system/pokemon-data"; import { PokemonData } from "#system/pokemon-data";
import type { HeldModifierConfig } from "#types/held-modifier-config"; import type { HeldModifierConfig } from "#types/held-modifier-config";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { isNullOrUndefined, randSeedShuffle } from "#utils/common"; import { isNullOrUndefined, randSeedShuffle } from "#utils/common";
import { getEnumValues } from "#utils/enums"; import { getEnumValues } from "#utils/enums";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -156,7 +156,7 @@ export class MysteryEncounterOption implements IMysteryEncounterOption {
return true; return true;
} }
console.log( console.log(
"Mystery Encounter Edge Case: Requirement not met due to primay pokemon overlapping with support pokemon. There's no valid primary pokemon left.", "Mystery Encounter Edge Case: Requirement not met due to primary pokemon overlapping with support pokemon. There's no valid primary pokemon left.",
); );
return false; return false;
} }

View File

@ -576,7 +576,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
*/ */
/** /**
* @statif Defines the type of encounter which is used as an identifier, should be tied to a unique MysteryEncounterType * @static Defines the type of encounter which is used as an identifier, should be tied to a unique MysteryEncounterType
* NOTE: if new functions are added to {@linkcode MysteryEncounter} class * NOTE: if new functions are added to {@linkcode MysteryEncounter} class
* @param encounterType * @param encounterType
* @returns this * @returns this
@ -605,7 +605,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
} }
/** /**
* Defines an option + phasefor the encounter. * Defines an option + phase for the encounter.
* Use for easy/streamlined options. * Use for easy/streamlined options.
* There should be at least 2 options defined and no more than 4. * There should be at least 2 options defined and no more than 4.
* If complex use {@linkcode MysteryEncounterBuilder.withOption} * If complex use {@linkcode MysteryEncounterBuilder.withOption}
@ -627,7 +627,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
} }
/** /**
* Defines an option + phasefor the encounter. * Defines an option + phase for the encounter.
* Use for easy/streamlined options. * Use for easy/streamlined options.
* There should be at least 2 options defined and no more than 4. * There should be at least 2 options defined and no more than 4.
* If complex use {@linkcode MysteryEncounterBuilder.withOption} * If complex use {@linkcode MysteryEncounterBuilder.withOption}

View File

@ -6,7 +6,7 @@ import { isNullOrUndefined } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";
/** /**
* Will inject all relevant dialogue tokens that exist in the {@linkcode BattlegScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text. * Will inject all relevant dialogue tokens that exist in the {@linkcode globalScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text.
* Also adds BBCodeText fragments for colored text, if applicable * Also adds BBCodeText fragments for colored text, if applicable
* @param keyOrString * @param keyOrString
* @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly * @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly

View File

@ -46,7 +46,7 @@ import type { PokemonData } from "#system/pokemon-data";
import type { TrainerConfig } from "#trainers/trainer-config"; import type { TrainerConfig } from "#trainers/trainer-config";
import { trainerConfigs } from "#trainers/trainer-config"; import { trainerConfigs } from "#trainers/trainer-config";
import type { HeldModifierConfig } from "#types/held-modifier-config"; import type { HeldModifierConfig } from "#types/held-modifier-config";
import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler"; import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler";
import { PartyUiMode } from "#ui/party-ui-handler"; import { PartyUiMode } from "#ui/party-ui-handler";
import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common"; import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common";

View File

@ -4045,7 +4045,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param damage integer * @param damage integer
* @param ignoreSegments boolean, not currently used * @param ignoreSegments boolean, not currently used
* @param preventEndure used to update damage if endure or sturdy * @param preventEndure used to update damage if endure or sturdy
* @param ignoreFaintPhas flag on whether to add FaintPhase if pokemon after applying damage faints * @param ignoreFaintPhase flag on whether to add FaintPhase if pokemon after applying damage faints
* @returns integer representing damage dealt * @returns integer representing damage dealt
*/ */
damage(damage: number, _ignoreSegments = false, preventEndure = false, ignoreFaintPhase = false): number { damage(damage: number, _ignoreSegments = false, preventEndure = false, ignoreFaintPhase = false): number {
@ -5206,38 +5206,38 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
} }
} }
updateFusionPalette(ignoreOveride?: boolean): void { updateFusionPalette(ignoreOverride?: boolean): void {
if (!this.getFusionSpeciesForm(ignoreOveride)) { if (!this.getFusionSpeciesForm(ignoreOverride)) {
[this.getSprite(), this.getTintSprite()] [this.getSprite(), this.getTintSprite()]
.filter(s => !!s) .filter(s => !!s)
.map(s => { .map(s => {
s.pipelineData[`spriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = []; s.pipelineData[`spriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = [];
s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = []; s.pipelineData[`fusionSpriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = [];
}); });
return; return;
} }
const speciesForm = this.getSpeciesForm(ignoreOveride); const speciesForm = this.getSpeciesForm(ignoreOverride);
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOveride); const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
const spriteKey = speciesForm.getSpriteKey( const spriteKey = speciesForm.getSpriteKey(
this.getGender(ignoreOveride) === Gender.FEMALE, this.getGender(ignoreOverride) === Gender.FEMALE,
speciesForm.formIndex, speciesForm.formIndex,
this.shiny, this.shiny,
this.variant, this.variant,
); );
const backSpriteKey = speciesForm const backSpriteKey = speciesForm
.getSpriteKey(this.getGender(ignoreOveride) === Gender.FEMALE, speciesForm.formIndex, this.shiny, this.variant) .getSpriteKey(this.getGender(ignoreOverride) === Gender.FEMALE, speciesForm.formIndex, this.shiny, this.variant)
.replace("pkmn__", "pkmn__back__"); .replace("pkmn__", "pkmn__back__");
const fusionSpriteKey = fusionSpeciesForm.getSpriteKey( const fusionSpriteKey = fusionSpeciesForm.getSpriteKey(
this.getFusionGender(ignoreOveride) === Gender.FEMALE, this.getFusionGender(ignoreOverride) === Gender.FEMALE,
fusionSpeciesForm.formIndex, fusionSpeciesForm.formIndex,
this.fusionShiny, this.fusionShiny,
this.fusionVariant, this.fusionVariant,
); );
const fusionBackSpriteKey = fusionSpeciesForm const fusionBackSpriteKey = fusionSpeciesForm
.getSpriteKey( .getSpriteKey(
this.getFusionGender(ignoreOveride) === Gender.FEMALE, this.getFusionGender(ignoreOverride) === Gender.FEMALE,
fusionSpeciesForm.formIndex, fusionSpeciesForm.formIndex,
this.fusionShiny, this.fusionShiny,
this.fusionVariant, this.fusionVariant,
@ -5522,8 +5522,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
[this.getSprite(), this.getTintSprite()] [this.getSprite(), this.getTintSprite()]
.filter(s => !!s) .filter(s => !!s)
.map(s => { .map(s => {
s.pipelineData[`spriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = spriteColors; s.pipelineData[`spriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = spriteColors;
s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = s.pipelineData[`fusionSpriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] =
fusionSpriteColors; fusionSpriteColors;
}); });

View File

@ -105,7 +105,7 @@ import {
TempExtraModifierModifier, TempExtraModifierModifier,
TempStatStageBoosterModifier, TempStatStageBoosterModifier,
TerastallizeAccessModifier, TerastallizeAccessModifier,
TerrastalizeModifier, TerastallizeModifier,
TmModifier, TmModifier,
TurnHealModifier, TurnHealModifier,
TurnHeldItemTransferModifier, TurnHeldItemTransferModifier,
@ -431,7 +431,7 @@ export class TerastallizeModifierType extends PokemonModifierType {
super( super(
"", "",
`${PokemonType[teraType].toLowerCase()}_tera_shard`, `${PokemonType[teraType].toLowerCase()}_tera_shard`,
(type, args) => new TerrastalizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), (type, args) => new TerastallizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType),
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if ( if (
[pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId].filter( [pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId].filter(

View File

@ -2073,7 +2073,7 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier {
} }
} }
export class TerrastalizeModifier extends ConsumablePokemonModifier { export class TerastallizeModifier extends ConsumablePokemonModifier {
public declare type: TerastallizeModifierType; public declare type: TerastallizeModifierType;
public teraType: PokemonType; public teraType: PokemonType;
@ -2084,9 +2084,9 @@ export class TerrastalizeModifier extends ConsumablePokemonModifier {
} }
/** /**
* Checks if {@linkcode TerrastalizeModifier} should be applied * Checks if {@linkcode TerastallizeModifier} should be applied
* @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item
* @returns `true` if the {@linkcode TerrastalizeModifier} should be applied * @returns `true` if the {@linkcode TerastallizeModifier} should be applied
*/ */
override shouldApply(playerPokemon?: PlayerPokemon): boolean { override shouldApply(playerPokemon?: PlayerPokemon): boolean {
return ( return (
@ -2098,7 +2098,7 @@ export class TerrastalizeModifier extends ConsumablePokemonModifier {
} }
/** /**
* Applies {@linkcode TerrastalizeModifier} * Applies {@linkcode TerastallizeModifier}
* @param pokemon The {@linkcode PlayerPokemon} that consumes the item * @param pokemon The {@linkcode PlayerPokemon} that consumes the item
* @returns `true` if hp was restored * @returns `true` if hp was restored
*/ */
@ -3875,7 +3875,7 @@ const ModifierClassMap = Object.freeze({
ResetNegativeStatStageModifier, ResetNegativeStatStageModifier,
FieldEffectModifier, FieldEffectModifier,
ConsumablePokemonModifier, ConsumablePokemonModifier,
TerrastalizeModifier, TerastallizeModifier,
PokemonHpRestoreModifier, PokemonHpRestoreModifier,
PokemonStatusHealModifier, PokemonStatusHealModifier,
ConsumablePokemonMoveModifier, ConsumablePokemonMoveModifier,

View File

@ -11,7 +11,7 @@ export abstract class Phase {
/** /**
* The string name of the phase, used to identify the phase type for {@linkcode is} * The string name of the phase, used to identify the phase type for {@linkcode is}
* *
* @privateremarks * @privateRemarks
* *
* When implementing a phase, you must set the `phaseName` property to the name of the phase. * When implementing a phase, you must set the `phaseName` property to the name of the phase.
*/ */

View File

@ -112,7 +112,7 @@ export class CommandPhase extends FieldPhase {
* Clear out all unusable moves in front of the currently acting pokemon's move queue. * Clear out all unusable moves in front of the currently acting pokemon's move queue.
*/ */
// TODO: Refactor move queue handling to ensure that this method is not necessary. // TODO: Refactor move queue handling to ensure that this method is not necessary.
private clearUnusuableMoves(): void { private clearUnusableMoves(): void {
const playerPokemon = this.getPokemon(); const playerPokemon = this.getPokemon();
const moveQueue = playerPokemon.getMoveQueue(); const moveQueue = playerPokemon.getMoveQueue();
if (moveQueue.length === 0) { if (moveQueue.length === 0) {
@ -143,7 +143,7 @@ export class CommandPhase extends FieldPhase {
* @returns Whether a queued move was successfully set to be executed. * @returns Whether a queued move was successfully set to be executed.
*/ */
private tryExecuteQueuedMove(): boolean { private tryExecuteQueuedMove(): boolean {
this.clearUnusuableMoves(); this.clearUnusableMoves();
const playerPokemon = globalScene.getPlayerField()[this.fieldIndex]; const playerPokemon = globalScene.getPlayerField()[this.fieldIndex];
const moveQueue = playerPokemon.getMoveQueue(); const moveQueue = playerPokemon.getMoveQueue();

View File

@ -5,7 +5,7 @@ import { ChallengeType } from "#enums/challenge-type";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { MapModifier, MoneyInterestModifier } from "#modifiers/modifier"; import { MapModifier, MoneyInterestModifier } from "#modifiers/modifier";
import { BattlePhase } from "#phases/battle-phase"; import { BattlePhase } from "#phases/battle-phase";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { applyChallenges } from "#utils/challenge-utils"; import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, randSeedInt } from "#utils/common"; import { BooleanHolder, randSeedInt } from "#utils/common";

View File

@ -16,7 +16,7 @@ import type { Modifier } from "#modifiers/modifier";
import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#modifiers/modifier-type";
import type { SessionSaveData } from "#system/game-data"; import type { SessionSaveData } from "#system/game-data";
import { vouchers } from "#system/voucher"; import { vouchers } from "#system/voucher";
import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler"; import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler";
import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#utils/common"; import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -144,7 +144,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole
handler: () => changeGamepadHandler(g), handler: () => changeGamepadHandler(g),
})), })),
{ {
label: i18next.t("settings:cancelContollerChoice"), label: i18next.t("settings:cancelControllerChoice"),
handler: cancelHandler, handler: cancelHandler,
}, },
], ],

View File

@ -36,7 +36,7 @@ interface ArenaEffectInfo {
/** The enum string representation of the effect */ /** The enum string representation of the effect */
name: string; name: string;
/** {@linkcode ArenaEffectType} type of effect */ /** {@linkcode ArenaEffectType} type of effect */
effecType: ArenaEffectType; effectType: ArenaEffectType;
/** The maximum duration set by the effect */ /** The maximum duration set by the effect */
maxDuration: number; maxDuration: number;
@ -246,7 +246,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
// Creates a proxy object to decide which text object needs to be updated // Creates a proxy object to decide which text object needs to be updated
let textObject: Phaser.GameObjects.Text; let textObject: Phaser.GameObjects.Text;
switch (fieldEffectInfo.effecType) { switch (fieldEffectInfo.effectType) {
case ArenaEffectType.PLAYER: case ArenaEffectType.PLAYER:
textObject = this.flyoutTextPlayer; textObject = this.flyoutTextPlayer;
break; break;
@ -300,7 +300,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
const existingTrapTagIndex = isArenaTrapTag const existingTrapTagIndex = isArenaTrapTag
? this.fieldEffectInfo.findIndex( ? this.fieldEffectInfo.findIndex(
e => tagAddedEvent.arenaTagType === e.tagType && arenaEffectType === e.effecType, e => tagAddedEvent.arenaTagType === e.tagType && arenaEffectType === e.effectType,
) )
: -1; : -1;
let name: string = getFieldEffectText(ArenaTagType[tagAddedEvent.arenaTagType]); let name: string = getFieldEffectText(ArenaTagType[tagAddedEvent.arenaTagType]);
@ -318,7 +318,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
this.fieldEffectInfo.push({ this.fieldEffectInfo.push({
name, name,
effecType: arenaEffectType, effectType: arenaEffectType,
maxDuration: tagAddedEvent.duration, maxDuration: tagAddedEvent.duration,
duration: tagAddedEvent.duration, duration: tagAddedEvent.duration,
tagType: tagAddedEvent.arenaTagType, tagType: tagAddedEvent.arenaTagType,
@ -353,7 +353,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
? WeatherType[fieldEffectChangedEvent.newWeatherType] ? WeatherType[fieldEffectChangedEvent.newWeatherType]
: TerrainType[fieldEffectChangedEvent.newTerrainType], : TerrainType[fieldEffectChangedEvent.newTerrainType],
), ),
effecType: effectType:
fieldEffectChangedEvent instanceof WeatherChangedEvent ? ArenaEffectType.WEATHER : ArenaEffectType.TERRAIN, fieldEffectChangedEvent instanceof WeatherChangedEvent ? ArenaEffectType.WEATHER : ArenaEffectType.TERRAIN,
maxDuration: fieldEffectChangedEvent.duration, maxDuration: fieldEffectChangedEvent.duration,
duration: fieldEffectChangedEvent.duration, duration: fieldEffectChangedEvent.duration,

View File

@ -1,6 +1,6 @@
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler";
export class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { export class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler {
modalContainer: Phaser.GameObjects.Container; modalContainer: Phaser.GameObjects.Container;

View File

@ -198,11 +198,11 @@ export class PlayerBattleInfo extends BattleInfo {
this.lastLevelCapped = isLevelCapped; this.lastLevelCapped = isLevelCapped;
if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) { if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) {
const durationMultipler = Math.max( const durationMultiplier = Math.max(
Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(1 - Math.min(pokemon.level - this.lastLevel, 10) / 10), Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(1 - Math.min(pokemon.level - this.lastLevel, 10) / 10),
0.1, 0.1,
); );
await this.updatePokemonExp(pokemon, false, durationMultipler); await this.updatePokemonExp(pokemon, false, durationMultiplier);
} else if (isLevelCapped !== oldLevelCapped) { } else if (isLevelCapped !== oldLevelCapped) {
this.setLevel(pokemon.level); this.setLevel(pokemon.level);
} }

View File

@ -42,7 +42,7 @@ export class CommandUiHandler extends UiHandler {
ui.add(this.commandsContainer); ui.add(this.commandsContainer);
this.teraButton = globalScene.add.sprite(-32, 15, "button_tera"); this.teraButton = globalScene.add.sprite(-32, 15, "button_tera");
this.teraButton.setName("terrastallize-button"); this.teraButton.setName("terastallize-button");
this.teraButton.setScale(1.3); this.teraButton.setScale(1.3);
this.teraButton.setFrame("fire"); this.teraButton.setFrame("fire");
this.teraButton.setPipeline(globalScene.spritePipeline, { this.teraButton.setPipeline(globalScene.spritePipeline, {

View File

@ -1,8 +1,8 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler";
import i18next from "i18next"; import i18next from "i18next";
export class ConfirmUiHandler extends AbstractOptionSelectUiHandler { export class ConfirmUiHandler extends AbstractOptionSelectUiHandler {

View File

@ -2,7 +2,7 @@ import { pokerogueApi } from "#api/pokerogue-api";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { TextStyle } from "#enums/text-style"; import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
import type { ModalConfig } from "#ui/modal-ui-handler"; import type { ModalConfig } from "#ui/modal-ui-handler";

View File

@ -7,7 +7,7 @@ import { Button } from "#enums/buttons";
import { GameDataType } from "#enums/game-data-type"; import { GameDataType } from "#enums/game-data-type";
import { TextStyle } from "#enums/text-style"; import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { AdminMode, getAdminModeName } from "#ui/admin-ui-handler"; import { AdminMode, getAdminModeName } from "#ui/admin-ui-handler";
import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
import { BgmBar } from "#ui/bgm-bar"; import { BgmBar } from "#ui/bgm-bar";

View File

@ -843,7 +843,7 @@ class ModifierOption extends Phaser.GameObjects.Container {
/** /**
* Start the tweens responsible for animating the option's appearance * Start the tweens responsible for animating the option's appearance
* *
* @privateremarks * @privateRemarks
* This method is unusual. It "returns" (one via the actual return, one by via appending to the `promiseHolder` * This method is unusual. It "returns" (one via the actual return, one by via appending to the `promiseHolder`
* parameter) two promises. The promise returned by the method resolves once the option's appearance animations have * parameter) two promises. The promise returned by the method resolves once the option's appearance animations have
* completed, and is meant to allow callers to synchronize with the completion of the option's appearance animations. * completed, and is meant to allow callers to synchronize with the completion of the option's appearance animations.

View File

@ -46,7 +46,7 @@ import { getVariantIcon, getVariantTint } from "#sprites/variant";
import type { StarterAttributes } from "#system/game-data"; import type { StarterAttributes } from "#system/game-data";
import { SettingKeyboard } from "#system/settings-keyboard"; import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data"; import type { DexEntry } from "#types/dex-data";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { BaseStatsOverlay } from "#ui/base-stats-overlay"; import { BaseStatsOverlay } from "#ui/base-stats-overlay";
import { MessageUiHandler } from "#ui/message-ui-handler"; import { MessageUiHandler } from "#ui/message-ui-handler";
import { MoveInfoOverlay } from "#ui/move-info-overlay"; import { MoveInfoOverlay } from "#ui/move-info-overlay";

View File

@ -1,7 +1,7 @@
import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; import { allAbilities, allMoves, allSpecies } from "#data/data-lists";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon"; import type { PlayerPokemon } from "#field/pokemon";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { FilterTextRow } from "#ui/filter-text"; import { FilterTextRow } from "#ui/filter-text";
import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; import { FormModalUiHandler } from "#ui/form-modal-ui-handler";

View File

@ -33,7 +33,7 @@ import { getVariantIcon, getVariantTint } from "#sprites/variant";
import type { DexAttrProps, StarterAttributes } from "#system/game-data"; import type { DexAttrProps, StarterAttributes } from "#system/game-data";
import { SettingKeyboard } from "#system/settings-keyboard"; import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data"; import type { DexEntry } from "#types/dex-data";
import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown";
import { FilterBar } from "#ui/filter-bar"; import { FilterBar } from "#ui/filter-bar";
import { FilterText, FilterTextRow } from "#ui/filter-text"; import { FilterText, FilterTextRow } from "#ui/filter-text";

View File

@ -702,11 +702,11 @@ export class RunInfoUiHandler extends UiHandler {
rules.push(i18next.t("challenges:inverseBattle.shortName")); rules.push(i18next.t("challenges:inverseBattle.shortName"));
break; break;
default: { default: {
const localisationKey = Challenges[this.runInfo.challenges[i].id] const localizationKey = Challenges[this.runInfo.challenges[i].id]
.split("_") .split("_")
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase())) .map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join(""); .join("");
rules.push(i18next.t(`challenges:${localisationKey}.name`)); rules.push(i18next.t(`challenges:${localizationKey}.name`));
break; break;
} }
} }

View File

@ -401,7 +401,7 @@ class SessionSlot extends Phaser.GameObjects.Container {
const gameModeLabel = addTextObject( const gameModeLabel = addTextObject(
8, 8,
5, 5,
`${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unkown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`, `${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`,
TextStyle.WINDOW, TextStyle.WINDOW,
); );
this.add(gameModeLabel); this.add(gameModeLabel);

View File

@ -8,7 +8,7 @@ import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme"; import { addWindow } from "#ui/ui-theme";
import i18next from "i18next"; import i18next from "i18next";
type CancelFn = (succes?: boolean) => boolean; type CancelFn = (success?: boolean) => boolean;
/** /**
* Abstract class for handling UI elements related to button bindings. * Abstract class for handling UI elements related to button bindings.

View File

@ -1,5 +1,5 @@
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler";
export class OptionSelectUiHandler extends AbstractOptionSelectUiHandler { export class OptionSelectUiHandler extends AbstractOptionSelectUiHandler {
constructor(mode: UiMode = UiMode.OPTION_SELECT) { constructor(mode: UiMode = UiMode.OPTION_SELECT) {

View File

@ -47,7 +47,7 @@ import { achvs } from "#system/achv";
import type { DexAttrProps, StarterAttributes, StarterMoveset } from "#system/game-data"; import type { DexAttrProps, StarterAttributes, StarterMoveset } from "#system/game-data";
import { SettingKeyboard } from "#system/settings-keyboard"; import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data"; import type { DexEntry } from "#types/dex-data";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown";
import { FilterBar } from "#ui/filter-bar"; import { FilterBar } from "#ui/filter-bar";
import { MessageUiHandler } from "#ui/message-ui-handler"; import { MessageUiHandler } from "#ui/message-ui-handler";
@ -901,7 +901,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonEggMovesContainer.add(eggMoveContainer); this.pokemonEggMovesContainer.add(eggMoveContainer);
} }
this.teraIcon = globalScene.add.sprite(85, 63, "button_tera").setName("terrastallize-icon").setFrame("fire"); this.teraIcon = globalScene.add.sprite(85, 63, "button_tera").setName("terastallize-icon").setFrame("fire");
// The font size should be set per language // The font size should be set per language
const instructionTextSize = textSettings.instructionTextSize; const instructionTextSize = textSettings.instructionTextSize;

View File

@ -879,7 +879,7 @@ export class SummaryUiHandler extends UiHandler {
!isNullOrUndefined(this.pokemon) !isNullOrUndefined(this.pokemon)
) { ) {
const teraIcon = globalScene.add.sprite(123, 26, "button_tera"); const teraIcon = globalScene.add.sprite(123, 26, "button_tera");
teraIcon.setName("terrastallize-icon"); teraIcon.setName("terastallize-icon");
teraIcon.setFrame(PokemonType[this.pokemon.getTeraType()].toLowerCase()); teraIcon.setFrame(PokemonType[this.pokemon.getTeraType()].toLowerCase());
profileContainer.add(teraIcon); profileContainer.add(teraIcon);
} }

View File

@ -1,6 +1,6 @@
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon"; import type { PlayerPokemon } from "#field/pokemon";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
import type { ModalConfig } from "#ui/modal-ui-handler"; import type { ModalConfig } from "#ui/modal-ui-handler";
@ -13,7 +13,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
setup() { setup() {
super.setup(); super.setup();
const flattenKeys = (object?: any, topKey?: string, midleKey?: string[]): Array<any> => { const flattenKeys = (object?: any, topKey?: string, middleKey?: string[]): Array<any> => {
return Object.keys(object ?? {}) return Object.keys(object ?? {})
.map((t, i) => { .map((t, i) => {
const value = Object.values(object)[i]; const value = Object.values(object)[i];
@ -23,7 +23,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
// If the value is an object, execute the same process // If the value is an object, execute the same process
// si el valor es un objeto ejecuta el mismo proceso // si el valor es un objeto ejecuta el mismo proceso
return flattenKeys(value, topKey ?? t, topKey ? (midleKey ? [...midleKey, t] : [t]) : undefined).filter( return flattenKeys(value, topKey ?? t, topKey ? (middleKey ? [...middleKey, t] : [t]) : undefined).filter(
t => t.length > 0, t => t.length > 0,
); );
} }
@ -31,7 +31,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
// we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
// Return in the format expected by i18next // Return in the format expected by i18next
return midleKey ? `${topKey}:${midleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`; return middleKey ? `${topKey}:${middleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`;
} }
}) })
.filter(t => t); .filter(t => t);

View File

@ -1,10 +1,15 @@
import { getPokemonNameWithAffix } from "#app/messages";
import { allAbilities } from "#data/data-lists"; import { allAbilities } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { Button } from "#enums/buttons";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import type { PartyUiHandler } from "#ui/party-ui-handler";
import i18next from "i18next";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Arena Trap", () => { describe("Abilities - Arena Trap", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -23,68 +28,64 @@ describe("Abilities - Arena Trap", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.moveset(MoveId.SPLASH)
.ability(AbilityId.ARENA_TRAP) .ability(AbilityId.ARENA_TRAP)
.enemyAbility(AbilityId.ARENA_TRAP)
.enemySpecies(SpeciesId.RALTS) .enemySpecies(SpeciesId.RALTS)
.enemyAbility(AbilityId.BALL_FETCH) .enemyMoveset(MoveId.SPLASH);
.enemyMoveset(MoveId.TELEPORT);
}); });
// TODO: Enable test when Issue #935 is addressed // NB: Since switching moves bypass trapping, the only way fleeing can occur in PKR is from the player
it.todo("should not allow grounded Pokémon to flee", async () => { // TODO: Implement once forced flee helper exists
it.todo("should interrupt player flee attempt and display message, unless user has Run Away");
// TODO: Figure out how to wrangle the UI into not timing out
it.todo("should interrupt player switch attempt and display message", async () => {
game.override.battleStyle("single"); game.override.battleStyle("single");
await game.classicMode.startBattle([SpeciesId.DUGTRIO, SpeciesId.GOTHITELLE]);
await game.classicMode.startBattle(); const enemy = game.field.getEnemyPokemon();
const enemy = game.scene.getEnemyPokemon(); game.doSwitchPokemon(1);
game.onNextPrompt("CommandPhase", UiMode.PARTY, () => {
// no switch out command should be queued due to arena trap
expect(game.scene.currentBattle.turnCommands[0]).toBeNull();
game.move.select(MoveId.SPLASH); // back out and end the phase to avoid timeout
console.log(game.scene.ui.getHandler().constructor.name);
(game.scene.ui.getHandler() as PartyUiHandler).processInput(Button.CANCEL);
});
await game.toNextTurn(); await game.phaseInterceptor.to("CommandPhase");
expect(enemy).toBe(game.scene.getEnemyPokemon()); expect(game.textInterceptor.logs).toContain(
i18next.t("abilityTriggers:arenaTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemy),
abilityName: allAbilities[AbilityId.ARENA_TRAP].name,
}),
);
}); });
it("should guarantee double battle with any one LURE", async () => { it("should guarantee double battle with any one LURE", async () => {
game.override.startingModifier([{ name: "LURE" }]).startingWave(2); game.override.startingModifier([{ name: "LURE" }]).startingWave(2);
await game.classicMode.startBattle([SpeciesId.DUGTRIO]);
await game.classicMode.startBattle(); expect(game.scene.getEnemyField()).toHaveLength(2);
expect(game.scene.getEnemyField().length).toBe(2);
}); });
/**
* This checks if the Player Pokemon is able to switch out/run away after the Enemy Pokemon with {@linkcode AbilityId.ARENA_TRAP}
* is forcefully moved out of the field from moves such as Roar {@linkcode MoveId.ROAR}
*
* Note: It should be able to switch out/run away
*/
it("should lift if pokemon with this ability leaves the field", async () => { it("should lift if pokemon with this ability leaves the field", async () => {
game.override game.override.battleStyle("single");
.battleStyle("double") await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
.enemyMoveset(MoveId.SPLASH)
.moveset([MoveId.ROAR, MoveId.SPLASH])
.ability(AbilityId.BALL_FETCH);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.SUDOWOODO, SpeciesId.LUNATONE]);
const [enemy1, enemy2] = game.scene.getEnemyField(); const player = game.field.getPlayerPokemon();
const [player1, player2] = game.scene.getPlayerField(); const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy1, "getAbility").mockReturnValue(allAbilities[AbilityId.ARENA_TRAP]); expect(player.isTrapped()).toBe(true);
expect(enemy.isOnField()).toBe(true);
game.move.select(MoveId.ROAR); game.move.use(MoveId.ROAR);
game.move.select(MoveId.SPLASH, 1); await game.toEndOfTurn();
// This runs the fist command phase where the moves are selected expect(player.isTrapped()).toBe(false);
await game.toNextTurn(); expect(enemy.isOnField()).toBe(false);
// During the next command phase the player pokemons should not be trapped anymore
game.move.select(MoveId.SPLASH);
game.move.select(MoveId.SPLASH, 1);
await game.toNextTurn();
expect(player1.isTrapped()).toBe(false);
expect(player2.isTrapped()).toBe(false);
expect(enemy1.isOnField()).toBe(false);
expect(enemy2.isOnField()).toBe(true);
}); });
}); });

View File

@ -10,7 +10,7 @@ import { EncounterPhase } from "#phases/encounter-phase";
import { SelectStarterPhase } from "#phases/select-starter-phase"; import { SelectStarterPhase } from "#phases/select-starter-phase";
import type { TitlePhase } from "#phases/title-phase"; import type { TitlePhase } from "#phases/title-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler";
import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler"; import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler";
import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler"; import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";