Fully implement save slots and ""title"" screen changes
Fully implement save slots and ""title"" screen changes; fix issues with slots including clear data not working on game over and export/import not working; fix session play time not being recorded correctlypull/16/head
parent
879971ae2b
commit
00255cb09a
|
@ -622,7 +622,8 @@ export default class BattleScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
initSession(): void {
|
initSession(): void {
|
||||||
this.sessionPlayTime = 0;
|
if (this.sessionPlayTime === null)
|
||||||
|
this.sessionPlayTime = 0;
|
||||||
|
|
||||||
if (this.playTimeTimer)
|
if (this.playTimeTimer)
|
||||||
this.playTimeTimer.destroy();
|
this.playTimeTimer.destroy();
|
||||||
|
@ -1336,13 +1337,9 @@ export default class BattleScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fadeOutBgm(duration?: integer, destroy?: boolean): boolean {
|
fadeOutBgm(duration: integer = 500, destroy: boolean = true): boolean {
|
||||||
if (!this.bgm)
|
if (!this.bgm)
|
||||||
return false;
|
return false;
|
||||||
if (!duration)
|
|
||||||
duration = 500;
|
|
||||||
if (destroy === undefined)
|
|
||||||
destroy = true;
|
|
||||||
const bgm = this.sound.getAllPlaying().find(bgm => bgm.key === this.bgm.key);
|
const bgm = this.sound.getAllPlaying().find(bgm => bgm.key === this.bgm.key);
|
||||||
if (bgm) {
|
if (bgm) {
|
||||||
SoundFade.fadeOut(this, this.bgm, duration, destroy);
|
SoundFade.fadeOut(this, this.bgm, duration, destroy);
|
||||||
|
|
|
@ -24,6 +24,13 @@ export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||||
|
|
||||||
const iconOverflowIndex = 24;
|
const iconOverflowIndex = 24;
|
||||||
|
|
||||||
|
export const modifierSortFunc = (a: Modifier, b: Modifier) => {
|
||||||
|
const aId = a instanceof PokemonHeldItemModifier ? a.pokemonId : 4294967295;
|
||||||
|
const bId = b instanceof PokemonHeldItemModifier ? b.pokemonId : 4294967295;
|
||||||
|
|
||||||
|
return aId < bId ? 1 : aId > bId ? -1 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
export class ModifierBar extends Phaser.GameObjects.Container {
|
export class ModifierBar extends Phaser.GameObjects.Container {
|
||||||
private player: boolean;
|
private player: boolean;
|
||||||
private modifierCache: PersistentModifier[];
|
private modifierCache: PersistentModifier[];
|
||||||
|
@ -40,12 +47,7 @@ export class ModifierBar extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
const visibleIconModifiers = modifiers.filter(m => m.isIconVisible(this.scene as BattleScene));
|
const visibleIconModifiers = modifiers.filter(m => m.isIconVisible(this.scene as BattleScene));
|
||||||
|
|
||||||
visibleIconModifiers.sort((a: Modifier, b: Modifier) => {
|
visibleIconModifiers.sort(modifierSortFunc);
|
||||||
const aId = a instanceof PokemonHeldItemModifier ? a.pokemonId : 4294967295;
|
|
||||||
const bId = b instanceof PokemonHeldItemModifier ? b.pokemonId : 4294967295;
|
|
||||||
|
|
||||||
return aId < bId ? 1 : aId > bId ? -1 : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const thisArg = this;
|
const thisArg = this;
|
||||||
|
|
||||||
|
@ -197,7 +199,7 @@ export abstract class PersistentModifier extends Modifier {
|
||||||
|
|
||||||
const text = addTextObject(scene, 8, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#f8f8f8' : maxColor });
|
const text = addTextObject(scene, 8, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#f8f8f8' : maxColor });
|
||||||
text.setShadow(0, 0, null);
|
text.setShadow(0, 0, null);
|
||||||
text.setStroke('#424242', 16)
|
text.setStroke('#424242', 16);
|
||||||
text.setOrigin(0, 0);
|
text.setOrigin(0, 0);
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
@ -495,6 +497,8 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
||||||
|
|
||||||
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): integer {
|
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): integer {
|
||||||
const pokemon = this.getPokemon(scene);
|
const pokemon = this.getPokemon(scene);
|
||||||
|
if (!pokemon)
|
||||||
|
return 0;
|
||||||
if (pokemon.isPlayer() && forThreshold)
|
if (pokemon.isPlayer() && forThreshold)
|
||||||
return scene.getParty().map(p => this.getMaxHeldItemCount(p)).reduce((stackCount: integer, maxStackCount: integer) => Math.max(stackCount, maxStackCount), 0);
|
return scene.getParty().map(p => this.getMaxHeldItemCount(p)).reduce((stackCount: integer, maxStackCount: integer) => Math.max(stackCount, maxStackCount), 0);
|
||||||
return this.getMaxHeldItemCount(pokemon);
|
return this.getMaxHeldItemCount(pokemon);
|
||||||
|
|
115
src/phases.ts
115
src/phases.ts
|
@ -51,7 +51,8 @@ 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";
|
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
|
||||||
|
import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler";
|
||||||
|
|
||||||
export class LoginPhase extends Phase {
|
export class LoginPhase extends Phase {
|
||||||
private showText: boolean;
|
private showText: boolean;
|
||||||
|
@ -134,23 +135,19 @@ export class TitlePhase extends Phase {
|
||||||
start(): void {
|
start(): void {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
|
this.scene.ui.fadeIn(250);
|
||||||
|
|
||||||
|
this.scene.fadeOutBgm(0, false);
|
||||||
|
|
||||||
|
this.showOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
showOptions(): void {
|
||||||
const options: OptionSelectItem[] = [];
|
const options: OptionSelectItem[] = [];
|
||||||
if (loggedInUser?.lastSessionSlot > -1) {
|
if (loggedInUser?.lastSessionSlot > -1) {
|
||||||
options.push({
|
options.push({
|
||||||
label: 'Continue',
|
label: 'Continue',
|
||||||
handler: () => {
|
handler: () => this.loadSaveSlot(loggedInUser.lastSessionSlot)
|
||||||
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({
|
options.push({
|
||||||
|
@ -158,16 +155,19 @@ export class TitlePhase extends Phase {
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.ui.clearText();
|
this.scene.ui.clearText();
|
||||||
this.scene.sessionSlotId = 0;
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/*{
|
{
|
||||||
label: 'Load',
|
label: 'Load',
|
||||||
handler: () => {
|
handler: () => this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD,
|
||||||
|
(slotId: integer) => {
|
||||||
}
|
if (slotId === -1)
|
||||||
},*/
|
return this.showOptions();
|
||||||
|
this.loadSaveSlot(slotId);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
/*{
|
/*{
|
||||||
label: 'Daily Run',
|
label: 'Daily Run',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
|
@ -176,8 +176,25 @@ export class TitlePhase extends Phase {
|
||||||
},
|
},
|
||||||
keepOpen: true
|
keepOpen: true
|
||||||
}*/);
|
}*/);
|
||||||
this.scene.ui.setMode(Mode.OPTION_SELECT, {
|
const config: OptionSelectConfig = {
|
||||||
options: options
|
options: options,
|
||||||
|
noCancel: true
|
||||||
|
};
|
||||||
|
this.scene.ui.setMode(Mode.OPTION_SELECT, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSaveSlot(slotId: integer): void {
|
||||||
|
this.scene.sessionSlotId = slotId;
|
||||||
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
|
this.scene.gameData.loadSession(this.scene, slotId).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.', null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,28 +320,35 @@ export class SelectStarterPhase extends Phase {
|
||||||
this.scene.playBgm('menu');
|
this.scene.playBgm('menu');
|
||||||
|
|
||||||
this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => {
|
this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => {
|
||||||
const party = this.scene.getParty();
|
this.scene.ui.clearText();
|
||||||
const loadPokemonAssets: Promise<void>[] = [];
|
this.scene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: integer) => {
|
||||||
for (let starter of starters) {
|
if (slotId === -1) {
|
||||||
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
this.scene.clearPhaseQueue();
|
||||||
const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
|
this.scene.pushPhase(new TitlePhase(this.scene));
|
||||||
const starterGender = starter.species.malePercent !== null
|
return this.end();
|
||||||
? !starterProps.female ? Gender.MALE : Gender.FEMALE
|
}
|
||||||
: Gender.GENDERLESS;
|
this.scene.sessionSlotId = slotId;
|
||||||
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
|
|
||||||
const starterPokemon = this.scene.addPlayerPokemon(starter.species, startingLevel, starterProps.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterIvs, starter.nature);
|
const party = this.scene.getParty();
|
||||||
starterPokemon.tryPopulateMoveset(starter.moveset);
|
const loadPokemonAssets: Promise<void>[] = [];
|
||||||
if (starter.pokerus)
|
for (let starter of starters) {
|
||||||
starterPokemon.pokerus = true;
|
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
||||||
if (this.scene.gameMode.isSplicedOnly)
|
const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
|
||||||
starterPokemon.generateFusionSpecies(true);
|
const starterGender = starter.species.malePercent !== null
|
||||||
starterPokemon.setVisible(false);
|
? !starterProps.female ? Gender.MALE : Gender.FEMALE
|
||||||
party.push(starterPokemon);
|
: Gender.GENDERLESS;
|
||||||
loadPokemonAssets.push(starterPokemon.loadAssets());
|
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
|
||||||
}
|
const starterPokemon = this.scene.addPlayerPokemon(starter.species, startingLevel, starterProps.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterIvs, starter.nature);
|
||||||
Promise.all(loadPokemonAssets).then(() => {
|
starterPokemon.tryPopulateMoveset(starter.moveset);
|
||||||
this.scene.ui.clearText();
|
if (starter.pokerus)
|
||||||
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
|
starterPokemon.pokerus = true;
|
||||||
|
if (this.scene.gameMode.isSplicedOnly)
|
||||||
|
starterPokemon.generateFusionSpecies(true);
|
||||||
|
starterPokemon.setVisible(false);
|
||||||
|
party.push(starterPokemon);
|
||||||
|
loadPokemonAssets.push(starterPokemon.loadAssets());
|
||||||
|
}
|
||||||
|
Promise.all(loadPokemonAssets).then(() => {
|
||||||
SoundFade.fadeOut(this.scene, this.scene.sound.get('menu'), 500, true);
|
SoundFade.fadeOut(this.scene, this.scene.sound.get('menu'), 500, true);
|
||||||
this.scene.time.delayedCall(500, () => this.scene.playBgm());
|
this.scene.time.delayedCall(500, () => this.scene.playBgm());
|
||||||
if (this.scene.gameMode.isClassic)
|
if (this.scene.gameMode.isClassic)
|
||||||
|
@ -332,6 +356,7 @@ export class SelectStarterPhase extends Phase {
|
||||||
else
|
else
|
||||||
this.scene.gameData.gameStats.endlessSessionsPlayed++;
|
this.scene.gameData.gameStats.endlessSessionsPlayed++;
|
||||||
this.scene.newBattle();
|
this.scene.newBattle();
|
||||||
|
this.scene.sessionPlayTime = 0;
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3029,7 +3054,7 @@ export class GameOverPhase extends BattlePhase {
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
this.scene.gameData.clearSession().then(() => {
|
this.scene.gameData.clearSession(this.scene.sessionSlotId).then(() => {
|
||||||
this.scene.time.delayedCall(1000, () => {
|
this.scene.time.delayedCall(1000, () => {
|
||||||
let firstClear = false;
|
let firstClear = false;
|
||||||
if (this.victory) {
|
if (this.victory) {
|
||||||
|
|
|
@ -33,7 +33,6 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -44,12 +43,15 @@ export enum PlayerGender {
|
||||||
FEMALE
|
FEMALE
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDataTypeKey(dataType: GameDataType): string {
|
export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): string {
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case GameDataType.SYSTEM:
|
case GameDataType.SYSTEM:
|
||||||
return 'data';
|
return 'data';
|
||||||
case GameDataType.SESSION:
|
case GameDataType.SESSION:
|
||||||
return 'sessionData';
|
let ret = 'sessionData';
|
||||||
|
if (slotId)
|
||||||
|
ret += slotId;
|
||||||
|
return ret;
|
||||||
case GameDataType.SETTINGS:
|
case GameDataType.SETTINGS:
|
||||||
return 'settings';
|
return 'settings';
|
||||||
case GameDataType.TUTORIALS:
|
case GameDataType.TUTORIALS:
|
||||||
|
@ -74,7 +76,7 @@ interface SystemSaveData {
|
||||||
timestamp: integer;
|
timestamp: integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SessionSaveData {
|
export interface SessionSaveData {
|
||||||
seed: string;
|
seed: string;
|
||||||
playTime: integer;
|
playTime: integer;
|
||||||
gameMode: GameModes;
|
gameMode: GameModes;
|
||||||
|
@ -496,12 +498,38 @@ export class GameData {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSession(scene: BattleScene, slotId: integer): Promise<boolean> {
|
getSession(slotId: integer): Promise<SessionSaveData> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const handleSessionData = async (sessionDataStr: string) => {
|
const handleSessionData = async (sessionDataStr: string) => {
|
||||||
try {
|
try {
|
||||||
const sessionData = this.parseSessionData(sessionDataStr);
|
const sessionData = this.parseSessionData(sessionDataStr);
|
||||||
|
resolve(sessionData);
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!bypassLogin) {
|
||||||
|
Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}&slot=${slotId}`)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(async response => {
|
||||||
|
if (!response.length || response[0] !== '{') {
|
||||||
|
console.error(response);
|
||||||
|
return resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
await handleSessionData(response);
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
await handleSessionData(atob(localStorage.getItem(`sessionData${slotId ? slotId : ''}`)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSession(scene: BattleScene, slotId: integer): Promise<boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this.getSession(slotId).then(async sessionData => {
|
||||||
console.debug(sessionData);
|
console.debug(sessionData);
|
||||||
|
|
||||||
scene.seed = sessionData.seed || scene.game.config.seed[0];
|
scene.seed = sessionData.seed || scene.game.config.seed[0];
|
||||||
|
@ -562,41 +590,27 @@ export class GameData {
|
||||||
|
|
||||||
scene.updateModifiers(true);
|
scene.updateModifiers(true);
|
||||||
|
|
||||||
// TODO: Remove if
|
for (let enemyModifierData of sessionData.enemyModifiers) {
|
||||||
if (battle.battleSpec !== BattleSpec.FINAL_BOSS) {
|
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]);
|
||||||
for (let enemyModifierData of sessionData.enemyModifiers) {
|
if (modifier)
|
||||||
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]);
|
scene.addEnemyModifier(modifier, true);
|
||||||
if (modifier)
|
|
||||||
scene.addEnemyModifier(modifier, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.updateModifiers(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scene.updateModifiers(false);
|
||||||
|
|
||||||
Promise.all(loadPokemonAssets).then(() => resolve(true));
|
Promise.all(loadPokemonAssets).then(() => resolve(true));
|
||||||
} catch (err) {
|
}).catch(err => {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
};
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
if (!bypassLogin) {
|
return;
|
||||||
Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}&slot=${slotId}`)
|
}
|
||||||
.then(response => response.text())
|
|
||||||
.then(async response => {
|
|
||||||
if (!response.length || response[0] !== '{') {
|
|
||||||
console.error(response);
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await handleSessionData(response);
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
await handleSessionData(atob(localStorage.getItem(`sessionData${slotId ? slotId : ''}`)));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearSession(): Promise<boolean> {
|
clearSession(slotId: integer): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
if (bypassLogin) {
|
if (bypassLogin) {
|
||||||
localStorage.removeItem('sessionData');
|
localStorage.removeItem('sessionData');
|
||||||
|
@ -606,9 +620,9 @@ export class GameData {
|
||||||
updateUserInfo().then(success => {
|
updateUserInfo().then(success => {
|
||||||
if (success !== null && !success)
|
if (success !== null && !success)
|
||||||
return resolve(false);
|
return resolve(false);
|
||||||
Utils.apiFetch(`savedata/delete?datatype=${GameDataType.SESSION}`).then(response => {
|
Utils.apiFetch(`savedata/delete?datatype=${GameDataType.SESSION}&slot=${slotId}`).then(response => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
loggedInUser.hasGameSession = false;
|
loggedInUser.lastSessionSlot = -1;
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
}
|
}
|
||||||
resolve(false);
|
resolve(false);
|
||||||
|
@ -654,39 +668,47 @@ export class GameData {
|
||||||
}) as SessionSaveData;
|
}) as SessionSaveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public exportData(dataType: GameDataType): void {
|
public tryExportData(dataType: GameDataType, slotId: integer = 0): Promise<boolean> {
|
||||||
const dataKey: string = getDataTypeKey(dataType);
|
return new Promise<boolean>(resolve => {
|
||||||
const handleData = (dataStr: string) => {
|
const dataKey: string = getDataTypeKey(dataType, slotId);
|
||||||
switch (dataType) {
|
const handleData = (dataStr: string) => {
|
||||||
case GameDataType.SYSTEM:
|
switch (dataType) {
|
||||||
dataStr = this.convertSystemDataStr(dataStr, true);
|
case GameDataType.SYSTEM:
|
||||||
break;
|
dataStr = this.convertSystemDataStr(dataStr, true);
|
||||||
}
|
break;
|
||||||
const encryptedData = AES.encrypt(dataStr, saveKey);
|
}
|
||||||
const blob = new Blob([ encryptedData.toString() ], {type: 'text/json'});
|
const encryptedData = AES.encrypt(dataStr, saveKey);
|
||||||
const link = document.createElement('a');
|
const blob = new Blob([ encryptedData.toString() ], {type: 'text/json'});
|
||||||
link.href = window.URL.createObjectURL(blob);
|
const link = document.createElement('a');
|
||||||
link.download = `${dataKey}.prsv`;
|
link.href = window.URL.createObjectURL(blob);
|
||||||
link.click();
|
link.download = `${dataKey}.prsv`;
|
||||||
link.remove();
|
link.click();
|
||||||
};
|
link.remove();
|
||||||
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
|
};
|
||||||
Utils.apiFetch(`savedata/get?datatype=${dataType}`)
|
if (!bypassLogin && dataType < GameDataType.SETTINGS) {
|
||||||
.then(response => response.text())
|
Utils.apiFetch(`savedata/get?datatype=${dataType}${dataType === GameDataType.SESSION ? `&slot=${slotId}` : ''}`)
|
||||||
.then(response => {
|
.then(response => response.text())
|
||||||
if (!response.length || response[0] !== '{') {
|
.then(response => {
|
||||||
console.error(response);
|
if (!response.length || response[0] !== '{') {
|
||||||
return;
|
console.error(response);
|
||||||
}
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handleData(response);
|
handleData(response);
|
||||||
});
|
resolve(true);
|
||||||
} else
|
});
|
||||||
handleData(atob(localStorage.getItem(dataKey)));
|
} else {
|
||||||
|
const data = localStorage.getItem(dataKey);
|
||||||
|
if (data)
|
||||||
|
handleData(atob(data));
|
||||||
|
resolve(!!data);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public importData(dataType: GameDataType): void {
|
public importData(dataType: GameDataType, slotId: integer = 0): void {
|
||||||
const dataKey = getDataTypeKey(dataType);
|
const dataKey = getDataTypeKey(dataType, slotId);
|
||||||
|
|
||||||
let saveFile: any = document.getElementById('saveFile');
|
let saveFile: any = document.getElementById('saveFile');
|
||||||
if (saveFile)
|
if (saveFile)
|
||||||
|
@ -751,7 +773,7 @@ export class GameData {
|
||||||
updateUserInfo().then(success => {
|
updateUserInfo().then(success => {
|
||||||
if (!success)
|
if (!success)
|
||||||
return displayError(`Could not contact the server. Your ${dataName} data could not be imported.`);
|
return displayError(`Could not contact the server. Your ${dataName} data could not be imported.`);
|
||||||
Utils.apiPost(`savedata/update?datatype=${dataType}`, dataStr)
|
Utils.apiPost(`savedata/update?datatype=${dataType}${dataType === GameDataType.SESSION ? `&slot=${slotId}` : ''}`, dataStr)
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(error => {
|
.then(error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ export interface OptionSelectConfig {
|
||||||
yOffset?: number;
|
yOffset?: number;
|
||||||
options: OptionSelectItem[];
|
options: OptionSelectItem[];
|
||||||
maxOptions?: integer;
|
maxOptions?: integer;
|
||||||
|
noCancel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OptionSelectItem {
|
export interface OptionSelectItem {
|
||||||
|
@ -110,8 +111,10 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||||
if (this.config?.maxOptions && this.config.options.length > this.config.maxOptions) {
|
if (this.config?.maxOptions && this.config.options.length > this.config.maxOptions) {
|
||||||
this.scrollCursor = (this.config.options.length - this.config.maxOptions) + 1;
|
this.scrollCursor = (this.config.options.length - this.config.maxOptions) + 1;
|
||||||
this.cursor = options.length - 1;
|
this.cursor = options.length - 1;
|
||||||
} else
|
} else if (!this.config?.noCancel)
|
||||||
this.setCursor(options.length - 1);
|
this.setCursor(options.length - 1);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
const option = this.config.options[this.cursor + (this.scrollCursor - (this.scrollCursor ? 1 : 0))];
|
const option = this.config.options[this.cursor + (this.scrollCursor - (this.scrollCursor ? 1 : 0))];
|
||||||
option.handler();
|
option.handler();
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class LoadingModalUiHandler extends ModalUiHandler {
|
||||||
setup(): void {
|
setup(): void {
|
||||||
super.setup();
|
super.setup();
|
||||||
|
|
||||||
const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Loading...', TextStyle.WINDOW);
|
const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Loading…', TextStyle.WINDOW);
|
||||||
label.setOrigin(0.5, 0.5);
|
label.setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
this.modalContainer.add(label);
|
this.modalContainer.add(label);
|
||||||
|
|
|
@ -86,14 +86,54 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||||
|
|
||||||
const manageDataOptions = [];
|
const manageDataOptions = [];
|
||||||
|
|
||||||
|
const confirmSlot = (message: string, slotFilter: (i: integer) => boolean, callback: (i: integer) => void) => {
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText(message, null, () => {
|
||||||
|
const config: OptionSelectConfig = {
|
||||||
|
options: new Array(3).fill(null).map((_, i) => i).filter(slotFilter).map(i => {
|
||||||
|
return {
|
||||||
|
label: `Slot ${i + 1}`,
|
||||||
|
handler: () => {
|
||||||
|
callback(i);
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText(null, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}).concat([{
|
||||||
|
label: 'Cancel',
|
||||||
|
handler: () => {
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText(null, 0);
|
||||||
|
}
|
||||||
|
}]),
|
||||||
|
xOffset: 98
|
||||||
|
};
|
||||||
|
ui.setOverlayMode(Mode.OPTION_SELECT, config);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
manageDataOptions.push({
|
manageDataOptions.push({
|
||||||
label: 'Import Session',
|
label: 'Import Session',
|
||||||
handler: () => this.scene.gameData.importData(GameDataType.SESSION),
|
handler: () => confirmSlot('Select a slot to import to.', () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)),
|
||||||
keepOpen: true
|
keepOpen: true
|
||||||
});
|
});
|
||||||
manageDataOptions.push({
|
manageDataOptions.push({
|
||||||
label: 'Export Session',
|
label: 'Export Session',
|
||||||
handler: () => this.scene.gameData.exportData(GameDataType.SESSION),
|
handler: () => {
|
||||||
|
const dataSlots: integer[] = [];
|
||||||
|
Promise.all(
|
||||||
|
new Array(3).fill(null).map((_, i) => {
|
||||||
|
const slotId = i;
|
||||||
|
return this.scene.gameData.getSession(slotId).then(data => {
|
||||||
|
if (data)
|
||||||
|
dataSlots.push(slotId);
|
||||||
|
})
|
||||||
|
})).then(() => {
|
||||||
|
confirmSlot('Select a slot to export from.',
|
||||||
|
i => dataSlots.indexOf(i) > -1,
|
||||||
|
slotId => this.scene.gameData.tryExportData(GameDataType.SESSION, slotId));
|
||||||
|
});
|
||||||
|
},
|
||||||
keepOpen: true
|
keepOpen: true
|
||||||
});
|
});
|
||||||
manageDataOptions.push({
|
manageDataOptions.push({
|
||||||
|
@ -104,7 +144,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||||
manageDataOptions.push(
|
manageDataOptions.push(
|
||||||
{
|
{
|
||||||
label: 'Export Data',
|
label: 'Export Data',
|
||||||
handler: () => this.scene.gameData.exportData(GameDataType.SYSTEM),
|
handler: () => this.scene.gameData.tryExportData(GameDataType.SYSTEM),
|
||||||
keepOpen: true
|
keepOpen: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,298 @@
|
||||||
|
import BattleScene, { Button } from "../battle-scene";
|
||||||
|
import { gameModes } from "../game-mode";
|
||||||
|
import { SessionSaveData } from "../system/game-data";
|
||||||
|
import { TextStyle, addTextObject } from "./text";
|
||||||
|
import { Mode } from "./ui";
|
||||||
|
import { addWindow } from "./window";
|
||||||
|
import * as Utils from "../utils";
|
||||||
|
import PokemonData from "../system/pokemon-data";
|
||||||
|
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
||||||
|
import { TitlePhase } from "../phases";
|
||||||
|
import MessageUiHandler from "./message-ui-handler";
|
||||||
|
|
||||||
|
const sessionSlotCount = 3;
|
||||||
|
|
||||||
|
export enum SaveSlotUiMode {
|
||||||
|
LOAD,
|
||||||
|
SAVE
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SaveSlotSelectCallback = (cursor: integer) => void;
|
||||||
|
|
||||||
|
export default class SaveSlotSelectUiHandler extends MessageUiHandler {
|
||||||
|
|
||||||
|
private saveSlotSelectContainer: Phaser.GameObjects.Container;
|
||||||
|
private sessionSlotsContainer: Phaser.GameObjects.Container;
|
||||||
|
private saveSlotSelectMessageBox: Phaser.GameObjects.NineSlice;
|
||||||
|
private saveSlotSelectMessageBoxContainer: Phaser.GameObjects.Container;
|
||||||
|
private sessionSlots: SessionSlot[];
|
||||||
|
|
||||||
|
private uiMode: SaveSlotUiMode;
|
||||||
|
private saveSlotSelectCallback: SaveSlotSelectCallback;
|
||||||
|
|
||||||
|
private cursorObj: Phaser.GameObjects.NineSlice;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene) {
|
||||||
|
super(scene, Mode.SAVE_SLOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const ui = this.getUi();
|
||||||
|
|
||||||
|
this.saveSlotSelectContainer = this.scene.add.container(0, 0);
|
||||||
|
this.saveSlotSelectContainer.setVisible(false);
|
||||||
|
ui.add(this.saveSlotSelectContainer);
|
||||||
|
|
||||||
|
const loadSessionBg = this.scene.add.rectangle(0, 0, this.scene.game.canvas.width / 6, -this.scene.game.canvas.height / 6, 0x006860);
|
||||||
|
loadSessionBg.setOrigin(0, 0);
|
||||||
|
this.saveSlotSelectContainer.add(loadSessionBg);
|
||||||
|
|
||||||
|
this.sessionSlotsContainer = this.scene.add.container(8, -this.scene.game.canvas.height / 6 + 8);
|
||||||
|
this.saveSlotSelectContainer.add(this.sessionSlotsContainer);
|
||||||
|
|
||||||
|
this.saveSlotSelectMessageBoxContainer = this.scene.add.container(0, 0);
|
||||||
|
this.saveSlotSelectMessageBoxContainer.setVisible(false);
|
||||||
|
this.saveSlotSelectContainer.add(this.saveSlotSelectMessageBoxContainer);
|
||||||
|
|
||||||
|
this.saveSlotSelectMessageBox = addWindow(this.scene, 1, -1, 318, 28);
|
||||||
|
this.saveSlotSelectMessageBox.setOrigin(0, 1);
|
||||||
|
this.saveSlotSelectMessageBoxContainer.add(this.saveSlotSelectMessageBox);
|
||||||
|
|
||||||
|
this.message = addTextObject(this.scene, 8, 8, '', TextStyle.WINDOW, { maxLines: 2 });
|
||||||
|
this.message.setOrigin(0, 0);
|
||||||
|
this.saveSlotSelectMessageBoxContainer.add(this.message);
|
||||||
|
|
||||||
|
this.sessionSlots = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
show(args: any[]): boolean {
|
||||||
|
if ((args.length < 2 || !(args[1] instanceof Function)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
super.show(args);
|
||||||
|
|
||||||
|
this.uiMode = args[0] as SaveSlotUiMode;;
|
||||||
|
this.saveSlotSelectCallback = args[1] as SaveSlotSelectCallback;
|
||||||
|
|
||||||
|
this.saveSlotSelectContainer.setVisible(true);
|
||||||
|
this.populateSessionSlots();
|
||||||
|
this.setCursor(0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
processInput(button: Button): boolean {
|
||||||
|
const ui = this.getUi();
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
let error = false;
|
||||||
|
|
||||||
|
if (button === Button.ACTION || button === Button.CANCEL) {
|
||||||
|
const originalCallback = this.saveSlotSelectCallback;
|
||||||
|
if (button === Button.ACTION) {
|
||||||
|
if (this.uiMode === SaveSlotUiMode.LOAD && !this.sessionSlots[this.cursor].hasData)
|
||||||
|
error = true;
|
||||||
|
else {
|
||||||
|
switch (this.uiMode) {
|
||||||
|
case SaveSlotUiMode.LOAD:
|
||||||
|
this.saveSlotSelectCallback = null;
|
||||||
|
originalCallback(this.cursor);
|
||||||
|
break;
|
||||||
|
case SaveSlotUiMode.SAVE:
|
||||||
|
const saveAndCallback = () => {
|
||||||
|
const originalCallback = this.saveSlotSelectCallback;
|
||||||
|
this.saveSlotSelectCallback = null;
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText(null, 0);
|
||||||
|
ui.setMode(Mode.MESSAGE);
|
||||||
|
originalCallback(this.cursor);
|
||||||
|
};
|
||||||
|
if (this.sessionSlots[this.cursor].hasData) {
|
||||||
|
ui.showText('Overwrite the data in the selected slot?', null, () => {
|
||||||
|
ui.setOverlayMode(Mode.CONFIRM, () => saveAndCallback(), () => {
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText(null, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
saveAndCallback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.saveSlotSelectCallback = null;
|
||||||
|
originalCallback(-1);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (button) {
|
||||||
|
case Button.UP:
|
||||||
|
success = this.setCursor(this.cursor ? this.cursor - 1 : 0);
|
||||||
|
break;
|
||||||
|
case Button.DOWN:
|
||||||
|
success = this.setCursor(this.cursor < sessionSlotCount - 1 ? this.cursor + 1 : 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
ui.playSelect();
|
||||||
|
else if (error)
|
||||||
|
ui.playError();
|
||||||
|
|
||||||
|
return success || error;
|
||||||
|
}
|
||||||
|
|
||||||
|
populateSessionSlots() {
|
||||||
|
for (let s = 0; s < sessionSlotCount; s++) {
|
||||||
|
const sessionSlot = new SessionSlot(this.scene, s);
|
||||||
|
sessionSlot.load();
|
||||||
|
this.scene.add.existing(sessionSlot);
|
||||||
|
this.sessionSlotsContainer.add(sessionSlot);
|
||||||
|
this.sessionSlots.push(sessionSlot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
|
||||||
|
super.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
|
||||||
|
|
||||||
|
if (text?.indexOf('\n') === -1) {
|
||||||
|
this.saveSlotSelectMessageBox.setSize(318, 28);
|
||||||
|
this.message.setY(-22);
|
||||||
|
} else {
|
||||||
|
this.saveSlotSelectMessageBox.setSize(318, 42);
|
||||||
|
this.message.setY(-37);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveSlotSelectMessageBoxContainer.setVisible(!!text?.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCursor(cursor: integer): boolean {
|
||||||
|
let changed = super.setCursor(cursor);
|
||||||
|
|
||||||
|
if (!this.cursorObj) {
|
||||||
|
this.cursorObj = this.scene.add.nineslice(0, 0, 'starter_select_cursor_highlight', null, 296, 44, 1, 1, 1, 1);
|
||||||
|
this.cursorObj.setOrigin(0, 0);
|
||||||
|
this.sessionSlotsContainer.add(this.cursorObj);
|
||||||
|
}
|
||||||
|
this.cursorObj.setPosition(4, 4 + cursor * 56);
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
super.clear();
|
||||||
|
this.saveSlotSelectContainer.setVisible(false);
|
||||||
|
this.eraseCursor();
|
||||||
|
this.saveSlotSelectCallback = null;
|
||||||
|
this.clearSessionSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
eraseCursor() {
|
||||||
|
if (this.cursorObj)
|
||||||
|
this.cursorObj.destroy();
|
||||||
|
this.cursorObj = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSessionSlots() {
|
||||||
|
this.sessionSlots.splice(0, this.sessionSlots.length);
|
||||||
|
this.sessionSlotsContainer.removeAll(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SessionSlot extends Phaser.GameObjects.Container {
|
||||||
|
public slotId: integer;
|
||||||
|
public hasData: boolean;
|
||||||
|
private loadingLabel: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene, slotId: integer) {
|
||||||
|
super(scene, 0, slotId * 56);
|
||||||
|
|
||||||
|
this.slotId = slotId;
|
||||||
|
this.hasData = false;
|
||||||
|
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const slotWindow = addWindow(this.scene, 0, 0, 304, 52);
|
||||||
|
this.add(slotWindow);
|
||||||
|
|
||||||
|
this.loadingLabel = addTextObject(this.scene, 152, 26, 'Loading…', TextStyle.WINDOW);
|
||||||
|
this.loadingLabel.setOrigin(0.5, 0.5);
|
||||||
|
this.add(this.loadingLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupWithData(data: SessionSaveData) {
|
||||||
|
this.remove(this.loadingLabel, true);
|
||||||
|
|
||||||
|
const gameModeLabel = addTextObject(this.scene, 8, 5, `${gameModes[data.gameMode].getName()} - Wave ${data.waveIndex}`, TextStyle.WINDOW);
|
||||||
|
this.add(gameModeLabel);
|
||||||
|
|
||||||
|
const timestampLabel = addTextObject(this.scene, 8, 19, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW);
|
||||||
|
this.add(timestampLabel);
|
||||||
|
|
||||||
|
const playTimeLabel = addTextObject(this.scene, 8, 33, Utils.getPlayTimeString(data.playTime), TextStyle.WINDOW);
|
||||||
|
this.add(playTimeLabel);
|
||||||
|
|
||||||
|
const pokemonIconsContainer = this.scene.add.container(144, 4);
|
||||||
|
data.party.forEach((p: PokemonData, i: integer) => {
|
||||||
|
const iconContainer = this.scene.add.container(26 * i, 0);
|
||||||
|
iconContainer.setScale(0.75);
|
||||||
|
|
||||||
|
const pokemon = p.toPokemon(this.scene);
|
||||||
|
const icon = this.scene.add.sprite(0, 0, pokemon.getIconAtlasKey(), pokemon.getIconId());
|
||||||
|
icon.setOrigin(0, 0);
|
||||||
|
|
||||||
|
const text = addTextObject(this.scene, 32, 20, `Lv${Utils.formatLargeNumber(pokemon.level, 1000)}`, TextStyle.PARTY, { fontSize: '54px', color: '#f8f8f8' });
|
||||||
|
text.setShadow(0, 0, null);
|
||||||
|
text.setStroke('#424242', 14);
|
||||||
|
text.setOrigin(1, 0);
|
||||||
|
|
||||||
|
iconContainer.add(icon);
|
||||||
|
iconContainer.add(text);
|
||||||
|
|
||||||
|
pokemonIconsContainer.add(iconContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.add(pokemonIconsContainer);
|
||||||
|
|
||||||
|
const modifiersModule = await import('../modifier/modifier');
|
||||||
|
|
||||||
|
const modifierIconsContainer = this.scene.add.container(148, 30);
|
||||||
|
modifierIconsContainer.setScale(0.5);
|
||||||
|
let visibleModifierIndex = 0;
|
||||||
|
for (let m of data.modifiers) {
|
||||||
|
const modifier = m.toModifier(this.scene, modifiersModule[m.className]);
|
||||||
|
if (modifier instanceof PokemonHeldItemModifier)
|
||||||
|
continue;
|
||||||
|
const icon = modifier.getIcon(this.scene, false);
|
||||||
|
icon.setPosition(24 * visibleModifierIndex, 0);
|
||||||
|
modifierIconsContainer.add(icon);
|
||||||
|
if (++visibleModifierIndex === 12)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.add(modifierIconsContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
load(): Promise<boolean> {
|
||||||
|
return new Promise<boolean>(resolve => {
|
||||||
|
this.scene.gameData.getSession(this.slotId).then(async sessionData => {
|
||||||
|
if (!sessionData) {
|
||||||
|
this.loadingLabel.setText('Empty');
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hasData = true;
|
||||||
|
await this.setupWithData(sessionData);
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SessionSlot {
|
||||||
|
scene: BattleScene;
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import { allMoves } from "../data/move";
|
||||||
import { Type } from "../data/type";
|
import { Type } from "../data/type";
|
||||||
import { Moves } from "../data/enums/moves";
|
import { Moves } from "../data/enums/moves";
|
||||||
import { speciesEggMoves } from "../data/egg-moves";
|
import { speciesEggMoves } from "../data/egg-moves";
|
||||||
|
import { TitlePhase } from "../phases";
|
||||||
|
|
||||||
export type StarterSelectCallback = (starters: Starter[]) => void;
|
export type StarterSelectCallback = (starters: Starter[]) => void;
|
||||||
|
|
||||||
|
@ -492,6 +493,20 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
success = true;
|
success = true;
|
||||||
else
|
else
|
||||||
error = true;
|
error = true;
|
||||||
|
} else if (button === Button.CANCEL) {
|
||||||
|
if (this.statsMode) {
|
||||||
|
this.toggleStatsMode(false);
|
||||||
|
success = true;
|
||||||
|
} else if (this.starterCursors.length) {
|
||||||
|
this.popStarter();
|
||||||
|
success = true;
|
||||||
|
this.updateInstructions();
|
||||||
|
} else {
|
||||||
|
this.scene.clearPhaseQueue();
|
||||||
|
this.scene.pushPhase(new TitlePhase(this.scene));
|
||||||
|
this.scene.getCurrentPhase().end();
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
} else if (this.startCursorObj.visible) {
|
} else if (this.startCursorObj.visible) {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.ACTION:
|
case Button.ACTION:
|
||||||
|
@ -658,15 +673,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
});
|
});
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
} else if (button === Button.CANCEL) {
|
|
||||||
if (this.statsMode) {
|
|
||||||
this.toggleStatsMode(false);
|
|
||||||
success = true;
|
|
||||||
} else if (this.starterCursors.length) {
|
|
||||||
this.popStarter();
|
|
||||||
success = true;
|
|
||||||
this.updateInstructions();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const genStarters = this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAll().length;
|
const genStarters = this.starterSelectGenIconContainers[this.getGenCursorWithScroll()].getAll().length;
|
||||||
const rows = Math.ceil(genStarters / 9);
|
const rows = Math.ceil(genStarters / 9);
|
||||||
|
|
15
src/ui/ui.ts
15
src/ui/ui.ts
|
@ -30,6 +30,7 @@ import LoadingModalUiHandler from './loading-modal-ui-handler';
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import GameStatsUiHandler from './game-stats-ui-handler';
|
import GameStatsUiHandler from './game-stats-ui-handler';
|
||||||
import AwaitableUiHandler from './awaitable-ui-handler';
|
import AwaitableUiHandler from './awaitable-ui-handler';
|
||||||
|
import SaveSlotSelectUiHandler from './save-slot-select-ui-handler';
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
|
@ -38,7 +39,7 @@ export enum Mode {
|
||||||
BALL,
|
BALL,
|
||||||
TARGET_SELECT,
|
TARGET_SELECT,
|
||||||
MODIFIER_SELECT,
|
MODIFIER_SELECT,
|
||||||
//LOAD_SESSION,
|
SAVE_SLOT,
|
||||||
PARTY,
|
PARTY,
|
||||||
SUMMARY,
|
SUMMARY,
|
||||||
BIOME_SELECT,
|
BIOME_SELECT,
|
||||||
|
@ -60,7 +61,7 @@ export enum Mode {
|
||||||
};
|
};
|
||||||
|
|
||||||
const transitionModes = [
|
const transitionModes = [
|
||||||
//Mode.LOAD_SESSION,
|
Mode.SAVE_SLOT,
|
||||||
Mode.PARTY,
|
Mode.PARTY,
|
||||||
Mode.SUMMARY,
|
Mode.SUMMARY,
|
||||||
Mode.STARTER_SELECT,
|
Mode.STARTER_SELECT,
|
||||||
|
@ -109,7 +110,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 SaveSlotSelectUiHandler(scene),
|
||||||
new PartyUiHandler(scene),
|
new PartyUiHandler(scene),
|
||||||
new SummaryUiHandler(scene),
|
new SummaryUiHandler(scene),
|
||||||
new BiomeSelectUiHandler(scene),
|
new BiomeSelectUiHandler(scene),
|
||||||
|
@ -274,10 +275,8 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
fadeOut(duration: integer): Promise<void> {
|
fadeOut(duration: integer): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (this.overlayActive) {
|
if (this.overlayActive)
|
||||||
resolve();
|
return resolve();
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.overlayActive = true;
|
this.overlayActive = true;
|
||||||
this.overlay.setAlpha(0);
|
this.overlay.setAlpha(0);
|
||||||
this.overlay.setVisible(true);
|
this.overlay.setVisible(true);
|
||||||
|
@ -293,6 +292,8 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
fadeIn(duration: integer): Promise<void> {
|
fadeIn(duration: integer): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
if (!this.overlayActive)
|
||||||
|
return resolve();
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this.overlay,
|
targets: this.overlay,
|
||||||
alpha: 0,
|
alpha: 0,
|
||||||
|
|
12
src/utils.ts
12
src/utils.ts
|
@ -139,10 +139,10 @@ export function decToBin(input: integer): string {
|
||||||
return bin;
|
return bin;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatStat(stat: integer, forHp: boolean = false): string {
|
export function formatLargeNumber(count: integer, threshold: integer): string {
|
||||||
if (stat < (forHp ? 100000 : 1000000))
|
if (count < threshold)
|
||||||
return stat.toString();
|
return count.toString();
|
||||||
let ret = stat.toString();
|
let ret = count.toString();
|
||||||
let suffix = '';
|
let suffix = '';
|
||||||
switch (Math.ceil(ret.length / 3) - 1) {
|
switch (Math.ceil(ret.length / 3) - 1) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -162,6 +162,10 @@ export function formatStat(stat: integer, forHp: boolean = false): string {
|
||||||
return `${ret.slice(0, digits)}${decimalNumber ? `.${decimalNumber}` : ''}${suffix}`;
|
return `${ret.slice(0, digits)}${decimalNumber ? `.${decimalNumber}` : ''}${suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatStat(stat: integer, forHp: boolean = false): string {
|
||||||
|
return formatLargeNumber(stat, forHp ? 100000 : 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
export function getEnumKeys(enumType): string[] {
|
export function getEnumKeys(enumType): string[] {
|
||||||
return Object.values(enumType).filter(v => isNaN(parseInt(v.toString()))).map(v => v.toString());
|
return Object.values(enumType).filter(v => isNaN(parseInt(v.toString()))).map(v => v.toString());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue