Compare commits

..

No commits in common. "d06eb1bcc7ec0e3476745018cc53f332d8ec0a3c" and "061859fb374539dfc4f1ad301874c093c0bd996a" have entirely different histories.

72 changed files with 898 additions and 776 deletions

View File

@ -3,7 +3,7 @@
# top-most EditorConfig file
root = true
[**/*.{js,ts,json,jsonc}]
[src/*.{js,ts}]
indent_style = space
indent_size = 2
end_of_line = lf

View File

@ -1,5 +1,4 @@
{
// ! Just ignore the errors for now guys.
"$schema": "https://biomejs.dev/schemas/2.1.4/schema.json",
"vcs": {
"enabled": true,
@ -67,7 +66,6 @@
},
"style": {
"useCollapsedIf": "error",
"useCollapsedElseIf": "error",
"noDoneCallback": "error",
"noSubstr": "error",
"noYodaExpression": "error",
@ -88,10 +86,7 @@
"level": "warn",
"fix": "none"
},
"useSingleVarDeclarator": {
"level": "error",
"fix": "safe"
},
"useSingleVarDeclarator": "error",
"useNodejsImportProtocol": "off",
"useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation
"useAsConstAssertion": "error",

View File

@ -19,22 +19,23 @@ Generally speaking, most users shouldn't need to run Biome directly; in addition
> ![WARNING]
> You will **not** be able to commit code if any staged files contain `error`-level linting problems. \
> If you, for whatever reason, _absolutely need_ to bypass Lefthook for a given commit,
> `LEFTHOOK=0 git commit` will skip running all pre-commit hooks during the commit process.
> If you, for whatever reason, _absolutely need_ to bypass Lefthook while committing,
> `LEFTHOOK=0 git commit` will skip all pre-commit hooks for the given operation.
We also have a [Github Action](../.github/workflows/linting.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. \
We also have a [Github Action](../.github/workflows/quality.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. \
These are effectively the same commands as run by Lefthook, merely on a project-wide scale.
## Running Biome via CLI
To run you Biome on your files manually, you have 2 main options:
1. Run the scripts included in `package.json` (`pnpm biome` and `pnpm biome:all`). \
These have sensible defaults for command-line options, but do not allow altering certain flags (as some cannot be specified twice in the same command)
If you want Biome to check your files manually, you have 2 options:
1. Run the `biome` script included in `package.json` (`pnpm biome`).
This has sensible defaults for command-line options, but does not allow altering certain flags (as some cannot be specified twice in the same command)
2. Execute the Biome executable manually from the command line like so:
```sh
pnpm exec biome check --[flags]
```
This allows customizing non-overridable flags like `--diagnostic-level` on a more granular level, but requires slightly more verbosity and specifying more options.
This allows customizing flags non-overridable flags like `--diagnostic-level` on a more granular level, but requires slightly more verbosity.
A full list of flags and options can be found on [their website](https://biomejs.dev/reference/cli/), but here's a few useful ones to keep in mind:

View File

@ -18,7 +18,6 @@
"eslint": "eslint --fix .",
"eslint-ci": "eslint .",
"biome": "biome check --write --changed --no-errors-on-unmatched --diagnostic-level=error",
"biome:all": "biome check --write --no-errors-on-unmatched --diagnostic-level=error",
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
"docs": "typedoc",
"depcruise": "depcruise src test",

View File

@ -1463,6 +1463,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
private chance: number;
private attacker: Pokemon;
private move: Move;
constructor(chance: number) {
super();
@ -1478,9 +1479,11 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
);
}
override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void {
override apply({ simulated, opponent: attacker, move, pokemon }: PostMoveInteractionAbAttrParams): void {
// TODO: investigate why this is setting properties
if (!simulated) {
this.attacker = attacker;
this.move = move;
this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id);
}
}
@ -6342,14 +6345,11 @@ class ForceSwitchOutHelper {
return !blockedByAbility.value;
}
if (
!player &&
globalScene.currentBattle.battleType === BattleType.WILD &&
!globalScene.currentBattle.waveIndex &&
globalScene.currentBattle.waveIndex % 10 === 0
) {
if (!player && globalScene.currentBattle.battleType === BattleType.WILD) {
if (!globalScene.currentBattle.waveIndex && globalScene.currentBattle.waveIndex % 10 === 0) {
return false;
}
}
if (
!player &&

View File

@ -616,7 +616,7 @@ export const speciesStarterCosts = {
[SpeciesId.PALDEA_TAUROS]: 5,
[SpeciesId.PALDEA_WOOPER]: 3,
[SpeciesId.BLOODMOON_URSALUNA]: 5,
} as const;
};
const starterCandyCosts: { passive: number; costReduction: [number, number]; egg: number; }[] = [
{ passive: 40, costReduction: [ 25, 60 ], egg: 30 }, // 1 Cost
@ -657,3 +657,4 @@ export function getValueReductionCandyCounts(starterCost: number): [number, numb
export function getSameSpeciesEggCandyCounts(starterCost: number): number {
return starterCandyCosts[starterCost - 1].egg;
}

View File

@ -1397,7 +1397,6 @@ export class EncounterBattleAnim extends BattleAnim {
}
}
// biome-ignore-start lint/style/useForOf: This is being removed
export async function populateAnims() {
const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase());
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/_/g, ""));
@ -1673,5 +1672,3 @@ export async function populateAnims() {
})();
}*/
}
// biome-ignore-end lint/style/useForOf: This is being removed

View File

@ -2537,11 +2537,13 @@ export class RoostedTag extends BattlerTag {
let modifiedTypes: PokemonType[];
if (this.isBasePureFlying && !isCurrentlyDualType) {
modifiedTypes = [PokemonType.NORMAL];
} else if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
} else {
if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) {
modifiedTypes = [PokemonType.UNKNOWN];
} else {
modifiedTypes = currentTypes.filter(type => type !== PokemonType.FLYING);
}
}
pokemon.summonData.types = modifiedTypes;
pokemon.updateInfo();
}

View File

