Add ""title"" screen and save data slots

This commit is contained in:
Flashfyre 2024-03-14 21:49:49 -04:00
parent bed98ad304
commit 879971ae2b
8 changed files with 118 additions and 81 deletions

View File

@ -3,7 +3,7 @@ import * as Utils from "./utils";
export interface UserInfo { export interface UserInfo {
username: string; username: string;
hasGameSession: boolean; lastSessionSlot: integer;
} }
export let loggedInUser: UserInfo = null; export let loggedInUser: UserInfo = null;
@ -11,7 +11,14 @@ export let loggedInUser: UserInfo = null;
export function updateUserInfo(): Promise<boolean> { export function updateUserInfo(): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
if (bypassLogin) { 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); return resolve(true);
} }
Utils.apiFetch('account/info').then(response => { Utils.apiFetch('account/info').then(response => {

View File

@ -1,6 +1,6 @@
import Phaser from 'phaser'; import Phaser from 'phaser';
import UI, { Mode } from './ui/ui'; 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 Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon';
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species'; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
import * as Utils from './utils'; import * as Utils from './utils';
@ -104,6 +104,7 @@ export default class BattleScene extends Phaser.Scene {
public enableVibration: boolean = false; public enableVibration: boolean = false;
public gameData: GameData; public gameData: GameData;
public sessionSlotId: integer;
private phaseQueue: Phase[]; private phaseQueue: Phase[];
private phaseQueuePrepend: Phase[]; private phaseQueuePrepend: Phase[];
@ -614,7 +615,7 @@ export default class BattleScene extends Phaser.Scene {
this.pushPhase(new LoginPhase(this)); this.pushPhase(new LoginPhase(this));
if (!bypassLogin) if (!bypassLogin)
this.pushPhase(new ConsolidateDataPhase(this)); // TODO: Remove this.pushPhase(new ConsolidateDataPhase(this)); // TODO: Remove
this.pushPhase(new CheckLoadPhase(this)); this.pushPhase(new TitlePhase(this));
this.shiftPhase(); this.shiftPhase();
}); });
@ -1205,6 +1206,7 @@ export default class BattleScene extends Phaser.Scene {
case Mode.BIOME_SELECT: case Mode.BIOME_SELECT:
case Mode.STARTER_SELECT: case Mode.STARTER_SELECT:
case Mode.CONFIRM: case Mode.CONFIRM:
case Mode.OPTION_SELECT:
this.ui.setOverlayMode(Mode.MENU); this.ui.setOverlayMode(Mode.MENU);
inputSuccess = true; inputSuccess = true;
break; break;

View File

@ -6,7 +6,7 @@ import Trainer from "./field/trainer";
import { Species } from "./data/enums/species"; import { Species } from "./data/enums/species";
import { Moves } from "./data/enums/moves"; import { Moves } from "./data/enums/moves";
import { TrainerType } from "./data/enums/trainer-type"; 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 { BattleSpec } from "./enums/battle-spec";
import { PlayerGender } from "./system/game-data"; import { PlayerGender } from "./system/game-data";
import { PokemonHeldItemModifier } from "./modifier/modifier"; import { PokemonHeldItemModifier } from "./modifier/modifier";

View File

@ -5,6 +5,7 @@ import { version } from '../package.json';
import UIPlugin from 'phaser3-rex-plugins/templates/ui/ui-plugin'; import UIPlugin from 'phaser3-rex-plugins/templates/ui/ui-plugin';
import BBCodeTextPlugin from 'phaser3-rex-plugins/plugins/bbcodetext-plugin'; import BBCodeTextPlugin from 'phaser3-rex-plugins/plugins/bbcodetext-plugin';
import InputTextPlugin from 'phaser3-rex-plugins/plugins/inputtext-plugin.js'; import InputTextPlugin from 'phaser3-rex-plugins/plugins/inputtext-plugin.js';
import BBCodeText from 'phaser3-rex-plugins/plugins/bbcodetext';
const config: Phaser.Types.Core.GameConfig = { const config: Phaser.Types.Core.GameConfig = {
type: Phaser.WEBGL, type: Phaser.WEBGL,
@ -62,6 +63,7 @@ Phaser.GameObjects.Sprite.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative;
BBCodeText.prototype.setPositionRelative = setPositionRelative;
Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative;
document.fonts.load('16px emerald').then(() => document.fonts.load('10px pkmnems')); document.fonts.load('16px emerald').then(() => document.fonts.load('10px pkmnems'));

View File

@ -35,7 +35,6 @@ import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena"; import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle"; import { BattleType, BattlerIndex, TurnCommand } from "./battle";
import { BattleSpec } from "./enums/battle-spec"; import { BattleSpec } from "./enums/battle-spec";
import { GameModes } from "./game-mode";
import { Species } from "./data/enums/species"; import { Species } from "./data/enums/species";
import { HealAchv, LevelAchv, MoneyAchv, achvs } from "./system/achv"; import { HealAchv, LevelAchv, MoneyAchv, achvs } from "./system/achv";
import { trainerConfigs } from "./data/trainer-config"; 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 { Setting } from "./system/settings";
import { Tutorial, handleTutorial } from "./tutorial"; import { Tutorial, handleTutorial } from "./tutorial";
import { TerrainType } from "./data/terrain"; import { TerrainType } from "./data/terrain";
import { OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
export class LoginPhase extends Phase { export class LoginPhase extends Phase {
private showText: boolean; 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 // TODO: Remove
export class ConsolidateDataPhase extends Phase { export class ConsolidateDataPhase extends Phase {
start(): void { 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 { export class SelectGenderPhase extends Phase {
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
super(scene); super(scene);
@ -3024,7 +3046,7 @@ export class GameOverPhase extends BattlePhase {
if (this.victory && !firstClear) if (this.victory && !firstClear)
this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM)); this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM));
this.scene.reset(); this.scene.reset();
this.scene.unshiftPhase(new CheckLoadPhase(this.scene)); this.scene.unshiftPhase(new TitlePhase(this.scene));
this.end(); this.end();
}); });
}); });

View File

@ -33,6 +33,7 @@ const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet n
export enum GameDataType { export enum GameDataType {
SYSTEM, SYSTEM,
SESSION, SESSION,
DAILY_SESSION,
SETTINGS, SETTINGS,
TUTORIALS TUTORIALS
} }
@ -474,7 +475,7 @@ export class GameData {
} as SessionSaveData; } as SessionSaveData;
if (!bypassLogin) { 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(response => response.text())
.then(error => { .then(error => {
if (error) { if (error) {
@ -495,7 +496,7 @@ export class GameData {
}); });
} }
loadSession(scene: BattleScene): Promise<boolean> { loadSession(scene: BattleScene, slotId: integer): Promise<boolean> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const handleSessionData = async (sessionDataStr: string) => { const handleSessionData = async (sessionDataStr: string) => {
try { try {
@ -580,7 +581,7 @@ export class GameData {
}; };
if (!bypassLogin) { if (!bypassLogin) {
Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}`) Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}&slot=${slotId}`)
.then(response => response.text()) .then(response => response.text())
.then(async response => { .then(async response => {
if (!response.length || response[0] !== '{') { if (!response.length || response[0] !== '{') {
@ -591,7 +592,7 @@ export class GameData {
await handleSessionData(response); await handleSessionData(response);
}); });
} else } else
await handleSessionData(atob(localStorage.getItem('sessionData'))); await handleSessionData(atob(localStorage.getItem(`sessionData${slotId ? slotId : ''}`)));
}); });
} }

View File

@ -40,8 +40,8 @@ export function addBBCodeTextObject(scene: Phaser.Scene, x: number, y: number, c
scene.add.existing(ret); scene.add.existing(ret);
ret.setScale(0.1666666667); ret.setScale(0.1666666667);
ret.setShadow(shadowSize, shadowSize, shadowColor); ret.setShadow(shadowSize, shadowSize, shadowColor);
if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) if (!(styleOptions as BBCodeText.TextStyle).lineSpacing)
ret.setLineSpacing(5); ret.setLineSpacing(10);
return ret; return ret;
} }

View File

@ -38,6 +38,7 @@ export enum Mode {
BALL, BALL,
TARGET_SELECT, TARGET_SELECT,
MODIFIER_SELECT, MODIFIER_SELECT,
//LOAD_SESSION,
PARTY, PARTY,
SUMMARY, SUMMARY,
BIOME_SELECT, BIOME_SELECT,
@ -59,6 +60,7 @@ export enum Mode {
}; };
const transitionModes = [ const transitionModes = [
//Mode.LOAD_SESSION,
Mode.PARTY, Mode.PARTY,
Mode.SUMMARY, Mode.SUMMARY,
Mode.STARTER_SELECT, Mode.STARTER_SELECT,
@ -107,6 +109,7 @@ export default class UI extends Phaser.GameObjects.Container {
new BallUiHandler(scene), new BallUiHandler(scene),
new TargetSelectUiHandler(scene), new TargetSelectUiHandler(scene),
new ModifierSelectUiHandler(scene), new ModifierSelectUiHandler(scene),
//LoadSessionUiHandler(scene),
new PartyUiHandler(scene), new PartyUiHandler(scene),
new SummaryUiHandler(scene), new SummaryUiHandler(scene),
new BiomeSelectUiHandler(scene), new BiomeSelectUiHandler(scene),