mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 09:02:47 +02:00
948 lines
32 KiB
TypeScript
948 lines
32 KiB
TypeScript
import { globalScene } from "#app/global-scene";
|
|
import type { ModifierTypeOption } from "../modifier/modifier-type";
|
|
import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "../modifier/modifier-type";
|
|
import { getPokeballAtlasKey } from "#app/data/pokeball";
|
|
import { addTextObject, getTextStyleOptions, getModifierTierTextTint, getTextColor, TextStyle } from "./text";
|
|
import AwaitableUiHandler from "./awaitable-ui-handler";
|
|
import { UiMode } from "#enums/ui-mode";
|
|
import { LockModifierTiersModifier, HealShopCostModifier } from "../modifier/modifier";
|
|
import { handleTutorial, Tutorial } from "../tutorial";
|
|
import { Button } from "#enums/buttons";
|
|
import MoveInfoOverlay from "./move-info-overlay";
|
|
import { allMoves } from "#app/data/data-lists";
|
|
import { formatMoney, NumberHolder } from "#app/utils/common";
|
|
import Overrides from "#app/overrides";
|
|
import i18next from "i18next";
|
|
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
|
import Phaser from "phaser";
|
|
import type { PokeballType } from "#enums/pokeball";
|
|
|
|
export const SHOP_OPTIONS_ROW_LIMIT = 7;
|
|
const SINGLE_SHOP_ROW_YOFFSET = 12;
|
|
const DOUBLE_SHOP_ROW_YOFFSET = 24;
|
|
const OPTION_BUTTON_YPOSITION = -62;
|
|
|
|
export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
|
private modifierContainer: Phaser.GameObjects.Container;
|
|
private rerollButtonContainer: Phaser.GameObjects.Container;
|
|
private lockRarityButtonContainer: Phaser.GameObjects.Container;
|
|
private transferButtonContainer: Phaser.GameObjects.Container;
|
|
private checkButtonContainer: Phaser.GameObjects.Container;
|
|
private continueButtonContainer: Phaser.GameObjects.Container;
|
|
private rerollCostText: Phaser.GameObjects.Text;
|
|
private lockRarityButtonText: Phaser.GameObjects.Text;
|
|
private moveInfoOverlay: MoveInfoOverlay;
|
|
private moveInfoOverlayActive = false;
|
|
|
|
private rowCursor = 0;
|
|
private player: boolean;
|
|
/**
|
|
* If reroll cost is negative, it is assumed there are 0 items in the shop.
|
|
* It will cause reroll button to be disabled, and a "Continue" button to show in the place of shop items
|
|
*/
|
|
private rerollCost: number;
|
|
private transferButtonWidth: number;
|
|
private checkButtonWidth: number;
|
|
|
|
public options: ModifierOption[];
|
|
public shopOptionsRows: ModifierOption[][];
|
|
|
|
private cursorObj: Phaser.GameObjects.Image | null;
|
|
|
|
constructor() {
|
|
super(UiMode.CONFIRM);
|
|
|
|
this.options = [];
|
|
this.shopOptionsRows = [];
|
|
}
|
|
|
|
setup() {
|
|
const ui = this.getUi();
|
|
|
|
this.modifierContainer = globalScene.add.container(0, 0);
|
|
ui.add(this.modifierContainer);
|
|
|
|
const canvas = document.createElement("canvas");
|
|
const context = canvas.getContext("2d");
|
|
const styleOptions = getTextStyleOptions(TextStyle.PARTY, globalScene.uiTheme).styleOptions;
|
|
|
|
if (context) {
|
|
context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily;
|
|
this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width;
|
|
this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width;
|
|
}
|
|
|
|
this.transferButtonContainer = globalScene.add.container(
|
|
(globalScene.game.canvas.width - this.checkButtonWidth) / 6 - 21,
|
|
OPTION_BUTTON_YPOSITION,
|
|
);
|
|
this.transferButtonContainer.setName("transfer-btn");
|
|
this.transferButtonContainer.setVisible(false);
|
|
ui.add(this.transferButtonContainer);
|
|
|
|
const transferButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:transfer"), TextStyle.PARTY);
|
|
transferButtonText.setName("text-transfer-btn");
|
|
transferButtonText.setOrigin(1, 0);
|
|
this.transferButtonContainer.add(transferButtonText);
|
|
|
|
this.checkButtonContainer = globalScene.add.container(
|
|
globalScene.game.canvas.width / 6 - 1,
|
|
OPTION_BUTTON_YPOSITION,
|
|
);
|
|
this.checkButtonContainer.setName("use-btn");
|
|
this.checkButtonContainer.setVisible(false);
|
|
ui.add(this.checkButtonContainer);
|
|
|
|
const checkButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:checkTeam"), TextStyle.PARTY);
|
|
checkButtonText.setName("text-use-btn");
|
|
checkButtonText.setOrigin(1, 0);
|
|
this.checkButtonContainer.add(checkButtonText);
|
|
|
|
this.rerollButtonContainer = globalScene.add.container(16, OPTION_BUTTON_YPOSITION);
|
|
this.rerollButtonContainer.setName("reroll-brn");
|
|
this.rerollButtonContainer.setVisible(false);
|
|
ui.add(this.rerollButtonContainer);
|
|
|
|
const rerollButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:reroll"), TextStyle.PARTY);
|
|
rerollButtonText.setName("text-reroll-btn");
|
|
rerollButtonText.setOrigin(0, 0);
|
|
this.rerollButtonContainer.add(rerollButtonText);
|
|
|
|
this.rerollCostText = addTextObject(0, 0, "", TextStyle.MONEY);
|
|
this.rerollCostText.setName("text-reroll-cost");
|
|
this.rerollCostText.setOrigin(0, 0);
|
|
this.rerollCostText.setPositionRelative(rerollButtonText, rerollButtonText.displayWidth + 5, 1);
|
|
this.rerollButtonContainer.add(this.rerollCostText);
|
|
|
|
this.lockRarityButtonContainer = globalScene.add.container(16, OPTION_BUTTON_YPOSITION);
|
|
this.lockRarityButtonContainer.setVisible(false);
|
|
ui.add(this.lockRarityButtonContainer);
|
|
|
|
this.lockRarityButtonText = addTextObject(
|
|
-4,
|
|
-2,
|
|
i18next.t("modifierSelectUiHandler:lockRarities"),
|
|
TextStyle.PARTY,
|
|
);
|
|
this.lockRarityButtonText.setOrigin(0, 0);
|
|
this.lockRarityButtonContainer.add(this.lockRarityButtonText);
|
|
|
|
this.continueButtonContainer = globalScene.add.container(
|
|
globalScene.game.canvas.width / 12,
|
|
-(globalScene.game.canvas.height / 12),
|
|
);
|
|
this.continueButtonContainer.setVisible(false);
|
|
ui.add(this.continueButtonContainer);
|
|
|
|
// Create continue button
|
|
const continueButtonText = addTextObject(
|
|
-24,
|
|
5,
|
|
i18next.t("modifierSelectUiHandler:continueNextWaveButton"),
|
|
TextStyle.MESSAGE,
|
|
);
|
|
continueButtonText.setName("text-continue-btn");
|
|
this.continueButtonContainer.add(continueButtonText);
|
|
|
|
// prepare move overlay
|
|
const overlayScale = 1;
|
|
this.moveInfoOverlay = new MoveInfoOverlay({
|
|
delayVisibility: true,
|
|
scale: overlayScale,
|
|
onSide: true,
|
|
right: true,
|
|
x: 1,
|
|
y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1,
|
|
width: globalScene.game.canvas.width / 6 - 2,
|
|
});
|
|
ui.add(this.moveInfoOverlay);
|
|
// register the overlay to receive toggle events
|
|
globalScene.addInfoToggle(this.moveInfoOverlay);
|
|
}
|
|
|
|
show(args: any[]): boolean {
|
|
globalScene.disableMenu = false;
|
|
|
|
if (this.active) {
|
|
if (args.length >= 3) {
|
|
this.awaitingActionInput = true;
|
|
this.onActionInput = args[2];
|
|
}
|
|
this.moveInfoOverlay.active = this.moveInfoOverlayActive;
|
|
return false;
|
|
}
|
|
|
|
if (args.length !== 4 || !Array.isArray(args[1]) || !(args[2] instanceof Function)) {
|
|
return false;
|
|
}
|
|
|
|
super.show(args);
|
|
|
|
this.getUi().clearText();
|
|
|
|
this.player = args[0];
|
|
|
|
const partyHasHeldItem =
|
|
globalScene
|
|
.getPlayerParty()
|
|
.map(p => p.heldItemManager.getTransferableHeldItems().length)
|
|
.reduce((tot, i) => tot + i, 0) > 0;
|
|
const canLockRarities = !!globalScene.findModifier(m => m instanceof LockModifierTiersModifier);
|
|
|
|
this.transferButtonContainer.setVisible(false);
|
|
this.transferButtonContainer.setAlpha(0);
|
|
|
|
this.checkButtonContainer.setVisible(false);
|
|
this.checkButtonContainer.setAlpha(0);
|
|
|
|
this.rerollButtonContainer.setVisible(false);
|
|
this.rerollButtonContainer.setAlpha(0);
|
|
|
|
this.lockRarityButtonContainer.setVisible(false);
|
|
this.lockRarityButtonContainer.setAlpha(0);
|
|
|
|
this.continueButtonContainer.setVisible(false);
|
|
this.continueButtonContainer.setAlpha(0);
|
|
|
|
this.rerollButtonContainer.setPositionRelative(this.lockRarityButtonContainer, 0, canLockRarities ? -12 : 0);
|
|
|
|
this.rerollCost = args[3] as number;
|
|
|
|
this.updateRerollCostText();
|
|
|
|
const typeOptions = args[1] as ModifierTypeOption[];
|
|
const removeHealShop = globalScene.gameMode.hasNoShop;
|
|
const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1));
|
|
globalScene.applyModifier(HealShopCostModifier, true, baseShopCost);
|
|
const shopTypeOptions = !removeHealShop
|
|
? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value)
|
|
: [];
|
|
const optionsYOffset =
|
|
shopTypeOptions.length > SHOP_OPTIONS_ROW_LIMIT ? -SINGLE_SHOP_ROW_YOFFSET : -DOUBLE_SHOP_ROW_YOFFSET;
|
|
|
|
for (let m = 0; m < typeOptions.length; m++) {
|
|
const sliceWidth = globalScene.game.canvas.width / 6 / (typeOptions.length + 2);
|
|
const option = new ModifierOption(
|
|
sliceWidth * (m + 1) + sliceWidth * 0.5,
|
|
-globalScene.game.canvas.height / 12 + optionsYOffset,
|
|
typeOptions[m],
|
|
);
|
|
option.setScale(0.5);
|
|
globalScene.add.existing(option);
|
|
this.modifierContainer.add(option);
|
|
this.options.push(option);
|
|
}
|
|
|
|
// Set "Continue" button height based on number of rows in healing items shop
|
|
const continueButton = this.continueButtonContainer.getAt<Phaser.GameObjects.Text>(0);
|
|
continueButton.y = optionsYOffset - 5;
|
|
continueButton.setVisible(this.options.length === 0);
|
|
|
|
for (let m = 0; m < shopTypeOptions.length; m++) {
|
|
const row = m < SHOP_OPTIONS_ROW_LIMIT ? 0 : 1;
|
|
const col = m < SHOP_OPTIONS_ROW_LIMIT ? m : m - SHOP_OPTIONS_ROW_LIMIT;
|
|
const rowOptions = shopTypeOptions.slice(
|
|
row ? SHOP_OPTIONS_ROW_LIMIT : 0,
|
|
row ? undefined : SHOP_OPTIONS_ROW_LIMIT,
|
|
);
|
|
const sliceWidth = globalScene.game.canvas.width / 6 / (rowOptions.length + 2);
|
|
const option = new ModifierOption(
|
|
sliceWidth * (col + 1) + sliceWidth * 0.5,
|
|
-globalScene.game.canvas.height / 12 - globalScene.game.canvas.height / 32 - (42 - (28 * row - 1)),
|
|
shopTypeOptions[m],
|
|
);
|
|
option.setScale(0.375);
|
|
globalScene.add.existing(option);
|
|
this.modifierContainer.add(option);
|
|
|
|
if (row >= this.shopOptionsRows.length) {
|
|
this.shopOptionsRows.push([]);
|
|
}
|
|
this.shopOptionsRows[row].push(option);
|
|
}
|
|
|
|
//TODO: temporary stopgap so the game does not crash, will have to fix this later
|
|
// console.log(typeOptions.map(to => to.upgradeCount))
|
|
// const maxUpgradeCount = typeOptions.map(to => to.upgradeCount).reduce((max, current) => Math.max(current, max), 0);
|
|
const maxUpgradeCount = 0;
|
|
|
|
/* Force updateModifiers without pokemon held items */
|
|
globalScene.updateModifiers(true, false);
|
|
|
|
/* Multiplies the appearance duration by the speed parameter so that it is always constant, and avoids "flashbangs" at game speed x5 */
|
|
globalScene.showShopOverlay(750 * globalScene.gameSpeed);
|
|
globalScene.updateAndShowText(750);
|
|
globalScene.updateBiomeWaveText();
|
|
globalScene.updateMoneyText();
|
|
|
|
let i = 0;
|
|
|
|
globalScene.tweens.addCounter({
|
|
ease: "Sine.easeIn",
|
|
duration: 1250,
|
|
onUpdate: t => {
|
|
const value = t.getValue();
|
|
const index = Math.floor(value * typeOptions.length);
|
|
if (index > i && index <= typeOptions.length) {
|
|
const option = this.options[i];
|
|
option?.show(
|
|
Math.floor((1 - value) * 1250) * 0.325 + 2000 * maxUpgradeCount,
|
|
-(maxUpgradeCount - typeOptions[i].upgradeCount),
|
|
);
|
|
i++;
|
|
}
|
|
},
|
|
});
|
|
|
|
globalScene.time.delayedCall(1000 + maxUpgradeCount * 2000, () => {
|
|
for (const shopOption of this.shopOptionsRows.flat()) {
|
|
shopOption.show(0, 0);
|
|
}
|
|
});
|
|
|
|
globalScene.time.delayedCall(4000 + maxUpgradeCount * 2000, () => {
|
|
if (partyHasHeldItem) {
|
|
this.transferButtonContainer.setAlpha(0);
|
|
this.transferButtonContainer.setVisible(true);
|
|
globalScene.tweens.add({
|
|
targets: this.transferButtonContainer,
|
|
alpha: 1,
|
|
duration: 250,
|
|
});
|
|
}
|
|
|
|
this.rerollButtonContainer.setAlpha(0);
|
|
this.checkButtonContainer.setAlpha(0);
|
|
this.lockRarityButtonContainer.setAlpha(0);
|
|
this.continueButtonContainer.setAlpha(0);
|
|
this.rerollButtonContainer.setVisible(true);
|
|
this.checkButtonContainer.setVisible(true);
|
|
this.continueButtonContainer.setVisible(this.rerollCost < 0);
|
|
this.lockRarityButtonContainer.setVisible(canLockRarities);
|
|
|
|
globalScene.tweens.add({
|
|
targets: [this.checkButtonContainer, this.continueButtonContainer],
|
|
alpha: 1,
|
|
duration: 250,
|
|
});
|
|
|
|
globalScene.tweens.add({
|
|
targets: [this.rerollButtonContainer, this.lockRarityButtonContainer],
|
|
alpha: this.rerollCost < 0 ? 0.5 : 1,
|
|
duration: 250,
|
|
});
|
|
|
|
const updateCursorTarget = () => {
|
|
if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
|
|
this.setRowCursor(0);
|
|
this.setCursor(2);
|
|
} else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) {
|
|
this.setRowCursor(ShopCursorTarget.REWARDS);
|
|
this.setCursor(0);
|
|
} else {
|
|
this.setRowCursor(globalScene.shopCursorTarget);
|
|
this.setCursor(0);
|
|
}
|
|
};
|
|
|
|
updateCursorTarget();
|
|
|
|
handleTutorial(Tutorial.Select_Item).then(res => {
|
|
if (res) {
|
|
updateCursorTarget();
|
|
}
|
|
this.awaitingActionInput = true;
|
|
this.onActionInput = args[2];
|
|
});
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
processInput(button: Button): boolean {
|
|
const ui = this.getUi();
|
|
|
|
if (!this.awaitingActionInput) {
|
|
return false;
|
|
}
|
|
|
|
let success = false;
|
|
|
|
if (button === Button.ACTION) {
|
|
success = true;
|
|
if (this.onActionInput) {
|
|
const originalOnActionInput = this.onActionInput;
|
|
this.awaitingActionInput = false;
|
|
this.onActionInput = null;
|
|
if (!originalOnActionInput(this.rowCursor, this.cursor)) {
|
|
this.awaitingActionInput = true;
|
|
this.onActionInput = originalOnActionInput;
|
|
} else {
|
|
this.moveInfoOverlayActive = this.moveInfoOverlay.active;
|
|
this.moveInfoOverlay.setVisible(false);
|
|
this.moveInfoOverlay.active = false; // this is likely unnecessary, but it should help future prove the UI
|
|
}
|
|
}
|
|
} else if (button === Button.CANCEL) {
|
|
if (this.player) {
|
|
success = true;
|
|
if (this.onActionInput) {
|
|
const originalOnActionInput = this.onActionInput;
|
|
this.awaitingActionInput = false;
|
|
this.onActionInput = null;
|
|
originalOnActionInput(-1);
|
|
this.moveInfoOverlayActive = this.moveInfoOverlay.active;
|
|
this.moveInfoOverlay.setVisible(false);
|
|
this.moveInfoOverlay.active = false; // don't clear here as we might need to restore the UI in case the user cancels the action
|
|
}
|
|
}
|
|
} else {
|
|
switch (button) {
|
|
case Button.UP:
|
|
if (this.rowCursor === 0 && this.cursor === 3) {
|
|
success = this.setCursor(0);
|
|
} else if (this.rowCursor < this.shopOptionsRows.length + 1) {
|
|
success = this.setRowCursor(this.rowCursor + 1);
|
|
} else {
|
|
success = this.setRowCursor(0);
|
|
}
|
|
break;
|
|
case Button.DOWN:
|
|
if (this.rowCursor) {
|
|
success = this.setRowCursor(this.rowCursor - 1);
|
|
} else if (this.lockRarityButtonContainer.visible && this.cursor === 0) {
|
|
success = this.setCursor(3);
|
|
} else {
|
|
success = this.setRowCursor(this.shopOptionsRows.length + 1);
|
|
}
|
|
break;
|
|
case Button.LEFT:
|
|
if (!this.rowCursor) {
|
|
switch (this.cursor) {
|
|
case 0:
|
|
success = this.setCursor(2);
|
|
break;
|
|
case 1:
|
|
if (this.lockRarityButtonContainer.visible) {
|
|
success = this.setCursor(3);
|
|
} else {
|
|
success = this.rerollButtonContainer.visible && this.setCursor(0);
|
|
}
|
|
break;
|
|
case 2:
|
|
if (this.transferButtonContainer.visible) {
|
|
success = this.setCursor(1);
|
|
} else if (this.rerollButtonContainer.visible) {
|
|
success = this.setCursor(0);
|
|
} else {
|
|
success = false;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (this.lockRarityButtonContainer.visible) {
|
|
success = this.setCursor(2);
|
|
} else {
|
|
success = false;
|
|
}
|
|
}
|
|
} else if (this.cursor) {
|
|
success = this.setCursor(this.cursor - 1);
|
|
} else {
|
|
if (this.rowCursor === 1 && this.options.length === 0) {
|
|
success = false;
|
|
} else {
|
|
success = this.setCursor(this.getRowItems(this.rowCursor) - 1);
|
|
}
|
|
}
|
|
break;
|
|
case Button.RIGHT:
|
|
if (!this.rowCursor) {
|
|
switch (this.cursor) {
|
|
case 0:
|
|
if (this.transferButtonContainer.visible) {
|
|
success = this.setCursor(1);
|
|
} else {
|
|
success = this.setCursor(2);
|
|
}
|
|
break;
|
|
case 1:
|
|
success = this.setCursor(2);
|
|
break;
|
|
case 2:
|
|
success = this.setCursor(0);
|
|
break;
|
|
case 3:
|
|
if (this.transferButtonContainer.visible) {
|
|
success = this.setCursor(1);
|
|
} else {
|
|
success = this.setCursor(2);
|
|
}
|
|
break;
|
|
}
|
|
} else if (this.cursor < this.getRowItems(this.rowCursor) - 1) {
|
|
success = this.setCursor(this.cursor + 1);
|
|
} else {
|
|
if (this.rowCursor === 1 && this.options.length === 0) {
|
|
success = this.setRowCursor(0);
|
|
} else {
|
|
success = this.setCursor(0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (success) {
|
|
ui.playSelect();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
setCursor(cursor: number): boolean {
|
|
const ui = this.getUi();
|
|
const ret = super.setCursor(cursor);
|
|
|
|
if (!this.cursorObj) {
|
|
this.cursorObj = globalScene.add.image(0, 0, "cursor");
|
|
this.modifierContainer.add(this.cursorObj);
|
|
}
|
|
|
|
const options =
|
|
this.rowCursor === 1 ? this.options : this.shopOptionsRows[this.shopOptionsRows.length - (this.rowCursor - 1)];
|
|
|
|
this.cursorObj.setScale(this.rowCursor === 1 ? 2 : this.rowCursor >= 2 ? 1.5 : 1);
|
|
|
|
// the modifier selection has been updated, always hide the overlay
|
|
this.moveInfoOverlay.clear();
|
|
if (this.rowCursor) {
|
|
if (this.rowCursor === 1 && options.length === 0) {
|
|
// Continue button when no shop items
|
|
this.cursorObj.setScale(1.25);
|
|
this.cursorObj.setPosition(
|
|
globalScene.game.canvas.width / 18 + 23,
|
|
-globalScene.game.canvas.height / 12 -
|
|
(this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2),
|
|
);
|
|
ui.showText(i18next.t("modifierSelectUiHandler:continueNextWaveDescription"));
|
|
return ret;
|
|
}
|
|
|
|
const sliceWidth = globalScene.game.canvas.width / 6 / (options.length + 2);
|
|
if (this.rowCursor < 2) {
|
|
// Cursor on free items
|
|
this.cursorObj.setPosition(
|
|
sliceWidth * (cursor + 1) + sliceWidth * 0.5 - 20,
|
|
-globalScene.game.canvas.height / 12 -
|
|
(this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2),
|
|
);
|
|
} else {
|
|
// Cursor on paying items
|
|
this.cursorObj.setPosition(
|
|
sliceWidth * (cursor + 1) + sliceWidth * 0.5 - 16,
|
|
-globalScene.game.canvas.height / 12 -
|
|
globalScene.game.canvas.height / 32 -
|
|
(-14 + 28 * (this.rowCursor - (this.shopOptionsRows.length - 1))),
|
|
);
|
|
}
|
|
|
|
const type = options[this.cursor].modifierTypeOption.type;
|
|
type && ui.showText(type.getDescription());
|
|
if (type instanceof TmModifierType) {
|
|
// prepare the move overlay to be shown with the toggle
|
|
this.moveInfoOverlay.show(allMoves[type.moveId]);
|
|
}
|
|
} else if (cursor === 0) {
|
|
this.cursorObj.setPosition(
|
|
6,
|
|
this.lockRarityButtonContainer.visible ? OPTION_BUTTON_YPOSITION - 8 : OPTION_BUTTON_YPOSITION + 4,
|
|
);
|
|
ui.showText(i18next.t("modifierSelectUiHandler:rerollDesc"));
|
|
} else if (cursor === 1) {
|
|
this.cursorObj.setPosition(
|
|
(globalScene.game.canvas.width - this.transferButtonWidth - this.checkButtonWidth) / 6 - 30,
|
|
OPTION_BUTTON_YPOSITION + 4,
|
|
);
|
|
ui.showText(i18next.t("modifierSelectUiHandler:transferDesc"));
|
|
} else if (cursor === 2) {
|
|
this.cursorObj.setPosition(
|
|
(globalScene.game.canvas.width - this.checkButtonWidth) / 6 - 10,
|
|
OPTION_BUTTON_YPOSITION + 4,
|
|
);
|
|
ui.showText(i18next.t("modifierSelectUiHandler:checkTeamDesc"));
|
|
} else {
|
|
this.cursorObj.setPosition(6, OPTION_BUTTON_YPOSITION + 4);
|
|
ui.showText(i18next.t("modifierSelectUiHandler:lockRaritiesDesc"));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
setRowCursor(rowCursor: number): boolean {
|
|
const lastRowCursor = this.rowCursor;
|
|
|
|
if (rowCursor !== lastRowCursor) {
|
|
this.rowCursor = rowCursor;
|
|
let newCursor = Math.round(
|
|
(this.cursor / Math.max(this.getRowItems(lastRowCursor) - 1, 1)) * (this.getRowItems(rowCursor) - 1),
|
|
);
|
|
if (rowCursor === 1 && this.options.length === 0) {
|
|
// Handle empty shop
|
|
newCursor = 0;
|
|
}
|
|
if (rowCursor === 0) {
|
|
if (this.options.length === 0) {
|
|
newCursor = 1;
|
|
}
|
|
if (newCursor === 0 && !this.rerollButtonContainer.visible) {
|
|
newCursor = 1;
|
|
}
|
|
if (newCursor === 1 && !this.transferButtonContainer.visible) {
|
|
newCursor = 2;
|
|
}
|
|
}
|
|
// Allows to find lock rarity button when looping from the top
|
|
if (rowCursor === 0 && lastRowCursor > 1 && newCursor === 0 && this.lockRarityButtonContainer.visible) {
|
|
newCursor = 3;
|
|
}
|
|
// Allows to loop to top when lock rarity button is shown
|
|
if (rowCursor === this.shopOptionsRows.length + 1 && lastRowCursor === 0 && this.cursor === 3) {
|
|
newCursor = 0;
|
|
}
|
|
this.cursor = -1;
|
|
this.setCursor(newCursor);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private getRowItems(rowCursor: number): number {
|
|
switch (rowCursor) {
|
|
case 0:
|
|
return 3;
|
|
case 1:
|
|
return this.options.length;
|
|
default:
|
|
return this.shopOptionsRows[this.shopOptionsRows.length - (rowCursor - 1)].length;
|
|
}
|
|
}
|
|
|
|
setRerollCost(rerollCost: number): void {
|
|
this.rerollCost = rerollCost;
|
|
}
|
|
|
|
updateCostText(): void {
|
|
const shopOptions = this.shopOptionsRows.flat();
|
|
for (const shopOption of shopOptions) {
|
|
shopOption.updateCostText();
|
|
}
|
|
|
|
this.updateRerollCostText();
|
|
}
|
|
|
|
updateRerollCostText(): void {
|
|
const rerollDisabled = this.rerollCost < 0;
|
|
if (rerollDisabled) {
|
|
this.rerollCostText.setVisible(false);
|
|
return;
|
|
}
|
|
this.rerollCostText.setVisible(true);
|
|
const canReroll = globalScene.money >= this.rerollCost;
|
|
|
|
const formattedMoney = formatMoney(globalScene.moneyFormat, this.rerollCost);
|
|
|
|
this.rerollCostText.setText(i18next.t("modifierSelectUiHandler:rerollCost", { formattedMoney }));
|
|
this.rerollCostText.setColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED));
|
|
this.rerollCostText.setShadowColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED, true));
|
|
}
|
|
|
|
updateLockRaritiesText(): void {
|
|
const textStyle = globalScene.lockModifierTiers ? TextStyle.SUMMARY_BLUE : TextStyle.PARTY;
|
|
this.lockRarityButtonText.setColor(this.getTextColor(textStyle));
|
|
this.lockRarityButtonText.setShadowColor(this.getTextColor(textStyle, true));
|
|
}
|
|
|
|
clear() {
|
|
super.clear();
|
|
|
|
this.moveInfoOverlay.clear();
|
|
this.moveInfoOverlayActive = false;
|
|
this.awaitingActionInput = false;
|
|
this.onActionInput = null;
|
|
this.getUi().clearText();
|
|
this.eraseCursor();
|
|
|
|
// Reset cursor positions
|
|
this.cursor = 0;
|
|
this.rowCursor = 0;
|
|
|
|
/* Multiplies the fade time duration by the speed parameter so that it is always constant, and avoids "flashbangs" at game speed x5 */
|
|
globalScene.hideShopOverlay(750 * globalScene.gameSpeed);
|
|
globalScene.hideLuckText(250);
|
|
|
|
/* Normally already called just after the shop, but not sure if it happens in 100% of cases */
|
|
globalScene.updateModifiers(true);
|
|
|
|
const options = this.options.concat(this.shopOptionsRows.flat());
|
|
this.options.splice(0, this.options.length);
|
|
this.shopOptionsRows.splice(0, this.shopOptionsRows.length);
|
|
|
|
globalScene.tweens.add({
|
|
targets: options,
|
|
scale: 0.01,
|
|
duration: 250,
|
|
ease: "Cubic.easeIn",
|
|
onComplete: () => options.forEach(o => o.destroy()),
|
|
});
|
|
|
|
[
|
|
this.rerollButtonContainer,
|
|
this.checkButtonContainer,
|
|
this.transferButtonContainer,
|
|
this.lockRarityButtonContainer,
|
|
this.continueButtonContainer,
|
|
].forEach(container => {
|
|
if (container.visible) {
|
|
globalScene.tweens.add({
|
|
targets: container,
|
|
alpha: 0,
|
|
duration: 250,
|
|
ease: "Cubic.easeIn",
|
|
onComplete: () => {
|
|
if (!this.options.length) {
|
|
container.setVisible(false);
|
|
} else {
|
|
container.setAlpha(1);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
eraseCursor() {
|
|
if (this.cursorObj) {
|
|
this.cursorObj.destroy();
|
|
}
|
|
this.cursorObj = null;
|
|
}
|
|
}
|
|
|
|
class ModifierOption extends Phaser.GameObjects.Container {
|
|
public modifierTypeOption: ModifierTypeOption;
|
|
private pb: Phaser.GameObjects.Sprite;
|
|
private pbTint: Phaser.GameObjects.Sprite;
|
|
private itemContainer: Phaser.GameObjects.Container;
|
|
private item: Phaser.GameObjects.Sprite;
|
|
private itemTint: Phaser.GameObjects.Sprite;
|
|
private itemText: Phaser.GameObjects.Text;
|
|
private itemCostText: Phaser.GameObjects.Text;
|
|
|
|
constructor(x: number, y: number, modifierTypeOption: ModifierTypeOption) {
|
|
super(globalScene, x, y);
|
|
|
|
this.modifierTypeOption = modifierTypeOption;
|
|
|
|
this.setup();
|
|
}
|
|
|
|
setup() {
|
|
if (!this.modifierTypeOption.cost) {
|
|
const getPb = (): Phaser.GameObjects.Sprite => {
|
|
const pb = globalScene.add.sprite(0, -182, "pb", this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount));
|
|
pb.setScale(2);
|
|
return pb;
|
|
};
|
|
|
|
this.pb = getPb();
|
|
this.add(this.pb);
|
|
|
|
this.pbTint = getPb();
|
|
this.pbTint.setVisible(false);
|
|
this.add(this.pbTint);
|
|
}
|
|
|
|
this.itemContainer = globalScene.add.container(0, 0);
|
|
this.itemContainer.setScale(0.5);
|
|
this.itemContainer.setAlpha(0);
|
|
this.add(this.itemContainer);
|
|
|
|
const getItem = () => {
|
|
const item = globalScene.add.sprite(0, 0, "items", this.modifierTypeOption.type?.getIcon());
|
|
return item;
|
|
};
|
|
|
|
this.item = getItem();
|
|
this.itemContainer.add(this.item);
|
|
|
|
if (!this.modifierTypeOption.cost) {
|
|
this.itemTint = getItem();
|
|
this.itemTint.setTintFill(Phaser.Display.Color.GetColor(255, 192, 255));
|
|
this.itemContainer.add(this.itemTint);
|
|
}
|
|
|
|
this.itemText = addTextObject(0, 35, this.modifierTypeOption.type?.name!, TextStyle.PARTY, { align: "center" }); // TODO: is this bang correct?
|
|
this.itemText.setOrigin(0.5, 0);
|
|
this.itemText.setAlpha(0);
|
|
this.itemText.setTint(
|
|
this.modifierTypeOption.type?.tier ? getModifierTierTextTint(this.modifierTypeOption.type?.tier) : undefined,
|
|
);
|
|
this.add(this.itemText);
|
|
|
|
if (this.modifierTypeOption.cost) {
|
|
this.itemCostText = addTextObject(0, 45, "", TextStyle.MONEY, {
|
|
align: "center",
|
|
});
|
|
|
|
this.itemCostText.setOrigin(0.5, 0);
|
|
this.itemCostText.setAlpha(0);
|
|
this.add(this.itemCostText);
|
|
|
|
this.updateCostText();
|
|
}
|
|
}
|
|
|
|
show(remainingDuration: number, upgradeCountOffset: number) {
|
|
if (!this.modifierTypeOption.cost) {
|
|
globalScene.tweens.add({
|
|
targets: this.pb,
|
|
y: 0,
|
|
duration: 1250,
|
|
ease: "Bounce.Out",
|
|
});
|
|
|
|
let lastValue = 1;
|
|
let bounceCount = 0;
|
|
let bounce = false;
|
|
|
|
globalScene.tweens.addCounter({
|
|
from: 1,
|
|
to: 0,
|
|
duration: 1250,
|
|
ease: "Bounce.Out",
|
|
onUpdate: t => {
|
|
if (!globalScene) {
|
|
return;
|
|
}
|
|
const value = t.getValue();
|
|
if (!bounce && value > lastValue) {
|
|
globalScene.playSound("se/pb_bounce_1", {
|
|
volume: 1 / ++bounceCount,
|
|
});
|
|
bounce = true;
|
|
} else if (bounce && value < lastValue) {
|
|
bounce = false;
|
|
}
|
|
lastValue = value;
|
|
},
|
|
});
|
|
|
|
for (let u = 0; u < this.modifierTypeOption.upgradeCount; u++) {
|
|
const upgradeIndex = u;
|
|
globalScene.time.delayedCall(
|
|
remainingDuration - 2000 * (this.modifierTypeOption.upgradeCount - (upgradeIndex + 1 + upgradeCountOffset)),
|
|
() => {
|
|
globalScene.playSound("se/upgrade", {
|
|
rate: 1 + 0.25 * upgradeIndex,
|
|
});
|
|
this.pbTint.setPosition(this.pb.x, this.pb.y);
|
|
this.pbTint.setTintFill(0xffffff);
|
|
this.pbTint.setAlpha(0);
|
|
this.pbTint.setVisible(true);
|
|
globalScene.tweens.add({
|
|
targets: this.pbTint,
|
|
alpha: 1,
|
|
duration: 1000,
|
|
ease: "Sine.easeIn",
|
|
onComplete: () => {
|
|
this.pb.setTexture(
|
|
"pb",
|
|
this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount + (upgradeIndex + 1)),
|
|
);
|
|
globalScene.tweens.add({
|
|
targets: this.pbTint,
|
|
alpha: 0,
|
|
duration: 750,
|
|
ease: "Sine.easeOut",
|
|
onComplete: () => {
|
|
this.pbTint.setVisible(false);
|
|
},
|
|
});
|
|
},
|
|
});
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
globalScene.time.delayedCall(remainingDuration + 2000, () => {
|
|
if (!globalScene) {
|
|
return;
|
|
}
|
|
|
|
if (!this.modifierTypeOption.cost) {
|
|
this.pb.setTexture("pb", `${this.getPbAtlasKey(0)}_open`);
|
|
globalScene.playSound("se/pb_rel");
|
|
|
|
globalScene.tweens.add({
|
|
targets: this.pb,
|
|
duration: 500,
|
|
delay: 250,
|
|
ease: "Sine.easeIn",
|
|
alpha: 0,
|
|
onComplete: () => this.pb.destroy(),
|
|
});
|
|
}
|
|
|
|
globalScene.tweens.add({
|
|
targets: this.itemContainer,
|
|
duration: 500,
|
|
ease: "Elastic.Out",
|
|
scale: 2,
|
|
alpha: 1,
|
|
});
|
|
if (!this.modifierTypeOption.cost) {
|
|
globalScene.tweens.add({
|
|
targets: this.itemTint,
|
|
alpha: 0,
|
|
duration: 500,
|
|
ease: "Sine.easeIn",
|
|
onComplete: () => this.itemTint.destroy(),
|
|
});
|
|
}
|
|
globalScene.tweens.add({
|
|
targets: this.itemText,
|
|
duration: 500,
|
|
alpha: 1,
|
|
y: 25,
|
|
ease: "Cubic.easeInOut",
|
|
});
|
|
if (this.itemCostText) {
|
|
globalScene.tweens.add({
|
|
targets: this.itemCostText,
|
|
duration: 500,
|
|
alpha: 1,
|
|
y: 35,
|
|
ease: "Cubic.easeInOut",
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
getPbAtlasKey(tierOffset = 0) {
|
|
return getPokeballAtlasKey((this.modifierTypeOption.type?.tier! + tierOffset) as number as PokeballType); // TODO: is this bang correct?
|
|
}
|
|
|
|
updateCostText(): void {
|
|
const cost = Overrides.WAIVE_ROLL_FEE_OVERRIDE ? 0 : this.modifierTypeOption.cost;
|
|
const textStyle = cost <= globalScene.money ? TextStyle.MONEY : TextStyle.PARTY_RED;
|
|
|
|
const formattedMoney = formatMoney(globalScene.moneyFormat, cost);
|
|
|
|
this.itemCostText.setText(i18next.t("modifierSelectUiHandler:itemCost", { formattedMoney }));
|
|
this.itemCostText.setColor(getTextColor(textStyle, false, globalScene.uiTheme));
|
|
this.itemCostText.setShadowColor(getTextColor(textStyle, true, globalScene.uiTheme));
|
|
}
|
|
}
|