mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-23 15:03:24 +02:00
Merge 36a2e52620
into 8d5ba221d8
This commit is contained in:
commit
80644d0c99
@ -1,3 +1,4 @@
|
||||
// TODO: Document this with default values
|
||||
export interface UserInfo {
|
||||
username: string;
|
||||
lastSessionSlot: number;
|
||||
|
@ -15,20 +15,21 @@ import { getBiomeKey } from "#field/arena";
|
||||
import type { Modifier } from "#modifiers/modifier";
|
||||
import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#modifiers/modifier-type";
|
||||
import { vouchers } from "#system/voucher";
|
||||
import type { SessionSaveData } from "#types/save-data";
|
||||
import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
|
||||
import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler";
|
||||
import { isLocal, isLocalServerConnected } from "#utils/common";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
import i18next from "i18next";
|
||||
|
||||
const NO_SAVE_SLOT = -1;
|
||||
|
||||
export class TitlePhase extends Phase {
|
||||
public readonly phaseName = "TitlePhase";
|
||||
private loaded = false;
|
||||
private lastSessionData: SessionSaveData;
|
||||
// TODO: Make `end` take a `GameModes` as a parameter rather than storing it on the class itself
|
||||
public gameMode: GameModes;
|
||||
|
||||
start(): void {
|
||||
async start(): Promise<void> {
|
||||
super.start();
|
||||
|
||||
globalScene.ui.clearText();
|
||||
@ -36,30 +37,46 @@ export class TitlePhase extends Phase {
|
||||
|
||||
globalScene.playBgm("title", true);
|
||||
|
||||
globalScene.gameData
|
||||
.getSession(loggedInUser?.lastSessionSlot ?? -1)
|
||||
.then(sessionData => {
|
||||
if (sessionData) {
|
||||
this.lastSessionData = sessionData;
|
||||
const biomeKey = getBiomeKey(sessionData.arena.biome);
|
||||
const bgTexture = `${biomeKey}_bg`;
|
||||
globalScene.arenaBg.setTexture(bgTexture);
|
||||
}
|
||||
this.showOptions();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
this.showOptions();
|
||||
});
|
||||
const lastSlot = await this.checkLastSaveSlot();
|
||||
await this.showOptions(lastSlot);
|
||||
}
|
||||
|
||||
showOptions(): void {
|
||||
/**
|
||||
* If a user is logged in, check the last save slot they loaded and adjust various variables
|
||||
* to account for it.
|
||||
* @returns A Promise that resolves with the last loaded session's slot ID.
|
||||
* Returns `NO_SAVE_SLOT` if not logged in or no session was found.
|
||||
*/
|
||||
private async checkLastSaveSlot(): Promise<number> {
|
||||
if (loggedInUser == null) {
|
||||
return NO_SAVE_SLOT;
|
||||
}
|
||||
try {
|
||||
const sessionData = await globalScene.gameData.getSession(loggedInUser.lastSessionSlot);
|
||||
if (!sessionData) {
|
||||
return NO_SAVE_SLOT;
|
||||
}
|
||||
|
||||
globalScene.sessionSlotId = loggedInUser.lastSessionSlot;
|
||||
// Set the BG texture to the last save's current biome
|
||||
const biomeKey = getBiomeKey(sessionData.arena.biome);
|
||||
const bgTexture = `${biomeKey}_bg`;
|
||||
globalScene.arenaBg.setTexture(bgTexture);
|
||||
return loggedInUser.lastSessionSlot;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return NO_SAVE_SLOT;
|
||||
}
|
||||
}
|
||||
|
||||
private async showOptions(lastSessionSlot: number): Promise<void> {
|
||||
const options: OptionSelectItem[] = [];
|
||||
if (loggedInUser && loggedInUser.lastSessionSlot > -1) {
|
||||
// Add a "continue" menu if the session slot ID is >-1
|
||||
if (lastSessionSlot > NO_SAVE_SLOT) {
|
||||
options.push({
|
||||
label: i18next.t("continue", { ns: "menu" }),
|
||||
handler: () => {
|
||||
this.loadSaveSlot(this.lastSessionData || !loggedInUser ? -1 : loggedInUser.lastSessionSlot);
|
||||
this.loadSaveSlot(lastSessionSlot);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
@ -136,8 +153,9 @@ export class TitlePhase extends Phase {
|
||||
label: i18next.t("menu:loadGame"),
|
||||
handler: () => {
|
||||
globalScene.ui.setOverlayMode(UiMode.SAVE_SLOT, SaveSlotUiMode.LOAD, (slotId: number) => {
|
||||
if (slotId === -1) {
|
||||
return this.showOptions();
|
||||
if (slotId === NO_SAVE_SLOT) {
|
||||
console.warn("Attempted to load save slot of -1 through load game menu!");
|
||||
return this.showOptions(slotId);
|
||||
}
|
||||
this.loadSaveSlot(slotId);
|
||||
});
|
||||
@ -166,30 +184,26 @@ export class TitlePhase extends Phase {
|
||||
noCancel: true,
|
||||
yOffset: 47,
|
||||
};
|
||||
globalScene.ui.setMode(UiMode.TITLE, config);
|
||||
await globalScene.ui.setMode(UiMode.TITLE, config);
|
||||
}
|
||||
|
||||
loadSaveSlot(slotId: number): void {
|
||||
globalScene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot;
|
||||
// TODO: Make callers actually wait for the save slot to load
|
||||
private async loadSaveSlot(slotId: number): Promise<void> {
|
||||
// TODO: Do we need to `await` this?
|
||||
globalScene.ui.setMode(UiMode.MESSAGE);
|
||||
globalScene.ui.resetModeChain();
|
||||
globalScene.gameData
|
||||
.loadSession(slotId, slotId === -1 ? this.lastSessionData : undefined)
|
||||
.then((success: boolean) => {
|
||||
if (success) {
|
||||
this.loaded = true;
|
||||
if (loggedInUser) {
|
||||
loggedInUser.lastSessionSlot = slotId;
|
||||
}
|
||||
globalScene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end());
|
||||
} else {
|
||||
this.end();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
globalScene.ui.showText(i18next.t("menu:failedToLoadSession"), null);
|
||||
});
|
||||
try {
|
||||
const success = await globalScene.gameData.loadSession(slotId);
|
||||
if (success) {
|
||||
this.loaded = true;
|
||||
globalScene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end());
|
||||
} else {
|
||||
this.end();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
globalScene.ui.showText(i18next.t("menu:failedToLoadSession"), null);
|
||||
}
|
||||
}
|
||||
|
||||
initDailyRun(): void {
|
||||
@ -294,6 +308,7 @@ export class TitlePhase extends Phase {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Refactor this
|
||||
end(): void {
|
||||
if (!this.loaded && !globalScene.gameMode.isDaily) {
|
||||
globalScene.arena.preloadBgm();
|
||||
@ -324,6 +339,7 @@ export class TitlePhase extends Phase {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this to a migrate script instead of running it on save slot load
|
||||
for (const achv of Object.keys(globalScene.gameData.achvUnlocks)) {
|
||||
if (vouchers.hasOwnProperty(achv) && achv !== "CLASSIC_VICTORY") {
|
||||
globalScene.validateVoucher(vouchers[achv]);
|
||||
|
@ -77,6 +77,7 @@ import { applyChallenges } from "#utils/challenge-utils";
|
||||
import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common";
|
||||
import { decrypt, encrypt } from "#utils/data";
|
||||
import { getEnumKeys } from "#utils/enums";
|
||||
import { getSaveDataLocalStorageKey } from "#utils/game-data-utils";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
import { isBeta } from "#utils/utility-vars";
|
||||
import { AES, enc } from "crypto-js";
|
||||
@ -838,57 +839,45 @@ export class GameData {
|
||||
} as SessionSaveData;
|
||||
}
|
||||
|
||||
async getSession(slotId: number): Promise<SessionSaveData | null> {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<SessionSaveData | null>();
|
||||
async getSession(slotId: number): Promise<SessionSaveData | undefined> {
|
||||
// TODO: Do we need this fallback anymore?
|
||||
if (slotId < 0) {
|
||||
resolve(null);
|
||||
return promise;
|
||||
return;
|
||||
}
|
||||
const handleSessionData = async (sessionDataStr: string) => {
|
||||
try {
|
||||
const sessionData = this.parseSessionData(sessionDataStr);
|
||||
resolve(sessionData);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
|
||||
// Check local storage for the cached session data
|
||||
if (bypassLogin || localStorage.getItem(getSaveDataLocalStorageKey(slotId))) {
|
||||
const sessionData = localStorage.getItem(getSaveDataLocalStorageKey(slotId));
|
||||
if (!sessionData) {
|
||||
console.error("No session data found!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`)) {
|
||||
const response = await pokerogueApi.savedata.session.get({ slot: slotId, clientSessionId });
|
||||
|
||||
if (!response || response?.length === 0 || response?.[0] !== "{") {
|
||||
console.error(response);
|
||||
resolve(null);
|
||||
return promise;
|
||||
}
|
||||
|
||||
localStorage.setItem(
|
||||
`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
|
||||
encrypt(response, bypassLogin),
|
||||
);
|
||||
|
||||
await handleSessionData(response);
|
||||
return promise;
|
||||
return this.parseSessionData(decrypt(sessionData, bypassLogin));
|
||||
}
|
||||
const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
|
||||
if (sessionData) {
|
||||
await handleSessionData(decrypt(sessionData, bypassLogin));
|
||||
return promise;
|
||||
|
||||
// Ask the server API for the save data and store it in localstorage
|
||||
const response = await pokerogueApi.savedata.session.get({ slot: slotId, clientSessionId });
|
||||
|
||||
// TODO: This is a far cry from proper JSON validation
|
||||
if (response == null || response.length === 0 || response.charAt(0) !== "{") {
|
||||
console.error("Invalid save data JSON detected!", response);
|
||||
return;
|
||||
}
|
||||
resolve(null);
|
||||
return promise;
|
||||
|
||||
localStorage.setItem(getSaveDataLocalStorageKey(slotId), encrypt(response, bypassLogin));
|
||||
|
||||
return this.parseSessionData(response);
|
||||
}
|
||||
|
||||
async renameSession(slotId: number, newName: string): Promise<boolean> {
|
||||
if (slotId < 0) {
|
||||
return false;
|
||||
}
|
||||
// TODO: Why do we consider renaming to an empty string successful if it does nothing?
|
||||
if (newName === "") {
|
||||
return true;
|
||||
}
|
||||
const sessionData: SessionSaveData | null = await this.getSession(slotId);
|
||||
|
||||
const sessionData = await this.getSession(slotId);
|
||||
if (!sessionData) {
|
||||
return false;
|
||||
}
|
||||
@ -902,10 +891,7 @@ export class GameData {
|
||||
const trainerId = this.trainerId;
|
||||
|
||||
if (bypassLogin) {
|
||||
localStorage.setItem(
|
||||
`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
|
||||
encrypt(updatedDataStr, bypassLogin),
|
||||
);
|
||||
localStorage.setItem(getSaveDataLocalStorageKey(slotId), encrypt(updatedDataStr, bypassLogin));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -917,186 +903,177 @@ export class GameData {
|
||||
if (response) {
|
||||
return false;
|
||||
}
|
||||
localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`, encrypted);
|
||||
localStorage.setItem(getSaveDataLocalStorageKey(slotId), encrypted);
|
||||
const success = await updateUserInfo();
|
||||
return !(success !== null && !success);
|
||||
}
|
||||
|
||||
async loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
|
||||
const { promise, resolve, reject } = Promise.withResolvers<boolean>();
|
||||
try {
|
||||
const initSessionFromData = (fromSession: SessionSaveData) => {
|
||||
if (isLocal || isBeta) {
|
||||
try {
|
||||
console.debug(
|
||||
this.parseSessionData(
|
||||
JSON.stringify(fromSession, (_, v: any) => (typeof v === "bigint" ? v.toString() : v)),
|
||||
),
|
||||
);
|
||||
} catch (err) {
|
||||
console.debug("Attempt to log session data failed:", err);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Load stored session data and re-initialize the game with its contents.
|
||||
* @param slotIndex - The 0-indexed position of the save slot to load.
|
||||
* Values `<=0` will be considered invalid.
|
||||
* @returns A Promise that resolves with whether the session load succeeded
|
||||
* (i.e. whether a save in the given slot exists)
|
||||
*/
|
||||
public async loadSession(slotIndex: number): Promise<boolean> {
|
||||
const sessionData = await this.getSession(slotIndex);
|
||||
if (!sessionData) {
|
||||
return false;
|
||||
}
|
||||
this.initSessionFromData(sessionData);
|
||||
return true;
|
||||
}
|
||||
|
||||
globalScene.gameMode = getGameMode(fromSession.gameMode || GameModes.CLASSIC);
|
||||
if (fromSession.challenges) {
|
||||
globalScene.gameMode.challenges = fromSession.challenges.map(c => c.toChallenge());
|
||||
}
|
||||
|
||||
globalScene.setSeed(fromSession.seed || globalScene.game.config.seed[0]);
|
||||
globalScene.resetSeed();
|
||||
|
||||
console.log("Seed:", globalScene.seed);
|
||||
|
||||
globalScene.sessionPlayTime = fromSession.playTime || 0;
|
||||
globalScene.lastSavePlayTime = 0;
|
||||
|
||||
const loadPokemonAssets: Promise<void>[] = [];
|
||||
|
||||
const party = globalScene.getPlayerParty();
|
||||
party.splice(0, party.length);
|
||||
|
||||
for (const p of fromSession.party) {
|
||||
const pokemon = p.toPokemon() as PlayerPokemon;
|
||||
pokemon.setVisible(false);
|
||||
loadPokemonAssets.push(pokemon.loadAssets(false));
|
||||
party.push(pokemon);
|
||||
}
|
||||
|
||||
Object.keys(globalScene.pokeballCounts).forEach((key: string) => {
|
||||
globalScene.pokeballCounts[key] = fromSession.pokeballCounts[key] || 0;
|
||||
});
|
||||
if (Overrides.POKEBALL_OVERRIDE.active) {
|
||||
globalScene.pokeballCounts = Overrides.POKEBALL_OVERRIDE.pokeballs;
|
||||
}
|
||||
|
||||
globalScene.money = Math.floor(fromSession.money || 0);
|
||||
globalScene.updateMoneyText();
|
||||
|
||||
if (globalScene.money > this.gameStats.highestMoney) {
|
||||
this.gameStats.highestMoney = globalScene.money;
|
||||
}
|
||||
|
||||
globalScene.score = fromSession.score;
|
||||
globalScene.updateScoreText();
|
||||
|
||||
globalScene.mysteryEncounterSaveData = new MysteryEncounterSaveData(fromSession.mysteryEncounterSaveData);
|
||||
|
||||
globalScene.newArena(fromSession.arena.biome, fromSession.playerFaints);
|
||||
|
||||
const battleType = fromSession.battleType || 0;
|
||||
const trainerConfig = fromSession.trainer ? trainerConfigs[fromSession.trainer.trainerType] : null;
|
||||
const mysteryEncounterType =
|
||||
fromSession.mysteryEncounterType !== -1 ? fromSession.mysteryEncounterType : undefined;
|
||||
const battle = globalScene.newBattle(
|
||||
fromSession.waveIndex,
|
||||
battleType,
|
||||
fromSession.trainer,
|
||||
battleType === BattleType.TRAINER
|
||||
? trainerConfig?.doubleOnly || fromSession.trainer?.variant === TrainerVariant.DOUBLE
|
||||
: fromSession.enemyParty.length > 1,
|
||||
mysteryEncounterType,
|
||||
// TODO: This needs a giant refactor and overhaul
|
||||
private async initSessionFromData(fromSession: SessionSaveData): Promise<void> {
|
||||
if (isLocal || isBeta) {
|
||||
try {
|
||||
console.debug(
|
||||
this.parseSessionData(JSON.stringify(fromSession, (_, v: any) => (typeof v === "bigint" ? v.toString() : v))),
|
||||
);
|
||||
battle.enemyLevels = fromSession.enemyParty.map(p => p.level);
|
||||
|
||||
globalScene.arena.init();
|
||||
|
||||
fromSession.enemyParty.forEach((enemyData, e) => {
|
||||
const enemyPokemon = enemyData.toPokemon(
|
||||
battleType,
|
||||
e,
|
||||
fromSession.trainer?.variant === TrainerVariant.DOUBLE,
|
||||
) as EnemyPokemon;
|
||||
battle.enemyParty[e] = enemyPokemon;
|
||||
if (battleType === BattleType.WILD) {
|
||||
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
|
||||
}
|
||||
|
||||
loadPokemonAssets.push(enemyPokemon.loadAssets());
|
||||
});
|
||||
|
||||
globalScene.arena.weather = fromSession.arena.weather;
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new WeatherChangedEvent(
|
||||
WeatherType.NONE,
|
||||
globalScene.arena.weather?.weatherType!,
|
||||
globalScene.arena.weather?.turnsLeft!,
|
||||
globalScene.arena.weather?.maxDuration!,
|
||||
),
|
||||
); // TODO: is this bang correct?
|
||||
|
||||
globalScene.arena.terrain = fromSession.arena.terrain;
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new TerrainChangedEvent(
|
||||
TerrainType.NONE,
|
||||
globalScene.arena.terrain?.terrainType!,
|
||||
globalScene.arena.terrain?.turnsLeft!,
|
||||
globalScene.arena.terrain?.maxDuration!,
|
||||
),
|
||||
); // TODO: is this bang correct?
|
||||
|
||||
globalScene.arena.playerTerasUsed = fromSession.arena.playerTerasUsed;
|
||||
|
||||
globalScene.arena.tags = fromSession.arena.tags;
|
||||
if (globalScene.arena.tags) {
|
||||
for (const tag of globalScene.arena.tags) {
|
||||
if (tag instanceof EntryHazardTag) {
|
||||
const { tagType, side, turnCount, maxDuration, layers, maxLayers } = tag as EntryHazardTag;
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new TagAddedEvent(tagType, side, turnCount, maxDuration, layers, maxLayers),
|
||||
);
|
||||
} else {
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new TagAddedEvent(tag.tagType, tag.side, tag.turnCount, tag.maxDuration),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
globalScene.arena.positionalTagManager.tags = fromSession.arena.positionalTags.map(tag =>
|
||||
loadPositionalTag(tag),
|
||||
);
|
||||
|
||||
if (globalScene.modifiers.length > 0) {
|
||||
console.warn("Existing modifiers not cleared on session load, deleting...");
|
||||
globalScene.modifiers = [];
|
||||
}
|
||||
for (const modifierData of fromSession.modifiers) {
|
||||
const modifier = modifierData.toModifier(Modifier[modifierData.className]);
|
||||
if (modifier) {
|
||||
globalScene.addModifier(modifier, true);
|
||||
}
|
||||
}
|
||||
globalScene.updateModifiers(true);
|
||||
|
||||
for (const enemyModifierData of fromSession.enemyModifiers) {
|
||||
const modifier = enemyModifierData.toModifier(Modifier[enemyModifierData.className]);
|
||||
if (modifier) {
|
||||
globalScene.addEnemyModifier(modifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
globalScene.updateModifiers(false);
|
||||
|
||||
Promise.all(loadPokemonAssets).then(() => resolve(true));
|
||||
};
|
||||
if (sessionData) {
|
||||
initSessionFromData(sessionData);
|
||||
} else {
|
||||
this.getSession(slotId)
|
||||
.then(data => {
|
||||
return data && initSessionFromData(data);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
return;
|
||||
});
|
||||
} catch (err) {
|
||||
console.debug("Attempt to log session data failed: ", err);
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
return promise;
|
||||
globalScene.gameMode = getGameMode(fromSession.gameMode || GameModes.CLASSIC);
|
||||
if (fromSession.challenges) {
|
||||
globalScene.gameMode.challenges = fromSession.challenges.map(c => c.toChallenge());
|
||||
}
|
||||
|
||||
globalScene.setSeed(fromSession.seed || globalScene.game.config.seed[0]);
|
||||
globalScene.resetSeed();
|
||||
|
||||
console.log("Seed:", globalScene.seed);
|
||||
|
||||
globalScene.sessionPlayTime = fromSession.playTime || 0;
|
||||
globalScene.lastSavePlayTime = 0;
|
||||
|
||||
const loadPokemonAssets: Promise<void>[] = [];
|
||||
|
||||
const party = globalScene.getPlayerParty();
|
||||
party.splice(0, party.length);
|
||||
|
||||
for (const p of fromSession.party) {
|
||||
const pokemon = p.toPokemon() as PlayerPokemon;
|
||||
pokemon.setVisible(false);
|
||||
loadPokemonAssets.push(pokemon.loadAssets(false));
|
||||
party.push(pokemon);
|
||||
}
|
||||
|
||||
Object.keys(globalScene.pokeballCounts).forEach((key: string) => {
|
||||
globalScene.pokeballCounts[key] = fromSession.pokeballCounts[key] || 0;
|
||||
});
|
||||
if (Overrides.POKEBALL_OVERRIDE.active) {
|
||||
globalScene.pokeballCounts = Overrides.POKEBALL_OVERRIDE.pokeballs;
|
||||
}
|
||||
|
||||
globalScene.money = Math.floor(fromSession.money || 0);
|
||||
globalScene.updateMoneyText();
|
||||
|
||||
if (globalScene.money > this.gameStats.highestMoney) {
|
||||
this.gameStats.highestMoney = globalScene.money;
|
||||
}
|
||||
|
||||
globalScene.score = fromSession.score;
|
||||
globalScene.updateScoreText();
|
||||
|
||||
globalScene.mysteryEncounterSaveData = new MysteryEncounterSaveData(fromSession.mysteryEncounterSaveData);
|
||||
|
||||
globalScene.newArena(fromSession.arena.biome, fromSession.playerFaints);
|
||||
|
||||
const battleType = fromSession.battleType || 0;
|
||||
const trainerConfig = fromSession.trainer ? trainerConfigs[fromSession.trainer.trainerType] : null;
|
||||
const mysteryEncounterType = fromSession.mysteryEncounterType !== -1 ? fromSession.mysteryEncounterType : undefined;
|
||||
const battle = globalScene.newBattle(
|
||||
fromSession.waveIndex,
|
||||
battleType,
|
||||
fromSession.trainer,
|
||||
battleType === BattleType.TRAINER
|
||||
? trainerConfig?.doubleOnly || fromSession.trainer?.variant === TrainerVariant.DOUBLE
|
||||
: fromSession.enemyParty.length > 1,
|
||||
mysteryEncounterType,
|
||||
);
|
||||
battle.enemyLevels = fromSession.enemyParty.map(p => p.level);
|
||||
|
||||
globalScene.arena.init();
|
||||
|
||||
fromSession.enemyParty.forEach((enemyData, e) => {
|
||||
const enemyPokemon = enemyData.toPokemon(
|
||||
battleType,
|
||||
e,
|
||||
fromSession.trainer?.variant === TrainerVariant.DOUBLE,
|
||||
) as EnemyPokemon;
|
||||
battle.enemyParty[e] = enemyPokemon;
|
||||
if (battleType === BattleType.WILD) {
|
||||
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
|
||||
}
|
||||
|
||||
loadPokemonAssets.push(enemyPokemon.loadAssets());
|
||||
});
|
||||
|
||||
globalScene.arena.weather = fromSession.arena.weather;
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new WeatherChangedEvent(
|
||||
WeatherType.NONE,
|
||||
globalScene.arena.weather?.weatherType!,
|
||||
globalScene.arena.weather?.turnsLeft!,
|
||||
globalScene.arena.weather?.maxDuration!,
|
||||
),
|
||||
); // TODO: is this bang correct?
|
||||
|
||||
globalScene.arena.terrain = fromSession.arena.terrain;
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new TerrainChangedEvent(
|
||||
TerrainType.NONE,
|
||||
globalScene.arena.terrain?.terrainType!,
|
||||
globalScene.arena.terrain?.turnsLeft!,
|
||||
globalScene.arena.terrain?.maxDuration!,
|
||||
),
|
||||
); // TODO: is this bang correct?
|
||||
|
||||
globalScene.arena.playerTerasUsed = fromSession.arena.playerTerasUsed;
|
||||
|
||||
globalScene.arena.tags = fromSession.arena.tags;
|
||||
if (globalScene.arena.tags) {
|
||||
for (const tag of globalScene.arena.tags) {
|
||||
if (tag instanceof EntryHazardTag) {
|
||||
const { tagType, side, turnCount, maxDuration, layers, maxLayers } = tag as EntryHazardTag;
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new TagAddedEvent(tagType, side, turnCount, maxDuration, layers, maxLayers),
|
||||
);
|
||||
} else {
|
||||
globalScene.arena.eventTarget.dispatchEvent(
|
||||
new TagAddedEvent(tag.tagType, tag.side, tag.turnCount, tag.maxDuration),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
globalScene.arena.positionalTagManager.tags = fromSession.arena.positionalTags.map(tag => loadPositionalTag(tag));
|
||||
|
||||
if (globalScene.modifiers.length > 0) {
|
||||
console.warn("Existing modifiers not cleared on session load, deleting...");
|
||||
globalScene.modifiers = [];
|
||||
}
|
||||
for (const modifierData of fromSession.modifiers) {
|
||||
const modifier = modifierData.toModifier(Modifier[modifierData.className]);
|
||||
if (modifier) {
|
||||
globalScene.addModifier(modifier, true);
|
||||
}
|
||||
}
|
||||
globalScene.updateModifiers(true);
|
||||
|
||||
for (const enemyModifierData of fromSession.enemyModifiers) {
|
||||
const modifier = enemyModifierData.toModifier(Modifier[enemyModifierData.className]);
|
||||
if (modifier) {
|
||||
globalScene.addEnemyModifier(modifier, true);
|
||||
}
|
||||
}
|
||||
|
||||
globalScene.updateModifiers(false);
|
||||
|
||||
await Promise.all(loadPokemonAssets);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1108,7 +1085,7 @@ export class GameData {
|
||||
deleteSession(slotId: number): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (bypassLogin) {
|
||||
localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
|
||||
localStorage.removeItem(getSaveDataLocalStorageKey(slotId));
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
@ -1129,7 +1106,7 @@ export class GameData {
|
||||
loggedInUser.lastSessionSlot = -1;
|
||||
}
|
||||
|
||||
localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
|
||||
localStorage.removeItem(getSaveDataLocalStorageKey(slotId));
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
@ -1173,7 +1150,7 @@ export class GameData {
|
||||
let result: [boolean, boolean] = [false, false];
|
||||
|
||||
if (bypassLogin) {
|
||||
localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
|
||||
localStorage.removeItem(getSaveDataLocalStorageKey(slotId));
|
||||
result = [true, true];
|
||||
} else {
|
||||
const sessionData = this.getSessionSaveData();
|
||||
@ -1188,7 +1165,7 @@ export class GameData {
|
||||
if (loggedInUser) {
|
||||
loggedInUser!.lastSessionSlot = -1;
|
||||
}
|
||||
localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`);
|
||||
localStorage.removeItem(getSaveDataLocalStorageKey(slotId));
|
||||
} else {
|
||||
if (jsonResponse?.error?.startsWith("session out of date")) {
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
|
15
src/utils/game-data-utils.ts
Normal file
15
src/utils/game-data-utils.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { loggedInUser } from "#app/account";
|
||||
|
||||
/**
|
||||
* Obtain the local storage key corresponding to a given save slot.
|
||||
* @param slotId - The numerical save slot ID
|
||||
* Will throw an error if `<0` (in line with standard util functions)
|
||||
* @returns The local storage key used to access the save data for the given slot.
|
||||
*/
|
||||
export function getSaveDataLocalStorageKey(slotId: number): string {
|
||||
if (slotId < 0) {
|
||||
throw new Error("Cannot access a negative save slot ID from localstorage!");
|
||||
}
|
||||
|
||||
return `sessionData${slotId || ""}_${loggedInUser?.username}`;
|
||||
}
|
@ -56,7 +56,7 @@ export class ReloadHelper extends GameManagerHelper {
|
||||
);
|
||||
this.game.scene.modifiers = [];
|
||||
}
|
||||
titlePhase.loadSaveSlot(-1); // Load the desired session data
|
||||
titlePhase["loadSaveSlot"](0); // Load the desired session data
|
||||
this.game.phaseInterceptor.shiftPhase(); // Loading the save slot also ended TitlePhase, clean it up
|
||||
|
||||
// Run through prompts for switching Pokemon, copied from classicModeHelper.ts
|
||||
|
Loading…
Reference in New Issue
Block a user