[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
This commit is contained in:
Opaque02 2024-07-23 08:32:49 +10:00 committed by GitHub
parent e2829f5225
commit 32d1b6b914
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 407 additions and 168 deletions

3
.gitignore vendored
View File

@ -39,4 +39,5 @@ coverage
# Local Documentation # Local Documentation
/typedoc /typedoc
/dependency-graph.svg /dependency-graph.svg
/.vs

View File

@ -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 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 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 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. * @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; return false;
} }
@ -400,7 +402,7 @@ export class SingleGenerationChallenge extends Challenge {
super(Challenges.SINGLE_GENERATION, 9); 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 * 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 * 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 starterGeneration = pokemon.speciesId === Species.VICTINI ? 5 : pokemon.generation;
const generations = [starterGeneration]; const generations = [starterGeneration];
const checkPokemonEvolutions = checkEvolutions ?? true as boolean;
if (soft) { if (soft) {
const speciesToCheck = [pokemon.speciesId]; const speciesToCheck = [pokemon.speciesId];
while (speciesToCheck.length) { while (speciesToCheck.length) {
const checking = speciesToCheck.pop(); const checking = speciesToCheck.pop();
if (pokemonEvolutions.hasOwnProperty(checking)) { if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) {
pokemonEvolutions[checking].forEach(e => { pokemonEvolutions[checking].forEach(e => {
speciesToCheck.push(e.speciesId); speciesToCheck.push(e.speciesId);
generations.push(getPokemonSpecies(e.speciesId).generation); generations.push(getPokemonSpecies(e.speciesId).generation);
@ -534,20 +537,22 @@ export class SingleTypeChallenge extends Challenge {
super(Challenges.SINGLE_TYPE, 18); 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 speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex);
const types = [speciesForm.type1, speciesForm.type2]; const types = [speciesForm.type1, speciesForm.type2];
const checkPokemonEvolutions = checkEvolutions ?? true as boolean;
const checkPokemonForms = checkForms ?? true as boolean;
if (soft) { if (soft) {
const speciesToCheck = [pokemon.speciesId]; const speciesToCheck = [pokemon.speciesId];
while (speciesToCheck.length) { while (speciesToCheck.length) {
const checking = speciesToCheck.pop(); const checking = speciesToCheck.pop();
if (pokemonEvolutions.hasOwnProperty(checking)) { if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) {
pokemonEvolutions[checking].forEach(e => { pokemonEvolutions[checking].forEach(e => {
speciesToCheck.push(e.speciesId); speciesToCheck.push(e.speciesId);
types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2);
}); });
} }
if (pokemonFormChanges.hasOwnProperty(checking)) { if (pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) {
pokemonFormChanges[checking].forEach(f1 => { pokemonFormChanges[checking].forEach(f1 => {
getPokemonSpecies(checking).forms.forEach(f2 => { getPokemonSpecies(checking).forms.forEach(f2 => {
if (f1.formKey === f2.formKey) { 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. * @param soft {@link boolean} If true, allow it if it could become a valid pokemon.
* @returns True if any challenge was successfully applied. * @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. * Apply all challenges that modify available total starter points.
* @param gameMode {@link GameMode} The current gameMode * @param gameMode {@link GameMode} The current gameMode
@ -851,7 +856,7 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
if (c.value !== 0) { if (c.value !== 0) {
switch (challengeType) { switch (challengeType) {
case ChallengeType.STARTER_CHOICE: 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; break;
case ChallengeType.STARTER_POINTS: case ChallengeType.STARTER_POINTS:
ret ||= c.applyStarterPoints(args[0]); ret ||= c.applyStarterPoints(args[0]);

View File

@ -7,6 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam": "Mit diesen Pokémon losziehen?", "confirmStartTeam": "Mit diesen Pokémon losziehen?",
"invalidParty": "This is not a valid starting party!",
"gen1": "I", "gen1": "I",
"gen2": "II", "gen2": "II",
"gen3": "III", "gen3": "III",

View File

@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
* account interactions, descriptive text, etc. * account interactions, descriptive text, etc.
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { 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", "gen1": "I",
"gen2": "II", "gen2": "II",
"gen3": "III", "gen3": "III",
@ -22,6 +23,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"nature": "Nature:", "nature": "Nature:",
"eggMoves": "Egg Moves", "eggMoves": "Egg Moves",
"addToParty": "Add to Party", "addToParty": "Add to Party",
"removeFromParty": "Remove from Party",
"toggleIVs": "Toggle IVs", "toggleIVs": "Toggle IVs",
"manageMoves": "Manage Moves", "manageMoves": "Manage Moves",
"manageNature": "Manage Nature", "manageNature": "Manage Nature",

View File

@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
* account interactions, descriptive text, etc. * account interactions, descriptive text, etc.
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { 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", "gen1": "I",
"gen2": "II", "gen2": "II",
"gen3": "III", "gen3": "III",

View File

@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
* account interactions, descriptive text, etc. * account interactions, descriptive text, etc.
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { 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", "gen1": "1G",
"gen2": "2G", "gen2": "2G",
"gen3": "3G", "gen3": "3G",

View File

@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
* account interactions, descriptive text, etc. * account interactions, descriptive text, etc.
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { 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ª", "gen1": "1ª",
"gen2": "2ª", "gen2": "2ª",
"gen3": "3ª", "gen3": "3ª",

View File

@ -7,6 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam": "이 포켓몬들로 시작하시겠습니까?", "confirmStartTeam": "이 포켓몬들로 시작하시겠습니까?",
"invalidParty": "This is not a valid starting party!",
"gen1": "1세대", "gen1": "1세대",
"gen2": "2세대", "gen2": "2세대",
"gen3": "3세대", "gen3": "3세대",

View File

@ -7,6 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam": "Começar com esses Pokémon?", "confirmStartTeam": "Começar com esses Pokémon?",
"invalidParty": "This is not a valid starting party!",
"gen1": "G1", "gen1": "G1",
"gen2": "G2", "gen2": "G2",
"gen3": "G3", "gen3": "G3",

View File

@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
* account interactions, descriptive text, etc. * account interactions, descriptive text, etc.
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam":"使用这些宝可梦开始游戏吗?", "confirmStartTeam": "使用这些宝可梦开始游戏吗?",
"invalidParty": "This is not a valid starting party!",
"gen1": "I", "gen1": "I",
"gen2": "II", "gen2": "II",
"gen3": "III", "gen3": "III",

View File

@ -6,7 +6,8 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
* account interactions, descriptive text, etc. * account interactions, descriptive text, etc.
*/ */
export const starterSelectUiHandler: SimpleTranslationEntries = { export const starterSelectUiHandler: SimpleTranslationEntries = {
"confirmStartTeam":"使用這些寶可夢開始嗎?", "confirmStartTeam": "使用這些寶可夢開始嗎?",
"invalidParty": "This is not a valid starting party!",
"gen1": "I", "gen1": "I",
"gen2": "II", "gen2": "II",
"gen3": "III", "gen3": "III",

View File

@ -39,6 +39,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import {Button} from "#enums/buttons"; import {Button} from "#enums/buttons";
import { EggSourceType } from "#app/enums/egg-source-types.js"; import { EggSourceType } from "#app/enums/egg-source-types.js";
import AwaitableUiHandler from "./awaitable-ui-handler";
export type StarterSelectCallback = (starters: Starter[]) => void; export type StarterSelectCallback = (starters: Starter[]) => void;
@ -224,6 +225,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private genMode: boolean; private genMode: boolean;
private statsMode: boolean; private statsMode: boolean;
private starterIconsCursorXOffset: number = -2;
private starterIconsCursorYOffset: number = 1;
private starterIconsCursorIndex: number;
private dexAttrCursor: bigint = 0n; private dexAttrCursor: bigint = 0n;
private abilityCursor: integer = -1; private abilityCursor: integer = -1;
private natureCursor: integer = -1; private natureCursor: integer = -1;
@ -260,6 +264,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private starterIcons: Phaser.GameObjects.Sprite[]; private starterIcons: Phaser.GameObjects.Sprite[];
private genCursorObj: Phaser.GameObjects.Image; private genCursorObj: Phaser.GameObjects.Image;
private genCursorHighlightObj: Phaser.GameObjects.Image; private genCursorHighlightObj: Phaser.GameObjects.Image;
private starterIconsCursorObj: Phaser.GameObjects.Image;
private valueLimitLabel: Phaser.GameObjects.Text; private valueLimitLabel: Phaser.GameObjects.Text;
private startCursorObj: Phaser.GameObjects.NineSlice; private startCursorObj: Phaser.GameObjects.NineSlice;
private starterValueLabels: Phaser.GameObjects.Text[]; private starterValueLabels: Phaser.GameObjects.Text[];
@ -447,6 +452,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.genCursorObj.setOrigin(0, 0); this.genCursorObj.setOrigin(0, 0);
this.starterSelectContainer.add(this.genCursorObj); 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 = addTextObject(this.scene, 124, 150, "0/10", TextStyle.TOOLTIP_CONTENT);
this.valueLimitLabel.setOrigin(0.5, 0); this.valueLimitLabel.setOrigin(0.5, 0);
this.starterSelectContainer.add(this.valueLimitLabel); this.starterSelectContainer.add(this.valueLimitLabel);
@ -1064,7 +1075,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.toggleStatsMode(false); this.toggleStatsMode(false);
success = true; success = true;
} else if (this.starterCursors.length) { } else if (this.starterCursors.length) {
this.popStarter(); this.popStarter(this.starterCursors.length - 1);
success = true; success = true;
this.updateInstructions(); this.updateInstructions();
} else { } else {
@ -1078,7 +1089,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.scene.getCurrentPhase().end(); this.scene.getCurrentPhase().end();
success = true; 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) { switch (button) {
case Button.ACTION: case Button.ACTION:
if (this.tryStart(true)) { if (this.tryStart(true)) {
@ -1088,6 +1101,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
break; break;
case Button.UP: 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.startCursorObj.setVisible(false);
this.setGenMode(true); this.setGenMode(true);
success = true; success = true;
@ -1095,27 +1118,37 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
case Button.LEFT: case Button.LEFT:
this.startCursorObj.setVisible(false); this.startCursorObj.setVisible(false);
this.setGenMode(false); this.setGenMode(false);
this.setCursor(this.cursor + 8); this.setCursor(genStarters - 1);
success = true; success = true;
break; break;
case Button.RIGHT: case Button.RIGHT:
this.startCursorObj.setVisible(false); this.startCursorObj.setVisible(false);
this.setGenMode(false); this.setGenMode(false);
this.setCursor((rows - 1) * 9);
success = true; success = true;
break; 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) { switch (button) {
case Button.UP: case Button.UP:
if (this.genCursor) { if (this.genCursor > 0) {
success = this.setCursor(this.genCursor - 1); success = this.setCursor(this.genCursor - 1);
} else {
this.startCursorObj.setVisible(true);
this.setGenMode(true);
success = true;
} }
break; break;
case Button.DOWN: case Button.DOWN:
if (this.genCursor < 2) { if (this.genCursor < 2) {
success = this.setCursor(this.genCursor + 1); success = this.setCursor(this.genCursor + 1);
} else { } 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); this.setGenMode(true);
success = true; success = true;
} }
@ -1129,66 +1162,72 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
break; break;
} }
} else { } 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 (button === Button.ACTION) {
if (!this.speciesStarterDexEntry?.caughtAttr) { if (!this.speciesStarterDexEntry?.caughtAttr) {
error = true; error = true;
} else if (this.starterCursors.length < 6) { } else if (this.starterCursors.length < 6) { // checks to see you have less than 6 pokemon in your party
const options = [ let pokemonGen;
{ let pokemonCursor;
label: i18next.t("starterSelectUiHandler:addToParty"), // 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: () => { handler: () => {
this.popStarter(removeIndex);
ui.setMode(Mode.STARTER_SELECT); 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; return true;
}, }
overrideSound: true }];
}, }
options.push( // this shows the IVs for the pokemon
{ {
label: i18next.t("starterSelectUiHandler:toggleIVs"), label: i18next.t("starterSelectUiHandler:toggleIVs"),
handler: () => { handler: () => {
@ -1196,9 +1235,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
ui.setMode(Mode.STARTER_SELECT); ui.setMode(Mode.STARTER_SELECT);
return true; return true;
} }
} });
]; if (this.speciesStarterMoves.length > 1) { // this lets you change the pokemon moves
if (this.speciesStarterMoves.length > 1) {
const showSwapOptions = (moveset: StarterMoveset) => { const showSwapOptions = (moveset: StarterMoveset) => {
ui.setMode(Mode.STARTER_SELECT).then(() => { ui.setMode(Mode.STARTER_SELECT).then(() => {
ui.showText(i18next.t("starterSelectUiHandler:selectMoveSwapOut"), null, () => { ui.showText(i18next.t("starterSelectUiHandler:selectMoveSwapOut"), null, () => {
@ -1294,8 +1332,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
handler: () => { handler: () => {
// update default nature in starter save data // update default nature in starter save data
if (!starterAttributes) { if (!starterAttributes) {
starterAttributes= starterAttributes =
this.starterPreferences[this.lastSpecies.speciesId] = {}; this.starterPreferences[this.lastSpecies.speciesId] = {};
} }
starterAttributes.nature = n as unknown as integer; starterAttributes.nature = n as unknown as integer;
this.clearText(); this.clearText();
@ -1330,7 +1368,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
const candyCount = starterData.candyCount; const candyCount = starterData.candyCount;
const passiveAttr = starterData.passiveAttr; const passiveAttr = starterData.passiveAttr;
if (passiveAttr & PassiveAttr.UNLOCKED) { if (passiveAttr & PassiveAttr.UNLOCKED) { // this is for enabling and disabling the passive
if (!(passiveAttr & PassiveAttr.ENABLED)) { if (!(passiveAttr & PassiveAttr.ENABLED)) {
options.push({ options.push({
label: i18next.t("starterSelectUiHandler:enablePassive"), 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 = []; const options = [];
if (!(passiveAttr & PassiveAttr.UNLOCKED)) { if (!(passiveAttr & PassiveAttr.UNLOCKED)) {
const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]);
options.push({ options.push({
label: `x${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")} (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`, label: `x${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")} (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`,
handler: () => { handler: () => {
if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= passiveCost) { 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); this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, undefined);
// Update the candy upgrade display // Update the candy upgrade display
if (this.isUpgradeIconEnabled() ) { if (this.isUpgradeIconEnabled()) {
this.setUpgradeIcon(this.cursor); this.setUpgradeIcon(pokemonCursor);
} }
if (this.isUpgradeAnimationEnabled()) { if (this.isUpgradeAnimationEnabled()) {
const genSpecies = this.genSpecies[this.lastSpecies.generation - 1]; const genSpecies = this.genSpecies[this.lastSpecies.generation - 1];
@ -1408,15 +1447,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
return this.scene.reset(true); return this.scene.reset(true);
} }
}); });
this.updateStarterValueLabel(this.cursor); this.updateStarterValueLabel(pokemonCursor);
this.tryUpdateValue(0); this.tryUpdateValue(0);
ui.setMode(Mode.STARTER_SELECT); ui.setMode(Mode.STARTER_SELECT);
this.scene.playSound("buy"); this.scene.playSound("buy");
// If the notification setting is set to 'On', update the candy upgrade display // If the notification setting is set to 'On', update the candy upgrade display
if (this.scene.candyUpgradeNotification === 2) { if (this.scene.candyUpgradeNotification === 2) {
if (this.isUpgradeIconEnabled() ) { if (this.isUpgradeIconEnabled()) {
this.setUpgradeIcon(this.cursor); this.setUpgradeIcon(pokemonCursor);
} }
if (this.isUpgradeAnimationEnabled()) { if (this.isUpgradeAnimationEnabled()) {
const genSpecies = this.genSpecies[this.lastSpecies.generation - 1]; const genSpecies = this.genSpecies[this.lastSpecies.generation - 1];
@ -1521,6 +1560,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.starterPreferences[this.lastSpecies.speciesId] = {}; this.starterPreferences[this.lastSpecies.speciesId] = {};
} }
switch (button) { switch (button) {
case Button.CYCLE_SHINY: case Button.CYCLE_SHINY:
if (this.canCycleShiny) { if (this.canCycleShiny) {
const newVariant = props.variant; const newVariant = props.variant;
@ -1624,50 +1664,118 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
success = true; success = true;
} }
break; break;
case Button.UP: case Button.UP:
if (row) { if (!this.starterIconsCursorObj.visible) {
success = this.setCursor(this.cursor - 9); if (row) {
} else { success = this.setCursor(this.cursor - 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 { } 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; break;
case Button.DOWN: case Button.DOWN:
if (row < rows - 2 || (row < rows - 1 && this.cursor % 9 <= (genStarters - 1) % 9)) { if (!this.starterIconsCursorObj.visible) {
success = this.setCursor(this.cursor + 9); if (row < rows - 2 || (row < rows - 1 && this.cursor % 9 <= (genStarters - 1) % 9)) {
} else { success = this.setCursor(this.cursor + 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 { } 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; break;
case Button.LEFT: case Button.LEFT:
if (this.cursor % 9) { if (!this.starterIconsCursorObj.visible) {
success = this.setCursor(this.cursor - 1); if (this.cursor % 9 !== 0) {
} else { success = this.setCursor(this.cursor - 1);
if (row >= Math.min(5, rows - 1)) { } else {
this.startCursorObj.setVisible(true); 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; break;
case Button.RIGHT: case Button.RIGHT:
if (this.cursor % 9 < (row < rows - 1 ? 8 : (genStarters - 1) % 9)) { if (!this.starterIconsCursorObj.visible) {
success = this.setCursor(this.cursor + 1); if (this.cursor % 9 < (row < rows - 1 ? 8 : (genStarters - 1) % 9)) {
} else { success = this.setCursor(this.cursor + 1);
if (row >= Math.min(5, rows - 1)) { } else {
this.startCursorObj.setVisible(true); 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; break;
} }
@ -1683,6 +1791,39 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
return success || error; 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) { switchMoveHandler(i: number, newMove: Moves, move: Moves) {
const speciesId = this.lastSpecies.speciesId; const speciesId = this.lastSpecies.speciesId;
const existingMoveIndex = this.starterMoveset.indexOf(newMove); const existingMoveIndex = this.starterMoveset.indexOf(newMove);
@ -1939,8 +2080,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
setGenMode(genMode: boolean): boolean { setGenMode(genMode: boolean): boolean {
this.genCursorObj.setVisible(genMode && !this.startCursorObj.visible); this.genCursorObj.setVisible(genMode && !(this.startCursorObj.visible || this.starterIconsCursorObj.visible));
this.cursorObj.setVisible(!genMode && !this.startCursorObj.visible); this.cursorObj.setVisible(!genMode && !(this.startCursorObj.visible || this.starterIconsCursorObj.visible));
if (genMode !== this.genMode) { if (genMode !== this.genMode) {
this.genMode = genMode; this.genMode = genMode;
@ -1949,6 +2090,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (genMode) { if (genMode) {
this.setSpecies(null); this.setSpecies(null);
} }
if (this.starterIconsCursorObj.visible) {
this.setSpecies(this.genSpecies[this.starterGens[this.starterIconsCursorIndex]][this.starterCursors[this.starterIconsCursorIndex]]);
}
return true; return true;
} }
@ -1956,7 +2100,17 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
return false; 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) { setSpecies(species: PokemonSpecies) {
this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; 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 dexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(this.lastSpecies, false, true);
const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, dexAttr); 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); 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.checkIconId(lastSpeciesIcon, this.lastSpecies, props.female, props.formIndex, props.shiny, props.variant);
this.iconAnimHandler.addOrUpdate(lastSpeciesIcon, PokemonIconAnimMode.NONE); this.iconAnimHandler.addOrUpdate(lastSpeciesIcon, PokemonIconAnimMode.NONE);
@ -2342,16 +2495,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonSprite.setVisible(!this.statsMode); this.pokemonSprite.setVisible(!this.statsMode);
}); });
if (!this.starterIconsCursorObj.visible) {
const isValidForChallenge = new Utils.BooleanHolder(true); (this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite)
const currentPartyValue = this.starterGens.reduce((total: number, gen: number, i: number) => total += this.scene.gameData.getSpeciesStarterValue(this.genSpecies[gen][this.starterCursors[i]].speciesId), 0); .setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant));
const cursorCost = this.scene.gameData.getSpeciesStarterValue(species.speciesId); this.checkIconId((this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite), species, female, formIndex, shiny, variant);
const isValidNextPartyValue = (currentPartyValue + cursorCost) <= this.getValueLimit(); } else {
Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), !!this.starterGens.length); (this.starterSelectGenIconContainers[this.starterGens[this.starterIconsCursorIndex]].getAt(this.starterCursors[this.starterIconsCursorIndex]) as Phaser.GameObjects.Sprite)
const starterSprite = this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAt(this.cursor) as Phaser.GameObjects.Sprite; .setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant));
starterSprite.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);
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);
this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY); this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY);
this.canCycleGender = !!(dexEntry.caughtAttr & DexAttr.MALE && dexEntry.caughtAttr & DexAttr.FEMALE); 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; 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 { popStarter(index: number): void {
this.starterGens.pop(); this.starterGens.splice(index, 1);
this.starterCursors.pop(); this.starterCursors.splice(index, 1);
this.starterAttr.pop(); this.starterAttr.splice(index, 1);
this.starterAbilityIndexes.pop(); this.starterAbilityIndexes.splice(index, 1);
this.starterNatures.pop(); this.starterNatures.splice(index, 1);
this.starterMovesets.pop(); 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.starterCursorObjs[this.starterCursors.length].setVisible(false);
this.starterIcons[this.starterCursors.length].setTexture("pokemon_icons_0"); this.starterIcons[this.starterCursors.length].setTexture("pokemon_icons_0");
this.starterIcons[this.starterCursors.length].setFrame("unknown"); 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(); this.tryUpdateValue();
} }
@ -2528,7 +2706,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.starterValueLabels[cursor].setShadowColor(this.getTextColor(textStyle, true)); 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 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 newValue = value + (add || 0);
const valueLimit = this.getValueLimit(); const valueLimit = this.getValueLimit();
@ -2545,6 +2723,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
return false; 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. * 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. * 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. * 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. * 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. * 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); 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; 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); speciesSprite.setAlpha(1);
if (speciesStarterDexEntry?.caughtAttr) { if (speciesStarterDexEntry?.caughtAttr) {
this.canAddParty = true; this.canAddParty = true;
} }
} else { } else {
/** /**
* If it can't be chosen, the user can't select. * If it can't be chosen, the user can't select.
* so that the alpha value of pokemon sprite set 0.375. * so that the alpha value of pokemon sprite set 0.375.
*/ */
speciesSprite.setAlpha(0.375); speciesSprite.setAlpha(0.375);
} }
} }
@ -2600,39 +2802,60 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const cancel = () => { const cancel = () => {
ui.setMode(Mode.STARTER_SELECT); ui.setMode(Mode.STARTER_SELECT);
if (!manualTrigger) { if (!manualTrigger) {
this.popStarter(); this.popStarter(this.starterGens.length - 1);
} }
this.clearText(); this.clearText();
}; };
ui.showText(i18next.t("starterSelectUiHandler:confirmStartTeam"), null, () => { const canStart = this.isPartyValid();
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);
});
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; 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 { toggleStatsMode(on?: boolean): void {
if (on === undefined) { if (on === undefined) {
on = !this.statsMode; on = !this.statsMode;
@ -2689,7 +2912,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.blockInput = false; this.blockInput = false;
while (this.starterCursors.length) { while (this.starterCursors.length) {
this.popStarter(); this.popStarter(this.starterCursors.length - 1);
} }
if (this.statsMode) { if (this.statsMode) {