Merge remote-tracking branch 'upstream/beta' into chilly-reception-msg

This commit is contained in:
Bertie690 2025-06-14 14:13:55 -04:00
commit 77ad05f11f
61 changed files with 248 additions and 261 deletions

View File

@ -14,13 +14,16 @@ Make sure the title includes categorization (choose the one that best fits):
- [Balance]: If the PR is related to game balance - [Balance]: If the PR is related to game balance
- [Challenge]: If the PR is adding or modifying challenges - [Challenge]: If the PR is adding or modifying challenges
- [Refactor]: If the PR is primarily rewriting existing code - [Refactor]: If the PR is primarily rewriting existing code
- [Docs]: If the PR is just adding or modifying documentation (such as tsdocs/code comments) - [Dev]: If the PR is primarily changing something pertaining to development (lefthook hooks, linter rules, etc.)
- [i18n]: If the PR is primarily adding/changing locale keys or key usage (may come with an associated locales PR)
- [Docs]: If the PR is adding or modifying documentation (such as tsdocs/code comments)
- [GitHub]: For changes to GitHub workflows/templates/etc - [GitHub]: For changes to GitHub workflows/templates/etc
- [Misc]: If no other category fits the PR - [Misc]: If no other category fits the PR
--> -->
<!-- <!--
Make sure that this PR is not overlapping with someone else's work Make sure that this PR is not overlapping with someone else's work
Please try to keep the PR self-contained (and small) Please try to keep the PR self-contained (and small!)
--> -->
## What are the changes the user will see? ## What are the changes the user will see?
@ -66,7 +69,7 @@ Do the reviewers need to do something special in order to test your changes?
- [ ] Have I provided a clear explanation of the changes? - [ ] Have I provided a clear explanation of the changes?
- [ ] Have I tested the changes manually? - [ ] Have I tested the changes manually?
- [ ] Are all unit tests still passing? (`npm run test:silent`) - [ ] Are all unit tests still passing? (`npm run test:silent`)
- [ ] Have I created new automated tests (`npm run create-test`) or updated existing tests related to the PR's changes? - [ ] Have I created new automated tests (`npm run test:create`) or updated existing tests related to the PR's changes?
- [ ] Have I provided screenshots/videos of the changes (if applicable)? - [ ] Have I provided screenshots/videos of the changes (if applicable)?
- [ ] Have I made sure that any UI change works for both UI themes (default and legacy)? - [ ] Have I made sure that any UI change works for both UI themes (default and legacy)?

View File

