diff --git a/public/images/trainer/guzma.png b/public/images/trainer/guzma.png index 97dadfbe6fd..6afd6f3b34a 100644 Binary files a/public/images/trainer/guzma.png and b/public/images/trainer/guzma.png differ diff --git a/public/images/trainer/rose.png b/public/images/trainer/rose.png index a79a754e21b..f90da7568d4 100644 Binary files a/public/images/trainer/rose.png and b/public/images/trainer/rose.png differ diff --git a/public/images/trainer/skull_grunt_f.json b/public/images/trainer/skull_grunt_f.json index 4aa3dc313cd..182f9300ad5 100644 --- a/public/images/trainer/skull_grunt_f.json +++ b/public/images/trainer/skull_grunt_f.json @@ -4,8 +4,8 @@ "image": "skull_grunt_f.png", "format": "RGBA8888", "size": { - "w": 69, - "h": 69 + "w": 74, + "h": 74 }, "scale": 1, "frames": [ @@ -14,20 +14,20 @@ "rotated": false, "trimmed": false, "sourceSize": { - "w": 80, - "h": 80 + "w": 31, + "h": 74 }, "spriteSourceSize": { - "x": 27, - "y": 9, - "w": 29, - "h": 69 + "x": 0, + "y": 0, + "w": 31, + "h": 74 }, "frame": { "x": 0, "y": 0, - "w": 29, - "h": 69 + "w": 31, + "h": 74 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:2e44c39efe8e78ec75d9119731b9b1cb:4923b5197ea74a9ed0b861e2408f595b:9035f560a0ab0d45bcc084aba7172990$" + "smartupdate": "$TexturePacker:SmartUpdate:71a1f5b1981674c6e81163ac8ea576c3:a5e612d58e5f0a1489e111212baea09d:dd369353af16e4c5eb6547e129dfac18$" } } diff --git a/public/images/trainer/skull_grunt_f.png b/public/images/trainer/skull_grunt_f.png index e0ab67a33d7..fe7834ba4a8 100644 Binary files a/public/images/trainer/skull_grunt_f.png and b/public/images/trainer/skull_grunt_f.png differ diff --git a/public/images/trainer/skull_grunt_m.json b/public/images/trainer/skull_grunt_m.json index 09d1a528c13..7c728e9d3fc 100644 --- a/public/images/trainer/skull_grunt_m.json +++ b/public/images/trainer/skull_grunt_m.json @@ -4,8 +4,8 @@ "image": "skull_grunt_m.png", "format": "RGBA8888", "size": { - "w": 67, - "h": 67 + "w": 72, + "h": 72 }, "scale": 1, "frames": [ @@ -14,20 +14,20 @@ "rotated": false, "trimmed": false, "sourceSize": { - "w": 80, - "h": 80 + "w": 51, + "h": 72 }, "spriteSourceSize": { - "x": 28, - "y": 11, - "w": 26, - "h": 67 + "x": 0, + "y": 0, + "w": 51, + "h": 72 }, "frame": { "x": 0, "y": 0, - "w": 26, - "h": 67 + "w": 51, + "h": 72 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:688a83ff13a77c6923f038db8c7e5e84:d0ece3ab82602eb0c5003bacc26dbd9f:1ff10b395daf6ebfa377680a6404f816$" + "smartupdate": "$TexturePacker:SmartUpdate:4deb2a68e4d168bb1a40cb5d190a7d1f:be3d7b29f4b544ba51cf907691fef51d:df57ca2c9bf5f80d930306e15a851d4d$" } } diff --git a/public/images/trainer/skull_grunt_m.png b/public/images/trainer/skull_grunt_m.png index 4ceba3dad4f..f2b8acba984 100644 Binary files a/public/images/trainer/skull_grunt_m.png and b/public/images/trainer/skull_grunt_m.png differ diff --git a/src/account.ts b/src/account.ts index 6682e3e0b7c..c6d2f85489a 100644 --- a/src/account.ts +++ b/src/account.ts @@ -6,6 +6,7 @@ export interface UserInfo { lastSessionSlot: integer; discordId: string; googleId: string; + hasAdminRole: boolean; } export let loggedInUser: UserInfo | null = null; @@ -13,13 +14,13 @@ export let loggedInUser: UserInfo | null = null; export const clientSessionId = Utils.randomString(32); export function initLoggedInUser(): void { - loggedInUser = { username: "Guest", lastSessionSlot: -1, discordId: "", googleId: ""}; + loggedInUser = { username: "Guest", lastSessionSlot: -1, discordId: "", googleId: "", hasAdminRole: false }; } export function updateUserInfo(): Promise<[boolean, integer]> { return new Promise<[boolean, integer]>(resolve => { if (bypassLogin) { - loggedInUser = { username: "Guest", lastSessionSlot: -1, discordId: "", googleId: "" }; + loggedInUser = { username: "Guest", lastSessionSlot: -1, discordId: "", googleId: "", hasAdminRole: false}; let lastSessionSlot = -1; for (let s = 0; s < 5; s++) { if (localStorage.getItem(`sessionData${s ? s : ""}_${loggedInUser.username}`)) { diff --git a/src/system/game-data.ts b/src/system/game-data.ts index e7bc85d9037..d10d6288696 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -635,13 +635,13 @@ export class GameData { async saveRunHistory(scene: BattleScene, runEntry : SessionSaveData, isVictory: boolean): Promise { const runHistoryData = await this.getRunHistoryData(scene); // runHistoryData should always return run history or {} empty object - const timestamps = Object.keys(runHistoryData); - const timestampsNo = timestamps.map(Number); + let timestamps = Object.keys(runHistoryData).map(Number); // Arbitrary limit of 25 entries per user --> Can increase or decrease while (timestamps.length >= RUN_HISTORY_LIMIT ) { - const oldestTimestamp = Math.min.apply(Math, timestampsNo); + const oldestTimestamp = (Math.min.apply(Math, timestamps)).toString(); delete runHistoryData[oldestTimestamp]; + timestamps = Object.keys(runHistoryData).map(Number); } const timestamp = (runEntry.timestamp).toString(); diff --git a/src/ui/admin-ui-handler.ts b/src/ui/admin-ui-handler.ts new file mode 100644 index 00000000000..371604c00a2 --- /dev/null +++ b/src/ui/admin-ui-handler.ts @@ -0,0 +1,85 @@ +import BattleScene from "#app/battle-scene.js"; +import { ModalConfig } from "./modal-ui-handler"; +import { Mode } from "./ui"; +import * as Utils from "../utils"; +import { FormModalUiHandler } from "./form-modal-ui-handler"; +import { Button } from "#app/enums/buttons.js"; + +export default class AdminUiHandler extends FormModalUiHandler { + + constructor(scene: BattleScene, mode: Mode | null = null) { + super(scene, mode); + } + + setup(): void { + super.setup(); + } + + getModalTitle(config?: ModalConfig): string { + return "Admin panel"; + } + + getFields(config?: ModalConfig): string[] { + return ["Username", "Discord ID"]; + } + + getWidth(config?: ModalConfig): number { + return 160; + } + + getMargin(config?: ModalConfig): [number, number, number, number] { + return [0, 0, 48, 0]; + } + + getButtonLabels(config?: ModalConfig): string[] { + return ["Link account", "Cancel"]; + } + + processInput(button: Button): boolean { + if (button === Button.SUBMIT && this.submitAction) { + this.submitAction(); + return true; + } + + return false; + } + + show(args: any[]): boolean { + if (super.show(args)) { + const config = args[0] as ModalConfig; + const originalSubmitAction = this.submitAction; + this.submitAction = (_) => { + this.submitAction = originalSubmitAction; + this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + const onFail = error => { + this.scene.ui.setMode(Mode.ADMIN, Object.assign(config, { errorMessage: error?.trim() })); + this.scene.ui.playError(); + }; + if (!this.inputs[0].text) { + return onFail("Username is required"); + } + if (!this.inputs[1].text) { + return onFail("Discord Id is required"); + } + Utils.apiPost("admin/account/discord-link", `username=${encodeURIComponent(this.inputs[0].text)}&discordId=${encodeURIComponent(this.inputs[1].text)}`, "application/x-www-form-urlencoded", true) + .then(response => { + if (!response.ok) { + return response.text(); + } + return response.json(); + }) + .then(response => { + this.scene.ui.setMode(Mode.ADMIN, config); + }); + return false; + }; + return true; + } + return false; + + } + + clear(): void { + super.clear(); + } +} diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 4516e39675c..8c4ea5f6768 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -6,7 +6,7 @@ import { WindowVariant, addWindow } from "./ui-theme"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; import * as Utils from "../utils"; import i18next from "i18next"; -import {Button} from "#enums/buttons"; +import { Button } from "#enums/buttons"; export interface FormModalConfig extends ModalConfig { errorMessage?: string; @@ -60,7 +60,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { const inputBg = addWindow(this.scene, 0, 0, 80, 16, false, false, 0, 0, WindowVariant.XTHIN); const isPassword = field.includes(i18next.t("menu:password")) || field.includes(i18next.t("menu:confirmPassword")); - const input = addTextInputObject(this.scene, 4, -2, 440, 116, TextStyle.TOOLTIP_CONTENT, { type: isPassword ? "password" : "text", maxLength: isPassword ? 64 : 16 }); + const input = addTextInputObject(this.scene, 4, -2, 440, 116, TextStyle.TOOLTIP_CONTENT, { type: isPassword ? "password" : "text", maxLength: isPassword ? 64 : 18 }); input.setOrigin(0, 0); inputContainer.add(inputBg); diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 6fdf98d14a3..0bbfe21e4f9 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -306,16 +306,34 @@ export default class MenuUiHandler extends MessageUiHandler { return true; }, keepOpen: true - }, - { - label: i18next.t("menuUiHandler:cancel"), + }]; + if (!bypassLogin && loggedInUser?.hasAdminRole) { + communityOptions.push({ + label: "Admin", handler: () => { - this.scene.ui.revertMode(); + ui.playSelect(); + ui.setOverlayMode(Mode.ADMIN, { + buttonActions: [ + () => { + ui.revertMode(); + }, + () => { + ui.revertMode(); + } + ] + }); return true; - } + }, + keepOpen: true + }); + } + communityOptions.push({ + label: i18next.t("menuUiHandler:cancel"), + handler: () => { + this.scene.ui.revertMode(); + return true; } - ]; - + }); this.communityConfig = { xOffset: 98, options: communityOptions diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 66c777944d1..e7c4069c16e 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -5,7 +5,7 @@ import { Command } from "./command-ui-handler"; import MessageUiHandler from "./message-ui-handler"; import { Mode } from "./ui"; import * as Utils from "../utils"; -import { PokemonBaseStatModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier } from "../modifier/modifier"; +import { PokemonFormChangeItemModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier } from "../modifier/modifier"; import { allMoves, ForceSwitchOutAttr } from "../data/move"; import { getGenderColor, getGenderSymbol } from "../data/gender"; import { StatusEffect } from "../data/status-effect"; @@ -989,14 +989,8 @@ export default class PartyUiHandler extends MessageUiHandler { optionText.setOrigin(0, 0); /** For every item that has stack bigger than 1, display the current quantity selection */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && this.transferQuantitiesMax[option] > 1) { - const itemModifier = itemModifiers[option]; - - /** Not sure why getMaxHeldItemCount had an error, but it only checks the Pokemon parameter if the modifier is PokemonBaseStatModifier */ - if (itemModifier === undefined || itemModifier instanceof PokemonBaseStatModifier) { - continue; - } - + const itemModifier = itemModifiers[option]; + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && this.transferQuantitiesMax[option] > 1 && !this.transferMode && itemModifier !== undefined && itemModifier.type.name === optionName) { let amountText = ` (${this.transferQuantities[option]})`; /** If the amount held is the maximum, display the count in red */ diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 250a21544dc..5e1c28eda2d 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -23,7 +23,7 @@ import OptionSelectUiHandler from "./settings/option-select-ui-handler"; import EggHatchSceneHandler from "./egg-hatch-scene-handler"; import EggListUiHandler from "./egg-list-ui-handler"; import EggGachaUiHandler from "./egg-gacha-ui-handler"; -import {addWindow} from "./ui-theme"; +import { addWindow } from "./ui-theme"; import LoginFormUiHandler from "./login-form-ui-handler"; import RegistrationFormUiHandler from "./registration-form-ui-handler"; import LoadingModalUiHandler from "./loading-modal-ui-handler"; @@ -46,6 +46,7 @@ import SettingsAudioUiHandler from "./settings/settings-audio-ui-handler"; import { PlayerGender } from "#enums/player-gender"; import BgmBar from "#app/ui/bgm-bar"; import RenameFormUiHandler from "./rename-form-ui-handler"; +import AdminUiHandler from "./admin-ui-handler"; import RunHistoryUiHandler from "./run-history-ui-handler"; import RunInfoUiHandler from "./run-info-ui-handler"; @@ -86,6 +87,7 @@ export enum Mode { OUTDATED, CHALLENGE_SELECT, RENAME_POKEMON, + ADMIN, RUN_HISTORY, RUN_INFO, } @@ -124,7 +126,8 @@ const noTransitionModes = [ Mode.SESSION_RELOAD, Mode.UNAVAILABLE, Mode.OUTDATED, - Mode.RENAME_POKEMON + Mode.RENAME_POKEMON, + Mode.ADMIN, ]; export default class UI extends Phaser.GameObjects.Container { @@ -188,6 +191,7 @@ export default class UI extends Phaser.GameObjects.Container { new RenameFormUiHandler(scene), new RunHistoryUiHandler(scene), new RunInfoUiHandler(scene), + new AdminUiHandler(scene), ]; }