From 32d1b6b914e63dc5d0a91e606f54321fd3374fb9 Mon Sep 17 00:00:00 2001 From: Opaque02 <66582645+Opaque02@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:32:49 +1000 Subject: [PATCH] [QoL] Starter UI selection update to allow removing specific pokemon from party (#1983) * Initial commits with logic to remove starters if they're in your party. Still need to make it work so that the starter selection cursor disappears when a starter is unselected * Updated code to be able to remove pokemon, including the side icons and cursor locations * Fixed popstarter to work with any index * Updating code to allow navigation of starter icons * Updating code to allow navigation of party starter icons * Updaing navigation of party icons * Updated logic to fix incorrect icon in top left of pokemon options when navigating the starter icons * Updated logic to include the ability to navigate and interact with the starter icons * Forgot to push the actual starter-select-ui-handler. Might be a bit hard to test things out without that :) * Removed some unnecessary comments * Fixed small bug with not being able to move from the far right to the gen selection when the starter icons were empty * Updated code to not be using a method to generate the party menu and made it more like it used to be. This should help with merge conflicts in the future * I committed the merged version but forgot to make the starter-select-ui-handler staged after making the changes * Accidentally broke challenges that had a specific typing requirement with last commit. This should fix it * Changed how navigation worked based on popular demand * Fixed code review comments * Accidentally left in a whole block of commented code. Intentionally removing it now * Started adding logic for mono type challenge runs to not break the game if the user tries to start a run with an invalid party * Updated the text to say the party is invalid * Updated logic to make invalid pokemon greyed out when no valid pokemon are in your party * Added comments on some code * Updated locales to include the key for trying to start with invalid parties during a challenge * Fixed some code from a bad merge where a challenge related param that was previously a number now needed to be a boolean and wasn't * Removed comment as per review --- .gitignore | 3 +- src/data/challenge.ts | 21 +- src/locales/de/starter-select-ui-handler.ts | 1 + src/locales/en/starter-select-ui-handler.ts | 4 +- src/locales/es/starter-select-ui-handler.ts | 3 +- src/locales/fr/starter-select-ui-handler.ts | 3 +- src/locales/it/starter-select-ui-handler.ts | 3 +- src/locales/ko/starter-select-ui-handler.ts | 1 + .../pt_BR/starter-select-ui-handler.ts | 1 + .../zh_CN/starter-select-ui-handler.ts | 3 +- .../zh_TW/starter-select-ui-handler.ts | 3 +- src/ui/starter-select-ui-handler.ts | 529 +++++++++++++----- 12 files changed, 407 insertions(+), 168 deletions(-) diff --git a/.gitignore b/.gitignore index 0ae49fe9e1c..c22d0b2ce4c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ coverage # Local Documentation /typedoc -/dependency-graph.svg \ No newline at end of file +/dependency-graph.svg +/.vs diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 9035c7e7a3f..eda97e9cdad 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -270,9 +270,11 @@ 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): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false, checkEvolutions?: boolean, checkForms?: boolean): boolean { return false; } @@ -400,7 +402,7 @@ export class SingleGenerationChallenge extends Challenge { super(Challenges.SINGLE_GENERATION, 9); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false, checkEvolutions?: boolean): 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 @@ -409,11 +411,12 @@ export class SingleGenerationChallenge extends Challenge { */ const starterGeneration = pokemon.speciesId === Species.VICTINI ? 5 : pokemon.generation; const generations = [starterGeneration]; + const checkPokemonEvolutions = checkEvolutions ?? true as boolean; if (soft) { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking)) { + if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); generations.push(getPokemonSpecies(e.speciesId).generation); @@ -534,20 +537,22 @@ export class SingleTypeChallenge extends Challenge { super(Challenges.SINGLE_TYPE, 18); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean = false, checkEvolutions?: boolean, checkForms?: boolean): 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 (pokemonEvolutions.hasOwnProperty(checking)) { + if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); }); } - if (pokemonFormChanges.hasOwnProperty(checking)) { + if (pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) { pokemonFormChanges[checking].forEach(f1 => { getPokemonSpecies(checking).forms.forEach(f2 => { if (f1.formKey === f2.formKey) { @@ -743,7 +748,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): boolean; +export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.STARTER_CHOICE, pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps, soft: boolean, checkEvolutions?: boolean, checkForms?: boolean): boolean; /** * Apply all challenges that modify available total starter points. * @param gameMode {@link GameMode} The current gameMode @@ -851,7 +856,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]); + ret ||= c.applyStarterChoice(args[0], args[1], args[2], args[3], args[4], args[5]); break; case ChallengeType.STARTER_POINTS: ret ||= c.applyStarterPoints(args[0]); diff --git a/src/locales/de/starter-select-ui-handler.ts b/src/locales/de/starter-select-ui-handler.ts index 92ead61ebe7..9cf08846401 100644 --- a/src/locales/de/starter-select-ui-handler.ts +++ b/src/locales/de/starter-select-ui-handler.ts @@ -7,6 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "Mit diesen Pokémon losziehen?", + "invalidParty": "This is not a valid starting party!", "gen1": "I", "gen2": "II", "gen3": "III", diff --git a/src/locales/en/starter-select-ui-handler.ts b/src/locales/en/starter-select-ui-handler.ts index ac59785bab7..6d7b7f7cfb2 100644 --- a/src/locales/en/starter-select-ui-handler.ts +++ b/src/locales/en/starter-select-ui-handler.ts @@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * account interactions, descriptive text, etc. */ export const starterSelectUiHandler: SimpleTranslationEntries = { - "confirmStartTeam":"Begin with these Pokémon?", + "confirmStartTeam": "Begin with these Pokémon?", + "invalidParty": "This is not a valid starting party!", "gen1": "I", "gen2": "II", "gen3": "III", @@ -22,6 +23,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = { "nature": "Nature:", "eggMoves": "Egg Moves", "addToParty": "Add to Party", + "removeFromParty": "Remove from Party", "toggleIVs": "Toggle IVs", "manageMoves": "Manage Moves", "manageNature": "Manage Nature", diff --git a/src/locales/es/starter-select-ui-handler.ts b/src/locales/es/starter-select-ui-handler.ts index 14c22e22097..6f7d845ca64 100644 --- a/src/locales/es/starter-select-ui-handler.ts +++ b/src/locales/es/starter-select-ui-handler.ts @@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * account interactions, descriptive text, etc. */ export const starterSelectUiHandler: SimpleTranslationEntries = { - "confirmStartTeam":"¿Comenzar con estos Pokémon?", + "confirmStartTeam": "¿Comenzar con estos Pokémon?", + "invalidParty": "This is not a valid starting party!", "gen1": "I", "gen2": "II", "gen3": "III", diff --git a/src/locales/fr/starter-select-ui-handler.ts b/src/locales/fr/starter-select-ui-handler.ts index bddc740681e..fa45624c0f0 100644 --- a/src/locales/fr/starter-select-ui-handler.ts +++ b/src/locales/fr/starter-select-ui-handler.ts @@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * account interactions, descriptive text, etc. */ export const starterSelectUiHandler: SimpleTranslationEntries = { - "confirmStartTeam":"Commencer avec ces Pokémon ?", + "confirmStartTeam": "Commencer avec ces Pokémon ?", + "invalidParty": "This is not a valid starting party!", "gen1": "1G", "gen2": "2G", "gen3": "3G", diff --git a/src/locales/it/starter-select-ui-handler.ts b/src/locales/it/starter-select-ui-handler.ts index c84334fcd6a..8b42a0db8de 100644 --- a/src/locales/it/starter-select-ui-handler.ts +++ b/src/locales/it/starter-select-ui-handler.ts @@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * account interactions, descriptive text, etc. */ export const starterSelectUiHandler: SimpleTranslationEntries = { - "confirmStartTeam":"Vuoi iniziare con questi Pokémon?", + "confirmStartTeam": "Vuoi iniziare con questi Pokémon?", + "invalidParty": "This is not a valid starting party!", "gen1": "1ª", "gen2": "2ª", "gen3": "3ª", diff --git a/src/locales/ko/starter-select-ui-handler.ts b/src/locales/ko/starter-select-ui-handler.ts index d7f8ddbe3ed..a0c305b4266 100644 --- a/src/locales/ko/starter-select-ui-handler.ts +++ b/src/locales/ko/starter-select-ui-handler.ts @@ -7,6 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "이 포켓몬들로 시작하시겠습니까?", + "invalidParty": "This is not a valid starting party!", "gen1": "1세대", "gen2": "2세대", "gen3": "3세대", diff --git a/src/locales/pt_BR/starter-select-ui-handler.ts b/src/locales/pt_BR/starter-select-ui-handler.ts index eb2709a0da8..0a06048cb51 100644 --- a/src/locales/pt_BR/starter-select-ui-handler.ts +++ b/src/locales/pt_BR/starter-select-ui-handler.ts @@ -7,6 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "Começar com esses Pokémon?", + "invalidParty": "This is not a valid starting party!", "gen1": "G1", "gen2": "G2", "gen3": "G3", diff --git a/src/locales/zh_CN/starter-select-ui-handler.ts b/src/locales/zh_CN/starter-select-ui-handler.ts index 059358078f4..c23ceb66eb0 100644 --- a/src/locales/zh_CN/starter-select-ui-handler.ts +++ b/src/locales/zh_CN/starter-select-ui-handler.ts @@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * account interactions, descriptive text, etc. */ export const starterSelectUiHandler: SimpleTranslationEntries = { - "confirmStartTeam":"使用这些宝可梦开始游戏吗?", + "confirmStartTeam": "使用这些宝可梦开始游戏吗?", + "invalidParty": "This is not a valid starting party!", "gen1": "I", "gen2": "II", "gen3": "III", diff --git a/src/locales/zh_TW/starter-select-ui-handler.ts b/src/locales/zh_TW/starter-select-ui-handler.ts index ae9a4083d20..c3b78c8078d 100644 --- a/src/locales/zh_TW/starter-select-ui-handler.ts +++ b/src/locales/zh_TW/starter-select-ui-handler.ts @@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; * account interactions, descriptive text, etc. */ export const starterSelectUiHandler: SimpleTranslationEntries = { - "confirmStartTeam":"使用這些寶可夢開始嗎?", + "confirmStartTeam": "使用這些寶可夢開始嗎?", + "invalidParty": "This is not a valid starting party!", "gen1": "I", "gen2": "II", "gen3": "III", diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index d13122c5265..724a643ae22 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -39,6 +39,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import {Button} from "#enums/buttons"; import { EggSourceType } from "#app/enums/egg-source-types.js"; +import AwaitableUiHandler from "./awaitable-ui-handler"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -224,6 +225,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private genMode: boolean; private statsMode: boolean; + private starterIconsCursorXOffset: number = -2; + private starterIconsCursorYOffset: number = 1; + private starterIconsCursorIndex: number; private dexAttrCursor: bigint = 0n; private abilityCursor: integer = -1; private natureCursor: integer = -1; @@ -260,6 +264,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterIcons: Phaser.GameObjects.Sprite[]; private genCursorObj: Phaser.GameObjects.Image; private genCursorHighlightObj: Phaser.GameObjects.Image; + private starterIconsCursorObj: Phaser.GameObjects.Image; private valueLimitLabel: Phaser.GameObjects.Text; private startCursorObj: Phaser.GameObjects.NineSlice; private starterValueLabels: Phaser.GameObjects.Text[]; @@ -447,6 +452,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.genCursorObj.setOrigin(0, 0); this.starterSelectContainer.add(this.genCursorObj); + this.starterIconsCursorObj = this.scene.add.image(this.genCursorObj.x, 64, "select_gen_cursor"); + this.starterIconsCursorObj.setName("starter-icons-cursor"); + this.starterIconsCursorObj.setVisible(false); + this.starterIconsCursorObj.setOrigin(0, 0); + this.starterSelectContainer.add(this.starterIconsCursorObj); + this.valueLimitLabel = addTextObject(this.scene, 124, 150, "0/10", TextStyle.TOOLTIP_CONTENT); this.valueLimitLabel.setOrigin(0.5, 0); this.starterSelectContainer.add(this.valueLimitLabel); @@ -1064,7 +1075,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.toggleStatsMode(false); success = true; } else if (this.starterCursors.length) { - this.popStarter(); + this.popStarter(this.starterCursors.length - 1); success = true; this.updateInstructions(); } else { @@ -1078,7 +1089,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.getCurrentPhase().end(); success = true; } - } else if (this.startCursorObj.visible) { + } else if (this.startCursorObj.visible) { // this checks to see if the start button is selected + const genStarters = this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAll().length; + const rows = Math.ceil(genStarters / 9); switch (button) { case Button.ACTION: if (this.tryStart(true)) { @@ -1088,6 +1101,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } break; case Button.UP: + this.startCursorObj.setVisible(false); + if (this.starterCursors.length > 0) { + this.starterIconsCursorIndex = this.starterCursors.length - 1; + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } else { + this.setGenMode(true); + } + success = true; + break; + case Button.DOWN: this.startCursorObj.setVisible(false); this.setGenMode(true); success = true; @@ -1095,27 +1118,37 @@ export default class StarterSelectUiHandler extends MessageUiHandler { case Button.LEFT: this.startCursorObj.setVisible(false); this.setGenMode(false); - this.setCursor(this.cursor + 8); + this.setCursor(genStarters - 1); success = true; break; case Button.RIGHT: this.startCursorObj.setVisible(false); this.setGenMode(false); + this.setCursor((rows - 1) * 9); success = true; break; } - } else if (this.genMode) { + } else if (this.genMode && this.genCursorObj.visible) { // this checks to see if the generation selection icons are selected switch (button) { case Button.UP: - if (this.genCursor) { + if (this.genCursor > 0) { success = this.setCursor(this.genCursor - 1); + } else { + this.startCursorObj.setVisible(true); + this.setGenMode(true); + success = true; } break; case Button.DOWN: if (this.genCursor < 2) { success = this.setCursor(this.genCursor + 1); } else { - this.startCursorObj.setVisible(true); + if (this.starterCursors.length === 0) { + this.startCursorObj.setVisible(true); + } else { + this.starterIconsCursorIndex = 0; + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } this.setGenMode(true); success = true; } @@ -1129,66 +1162,72 @@ export default class StarterSelectUiHandler extends MessageUiHandler { break; } } else { + /** + * This code generates the menu for a pokemon when you press the action button + * This works in modules; it does a check for each option to see if it's valid, and if so, will add that option to the menu + * As an example, if you try to add an invalid pokemon, the "Add to Party" option won't show up + * But if you can still use candies or change natures, those menu items will be added + * Once it's all done, it displays the menu for you + **/ if (button === Button.ACTION) { if (!this.speciesStarterDexEntry?.caughtAttr) { error = true; - } else if (this.starterCursors.length < 6) { - const options = [ - { - label: i18next.t("starterSelectUiHandler:addToParty"), + } else if (this.starterCursors.length < 6) { // checks to see you have less than 6 pokemon in your party + let pokemonGen; + let pokemonCursor; + // this gets the correct generation and pokemon cursor depending on whether you're in the starter screen or the party icons + if (!this.starterIconsCursorObj.visible) { + pokemonGen = this.getGenCursorWithScroll(); + pokemonCursor = this.cursor; + } else { + pokemonGen = this.starterGens[this.starterIconsCursorIndex]; + pokemonCursor = this.starterCursors[this.starterIconsCursorIndex]; + } + const ui = this.getUi(); + let options = []; + + const [isDupe, removeIndex]: [boolean, number] = this.isInParty(pokemonGen, pokemonCursor); // checks to see if the pokemon is a duplicate; if it is, returns the index that will be removed + + const species = this.genSpecies[pokemonGen][pokemonCursor]; + + 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.starterGens.length)); + } 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.starterGens.length), false, false); + } + 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 newCost = this.scene.gameData.getSpeciesStarterValue(species.speciesId); + if (!isDupe && isValidForChallenge.value && currentPartyValue + newCost <= this.getValueLimit()) { // this checks to make sure the pokemon doesn't exist in your party, it's valid for the challenge and that it won't go over the cost limit; if it meets all these criteria it will add it to your party + options = [ + { + label: i18next.t("starterSelectUiHandler:addToParty"), + handler: () => { + ui.setMode(Mode.STARTER_SELECT); + + if (!isDupe && isValidForChallenge.value && this.tryUpdateValue(this.scene.gameData.getSpeciesStarterValue(species.speciesId), true)) { + this.addToParty(species, pokemonGen, pokemonCursor); + ui.playSelect(); + } else { + ui.playError(); // this should be redundant as there is now a trigger for when a pokemon can't be added to party + } + return true; + }, + overrideSound: true + }]; + } else if (isDupe) { // if it already exists in your party, it will give you the option to remove from your party + options = [{ + label: i18next.t("starterSelectUiHandler:removeFromParty"), handler: () => { + this.popStarter(removeIndex); ui.setMode(Mode.STARTER_SELECT); - let isDupe = false; - for (let s = 0; s < this.starterCursors.length; s++) { - if (this.starterGens[s] === this.getGenCursorWithScroll() && this.starterCursors[s] === this.cursor) { - isDupe = true; - break; - } - } - 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); - - if (!isDupe && isValidForChallenge.value && this.tryUpdateValue(this.scene.gameData.getSpeciesStarterValue(species.speciesId))) { - const cursorObj = this.starterCursorObjs[this.starterCursors.length]; - cursorObj.setVisible(true); - cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y); - const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor); - this.starterIcons[this.starterCursors.length].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant)); - this.starterIcons[this.starterCursors.length].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant)); - this.checkIconId(this.starterIcons[this.starterCursors.length], species, props.female, props.formIndex, props.shiny, props.variant); - this.starterGens.push(this.getGenCursorWithScroll()); - this.starterCursors.push(this.cursor); - this.starterAttr.push(this.dexAttrCursor); - this.starterAbilityIndexes.push(this.abilityCursor); - this.starterNatures.push(this.natureCursor as unknown as Nature); - this.starterMovesets.push(this.starterMoveset.slice(0) as StarterMoveset); - if (this.speciesLoaded.get(species.speciesId)) { - getPokemonSpeciesForm(species.speciesId, props.formIndex).cry(this.scene); - } - if (this.starterCursors.length === 6 || this.value === this.getValueLimit()) { - this.tryStart(); - } - this.updateInstructions(); - - /** - * If the user can't select a pokemon anymore, - * go to start button. - */ - if (!this.canAddParty) { - this.startCursorObj.setVisible(true); - this.setGenMode(true); - } - - ui.playSelect(); - } else { - ui.playError(); - } return true; - }, - overrideSound: true - }, + } + }]; + } + + options.push( // this shows the IVs for the pokemon { label: i18next.t("starterSelectUiHandler:toggleIVs"), handler: () => { @@ -1196,9 +1235,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setMode(Mode.STARTER_SELECT); return true; } - } - ]; - if (this.speciesStarterMoves.length > 1) { + }); + if (this.speciesStarterMoves.length > 1) { // this lets you change the pokemon moves const showSwapOptions = (moveset: StarterMoveset) => { ui.setMode(Mode.STARTER_SELECT).then(() => { ui.showText(i18next.t("starterSelectUiHandler:selectMoveSwapOut"), null, () => { @@ -1294,8 +1332,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { handler: () => { // update default nature in starter save data if (!starterAttributes) { - starterAttributes= - this.starterPreferences[this.lastSpecies.speciesId] = {}; + starterAttributes = + this.starterPreferences[this.lastSpecies.speciesId] = {}; } starterAttributes.nature = n as unknown as integer; this.clearText(); @@ -1330,7 +1368,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } const candyCount = starterData.candyCount; const passiveAttr = starterData.passiveAttr; - if (passiveAttr & PassiveAttr.UNLOCKED) { + if (passiveAttr & PassiveAttr.UNLOCKED) { // this is for enabling and disabling the passive if (!(passiveAttr & PassiveAttr.ENABLED)) { options.push({ label: i18next.t("starterSelectUiHandler:enablePassive"), @@ -1353,11 +1391,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }); } } - const showUseCandies = () => { + const showUseCandies = () => { // this lets you use your candies const options = []; if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); options.push({ + label: `x${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")} (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= passiveCost) { @@ -1375,8 +1414,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, undefined); // Update the candy upgrade display - if (this.isUpgradeIconEnabled() ) { - this.setUpgradeIcon(this.cursor); + if (this.isUpgradeIconEnabled()) { + this.setUpgradeIcon(pokemonCursor); } if (this.isUpgradeAnimationEnabled()) { const genSpecies = this.genSpecies[this.lastSpecies.generation - 1]; @@ -1408,15 +1447,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return this.scene.reset(true); } }); - this.updateStarterValueLabel(this.cursor); + this.updateStarterValueLabel(pokemonCursor); this.tryUpdateValue(0); ui.setMode(Mode.STARTER_SELECT); this.scene.playSound("buy"); // If the notification setting is set to 'On', update the candy upgrade display if (this.scene.candyUpgradeNotification === 2) { - if (this.isUpgradeIconEnabled() ) { - this.setUpgradeIcon(this.cursor); + if (this.isUpgradeIconEnabled()) { + this.setUpgradeIcon(pokemonCursor); } if (this.isUpgradeAnimationEnabled()) { const genSpecies = this.genSpecies[this.lastSpecies.generation - 1]; @@ -1521,6 +1560,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterPreferences[this.lastSpecies.speciesId] = {}; } switch (button) { + case Button.CYCLE_SHINY: if (this.canCycleShiny) { const newVariant = props.variant; @@ -1624,50 +1664,118 @@ export default class StarterSelectUiHandler extends MessageUiHandler { success = true; } break; + case Button.UP: - if (row) { - success = this.setCursor(this.cursor - 9); - } else { - // when strictly opposite starter based on rows length - // does not exits, set cursor on the second to last row - if (this.cursor + (rows - 1) * 9 > genStarters - 1) { - success = this.setCursor(this.cursor + (rows - 2) * 9); + if (!this.starterIconsCursorObj.visible) { + if (row) { + success = this.setCursor(this.cursor - 9); } else { - success = this.setCursor(this.cursor + (rows - 1) * 9); + // when strictly opposite starter based on rows length + // does not exits, set cursor on the second to last row + if (this.cursor + (rows - 1) * 9 > genStarters - 1) { + success = this.setCursor(this.cursor + (rows - 2) * 9); + } else { + success = this.setCursor(this.cursor + (rows - 1) * 9); + } + } + } else { + if (this.starterIconsCursorIndex === 0) { + this.starterIconsCursorObj.setVisible(false); + this.setSpecies(null); + this.setGenMode(true); + } else { + this.starterIconsCursorIndex--; + this.moveStarterIconsCursor(this.starterIconsCursorIndex); } } break; case Button.DOWN: - if (row < rows - 2 || (row < rows - 1 && this.cursor % 9 <= (genStarters - 1) % 9)) { - success = this.setCursor(this.cursor + 9); - } else { - // if there is no starter below while being on the second to - // last row, adjust cursor position with one line less - if (row === rows - 2 && this.cursor + 9 > genStarters - 1) { - success = this.setCursor(this.cursor - (rows - 2) * 9); + if (!this.starterIconsCursorObj.visible) { + if (row < rows - 2 || (row < rows - 1 && this.cursor % 9 <= (genStarters - 1) % 9)) { + success = this.setCursor(this.cursor + 9); } else { - success = this.setCursor(this.cursor - (rows - 1) * 9); + // if there is no starter below while being on the second to + // last row, adjust cursor position with one line less + if (row === rows - 2 && this.cursor + 9 > genStarters - 1) { + success = this.setCursor(this.cursor - (rows - 2) * 9); + } else { + success = this.setCursor(this.cursor - (rows - 1) * 9); + } + } + } else { + if (this.starterIconsCursorIndex <= this.starterCursors.length - 2) { + this.starterIconsCursorIndex++; + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } else { + this.starterIconsCursorObj.setVisible(false); + this.setSpecies(null); + this.startCursorObj.setVisible(true); } } break; case Button.LEFT: - if (this.cursor % 9) { - success = this.setCursor(this.cursor - 1); - } else { - if (row >= Math.min(5, rows - 1)) { - this.startCursorObj.setVisible(true); + if (!this.starterIconsCursorObj.visible) { + if (this.cursor % 9 !== 0) { + success = this.setCursor(this.cursor - 1); + } else { + if (this.starterCursors.length === 0) { + if (row >= Math.min(5, rows - 1)) { + this.startCursorObj.setVisible(true); + } + success = this.setGenMode(true); + } else { + if (row >= rows - 1) { // the last row will always go to the starter button + this.startCursorObj.setVisible(true); + } else if (row > 2) { // the first three rows will always go to the gen select, so anything else will go to the starterIcons party section + if (this.starterCursors.length >= row - 2) { + this.starterIconsCursorIndex = row - 3; + } else { + this.starterIconsCursorIndex = this.starterCursors.length - 1; + } + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } + success = this.setGenMode(true); + } } - success = this.setGenMode(true); + } else { + this.starterIconsCursorObj.setVisible(false); + this.setGenMode(false); + const rowToUse = Math.min(this.starterIconsCursorIndex + 3, rows - 1); + this.setCursor(Math.min((rowToUse * 9) + 8, genStarters - 1)); + success = true; } break; case Button.RIGHT: - if (this.cursor % 9 < (row < rows - 1 ? 8 : (genStarters - 1) % 9)) { - success = this.setCursor(this.cursor + 1); - } else { - if (row >= Math.min(5, rows - 1)) { - this.startCursorObj.setVisible(true); + if (!this.starterIconsCursorObj.visible) { + if (this.cursor % 9 < (row < rows - 1 ? 8 : (genStarters - 1) % 9)) { + success = this.setCursor(this.cursor + 1); + } else { + if (this.starterCursors.length === 0) { + if (row >= Math.min(5, rows - 1)) { + this.startCursorObj.setVisible(true); + } + success = this.setGenMode(true); + } else { + if (row >= rows - 1) { // the last row will always go to the starter button + this.startCursorObj.setVisible(true); + } else if (row > 2) { // the first three rows will always go to the gen select, so anything else will go to the starterIcons party section + if (this.starterCursors.length >= row - 2) { + this.starterIconsCursorIndex = row - 3; + } else { + this.starterIconsCursorIndex = this.starterCursors.length - 1; + } + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } + success = this.setGenMode(true); + } } - success = this.setGenMode(true); + } else { + this.starterIconsCursorObj.setVisible(false); + this.setGenMode(false); + const rowToUse = Math.min(this.starterIconsCursorIndex + 3, rows - 1); + this.setCursor(Math.min((rowToUse * 9), genStarters - 1)); + this.setSpecies(this.genSpecies[this.getGenCursorWithScroll()][this.cursor]); + success = true; } break; } @@ -1683,6 +1791,39 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return success || error; } + isInParty(pokemonGen: number, pokemonCursor: number): [boolean, number] { + let removeIndex = 0; + let isDupe = false; + for (let s = 0; s < this.starterCursors.length; s++) { + if (this.starterGens[s] === pokemonGen && this.starterCursors[s] === pokemonCursor) { + isDupe = true; + removeIndex = s; + break; + } + } + return [isDupe, removeIndex]; + } + + addToParty(species: PokemonSpecies, pokemonGen: number, pokemonCursor: number) { + const cursorObj = this.starterCursorObjs[this.starterCursors.length]; + cursorObj.setVisible(true); + cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y); + const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor); + this.starterIcons[this.starterCursors.length].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant)); + this.starterIcons[this.starterCursors.length].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant)); + this.checkIconId(this.starterIcons[this.starterCursors.length], species, props.female, props.formIndex, props.shiny, props.variant); + this.starterGens.push(pokemonGen); + this.starterCursors.push(pokemonCursor); + this.starterAttr.push(this.dexAttrCursor); + this.starterAbilityIndexes.push(this.abilityCursor); + this.starterNatures.push(this.natureCursor as unknown as Nature); + this.starterMovesets.push(this.starterMoveset.slice(0) as StarterMoveset); + if (this.speciesLoaded.get(species.speciesId)) { + getPokemonSpeciesForm(species.speciesId, props.formIndex).cry(this.scene); + } + this.updateInstructions(); + } + switchMoveHandler(i: number, newMove: Moves, move: Moves) { const speciesId = this.lastSpecies.speciesId; const existingMoveIndex = this.starterMoveset.indexOf(newMove); @@ -1939,8 +2080,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } setGenMode(genMode: boolean): boolean { - this.genCursorObj.setVisible(genMode && !this.startCursorObj.visible); - this.cursorObj.setVisible(!genMode && !this.startCursorObj.visible); + this.genCursorObj.setVisible(genMode && !(this.startCursorObj.visible || this.starterIconsCursorObj.visible)); + this.cursorObj.setVisible(!genMode && !(this.startCursorObj.visible || this.starterIconsCursorObj.visible)); if (genMode !== this.genMode) { this.genMode = genMode; @@ -1949,6 +2090,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (genMode) { this.setSpecies(null); } + if (this.starterIconsCursorObj.visible) { + this.setSpecies(this.genSpecies[this.starterGens[this.starterIconsCursorIndex]][this.starterCursors[this.starterIconsCursorIndex]]); + } return true; } @@ -1956,7 +2100,17 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return false; } - + moveStarterIconsCursor(index: number): void { + this.starterIconsCursorObj.x = this.starterIcons[index].x + this.starterIconsCursorXOffset; + this.starterIconsCursorObj.y = this.starterIcons[index].y + this.starterIconsCursorYOffset; + if (this.starterCursors.length > 0) { + this.starterIconsCursorObj.setVisible(true); + this.setSpecies(this.genSpecies[this.starterGens[index]][this.starterCursors[index]]); + } else { + this.starterIconsCursorObj.setVisible(false); + this.setSpecies(null); + } + } setSpecies(species: PokemonSpecies) { this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; @@ -2032,7 +2186,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const dexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(this.lastSpecies, false, true); const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, dexAttr); const lastSpeciesIcon = (this.starterSelectGenIconContainers[this.lastSpecies.generation - 1].getAt(this.genSpecies[this.lastSpecies.generation - 1].indexOf(this.lastSpecies)) as Phaser.GameObjects.Sprite); - lastSpeciesIcon.setTexture(this.lastSpecies.getIconAtlasKey(props.formIndex, props.shiny, props.variant), this.lastSpecies.getIconId(props.female, props.formIndex, props.shiny, props.variant)); this.checkIconId(lastSpeciesIcon, this.lastSpecies, props.female, props.formIndex, props.shiny, props.variant); this.iconAnimHandler.addOrUpdate(lastSpeciesIcon, PokemonIconAnimMode.NONE); @@ -2342,16 +2495,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonSprite.setVisible(!this.statsMode); }); - - const isValidForChallenge = new Utils.BooleanHolder(true); - 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); - 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); - this.checkIconId((this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite), species, female, formIndex, shiny, variant); + if (!this.starterIconsCursorObj.visible) { + (this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite) + .setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant)); + this.checkIconId((this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite), species, female, formIndex, shiny, variant); + } else { + (this.starterSelectGenIconContainers[this.starterGens[this.starterIconsCursorIndex]].getAt(this.starterCursors[this.starterIconsCursorIndex]) as Phaser.GameObjects.Sprite) + .setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant)); + this.checkIconId((this.starterSelectGenIconContainers[this.starterGens[this.starterIconsCursorIndex]].getAt(this.starterCursors[this.starterIconsCursorIndex]) as Phaser.GameObjects.Sprite), species, 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; @@ -2489,16 +2641,42 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } - popStarter(): void { - this.starterGens.pop(); - this.starterCursors.pop(); - this.starterAttr.pop(); - this.starterAbilityIndexes.pop(); - this.starterNatures.pop(); - this.starterMovesets.pop(); + popStarter(index: number): void { + this.starterGens.splice(index, 1); + this.starterCursors.splice(index, 1); + this.starterAttr.splice(index, 1); + this.starterAbilityIndexes.splice(index, 1); + this.starterNatures.splice(index, 1); + this.starterMovesets.splice(index, 1); + + for (let s = 0; s < this.starterCursors.length; s++) { + const species = this.genSpecies[this.starterGens[s]][this.starterCursors[s]]; + const currentDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true); + 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)); + if (s >= index) { + this.starterCursorObjs[s].setPosition(this.starterCursorObjs[s + 1].x, this.starterCursorObjs[s + 1].y); + this.starterCursorObjs[s].setVisible(this.starterCursorObjs[s + 1].visible); + } + } this.starterCursorObjs[this.starterCursors.length].setVisible(false); this.starterIcons[this.starterCursors.length].setTexture("pokemon_icons_0"); this.starterIcons[this.starterCursors.length].setFrame("unknown"); + + if (this.starterIconsCursorObj.visible) { + if (this.starterIconsCursorIndex === this.starterCursors.length) { + if (this.starterCursors.length > 0) { + this.starterIconsCursorIndex--; + } else { + this.starterIconsCursorObj.setVisible(false); + this.setSpecies(null); + this.setGenMode(true); + } + } + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } + this.tryUpdateValue(); } @@ -2528,7 +2706,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterValueLabels[cursor].setShadowColor(this.getTextColor(textStyle, true)); } - tryUpdateValue(add?: integer): boolean { + tryUpdateValue(add?: integer, addingToParty?: boolean): boolean { const value = this.starterGens.reduce((total: integer, gen: integer, i: integer) => total += this.scene.gameData.getSpeciesStarterValue(this.genSpecies[gen][this.starterCursors[i]].speciesId), 0); const newValue = value + (add || 0); const valueLimit = this.getValueLimit(); @@ -2545,6 +2723,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return false; } + let isPartyValid: boolean = this.isPartyValid(); // this checks to see if the party is valid + 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); + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, this.genSpecies[this.getGenCursorWithScroll()][this.cursor], isNewPokemonValid, this.scene.gameData.getSpeciesDexAttrProps(this.genSpecies[this.getGenCursorWithScroll()][this.cursor], this.scene.gameData.getSpeciesDefaultDexAttr(this.genSpecies[this.getGenCursorWithScroll()][this.cursor], false, true)), !!(this.starterGens.length), false, false); + isPartyValid = isPartyValid || isNewPokemonValid.value; + } + /** * this loop is used to set the Sprite's alpha value and check if the user can select other pokemon more. */ @@ -2563,24 +2748,41 @@ export default class StarterSelectUiHandler extends MessageUiHandler { * If remainValue greater than or equal pokemon species and the pokemon is legal for this challenge, the user can select. * so that the alpha value of pokemon sprite set 1. * + * However, if isPartyValid is false, that means none of the party members are valid for the run. In this case, we should + * check the challenge to make sure evolutions and forms aren't being checked for mono type runs. + * This will let us set the sprite's alpha to show it can't be selected + * * If speciesStarterDexEntry?.caughtAttr is true, this species registered in stater. * 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)); + 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.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))); + } else { + 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)), false, false); + } const canBeChosen = remainValue >= speciesStarterValue && isValidForChallenge.value; - if (canBeChosen) { + const isPokemonInParty = this.isInParty(g, s)[0]; // this will get the valud of isDupe from isInParty. This will let us see if the pokemon in question is in our party already so we don't grey out the sprites if they're invalid + + /* This code does a check to tell whether or not a sprite should be lit up or greyed out. There are 3 ways a pokemon's sprite should be lit up: + * 1) If it's in your party, it's a valid pokemon (i.e. for challenge) and you have enough points to have it + * 2) If it's in your party, it's not valid (i.e. for challenges), and you have enough points to have it + * 3) If it's not in your party, but it's a valid pokemon and you have enough points for it + * Any other time, the sprite should be greyed out. + * For example, if it's in your party, valid, but costs too much, or if it's not in your party and not valid, regardless of cost + */ + if (canBeChosen || (isPokemonInParty && remainValue >= speciesStarterValue)) { speciesSprite.setAlpha(1); if (speciesStarterDexEntry?.caughtAttr) { this.canAddParty = true; } } else { /** - * If it can't be chosen, the user can't select. - * so that the alpha value of pokemon sprite set 0.375. - */ + * If it can't be chosen, the user can't select. + * so that the alpha value of pokemon sprite set 0.375. + */ speciesSprite.setAlpha(0.375); } } @@ -2600,39 +2802,60 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const cancel = () => { ui.setMode(Mode.STARTER_SELECT); if (!manualTrigger) { - this.popStarter(); + this.popStarter(this.starterGens.length - 1); } this.clearText(); }; - ui.showText(i18next.t("starterSelectUiHandler:confirmStartTeam"), null, () => { - ui.setModeWithoutClear(Mode.CONFIRM, () => { - const startRun = () => { - this.scene.money = this.scene.gameMode.getStartingMoney(); - ui.setMode(Mode.STARTER_SELECT); - const thisObj = this; - const originalStarterSelectCallback = this.starterSelectCallback; - this.starterSelectCallback = null; - originalStarterSelectCallback(new Array(this.starterGens.length).fill(0).map(function (_, i) { - const starterSpecies = thisObj.genSpecies[thisObj.starterGens[i]][thisObj.starterCursors[i]]; - return { - species: starterSpecies, - dexAttr: thisObj.starterAttr[i], - abilityIndex: thisObj.starterAbilityIndexes[i], - passive: !(thisObj.scene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)), - nature: thisObj.starterNatures[i] as Nature, - moveset: thisObj.starterMovesets[i], - pokerus: !![ 0, 1, 2 ].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length - }; - })); - }; - startRun(); - }, cancel, null, null, 19); - }); + const canStart = this.isPartyValid(); + if (canStart) { + ui.showText(i18next.t("starterSelectUiHandler:confirmStartTeam"), null, () => { + ui.setModeWithoutClear(Mode.CONFIRM, () => { + const startRun = () => { + this.scene.money = this.scene.gameMode.getStartingMoney(); + ui.setMode(Mode.STARTER_SELECT); + const thisObj = this; + const originalStarterSelectCallback = this.starterSelectCallback; + this.starterSelectCallback = null; + originalStarterSelectCallback(new Array(this.starterGens.length).fill(0).map(function (_, i) { + const starterSpecies = thisObj.genSpecies[thisObj.starterGens[i]][thisObj.starterCursors[i]]; + return { + species: starterSpecies, + dexAttr: thisObj.starterAttr[i], + abilityIndex: thisObj.starterAbilityIndexes[i], + passive: !(thisObj.scene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)), + nature: thisObj.starterNatures[i] as Nature, + moveset: thisObj.starterMovesets[i], + pokerus: !![0, 1, 2].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length + }; + })); + }; + startRun(); + }, cancel, null, null, 19); + }); + } else { + const handler = this.scene.ui.getHandler() as AwaitableUiHandler; + handler.tutorialActive = true; + this.scene.ui.showText(i18next.t("starterSelectUiHandler:invalidParty"), null, () => this.scene.ui.showText(null, 0, () => handler.tutorialActive = false), null, true); + } return true; } + /* This block checks to see if your party is valid + * It checks each pokemon against the challenge - noting that due to monotype challenges it needs to check the pokemon while ignoring their evolutions/form change items + */ + isPartyValid(): boolean { + let canStart = false; + for (let s = 0; s < this.starterGens.length; s++) { + const isValidForChallenge = new Utils.BooleanHolder(true); + const species = this.genSpecies[this.starterGens[s]][this.starterCursors[s]]; + Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), !!(this.starterGens.length), false, false); + canStart = canStart || isValidForChallenge.value; + } + return canStart; + } + toggleStatsMode(on?: boolean): void { if (on === undefined) { on = !this.statsMode; @@ -2689,7 +2912,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.blockInput = false; while (this.starterCursors.length) { - this.popStarter(); + this.popStarter(this.starterCursors.length - 1); } if (this.statsMode) {