// Library to validate data before writing to .csv
import { z } from "zod";
// readonly ["M", "F", "N"]
import { genderNames } from "../enums";
// Constructed according to @pkmn package
import { gens } from "../gens";
// Writes JSON object to .csv using json-2-csv library
import { writeJunctionJsonToCsv } from "../path";
import { toID } from "@pkmn/data";
// Validates a .csv row for event data
const eventSchema = z.object({
gen: z.number(),
pokemonPsId: z.string(),
eventIndex: z.number(),
level: z.number().optional(),
shinyChance: z.enum(["ALWAYS", "SOMETIMES", "NEVER"]),
gender: z.enum(genderNames).optional(),
nature: z.string().optional(),
// Written to JSON string in the .csv
guaranteedIvs: z
.object({
hp: z.number().optional(),
atk: z.number().optional(),
def: z.number().optional(),
spa: z.number().optional(),
spd: z.number().optional(),
spe: z.number().optional(),
})
.optional(),
numberPerfectIvs: z.number().optional(),
hiddenAbilityChance: z.enum(["ALWAYS", "NEVER"]).optional(),
// Written to JSON string in the .csv
abilityPsIds: z.array(z.string()).optional(),
movePsIds: z.array(z.string()).optional(),
pokeball: z.string().optional(),
japanOnly: z.boolean().optional(),
});
// Matches Pokemon to Move
const learnsMoveSchema = z.object({
gen: z.number(),
pokemonPsId: z.string(),
movePsId: z.string(),
// Semicolon separated
learnMethods: z.string(),
});
export async function writeLearnsetAndEventCsv() {
const eventRows: z.infer<typeof eventSchema>[] = [];
const learnsMoveRows: z.infer<typeof learnsMoveSchema>[] = [];
// Keys are "gen_pokemon_move", values are learn method codes
const learnMethodByPokemonPsId_MovePsId = new Map<string, Set<string>>();
for (const gen of gens) {
const learnsets = gen.learnsets;
const genString = gen.num.toString();
for (const specie of gen.species) {
for await (const learnset of learnsets.all(specie)) {
const { learnset: learnData, eventData } = learnset;
// Parse the event data
if (eventData) {
eventData.forEach((eventInfo, index) => {
eventRows.push(
eventSchema.parse({
gen: gen.num,
pokemonPsId: specie.id,
eventIndex: index,
level: eventInfo.level,
shinyChance:
eventInfo.shiny === 1
? "SOMETIMES"
: eventInfo.shiny
? "ALWAYS"
: "NEVER",
gender: eventInfo.gender,
nature: toID(eventInfo.nature),
guaranteedIvs: eventInfo.ivs,
numberPerfectIvs: eventInfo.perfectIVs,
hiddenAbilityChance: eventInfo.isHidden ? "ALWAYS" : "NEVER",
abilityPsIds: eventInfo.abilities,
movePsIds: eventInfo.moves,
pokeball: eventInfo.pokeball,
japanOnly: eventInfo.japan,
})
);
});
}
// Parse the learnset data
if (learnData) {
for (const [movePsId, learnMethods] of Object.entries(learnData)) {
for (let learnMethod of learnMethods) {
if (learnMethod.indexOf(genString) !== 0) {
continue;
}
// Not a real source, so I don't want to store it
// See https://github.com/smogon/pokemon-showdown/blob/master/sim/global-types.ts, 'MoveSource' type for codes
else if (learnMethod.includes("C")) {
continue;
}
learnMethod = learnMethod.replace(genString, "");
const pokemonPsId = specie.id;
const key = [genString, pokemonPsId, movePsId].join("_");
if (learnMethodByPokemonPsId_MovePsId.has(key)) {
learnMethodByPokemonPsId_MovePsId.get(key)?.add(learnMethod);
} else {
learnMethodByPokemonPsId_MovePsId.set(
key,
new Set([learnMethod])
);
}
}
}
}
}
}
for (let [key, value] of learnMethodByPokemonPsId_MovePsId.entries()) {
const [genString, pokemonId, moveId] = key.split("_");
if (!genString || !pokemonId || !moveId) {
continue;
}
learnsMoveRows.push({
gen: +genString,
pokemonPsId: pokemonId,
movePsId: moveId,
learnMethods: [...value].join(";"),
});
}
}
writeJunctionJsonToCsv("pokemon", "event", eventRows);
writeJunctionJsonToCsv("pokemon", "learnsMove", learnsMoveRows);
}