diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 75b200b24ee..9035c7e7a3f 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,52 +1,94 @@ import * as Utils from "../utils"; import i18next from "i18next"; -import { DexAttrProps, GameData } from "#app/system/game-data.js"; +import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data.js"; import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "./pokemon-species"; -import Pokemon from "#app/field/pokemon.js"; +import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; import { BattleType, FixedBattleConfig } from "#app/battle.js"; import Trainer, { TrainerVariant } from "#app/field/trainer.js"; import { GameMode } from "#app/game-mode.js"; import { Type } from "./type"; -import { pokemonEvolutions } from "./pokemon-evolutions"; -import { pokemonFormChanges } from "./pokemon-forms"; import { Challenges } from "#enums/challenges"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; +import { Nature } from "./nature"; +import { Moves } from "#app/enums/moves.js"; import { TypeColor, TypeShadow } from "#app/enums/color.js"; +import { Gender } from "./gender"; +import { pokemonEvolutions } from "./pokemon-evolutions"; +import { pokemonFormChanges } from "./pokemon-forms"; /** * An enum for all the challenge types. The parameter entries on these describe the * parameters to use when calling the applyChallenges function. */ export enum ChallengeType { - /** - * Challenges which modify what starters you can choose - * @param args [0] {@link PokemonSpecies} The species to check - * [1] {@link Utils.BooleanHolder} Sets to false if illegal, pass in true. - */ + /** + * Challenges which modify what starters you can choose + * @see {@link Challenge.applyStarterChoice} + */ STARTER_CHOICE, - /** - * Challenges which modify how many starter points you have - * @param args [0] {@link Utils.NumberHolder} The amount of starter points you have - */ + /** + * Challenges which modify how many starter points you have + * @see {@link Challenge.applyStarterPoints} + */ STARTER_POINTS, + /** + * Challenges which modify how many starter points you have + * @see {@link Challenge.applyStarterPointCost} + */ + STARTER_COST, /** * Challenges which modify your starters in some way - * Not Fully Implemented + * @see {@link Challenge.applyStarterModify} */ STARTER_MODIFY, /** * Challenges which limit which pokemon you can have in battle. - * @param args [0] {@link Pokemon} The pokemon to check - * [1] {@link Utils.BooleanHolder} Sets to false if illegal, pass in true. + * @see {@link Challenge.applyPokemonInBattle} */ POKEMON_IN_BATTLE, /** * Adds or modifies the fixed battles in a run - * @param args [0] integer The wave to get a battle for - * [1] {@link FixedBattleConfig} A new fixed battle. It'll be modified if a battle exists. + * @see {@link Challenge.applyFixedBattle} */ FIXED_BATTLES, + /** + * Modifies what level the AI pokemon are. UNIMPLEMENTED. + */ + AI_LEVEL, + /** + * Modifies how many move slots the AI has. UNIMPLEMENTED. + */ + AI_MOVE_SLOTS, + /** + * Modifies if a pokemon has its passive. UNIMPLEMENTED. + */ + PASSIVE_ACCESS, + /** + * Modifies the game mode settings in some way. UNIMPLEMENTED. + */ + GAME_MODE_MODIFY, + /** + * Modifies what level AI pokemon can access a move. UNIMPLEMENTED. + */ + MOVE_ACCESS, + /** + * Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED. + */ + MOVE_WEIGHT, +} + +/** + * Used for challenge types that modify movesets, these denote the various sources of moves for pokemon. + */ +export enum MoveSourceType { + LEVEL_UP, // Currently unimplemented for move access + RELEARNER, // Relearner moves currently unimplemented + COMMON_TM, + GREAT_TM, + ULTRA_TM, + COMMON_EGG, + RARE_EGG } /** @@ -61,10 +103,9 @@ export abstract class Challenge { public maxSeverity: integer; // The maximum severity of the challenge. public conditions: ChallengeCondition[]; - public challengeTypes: ChallengeType[]; /** - * @param {Challenges} id The enum value for the challenge + * @param id {@link Challenges} The enum value for the challenge */ constructor(id: Challenges, maxValue: integer = Number.MAX_SAFE_INTEGER) { this.id = id; @@ -74,7 +115,6 @@ export abstract class Challenge { this.severity = 0; this.maxSeverity = 0; this.conditions = []; - this.challengeTypes = []; } /** @@ -87,7 +127,7 @@ export abstract class Challenge { /** * Gets the localisation key for the challenge - * @returns The i18n key for this challenge + * @returns {@link string} The i18n key for this challenge */ geti18nKey(): string { return Challenges[this.id].split("_").map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join(""); @@ -95,8 +135,8 @@ export abstract class Challenge { /** * Used for unlockable challenges to check if they're unlocked. - * @param {GameData} data The save data. - * @returns {boolean} Whether this challenge is unlocked. + * @param data {@link GameData} The save data. + * @returns {@link boolean} Whether this challenge is unlocked. */ isUnlocked(data: GameData): boolean { return this.conditions.every(f => f(data)); @@ -104,8 +144,8 @@ export abstract class Challenge { /** * Adds an unlock condition to this challenge. - * @param {ChallengeCondition} condition The condition to add. - * @returns {Challenge} This challenge + * @param condition {@link ChallengeCondition} The condition to add. + * @returns {@link Challenge} This challenge */ condition(condition: ChallengeCondition): Challenge { this.conditions.push(condition); @@ -114,27 +154,7 @@ export abstract class Challenge { } /** - * If this challenge is of a particular type - * @param {ChallengeType} challengeType The challenge type to check. - * @returns {Challenge} This challenge - */ - isOfType(challengeType: ChallengeType): boolean { - return this.challengeTypes.some(c => c === challengeType); - } - - /** - * Adds a challenge type to this challenge. - * @param {ChallengeType} challengeType The challenge type to add. - * @returns {Challenge} This challenge - */ - addChallengeType(challengeType: ChallengeType): Challenge { - this.challengeTypes.push(challengeType); - - return this; - } - - /** - * @returns {string} The localised name of this challenge. + * @returns {@link string} The localised name of this challenge. */ getName(): string { return i18next.t(`challenges:${this.geti18nKey()}.name`); @@ -142,8 +162,8 @@ export abstract class Challenge { /** * Returns the textual representation of a challenge's current value. - * @param {value} overrideValue The value to check for. If undefined, gets the current value. - * @returns {string} The localised name for the current value. + * @param overrideValue {@link integer} The value to check for. If undefined, gets the current value. + * @returns {@link string} The localised name for the current value. */ getValue(overrideValue?: integer): string { if (overrideValue === undefined) { @@ -154,19 +174,19 @@ export abstract class Challenge { /** * Returns the description of a challenge's current value. - * @param {value} overrideValue The value to check for. If undefined, gets the current value. - * @returns {string} The localised description for the current value. + * @param overrideValue {@link integer} The value to check for. If undefined, gets the current value. + * @returns {@link string} The localised description for the current value. */ getDescription(overrideValue?: integer): string { if (overrideValue === undefined) { overrideValue = this.value; } - return `${i18next.t("challenges:usePokemon")}${i18next.t(`challenges:${this.geti18nKey()}.desc.${this.value}`)}`; + return `${i18next.t([`challenges:${this.geti18nKey()}.desc.${this.value}`, `challenges:${this.geti18nKey()}.desc`])}`; } /** * Increase the value of the challenge - * @returns {boolean} Returns true if the value changed + * @returns {@link boolean} Returns true if the value changed */ increaseValue(): boolean { if (this.value < this.maxValue) { @@ -178,7 +198,7 @@ export abstract class Challenge { /** * Decrease the value of the challenge - * @returns {boolean} Returns true if the value changed + * @returns {@link boolean} Returns true if the value changed */ decreaseValue(): boolean { if (this.value > 0) { @@ -197,7 +217,7 @@ export abstract class Challenge { /** * Decrease the severity of the challenge - * @returns {boolean} Returns true if the value changed + * @returns {@link boolean} Returns true if the value changed */ decreaseSeverity(): boolean { if (this.severity > 0) { @@ -209,7 +229,7 @@ export abstract class Challenge { /** * Increase the severity of the challenge - * @returns {boolean} Returns true if the value changed + * @returns {@link boolean} Returns true if the value changed */ increaseSeverity(): boolean { if (this.severity < this.maxSeverity) { @@ -221,7 +241,7 @@ export abstract class Challenge { /** * Gets the "difficulty" value of this challenge. - * @returns {integer} The difficulty value. + * @returns {@link integer} The difficulty value. */ getDifficulty(): integer { return this.value; @@ -229,27 +249,145 @@ export abstract class Challenge { /** * Gets the minimum difficulty added by this challenge. - * @returns {integer} The difficulty value. + * @returns {@link integer} The difficulty value. */ getMinDifficulty(): integer { return 0; } - /** - * Modifies the data or game state in some way to apply the challenge. - * @param {ChallengeType} challengeType Which challenge type this is being applied for. - * @param args Irrelevant. See the specific challenge's apply function for additional information. - */ - abstract apply(challengeType: ChallengeType, args: any[]): boolean; - /** * Clones a challenge, either from another challenge or json. Chainable. - * @param {Challenge | any} source The source challenge of json. - * @returns {Challenge} This challenge. + * @param source The source challenge or json. + * @returns This challenge. */ static loadChallenge(source: Challenge | any): Challenge { throw new Error("Method not implemented! Use derived class"); } + + /** + * An apply function for STARTER_CHOICE challenges. Derived classes should alter this. + * @param pokemon {@link PokemonSpecies} The pokemon to check the validity of. + * @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon. + * @param soft {@link boolean} If true, allow it if it could become a valid pokemon. + * @returns {@link boolean} Whether this function did anything. + */ + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { + return false; + } + + /** + * An apply function for STARTER_POINTS challenges. Derived classes should alter this. + * @param points {@link Utils.NumberHolder} The amount of points you have available. + * @returns {@link boolean} Whether this function did anything. + */ + applyStarterPoints(points: Utils.NumberHolder): boolean { + return false; + } + + /** + * An apply function for STARTER_COST challenges. Derived classes should alter this. + * @param species {@link Species} The pokemon to change the cost of. + * @param cost {@link Utils.NumberHolder} The cost of the starter. + * @returns {@link boolean} Whether this function did anything. + */ + applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean { + return false; + } + + /** + * An apply function for STARTER_MODIFY challenges. Derived classes should alter this. + * @param pokemon {@link Pokemon} The starter pokemon to modify. + * @returns {@link boolean} Whether this function did anything. + */ + applyStarterModify(pokemon: Pokemon): boolean { + return false; + } + + /** + * An apply function for POKEMON_IN_BATTLE challenges. Derived classes should alter this. + * @param pokemon {@link Pokemon} The pokemon to check the validity of. + * @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @returns {@link boolean} Whether this function did anything. + */ + applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { + return false; + } + + /** + * An apply function for FIXED_BATTLE challenges. Derived classes should alter this. + * @param waveIndex {@link Number} The current wave index. + * @param battleConfig {@link FixedBattleConfig} The battle config to modify. + * @returns {@link boolean} Whether this function did anything. + */ + applyFixedBattle(waveIndex: Number, battleConfig: FixedBattleConfig): boolean { + return false; + } + + /** + * An apply function for AI_LEVEL challenges. Derived classes should alter this. + * @param level {@link Utils.IntegerHolder} The generated level. + * @param levelCap {@link Number} The current level cap. + * @param isTrainer {@link Boolean} Whether this is a trainer pokemon. + * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. + * @returns {@link boolean} Whether this function did anything. + */ + applyLevelChange(level: Utils.IntegerHolder, levelCap: number, isTrainer: boolean, isBoss: boolean): boolean { + return false; + } + + /** + * An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this. + * @param pokemon {@link Pokemon} The pokemon that is being considered. + * @param moveSlots {@link Utils.IntegerHolder} The amount of move slots. + * @returns {@link boolean} Whether this function did anything. + */ + applyMoveSlot(pokemon: Pokemon, moveSlots: Utils.IntegerHolder): boolean { + return false; + } + + /** + * An apply function for PASSIVE_ACCESS challenges. Derived classes should alter this. + * @param pokemon {@link Pokemon} The pokemon to change. + * @param hasPassive {@link Utils.BooleanHolder} Whether it should have its passive. + * @returns {@link boolean} Whether this function did anything. + */ + applyPassiveAccess(pokemon: Pokemon, hasPassive: Utils.BooleanHolder): boolean { + return false; + } + + /** + * An apply function for GAME_MODE_MODIFY challenges. Derived classes should alter this. + * @param gameMode {@link GameMode} The current game mode. + * @returns {@link boolean} Whether this function did anything. + */ + applyGameModeModify(gameMode: GameMode): boolean { + return false; + } + + /** + * An apply function for MOVE_ACCESS. Derived classes should alter this. + * @param pokemon {@link Pokemon} What pokemon would learn the move. + * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. + * @param move {@link Moves} The move in question. + * @param level {@link Utils.IntegerHolder} The level threshold for access. + * @returns {@link boolean} Whether this function did anything. + */ + applyMoveAccessLevel(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean { + return false; + } + + /** + * An apply function for MOVE_WEIGHT. Derived classes should alter this. + * @param pokemon {@link Pokemon} What pokemon would learn the move. + * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. + * @param move {@link Moves} The move in question. + * @param weight {@link Utils.IntegerHolder} The base weight of the move + * @returns {@link boolean} Whether this function did anything. + */ + applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean { + return false; + } } type ChallengeCondition = (data: GameData) => boolean; @@ -260,85 +398,72 @@ type ChallengeCondition = (data: GameData) => boolean; export class SingleGenerationChallenge extends Challenge { constructor() { super(Challenges.SINGLE_GENERATION, 9); - this.addChallengeType(ChallengeType.STARTER_CHOICE); - this.addChallengeType(ChallengeType.POKEMON_IN_BATTLE); - this.addChallengeType(ChallengeType.FIXED_BATTLES); } - apply(challengeType: ChallengeType, args: any[]): boolean { - if (this.value === 0) { - return false; - } - + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { /** * We have special code below for victini because it is classed as a generation 4 pokemon in the code * despite being a generation 5 pokemon. This is due to UI constraints, the starter select screen has * no more room for pokemon so victini is put in the gen 4 section instead. This code just overrides the * normal generation check to correctly treat victini as gen 5. */ - switch (challengeType) { - case ChallengeType.STARTER_CHOICE: - const species = args[0] as PokemonSpecies; - const isValidStarter = args[1] as Utils.BooleanHolder; - const amountOfPokemon = args[3] as number; - const starterGeneration = species.speciesId === Species.VICTINI ? 5 : species.generation; - const generations = [starterGeneration]; - if (amountOfPokemon > 0) { - const speciesToCheck = [species.speciesId]; - while (speciesToCheck.length) { - const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking)) { - pokemonEvolutions[checking].forEach(e => { - speciesToCheck.push(e.speciesId); - generations.push(getPokemonSpecies(e.speciesId).generation); - }); - } + const starterGeneration = pokemon.speciesId === Species.VICTINI ? 5 : pokemon.generation; + const generations = [starterGeneration]; + if (soft) { + const speciesToCheck = [pokemon.speciesId]; + while (speciesToCheck.length) { + const checking = speciesToCheck.pop(); + if (pokemonEvolutions.hasOwnProperty(checking)) { + pokemonEvolutions[checking].forEach(e => { + speciesToCheck.push(e.speciesId); + generations.push(getPokemonSpecies(e.speciesId).generation); + }); } } - if (!generations.includes(this.value)) { - isValidStarter.value = false; - return true; - } + } + + if (!generations.includes(this.value)) { + valid.value = false; + return true; + } + return false; + } + + applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { + const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation; + const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies.speciesId).generation : 0; + if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) { + valid.value = false; + return true; + } + return false; + } + + applyFixedBattle(waveIndex: Number, battleConfig: FixedBattleConfig): boolean { + let trainerTypes: TrainerType[] = []; + switch (waveIndex) { + case 182: + trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]),TrainerType.MARNIE_ELITE, TrainerType.RIKA ]; break; - case ChallengeType.POKEMON_IN_BATTLE: - const pokemon = args[0] as Pokemon; - const isValidPokemon = args[1] as Utils.BooleanHolder; - const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation; - const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies.speciesId).generation : 0; - if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) { - isValidPokemon.value = false; - return true; - } + case 184: + trainerTypes = [ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY ]; + break; + case 186: + trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([TrainerType.BEA_ELITE,TrainerType.ALLISTER_ELITE]), TrainerType.LARRY_ELITE ]; + break; + case 188: + trainerTypes = [ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL ]; + break; + case 190: + trainerTypes = [ TrainerType.BLUE, Utils.randSeedItem([ TrainerType.RED, TrainerType.LANCE_CHAMPION ]), Utils.randSeedItem([ TrainerType.STEVEN, TrainerType.WALLACE ]), TrainerType.CYNTHIA, Utils.randSeedItem([ TrainerType.ALDER, TrainerType.IRIS ]), TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, Utils.randSeedItem([ TrainerType.GEETA, TrainerType.NEMONA ]) ]; break; - case ChallengeType.FIXED_BATTLES: - const waveIndex = args[0] as integer; - const battleConfig = args[1] as FixedBattleConfig; - let trainerTypes: TrainerType[] = []; - switch (waveIndex) { - case 182: - trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]),TrainerType.MARNIE_ELITE, TrainerType.RIKA ]; - break; - case 184: - trainerTypes = [ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY ]; - break; - case 186: - trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([TrainerType.BEA_ELITE,TrainerType.ALLISTER_ELITE]), TrainerType.LARRY_ELITE ]; - break; - case 188: - trainerTypes = [ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL ]; - break; - case 190: - trainerTypes = [ TrainerType.BLUE, Utils.randSeedItem([ TrainerType.RED, TrainerType.LANCE_CHAMPION ]), Utils.randSeedItem([ TrainerType.STEVEN, TrainerType.WALLACE ]), TrainerType.CYNTHIA, Utils.randSeedItem([ TrainerType.ALDER, TrainerType.IRIS ]), TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, Utils.randSeedItem([ TrainerType.GEETA, TrainerType.NEMONA ]) ]; - break; - } - if (trainerTypes.length === 0) { - return false; - } else { - battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(scene => new Trainer(scene, trainerTypes[this.value - 1], TrainerVariant.DEFAULT)); - return true; - } } - return false; + if (trainerTypes.length === 0) { + return false; + } else { + battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(scene => new Trainer(scene, trainerTypes[this.value - 1], TrainerVariant.DEFAULT)); + return true; + } } /** @@ -407,62 +532,47 @@ export class SingleTypeChallenge extends Challenge { constructor() { super(Challenges.SINGLE_TYPE, 18); - this.addChallengeType(ChallengeType.STARTER_CHOICE); - this.addChallengeType(ChallengeType.POKEMON_IN_BATTLE); } - apply(challengeType: ChallengeType, args: any[]): boolean { - if (this.value === 0) { - return false; - } - - switch (challengeType) { - case ChallengeType.STARTER_CHOICE: - const species = args[0] as PokemonSpecies; - const isValidStarter = args[1] as Utils.BooleanHolder; - const dexAttr = args[2] as DexAttrProps; - const amountOfPokemon = args[3] as number; - const speciesForm = getPokemonSpeciesForm(species.speciesId, dexAttr.formIndex); - const types = [speciesForm.type1, speciesForm.type2]; - if (amountOfPokemon > 0) { - const speciesToCheck = [species.speciesId]; - while (speciesToCheck.length) { - const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking)) { - pokemonEvolutions[checking].forEach(e => { - speciesToCheck.push(e.speciesId); - types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); - }); - } - if (pokemonFormChanges.hasOwnProperty(checking)) { - pokemonFormChanges[checking].forEach(f1 => { - getPokemonSpecies(checking).forms.forEach(f2 => { - if (f1.formKey === f2.formKey) { - types.push(f2.type1, f2.type2); - } - }); + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { + const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex); + const types = [speciesForm.type1, speciesForm.type2]; + if (soft) { + const speciesToCheck = [pokemon.speciesId]; + while (speciesToCheck.length) { + const checking = speciesToCheck.pop(); + if (pokemonEvolutions.hasOwnProperty(checking)) { + pokemonEvolutions[checking].forEach(e => { + speciesToCheck.push(e.speciesId); + types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); + }); + } + if (pokemonFormChanges.hasOwnProperty(checking)) { + pokemonFormChanges[checking].forEach(f1 => { + getPokemonSpecies(checking).forms.forEach(f2 => { + if (f1.formKey === f2.formKey) { + types.push(f2.type1, f2.type2); + } }); - } + }); } } - if (!types.includes(this.value - 1)) { - isValidStarter.value = false; - return true; - } - break; - case ChallengeType.POKEMON_IN_BATTLE: - const pokemon = args[0] as Pokemon; - const isValidPokemon = args[1] as Utils.BooleanHolder; - if (pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true) - && !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies : pokemon.species).speciesId === o.species)) { - isValidPokemon.value = false; - return true; - } - break; + } + if (!types.includes(this.value - 1)) { + valid.value = false; + return true; } return false; } + applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { + if (pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true) + && !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies : pokemon.species).speciesId === o.species)) { + valid.value = false; + return true; + } + } + /** * @overrides */ @@ -512,28 +622,37 @@ export class SingleTypeChallenge extends Challenge { export class FreshStartChallenge extends Challenge { constructor() { super(Challenges.FRESH_START, 1); - this.addChallengeType(ChallengeType.STARTER_CHOICE); - this.addChallengeType(ChallengeType.STARTER_MODIFY); } - apply(challengeType: ChallengeType, args: any[]): boolean { - if (this.value === 0) { - return false; + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean { + if (!defaultStarterSpecies.includes(pokemon.speciesId)) { + valid.value = false; + return true; } + return false; + } - switch (challengeType) { - case ChallengeType.STARTER_CHOICE: - const species = args[0] as PokemonSpecies; - const isValidStarter = args[1] as Utils.BooleanHolder; - if (species) { - isValidStarter.value = false; - return true; - } - break; + applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean { + if (defaultStarterSpecies.includes(species) && cost.value !== 3) { + cost.value = 3; + return true; } return false; } + applyStarterModify(pokemon: Pokemon): boolean { + pokemon.abilityIndex = 0; // Always base ability, not hidden ability + pokemon.passive = false; // Passive isn't unlocked + pokemon.nature = Nature.HARDY; // Neutral nature + pokemon.moveset = pokemon.species.getLevelMoves().filter(m => m[0] <= 5).map(lm => lm[1]).slice(0, 4).map(m => new PokemonMove(m)); // No egg moves + pokemon.luck = 0; // No luck + pokemon.shiny = false; // Not shiny + pokemon.variant = 0; // Not shiny + pokemon.gender = Gender.MALE; // Starters default to male + pokemon.formIndex = 0; // Froakie should be base form + return true; + } + /** * @overrides */ @@ -555,7 +674,6 @@ export class FreshStartChallenge extends Challenge { export class LowerStarterMaxCostChallenge extends Challenge { constructor() { super(Challenges.LOWER_MAX_STARTER_COST, 9); - this.addChallengeType(ChallengeType.STARTER_CHOICE); } /** @@ -568,19 +686,10 @@ export class LowerStarterMaxCostChallenge extends Challenge { return (10 - overrideValue).toString(); } - apply(challengeType: ChallengeType, args: any[]): boolean { - if (this.value === 0) { - return false; - } - - switch (challengeType) { - case ChallengeType.STARTER_CHOICE: - const species = args[0] as PokemonSpecies; - const isValid = args[1] as Utils.BooleanHolder; - if (speciesStarters[species.speciesId] > 10 - this.value) { - isValid.value = false; - return true; - } + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean { + if (speciesStarters[pokemon.speciesId] > 10 - this.value) { + valid.value = false; + return true; } return false; } @@ -599,7 +708,6 @@ export class LowerStarterMaxCostChallenge extends Challenge { export class LowerStarterPointsChallenge extends Challenge { constructor() { super(Challenges.LOWER_STARTER_POINTS, 9); - this.addChallengeType(ChallengeType.STARTER_POINTS); } /** @@ -612,18 +720,9 @@ export class LowerStarterPointsChallenge extends Challenge { return (10 - overrideValue).toString(); } - apply(challengeType: ChallengeType, args: any[]): boolean { - if (this.value === 0) { - return false; - } - - switch (challengeType) { - case ChallengeType.STARTER_POINTS: - const points = args[0] as Utils.NumberHolder; - points.value -= this.value; - return true; - } - return false; + applyStarterPoints(points: Utils.NumberHolder): boolean { + points.value -= this.value; + return true; } static loadChallenge(source: LowerStarterPointsChallenge | any): LowerStarterPointsChallenge { @@ -635,22 +734,169 @@ export class LowerStarterPointsChallenge extends Challenge { } /** - * Apply all challenges of a given challenge type. - * @param {GameMode} gameMode The current game mode - * @param {ChallengeType} challengeType What challenge type to apply - * @param {any[]} args Any args for that challenge type - * @returns {boolean} True if any challenge was successfully applied. + * Apply all challenges that modify starter choice. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE + * @param pokemon {@link PokemonSpecies} The pokemon to check the validity of. + * @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon. + * @param soft {@link boolean} If true, allow it if it could become a valid pokemon. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.STARTER_CHOICE, pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean): boolean; +/** + * Apply all challenges that modify available total starter points. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS + * @param points {@link Utils.NumberHolder} The amount of points you have available. + * @returns True if any challenge was successfully applied. */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.STARTER_POINTS, points: Utils.NumberHolder): boolean; +/** + * Apply all challenges that modify the cost of a starter. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST + * @param species {@link Species} The pokemon to change the cost of. + * @param points {@link Utils.NumberHolder} The cost of the pokemon. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.STARTER_COST, species: Species, cost: Utils.NumberHolder): boolean; +/** + * Apply all challenges that modify a starter after selection. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY + * @param pokemon {@link Pokemon} The starter pokemon to modify. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.STARTER_MODIFY, pokemon: Pokemon): boolean; +/** + * Apply all challenges that what pokemon you can have in battle. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE + * @param pokemon {@link Pokemon} The pokemon to check the validity of. + * @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.POKEMON_IN_BATTLE, pokemon: Pokemon, valid: Utils.BooleanHolder): boolean; +/** + * Apply all challenges that modify what fixed battles there are. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.FIXED_BATTLES + * @param waveIndex {@link Number} The current wave index. + * @param battleConfig {@link FixedBattleConfig} The battle config to modify. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FIXED_BATTLES, waveIndex: Number, battleConfig: FixedBattleConfig): boolean; +/** + * Apply all challenges that modify what level AI are. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL + * @param level {@link Utils.IntegerHolder} The generated level of the pokemon. + * @param levelCap {@link Number} The maximum level cap for the current wave. + * @param isTrainer {@link Boolean} Whether this is a trainer pokemon. + * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.AI_LEVEL, level: Utils.IntegerHolder, levelCap: number, isTrainer: boolean, isBoss: boolean): boolean; +/** + * Apply all challenges that modify how many move slots the AI has. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS + * @param pokemon {@link Pokemon} The pokemon being considered. + * @param moveSlots {@link Utils.IntegerHolder} The amount of move slots. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.AI_MOVE_SLOTS, pokemon: Pokemon, moveSlots: Utils.IntegerHolder): boolean; +/** + * Apply all challenges that modify whether a pokemon has its passive. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS + * @param pokemon {@link Pokemon} The pokemon to modify. + * @param hasPassive {@link Utils.BooleanHolder} Whether it has its passive. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.PASSIVE_ACCESS, pokemon: Pokemon, hasPassive: Utils.BooleanHolder): boolean; +/** + * Apply all challenges that modify the game modes settings. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.GAME_MODE_MODIFY + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.GAME_MODE_MODIFY): boolean; +/** + * Apply all challenges that modify what level a pokemon can access a move. + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.MOVE_ACCESS + * @param pokemon {@link Pokemon} What pokemon would learn the move. + * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. + * @param move {@link Moves} The move in question. + * @param level {@link Utils.IntegerHolder} The level threshold for access. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_ACCESS, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean; +/** + * Apply all challenges that modify what weight a pokemon gives to move generation + * @param gameMode {@link GameMode} The current gameMode + * @param challengeType {@link ChallengeType} ChallengeType.MOVE_WEIGHT + * @param pokemon {@link Pokemon} What pokemon would learn the move. + * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. + * @param move {@link Moves} The move in question. + * @param weight {@link Utils.IntegerHolder} The weight of the move. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.IntegerHolder): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean { let ret = false; - gameMode.challenges.forEach(v => { - if (v.isOfType(challengeType)) { - ret ||= v.apply(challengeType, args); + gameMode.challenges.forEach(c => { + if (c.value !== 0) { + switch (challengeType) { + case ChallengeType.STARTER_CHOICE: + ret ||= c.applyStarterChoice(args[0], args[1], args[2], args[3]); + break; + case ChallengeType.STARTER_POINTS: + ret ||= c.applyStarterPoints(args[0]); + break; + case ChallengeType.STARTER_COST: + ret ||= c.applyStarterCost(args[0], args[1]); + break; + case ChallengeType.STARTER_MODIFY: + ret ||= c.applyStarterModify(args[0]); + break; + case ChallengeType.POKEMON_IN_BATTLE: + ret ||= c.applyPokemonInBattle(args[0], args[1]); + break; + case ChallengeType.FIXED_BATTLES: + ret ||= c.applyFixedBattle(args[0], args[1]); + break; + case ChallengeType.AI_LEVEL: + ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]); + break; + case ChallengeType.AI_MOVE_SLOTS: + ret ||= c.applyMoveSlot(args[0], args[1]); + break; + case ChallengeType.PASSIVE_ACCESS: + ret ||= c.applyPassiveAccess(args[0], args[1]); + break; + case ChallengeType.GAME_MODE_MODIFY: + ret ||= c.applyGameModeModify(gameMode); + break; + case ChallengeType.MOVE_ACCESS: + ret ||= c.applyMoveAccessLevel(args[0], args[1], args[2], args[3]); + break; + case ChallengeType.MOVE_WEIGHT: + ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]); + break; + } } }); return ret; } +/** + * + * @param source A challenge to copy, or an object of a challenge's properties. Missing values are treated as defaults. + * @returns The challenge in question. + */ export function copyChallenge(source: Challenge | any): Challenge { switch (source.id) { case Challenges.SINGLE_GENERATION: @@ -661,6 +907,8 @@ export function copyChallenge(source: Challenge | any): Challenge { return LowerStarterMaxCostChallenge.loadChallenge(source); case Challenges.LOWER_STARTER_POINTS: return LowerStarterPointsChallenge.loadChallenge(source); + case Challenges.FRESH_START: + return FreshStartChallenge.loadChallenge(source); } throw new Error("Unknown challenge copied"); } @@ -671,8 +919,6 @@ export function initChallenges() { allChallenges.push( new SingleGenerationChallenge(), new SingleTypeChallenge(), - // new LowerStarterMaxCostChallenge(), - // new LowerStarterPointsChallenge(), - // new FreshStartChallenge() + new FreshStartChallenge(), ); } diff --git a/src/locales/en/challenges.ts b/src/locales/en/challenges.ts index a40f05a0843..a3b45d31792 100644 --- a/src/locales/en/challenges.ts +++ b/src/locales/en/challenges.ts @@ -23,4 +23,10 @@ export const challenges: TranslationEntries = { "desc_default": "You can only use Pokémon of the chosen type." //types in pokemon-info }, + "freshStart": { + "name": "Fresh Start", + "desc": "You can only use the original starters, and only as if you had just started pokerogue.", + "value.0": "Off", + "value.1": "On", + } } as const; diff --git a/src/phases.ts b/src/phases.ts index c9f9c3c31b2..0ae6f375ce5 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -65,6 +65,7 @@ import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; +import { applyChallenges, ChallengeType } from "./data/challenge"; const { t } = i18next; @@ -624,6 +625,7 @@ export class SelectStarterPhase extends Phase { starterPokemon.generateFusionSpecies(true); } starterPokemon.setVisible(false); + applyChallenges(this.scene.gameMode, ChallengeType.STARTER_MODIFY, starterPokemon); party.push(starterPokemon); loadPokemonAssets.push(starterPokemon.loadAssets()); }); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index ac54c942fc7..2f5bcd1f5c2 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -40,6 +40,7 @@ import { GameDataType } from "#enums/game-data-type"; import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; +import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -1676,7 +1677,10 @@ export class GameData { value = decrementValue(value); } - return value; + const cost = new Utils.NumberHolder(value); + applyChallenges(this.scene.gameMode, ChallengeType.STARTER_COST, speciesId, cost); + + return cost.value; } getFormIndex(attr: bigint): integer { diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index dce04276a4d..6dcc359ef31 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -6,7 +6,7 @@ import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; import i18next from "i18next"; import { SelectStarterPhase, TitlePhase } from "#app/phases.js"; -import { Challenge, ChallengeType } from "#app/data/challenge.js"; +import { Challenge } from "#app/data/challenge.js"; import * as Utils from "../utils"; import { Challenges } from "#app/enums/challenges.js"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; @@ -27,7 +27,8 @@ export default class GameChallengesUiHandler extends UiHandler { private descriptionText: BBCodeText; - private challengeLabels: Array<{ label: Phaser.GameObjects.Text, value: Phaser.GameObjects.Text | Phaser.GameObjects.Sprite }>; + private challengeLabels: Array<{ label: Phaser.GameObjects.Text, value: Phaser.GameObjects.Text }>; + private monoTypeValue: Phaser.GameObjects.Sprite; private cursorObj: Phaser.GameObjects.NineSlice; @@ -120,33 +121,30 @@ export default class GameChallengesUiHandler extends UiHandler { this.challengeLabels = []; - for (let i = 0; i < Object.keys(ChallengeType).length; i++) { + for (let i = 0; i < 9; i++) { const label = addTextObject(this.scene, 8, 28 + i * 16, "", TextStyle.SETTINGS_LABEL); label.setName(`text-challenge-label-${i}`); label.setOrigin(0, 0); this.valuesContainer.add(label); - let value; - if (i === Challenges.SINGLE_TYPE) { - const type = `types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`; - value = this.scene.add.sprite(8, 98, type); - value.setName("sprite-single-type"); - value.setScale(0.86); - value.setPositionRelative(label, 113, 8); - } else { - value = addTextObject(this.scene, 0, 28 + i * 16, "", TextStyle.SETTINGS_LABEL); - value.setName(`text-challenge-value-label-${i}`); - value.setPositionRelative(label, 100, 0); - } - + const value = addTextObject(this.scene, 0, 28 + i * 16, "", TextStyle.SETTINGS_LABEL); + value.setName(`challenge-value-text-${i}`); + value.setPositionRelative(label, 100, 0); this.valuesContainer.add(value); + this.challengeLabels[i] = { label: label, value: value }; } + this.monoTypeValue = this.scene.add.sprite(8, 98, `types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`); + this.monoTypeValue.setName("challenge-value-monotype-sprite"); + this.monoTypeValue.setScale(0.86); + this.monoTypeValue.setVisible(false); + this.valuesContainer.add(this.monoTypeValue); + this.challengesContainer.add(headerBg); this.challengesContainer.add(headerText); // this.challengesContainer.add(difficultyBg); @@ -181,32 +179,39 @@ export default class GameChallengesUiHandler extends UiHandler { * init all challenge labels */ initLabels(): void { - this.setDescription(this.scene.gameMode.challenges[this.cursor].getDescription()); - this.scene.gameMode.challenges.forEach((challenge, i) => { - this.challengeLabels[i].label.setVisible(true); - this.challengeLabels[i].value.setVisible(true); - this.challengeLabels[i].label.setText(challenge.getName()); - if (this.challengeLabels[i].value.type.toLowerCase() === "sprite") { - (this.challengeLabels[i].value as Phaser.GameObjects.Sprite).setFrame(challenge.getValue()); - } else { - (this.challengeLabels[i].value as Phaser.GameObjects.Text).setText(challenge.getValue()); + this.setDescription(this.scene.gameMode.challenges[0].getDescription()); + for (let i = 0; i < 9; i++) { + if (i < this.scene.gameMode.challenges.length) { + this.challengeLabels[i].label.setVisible(true); + this.challengeLabels[i].value.setVisible(true); } - }); + } } /** * update the text the cursor is on */ updateText(): void { - const challenge = this.getActiveChallenge(); - const { id } = challenge; this.setDescription(this.getActiveChallenge().getDescription()); - - if (this.challengeLabels[id].value.type.toLowerCase() === "sprite") { - (this.challengeLabels[id].value as Phaser.GameObjects.Sprite).setFrame(challenge.getValue()); - } else { - (this.challengeLabels[id].value as Phaser.GameObjects.Text).setText(challenge.getValue()); + let monoTypeVisible = false; + for (let i = 0; i < Math.min(9, this.scene.gameMode.challenges.length); i++) { + const challenge = this.scene.gameMode.challenges[this.scrollCursor + i]; + this.challengeLabels[i].label.setText(challenge.getName()); + if (challenge.id === Challenges.SINGLE_TYPE) { + this.monoTypeValue.setPositionRelative(this.challengeLabels[i].label, 113, 8); + this.monoTypeValue.setFrame(challenge.getValue()); + this.monoTypeValue.setVisible(true); + this.challengeLabels[i].value.setVisible(false); + monoTypeVisible = true; + } else { + this.challengeLabels[i].value.setText(challenge.getValue()); + this.challengeLabels[i].value.setVisible(true); + } } + if (!monoTypeVisible) { + this.monoTypeValue.setVisible(false); + } + // const totalDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getDifficulty(), 0); // const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); // this.difficultyText.text = `${totalDifficulty}` + (totalMinDifficulty ? `/${totalMinDifficulty}` : ""); @@ -221,6 +226,7 @@ export default class GameChallengesUiHandler extends UiHandler { this.setCursor(0); this.initLabels(); + this.updateText(); this.getUi().moveTo(this.challengesContainer, this.getUi().length - 1); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 0caf9ef6fa6..c86f0168f40 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1137,7 +1137,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const species = this.genSpecies[this.getGenCursorWithScroll()][this.cursor]; const isValidForChallenge = new Utils.BooleanHolder(true); - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), this.starterGens.length); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), !!this.starterGens.length); if (!isDupe && isValidForChallenge.value && this.tryUpdateValue(this.scene.gameData.getSpeciesStarterValue(species.speciesId))) { const cursorObj = this.starterCursorObjs[this.starterCursors.length]; @@ -2325,7 +2325,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const currentPartyValue = this.starterGens.reduce((total: number, gen: number, i: number) => total += this.scene.gameData.getSpeciesStarterValue(this.genSpecies[gen][this.starterCursors[i]].speciesId), 0); const cursorCost = this.scene.gameData.getSpeciesStarterValue(species.speciesId); const isValidNextPartyValue = (currentPartyValue + cursorCost) <= this.getValueLimit(); - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), this.starterGens.length); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), !!this.starterGens.length); const starterSprite = this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite; starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant)); starterSprite.setAlpha(isValidForChallenge.value && isValidNextPartyValue ? 1 : 0.375); @@ -2545,7 +2545,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { * we change to can AddParty value to true since the user has enough cost to choose this pokemon and this pokemon registered too. */ const isValidForChallenge = new Utils.BooleanHolder(true); - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, this.genSpecies[g][s], isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(this.genSpecies[g][s], this.scene.gameData.getSpeciesDefaultDexAttr(this.genSpecies[g][s], false, true)), this.starterGens.length + (add ? 1 : 0)); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, this.genSpecies[g][s], isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(this.genSpecies[g][s], this.scene.gameData.getSpeciesDefaultDexAttr(this.genSpecies[g][s], false, true)), !!(this.starterGens.length || add)); const canBeChosen = remainValue >= speciesStarterValue && isValidForChallenge.value;