[Dev] Bump Game Version, Overhaul Version Migration (#4388)
* Bump Version, Remove "Outdated" Message * Fix `src/ui/ui.ts` * Fix `src/system/game-data.ts` * Clean Up & Organize Version Migration * Rename Methods & Session Migration Adjustment * Collapse Version Migrators to Single File as Arrays * Address NITs * Restructure Migration Initialization * Fix Spacing, Increment to v1.6.0 * Revert Back to v1.1.0 * Add `gameVersion` to Mocked Game * Add More Documentation --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
afebecd43c
commit
85b8ca6467
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "pokemon-rogue-battle",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pokemon-rogue-battle",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "pokemon-rogue-battle",
|
||||
"private": true,
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
|
@ -20,7 +20,7 @@
|
|||
"depcruise": "depcruise src",
|
||||
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
|
||||
"create-test": "node ./create-test-boilerplate.js",
|
||||
"postinstall": "npx lefthook install && npx lefthook run post-merge"
|
||||
"postinstall": "npx lefthook install && npx lefthook run post-merge"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.3.0",
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import BattleScene from "#app/battle-scene";
|
||||
import { Phase } from "#app/phase";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
|
||||
export class OutdatedPhase extends Phase {
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene);
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this.scene.ui.setMode(Mode.OUTDATED);
|
||||
}
|
||||
}
|
|
@ -43,10 +43,9 @@ import { Species } from "#enums/species";
|
|||
import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { TerrainType } from "#app/data/terrain";
|
||||
import { OutdatedPhase } from "#app/phases/outdated-phase";
|
||||
import { ReloadSessionPhase } from "#app/phases/reload-session-phase";
|
||||
import { RUN_HISTORY_LIMIT } from "#app/ui/run-history-ui-handler";
|
||||
import { applySessionDataPatches, applySettingsDataPatches, applySystemDataPatches } from "#app/system/version-converter";
|
||||
import { applySessionVersionMigration, applySystemVersionMigration, applySettingsVersionMigration } from "./version_migration/version_converter";
|
||||
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PokerogueApiClearSessionData } from "#app/@types/pokerogue-api";
|
||||
|
@ -403,10 +402,7 @@ export class GameData {
|
|||
.then(error => {
|
||||
this.scene.ui.savingIcon.hide();
|
||||
if (error) {
|
||||
if (error.startsWith("client version out of date")) {
|
||||
this.scene.clearPhaseQueue();
|
||||
this.scene.unshiftPhase(new OutdatedPhase(this.scene));
|
||||
} else if (error.startsWith("session out of date")) {
|
||||
if (error.startsWith("session out of date")) {
|
||||
this.scene.clearPhaseQueue();
|
||||
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
|
||||
}
|
||||
|
@ -482,7 +478,7 @@ export class GameData {
|
|||
localStorage.setItem(lsItemKey, "");
|
||||
}
|
||||
|
||||
applySystemDataPatches(systemData);
|
||||
applySystemVersionMigration(systemData);
|
||||
|
||||
this.trainerId = systemData.trainerId;
|
||||
this.secretId = systemData.secretId;
|
||||
|
@ -857,7 +853,7 @@ export class GameData {
|
|||
|
||||
const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct?
|
||||
|
||||
applySettingsDataPatches(settings);
|
||||
applySettingsVersionMigration(settings);
|
||||
|
||||
for (const setting of Object.keys(settings)) {
|
||||
setSetting(this.scene, setting, settings[setting]);
|
||||
|
@ -1313,7 +1309,7 @@ export class GameData {
|
|||
return v;
|
||||
}) as SessionSaveData;
|
||||
|
||||
applySessionDataPatches(sessionData);
|
||||
applySessionVersionMigration(sessionData);
|
||||
|
||||
return sessionData;
|
||||
}
|
||||
|
@ -1354,10 +1350,7 @@ export class GameData {
|
|||
this.scene.ui.savingIcon.hide();
|
||||
}
|
||||
if (error) {
|
||||
if (error.startsWith("client version out of date")) {
|
||||
this.scene.clearPhaseQueue();
|
||||
this.scene.unshiftPhase(new OutdatedPhase(this.scene));
|
||||
} else if (error.startsWith("session out of date")) {
|
||||
if (error.startsWith("session out of date")) {
|
||||
this.scene.clearPhaseQueue();
|
||||
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
|
||||
}
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
import { allSpecies } from "#app/data/pokemon-species";
|
||||
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;
|
||||
|
||||
// Always sanitize money as a safeguard
|
||||
data.money = Math.floor(data.money);
|
||||
|
||||
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") {
|
||||
// Dire Hit no longer a part of the TempBattleStatBoosterModifierTypeGenerator
|
||||
if (m.typeId !== "DIRE_HIT") {
|
||||
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 {
|
||||
m.className = "TempCritBoosterModifier";
|
||||
m.typePregenArgs = [];
|
||||
|
||||
// From [ stat, battlesLeft ] to [ maxBattles, battleCount ]
|
||||
m.args = [ 5, m.args[1] ];
|
||||
}
|
||||
|
||||
} else if (m.className === "DoubleBattleChanceBoosterModifier" && m.args.length === 1) {
|
||||
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";
|
||||
} else if (m.className === "PokemonResetNegativeStatStageModifier") {
|
||||
m.className = "ResetNegativeStatStageModifier";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 && data.dexData) {
|
||||
// Migrate ability starter data if empty for caught species
|
||||
Object.keys(data.starterData).forEach(sd => {
|
||||
if (data.dexData[sd]?.caughtAttr && (data.starterData[sd] && !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.starterData && data.dexData) {
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
if (data.starterData[starterId]?.abilityAttr) {
|
||||
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
|
||||
}
|
||||
if (data.dexData[starterId]?.caughtAttr) {
|
||||
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`
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
import { SessionSaveData, SystemSaveData } from "../game-data";
|
||||
import { version } from "../../../package.json";
|
||||
|
||||
// --- v1.0.4 (and below) PATCHES --- //
|
||||
import * as v1_0_4 from "./versions/v1_0_4";
|
||||
|
||||
const LATEST_VERSION = version.split(".").map(value => parseInt(value));
|
||||
|
||||
/**
|
||||
* Converts incoming {@linkcode SystemSaveData} that has a version below the
|
||||
* current version number listed in `package.json`.
|
||||
*
|
||||
* Note that no transforms act on the {@linkcode data} if its version matches
|
||||
* the current version or if there are no migrations made between its version up
|
||||
* to the current version.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
* @see {@link SystemVersionConverter}
|
||||
*/
|
||||
export function applySystemVersionMigration(data: SystemSaveData) {
|
||||
const curVersion = data.gameVersion.split(".").map(value => parseInt(value));
|
||||
|
||||
if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) {
|
||||
const converter = new SystemVersionConverter();
|
||||
converter.applyStaticPreprocessors(data);
|
||||
converter.applyMigration(data, curVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts incoming {@linkcode SessionSavaData} that has a version below the
|
||||
* current version number listed in `package.json`.
|
||||
*
|
||||
* Note that no transforms act on the {@linkcode data} if its version matches
|
||||
* the current version or if there are no migrations made between its version up
|
||||
* to the current version.
|
||||
* @param data {@linkcode SessionSaveData}
|
||||
* @see {@link SessionVersionConverter}
|
||||
*/
|
||||
export function applySessionVersionMigration(data: SessionSaveData) {
|
||||
const curVersion = data.gameVersion.split(".").map(value => parseInt(value));
|
||||
|
||||
if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) {
|
||||
const converter = new SessionVersionConverter();
|
||||
converter.applyStaticPreprocessors(data);
|
||||
converter.applyMigration(data, curVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts incoming settings data that has a version below the
|
||||
* current version number listed in `package.json`.
|
||||
*
|
||||
* Note that no transforms act on the {@linkcode data} if its version matches
|
||||
* the current version or if there are no migrations made between its version up
|
||||
* to the current version.
|
||||
* @param data Settings data object
|
||||
* @see {@link SettingsVersionConverter}
|
||||
*/
|
||||
export function applySettingsVersionMigration(data: Object) {
|
||||
const gameVersion: string = data.hasOwnProperty("gameVersion") ? data["gameVersion"] : "1.0.0";
|
||||
const curVersion = gameVersion.split(".").map(value => parseInt(value));
|
||||
|
||||
if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) {
|
||||
const converter = new SettingsVersionConverter();
|
||||
converter.applyStaticPreprocessors(data);
|
||||
converter.applyMigration(data, curVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class encapsulating the logic for migrating data from a given version up to
|
||||
* the current version listed in `package.json`.
|
||||
*
|
||||
* Note that, for any version converter, the corresponding `applyMigration`
|
||||
* function would only need to be changed once when the first migration for a
|
||||
* given version is introduced. Similarly, a version file (within the `versions`
|
||||
* folder) would only need to be created for a version once with the appropriate
|
||||
* array nomenclature.
|
||||
*/
|
||||
abstract class VersionConverter {
|
||||
/**
|
||||
* Iterates through an array of designated migration functions that are each
|
||||
* called one by one to transform the data.
|
||||
* @param data The data to be operated on
|
||||
* @param migrationArr An array of functions that will transform the incoming data
|
||||
*/
|
||||
callMigrators(data: any, migrationArr: readonly any[]) {
|
||||
for (const migrate of migrationArr) {
|
||||
migrate(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies any version-agnostic data sanitation as defined within the function
|
||||
* body.
|
||||
* @param data The data to be operated on
|
||||
*/
|
||||
applyStaticPreprocessors(_data: any): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the current version the incoming data to determine the starting point
|
||||
* of the migration which will cascade up to the latest version, calling the
|
||||
* necessary migration functions in the process.
|
||||
* @param data The data to be operated on
|
||||
* @param curVersion [0] Current major version
|
||||
* [1] Current minor version
|
||||
* [2] Current patch version
|
||||
*/
|
||||
abstract applyMigration(data: any, curVersion: number[]): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulating the logic for migrating {@linkcode SessionSaveData} from
|
||||
* a given version up to the current version listed in `package.json`.
|
||||
* @extends VersionConverter
|
||||
*/
|
||||
class SessionVersionConverter extends VersionConverter {
|
||||
override applyStaticPreprocessors(data: SessionSaveData): void {
|
||||
// Always sanitize money as a safeguard
|
||||
data.money = Math.floor(data.money);
|
||||
}
|
||||
|
||||
override applyMigration(data: SessionSaveData, curVersion: number[]): void {
|
||||
const [ curMajor, curMinor, curPatch ] = curVersion;
|
||||
|
||||
if (curMajor === 1) {
|
||||
if (curMinor === 0) {
|
||||
if (curPatch <= 4) {
|
||||
console.log("Applying v1.0.4 session data migration!");
|
||||
this.callMigrators(data, v1_0_4.sessionMigrators);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Session data successfully migrated to v${version}!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulating the logic for migrating {@linkcode SystemSaveData} from
|
||||
* a given version up to the current version listed in `package.json`.
|
||||
* @extends VersionConverter
|
||||
*/
|
||||
class SystemVersionConverter extends VersionConverter {
|
||||
override applyMigration(data: SystemSaveData, curVersion: number[]): void {
|
||||
const [ curMajor, curMinor, curPatch ] = curVersion;
|
||||
|
||||
if (curMajor === 1) {
|
||||
if (curMinor === 0) {
|
||||
if (curPatch <= 4) {
|
||||
console.log("Applying v1.0.4 system data migraton!");
|
||||
this.callMigrators(data, v1_0_4.systemMigrators);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`System data successfully migrated to v${version}!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulating the logic for migrating settings data from
|
||||
* a given version up to the current version listed in `package.json`.
|
||||
* @extends VersionConverter
|
||||
*/
|
||||
class SettingsVersionConverter extends VersionConverter {
|
||||
override applyMigration(data: Object, curVersion: number[]): void {
|
||||
const [ curMajor, curMinor, curPatch ] = curVersion;
|
||||
|
||||
if (curMajor === 1) {
|
||||
if (curMinor === 0) {
|
||||
if (curPatch <= 4) {
|
||||
console.log("Applying v1.0.4 settings data migraton!");
|
||||
this.callMigrators(data, v1_0_4.settingsMigrators);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`System data successfully migrated to v${version}!`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
import { SettingKeys } from "../../settings/settings";
|
||||
import { AbilityAttr, defaultStarterSpecies, DexAttr, SystemSaveData, SessionSaveData } from "../../game-data";
|
||||
import { allSpecies } from "../../../data/pokemon-species";
|
||||
|
||||
export const systemMigrators = [
|
||||
/**
|
||||
* Migrate ability starter data if empty for caught species.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function migrateAbilityData(data: SystemSaveData) {
|
||||
if (data.starterData && data.dexData) {
|
||||
Object.keys(data.starterData).forEach(sd => {
|
||||
if (data.dexData[sd]?.caughtAttr && (data.starterData[sd] && !data.starterData[sd].abilityAttr)) {
|
||||
data.starterData[sd].abilityAttr = 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate legendary Pokémon statistics if they are missing.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function fixLegendaryStats(data: SystemSaveData) {
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Unlock all starters' first ability and female gender option.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function fixStarterData(data: SystemSaveData) {
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
if (data.starterData[starterId]?.abilityAttr) {
|
||||
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
|
||||
}
|
||||
if (data.dexData[starterId]?.caughtAttr) {
|
||||
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
] as const;
|
||||
|
||||
export const settingsMigrators = [
|
||||
/**
|
||||
* Migrate from "REROLL_TARGET" property to {@linkcode
|
||||
* SettingKeys.Shop_Cursor_Target}.
|
||||
* @param data the `settings` object
|
||||
*/
|
||||
function fixRerollTarget(data: Object) {
|
||||
if (data.hasOwnProperty("REROLL_TARGET") && !data.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) {
|
||||
data[SettingKeys.Shop_Cursor_Target] = data["REROLL_TARGET"];
|
||||
delete data["REROLL_TARGET"];
|
||||
localStorage.setItem("settings", JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
] as const;
|
||||
|
||||
export const sessionMigrators = [
|
||||
/**
|
||||
* Converts old lapsing modifiers (battle items, lures, and Dire Hit) and
|
||||
* other miscellaneous modifiers (vitamins, White Herb) to any new class
|
||||
* names and/or change in reload arguments.
|
||||
* @param data {@linkcode SessionSaveData}
|
||||
*/
|
||||
function migrateModifiers(data: SessionSaveData) {
|
||||
data.modifiers.forEach((m) => {
|
||||
if (m.className === "PokemonBaseStatModifier") {
|
||||
m.className = "BaseStatModifier";
|
||||
} else if (m.className === "PokemonResetNegativeStatStageModifier") {
|
||||
m.className = "ResetNegativeStatStageModifier";
|
||||
} else if (m.className === "TempBattleStatBoosterModifier") {
|
||||
const maxBattles = 5;
|
||||
// Dire Hit no longer a part of the TempBattleStatBoosterModifierTypeGenerator
|
||||
if (m.typeId !== "DIRE_HIT") {
|
||||
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, maxBattles, Math.min(m.args[1], maxBattles) ];
|
||||
} else {
|
||||
m.className = "TempCritBoosterModifier";
|
||||
m.typePregenArgs = [];
|
||||
|
||||
// From [ stat, battlesLeft ] to [ maxBattles, battleCount ]
|
||||
m.args = [ maxBattles, Math.min(m.args[1], maxBattles) ];
|
||||
}
|
||||
} else if (m.className === "DoubleBattleChanceBoosterModifier" && m.args.length === 1) {
|
||||
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, Math.min(m.args[0], maxBattles) ];
|
||||
}
|
||||
});
|
||||
|
||||
data.enemyModifiers.forEach((m) => {
|
||||
if (m.className === "PokemonBaseStatModifier") {
|
||||
m.className = "BaseStatModifier";
|
||||
} else if (m.className === "PokemonResetNegativeStatStageModifier") {
|
||||
m.className = "ResetNegativeStatStageModifier";
|
||||
}
|
||||
});
|
||||
}
|
||||
] as const;
|
|
@ -23,6 +23,7 @@ import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin;
|
|||
import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin;
|
||||
import EventEmitter = Phaser.Events.EventEmitter;
|
||||
import UpdateList = Phaser.GameObjects.UpdateList;
|
||||
import { version } from "../../../package.json";
|
||||
|
||||
Object.defineProperty(window, "localStorage", {
|
||||
value: mockLocalStorage(),
|
||||
|
@ -101,6 +102,7 @@ export default class GameWrapper {
|
|||
injectMandatory() {
|
||||
this.game.config = {
|
||||
seed: ["test"],
|
||||
gameVersion: version
|
||||
};
|
||||
this.scene.game = this.game;
|
||||
this.game.renderer = {
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import BattleScene from "../battle-scene";
|
||||
import { ModalConfig, ModalUiHandler } from "./modal-ui-handler";
|
||||
import { addTextObject, TextStyle } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
|
||||
export default class OutdatedModalUiHandler extends ModalUiHandler {
|
||||
constructor(scene: BattleScene, mode: Mode | null = null) {
|
||||
super(scene, mode);
|
||||
}
|
||||
|
||||
getModalTitle(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
getWidth(): number {
|
||||
return 160;
|
||||
}
|
||||
|
||||
getHeight(): number {
|
||||
return 64;
|
||||
}
|
||||
|
||||
getMargin(): [number, number, number, number] {
|
||||
return [ 0, 0, 48, 0 ];
|
||||
}
|
||||
|
||||
getButtonLabels(): string[] {
|
||||
return [ ];
|
||||
}
|
||||
|
||||
setup(): void {
|
||||
super.setup();
|
||||
|
||||
const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, "Your client is currently outdated.\nPlease reload to update the game.\n\nIf this error persists, please clear your browser cache.", TextStyle.WINDOW, { fontSize: "48px", align: "center" });
|
||||
label.setOrigin(0.5, 0.5);
|
||||
|
||||
this.modalContainer.add(label);
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
const config: ModalConfig = {
|
||||
buttonActions: []
|
||||
};
|
||||
|
||||
return super.show([ config ]);
|
||||
}
|
||||
}
|
|
@ -34,7 +34,6 @@ import SaveSlotSelectUiHandler from "./save-slot-select-ui-handler";
|
|||
import TitleUiHandler from "./title-ui-handler";
|
||||
import SavingIconHandler from "./saving-icon-handler";
|
||||
import UnavailableModalUiHandler from "./unavailable-modal-ui-handler";
|
||||
import OutdatedModalUiHandler from "./outdated-modal-ui-handler";
|
||||
import SessionReloadModalUiHandler from "./session-reload-modal-ui-handler";
|
||||
import { Button } from "#enums/buttons";
|
||||
import i18next from "i18next";
|
||||
|
@ -90,7 +89,6 @@ export enum Mode {
|
|||
LOADING,
|
||||
SESSION_RELOAD,
|
||||
UNAVAILABLE,
|
||||
OUTDATED,
|
||||
CHALLENGE_SELECT,
|
||||
RENAME_POKEMON,
|
||||
RUN_HISTORY,
|
||||
|
@ -134,7 +132,6 @@ const noTransitionModes = [
|
|||
Mode.LOADING,
|
||||
Mode.SESSION_RELOAD,
|
||||
Mode.UNAVAILABLE,
|
||||
Mode.OUTDATED,
|
||||
Mode.RENAME_POKEMON,
|
||||
Mode.TEST_DIALOGUE,
|
||||
Mode.AUTO_COMPLETE,
|
||||
|
@ -200,7 +197,6 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||
new LoadingModalUiHandler(scene),
|
||||
new SessionReloadModalUiHandler(scene),
|
||||
new UnavailableModalUiHandler(scene),
|
||||
new OutdatedModalUiHandler(scene),
|
||||
new GameChallengesUiHandler(scene),
|
||||
new RenameFormUiHandler(scene),
|
||||
new RunHistoryUiHandler(scene),
|
||||
|
|
Loading…
Reference in New Issue