From 879971ae2b5c5485553fee2c852dd0fde7a723de Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Thu, 14 Mar 2024 21:49:49 -0400 Subject: [PATCH] Add ""title"" screen and save data slots --- src/account.ts | 11 ++- src/battle-scene.ts | 6 +- src/battle.ts | 2 +- src/main.ts | 2 + src/phases.ts | 162 +++++++++++++++++++++++----------------- src/system/game-data.ts | 9 ++- src/ui/text.ts | 4 +- src/ui/ui.ts | 3 + 8 files changed, 118 insertions(+), 81 deletions(-) diff --git a/src/account.ts b/src/account.ts index 63eba2215ac..b1f008e5a58 100644 --- a/src/account.ts +++ b/src/account.ts @@ -3,7 +3,7 @@ import * as Utils from "./utils"; export interface UserInfo { username: string; - hasGameSession: boolean; + lastSessionSlot: integer; } export let loggedInUser: UserInfo = null; @@ -11,7 +11,14 @@ export let loggedInUser: UserInfo = null; export function updateUserInfo(): Promise { return new Promise(resolve => { if (bypassLogin) { - loggedInUser = { username: 'Guest', hasGameSession: !!localStorage.getItem('sessionData') }; + let lastSessionSlot = -1; + for (let s = 0; s < 2; s++) { + if (localStorage.getItem(`sessionData${s ? s : ''}`)) { + lastSessionSlot = s; + break; + } + } + loggedInUser = { username: 'Guest', lastSessionSlot: lastSessionSlot }; return resolve(true); } Utils.apiFetch('account/info').then(response => { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 5d6b3d67077..10387def3eb 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,6 +1,6 @@ import Phaser from 'phaser'; import UI, { Mode } from './ui/ui'; -import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, ConsolidateDataPhase, MovePhase } from './phases'; +import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, ConsolidateDataPhase, MovePhase, TitlePhase } from './phases'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon'; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species'; import * as Utils from './utils'; @@ -104,6 +104,7 @@ export default class BattleScene extends Phaser.Scene { public enableVibration: boolean = false; public gameData: GameData; + public sessionSlotId: integer; private phaseQueue: Phase[]; private phaseQueuePrepend: Phase[]; @@ -614,7 +615,7 @@ export default class BattleScene extends Phaser.Scene { this.pushPhase(new LoginPhase(this)); if (!bypassLogin) this.pushPhase(new ConsolidateDataPhase(this)); // TODO: Remove - this.pushPhase(new CheckLoadPhase(this)); + this.pushPhase(new TitlePhase(this)); this.shiftPhase(); }); @@ -1205,6 +1206,7 @@ export default class BattleScene extends Phaser.Scene { case Mode.BIOME_SELECT: case Mode.STARTER_SELECT: case Mode.CONFIRM: + case Mode.OPTION_SELECT: this.ui.setOverlayMode(Mode.MENU); inputSuccess = true; break; diff --git a/src/battle.ts b/src/battle.ts index b3e96d6c20d..441d305bf37 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -6,7 +6,7 @@ import Trainer from "./field/trainer"; import { Species } from "./data/enums/species"; import { Moves } from "./data/enums/moves"; import { TrainerType } from "./data/enums/trainer-type"; -import { GameMode, GameModes } from "./game-mode"; +import { GameMode } from "./game-mode"; import { BattleSpec } from "./enums/battle-spec"; import { PlayerGender } from "./system/game-data"; import { PokemonHeldItemModifier } from "./modifier/modifier"; diff --git a/src/main.ts b/src/main.ts index 3a9242e8f13..befb6abd53a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,7 @@ 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'; +import BBCodeText from 'phaser3-rex-plugins/plugins/bbcodetext'; const config: Phaser.Types.Core.GameConfig = { type: Phaser.WEBGL, @@ -62,6 +63,7 @@ Phaser.GameObjects.Sprite.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative; +BBCodeText.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative; document.fonts.load('16px emerald').then(() => document.fonts.load('10px pkmnems')); diff --git a/src/phases.ts b/src/phases.ts index b1925937fe3..887a65e0658 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -35,7 +35,6 @@ import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; import { BattleSpec } from "./enums/battle-spec"; -import { GameModes } from "./game-mode"; import { Species } from "./data/enums/species"; import { HealAchv, LevelAchv, MoneyAchv, achvs } from "./system/achv"; import { trainerConfigs } from "./data/trainer-config"; @@ -52,6 +51,7 @@ import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-s import { Setting } from "./system/settings"; import { Tutorial, handleTutorial } from "./tutorial"; import { TerrainType } from "./data/terrain"; +import { OptionSelectItem } from "./ui/abstact-option-select-ui-handler"; export class LoginPhase extends Phase { private showText: boolean; @@ -122,6 +122,96 @@ export class LoginPhase extends Phase { } } +export class TitlePhase extends Phase { + private loaded: boolean; + + constructor(scene: BattleScene) { + super(scene); + + this.loaded = false; + } + + start(): void { + super.start(); + + const options: OptionSelectItem[] = []; + if (loggedInUser?.lastSessionSlot > -1) { + options.push({ + label: 'Continue', + handler: () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.gameData.loadSession(this.scene, loggedInUser.lastSessionSlot).then((success: boolean) => { + if (success) { + this.loaded = true; + this.scene.ui.showText('Session loaded successfully.', null, () => this.end()); + } else + this.end(); + }).catch(err => { + console.error(err); + this.scene.ui.showText('Your session data could not be loaded.\nIt may be corrupted. Please reload the page.', null); + }); + } + }); + } + options.push({ + label: 'New Game', + handler: () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + this.scene.sessionSlotId = 0; + this.end(); + } + }, + /*{ + label: 'Load', + handler: () => { + + } + },*/ + /*{ + label: 'Daily Run', + handler: () => { + //this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.showText('This feature is not available yet.\nPlease check back soon!', null, () => this.scene.ui.clearText(), Utils.fixedInt(1000)); + }, + keepOpen: true + }*/); + this.scene.ui.setMode(Mode.OPTION_SELECT, { + options: options + }); + } + + end(): void { + if (!this.loaded) { + this.scene.arena.preloadBgm(); + this.scene.pushPhase(new SelectStarterPhase(this.scene)); + } else + this.scene.playBgm(); + + this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded)); + + if (this.loaded) { + const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted()).length; + + this.scene.pushPhase(new SummonPhase(this.scene, 0)); + if (this.scene.currentBattle.double && availablePartyMembers > 1) + this.scene.pushPhase(new SummonPhase(this.scene, 1)); + if (this.scene.currentBattle.waveIndex > 1 && this.scene.currentBattle.battleType !== BattleType.TRAINER) { + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); + if (this.scene.currentBattle.double && availablePartyMembers > 1) + this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); + } + } + + for (let achv of Object.keys(this.scene.gameData.achvUnlocks)) { + if (vouchers.hasOwnProperty(achv)) + this.scene.validateVoucher(vouchers[achv]); + } + + super.end(); + } +} + // TODO: Remove export class ConsolidateDataPhase extends Phase { start(): void { @@ -169,74 +259,6 @@ export class ConsolidateDataPhase extends Phase { } } -export class CheckLoadPhase extends Phase { - private loaded: boolean; - - constructor(scene: BattleScene) { - super(scene); - - this.loaded = false; - } - - start(): void { - super.start(); - - if (!loggedInUser?.hasGameSession) - return this.end(); - - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText('You currently have a session in progress.\nWould you like to continue where you left off?', null, () => { - this.scene.ui.setMode(Mode.CONFIRM, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.gameData.loadSession(this.scene).then((success: boolean) => { - if (success) { - this.loaded = true; - this.scene.ui.showText('Session loaded successfully.', null, () => this.end()); - } else - this.end(); - }).catch(err => { - console.error(err); - this.scene.ui.showText('Your session data could not be loaded.\nIt may be corrupted. Please reload the page.', null); - }); - }, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - this.end(); - }) - }); - } - - end(): void { - if (!this.loaded) { - this.scene.arena.preloadBgm(); - this.scene.pushPhase(new SelectStarterPhase(this.scene)); - } else - this.scene.playBgm(); - - this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded)); - - if (this.loaded) { - const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted()).length; - - this.scene.pushPhase(new SummonPhase(this.scene, 0)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) - this.scene.pushPhase(new SummonPhase(this.scene, 1)); - if (this.scene.currentBattle.waveIndex > 1 && this.scene.currentBattle.battleType !== BattleType.TRAINER) { - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); - if (this.scene.currentBattle.double && availablePartyMembers > 1) - this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); - } - } - - for (let achv of Object.keys(this.scene.gameData.achvUnlocks)) { - if (vouchers.hasOwnProperty(achv)) - this.scene.validateVoucher(vouchers[achv]); - } - - super.end(); - } -} - export class SelectGenderPhase extends Phase { constructor(scene: BattleScene) { super(scene); @@ -3024,7 +3046,7 @@ export class GameOverPhase extends BattlePhase { if (this.victory && !firstClear) this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM)); this.scene.reset(); - this.scene.unshiftPhase(new CheckLoadPhase(this.scene)); + this.scene.unshiftPhase(new TitlePhase(this.scene)); this.end(); }); }); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index f35a7b96487..ad785b98c9a 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -33,6 +33,7 @@ const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet n export enum GameDataType { SYSTEM, SESSION, + DAILY_SESSION, SETTINGS, TUTORIALS } @@ -474,7 +475,7 @@ export class GameData { } as SessionSaveData; if (!bypassLogin) { - Utils.apiPost(`savedata/update?datatype=${GameDataType.SESSION}`, JSON.stringify(sessionData)) + Utils.apiPost(`savedata/update?datatype=${GameDataType.SESSION}&slot=${scene.sessionSlotId}`, JSON.stringify(sessionData)) .then(response => response.text()) .then(error => { if (error) { @@ -495,7 +496,7 @@ export class GameData { }); } - loadSession(scene: BattleScene): Promise { + loadSession(scene: BattleScene, slotId: integer): Promise { return new Promise(async (resolve, reject) => { const handleSessionData = async (sessionDataStr: string) => { try { @@ -580,7 +581,7 @@ export class GameData { }; if (!bypassLogin) { - Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}`) + Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}&slot=${slotId}`) .then(response => response.text()) .then(async response => { if (!response.length || response[0] !== '{') { @@ -591,7 +592,7 @@ export class GameData { await handleSessionData(response); }); } else - await handleSessionData(atob(localStorage.getItem('sessionData'))); + await handleSessionData(atob(localStorage.getItem(`sessionData${slotId ? slotId : ''}`))); }); } diff --git a/src/ui/text.ts b/src/ui/text.ts index 70c788b3770..d715eff4712 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -40,8 +40,8 @@ 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 as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) - ret.setLineSpacing(5); + if (!(styleOptions as BBCodeText.TextStyle).lineSpacing) + ret.setLineSpacing(10); return ret; } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 746df09c383..30d4687f59c 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -38,6 +38,7 @@ export enum Mode { BALL, TARGET_SELECT, MODIFIER_SELECT, + //LOAD_SESSION, PARTY, SUMMARY, BIOME_SELECT, @@ -59,6 +60,7 @@ export enum Mode { }; const transitionModes = [ + //Mode.LOAD_SESSION, Mode.PARTY, Mode.SUMMARY, Mode.STARTER_SELECT, @@ -107,6 +109,7 @@ export default class UI extends Phaser.GameObjects.Container { new BallUiHandler(scene), new TargetSelectUiHandler(scene), new ModifierSelectUiHandler(scene), + //LoadSessionUiHandler(scene), new PartyUiHandler(scene), new SummaryUiHandler(scene), new BiomeSelectUiHandler(scene),