mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-20 06:19:29 +02:00
Merge e420b976cf
into ee4950633e
This commit is contained in:
commit
bff9f5dd84
7
.github/workflows/github-pages.yml
vendored
7
.github/workflows/github-pages.yml
vendored
@ -62,8 +62,15 @@ jobs:
|
||||
working-directory: ${{env.api-dir}}
|
||||
run: |
|
||||
cd pokerogue_docs
|
||||
pnpm typedoc
|
||||
pnpm exec typedoc --out /tmp/docs --githubPages false --entryPoints ./src/
|
||||
|
||||
- name: Typecheck Node.js Scripts
|
||||
working-directory: ${{env.api-dir}}
|
||||
run: |
|
||||
cd pokerogue_docs
|
||||
pnpm typecheck:scripts
|
||||
|
||||
- name: Commit & Push docs
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
|
@ -19,6 +19,7 @@
|
||||
// and having to verify whether each individual file is ignored
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/*.d.ts",
|
||||
"!**/dist/**/*",
|
||||
"!**/build/**/*",
|
||||
"!**/coverage/**/*",
|
||||
@ -177,9 +178,10 @@
|
||||
},
|
||||
|
||||
// Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes),
|
||||
// as well as in all TS files in `scripts/` (which are assumed to be boilerplate templates).
|
||||
// as well as inside script boilerplate files.
|
||||
{
|
||||
"includes": ["**/src/overrides.ts", "**/src/enums/**/*", "**/scripts/**/*.ts", "**/*.d.ts"],
|
||||
// TODO: Rename existing boilerplates in the folder and remove this last alias
|
||||
"includes": ["**/src/overrides.ts", "**/src/enums/**/*", "scripts/**/*.boilerplate.ts", "**/boilerplates/*.ts"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"correctness": {
|
||||
@ -189,7 +191,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"includes": ["**/src/overrides.ts", "**/scripts/**/*.ts"],
|
||||
"includes": ["**/src/overrides.ts"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
|
@ -14,7 +14,9 @@
|
||||
"test:watch": "vitest watch --coverage --no-isolate",
|
||||
"test:silent": "vitest run --silent='passed-only' --no-isolate",
|
||||
"test:create": "node scripts/create-test/create-test.js",
|
||||
"eggMoves:parse": "node scripts/parse-egg-moves/main.js",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:scripts": "tsc -p scripts/jsconfig.json",
|
||||
"eslint": "eslint --fix .",
|
||||
"eslint-ci": "eslint .",
|
||||
"biome": "biome check --write --changed --no-errors-on-unmatched --diagnostic-level=error",
|
||||
@ -29,6 +31,7 @@
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@ls-lint/ls-lint": "2.3.1",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/node": "^22.16.5",
|
||||
"@vitest/coverage-istanbul": "^3.2.4",
|
||||
|
@ -48,6 +48,9 @@ importers:
|
||||
'@ls-lint/ls-lint':
|
||||
specifier: 2.3.1
|
||||
version: 2.3.1
|
||||
'@types/crypto-js':
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
'@types/jsdom':
|
||||
specifier: ^21.1.7
|
||||
version: 21.1.7
|
||||
@ -718,6 +721,9 @@ packages:
|
||||
'@types/cookie@0.6.0':
|
||||
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
|
||||
|
||||
'@types/crypto-js@4.2.2':
|
||||
resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
@ -2525,6 +2531,8 @@ snapshots:
|
||||
|
||||
'@types/cookie@0.6.0': {}
|
||||
|
||||
'@types/crypto-js@4.2.2': {}
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
@ -156,7 +156,7 @@ async function runInteractive() {
|
||||
console.log(chalk.green.bold(`✔ File created at: test/${localDir}/${fileName}.test.ts\n`));
|
||||
console.groupEnd();
|
||||
} catch (err) {
|
||||
console.error(chalk.red("✗ Error: ", err.message));
|
||||
console.error(chalk.red("✗ Error: ", err));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Usage: node decrypt-save.js <encrypted-file> [save-file]
|
||||
|
||||
// biome-ignore lint/performance/noNamespaceImport: This is how you import fs from node
|
||||
import * as fs from "node:fs";
|
||||
import fs from "node:fs";
|
||||
import crypto_js from "crypto-js";
|
||||
|
||||
const { AES, enc } = crypto_js;
|
||||
@ -60,6 +59,11 @@ function decryptSave(path) {
|
||||
try {
|
||||
fileData = fs.readFileSync(path, "utf8");
|
||||
} catch (e) {
|
||||
if (!(e instanceof Error)) {
|
||||
console.error(`Unrecognized error: ${e}`);
|
||||
process.exit(1);
|
||||
}
|
||||
// @ts-expect-error: TODO fix
|
||||
switch (e.code) {
|
||||
case "ENOENT":
|
||||
console.error(`File not found: ${path}`);
|
||||
@ -104,6 +108,13 @@ function writeToFile(filePath, data) {
|
||||
try {
|
||||
fs.writeFileSync(filePath, data);
|
||||
} catch (e) {
|
||||
if (!(e instanceof Error)) {
|
||||
console.error("Unknown error detected: ", e);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error - e is usually a SystemError (all of which have codes)
|
||||
switch (e.code) {
|
||||
case "EACCES":
|
||||
console.error(`Could not open ${filePath}: Permission denied`);
|
||||
@ -114,7 +125,8 @@ function writeToFile(filePath, data) {
|
||||
default:
|
||||
console.error(`Error writing file: ${e.message}`);
|
||||
}
|
||||
process.exit(1);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
17
scripts/jsconfig.json
Normal file
17
scripts/jsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"include": ["**/*.js"],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"rootDir": ".",
|
||||
"target": "esnext",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"erasableSyntaxOnly": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
// Forcibly disable `node_modules` recursion to prevent TSC from typechecking random JS files.
|
||||
// This is disabled by default in `tsconfig.json`, but needs to be explicitly disabled from the default of `2`
|
||||
"maxNodeModuleJsDepth": 0
|
||||
}
|
||||
}
|
10
scripts/parse-egg-moves/egg-move-template.boilerplate.ts
Normal file
10
scripts/parse-egg-moves/egg-move-template.boilerplate.ts
Normal file
@ -0,0 +1,10 @@
|
||||
//! DO NOT EDIT THIS FILE - CREATED BY THE `eggMoves:parse` script automatically
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/**
|
||||
* An object mapping all base form {@linkcode SpeciesId}s to an array of {@linkcode MoveId}s corresponding
|
||||
* to their current egg moves.
|
||||
* Generated by the `eggMoves:parse` script using a CSV sourced from the current Balance Team spreadsheet.
|
||||
*/
|
||||
export const speciesEggMoves = "{{table}}";
|
17
scripts/parse-egg-moves/help-message.js
Normal file
17
scripts/parse-egg-moves/help-message.js
Normal file
@ -0,0 +1,17 @@
|
||||
import chalk from "chalk";
|
||||
|
||||
/** Show help/usage text for the `eggMoves:parse` CLI. */
|
||||
export function showHelpText() {
|
||||
console.log(`
|
||||
Usage: ${chalk.cyan("pnpm eggMoves:parse [options]")}
|
||||
If given no options, assumes ${chalk.blue("\`--interactive\`")}.
|
||||
If given only a file path, assumes ${chalk.blue("\`--file\`")}.
|
||||
|
||||
${chalk.hex("#ffa500")("Options:")}
|
||||
${chalk.blue("-h, --help")} Show this help message.
|
||||
${chalk.blue("-f, --file[=PATH]")} Specify a path to a CSV file to read, or provide one from stdin.
|
||||
${chalk.blue("-t, --text[=TEXT]")}
|
||||
${chalk.blue("-c, --console[=TEXT]")} Specify CSV text to read, or provide it from stdin.
|
||||
${chalk.blue("-i, --interactive")} Run in interactive mode (default)
|
||||
`);
|
||||
}
|
104
scripts/parse-egg-moves/interactive.js
Normal file
104
scripts/parse-egg-moves/interactive.js
Normal file
@ -0,0 +1,104 @@
|
||||
import fs from "fs";
|
||||
import chalk from "chalk";
|
||||
import inquirer from "inquirer";
|
||||
import { showHelpText } from "./help-message.js";
|
||||
|
||||
/**
|
||||
* Prompt the user to interactively select an option (console/file) to retrieve the egg move CSV.
|
||||
* @returns {Promise<{type: "Console" | "File", value: string} | {type: "Exit"}>} The selected option with value
|
||||
*/
|
||||
export async function runInteractive() {
|
||||
/** @type {"Console" | "File" | "Help" | "Exit"} */
|
||||
const answer = await inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "list",
|
||||
name: "type",
|
||||
message: "Select the method to obtain egg moves.",
|
||||
choices: ["Console", "File", "Help", "Exit"],
|
||||
},
|
||||
])
|
||||
.then(a => a.type);
|
||||
|
||||
if (answer === "Exit") {
|
||||
console.log("Exiting...");
|
||||
process.exitCode = 1;
|
||||
return { type: "Exit" };
|
||||
}
|
||||
|
||||
if (answer === "Help") {
|
||||
showHelpText();
|
||||
return { type: "Exit" };
|
||||
}
|
||||
|
||||
if (!["Console", "File"].includes(answer)) {
|
||||
console.error(chalk.red("Please provide a valid type!"));
|
||||
return await runInteractive();
|
||||
}
|
||||
|
||||
return { type: answer, value: await promptForValue(answer) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to give a value (either the direct CSV or the file path).
|
||||
* @param {"Console" | "File"} type - The input method
|
||||
* @returns {Promise<string>} A Promise resolving with the CSV/file path.
|
||||
*/
|
||||
function promptForValue(type) {
|
||||
switch (type) {
|
||||
case "Console":
|
||||
return doPromptConsole();
|
||||
case "File":
|
||||
return getFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to enter a file path from the console.
|
||||
* @returns {Promise<string>} The file path inputted by the user.
|
||||
*/
|
||||
async function getFilePath() {
|
||||
return await inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "path",
|
||||
message: "Please enter the path to the egg move CSV file.",
|
||||
validate: input => {
|
||||
if (input.trim() === "") {
|
||||
return "File path cannot be empty!";
|
||||
}
|
||||
if (!fs.existsSync(input)) {
|
||||
return "File does not exist!";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
])
|
||||
.then(answer => answer.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for CSV input from the console.
|
||||
* @returns {Promise<string>} The CSV input from the user.
|
||||
*/
|
||||
async function doPromptConsole() {
|
||||
return await inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "csv",
|
||||
message: "Please enter the egg move CSV text.",
|
||||
validate: input => {
|
||||
if (input.trim() === "") {
|
||||
return "CSV text cannot be empty!";
|
||||
}
|
||||
if (!input.match(/^[^,]+(,[^,]+){4}$/gm)) {
|
||||
return "CSV text malformed - should contain 5 consecutive comma-separated values per line!";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
])
|
||||
.then(answer => answer.csv);
|
||||
}
|
168
scripts/parse-egg-moves/main.js
Normal file
168
scripts/parse-egg-moves/main.js
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This script accepts a CSV value or file path as input, parses the egg moves,
|
||||
* and writes the output to a TypeScript file.
|
||||
* It can be run interactively or with command line arguments.
|
||||
* Usage: `pnpm eggMoves:parse`
|
||||
*/
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import chalk from "chalk";
|
||||
import { showHelpText } from "./help-message.js";
|
||||
import { runInteractive } from "./interactive.js";
|
||||
import { parseEggMoves } from "./parse.js";
|
||||
|
||||
const version = "1.0.0";
|
||||
|
||||
// Get the directory name of the current module file
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const projectRoot = path.join(__dirname, "..", "..");
|
||||
const templatePath = path.join(__dirname, "egg-move-template.ts");
|
||||
// TODO: Do we want this to be configurable?
|
||||
const eggMoveTargetPath = path.join(projectRoot, "src/data/balance/egg-moves.ts");
|
||||
|
||||
/**
|
||||
* @typedef {{type: "Console" | "File", value: string} | {type: "Exit"}}
|
||||
* Option
|
||||
* An option selected by the user.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Runs the interactive eggMoves:parse CLI.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function start() {
|
||||
console.log(chalk.yellow(`🥚 Egg Move Parser - v${version}`));
|
||||
|
||||
if (process.argv.length > 4) {
|
||||
console.error(
|
||||
chalk.redBright.bold(
|
||||
`✗ Error: Too many arguments provided!\nArgs: ${chalk.hex("#7310fdff")(process.argv.slice(2).join(" "))}`,
|
||||
),
|
||||
);
|
||||
showHelpText();
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
let csv = "";
|
||||
const inputType = await parseArguments();
|
||||
if (process.exitCode) {
|
||||
// If exit code is non-zero, return to allow it to propagate up the chain.
|
||||
return;
|
||||
}
|
||||
switch (inputType.type) {
|
||||
case "Console":
|
||||
csv = inputType.value;
|
||||
break;
|
||||
case "File":
|
||||
csv = await fs.promises.readFile(inputType.value, "utf-8");
|
||||
break;
|
||||
case "Exit":
|
||||
// Help screen triggered; break out
|
||||
return;
|
||||
}
|
||||
|
||||
await writeToFile(parseEggMoves(csv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the arguments passed to the script and obtain the CSV input type.
|
||||
* @returns {Promise<{type: "Console" | "File", value: string} | {type: "Exit"}>} The input method selected by the user
|
||||
*/
|
||||
async function parseArguments() {
|
||||
const args = process.argv.slice(2); // first 2 args are node and script name (irrelevant)
|
||||
|
||||
/** @type {string | undefined} */
|
||||
const arg = args[0].split("=")[0]; // Yoink everything up to the first "=" to get the raw command
|
||||
switch (arg) {
|
||||
case "-f":
|
||||
case "--file":
|
||||
return { type: "File", value: getArgValue() };
|
||||
case "-t":
|
||||
case "--text":
|
||||
case "-c":
|
||||
case "--console":
|
||||
return { type: "Console", value: getArgValue() };
|
||||
case "-h":
|
||||
case "--help":
|
||||
showHelpText();
|
||||
process.exitCode = 0;
|
||||
return { type: "Exit" };
|
||||
case "--interactive":
|
||||
case "-i":
|
||||
case undefined:
|
||||
return await runInteractive();
|
||||
default:
|
||||
// If no arguments are found, check if it's a file path
|
||||
if (fs.existsSync(arg)) {
|
||||
console.log(chalk.green(`Using file path from stdin: ${chalk.blue(arg)}`));
|
||||
return { type: "File", value: arg };
|
||||
}
|
||||
badArgs();
|
||||
return { type: "Exit" };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the argument provided.
|
||||
* @returns {string} The CSV or file path from the arguments
|
||||
* @throws {Error} If arguments are malformed
|
||||
*/
|
||||
function getArgValue() {
|
||||
// If the user provided a value as argument 2, take that as the argument.
|
||||
// Otherwise, check the 1st argument to see if it contains an `=` and extract everything afterwards.
|
||||
/** @type {string | undefined} */
|
||||
let filePath = process.argv[3];
|
||||
const equalsIndex = process.argv[2].indexOf("=");
|
||||
if (equalsIndex > -1) {
|
||||
// If arg 3 was aleady existing and someone used `=` notation to assign a property, throw an error.
|
||||
filePath = filePath ? undefined : process.argv[2].slice(equalsIndex + 1);
|
||||
}
|
||||
|
||||
if (!filePath?.trim()) {
|
||||
badArgs();
|
||||
return "";
|
||||
}
|
||||
// NB: It doesn't really matter that this can be `undefined` - we'll always break out by lieu of setting the exit code
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the parsed CSV to a file.
|
||||
* @param {string} moves - The parsed CSV
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function writeToFile(moves) {
|
||||
try {
|
||||
// Read the template file, replacing the placeholder with the move table.
|
||||
const content = fs.readFileSync(templatePath, "utf8").replace(`"{{table}}"`, moves);
|
||||
|
||||
if (fs.existsSync(eggMoveTargetPath)) {
|
||||
console.warn(chalk.hex("#ffa500")("\nEgg moves file already exists, overwriting...\n"));
|
||||
}
|
||||
|
||||
// Write the template content to the file
|
||||
fs.writeFileSync(eggMoveTargetPath, content, "utf8");
|
||||
|
||||
console.log(chalk.green.bold(`\n✔ Egg Moves written to ${eggMoveTargetPath}`));
|
||||
console.groupEnd();
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`✗ Error while writing egg moves: ${err}`));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do logging for incorrect or malformed CLI arguments.
|
||||
* @returns {void}
|
||||
*/
|
||||
function badArgs() {
|
||||
chalk.red.bold(`✗ Error: Malformed arguments!\nArgs: ${chalk.hex("#7310fdff")(process.argv.slice(2).join(" "))}`);
|
||||
showHelpText();
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
start();
|
79
scripts/parse-egg-moves/parse.js
Normal file
79
scripts/parse-egg-moves/parse.js
Normal file
@ -0,0 +1,79 @@
|
||||
import chalk from "chalk";
|
||||
|
||||
/**
|
||||
* A single line of the inputted CSV.
|
||||
* @typedef {[speciesName: string, move1: string, move2: string, move3: string, move4: string]}
|
||||
* CSVLine
|
||||
*/
|
||||
|
||||
/**
|
||||
* Regex to determine if a string follows the required CSV format.
|
||||
*/
|
||||
const csvRegex = /^((?:[^,]+?,){4}(?:\w|\s)+?,?\n?)+$/g;
|
||||
|
||||
/**
|
||||
* Given a CSV string, parse it and return a structured table ready to be inputted into code.
|
||||
* @param {string} csv - The formatted CSV string.
|
||||
* @returns {string} The fully formatted table.
|
||||
*/
|
||||
export function parseEggMoves(csv) {
|
||||
console.log(chalk.grey("⚙️ Parsing egg moves..."));
|
||||
if (!csvRegex.test(csv)) {
|
||||
console.error(chalk.redBright("! Input was not proper CSV!"));
|
||||
process.exitCode = 1;
|
||||
return "";
|
||||
}
|
||||
|
||||
let output = "{\n";
|
||||
|
||||
const lines = csv.split(/\n/g);
|
||||
|
||||
for (const line of lines) {
|
||||
/**
|
||||
* The individual CSV column for this species.
|
||||
*/
|
||||
const cols =
|
||||
/** @type {CSVLine} */
|
||||
(line.split(",").slice(0, 5));
|
||||
const speciesName = toUpperSnakeCase(cols[0]);
|
||||
|
||||
const eggMoves =
|
||||
/** @type {string[]} */
|
||||
([]);
|
||||
|
||||
for (let m = 1; m < 5; m++) {
|
||||
const moveName = cols[m].trim();
|
||||
if (!moveName || moveName === "N/A") {
|
||||
console.warn(`Species ${speciesName} missing ${m}th egg move!`);
|
||||
eggMoves.push("MoveId.NONE");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove (N) and (P) from the ends of move names before UPPER_SNAKE_CASE-ing them
|
||||
const moveNameTitle = toUpperSnakeCase(moveName.replace(/ \([A-Z]\)$/, ""));
|
||||
eggMoves.push("MoveId." + moveNameTitle);
|
||||
}
|
||||
|
||||
if (eggMoves.every(move => move === "MoveId.NONE")) {
|
||||
console.warn(`Species ${speciesName} could not be parsed, excluding from output...`);
|
||||
output += ` // [SpeciesId.${speciesName}]: [ MoveId.NONE, MoveId.NONE, MoveId.NONE, MoveId.NONE ],\n`;
|
||||
} else {
|
||||
output += ` [SpeciesId.${speciesName}]: [ ${eggMoves.join(", ")} ],\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// NB: We omit the semicolon as it is contained in the template string itself
|
||||
return output + "} satisfies Partial<Record<SpeciesId, [MoveId, MoveId, MoveId, MoveId]>>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert a string into `UPPER_SNAKE_CASE`.
|
||||
* @param {string} str - The string being converted
|
||||
* @returns {string} The result of converting `str` into upper snake case.
|
||||
*/
|
||||
function toUpperSnakeCase(str) {
|
||||
return str
|
||||
.split(/[_ -]+/g)
|
||||
.map(word => word.toUpperCase())
|
||||
.join("_");
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
import { allMoves } from "#data/data-lists";
|
||||
//! DO NOT EDIT THIS FILE - CREATED BY THE `eggMoves:parse` script automatically
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getEnumKeys, getEnumValues } from "#utils/enums";
|
||||
import { toTitleCase } from "#utils/strings";
|
||||
|
||||
/**
|
||||
* An object mapping all base form {@linkcode SpeciesId}s to an array of {@linkcode MoveId}s corresponding
|
||||
* to their current egg moves.
|
||||
* Generated by the `eggMoves:parse` script using a CSV sourced from the current Balance Team spreadsheet.
|
||||
*/
|
||||
export const speciesEggMoves = {
|
||||
[SpeciesId.BULBASAUR]: [ MoveId.SAPPY_SEED, MoveId.MALIGNANT_CHAIN, MoveId.EARTH_POWER, MoveId.MATCHA_GOTCHA ],
|
||||
[SpeciesId.CHARMANDER]: [ MoveId.DRAGON_DANCE, MoveId.BITTER_BLADE, MoveId.EARTH_POWER, MoveId.OBLIVION_WING ],
|
||||
@ -15,7 +18,7 @@ export const speciesEggMoves = {
|
||||
[SpeciesId.SPEAROW]: [ MoveId.FLOATY_FALL, MoveId.EXTREME_SPEED, MoveId.KNOCK_OFF, MoveId.TRIPLE_ARROWS ],
|
||||
[SpeciesId.EKANS]: [ MoveId.NOXIOUS_TORQUE, MoveId.DRAGON_DANCE, MoveId.SLACK_OFF, MoveId.SHED_TAIL ],
|
||||
[SpeciesId.SANDSHREW]: [ MoveId.HIGH_HORSEPOWER, MoveId.DIRE_CLAW, MoveId.SHORE_UP, MoveId.MIGHTY_CLEAVE ],
|
||||
[SpeciesId.NIDORAN_F]: [ MoveId.BANEFUL_BUNKER, MoveId.MOONLIGHT, MoveId.BARB_BARRAGE, MoveId.THOUSAND_WAVES ],
|
||||
[SpeciesId.NIDORAN_F]: [ MoveId.CALM_MIND, MoveId.MOONLIGHT, MoveId.MALIGNANT_CHAIN, MoveId.SANDSEAR_STORM ],
|
||||
[SpeciesId.NIDORAN_M]: [ MoveId.DRAGON_DANCE, MoveId.MOUNTAIN_GALE, MoveId.NOXIOUS_TORQUE, MoveId.PRECIPICE_BLADES ],
|
||||
[SpeciesId.VULPIX]: [ MoveId.MOONBLAST, MoveId.INFERNAL_PARADE, MoveId.MORNING_SUN, MoveId.TAIL_GLOW ],
|
||||
[SpeciesId.ZUBAT]: [ MoveId.FLOATY_FALL, MoveId.DIRE_CLAW, MoveId.SWORDS_DANCE, MoveId.COLLISION_COURSE ],
|
||||
@ -293,7 +296,7 @@ export const speciesEggMoves = {
|
||||
[SpeciesId.ARCHEN]: [ MoveId.ROOST, MoveId.EARTHQUAKE, MoveId.FLOATY_FALL, MoveId.MIGHTY_CLEAVE ],
|
||||
[SpeciesId.TRUBBISH]: [ MoveId.COIL, MoveId.RECOVER, MoveId.DIRE_CLAW, MoveId.GIGATON_HAMMER ],
|
||||
[SpeciesId.ZORUA]: [ MoveId.MALIGNANT_CHAIN, MoveId.MOONBLAST, MoveId.SECRET_SWORD, MoveId.FIERY_WRATH ],
|
||||
[SpeciesId.MINCCINO]: [ MoveId.ICICLE_SPEAR, MoveId.TIDY_UP, MoveId.LOW_KICK, MoveId.POPULATION_BOMB ],
|
||||
[SpeciesId.MINCCINO]: [ MoveId.ICICLE_SPEAR, MoveId.TIDY_UP, MoveId.KNOCK_OFF, MoveId.POPULATION_BOMB ],
|
||||
[SpeciesId.GOTHITA]: [ MoveId.RECOVER, MoveId.MOONBLAST, MoveId.AURA_SPHERE, MoveId.LUMINA_CRASH ],
|
||||
[SpeciesId.SOLOSIS]: [ MoveId.MIST_BALL, MoveId.SPEED_SWAP, MoveId.FLAMETHROWER, MoveId.LIGHT_OF_RUIN ],
|
||||
[SpeciesId.DUCKLETT]: [ MoveId.SPLISHY_SPLASH, MoveId.SANDSEAR_STORM, MoveId.WILDBOLT_STORM, MoveId.QUIVER_DANCE ],
|
||||
@ -310,7 +313,7 @@ export const speciesEggMoves = {
|
||||
[SpeciesId.TYNAMO]: [ MoveId.SCALD, MoveId.STRENGTH_SAP, MoveId.FIRE_LASH, MoveId.AURA_WHEEL ],
|
||||
[SpeciesId.ELGYEM]: [ MoveId.THUNDERCLAP, MoveId.BADDY_BAD, MoveId.AURA_SPHERE, MoveId.PHOTON_GEYSER ],
|
||||
[SpeciesId.LITWICK]: [ MoveId.GIGA_DRAIN, MoveId.EARTH_POWER, MoveId.MOONBLAST, MoveId.TORCH_SONG ],
|
||||
[SpeciesId.AXEW]: [ MoveId.STONE_AXE, MoveId.DIRE_CLAW, MoveId.RAGING_FURY, MoveId.BITTER_BLADE ],
|
||||
[SpeciesId.AXEW]: [ MoveId.STONE_AXE, MoveId.DIRE_CLAW, MoveId.BITTER_BLADE, MoveId.GLAIVE_RUSH ],
|
||||
[SpeciesId.CUBCHOO]: [ MoveId.MOUNTAIN_GALE, MoveId.AQUA_STEP, MoveId.ICE_SHARD, MoveId.COLLISION_COURSE ],
|
||||
[SpeciesId.CRYOGONAL]: [ MoveId.FREEZING_GLARE, MoveId.AURORA_VEIL, MoveId.NASTY_PLOT, MoveId.ORIGIN_PULSE ],
|
||||
[SpeciesId.SHELMET]: [ MoveId.POWER_GEM, MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.STEAM_ERUPTION ],
|
||||
@ -448,7 +451,7 @@ export const speciesEggMoves = {
|
||||
[SpeciesId.ROOKIDEE]: [ MoveId.ROOST, MoveId.BODY_PRESS, MoveId.KINGS_SHIELD, MoveId.BEHEMOTH_BASH ],
|
||||
[SpeciesId.BLIPBUG]: [ MoveId.HEAL_ORDER, MoveId.LUSTER_PURGE, MoveId.SLEEP_POWDER, MoveId.TAIL_GLOW ],
|
||||
[SpeciesId.NICKIT]: [ MoveId.BADDY_BAD, MoveId.MYSTICAL_FIRE, MoveId.SPARKLY_SWIRL, MoveId.MAKE_IT_RAIN ],
|
||||
[SpeciesId.GOSSIFLEUR]: [ MoveId.BATON_PASS, MoveId.TAILWIND, MoveId.SAPPY_SEED, MoveId.SPORE ],
|
||||
[SpeciesId.GOSSIFLEUR]: [ MoveId.PARTING_SHOT, MoveId.STRENGTH_SAP, MoveId.SAPPY_SEED, MoveId.SEED_FLARE ],
|
||||
[SpeciesId.WOOLOO]: [ MoveId.NUZZLE, MoveId.MILK_DRINK, MoveId.BODY_PRESS, MoveId.MULTI_ATTACK ],
|
||||
[SpeciesId.CHEWTLE]: [ MoveId.ICE_FANG, MoveId.PSYCHIC_FANGS, MoveId.SHELL_SMASH, MoveId.MIGHTY_CLEAVE ],
|
||||
[SpeciesId.YAMPER]: [ MoveId.ICE_FANG, MoveId.SWORDS_DANCE, MoveId.THUNDER_FANG, MoveId.BOLT_STRIKE ],
|
||||
@ -514,7 +517,7 @@ export const speciesEggMoves = {
|
||||
[SpeciesId.TAROUNTULA]: [ MoveId.STONE_AXE, MoveId.LEECH_LIFE, MoveId.FAKE_OUT, MoveId.SPORE ],
|
||||
[SpeciesId.NYMBLE]: [ MoveId.KNOCK_OFF, MoveId.FELL_STINGER, MoveId.ATTACK_ORDER, MoveId.WICKED_BLOW ],
|
||||
[SpeciesId.PAWMI]: [ MoveId.DRAIN_PUNCH, MoveId.METEOR_MASH, MoveId.JET_PUNCH, MoveId.PLASMA_FISTS ],
|
||||
[SpeciesId.TANDEMAUS]: [ MoveId.BATON_PASS, MoveId.FAKE_OUT, MoveId.POWER_UP_PUNCH, MoveId.REVIVAL_BLESSING ],
|
||||
[SpeciesId.TANDEMAUS]: [ MoveId.BATON_PASS, MoveId.COVET, MoveId.SIZZLY_SLIDE, MoveId.REVIVAL_BLESSING ],
|
||||
[SpeciesId.FIDOUGH]: [ MoveId.SOFT_BOILED, MoveId.HIGH_HORSEPOWER, MoveId.SIZZLY_SLIDE, MoveId.TIDY_UP ],
|
||||
[SpeciesId.SMOLIV]: [ MoveId.STRENGTH_SAP, MoveId.EARTH_POWER, MoveId.CALM_MIND, MoveId.BOOMBURST ],
|
||||
[SpeciesId.SQUAWKABILLY]: [ MoveId.PARTING_SHOT, MoveId.EARTHQUAKE, MoveId.FLARE_BLITZ, MoveId.EXTREME_SPEED ],
|
||||
@ -582,55 +585,4 @@ export const speciesEggMoves = {
|
||||
[SpeciesId.PALDEA_TAUROS]: [ MoveId.NO_RETREAT, MoveId.BLAZING_TORQUE, MoveId.AQUA_STEP, MoveId.THUNDEROUS_KICK ],
|
||||
[SpeciesId.PALDEA_WOOPER]: [ MoveId.STONE_AXE, MoveId.RECOVER, MoveId.BANEFUL_BUNKER, MoveId.BARB_BARRAGE ],
|
||||
[SpeciesId.BLOODMOON_URSALUNA]: [ MoveId.NASTY_PLOT, MoveId.ROCK_POLISH, MoveId.SANDSEAR_STORM, MoveId.BOOMBURST ]
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a CSV-separated list of Egg Moves (such as one sourced from a Google Sheets)
|
||||
* into code able to form the `speciesEggMoves` const object as above.
|
||||
* @param content - The CSV-formatted string to convert into code.
|
||||
*/
|
||||
// TODO: Move this into the scripts folder and stop running it on initialization
|
||||
function parseEggMoves(content: string): void {
|
||||
let output = "";
|
||||
|
||||
const speciesNames = getEnumKeys(SpeciesId);
|
||||
const speciesValues = getEnumValues(SpeciesId);
|
||||
const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, "").toLowerCase());
|
||||
const lines = content.split(/\n/g);
|
||||
|
||||
for (const line of lines) {
|
||||
const cols = line.split(",").slice(0, 5);
|
||||
const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_") as keyof typeof SpeciesId;
|
||||
// TODO: This should use reverse mapping instead of `indexOf`
|
||||
const species = speciesValues[speciesNames.indexOf(enumSpeciesName)];
|
||||
|
||||
const eggMoves: MoveId[] = [];
|
||||
|
||||
for (let m = 0; m < 4; m++) {
|
||||
const moveName = cols[m + 1].trim();
|
||||
const moveIndex = moveName !== "N/A" ? moveNames.indexOf(moveName.toLowerCase()) : -1;
|
||||
if (moveIndex === -1) {
|
||||
console.warn(moveName, "could not be parsed");
|
||||
}
|
||||
|
||||
eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE);
|
||||
}
|
||||
|
||||
if (eggMoves.every(m => m === MoveId.NONE)) {
|
||||
console.warn(`Species ${toTitleCase(SpeciesId[species])} could not be parsed, excluding from output...`)
|
||||
} else {
|
||||
output += `[SpeciesId.${SpeciesId[species]}]: [ ${eggMoves.map(m => `MoveId.${MoveId[m]}`).join(", ")} ],\n`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(output);
|
||||
}
|
||||
|
||||
export function initEggMoves() {
|
||||
const eggMovesStr = "";
|
||||
if (eggMovesStr) {
|
||||
setTimeout(() => {
|
||||
parseEggMoves(eggMovesStr);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
} satisfies Partial<Record<SpeciesId, [MoveId, MoveId, MoveId, MoveId]>>;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { initAbilities } from "#abilities/ability";
|
||||
import { initBiomes } from "#balance/biomes";
|
||||
import { initEggMoves } from "#balance/egg-moves";
|
||||
import { initPokemonPrevolutions, initPokemonStarters } from "#balance/pokemon-evolutions";
|
||||
import { initSpecies } from "#balance/pokemon-species";
|
||||
import { initChallenges } from "#data/challenge";
|
||||
@ -24,7 +23,6 @@ export function initializeGame() {
|
||||
initPokemonPrevolutions();
|
||||
initPokemonStarters();
|
||||
initBiomes();
|
||||
initEggMoves();
|
||||
initPokemonForms();
|
||||
initTrainerTypeDialogue();
|
||||
initSpecies();
|
||||
|
@ -1,5 +1,6 @@
|
||||
import overrides from "#app/overrides";
|
||||
import type { Challenge } from "#data/challenge";
|
||||
import { copyChallenge } from "#data/challenge";
|
||||
import { BattleStyle } from "#enums/battle-style";
|
||||
import type { Challenges } from "#enums/challenges";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
@ -10,7 +11,6 @@ import { SelectStarterPhase } from "#phases/select-starter-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { generateStarter } from "#test/test-utils/game-manager-utils";
|
||||
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
|
||||
import { copyChallenge } from "data/challenge";
|
||||
|
||||
/**
|
||||
* Helper to handle Challenge mode specifics
|
||||
|
@ -18,46 +18,46 @@
|
||||
"esModuleInterop": true,
|
||||
"strictNullChecks": true,
|
||||
"sourceMap": false,
|
||||
"checkJs": true,
|
||||
"strict": false, // TODO: Enable this eventually
|
||||
"rootDir": ".",
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"#abilities/*": ["./data/abilities/*.ts"],
|
||||
"#api/*": ["./plugins/api/*.ts"],
|
||||
"#balance/*": ["./data/balance/*.ts"],
|
||||
"#enums/*": ["./enums/*.ts"],
|
||||
"#events/*": ["./events/*.ts"],
|
||||
"#field/*": ["./field/*.ts"],
|
||||
"#inputs/*": ["./configs/inputs/*.ts"],
|
||||
"#modifiers/*": ["./modifier/*.ts"],
|
||||
"#moves/*": ["./data/moves/*.ts"],
|
||||
"#mystery-encounters/*": [
|
||||
"./data/mystery-encounters/utils/*.ts",
|
||||
"./data/mystery-encounters/encounters/*.ts",
|
||||
"./data/mystery-encounters/requirements/*.ts",
|
||||
"./data/mystery-encounters/*.ts"
|
||||
],
|
||||
"#package.json": ["../package.json"],
|
||||
"#phases/*": ["./phases/*.ts"],
|
||||
"#plugins/*": ["./plugins/vite/*.ts", "./plugins/*.ts"],
|
||||
"#sprites/*": ["./sprites/*.ts"],
|
||||
"#system/*": [
|
||||
"./system/settings/*.ts",
|
||||
"./system/version-migration/versions/*.ts",
|
||||
"./system/version-migration/*.ts",
|
||||
"./system/*.ts"
|
||||
],
|
||||
"#trainers/*": ["./data/trainers/*.ts"],
|
||||
"#types/*": ["./@types/helpers/*.ts", "./@types/*.ts", "./typings/phaser/*.ts"],
|
||||
"#ui/*": ["./ui/battle-info/*.ts", "./ui/settings/*.ts", "./ui/*.ts"],
|
||||
"#utils/*": ["./utils/*.ts"],
|
||||
"#data/*": ["./data/pokemon-forms/*.ts", "./data/pokemon/*.ts", "./data/*.ts"],
|
||||
"#test/*": ["../test/*.ts"],
|
||||
"#app/*": ["*.ts"]
|
||||
},
|
||||
"outDir": "./build",
|
||||
"noEmit": true
|
||||
"noEmit": true,
|
||||
"paths": {
|
||||
"#abilities/*": ["./src/data/abilities/*.ts"],
|
||||
"#api/*": ["./src/plugins/api/*.ts"],
|
||||
"#balance/*": ["./src/data/balance/*.ts"],
|
||||
"#enums/*": ["./src/enums/*.ts"],
|
||||
"#events/*": ["./src/events/*.ts"],
|
||||
"#field/*": ["./src/field/*.ts"],
|
||||
"#inputs/*": ["./src/configs/inputs/*.ts"],
|
||||
"#modifiers/*": ["./src/modifier/*.ts"],
|
||||
"#moves/*": ["./src/data/moves/*.ts"],
|
||||
"#mystery-encounters/*": [
|
||||
"./src/data/mystery-encounters/utils/*.ts",
|
||||
"./src/data/mystery-encounters/encounters/*.ts",
|
||||
"./src/data/mystery-encounters/requirements/*.ts",
|
||||
"./src/data/mystery-encounters/*.ts"
|
||||
],
|
||||
"#package.json": ["./package.json"],
|
||||
"#phases/*": ["./src/phases/*.ts"],
|
||||
"#plugins/*": ["./src/plugins/vite/*.ts", "./src/plugins/*.ts"],
|
||||
"#sprites/*": ["./src/sprites/*.ts"],
|
||||
"#system/*": [
|
||||
"./src/system/settings/*.ts",
|
||||
"./src/system/version-migration/versions/*.ts",
|
||||
"./src/system/version-migration/*.ts",
|
||||
"./src/system/*.ts"
|
||||
],
|
||||
"#trainers/*": ["./src/data/trainers/*.ts"],
|
||||
"#types/*": ["./src/@types/helpers/*.ts", "./src/@types/*.ts", "./src/typings/phaser/*.ts"],
|
||||
"#ui/*": ["./src/ui/battle-info/*.ts", "./src/ui/settings/*.ts", "./src/ui/*.ts"],
|
||||
"#utils/*": ["./src/utils/*.ts"],
|
||||
"#data/*": ["./src/data/pokemon-forms/*.ts", "./src/data/pokemon/*.ts", "./src/data/*.ts"],
|
||||
"#test/*": ["./test/*.ts"],
|
||||
"#app/*": ["./src/*.ts"]
|
||||
}
|
||||
},
|
||||
// Exclude checking for script JS files as those are covered by the folder's `jsconfig/json`
|
||||
"include": ["**/*.ts", "**/*.d.ts"],
|
||||
"exclude": ["node_modules", "dist", "vite.config.ts", "vitest.config.ts", "vitest.workspace.ts"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user