@ -42,9 +42,10 @@ export function getDailyRunStarters(seed: string): Starter[] {
starterCosts.push(randSeedInt(9 - starterCosts[0], 1));
starterCosts.push(10 - (starterCosts[0] + starterCosts[1]));
for (const cost of starterCosts) {
for (let c = 0; c < starterCosts.length; c++) {
const cost = starterCosts[c];
const costSpecies = Object.keys(speciesStarterCosts)
.map(s => Number.parseInt(s) as SpeciesId) // TODO: Remove
.map(s => Number.parseInt(s) as SpeciesId)
.filter(s => speciesStarterCosts[s] === cost);
const randPkmSpecies = getPokemonSpecies(randSeedItem(costSpecies));
const starterSpecies = getPokemonSpecies(

View File

@ -419,9 +419,11 @@ export class Egg {
const rand = randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1;
return rand ? SpeciesId.PHIONE : SpeciesId.MANAPHY;
}
if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY && !randSeedInt(2)) {
if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
if (!randSeedInt(2)) {
return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
}
}
let minStarterValue: number;
let maxStarterValue: number;

View File

@ -527,8 +527,7 @@ function doBerrySpritePile(isEat = false) {
const encounter = globalScene.currentBattle.mysteryEncounter!;
animationOrder.forEach((berry, i) => {
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
let sprite: Phaser.GameObjects.Sprite;
let tintSprite: Phaser.GameObjects.Sprite;
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
const sprites = encounter.introVisuals?.getSpriteAtIndex(introVisualsIndex);
if (sprites) {
sprite = sprites[0];

View File

@ -213,8 +213,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
female: true,
});
let beedrillKeys: { spriteKey: string; fileRoot: string };
let butterfreeKeys: { spriteKey: string; fileRoot: string };
let beedrillKeys: { spriteKey: string; fileRoot: string }, butterfreeKeys: { spriteKey: string; fileRoot: string };
if (globalScene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
beedrillKeys = getSpriteKeysFromSpecies(SpeciesId.BEEDRILL, false);
butterfreeKeys = getSpriteKeysFromSpecies(SpeciesId.BUTTERFREE, false);

View File

@ -289,7 +289,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
// Extra HA roll at base 1/64 odds (boosted by events and charms)
const hiddenIndex = tradePokemon.species.ability2 ? 2 : 1;
if (tradePokemon.species.abilityHidden && tradePokemon.abilityIndex < hiddenIndex) {
if (tradePokemon.species.abilityHidden) {
if (tradePokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new NumberHolder(64);
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
@ -299,6 +300,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
tradePokemon.abilityIndex = hiddenIndex;
}
}
}
// If Pokemon is still not shiny or with HA, give the Pokemon a random Common egg move in its moveset
if (!tradePokemon.shiny && (!tradePokemon.species.abilityHidden || tradePokemon.abilityIndex < hiddenIndex)) {

View File

@ -1107,11 +1107,13 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) {
}
} else if (biomeLinks.hasOwnProperty(currentBiome)) {
currentBiome = biomeLinks[currentBiome] as BiomeId;
} else if (!(i % 50)) {
} else {
if (!(i % 50)) {
currentBiome = BiomeId.END;
} else {
currentBiome = globalScene.generateRandomBiome(i);
}
}
currentArena = globalScene.newArena(currentBiome);
}

View File

@ -986,7 +986,8 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
if (!forTrainer && isRegionalEvolution) {
evolutionChance = 0;
} else if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
} else {
if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
if (strength === PartyMemberStrength.STRONGER) {
evolutionChance = 1;
} else {
@ -1020,6 +1021,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
1,
);
}
}
//TODO: Adjust templates and delays so we don't have to hardcode it
/* TEMPORARY! (Most) Trainers shouldn't be using unevolved Pokemon by the third gym leader / wave 80. Exceptions to this include Breeders, whose large teams are balanced by the use of weaker pokemon */
@ -1208,9 +1210,9 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
}
/**
* Generates a {@linkcode BigInt} corresponding to the maximum unlocks possible for this species,
* Generates a {@linkcode bigint} corresponding to the maximum unlocks possible for this species,
* taking into account if the species has a male/female gender, and which variants are implemented.
* @returns The maximum unlocks for the species as a `BigInt`; can be compared with {@linkcode DexEntry.caughtAttr}.
* @returns {@linkcode bigint} Maximum unlocks, can be compared with {@linkcode DexEntry.caughtAttr}.
*/
getFullUnlocksData(): bigint {
let caughtAttr = 0n;

View File

@ -6,7 +6,6 @@ import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { loadPokemonVariantAssets } from "#sprites/pokemon-sprite";
import type { Variant } from "#sprites/variant";
import { isNullOrUndefined } from "#utils/common";
import console from "node:console";
import type { GameObjects } from "phaser";
type PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig;
@ -88,7 +87,6 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
variant: Variant;
}[];
// TODO: Refactor
constructor(encounter: MysteryEncounter) {
super(globalScene, -72, 76);
this.encounter = encounter;
@ -195,8 +193,9 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
sprite.setPosition(sprite.x, sprite.y + y);
tintSprite.setPosition(tintSprite.x, tintSprite.y + y);
}
} else {
// Single sprite
} else if (this.spriteConfigs.length === 1) {
if (this.spriteConfigs.length === 1) {
sprite.x = origin;
tintSprite.x = origin;
} else {
@ -205,6 +204,7 @@ export class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
tintSprite.x = minX + (n + 0.5) * spacingValue + origin;
n++;
}
}
if (!isNullOrUndefined(pokemonShinySparkle)) {
// Offset the sparkle to match the Pokemon's position

View File

@ -2511,15 +2511,19 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
defenderType,
});
}
if (ignoreImmunity.value && multiplier.value === 0) {
if (ignoreImmunity.value) {
if (multiplier.value === 0) {
return 1;
}
}
const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[];
if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType)) && multiplier.value === 0) {
if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType))) {
if (multiplier.value === 0) {
return 1;
}
}
}
return multiplier.value;
})
.reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
@ -2956,9 +2960,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
}
const haThreshold = new NumberHolder(thresholdOverride ?? BASE_HIDDEN_ABILITY_CHANCE);
if (applyModifiersToOverride && !this.hasTrainer()) {
if (applyModifiersToOverride) {
if (!this.hasTrainer()) {
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, haThreshold);
}
}
if (randSeedInt(65536) < haThreshold.value) {
this.abilityIndex = 2;
@ -3057,7 +3063,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return;
}
for (const levelMove of allLevelMoves) {
for (let m = 0; m < allLevelMoves.length; m++) {
const levelMove = allLevelMoves[m];
if (this.level < levelMove[0]) {
break;
}
@ -4822,10 +4829,12 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
return true;
});
if ((this.isOfType(PokemonType.POISON) || this.isOfType(PokemonType.STEEL)) && poisonImmunity.includes(true)) {
if (this.isOfType(PokemonType.POISON) || this.isOfType(PokemonType.STEEL)) {
if (poisonImmunity.includes(true)) {
this.queueStatusImmuneMessage(quiet);
return false;
}
}
break;
}
case StatusEffect.PARALYSIS:
@ -5004,9 +5013,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
this.lapseTag(BattlerTagType.NIGHTMARE);
}
}
if (confusion && this.getTag(BattlerTagType.CONFUSED)) {
if (confusion) {
if (this.getTag(BattlerTagType.CONFUSED)) {
this.lapseTag(BattlerTagType.CONFUSED);
}
}
if (reloadAssets) {
this.loadAssets(false).then(() => this.playAnim());
}
@ -5489,8 +5500,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
spriteColors.forEach((sc: number[], i: number) => {
paletteDeltas.push([]);
for (const p of palette) {
paletteDeltas[i].push(deltaRgb(sc, p));
for (let p = 0; p < palette.length; p++) {
paletteDeltas[i].push(deltaRgb(sc, palette[p]));
}
});
@ -6763,13 +6774,11 @@ export class EnemyPokemon extends Pokemon {
}
canBypassBossSegments(segmentCount = 1): boolean {
if (
globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS &&
!this.formIndex &&
this.bossSegmentIndex - segmentCount < 1
) {
if (globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
if (!this.formIndex && this.bossSegmentIndex - segmentCount < 1) {
return false;
}
}
return true;
}

View File

@ -403,15 +403,17 @@ export class Trainer extends Phaser.GameObjects.Container {
} else {
newSpeciesPool = speciesPoolFiltered;
}
} else {
// If the index is odd, use the species pool for the partner trainer (that way he only uses his own pokemon in battle)
// Since the only currently allowed double battle with named trainers is Tate & Liza, we need to make sure that Solrock is the first pokemon in the party for Tate and Lunatone for Liza
} else if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.TATE]) {
if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.TATE]) {
newSpeciesPool = [SpeciesId.SOLROCK];
} else if (index === 1 && TrainerType[this.config.trainerTypeDouble] === TrainerType[TrainerType.LIZA]) {
newSpeciesPool = [SpeciesId.LUNATONE];
} else {
newSpeciesPool = speciesPoolPartnerFiltered;
}
}
// Fallback for when the species pool is empty
if (newSpeciesPool.length === 0) {
// If all pokemon from this pool are already in the party, generate a random species
@ -792,13 +794,11 @@ export class Trainer extends Phaser.GameObjects.Container {
* @returns boolean Whether the EnemyPokemon should Terastalize this turn
*/
shouldTera(pokemon: EnemyPokemon): boolean {
if (
this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA &&
!pokemon.isTerastallized &&
this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)
) {
if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA) {
if (!pokemon.isTerastallized && this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)) {
return true;
}
}
return false;
}
}

View File