@ -47,8 +47,8 @@
"correctness": { "correctness": {
"noUndeclaredVariables": "off", "noUndeclaredVariables": "off",
"noUnusedVariables": "error", "noUnusedVariables": "error",
"noSwitchDeclarations": "warn", // TODO: refactor and make this an error "noSwitchDeclarations": "error",
"noVoidTypeReturn": "warn", // TODO: Refactor and make this an error "noVoidTypeReturn": "error",
"noUnusedImports": "error" "noUnusedImports": "error"
}, },
"style": { "style": {
@ -85,7 +85,7 @@
"useLiteralKeys": "off", "useLiteralKeys": "off",
"noForEach": "off", // Foreach vs for of is not that simple. "noForEach": "off", // Foreach vs for of is not that simple.
"noUselessSwitchCase": "off", // Explicit > Implicit "noUselessSwitchCase": "off", // Explicit > Implicit
"noUselessConstructor": "warn", // TODO: Refactor and make this an error "noUselessConstructor": "error",
"noBannedTypes": "warn" // TODO: Refactor and make this an error "noBannedTypes": "warn" // TODO: Refactor and make this an error
}, },
"nursery": { "nursery": {

View File

@ -18,7 +18,7 @@
"eslint": "eslint --fix .", "eslint": "eslint --fix .",
"eslint-ci": "eslint .", "eslint-ci": "eslint .",
"biome": "biome check --write --changed --no-errors-on-unmatched", "biome": "biome check --write --changed --no-errors-on-unmatched",
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --changed --no-errors-on-unmatched", "biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
"docs": "typedoc", "docs": "typedoc",
"depcruise": "depcruise src", "depcruise": "depcruise src",
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",

View File

@ -29,4 +29,4 @@ export type ModifierString = keyof ModifierConstructorMap;
export type ModifierPool = { export type ModifierPool = {
[tier: string]: WeightedModifierType[]; [tier: string]: WeightedModifierType[];
} };

View File

@ -9,6 +9,7 @@ import {
randSeedInt, randSeedInt,
type Constructor, type Constructor,
randSeedFloat, randSeedFloat,
coerceArray,
} from "#app/utils/common"; } from "#app/utils/common";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { GroundedTag } from "#app/data/battler-tags"; import { GroundedTag } from "#app/data/battler-tags";
@ -4689,7 +4690,7 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr {
constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) { constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) {
super(true); super(true);
this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [immuneTagTypes]; this.immuneTagTypes = coerceArray(immuneTagTypes);
} }
override canApplyPreApplyBattlerTag( override canApplyPreApplyBattlerTag(
@ -6694,7 +6695,7 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr {
constructor(stats: BattleStat[], stages: number) { constructor(stats: BattleStat[], stages: number) {
super(); super();
this.stats = Array.isArray(stats) ? stats : [stats]; this.stats = stats;
this.stages = stages; this.stages = stages;
} }

View File

@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
import { allMoves } from "./data-lists"; import { allMoves } from "./data-lists";
import { MoveFlags } from "#enums/MoveFlags"; import { MoveFlags } from "#enums/MoveFlags";
import type Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common"; import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName, coerceArray } from "../utils/common";
import type { BattlerIndex } from "#enums/battler-index"; import type { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SubstituteTag } from "./battler-tags"; import { SubstituteTag } from "./battler-tags";
@ -520,7 +520,7 @@ function logMissingMoveAnim(move: MoveId, ...optionalParams: any[]) {
* @param encounterAnim one or more animations to fetch * @param encounterAnim one or more animations to fetch
*/ */
export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> { export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim]; const anims = coerceArray(encounterAnim);
const encounterAnimNames = getEnumKeys(EncounterAnim); const encounterAnimNames = getEnumKeys(EncounterAnim);
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = []; const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
for (const anim of anims) { for (const anim of anims) {

View File

@ -21,7 +21,7 @@ import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
import type { MovePhase } from "#app/phases/move-phase"; import type { MovePhase } from "#app/phases/move-phase";
import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import { BooleanHolder, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common"; import { BooleanHolder, coerceArray, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
@ -50,7 +50,7 @@ export class BattlerTag {
isBatonPassable = false, isBatonPassable = false,
) { ) {
this.tagType = tagType; this.tagType = tagType;
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [lapseType]; this.lapseTypes = coerceArray(lapseType);
this.turnCount = turnCount; this.turnCount = turnCount;
this.sourceMove = sourceMove!; // TODO: is this bang correct? this.sourceMove = sourceMove!; // TODO: is this bang correct?
this.sourceId = sourceId; this.sourceId = sourceId;
@ -125,16 +125,6 @@ export interface TerrainBattlerTag {
* Players and enemies should not be allowed to select restricted moves. * Players and enemies should not be allowed to select restricted moves.
*/ */
export abstract class MoveRestrictionBattlerTag extends BattlerTag { export abstract class MoveRestrictionBattlerTag extends BattlerTag {
constructor(
tagType: BattlerTagType,
lapseType: BattlerTagLapseType | BattlerTagLapseType[],
turnCount: number,
sourceMove?: MoveId,
sourceId?: number,
) {
super(tagType, lapseType, turnCount, sourceMove, sourceId);
}
/** @override */ /** @override */
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattlerTagLapseType.PRE_MOVE) { if (lapseType === BattlerTagLapseType.PRE_MOVE) {
@ -1470,16 +1460,6 @@ export class WrapTag extends DamagingTrapTag {
} }
export abstract class VortexTrapTag extends DamagingTrapTag { export abstract class VortexTrapTag extends DamagingTrapTag {
constructor(
tagType: BattlerTagType,
commonAnim: CommonAnim,
turnCount: number,
sourceMove: MoveId,
sourceId: number,
) {
super(tagType, commonAnim, turnCount, sourceMove, sourceId);
}
getTrapMessage(pokemon: Pokemon): string { getTrapMessage(pokemon: Pokemon): string {
return i18next.t("battlerTags:vortexOnTrap", { return i18next.t("battlerTags:vortexOnTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),

View File

@ -2173,6 +2173,7 @@ export class PlantHealAttr extends WeatherHealAttr {
case WeatherType.SANDSTORM: case WeatherType.SANDSTORM:
case WeatherType.HAIL: case WeatherType.HAIL:
case WeatherType.SNOW: case WeatherType.SNOW:
case WeatherType.FOG:
case WeatherType.HEAVY_RAIN: case WeatherType.HEAVY_RAIN:
return 0.25; return 0.25;
default: default:
@ -4174,6 +4175,7 @@ export class AntiSunlightPowerDecreaseAttr extends VariablePowerAttr {
case WeatherType.SANDSTORM: case WeatherType.SANDSTORM:
case WeatherType.HAIL: case WeatherType.HAIL:
case WeatherType.SNOW: case WeatherType.SNOW:
case WeatherType.FOG:
case WeatherType.HEAVY_RAIN: case WeatherType.HEAVY_RAIN:
power.value *= 0.5; power.value *= 0.5;
return true; return true;

View File

@ -11,10 +11,7 @@ import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requir
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { ModifierTier } from "#enums/modifier-tier"; import { ModifierTier } from "#enums/modifier-tier";
import type { ModifierTypeOption } from "#app/modifier/modifier-type"; import type { ModifierTypeOption } from "#app/modifier/modifier-type";
import { import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
getPlayerModifierTypeOptions,
regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type";
import { ModifierPoolType } from "#enums/modifier-pool-type"; import { ModifierPoolType } from "#enums/modifier-pool-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";

View File

@ -7,10 +7,7 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { ModifierTier } from "#enums/modifier-tier"; import { ModifierTier } from "#enums/modifier-tier";
import { MusicPreference } from "#app/system/settings/settings"; import { MusicPreference } from "#app/system/settings/settings";
import type { ModifierTypeOption } from "#app/modifier/modifier-type"; import type { ModifierTypeOption } from "#app/modifier/modifier-type";
import { import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
getPlayerModifierTypeOptions,
regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type";
import { ModifierPoolType } from "#enums/modifier-pool-type"; import { ModifierPoolType } from "#enums/modifier-pool-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";

View File

@ -11,7 +11,7 @@ import { WeatherType } from "#enums/weather-type";
import type { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { AttackTypeBoosterModifier } from "#app/modifier/modifier"; import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type"; import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
import { isNullOrUndefined } from "#app/utils/common"; import { coerceArray, isNullOrUndefined } from "#app/utils/common";
import type { AbilityId } from "#enums/ability-id"; import type { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
@ -272,7 +272,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) { constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
super(); super();
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay]; this.requiredTimeOfDay = coerceArray(timeOfDay);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -294,7 +294,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
constructor(weather: WeatherType | WeatherType[]) { constructor(weather: WeatherType | WeatherType[]) {
super(); super();
this.requiredWeather = Array.isArray(weather) ? weather : [weather]; this.requiredWeather = coerceArray(weather);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -360,7 +360,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
constructor(heldItem: string | string[], minNumberOfItems = 1) { constructor(heldItem: string | string[], minNumberOfItems = 1) {
super(); super();
this.minNumberOfItems = minNumberOfItems; this.minNumberOfItems = minNumberOfItems;
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem]; this.requiredHeldItemModifiers = coerceArray(heldItem);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -426,7 +426,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredSpecies = Array.isArray(species) ? species : [species]; this.requiredSpecies = coerceArray(species);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -466,7 +466,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredNature = Array.isArray(nature) ? nature : [nature]; this.requiredNature = coerceArray(nature);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -504,7 +504,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
this.excludeFainted = excludeFainted; this.excludeFainted = excludeFainted;
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredType = Array.isArray(type) ? type : [type]; this.requiredType = coerceArray(type);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -558,7 +558,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
this.excludeDisallowedPokemon = excludeDisallowedPokemon; this.excludeDisallowedPokemon = excludeDisallowedPokemon;
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredMoves = Array.isArray(moves) ? moves : [moves]; this.requiredMoves = coerceArray(moves);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -609,7 +609,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove]; this.requiredMoves = coerceArray(learnableMove);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -665,7 +665,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
this.excludeDisallowedPokemon = excludeDisallowedPokemon; this.excludeDisallowedPokemon = excludeDisallowedPokemon;
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities]; this.requiredAbilities = coerceArray(abilities);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -710,7 +710,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect]; this.requiredStatusEffect = coerceArray(statusEffect);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -785,7 +785,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem]; this.requiredFormChangeItem = coerceArray(formChangeItem);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -843,7 +843,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems]; this.requiredEvolutionItem = coerceArray(evolutionItems);
} }
override meetsRequirement(): boolean { override meetsRequirement(): boolean {
@ -908,7 +908,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem]; this.requiredHeldItemModifiers = coerceArray(heldItem);
this.requireTransferable = requireTransferable; this.requireTransferable = requireTransferable;
} }
@ -972,7 +972,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
super(); super();
this.minNumberOfPokemon = minNumberOfPokemon; this.minNumberOfPokemon = minNumberOfPokemon;
this.invertQuery = invertQuery; this.invertQuery = invertQuery;
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes]; this.requiredHeldItemTypes = coerceArray(heldItemTypes);
this.requireTransferable = requireTransferable; this.requireTransferable = requireTransferable;
} }

View File

@ -2,7 +2,7 @@ import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encoun
import type { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type { PokemonMove } from "../moves/pokemon-move"; import type { PokemonMove } from "../moves/pokemon-move";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common"; import { capitalizeFirstLetter, coerceArray, isNullOrUndefined } from "#app/utils/common";
import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro"; import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
@ -717,7 +717,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withAnimations( withAnimations(
...encounterAnimations: EncounterAnim[] ...encounterAnimations: EncounterAnim[]
): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> { ): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations]; const animations = coerceArray(encounterAnimations);
return Object.assign(this, { encounterAnimations: animations }); return Object.assign(this, { encounterAnimations: animations });
} }
@ -729,7 +729,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withDisallowedGameModes( withDisallowedGameModes(
...disallowedGameModes: GameModes[] ...disallowedGameModes: GameModes[]
): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> { ): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
const gameModes = Array.isArray(disallowedGameModes) ? disallowedGameModes : [disallowedGameModes]; const gameModes = coerceArray(disallowedGameModes);
return Object.assign(this, { disallowedGameModes: gameModes }); return Object.assign(this, { disallowedGameModes: gameModes });
} }
@ -741,7 +741,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
withDisallowedChallenges( withDisallowedChallenges(
...disallowedChallenges: Challenges[] ...disallowedChallenges: Challenges[]
): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> { ): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
const challenges = Array.isArray(disallowedChallenges) ? disallowedChallenges : [disallowedChallenges]; const challenges = coerceArray(disallowedChallenges);
return Object.assign(this, { disallowedChallenges: challenges }); return Object.assign(this, { disallowedChallenges: challenges });
} }

View File

@ -1,7 +1,7 @@
import type { MoveId } from "#enums/move-id"; import type { MoveId } from "#enums/move-id";
import type { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { PokemonMove } from "#app/data/moves/pokemon-move"; import { PokemonMove } from "#app/data/moves/pokemon-move";
import { isNullOrUndefined } from "#app/utils/common"; import { coerceArray, isNullOrUndefined } from "#app/utils/common";
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
@ -29,7 +29,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
constructor(requiredMoves: MoveId | MoveId[], options: CanLearnMoveRequirementOptions = {}) { constructor(requiredMoves: MoveId | MoveId[], options: CanLearnMoveRequirementOptions = {}) {
super(); super();
this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves]; this.requiredMoves = coerceArray(requiredMoves);
this.excludeLevelMoves = options.excludeLevelMoves ?? false; this.excludeLevelMoves = options.excludeLevelMoves ?? false;
this.excludeTmMoves = options.excludeTmMoves ?? false; this.excludeTmMoves = options.excludeTmMoves ?? false;

View File

@ -25,7 +25,7 @@ import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-optio
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
import { PartyUiMode } from "#app/ui/party-ui-handler"; import { PartyUiMode } from "#app/ui/party-ui-handler";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils/common"; import { isNullOrUndefined, randSeedInt, randomString, randSeedItem, coerceArray } from "#app/utils/common";
import type { BattlerTagType } from "#enums/battler-tag-type"; import type { BattlerTagType } from "#enums/battler-tag-type";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
import type { TrainerType } from "#enums/trainer-type"; import type { TrainerType } from "#enums/trainer-type";
@ -449,7 +449,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
* @param moves * @param moves
*/ */
export function loadCustomMovesForEncounter(moves: MoveId | MoveId[]) { export function loadCustomMovesForEncounter(moves: MoveId | MoveId[]) {
moves = Array.isArray(moves) ? moves : [moves]; moves = coerceArray(moves);
return Promise.all(moves.map(move => initMoveAnim(move))).then(() => loadMoveAnimAssets(moves)); return Promise.all(moves.map(move => initMoveAnim(move))).then(() => loadMoveAnimAssets(moves));
} }
@ -792,7 +792,7 @@ export function setEncounterRewards(
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue * @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
*/ */
export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex = true) { export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex = true) {
const participantIds = Array.isArray(participantId) ? participantId : [participantId]; const participantIds = coerceArray(participantId);
globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => { globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds)); globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds));

View File

@ -1,5 +1,5 @@
import i18next from "i18next"; import i18next from "i18next";
import type { Constructor } from "#app/utils/common"; import { coerceArray, type Constructor } from "#app/utils/common";
import type { TimeOfDay } from "#enums/time-of-day"; import type { TimeOfDay } from "#enums/time-of-day";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import type { SpeciesFormChange } from "#app/data/pokemon-forms"; import type { SpeciesFormChange } from "#app/data/pokemon-forms";
@ -125,10 +125,7 @@ export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigg
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) { constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
super(); super();
if (!Array.isArray(statusEffects)) { this.statusEffects = coerceArray(statusEffects);
statusEffects = [statusEffects];
}
this.statusEffects = statusEffects;
this.invert = invert; this.invert = invert;
// this.description = i18next.t("pokemonEvolutions:Forms.statusEffect"); // this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
} }

View File

@ -1,7 +1,14 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { modifierTypes } from "../data-lists"; import { modifierTypes } from "../data-lists";
import { PokemonMove } from "../moves/pokemon-move"; import { PokemonMove } from "../moves/pokemon-move";
import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt, randSeedIntRange } from "#app/utils/common"; import {
toReadableString,
isNullOrUndefined,
randSeedItem,
randSeedInt,
coerceArray,
randSeedIntRange,
} from "#app/utils/common";
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { tmSpecies } from "#app/data/balance/tms"; import { tmSpecies } from "#app/data/balance/tms";
@ -554,10 +561,7 @@ export class TrainerConfig {
this.speciesPools = evilAdminTrainerPools[poolName]; this.speciesPools = evilAdminTrainerPools[poolName];
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
if (!Array.isArray(speciesPool)) { this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
speciesPool = [speciesPool];
}
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");
@ -620,10 +624,7 @@ export class TrainerConfig {
this.setPartyTemplates(trainerPartyTemplates.RIVAL_5); this.setPartyTemplates(trainerPartyTemplates.RIVAL_5);
} }
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
if (!Array.isArray(speciesPool)) { this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
speciesPool = [speciesPool];
}
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
if (!isNullOrUndefined(specialtyType)) { if (!isNullOrUndefined(specialtyType)) {
this.setSpeciesFilter(p => p.isOfType(specialtyType)); this.setSpeciesFilter(p => p.isOfType(specialtyType));
@ -668,12 +669,8 @@ export class TrainerConfig {
// Set up party members with their corresponding species. // Set up party members with their corresponding species.
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
// Ensure speciesPool is an array.
if (!Array.isArray(speciesPool)) {
speciesPool = [speciesPool];
}
// Set a function to get a random party member from the species pool. // Set a function to get a random party member from the species pool.
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
}); });
// If specialty type is provided, set species filter and specialty type. // If specialty type is provided, set species filter and specialty type.
@ -729,12 +726,8 @@ export class TrainerConfig {
// Set up party members with their corresponding species. // Set up party members with their corresponding species.
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
// Ensure speciesPool is an array.
if (!Array.isArray(speciesPool)) {
speciesPool = [speciesPool];
}
// Set a function to get a random party member from the species pool. // Set a function to get a random party member from the species pool.
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(coerceArray(speciesPool)));
}); });
// Set species filter and specialty type if provided, otherwise filter by base total. // Set species filter and specialty type if provided, otherwise filter by base total.

View File

@ -262,7 +262,7 @@ export class Arena {
return 5; return 5;
} }
break; break;
case SpeciesId.LYCANROC: case SpeciesId.LYCANROC: {
const timeOfDay = this.getTimeOfDay(); const timeOfDay = this.getTimeOfDay();
switch (timeOfDay) { switch (timeOfDay) {
case TimeOfDay.DAY: case TimeOfDay.DAY:
@ -275,6 +275,7 @@ export class Arena {
} }
break; break;
} }
}
return 0; return 0;
} }

