Introducing tray to display form icons in the pokedex; displaying correct information for uncaught and seen forms in pokedex page; dexForDevs now unlocks everything in the main page

This commit is contained in:
Wlowscha 2025-02-08 22:14:03 +01:00
parent e6340de046
commit d1ef1f34fb
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
3 changed files with 379 additions and 163 deletions

View File

@ -1,7 +1,16 @@
import type { Variant } from "#app/data/variant";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { isNullOrUndefined } from "#app/utils";
import type PokemonSpecies from "../data/pokemon-species"; import type PokemonSpecies from "../data/pokemon-species";
import { addTextObject, TextStyle } from "./text"; import { addTextObject, TextStyle } from "./text";
interface SpeciesDetails {
shiny?: boolean,
formIndex?: number
female?: boolean,
variant?: Variant
}
export class PokedexMonContainer extends Phaser.GameObjects.Container { export class PokedexMonContainer extends Phaser.GameObjects.Container {
public species: PokemonSpecies; public species: PokemonSpecies;
public icon: Phaser.GameObjects.Sprite; public icon: Phaser.GameObjects.Sprite;
@ -21,14 +30,29 @@ export class PokedexMonContainer extends Phaser.GameObjects.Container {
public passive2Icon: Phaser.GameObjects.Image; public passive2Icon: Phaser.GameObjects.Image;
public cost: number = 0; public cost: number = 0;
constructor(species: PokemonSpecies) { constructor(species: PokemonSpecies, options: SpeciesDetails = {}) {
super(globalScene, 0, 0); super(globalScene, 0, 0);
this.species = species; this.species = species;
const { shiny, formIndex, female, variant } = options;
const defaultDexAttr = globalScene.gameData.getSpeciesDefaultDexAttr(species, false, true); const defaultDexAttr = globalScene.gameData.getSpeciesDefaultDexAttr(species, false, true);
const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
if (!isNullOrUndefined(formIndex)) {
defaultProps.formIndex = formIndex;
}
if (!isNullOrUndefined(shiny)) {
defaultProps.shiny = shiny;
}
if (!isNullOrUndefined(variant)) {
defaultProps.variant = variant;
}
if (!isNullOrUndefined(female)) {
defaultProps.female = female;
}
// starter passive bg // starter passive bg
const starterPassiveBg = globalScene.add.image(2, 5, "passive_bg"); const starterPassiveBg = globalScene.add.image(2, 5, "passive_bg");
starterPassiveBg.setOrigin(0, 0); starterPassiveBg.setOrigin(0, 0);

View File

@ -43,7 +43,6 @@ import type { 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 "#enums/egg-source-types"; import { EggSourceType } from "#enums/egg-source-types";
import { StarterContainer } from "#app/ui/starter-container";
import { getPassiveCandyCount, getValueReductionCandyCounts, getSameSpeciesEggCandyCounts } from "#app/data/balance/starters"; import { getPassiveCandyCount, getValueReductionCandyCounts, getSameSpeciesEggCandyCounts } from "#app/data/balance/starters";
import { BooleanHolder, capitalizeString, getLocalizedSpriteKey, isNullOrUndefined, NumberHolder, padInt, rgbHexToRgba, toReadableString } from "#app/utils"; import { BooleanHolder, capitalizeString, getLocalizedSpriteKey, isNullOrUndefined, NumberHolder, padInt, rgbHexToRgba, toReadableString } from "#app/utils";
import type { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
@ -127,8 +126,7 @@ interface SpeciesDetails {
shiny?: boolean, shiny?: boolean,
formIndex?: number formIndex?: number
female?: boolean, female?: boolean,
variant?: number, variant?: number
forSeen?: boolean, // default = false
} }
enum MenuOptions { enum MenuOptions {
@ -147,8 +145,6 @@ enum MenuOptions {
export default class PokedexPageUiHandler extends MessageUiHandler { export default class PokedexPageUiHandler extends MessageUiHandler {
private starterSelectContainer: Phaser.GameObjects.Container; private starterSelectContainer: Phaser.GameObjects.Container;
private shinyOverlay: Phaser.GameObjects.Image; private shinyOverlay: Phaser.GameObjects.Image;
private starterContainers: StarterContainer[] = [];
private filteredStarterContainers: StarterContainer[] = [];
private pokemonNumberText: Phaser.GameObjects.Text; private pokemonNumberText: Phaser.GameObjects.Text;
private pokemonSprite: Phaser.GameObjects.Sprite; private pokemonSprite: Phaser.GameObjects.Sprite;
private pokemonNameText: Phaser.GameObjects.Text; private pokemonNameText: Phaser.GameObjects.Text;
@ -312,10 +308,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.speciesLoaded.set(species.speciesId, false); this.speciesLoaded.set(species.speciesId, false);
this.allSpecies.push(species); this.allSpecies.push(species);
const starterContainer = new StarterContainer(species).setVisible(false);
this.starterContainers.push(starterContainer);
starterBoxContainer.add(starterContainer);
} }
this.starterSelectContainer.add(starterBoxContainer); this.starterSelectContainer.add(starterBoxContainer);
@ -799,39 +791,43 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
const hasShiny = caughtAttr & DexAttr.SHINY; const hasShiny = caughtAttr & DexAttr.SHINY;
const hasNonShiny = caughtAttr & DexAttr.NON_SHINY; const hasNonShiny = caughtAttr & DexAttr.NON_SHINY;
if (starterAttributes.shiny && !hasShiny) { if (!hasShiny || (starterAttributes.shiny === undefined && hasNonShiny)) {
// shiny form wasn't unlocked, purging shiny and variant setting // shiny form wasn't unlocked, purging shiny and variant setting
starterAttributes.shiny = false; starterAttributes.shiny = false;
starterAttributes.variant = 0; starterAttributes.variant = 0;
} else if (starterAttributes.shiny === false && !hasNonShiny) { } else if (!hasNonShiny || (starterAttributes.shiny === undefined && hasShiny)) {
// non shiny form wasn't unlocked, purging shiny setting starterAttributes.shiny = true;
starterAttributes.shiny = false; starterAttributes.variant = 0;
} }
if (starterAttributes.variant !== undefined) { const unlockedVariants = [
const unlockedVariants = [ hasShiny && caughtAttr & DexAttr.DEFAULT_VARIANT,
hasShiny && caughtAttr & DexAttr.DEFAULT_VARIANT, hasShiny && caughtAttr & DexAttr.VARIANT_2,
hasShiny && caughtAttr & DexAttr.VARIANT_2, hasShiny && caughtAttr & DexAttr.VARIANT_3
hasShiny && caughtAttr & DexAttr.VARIANT_3 ];
]; if (starterAttributes.variant === undefined || isNaN(starterAttributes.variant) || starterAttributes.variant < 0) {
if (isNaN(starterAttributes.variant) || starterAttributes.variant < 0) { starterAttributes.variant = 0;
starterAttributes.variant = 0; } else if (!unlockedVariants[starterAttributes.variant]) {
} else if (!unlockedVariants[starterAttributes.variant]) { let highestValidIndex = -1;
let highestValidIndex = -1; for (let i = 0; i <= starterAttributes.variant && i < unlockedVariants.length; i++) {
for (let i = 0; i <= starterAttributes.variant && i < unlockedVariants.length; i++) { if (unlockedVariants[i] !== 0n) {
if (unlockedVariants[i] !== 0n) { highestValidIndex = i;
highestValidIndex = i;
}
} }
// Set to the highest valid index found or default to 0
starterAttributes.variant = highestValidIndex !== -1 ? highestValidIndex : 0;
} }
// Set to the highest valid index found or default to 0
starterAttributes.variant = highestValidIndex !== -1 ? highestValidIndex : 0;
} }
if (starterAttributes.female !== undefined) { if (starterAttributes.female !== undefined) {
if ((starterAttributes.female && !(caughtAttr & DexAttr.FEMALE)) || (!starterAttributes.female && !(caughtAttr & DexAttr.MALE))) { if ((starterAttributes.female && !(caughtAttr & DexAttr.FEMALE)) || (!starterAttributes.female && !(caughtAttr & DexAttr.MALE))) {
starterAttributes.female = !starterAttributes.female; starterAttributes.female = !starterAttributes.female;
} }
} else {
if (caughtAttr & DexAttr.FEMALE) {
starterAttributes.female = true;
} else if (caughtAttr & DexAttr.MALE) {
starterAttributes.female = false;
}
} }
return starterAttributes; return starterAttributes;
@ -1967,88 +1963,10 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
if (species && (this.speciesStarterDexEntry?.seenAttr || this.isCaught())) { if (species && (this.speciesStarterDexEntry?.seenAttr || this.isCaught())) {
this.pokemonNumberText.setText(padInt(species.speciesId, 4)); this.pokemonNumberText.setText(padInt(species.speciesId, 4));
if (starterAttributes?.nickname) {
const name = decodeURIComponent(escape(atob(starterAttributes.nickname)));
this.pokemonNameText.setText(name);
} else {
this.pokemonNameText.setText(species.name);
}
if (this.isCaught()) { if (this.isCaught()) {
const colorScheme = starterColors[species.speciesId];
const luck = globalScene.gameData.getDexAttrLuck(this.isCaught());
this.pokemonLuckText.setVisible(!!luck);
this.pokemonLuckText.setText(luck.toString());
this.pokemonLuckText.setTint(getVariantTint(Math.min(luck - 1, 2) as Variant));
this.pokemonLuckLabelText.setVisible(this.pokemonLuckText.visible);
//Growth translate
let growthReadable = toReadableString(GrowthRate[species.growthRate]);
const growthAux = growthReadable.replace(" ", "_");
if (i18next.exists("growth:" + growthAux)) {
growthReadable = i18next.t("growth:" + growthAux as any);
}
this.pokemonGrowthRateText.setText(growthReadable);
this.pokemonGrowthRateText.setColor(getGrowthRateColor(species.growthRate));
this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true));
this.pokemonGrowthRateLabelText.setVisible(true);
this.pokemonUncaughtText.setVisible(false);
this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry?.caughtCount}`);
if (species.speciesId === Species.MANAPHY || species.speciesId === Species.PHIONE) {
this.pokemonHatchedIcon.setFrame("manaphy");
} else {
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
}
this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry?.hatchedCount}`);
const defaultDexAttr = this.getCurrentDexProps(species.speciesId); const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
const variant = defaultProps.variant;
const tint = getVariantTint(variant);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
this.pokemonShinyIcon.setTint(tint);
this.pokemonShinyIcon.setVisible(defaultProps.shiny);
this.pokemonCaughtHatchedContainer.setVisible(true);
this.pokemonFormText.setVisible(true);
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
this.pokemonCaughtHatchedContainer.setY(16);
this.pokemonShinyIcon.setY(135);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
[
this.pokemonCandyContainer,
this.pokemonHatchedIcon,
this.pokemonHatchedCountText
].map(c => c.setVisible(false));
this.pokemonFormText.setY(25);
} else {
this.pokemonCaughtHatchedContainer.setY(25);
this.pokemonShinyIcon.setY(117);
this.pokemonCandyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0])));
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1])));
this.pokemonCandyCountText.setText(`x${globalScene.gameData.starterData[this.getStarterSpeciesId(species.speciesId)].candyCount}`);
this.pokemonCandyContainer.setVisible(true);
this.pokemonFormText.setY(42);
this.pokemonHatchedIcon.setVisible(true);
this.pokemonHatchedCountText.setVisible(true);
const { currentFriendship, friendshipCap } = this.getFriendship(this.species.speciesId);
const candyCropY = 16 - (16 * (currentFriendship / friendshipCap));
this.pokemonCandyDarknessOverlay.setCrop(0, 0, 16, candyCropY);
this.pokemonCandyContainer.on("pointerover", () => {
globalScene.ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true);
this.activeTooltip = "CANDY";
});
this.pokemonCandyContainer.on("pointerout", () => {
globalScene.ui.hideTooltip();
this.activeTooltip = undefined;
});
}
// Set default attributes if for some reason starterAttributes does not exist or attributes missing // Set default attributes if for some reason starterAttributes does not exist or attributes missing
const props: StarterAttributes = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); const props: StarterAttributes = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
if (starterAttributes?.variant && !isNaN(starterAttributes.variant)) { if (starterAttributes?.variant && !isNaN(starterAttributes.variant)) {
@ -2065,12 +1983,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
female: props.female, female: props.female,
variant: props.variant ?? 0, variant: props.variant ?? 0,
}); });
if (this.isFormCaught(this.species, props.form)) {
const speciesForm = getPokemonSpeciesForm(species.speciesId, props.form ?? 0);
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
this.pokemonSprite.clearTint();
}
} else { } else {
this.pokemonGrowthRateText.setText(""); this.pokemonGrowthRateText.setText("");
this.pokemonGrowthRateLabelText.setVisible(false); this.pokemonGrowthRateLabelText.setVisible(false);
@ -2091,8 +2003,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
shiny: props.shiny, shiny: props.shiny,
formIndex: props.formIndex, formIndex: props.formIndex,
female: props.female, female: props.female,
variant: props.variant, variant: props.variant
forSeen: true
}); });
this.pokemonSprite.setTint(0x808080); this.pokemonSprite.setTint(0x808080);
} }
@ -2123,7 +2034,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}, forceUpdate?: boolean): void { setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}, forceUpdate?: boolean): void {
let { shiny, formIndex, female, variant } = options; let { shiny, formIndex, female, variant } = options;
const forSeen: boolean = options.forSeen ?? false;
const oldProps = species ? this.starterAttributes : null; const oldProps = species ? this.starterAttributes : null;
// We will only update the sprite if there is a change to form, shiny/variant // We will only update the sprite if there is a change to form, shiny/variant
@ -2194,12 +2104,12 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
} }
const isFormCaught = this.isFormCaught(); const isFormCaught = this.isFormCaught();
const isFormSeen = dexEntry ? (dexEntry.seenAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n : false;
this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default?
this.pokemonNumberText.setColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, false)); this.pokemonNumberText.setColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, false));
this.pokemonNumberText.setShadowColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, true)); this.pokemonNumberText.setShadowColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, true));
const assetLoadCancelled = new BooleanHolder(false); const assetLoadCancelled = new BooleanHolder(false);
this.assetLoadCancelled = assetLoadCancelled; this.assetLoadCancelled = assetLoadCancelled;
@ -2221,13 +2131,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.pokemonSprite.setVisible(!this.statsMode); this.pokemonSprite.setVisible(!this.statsMode);
} }
const currentFilteredContainer = this.filteredStarterContainers.find(p => p.species.speciesId === species.speciesId);
if (currentFilteredContainer) {
const starterSprite = currentFilteredContainer.icon as Phaser.GameObjects.Sprite;
starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female!, formIndex, shiny, variant));
currentFilteredContainer.checkIconId(female, formIndex, shiny, variant);
}
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY); const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
const isShinyCaught = !!(caughtAttr & DexAttr.SHINY); const isShinyCaught = !!(caughtAttr & DexAttr.SHINY);
@ -2250,24 +2153,127 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.pokemonGenderText.setText(""); this.pokemonGenderText.setText("");
} }
if (caughtAttr) { // Setting tint of the sprite
if (isFormCaught) { if (isFormCaught) {
this.species.loadAssets(female!, formIndex, shiny, variant as Variant, true).then(() => { this.species.loadAssets(female!, formIndex, shiny, variant as Variant, true).then(() => {
const crier = (this.species.forms && this.species.forms.length > 0) ? this.species.forms[formIndex ?? this.formIndex] : this.species; const crier = (this.species.forms && this.species.forms.length > 0) ? this.species.forms[formIndex ?? this.formIndex] : this.species;
crier.cry(); crier.cry();
}); });
this.pokemonSprite.clearTint();
this.pokemonSprite.clearTint(); } else if (isFormSeen) {
} else { this.pokemonSprite.setTint(0x808080);
this.pokemonSprite.setTint(0x000000); } else {
} this.pokemonSprite.setTint(0);
} }
if (caughtAttr || forSeen) { // Setting luck text and sparks
if (isFormCaught) {
const luck = globalScene.gameData.getDexAttrLuck(this.isCaught());
this.pokemonLuckText.setVisible(!!luck);
this.pokemonLuckText.setText(luck.toString());
this.pokemonLuckText.setTint(getVariantTint(Math.min(luck - 1, 2) as Variant));
this.pokemonLuckLabelText.setVisible(this.pokemonLuckText.visible);
} else {
this.pokemonLuckText.setVisible(false);
this.pokemonLuckLabelText.setVisible(false);
}
// Setting growth rate text
if (isFormCaught) {
let growthReadable = toReadableString(GrowthRate[species.growthRate]);
const growthAux = growthReadable.replace(" ", "_");
if (i18next.exists("growth:" + growthAux)) {
growthReadable = i18next.t("growth:" + growthAux as any);
}
this.pokemonGrowthRateText.setText(growthReadable);
this.pokemonGrowthRateText.setColor(getGrowthRateColor(species.growthRate));
this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true));
this.pokemonGrowthRateLabelText.setVisible(true);
} else {
this.pokemonGrowthRateText.setText("");
this.pokemonGrowthRateLabelText.setVisible(false);
}
// Caught and hatched
if (isFormCaught) {
const colorScheme = starterColors[species.speciesId];
this.pokemonUncaughtText.setVisible(false);
this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry?.caughtCount}`);
if (species.speciesId === Species.MANAPHY || species.speciesId === Species.PHIONE) {
this.pokemonHatchedIcon.setFrame("manaphy");
} else {
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
}
this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry?.hatchedCount}`);
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
const variant = defaultProps.variant;
const tint = getVariantTint(variant);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
this.pokemonShinyIcon.setTint(tint);
this.pokemonShinyIcon.setVisible(defaultProps.shiny);
this.pokemonCaughtHatchedContainer.setVisible(true);
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
this.pokemonCaughtHatchedContainer.setY(16);
this.pokemonShinyIcon.setY(135);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
[
this.pokemonCandyContainer,
this.pokemonHatchedIcon,
this.pokemonHatchedCountText
].map(c => c.setVisible(false));
this.pokemonFormText.setY(25);
} else {
this.pokemonCaughtHatchedContainer.setY(25);
this.pokemonShinyIcon.setY(117);
this.pokemonCandyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0])));
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1])));
this.pokemonCandyCountText.setText(`x${globalScene.gameData.starterData[this.getStarterSpeciesId(species.speciesId)].candyCount}`);
this.pokemonCandyContainer.setVisible(true);
this.pokemonFormText.setY(42);
this.pokemonHatchedIcon.setVisible(true);
this.pokemonHatchedCountText.setVisible(true);
const { currentFriendship, friendshipCap } = this.getFriendship(this.species.speciesId);
const candyCropY = 16 - (16 * (currentFriendship / friendshipCap));
this.pokemonCandyDarknessOverlay.setCrop(0, 0, 16, candyCropY);
this.pokemonCandyContainer.on("pointerover", () => {
globalScene.ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true);
this.activeTooltip = "CANDY";
});
this.pokemonCandyContainer.on("pointerout", () => {
globalScene.ui.hideTooltip();
this.activeTooltip = undefined;
});
}
} else {
this.pokemonUncaughtText.setVisible(true);
this.pokemonCaughtHatchedContainer.setVisible(false);
this.pokemonCandyContainer.setVisible(false);
this.pokemonShinyIcon.setVisible(false);
}
// Form text
if (isFormCaught || isFormSeen) {
this.pokemonFormText.setVisible(true);
if (isFormSeen) {
this.pokemonFormText.setY(18);
}
} else {
this.pokemonFormText.setVisible(false);
}
// Setting type icons
if (isFormCaught || isFormSeen) {
const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex!); // TODO: is the bang correct? const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex!); // TODO: is the bang correct?
this.setTypeIcons(speciesForm.type1, speciesForm.type2); this.setTypeIcons(speciesForm.type1, speciesForm.type2);
this.pokemonFormText.setText(this.getFormString((speciesForm as PokemonForm).formKey, species)); this.pokemonFormText.setText(this.getFormString((speciesForm as PokemonForm).formKey, species));
} else { } else {
this.setTypeIcons(null, null); this.setTypeIcons(null, null);
this.pokemonFormText.setText(""); this.pokemonFormText.setText("");

View File

@ -42,7 +42,6 @@ import { pokemonStarters } from "#app/data/balance/pokemon-evolutions";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
interface LanguageSetting { interface LanguageSetting {
starterInfoTextSize: string, starterInfoTextSize: string,
instructionTextSize: string, instructionTextSize: string,
@ -138,8 +137,7 @@ interface SpeciesDetails {
female?: boolean, female?: boolean,
variant?: Variant, variant?: Variant,
abilityIndex?: number, abilityIndex?: number,
natureIndex?: number, natureIndex?: number
forSeen?: boolean, // default = false
} }
export default class PokedexUiHandler extends MessageUiHandler { export default class PokedexUiHandler extends MessageUiHandler {
@ -206,6 +204,17 @@ export default class PokedexUiHandler extends MessageUiHandler {
private toggleDecorationsIconElement: Phaser.GameObjects.Sprite; private toggleDecorationsIconElement: Phaser.GameObjects.Sprite;
private toggleDecorationsLabel: Phaser.GameObjects.Text; private toggleDecorationsLabel: Phaser.GameObjects.Text;
private formTrayContainer: Phaser.GameObjects.Container;
private trayBg: Phaser.GameObjects.NineSlice;
private trayForms: PokemonForm[];
private trayContainers: PokedexMonContainer[] = [];
private trayNumIcons: number;
private trayRows: number;
private trayColumns: number;
private trayCursorObj: Phaser.GameObjects.Image;
private trayCursor: number = 0;
private showTray: boolean = false;
constructor() { constructor() {
super(Mode.POKEDEX); super(Mode.POKEDEX);
} }
@ -425,7 +434,6 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.cursorObj = globalScene.add.image(0, 0, "select_cursor"); this.cursorObj = globalScene.add.image(0, 0, "select_cursor");
this.cursorObj.setOrigin(0, 0); this.cursorObj.setOrigin(0, 0);
starterBoxContainer.add(this.cursorObj); starterBoxContainer.add(this.cursorObj);
for (const species of allSpecies) { for (const species of allSpecies) {
@ -438,6 +446,20 @@ export default class PokedexUiHandler extends MessageUiHandler {
starterBoxContainer.add(pokemonContainer); starterBoxContainer.add(pokemonContainer);
} }
// Tray to display forms
this.formTrayContainer = globalScene.add.container(0, 0);
this.trayBg = addWindow(0, 0, 0, 0);
this.trayBg.setOrigin(0, 0);
this.formTrayContainer.add(this.trayBg);
this.trayCursorObj = globalScene.add.image(0, 0, "select_cursor");
this.trayCursorObj.setOrigin(0, 0);
this.formTrayContainer.add(this.trayCursorObj);
starterBoxContainer.add(this.formTrayContainer);
starterBoxContainer.bringToTop(this.formTrayContainer);
this.formTrayContainer.setVisible(false);
this.starterSelectContainer.add(starterBoxContainer); this.starterSelectContainer.add(starterBoxContainer);
this.pokemonSprite = globalScene.add.sprite(96, 143, "pkmn__sub"); this.pokemonSprite = globalScene.add.sprite(96, 143, "pkmn__sub");
@ -449,7 +471,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.type1Icon.setOrigin(0, 0); this.type1Icon.setOrigin(0, 0);
this.starterSelectContainer.add(this.type1Icon); this.starterSelectContainer.add(this.type1Icon);
this.type2Icon = globalScene.add.sprite(10, 166, getLocalizedSpriteKey("types")); this.type2Icon = globalScene.add.sprite(28, 158, getLocalizedSpriteKey("types"));
this.type2Icon.setScale(0.5); this.type2Icon.setScale(0.5);
this.type2Icon.setOrigin(0, 0); this.type2Icon.setOrigin(0, 0);
this.starterSelectContainer.add(this.type2Icon); this.starterSelectContainer.add(this.type2Icon);
@ -527,7 +549,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.starterPreferences[species.speciesId] = this.initStarterPrefs(species); this.starterPreferences[species.speciesId] = this.initStarterPrefs(species);
if (dexEntry.caughtAttr) { if (dexEntry.caughtAttr || globalScene.dexForDevs) {
icon.clearTint(); icon.clearTint();
} else if (dexEntry.seenAttr) { } else if (dexEntry.seenAttr) {
icon.setTint(0x808080); icon.setTint(0x808080);
@ -860,32 +882,42 @@ export default class PokedexUiHandler extends MessageUiHandler {
} else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) { } else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) {
this.filterText.resetSelection(this.filterTextCursor); this.filterText.resetSelection(this.filterTextCursor);
success = true; success = true;
} else if (this.showTray) {
success = this.closeFormTray();
} else { } else {
this.tryExit(); this.tryExit();
success = true; success = true;
} }
} else if (button === Button.STATS) { } else if (button === Button.STATS) {
if (!this.filterMode) { if (!this.filterMode && !this.showTray) {
this.cursorObj.setVisible(false); this.cursorObj.setVisible(false);
this.setSpecies(null); this.setSpecies(null);
this.filterText.cursorObj.setVisible(false); this.filterText.cursorObj.setVisible(false);
this.filterTextMode = false; this.filterTextMode = false;
this.filterBarCursor = 0; this.filterBarCursor = 0;
this.setFilterMode(true); this.setFilterMode(true);
} else {
error = true;
} }
} else if (button === Button.V) { } else if (button === Button.V) {
if (!this.filterTextMode) { if (!this.filterTextMode && !this.showTray) {
this.cursorObj.setVisible(false); this.cursorObj.setVisible(false);
this.setSpecies(null); this.setSpecies(null);
this.filterBar.cursorObj.setVisible(false); this.filterBar.cursorObj.setVisible(false);
this.filterMode = false; this.filterMode = false;
this.filterTextCursor = 0; this.filterTextCursor = 0;
this.setFilterTextMode(true); this.setFilterTextMode(true);
} else {
error = true;
} }
} else if (button === Button.CYCLE_SHINY) { } else if (button === Button.CYCLE_SHINY) {
this.showDecorations = !this.showDecorations; if (!this.showTray) {
this.updateScroll(); this.showDecorations = !this.showDecorations;
success = true; this.updateScroll();
success = true;
} else {
error = true;
}
} else if (this.filterMode) { } else if (this.filterMode) {
switch (button) { switch (button) {
case Button.LEFT: case Button.LEFT:
@ -982,8 +1014,55 @@ export default class PokedexUiHandler extends MessageUiHandler {
success = true; success = true;
break; break;
} }
} else if (this.showTray) {
if (button === Button.ACTION) {
const formIndex = this.trayForms[this.trayCursor].formIndex;
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, formIndex, { form: formIndex });
success = true;
} else {
const numberOfForms = this.trayContainers.length;
const numOfRows = Math.ceil(numberOfForms / maxColumns);
const currentRow = Math.floor(this.trayCursor / maxColumns);
switch (button) {
case Button.UP:
if (currentRow > 0) {
success = this.setTrayCursor(this.trayCursor - 9);
} else {
const targetCol = this.trayCursor;
if (numberOfForms % 9 > targetCol) {
success = this.setTrayCursor(numberOfForms - (numberOfForms) % 9 + targetCol);
} else {
success = this.setTrayCursor(Math.max(numberOfForms - (numberOfForms) % 9 + targetCol - 9, 0));
}
}
break;
case Button.DOWN:
if (currentRow < numOfRows - 1) {
success = this.setTrayCursor(this.trayCursor + 9);
} else {
success = this.setTrayCursor(this.trayCursor % 9);
}
break;
case Button.LEFT:
if (this.trayCursor % 9 !== 0) {
success = this.setTrayCursor(this.trayCursor - 1);
} else {
success = this.setTrayCursor(currentRow < numOfRows - 1 ? (currentRow + 1) * maxColumns - 1 : numberOfForms - 1);
}
break;
case Button.RIGHT:
if (this.trayCursor % 9 < (currentRow < numOfRows - 1 ? 8 : (numberOfForms - 1) % 9)) {
success = this.setTrayCursor(this.trayCursor + 1);
} else {
success = this.setTrayCursor(currentRow * 9);
}
break;
case Button.CYCLE_FORM:
success = this.closeFormTray();
break;
}
}
} else { } else {
if (button === Button.ACTION) { if (button === Button.ACTION) {
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, 0); ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, 0);
success = true; success = true;
@ -1042,6 +1121,12 @@ export default class PokedexUiHandler extends MessageUiHandler {
success = true; success = true;
} }
break; break;
case Button.CYCLE_FORM:
const species = this.filteredPokemonContainers[this.cursor].species;
if (species.forms && species.forms.length > 1) {
success = this.openFormTray(species);
}
break;
} }
} }
} }
@ -1068,6 +1153,9 @@ export default class PokedexUiHandler extends MessageUiHandler {
case SettingKeyboard.Button_Cycle_Variant: case SettingKeyboard.Button_Cycle_Variant:
iconPath = "V.png"; iconPath = "V.png";
break; break;
case SettingKeyboard.Button_Cycle_Form:
iconPath = "F.png";
break;
case SettingKeyboard.Button_Stats: case SettingKeyboard.Button_Stats:
iconPath = "C.png"; iconPath = "C.png";
break; break;
@ -1558,6 +1646,98 @@ export default class PokedexUiHandler extends MessageUiHandler {
return false; return false;
} }
openFormTray(species: PokemonSpecies): boolean {
this.trayForms = species.forms;
this.trayNumIcons = this.trayForms.length;
this.trayRows = Math.floor(this.trayNumIcons / 9) + (this.trayNumIcons % 9 === 0 ? 0 : 1);
this.trayColumns = Math.min(this.trayNumIcons, 9);
const maxColumns = 9;
const onScreenFirstIndex = this.scrollCursor * maxColumns;
const boxCursor = this.cursor - onScreenFirstIndex;
const boxCursorY = Math.floor(boxCursor / maxColumns);
const boxCursorX = boxCursor - boxCursorY * 9;
const spaceBelow = 9 - 1 - boxCursorY;
const spaceRight = 9 - boxCursorX;
const boxPos = calcStarterPosition(this.cursor, this.scrollCursor);
const goUp = this.trayRows <= spaceBelow - 1 ? 0 : 1;
const goLeft = this.trayColumns <= spaceRight ? 0 : 1;
this.trayBg.setSize(13 + this.trayColumns * 17, 8 + this.trayRows * 18);
this.formTrayContainer.setX(
(goLeft ? boxPos.x - 18 * (this.trayColumns - spaceRight) : boxPos.x) - 3
);
this.formTrayContainer.setY(
goUp ? boxPos.y - this.trayBg.height : boxPos.y + 17
);
const dexEntry = globalScene.gameData.dexData[species.speciesId];
const dexAttr = this.getCurrentDexProps(species.speciesId);
const props = this.getSanitizedProps(globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, dexAttr));
this.trayContainers = [];
this.trayForms.map((f, index) => {
const isFormCaught = dexEntry ? (dexEntry.caughtAttr & globalScene.gameData.getFormAttr(f.formIndex ?? 0)) > 0n : false;
const isFormSeen = dexEntry ? (dexEntry.seenAttr & globalScene.gameData.getFormAttr(f.formIndex ?? 0)) > 0n : false;
const formContainer = new PokedexMonContainer(species, { formIndex: f.formIndex, female: props.female, shiny: props.shiny, variant: props.variant });
this.iconAnimHandler.addOrUpdate(formContainer.icon, PokemonIconAnimMode.NONE);
// Setting tint, for all saves some caught forms may only show up as seen
if (isFormCaught || globalScene.dexForDevs) {
formContainer.icon.clearTint();
} else if (isFormSeen) {
formContainer.icon.setTint(0x808080);
}
formContainer.setPosition(5 + (index % 9) * 18, 4 + Math.floor(index / 9) * 17);
this.formTrayContainer.add(formContainer);
this.trayContainers.push(formContainer);
});
this.showTray = true;
this.setTrayCursor(0);
this.formTrayContainer.setVisible(true);
return true;
}
closeFormTray(): boolean {
this.trayContainers.forEach(obj => {
this.formTrayContainer.remove(obj, true); // Removes from container and destroys it
});
this.trayContainers = [];
this.formTrayContainer.setVisible(false);
this.showTray = false;
this.setSpeciesDetails(this.lastSpecies);
return true;
}
setTrayCursor(cursor: number): boolean {
if (!this.showTray) {
return false;
}
cursor = Math.max(Math.min(this.trayContainers.length - 1, cursor), 0);
const changed = this.trayCursor !== cursor;
if (changed) {
this.trayCursor = cursor;
}
this.trayCursorObj.setPosition(5 + (cursor % 9) * 18, 4 + Math.floor(cursor / 9) * 17);
const species = this.lastSpecies;
const formIndex = this.trayForms[cursor].formIndex;
this.setSpeciesDetails(species, { formIndex: formIndex });
return changed;
}
getFriendship(speciesId: number) { getFriendship(speciesId: number) {
let currentFriendship = globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)].friendship; let currentFriendship = globalScene.gameData.starterData[this.getStarterSpeciesId(speciesId)].friendship;
if (!currentFriendship || currentFriendship === undefined) { if (!currentFriendship || currentFriendship === undefined) {
@ -1592,13 +1772,13 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.lastSpecies = species!; // TODO: is this bang correct? this.lastSpecies = species!; // TODO: is this bang correct?
if (species && (this.speciesStarterDexEntry?.seenAttr || this.speciesStarterDexEntry?.caughtAttr)) { if (species && (this.speciesStarterDexEntry?.seenAttr || this.speciesStarterDexEntry?.caughtAttr || globalScene.dexForDevs)) {
this.pokemonNumberText.setText(i18next.t("pokedexUiHandler:pokemonNumber") + padInt(species.speciesId, 4)); this.pokemonNumberText.setText(i18next.t("pokedexUiHandler:pokemonNumber") + padInt(species.speciesId, 4));
this.pokemonNameText.setText(species.name); this.pokemonNameText.setText(species.name);
if (this.speciesStarterDexEntry?.caughtAttr) { if (this.speciesStarterDexEntry?.caughtAttr || globalScene.dexForDevs) {
// Pause the animation when the species is selected // Pause the animation when the species is selected
const speciesIndex = this.allSpecies.indexOf(species); const speciesIndex = this.allSpecies.indexOf(species);
@ -1627,9 +1807,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.type1Icon.setVisible(true); this.type1Icon.setVisible(true);
this.type2Icon.setVisible(true); this.type2Icon.setVisible(true);
this.setSpeciesDetails(species, { this.setSpeciesDetails(species);
forSeen: true
});
this.pokemonSprite.setTint(0x808080); this.pokemonSprite.setTint(0x808080);
} }
} else { } else {
@ -1646,7 +1824,6 @@ export default class PokedexUiHandler extends MessageUiHandler {
setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void { setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void {
let { shiny, formIndex, female, variant } = options; let { shiny, formIndex, female, variant } = options;
const forSeen: boolean = options.forSeen ?? false;
// We will only update the sprite if there is a change to form, shiny/variant // We will only update the sprite if there is a change to form, shiny/variant
// or gender for species with gender sprite differences // or gender for species with gender sprite differences
@ -1676,25 +1853,27 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (!dexEntry.caughtAttr) { if (!dexEntry.caughtAttr) {
const props = this.getSanitizedProps(globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId))); const props = this.getSanitizedProps(globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)));
if (shiny === undefined || shiny !== props.shiny) { if (shiny === undefined) {
shiny = props.shiny; shiny = props.shiny;
} }
if (formIndex === undefined || formIndex !== props.formIndex) { if (formIndex === undefined) {
formIndex = props.formIndex; formIndex = props.formIndex;
} }
if (female === undefined || female !== props.female) { if (female === undefined) {
female = props.female; female = props.female;
} }
if (variant === undefined || variant !== props.variant) { if (variant === undefined) {
variant = props.variant; variant = props.variant;
} }
} }
const isFormCaught = dexEntry ? (dexEntry.caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n : false;
const isFormSeen = dexEntry ? (dexEntry.seenAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n : false;
const assetLoadCancelled = new BooleanHolder(false); const assetLoadCancelled = new BooleanHolder(false);
this.assetLoadCancelled = assetLoadCancelled; this.assetLoadCancelled = assetLoadCancelled;
if (shouldUpdateSprite) { if (shouldUpdateSprite) {
species.loadAssets(female!, formIndex, shiny, variant, true).then(() => { // TODO: is this bang correct? species.loadAssets(female!, formIndex, shiny, variant, true).then(() => { // TODO: is this bang correct?
if (assetLoadCancelled.value) { if (assetLoadCancelled.value) {
return; return;
@ -1711,14 +1890,21 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.pokemonSprite.setVisible(!(this.filterMode || this.filterTextMode)); this.pokemonSprite.setVisible(!(this.filterMode || this.filterTextMode));
} }
if (dexEntry.caughtAttr || forSeen) { if (isFormCaught || globalScene.dexForDevs) {
this.pokemonSprite.clearTint();
} else if (isFormSeen) {
this.pokemonSprite.setTint(0x808080);
} else {
this.pokemonSprite.setTint(0);
}
if (isFormCaught || isFormSeen || globalScene.dexForDevs) {
const speciesForm = getPokemonSpeciesForm(species.speciesId, 0); // TODO: always selecting the first form const speciesForm = getPokemonSpeciesForm(species.speciesId, 0); // TODO: always selecting the first form
this.setTypeIcons(speciesForm.type1, speciesForm.type2); this.setTypeIcons(speciesForm.type1, speciesForm.type2);
} else { } else {
this.setTypeIcons(null, null); this.setTypeIcons(null, null);
} }
} else { } else {
this.setTypeIcons(null, null); this.setTypeIcons(null, null);
} }