Skip to content

Commit

Permalink
[UI/UX] In-Game Pokedex (#5083)
Browse files Browse the repository at this point in the history
* Working ui, missing logic, logs

* Filtering starters by name is working

* Filtering moves and abilities correctly

* Opening starter page on button.action

* Removed ugly leftover from title

* Added container for text with different colors and titles

* Showing all species in pokedex with no decorations and shinies

* Filtering includes extra forms; moving cursor from filterText to starters does not reset scrollIndex; toggle button for decorations

* Can access evolution page

* Abilities are colored properly (still missing info overlay)

* Biome filter; displays for baseStats, biomes and evolutions

* Removed lockable select ui handler, replaced by changes to standard ui handler.

* Evolutions are selectable from list and displayed properly

* Keeps shiny variant, gender and form when switching to evolutions; show ability descriptions; properly displaying sprites for megas and other forms

* Listing prevolutions and base forms

* Fixed filtering of baby forms with no biome assigned; Caught filter is ALL by default

* Highlighting text filters, resetting all filters when starting up

* No error messag when cursor on uncaught species, showing sprite again after toggling stats

* Simplified Pokemon Scan logic, accepts separate words as input

* Dynamically resizing ability box, showing ability description on first hover. Removed debug logs.

* Removed some more debug messages.

* Filter bar can adjust cursorOffset and x padding

* Fixed some type definitions

* Fixed more warnings; added localization strings in the pokedex scan overlay.

* Fixed fatal bug due to using Object.keys

* Removed debug messages

* Added try catch construct to prevent error that was breaking reloadHelper tests

* Added filter for starters / evolutions

* Biome filter option for uncatchable mons

* C and V buttons snap cursor to filters

* Changing background to make instructions visible

* Can buy candy upgrades through pokedex

* Displaying base stats as bars in an overlay

* Including baby forms among uncatchable mons

* Including evolutions when filtering by biome

* Working logic for select ui handler with skips and scroll

* -Pokedex page showing biomes from prevolutions; displaying correct biomes for forms of Rotom, Burmy and Lycanroc

* Fixed bug in base stats overlay

* Regional forms display name of region in evolutions and prevolutions

* Better messages for evolution conditions

* Showing proper descriptions for menu

* Adding sound effects to menu, and pokemon cry when opening page

* Changing menu colors to textstyle options supporting a legacy version.

* Fix to getStarterSpeciesId to work with all-unlocks files

* Passing a TextStyle to option select ui handler to allow for shadowed text

* Fixed bug of overlapping labels in text filters

* Fixed bug with supportHover and skipped indices in option select ui handler

* Localization of pokemon number label

* Fix to pokemon number localization

* Fix to pokemon number localization

* Adding some comments, removing useless elements

* More cleanup

* Removed candy upgrade instructions from evolved pokemon; attempting to buy candies from evolution now gives error sound instead of crashing the game

* Attempting to exit from filter text is now allowed if current option is empty

* UI changes to make dex pages work in legacy style

* Pokemon name shown while in alt form is no more capitalized

* Handling uncaught pokemon

* Showing types on Pokémon page

* Introducing globalScene everywhere

* Showing evolution requirements in message box

* Displaying form changing items; now using pokemonFormChanges to only show reachable forms

* Playing correct cry

* Pokemon cry in setSpeciesDetails

* Left and right buttons to turn previous or next pokedex page

* Cleaned up "last" from this.species; turning pages now preserves memories of unlocks

* Pokerus cursor is now treated as decoration

* Correctly displaying prevolutions for Pikachu and Gholdengo

* Uncaught forms can be cycled through (with black sprite and no options available)

* Filtering by moves now shows icons to distinguish egg and tm moves

* Added icons for passive abilities

* Added icons to legacy mode; fixed bug that caused game to hang when switching to or from legacy mode

* Pokedex entries are accessible through party screen

* Adding sort criteria for consistency with starter select screen

* Added options to cost reduction filter for consistency with starter select screen

* Updating optionSelectUiHandler to simplify logic and fix bug of autocomplete showing options incorrectly

* Adding Pokedéx option in starter select screen

* Prevolutions are shown properly again; battle forms are considered caught as long as the base form is caught

* Small fixes to evolution and form change descriptions

* Reworked evolutions menu to incorporate condition descriptions

* Moving evolution condition description logic entirely to the SpeciesEvolution class

* Removed extra Miraidon and Koraidon forms

* Properly showing evolution text for Dunsparce and Maushold

* Displaying uncaught forms for Dudunsparce and Maushold properly

* Displaying correct forms for Urshifu and Toxicitry after evolution

* Cleared up comments

* Updating test for tandemaus evolution

* Localized labels for egg moves and abilities

* Added button to show back sprites

* Back to showing only caught battleforms; added dexForDevs option

* Merging shiny and variant buttons

* Uncaught battle forms options are shown in dark text, like evolutions

* Showing proper gender for mons that can only be (or have only caught in) one gender

* Apply suggestions from code review

Co-authored-by: NightKev <[email protected]>

* Removed unused options from base-stats-overlay

* Fixed import of BaseStatsOverlay

* Displaying form-specific TMs properly; adjusting for passives rework

* Removed logging messages

* resetting containers to prevent memory leaks

* Updating integer to number in pokedex

* Implemented suggestion

* Removed some stray comments

* Fixed logic for cursor coming down from filter bar

* Transition from filters to dex box now works in a visually pleasing way

---------

Co-authored-by: Lugiad <[email protected]>
Co-authored-by: NightKev <[email protected]>
Co-authored-by: damocleas <[email protected]>
  • Loading branch information
4 people authored Feb 8, 2025
1 parent 6e0c8db commit 66c70b0
Show file tree
Hide file tree
Showing 31 changed files with 5,513 additions and 110 deletions.
Binary file added public/images/ui/legacy/mystery_egg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ui/legacy/normal_memory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ui/legacy/pokedex_summary_bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ui/mystery_egg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ui/normal_memory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/ui/pokedex_summary_bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/battle-scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export default class BattleScene extends SceneBase {
public reroll: boolean = false;
public shopCursorTarget: number = ShopCursorTarget.REWARDS;
public commandCursorMemory: boolean = false;
public dexForDevs: boolean = false;
public showMovesetFlyout: boolean = true;
public showArenaFlyout: boolean = true;
public showTimeOfDayWidget: boolean = true;
Expand Down
23 changes: 21 additions & 2 deletions src/data/balance/pokemon-evolutions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class SpeciesFormEvolution {
public item: EvolutionItem | null;
public condition: SpeciesEvolutionCondition | null;
public wildDelay: SpeciesWildEvolutionDelay;
public description: string = "";

constructor(speciesId: Species, preFormKey: string | null, evoFormKey: string | null, level: number, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay) {
this.speciesId = speciesId;
Expand All @@ -101,6 +102,23 @@ export class SpeciesFormEvolution {
this.item = item || EvolutionItem.NONE;
this.condition = condition;
this.wildDelay = wildDelay ?? SpeciesWildEvolutionDelay.NONE;

const strings: string[] = [];
if (this.level > 1) {
strings.push(i18next.t("pokemonEvolutions:level") + ` ${this.level}`);
}
if (this.item) {
const itemDescription = i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.item].toUpperCase()}`);
const rarity = this.item > 50 ? i18next.t("pokemonEvolutions:ULTRA") : i18next.t("pokemonEvolutions:GREAT");
strings.push(i18next.t("pokemonEvolutions:using") + itemDescription + ` (${rarity})`);
}
if (this.condition) {
strings.push(this.condition.description);
}
this.description = strings
.filter(str => str !== "")
.map((str, index) => index > 0 ? str[0].toLowerCase() + str.slice(1) : str)
.join(i18next.t("pokemonEvolutions:connector"));
}
}

Expand Down Expand Up @@ -237,6 +255,7 @@ class WeatherEvolutionCondition extends SpeciesEvolutionCondition {
constructor(weatherTypes: WeatherType[]) {
super(() => weatherTypes.indexOf(globalScene.arena.weather?.weatherType || WeatherType.NONE) > -1);
this.weatherTypes = weatherTypes;
this.description = i18next.t("pokemonEvolutions:weather");
}
}

Expand Down Expand Up @@ -1377,7 +1396,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
],
[Species.TANDEMAUS]: [
new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new TandemausEvolutionCondition()),
new SpeciesEvolution(Species.MAUSHOLD, 25, null, null)
new SpeciesFormEvolution(Species.MAUSHOLD, "", "four", 25, null, null)
],
[Species.FIDOUGH]: [
new SpeciesEvolution(Species.DACHSBUN, 26, null, null)
Expand Down Expand Up @@ -1540,7 +1559,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
],
[Species.DUNSPARCE]: [
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new DunsparceEvolutionCondition(), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new MoveEvolutionCondition(Moves.HYPER_DRILL), SpeciesWildEvolutionDelay.LONG)
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "two-segment", 32, null, new MoveEvolutionCondition(Moves.HYPER_DRILL), SpeciesWildEvolutionDelay.LONG)
],
[Species.GLIGAR]: [
new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new TimeOfDayEvolutionCondition("night") /* Razor fang at night*/, SpeciesWildEvolutionDelay.VERY_LONG)
Expand Down
3 changes: 2 additions & 1 deletion src/data/pokemon-forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
this.move = move;
this.known = known;
const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string;
this.description = i18next.t("pokemonEvolutions:Forms.moveLearned", { move: i18next.t(`move:${moveKey}.name`) });
this.description = known ? i18next.t("pokemonEvolutions:Forms.moveLearned", { move: i18next.t(`move:${moveKey}.name`) }) :
i18next.t("pokemonEvolutions:Forms.moveForgotten", { move: i18next.t(`move:${moveKey}.name`) });
}

canChange(pokemon: Pokemon): boolean {
Expand Down
18 changes: 9 additions & 9 deletions src/data/pokemon-species.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ export abstract class PokemonSpeciesForm {
return ret;
}

getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number): string {
const spriteId = this.getSpriteId(female, formIndex, shiny, variant).replace(/\_{2}/g, "/");
getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number, back?: boolean): string {
const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/\_{2}/g, "/");
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
}

Expand All @@ -346,8 +346,8 @@ export abstract class PokemonSpeciesForm {
return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant] === 2 ? `_${variant + 1}` : ""}`;
}

getSpriteKey(female: boolean, formIndex?: number, shiny?: boolean, variant?: number): string {
return `pkmn__${this.getSpriteId(female, formIndex, shiny, variant)}`;
getSpriteKey(female: boolean, formIndex?: number, shiny?: boolean, variant?: number, back?: boolean): string {
return `pkmn__${this.getSpriteId(female, formIndex, shiny, variant, back)}`;
}

abstract getFormSpriteKey(formIndex?: number): string;
Expand Down Expand Up @@ -520,10 +520,10 @@ export abstract class PokemonSpeciesForm {
return true;
}

loadAssets(female: boolean, formIndex?: number, shiny?: boolean, variant?: Variant, startLoad?: boolean): Promise<void> {
loadAssets(female: boolean, formIndex?: number, shiny?: boolean, variant?: Variant, startLoad?: boolean, back?: boolean): Promise<void> {
return new Promise(resolve => {
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant);
globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant));
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back);
globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back));
globalScene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`);
globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => {
const originalWarn = console.warn;
Expand All @@ -533,15 +533,15 @@ export abstract class PokemonSpeciesForm {
console.warn = originalWarn;
if (!(globalScene.anims.exists(spriteKey))) {
globalScene.anims.create({
key: this.getSpriteKey(female, formIndex, shiny, variant),
key: this.getSpriteKey(female, formIndex, shiny, variant, back),
frames: frameNames,
frameRate: 10,
repeat: -1
});
} else {
globalScene.anims.get(spriteKey).frameRate = 10;
}
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant, back).replace("variant/", "").replace(/_[1-3]$/, "");
globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve());
});
if (startLoad) {
Expand Down
6 changes: 5 additions & 1 deletion src/loading-scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SceneBase } from "#app/scene-base";
import { WindowVariant, getWindowVariantSuffix } from "#app/ui/ui-theme";
import { isMobile } from "#app/touch-controls";
import * as Utils from "#app/utils";
import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions";
import { initBiomes } from "#app/data/balance/biomes";
import { initEggMoves } from "#app/data/balance/egg-moves";
import { initPokemonForms } from "#app/data/pokemon-forms";
Expand Down Expand Up @@ -103,6 +103,8 @@ export class LoadingScene extends SceneBase {
this.loadImage("icon_tera", "ui");
this.loadImage("type_tera", "ui");
this.loadAtlas("type_bgs", "ui");
this.loadImage("mystery_egg", "ui");
this.loadImage("normal_memory", "ui");

this.loadImage("dawn_icon_fg", "ui");
this.loadImage("dawn_icon_mg", "ui");
Expand Down Expand Up @@ -154,6 +156,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("scroll_bar_handle", "ui");
this.loadImage("starter_container_bg", "ui");
this.loadImage("starter_select_bg", "ui");
this.loadImage("pokedex_summary_bg", "ui");
this.loadImage("select_cursor", "ui");
this.loadImage("select_cursor_highlight", "ui");
this.loadImage("select_cursor_highlight_thick", "ui");
Expand Down Expand Up @@ -354,6 +357,7 @@ export class LoadingScene extends SceneBase {
initVouchers();
initStatsKeys();
initPokemonPrevolutions();
initPokemonStarters();
initBiomes();
initEggMoves();
initPokemonForms();
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export async function initI18n(): Promise<void> {
"egg",
"fightUiHandler",
"filterBar",
"filterText",
"gameMode",
"gameStatsUiHandler",
"growth",
Expand All @@ -203,6 +204,7 @@ export async function initI18n(): Promise<void> {
"move",
"nature",
"pokeball",
"pokedexUiHandler",
"pokemon",
"pokemonEvolutions",
"pokemonForm",
Expand Down
15 changes: 15 additions & 0 deletions src/system/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { EaseType } from "#enums/ease-type";
import { MoneyFormat } from "#enums/money-format";
import { PlayerGender } from "#enums/player-gender";
import { ShopCursorTarget } from "#enums/shop-cursor-target";
import { isLocal } from "#app/utils";

const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) => i ? {
value: (i * 10).toString(),
Expand Down Expand Up @@ -150,6 +151,7 @@ export const SettingKeys = {
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
Shop_Cursor_Target: "SHOP_CURSOR_TARGET",
Command_Cursor_Memory: "COMMAND_CURSOR_MEMORY",
Dex_For_Devs: "DEX_FOR_DEVS",
Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION",
Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY",
Move_Info: "MOVE_INFO",
Expand Down Expand Up @@ -691,6 +693,16 @@ export const Setting: Array<Setting> = [
}
];

if (isLocal) {
Setting.push({
key: SettingKeys.Dex_For_Devs,
label: i18next.t("settings:dexForDevs"),
options: OFF_ON,
default: 0,
type: SettingType.GENERAL
});
}

/**
* Return the index of a Setting
* @param key SettingKey
Expand Down Expand Up @@ -828,6 +840,9 @@ export function setSetting(setting: string, value: number): boolean {
case SettingKeys.Command_Cursor_Memory:
globalScene.commandCursorMemory = Setting[index].options[value].value === "On";
break;
case SettingKeys.Dex_For_Devs:
globalScene.dexForDevs = Setting[index].options[value].value === "On";
break;
case SettingKeys.EXP_Gains_Speed:
globalScene.expGainsSpeed = value;
break;
Expand Down
2 changes: 1 addition & 1 deletion src/test/evolution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ describe("Evolution", () => {
for (let f = 1; f < 4; f++) {
vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds
const fourForm = playerPokemon.getEvolution()!;
expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null
expect(fourForm.evoFormKey).toBe("four"); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is "four"
}
});
});
1 change: 1 addition & 0 deletions src/tutorial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export enum Tutorial {
Access_Menu = "ACCESS_MENU",
Menu = "MENU",
Starter_Select = "STARTER_SELECT",
Pokedex = "POKEDEX",
Pokerus = "POKERUS",
Stat_Change = "STAT_CHANGE",
Select_Item = "SELECT_ITEM",
Expand Down
7 changes: 5 additions & 2 deletions src/ui-inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { globalScene } from "#app/global-scene";
import SettingsDisplayUiHandler from "./ui/settings/settings-display-ui-handler";
import SettingsAudioUiHandler from "./ui/settings/settings-audio-ui-handler";
import RunInfoUiHandler from "./ui/run-info-ui-handler";
import PokedexUiHandler from "./ui/pokedex-ui-handler";
import PokedexPageUiHandler from "./ui/pokedex-page-ui-handler";

type ActionKeys = Record<Button, () => void>;

Expand Down Expand Up @@ -140,7 +142,7 @@ export class UiInputs {
}

buttonGoToFilter(button: Button): void {
const whitelist = [ StarterSelectUiHandler ];
const whitelist = [ StarterSelectUiHandler, PokedexUiHandler, PokedexPageUiHandler ];
const uiHandler = globalScene.ui?.getHandler();
if (whitelist.some(handler => uiHandler instanceof handler)) {
globalScene.ui.processInput(button);
Expand Down Expand Up @@ -178,6 +180,7 @@ export class UiInputs {
globalScene.ui.setOverlayMode(Mode.MENU);
break;
case Mode.STARTER_SELECT:
case Mode.POKEDEX_PAGE:
this.buttonTouch();
break;
case Mode.MENU:
Expand All @@ -190,7 +193,7 @@ export class UiInputs {
}

buttonCycleOption(button: Button): void {
const whitelist = [ StarterSelectUiHandler, SettingsUiHandler, RunInfoUiHandler, SettingsDisplayUiHandler, SettingsAudioUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler ];
const whitelist = [ StarterSelectUiHandler, PokedexUiHandler, PokedexPageUiHandler, SettingsUiHandler, RunInfoUiHandler, SettingsDisplayUiHandler, SettingsAudioUiHandler, SettingsGamepadUiHandler, SettingsKeyboardUiHandler ];
const uiHandler = globalScene.ui?.getHandler();
if (whitelist.some(handler => uiHandler instanceof handler)) {
globalScene.ui.processInput(button);
Expand Down
Loading

0 comments on commit 66c70b0

Please sign in to comment.