[Dev] Save Data Version Migration (#4080)
* Initial Draft * Successfuly Migration with Vitamins and White Herb * Apply Session Data Patches Earlier * Remove Stray `console.log`
This commit is contained in:
parent
b36823af4a
commit
ffcedfd9a4
|
@ -1,7 +1,7 @@
|
|||
import i18next from "i18next";
|
||||
import BattleScene, { PokeballCounts, bypassLogin } from "../battle-scene";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "../field/pokemon";
|
||||
import { pokemonEvolutions, pokemonPrevolutions } from "../data/pokemon-evolutions";
|
||||
import { pokemonPrevolutions } from "../data/pokemon-evolutions";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, noStarterFormKeys, speciesStarters } from "../data/pokemon-species";
|
||||
import * as Utils from "../utils";
|
||||
import Overrides from "#app/overrides";
|
||||
|
@ -27,7 +27,7 @@ import { Tutorial } from "../tutorial";
|
|||
import { speciesEggMoves } from "../data/egg-moves";
|
||||
import { allMoves } from "../data/move";
|
||||
import { TrainerVariant } from "../field/trainer";
|
||||
import { Variant, variantData } from "#app/data/variant";
|
||||
import { Variant } from "#app/data/variant";
|
||||
import {setSettingGamepad, SettingGamepad, settingGamepadDefaults} from "./settings/settings-gamepad";
|
||||
import {setSettingKeyboard, SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena.js";
|
||||
|
@ -45,6 +45,7 @@ import { TerrainType } from "#app/data/terrain.js";
|
|||
import { OutdatedPhase } from "#app/phases/outdated-phase.js";
|
||||
import { ReloadSessionPhase } from "#app/phases/reload-session-phase.js";
|
||||
import { RUN_HISTORY_LIMIT } from "#app/ui/run-history-ui-handler";
|
||||
import { applySessionDataPatches, applySettingsDataPatches, applySystemDataPatches } from "./version-converter";
|
||||
|
||||
export const defaultStarterSpecies: Species[] = [
|
||||
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
||||
|
@ -93,7 +94,7 @@ export function decrypt(data: string, bypassLogin: boolean): string {
|
|||
: (data: string) => AES.decrypt(data, saveKey).toString(enc.Utf8))(data);
|
||||
}
|
||||
|
||||
interface SystemSaveData {
|
||||
export interface SystemSaveData {
|
||||
trainerId: integer;
|
||||
secretId: integer;
|
||||
gender: PlayerGender;
|
||||
|
@ -456,17 +457,14 @@ export class GameData {
|
|||
|
||||
localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemDataStr, bypassLogin));
|
||||
|
||||
/*const versions = [ this.scene.game.config.gameVersion, data.gameVersion || '0.0.0' ];
|
||||
|
||||
if (versions[0] !== versions[1]) {
|
||||
const [ versionNumbers, oldVersionNumbers ] = versions.map(ver => ver.split('.').map(v => parseInt(v)));
|
||||
}*/
|
||||
const lsItemKey = `runHistoryData_${loggedInUser?.username}`;
|
||||
const lsItem = localStorage.getItem(lsItemKey);
|
||||
if (!lsItem) {
|
||||
localStorage.setItem(lsItemKey, "");
|
||||
}
|
||||
|
||||
applySystemDataPatches(systemData);
|
||||
|
||||
this.trainerId = systemData.trainerId;
|
||||
this.secretId = systemData.secretId;
|
||||
|
||||
|
@ -474,9 +472,7 @@ export class GameData {
|
|||
|
||||
this.saveSetting(SettingKeys.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0);
|
||||
|
||||
const initStarterData = !systemData.starterData;
|
||||
|
||||
if (initStarterData) {
|
||||
if (!systemData.starterData) {
|
||||
this.initStarterData();
|
||||
|
||||
if (systemData["starterMoveData"]) {
|
||||
|
@ -494,25 +490,20 @@ export class GameData {
|
|||
}
|
||||
|
||||
this.migrateStarterAbilities(systemData, this.starterData);
|
||||
} else {
|
||||
if ([ "1.0.0", "1.0.1" ].includes(systemData.gameVersion)) {
|
||||
this.migrateStarterAbilities(systemData);
|
||||
}
|
||||
//this.fixVariantData(systemData);
|
||||
this.fixStarterData(systemData);
|
||||
// Migrate ability starter data if empty for caught species
|
||||
Object.keys(systemData.starterData).forEach(sd => {
|
||||
if (systemData.dexData[sd].caughtAttr && !systemData.starterData[sd].abilityAttr) {
|
||||
systemData.starterData[sd].abilityAttr = 1;
|
||||
|
||||
const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species);
|
||||
for (const s of starterIds) {
|
||||
this.starterData[s].candyCount += this.dexData[s].caughtCount;
|
||||
this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2;
|
||||
if (this.dexData[s].caughtAttr & DexAttr.SHINY) {
|
||||
this.starterData[s].candyCount += 4;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.starterData = systemData.starterData;
|
||||
}
|
||||
|
||||
if (systemData.gameStats) {
|
||||
if (systemData.gameStats.legendaryPokemonCaught !== undefined && systemData.gameStats.subLegendaryPokemonCaught === undefined) {
|
||||
this.fixLegendaryStats(systemData);
|
||||
}
|
||||
this.gameStats = systemData.gameStats;
|
||||
}
|
||||
|
||||
|
@ -558,17 +549,6 @@ export class GameData {
|
|||
this.consolidateDexData(this.dexData);
|
||||
this.defaultDexData = null;
|
||||
|
||||
if (initStarterData) {
|
||||
const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species);
|
||||
for (const s of starterIds) {
|
||||
this.starterData[s].candyCount += this.dexData[s].caughtCount;
|
||||
this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2;
|
||||
if (this.dexData[s].caughtAttr & DexAttr.SHINY) {
|
||||
this.starterData[s].candyCount += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
@ -747,6 +727,7 @@ export class GameData {
|
|||
setSetting(this.scene, setting, valueIndex);
|
||||
|
||||
settings[setting] = valueIndex;
|
||||
settings["gameVersion"] = this.scene.game.config.gameVersion;
|
||||
|
||||
localStorage.setItem("settings", JSON.stringify(settings));
|
||||
|
||||
|
@ -857,13 +838,7 @@ export class GameData {
|
|||
|
||||
const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct?
|
||||
|
||||
// TODO: Remove this block after save migration is implemented
|
||||
if (settings.hasOwnProperty("REROLL_TARGET") && !settings.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) {
|
||||
settings[SettingKeys.Shop_Cursor_Target] = settings["REROLL_TARGET"];
|
||||
delete settings["REROLL_TARGET"];
|
||||
localStorage.setItem("settings", JSON.stringify(settings));
|
||||
}
|
||||
// End of block to remove
|
||||
applySettingsDataPatches(settings);
|
||||
|
||||
for (const setting of Object.keys(settings)) {
|
||||
setSetting(this.scene, setting, settings[setting]);
|
||||
|
@ -1226,7 +1201,7 @@ export class GameData {
|
|||
}
|
||||
|
||||
parseSessionData(dataStr: string): SessionSaveData {
|
||||
return JSON.parse(dataStr, (k: string, v: any) => {
|
||||
const sessionData = JSON.parse(dataStr, (k: string, v: any) => {
|
||||
/*const versions = [ scene.game.config.gameVersion, sessionData.gameVersion || '0.0.0' ];
|
||||
|
||||
if (versions[0] !== versions[1]) {
|
||||
|
@ -1283,6 +1258,10 @@ export class GameData {
|
|||
|
||||
return v;
|
||||
}) as SessionSaveData;
|
||||
|
||||
applySessionDataPatches(sessionData);
|
||||
|
||||
return sessionData;
|
||||
}
|
||||
|
||||
saveAll(scene: BattleScene, skipVerification: boolean = false, sync: boolean = false, useCachedSession: boolean = false, useCachedSystem: boolean = false): Promise<boolean> {
|
||||
|
@ -1885,75 +1864,4 @@ export class GameData {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixVariantData(systemData: SystemSaveData): void {
|
||||
const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species);
|
||||
const starterData = systemData.starterData;
|
||||
const dexData = systemData.dexData;
|
||||
if (starterIds.find(id => (dexData[id].caughtAttr & DexAttr.VARIANT_2 || dexData[id].caughtAttr & DexAttr.VARIANT_3) && !variantData[id])) {
|
||||
for (const s of starterIds) {
|
||||
const species = getPokemonSpecies(s);
|
||||
if (variantData[s]) {
|
||||
const tempCaughtAttr = dexData[s].caughtAttr;
|
||||
let seenVariant2 = false;
|
||||
let seenVariant3 = false;
|
||||
const checkEvoSpecies = (es: Species) => {
|
||||
seenVariant2 ||= !!(dexData[es].seenAttr & DexAttr.VARIANT_2);
|
||||
seenVariant3 ||= !!(dexData[es].seenAttr & DexAttr.VARIANT_3);
|
||||
if (pokemonEvolutions.hasOwnProperty(es)) {
|
||||
for (const pe of pokemonEvolutions[es]) {
|
||||
checkEvoSpecies(pe.speciesId);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkEvoSpecies(s);
|
||||
if (dexData[s].caughtAttr & DexAttr.VARIANT_2 && !seenVariant2) {
|
||||
dexData[s].caughtAttr ^= DexAttr.VARIANT_2;
|
||||
}
|
||||
if (dexData[s].caughtAttr & DexAttr.VARIANT_3 && !seenVariant3) {
|
||||
dexData[s].caughtAttr ^= DexAttr.VARIANT_3;
|
||||
}
|
||||
starterData[s].abilityAttr = (tempCaughtAttr & DexAttr.DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0)
|
||||
| (tempCaughtAttr & DexAttr.VARIANT_2 && species.ability2 ? AbilityAttr.ABILITY_2 : 0)
|
||||
| (tempCaughtAttr & DexAttr.VARIANT_3 && species.abilityHidden ? AbilityAttr.ABILITY_HIDDEN : 0);
|
||||
} else {
|
||||
const tempCaughtAttr = dexData[s].caughtAttr;
|
||||
if (dexData[s].caughtAttr & DexAttr.VARIANT_2) {
|
||||
dexData[s].caughtAttr ^= DexAttr.VARIANT_2;
|
||||
}
|
||||
if (dexData[s].caughtAttr & DexAttr.VARIANT_3) {
|
||||
dexData[s].caughtAttr ^= DexAttr.VARIANT_3;
|
||||
}
|
||||
starterData[s].abilityAttr = (tempCaughtAttr & DexAttr.DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0)
|
||||
| (tempCaughtAttr & DexAttr.VARIANT_2 && species.ability2 ? AbilityAttr.ABILITY_2 : 0)
|
||||
| (tempCaughtAttr & DexAttr.VARIANT_3 && species.abilityHidden ? AbilityAttr.ABILITY_HIDDEN : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixStarterData(systemData: SystemSaveData): void {
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
systemData.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
|
||||
systemData.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
|
||||
}
|
||||
}
|
||||
|
||||
fixLegendaryStats(systemData: SystemSaveData): void {
|
||||
systemData.gameStats.subLegendaryPokemonSeen = 0;
|
||||
systemData.gameStats.subLegendaryPokemonCaught = 0;
|
||||
systemData.gameStats.subLegendaryPokemonHatched = 0;
|
||||
allSpecies.filter(s => s.subLegendary).forEach(s => {
|
||||
const dexEntry = systemData.dexData[s.speciesId];
|
||||
systemData.gameStats.subLegendaryPokemonSeen += dexEntry.seenCount;
|
||||
systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen - dexEntry.seenCount, 0);
|
||||
systemData.gameStats.subLegendaryPokemonCaught += dexEntry.caughtCount;
|
||||
systemData.gameStats.legendaryPokemonCaught = Math.max(systemData.gameStats.legendaryPokemonCaught - dexEntry.caughtCount, 0);
|
||||
systemData.gameStats.subLegendaryPokemonHatched += dexEntry.hatchedCount;
|
||||
systemData.gameStats.legendaryPokemonHatched = Math.max(systemData.gameStats.legendaryPokemonHatched - dexEntry.hatchedCount, 0);
|
||||
});
|
||||
systemData.gameStats.subLegendaryPokemonSeen = Math.max(systemData.gameStats.subLegendaryPokemonSeen, systemData.gameStats.subLegendaryPokemonCaught);
|
||||
systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen, systemData.gameStats.legendaryPokemonCaught);
|
||||
systemData.gameStats.mythicalPokemonSeen = Math.max(systemData.gameStats.mythicalPokemonSeen, systemData.gameStats.mythicalPokemonCaught);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ import { PersistentModifier } from "../modifier/modifier";
|
|||
import { GeneratedPersistentModifierType, ModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type";
|
||||
|
||||
export default class ModifierData {
|
||||
private player: boolean;
|
||||
private typeId: string;
|
||||
private typePregenArgs: any[];
|
||||
private args: any[];
|
||||
private stackCount: integer;
|
||||
public player: boolean;
|
||||
public typeId: string;
|
||||
public typePregenArgs: any[];
|
||||
public args: any[];
|
||||
public stackCount: integer;
|
||||
|
||||
public className: string;
|
||||
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
import { allSpecies } from "#app/data/pokemon-species.js";
|
||||
import { AbilityAttr, defaultStarterSpecies, DexAttr, SessionSaveData, SystemSaveData } from "./game-data";
|
||||
import { SettingKeys } from "./settings/settings";
|
||||
|
||||
const LATEST_VERSION = "1.0.5";
|
||||
|
||||
export function applySessionDataPatches(data: SessionSaveData) {
|
||||
const curVersion = data.gameVersion;
|
||||
if (curVersion !== LATEST_VERSION) {
|
||||
switch (curVersion) {
|
||||
case "1.0.0":
|
||||
case "1.0.1":
|
||||
case "1.0.2":
|
||||
case "1.0.3":
|
||||
case "1.0.4":
|
||||
// --- PATCHES ---
|
||||
|
||||
// Fix Battle Items, Vitamins, and Lures
|
||||
data.modifiers.forEach((m) => {
|
||||
if (m.className === "PokemonBaseStatModifier") {
|
||||
m.className = "BaseStatModifier";
|
||||
} else if (m.className === "PokemonResetNegativeStatStageModifier") {
|
||||
m.className = "ResetNegativeStatStageModifier";
|
||||
} else if (m.className === "TempBattleStatBoosterModifier") {
|
||||
m.className = "TempStatStageBoosterModifier";
|
||||
m.typeId = "TEMP_STAT_STAGE_BOOSTER";
|
||||
|
||||
// Migration from TempBattleStat to Stat
|
||||
const newStat = m.typePregenArgs[0] + 1;
|
||||
m.typePregenArgs[0] = newStat;
|
||||
|
||||
// From [ stat, battlesLeft ] to [ stat, maxBattles, battleCount ]
|
||||
m.args = [ newStat, 5, m.args[1] ];
|
||||
} else if (m.className === "DoubleBattleChanceBoosterModifier") {
|
||||
let maxBattles: number;
|
||||
switch (m.typeId) {
|
||||
case "MAX_LURE":
|
||||
maxBattles = 30;
|
||||
break;
|
||||
case "SUPER_LURE":
|
||||
maxBattles = 15;
|
||||
break;
|
||||
default:
|
||||
maxBattles = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
// From [ battlesLeft ] to [ maxBattles, battleCount ]
|
||||
m.args = [ maxBattles, m.args[0] ];
|
||||
}
|
||||
});
|
||||
|
||||
data.enemyModifiers.forEach((m) => {
|
||||
if (m.className === "PokemonBaseStatModifier") {
|
||||
m.className = "BaseStatModifier";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
data.gameVersion = LATEST_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
export function applySystemDataPatches(data: SystemSaveData) {
|
||||
const curVersion = data.gameVersion;
|
||||
if (curVersion !== LATEST_VERSION) {
|
||||
switch (curVersion) {
|
||||
case "1.0.0":
|
||||
case "1.0.1":
|
||||
case "1.0.2":
|
||||
case "1.0.3":
|
||||
case "1.0.4":
|
||||
// --- LEGACY PATCHES ---
|
||||
if (data.starterData) {
|
||||
// Migrate ability starter data if empty for caught species
|
||||
Object.keys(data.starterData).forEach(sd => {
|
||||
if (data.dexData[sd].caughtAttr && !data.starterData[sd].abilityAttr) {
|
||||
data.starterData[sd].abilityAttr = 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fix Legendary Stats
|
||||
if (data.gameStats && (data.gameStats.legendaryPokemonCaught !== undefined && data.gameStats.subLegendaryPokemonCaught === undefined)) {
|
||||
data.gameStats.subLegendaryPokemonSeen = 0;
|
||||
data.gameStats.subLegendaryPokemonCaught = 0;
|
||||
data.gameStats.subLegendaryPokemonHatched = 0;
|
||||
allSpecies.filter(s => s.subLegendary).forEach(s => {
|
||||
const dexEntry = data.dexData[s.speciesId];
|
||||
data.gameStats.subLegendaryPokemonSeen += dexEntry.seenCount;
|
||||
data.gameStats.legendaryPokemonSeen = Math.max(data.gameStats.legendaryPokemonSeen - dexEntry.seenCount, 0);
|
||||
data.gameStats.subLegendaryPokemonCaught += dexEntry.caughtCount;
|
||||
data.gameStats.legendaryPokemonCaught = Math.max(data.gameStats.legendaryPokemonCaught - dexEntry.caughtCount, 0);
|
||||
data.gameStats.subLegendaryPokemonHatched += dexEntry.hatchedCount;
|
||||
data.gameStats.legendaryPokemonHatched = Math.max(data.gameStats.legendaryPokemonHatched - dexEntry.hatchedCount, 0);
|
||||
});
|
||||
data.gameStats.subLegendaryPokemonSeen = Math.max(data.gameStats.subLegendaryPokemonSeen, data.gameStats.subLegendaryPokemonCaught);
|
||||
data.gameStats.legendaryPokemonSeen = Math.max(data.gameStats.legendaryPokemonSeen, data.gameStats.legendaryPokemonCaught);
|
||||
data.gameStats.mythicalPokemonSeen = Math.max(data.gameStats.mythicalPokemonSeen, data.gameStats.mythicalPokemonCaught);
|
||||
}
|
||||
|
||||
// --- PATCHES ---
|
||||
|
||||
// Fix Starter Data
|
||||
if (data.gameVersion) {
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
|
||||
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.gameVersion = LATEST_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
export function applySettingsDataPatches(settings: Object) {
|
||||
const curVersion = settings.hasOwnProperty("gameVersion") ? settings["gameVersion"] : "1.0.0";
|
||||
if (curVersion !== LATEST_VERSION) {
|
||||
switch (curVersion) {
|
||||
case "1.0.0":
|
||||
case "1.0.1":
|
||||
case "1.0.2":
|
||||
case "1.0.3":
|
||||
case "1.0.4":
|
||||
// --- PATCHES ---
|
||||
|
||||
// Fix Reward Cursor Target
|
||||
if (settings.hasOwnProperty("REROLL_TARGET") && !settings.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) {
|
||||
settings[SettingKeys.Shop_Cursor_Target] = settings["REROLL_TARGET"];
|
||||
delete settings["REROLL_TARGET"];
|
||||
localStorage.setItem("settings", JSON.stringify(settings));
|
||||
}
|
||||
}
|
||||
// Note that the current game version will be written at `saveSettings`
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue