Add accounts, login, and registration
|
@ -27,6 +27,10 @@ body {
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
#app > div:first-child {
|
||||
transform-origin: top !important;
|
||||
}
|
||||
|
||||
#touchControls:not(.visible) {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -618,9 +618,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz",
|
||||
"integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
|
@ -629,9 +629,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/axios-cache-interceptor": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.3.3.tgz",
|
||||
"integrity": "sha512-i+AU7qIf3trytWR8KmQpGbNm47T9OllnM3j/jtbybG2CN0eSmPL7Szo0VzJZPtuoKXVINvRozieXDLCUREW5kw==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.4.1.tgz",
|
||||
"integrity": "sha512-Ax4+PiGfNxpQvyF00t55nFzWoVnqW7slKCg9va6dbqiuAGIxRE8r1uMzunw8TKJ5iwLivFqAb0EeiLeUCxuZIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cache-parser": "1.2.4",
|
||||
|
|
After Width: | Height: | Size: 232 B |
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 248 B |
After Width: | Height: | Size: 229 B |
After Width: | Height: | Size: 234 B |
After Width: | Height: | Size: 218 B |
After Width: | Height: | Size: 243 B |
After Width: | Height: | Size: 225 B |
|
@ -0,0 +1,24 @@
|
|||
import { bypassLogin } from "./battle-scene";
|
||||
import * as Utils from "./utils";
|
||||
|
||||
export let loggedInUser = null;
|
||||
|
||||
export function updateUserInfo(): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (bypassLogin) {
|
||||
loggedInUser = { username: 'Guest' };
|
||||
return resolve(true);
|
||||
}
|
||||
Utils.apiFetch('account/info').then(response => {
|
||||
if (!response.ok) {
|
||||
loggedInUser = null;
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
return response.json();
|
||||
}).then(jsonResponse => {
|
||||
loggedInUser = jsonResponse;
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -8,7 +8,7 @@ export class BattlePhase {
|
|||
}
|
||||
|
||||
start() {
|
||||
console.log(`%cStart Phase ${this.constructor.name}`, 'color:green;')
|
||||
console.log(`%cStart Phase ${this.constructor.name}`, 'color:green;');
|
||||
}
|
||||
|
||||
end() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import BattleScene, { startingLevel, startingWave } from "./battle-scene";
|
||||
import BattleScene, { bypassLogin, startingLevel, startingWave } from "./battle-scene";
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./pokemon";
|
||||
import * as Utils from './utils';
|
||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveCategory, MoveEffectAttr, MoveFlags, Moves, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr } from "./data/move";
|
||||
|
@ -36,6 +36,55 @@ import { TrainerType, trainerConfigs } from "./data/trainer-type";
|
|||
import { EggHatchPhase } from "./egg-hatch-phase";
|
||||
import { Egg } from "./data/egg";
|
||||
import { vouchers } from "./system/voucher";
|
||||
import { updateUserInfo } from "./account";
|
||||
|
||||
export class LoginPhase extends BattlePhase {
|
||||
private showText: boolean;
|
||||
|
||||
constructor(scene: BattleScene, showText?: boolean) {
|
||||
super(scene);
|
||||
|
||||
this.showText = showText === undefined || !!showText;
|
||||
}
|
||||
|
||||
start(): void {
|
||||
super.start();
|
||||
|
||||
this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] });
|
||||
Utils.executeIf(bypassLogin || !!Utils.getCookie(Utils.sessionIdKey), updateUserInfo).then(success => {
|
||||
if (!success) {
|
||||
if (this.showText)
|
||||
this.scene.ui.showText('Log in or create an account to start. No email required!');
|
||||
|
||||
this.scene.playSound('menu_open');
|
||||
|
||||
this.scene.ui.setMode(Mode.LOGIN_FORM, {
|
||||
buttonActions: [
|
||||
() => {
|
||||
this.scene.ui.playSelect();
|
||||
this.end();
|
||||
}, () => {
|
||||
this.scene.playSound('menu_open');
|
||||
this.scene.ui.setMode(Mode.REGISTRATION_FORM, {
|
||||
buttonActions: [
|
||||
() => {
|
||||
this.scene.ui.playSelect();
|
||||
this.end();
|
||||
}, () => {
|
||||
this.scene.unshiftPhase(new LoginPhase(this.scene, false))
|
||||
this.end();
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
]
|
||||
});
|
||||
return null;
|
||||
} else
|
||||
this.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class CheckLoadPhase extends BattlePhase {
|
||||
private loaded: boolean;
|
||||
|
@ -47,6 +96,8 @@ export class CheckLoadPhase extends BattlePhase {
|
|||
}
|
||||
|
||||
start(): void {
|
||||
super.start();
|
||||
|
||||
if (!this.scene.gameData.hasSession())
|
||||
return this.end();
|
||||
|
||||
|
@ -312,10 +363,13 @@ export class EncounterPhase extends BattlePhase {
|
|||
|
||||
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
if (!this.loaded) {
|
||||
this.scene.gameData.saveSession(this.scene);
|
||||
this.scene.gameData.saveSystem();
|
||||
}
|
||||
this.doEncounter();
|
||||
this.scene.gameData.saveSystem().then(success => {
|
||||
if (!success)
|
||||
return this.scene.reset(true);
|
||||
this.scene.gameData.saveSession(this.scene, true).then(() => this.doEncounter());
|
||||
});
|
||||
} else
|
||||
this.doEncounter();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -2531,15 +2585,19 @@ export class UnlockPhase extends BattlePhase {
|
|||
start(): void {
|
||||
this.scene.time.delayedCall(2000, () => {
|
||||
this.scene.gameData.unlocks[this.unlockable] = true;
|
||||
this.scene.gameData.saveSystem();
|
||||
this.scene.playSoundWithoutBgm('level_up_fanfare');
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.arenaBg.setVisible(false);
|
||||
this.scene.ui.fadeIn(250).then(() => {
|
||||
this.scene.ui.showText(`${getUnlockableName(this.unlockable)}\nhas been unlocked.`, null, () => {
|
||||
this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true));
|
||||
this.end();
|
||||
}, null, true, 1500);
|
||||
this.scene.gameData.saveSystem().then(success => {
|
||||
if (success) {
|
||||
this.scene.playSoundWithoutBgm('level_up_fanfare');
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.arenaBg.setVisible(false);
|
||||
this.scene.ui.fadeIn(250).then(() => {
|
||||
this.scene.ui.showText(`${getUnlockableName(this.unlockable)}\nhas been unlocked.`, null, () => {
|
||||
this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true));
|
||||
this.end();
|
||||
}, null, true, 1500);
|
||||
});
|
||||
} else
|
||||
this.scene.reset(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Phaser from 'phaser';
|
||||
import { Biome } from './data/biome';
|
||||
import UI, { Mode } from './ui/ui';
|
||||
import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, LevelCapPhase, TestMessagePhase, ShowTrainerPhase, TrainerMessageTestPhase } from './battle-phases';
|
||||
import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, LevelCapPhase, TestMessagePhase, ShowTrainerPhase, TrainerMessageTestPhase, LoginPhase } from './battle-phases';
|
||||
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
|
||||
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
|
||||
import * as Utils from './utils';
|
||||
|
@ -39,9 +39,12 @@ import { Achv, ModifierAchv, achvs } from './system/achv';
|
|||
import { GachaType } from './data/egg';
|
||||
import { Voucher, vouchers } from './system/voucher';
|
||||
import { Gender } from './data/gender';
|
||||
import UIPlugin from 'phaser3-rex-plugins/templates/ui/ui-plugin';
|
||||
import { WindowVariant, getWindowVariantSuffix } from './ui/window';
|
||||
|
||||
const enableAuto = true;
|
||||
const quickStart = false;
|
||||
export const bypassLogin = false;
|
||||
export const startingLevel = 5;
|
||||
export const startingWave = 1;
|
||||
export const startingBiome = Biome.TOWN;
|
||||
|
@ -52,6 +55,7 @@ export enum Button {
|
|||
DOWN,
|
||||
LEFT,
|
||||
RIGHT,
|
||||
SUBMIT,
|
||||
ACTION,
|
||||
CANCEL,
|
||||
MENU,
|
||||
|
@ -72,6 +76,8 @@ export interface PokeballCounts {
|
|||
export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound | Phaser.Sound.NoAudioSound;
|
||||
|
||||
export default class BattleScene extends Phaser.Scene {
|
||||
public rexUI: UIPlugin;
|
||||
|
||||
public auto: boolean;
|
||||
public masterVolume: number = 0.5;
|
||||
public bgmVolume: number = 1;
|
||||
|
@ -192,8 +198,10 @@ export default class BattleScene extends Phaser.Scene {
|
|||
this.loadImage('command_fight_labels', 'ui');
|
||||
this.loadAtlas('prompt', 'ui');
|
||||
this.loadImage('cursor', 'ui');
|
||||
for (let w = 1; w <= 4; w++)
|
||||
this.loadImage(`window_${w}`, 'ui/windows');
|
||||
for (let wv of Utils.getEnumValues(WindowVariant)) {
|
||||
for (let w = 1; w <= 4; w++)
|
||||
this.loadImage(`window_${w}${getWindowVariantSuffix(wv)}`, 'ui/windows');
|
||||
}
|
||||
this.loadImage('namebox', 'ui');
|
||||
this.loadImage('pbinfo_player', 'ui');
|
||||
this.loadImage('pbinfo_player_mini', 'ui');
|
||||
|
@ -376,7 +384,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
|
||||
populateAnims();
|
||||
|
||||
//this.load.plugin('rexfilechooserplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexfilechooserplugin.min.js', true);
|
||||
this.load.plugin('rextexteditplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rextexteditplugin.min.js', true);
|
||||
}
|
||||
|
||||
create() {
|
||||
|
@ -512,6 +520,8 @@ export default class BattleScene extends Phaser.Scene {
|
|||
this.reset();
|
||||
|
||||
if (this.quickStart) {
|
||||
this.newBattle();
|
||||
|
||||
for (let s = 0; s < 3; s++) {
|
||||
const playerSpecies = this.randomSpecies(startingWave, startingLevel);
|
||||
const playerPokemon = new PlayerPokemon(this, playerSpecies, startingLevel, 0, 0);
|
||||
|
@ -537,9 +547,10 @@ export default class BattleScene extends Phaser.Scene {
|
|||
if (enableAuto)
|
||||
initAutoPlay.apply(this);
|
||||
|
||||
if (!this.quickStart)
|
||||
if (!this.quickStart) {
|
||||
this.pushPhase(new LoginPhase(this));
|
||||
this.pushPhase(new CheckLoadPhase(this));
|
||||
else
|
||||
} else
|
||||
this.pushPhase(new EncounterPhase(this));
|
||||
|
||||
this.shiftPhase();
|
||||
|
@ -553,6 +564,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
[Button.DOWN]: [keyCodes.DOWN, keyCodes.S],
|
||||
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
|
||||
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
|
||||
[Button.SUBMIT]: [keyCodes.ENTER],
|
||||
[Button.ACTION]: [keyCodes.ENTER, keyCodes.SPACE, keyCodes.Z],
|
||||
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X],
|
||||
[Button.MENU]: [keyCodes.ESC, keyCodes.M],
|
||||
|
@ -571,7 +583,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
const keys: Phaser.Input.Keyboard.Key[] = [];
|
||||
if (keyConfig.hasOwnProperty(b)) {
|
||||
for (let k of keyConfig[b])
|
||||
keys.push(this.input.keyboard.addKey(k));
|
||||
keys.push(this.input.keyboard.addKey(k, false));
|
||||
mobileKeyConfig[Button[b]] = keys[0];
|
||||
}
|
||||
this.buttonKeys[b] = keys;
|
||||
|
@ -620,7 +632,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
return findInParty(this.getParty()) || findInParty(this.getEnemyParty());
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
reset(clearScene?: boolean): void {
|
||||
this.seed = Utils.randomString(16);
|
||||
console.log('Seed:', this.seed);
|
||||
|
||||
|
@ -658,6 +670,23 @@ export default class BattleScene extends Phaser.Scene {
|
|||
|
||||
this.trainer.setTexture('trainer_m');
|
||||
this.trainer.setPosition(406, 132);
|
||||
|
||||
if (clearScene) {
|
||||
this.fadeOutBgm(250, false);
|
||||
this.tweens.add({
|
||||
targets: [ this.uiContainer ],
|
||||
alpha: 0,
|
||||
duration: 250,
|
||||
ease: 'Sine.easeInOut',
|
||||
onComplete: () => {
|
||||
this.clearPhaseQueue();
|
||||
|
||||
this.children.removeAll(true);
|
||||
this.game.domContainer.innerHTML = '';
|
||||
this.launchBattle();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle {
|
||||
|
@ -960,6 +989,8 @@ export default class BattleScene extends Phaser.Scene {
|
|||
} else if (this.isButtonPressed(Button.RIGHT)) {
|
||||
inputSuccess = this.ui.processInput(Button.RIGHT);
|
||||
vibrationLength = 5;
|
||||
} else if (this.isButtonPressed(Button.SUBMIT)) {
|
||||
inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION);
|
||||
} else if (this.isButtonPressed(Button.ACTION))
|
||||
inputSuccess = this.ui.processInput(Button.ACTION);
|
||||
else if (this.isButtonPressed(Button.CANCEL)) {
|
||||
|
@ -1117,7 +1148,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
|
||||
fadeOutBgm(duration?: integer, destroy?: boolean): boolean {
|
||||
if (!this.bgm)
|
||||
return;
|
||||
return false;
|
||||
if (!duration)
|
||||
duration = 500;
|
||||
if (destroy === undefined)
|
||||
|
|
22
src/main.ts
|
@ -2,7 +2,9 @@ import Phaser from 'phaser';
|
|||
import BattleScene from './battle-scene';
|
||||
import InvertPostFX from './pipelines/invert';
|
||||
import { version } from '../package.json';
|
||||
import UIPlugin from 'phaser3-rex-plugins/templates/ui/ui-plugin';
|
||||
import BBCodeTextPlugin from 'phaser3-rex-plugins/plugins/bbcodetext-plugin';
|
||||
import InputTextPlugin from 'phaser3-rex-plugins/plugins/inputtext-plugin.js';
|
||||
|
||||
const config: Phaser.Types.Core.GameConfig = {
|
||||
type: Phaser.WEBGL,
|
||||
|
@ -14,11 +16,31 @@ const config: Phaser.Types.Core.GameConfig = {
|
|||
},
|
||||
plugins: {
|
||||
global: [{
|
||||
key: 'rexInputTextPlugin',
|
||||
plugin: InputTextPlugin,
|
||||
start: true
|
||||
}, {
|
||||
key: 'rexBBCodeTextPlugin',
|
||||
plugin: BBCodeTextPlugin,
|
||||
start: true
|
||||
}],
|
||||
scene: [{
|
||||
key: 'rexUI',
|
||||
plugin: UIPlugin,
|
||||
mapping: 'rexUI'
|
||||
}]
|
||||
},
|
||||
input: {
|
||||
mouse: {
|
||||
target: 'app'
|
||||
},
|
||||
touch: {
|
||||
target: 'app'
|
||||
},
|
||||
},
|
||||
dom: {
|
||||
createContainer: true
|
||||
},
|
||||
pixelArt: true,
|
||||
pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig,
|
||||
scene: [ BattleScene ],
|
||||
|
|
|
@ -19,6 +19,7 @@ import { Egg } from "../data/egg";
|
|||
import { VoucherType, vouchers } from "./voucher";
|
||||
import { AES, enc } from "crypto-js";
|
||||
import { Mode } from "../ui/ui";
|
||||
import { updateUserInfo } from "../account";
|
||||
|
||||
const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary
|
||||
|
||||
|
@ -155,29 +156,35 @@ export class GameData {
|
|||
this.loadSystem();
|
||||
}
|
||||
|
||||
public saveSystem(): boolean {
|
||||
if (this.scene.quickStart)
|
||||
return false;
|
||||
|
||||
const data: SystemSaveData = {
|
||||
trainerId: this.trainerId,
|
||||
secretId: this.secretId,
|
||||
dexData: this.dexData,
|
||||
unlocks: this.unlocks,
|
||||
achvUnlocks: this.achvUnlocks,
|
||||
voucherUnlocks: this.voucherUnlocks,
|
||||
voucherCounts: this.voucherCounts,
|
||||
eggs: this.eggs.map(e => new EggData(e)),
|
||||
gameVersion: this.scene.game.config.gameVersion,
|
||||
timestamp: new Date().getTime()
|
||||
};
|
||||
public saveSystem(): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (this.scene.quickStart)
|
||||
return resolve(true);
|
||||
|
||||
localStorage.setItem('data_bak', localStorage.getItem('data'));
|
||||
|
||||
const maxIntAttrValue = Math.pow(2, 31);
|
||||
localStorage.setItem('data', btoa(JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v)));
|
||||
|
||||
return true;
|
||||
updateUserInfo().then(success => {
|
||||
if (!success)
|
||||
return resolve(false);
|
||||
const data: SystemSaveData = {
|
||||
trainerId: this.trainerId,
|
||||
secretId: this.secretId,
|
||||
dexData: this.dexData,
|
||||
unlocks: this.unlocks,
|
||||
achvUnlocks: this.achvUnlocks,
|
||||
voucherUnlocks: this.voucherUnlocks,
|
||||
voucherCounts: this.voucherCounts,
|
||||
eggs: this.eggs.map(e => new EggData(e)),
|
||||
gameVersion: this.scene.game.config.gameVersion,
|
||||
timestamp: new Date().getTime()
|
||||
};
|
||||
|
||||
localStorage.setItem('data_bak', localStorage.getItem('data'));
|
||||
|
||||
const maxIntAttrValue = Math.pow(2, 31);
|
||||
localStorage.setItem('data', btoa(JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v)));
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private loadSystem(): boolean {
|
||||
|
@ -279,29 +286,36 @@ export class GameData {
|
|||
setSetting(this.scene, setting as Setting, settings[setting]);
|
||||
}
|
||||
|
||||
saveSession(scene: BattleScene): boolean {
|
||||
const sessionData = {
|
||||
seed: scene.seed,
|
||||
gameMode: scene.gameMode,
|
||||
party: scene.getParty().map(p => new PokemonData(p)),
|
||||
enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)),
|
||||
modifiers: scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)),
|
||||
enemyModifiers: scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)),
|
||||
arena: new ArenaData(scene.arena),
|
||||
pokeballCounts: scene.pokeballCounts,
|
||||
money: scene.money,
|
||||
waveIndex: scene.currentBattle.waveIndex,
|
||||
battleType: scene.currentBattle.battleType,
|
||||
trainer: scene.currentBattle.battleType == BattleType.TRAINER ? new TrainerData(scene.currentBattle.trainer) : null,
|
||||
gameVersion: scene.game.config.gameVersion,
|
||||
timestamp: new Date().getTime()
|
||||
} as SessionSaveData;
|
||||
saveSession(scene: BattleScene, skipVerification?: boolean): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
Utils.executeIf(!skipVerification, updateUserInfo).then(success => {
|
||||
if (success !== null && !success)
|
||||
return resolve(false);
|
||||
|
||||
localStorage.setItem('sessionData', btoa(JSON.stringify(sessionData)));
|
||||
const sessionData = {
|
||||
seed: scene.seed,
|
||||
gameMode: scene.gameMode,
|
||||
party: scene.getParty().map(p => new PokemonData(p)),
|
||||
enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)),
|
||||
modifiers: scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)),
|
||||
enemyModifiers: scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)),
|
||||
arena: new ArenaData(scene.arena),
|
||||
pokeballCounts: scene.pokeballCounts,
|
||||
money: scene.money,
|
||||
waveIndex: scene.currentBattle.waveIndex,
|
||||
battleType: scene.currentBattle.battleType,
|
||||
trainer: scene.currentBattle.battleType == BattleType.TRAINER ? new TrainerData(scene.currentBattle.trainer) : null,
|
||||
gameVersion: scene.game.config.gameVersion,
|
||||
timestamp: new Date().getTime()
|
||||
} as SessionSaveData;
|
||||
|
||||
console.debug('Session data saved');
|
||||
localStorage.setItem('sessionData', btoa(JSON.stringify(sessionData)));
|
||||
|
||||
return true;
|
||||
console.debug('Session data saved');
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
hasSession() {
|
||||
|
|
|
@ -54,7 +54,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
|||
this.optionSelectText.setPositionRelative(this.optionSelectBg, 16, 9);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
const options = this.getOptions();
|
||||
|
||||
if (args.length >= options.length && args.slice(0, options.length).filter(a => a instanceof Function).length === options.length) {
|
||||
|
@ -64,7 +64,11 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
|||
|
||||
this.optionSelectContainer.setVisible(true);
|
||||
this.setCursor(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -105,7 +105,7 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
this.achvsContainer.setVisible(false);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
const achvUnlocks = this.scene.gameData.achvUnlocks;
|
||||
|
@ -129,6 +129,8 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
this.getUi().moveTo(this.achvsContainer, this.getUi().length - 1);
|
||||
|
||||
this.getUi().hideTooltip();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected showAchv(achv: Achv) {
|
||||
|
|
|
@ -48,12 +48,14 @@ export default class BallUiHandler extends UiHandler {
|
|||
this.setCursor(0);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.updateCounts();
|
||||
this.pokeballSelectContainer.setVisible(true);
|
||||
this.setCursor(this.cursor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -120,12 +120,14 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||
this.levelUpStatsValuesContent = levelUpStatsValuesContent;
|
||||
}
|
||||
|
||||
show(args: any[]): void {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.commandWindow.setVisible(false);
|
||||
this.movesWindowContainer.setVisible(false);
|
||||
this.message.setWordWrapWidth(1780);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class BiomeSelectUiHandler extends UiHandler {
|
|||
this.biomeSelectContainer.add(this.biomesText);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (args.length >= 2 && typeof(args[0]) === 'number' && args[1] instanceof Function) {
|
||||
super.show(args);
|
||||
|
||||
|
@ -59,6 +59,8 @@ export default class BiomeSelectUiHandler extends UiHandler {
|
|||
this.biomeSelectContainer.setVisible(true);
|
||||
this.setCursor(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -37,7 +37,7 @@ export default class CommandUiHandler extends UiHandler {
|
|||
}
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.fieldIndex = args.length ? args[0] as integer : 0;
|
||||
|
@ -50,6 +50,8 @@ export default class CommandUiHandler extends UiHandler {
|
|||
messageHandler.message.setWordWrapWidth(1110);
|
||||
messageHandler.showText(`What will\n${(this.scene.getCurrentPhase() as CommandPhase).getPokemon().name} do?`, 0);
|
||||
this.setCursor(this.getCursor());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -22,7 +22,7 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
|
|||
return [ 'Yes', 'No' ];
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (args.length >= 2 && args[0] instanceof Function && args[1] instanceof Function) {
|
||||
super.show(args);
|
||||
|
||||
|
@ -33,7 +33,11 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
|
|||
this.optionSelectContainer.x = (this.scene.game.canvas.width / 6) - 1 + xOffset;
|
||||
|
||||
this.setCursor(this.switchCheck ? this.switchCheckCursor : 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setCursor(cursor: integer): boolean {
|
||||
|
|
|
@ -219,7 +219,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||
this.setCursor(0);
|
||||
}
|
||||
|
||||
show(args: any[]): void {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.getUi().showText(defaultText, 0);
|
||||
|
@ -232,6 +232,8 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||
this.updateVoucherCounts();
|
||||
|
||||
this.eggGachaContainer.setVisible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getDelayValue(delay: integer) {
|
||||
|
@ -240,10 +242,90 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||
return Utils.fixedInt(delay);
|
||||
}
|
||||
|
||||
pull(pullCount?: integer, count?: integer, eggs?: Egg[]) {
|
||||
pull(pullCount?: integer, count?: integer, eggs?: Egg[]): void {
|
||||
this.eggGachaOptionsContainer.setVisible(false);
|
||||
this.setTransitioning(true);
|
||||
|
||||
const doPull = () => {
|
||||
if (this.transitionCancelled)
|
||||
return this.showSummary(eggs);
|
||||
|
||||
const egg = this.scene.add.sprite(127, 75, 'egg', `egg_${eggs[count].getKey()}`);
|
||||
egg.setScale(0.5);
|
||||
|
||||
this.gachaContainers[this.gachaCursor].add(egg);
|
||||
this.gachaContainers[this.gachaCursor].moveTo(egg, 1);
|
||||
|
||||
const doPullAnim = () => {
|
||||
this.scene.playSound('gacha_running', { loop: true });
|
||||
this.scene.time.delayedCall(this.getDelayValue(count ? 500 : 1250), () => {
|
||||
this.scene.playSound('gacha_dispense');
|
||||
this.scene.time.delayedCall(this.getDelayValue(750), () => {
|
||||
this.scene.sound.stopByKey('gacha_running');
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
duration: this.getDelayValue(350),
|
||||
y: 95,
|
||||
ease: 'Bounce.easeOut',
|
||||
onComplete: () => {
|
||||
this.scene.time.delayedCall(this.getDelayValue(125), () => {
|
||||
this.scene.playSound('pb_catch');
|
||||
this.gachaHatches[this.gachaCursor].play('open');
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
duration: this.getDelayValue(350),
|
||||
scale: 0.75,
|
||||
ease: 'Sine.easeIn'
|
||||
});
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
y: 110,
|
||||
duration: this.getDelayValue(350),
|
||||
ease: 'Back.easeOut',
|
||||
onComplete: () => {
|
||||
this.gachaHatches[this.gachaCursor].play('close');
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
y: 200,
|
||||
duration: this.getDelayValue(350),
|
||||
ease: 'Cubic.easeIn',
|
||||
onComplete: () => {
|
||||
if (++count < pullCount)
|
||||
this.pull(pullCount, count, eggs);
|
||||
else
|
||||
this.showSummary(eggs);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (!count) {
|
||||
this.scene.playSound('gacha_dial');
|
||||
this.scene.tweens.add({
|
||||
targets: this.gachaKnobs[this.gachaCursor],
|
||||
duration: this.getDelayValue(350),
|
||||
angle: 90,
|
||||
ease: 'Cubic.easeInOut',
|
||||
onComplete: () => {
|
||||
this.scene.tweens.add({
|
||||
targets: this.gachaKnobs[this.gachaCursor],
|
||||
duration: this.getDelayValue(350),
|
||||
angle: 0,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
this.scene.time.delayedCall(this.getDelayValue(350), doPullAnim);
|
||||
}
|
||||
});
|
||||
} else
|
||||
doPullAnim();
|
||||
};
|
||||
|
||||
if (!pullCount)
|
||||
pullCount = 1;
|
||||
if (!count)
|
||||
|
@ -270,87 +352,15 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||
this.scene.gameData.eggs.push(egg);
|
||||
}
|
||||
|
||||
this.scene.gameData.saveSystem();
|
||||
}
|
||||
|
||||
if (this.transitionCancelled) {
|
||||
return this.showSummary(eggs);
|
||||
}
|
||||
|
||||
const egg = this.scene.add.sprite(127, 75, 'egg', `egg_${eggs[count].getKey()}`);
|
||||
egg.setScale(0.5);
|
||||
|
||||
this.gachaContainers[this.gachaCursor].add(egg);
|
||||
this.gachaContainers[this.gachaCursor].moveTo(egg, 1);
|
||||
|
||||
const doPullAnim = () => {
|
||||
this.scene.playSound('gacha_running', { loop: true });
|
||||
this.scene.time.delayedCall(this.getDelayValue(count ? 500 : 1250), () => {
|
||||
this.scene.playSound('gacha_dispense');
|
||||
this.scene.time.delayedCall(this.getDelayValue(750), () => {
|
||||
this.scene.sound.stopByKey('gacha_running');
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
duration: this.getDelayValue(350),
|
||||
y: 95,
|
||||
ease: 'Bounce.easeOut',
|
||||
onComplete: () => {
|
||||
this.scene.time.delayedCall(this.getDelayValue(125), () => {
|
||||
this.scene.playSound('pb_catch');
|
||||
this.gachaHatches[this.gachaCursor].play('open');
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
duration: this.getDelayValue(350),
|
||||
scale: 0.75,
|
||||
ease: 'Sine.easeIn'
|
||||
});
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
y: 110,
|
||||
duration: this.getDelayValue(350),
|
||||
ease: 'Back.easeOut',
|
||||
onComplete: () => {
|
||||
this.gachaHatches[this.gachaCursor].play('close');
|
||||
this.scene.tweens.add({
|
||||
targets: egg,
|
||||
y: 200,
|
||||
duration: this.getDelayValue(350),
|
||||
ease: 'Cubic.easeIn',
|
||||
onComplete: () => {
|
||||
if (++count < pullCount)
|
||||
this.pull(pullCount, count, eggs);
|
||||
else
|
||||
this.showSummary(eggs);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (!count) {
|
||||
this.scene.playSound('gacha_dial');
|
||||
this.scene.tweens.add({
|
||||
targets: this.gachaKnobs[this.gachaCursor],
|
||||
duration: this.getDelayValue(350),
|
||||
angle: 90,
|
||||
ease: 'Cubic.easeInOut',
|
||||
onComplete: () => {
|
||||
this.scene.tweens.add({
|
||||
targets: this.gachaKnobs[this.gachaCursor],
|
||||
duration: this.getDelayValue(350),
|
||||
angle: 0,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
this.scene.time.delayedCall(this.getDelayValue(350), doPullAnim);
|
||||
}
|
||||
this.scene.gameData.saveSystem().then(success => {
|
||||
if (!success)
|
||||
return this.scene.reset(true);
|
||||
doPull();
|
||||
});
|
||||
} else
|
||||
doPullAnim();
|
||||
return;
|
||||
}
|
||||
|
||||
doPull();
|
||||
}
|
||||
|
||||
showSummary(eggs: Egg[]): void {
|
||||
|
|
|
@ -21,10 +21,12 @@ export default class EggHatchSceneHandler extends UiHandler {
|
|||
});
|
||||
}
|
||||
|
||||
show(_args: any[]): void {
|
||||
show(_args: any[]): boolean {
|
||||
super.show(_args);
|
||||
|
||||
this.getUi().showText(null, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -88,7 +88,7 @@ export default class EggListUiHandler extends MessageUiHandler {
|
|||
this.cursor = -1;
|
||||
}
|
||||
|
||||
show(args: any[]): void {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.eggListContainer.setVisible(true);
|
||||
|
@ -115,6 +115,8 @@ export default class EggListUiHandler extends MessageUiHandler {
|
|||
}
|
||||
|
||||
this.setCursor(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -19,10 +19,12 @@ export default class EvolutionSceneHandler extends UiHandler {
|
|||
this.scene.fieldUI.add(this.evolutionContainer);
|
||||
}
|
||||
|
||||
show(_args: any[]): void {
|
||||
show(_args: any[]): boolean {
|
||||
super.show(_args);
|
||||
|
||||
this.scene.fieldUI.bringToTop(this.evolutionContainer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class FightUiHandler extends UiHandler {
|
|||
ui.add(this.ppText);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.fieldIndex = args.length ? args[0] as integer : 0;
|
||||
|
@ -46,6 +46,8 @@ export default class FightUiHandler extends UiHandler {
|
|||
messageHandler.movesWindowContainer.setVisible(true);
|
||||
this.setCursor(this.getCursor());
|
||||
this.displayMoves();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
import BattleScene, { Button } from "../battle-scene";
|
||||
import { ModalConfig, ModalUiHandler } from "./modal-ui-handler";
|
||||
import { Mode } from "./ui";
|
||||
import { TextStyle, addTextInputObject, addTextObject, getTextColor } from "./text";
|
||||
import { WindowVariant, addWindow } from "./window";
|
||||
import InputText from "phaser3-rex-plugins/plugins/inputtext";
|
||||
|
||||
export interface FormModalConfig extends ModalConfig {
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export abstract class FormModalUiHandler extends ModalUiHandler {
|
||||
protected editing: boolean;
|
||||
protected inputContainers: Phaser.GameObjects.Container[];
|
||||
protected inputs: InputText[];
|
||||
protected errorMessage: Phaser.GameObjects.Text;
|
||||
protected submitAction: Function;
|
||||
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
|
||||
this.editing = false;
|
||||
this.inputContainers = [];
|
||||
this.inputs = [];
|
||||
}
|
||||
|
||||
abstract getFields(): string[];
|
||||
|
||||
getHeight(config?: ModalConfig): number {
|
||||
return 20 * this.getFields().length + (this.getModalTitle() ? 26 : 0) + ((config as FormModalConfig)?.errorMessage ? 12 : 0) + 28;
|
||||
}
|
||||
|
||||
getReadableErrorMessage(error: string): string {
|
||||
if (error?.indexOf('connection refused') > -1)
|
||||
return 'Could not connect to the server';
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
setup(): void {
|
||||
super.setup();
|
||||
|
||||
const fields = this.getFields();
|
||||
|
||||
const hasTitle = !!this.getModalTitle();
|
||||
|
||||
fields.forEach((field, f) => {
|
||||
const label = addTextObject(this.scene, 10, (hasTitle ? 31 : 5) + 20 * f, field, TextStyle.TOOLTIP_CONTENT);
|
||||
|
||||
this.modalContainer.add(label);
|
||||
|
||||
const inputContainer = this.scene.add.container(70, (hasTitle ? 28 : 2) + 20 * f);
|
||||
|
||||
const inputBg = addWindow(this.scene, 0, 0, 80, 16, false, false, 0, 0, WindowVariant.XTHIN);
|
||||
|
||||
const input = addTextInputObject(this.scene, 4, -2, 440, 116, TextStyle.TOOLTIP_CONTENT, { type: field.indexOf('Password') > -1 ? 'password' : 'text', maxLength: 16 });
|
||||
input.setOrigin(0, 0);
|
||||
|
||||
inputContainer.add(inputBg);
|
||||
inputContainer.add(input);
|
||||
this.modalContainer.add(inputContainer);
|
||||
|
||||
this.inputContainers.push(inputContainer);
|
||||
this.inputs.push(input);
|
||||
});
|
||||
|
||||
this.errorMessage = addTextObject(this.scene, 10, (hasTitle ? 31 : 5) + 20 * (fields.length - 1) + 16, '', TextStyle.TOOLTIP_CONTENT);
|
||||
this.errorMessage.setColor(getTextColor(TextStyle.SUMMARY_RED));
|
||||
this.errorMessage.setShadowColor(getTextColor(TextStyle.SUMMARY_RED, true));
|
||||
this.errorMessage.setVisible(false);
|
||||
this.modalContainer.add(this.errorMessage);
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
if (super.show(args)) {
|
||||
this.inputContainers.map(ic => ic.setVisible(true));
|
||||
|
||||
const config = args[0] as FormModalConfig;
|
||||
|
||||
this.submitAction = config.buttonActions.length
|
||||
? config.buttonActions[0]
|
||||
: null;
|
||||
|
||||
if (this.buttonBgs.length) {
|
||||
this.buttonBgs[0].off('pointerdown');
|
||||
this.buttonBgs[0].on('pointerdown', () => {
|
||||
if (this.submitAction)
|
||||
this.submitAction();
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
if (button === Button.SUBMIT && this.submitAction) {
|
||||
this.submitAction();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
sanitizeInputs(): void {
|
||||
for (let input of this.inputs)
|
||||
input.text = input.text.trim();
|
||||
}
|
||||
|
||||
updateContainer(config?: ModalConfig): void {
|
||||
super.updateContainer(config);
|
||||
|
||||
this.errorMessage.setText(this.getReadableErrorMessage((config as FormModalConfig)?.errorMessage || ''));
|
||||
this.errorMessage.setVisible(!!this.errorMessage.text);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
super.clear();
|
||||
this.modalContainer.setVisible(false);
|
||||
|
||||
this.inputContainers.map(ic => ic.setVisible(false));
|
||||
|
||||
this.submitAction = null;
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ export default class GameModeSelectUiHandler extends AbstractOptionSelectUiHandl
|
|||
return ret;
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (args.length === 2 && args[0] instanceof Function && args[1] instanceof Function) {
|
||||
this.setupOptions();
|
||||
|
||||
|
@ -39,7 +39,11 @@ export default class GameModeSelectUiHandler extends AbstractOptionSelectUiHandl
|
|||
|
||||
this.optionSelectContainer.setVisible(true);
|
||||
this.setCursor(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import BattleScene from "../battle-scene";
|
||||
import { ModalUiHandler } from "./modal-ui-handler";
|
||||
import { addTextObject, TextStyle } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
|
||||
export default class LoadingModalUiHandler extends ModalUiHandler {
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
}
|
||||
|
||||
getModalTitle(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getWidth(): number {
|
||||
return 80;
|
||||
}
|
||||
|
||||
getHeight(): number {
|
||||
return 32;
|
||||
}
|
||||
|
||||
getMargin(): [number, number, number, number] {
|
||||
return [ 0, 0, 48, 0 ];
|
||||
}
|
||||
|
||||
getButtonLabels(): string[] {
|
||||
return [ ];
|
||||
}
|
||||
|
||||
setup(): void {
|
||||
super.setup();
|
||||
|
||||
const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Loading...', TextStyle.WINDOW);
|
||||
label.setOrigin(0.5, 0.5);
|
||||
|
||||
this.modalContainer.add(label);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
import { FormModalUiHandler } from "./form-modal-ui-handler";
|
||||
import { ModalConfig } from "./modal-ui-handler";
|
||||
import * as Utils from "../utils";
|
||||
import { Mode } from "./ui";
|
||||
|
||||
export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||
getModalTitle(config?: ModalConfig): string {
|
||||
return 'Login';
|
||||
}
|
||||
|
||||
getFields(config?: ModalConfig): string[] {
|
||||
return [ 'Username', 'Password' ];
|
||||
}
|
||||
|
||||
getWidth(config?: ModalConfig): number {
|
||||
return 160;
|
||||
}
|
||||
|
||||
getMargin(config?: ModalConfig): [number, number, number, number] {
|
||||
return [ 0, 0, 48, 0 ];
|
||||
}
|
||||
|
||||
getButtonLabels(config?: ModalConfig): string[] {
|
||||
return [ 'Log In', 'Register' ];
|
||||
}
|
||||
|
||||
getReadableErrorMessage(error: string): string {
|
||||
let colonIndex = error?.indexOf(':');
|
||||
if (colonIndex > 0)
|
||||
error = error.slice(0, colonIndex);
|
||||
switch (error) {
|
||||
case 'invalid username':
|
||||
return 'The provided username is invalid';
|
||||
case 'invalid password':
|
||||
return 'The provided password is invalid';
|
||||
case 'account doesn\'t exist':
|
||||
return 'The provided user does not exist';
|
||||
case 'password doesn\'t match':
|
||||
return 'The provided password does not match';
|
||||
}
|
||||
|
||||
return super.getReadableErrorMessage(error);
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
if (super.show(args)) {
|
||||
const config = args[0] as ModalConfig;
|
||||
|
||||
const originalLoginAction = this.submitAction;
|
||||
this.submitAction = (_) => {
|
||||
// Prevent overlapping overrides on action modification
|
||||
this.submitAction = originalLoginAction;
|
||||
this.sanitizeInputs();
|
||||
this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] });
|
||||
const onFail = error => {
|
||||
this.scene.ui.setMode(Mode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() }));
|
||||
this.scene.ui.playError();
|
||||
};
|
||||
if (!this.inputs[0].text)
|
||||
return onFail('Username must not be empty');
|
||||
Utils.apiPost('account/login', JSON.stringify({ username: this.inputs[0].text, password: this.inputs[1].text }))
|
||||
.then(response => {
|
||||
if (!response.ok)
|
||||
return response.text();
|
||||
return response.json();
|
||||
})
|
||||
.then(response => {
|
||||
if (response.hasOwnProperty('token')) {
|
||||
Utils.setCookie(Utils.sessionIdKey, response.token);
|
||||
originalLoginAction();
|
||||
} else
|
||||
onFail(response);
|
||||
});
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
import BattleScene, { Button } from "../battle-scene";
|
||||
import { TextStyle, addTextObject } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
import UiHandler from "./ui-handler";
|
||||
import * as Utils from "../utils";
|
||||
import { addWindow } from "./window";
|
||||
import MessageUiHandler from "./message-ui-handler";
|
||||
import { GameDataType } from "../system/game-data";
|
||||
import { CheckLoadPhase, LoginPhase } from "../battle-phases";
|
||||
|
||||
export enum MenuOptions {
|
||||
SETTINGS,
|
||||
GAME_SETTINGS,
|
||||
ACHIEVEMENTS,
|
||||
VOUCHERS,
|
||||
EGG_LIST,
|
||||
|
@ -16,7 +16,8 @@ export enum MenuOptions {
|
|||
IMPORT_SESSION,
|
||||
EXPORT_SESSION,
|
||||
IMPORT_DATA,
|
||||
EXPORT_DATA
|
||||
EXPORT_DATA,
|
||||
LOG_OUT
|
||||
}
|
||||
|
||||
export default class MenuUiHandler extends MessageUiHandler {
|
||||
|
@ -73,7 +74,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
this.menuContainer.setVisible(false);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.menuContainer.setVisible(true);
|
||||
|
@ -84,6 +85,8 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
this.getUi().hideTooltip();
|
||||
|
||||
this.scene.playSound('menu_open');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
@ -94,7 +97,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
|
||||
if (button === Button.ACTION) {
|
||||
switch (this.cursor as MenuOptions) {
|
||||
case MenuOptions.SETTINGS:
|
||||
case MenuOptions.GAME_SETTINGS:
|
||||
this.scene.ui.setOverlayMode(Mode.SETTINGS);
|
||||
success = true;
|
||||
break;
|
||||
|
@ -129,6 +132,26 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
this.scene.gameData.exportData(this.cursor === MenuOptions.EXPORT_DATA ? GameDataType.SYSTEM : GameDataType.SESSION);
|
||||
success = true;
|
||||
break;
|
||||
case MenuOptions.LOG_OUT:
|
||||
success = true;
|
||||
const doLogout = () => {
|
||||
Utils.apiPost('account/logout').then(res => {
|
||||
if (!res.ok)
|
||||
console.error(`Log out failed (${res.status}: ${res.statusText})`);
|
||||
Utils.setCookie(Utils.sessionIdKey, '');
|
||||
this.scene.reset(true);
|
||||
});
|
||||
};
|
||||
if (this.scene.currentBattle) {
|
||||
this.scene.ui.showText('You will lose any progress since the beginning of the battle. Proceed?', null, () => {
|
||||
this.scene.ui.setOverlayMode(Mode.CONFIRM, doLogout, () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.scene.ui.showText(null, 0);
|
||||
}, false, 98);
|
||||
});
|
||||
} else
|
||||
doLogout();
|
||||
break;
|
||||
}
|
||||
} else if (button === Button.CANCEL) {
|
||||
success = true;
|
||||
|
@ -141,7 +164,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
success = this.setCursor(this.cursor - 1);
|
||||
break;
|
||||
case Button.DOWN:
|
||||
if (this.cursor < Utils.getEnumKeys(MenuOptions).length)
|
||||
if (this.cursor + 1 < Utils.getEnumKeys(MenuOptions).length)
|
||||
success = this.setCursor(this.cursor + 1);
|
||||
break;
|
||||
}
|
||||
|
@ -152,7 +175,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
else if (error)
|
||||
ui.playError();
|
||||
|
||||
return true;
|
||||
return success || error;
|
||||
}
|
||||
|
||||
showText(text: string, delay?: number, callback?: Function, callbackDelay?: number, prompt?: boolean, promptDelay?: number): void {
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
import BattleScene, { Button } from "../battle-scene";
|
||||
import { TextStyle, addTextObject } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
import UiHandler from "./ui-handler";
|
||||
import { WindowVariant, addWindow } from "./window";
|
||||
|
||||
export interface ModalConfig {
|
||||
buttonActions: Function[];
|
||||
}
|
||||
|
||||
export abstract class ModalUiHandler extends UiHandler {
|
||||
protected modalContainer: Phaser.GameObjects.Container;
|
||||
protected modalBg: Phaser.GameObjects.NineSlice;
|
||||
protected titleText: Phaser.GameObjects.Text;
|
||||
protected buttonContainers: Phaser.GameObjects.Container[];
|
||||
protected buttonBgs: Phaser.GameObjects.NineSlice[];
|
||||
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
|
||||
this.buttonContainers = [];
|
||||
this.buttonBgs = [];
|
||||
}
|
||||
|
||||
abstract getModalTitle(config?: ModalConfig): string;
|
||||
|
||||
abstract getWidth(config?: ModalConfig): number;
|
||||
|
||||
abstract getHeight(config?: ModalConfig): number;
|
||||
|
||||
abstract getMargin(config?: ModalConfig): [number, number, number, number];
|
||||
|
||||
abstract getButtonLabels(config?: ModalConfig): string[];
|
||||
|
||||
setup() {
|
||||
const ui = this.getUi();
|
||||
|
||||
this.modalContainer = this.scene.add.container(0, 0);
|
||||
|
||||
this.modalContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains);
|
||||
|
||||
this.modalBg = addWindow(this.scene, 0, 0, 0, 0);
|
||||
|
||||
this.modalContainer.add(this.modalBg);
|
||||
|
||||
this.titleText = addTextObject(this.scene, 0, 4, '', TextStyle.SETTINGS_LABEL);
|
||||
this.titleText.setOrigin(0.5, 0);
|
||||
|
||||
this.modalContainer.add(this.titleText);
|
||||
|
||||
ui.add(this.modalContainer);
|
||||
|
||||
const buttonLabels = this.getButtonLabels();
|
||||
|
||||
for (let label of buttonLabels) {
|
||||
const buttonLabel = addTextObject(this.scene, 0, 8, label, TextStyle.TOOLTIP_CONTENT);
|
||||
buttonLabel.setOrigin(0.5, 0.5);
|
||||
|
||||
const buttonBg = addWindow(this.scene, 0, 0, buttonLabel.getBounds().width + 8, 16, false, false, 0, 0, WindowVariant.THIN);
|
||||
buttonBg.setOrigin(0.5, 0);
|
||||
buttonBg.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonBg.width, buttonBg.height), Phaser.Geom.Rectangle.Contains);
|
||||
|
||||
const buttonContainer = this.scene.add.container(0, 0);
|
||||
|
||||
this.buttonBgs.push(buttonBg);
|
||||
this.buttonContainers.push(buttonContainer);
|
||||
|
||||
buttonContainer.add(buttonBg);
|
||||
buttonContainer.add(buttonLabel);
|
||||
this.modalContainer.add(buttonContainer);
|
||||
}
|
||||
|
||||
this.modalContainer.setVisible(false);
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
if (args.length >= 1 && 'buttonActions' in args[0]) {
|
||||
super.show(args);
|
||||
|
||||
const config = args[0] as ModalConfig;
|
||||
|
||||
this.updateContainer(config);
|
||||
|
||||
this.modalContainer.setVisible(true);
|
||||
|
||||
this.getUi().moveTo(this.modalContainer, this.getUi().length - 1);
|
||||
|
||||
for (let a = 0; a < this.buttonBgs.length; a++) {
|
||||
if (a < this.buttonBgs.length)
|
||||
this.buttonBgs[a].on('pointerdown', (_) => config.buttonActions[a]());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
updateContainer(config?: ModalConfig): void {
|
||||
const [ marginTop, marginRight, marginBottom, marginLeft ] = this.getMargin(config);
|
||||
|
||||
const [ width, height ] = [ this.getWidth(config), this.getHeight(config) ];
|
||||
this.modalContainer.setPosition((((this.scene.game.canvas.width / 6) - (width + (marginRight - marginLeft))) / 2), (((-this.scene.game.canvas.height / 6) - (height + (marginBottom - marginTop))) / 2));
|
||||
|
||||
this.modalBg.setSize(width, height);
|
||||
|
||||
const title = this.getModalTitle(config);
|
||||
|
||||
this.titleText.setText(title);
|
||||
this.titleText.setX(width / 2);
|
||||
this.titleText.setVisible(!!title);
|
||||
|
||||
for (let b = 0; b < this.buttonContainers.length; b++) {
|
||||
const sliceWidth = width / (this.buttonContainers.length + 1);
|
||||
|
||||
this.buttonContainers[b].setPosition(sliceWidth * (b + 1), this.modalBg.height - (this.buttonBgs[b].height + 8));
|
||||
}
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
clear() {
|
||||
super.clear();
|
||||
this.modalContainer.setVisible(false);
|
||||
|
||||
this.buttonBgs.map(bg => bg.off('pointerdown'));
|
||||
}
|
||||
}
|
|
@ -53,17 +53,17 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
|||
this.rerollButtonContainer.add(this.rerollCostText);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (this.active) {
|
||||
if (args.length >= 3) {
|
||||
this.awaitingActionInput = true;
|
||||
this.onActionInput = args[2];
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length !== 4 || !(args[1] instanceof Array) || !args[1].length || !(args[2] instanceof Function))
|
||||
return;
|
||||
return false;
|
||||
|
||||
super.show(args);
|
||||
|
||||
|
@ -135,6 +135,8 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
|||
this.awaitingActionInput = true;
|
||||
this.onActionInput = args[2];
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -21,9 +21,9 @@ export default class OptionSelectUiHandler extends AbstractOptionSelectUiHandler
|
|||
return this.options;
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (args.length < 2 || args.length % 2 === 1)
|
||||
return;
|
||||
return false;
|
||||
|
||||
const optionNames: string[] = [];
|
||||
const optionFuncs: Function[] = [];
|
||||
|
@ -34,6 +34,6 @@ export default class OptionSelectUiHandler extends AbstractOptionSelectUiHandler
|
|||
|
||||
this.setupOptions();
|
||||
|
||||
super.show(optionFuncs);
|
||||
return super.show(optionFuncs);
|
||||
}
|
||||
}
|
|
@ -161,9 +161,9 @@ export default class PartyUiHandler extends MessageUiHandler {
|
|||
this.partySlots = [];
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (!args.length || this.active)
|
||||
return;
|
||||
return false;
|
||||
|
||||
super.show(args);
|
||||
|
||||
|
@ -184,6 +184,8 @@ export default class PartyUiHandler extends MessageUiHandler {
|
|||
this.partyBg.setTexture(`party_bg${this.scene.currentBattle.double ? '_double' : ''}`);
|
||||
this.populatePartySlots();
|
||||
this.setCursor(this.cursor < 6 ? this.cursor : 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import { FormModalUiHandler } from "./form-modal-ui-handler";
|
||||
import { ModalConfig } from "./modal-ui-handler";
|
||||
import * as Utils from "../utils";
|
||||
import { Mode } from "./ui";
|
||||
|
||||
export default class RegistrationFormUiHandler extends FormModalUiHandler {
|
||||
getModalTitle(config?: ModalConfig): string {
|
||||
return 'Register';
|
||||
}
|
||||
|
||||
getFields(config?: ModalConfig): string[] {
|
||||
return [ 'Username', 'Password', 'Confirm Password' ];
|
||||
}
|
||||
|
||||
getWidth(config?: ModalConfig): number {
|
||||
return 160;
|
||||
}
|
||||
|
||||
getMargin(config?: ModalConfig): [number, number, number, number] {
|
||||
return [ 0, 0, 48, 0 ];
|
||||
}
|
||||
|
||||
getButtonLabels(config?: ModalConfig): string[] {
|
||||
return [ 'Register', 'Back to Login' ];
|
||||
}
|
||||
|
||||
getReadableErrorMessage(error: string): string {
|
||||
let colonIndex = error?.indexOf(':');
|
||||
if (colonIndex > 0)
|
||||
error = error.slice(0, colonIndex);
|
||||
switch (error) {
|
||||
case 'invalid username':
|
||||
return 'Username must only contain letters, numbers, or underscores';
|
||||
case 'invalid password':
|
||||
return 'Password must be 6 characters or longer';
|
||||
case 'failed to add account record':
|
||||
return 'The provided username is already in use';
|
||||
}
|
||||
|
||||
return super.getReadableErrorMessage(error);
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
if (super.show(args)) {
|
||||
const config = args[0] as ModalConfig;
|
||||
|
||||
const originalRegistrationAction = this.submitAction;
|
||||
this.submitAction = (_) => {
|
||||
// Prevent overlapping overrides on action modification
|
||||
this.submitAction = originalRegistrationAction;
|
||||
this.sanitizeInputs();
|
||||
this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] });
|
||||
const onFail = error => {
|
||||
this.scene.ui.setMode(Mode.REGISTRATION_FORM, Object.assign(config, { errorMessage: error?.trim() }));
|
||||
this.scene.ui.playError();
|
||||
};
|
||||
if (!this.inputs[0].text)
|
||||
return onFail('Username must not be empty');
|
||||
if (!this.inputs[1].text)
|
||||
return onFail(this.getReadableErrorMessage('invalid password'));
|
||||
if (this.inputs[1].text !== this.inputs[2].text)
|
||||
return onFail('Password must match confirm password');
|
||||
Utils.apiPost('account/register', JSON.stringify({ username: this.inputs[0].text, password: this.inputs[1].text }))
|
||||
.then(response => response.text())
|
||||
.then(response => {
|
||||
if (!response) {
|
||||
Utils.apiPost('account/login', JSON.stringify({ username: this.inputs[0].text, password: this.inputs[1].text }))
|
||||
.then(response => {
|
||||
if (!response.ok)
|
||||
return response.text();
|
||||
return response.json();
|
||||
})
|
||||
.then(response => {
|
||||
if (response.hasOwnProperty('token')) {
|
||||
Utils.setCookie(Utils.sessionIdKey, response.token);
|
||||
originalRegistrationAction();
|
||||
} else
|
||||
onFail(response);
|
||||
});
|
||||
} else
|
||||
onFail(response);
|
||||
});
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ export default class SettingsUiHandler extends UiHandler {
|
|||
this.settingsContainer.setVisible(false);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
const settings: object = localStorage.hasOwnProperty('settings') ? JSON.parse(localStorage.getItem('settings')) : {};
|
||||
|
@ -100,6 +100,8 @@ export default class SettingsUiHandler extends UiHandler {
|
|||
this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
|
||||
|
||||
this.getUi().hideTooltip();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -314,7 +314,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.updateInstructions();
|
||||
}
|
||||
|
||||
show(args: any[]): void {
|
||||
show(args: any[]): boolean {
|
||||
if (args.length >= 1 && args[0] instanceof Function) {
|
||||
super.show(args);
|
||||
|
||||
|
@ -335,7 +335,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.setCursor(0);
|
||||
this.setGenMode(true);
|
||||
this.setCursor(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
|
||||
|
|
|
@ -181,7 +181,7 @@ export default class SummaryUiHandler extends UiHandler {
|
|||
return `summary_${Page[page].toLowerCase()}`;
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
this.pokemon = args[0] as PlayerPokemon;
|
||||
|
@ -237,6 +237,8 @@ export default class SummaryUiHandler extends UiHandler {
|
|||
this.status.setFrame(this.pokemon.status ? StatusEffect[this.pokemon.status.effect].toLowerCase() : 'pokerus');
|
||||
} else
|
||||
this.hideStatus(!fromSummary);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -23,9 +23,9 @@ export default class TargetSelectUiHandler extends UiHandler {
|
|||
|
||||
setup(): void { }
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
if (args.length < 3)
|
||||
return;
|
||||
return false;
|
||||
|
||||
super.show(args);
|
||||
|
||||
|
@ -36,9 +36,11 @@ export default class TargetSelectUiHandler extends UiHandler {
|
|||
this.targets = getMoveTargets(this.scene.getPlayerField()[this.fieldIndex], this.move).targets;
|
||||
|
||||
if (!this.targets.length)
|
||||
return;
|
||||
return false;
|
||||
|
||||
this.setCursor(this.targets.indexOf(this.cursor) > -1 ? this.cursor : this.targets[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
processInput(button: Button): boolean {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText";
|
||||
import InputText from "phaser3-rex-plugins/plugins/inputtext";
|
||||
|
||||
export enum TextStyle {
|
||||
MESSAGE,
|
||||
|
@ -22,7 +23,7 @@ export function addTextObject(scene: Phaser.Scene, x: number, y: number, content
|
|||
const ret = scene.add.text(x, y, content, styleOptions);
|
||||
ret.setScale(0.1666666667);
|
||||
ret.setShadow(shadowSize, shadowSize, shadowColor);
|
||||
if (!styleOptions.lineSpacing)
|
||||
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing)
|
||||
ret.setLineSpacing(5);
|
||||
|
||||
return ret;
|
||||
|
@ -35,13 +36,23 @@ export function addBBCodeTextObject(scene: Phaser.Scene, x: number, y: number, c
|
|||
scene.add.existing(ret);
|
||||
ret.setScale(0.1666666667);
|
||||
ret.setShadow(shadowSize, shadowSize, shadowColor);
|
||||
if (!styleOptions.lineSpacing)
|
||||
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing)
|
||||
ret.setLineSpacing(5);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getTextStyleOptions(style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): [ Phaser.Types.GameObjects.Text.TextStyle, string, integer ] {
|
||||
export function addTextInputObject(scene: Phaser.Scene, x: number, y: number, width: number, height: number, style: TextStyle, extraStyleOptions?: InputText.IConfig): InputText {
|
||||
const [ styleOptions ] = getTextStyleOptions(style, extraStyleOptions);
|
||||
|
||||
const ret = new InputText(scene, x, y, width, height, styleOptions as InputText.IConfig);
|
||||
scene.add.existing(ret);
|
||||
ret.setScale(0.1666666667);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getTextStyleOptions(style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): [ Phaser.Types.GameObjects.Text.TextStyle | InputText.IConfig, string, integer ] {
|
||||
let shadowColor: string;
|
||||
let shadowSize = 6;
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@ export default abstract class UiHandler {
|
|||
|
||||
abstract setup(): void;
|
||||
|
||||
show(_args: any[]): void {
|
||||
show(_args: any[]): boolean {
|
||||
this.active = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract processInput(button: Button): boolean;
|
||||
|
|
33
src/ui/ui.ts
|
@ -24,8 +24,11 @@ import EggHatchSceneHandler from './egg-hatch-scene-handler';
|
|||
import EggListUiHandler from './egg-list-ui-handler';
|
||||
import EggGachaUiHandler from './egg-gacha-ui-handler';
|
||||
import VouchersUiHandler from './vouchers-ui-handler';
|
||||
import VoucherBar from './voucher-bar';
|
||||
import { addWindow } from './window';
|
||||
import LoginFormUiHandler from './login-form-ui-handler';
|
||||
import RegistrationFormUiHandler from './registration-form-ui-handler';
|
||||
import LoadingModalUiHandler from './loading-modal-ui-handler';
|
||||
import * as Utils from "../utils";
|
||||
|
||||
export enum Mode {
|
||||
MESSAGE,
|
||||
|
@ -48,7 +51,10 @@ export enum Mode {
|
|||
ACHIEVEMENTS,
|
||||
VOUCHERS,
|
||||
EGG_LIST,
|
||||
EGG_GACHA
|
||||
EGG_GACHA,
|
||||
LOGIN_FORM,
|
||||
REGISTRATION_FORM,
|
||||
LOADING
|
||||
};
|
||||
|
||||
const transitionModes = [
|
||||
|
@ -68,7 +74,10 @@ const noTransitionModes = [
|
|||
Mode.MENU,
|
||||
Mode.SETTINGS,
|
||||
Mode.ACHIEVEMENTS,
|
||||
Mode.VOUCHERS
|
||||
Mode.VOUCHERS,
|
||||
Mode.LOGIN_FORM,
|
||||
Mode.REGISTRATION_FORM,
|
||||
Mode.LOADING
|
||||
];
|
||||
|
||||
export default class UI extends Phaser.GameObjects.Container {
|
||||
|
@ -77,7 +86,6 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||
private handlers: UiHandler[];
|
||||
private overlay: Phaser.GameObjects.Rectangle;
|
||||
public achvBar: AchvBar;
|
||||
public voucherBar: VoucherBar;
|
||||
|
||||
private tooltipContainer: Phaser.GameObjects.Container;
|
||||
private tooltipBg: Phaser.GameObjects.NineSlice;
|
||||
|
@ -112,7 +120,10 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||
new AchvsUiHandler(scene),
|
||||
new VouchersUiHandler(scene),
|
||||
new EggListUiHandler(scene),
|
||||
new EggGachaUiHandler(scene)
|
||||
new EggGachaUiHandler(scene),
|
||||
new LoginFormUiHandler(scene),
|
||||
new RegistrationFormUiHandler(scene),
|
||||
new LoadingModalUiHandler(scene)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -135,7 +146,7 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||
this.tooltipContainer = this.scene.add.container(0, 0);
|
||||
this.tooltipContainer.setVisible(false);
|
||||
|
||||
this.tooltipBg = addWindow(this.scene, 0, 0, 128, 31);
|
||||
this.tooltipBg = addWindow(this.scene as BattleScene, 0, 0, 128, 31);
|
||||
this.tooltipBg.setOrigin(0, 0);
|
||||
|
||||
this.tooltipTitle = addTextObject(this.scene, 64, 4, '', TextStyle.TOOLTIP_TITLE);
|
||||
|
@ -323,7 +334,7 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||
|
||||
revertMode(): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (!this.modeChain.length)
|
||||
if (!this?.modeChain?.length)
|
||||
return resolve(false);
|
||||
|
||||
const lastMode = this.mode;
|
||||
|
@ -349,4 +360,12 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
revertModes(): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
if (!this?.modeChain?.length)
|
||||
return resolve();
|
||||
this.revertMode().then(success => Utils.executeIf(success, this.revertModes).then(() => resolve()));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -95,7 +95,7 @@ export default class VouchersUiHandler extends MessageUiHandler {
|
|||
this.vouchersContainer.setVisible(false);
|
||||
}
|
||||
|
||||
show(args: any[]) {
|
||||
show(args: any[]): boolean {
|
||||
super.show(args);
|
||||
|
||||
const voucherUnlocks = this.scene.gameData.voucherUnlocks;
|
||||
|
@ -117,6 +117,8 @@ export default class VouchersUiHandler extends MessageUiHandler {
|
|||
this.getUi().moveTo(this.vouchersContainer, this.getUi().length - 1);
|
||||
|
||||
this.getUi().hideTooltip();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected showVoucher(voucher: Voucher) {
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
import BattleScene from "../battle-scene";
|
||||
|
||||
export enum WindowVariant {
|
||||
NORMAL,
|
||||
THIN,
|
||||
XTHIN
|
||||
}
|
||||
|
||||
export function getWindowVariantSuffix(windowVariant: WindowVariant): string {
|
||||
switch (windowVariant) {
|
||||
case WindowVariant.THIN:
|
||||
return '_thin';
|
||||
case WindowVariant.XTHIN:
|
||||
return '_xthin';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
const windowTypeControlColors = {
|
||||
0: [ '#706880', '#8888c8', '#484868' ],
|
||||
1: [ '#d04028', '#e0a028', '#902008' ],
|
||||
|
@ -7,8 +24,11 @@ const windowTypeControlColors = {
|
|||
3: [ '#2068d0', '#80b0e0', '#104888' ]
|
||||
};
|
||||
|
||||
export function addWindow(scene: BattleScene, x: number, y: number, width: number, height: number, mergeMaskTop?: boolean, mergeMaskLeft?: boolean, maskOffsetX?: number, maskOffsetY?: number): Phaser.GameObjects.NineSlice {
|
||||
const window = scene.add.nineslice(x, y, `window_${scene.windowType}`, null, width, height, 8, 8, 8, 8);
|
||||
export function addWindow(scene: BattleScene, x: number, y: number, width: number, height: number, mergeMaskTop?: boolean, mergeMaskLeft?: boolean, maskOffsetX?: number, maskOffsetY?: number, windowVariant?: WindowVariant): Phaser.GameObjects.NineSlice {
|
||||
if (windowVariant === undefined)
|
||||
windowVariant = WindowVariant.NORMAL;
|
||||
|
||||
const window = scene.add.nineslice(x, y, `window_${scene.windowType}${getWindowVariantSuffix(windowVariant)}`, null, width, height, 6, 6, 6, 6);
|
||||
window.setOrigin(0, 0);
|
||||
|
||||
if (mergeMaskTop || mergeMaskLeft) {
|
||||
|
@ -24,7 +44,7 @@ export function addWindow(scene: BattleScene, x: number, y: number, width: numbe
|
|||
}
|
||||
|
||||
export function updateWindowType(scene: BattleScene, windowTypeIndex: integer): void {
|
||||
const windowObjects: Phaser.GameObjects.NineSlice[] = [];
|
||||
const windowObjects: [Phaser.GameObjects.NineSlice, WindowVariant][] = [];
|
||||
const traverse = (object: any) => {
|
||||
if (object.hasOwnProperty('children')) {
|
||||
const children = object.children as Phaser.GameObjects.DisplayList;
|
||||
|
@ -35,7 +55,7 @@ export function updateWindowType(scene: BattleScene, windowTypeIndex: integer):
|
|||
traverse(child);
|
||||
} else if (object instanceof Phaser.GameObjects.NineSlice) {
|
||||
if (object.texture.key.startsWith('window_'))
|
||||
windowObjects.push(object);
|
||||
windowObjects.push([ object, object.texture.key.endsWith(getWindowVariantSuffix(WindowVariant.XTHIN)) ? WindowVariant.XTHIN : object.texture.key.endsWith(getWindowVariantSuffix(WindowVariant.THIN)) ? WindowVariant.THIN : WindowVariant.NORMAL ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +68,6 @@ export function updateWindowType(scene: BattleScene, windowTypeIndex: integer):
|
|||
|
||||
const windowKey = `window_${windowTypeIndex}`;
|
||||
|
||||
for (let window of windowObjects)
|
||||
window.setTexture(windowKey);
|
||||
for (let [ window, variant ] of windowObjects)
|
||||
window.setTexture(`${windowKey}${getWindowVariantSuffix(variant)}`);
|
||||
}
|
49
src/utils.ts
|
@ -130,6 +130,55 @@ export function executeIf<T>(condition: boolean, promiseFunc: () => Promise<T>):
|
|||
return condition ? promiseFunc() : new Promise<T>(resolve => resolve(null));
|
||||
}
|
||||
|
||||
export const sessionIdKey = 'pokerogue_sessionId';
|
||||
export const isLocal = window.location.hostname === 'localhost';
|
||||
export const serverUrl = isLocal ? 'http://localhost:8001' : '';
|
||||
export const apiUrl = isLocal ? serverUrl : 'api';
|
||||
|
||||
export function setCookie(cName: string, cValue: string): void {
|
||||
document.cookie = `${cName}=${cValue};SameSite=Strict;path=/`;
|
||||
}
|
||||
|
||||
export function getCookie(cName: string): string {
|
||||
const name = `${cName}=`;
|
||||
const ca = document.cookie.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) === ' ')
|
||||
c = c.substring(1);
|
||||
if (c.indexOf(name) === 0)
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function apiFetch(path: string): Promise<Response> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const sId = getCookie(sessionIdKey);
|
||||
const headers = sId ? { 'Authorization': sId } : {};
|
||||
fetch(`${apiUrl}/${path}`, { headers: headers })
|
||||
.then(response => resolve(response))
|
||||
.catch(err => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
export function apiPost(path: string, data?: any, contentType?: string): Promise<Response> {
|
||||
if (!contentType)
|
||||
contentType = 'application/json';
|
||||
return new Promise((resolve, reject) => {
|
||||
const headers = {
|
||||
'Accept': contentType,
|
||||
'Content-Type': contentType,
|
||||
};
|
||||
const sId = getCookie(sessionIdKey);
|
||||
if (sId)
|
||||
headers['Authorization'] = sId;
|
||||
fetch(`${apiUrl}/${path}`, { method: 'POST', headers: headers, body: data })
|
||||
.then(response => resolve(response))
|
||||
.catch(err => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
export class BooleanHolder {
|
||||
public value: boolean;
|
||||
|
||||
|
|