pokerogue/src/system/achv.ts
Sirz Benjie 5854b21da0
[Refactor] Remove circular imports part 1 (#5663)
* Extract Mode enum out of UI and into its own file

Reduces circular imports from 909 to 773

* Move around utility files

Reduces cyclical dependencies from 773 to 765

* Remove starterColors and bypassLogin from battle-scene

Reduces cyclical dependencies from 765 to 623

* Fix test runner error

* Update import for bypassLogin in test

* Update mocks for utils in tests

* Fix broken tests

* Update selectWithTera override

* Update path for utils in ab-attr.ts

* Update path for utils in ability-class.ts

* Fix utils import path in healer.test.ts
2025-04-19 11:57:03 +00:00

937 lines
29 KiB
TypeScript

import type { Modifier } from "typescript";
import { TurnHeldItemTransferModifier } from "../modifier/modifier";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import i18next from "i18next";
import { NumberHolder } from "#app/utils/common";
import { PlayerGender } from "#enums/player-gender";
import type { Challenge } from "#app/data/challenge";
import {
FlipStatChallenge,
FreshStartChallenge,
SingleGenerationChallenge,
SingleTypeChallenge,
InverseBattleChallenge,
} from "#app/data/challenge";
import type { ConditionFn } from "#app/@types/common";
import { Stat, getShortenedStatKey } from "#app/enums/stat";
import { Challenges } from "#app/enums/challenges";
import { globalScene } from "#app/global-scene";
export enum AchvTier {
COMMON,
GREAT,
ULTRA,
ROGUE,
MASTER,
}
export class Achv {
public localizationKey: string;
public id: string;
public name: string;
public description: string;
public iconImage: string;
public score: number;
public secret: boolean;
public hasParent: boolean;
public parentId: string;
private conditionFunc: ConditionFn | undefined;
constructor(
localizationKey: string,
name: string,
description: string,
iconImage: string,
score: number,
conditionFunc?: ConditionFn,
) {
this.name = name;
this.description = description;
this.iconImage = iconImage;
this.score = score;
this.conditionFunc = conditionFunc;
this.localizationKey = localizationKey;
}
/**
* Get the name of the achievement based on the gender of the player
* @param playerGender - the gender of the player (default: {@linkcode PlayerGender.UNSET})
* @returns the name of the achievement localized for the player gender
*/
getName(playerGender: PlayerGender = PlayerGender.UNSET): string {
const genderStr = PlayerGender[playerGender].toLowerCase();
// Localization key is used to get the name of the achievement
return i18next.t(`achv:${this.localizationKey}.name`, {
context: genderStr,
});
}
getDescription(): string {
return this.description;
}
getIconImage(): string {
return this.iconImage;
}
setSecret(hasParent?: boolean): this {
this.secret = true;
this.hasParent = !!hasParent;
return this;
}
validate(args?: any[]): boolean {
return !this.conditionFunc || this.conditionFunc(args);
}
getTier(): AchvTier {
if (this.score >= 100) {
return AchvTier.MASTER;
}
if (this.score >= 75) {
return AchvTier.ROGUE;
}
if (this.score >= 50) {
return AchvTier.ULTRA;
}
if (this.score >= 25) {
return AchvTier.GREAT;
}
return AchvTier.COMMON;
}
}
export class MoneyAchv extends Achv {
moneyAmount: number;
constructor(localizationKey: string, name: string, moneyAmount: number, iconImage: string, score: number) {
super(localizationKey, name, "", iconImage, score, (_args: any[]) => globalScene.money >= this.moneyAmount);
this.moneyAmount = moneyAmount;
}
}
export class RibbonAchv extends Achv {
ribbonAmount: number;
constructor(localizationKey: string, name: string, ribbonAmount: number, iconImage: string, score: number) {
super(
localizationKey,
name,
"",
iconImage,
score,
(_args: any[]) => globalScene.gameData.gameStats.ribbonsOwned >= this.ribbonAmount,
);
this.ribbonAmount = ribbonAmount;
}
}
export class DamageAchv extends Achv {
damageAmount: number;
constructor(localizationKey: string, name: string, damageAmount: number, iconImage: string, score: number) {
super(
localizationKey,
name,
"",
iconImage,
score,
(args: any[]) => (args[0] instanceof NumberHolder ? args[0].value : args[0]) >= this.damageAmount,
);
this.damageAmount = damageAmount;
}
}
export class HealAchv extends Achv {
healAmount: number;
constructor(localizationKey: string, name: string, healAmount: number, iconImage: string, score: number) {
super(
localizationKey,
name,
"",
iconImage,
score,
(args: any[]) => (args[0] instanceof NumberHolder ? args[0].value : args[0]) >= this.healAmount,
);
this.healAmount = healAmount;
}
}
export class LevelAchv extends Achv {
level: number;
constructor(localizationKey: string, name: string, level: number, iconImage: string, score: number) {
super(
localizationKey,
name,
"",
iconImage,
score,
(args: any[]) => (args[0] instanceof NumberHolder ? args[0].value : args[0]) >= this.level,
);
this.level = level;
}
}
export class ModifierAchv extends Achv {
constructor(
localizationKey: string,
name: string,
description: string,
iconImage: string,
score: number,
modifierFunc: (modifier: Modifier) => boolean,
) {
super(localizationKey, name, description, iconImage, score, (args: any[]) => modifierFunc(args[0] as Modifier));
}
}
export class ChallengeAchv extends Achv {
constructor(
localizationKey: string,
name: string,
description: string,
iconImage: string,
score: number,
challengeFunc: (challenge: Challenge) => boolean,
) {
super(localizationKey, name, description, iconImage, score, (args: any[]) => challengeFunc(args[0] as Challenge));
}
}
/**
* Get the description of an achievement from the localization file with all the necessary variables filled in
* @param localizationKey The localization key of the achievement
* @returns The description of the achievement
*/
export function getAchievementDescription(localizationKey: string): string {
// We need to get the player gender from the game data to add the correct prefix to the achievement name
const genderIndex = globalScene?.gameData?.gender ?? PlayerGender.MALE;
const genderStr = PlayerGender[genderIndex].toLowerCase();
switch (localizationKey) {
case "10K_MONEY":
return i18next.t("achv:MoneyAchv.description", {
context: genderStr,
moneyAmount: achvs._10K_MONEY.moneyAmount.toLocaleString("en-US"),
});
case "100K_MONEY":
return i18next.t("achv:MoneyAchv.description", {
context: genderStr,
moneyAmount: achvs._100K_MONEY.moneyAmount.toLocaleString("en-US"),
});
case "1M_MONEY":
return i18next.t("achv:MoneyAchv.description", {
context: genderStr,
moneyAmount: achvs._1M_MONEY.moneyAmount.toLocaleString("en-US"),
});
case "10M_MONEY":
return i18next.t("achv:MoneyAchv.description", {
context: genderStr,
moneyAmount: achvs._10M_MONEY.moneyAmount.toLocaleString("en-US"),
});
case "250_DMG":
return i18next.t("achv:DamageAchv.description", {
context: genderStr,
damageAmount: achvs._250_DMG.damageAmount.toLocaleString("en-US"),
});
case "1000_DMG":
return i18next.t("achv:DamageAchv.description", {
context: genderStr,
damageAmount: achvs._1000_DMG.damageAmount.toLocaleString("en-US"),
});
case "2500_DMG":
return i18next.t("achv:DamageAchv.description", {
context: genderStr,
damageAmount: achvs._2500_DMG.damageAmount.toLocaleString("en-US"),
});
case "10000_DMG":
return i18next.t("achv:DamageAchv.description", {
context: genderStr,
damageAmount: achvs._10000_DMG.damageAmount.toLocaleString("en-US"),
});
case "250_HEAL":
return i18next.t("achv:HealAchv.description", {
context: genderStr,
healAmount: achvs._250_HEAL.healAmount.toLocaleString("en-US"),
HP: i18next.t(getShortenedStatKey(Stat.HP)),
});
case "1000_HEAL":
return i18next.t("achv:HealAchv.description", {
context: genderStr,
healAmount: achvs._1000_HEAL.healAmount.toLocaleString("en-US"),
HP: i18next.t(getShortenedStatKey(Stat.HP)),
});
case "2500_HEAL":
return i18next.t("achv:HealAchv.description", {
context: genderStr,
healAmount: achvs._2500_HEAL.healAmount.toLocaleString("en-US"),
HP: i18next.t(getShortenedStatKey(Stat.HP)),
});
case "10000_HEAL":
return i18next.t("achv:HealAchv.description", {
context: genderStr,
healAmount: achvs._10000_HEAL.healAmount.toLocaleString("en-US"),
HP: i18next.t(getShortenedStatKey(Stat.HP)),
});
case "LV_100":
return i18next.t("achv:LevelAchv.description", {
context: genderStr,
level: achvs.LV_100.level,
});
case "LV_250":
return i18next.t("achv:LevelAchv.description", {
context: genderStr,
level: achvs.LV_250.level,
});
case "LV_1000":
return i18next.t("achv:LevelAchv.description", {
context: genderStr,
level: achvs.LV_1000.level,
});
case "10_RIBBONS":
return i18next.t("achv:RibbonAchv.description", {
context: genderStr,
ribbonAmount: achvs._10_RIBBONS.ribbonAmount.toLocaleString("en-US"),
});
case "25_RIBBONS":
return i18next.t("achv:RibbonAchv.description", {
context: genderStr,
ribbonAmount: achvs._25_RIBBONS.ribbonAmount.toLocaleString("en-US"),
});
case "50_RIBBONS":
return i18next.t("achv:RibbonAchv.description", {
context: genderStr,
ribbonAmount: achvs._50_RIBBONS.ribbonAmount.toLocaleString("en-US"),
});
case "75_RIBBONS":
return i18next.t("achv:RibbonAchv.description", {
context: genderStr,
ribbonAmount: achvs._75_RIBBONS.ribbonAmount.toLocaleString("en-US"),
});
case "100_RIBBONS":
return i18next.t("achv:RibbonAchv.description", {
context: genderStr,
ribbonAmount: achvs._100_RIBBONS.ribbonAmount.toLocaleString("en-US"),
});
case "TRANSFER_MAX_STAT_STAGE":
return i18next.t("achv:TRANSFER_MAX_STAT_STAGE.description", {
context: genderStr,
});
case "MAX_FRIENDSHIP":
return i18next.t("achv:MAX_FRIENDSHIP.description", {
context: genderStr,
});
case "MEGA_EVOLVE":
return i18next.t("achv:MEGA_EVOLVE.description", { context: genderStr });
case "GIGANTAMAX":
return i18next.t("achv:GIGANTAMAX.description", { context: genderStr });
case "TERASTALLIZE":
return i18next.t("achv:TERASTALLIZE.description", { context: genderStr });
case "STELLAR_TERASTALLIZE":
return i18next.t("achv:STELLAR_TERASTALLIZE.description", {
context: genderStr,
});
case "SPLICE":
return i18next.t("achv:SPLICE.description", { context: genderStr });
case "MINI_BLACK_HOLE":
return i18next.t("achv:MINI_BLACK_HOLE.description", {
context: genderStr,
});
case "CATCH_MYTHICAL":
return i18next.t("achv:CATCH_MYTHICAL.description", {
context: genderStr,
});
case "CATCH_SUB_LEGENDARY":
return i18next.t("achv:CATCH_SUB_LEGENDARY.description", {
context: genderStr,
});
case "CATCH_LEGENDARY":
return i18next.t("achv:CATCH_LEGENDARY.description", {
context: genderStr,
});
case "SEE_SHINY":
return i18next.t("achv:SEE_SHINY.description", { context: genderStr });
case "SHINY_PARTY":
return i18next.t("achv:SHINY_PARTY.description", { context: genderStr });
case "HATCH_MYTHICAL":
return i18next.t("achv:HATCH_MYTHICAL.description", {
context: genderStr,
});
case "HATCH_SUB_LEGENDARY":
return i18next.t("achv:HATCH_SUB_LEGENDARY.description", {
context: genderStr,
});
case "HATCH_LEGENDARY":
return i18next.t("achv:HATCH_LEGENDARY.description", {
context: genderStr,
});
case "HATCH_SHINY":
return i18next.t("achv:HATCH_SHINY.description", { context: genderStr });
case "HIDDEN_ABILITY":
return i18next.t("achv:HIDDEN_ABILITY.description", {
context: genderStr,
});
case "PERFECT_IVS":
return i18next.t("achv:PERFECT_IVS.description", { context: genderStr });
case "CLASSIC_VICTORY":
return i18next.t("achv:CLASSIC_VICTORY.description", {
context: genderStr,
});
case "UNEVOLVED_CLASSIC_VICTORY":
return i18next.t("achv:UNEVOLVED_CLASSIC_VICTORY.description", {
context: genderStr,
});
case "MONO_GEN_ONE":
return i18next.t("achv:MONO_GEN_ONE.description", { context: genderStr });
case "MONO_GEN_TWO":
return i18next.t("achv:MONO_GEN_TWO.description", { context: genderStr });
case "MONO_GEN_THREE":
return i18next.t("achv:MONO_GEN_THREE.description", {
context: genderStr,
});
case "MONO_GEN_FOUR":
return i18next.t("achv:MONO_GEN_FOUR.description", {
context: genderStr,
});
case "MONO_GEN_FIVE":
return i18next.t("achv:MONO_GEN_FIVE.description", {
context: genderStr,
});
case "MONO_GEN_SIX":
return i18next.t("achv:MONO_GEN_SIX.description", { context: genderStr });
case "MONO_GEN_SEVEN":
return i18next.t("achv:MONO_GEN_SEVEN.description", {
context: genderStr,
});
case "MONO_GEN_EIGHT":
return i18next.t("achv:MONO_GEN_EIGHT.description", {
context: genderStr,
});
case "MONO_GEN_NINE":
return i18next.t("achv:MONO_GEN_NINE.description", {
context: genderStr,
});
case "MONO_NORMAL":
case "MONO_FIGHTING":
case "MONO_FLYING":
case "MONO_POISON":
case "MONO_GROUND":
case "MONO_ROCK":
case "MONO_BUG":
case "MONO_GHOST":
case "MONO_STEEL":
case "MONO_FIRE":
case "MONO_WATER":
case "MONO_GRASS":
case "MONO_ELECTRIC":
case "MONO_PSYCHIC":
case "MONO_ICE":
case "MONO_DRAGON":
case "MONO_DARK":
case "MONO_FAIRY":
return i18next.t("achv:MonoType.description", {
context: genderStr,
type: i18next.t(`pokemonInfo:Type.${localizationKey.slice(5)}`),
});
case "FRESH_START":
return i18next.t("achv:FRESH_START.description", { context: genderStr });
case "INVERSE_BATTLE":
return i18next.t("achv:INVERSE_BATTLE.description", {
context: genderStr,
});
case "FLIP_STATS":
return i18next.t("achv:FLIP_STATS.description", { context: genderStr });
case "FLIP_INVERSE":
return i18next.t("achv:FLIP_INVERSE.description", { context: genderStr });
case "BREEDERS_IN_SPACE":
return i18next.t("achv:BREEDERS_IN_SPACE.description", {
context: genderStr,
});
default:
return "";
}
}
export const achvs = {
_10K_MONEY: new MoneyAchv("10K_MONEY", "", 10000, "nugget", 10),
_100K_MONEY: new MoneyAchv("100K_MONEY", "", 100000, "big_nugget", 25).setSecret(true),
_1M_MONEY: new MoneyAchv("1M_MONEY", "", 1000000, "relic_gold", 50).setSecret(true),
_10M_MONEY: new MoneyAchv("10M_MONEY", "", 10000000, "coin_case", 100).setSecret(true),
_250_DMG: new DamageAchv("250_DMG", "", 250, "lucky_punch", 10),
_1000_DMG: new DamageAchv("1000_DMG", "", 1000, "lucky_punch_great", 25).setSecret(true),
_2500_DMG: new DamageAchv("2500_DMG", "", 2500, "lucky_punch_ultra", 50).setSecret(true),
_10000_DMG: new DamageAchv("10000_DMG", "", 10000, "lucky_punch_master", 100).setSecret(true),
_250_HEAL: new HealAchv("250_HEAL", "", 250, "potion", 10),
_1000_HEAL: new HealAchv("1000_HEAL", "", 1000, "super_potion", 25).setSecret(true),
_2500_HEAL: new HealAchv("2500_HEAL", "", 2500, "hyper_potion", 50).setSecret(true),
_10000_HEAL: new HealAchv("10000_HEAL", "", 10000, "max_potion", 100).setSecret(true),
LV_100: new LevelAchv("LV_100", "", 100, "rare_candy", 25).setSecret(),
LV_250: new LevelAchv("LV_250", "", 250, "rarer_candy", 50).setSecret(true),
LV_1000: new LevelAchv("LV_1000", "", 1000, "candy_jar", 100).setSecret(true),
_10_RIBBONS: new RibbonAchv("10_RIBBONS", "", 10, "bronze_ribbon", 10),
_25_RIBBONS: new RibbonAchv("25_RIBBONS", "", 25, "great_ribbon", 25).setSecret(true),
_50_RIBBONS: new RibbonAchv("50_RIBBONS", "", 50, "ultra_ribbon", 50).setSecret(true),
_75_RIBBONS: new RibbonAchv("75_RIBBONS", "", 75, "rogue_ribbon", 75).setSecret(true),
_100_RIBBONS: new RibbonAchv("100_RIBBONS", "", 100, "master_ribbon", 100).setSecret(true),
TRANSFER_MAX_STAT_STAGE: new Achv("TRANSFER_MAX_STAT_STAGE", "", "TRANSFER_MAX_STAT_STAGE.description", "baton", 20),
MAX_FRIENDSHIP: new Achv("MAX_FRIENDSHIP", "", "MAX_FRIENDSHIP.description", "soothe_bell", 25),
MEGA_EVOLVE: new Achv("MEGA_EVOLVE", "", "MEGA_EVOLVE.description", "mega_bracelet", 50),
GIGANTAMAX: new Achv("GIGANTAMAX", "", "GIGANTAMAX.description", "dynamax_band", 50),
TERASTALLIZE: new Achv("TERASTALLIZE", "", "TERASTALLIZE.description", "tera_orb", 25),
STELLAR_TERASTALLIZE: new Achv(
"STELLAR_TERASTALLIZE",
"",
"STELLAR_TERASTALLIZE.description",
"stellar_tera_shard",
25,
).setSecret(true),
SPLICE: new Achv("SPLICE", "", "SPLICE.description", "dna_splicers", 10),
MINI_BLACK_HOLE: new ModifierAchv(
"MINI_BLACK_HOLE",
"",
"MINI_BLACK_HOLE.description",
"mini_black_hole",
25,
modifier => modifier instanceof TurnHeldItemTransferModifier,
).setSecret(),
CATCH_MYTHICAL: new Achv("CATCH_MYTHICAL", "", "CATCH_MYTHICAL.description", "strange_ball", 50).setSecret(),
CATCH_SUB_LEGENDARY: new Achv("CATCH_SUB_LEGENDARY", "", "CATCH_SUB_LEGENDARY.description", "rb", 75).setSecret(),
CATCH_LEGENDARY: new Achv("CATCH_LEGENDARY", "", "CATCH_LEGENDARY.description", "mb", 100).setSecret(),
SEE_SHINY: new Achv("SEE_SHINY", "", "SEE_SHINY.description", "pb_gold", 75),
SHINY_PARTY: new Achv("SHINY_PARTY", "", "SHINY_PARTY.description", "shiny_charm", 100).setSecret(true),
HATCH_MYTHICAL: new Achv("HATCH_MYTHICAL", "", "HATCH_MYTHICAL.description", "mystery_egg", 75).setSecret(),
HATCH_SUB_LEGENDARY: new Achv(
"HATCH_SUB_LEGENDARY",
"",
"HATCH_SUB_LEGENDARY.description",
"oval_stone",
100,
).setSecret(),
HATCH_LEGENDARY: new Achv("HATCH_LEGENDARY", "", "HATCH_LEGENDARY.description", "lucky_egg", 125).setSecret(),
HATCH_SHINY: new Achv("HATCH_SHINY", "", "HATCH_SHINY.description", "golden_egg", 100).setSecret(),
HIDDEN_ABILITY: new Achv("HIDDEN_ABILITY", "", "HIDDEN_ABILITY.description", "ability_charm", 75),
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
CLASSIC_VICTORY: new Achv(
"CLASSIC_VICTORY",
"",
"CLASSIC_VICTORY.description",
"relic_crown",
150,
_ => globalScene.gameData.gameStats.sessionsWon === 0,
),
UNEVOLVED_CLASSIC_VICTORY: new Achv(
"UNEVOLVED_CLASSIC_VICTORY",
"",
"UNEVOLVED_CLASSIC_VICTORY.description",
"eviolite",
175,
_ => globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions),
),
MONO_GEN_ONE_VICTORY: new ChallengeAchv(
"MONO_GEN_ONE",
"",
"MONO_GEN_ONE.description",
"ribbon_gen1",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 1 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_TWO_VICTORY: new ChallengeAchv(
"MONO_GEN_TWO",
"",
"MONO_GEN_TWO.description",
"ribbon_gen2",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 2 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_THREE_VICTORY: new ChallengeAchv(
"MONO_GEN_THREE",
"",
"MONO_GEN_THREE.description",
"ribbon_gen3",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 3 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_FOUR_VICTORY: new ChallengeAchv(
"MONO_GEN_FOUR",
"",
"MONO_GEN_FOUR.description",
"ribbon_gen4",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 4 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_FIVE_VICTORY: new ChallengeAchv(
"MONO_GEN_FIVE",
"",
"MONO_GEN_FIVE.description",
"ribbon_gen5",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 5 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_SIX_VICTORY: new ChallengeAchv(
"MONO_GEN_SIX",
"",
"MONO_GEN_SIX.description",
"ribbon_gen6",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 6 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv(
"MONO_GEN_SEVEN",
"",
"MONO_GEN_SEVEN.description",
"ribbon_gen7",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 7 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv(
"MONO_GEN_EIGHT",
"",
"MONO_GEN_EIGHT.description",
"ribbon_gen8",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 8 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GEN_NINE_VICTORY: new ChallengeAchv(
"MONO_GEN_NINE",
"",
"MONO_GEN_NINE.description",
"ribbon_gen9",
100,
c =>
c instanceof SingleGenerationChallenge &&
c.value === 9 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_NORMAL: new ChallengeAchv(
"MONO_NORMAL",
"",
"MONO_NORMAL.description",
"silk_scarf",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 1 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_FIGHTING: new ChallengeAchv(
"MONO_FIGHTING",
"",
"MONO_FIGHTING.description",
"black_belt",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 2 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_FLYING: new ChallengeAchv(
"MONO_FLYING",
"",
"MONO_FLYING.description",
"sharp_beak",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 3 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_POISON: new ChallengeAchv(
"MONO_POISON",
"",
"MONO_POISON.description",
"poison_barb",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 4 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GROUND: new ChallengeAchv(
"MONO_GROUND",
"",
"MONO_GROUND.description",
"soft_sand",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 5 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_ROCK: new ChallengeAchv(
"MONO_ROCK",
"",
"MONO_ROCK.description",
"hard_stone",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 6 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_BUG: new ChallengeAchv(
"MONO_BUG",
"",
"MONO_BUG.description",
"silver_powder",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 7 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GHOST: new ChallengeAchv(
"MONO_GHOST",
"",
"MONO_GHOST.description",
"spell_tag",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 8 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_STEEL: new ChallengeAchv(
"MONO_STEEL",
"",
"MONO_STEEL.description",
"metal_coat",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 9 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_FIRE: new ChallengeAchv(
"MONO_FIRE",
"",
"MONO_FIRE.description",
"charcoal",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 10 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_WATER: new ChallengeAchv(
"MONO_WATER",
"",
"MONO_WATER.description",
"mystic_water",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 11 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_GRASS: new ChallengeAchv(
"MONO_GRASS",
"",
"MONO_GRASS.description",
"miracle_seed",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 12 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_ELECTRIC: new ChallengeAchv(
"MONO_ELECTRIC",
"",
"MONO_ELECTRIC.description",
"magnet",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 13 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_PSYCHIC: new ChallengeAchv(
"MONO_PSYCHIC",
"",
"MONO_PSYCHIC.description",
"twisted_spoon",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 14 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_ICE: new ChallengeAchv(
"MONO_ICE",
"",
"MONO_ICE.description",
"never_melt_ice",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 15 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_DRAGON: new ChallengeAchv(
"MONO_DRAGON",
"",
"MONO_DRAGON.description",
"dragon_fang",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 16 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_DARK: new ChallengeAchv(
"MONO_DARK",
"",
"MONO_DARK.description",
"black_glasses",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 17 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
MONO_FAIRY: new ChallengeAchv(
"MONO_FAIRY",
"",
"MONO_FAIRY.description",
"fairy_feather",
100,
c =>
c instanceof SingleTypeChallenge &&
c.value === 18 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
FRESH_START: new ChallengeAchv(
"FRESH_START",
"",
"FRESH_START.description",
"reviver_seed",
100,
c =>
c instanceof FreshStartChallenge &&
c.value > 0 &&
!globalScene.gameMode.challenges.some(
c => [Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT].includes(c.id) && c.value > 0,
),
),
INVERSE_BATTLE: new ChallengeAchv(
"INVERSE_BATTLE",
"",
"INVERSE_BATTLE.description",
"inverse",
100,
c => c instanceof InverseBattleChallenge && c.value > 0,
),
FLIP_STATS: new ChallengeAchv(
"FLIP_STATS",
"",
"FLIP_STATS.description",
"dubious_disc",
100,
c => c instanceof FlipStatChallenge && c.value > 0,
),
FLIP_INVERSE: new ChallengeAchv(
"FLIP_INVERSE",
"",
"FLIP_INVERSE.description",
"cracked_pot",
100,
c =>
c instanceof FlipStatChallenge &&
c.value > 0 &&
globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0),
).setSecret(),
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 50).setSecret(),
};
export function initAchievements() {
const achvKeys = Object.keys(achvs);
achvKeys.forEach((a: string, i: number) => {
achvs[a].id = a;
if (achvs[a].hasParent) {
achvs[a].parentId = achvKeys[i - 1];
}
});
}