mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-09 00:49:27 +02:00
[WIP]
This commit is contained in:
parent
e50ebaa815
commit
0c42f16fdd
@ -49,6 +49,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
"ajv": "^8.17.1",
|
||||
"compare-versions": "^6.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"i18next": "^24.2.3",
|
||||
@ -58,7 +59,8 @@
|
||||
"json-stable-stringify": "^1.3.0",
|
||||
"jszip": "^3.10.1",
|
||||
"phaser": "^3.90.0",
|
||||
"phaser3-rex-plugins": "^1.80.16"
|
||||
"phaser3-rex-plugins": "^1.80.16",
|
||||
"zod": "^4.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
|
@ -11,6 +11,9 @@ importers:
|
||||
'@material/material-color-utilities':
|
||||
specifier: ^0.2.7
|
||||
version: 0.2.7
|
||||
ajv:
|
||||
specifier: ^8.17.1
|
||||
version: 8.17.1
|
||||
compare-versions:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1
|
||||
@ -41,6 +44,9 @@ importers:
|
||||
phaser3-rex-plugins:
|
||||
specifier: ^1.80.16
|
||||
version: 1.80.16(graphology-types@0.24.8)
|
||||
zod:
|
||||
specifier: ^4.0.5
|
||||
version: 4.0.5
|
||||
devDependencies:
|
||||
'@biomejs/biome':
|
||||
specifier: 2.0.0
|
||||
@ -2002,6 +2008,9 @@ packages:
|
||||
resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
zod@4.0.5:
|
||||
resolution: {integrity: sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
@ -3828,3 +3837,5 @@ snapshots:
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
yoctocolors-cjs@2.1.2: {}
|
||||
|
||||
zod@4.0.5: {}
|
||||
|
165
sample_session_data.json
Normal file
165
sample_session_data.json
Normal file
@ -0,0 +1,165 @@
|
||||
{
|
||||
"seed": "XsQk92D0JOcTKUGejq4HpzvC",
|
||||
"playTime": 0,
|
||||
"gameMode": 0,
|
||||
"party": [
|
||||
{
|
||||
"id": 674110050,
|
||||
"player": true,
|
||||
"species": 479,
|
||||
"formIndex": 0,
|
||||
"abilityIndex": 0,
|
||||
"passive": true,
|
||||
"shiny": false,
|
||||
"variant": 0,
|
||||
"pokeball": 0,
|
||||
"level": 5,
|
||||
"exp": 125,
|
||||
"levelExp": 0,
|
||||
"gender": -1,
|
||||
"hp": 21,
|
||||
"stats": [21, 13, 13, 16, 14, 13],
|
||||
"ivs": [31, 31, 25, 31, 31, 26],
|
||||
"nature": 2,
|
||||
"moveset": [],
|
||||
"status": null,
|
||||
"friendship": 50,
|
||||
"metLevel": 5,
|
||||
"metBiome": -1,
|
||||
"metSpecies": 479,
|
||||
"metWave": -1,
|
||||
"luck": 2,
|
||||
"pauseEvolutions": false,
|
||||
"pokerus": false,
|
||||
"usedTMs": [],
|
||||
"teraType": 12,
|
||||
"isTerastallized": false,
|
||||
"stellarTypesBoosted": [],
|
||||
"mysteryEncounterPokemonData": null,
|
||||
"fusionMysteryEncounterPokemonData": null,
|
||||
"fusionLuck": 0,
|
||||
"fusionTeraType": 0,
|
||||
"boss": false,
|
||||
"bossSegments": 0,
|
||||
"summonData": {
|
||||
"statStages": [0, 0, 0, 0, 0, 0, 0],
|
||||
"moveQueue": [],
|
||||
"tags": [],
|
||||
"abilitySuppressed": false,
|
||||
"speciesForm": null,
|
||||
"fusionSpeciesForm": null,
|
||||
"stats": [0, 0, 0, 0, 0, 0],
|
||||
"types": [],
|
||||
"addedType": null,
|
||||
"illusion": null,
|
||||
"illusionBroken": false,
|
||||
"berriesEatenLast": [],
|
||||
"moveHistory": []
|
||||
},
|
||||
"battleData": { "hitCount": 0, "hasEatenBerry": false, "berriesEaten": [] },
|
||||
"customPokemonData": {
|
||||
"spriteScale": -1,
|
||||
"hitsRecCount": null,
|
||||
"ability": -1,
|
||||
"passive": -1,
|
||||
"nature": -1,
|
||||
"types": []
|
||||
},
|
||||
"fusionCustomPokemonData": {
|
||||
"spriteScale": -1,
|
||||
"hitsRecCount": null,
|
||||
"ability": -1,
|
||||
"passive": -1,
|
||||
"nature": -1,
|
||||
"types": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"enemyParty": [
|
||||
{
|
||||
"id": 1442439343,
|
||||
"player": false,
|
||||
"species": 876,
|
||||
"formIndex": 1,
|
||||
"abilityIndex": 1,
|
||||
"shiny": false,
|
||||
"variant": 0,
|
||||
"pokeball": 0,
|
||||
"level": 2,
|
||||
"exp": 7,
|
||||
"levelExp": 0,
|
||||
"gender": 1,
|
||||
"hp": 15,
|
||||
"stats": [15, 7, 7, 9, 10, 7],
|
||||
"ivs": [10, 31, 19, 24, 5, 15],
|
||||
"nature": 22,
|
||||
"moveset": [{ "moveId": 500, "ppUsed": 0, "ppUp": 0 }, { "moveId": 589, "ppUsed": 0, "ppUp": 0 }],
|
||||
"status": null,
|
||||
"friendship": 140,
|
||||
"metLevel": 2,
|
||||
"metBiome": 0,
|
||||
"metSpecies": 876,
|
||||
"metWave": 1,
|
||||
"luck": 0,
|
||||
"pauseEvolutions": false,
|
||||
"pokerus": false,
|
||||
"usedTMs": [],
|
||||
"teraType": 13,
|
||||
"isTerastallized": false,
|
||||
"stellarTypesBoosted": [],
|
||||
"mysteryEncounterPokemonData": null,
|
||||
"fusionMysteryEncounterPokemonData": null,
|
||||
"fusionLuck": 0,
|
||||
"fusionTeraType": 0,
|
||||
"boss": false,
|
||||
"bossSegments": 0,
|
||||
"summonData": {
|
||||
"statStages": [0, 0, 0, 0, 0, 0, 0],
|
||||
"moveQueue": [],
|
||||
"tags": [],
|
||||
"abilitySuppressed": false,
|
||||
"speciesForm": null,
|
||||
"fusionSpeciesForm": null,
|
||||
"stats": [0, 0, 0, 0, 0, 0],
|
||||
"types": [],
|
||||
"addedType": null,
|
||||
"illusion": null,
|
||||
"illusionBroken": false,
|
||||
"berriesEatenLast": [],
|
||||
"moveHistory": []
|
||||
},
|
||||
"battleData": { "hitCount": 0, "hasEatenBerry": false, "berriesEaten": [] },
|
||||
"customPokemonData": {
|
||||
"spriteScale": -1,
|
||||
"hitsRecCount": null,
|
||||
"ability": -1,
|
||||
"passive": -1,
|
||||
"nature": -1,
|
||||
"types": []
|
||||
},
|
||||
"fusionCustomPokemonData": {
|
||||
"spriteScale": -1,
|
||||
"hitsRecCount": null,
|
||||
"ability": -1,
|
||||
"passive": -1,
|
||||
"nature": -1,
|
||||
"types": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"modifiers": [],
|
||||
"enemyModifiers": [],
|
||||
"arena": { "biome": 0, "weather": null, "terrain": null, "playerTerasUsed": 0, "tags": [] },
|
||||
"pokeballCounts": { "0": 5, "1": 0, "2": 0, "3": 0, "4": 5 },
|
||||
"money": 1000,
|
||||
"score": 0,
|
||||
"waveIndex": 1,
|
||||
"battleType": 0,
|
||||
"trainer": null,
|
||||
"gameVersion": "1.9.6",
|
||||
"timestamp": 1751940739555,
|
||||
"challenges": [],
|
||||
"mysteryEncounterType": -1,
|
||||
"mysteryEncounterSaveData": { "encounteredEvents": [], "encounterSpawnChance": 3, "queuedEncounters": [] },
|
||||
"playerFaints": 0
|
||||
}
|
61
scripts/zod-test.ts
Normal file
61
scripts/zod-test.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import z from "zod";
|
||||
|
||||
/*
|
||||
Schema for session data
|
||||
*/
|
||||
|
||||
/*
|
||||
Schema of a `PokemonMove` as of version 1.10
|
||||
*/
|
||||
|
||||
// const IVSet = z.tuple([IVSchema, IVSchema, IVSchema, IVSchema, IVSchema, IVSchema]).catch((e) => {
|
||||
// e.value
|
||||
// }
|
||||
|
||||
export const Z$NonNegativeCatchNeg1 = /*@__PURE__*/ z
|
||||
.int()
|
||||
.nonnegative()
|
||||
.optional()
|
||||
.catch(-1);
|
||||
|
||||
console.log(Z$NonNegativeCatchNeg1.safeParse(undefined));
|
||||
|
||||
// export const PokemonMoveSchema = z.object({
|
||||
// moveId: z.number().int().min(0).optional().overwrite(() => 15), // Move ID, default to 0
|
||||
// ppUsed: z.number().int().catch(0), // PP used, default to 0
|
||||
// ppUp: z.number().int().default(0).catch(0), // PP Up count, default to 0
|
||||
// maxPpOverride: z.int().min(1).optional(), // Optional max PP override, can be null
|
||||
// });
|
||||
|
||||
// // export const PokemonDataSchema = z.object({
|
||||
// // id: z.int(),
|
||||
// // player: z.boolean(),
|
||||
// // species: z.int(),
|
||||
// // nickname: z.string(),
|
||||
// // formIndex: z.int().default(0),
|
||||
// // abilityIndex: z.int().min(0).max(2).default(0).catch,
|
||||
// // }).check(data => {
|
||||
// // // clamp form index to be between 0 and the max form index for the species
|
||||
// // const species = getPokemonSpecies(data.value.species);
|
||||
// // // Clamp formindex to be between 0 and the max form index for the species
|
||||
|
||||
// // })
|
||||
|
||||
// const mockData = {
|
||||
// // ppUsed: 5,
|
||||
// ppUp: 2,
|
||||
// iv: [1, 15, 14, 14, 15, 16, 17]
|
||||
// };
|
||||
|
||||
// try {
|
||||
// // Parse and validate the mock data
|
||||
// const result = PokemonMoveSchema.parse(mockData);
|
||||
// console.log("Validation successful:", result);
|
||||
// } catch (error: unknown) {
|
||||
// if (error instanceof ZodError) {
|
||||
// console.error(error.message);
|
||||
// console.error(z.treeifyError(error));
|
||||
// } else {
|
||||
// console.error("Validation failed:", error);
|
||||
// }
|
||||
// }
|
@ -10,4 +10,4 @@ export interface TurnMove {
|
||||
useMode: MoveUseMode;
|
||||
result?: MoveResult;
|
||||
turn?: number;
|
||||
}
|
||||
}
|
@ -51,6 +51,22 @@ export class CustomPokemonData {
|
||||
this.types = data?.types ?? [];
|
||||
this.hitsRecCount = data?.hitsRecCount ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this CustomPokemonData has only the default properties.
|
||||
* Primarily used to avoid serializing this data if it is not customized.
|
||||
*/
|
||||
static isDefault(data: CustomPokemonData | Partial<CustomPokemonData>): boolean {
|
||||
return (
|
||||
data.spriteScale === -1 &&
|
||||
data.ability === -1 &&
|
||||
data.passive === -1 &&
|
||||
data.nature === -1 &&
|
||||
Array.isArray(data.types) &&
|
||||
data.types.length === 0 &&
|
||||
(data.hitsRecCount === null || data.hitsRecCount === 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
27
src/system/schemas/common.ts
Normal file
27
src/system/schemas/common.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/*
|
||||
Schemas for commonly used primitive types, to avoid repeated instantiations.
|
||||
*/
|
||||
|
||||
/** Reusable schema for a positive integer, equivalent to `z.int().positive()`.*/
|
||||
export const Z$PositiveInt = /*@__PURE__*/ z
|
||||
.int()
|
||||
.positive();
|
||||
|
||||
/** Reusable schema for a non-negative integer, equivalent to `z.int().nonnegative()`.*/
|
||||
export const Z$NonNegativeInt = /*@__PURE__*/ z
|
||||
.int()
|
||||
.nonnegative();
|
||||
|
||||
/** Reusable schema for a boolean that coerces non-boolean inputs to `false` */
|
||||
export const Z$BoolCatchToFalse = /*@__PURE__*/ z
|
||||
.boolean()
|
||||
.catch(false);
|
||||
|
||||
/** Reusable schema for an optional non-negative integer that coerces invalid inputs to `undefined` */
|
||||
export const Z$OptionalNonNegativeIntCatchToUndef = /*@__PURE__*/ z
|
||||
.int()
|
||||
.nonnegative()
|
||||
.optional()
|
||||
.catch(undefined);
|
8
src/system/schemas/game-version.ts
Normal file
8
src/system/schemas/game-version.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema matching a version string
|
||||
*
|
||||
* @remarks Equivalent to `z.string().regex(/^\d+\.\d+\.\d+$/)`
|
||||
*/
|
||||
export const Z$GameVersion = z.string().regex(/^\d+\.\d+\.\d+$/);
|
15
src/system/schemas/v1.10/battler-index.ts
Normal file
15
src/system/schemas/v1.10/battler-index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// biome-ignore lint/correctness/noUnusedImports: used in tsdoc comment
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for the {@linkcode BattlerIndex} as of version 1.10
|
||||
*
|
||||
* @remarks
|
||||
* - `-1`: Attacker
|
||||
* - `0`: Player
|
||||
* - `1`: Player 2
|
||||
* - `2`: Enemy
|
||||
* - `3`: Enemy 2
|
||||
*/
|
||||
export const Z$BattlerIndex = z.literal([-1, 0, 1, 2, 3]);
|
142
src/system/schemas/v1.10/battler-tag.ts
Normal file
142
src/system/schemas/v1.10/battler-tag.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import { Z$NonNegativeInt, Z$PositiveInt } from "#system/schemas/common";
|
||||
import { Z$BattlerIndex } from "#system/schemas/v1.10/battler-index";
|
||||
import { z } from "zod";
|
||||
|
||||
/*
|
||||
Schemas for battler tags are a bit more cumbersome,
|
||||
as we need to have schemas for each subclass that has a different shape.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Zod schema for the {@linkcode BattlerTagType} enum as of version 1.10
|
||||
*/
|
||||
const Z$BattlerTagType = z.literal([
|
||||
"NONE",
|
||||
"RECHARGING",
|
||||
"FLINCHED",
|
||||
"INTERRUPTED",
|
||||
"CONFUSED",
|
||||
"INFATUATED",
|
||||
"SEEDED",
|
||||
"NIGHTMARE",
|
||||
"FRENZY",
|
||||
"CHARGING",
|
||||
"ENCORE",
|
||||
"HELPING_HAND",
|
||||
"INGRAIN",
|
||||
"OCTOLOCK",
|
||||
"AQUA_RING",
|
||||
"DROWSY",
|
||||
"TRAPPED",
|
||||
"BIND",
|
||||
"WRAP",
|
||||
"FIRE_SPIN",
|
||||
"WHIRLPOOL",
|
||||
"CLAMP",
|
||||
"SAND_TOMB",
|
||||
"MAGMA_STORM",
|
||||
"SNAP_TRAP",
|
||||
"THUNDER_CAGE",
|
||||
"INFESTATION",
|
||||
"PROTECTED",
|
||||
"SPIKY_SHIELD",
|
||||
"KINGS_SHIELD",
|
||||
"OBSTRUCT",
|
||||
"SILK_TRAP",
|
||||
"BANEFUL_BUNKER",
|
||||
"BURNING_BULWARK",
|
||||
"ENDURING",
|
||||
"STURDY",
|
||||
"PERISH_SONG",
|
||||
"TRUANT",
|
||||
"SLOW_START",
|
||||
"PROTOSYNTHESIS",
|
||||
"QUARK_DRIVE",
|
||||
"FLYING",
|
||||
"UNDERGROUND",
|
||||
"UNDERWATER",
|
||||
"HIDDEN",
|
||||
"FIRE_BOOST",
|
||||
"CRIT_BOOST",
|
||||
"ALWAYS_CRIT",
|
||||
"IGNORE_ACCURACY",
|
||||
"BYPASS_SLEEP",
|
||||
"IGNORE_FLYING",
|
||||
"SALT_CURED",
|
||||
"CURSED",
|
||||
"CHARGED",
|
||||
"ROOSTED",
|
||||
"FLOATING",
|
||||
"MINIMIZED",
|
||||
"DESTINY_BOND",
|
||||
"CENTER_OF_ATTENTION",
|
||||
"ICE_FACE",
|
||||
"DISGUISE",
|
||||
"STOCKPILING",
|
||||
"RECEIVE_DOUBLE_DAMAGE",
|
||||
"ALWAYS_GET_HIT",
|
||||
"DISABLED",
|
||||
"SUBSTITUTE",
|
||||
"IGNORE_GHOST",
|
||||
"IGNORE_DARK",
|
||||
"GULP_MISSILE_ARROKUDA",
|
||||
"GULP_MISSILE_PIKACHU",
|
||||
"BEAK_BLAST_CHARGING",
|
||||
"SHELL_TRAP",
|
||||
"DRAGON_CHEER",
|
||||
"NO_RETREAT",
|
||||
"GORILLA_TACTICS",
|
||||
"UNBURDEN",
|
||||
"THROAT_CHOPPED",
|
||||
"TAR_SHOT",
|
||||
"BURNED_UP",
|
||||
"DOUBLE_SHOCKED",
|
||||
"AUTOTOMIZED",
|
||||
"MYSTERY_ENCOUNTER_POST_SUMMON",
|
||||
"POWER_TRICK",
|
||||
"HEAL_BLOCK",
|
||||
"TORMENT",
|
||||
"TAUNT",
|
||||
"IMPRISON",
|
||||
"SYRUP_BOMB",
|
||||
"ELECTRIFIED",
|
||||
"TELEKINESIS",
|
||||
"COMMANDED",
|
||||
"GRUDGE",
|
||||
"PSYCHO_SHIFT",
|
||||
"ENDURE_TOKEN",
|
||||
"POWDER",
|
||||
"MAGIC_COAT",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Zod schema for {@linkcode BattlerTagLapseType} as of version 1.10
|
||||
* @remarks
|
||||
* - `0`: Faint
|
||||
* - `1`: Move
|
||||
* - `2`: Pre-Move
|
||||
* - `3`: After Move
|
||||
* - `4`: Move Effect
|
||||
* - `5`: Turn End
|
||||
* - `6`: Hit
|
||||
* - `7`: After Hit
|
||||
* - `8`: Custom
|
||||
*/
|
||||
const Z$BattlerTagLapseType = z.literal([0, 1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
|
||||
// DamagingTrapTag may have a `commonAnim` field, though it's always instantiated.
|
||||
const Z$BattlerTag = z.object({
|
||||
tagType: Z$BattlerTagType,
|
||||
lapseTypes: z.array(Z$BattlerTagLapseType),
|
||||
turnCount: Z$PositiveInt,
|
||||
// Source move can be `none` for tags not applied by move, so allow `0` here.
|
||||
sourceMove: Z$NonNegativeInt,
|
||||
sourceId: z.int().optional().catch(undefined),
|
||||
isBatonPassable: z.boolean(),
|
||||
});
|
||||
|
||||
export const Z$SeedTag = z.object({
|
||||
...Z$BattlerTag.shape,
|
||||
seedType: z.literal("SEEDED"),
|
||||
sourceIndex: Z$BattlerIndex,
|
||||
})
|
19
src/system/schemas/v1.10/berry-type.ts
Normal file
19
src/system/schemas/v1.10/berry-type.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for Berry types as of version 1.10.
|
||||
*
|
||||
* @remarks
|
||||
* - `0`: Sitrus
|
||||
* - `1`: Lum
|
||||
* - `2`: Enigma
|
||||
* - `3`: Liechi
|
||||
* - `4`: Ganlon
|
||||
* - `5`: Petaya
|
||||
* - `6`: Apicot
|
||||
* - `7`: Salac
|
||||
* - `8`: Lansat
|
||||
* - `9`: Starf
|
||||
* - `10`: Leppa
|
||||
*/
|
||||
export const Z$BerryType = z.literal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
55
src/system/schemas/v1.10/biome-id.ts
Normal file
55
src/system/schemas/v1.10/biome-id.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Schema for biome IDs, as of version 1.10
|
||||
*
|
||||
* @remarks
|
||||
* - `0`: Town,
|
||||
* - `1`: Plains,
|
||||
* - `2`: Grass,
|
||||
* - `3`: Tall Grass,
|
||||
* - `4`: Metropolis,
|
||||
* - `5`: Forest,
|
||||
* - `6`: Sea,
|
||||
* - `7`: Swamp,
|
||||
* - `8`: Beach,
|
||||
* - `9`: Lake,
|
||||
* - `10`: Seabed,
|
||||
* - `11`: Mountain,
|
||||
* - `12`: Badlands,
|
||||
* - `13`: Cave,
|
||||
* - `14`: Desert,
|
||||
* - `15`: Ice Cave,
|
||||
* - `16`: Meadow,
|
||||
* - `17`: Power Plant,
|
||||
* - `18`: Volcano,
|
||||
* - `19`: Graveyard,
|
||||
* - `20`: Dojo,
|
||||
* - `21`: Factory,
|
||||
* - `22`: Ruins,
|
||||
* - `23`: Wasteland,
|
||||
* - `24`: Abyss,
|
||||
* - `25`: Space,
|
||||
* - `26`: Construction Site,
|
||||
* - `27`: Jungle,
|
||||
* - `28`: Fairy Cave,
|
||||
* - `29`: Temple,
|
||||
* - `30`: Slum,
|
||||
* - `31`: Snowy Forest,
|
||||
* - `40`: Island,
|
||||
* - `41`: Laboratory,
|
||||
* - `50`: End
|
||||
*/
|
||||
export const Z$BiomeID = z.literal([
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
40, 41, 50,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Schema for a pokemon's met biome, as of version 1.10
|
||||
* Same as {@linkcode Z$BiomeID}, additionally allowing `-1` for starters.
|
||||
*/
|
||||
export const Z$MetBiome = z.union([
|
||||
z.literal(-1), // For starters
|
||||
Z$BiomeID, // All other biomes
|
||||
]);
|
17
src/system/schemas/v1.10/custom-pokemon-data.ts
Normal file
17
src/system/schemas/v1.10/custom-pokemon-data.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Z$OptionalNonNegativeIntCatchToUndef } from "#schemas/common";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for custom Pokémon data as of version 1.10.
|
||||
*
|
||||
* @remarks All fields are optional, but catch to `undefined`
|
||||
* on malformed inputs, as the `CustomPokemonData` allows partial data and
|
||||
* uses defaults for missing fields.
|
||||
*/
|
||||
export const Z$CustomPokemonData = z.object({
|
||||
// sprite scale of -1 is allowed, but it's the default meaning there is no override for it
|
||||
spriteScale: z.number().nonnegative().optional().catch(undefined),
|
||||
ability: Z$OptionalNonNegativeIntCatchToUndef.catch(undefined),
|
||||
passive: Z$OptionalNonNegativeIntCatchToUndef.catch(undefined),
|
||||
nature: Z$OptionalNonNegativeIntCatchToUndef.catch(undefined),
|
||||
});
|
8
src/system/schemas/v1.10/move-result.ts
Normal file
8
src/system/schemas/v1.10/move-result.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// biome-ignore lint/correctness/noUnusedImports: used in tsdoc comment
|
||||
import type { MoveResult } from "#enums/move-result";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for the {@linkcode MoveResult} enum as of version 1.10
|
||||
*/
|
||||
export const Z$MoveResult = z.literal([0, 1, 2, 3, 4]);
|
17
src/system/schemas/v1.10/pokeball-type.ts
Normal file
17
src/system/schemas/v1.10/pokeball-type.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Schema for Pokéball types that a pokemon can be caught in, as of version 1.10. Excludes Luxury Ball
|
||||
* - `0`: POKEBALL,
|
||||
* - `1`: GREAT_BALL,
|
||||
* - `2`: ULTRA_BALL,
|
||||
* - `3`: ROGUE_BALL,
|
||||
* - `4`: MASTER_BALL
|
||||
*/
|
||||
export const Z$PokeballType = z.literal([
|
||||
0, // POKEBALL
|
||||
1, // GREAT_BALL
|
||||
2, // ULTRA_BALL
|
||||
3, // ROGUE BALL
|
||||
4, // MASTER_BALL
|
||||
]);
|
18
src/system/schemas/v1.10/pokemon-battle-data.ts
Normal file
18
src/system/schemas/v1.10/pokemon-battle-data.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Z$BerryType } from "#schemas/berry-type";
|
||||
import { Z$NonNegativeInt } from "#schemas/common";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for Pokémon battle data as of version 1.10.
|
||||
*
|
||||
* @remarks
|
||||
* All fields are optional and fallback to undefined if malformed, as `BattleData`'s
|
||||
* constructor supports taking partial data, and using defaults for missing fields.
|
||||
*/
|
||||
export const Z$PokemonBattleData = z.object({
|
||||
hitCount: Z$NonNegativeInt.optional().catch(undefined),
|
||||
hasEatenBerry: z.boolean().optional().catch(undefined),
|
||||
berriesEaten: z.array(Z$BerryType).optional().catch(undefined),
|
||||
});
|
||||
|
||||
export type ParsedPokemonBattleData = z.output<typeof Z$PokemonBattleData>;
|
123
src/system/schemas/v1.10/pokemon-data.ts
Normal file
123
src/system/schemas/v1.10/pokemon-data.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { Z$BoolCatchToFalse, Z$NonNegativeInt, Z$PositiveInt } from "#schemas/common";
|
||||
import { Z$PokeballType } from "#schemas/pokeball-type";
|
||||
import { Z$PokemonMove } from "#schemas/pokemon-move";
|
||||
import { Z$Gender } from "#system/schemas/v1.10/pokemon-gender";
|
||||
import z from "zod";
|
||||
import { NatureSchema } from "./pokemon-nature";
|
||||
import { IVSetSchema, statSetSchema } from "./pokemon-stats";
|
||||
import { Z$PokemonType } from "./pokemon-type";
|
||||
import { StatusSchema } from "./status-effect";
|
||||
|
||||
/**
|
||||
* Not meant to actually be used itself.
|
||||
* Instead, use either {@linkcode Z$PlayerPokemonData} or {@linkcode Z$EnemyPokemonData}.
|
||||
* `looseObject` used here to allow properties specific to player or enemy Pokémon
|
||||
* to be handled by their respective schemas.
|
||||
*/
|
||||
const Z$PokemonData = z.looseObject({
|
||||
// malformed pokemon ids are _not_ supported.
|
||||
id: z.uint32(),
|
||||
species: z.uint32(),
|
||||
nickname: z.string().optional(),
|
||||
formIndex: Z$NonNegativeInt.catch(0),
|
||||
// Between 0 and 2, with malformed inputs defaulting to 0
|
||||
abilityIndex: z.int().min(0).max(2).catch(0),
|
||||
passive: Z$BoolCatchToFalse,
|
||||
shiny: Z$BoolCatchToFalse,
|
||||
variant: z.literal([0, 1, 2]).catch(0),
|
||||
pokeball: Z$PokeballType.catch(0),
|
||||
|
||||
// No fallbacks for level
|
||||
level: Z$NonNegativeInt,
|
||||
// TODO: Fallback to minimum experience for level if parsing error?
|
||||
exp: Z$NonNegativeInt,
|
||||
// Fallback to 0, patch in transformer
|
||||
levelExp: Z$NonNegativeInt.catch(0),
|
||||
// Fallback to -1, patch to a default gender in the transformer.
|
||||
gender: Z$Gender.catch(-1),
|
||||
// hp can be 0 if fainted
|
||||
hp: Z$NonNegativeInt,
|
||||
stats: statSetSchema,
|
||||
ivs: IVSetSchema,
|
||||
nature: NatureSchema,
|
||||
moveset: z.array(Z$PokemonMove).catch([]),
|
||||
status: z.union([z.null(), StatusSchema]).catch(null),
|
||||
friendship: z.int().min(0).max(255).catch(0),
|
||||
|
||||
//#region "met" information
|
||||
metLevel: z.int().positive().catch(1),
|
||||
metBiome: z.union([z.int().nonnegative(), z.literal(-1)]).catch(-1), // -1 for starters
|
||||
metSpecies: z.uint32(),
|
||||
metWave: z.int().min(-1).default(0),
|
||||
//#endregion "met" information
|
||||
|
||||
luck: Z$NonNegativeInt.catch(0),
|
||||
teraType: Z$PokemonType,
|
||||
isTerastallized: Z$BoolCatchToFalse,
|
||||
stellarTypesBoosted: z.array(Z$PokemonType).catch([]),
|
||||
|
||||
//#region "fusion" information
|
||||
fusionSpecies: z.uint32().optional(),
|
||||
fusionFormIndex: Z$NonNegativeInt.optional(),
|
||||
fusionAbilityIndex: z.int().min(0).max(2).optional().catch(0),
|
||||
fusionShiny: Z$BoolCatchToFalse,
|
||||
fusionLuck: Z$NonNegativeInt.catch(0),
|
||||
//#endregion "fusion" information
|
||||
|
||||
pokerus: z.boolean().catch(false),
|
||||
});
|
||||
|
||||
export const Z$PlayerPokemonData = z.object({
|
||||
...Z$PokemonData.shape,
|
||||
player: z.transform((): true => true),
|
||||
pauseEvolutions: Z$BoolCatchToFalse,
|
||||
// Assume player pokemon can never be bosses
|
||||
boss: z.transform((): false => false),
|
||||
bossSegments: z.transform((): 0 => 0),
|
||||
// 0 is unknown, -1 is starter
|
||||
metWave: z.int().min(-1).default(0),
|
||||
usedTms: z.array(Z$PositiveInt).catch([]),
|
||||
// Fallback for empty pokemon movesets handled by transformer
|
||||
moveset: z.array(Z$PokemonMove).catch([]),
|
||||
});
|
||||
|
||||
export const Z$EnemyPokemonData = z.object({
|
||||
...Z$PokemonData.shape,
|
||||
player: z.transform((): false => false),
|
||||
boss: z.boolean().catch(false),
|
||||
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 ParsedPokemonData = z.output<typeof Z$PokemonData>;
|
||||
|
||||
export type PreParsedPlayerPokemonData = z.input<typeof Z$PlayerPokemonData>;
|
||||
export type ParsedPlayerPokemon = z.output<typeof Z$PlayerPokemonData>;
|
||||
|
||||
export type PreParsedEnemyPokemonData = z.input<typeof Z$EnemyPokemonData>;
|
||||
export type ParsedEnemyPokemon = z.output<typeof Z$EnemyPokemonData>;
|
11
src/system/schemas/v1.10/pokemon-gender.ts
Normal file
11
src/system/schemas/v1.10/pokemon-gender.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Schema for a Pokémon's Gender, as of version 1.10
|
||||
*
|
||||
* @remarks
|
||||
* - `-1`: Genderless,
|
||||
* - `0`: Male,
|
||||
* - `1`: Female
|
||||
*/
|
||||
export const Z$Gender = z.literal([-1, 0, 1]);
|
17
src/system/schemas/v1.10/pokemon-move.ts
Normal file
17
src/system/schemas/v1.10/pokemon-move.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
|
||||
/**
|
||||
* Zod schema for a Pokémon move, as of version 1.10.
|
||||
*/
|
||||
export const Z$PokemonMove = z.object({
|
||||
moveId: z.number().int().min(0), // Move ID, default to 0
|
||||
ppUsed: z.number().int().default(0).catch(0), // PP used, default to 0
|
||||
ppUp: z.number().int().default(0).catch(0), // PP Up count, default to 0
|
||||
maxPpOverride: z.int().min(1).optional(), // Optional max PP override, can be null
|
||||
});
|
||||
|
||||
// If necessary, we can define `transforms` which implicitly create an instance of the class.
|
||||
|
||||
export type ParsedPokemonMove = z.output<typeof Z$PokemonMove>;
|
||||
|
33
src/system/schemas/v1.10/pokemon-nature.ts
Normal file
33
src/system/schemas/v1.10/pokemon-nature.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for a Pokémon's nature, as of version 1.10
|
||||
* - `0`: Hardy,
|
||||
* - `1`: Lonely,
|
||||
* - `2`: Brave,
|
||||
* - `3`: Adamant,
|
||||
* - `4`: Naughty,
|
||||
* - `5`: Bold,
|
||||
* - `6`: Docile,
|
||||
* - `7`: Relaxed,
|
||||
* - `8`: Impish,
|
||||
* - `9`: Lax,
|
||||
* - `10`: Timid,
|
||||
* - `11`: Hasty,
|
||||
* - `12`: Serious,
|
||||
* - `13`: Jolly,
|
||||
* - `14`: Naive,
|
||||
* - `15`: Modest,
|
||||
* - `16`: Mild,
|
||||
* - `17`: Quiet,
|
||||
* - `18`: Bashful,
|
||||
* - `19`: Rash,
|
||||
* - `20`: Calm,
|
||||
* - `21`: Gentle,
|
||||
* - `22`: Sassy,
|
||||
* - `23`: Careful,
|
||||
* - `24`: Quirky
|
||||
*/
|
||||
export const NatureSchema = z.literal([
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
]);
|
39
src/system/schemas/v1.10/pokemon-stats.ts
Normal file
39
src/system/schemas/v1.10/pokemon-stats.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { Z$PositiveInt } from "#schemas/common";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Schema for a Pokémon's Individual Values (IVs), as of version 1.10
|
||||
*
|
||||
* @remarks
|
||||
* - Each IV is an integer between 0 and 31, inclusive.
|
||||
* - Malformed values parse to 15 by default.
|
||||
*/
|
||||
export const IVSchema = z.int().min(0).max(31).catch(15);
|
||||
|
||||
/**
|
||||
* Schema for a set of 6 Pokémon IVs, as of version 1.10
|
||||
*
|
||||
* @remarks
|
||||
* Malformed IV sets default to [15, 15, 15, 15, 15, 15].
|
||||
*/
|
||||
export const IVSetSchema = z
|
||||
.tuple([IVSchema, IVSchema, IVSchema, IVSchema, IVSchema, IVSchema])
|
||||
.catch([15, 15, 15, 15, 15, 15]);
|
||||
|
||||
/**
|
||||
* Schema for a Pokémon's stats, as of version 1.10
|
||||
* - [0]: HP,
|
||||
* - [1]: Attack,
|
||||
* - [2]: Defense,
|
||||
* - [3]: Special Attack,
|
||||
* - [4]: Special Defense,
|
||||
* - [5]: Speed
|
||||
*/
|
||||
export const statSetSchema = z.tuple([
|
||||
Z$PositiveInt, // HP
|
||||
Z$PositiveInt, // Attack
|
||||
Z$PositiveInt, // Defense
|
||||
Z$PositiveInt, // Special Attack
|
||||
Z$PositiveInt, // Special Defense
|
||||
Z$PositiveInt, // Speed
|
||||
]);
|
27
src/system/schemas/v1.10/pokemon-summon-data.ts
Normal file
27
src/system/schemas/v1.10/pokemon-summon-data.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Z$TurnMove } from "#system/schemas/v1.10/turn-move";
|
||||
import { z } from "zod";
|
||||
|
||||
|
||||
const Z$StatStage = z.int().min(-6).max(6).catch(0);
|
||||
|
||||
const Z$StatStageSet = z.tuple([
|
||||
Z$StatStage,
|
||||
Z$StatStage,
|
||||
Z$StatStage,
|
||||
Z$StatStage,
|
||||
Z$StatStage,
|
||||
Z$StatStage,
|
||||
Z$StatStage])
|
||||
/**
|
||||
* Zod schema for Pokémon summon data as of version 1.10.
|
||||
*
|
||||
*/
|
||||
export const Z$PokemonSummonData = z.object({
|
||||
statStages: Z$StatStageSet.optional().catch(undefined),
|
||||
moveQueue: z.array(Z$TurnMove).optional().catch(undefined),
|
||||
|
||||
//#region Overrides for transform
|
||||
|
||||
//#endregion Overrides for transform
|
||||
|
||||
});
|
26
src/system/schemas/v1.10/pokemon-type.ts
Normal file
26
src/system/schemas/v1.10/pokemon-type.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Schema for a Pokémon's type, as of version 1.10
|
||||
* - `-1`: Unknown (aka typeless),
|
||||
* - `0`: Normal,
|
||||
* - `1`: Fighting,
|
||||
* - `2`: Flying,
|
||||
* - `3`: Poison,
|
||||
* - `4`: Ground,
|
||||
* - `5`: Rock,
|
||||
* - `6`: Bug,
|
||||
* - `7`: Ghost,
|
||||
* - `8`: Steel,
|
||||
* - `9`: Fire,
|
||||
* - `10`: Water,
|
||||
* - `11`: Grass,
|
||||
* - `12`: Electric,
|
||||
* - `13`: Psychic,
|
||||
* - `14`: Ice,
|
||||
* - `15`: Dragon,
|
||||
* - `16`: Dark,
|
||||
* - `17`: Fairy
|
||||
* - `18`: Stellar
|
||||
*/
|
||||
export const Z$PokemonType = z.literal([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]);
|
0
src/system/schemas/v1.10/session-save-data.ts
Normal file
0
src/system/schemas/v1.10/session-save-data.ts
Normal file
31
src/system/schemas/v1.10/status-effect.ts
Normal file
31
src/system/schemas/v1.10/status-effect.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// biome-ignore lint/correctness/noUnusedImports: used in tsdoc comment
|
||||
import type { Status } from "#data/status-effect";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { z } from "zod";
|
||||
import { Z$NonNegativeInt, Z$PositiveInt } from "../common";
|
||||
|
||||
/**
|
||||
* Zod schema for the {@linkcode StatusEffect} enum
|
||||
*
|
||||
* @remarks
|
||||
* - `0`: NONE,
|
||||
* - `1`: POISON,
|
||||
* - `2`: TOXIC,
|
||||
* - `3`: PARALYSIS,
|
||||
* - `4`: SLEEP,
|
||||
* - `5`: FREEZE,
|
||||
* - `6`: BURN,
|
||||
* - `7`: FAINT
|
||||
*/
|
||||
const Z$StatusEffect = z.int().min(StatusEffect.NONE).max(StatusEffect.FAINT).catch(StatusEffect.NONE);
|
||||
|
||||
// Note: This does not validate that sleepTurnsRemaining exists when effect is SLEEP.
|
||||
// This is game logic that should perhaps exist in the constructor.
|
||||
/**
|
||||
* Zod schema for the {@linkcode Status} class
|
||||
*/
|
||||
export const StatusSchema = z.object({
|
||||
effect: Z$StatusEffect,
|
||||
toxicTurnCount: Z$NonNegativeInt.catch(0),
|
||||
sleepTurnsRemaining: Z$PositiveInt.optional().catch(0),
|
||||
});
|
25
src/system/schemas/v1.10/turn-move.ts
Normal file
25
src/system/schemas/v1.10/turn-move.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// biome-ignore-start lint/correctness/noUnusedImports: used in tsdoc comment
|
||||
import type { MoveUseMode } from "#enums/move-use-mode";
|
||||
import type { TurnMove } from "#types/turn-move";
|
||||
// biome-ignore-end lint/correctness/noUnusedImports: used in tsdoc comment
|
||||
|
||||
import { Z$NonNegativeInt, Z$PositiveInt } from "#system/schemas/common";
|
||||
import { Z$BattlerIndex } from "#system/schemas/v1.10/battler-index";
|
||||
import { Z$MoveResult } from "#system/schemas/v1.10/move-result";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Zod schema for the {@linkcode MoveUseMode} enum
|
||||
*/
|
||||
export const Z$MoveUseMode = z.literal([1, 2, 3, 4, 5]);
|
||||
|
||||
/**
|
||||
* Zod schema for `{@linkcode TurnMove} as of version 1.10.
|
||||
*/
|
||||
export const Z$TurnMove = z.object({
|
||||
move: Z$PositiveInt,
|
||||
targets: z.array(Z$BattlerIndex),
|
||||
useMode: Z$MoveUseMode,
|
||||
result: Z$MoveResult.optional().catch(undefined),
|
||||
turn: Z$NonNegativeInt.optional().catch(undefined)
|
||||
});
|
@ -48,6 +48,7 @@
|
||||
"./system/version-migration/*.ts",
|
||||
"./system/*.ts"
|
||||
],
|
||||
"#schemas/*": ["./system/schemas/*.ts", "./system/schemas/v1.10/*.ts"],
|
||||
"#trainers/*": ["./data/trainers/*.ts"],
|
||||
"#types/*": ["./@types/helpers/*.ts", "./@types/*.ts", "./typings/phaser/*.ts"],
|
||||
"#ui/*": ["./ui/battle-info/*.ts", "./ui/settings/*.ts", "./ui/*.ts"],
|
||||
|
Loading…
Reference in New Issue
Block a user