Wlowscha 66c70b07a7
[UI/UX] In-Game Pokedex ()
* 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 <34855794+DayKev@users.noreply.github.com>

* 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 <2070109+Adri1@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: damocleas <damocleas25@gmail.com>
2025-02-08 11:48:06 -05:00

401 lines
14 KiB
TypeScript

import { EggTier } from "#enums/egg-type";
import { UiTheme } from "#enums/ui-theme";
import type Phaser from "phaser";
import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText";
import InputText from "phaser3-rex-plugins/plugins/inputtext";
import { globalScene } from "#app/global-scene";
import { ModifierTier } from "../modifier/modifier-tier";
import i18next from "#app/plugins/i18n";
export enum TextStyle {
MESSAGE,
WINDOW,
WINDOW_ALT,
BATTLE_INFO,
PARTY,
PARTY_RED,
SUMMARY,
SUMMARY_ALT,
SUMMARY_RED,
SUMMARY_BLUE,
SUMMARY_PINK,
SUMMARY_GOLD,
SUMMARY_GRAY,
SUMMARY_GREEN,
MONEY, // Money default styling (pale yellow)
MONEY_WINDOW, // Money displayed in Windows (needs different colors based on theme)
STATS_LABEL,
STATS_VALUE,
SETTINGS_VALUE,
SETTINGS_LABEL,
SETTINGS_SELECTED,
SETTINGS_LOCKED,
TOOLTIP_TITLE,
TOOLTIP_CONTENT,
MOVE_INFO_CONTENT,
MOVE_PP_FULL,
MOVE_PP_HALF_FULL,
MOVE_PP_NEAR_EMPTY,
MOVE_PP_EMPTY,
SMALLER_WINDOW_ALT,
BGM_BAR,
PERFECT_IV,
ME_OPTION_DEFAULT, // Default style for choices in ME
ME_OPTION_SPECIAL, // Style for choices with special requirements in ME
SHADOW_TEXT // To obscure unavailable options
}
export interface TextStyleOptions {
scale: number,
styleOptions: Phaser.Types.GameObjects.Text.TextStyle | InputText.IConfig,
shadowColor: string,
shadowXpos: number,
shadowYpos: number
}
export function addTextObject(x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): Phaser.GameObjects.Text {
const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, globalScene.uiTheme, extraStyleOptions);
const ret = globalScene.add.text(x, y, content, styleOptions);
ret.setScale(scale);
ret.setShadow(shadowXpos, shadowYpos, shadowColor);
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) {
ret.setLineSpacing(scale * 30);
}
if (ret.lineSpacing < 12 && i18next.resolvedLanguage === "ja") {
ret.setLineSpacing(ret.lineSpacing + 35);
}
return ret;
}
export function setTextStyle(obj: Phaser.GameObjects.Text, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle) {
const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, globalScene.uiTheme, extraStyleOptions);
obj.setScale(scale);
obj.setShadow(shadowXpos, shadowYpos, shadowColor);
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) {
obj.setLineSpacing(scale * 30);
}
if (obj.lineSpacing < 12 && i18next.resolvedLanguage === "ja") {
obj.setLineSpacing(obj.lineSpacing + 35);
}
}
export function addBBCodeTextObject(x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): BBCodeText {
const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, globalScene.uiTheme, extraStyleOptions);
const ret = new BBCodeText(globalScene, x, y, content, styleOptions as BBCodeText.TextStyle);
globalScene.add.existing(ret);
ret.setScale(scale);
ret.setShadow(shadowXpos, shadowYpos, shadowColor);
if (!(styleOptions as BBCodeText.TextStyle).lineSpacing) {
ret.setLineSpacing(scale * 60);
}
if (ret.lineSpacing < 12 && i18next.resolvedLanguage === "ja") {
ret.setLineSpacing(ret.lineSpacing + 35);
}
return ret;
}
export function addTextInputObject(x: number, y: number, width: number, height: number, style: TextStyle, extraStyleOptions?: InputText.IConfig): InputText {
const { scale, styleOptions } = getTextStyleOptions(style, globalScene.uiTheme, extraStyleOptions);
const ret = new InputText(globalScene, x, y, width, height, styleOptions as InputText.IConfig);
globalScene.add.existing(ret);
ret.setScale(scale);
return ret;
}
export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): TextStyleOptions {
const lang = i18next.resolvedLanguage;
let shadowXpos = 4;
let shadowYpos = 5;
let scale = 0.1666666667;
const defaultFontSize = 96;
let styleOptions: Phaser.Types.GameObjects.Text.TextStyle = {
fontFamily: "emerald",
fontSize: 96,
color: getTextColor(style, false, uiTheme),
padding: {
bottom: 6
}
};
if (i18next.resolvedLanguage === "ja") {
scale = 0.1388888889;
styleOptions.padding = { top:2, bottom:4 };
}
switch (style) {
case TextStyle.SUMMARY:
case TextStyle.SUMMARY_ALT:
case TextStyle.SUMMARY_BLUE:
case TextStyle.SUMMARY_RED:
case TextStyle.SUMMARY_PINK:
case TextStyle.SUMMARY_GOLD:
case TextStyle.SUMMARY_GRAY:
case TextStyle.SUMMARY_GREEN:
case TextStyle.WINDOW:
case TextStyle.WINDOW_ALT:
case TextStyle.ME_OPTION_DEFAULT:
case TextStyle.ME_OPTION_SPECIAL:
shadowXpos = 3;
shadowYpos = 3;
break;
case TextStyle.STATS_LABEL:
let fontSizeLabel = "96px";
switch (lang) {
case "de":
shadowXpos = 3;
shadowYpos = 3;
fontSizeLabel = "80px";
break;
default:
fontSizeLabel = "96px";
break;
}
styleOptions.fontSize = fontSizeLabel;
break;
case TextStyle.STATS_VALUE:
shadowXpos = 3;
shadowYpos = 3;
let fontSizeValue = "96px";
switch (lang) {
case "de":
fontSizeValue = "80px";
break;
default:
fontSizeValue = "96px";
break;
}
styleOptions.fontSize = fontSizeValue;
break;
case TextStyle.MESSAGE:
case TextStyle.SETTINGS_LABEL:
case TextStyle.SETTINGS_LOCKED:
case TextStyle.SETTINGS_SELECTED:
break;
case TextStyle.BATTLE_INFO:
case TextStyle.MONEY:
case TextStyle.MONEY_WINDOW:
case TextStyle.TOOLTIP_TITLE:
styleOptions.fontSize = defaultFontSize - 24;
shadowXpos = 3.5;
shadowYpos = 3.5;
break;
case TextStyle.PARTY:
case TextStyle.PARTY_RED:
styleOptions.fontSize = defaultFontSize - 30;
styleOptions.fontFamily = "pkmnems";
break;
case TextStyle.TOOLTIP_CONTENT:
styleOptions.fontSize = defaultFontSize - 32;
shadowXpos = 3;
shadowYpos = 3;
break;
case TextStyle.MOVE_INFO_CONTENT:
styleOptions.fontSize = defaultFontSize - 40;
shadowXpos = 3;
shadowYpos = 3;
break;
case TextStyle.SMALLER_WINDOW_ALT:
styleOptions.fontSize = defaultFontSize - 36;
shadowXpos = 3;
shadowYpos = 3;
break;
case TextStyle.BGM_BAR:
styleOptions.fontSize = defaultFontSize - 24;
shadowXpos = 3;
shadowYpos = 3;
break;
}
const shadowColor = getTextColor(style, true, uiTheme);
if (extraStyleOptions) {
if (extraStyleOptions.fontSize) {
const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize?.toString().slice(0, -2) ?? "1");
shadowXpos *= sizeRatio;
}
styleOptions = Object.assign(styleOptions, extraStyleOptions);
}
return { scale, styleOptions, shadowColor, shadowXpos, shadowYpos };
}
export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string {
return `[color=${getTextColor(textStyle, false, uiTheme)}][shadow=${getTextColor(textStyle, true, uiTheme)}]${content}`;
}
/**
* Should only be used with BBCodeText (see {@linkcode addBBCodeTextObject()})
* This does NOT work with UI showText() or showDialogue() methods.
* Method will do pattern match/replace and apply BBCode color/shadow styling to substrings within the content:
* @[<TextStyle>]{<text to color>}
*
* Example: passing a content string of "@[SUMMARY_BLUE]{blue text} primaryStyle text @[SUMMARY_RED]{red text}" will result in:
* - "blue text" with TextStyle.SUMMARY_BLUE applied
* - " primaryStyle text " with primaryStyle TextStyle applied
* - "red text" with TextStyle.SUMMARY_RED applied
* @param content string with styling that need to be applied for BBCodeTextObject
* @param primaryStyle Primary style is required in order to escape BBCode styling properly.
* @param uiTheme the {@linkcode UiTheme} to get TextStyle for
* @param forWindow set to `true` if the text is to be displayed in a window ({@linkcode BattleScene.addWindow})
* it will replace all instances of the default MONEY TextStyle by {@linkcode TextStyle.MONEY_WINDOW}
*/
export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme, forWindow?: boolean): string {
// Apply primary styling before anything else
let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]";
const primaryStyleString = [ ...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))! ][0];
/* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY}
* or it will look wrong in legacy mode because of the different window background color
* So, for text to be displayed in windows replace all "@[MONEY]" with "@[MONEY_WINDOW]" */
if (forWindow) {
text = text.replace(/@\[MONEY\]/g, (_substring: string) => "@[MONEY_WINDOW]");
}
// Set custom colors
text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => {
return "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle], uiTheme) + "[/color][/shadow]" + primaryStyleString;
});
// Remove extra style block at the end
return text.replace(/\[color=[^\[]*\]\[shadow=[^\[]*\]\[\/color\]\[\/shadow\]/gi, "");
}
export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: UiTheme = UiTheme.DEFAULT): string {
const isLegacyTheme = uiTheme === UiTheme.LEGACY;
switch (textStyle) {
case TextStyle.MESSAGE:
return !shadow ? "#f8f8f8" : "#6b5a73";
case TextStyle.WINDOW:
case TextStyle.MOVE_INFO_CONTENT:
case TextStyle.MOVE_PP_FULL:
case TextStyle.TOOLTIP_CONTENT:
case TextStyle.SETTINGS_VALUE:
if (isLegacyTheme) {
return !shadow ? "#484848" : "#d0d0c8";
}
return !shadow ? "#f8f8f8" : "#6b5a73";
case TextStyle.MOVE_PP_HALF_FULL:
if (isLegacyTheme) {
return !shadow ? "#a68e17" : "#ebd773";
}
return !shadow ? "#ccbe00" : "#6e672c";
case TextStyle.MOVE_PP_NEAR_EMPTY:
if (isLegacyTheme) {
return !shadow ? "#d64b00" : "#f7b18b";
}
return !shadow ? "#d64b00" : "#69402a";
case TextStyle.MOVE_PP_EMPTY:
if (isLegacyTheme) {
return !shadow ? "#e13d3d" : "#fca2a2";
}
return !shadow ? "#e13d3d" : "#632929";
case TextStyle.WINDOW_ALT:
return !shadow ? "#484848" : "#d0d0c8";
case TextStyle.BATTLE_INFO:
if (isLegacyTheme) {
return !shadow ? "#404040" : "#ded6b5";
}
return !shadow ? "#f8f8f8" : "#6b5a73";
case TextStyle.PARTY:
return !shadow ? "#f8f8f8" : "#707070";
case TextStyle.PARTY_RED:
return !shadow ? "#f89890" : "#984038";
case TextStyle.SUMMARY:
return !shadow ? "#f8f8f8" : "#636363";
case TextStyle.SUMMARY_ALT:
if (isLegacyTheme) {
return !shadow ? "#f8f8f8" : "#636363";
}
return !shadow ? "#484848" : "#d0d0c8";
case TextStyle.SUMMARY_RED:
case TextStyle.TOOLTIP_TITLE:
return !shadow ? "#e70808" : "#ffbd73";
case TextStyle.SUMMARY_BLUE:
return !shadow ? "#40c8f8" : "#006090";
case TextStyle.SUMMARY_PINK:
return !shadow ? "#f89890" : "#984038";
case TextStyle.SUMMARY_GOLD:
case TextStyle.MONEY:
return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold
case TextStyle.MONEY_WINDOW:
if (isLegacyTheme) {
return !shadow ? "#f8b050" : "#c07800"; // Gold
}
return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold
case TextStyle.SETTINGS_LOCKED:
case TextStyle.SUMMARY_GRAY:
return !shadow ? "#a0a0a0" : "#636363";
case TextStyle.STATS_LABEL:
return !shadow ? "#f8b050" : "#c07800";
case TextStyle.STATS_VALUE:
if (isLegacyTheme) {
return !shadow ? "#484848" : "#d0d0c8";
}
return !shadow ? "#f8f8f8" : "#6b5a73";
case TextStyle.SUMMARY_GREEN:
return !shadow ? "#78c850" : "#306850";
case TextStyle.SETTINGS_LABEL:
case TextStyle.PERFECT_IV:
return !shadow ? "#f8b050" : "#c07800";
case TextStyle.SETTINGS_SELECTED:
return !shadow ? "#f88880" : "#f83018";
case TextStyle.SMALLER_WINDOW_ALT:
return !shadow ? "#484848" : "#d0d0c8";
case TextStyle.BGM_BAR:
return !shadow ? "#f8f8f8" : "#6b5a73";
case TextStyle.ME_OPTION_DEFAULT:
return !shadow ? "#f8f8f8" : "#6b5a73"; // White
case TextStyle.ME_OPTION_SPECIAL:
if (isLegacyTheme) {
return !shadow ? "#f8b050" : "#c07800"; // Gold
}
return !shadow ? "#78c850" : "#306850"; // Green
// Leaving the logic in place, in case someone wants to pick an even darker hue for the shadow down the line
case TextStyle.SHADOW_TEXT:
if (isLegacyTheme) {
return !shadow ? "#d0d0c8" : "#d0d0c8";
}
return !shadow ? "#6b5a73" : "#6b5a73";
}
}
export function getModifierTierTextTint(tier: ModifierTier): number {
switch (tier) {
case ModifierTier.COMMON:
return 0xf8f8f8;
case ModifierTier.GREAT:
return 0x4998f8;
case ModifierTier.ULTRA:
return 0xf8d038;
case ModifierTier.ROGUE:
return 0xdb4343;
case ModifierTier.MASTER:
return 0xe331c5;
case ModifierTier.LUXURY:
return 0xe74c18;
}
}
export function getEggTierTextTint(tier: EggTier): number {
switch (tier) {
case EggTier.COMMON:
return getModifierTierTextTint(ModifierTier.COMMON);
case EggTier.RARE:
return getModifierTierTextTint(ModifierTier.GREAT);
case EggTier.EPIC:
return getModifierTierTextTint(ModifierTier.ULTRA);
case EggTier.LEGENDARY:
return getModifierTierTextTint(ModifierTier.MASTER);
}
}