diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 5f17fdedb78..18cc58ddaf1 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -273,11 +273,9 @@ export abstract class Challenge { * @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. - * @param checkEvolutions {@link boolean} If true, check the pokemon's future evolutions - * @param checkForms {@link boolean} If true, check the pokemon's alternative forms * @returns {@link boolean} Whether this function did anything. */ - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false, checkEvolutions?: boolean, checkForms?: boolean): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { return false; } @@ -405,14 +403,13 @@ export class SingleGenerationChallenge extends Challenge { super(Challenges.SINGLE_GENERATION, 9); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false, checkEvolutions?: boolean): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { const generations = [pokemon.generation]; - const checkPokemonEvolutions = checkEvolutions ?? true as boolean; if (soft) { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { + if (checking && pokemonEvolutions.hasOwnProperty(checking)) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); generations.push(getPokemonSpecies(e.speciesId).generation); @@ -533,22 +530,20 @@ export class SingleTypeChallenge extends Challenge { super(Challenges.SINGLE_TYPE, 18); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false, checkEvolutions?: boolean, checkForms?: boolean): boolean { + 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]; - const checkPokemonEvolutions = checkEvolutions ?? true as boolean; - const checkPokemonForms = checkForms ?? true as boolean; if (soft) { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { + if (checking && pokemonEvolutions.hasOwnProperty(checking)) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); }); } - if (checking && pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) { + if (checking && pokemonFormChanges.hasOwnProperty(checking)) { pokemonFormChanges[checking].forEach(f1 => { getPokemonSpecies(checking).forms.forEach(f2 => { if (f1.formKey === f2.formKey) { @@ -746,7 +741,7 @@ export class LowerStarterPointsChallenge extends Challenge { * @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, checkEvolutions?: boolean, checkForms?: boolean): boolean; +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 @@ -854,7 +849,7 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType if (c.value !== 0) { switch (challengeType) { case ChallengeType.STARTER_CHOICE: - ret ||= c.applyStarterChoice(args[0], args[1], args[2], args[3], args[4], args[5]); + ret ||= c.applyStarterChoice(args[0], args[1], args[2], args[3]); break; case ChallengeType.STARTER_POINTS: ret ||= c.applyStarterPoints(args[0]); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 8b756735ee6..b483ada2829 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -197,6 +197,7 @@ export interface StarterAttributes { variant?: integer; form?: integer; female?: boolean; + shiny?: boolean; } export interface StarterPreferences { diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index b61338bfcad..020b26b7f66 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -51,6 +51,9 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.V); + handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -112,6 +115,9 @@ describe("UI - Starter select", () => { handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); handler.processInput(Button.CYCLE_GENDER); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.V); + handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -176,6 +182,9 @@ describe("UI - Starter select", () => { handler.processInput(Button.CYCLE_GENDER); handler.processInput(Button.CYCLE_NATURE); handler.processInput(Button.CYCLE_ABILITY); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.V); + handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -238,6 +247,9 @@ describe("UI - Starter select", () => { handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); handler.processInput(Button.CYCLE_GENDER); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.V); + handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -298,7 +310,6 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -358,7 +369,7 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.V); + handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.V); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); @@ -419,7 +430,7 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.V); + handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.V); handler.processInput(Button.V); handler.processInput(Button.ACTION); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 447f5c95e46..5a6271cbc4b 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1303,11 +1303,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const isPartyValid = this.isPartyValid(); const isValidForChallenge = new Utils.BooleanHolder(true); - if (isPartyValid) { - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true)), this.starterSpecies.length !== 0); - } else { - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true)), this.starterSpecies.length !== 0, false, false); - } + + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), isPartyValid); const currentPartyValue = this.starterSpecies.map(s => s.generation).reduce((total: number, gen: number, i: number) => total += this.scene.gameData.getSpeciesStarterValue(this.starterSpecies[i].speciesId), 0); const newCost = this.scene.gameData.getSpeciesStarterValue(species.speciesId); @@ -1661,7 +1658,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { success = true; } } else { - const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor); + const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.getCurrentDexProps(this.lastSpecies.speciesId)); // prepare persistent starter data to store changes let starterAttributes = this.starterPreferences[this.lastSpecies.speciesId]; if (!starterAttributes) { @@ -1671,8 +1668,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { case Button.CYCLE_SHINY: if (this.canCycleShiny) { const newVariant = props.variant; + starterAttributes.shiny = starterAttributes.shiny ? !starterAttributes.shiny : true; this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, props.shiny ? 0 : undefined, undefined, undefined); - if (this.dexAttrCursor & DexAttr.SHINY) { + if (starterAttributes.shiny) { this.scene.playSound("sparkle"); // Set the variant label to the shiny tint const tint = getVariantTint(newVariant); @@ -1680,6 +1678,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonShinyIcon.setTint(tint); this.pokemonShinyIcon.setVisible(true); } else { + // starterAttributes.variant = 0; + if (starterAttributes?.variant) { + delete starterAttributes.variant; + } this.pokemonShinyIcon.setVisible(false); success = true; } @@ -1948,6 +1950,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.updateInstructions(); } + updatePartyIcon(species: PokemonSpecies, index: number) { + const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); + this.starterIcons[index].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant)); + this.starterIcons[index].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant)); + this.checkIconId(this.starterIcons[index], species, props.female, props.formIndex, props.shiny, props.variant); + } + switchMoveHandler(i: number, newMove: Moves, move: Moves) { const speciesId = this.lastSpecies.speciesId; const existingMoveIndex = this.starterMoveset?.indexOf(newMove)!; // TODO: is this bang correct? @@ -2099,9 +2108,24 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // pre filter for challenges if (this.scene.gameMode.modeId === GameModes.CHALLENGE) { this.starterContainers.forEach(container => { - const isValidForChallenge = new Utils.BooleanHolder(true); - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, container.species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(container.species, this.scene.gameData.getSpeciesDefaultDexAttr(container.species, false, true)), true); - if (isValidForChallenge.value) { + const species = container.species; + let allFormsValid = false; + if (species.forms?.length > 0) { + for (let i = 0; i < species.forms.length; i++) { + /* Here we are making a fake form index dex props for challenges + * Since some pokemon rely on forms to be valid (i.e. blaze tauros for fire challenges), we make a fake form and dex props to use in the challenge + */ + const tempFormProps = BigInt(Math.pow(2, i)) * DexAttr.DEFAULT_FORM; + const isValidForChallenge = new Utils.BooleanHolder(true); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, container.species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, tempFormProps), true); + allFormsValid = allFormsValid || isValidForChallenge.value; + } + } else { + const isValidForChallenge = new Utils.BooleanHolder(true); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, container.species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(container.species, false, true)), true); + allFormsValid = isValidForChallenge.value; + } + if (allFormsValid) { this.validStarterContainers.push(container); } else { container.setVisible(false); @@ -2111,6 +2135,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.validStarterContainers = this.starterContainers; } + // this updates icons for previously saved pokemon + 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); + const props = this.scene.gameData.getSpeciesDexAttrProps(currentFilteredContainer.species, currentDexAttr); + + starterSprite.setTexture(currentFilteredContainer.species.getIconAtlasKey(props.formIndex, props.shiny, props.variant), currentFilteredContainer.species.getIconId(props.female!, props.formIndex, props.shiny, props.variant)); + currentFilteredContainer.checkIconId(props.female, props.formIndex, props.shiny, props.variant); + } + // filter this.validStarterContainers.forEach(container => { container.setVisible(false); @@ -2336,9 +2372,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const species = this.filteredStarterContainers[cursor]?.species; if (species) { - const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true); + const defaultDexAttr = this.getCurrentDexProps(species.speciesId); const defaultProps = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); - const variant = defaultProps.variant; + const variant = this.starterPreferences[species.speciesId]?.variant ? this.starterPreferences[species.speciesId].variant as Variant : defaultProps.variant; const tint = getVariantTint(variant); this.pokemonShinyIcon.setFrame(getVariantIcon(variant)); this.pokemonShinyIcon.setTint(tint); @@ -2384,7 +2420,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { setSpecies(species: PokemonSpecies | null) { this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; - this.dexAttrCursor = species ? this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true) : 0n; + this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n; this.abilityCursor = species ? this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0; this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0; @@ -2454,7 +2490,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (this.lastSpecies) { - const dexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(this.lastSpecies, false, true); + const dexAttr = this.getCurrentDexProps(this.lastSpecies.speciesId); const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, dexAttr); const speciesIndex = this.allSpecies.indexOf(this.lastSpecies); const lastSpeciesIcon = this.starterContainers[speciesIndex].icon; @@ -2480,7 +2516,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonLuckText.setText(luck.toString()); this.pokemonLuckText.setTint(getVariantTint(Math.min(luck - 1, 2) as Variant)); this.pokemonLuckLabelText.setVisible(this.pokemonLuckText.visible); - this.pokemonShinyIcon.setVisible(this.pokemonLuckText.visible); + this.pokemonShinyIcon.setVisible(this.starterPreferences[species.speciesId]?.shiny ?? false); //Growth translate let growthReadable = Utils.toReadableString(GrowthRate[species.growthRate]); @@ -2504,7 +2540,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species)); } this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry.hatchedCount}`); - const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true); + const defaultDexAttr = this.getCurrentDexProps(species.speciesId); const defaultProps = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); const variant = defaultProps.variant; const tint = getVariantTint(variant); @@ -2578,13 +2614,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { props = this.scene.gameData.getSpeciesDexAttrProps(species, this.starterAttr[starterIndex]); this.setSpeciesDetails(species, props.shiny, props.formIndex, props.female, props.variant, this.starterAbilityIndexes[starterIndex], this.starterNatures[starterIndex]); } else { - const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true); + const defaultDexAttr = this.getCurrentDexProps(species.speciesId); const defaultAbilityIndex = starterAttributes?.ability ?? this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); // load default nature from stater save data, if set const defaultNature = starterAttributes?.nature || this.scene.gameData.getSpeciesDefaultNature(species); props = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); if (starterAttributes?.variant && !isNaN(starterAttributes.variant)) { - if (props.shiny = (starterAttributes.variant >= 0)) { + if (props.shiny) { props.variant = starterAttributes.variant as Variant; } } @@ -2679,6 +2715,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps!.formIndex)); // TODO: is this bang correct? this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex); this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex); + const [isInParty, partyIndex]: [boolean, number] = this.isInParty(species); // we use this to firstly check if the pokemon is in the party, and if so, to get the party index in order to update the icon image + if (isInParty) { + this.updatePartyIcon(species, partyIndex); + } } this.pokemonSprite.setVisible(false); @@ -2695,7 +2735,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const dexEntry = this.scene.gameData.dexData[species.speciesId]; const abilityAttr = this.scene.gameData.starterData[species.speciesId].abilityAttr; if (!dexEntry.caughtAttr) { - const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(species, forSeen, !forSeen)); + const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species); if (shiny === undefined || shiny !== props.shiny) { @@ -2750,9 +2790,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { 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.starterSpecies.length); - const starterSprite = this.filteredStarterContainers[this.cursor].icon as Phaser.GameObjects.Sprite; - starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female!, formIndex, shiny, variant)); // TODO: is this bang correct? - this.filteredStarterContainers[this.cursor].checkIconId(female, formIndex, shiny, variant); + const currentFilteredContainer = this.filteredStarterContainers.find(p => p.species.speciesId === species.speciesId)!; + const starterSprite = currentFilteredContainer.icon as Phaser.GameObjects.Sprite; + starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female!, formIndex, shiny, variant)); + currentFilteredContainer.checkIconId(female, formIndex, shiny, variant); this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY); this.canCycleGender = !!(dexEntry.caughtAttr & DexAttr.MALE && dexEntry.caughtAttr & DexAttr.FEMALE); this.canCycleAbility = [ abilityAttr & AbilityAttr.ABILITY_1, (abilityAttr & AbilityAttr.ABILITY_2) && species.ability2, abilityAttr & AbilityAttr.ABILITY_HIDDEN ].filter(a => a).length > 1; @@ -2876,6 +2917,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAdditionalMoveCountLabel.setText(`(+${Math.max(this.speciesStarterMoves.length - 4, 0)})`); this.pokemonAdditionalMoveCountLabel.setVisible(this.speciesStarterMoves.length > 4); + this.tryUpdateValue(); + this.updateInstructions(); } @@ -2903,7 +2946,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let s = 0; s < this.starterSpecies.length; s++) { const species = this.starterSpecies[s]; - const currentDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true); + const currentDexAttr = this.getCurrentDexProps(species.speciesId); const props = this.scene.gameData.getSpeciesDexAttrProps(species, currentDexAttr); this.starterIcons[s].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant)); this.starterIcons[s].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant)); @@ -2984,7 +3027,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (addingToParty) { // this does a check to see if the pokemon being added is valid; if so, it will update the isPartyValid boolean const isNewPokemonValid = new Utils.BooleanHolder(true); const species = this.filteredStarterContainers[this.cursor].species; - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isNewPokemonValid, this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true)), !!(this.starterSpecies.length), false, false); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isNewPokemonValid, this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), false); isPartyValid = isPartyValid || isNewPokemonValid.value; } @@ -3013,11 +3056,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); - if (isPartyValid) { // we have two checks here - one for the party being valid and one for not. This comes from mono type challenges - if the party is valid it will check pokemon's evolutions and forms, and if it's not valid it won't check their evolutions and forms - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, this.allSpecies[s], isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(this.allSpecies[s], this.scene.gameData.getSpeciesDefaultDexAttr(this.allSpecies[s], false, true)), !!(this.starterSpecies.length + (add ? 1 : 0))); - } else { - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, this.allSpecies[s], isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(this.allSpecies[s], this.scene.gameData.getSpeciesDefaultDexAttr(this.allSpecies[s], false, true)), !!(this.starterSpecies.length + (add ? 1 : 0)), false, false); - } + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, this.allSpecies[s], isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(this.allSpecies[s], this.getCurrentDexProps(this.allSpecies[s].speciesId)), isPartyValid); const canBeChosen = remainValue >= speciesStarterValue && isValidForChallenge.value; @@ -3132,12 +3171,50 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let s = 0; s < this.starterSpecies.length; s++) { const isValidForChallenge = new Utils.BooleanHolder(true); const species = this.starterSpecies[s]; - Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), this.starterSpecies.length !== 0, false, false); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), false); canStart = canStart || isValidForChallenge.value; } return canStart; } + /* this creates a temporary dex attr props that we use to check whether a pokemon is valid for a challenge. + * when checking for certain challenges (i.e. mono type), we need to check for form changes AND evolutions + * However, since some pokemon can evolve based on their intial gender/form, we need a way to look for that + * This temporary dex attr will therefore ONLY look at gender and form, since there's no cases of shinies/variants + * having different evolutions to their non shiny/variant part, and so those can be ignored + * Since the current form and gender is stored in the starter preferences, this is where we get the values from + */ + getCurrentDexProps(speciesId: number): bigint { + let props = 0n; + + if (this.starterPreferences[speciesId]?.female) { // this checks the gender of the pokemon + props += DexAttr.FEMALE; + } else { + props += DexAttr.MALE; + } + if (this.starterPreferences[speciesId]?.shiny) { + props += DexAttr.SHINY; + if (this.starterPreferences[speciesId]?.variant) { + props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT; + } else { + props += DexAttr.DEFAULT_VARIANT; + } + } else { + props += DexAttr.NON_SHINY; + if (this.starterPreferences[speciesId]?.variant) { + delete this.starterPreferences[speciesId].variant; + } + props += DexAttr.DEFAULT_VARIANT; // we add the default variant here because non shiny versions are listed as default variant + } + if (this.starterPreferences[speciesId]?.form) { // this checks for the form of the pokemon + props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.form)) * DexAttr.DEFAULT_FORM; + } else { + props += DexAttr.DEFAULT_FORM; + } + + return props; + } + toggleStatsMode(on?: boolean): void { if (on === undefined) { on = !this.statsMode;