diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index fd8444f73ef..f1bed5d3498 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -3,14 +3,18 @@ import { Mode } from "./ui"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import { TextStyle, addTextObject } from "./text"; import MessageUiHandler from "./message-ui-handler"; -import { Egg } from "../data/egg"; import { addWindow } from "./ui-theme"; import {Button} from "#enums/buttons"; import i18next from "i18next"; +import { ScrollBar } from "./scroll-bar"; export default class EggListUiHandler extends MessageUiHandler { + private readonly ROWS = 9; + private readonly COLUMNS = 11; + private eggListContainer: Phaser.GameObjects.Container; private eggListIconContainer: Phaser.GameObjects.Container; + private eggIcons: Phaser.GameObjects.Sprite[]; private eggSprite: Phaser.GameObjects.Sprite; private eggNameText: Phaser.GameObjects.Text; private eggDateText: Phaser.GameObjects.Text; @@ -19,6 +23,8 @@ export default class EggListUiHandler extends MessageUiHandler { private eggListMessageBoxContainer: Phaser.GameObjects.Container; private cursorObj: Phaser.GameObjects.Image; + private scrollCursor: number; + private scrollBar: ScrollBar; private iconAnimHandler: PokemonIconAnimHandler; @@ -64,7 +70,7 @@ export default class EggListUiHandler extends MessageUiHandler { this.eggGachaInfoText.setWordWrapWidth(540); this.eggListContainer.add(this.eggGachaInfoText); - this.eggListIconContainer = this.scene.add.container(115, 9); + this.eggListIconContainer = this.scene.add.container(113, 5); this.eggListContainer.add(this.eggListIconContainer); this.cursorObj = this.scene.add.image(0, 0, "select_cursor"); @@ -74,6 +80,9 @@ export default class EggListUiHandler extends MessageUiHandler { this.eggSprite = this.scene.add.sprite(54, 37, "egg"); this.eggListContainer.add(this.eggSprite); + this.scrollBar = new ScrollBar(this.scene, 310, 5, 4, 170, this.ROWS); + this.eggListContainer.add(this.scrollBar); + this.eggListMessageBoxContainer = this.scene.add.container(0, this.scene.game.canvas.height / 6); this.eggListMessageBoxContainer.setVisible(false); this.eggListContainer.add(this.eggListMessageBoxContainer); @@ -87,32 +96,55 @@ export default class EggListUiHandler extends MessageUiHandler { this.eggListMessageBoxContainer.add(this.message); this.cursor = -1; + this.scrollCursor = 0; } show(args: any[]): boolean { super.show(args); + this.initEggIcons(); + this.getUi().bringToTop(this.eggListContainer); this.eggListContainer.setVisible(true); - let e = 0; + this.scrollBar.setTotalRows(Math.ceil(this.scene.gameData.eggs.length / this.COLUMNS)); - for (const egg of this.scene.gameData.eggs) { - const x = (e % 11) * 18; - const y = Math.floor(e / 11) * 18; + this.updateEggIcons(); + this.setCursor(0); + this.setScrollCursor(0); + + return true; + } + + private initEggIcons() { + this.eggIcons = []; + for (let i = 0; i < Math.min(this.ROWS * this.COLUMNS, this.scene.gameData.eggs.length); i++) { + const x = (i % this.COLUMNS) * 18; + const y = Math.floor(i / this.COLUMNS) * 18; const icon = this.scene.add.sprite(x - 2, y + 2, "egg_icons"); icon.setScale(0.5); icon.setOrigin(0, 0); - icon.setFrame(egg.getKey()); this.eggListIconContainer.add(icon); - this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE); - e++; + this.eggIcons.push(icon); } + } - this.setCursor(0); + private updateEggIcons() { + const indexOffset = this.scrollCursor * this.COLUMNS; + const eggsToShow = Math.min(this.eggIcons.length, this.scene.gameData.eggs.length - indexOffset); + + this.eggIcons.forEach((icon, i) => { + this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE); + if (i < eggsToShow) { + const egg = this.scene.gameData.eggs[i + indexOffset]; + icon.setFrame(egg.getKey()); + icon.setVisible(true); + } else { + icon.setVisible(false); + } + }); - return true; } processInput(button: Button): boolean { @@ -125,27 +157,53 @@ export default class EggListUiHandler extends MessageUiHandler { ui.revertMode(); success = true; } else { - const eggCount = this.eggListIconContainer.getAll().length; - const rows = Math.ceil(eggCount / 11); - const row = Math.floor(this.cursor / 11); + const totalEggs = this.scene.gameData.eggs.length; + const onScreenRows = Math.min(this.ROWS, Math.ceil(this.eggIcons.length / this.COLUMNS)); + const maxScrollCursor = Math.max(0, Math.ceil(totalEggs / this.COLUMNS) - onScreenRows); + const currentRowIndex = Math.floor(this.cursor / this.COLUMNS); + const currentColumnIndex = this.cursor % this.COLUMNS; + const lastEggIndex = Math.min(this.eggIcons.length - 1, totalEggs - maxScrollCursor * this.COLUMNS - 1); switch (button) { case Button.UP: - if (row) { - success = this.setCursor(this.cursor - 11); + if (currentRowIndex > 0) { + success = this.setCursor(this.cursor - this.COLUMNS); + } else if (this.scrollCursor > 0) { + success = this.setScrollCursor(this.scrollCursor - 1); + } else { + // wrap around to the last row + const newCursor = this.cursor + (onScreenRows - 1) * this.COLUMNS; + if (newCursor > lastEggIndex) { + success = this.setCursor(newCursor - this.COLUMNS); + } else { + success = this.setCursor(newCursor); + } + success = this.setScrollCursor(maxScrollCursor) || success; } break; case Button.DOWN: - if (row < rows - 2 || (row < rows - 1 && this.cursor % 11 <= (eggCount - 1) % 11)) { - success = this.setCursor(this.cursor + 11); + if (currentRowIndex < onScreenRows - 1) { + // Go down one row + success = this.setCursor(Math.min(this.cursor + this.COLUMNS, totalEggs - this.scrollCursor * this.COLUMNS - 1)); + } else if (this.scrollCursor < maxScrollCursor) { + // Scroll down one row + success = this.setScrollCursor(this.scrollCursor + 1); + } else { + // Wrap around to the top row + success = this.setCursor(this.cursor % this.COLUMNS); + success = this.setScrollCursor(0) || success; } break; case Button.LEFT: - if (this.cursor % 11) { + if (currentColumnIndex > 0) { success = this.setCursor(this.cursor - 1); + } else { + success = this.setCursor(Math.min(this.cursor + this.COLUMNS - 1, lastEggIndex)); } break; case Button.RIGHT: - if (this.cursor % 11 < (row < rows - 1 ? 10 : (eggCount - 1) % 11)) { + if (currentColumnIndex === this.COLUMNS - 1 || this.cursor === lastEggIndex) { + success = this.setCursor(this.cursor - currentColumnIndex); + } else { success = this.setCursor(this.cursor + 1); } break; @@ -161,7 +219,8 @@ export default class EggListUiHandler extends MessageUiHandler { return success || error; } - setEggDetails(egg: Egg): void { + setEggDetails(index: number): void { + const egg = this.scene.gameData.eggs[index]; this.eggSprite.setFrame(`egg_${egg.getKey()}`); this.eggNameText.setText(`${i18next.t("egg:egg")} (${egg.getEggDescriptor()})`); this.eggDateText.setText( @@ -176,7 +235,7 @@ export default class EggListUiHandler extends MessageUiHandler { this.eggGachaInfoText.setText(egg.getEggTypeDescriptor(this.scene)); } - setCursor(cursor: integer): boolean { + setCursor(cursor: number): boolean { let changed = false; const lastCursor = this.cursor; @@ -184,24 +243,46 @@ export default class EggListUiHandler extends MessageUiHandler { changed = super.setCursor(cursor); if (changed) { - this.cursorObj.setPosition(114 + 18 * (cursor % 11), 10 + 18 * Math.floor(cursor / 11)); + const icon = this.eggIcons[cursor]; + this.cursorObj.setPosition(icon.x + 114, icon.y + 5); if (lastCursor > -1) { this.iconAnimHandler.addOrUpdate(this.eggListIconContainer.getAt(lastCursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.NONE); } this.iconAnimHandler.addOrUpdate(this.eggListIconContainer.getAt(cursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.ACTIVE); - this.setEggDetails(this.scene.gameData.eggs[cursor]); + this.setEggDetails(cursor + this.scrollCursor * this.COLUMNS); } return changed; } + setScrollCursor(cursor: number): boolean { + if (cursor === this.scrollCursor) { + return false; + } + + this.scrollCursor = cursor; + this.scrollBar.setScrollCursor(cursor); + + const newEggIndex = this.cursor + this.scrollCursor * this.COLUMNS; + if (newEggIndex >= this.scene.gameData.eggs.length) { + this.setCursor(this.scene.gameData.eggs.length - this.scrollCursor * this.COLUMNS - 1); + } else { + this.setEggDetails(newEggIndex); + } + + this.updateEggIcons(); + return true; + } + clear(): void { super.clear(); + this.setScrollCursor(0); this.cursor = -1; this.eggListContainer.setVisible(false); this.iconAnimHandler.removeAll(); this.eggListIconContainer.removeAll(true); + this.eggIcons = []; } } diff --git a/src/ui/scroll-bar.ts b/src/ui/scroll-bar.ts index 5ed79d0cdad..9874be0f73a 100644 --- a/src/ui/scroll-bar.ts +++ b/src/ui/scroll-bar.ts @@ -22,6 +22,8 @@ export class ScrollBar extends Phaser.GameObjects.Container { super(scene, x, y); this.maxRows = maxRows; + this.totalRows = maxRows; + this.currentRow = 0; const borderSize = 2; width = Math.max(width, 4); @@ -46,8 +48,7 @@ export class ScrollBar extends Phaser.GameObjects.Container { */ setScrollCursor(scrollCursor: number): void { this.currentRow = scrollCursor; - this.handleBody.y = 1 + (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) / this.totalRows * this.currentRow; - this.handleBottom.y = this.handleBody.y + this.handleBody.displayHeight; + this.updateHandlePosition(); } /** @@ -59,7 +60,13 @@ export class ScrollBar extends Phaser.GameObjects.Container { setTotalRows(rows: number): void { this.totalRows = rows; this.handleBody.height = (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) * this.maxRows / this.totalRows; + this.updateHandlePosition(); this.setVisible(this.totalRows > this.maxRows); } + + private updateHandlePosition(): void { + this.handleBody.y = 1 + (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) / this.totalRows * this.currentRow; + this.handleBottom.y = this.handleBody.y + this.handleBody.displayHeight; + } }