mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-15 06:15:20 +01:00
[UI/UX] Add option to see ribbons in Pokédex (#6596)
* Added various ribbon utils * Added ribbon tray to pokédex page * V button in Pokédex toggles IVs * Introduced visibility toggle * Added ribbons (and full ivs) to unlocks file * For real this time * Added descriptions to the ribbons * Fixed bug of tray not opening with visibility option on * Minor cleanup of ribbon tray * Use unique ribbon icons * Make achv use image instead of sprite * Tweak size of ribbons * Improve clarity on comment --------- Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
This commit is contained in:
parent
07c1491649
commit
06fe3c7b76
@ -182,6 +182,7 @@ export class BattleScene extends SceneBase {
|
||||
public shopCursorTarget: number = ShopCursorTarget.REWARDS;
|
||||
public commandCursorMemory = false;
|
||||
public dexForDevs = false;
|
||||
public showMissingRibbons = false;
|
||||
public showMovesetFlyout = true;
|
||||
public showArenaFlyout = true;
|
||||
public showTimeOfDayWidget = true;
|
||||
|
||||
@ -103,7 +103,7 @@ export class RibbonData {
|
||||
//#endregion Ribbons
|
||||
|
||||
/** Create a new instance of RibbonData. Generally, {@linkcode fromJSON} is used instead. */
|
||||
constructor(value: number) {
|
||||
constructor(value: number | bigint) {
|
||||
this.payload = BigInt(value);
|
||||
}
|
||||
|
||||
@ -145,4 +145,12 @@ export class RibbonData {
|
||||
public has(flag: RibbonFlag): boolean {
|
||||
return !!(this.payload & flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow access to the bigint of ribbons
|
||||
* @returns The ribbons as a bigint
|
||||
*/
|
||||
public getRibbons(): bigint {
|
||||
return this.payload;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { pokemonPrevolutions } from "#balance/pokemon-evolutions";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import type { RibbonFlag } from "#system/ribbons/ribbon-data";
|
||||
import { RibbonData, type RibbonFlag } from "#system/ribbons/ribbon-data";
|
||||
|
||||
/**
|
||||
* Award one or more ribbons to a species and its pre-evolutions
|
||||
@ -17,3 +17,43 @@ export function awardRibbonsToSpeciesLine(id: SpeciesId, ribbons: RibbonFlag): v
|
||||
dexData[prevoId].ribbons.award(ribbons);
|
||||
}
|
||||
}
|
||||
|
||||
export function ribbonFlagToAssetKey(flag: RibbonFlag): Phaser.GameObjects.Sprite | Phaser.GameObjects.Image {
|
||||
let imageKey: string;
|
||||
switch (flag) {
|
||||
// biome-ignore-start lint/suspicious/noFallthroughSwitchClause: intentional
|
||||
case RibbonData.MONO_GEN_1:
|
||||
imageKey = "ribbon_gen1";
|
||||
case RibbonData.MONO_GEN_2:
|
||||
imageKey ??= "ribbon_gen2";
|
||||
case RibbonData.MONO_GEN_3:
|
||||
imageKey ??= "ribbon_gen3";
|
||||
case RibbonData.MONO_GEN_4:
|
||||
imageKey ??= "ribbon_gen4";
|
||||
case RibbonData.MONO_GEN_5:
|
||||
imageKey ??= "ribbon_gen5";
|
||||
case RibbonData.MONO_GEN_6:
|
||||
imageKey ??= "ribbon_gen6";
|
||||
case RibbonData.MONO_GEN_7:
|
||||
imageKey ??= "ribbon_gen7";
|
||||
case RibbonData.MONO_GEN_8:
|
||||
imageKey ??= "ribbon_gen8";
|
||||
case RibbonData.MONO_GEN_9:
|
||||
imageKey ??= "ribbon_gen9";
|
||||
return globalScene.add.image(0, 0, "items", imageKey).setDisplaySize(16, 16);
|
||||
// biome-ignore-end lint/suspicious/noFallthroughSwitchClause: done with fallthrough
|
||||
// Ribbons that don't use the items atlas
|
||||
// biome-ignore-start lint/suspicious/noFallthroughSwitchClause: Another fallthrough block
|
||||
case RibbonData.NUZLOCKE:
|
||||
imageKey = "champion_ribbon_emerald";
|
||||
default:
|
||||
imageKey ??= "champion_ribbon";
|
||||
{
|
||||
const img = globalScene.add.image(0, 0, imageKey);
|
||||
const target = 12;
|
||||
const scale = Math.min(target / img.width, target / img.height);
|
||||
return img.setScale(scale);
|
||||
}
|
||||
// biome-ignore-end lint/suspicious/noFallthroughSwitchClause: End fallthrough block
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,6 +180,7 @@ export const SettingKeys = {
|
||||
Battle_Music: "BATTLE_MUSIC",
|
||||
Show_BGM_Bar: "SHOW_BGM_BAR",
|
||||
Hide_Username: "HIDE_USERNAME",
|
||||
Show_Missing_Ribbons: "SHOW_MISSING_RIBBONS",
|
||||
Move_Touch_Controls: "MOVE_TOUCH_CONTROLS",
|
||||
Shop_Overlay_Opacity: "SHOP_OVERLAY_OPACITY",
|
||||
};
|
||||
@ -642,6 +643,13 @@ export const Setting: Array<Setting> = [
|
||||
default: 0,
|
||||
type: SettingType.DISPLAY,
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Show_Missing_Ribbons,
|
||||
label: i18next.t("settings:showMissingRibbons"),
|
||||
options: OFF_ON,
|
||||
default: 0,
|
||||
type: SettingType.DISPLAY,
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Master_Volume,
|
||||
label: i18next.t("settings:masterVolume"),
|
||||
@ -874,6 +882,9 @@ export function setSetting(setting: string, value: number): boolean {
|
||||
case SettingKeys.Dex_For_Devs:
|
||||
globalScene.dexForDevs = Setting[index].options[value].value === "On";
|
||||
break;
|
||||
case SettingKeys.Show_Missing_Ribbons:
|
||||
globalScene.showMissingRibbons = Setting[index].options[value].value === "On";
|
||||
break;
|
||||
case SettingKeys.EXP_Gains_Speed:
|
||||
globalScene.expGainsSpeed = value;
|
||||
break;
|
||||
|
||||
@ -10,13 +10,13 @@ export class AchvBar extends Phaser.GameObjects.Container {
|
||||
private defaultHeight: number;
|
||||
|
||||
private bg: Phaser.GameObjects.NineSlice;
|
||||
private icon: Phaser.GameObjects.Sprite;
|
||||
private icon: Phaser.GameObjects.Image;
|
||||
private titleText: Phaser.GameObjects.Text;
|
||||
private scoreText: Phaser.GameObjects.Text;
|
||||
private descriptionText: Phaser.GameObjects.Text;
|
||||
|
||||
private queue: (Achv | Voucher)[] = [];
|
||||
private playerGender: PlayerGender;
|
||||
private readonly queue: (Achv | Voucher)[] = [];
|
||||
private readonly playerGender: PlayerGender;
|
||||
|
||||
public shown: boolean;
|
||||
|
||||
@ -29,47 +29,31 @@ export class AchvBar extends Phaser.GameObjects.Container {
|
||||
this.defaultWidth = 200;
|
||||
this.defaultHeight = 40;
|
||||
|
||||
this.bg = globalScene.add.nineslice(
|
||||
0,
|
||||
0,
|
||||
"achv_bar",
|
||||
undefined,
|
||||
this.defaultWidth,
|
||||
this.defaultHeight,
|
||||
41,
|
||||
6,
|
||||
16,
|
||||
4,
|
||||
);
|
||||
this.bg.setOrigin(0, 0);
|
||||
this.bg = globalScene.add
|
||||
.nineslice(0, 0, "achv_bar", undefined, this.defaultWidth, this.defaultHeight, 41, 6, 16, 4)
|
||||
.setOrigin(0);
|
||||
|
||||
this.add(this.bg);
|
||||
|
||||
this.icon = globalScene.add.sprite(4, 4, "items");
|
||||
this.icon.setOrigin(0, 0);
|
||||
this.icon = globalScene.add.image(4, 4, "items").setOrigin(0);
|
||||
this.add(this.icon);
|
||||
|
||||
this.titleText = addTextObject(40, 3, "", TextStyle.MESSAGE, {
|
||||
fontSize: "72px",
|
||||
});
|
||||
this.titleText.setOrigin(0, 0);
|
||||
}).setOrigin(0);
|
||||
this.add(this.titleText);
|
||||
|
||||
this.scoreText = addTextObject(150, 3, "", TextStyle.MESSAGE, {
|
||||
fontSize: "72px",
|
||||
});
|
||||
this.scoreText.setOrigin(1, 0);
|
||||
}).setOrigin(1, 0);
|
||||
this.add(this.scoreText);
|
||||
|
||||
this.descriptionText = addTextObject(43, 16, "", TextStyle.WINDOW_ALT, {
|
||||
fontSize: "72px",
|
||||
});
|
||||
this.descriptionText.setOrigin(0, 0);
|
||||
this.descriptionText.setOrigin(0).setWordWrapWidth(664).setLineSpacing(-5);
|
||||
this.add(this.descriptionText);
|
||||
|
||||
this.descriptionText.setWordWrapWidth(664);
|
||||
this.descriptionText.setLineSpacing(-5);
|
||||
|
||||
this.setScale(0.5);
|
||||
|
||||
this.shown = false;
|
||||
|
||||
165
src/ui/containers/ribbon-tray-container.ts
Normal file
165
src/ui/containers/ribbon-tray-container.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { PokemonSpecies } from "#data/pokemon-species";
|
||||
import { Button } from "#enums/buttons";
|
||||
import type { RibbonData, RibbonFlag } from "#system/ribbons/ribbon-data";
|
||||
import { ribbonFlagToAssetKey } from "#system/ribbons/ribbon-methods";
|
||||
import type { MessageUiHandler } from "#ui/message-ui-handler";
|
||||
import { addWindow } from "#ui/ui-theme";
|
||||
import { getAvailableRibbons, getRibbonKey } from "#utils/ribbon-utils";
|
||||
import { toCamelCase } from "#utils/strings";
|
||||
import i18next from "i18next";
|
||||
|
||||
export class RibbonTray extends Phaser.GameObjects.Container {
|
||||
private trayBg: Phaser.GameObjects.NineSlice;
|
||||
private ribbons: RibbonFlag[] = [];
|
||||
private trayIcons: Phaser.GameObjects.Image[] = [];
|
||||
private trayNumIcons: number;
|
||||
private trayRows: number;
|
||||
private trayColumns: number;
|
||||
private trayCursorObj: Phaser.GameObjects.Image;
|
||||
private trayCursor = 0;
|
||||
|
||||
private readonly handler: MessageUiHandler;
|
||||
|
||||
private ribbonData: RibbonData;
|
||||
|
||||
private readonly maxColumns = 6;
|
||||
|
||||
constructor(handler: MessageUiHandler, x: number, y: number) {
|
||||
super(globalScene, x, y);
|
||||
this.handler = handler;
|
||||
|
||||
this.setup();
|
||||
}
|
||||
|
||||
setup() {
|
||||
this.trayBg = addWindow(0, 0, 0, 0).setOrigin(0);
|
||||
this.add(this.trayBg);
|
||||
|
||||
this.trayCursorObj = globalScene.add.image(0, 0, "select_cursor").setOrigin(0);
|
||||
this.add(this.trayCursorObj);
|
||||
}
|
||||
|
||||
processInput(button: Button) {
|
||||
let success = false;
|
||||
|
||||
const numberOfIcons = this.trayIcons.length;
|
||||
const numOfRows = Math.ceil(numberOfIcons / this.maxColumns);
|
||||
const currentTrayRow = Math.floor(this.trayCursor / this.maxColumns);
|
||||
switch (button) {
|
||||
case Button.UP:
|
||||
if (currentTrayRow > 0) {
|
||||
success = this.setTrayCursor(this.trayCursor - this.maxColumns);
|
||||
} else {
|
||||
const targetCol = this.trayCursor;
|
||||
if (numberOfIcons % this.maxColumns > targetCol) {
|
||||
success = this.setTrayCursor(numberOfIcons - (numberOfIcons % this.maxColumns) + targetCol);
|
||||
} else {
|
||||
success = this.setTrayCursor(
|
||||
Math.max(numberOfIcons - (numberOfIcons % this.maxColumns) + targetCol - this.maxColumns, 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Button.DOWN:
|
||||
if (currentTrayRow < numOfRows - 1 && this.trayCursor + this.maxColumns < numberOfIcons) {
|
||||
success = this.setTrayCursor(this.trayCursor + this.maxColumns);
|
||||
} else {
|
||||
success = this.setTrayCursor(this.trayCursor % this.maxColumns);
|
||||
}
|
||||
break;
|
||||
case Button.LEFT:
|
||||
if (this.trayCursor % this.maxColumns !== 0) {
|
||||
success = this.setTrayCursor(this.trayCursor - 1);
|
||||
} else {
|
||||
success = this.setTrayCursor(
|
||||
currentTrayRow < numOfRows - 1 ? (currentTrayRow + 1) * this.maxColumns - 1 : numberOfIcons - 1,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Button.RIGHT:
|
||||
if (
|
||||
this.trayCursor % this.maxColumns
|
||||
< (currentTrayRow < numOfRows - 1 ? 8 : (numberOfIcons - 1) % this.maxColumns)
|
||||
) {
|
||||
success = this.setTrayCursor(this.trayCursor + 1);
|
||||
} else {
|
||||
success = this.setTrayCursor(currentTrayRow * this.maxColumns);
|
||||
}
|
||||
break;
|
||||
case Button.CANCEL:
|
||||
success = this.close();
|
||||
break;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
setTrayCursor(cursor: number): boolean {
|
||||
cursor = Phaser.Math.Clamp(this.trayIcons.length - 1, cursor, 0);
|
||||
const changed = this.trayCursor !== cursor;
|
||||
if (changed) {
|
||||
this.trayCursor = cursor;
|
||||
}
|
||||
|
||||
this.trayCursorObj.setPosition(5 + (cursor % this.maxColumns) * 18, 4 + Math.floor(cursor / this.maxColumns) * 17);
|
||||
|
||||
const ribbonDescription = i18next.t(`ribbons:${toCamelCase(getRibbonKey(this.ribbons[cursor]))}`);
|
||||
|
||||
this.handler.showText(ribbonDescription);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
open(species: PokemonSpecies): boolean {
|
||||
this.ribbons = getAvailableRibbons(species);
|
||||
|
||||
this.ribbonData = globalScene.gameData.dexData[species.speciesId].ribbons;
|
||||
|
||||
this.trayNumIcons = this.ribbons.length;
|
||||
this.trayRows =
|
||||
Math.floor(this.trayNumIcons / this.maxColumns) + (this.trayNumIcons % this.maxColumns === 0 ? 0 : 1);
|
||||
this.trayColumns = Math.min(this.trayNumIcons, this.maxColumns);
|
||||
|
||||
this.trayBg.setSize(15 + this.trayColumns * 17, 8 + this.trayRows * 18);
|
||||
|
||||
this.trayIcons = [];
|
||||
let index = 0;
|
||||
for (const ribbon of this.ribbons) {
|
||||
const hasRibbon = this.ribbonData.has(ribbon);
|
||||
|
||||
const icon = ribbonFlagToAssetKey(ribbon);
|
||||
|
||||
if (hasRibbon || globalScene.dexForDevs) {
|
||||
icon.clearTint();
|
||||
} else {
|
||||
icon.setTint(0);
|
||||
}
|
||||
|
||||
if (hasRibbon || globalScene.dexForDevs || globalScene.showMissingRibbons) {
|
||||
icon.setPosition(14 + (index % this.maxColumns) * 18, 14 + Math.floor(index / this.maxColumns) * 17);
|
||||
|
||||
this.add(icon);
|
||||
this.trayIcons.push(icon);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
this.setVisible(true).setTrayCursor(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
close(): boolean {
|
||||
this.trayIcons.forEach(obj => {
|
||||
this.remove(obj, true); // Removes from container and destroys it
|
||||
});
|
||||
|
||||
this.trayIcons = [];
|
||||
this.ribbons = [];
|
||||
this.setVisible(false);
|
||||
|
||||
// this.exitCallback();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -51,6 +51,7 @@ import { BaseStatsOverlay } from "#ui/base-stats-overlay";
|
||||
import { MessageUiHandler } from "#ui/message-ui-handler";
|
||||
import { MoveInfoOverlay } from "#ui/move-info-overlay";
|
||||
import { PokedexInfoOverlay } from "#ui/pokedex-info-overlay";
|
||||
import { RibbonTray } from "#ui/ribbon-tray-container";
|
||||
import { StatsContainer } from "#ui/stats-container";
|
||||
import { addBBCodeTextObject, addTextObject, getTextColor, getTextStyleOptions } from "#ui/text";
|
||||
import { addWindow } from "#ui/ui-theme";
|
||||
@ -168,7 +169,7 @@ enum MenuOptions {
|
||||
TM_MOVES,
|
||||
BIOMES,
|
||||
NATURES,
|
||||
TOGGLE_IVS,
|
||||
RIBBONS,
|
||||
EVOLUTIONS,
|
||||
}
|
||||
|
||||
@ -206,11 +207,11 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
private shinyIconElement: Phaser.GameObjects.Sprite;
|
||||
private formIconElement: Phaser.GameObjects.Sprite;
|
||||
private genderIconElement: Phaser.GameObjects.Sprite;
|
||||
private variantIconElement: Phaser.GameObjects.Sprite;
|
||||
private ivIconElement: Phaser.GameObjects.Sprite;
|
||||
private shinyLabel: Phaser.GameObjects.Text;
|
||||
private formLabel: Phaser.GameObjects.Text;
|
||||
private genderLabel: Phaser.GameObjects.Text;
|
||||
private variantLabel: Phaser.GameObjects.Text;
|
||||
private ivLabel: Phaser.GameObjects.Text;
|
||||
private candyUpgradeIconElement: Phaser.GameObjects.Sprite;
|
||||
private candyUpgradeLabel: Phaser.GameObjects.Text;
|
||||
private showBackSpriteIconElement: Phaser.GameObjects.Sprite;
|
||||
@ -288,6 +289,10 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
private canUseCandies: boolean;
|
||||
private exitCallback;
|
||||
|
||||
// Ribbons
|
||||
private ribbonContainer: RibbonTray;
|
||||
private isRibbonTrayOpen = false;
|
||||
|
||||
constructor() {
|
||||
super(UiMode.POKEDEX_PAGE);
|
||||
}
|
||||
@ -563,24 +568,24 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
);
|
||||
this.genderLabel.setName("text-gender-label");
|
||||
|
||||
this.variantIconElement = new Phaser.GameObjects.Sprite(
|
||||
this.ivIconElement = new Phaser.GameObjects.Sprite(
|
||||
globalScene,
|
||||
this.instructionRowX,
|
||||
this.instructionRowY,
|
||||
"keyboard",
|
||||
"V.png",
|
||||
);
|
||||
this.variantIconElement.setName("sprite-variant-icon-element");
|
||||
this.variantIconElement.setScale(0.675);
|
||||
this.variantIconElement.setOrigin(0.0, 0.0);
|
||||
this.variantLabel = addTextObject(
|
||||
this.ivIconElement.setName("sprite-variant-icon-element");
|
||||
this.ivIconElement.setScale(0.675);
|
||||
this.ivIconElement.setOrigin(0.0, 0.0);
|
||||
this.ivLabel = addTextObject(
|
||||
this.instructionRowX + this.instructionRowTextOffset,
|
||||
this.instructionRowY,
|
||||
i18next.t("pokedexUiHandler:cycleVariant"),
|
||||
i18next.t("pokedexUiHandler:toggleIVs"),
|
||||
TextStyle.INSTRUCTIONS_TEXT,
|
||||
{ fontSize: instructionTextSize },
|
||||
);
|
||||
this.variantLabel.setName("text-variant-label");
|
||||
this.ivLabel.setName("text-iv-label");
|
||||
|
||||
this.showBackSpriteIconElement = new Phaser.GameObjects.Sprite(globalScene, 50, 7, "keyboard", "E.png");
|
||||
this.showBackSpriteIconElement.setName("show-backSprite-icon-element");
|
||||
@ -656,7 +661,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
i18next.t("pokedexUiHandler:showTmMoves"),
|
||||
i18next.t("pokedexUiHandler:showBiomes"),
|
||||
i18next.t("pokedexUiHandler:showNatures"),
|
||||
i18next.t("pokedexUiHandler:toggleIVs"),
|
||||
i18next.t("pokedexUiHandler:showRibbons"),
|
||||
i18next.t("pokedexUiHandler:showEvolutions"),
|
||||
];
|
||||
|
||||
@ -698,6 +703,10 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
});
|
||||
this.starterSelectContainer.add(this.infoOverlay);
|
||||
|
||||
this.ribbonContainer = new RibbonTray(this, 192, 0);
|
||||
this.starterSelectContainer.add(this.ribbonContainer);
|
||||
this.ribbonContainer.setVisible(false);
|
||||
|
||||
// Filter bar sits above everything, except the message box
|
||||
this.starterSelectContainer.bringToTop(this.starterSelectMessageBoxContainer);
|
||||
|
||||
@ -763,8 +772,11 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
const label = i18next.t(`pokedexUiHandler:${toCamelCase(`menu${MenuOptions[o]}`)}`);
|
||||
const isDark =
|
||||
!isSeen
|
||||
|| (!isStarterCaught && (o === MenuOptions.TOGGLE_IVS || o === MenuOptions.NATURES))
|
||||
|| (this.tmMoves.length === 0 && o === MenuOptions.TM_MOVES);
|
||||
|| (!isStarterCaught && (o === MenuOptions.NATURES || o === MenuOptions.RIBBONS))
|
||||
|| (this.tmMoves.length === 0 && o === MenuOptions.TM_MOVES)
|
||||
|| (!globalScene.gameData.dexData[this.species.speciesId].ribbons.getRibbons()
|
||||
&& o === MenuOptions.RIBBONS
|
||||
&& !globalScene.showMissingRibbons);
|
||||
const color = getTextColor(isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE, false);
|
||||
const shadow = getTextColor(isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE, true);
|
||||
return `[shadow=${shadow}][color=${color}]${label}[/color][/shadow]`;
|
||||
@ -1152,6 +1164,17 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
const isSeen = this.isSeen();
|
||||
const isStarterCaught = !!this.isCaught(this.getStarterSpecies(this.species));
|
||||
|
||||
if (this.isRibbonTrayOpen) {
|
||||
if (button === Button.CANCEL) {
|
||||
this.isRibbonTrayOpen = false;
|
||||
this.ribbonContainer.close();
|
||||
this.setCursor(MenuOptions.RIBBONS);
|
||||
ui.playSelect();
|
||||
return success;
|
||||
}
|
||||
return this.ribbonContainer.processInput(button);
|
||||
}
|
||||
|
||||
if (this.blockInputOverlay) {
|
||||
if (button === Button.CANCEL || button === Button.ACTION) {
|
||||
this.blockInputOverlay = false;
|
||||
@ -1749,12 +1772,18 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
}
|
||||
break;
|
||||
|
||||
case MenuOptions.TOGGLE_IVS:
|
||||
case MenuOptions.RIBBONS:
|
||||
if (!isStarterCaught) {
|
||||
error = true;
|
||||
} else if (
|
||||
!globalScene.gameData.dexData[this.species.speciesId].ribbons.getRibbons()
|
||||
&& !globalScene.showMissingRibbons
|
||||
) {
|
||||
ui.showText(i18next.t("pokedexUiHandler:noRibbons"));
|
||||
error = true;
|
||||
} else {
|
||||
this.toggleStatsMode();
|
||||
ui.setMode(UiMode.POKEDEX_PAGE, "refresh");
|
||||
this.isRibbonTrayOpen = true;
|
||||
this.ribbonContainer.open(this.species);
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
@ -1903,6 +1932,15 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
case Button.CYCLE_TERA:
|
||||
if (isStarterCaught) {
|
||||
this.toggleStatsMode();
|
||||
ui.setMode(UiMode.POKEDEX_PAGE, "refresh");
|
||||
success = true;
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
break;
|
||||
case Button.STATS:
|
||||
if (!isCaught || !isFormCaught || !this.canUseCandies) {
|
||||
error = true;
|
||||
@ -2171,6 +2209,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
case SettingKeyboard.Button_Cycle_Ability:
|
||||
iconPath = "E.png";
|
||||
break;
|
||||
case SettingKeyboard.Button_Cycle_Tera:
|
||||
iconPath = "V.png";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2246,6 +2287,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
if (this.canCycleForm) {
|
||||
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Form, gamepadType, this.formIconElement, this.formLabel);
|
||||
}
|
||||
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Tera, gamepadType, this.ivIconElement, this.ivLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2826,8 +2868,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
||||
this.formLabel.setVisible(false);
|
||||
this.genderIconElement.setVisible(false);
|
||||
this.genderLabel.setVisible(false);
|
||||
this.variantIconElement.setVisible(false);
|
||||
this.variantLabel.setVisible(false);
|
||||
this.ivIconElement.setVisible(false);
|
||||
this.ivLabel.setVisible(false);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
|
||||
94
src/utils/ribbon-utils.ts
Normal file
94
src/utils/ribbon-utils.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { pokemonEvolutions } from "#balance/pokemon-evolutions";
|
||||
import type { PokemonSpecies } from "#data/pokemon-species";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { RibbonData, type RibbonFlag } from "#system/ribbons/ribbon-data";
|
||||
import { getPokemonSpecies } from "./pokemon-utils";
|
||||
|
||||
export function getRibbonForType(type: PokemonType): RibbonFlag {
|
||||
// Valid types: 0–17, excluding UNKNOWN (-1) and STELLAR (19)
|
||||
if (type < PokemonType.NORMAL || type > PokemonType.FAIRY) {
|
||||
return 0n;
|
||||
}
|
||||
return (1n << BigInt(type)) as RibbonFlag;
|
||||
}
|
||||
|
||||
export function getRibbonForGeneration(gen: number): RibbonFlag {
|
||||
// Valid generations: 1–9
|
||||
if (gen < 1 || gen > 9) {
|
||||
return 0n;
|
||||
}
|
||||
return (1n << BigInt(17 + gen)) as RibbonFlag;
|
||||
}
|
||||
|
||||
export function extractRibbons(data: bigint): RibbonFlag[] {
|
||||
const ribbons: RibbonFlag[] = [];
|
||||
let bit = 1n;
|
||||
|
||||
while (bit <= data) {
|
||||
if ((data & bit) !== 0n) {
|
||||
ribbons.push(bit as RibbonFlag);
|
||||
}
|
||||
bit <<= 1n; // move to the next bit
|
||||
}
|
||||
|
||||
return ribbons;
|
||||
}
|
||||
|
||||
export function getAvailableRibbons(species: PokemonSpecies): RibbonFlag[] {
|
||||
const ribbons: RibbonFlag[] = [
|
||||
RibbonData.CLASSIC,
|
||||
RibbonData.NUZLOCKE,
|
||||
RibbonData.FRIENDSHIP,
|
||||
RibbonData.FLIP_STATS,
|
||||
RibbonData.INVERSE,
|
||||
RibbonData.FRESH_START,
|
||||
RibbonData.HARDCORE,
|
||||
RibbonData.LIMITED_CATCH,
|
||||
RibbonData.NO_HEAL,
|
||||
RibbonData.NO_SHOP,
|
||||
RibbonData.NO_SUPPORT,
|
||||
];
|
||||
|
||||
let data = 0n;
|
||||
|
||||
const speciesToCheck = [species.speciesId];
|
||||
while (speciesToCheck.length > 0) {
|
||||
const checking = speciesToCheck.pop();
|
||||
if (checking == null) {
|
||||
continue;
|
||||
}
|
||||
const checkingSpecies = getPokemonSpecies(checking);
|
||||
|
||||
data |= getRibbonForGeneration(checkingSpecies.generation);
|
||||
data |= getRibbonForType(checkingSpecies.type1);
|
||||
if (checkingSpecies.type2 != null) {
|
||||
data |= getRibbonForType(checkingSpecies.type2);
|
||||
}
|
||||
|
||||
for (const form of species.forms) {
|
||||
data |= getRibbonForType(form.type1);
|
||||
if (form.type2 != null) {
|
||||
data |= getRibbonForType(form.type2);
|
||||
}
|
||||
}
|
||||
|
||||
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
|
||||
pokemonEvolutions[checking].forEach(e => {
|
||||
speciesToCheck.push(e.speciesId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const extraRibbons = extractRibbons(data);
|
||||
|
||||
return ribbons.concat(extraRibbons);
|
||||
}
|
||||
|
||||
export function getRibbonKey(flag: RibbonFlag): string {
|
||||
for (const [key, value] of Object.entries(RibbonData)) {
|
||||
if (typeof value === "bigint" && value === flag) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user