View File

@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import Pokemon from "./pokemon"; import Pokemon from "./pokemon";
import { fixedInt, randInt } from "#app/utils/common"; import { fixedInt, coerceArray, randInt } from "#app/utils/common";
export default class PokemonSpriteSparkleHandler { export default class PokemonSpriteSparkleHandler {
private sprites: Set<Phaser.GameObjects.Sprite>; private sprites: Set<Phaser.GameObjects.Sprite>;
@ -57,9 +57,7 @@ export default class PokemonSpriteSparkleHandler {
} }
add(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void { add(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
if (!Array.isArray(sprites)) { sprites = coerceArray(sprites);
sprites = [sprites];
}
for (const s of sprites) { for (const s of sprites) {
if (this.sprites.has(s)) { if (this.sprites.has(s)) {
continue; continue;
@ -69,9 +67,7 @@ export default class PokemonSpriteSparkleHandler {
} }
remove(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void { remove(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
if (!Array.isArray(sprites)) { sprites = coerceArray(sprites);
sprites = [sprites];
}
for (const s of sprites) { for (const s of sprites) {
this.sprites.delete(s); this.sprites.delete(s);
} }

View File

@ -41,6 +41,7 @@ import {
type nil, type nil,
type Constructor, type Constructor,
randSeedIntRange, randSeedIntRange,
coerceArray,
} from "#app/utils/common"; } from "#app/utils/common";
import type { TypeDamageMultiplier } from "#app/data/type"; import type { TypeDamageMultiplier } from "#app/data/type";
import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type"; import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
@ -1774,9 +1775,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
let overrideArray: MoveId | Array<MoveId> = this.isPlayer() let overrideArray: MoveId | Array<MoveId> = this.isPlayer()
? Overrides.MOVESET_OVERRIDE ? Overrides.MOVESET_OVERRIDE
: Overrides.OPP_MOVESET_OVERRIDE; : Overrides.OPP_MOVESET_OVERRIDE;
if (!Array.isArray(overrideArray)) { overrideArray = coerceArray(overrideArray);
overrideArray = [overrideArray];
}
if (overrideArray.length > 0) { if (overrideArray.length > 0) {
if (!this.isPlayer()) { if (!this.isPlayer()) {
this.moveset = []; this.moveset = [];
@ -4386,14 +4385,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// biome-ignore lint: there are a ton of issues.. // biome-ignore lint: there are a ton of issues..
faintCry(callback: Function): void { faintCry(callback: Function): void {
if (this.fusionSpecies && this.getSpeciesForm() !== this.getFusionSpeciesForm()) { if (this.fusionSpecies && this.getSpeciesForm() !== this.getFusionSpeciesForm()) {
return this.fusionFaintCry(callback); this.fusionFaintCry(callback);
return;
} }
const key = this.species.getCryKey(this.formIndex); const key = this.species.getCryKey(this.formIndex);
let rate = 0.85; let rate = 0.85;
const cry = globalScene.playSound(key, { rate: rate }) as AnySound; const cry = globalScene.playSound(key, { rate: rate }) as AnySound;
if (!cry || globalScene.fieldVolume === 0) { if (!cry || globalScene.fieldVolume === 0) {
return callback(); callback();
return;
} }
const sprite = this.getSprite(); const sprite = this.getSprite();
const tintSprite = this.getTintSprite(); const tintSprite = this.getTintSprite();
@ -4461,7 +4462,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
rate: rate, rate: rate,
}) as AnySound; }) as AnySound;
if (!cry || !fusionCry || globalScene.fieldVolume === 0) { if (!cry || !fusionCry || globalScene.fieldVolume === 0) {
return callback(); callback();
return;
} }
fusionCry.stop(); fusionCry.stop();
duration = Math.min(duration, fusionCry.totalDuration * 1000); duration = Math.min(duration, fusionCry.totalDuration * 1000);

View File

@ -2393,8 +2393,6 @@ export interface ModifierPool {
[tier: string]: WeightedModifierType[]; [tier: string]: WeightedModifierType[];
} }
const modifierPool: ModifierPool = {};
let modifierPoolThresholds = {}; let modifierPoolThresholds = {};
let ignoredPoolIndexes = {}; let ignoredPoolIndexes = {};
@ -2859,7 +2857,7 @@ function getNewModifierTypeOption(
} }
tier += upgradeCount; tier += upgradeCount;
while (tier && (!modifierPool.hasOwnProperty(tier) || !modifierPool[tier].length)) { while (tier && (!pool.hasOwnProperty(tier) || !pool[tier].length)) {
tier--; tier--;
if (upgradeCount) { if (upgradeCount) {
upgradeCount--; upgradeCount--;
@ -2870,7 +2868,7 @@ function getNewModifierTypeOption(
if (tier < ModifierTier.MASTER && allowLuckUpgrades) { if (tier < ModifierTier.MASTER && allowLuckUpgrades) {
const partyLuckValue = getPartyLuckValue(party); const partyLuckValue = getPartyLuckValue(party);
const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4)); const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4));
while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) { while (pool.hasOwnProperty(tier + upgradeCount + 1) && pool[tier + upgradeCount + 1].length) {
if (randSeedInt(upgradeOdds) < 4) { if (randSeedInt(upgradeOdds) < 4) {
upgradeCount++; upgradeCount++;
} else { } else {
@ -2920,6 +2918,7 @@ function getNewModifierTypeOption(
} }
export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType { export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType {
const modifierPool = getModifierPoolForType(ModifierPoolType.PLAYER);
let modifierType: ModifierType | WeightedModifierType = modifierPool[tier || ModifierTier.COMMON][0]; let modifierType: ModifierType | WeightedModifierType = modifierPool[tier || ModifierTier.COMMON][0];
if (modifierType instanceof WeightedModifierType) { if (modifierType instanceof WeightedModifierType) {
modifierType = (modifierType as WeightedModifierType).modifierType; modifierType = (modifierType as WeightedModifierType).modifierType;

View File

@ -12,7 +12,7 @@ import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase";
import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
import { CommandPhase } from "#app/phases/command-phase"; import { CommandPhase } from "#app/phases/command-phase";
import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import type { Constructor } from "#app/utils/common"; import { coerceArray, type Constructor } from "#app/utils/common";
import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
import type { DynamicPhaseType } from "#enums/dynamic-phase-type"; import type { DynamicPhaseType } from "#enums/dynamic-phase-type";
import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; import { EggHatchPhase } from "#app/phases/egg-hatch-phase";
@ -438,9 +438,7 @@ export class PhaseManager {
* @returns boolean if a targetPhase was found and added * @returns boolean if a targetPhase was found and added
*/ */
prependToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean { prependToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean {
if (!Array.isArray(phase)) { phase = coerceArray(phase);
phase = [phase];
}
const target = PHASES[targetPhase]; const target = PHASES[targetPhase];
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target); const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target);
@ -460,9 +458,7 @@ export class PhaseManager {
* @returns `true` if a `targetPhase` was found to append to * @returns `true` if a `targetPhase` was found to append to
*/ */
appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString, condition?: (p: Phase) => boolean): boolean { appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString, condition?: (p: Phase) => boolean): boolean {
if (!Array.isArray(phase)) { phase = coerceArray(phase);
phase = [phase];
}
const target = PHASES[targetPhase]; const target = PHASES[targetPhase];
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target && (!condition || condition(ph))); const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target && (!condition || condition(ph)));

View File

@ -1,8 +1,5 @@
import { ModifierTier } from "#enums/modifier-tier"; import { ModifierTier } from "#enums/modifier-tier";
import { import { regenerateModifierPoolThresholds, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type";
regenerateModifierPoolThresholds,
getEnemyBuffModifierForWave,
} from "#app/modifier/modifier-type";
import { ModifierPoolType } from "#enums/modifier-pool-type"; import { ModifierPoolType } from "#enums/modifier-pool-type";
import { EnemyPersistentModifier } from "#app/modifier/modifier"; import { EnemyPersistentModifier } from "#app/modifier/modifier";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";

View File

@ -149,7 +149,7 @@ export class CommandPhase extends FieldPhase {
switch (command) { switch (command) {
case Command.TERA: case Command.TERA:
case Command.FIGHT: case Command.FIGHT: {
let useStruggle = false; let useStruggle = false;
const turnMove: TurnMove | undefined = args.length === 2 ? (args[1] as TurnMove) : undefined; const turnMove: TurnMove | undefined = args.length === 2 ? (args[1] as TurnMove) : undefined;
if ( if (
@ -233,7 +233,8 @@ export class CommandPhase extends FieldPhase {
); );
} }
break; break;
case Command.BALL: }
case Command.BALL: {
const notInDex = const notInDex =
globalScene globalScene
.getEnemyField() .getEnemyField()
@ -337,8 +338,9 @@ export class CommandPhase extends FieldPhase {
} }
} }
break; break;
}
case Command.POKEMON: case Command.POKEMON:
case Command.RUN: case Command.RUN: {
const isSwitch = command === Command.POKEMON; const isSwitch = command === Command.POKEMON;
const { currentBattle, arena } = globalScene; const { currentBattle, arena } = globalScene;
const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed; const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed;
@ -446,6 +448,7 @@ export class CommandPhase extends FieldPhase {
} }
break; break;
} }
}
if (success) { if (success) {
this.end(); this.end();

View File

@ -135,7 +135,8 @@ export class MovePhase extends BattlePhase {
this.showMoveText(); this.showMoveText();
this.showFailedText(); this.showFailedText();
} }
return this.end(); this.end();
return;
} }
this.pokemon.turnData.acted = true; this.pokemon.turnData.acted = true;
@ -310,7 +311,8 @@ export class MovePhase extends BattlePhase {
if (fail) { if (fail) {
this.showMoveText(); this.showMoveText();
this.showFailedText(); this.showFailedText();
return this.end(); this.end();
return;
} }
} }

View File

@ -53,7 +53,8 @@ export class PokemonAnimPhase extends BattlePhase {
private doSubstituteAddAnim(): void { private doSubstituteAddAnim(): void {
const substitute = this.pokemon.getTag(SubstituteTag); const substitute = this.pokemon.getTag(SubstituteTag);
if (isNullOrUndefined(substitute)) { if (isNullOrUndefined(substitute)) {
return this.end(); this.end();
return;
} }
const getSprite = () => { const getSprite = () => {
@ -116,12 +117,14 @@ export class PokemonAnimPhase extends BattlePhase {
private doSubstitutePreMoveAnim(): void { private doSubstitutePreMoveAnim(): void {
if (this.fieldAssets.length !== 1) { if (this.fieldAssets.length !== 1) {
return this.end(); this.end();
return;
} }
const subSprite = this.fieldAssets[0]; const subSprite = this.fieldAssets[0];
if (subSprite === undefined) { if (subSprite === undefined) {
return this.end(); this.end();
return;
} }
globalScene.tweens.add({ globalScene.tweens.add({
@ -145,12 +148,14 @@ export class PokemonAnimPhase extends BattlePhase {
private doSubstitutePostMoveAnim(): void { private doSubstitutePostMoveAnim(): void {
if (this.fieldAssets.length !== 1) { if (this.fieldAssets.length !== 1) {
return this.end(); this.end();
return;
} }
const subSprite = this.fieldAssets[0]; const subSprite = this.fieldAssets[0];
if (subSprite === undefined) { if (subSprite === undefined) {
return this.end(); this.end();
return;
} }
globalScene.tweens.add({ globalScene.tweens.add({
@ -174,12 +179,14 @@ export class PokemonAnimPhase extends BattlePhase {
private doSubstituteRemoveAnim(): void { private doSubstituteRemoveAnim(): void {
if (this.fieldAssets.length !== 1) { if (this.fieldAssets.length !== 1) {
return this.end(); this.end();
return;
} }
const subSprite = this.fieldAssets[0]; const subSprite = this.fieldAssets[0];
if (subSprite === undefined) { if (subSprite === undefined) {
return this.end(); this.end();
return;
} }
const getSprite = () => { const getSprite = () => {
@ -244,12 +251,14 @@ export class PokemonAnimPhase extends BattlePhase {
private doCommanderApplyAnim(): void { private doCommanderApplyAnim(): void {
if (!globalScene.currentBattle?.double) { if (!globalScene.currentBattle?.double) {
return this.end(); this.end();
return;
} }
const dondozo = this.pokemon.getAlly(); const dondozo = this.pokemon.getAlly();
if (dondozo?.species?.speciesId !== SpeciesId.DONDOZO) { if (dondozo?.species?.speciesId !== SpeciesId.DONDOZO) {
return this.end(); this.end();
return;
} }
const tatsugiriX = this.pokemon.x + this.pokemon.getSprite().x; const tatsugiriX = this.pokemon.x + this.pokemon.getSprite().x;
@ -329,7 +338,8 @@ export class PokemonAnimPhase extends BattlePhase {
const tatsugiri = this.pokemon.getAlly(); const tatsugiri = this.pokemon.getAlly();
if (isNullOrUndefined(tatsugiri)) { if (isNullOrUndefined(tatsugiri)) {
console.warn("Aborting COMMANDER_REMOVE anim: Tatsugiri is undefined"); console.warn("Aborting COMMANDER_REMOVE anim: Tatsugiri is undefined");
return this.end(); this.end();
return;
} }
const tatsuSprite = globalScene.addPokemonSprite( const tatsuSprite = globalScene.addPokemonSprite(

View File

@ -29,7 +29,8 @@ export class PokemonTransformPhase extends PokemonPhase {
const target = globalScene.getField(true).find(p => p.getBattlerIndex() === this.targetIndex); const target = globalScene.getField(true).find(p => p.getBattlerIndex() === this.targetIndex);
if (!target) { if (!target) {
return this.end(); this.end();
return;
} }
user.summonData.speciesForm = target.getSpeciesForm(); user.summonData.speciesForm = target.getSpeciesForm();

View File

@ -29,7 +29,8 @@ export class QuietFormChangePhase extends BattlePhase {
super.start(); super.start();
if (this.pokemon.formIndex === this.pokemon.species.forms.findIndex(f => f.formKey === this.formChange.formKey)) { if (this.pokemon.formIndex === this.pokemon.species.forms.findIndex(f => f.formKey === this.formChange.formKey)) {
return this.end(); this.end();
return;
} }
const preName = getPokemonNameWithAffix(this.pokemon); const preName = getPokemonNameWithAffix(this.pokemon);

View File

@ -1,10 +1,8 @@
import { coerceArray } from "#app/utils/common";
let manifest: object; let manifest: object;
export default class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin { export default class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin {
constructor(scene: Phaser.Scene) {
super(scene);
}
get manifest() { get manifest() {
return manifest; return manifest;
} }
@ -14,9 +12,7 @@ export default class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin
} }
addFile(file): void { addFile(file): void {
if (!Array.isArray(file)) { file = coerceArray(file);
file = [file];
}
file.forEach(item => { file.forEach(item => {
if (manifest) { if (manifest) {

View File

@ -174,7 +174,24 @@ export async function initI18n(): Promise<void> {
"es-MX": ["es-ES", "en"], "es-MX": ["es-ES", "en"],
default: ["en"], default: ["en"],
}, },
supportedLngs: ["en", "es-ES", "es-MX", "fr", "it", "de", "zh-CN", "zh-TW", "pt-BR", "ko", "ja", "ca", "da", "tr", "ro", "ru"], supportedLngs: [
"en",
"es-ES",
"es-MX",
"fr",
"it",
"de",
"zh-CN",
"zh-TW",
"pt-BR",
"ko",
"ja",
"ca",
"da",
"tr",
"ro",
"ru",
],
backend: { backend: {
loadPath(lng: string, [ns]: string[]) { loadPath(lng: string, [ns]: string[]) {
let fileName: string; let fileName: string;

View File

@ -41,9 +41,9 @@ export function minifyJsonPlugin(basePath: string | string[], recursive?: boolea
}, },
async closeBundle() { async closeBundle() {
console.log("Minifying JSON files..."); console.log("Minifying JSON files...");
const basePathes = Array.isArray(basePath) ? basePath : [basePath]; const basePaths = Array.isArray(basePath) ? basePath : [basePath];
basePathes.forEach(basePath => { basePaths.forEach(basePath => {
const baseDir = path.resolve(buildDir, basePath); const baseDir = path.resolve(buildDir, basePath);
if (fs.existsSync(baseDir)) { if (fs.existsSync(baseDir)) {
applyToDir(baseDir, recursive); applyToDir(baseDir, recursive);

View File

@ -1,3 +1,5 @@
import { coerceArray } from "#app/utils/common";
export const legacyCompatibleImages: string[] = []; export const legacyCompatibleImages: string[] = [];
export class SceneBase extends Phaser.Scene { export class SceneBase extends Phaser.Scene {
@ -88,9 +90,7 @@ export class SceneBase extends Phaser.Scene {
} else { } else {
folder += "/"; folder += "/";
} }
if (!Array.isArray(filenames)) { filenames = coerceArray(filenames);
filenames = [filenames];
}
for (const f of filenames as string[]) { for (const f of filenames as string[]) {
this.load.audio(folder + key, this.getCachedUrl(`audio/${folder}${f}`)); this.load.audio(folder + key, this.getCachedUrl(`audio/${folder}${f}`));
} }

View File

@ -32,7 +32,10 @@ const pressAction = i18next.t("settings:pressActionToAssign");
export const settingGamepadOptions = { export const settingGamepadOptions = {
[SettingGamepad.Controller]: [i18next.t("settings:controllerDefault"), i18next.t("settings:controllerChange")], [SettingGamepad.Controller]: [i18next.t("settings:controllerDefault"), i18next.t("settings:controllerChange")],
[SettingGamepad.Gamepad_Support]: [i18next.t("settings:gamepadSupportAuto"), i18next.t("settings:gamepadSupportDisabled")], [SettingGamepad.Gamepad_Support]: [
i18next.t("settings:gamepadSupportAuto"),
i18next.t("settings:gamepadSupportDisabled"),
],
[SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction], [SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction],
[SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction], [SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction],
[SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction], [SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction],

View File

@ -959,7 +959,7 @@ export function setSetting(setting: string, value: number): boolean {
}, },
{ {
label: "Türkçe (Needs Help)", label: "Türkçe (Needs Help)",
handler: () => changeLocaleHandler("tr") handler: () => changeLocaleHandler("tr"),
}, },
{ {
label: "Русский (Needs Help)", label: "Русский (Needs Help)",
@ -967,11 +967,11 @@ export function setSetting(setting: string, value: number): boolean {
}, },
{ {
label: "Dansk (Needs Help)", label: "Dansk (Needs Help)",
handler: () => changeLocaleHandler("da") handler: () => changeLocaleHandler("da"),
}, },
{ {
label: "Română (Needs Help)", label: "Română (Needs Help)",
handler: () => changeLocaleHandler("ro") handler: () => changeLocaleHandler("ro"),
}, },
{ {
label: i18next.t("settings:back"), label: i18next.t("settings:back"),

View File

@ -56,10 +56,6 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
protected defaultTextStyle: TextStyle = TextStyle.WINDOW; protected defaultTextStyle: TextStyle = TextStyle.WINDOW;
protected textContent: string; protected textContent: string;
constructor(mode: UiMode | null) {
super(mode);
}
abstract getWindowWidth(): number; abstract getWindowWidth(): number;
getWindowHeight(): number { getWindowHeight(): number {

View File

@ -69,7 +69,7 @@ export default class AdminUiHandler extends FormModalUiHandler {
case AdminMode.SEARCH: case AdminMode.SEARCH:
inputFieldConfigs.push({ label: "Username" }); inputFieldConfigs.push({ label: "Username" });
break; break;
case AdminMode.ADMIN: case AdminMode.ADMIN: {
const adminResult = this.adminResult ?? { const adminResult = this.adminResult ?? {
username: "", username: "",
discordId: "", discordId: "",
@ -91,6 +91,7 @@ export default class AdminUiHandler extends FormModalUiHandler {
inputFieldConfigs.push({ label: "Registered", isReadOnly: true }); inputFieldConfigs.push({ label: "Registered", isReadOnly: true });
break; break;
} }
}
return inputFieldConfigs; return inputFieldConfigs;
} }

View File

@ -169,12 +169,13 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container {
entryContainer.add(scoreLabel); entryContainer.add(scoreLabel);
switch (this.category) { switch (this.category) {
case ScoreboardCategory.DAILY: case ScoreboardCategory.DAILY: {
const waveLabel = addTextObject(68, 0, wave, TextStyle.WINDOW, { const waveLabel = addTextObject(68, 0, wave, TextStyle.WINDOW, {
fontSize: "54px", fontSize: "54px",
}); });
entryContainer.add(waveLabel); entryContainer.add(waveLabel);
break; break;
}
case ScoreboardCategory.WEEKLY: case ScoreboardCategory.WEEKLY:
scoreLabel.x -= 16; scoreLabel.x -= 16;
break; break;

View File

@ -131,7 +131,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
gachaInfoContainer.add(gachaUpLabel); gachaInfoContainer.add(gachaUpLabel);
switch (gachaType as GachaType) { switch (gachaType as GachaType) {
case GachaType.LEGENDARY: case GachaType.LEGENDARY: {
if (["de", "es-ES"].includes(currentLanguage)) { if (["de", "es-ES"].includes(currentLanguage)) {
gachaUpLabel.setAlign("center"); gachaUpLabel.setAlign("center");
gachaUpLabel.setY(0); gachaUpLabel.setY(0);
@ -152,6 +152,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
gachaInfoContainer.add(pokemonIcon); gachaInfoContainer.add(pokemonIcon);
break; break;
}
case GachaType.MOVE: case GachaType.MOVE:
if (["de", "es-ES", "fr", "pt-BR", "ru"].includes(currentLanguage)) { if (["de", "es-ES", "fr", "pt-BR", "ru"].includes(currentLanguage)) {
gachaUpLabel.setAlign("center"); gachaUpLabel.setAlign("center");
@ -623,13 +624,14 @@ export default class EggGachaUiHandler extends MessageUiHandler {
updateGachaInfo(gachaType: GachaType): void { updateGachaInfo(gachaType: GachaType): void {
const infoContainer = this.gachaInfoContainers[gachaType]; const infoContainer = this.gachaInfoContainers[gachaType];
switch (gachaType as GachaType) { switch (gachaType as GachaType) {
case GachaType.LEGENDARY: case GachaType.LEGENDARY: {
const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(new Date().getTime())); const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(new Date().getTime()));
const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite; const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite;
pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false)); pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false));
break; break;
} }
} }
}
consumeVouchers(voucherType: VoucherType, count: number): void { consumeVouchers(voucherType: VoucherType, count: number): void {
globalScene.gameData.voucherCounts[voucherType] = Math.max( globalScene.gameData.voucherCounts[voucherType] = Math.max(

View File

@ -686,7 +686,7 @@ export default class MenuUiHandler extends MessageUiHandler {
error = true; error = true;
} }
break; break;
case MenuOptions.LOG_OUT: case MenuOptions.LOG_OUT: {
success = true; success = true;
const doLogout = () => { const doLogout = () => {
ui.setMode(UiMode.LOADING, { ui.setMode(UiMode.LOADING, {
@ -719,6 +719,7 @@ export default class MenuUiHandler extends MessageUiHandler {
} }
break; break;
} }
}
} else if (button === Button.CANCEL) { } else if (button === Button.CANCEL) {
success = true; success = true;
ui.revertMode().then(result => { ui.revertMode().then(result => {

View File

@ -1385,7 +1385,7 @@ export default class PartyUiHandler extends MessageUiHandler {
case PartyOption.MOVE_1: case PartyOption.MOVE_1:
case PartyOption.MOVE_2: case PartyOption.MOVE_2:
case PartyOption.MOVE_3: case PartyOption.MOVE_3:
case PartyOption.MOVE_4: case PartyOption.MOVE_4: {
const move = pokemon.moveset[option - PartyOption.MOVE_1]; const move = pokemon.moveset[option - PartyOption.MOVE_1];
if (this.showMovePp) { if (this.showMovePp) {
const maxPP = move.getMovePp(); const maxPP = move.getMovePp();
@ -1395,7 +1395,8 @@ export default class PartyUiHandler extends MessageUiHandler {
optionName = move.getName(); optionName = move.getName();
} }
break; break;
default: }
default: {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
if (formChangeItemModifiers && option >= PartyOption.FORM_CHANGE_ITEM) { if (formChangeItemModifiers && option >= PartyOption.FORM_CHANGE_ITEM) {
const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM];
@ -1411,6 +1412,7 @@ export default class PartyUiHandler extends MessageUiHandler {
} }
break; break;
} }
}
} else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { } else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) {
const learnableLevelMoves = pokemon.getLearnableLevelMoves(); const learnableLevelMoves = pokemon.getLearnableLevelMoves();
const move = learnableLevelMoves[option]; const move = learnableLevelMoves[option];

View File

@ -1,5 +1,5 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { fixedInt } from "#app/utils/common"; import { fixedInt, coerceArray } from "#app/utils/common";
export enum PokemonIconAnimMode { export enum PokemonIconAnimMode {
NONE, NONE,
@ -49,9 +49,7 @@ export default class PokemonIconAnimHandler {
} }
addOrUpdate(icons: PokemonIcon | PokemonIcon[], mode: PokemonIconAnimMode): void { addOrUpdate(icons: PokemonIcon | PokemonIcon[], mode: PokemonIconAnimMode): void {
if (!Array.isArray(icons)) { icons = coerceArray(icons);
icons = [icons];
}
for (const i of icons) { for (const i of icons) {
if (this.icons.has(i) && this.icons.get(i) === mode) { if (this.icons.has(i) && this.icons.get(i) === mode) {
continue; continue;
@ -66,9 +64,7 @@ export default class PokemonIconAnimHandler {
} }
remove(icons: PokemonIcon | PokemonIcon[]): void { remove(icons: PokemonIcon | PokemonIcon[]): void {
if (!Array.isArray(icons)) { icons = coerceArray(icons);
icons = [icons];
}
for (const i of icons) { for (const i of icons) {
if (this.toggled) { if (this.toggled) {
const icon = this.icons.get(i); const icon = this.icons.get(i);

View File

@ -567,7 +567,7 @@ export default class RunInfoUiHandler extends UiHandler {
case GameModes.SPLICED_ENDLESS: case GameModes.SPLICED_ENDLESS:
modeText.appendText(`${i18next.t("gameMode:endlessSpliced")}`, false); modeText.appendText(`${i18next.t("gameMode:endlessSpliced")}`, false);
break; break;
case GameModes.CHALLENGE: case GameModes.CHALLENGE: {
modeText.appendText(`${i18next.t("gameMode:challenge")}`, false); modeText.appendText(`${i18next.t("gameMode:challenge")}`, false);
modeText.appendText(`${i18next.t("runHistory:challengeRules")}: `); modeText.appendText(`${i18next.t("runHistory:challengeRules")}: `);
modeText.setWrapMode(1); // wrap by word modeText.setWrapMode(1); // wrap by word
@ -582,6 +582,7 @@ export default class RunInfoUiHandler extends UiHandler {
} }
} }
break; break;
}
case GameModes.ENDLESS: case GameModes.ENDLESS:
modeText.appendText(`${i18next.t("gameMode:endless")}`, false); modeText.appendText(`${i18next.t("gameMode:endless")}`, false);
break; break;
@ -687,7 +688,7 @@ export default class RunInfoUiHandler extends UiHandler {
case Challenges.SINGLE_GENERATION: case Challenges.SINGLE_GENERATION:
rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`)); rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`));
break; break;
case Challenges.SINGLE_TYPE: case Challenges.SINGLE_TYPE: {
const typeRule = PokemonType[this.runInfo.challenges[i].value - 1]; const typeRule = PokemonType[this.runInfo.challenges[i].value - 1];
const typeTextColor = `[color=${TypeColor[typeRule]}]`; const typeTextColor = `[color=${TypeColor[typeRule]}]`;
const typeShadowColor = `[shadow=${TypeShadow[typeRule]}]`; const typeShadowColor = `[shadow=${TypeShadow[typeRule]}]`;
@ -695,10 +696,11 @@ export default class RunInfoUiHandler extends UiHandler {
typeTextColor + typeShadowColor + i18next.t(`pokemonInfo:Type.${typeRule}`)! + "[/color]" + "[/shadow]"; typeTextColor + typeShadowColor + i18next.t(`pokemonInfo:Type.${typeRule}`)! + "[/color]" + "[/shadow]";
rules.push(typeText); rules.push(typeText);
break; break;
}
case Challenges.INVERSE_BATTLE: case Challenges.INVERSE_BATTLE:
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 localisationKey = 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()))
@ -708,6 +710,7 @@ export default class RunInfoUiHandler extends UiHandler {
} }
} }
} }
}
return rules; return rules;
} }

View File

@ -126,6 +126,11 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
); );
this.actionsBg.setOrigin(0, 0); this.actionsBg.setOrigin(0, 0);
/*
* If there isn't enough space to fit all the icons and texts, there will be an overlap
* This currently doesn't happen, but it's something to keep in mind.
*/
const iconAction = globalScene.add.sprite(0, 0, "keyboard"); const iconAction = globalScene.add.sprite(0, 0, "keyboard");
iconAction.setOrigin(0, -0.1); iconAction.setOrigin(0, -0.1);
iconAction.setPositionRelative(this.actionsBg, this.navigationContainer.width - 32, 4); iconAction.setPositionRelative(this.actionsBg, this.navigationContainer.width - 32, 4);
@ -137,7 +142,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
const iconCancel = globalScene.add.sprite(0, 0, "keyboard"); const iconCancel = globalScene.add.sprite(0, 0, "keyboard");
iconCancel.setOrigin(0, -0.1); iconCancel.setOrigin(0, -0.1);
iconCancel.setPositionRelative(this.actionsBg, this.navigationContainer.width - 100, 4); iconCancel.setPositionRelative(this.actionsBg, actionText.x - 28, 4);
this.navigationIcons["BUTTON_CANCEL"] = iconCancel; this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
const cancelText = addTextObject(0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL); const cancelText = addTextObject(0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL);
@ -146,7 +151,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
const iconReset = globalScene.add.sprite(0, 0, "keyboard"); const iconReset = globalScene.add.sprite(0, 0, "keyboard");
iconReset.setOrigin(0, -0.1); iconReset.setOrigin(0, -0.1);
iconReset.setPositionRelative(this.actionsBg, this.navigationContainer.width - 180, 4); iconReset.setPositionRelative(this.actionsBg, cancelText.x - 28, 4);
this.navigationIcons["BUTTON_HOME"] = iconReset; this.navigationIcons["BUTTON_HOME"] = iconReset;
const resetText = addTextObject(0, 0, i18next.t("settings:reset"), TextStyle.SETTINGS_LABEL); const resetText = addTextObject(0, 0, i18next.t("settings:reset"), TextStyle.SETTINGS_LABEL);

View File

@ -94,7 +94,7 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler {
const iconCancel = globalScene.add.sprite(0, 0, "keyboard"); const iconCancel = globalScene.add.sprite(0, 0, "keyboard");
iconCancel.setOrigin(0, -0.1); iconCancel.setOrigin(0, -0.1);
iconCancel.setPositionRelative(actionsBg, this.navigationContainer.width - 100, 4); iconCancel.setPositionRelative(actionsBg, actionText.x - 28, 4);
this.navigationIcons["BUTTON_CANCEL"] = iconCancel; this.navigationIcons["BUTTON_CANCEL"] = iconCancel;
const cancelText = addTextObject(0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL); const cancelText = addTextObject(0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL);
@ -332,7 +332,7 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler {
case Button.CYCLE_SHINY: case Button.CYCLE_SHINY:
success = this.navigationContainer.navigate(button); success = this.navigationContainer.navigate(button);
break; break;
case Button.ACTION: case Button.ACTION: {
const setting: Setting = this.settings[cursor]; const setting: Setting = this.settings[cursor];
if (setting?.activatable) { if (setting?.activatable) {
success = this.activateSetting(setting); success = this.activateSetting(setting);
@ -340,6 +340,7 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler {
break; break;
} }
} }
}
// Plays a select sound effect if an action was successfully processed. // Plays a select sound effect if an action was successfully processed.
if (success) { if (success) {

View File

@ -1763,7 +1763,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
} else if (this.randomCursorObj.visible) { } else if (this.randomCursorObj.visible) {
switch (button) { switch (button) {
case Button.ACTION: case Button.ACTION: {
if (this.starterSpecies.length >= 6) { if (this.starterSpecies.length >= 6) {
error = true; error = true;
break; break;
@ -1815,6 +1815,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
}); });
break; break;
}
case Button.UP: case Button.UP:
this.randomCursorObj.setVisible(false); this.randomCursorObj.setVisible(false);
this.filterBarCursor = this.filterBar.numFilters - 1; this.filterBarCursor = this.filterBar.numFilters - 1;

View File

@ -10,10 +10,6 @@ import { UiMode } from "#enums/ui-mode";
export default class TestDialogueUiHandler extends FormModalUiHandler { export default class TestDialogueUiHandler extends FormModalUiHandler {
keys: string[]; keys: string[];
constructor(mode) {
super(mode);
}
setup() { setup() {
super.setup(); super.setup();

View File

@ -611,3 +611,12 @@ export function getShinyDescriptor(variant: Variant): string {
return i18next.t("common:commonShiny"); return i18next.t("common:commonShiny");
} }
} }
/**
* If the input isn't already an array, turns it into one.
* @returns An array with the same type as the type of the input
*/
export function coerceArray<T>(input: T): T extends any[] ? T : [T];
export function coerceArray<T>(input: T): T | [T] {
return Array.isArray(input) ? input : [input];
}

View File

@ -164,15 +164,13 @@ describe("Moves - Last Respects", () => {
await game.toNextWave(); await game.toNextWave();
expect(game.scene.currentBattle.enemyFaints).toBe(0); expect(game.scene.currentBattle.enemyFaints).toBe(0);
game.removeEnemyHeldItems();
game.move.select(MoveId.LAST_RESPECTS); game.move.select(MoveId.LAST_RESPECTS);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("MoveEndPhase"); await game.phaseInterceptor.to("MoveEndPhase");
const enemy = game.field.getEnemyPokemon(); expect(move.calculateBattlePower).toHaveLastReturnedWith(50);
const player = game.field.getPlayerPokemon();
const items = `Player items: ${player.getHeldItems()} | Enemy Items: ${enemy.getHeldItems()} |`;
expect(move.calculateBattlePower, items).toHaveLastReturnedWith(50);
}); });
it("should reset playerFaints count if we enter new trainer battle", async () => { it("should reset playerFaints count if we enter new trainer battle", async () => {

View File

@ -12,6 +12,7 @@ import { UiMode } from "#enums/ui-mode";
import { getMovePosition } from "#test/testUtils/gameManagerUtils"; import { getMovePosition } from "#test/testUtils/gameManagerUtils";
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
import { vi } from "vitest"; import { vi } from "vitest";
import { coerceArray } from "#app/utils/common";
/** /**
* Helper to handle a Pokemon's move * Helper to handle a Pokemon's move
@ -157,9 +158,7 @@ export class MoveHelper extends GameManagerHelper {
* @param moveset - The {@linkcode MoveId} (single or array) to change the Pokemon's moveset to. * @param moveset - The {@linkcode MoveId} (single or array) to change the Pokemon's moveset to.
*/ */
public changeMoveset(pokemon: Pokemon, moveset: MoveId | MoveId[]): void { public changeMoveset(pokemon: Pokemon, moveset: MoveId | MoveId[]): void {
if (!Array.isArray(moveset)) { moveset = coerceArray(moveset);
moveset = [moveset];
}
pokemon.moveset = []; pokemon.moveset = [];
moveset.forEach(move => { moveset.forEach(move => {
pokemon.moveset.push(new PokemonMove(move)); pokemon.moveset.push(new PokemonMove(move));

View File

@ -14,7 +14,7 @@ import { StatusEffect } from "#enums/status-effect";
import type { WeatherType } from "#enums/weather-type"; import type { WeatherType } from "#enums/weather-type";
import { expect, vi } from "vitest"; import { expect, vi } from "vitest";
import { GameManagerHelper } from "./gameManagerHelper"; import { GameManagerHelper } from "./gameManagerHelper";
import { shiftCharCodes } from "#app/utils/common"; import { coerceArray, shiftCharCodes } from "#app/utils/common";
import type { RandomTrainerOverride } from "#app/overrides"; import type { RandomTrainerOverride } from "#app/overrides";
import type { BattleType } from "#enums/battle-type"; import type { BattleType } from "#enums/battle-type";
@ -202,9 +202,7 @@ export class OverridesHelper extends GameManagerHelper {
*/ */
public moveset(moveset: MoveId | MoveId[]): this { public moveset(moveset: MoveId | MoveId[]): this {
vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue(moveset); vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue(moveset);
if (!Array.isArray(moveset)) { moveset = coerceArray(moveset);
moveset = [moveset];
}
const movesetStr = moveset.map(moveId => MoveId[moveId]).join(", "); const movesetStr = moveset.map(moveId => MoveId[moveId]).join(", ");
this.log(`Player Pokemon moveset set to ${movesetStr} (=[${moveset.join(", ")}])!`); this.log(`Player Pokemon moveset set to ${movesetStr} (=[${moveset.join(", ")}])!`);
return this; return this;
@ -382,9 +380,7 @@ export class OverridesHelper extends GameManagerHelper {
*/ */
public enemyMoveset(moveset: MoveId | MoveId[]): this { public enemyMoveset(moveset: MoveId | MoveId[]): this {
vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue(moveset); vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue(moveset);
if (!Array.isArray(moveset)) { moveset = coerceArray(moveset);
moveset = [moveset];
}
const movesetStr = moveset.map(moveId => MoveId[moveId]).join(", "); const movesetStr = moveset.map(moveId => MoveId[moveId]).join(", ");
this.log(`Enemy Pokemon moveset set to ${movesetStr} (=[${moveset.join(", ")}])!`); this.log(`Enemy Pokemon moveset set to ${movesetStr} (=[${moveset.join(", ")}])!`);
return this; return this;

View File

@ -1,3 +1,4 @@
import { coerceArray } from "#app/utils/common";
import type MockTextureManager from "#test/testUtils/mocks/mockTextureManager"; import type MockTextureManager from "#test/testUtils/mocks/mockTextureManager";
import type { MockGameObject } from "../mockGameObject"; import type { MockGameObject } from "../mockGameObject";
@ -216,11 +217,7 @@ export default class MockContainer implements MockGameObject {
} }
add(obj: MockGameObject | MockGameObject[]): this { add(obj: MockGameObject | MockGameObject[]): this {
if (Array.isArray(obj)) { this.list.push(...coerceArray(obj));
this.list.push(...obj);
} else {
this.list.push(obj);
}
return this; return this;
} }
@ -232,18 +229,12 @@ export default class MockContainer implements MockGameObject {
addAt(obj: MockGameObject | MockGameObject[], index = 0): this { addAt(obj: MockGameObject | MockGameObject[], index = 0): this {
// Adds a Game Object to this Container at the given index. // Adds a Game Object to this Container at the given index.
if (!Array.isArray(obj)) { this.list.splice(index, 0, ...coerceArray(obj));
obj = [obj];
}
this.list.splice(index, 0, ...obj);
return this; return this;
} }
remove(obj: MockGameObject | MockGameObject[], destroyChild = false): this { remove(obj: MockGameObject | MockGameObject[], destroyChild = false): this {
if (!Array.isArray(obj)) { for (const item of coerceArray(obj)) {
obj = [obj];
}
for (const item of obj) {
const index = this.list.indexOf(item); const index = this.list.indexOf(item);
if (index !== -1) { if (index !== -1) {
this.list.splice(index, 1); this.list.splice(index, 1);

View File

@ -1,3 +1,4 @@
import { coerceArray } from "#app/utils/common";
import type { MockGameObject } from "../mockGameObject"; import type { MockGameObject } from "../mockGameObject";
export default class MockRectangle implements MockGameObject { export default class MockRectangle implements MockGameObject {
@ -50,11 +51,7 @@ export default class MockRectangle implements MockGameObject {
add(obj: MockGameObject | MockGameObject[]): this { add(obj: MockGameObject | MockGameObject[]): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
if (Array.isArray(obj)) { this.list.push(...coerceArray(obj));
this.list.push(...obj);
} else {
this.list.push(obj);
}
return this; return this;
} }

View File

@ -1,6 +1,8 @@
import { coerceArray } from "#app/utils/common";
import Phaser from "phaser"; import Phaser from "phaser";
import type { MockGameObject } from "../mockGameObject"; import type { MockGameObject } from "../mockGameObject";
import Frame = Phaser.Textures.Frame;
type Frame = Phaser.Textures.Frame;
export default class MockSprite implements MockGameObject { export default class MockSprite implements MockGameObject {
private phaserSprite; private phaserSprite;
@ -204,11 +206,7 @@ export default class MockSprite implements MockGameObject {
add(obj: MockGameObject | MockGameObject[]): this { add(obj: MockGameObject | MockGameObject[]): this {
// Adds a child to this Game Object. // Adds a child to this Game Object.
if (Array.isArray(obj)) { this.list.push(...coerceArray(obj));
this.list.push(...obj);
} else {
this.list.push(obj);
}
return this; return this;
} }