@ -1634,9 +1634,9 @@ export class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
if (p.species.speciesId === SpeciesId.NECROZMA) {
// technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break...
let foundULTRA_Z = false;
let foundN_LUNA = false;
let foundN_SOLAR = false;
let foundULTRA_Z = false,
foundN_LUNA = false,
foundN_SOLAR = false;
formChangeItemTriggers.forEach((fc, _i) => {
console.log("Checking ", fc.item);
switch (fc.item) {

View File

@ -102,13 +102,10 @@ import { WeatherEffectPhase } from "#phases/weather-effect-phase";
import type { PhaseMap, PhaseString } from "#types/phase-types";
import { type Constructor, coerceArray } from "#utils/common";
/**
* @module
/*
* Manager for phases used by battle scene.
*
* @remarks
* **This file must not be imported or used directly.**
* The manager is exclusively used by the Battle Scene and is NOT intended for external use.
* *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.*
*/
/**

View File

@ -604,8 +604,9 @@ export class CommandPhase extends FieldPhase {
* @returns Whether the command was successful
*/
handleCommand(command: Command.FIGHT | Command.TERA, cursor: number, useMode?: MoveUseMode, move?: TurnMove): boolean;
handleCommand(command: Command.BALL, cursor: number): boolean;
handleCommand(command: Command.POKEMON, cursor: number, useBaton: boolean): boolean;
handleCommand(command: Command.BALL | Command.RUN, cursor: number): boolean;
handleCommand(command: Command.RUN, cursor: number): boolean;
handleCommand(command: Command, cursor: number, useMode?: boolean | MoveUseMode, move?: TurnMove): boolean;
public handleCommand(

View File

@ -10,12 +10,19 @@ export class CommonAnimPhase extends PokemonPhase {
public readonly phaseName: "CommonAnimPhase" | "PokemonHealPhase" | "WeatherEffectPhase" = "CommonAnimPhase";
private anim: CommonAnim | null;
private targetIndex?: BattlerIndex;
private playOnEmptyField: boolean;
constructor(battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex, anim: CommonAnim | null = null) {
constructor(
battlerIndex?: BattlerIndex,
targetIndex?: BattlerIndex,
anim: CommonAnim | null = null,
playOnEmptyField = false,
) {
super(battlerIndex);
this.anim = anim;
this.targetIndex = targetIndex;
this.playOnEmptyField = playOnEmptyField;
}
setAnimation(anim: CommonAnim) {

View File

@ -559,12 +559,11 @@ export class MoveEffectPhase extends PokemonPhase {
// Strikes after the first in a multi-strike move are guaranteed to hit,
// unless the move is flagged to check all hits and the user does not have Skill Link.
if (
user.turnData.hitsLeft < user.turnData.hitCount &&
(!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr("MaxMultiHitAbAttr"))
) {
if (user.turnData.hitsLeft < user.turnData.hitCount) {
if (!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr("MaxMultiHitAbAttr")) {
return [HitCheckResult.HIT, effectiveness];
}
}
const bypassAccuracy =
bypassAccAndInvuln ||

View File

@ -23,24 +23,25 @@ export class ScanIvsPhase extends PokemonPhase {
let enemyIvs: number[] = [];
let statsContainer: Phaser.GameObjects.Sprite[] = [];
let statsContainerLabels: Phaser.GameObjects.Sprite[] = [];
const enemyField = globalScene.getEnemyField();
const uiTheme = globalScene.uiTheme; // Assuming uiTheme is accessible
for (const enemy of globalScene.getEnemyField()) {
enemyIvs = enemy.ivs;
for (let e = 0; e < enemyField.length; e++) {
enemyIvs = enemyField[e].ivs;
// we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists
const currentIvs = globalScene.gameData.dexData[enemy.species.getRootSpeciesId()].ivs;
statsContainer = enemy.getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[];
const currentIvs = globalScene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs;
statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[];
statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0);
for (const statContainer of statsContainerLabels) {
const ivStat = Stat[statContainer.frame.name];
for (let s = 0; s < statsContainerLabels.length; s++) {
const ivStat = Stat[statsContainerLabels[s].frame.name];
if (enemyIvs[ivStat] > currentIvs[ivStat] && PERMANENT_STATS.indexOf(Number(ivStat)) >= 0) {
const hexColour =
enemyIvs[ivStat] === 31
? getTextColor(TextStyle.PERFECT_IV, false, uiTheme)
: getTextColor(TextStyle.SUMMARY_GREEN, false, uiTheme);
const hexTextColour = Phaser.Display.Color.HexStringToColor(hexColour).color;
statContainer.setTint(hexTextColour);
statsContainerLabels[s].setTint(hexTextColour);
}
statContainer.setVisible(true);
statsContainerLabels[s].setVisible(true);
}
}

View File

@ -22,7 +22,6 @@ export type StatStageChangeCallback = (
relativeChanges: number[],
) => void;
// TODO: Refactor this mess of a phase
export class StatStageChangePhase extends PokemonPhase {
public readonly phaseName = "StatStageChangePhase";
private stats: BattleStat[];
@ -63,12 +62,13 @@ export class StatStageChangePhase extends PokemonPhase {
start() {
// Check if multiple stats are being changed at the same time, then run SSCPhase for each of them
if (this.stats.length > 1) {
for (const stat of this.stats) {
for (let i = 0; i < this.stats.length; i++) {
const stat = [this.stats[i]];
globalScene.phaseManager.unshiftNew(
"StatStageChangePhase",
this.battlerIndex,
this.selfTarget,
[stat],
stat,
this.stages,
this.showMessage,
this.ignoreAbilities,
@ -100,7 +100,8 @@ export class StatStageChangePhase extends PokemonPhase {
}
});
}
} else if (!this.comingFromStickyWeb) {
} else {
if (!this.comingFromStickyWeb) {
opponentPokemon = globalScene.getPlayerField()[globalScene.currentBattle.lastPlayerInvolved];
} else {
const stickyTagID = globalScene.arena.findTagsOnSide(
@ -113,6 +114,7 @@ export class StatStageChangePhase extends PokemonPhase {
}
});
}
}
if (!pokemon.isActive(true)) {
return this.end();

View File

@ -27,8 +27,8 @@ export class TrainerVictoryPhase extends BattlePhase {
const trainerType = globalScene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct?
// Validate Voucher for boss trainers
if (vouchers.hasOwnProperty(TrainerType[trainerType])) {
if (
vouchers.hasOwnProperty(TrainerType[trainerType]) &&
!globalScene.validateVoucher(vouchers[TrainerType[trainerType]]) &&
globalScene.currentBattle.trainer?.config.isBoss
) {
@ -51,6 +51,7 @@ export class TrainerVictoryPhase extends BattlePhase {
);
}
}
}
// Breeders in Space achievement
if (
globalScene.arena.biomeType === BiomeId.SPACE &&

View File

@ -16,8 +16,8 @@ export class PokerogueSessionSavedataApi extends ApiBase {
//#region Public
/**
* Mark a session as cleared aka "newclear". \
* _This is **NOT** the same as {@linkcode clear | clear()}._
* Mark a session as cleared aka "newclear".\
* *This is **NOT** the same as {@linkcode clear | clear()}.*
* @param params The {@linkcode NewClearSessionSavedataRequest} to send
* @returns The raw savedata as `string`.
* @throws Error if the request fails
@ -94,8 +94,8 @@ export class PokerogueSessionSavedataApi extends ApiBase {
}
/**
* Clears the session savedata of the given slot. \
* _This is **NOT** the same as {@linkcode newclear | newclear()}._
* Clears the session savedata of the given slot.\
* *This is **NOT** the same as {@linkcode newclear | newclear()}.*
* @param params The {@linkcode ClearSessionSavedataRequest} to send
* @param sessionData The {@linkcode SessionSaveData} object
*/

View File

@ -121,8 +121,8 @@ async function initFonts(language: string | undefined) {
}
/**
* I18n money formatter with. (useful for BBCode coloring of text) \
* _If you don't want the BBCode tag applied, just use 'number' formatter_
* I18n money formatter with. (useful for BBCode coloring of text)\
* *If you don't want the BBCode tag applied, just use 'number' formatter*
* @example Input: `{{myMoneyValue, money}}`
* Output: `@[MONEY]{₽100,000,000}`
* @param amount the money amount

View File

@ -1595,7 +1595,7 @@ export class GameData {
globalScene.executeWithSeedOffset(
() => {
const neutralNatures = [Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY];
for (const _ of defaultStarterSpecies) {
for (let s = 0; s < defaultStarterSpecies.length; s++) {
defaultStarterNatures.push(randSeedItem(neutralNatures));
}
},

View File

@ -104,7 +104,8 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole
case SettingGamepad.Button_Speed_Up:
case SettingGamepad.Button_Slow_Down:
case SettingGamepad.Button_Submit:
if (value && globalScene.ui) {
if (value) {
if (globalScene.ui) {
const cancelHandler = (success = false): boolean => {
globalScene.ui.revertMode();
(globalScene.ui.getHandler() as SettingsGamepadUiHandler).updateBindings();
@ -115,6 +116,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole
cancelHandler: cancelHandler,
});
}
}
break;
case SettingGamepad.Controller:
if (value) {

View File

@ -167,7 +167,8 @@ export function setSettingKeyboard(setting: SettingKeyboard, value: number): boo
case SettingKeyboard.Alt_Button_Speed_Up:
case SettingKeyboard.Alt_Button_Slow_Down:
case SettingKeyboard.Alt_Button_Submit:
if (value && globalScene.ui) {
if (value) {
if (globalScene.ui) {
const cancelHandler = (success = false): boolean => {
globalScene.ui.revertMode();
(globalScene.ui.getHandler() as SettingsKeyboardUiHandler).updateBindings();
@ -178,6 +179,7 @@ export function setSettingKeyboard(setting: SettingKeyboard, value: number): boo
cancelHandler: cancelHandler,
});
}
}
break;
}
return true;

View File

@ -896,7 +896,8 @@ export function setSetting(setting: string, value: number): boolean {
globalScene.typeHints = Setting[index].options[value].value === "On";
break;
case SettingKeys.Language:
if (value && globalScene.ui) {
if (value) {
if (globalScene.ui) {
const cancelHandler = () => {
globalScene.ui.revertMode();
(globalScene.ui.getHandler() as SettingsUiHandler).setOptionCursor(-1, 0, true);
@ -993,6 +994,7 @@ export function setSetting(setting: string, value: number): boolean {
});
return false;
}
}
break;
case SettingKeys.Shop_Overlay_Opacity:
globalScene.updateShopOverlayOpacity(Number.parseInt(Setting[index].options[value].value) * 0.01);

View File

@ -17,6 +17,8 @@ export class AdminUiHandler extends FormModalUiHandler {
private config: ModalConfig;
private readonly buttonGap = 10;
// http response from the server when a username isn't found in the server
private readonly httpUserNotFoundErrorCode: number = 404;
private readonly ERR_REQUIRED_FIELD = (field: string) => {
if (field === "username") {
return `${toTitleCase(field)} is required`;

View File

@ -241,7 +241,9 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration);
for (const fieldEffectInfo of this.fieldEffectInfo) {
for (let i = 0; i < this.fieldEffectInfo.length; i++) {
const fieldEffectInfo = this.fieldEffectInfo[i];
// Creates a proxy object to decide which text object needs to be updated
let textObject: Phaser.GameObjects.Text;
switch (fieldEffectInfo.effectType) {
@ -387,7 +389,9 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
const fieldEffectInfo: ArenaEffectInfo[] = [];
this.fieldEffectInfo.forEach(i => fieldEffectInfo.push(i));
for (const info of fieldEffectInfo) {
for (let i = 0; i < fieldEffectInfo.length; i++) {
const info = fieldEffectInfo[i];
if (info.maxDuration === 0) {
continue;
}

View File

@ -68,14 +68,14 @@ export class BaseStatsOverlay extends Phaser.GameObjects.Container implements In
// show this component with infos for the specific move
show(values: number[], total: number): boolean {
for (let i = 0; i < 6; i++) {
this.statsLabels[i].setText(`${i18next.t(`pokemonInfo:Stat.${shortStats[i]}shortened`)}: ${values[i]}`);
this.statsLabels[i].setText(i18next.t(`pokemonInfo:Stat.${shortStats[i]}shortened`) + ": " + `${values[i]}`);
// This accounts for base stats up to 200, might not be enough.
// TODO: change color based on value.
this.statsShadows[i].setSize(values[i] / 2, 5);
this.statsRectangles[i].setSize(values[i] / 2, 5);
}
this.statsTotalLabel.setText(`${i18next.t("pokedexUiHandler:baseTotal")}: ${total}`);
this.statsTotalLabel.setText(i18next.t("pokedexUiHandler:baseTotal") + ": " + `${total}`);
this.setVisible(true);
this.active = true;

View File

@ -153,13 +153,17 @@ export class BattleMessageUiHandler extends MessageUiHandler {
processInput(button: Button): boolean {
const ui = this.getUi();
if (this.awaitingActionInput && (button === Button.CANCEL || button === Button.ACTION) && this.onActionInput) {
if (this.awaitingActionInput) {
if (button === Button.CANCEL || button === Button.ACTION) {
if (this.onActionInput) {
ui.playSelect();
const originalOnActionInput = this.onActionInput;
this.onActionInput = null;
originalOnActionInput();
return true;
}
}
}
return false;
}

View File

@ -400,7 +400,8 @@ export class GameChallengesUiHandler extends UiHandler {
} else {
success = false;
}
} else if (this.cursorObj?.visible && !this.startCursor.visible) {
} else {
if (this.cursorObj?.visible && !this.startCursor.visible) {
switch (button) {
case Button.UP:
if (this.cursor === 0) {
@ -469,6 +470,7 @@ export class GameChallengesUiHandler extends UiHandler {
break;
}
}
}
// Plays a select sound effect if an action was successfully processed.
if (success) {

View File

@ -504,12 +504,14 @@ export class DropDown extends Phaser.GameObjects.Container {
if (index === 0) {
// we are on the All option > put all other options to the newState
this.setAllOptions(newState);
} else if (newState === DropDownState.ON && this.checkForAllOn()) {
} else {
// select the "all" option if all others are selected, other unselect it
if (newState === DropDownState.ON && this.checkForAllOn()) {
this.options[0].setOptionState(DropDownState.ON);
} else {
this.options[0].setOptionState(DropDownState.OFF);
}
}
} else if (this.dropDownType === DropDownType.SINGLE) {
if (option.state === DropDownState.OFF) {
this.options.forEach(option => {
@ -651,10 +653,12 @@ export class DropDown extends Phaser.GameObjects.Container {
this.options[i].setDirection(SortDirection.ASC);
this.options[i].toggle.setVisible(true);
}
} else if (this.defaultSettings[i]) {
} else {
if (this.defaultSettings[i]) {
this.options[i].setOptionState(this.defaultSettings[i]["state"]);
}
}
}
this.onChange();
}
@ -695,11 +699,11 @@ export class DropDown extends Phaser.GameObjects.Container {
autoSize(): void {
let maxWidth = 0;
let x = 0;
for (const option of this.options) {
const optionWidth = option.getWidth();
for (let i = 0; i < this.options.length; i++) {
const optionWidth = this.options[i].getWidth();
if (optionWidth > maxWidth) {
maxWidth = optionWidth;
x = option.getCurrentLabelX() ?? 0;
x = this.options[i].getCurrentLabelX() ?? 0;
}
}
this.window.width = maxWidth + x - this.window.x + 9;

View File

@ -62,13 +62,17 @@ export class EvolutionSceneHandler extends MessageUiHandler {
}
const ui = this.getUi();
if (this.awaitingActionInput && (button === Button.CANCEL || button === Button.ACTION) && this.onActionInput) {
if (this.awaitingActionInput) {
if (button === Button.CANCEL || button === Button.ACTION) {
if (this.onActionInput) {
ui.playSelect();
const originalOnActionInput = this.onActionInput;
this.onActionInput = null;
originalOnActionInput();
return true;
}
}
}
return false;
}

View File

@ -130,21 +130,23 @@ export class FilterBar extends Phaser.GameObjects.Container {
* Move the leftmost dropdown to the left of the FilterBar instead of below it
*/
offsetHybridFilters(): void {
for (const dropDown of this.dropDowns) {
if (dropDown.dropDownType === DropDownType.HYBRID) {
dropDown.autoSize();
dropDown.x = -dropDown.getWidth();
dropDown.y = 0;
for (let i = 0; i < this.dropDowns.length; i++) {
if (this.dropDowns[i].dropDownType === DropDownType.HYBRID) {
this.dropDowns[i].autoSize();
this.dropDowns[i].x = -this.dropDowns[i].getWidth();
this.dropDowns[i].y = 0;
}
}
}
setCursor(cursor: number): void {
if (this.lastCursor > -1 && this.dropDowns[this.lastCursor].visible) {
if (this.lastCursor > -1) {
if (this.dropDowns[this.lastCursor].visible) {
this.dropDowns[this.lastCursor].setVisible(false);
this.dropDowns[cursor].setVisible(true);
this.dropDowns[cursor].resetCursor();
}
}
this.cursorObj.setPosition(this.labels[cursor].x - this.cursorOffset + 2, 6);
this.lastCursor = cursor;

View File

@ -24,12 +24,14 @@ export class FilterText extends Phaser.GameObjects.Container {
private rows: FilterTextRow[] = [];
public cursorObj: Phaser.GameObjects.Image;
public numFilters = 0;
private lastCursor = -1;
private uiTheme: UiTheme;
private menuMessageBoxContainer: Phaser.GameObjects.Container;
private dialogueMessageBox: Phaser.GameObjects.NineSlice;
message: any;
private readonly textPadding = 8;
private readonly defaultWordWrapWidth = 1224;
private onChange: () => void;
@ -156,6 +158,7 @@ export class FilterText extends Phaser.GameObjects.Container {
const cursorOffset = 8;
this.cursorObj.setPosition(cursorOffset, this.labels[cursor].y + 3);
this.lastCursor = cursor;
}
/**

View File

@ -234,9 +234,9 @@ export class LoginFormUiHandler extends FormModalUiHandler {
const dataKeys = localStorageKeys.filter(ls => ls.indexOf(keyToFind) >= 0);
if (dataKeys.length > 0 && dataKeys.length <= 2) {
const options: OptionSelectItem[] = [];
for (const key of dataKeys) {
for (let i = 0; i < dataKeys.length; i++) {
options.push({
label: key.replace(keyToFind, ""),
label: dataKeys[i].replace(keyToFind, ""),
handler: () => {
globalScene.ui.revertMode();
this.infoContainer.disableInteractive();
@ -261,7 +261,7 @@ export class LoginFormUiHandler extends FormModalUiHandler {
}
});
this.saveDownloadImage.on("pointerdown", async () => {
this.saveDownloadImage.on("pointerdown", () => {
// find all data_ and sessionData keys, put them in a .txt file and download everything in a single zip
const localStorageKeys = Object.keys(localStorage); // this gets the keys for localStorage
const keyToFind = "data_";
@ -270,19 +270,20 @@ export class LoginFormUiHandler extends FormModalUiHandler {
const sessionKeys = localStorageKeys.filter(ls => ls.indexOf(sessionKeyToFind) >= 0);
if (dataKeys.length > 0 || sessionKeys.length > 0) {
const zip = new JSZip();
for (const dataKey of dataKeys) {
zip.file(dataKey + ".prsv", localStorage.getItem(dataKey)!);
for (let i = 0; i < dataKeys.length; i++) {
zip.file(dataKeys[i] + ".prsv", localStorage.getItem(dataKeys[i])!);
}
for (const sessionKey of sessionKeys) {
zip.file(sessionKey + ".prsv", localStorage.getItem(sessionKey)!);
for (let i = 0; i < sessionKeys.length; i++) {
zip.file(sessionKeys[i] + ".prsv", localStorage.getItem(sessionKeys[i])!);
}
const content = await zip.generateAsync({ type: "blob" });
zip.generateAsync({ type: "blob" }).then(content => {
const url = URL.createObjectURL(content);
const a = document.createElement("a");
a.href = url;
a.download = "pokerogue_saves.zip";
a.click();
URL.revokeObjectURL(url);
});
} else {
return onFail(this.ERR_NO_SAVES);
}

View File

@ -108,17 +108,17 @@ export abstract class MessageUiHandler extends AwaitableUiHandler {
const textWords = text.split(" ");
let lastLineCount = 1;
let newText = "";
for (const textWord of textWords) {
const nextWordText = newText ? `${newText} ${textWord}` : textWord;
for (let w = 0; w < textWords.length; w++) {
const nextWordText = newText ? `${newText} ${textWords[w]}` : textWords[w];
if (textWord.includes("\n")) {
if (textWords[w].includes("\n")) {
newText = nextWordText;
lastLineCount++;
} else {
const lineCount = this.message.runWordWrap(nextWordText).split(/\n/g).length;
if (lineCount > lastLineCount) {
lastLineCount = lineCount;
newText = `${newText}\n${textWord}`;
newText = `${newText}\n${textWords[w]}`;
} else {
newText = nextWordText;
}

View File

@ -480,11 +480,13 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
}
} else if (this.cursor) {
success = this.setCursor(this.cursor - 1);
} else if (this.rowCursor === 1 && this.options.length === 0) {
} else {
if (this.rowCursor === 1 && this.options.length === 0) {
success = false;
} else {
success = this.setCursor(this.getRowItems(this.rowCursor) - 1);
}
}
break;
case Button.RIGHT:
if (!this.rowCursor) {
@ -512,11 +514,13 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
}
} else if (this.cursor < this.getRowItems(this.rowCursor) - 1) {
success = this.setCursor(this.cursor + 1);
} else if (this.rowCursor === 1 && this.options.length === 0) {
} else {
if (this.rowCursor === 1 && this.options.length === 0) {
success = this.setRowCursor(0);
} else {
success = this.setCursor(0);
}
}
break;
}
}

View File

@ -41,6 +41,8 @@ const GLOBAL_SCALE = 6;
export class MoveInfoOverlay extends Phaser.GameObjects.Container implements InfoToggle {
public active = false;
private move: Move;
private desc: Phaser.GameObjects.Text;
private descScroll: Phaser.Tweens.Tween | null = null;
@ -175,6 +177,7 @@ export class MoveInfoOverlay extends Phaser.GameObjects.Container implements Inf
if (!globalScene.enableMoveInfo) {
return false; // move infos have been disabled // TODO:: is `false` correct? i used to be `undeefined`
}
this.move = move;
this.pow.setText(move.power >= 0 ? move.power.toString() : "---");
this.acc.setText(move.accuracy >= 0 ? move.accuracy.toString() : "---");
this.pp.setText(move.pp >= 0 ? move.pp.toString() : "---");

View File

@ -156,13 +156,15 @@ export class MysteryEncounterUiHandler extends UiHandler {
selected.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL))
) {
success = false;
} else if (
} else {
if (
(globalScene.phaseManager.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)
) {
success = true;
} else {
ui.playError();
}
}
} else {
// TODO: If we need to handle cancel option? Maybe default logic to leave/run from encounter idk
}

View File

@ -614,13 +614,11 @@ export class PartyUiHandler extends MessageUiHandler {
// TODO: Might need to check here for when this.transferMode is active.
private processModifierTransferModeLeftRightInput(button: Button) {
if (!this.isItemManageMode()) {
return false;
}
let success = false;
const option = this.options[this.optionsCursor];
if (button === Button.LEFT) {
/** Decrease quantity for the current item and update UI */
if (this.isItemManageMode()) {
this.transferQuantities[option] =
this.transferQuantities[option] === 1
? this.transferQuantitiesMax[option]
@ -630,8 +628,11 @@ export class PartyUiHandler extends MessageUiHandler {
this.optionsCursor,
); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
}
}
if (button === Button.RIGHT) {
/** Increase quantity for the current item and update UI */
if (this.isItemManageMode()) {
this.transferQuantities[option] =
this.transferQuantities[option] === this.transferQuantitiesMax[option]
? 1
@ -641,6 +642,7 @@ export class PartyUiHandler extends MessageUiHandler {
this.optionsCursor,
); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */
}
}
return success;
}
@ -928,9 +930,11 @@ export class PartyUiHandler extends MessageUiHandler {
return this.moveOptionCursor(button);
}
if ((button === Button.LEFT || button === Button.RIGHT) && this.isItemManageMode()) {
if (button === Button.LEFT || button === Button.RIGHT) {
if (this.isItemManageMode()) {
return this.processModifierTransferModeLeftRightInput(button);
}
}
return false;
}
@ -1211,10 +1215,12 @@ export class PartyUiHandler extends MessageUiHandler {
isScroll = true;
this.optionsScrollCursor++;
}
} else if (!cursor && this.optionsScrollCursor) {
} else {
if (!cursor && this.optionsScrollCursor) {
isScroll = true;
this.optionsScrollCursor--;
}
}
if (isScroll && this.optionsScrollCursor === 1) {
this.optionsScrollCursor += isDown ? 1 : -1;
}
@ -1567,11 +1573,13 @@ export class PartyUiHandler extends MessageUiHandler {
optionName = `${modifier.active ? i18next.t("partyUiHandler:DEACTIVATE") : i18next.t("partyUiHandler:ACTIVATE")} ${modifier.type.name}`;
} else if (option === PartyOption.UNPAUSE_EVOLUTION) {
optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:UNPAUSE_EVOLUTION") : i18next.t("partyUiHandler:PAUSE_EVOLUTION")}`;
} else if (this.localizedOptions.includes(option)) {
} else {
if (this.localizedOptions.includes(option)) {
optionName = i18next.t(`partyUiHandler:${PartyOption[option]}`);
} else {
optionName = toTitleCase(PartyOption[option]);
}
}
break;
}
}
@ -1646,11 +1654,11 @@ export class PartyUiHandler extends MessageUiHandler {
this.transferMode = false;
this.transferAll = false;
this.partySlots[this.transferCursor].setTransfer(false);
for (const partySlot of this.partySlots) {
partySlot.slotDescriptionLabel.setVisible(false);
partySlot.slotHpBar.setVisible(true);
partySlot.slotHpOverlay.setVisible(true);
partySlot.slotHpText.setVisible(true);
for (let i = 0; i < this.partySlots.length; i++) {
this.partySlots[i].slotDescriptionLabel.setVisible(false);
this.partySlots[i].slotHpBar.setVisible(true);
this.partySlots[i].slotHpOverlay.setVisible(true);
this.partySlots[i].slotHpText.setVisible(true);
}
}

View File

@ -259,6 +259,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
private instructionRowX = 0;
private instructionRowY = 0;
private instructionRowTextOffset = 9;
private filterInstructionRowX = 0;
private filterInstructionRowY = 0;
private starterAttributes: StarterAttributes;
private savedStarterAttributes: StarterAttributes;
@ -1067,11 +1069,13 @@ export class PokedexPageUiHandler extends MessageUiHandler {
) {
starterAttributes.female = !starterAttributes.female;
}
} else if (caughtAttr & DexAttr.FEMALE) {
} else {
if (caughtAttr & DexAttr.FEMALE) {
starterAttributes.female = true;
} else if (caughtAttr & DexAttr.MALE) {
starterAttributes.female = false;
}
}
return starterAttributes;
}
@ -1835,9 +1839,11 @@ export class PokedexPageUiHandler extends MessageUiHandler {
if (this.isCaught() & DexAttr.VARIANT_2) {
break;
}
} else if (this.isCaught() & DexAttr.VARIANT_3) {
} else {
if (this.isCaught() & DexAttr.VARIANT_3) {
break;
}
}
} while (newVariant !== props.variant);
starterAttributes.variant = newVariant; // store the selected variant
@ -2198,6 +2204,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
updateInstructions(): void {
this.instructionRowX = 0;
this.instructionRowY = 0;
this.filterInstructionRowX = 0;
this.filterInstructionRowY = 0;
this.hideInstructions();
this.instructionsContainer.removeAll();
this.filterInstructionsContainer.removeAll();
@ -2354,9 +2362,11 @@ export class PokedexPageUiHandler extends MessageUiHandler {
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
// Set default attributes if for some reason starterAttributes does not exist or attributes missing
const props: StarterAttributes = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant) && props.shiny) {
if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant)) {
if (props.shiny) {
props.variant = starterAttributes.variant as Variant;
}
}
props.form = starterAttributes?.form ?? props.form;
props.female = starterAttributes?.female ?? props.female;
@ -2768,16 +2778,18 @@ export class PokedexPageUiHandler extends MessageUiHandler {
props += DexAttr.SHINY;
if (this.starterAttributes?.variant !== undefined) {
props += BigInt(Math.pow(2, this.starterAttributes?.variant)) * DexAttr.DEFAULT_VARIANT;
/* This chunk calculates the correct variant if there's no starter preferences for it.
} else {
/* This calculates the correct variant if there's no starter preferences for it.
* This gets the highest tier variant that you've caught and adds it to the temp props
*/
} else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
props += DexAttr.VARIANT_3;
} else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
props += DexAttr.VARIANT_2;
} else {
props += DexAttr.DEFAULT_VARIANT;
}
}
} else {
props += DexAttr.NON_SHINY;
props += DexAttr.DEFAULT_VARIANT; // we add the default variant here because non shiny versions are listed as default variant

View File

@ -709,13 +709,12 @@ export class PokedexUiHandler extends MessageUiHandler {
}
}
if (
starterAttributes.female !== undefined &&
!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)
) {
if (starterAttributes.female !== undefined) {
if (!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)) {
// requested gender wasn't unlocked, purging setting
starterAttributes.female = undefined;
}
}
if (starterAttributes.ability !== undefined) {
const speciesHasSingleAbility = species.ability2 === species.ability1;
@ -1188,7 +1187,8 @@ export class PokedexUiHandler extends MessageUiHandler {
break;
}
}
} else if (button === Button.ACTION) {
} else {
if (button === Button.ACTION) {
ui.setOverlayMode(UiMode.POKEDEX_PAGE, this.lastSpecies, null, this.filteredIndices);
success = true;
} else {
@ -1261,6 +1261,7 @@ export class PokedexUiHandler extends MessageUiHandler {
}
}
}
}
if (success) {
ui.playSelect();
@ -1452,12 +1453,14 @@ export class PokedexUiHandler extends MessageUiHandler {
} else {
data.passive1 = false;
}
} else if (starterData.passiveAttr > 0) {
} else {
if (starterData.passiveAttr > 0) {
data.passive2 = true;
} else {
data.passive2 = false;
}
}
}
// Gen filter
const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(species.generation);
@ -2331,16 +2334,18 @@ export class PokedexUiHandler extends MessageUiHandler {
props += DexAttr.SHINY;
if (this.starterPreferences[speciesId]?.variant !== undefined) {
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
} else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
} else {
/* This calculates the correct variant if there's no starter preferences for it.
* This gets the highest tier variant that you've caught and adds it to the temp props
*/
if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
props += DexAttr.VARIANT_3;
} else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
props += DexAttr.VARIANT_2;
} else {
props += DexAttr.DEFAULT_VARIANT;
}
}
} else {
props += DexAttr.NON_SHINY;
props += DexAttr.DEFAULT_VARIANT; // we add the default variant here because non shiny versions are listed as default variant

View File

@ -32,6 +32,8 @@ export class RunHistoryUiHandler extends MessageUiHandler {
private runsContainer: Phaser.GameObjects.Container;
private runs: RunEntryContainer[];
private runSelectCallback: RunSelectCallback | null;
private scrollCursor = 0;
private cursorObj: Phaser.GameObjects.NineSlice | null;
@ -116,6 +118,7 @@ export class RunHistoryUiHandler extends MessageUiHandler {
success = true;
return success;
}
this.runSelectCallback = null;
success = true;
globalScene.ui.revertMode();
} else if (this.runs.length > 0) {
@ -232,6 +235,7 @@ export class RunHistoryUiHandler extends MessageUiHandler {
this.runSelectContainer.setVisible(false);
this.setScrollCursor(0);
this.clearCursor();
this.runSelectCallback = null;
this.clearRuns();
}
@ -254,11 +258,13 @@ export class RunHistoryUiHandler extends MessageUiHandler {
* entryData: the data of an individual run
*/
class RunEntryContainer extends Phaser.GameObjects.Container {
private slotId: number;
public entryData: RunEntry;
constructor(entryData: RunEntry, slotId: number) {
super(globalScene, 0, slotId * 56);
this.slotId = slotId;
this.entryData = entryData;
this.setup(this.entryData);

View File

@ -683,20 +683,18 @@ export class RunInfoUiHandler extends UiHandler {
*/
private challengeParser(): string[] {
const rules: string[] = [];
for (const challenge of this.runInfo.challenges) {
if (challenge.value === 0) {
continue;
}
switch (challenge.id) {
for (let i = 0; i < this.runInfo.challenges.length; i++) {
if (this.runInfo.challenges[i].value !== 0) {
switch (this.runInfo.challenges[i].id) {
case Challenges.SINGLE_GENERATION:
rules.push(i18next.t(`runHistory:challengeMonoGen${challenge.value}`));
rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`));
break;
case Challenges.SINGLE_TYPE: {
const typeRule = PokemonType[challenge.value - 1];
const typeRule = PokemonType[this.runInfo.challenges[i].value - 1];
const typeTextColor = `[color=${TypeColor[typeRule]}]`;
const typeShadowColor = `[shadow=${TypeShadow[typeRule]}]`;
const typeText =
typeTextColor + typeShadowColor + i18next.t(`pokemonInfo:Type.${typeRule}`)! + "[/color][/shadow]";
typeTextColor + typeShadowColor + i18next.t(`pokemonInfo:Type.${typeRule}`)! + "[/color]" + "[/shadow]";
rules.push(typeText);
break;
}
@ -704,7 +702,7 @@ export class RunInfoUiHandler extends UiHandler {
rules.push(i18next.t("challenges:inverseBattle.shortName"));
break;
default: {
const localizationKey = Challenges[challenge.id]
const localizationKey = Challenges[this.runInfo.challenges[i].id]
.split("_")
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join("");
@ -713,6 +711,7 @@ export class RunInfoUiHandler extends UiHandler {
}
}
}
}
return rules;
}

View File

@ -34,6 +34,8 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
protected navigationIcons: InputsIcons;
private cursorObj: Phaser.GameObjects.NineSlice | null;
private reloadSettings: Array<Setting>;
private reloadRequired: boolean;
protected rowsToDisplay: number;
@ -105,6 +107,8 @@ export class AbstractSettingsUiHandler extends MessageUiHandler {
this.settingLabels = [];
this.optionValueLabels = [];
this.reloadSettings = this.settings.filter(s => s?.requireReload);
let anyReloadRequired = false;
this.settings.forEach((setting, s) => {
let settingName = setting.label;

View File

@ -1217,13 +1217,12 @@ export class StarterSelectUiHandler extends MessageUiHandler {
}
}
if (
starterAttributes.female !== undefined &&
!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)
) {
if (starterAttributes.female !== undefined) {
if (!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)) {
// requested gender wasn't unlocked, purging setting
starterAttributes.female = undefined;
}
}
if (starterAttributes.ability !== undefined) {
const speciesHasSingleAbility = species.ability2 === species.ability1;
@ -2343,10 +2342,12 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// TODO: is this bang correct?
break;
}
} else if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) {
} else {
if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) {
// TODO: is this bang correct?
break;
}
}
} while (newVariant !== props.variant);
starterAttributes.variant = newVariant; // store the selected variant
if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY && newVariant <= props.variant) {
@ -2421,9 +2422,11 @@ export class StarterSelectUiHandler extends MessageUiHandler {
newAbilityIndex = (newAbilityIndex + 1) % abilityCount;
}
break;
} else if (abilityAttr & AbilityAttr.ABILITY_HIDDEN) {
} else {
if (abilityAttr & AbilityAttr.ABILITY_HIDDEN) {
break;
}
}
} while (newAbilityIndex !== this.abilityCursor);
starterAttributes.ability = newAbilityIndex; // store the selected ability
@ -2969,7 +2972,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
}
// this updates icons for previously saved pokemon
for (const currentFilteredContainer of this.validStarterContainers) {
for (let i = 0; i < this.validStarterContainers.length; i++) {
const currentFilteredContainer = this.validStarterContainers[i];
const starterSprite = currentFilteredContainer.icon as Phaser.GameObjects.Sprite;
const currentDexAttr = this.getCurrentDexProps(currentFilteredContainer.species.speciesId);
@ -3558,9 +3562,11 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// load default nature from stater save data, if set
const defaultNature = starterAttributes?.nature || globalScene.gameData.getSpeciesDefaultNature(species);
props = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant) && props.shiny) {
if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant)) {
if (props.shiny) {
props.variant = starterAttributes.variant as Variant;
}
}
props.formIndex = starterAttributes?.form ?? props.formIndex;
props.female = starterAttributes?.female ?? props.female;
@ -4344,13 +4350,13 @@ export class StarterSelectUiHandler extends MessageUiHandler {
return true;
}
/**
* This block checks to see if your party is valid
/* This block checks to see if your party is valid
* It checks each pokemon against the challenge - noting that due to monotype challenges it needs to check the pokemon while ignoring their evolutions/form change items
*/
isPartyValid(): boolean {
let canStart = false;
for (const species of this.starterSpecies) {
for (let s = 0; s < this.starterSpecies.length; s++) {
const species = this.starterSpecies[s];
const isValidForChallenge = checkStarterValidForChallenge(
species,
globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)),
@ -4394,16 +4400,18 @@ export class StarterSelectUiHandler extends MessageUiHandler {
props += DexAttr.SHINY;
if (this.starterPreferences[speciesId]?.variant !== undefined) {
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
} else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
} else {
/* This calculates the correct variant if there's no starter preferences for it.
* This gets the highest tier variant that you've caught and adds it to the temp props
*/
if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
props += DexAttr.VARIANT_3;
} else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
props += DexAttr.VARIANT_2;
} else {
props += DexAttr.DEFAULT_VARIANT;
}
}
} else {
props += DexAttr.NON_SHINY;
props += DexAttr.DEFAULT_VARIANT; // we add the default variant here because non shiny versions are listed as default variant

View File

@ -515,7 +515,8 @@ export class SummaryUiHandler extends UiHandler {
if (this.pokemon && this.moveCursor < this.pokemon.moveset.length) {
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
this.moveSelectFunction?.(this.moveCursor);
} else if (this.selectedMoveIndex === -1) {
} else {
if (this.selectedMoveIndex === -1) {
this.selectedMoveIndex = this.moveCursor;
this.setCursor(this.moveCursor);
} else {
@ -542,6 +543,7 @@ export class SummaryUiHandler extends UiHandler {
this.selectedMoveCursorObj = null;
}
}
}
success = true;
} else if (this.moveCursor === 4) {
return this.processInput(Button.CANCEL);
@ -573,7 +575,8 @@ export class SummaryUiHandler extends UiHandler {
break;
}
}
} else if (button === Button.ACTION) {
} else {
if (button === Button.ACTION) {
if (this.cursor === Page.MOVES) {
this.showMoveSelect();
success = true;
@ -645,6 +648,7 @@ export class SummaryUiHandler extends UiHandler {
break;
}
}
}
if (success) {
ui.playSelect();

View File

@ -70,13 +70,12 @@ export class TargetSelectUiHandler extends UiHandler {
* @param user the Pokemon using the move
*/
resetCursor(cursorN: number, user: Pokemon): void {
if (
!isNullOrUndefined(cursorN) &&
([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.tempSummonData.waveTurnCount === 1)
) {
if (!isNullOrUndefined(cursorN)) {
if ([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.tempSummonData.waveTurnCount === 1) {
// Reset cursor on the first turn of a fight or if an ally was targeted last turn
cursorN = -1;
}
}
this.setCursor(this.targets.includes(cursorN) ? cursorN : this.targets[0]);
}
@ -93,12 +92,11 @@ export class TargetSelectUiHandler extends UiHandler {
if (isNullOrUndefined(this.cursor0) || this.cursor0 !== this.cursor) {
this.cursor0 = this.cursor;
}
} else if (
this.fieldIndex === BattlerIndex.PLAYER_2 &&
(isNullOrUndefined(this.cursor1) || this.cursor1 !== this.cursor)
) {
} else if (this.fieldIndex === BattlerIndex.PLAYER_2) {
if (isNullOrUndefined(this.cursor1) || this.cursor1 !== this.cursor) {
this.cursor1 = this.cursor;
}
}
} else if (this.isMultipleTargets) {
success = false;
} else {

View File

@ -117,9 +117,11 @@ export function updateWindowType(windowTypeIndex: number): void {
} else if (object.texture?.key === "namebox") {
themedObjects.push(object);
}
} else if (object instanceof Phaser.GameObjects.Sprite && object.texture?.key === "bg") {
} else if (object instanceof Phaser.GameObjects.Sprite) {
if (object.texture?.key === "bg") {
themedObjects.push(object);
}
}
};
traverse(globalScene);

View File

@ -374,13 +374,11 @@ export class UI extends Phaser.GameObjects.Container {
}
shouldSkipDialogue(i18nKey: string): boolean {
if (
i18next.exists(i18nKey) &&
globalScene.skipSeenDialogues &&
globalScene.gameData.getSeenDialogues()[i18nKey] === true
) {
if (i18next.exists(i18nKey)) {
if (globalScene.skipSeenDialogues && globalScene.gameData.getSeenDialogues()[i18nKey] === true) {
return true;
}
}
return false;
}

View File

@ -10,6 +10,7 @@ import { removeCookie } from "#utils/cookies";
import i18next from "i18next";
export class UnavailableModalUiHandler extends ModalUiHandler {
private reconnectTimer: NodeJS.Timeout | null;
private reconnectDuration: number;
private reconnectCallback: () => void;
@ -61,6 +62,7 @@ export class UnavailableModalUiHandler extends ModalUiHandler {
tryReconnect(): void {
updateUserInfo().then(response => {
if (response[0] || [200, 400].includes(response[1])) {
this.reconnectTimer = null;
this.reconnectDuration = this.minTime;
globalScene.playSound("se/pb_bounce_1");
this.reconnectCallback();
@ -69,7 +71,7 @@ export class UnavailableModalUiHandler extends ModalUiHandler {
globalScene.reset(true, true);
} else {
this.reconnectDuration = Math.min(this.reconnectDuration * 2, this.maxTime); // Set a max delay so it isn't infinite
setTimeout(
this.reconnectTimer = setTimeout(
() => this.tryReconnect(),
// Adds a random factor to avoid pendulum effect during long total breakdown
this.reconnectDuration + Math.random() * this.randVarianceTime,
@ -86,7 +88,7 @@ export class UnavailableModalUiHandler extends ModalUiHandler {
this.reconnectCallback = args[0];
this.reconnectDuration = this.minTime;
setTimeout(() => this.tryReconnect(), this.reconnectDuration);
this.reconnectTimer = setTimeout(() => this.tryReconnect(), this.reconnectDuration);
return super.show([config]);
}

View File

@ -1,5 +1,3 @@
// biome-ignore-all lint/nursery/useUnifiedTypeSignature: Rule does not allow stuff with JSDoc comments
import type { FixedBattleConfig } from "#app/battle";
import { globalScene } from "#app/global-scene";
import { pokemonEvolutions } from "#balance/pokemon-evolutions";

View File

@ -23,11 +23,13 @@ export function getCookie(cName: string): string {
}
const name = `${cName}=`;
const ca = document.cookie.split(";");
// Check all cookies in the document and see if any of them match, grabbing the first one whose value lines up
for (const c of ca) {
const cTrimmed = c.trim();
if (cTrimmed.startsWith(name)) {
return c.slice(name.length, c.length);
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === " ") {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";

View File

@ -33,8 +33,8 @@ describe("Abilities - POWER CONSTRUCT", () => {
});
test("check if fainted 50% Power Construct Pokemon switches to base form on arena reset", async () => {
const baseForm = 2;
const completeForm = 4;
const baseForm = 2,
completeForm = 4;
game.override.startingWave(4).starterForms({
[SpeciesId.ZYGARDE]: completeForm,
});
@ -59,8 +59,8 @@ describe("Abilities - POWER CONSTRUCT", () => {
});
test("check if fainted 10% Power Construct Pokemon switches to base form on arena reset", async () => {
const baseForm = 3;
const completeForm = 5;
const baseForm = 3,
completeForm = 5;
game.override.startingWave(4).starterForms({
[SpeciesId.ZYGARDE]: completeForm,
});

View File

@ -29,8 +29,8 @@ describe("Abilities - SCHOOLING", () => {
});
test("check if fainted pokemon switches to base form on arena reset", async () => {
const soloForm = 0;
const schoolForm = 1;
const soloForm = 0,
schoolForm = 1;
game.override.startingWave(4).starterForms({
[SpeciesId.WISHIWASHI]: schoolForm,
});

View File

@ -34,8 +34,8 @@ describe("Abilities - SHIELDS DOWN", () => {
});
test("check if fainted pokemon switched to base form on arena reset", async () => {
const meteorForm = 0;
const coreForm = 7;
const meteorForm = 0,
coreForm = 7;
game.override.startingWave(4).starterForms({
[SpeciesId.MINIOR]: coreForm,
});

View File

@ -76,9 +76,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 2, escapeAttempts: 3, expectedEscapeChance: 80 },
];
for (const check of escapeChances) {
// set the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
for (let i = 0; i < escapeChances.length; i++) {
// sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
// set playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@ -86,10 +86,10 @@ describe("Escape chance calculations", () => {
20,
20,
20,
check.pokemonSpeedRatio * enemySpeed,
escapeChances[i].pokemonSpeedRatio * enemySpeed,
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
expect(chance).toBe(check.expectedEscapeChance);
expect(chance).toBe(escapeChances[i].expectedEscapeChance);
}
});
@ -146,9 +146,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 2, escapeAttempts: 10, expectedEscapeChance: 95 },
];
for (const check of escapeChances) {
for (let i = 0; i < escapeChances.length; i++) {
// sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
// set the first playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@ -156,7 +156,7 @@ describe("Escape chance calculations", () => {
20,
20,
20,
Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
]);
// set the second playerPokemon's speed to the remaining value of speed
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([
@ -165,13 +165,15 @@ describe("Escape chance calculations", () => {
20,
20,
20,
check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
// checks to make sure the escape values are the same
expect(chance).toBe(check.expectedEscapeChance);
expect(chance).toBe(escapeChances[i].expectedEscapeChance);
// checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed
expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(check.pokemonSpeedRatio * totalEnemySpeed);
expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(
escapeChances[i].pokemonSpeedRatio * totalEnemySpeed,
);
}
});
@ -236,9 +238,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 6.1, escapeAttempts: 3, expectedEscapeChance: 25 },
];
for (const check of escapeChances) {
for (let i = 0; i < escapeChances.length; i++) {
// sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
// set playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@ -246,10 +248,10 @@ describe("Escape chance calculations", () => {
20,
20,
20,
check.pokemonSpeedRatio * enemySpeed,
escapeChances[i].pokemonSpeedRatio * enemySpeed,
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
expect(chance).toBe(check.expectedEscapeChance);
expect(chance).toBe(escapeChances[i].expectedEscapeChance);
}
});
@ -319,9 +321,9 @@ describe("Escape chance calculations", () => {
{ pokemonSpeedRatio: 5.2, escapeAttempts: 2, expectedEscapeChance: 25 },
];
for (const check of escapeChances) {
for (let i = 0; i < escapeChances.length; i++) {
// sets the number of escape attempts to the required amount
game.scene.currentBattle.escapeAttempts = check.escapeAttempts;
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
// set the first playerPokemon's speed to a multiple of the enemySpeed
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([
20,
@ -329,7 +331,7 @@ describe("Escape chance calculations", () => {
20,
20,
20,
Math.floor(check.pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage),
]);
// set the second playerPokemon's speed to the remaining value of speed
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([
@ -338,13 +340,15 @@ describe("Escape chance calculations", () => {
20,
20,
20,
check.pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5],
]);
const chance = phase.calculateEscapeChance(game.scene.currentBattle.escapeAttempts);
// checks to make sure the escape values are the same
expect(chance).toBe(check.expectedEscapeChance);
expect(chance).toBe(escapeChances[i].expectedEscapeChance);
// checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed
expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(check.pokemonSpeedRatio * totalEnemySpeed);
expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(
escapeChances[i].pokemonSpeedRatio * totalEnemySpeed,
);
}
});
});

View File

@ -139,7 +139,8 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
const multiplierHolder = new NumberHolder(1);
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (globalScene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side) && move.getAttrs("CritOnlyAttr").length === 0) {
if (globalScene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side)) {
if (move.getAttrs("CritOnlyAttr").length === 0) {
globalScene.arena.applyTagsForSide(
ArenaTagType.AURORA_VEIL,
side,
@ -149,6 +150,7 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
multiplierHolder,
);
}
}
return move.power * multiplierHolder.value;
};

View File

@ -127,7 +127,8 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
const multiplierHolder = new NumberHolder(1);
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (globalScene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side) && move.getAttrs("CritOnlyAttr").length === 0) {
if (globalScene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side)) {
if (move.getAttrs("CritOnlyAttr").length === 0) {
globalScene.arena.applyTagsForSide(
ArenaTagType.LIGHT_SCREEN,
side,
@ -137,6 +138,7 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
multiplierHolder,
);
}
}
return move.power * multiplierHolder.value;
};

View File

@ -143,9 +143,11 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) =
const multiplierHolder = new NumberHolder(1);
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (globalScene.arena.getTagOnSide(ArenaTagType.REFLECT, side) && move.getAttrs("CritOnlyAttr").length === 0) {
if (globalScene.arena.getTagOnSide(ArenaTagType.REFLECT, side)) {
if (move.getAttrs("CritOnlyAttr").length === 0) {
globalScene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, false, attacker, move.category, multiplierHolder);
}
}
return move.power * multiplierHolder.value;
};

View File

@ -17,12 +17,16 @@ export class MenuManip {
private config;
private settingName;
private keycode;
private icon;
private iconDisplayed;
private specialCaseIcon;
constructor(config) {
this.config = config;
this.settingName = null;
this.icon = null;
this.iconDisplayed = null;
this.specialCaseIcon = null;
}
// TODO: Review this

View File

@ -18,10 +18,8 @@ import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper"
export class ClassicModeHelper extends GameManagerHelper {
/**
* Runs the classic game to the summon phase.
* @param species - An array of {@linkcode SpeciesId} to summon.
* @param species - An array of {@linkcode Species} to summon.
* @returns A promise that resolves when the summon phase is reached.
* @remarks
* Do not use this when {@linkcode startBattle} can be used!
*/
async runToSummon(species: SpeciesId[]): Promise<void>;
/**
@ -31,7 +29,6 @@ export class ClassicModeHelper extends GameManagerHelper {
* @returns A promise that resolves when the summon phase is reached.
* @deprecated - Specifying the starters helps prevent inconsistencies from internal RNG changes.
*/
// biome-ignore lint/nursery/useUnifiedTypeSignature: Marks the overload for deprecation
async runToSummon(): Promise<void>;
async runToSummon(species: SpeciesId[] | undefined): Promise<void>;
async runToSummon(species?: SpeciesId[]): Promise<void> {
@ -63,7 +60,7 @@ export class ClassicModeHelper extends GameManagerHelper {
/**
* Transitions to the start of a battle.
* @param species - An array of {@linkcode SpeciesId} to start the battle with.
* @param species - An array of {@linkcode Species} to start the battle with.
* @returns A promise that resolves when the battle is started.
*/
async startBattle(species: SpeciesId[]): Promise<void>;
@ -74,7 +71,6 @@ export class ClassicModeHelper extends GameManagerHelper {
* @returns A promise that resolves when the battle is started.
* @deprecated - Specifying the starters helps prevent inconsistencies from internal RNG changes.
*/
// biome-ignore lint/nursery/useUnifiedTypeSignature: Marks the overload for deprecation
async startBattle(): Promise<void>;
async startBattle(species?: SpeciesId[]): Promise<void> {
await this.runToSummon(species);

View File

@ -227,10 +227,12 @@ export class MoveHelper extends GameManagerHelper {
vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]);
console.warn("Player moveset override disabled due to use of `game.move.changeMoveset`!");
}
} else if (coerceArray(Overrides.OPP_MOVESET_OVERRIDE).length > 0) {
} else {
if (coerceArray(Overrides.OPP_MOVESET_OVERRIDE).length > 0) {
vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([]);
console.warn("Enemy moveset override disabled due to use of `game.move.changeMoveset`!");
}
}
moveset = coerceArray(moveset);
expect(moveset.length, "Cannot assign more than 4 moves to a moveset!").toBeLessThanOrEqual(4);
pokemon.moveset = [];

View File

@ -53,12 +53,14 @@ export class MockText implements MockGameObject {
wordWidthWithSpace += whiteSpaceWidth;
}
if (wordWidthWithSpace > spaceLeft) {
// Skip printing the newline if it's the first word of the line that is greater
// than the word wrap width.
if (wordWidthWithSpace > spaceLeft && j > 0) {
if (j > 0) {
result += "\n";
spaceLeft = this.wordWrapWidth;
}
}
result += word;

View File

@ -2,8 +2,10 @@
* Class will intercept any text or dialogue message calls and log them for test purposes
*/
export class TextInterceptor {
private scene;
public logs: string[] = [];
constructor(scene) {
this.scene = scene;
scene.messageWrapper = this;
}