mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-09 00:49:27 +02:00
[WIP]
This commit is contained in:
parent
09bb498c7a
commit
10af69a777
@ -97,10 +97,10 @@ interface SerializedIllusionData extends Omit<IllusionData, "fusionSpecies"> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SerializedPokemonSummonData {
|
interface SerializedPokemonSummonData {
|
||||||
statStages: number[];
|
statStages?: number[];
|
||||||
moveQueue: TurnMove[];
|
moveQueue?: TurnMove[];
|
||||||
tags: BattlerTag[];
|
tags?: BattlerTag[];
|
||||||
abilitySuppressed: boolean;
|
abilitySuppressed?: boolean;
|
||||||
speciesForm?: SerializedSpeciesForm;
|
speciesForm?: SerializedSpeciesForm;
|
||||||
fusionSpeciesForm?: SerializedSpeciesForm;
|
fusionSpeciesForm?: SerializedSpeciesForm;
|
||||||
ability?: AbilityId;
|
ability?: AbilityId;
|
||||||
@ -109,12 +109,12 @@ interface SerializedPokemonSummonData {
|
|||||||
fusionGender?: Gender;
|
fusionGender?: Gender;
|
||||||
stats: number[];
|
stats: number[];
|
||||||
moveset?: PokemonMove[];
|
moveset?: PokemonMove[];
|
||||||
types: PokemonType[];
|
types?: PokemonType[];
|
||||||
addedType?: PokemonType;
|
addedType?: PokemonType;
|
||||||
illusion?: SerializedIllusionData;
|
illusion?: SerializedIllusionData;
|
||||||
illusionBroken: boolean;
|
illusionBroken?: boolean;
|
||||||
berriesEatenLast: BerryType[];
|
berriesEatenLast?: BerryType[];
|
||||||
moveHistory: TurnMove[];
|
moveHistory?: TurnMove[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,9 +250,12 @@ export class PokemonSummonData {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
// Replace `null` with `undefined`, as `undefined` never gets serialized
|
// Replace `null` with `undefined`, as `undefined` never gets serialized
|
||||||
|
// Replace empty arrays with `[]`
|
||||||
for (const [key, value] of Object.entries(t)) {
|
for (const [key, value] of Object.entries(t)) {
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
t[key] = undefined;
|
t[key] = undefined;
|
||||||
|
} else if (Array.isArray(value) && value.length === 0) {
|
||||||
|
t[key] = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
|
30
src/system/schema-migrators/custom-pokemon-data.ts
Normal file
30
src/system/schema-migrators/custom-pokemon-data.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import type { Z$PokemonData } from "#system/schemas/v1.10/pokemon-data";
|
||||||
|
import { NatureSchema } from "#system/schemas/v1.10/pokemon-nature";
|
||||||
|
import type z from "zod";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very early saves did not have `customPokemonData` and instead
|
||||||
|
* stored things like nature overrides and abilities from MEs directly.
|
||||||
|
* This migrator moves the properties into the `customPokemonData` field.
|
||||||
|
*/
|
||||||
|
export function PreCustomPokemonDataMigrator(
|
||||||
|
data: z.output<typeof Z$PokemonData>,
|
||||||
|
): asserts data is z.output<typeof Z$PokemonData> {
|
||||||
|
// Value of `-1` indicated no override, so we can ignore it.
|
||||||
|
const nature = NatureSchema.safeParse(data.natureOverride);
|
||||||
|
if (nature.success) {
|
||||||
|
const customPokemonData = data.customPokemonData;
|
||||||
|
// If natureOverride is valid, use it
|
||||||
|
if (
|
||||||
|
customPokemonData &&
|
||||||
|
typeof customPokemonData === "object" &&
|
||||||
|
((customPokemonData as { nature?: number }).nature ?? -1) === -1
|
||||||
|
) {
|
||||||
|
customPokemonData;
|
||||||
|
} else {
|
||||||
|
data.customPokemonData = {
|
||||||
|
nature: nature.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
src/system/schema-migrators/summon-data.ts
Normal file
69
src/system/schema-migrators/summon-data.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Z$PositiveInt } from "#system/schemas/common";
|
||||||
|
import type { Z$IllusionData } from "#system/schemas/v1.10/illusion-data";
|
||||||
|
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In version 1.9, serialized illusion data looked like this, where `basePokemon` held
|
||||||
|
* information about the pokemon that had the illusion ability, while the pokemon's own
|
||||||
|
* properties were modified. (though were overwritten on save).
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* interface IllusionData {
|
||||||
|
* basePokemon: {
|
||||||
|
* name: string;
|
||||||
|
* nickname: string;
|
||||||
|
* shiny: boolean;
|
||||||
|
* variant: Variant;
|
||||||
|
* fusionShiny: boolean;
|
||||||
|
* fusionVariant: Variant;
|
||||||
|
* }
|
||||||
|
* species: Species;
|
||||||
|
* formIndex: number;
|
||||||
|
* gender: Gender;
|
||||||
|
* pokeball: PokeballType;
|
||||||
|
* fusionSpecies?: PokemonSpecies;
|
||||||
|
* fusionFormIndex?: number;
|
||||||
|
* fusionGender?: Gender;
|
||||||
|
* level?: number
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* As this version of the data is compatible with version 1.10, we only need to specify the new properties.
|
||||||
|
*/
|
||||||
|
export const Z$V1_9_IllusionData = z.looseObject({
|
||||||
|
species: Z$PositiveInt,
|
||||||
|
fusionSpecies: z
|
||||||
|
.object({
|
||||||
|
speciesId: Z$PositiveInt,
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.catch(undefined),
|
||||||
|
});
|
||||||
|
type V1_9_IllusionData = z.input<typeof Z$V1_9_IllusionData>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate illusion data from version 1.9 to 1.10
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Extract `speciesId` from `fusionSpecies`, and use defaults for all fields from the illusioned pokemon.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function V1_10_IllusionDataMigrator(arg: V1_9_IllusionData): Partial<z.input<typeof Z$IllusionData>> {
|
||||||
|
return Z$V1_9_IllusionData.transform(data => {
|
||||||
|
// Needed to fetch a default name.
|
||||||
|
const pokemon = getPokemonSpecies(data.species);
|
||||||
|
return {
|
||||||
|
...(data as Omit<V1_9_IllusionData, "fusionSpecies">),
|
||||||
|
// unwrap fusion species
|
||||||
|
fusionSpecies: data.fusionSpecies?.speciesId,
|
||||||
|
// and use defaults for all fields from the illusioned pokemon
|
||||||
|
name: pokemon.name,
|
||||||
|
nickname: pokemon.name,
|
||||||
|
shiny: false,
|
||||||
|
fusionShiny: false,
|
||||||
|
variant: 0,
|
||||||
|
fusionVariant: 0,
|
||||||
|
fusionFormIndex: 0,
|
||||||
|
};
|
||||||
|
}).parse(arg);
|
||||||
|
}
|
25
src/system/schemas/v1.10/illusion-data.ts
Normal file
25
src/system/schemas/v1.10/illusion-data.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Z$NonNegativeInt, Z$PositiveInt } from "#system/schemas/common";
|
||||||
|
import { Z$PokeballType } from "#system/schemas/v1.10/pokeball-type";
|
||||||
|
import { Z$Gender } from "#system/schemas/v1.10/pokemon-gender";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Write migrator for illusion data's fusionSpecies field
|
||||||
|
// that transforms incoming fusion species
|
||||||
|
|
||||||
|
export const Z$IllusionData = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
nickname: z.string(),
|
||||||
|
shiny: z.boolean(),
|
||||||
|
variant: z.literal([0, 1, 2]).catch(0),
|
||||||
|
species: Z$PositiveInt,
|
||||||
|
formIndex: Z$NonNegativeInt.catch(0),
|
||||||
|
gender: Z$Gender,
|
||||||
|
pokeball: Z$PokeballType,
|
||||||
|
fusionSpecies: Z$PositiveInt.optional().catch(undefined),
|
||||||
|
fusionFormIndex: Z$NonNegativeInt.optional().catch(undefined),
|
||||||
|
fusionShiny: z.boolean().optional().catch(false),
|
||||||
|
fusionVariant: z.literal([0, 1, 2]).optional().catch(0),
|
||||||
|
fusionGender: Z$Gender.optional().catch(undefined),
|
||||||
|
level: Z$PositiveInt.optional().catch(undefined),
|
||||||
|
});
|
@ -1,6 +1,7 @@
|
|||||||
import { Z$BoolCatchToFalse, Z$NonNegativeInt, Z$PositiveInt } from "#schemas/common";
|
import { Z$BoolCatchToFalse, Z$NonNegativeInt, Z$PositiveInt } from "#schemas/common";
|
||||||
import { Z$PokeballType } from "#schemas/pokeball-type";
|
import { Z$PokeballType } from "#schemas/pokeball-type";
|
||||||
import { Z$PokemonMove } from "#schemas/pokemon-move";
|
import { Z$PokemonMove } from "#schemas/pokemon-move";
|
||||||
|
import { Z$PokemonBattleData } from "#system/schemas/v1.10/pokemon-battle-data";
|
||||||
import { Z$Gender } from "#system/schemas/v1.10/pokemon-gender";
|
import { Z$Gender } from "#system/schemas/v1.10/pokemon-gender";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { NatureSchema } from "./pokemon-nature";
|
import { NatureSchema } from "./pokemon-nature";
|
||||||
@ -14,7 +15,7 @@ import { StatusSchema } from "./status-effect";
|
|||||||
* `looseObject` used here to allow properties specific to player or enemy Pokémon
|
* `looseObject` used here to allow properties specific to player or enemy Pokémon
|
||||||
* to be handled by their respective schemas.
|
* to be handled by their respective schemas.
|
||||||
*/
|
*/
|
||||||
const Z$PokemonData = z.looseObject({
|
export const Z$PokemonData = z.looseObject({
|
||||||
// malformed pokemon ids are _not_ supported.
|
// malformed pokemon ids are _not_ supported.
|
||||||
id: z.uint32(),
|
id: z.uint32(),
|
||||||
species: z.uint32(),
|
species: z.uint32(),
|
||||||
@ -65,6 +66,9 @@ const Z$PokemonData = z.looseObject({
|
|||||||
//#endregion "fusion" information
|
//#endregion "fusion" information
|
||||||
|
|
||||||
pokerus: z.boolean().catch(false),
|
pokerus: z.boolean().catch(false),
|
||||||
|
summonData: Z$PokemonSummonData,
|
||||||
|
battleData: Z$PokemonBattleData,
|
||||||
|
summonDataSpeciesFormIndex: Z$NonNegativeInt,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Z$PlayerPokemonData = z.object({
|
export const Z$PlayerPokemonData = z.object({
|
||||||
@ -88,29 +92,6 @@ export const Z$EnemyPokemonData = z.object({
|
|||||||
bossSegments: z.int().nonnegative().default(0),
|
bossSegments: z.int().nonnegative().default(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Replace output assertion type with the type of pokemon data that has CustomPokemonData.
|
|
||||||
export function PreCustomPokemonDataMigrator(
|
|
||||||
data: z.output<typeof Z$PokemonData>,
|
|
||||||
): asserts data is z.output<typeof Z$PokemonData> {
|
|
||||||
// Value of `-1` indicated no override, so we can ignore it.
|
|
||||||
const nature = NatureSchema.safeParse(data.natureOverride);
|
|
||||||
if (nature.success) {
|
|
||||||
const customPokemonData = data.customPokemonData;
|
|
||||||
// If natureOverride is valid, use it
|
|
||||||
if (
|
|
||||||
customPokemonData &&
|
|
||||||
typeof customPokemonData === "object" &&
|
|
||||||
((customPokemonData as { nature?: number }).nature ?? -1) === -1
|
|
||||||
) {
|
|
||||||
customPokemonData;
|
|
||||||
} else {
|
|
||||||
data.customPokemonData = {
|
|
||||||
nature: nature.data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PreParsedPokemonData = z.input<typeof Z$PokemonData>;
|
export type PreParsedPokemonData = z.input<typeof Z$PokemonData>;
|
||||||
export type ParsedPokemonData = z.output<typeof Z$PokemonData>;
|
export type ParsedPokemonData = z.output<typeof Z$PokemonData>;
|
||||||
|
|
||||||
|
@ -1,47 +1,36 @@
|
|||||||
import { Z$NonNegativeInt } from "#system/schemas/common";
|
import { Z$NonNegativeInt } from "#system/schemas/common";
|
||||||
import { Z$Gender } from "#system/schemas/v1.10/pokemon-gender";
|
import { Z$Gender } from "#system/schemas/v1.10/pokemon-gender";
|
||||||
import { Z$PokemonSpeciesForm } from "#system/schemas/v1.10/pokemon-species-form";
|
import { Z$PokemonMove } from "#system/schemas/v1.10/pokemon-move";
|
||||||
import { Z$StatSet } from "#system/schemas/v1.10/pokemon-stats";
|
import { Z$StatSet } from "#system/schemas/v1.10/pokemon-stats";
|
||||||
import { Z$PokemonType } from "#system/schemas/v1.10/pokemon-type";
|
import { Z$PokemonType } from "#system/schemas/v1.10/pokemon-type";
|
||||||
import { Z$TurnMove } from "#system/schemas/v1.10/turn-move";
|
import { Z$TurnMove } from "#system/schemas/v1.10/turn-move";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const Z$StatStage = z.int().min(-6).max(6).catch(0);
|
export const Z$SerializedSpeciesForm = z.object({
|
||||||
|
id: Z$NonNegativeInt,
|
||||||
const Z$StatStageSet = z.tuple([
|
formIndex: Z$NonNegativeInt.catch(0),
|
||||||
Z$StatStage,
|
});
|
||||||
Z$StatStage,
|
|
||||||
Z$StatStage,
|
|
||||||
Z$StatStage,
|
|
||||||
Z$StatStage,
|
|
||||||
Z$StatStage,
|
|
||||||
Z$StatStage,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Pre version 1.10 pokemon summon data migration needs to rename
|
|
||||||
// input fields
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zod schema for Pokémon summon data as of version 1.10.
|
* Zod schema for Pokémon summon data as of version 1.10.
|
||||||
*
|
*
|
||||||
|
* @remarks
|
||||||
|
* All fields other than `stats` are optional, and catch to `undefined` on parse error,
|
||||||
|
* allowing {@linkcode PokemonSummonData} to fill in defaults.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
export const Z$PokemonSummonData = z.object({
|
export const Z$PokemonSummonData = z.object({
|
||||||
statStages: Z$StatStageSet.optional().catch(undefined),
|
statSages: z.array(z.int().min(-6).max(6).catch(0)).optional().catch(undefined),
|
||||||
moveQueue: z.array(Z$TurnMove).optional().catch(undefined),
|
moveQueue: z.array(Z$TurnMove).optional().catch(undefined),
|
||||||
// todo: tags
|
|
||||||
abilitySuppressed: z.boolean().optional().catch(undefined),
|
abilitySuppressed: z.boolean().optional().catch(undefined),
|
||||||
|
speciesForm: Z$SerializedSpeciesForm.optional().catch(undefined),
|
||||||
//#region Overrides for transform
|
|
||||||
speciesForm: Z$PokemonSpeciesForm.nullable().catch(null),
|
|
||||||
fusionSpeciesForm: Z$PokemonSpeciesForm.nullable().catch(null),
|
|
||||||
ability: Z$NonNegativeInt.optional().catch(undefined),
|
ability: Z$NonNegativeInt.optional().catch(undefined),
|
||||||
passiveAbility: Z$NonNegativeInt.optional().catch(undefined),
|
passiveAbility: Z$NonNegativeInt.optional().catch(undefined),
|
||||||
gender: Z$Gender.optional().catch(undefined),
|
gender: Z$Gender.optional().catch(undefined),
|
||||||
fusionGender: Z$Gender.optional().catch(undefined),
|
fusionGender: Z$Gender.optional().catch(undefined),
|
||||||
stats: Z$StatSet.optional().catch(undefined),
|
stats: Z$StatSet,
|
||||||
moveset: z.array(Z$TurnMove).nullable().catch(null),
|
moveset: z.array(Z$PokemonMove).optional().catch(undefined),
|
||||||
//#endregion Overrides for transform
|
|
||||||
|
|
||||||
types: z.array(Z$PokemonType).optional().catch(undefined),
|
types: z.array(Z$PokemonType).optional().catch(undefined),
|
||||||
addedType: Z$PokemonType.nullable().catch(null),
|
addedType: Z$PokemonType.optional().catch(undefined),
|
||||||
|
illusion
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user