Merge branch 'beta' into actual

This commit is contained in:
Jannik Tappert 2024-09-20 20:14:00 +02:00 committed by GitHub
commit 5c1ee5bea8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 1368 additions and 619 deletions

View File

@ -1,7 +1,3 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
/** /**
* This script creates a test boilerplate file for a move or ability. * This script creates a test boilerplate file for a move or ability.
* @param {string} type - The type of test to create. Either "move", "ability", * @param {string} type - The type of test to create. Either "move", "ability",
@ -10,61 +6,101 @@ import { fileURLToPath } from 'url';
* @example npm run create-test move tackle * @example npm run create-test move tackle
*/ */
import fs from "fs";
import inquirer from "inquirer";
import path from "path";
import { fileURLToPath } from "url";
// Get the directory name of the current module file // Get the directory name of the current module file
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
const typeChoices = ["Move", "Ability", "Item"];
// Get the arguments from the command line /**
const args = process.argv.slice(2); * Prompts the user to select a type via list.
const type = args[0]; // "move" or "ability" * @returns {Promise<{selectedOption: string}>} the selected type
let fileName = args[1]; // The file name */
async function promptTestType() {
const typeAnswer = await inquirer.prompt([
{
type: "list",
name: "selectedOption",
message: "What type of test would you like to create:",
choices: [...typeChoices, "EXIT"],
},
]);
if (!type || !fileName) { if (typeAnswer.selectedOption === "EXIT") {
console.error('Please provide a type ("move", "ability", or "item") and a file name.'); console.log("Exiting...");
process.exit(1); return process.exit();
} else if (!typeChoices.includes(typeAnswer.selectedOption)) {
console.error('Please provide a valid type ("move", "ability", or "item")!');
return await promptTestType();
} }
return typeAnswer;
}
/**
* Prompts the user to provide a file name.
* @param {string} selectedType
* @returns {Promise<{userInput: string}>} the selected file name
*/
async function promptFileName(selectedType) {
const fileNameAnswer = await inquirer.prompt([
{
type: "input",
name: "userInput",
message: `Please provide a file name for the ${selectedType} test:`,
},
]);
if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) {
console.error("Please provide a valid file name!");
return await promptFileName(selectedType);
}
return fileNameAnswer;
}
/**
* Runs the interactive create-test "CLI"
* @returns {Promise<void>}
*/
async function runInteractive() {
const typeAnswer = await promptTestType();
const fileNameAnswer = await promptFileName(typeAnswer.selectedOption);
const type = typeAnswer.selectedOption.toLowerCase();
// Convert fileName from kebab-case or camelCase to snake_case // Convert fileName from kebab-case or camelCase to snake_case
fileName = fileName const fileName = fileNameAnswer.userInput
.replace(/-+/g, '_') // Convert kebab-case (dashes) to underscores .replace(/-+/g, "_") // Convert kebab-case (dashes) to underscores
.replace(/([a-z])([A-Z])/g, '$1_$2') // Convert camelCase to snake_case .replace(/([a-z])([A-Z])/g, "$1_$2") // Convert camelCase to snake_case
.toLowerCase(); // Ensure all lowercase .toLowerCase(); // Ensure all lowercase
// Format the description for the test case // Format the description for the test case
const formattedName = fileName
.replace(/_/g, ' ')
.replace(/\b\w/g, char => char.toUpperCase());
const formattedName = fileName.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
// Determine the directory based on the type // Determine the directory based on the type
let dir; let dir;
let description; let description;
if (type === 'move') { switch (type) {
dir = path.join(__dirname, 'src', 'test', 'moves'); case "move":
dir = path.join(__dirname, "src", "test", "moves");
description = `Moves - ${formattedName}`; description = `Moves - ${formattedName}`;
} else if (type === 'ability') { break;
dir = path.join(__dirname, 'src', 'test', 'abilities'); case "ability":
dir = path.join(__dirname, "src", "test", "abilities");
description = `Abilities - ${formattedName}`; description = `Abilities - ${formattedName}`;
} else if (type === "item") { break;
dir = path.join(__dirname, 'src', 'test', 'items'); case "item":
dir = path.join(__dirname, "src", "test", "items");
description = `Items - ${formattedName}`; description = `Items - ${formattedName}`;
} else { break;
default:
console.error('Invalid type. Please use "move", "ability", or "item".'); console.error('Invalid type. Please use "move", "ability", or "item".');
process.exit(1); process.exit(1);
} }
// Ensure the directory exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
// Create the file with the given name
const filePath = path.join(dir, `${fileName}.test.ts`);
if (fs.existsSync(filePath)) {
console.error(`File "${fileName}.test.ts" already exists.`);
process.exit(1);
}
// Define the content template // Define the content template
const content = `import { Abilities } from "#enums/abilities"; const content = `import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -104,7 +140,23 @@ describe("${description}", () => {
}); });
`; `;
// Ensure the directory exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
// Create the file with the given name
const filePath = path.join(dir, `${fileName}.test.ts`);
if (fs.existsSync(filePath)) {
console.error(`File "${fileName}.test.ts" already exists.`);
process.exit(1);
}
// Write the template content to the file // Write the template content to the file
fs.writeFileSync(filePath, content, 'utf8'); fs.writeFileSync(filePath, content, "utf8");
console.log(`File created at: ${filePath}`); console.log(`File created at: ${filePath}`);
}
runInteractive();

453
package-lock.json generated
View File

@ -29,6 +29,7 @@
"dependency-cruiser": "^16.3.10", "dependency-cruiser": "^16.3.10",
"eslint": "^9.7.0", "eslint": "^9.7.0",
"eslint-plugin-import-x": "^4.2.1", "eslint-plugin-import-x": "^4.2.1",
"inquirer": "^11.0.2",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"lefthook": "^1.6.12", "lefthook": "^1.6.12",
"phaser3spectorjs": "^0.0.8", "phaser3spectorjs": "^0.0.8",
@ -1070,6 +1071,281 @@
"url": "https://github.com/sponsors/nzakas" "url": "https://github.com/sponsors/nzakas"
} }
}, },
"node_modules/@inquirer/checkbox": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz",
"integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/figures": "^1.0.6",
"@inquirer/type": "^2.0.0",
"ansi-escapes": "^4.3.2",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/confirm": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz",
"integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/core": {
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz",
"integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/figures": "^1.0.6",
"@inquirer/type": "^2.0.0",
"@types/mute-stream": "^0.0.4",
"@types/node": "^22.5.5",
"@types/wrap-ansi": "^3.0.0",
"ansi-escapes": "^4.3.2",
"cli-width": "^4.1.0",
"mute-stream": "^1.0.0",
"signal-exit": "^4.1.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^6.2.0",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/core/node_modules/@types/node": {
"version": "22.5.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
"integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@inquirer/core/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/@inquirer/core/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@inquirer/core/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
"node_modules/@inquirer/core/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@inquirer/editor": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz",
"integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0",
"external-editor": "^3.1.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/expand": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz",
"integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/figures": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.6.tgz",
"integrity": "sha512-yfZzps3Cso2UbM7WlxKwZQh2Hs6plrbjs1QnzQDZhK2DgyCo6D8AaHps9olkNcUFlcYERMqU3uJSp1gmy3s/qQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/input": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz",
"integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/number": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz",
"integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/password": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz",
"integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0",
"ansi-escapes": "^4.3.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/prompts": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz",
"integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/checkbox": "^3.0.1",
"@inquirer/confirm": "^4.0.1",
"@inquirer/editor": "^3.0.1",
"@inquirer/expand": "^3.0.1",
"@inquirer/input": "^3.0.1",
"@inquirer/number": "^2.0.1",
"@inquirer/password": "^3.0.1",
"@inquirer/rawlist": "^3.0.1",
"@inquirer/search": "^2.0.1",
"@inquirer/select": "^3.0.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/rawlist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz",
"integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/type": "^2.0.0",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/search": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz",
"integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/figures": "^1.0.6",
"@inquirer/type": "^2.0.0",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/select": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz",
"integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/figures": "^1.0.6",
"@inquirer/type": "^2.0.0",
"ansi-escapes": "^4.3.2",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz",
"integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==",
"dev": true,
"license": "MIT",
"dependencies": {
"mute-stream": "^1.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@isaacs/cliui": { "node_modules/@isaacs/cliui": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -1563,6 +1839,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/mute-stream": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz",
"integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.14.11", "version": "20.14.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
@ -1585,6 +1871,13 @@
"integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==",
"dev": true "dev": true
}, },
"node_modules/@types/wrap-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz",
"integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==",
"dev": true,
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.0.0-alpha.58", "version": "8.0.0-alpha.58",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.58.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.58.tgz",
@ -1970,6 +2263,22 @@
"url": "https://github.com/sponsors/epoberezkin" "url": "https://github.com/sponsors/epoberezkin"
} }
}, },
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"type-fest": "^0.21.3"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@ -2192,6 +2501,13 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true,
"license": "MIT"
},
"node_modules/check-error": { "node_modules/check-error": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
@ -2202,6 +2518,16 @@
"node": ">= 16" "node": ">= 16"
} }
}, },
"node_modules/cli-width": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">= 12"
}
},
"node_modules/cliui": { "node_modules/cliui": {
"version": "8.0.1", "version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@ -3057,6 +3383,21 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1" "url": "https://github.com/sindresorhus/execa?sponsor=1"
} }
}, },
"node_modules/external-editor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
"dev": true,
"license": "MIT",
"dependencies": {
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
"tmp": "^0.0.33"
},
"engines": {
"node": ">=4"
}
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -3579,6 +3920,19 @@
"i18next": ">=8.4.0" "i18next": ">=8.4.0"
} }
}, },
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@ -3626,6 +3980,26 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
} }
}, },
"node_modules/inquirer": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-11.0.2.tgz",
"integrity": "sha512-pnbn3nL+JFrTw/pLhzyE/IQ3+gA3n5JxTAZQDjB6qu4gbjOaiTnpZbxT6HY2DDCT7bzDjTTsd3snRP+B6N//Pg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^9.2.1",
"@inquirer/prompts": "^6.0.1",
"@inquirer/type": "^2.0.0",
"@types/mute-stream": "^0.0.4",
"ansi-escapes": "^4.3.2",
"mute-stream": "^1.0.0",
"run-async": "^3.0.0",
"rxjs": "^7.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/interpret": { "node_modules/interpret": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
@ -4456,6 +4830,16 @@
"mustache": "bin/mustache" "mustache": "bin/mustache"
} }
}, },
"node_modules/mute-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
"integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.7", "version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@ -4605,6 +4989,16 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/p-limit": { "node_modules/p-limit": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -5092,6 +5486,16 @@
"integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
"dev": true "dev": true
}, },
"node_modules/run-async": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
"integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -5116,6 +5520,16 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-regex": { "node_modules/safe-regex": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
@ -5538,6 +5952,19 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"os-tmpdir": "~1.0.2"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/to-fast-properties": { "node_modules/to-fast-properties": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@ -5673,6 +6100,19 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/type-fest": {
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typedoc": { "node_modules/typedoc": {
"version": "0.26.5", "version": "0.26.5",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.5.tgz", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.5.tgz",
@ -6346,6 +6786,19 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/yoctocolors-cjs": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz",
"integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
} }
} }
} }

View File

@ -33,6 +33,7 @@
"dependency-cruiser": "^16.3.10", "dependency-cruiser": "^16.3.10",
"eslint": "^9.7.0", "eslint": "^9.7.0",
"eslint-plugin-import-x": "^4.2.1", "eslint-plugin-import-x": "^4.2.1",
"inquirer": "^11.0.2",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"lefthook": "^1.6.12", "lefthook": "^1.6.12",
"phaser3spectorjs": "^0.0.8", "phaser3spectorjs": "^0.0.8",

View File

@ -782,6 +782,14 @@ export default class BattleScene extends SceneBase {
return this.getPlayerField().find(p => p.isActive()); return this.getPlayerField().find(p => p.isActive());
} }
/**
* Finds the first {@linkcode Pokemon.isActive() | active PlayerPokemon} that isn't also currently switching out
* @returns Either the first {@linkcode PlayerPokemon} satisfying, or undefined if no player pokemon on the field satisfy
*/
getNonSwitchedPlayerPokemon(): PlayerPokemon | undefined {
return this.getPlayerField().find(p => p.isActive() && p.switchOutStatus === false);
}
/** /**
* Returns an array of PlayerPokemon of length 1 or 2 depending on if double battles or not * Returns an array of PlayerPokemon of length 1 or 2 depending on if double battles or not
* @returns array of {@linkcode PlayerPokemon} * @returns array of {@linkcode PlayerPokemon}
@ -799,6 +807,14 @@ export default class BattleScene extends SceneBase {
return this.getEnemyField().find(p => p.isActive()); return this.getEnemyField().find(p => p.isActive());
} }
/**
* Finds the first {@linkcode Pokemon.isActive() | active EnemyPokemon} pokemon from the enemy that isn't also currently switching out
* @returns Either the first {@linkcode EnemyPokemon} satisfying, or undefined if no player pokemon on the field satisfy
*/
getNonSwitchedEnemyPokemon(): EnemyPokemon | undefined {
return this.getEnemyField().find(p => p.isActive() && p.switchOutStatus === false);
}
/** /**
* Returns an array of EnemyPokemon of length 1 or 2 depending on if double battles or not * Returns an array of EnemyPokemon of length 1 or 2 depending on if double battles or not
* @returns array of {@linkcode EnemyPokemon} * @returns array of {@linkcode EnemyPokemon}

View File

@ -16,6 +16,14 @@ import { TrainerType } from "#enums/trainer-type";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { CustomModifierSettings } from "#app/modifier/modifier-type";
import { ModifierTier } from "#app/modifier/modifier-tier";
export enum ClassicFixedBossWaves {
// TODO: other fixed wave battles should be added here
EVIL_BOSS_1 = 115,
EVIL_BOSS_2 = 165,
}
export enum BattleType { export enum BattleType {
WILD, WILD,
@ -419,6 +427,7 @@ export class FixedBattleConfig {
public getTrainer: GetTrainerFunc; public getTrainer: GetTrainerFunc;
public getEnemyParty: GetEnemyPartyFunc; public getEnemyParty: GetEnemyPartyFunc;
public seedOffsetWaveIndex: number; public seedOffsetWaveIndex: number;
public customModifierRewardSettings?: CustomModifierSettings;
setBattleType(battleType: BattleType): FixedBattleConfig { setBattleType(battleType: BattleType): FixedBattleConfig {
this.battleType = battleType; this.battleType = battleType;
@ -444,6 +453,11 @@ export class FixedBattleConfig {
this.seedOffsetWaveIndex = seedOffsetWaveIndex; this.seedOffsetWaveIndex = seedOffsetWaveIndex;
return this; return this;
} }
setCustomModifierRewards(customModifierRewardSettings: CustomModifierSettings) {
this.customModifierRewardSettings = customModifierRewardSettings;
return this;
}
} }
@ -503,11 +517,13 @@ export const classicFixedBattles: FixedBattleConfigs = {
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }),
[35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)),
[55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }),
[62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)),
[64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
@ -515,17 +531,21 @@ export const classicFixedBattles: FixedBattleConfigs = {
[66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA ], true)), .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA ], true)),
[95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
[112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT ], true)),
[114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA ], true, 1)), .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA ], true, 1)),
[115]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_BOSS_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
[165]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) .setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2 ])), [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2 ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
[182]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [182]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])),
[184]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) [184]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182)
@ -538,4 +558,5 @@ export const classicFixedBattles: FixedBattleConfigs = {
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])),
[195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false })
}; };

View File

@ -1 +1,5 @@
export const PLAYER_PARTY_MAX_SIZE = 6; /** The maximum size of the player's party */
export const PLAYER_PARTY_MAX_SIZE: number = 6;
/** Whether to use seasonal splash messages in general */
export const USE_SEASONAL_SPLASH_MESSAGES: boolean = false;

View File

@ -428,7 +428,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
moveAnim.bgSprite.setScale(1.25); moveAnim.bgSprite.setScale(1.25);
moveAnim.bgSprite.setAlpha(this.opacity / 255); moveAnim.bgSprite.setAlpha(this.opacity / 255);
scene.field.add(moveAnim.bgSprite); scene.field.add(moveAnim.bgSprite);
const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon(); const fieldPokemon = scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon();
if (!isNullOrUndefined(priority)) { if (!isNullOrUndefined(priority)) {
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!); scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!);
} else if (fieldPokemon?.isOnField()) { } else if (fieldPokemon?.isOnField()) {
@ -989,7 +989,7 @@ export abstract class BattleAnim {
const setSpritePriority = (priority: integer) => { const setSpritePriority = (priority: integer) => {
switch (priority) { switch (priority) {
case 0: case 0:
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon()!); // TODO: is this bang correct? scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon()!); // This bang assumes that if (the EnemyPokemon is undefined, then the PlayerPokemon function must return an object), correct assumption?
break; break;
case 1: case 1:
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);

View File

@ -5221,7 +5221,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
switchOutTarget.leaveField(false); switchOutTarget.leaveField(false);
if (switchOutTarget.hp) { if (switchOutTarget.hp) {
switchOutTarget.setWildFlee(true);
user.scene.queueMessage(i18next.t("moveTriggers:fled", {pokemonName: getPokemonNameWithAffix(switchOutTarget)}), null, true, 500); user.scene.queueMessage(i18next.t("moveTriggers:fled", {pokemonName: getPokemonNameWithAffix(switchOutTarget)}), null, true, 500);
// in double battles redirect potential moves off fled pokemon // in double battles redirect potential moves off fled pokemon

View File

@ -1,46 +1,136 @@
import i18next from "i18next"; import { USE_SEASONAL_SPLASH_MESSAGES } from "#app/constants";
export function getBattleCountSplashMessage(): string { //#region Interfaces/Types
return `{COUNT} ${i18next.t("splashMessages:battlesWon")}`;
type Month = "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10" | "11" | "12";
type Day =
| Month
| "13"
| "14"
| "15"
| "16"
| "17"
| "18"
| "19"
| "20"
| "21"
| "22"
| "23"
| "24"
| "25"
| "26"
| "27"
| "28"
| "29"
| "30"
| "31";
/**
* Represents a season with its {@linkcode name},
* {@linkcode start} day+month, {@linkcode end} day+month
* and {@linkcode messages}.
*/
interface Season {
/** The name of the season (internal use only) */
name: string;
/** The start day and month of the season. Format `MM-DD` */
start: `${Month}-${Day}`;
/** The end day and month of the season. Format `MM-DD` */
end: `${Month}-${Day}`;
/** Collection of the messages to display (without the `i18next.t()` call!) */
messages: string[];
} }
//#region Constants
/** The weight multiplier for the battles-won splash message */
const BATTLES_WON_WEIGHT_MULTIPLIER = 10;
/** The weight multiplier for the seasonal splash messages */
const SEASONAL_WEIGHT_MULTIPLIER = 10;
//#region Common Messages
const commonSplashMessages = [
...Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"),
"joinTheDiscord",
"infiniteLevels",
"everythingStacks",
"optionalSaveScumming",
"biomes",
"openSource",
"playWithSpeed",
"liveBugTesting",
"heavyInfluence",
"pokemonRiskAndPokemonRain",
"nowWithMoreSalt",
"infiniteFusionAtHome",
"brokenEggMoves",
"magnificent",
"mubstitute",
"thatsCrazy",
"oranceJuice",
"questionableBalancing",
"coolShaders",
"aiFree",
"suddenDifficultySpikes",
"basedOnAnUnfinishedFlashGame",
"moreAddictiveThanIntended",
"mostlyConsistentSeeds",
"achievementPointsDontDoAnything",
"youDoNotStartAtLevel",
"dontTalkAboutTheManaphyEggIncident",
"alsoTryPokengine",
"alsoTryEmeraldRogue",
"alsoTryRadicalRed",
"eeveeExpo",
"ynoproject",
"breedersInSpace",
];
//#region Seasonal Messages
const seasonalSplashMessages: Season[] = [
{
name: "Halloween",
start: "09-15",
end: "10-31",
messages: ["halloween.pumpkaboosAbout", "halloween.mayContainSpiders", "halloween.spookyScaryDuskulls"],
},
{
name: "XMAS",
start: "12-01",
end: "12-26",
messages: ["xmas.happyHolidays", "xmas.delibirdSeason"],
},
{
name: "New Year's",
start: "01-01",
end: "01-31",
messages: ["newYears.happyNewYear"],
},
];
//#endregion
export function getSplashMessages(): string[] { export function getSplashMessages(): string[] {
const splashMessages = Array(10).fill(getBattleCountSplashMessage()); const splashMessages: string[] = [...commonSplashMessages];
splashMessages.push( console.log("use seasonal splash messages", USE_SEASONAL_SPLASH_MESSAGES);
i18next.t("splashMessages:joinTheDiscord"), if (USE_SEASONAL_SPLASH_MESSAGES) {
i18next.t("splashMessages:infiniteLevels"), // add seasonal splash messages if the season is active
i18next.t("splashMessages:everythingStacks"), for (const { name, start, end, messages } of seasonalSplashMessages) {
i18next.t("splashMessages:optionalSaveScumming"), const now = new Date();
i18next.t("splashMessages:biomes"), const startDate = new Date(`${start}-${now.getFullYear()}`);
i18next.t("splashMessages:openSource"), const endDate = new Date(`${end}-${now.getFullYear()}`);
i18next.t("splashMessages:playWithSpeed"),
i18next.t("splashMessages:liveBugTesting"),
i18next.t("splashMessages:heavyInfluence"),
i18next.t("splashMessages:pokemonRiskAndPokemonRain"),
i18next.t("splashMessages:nowWithMoreSalt"),
i18next.t("splashMessages:infiniteFusionAtHome"),
i18next.t("splashMessages:brokenEggMoves"),
i18next.t("splashMessages:magnificent"),
i18next.t("splashMessages:mubstitute"),
i18next.t("splashMessages:thatsCrazy"),
i18next.t("splashMessages:oranceJuice"),
i18next.t("splashMessages:questionableBalancing"),
i18next.t("splashMessages:coolShaders"),
i18next.t("splashMessages:aiFree"),
i18next.t("splashMessages:suddenDifficultySpikes"),
i18next.t("splashMessages:basedOnAnUnfinishedFlashGame"),
i18next.t("splashMessages:moreAddictiveThanIntended"),
i18next.t("splashMessages:mostlyConsistentSeeds"),
i18next.t("splashMessages:achievementPointsDontDoAnything"),
i18next.t("splashMessages:youDoNotStartAtLevel"),
i18next.t("splashMessages:dontTalkAboutTheManaphyEggIncident"),
i18next.t("splashMessages:alsoTryPokengine"),
i18next.t("splashMessages:alsoTryEmeraldRogue"),
i18next.t("splashMessages:alsoTryRadicalRed"),
i18next.t("splashMessages:eeveeExpo"),
i18next.t("splashMessages:ynoproject"),
i18next.t("splashMessages:breedersInSpace"),
);
return splashMessages; if (now >= startDate && now <= endDate) {
console.log(`Adding ${messages.length} ${name} splash messages (weight: x${SEASONAL_WEIGHT_MULTIPLIER})`);
messages.forEach((message) => {
const weightedMessage = Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message);
splashMessages.push(...weightedMessage);
});
}
}
}
return splashMessages.map((message) => `splashMessages:${message}`);
} }

View File

@ -99,7 +99,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public luck: integer; public luck: integer;
public pauseEvolutions: boolean; public pauseEvolutions: boolean;
public pokerus: boolean; public pokerus: boolean;
public wildFlee: boolean; public switchOutStatus: boolean;
public evoCounter: integer; public evoCounter: integer;
public fusionSpecies: PokemonSpecies | null; public fusionSpecies: PokemonSpecies | null;
@ -145,7 +145,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.species = species; this.species = species;
this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL;
this.level = level; this.level = level;
this.wildFlee = false; this.switchOutStatus = false;
// Determine the ability index // Determine the ability index
if (abilityIndex !== undefined) { if (abilityIndex !== undefined) {
@ -343,7 +343,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
isAllowed(): boolean { isAllowed(): boolean {
const challengeAllowed = new Utils.BooleanHolder(true); const challengeAllowed = new Utils.BooleanHolder(true);
applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed);
return !this.wildFlee && challengeAllowed.value; return !this.isFainted() && challengeAllowed.value;
} }
isActive(onField?: boolean): boolean { isActive(onField?: boolean): boolean {
@ -2152,11 +2152,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
/** /**
* sets if the pokemon has fled (implies it's a wild pokemon) * sets if the pokemon is switching out (if it's a enemy wild implies it's going to flee)
* @param status - boolean * @param status - boolean
*/ */
setWildFlee(status: boolean): void { setSwitchOutStatus(status: boolean): void {
this.wildFlee = status; this.switchOutStatus = status;
} }
updateInfo(instant?: boolean): Promise<void> { updateInfo(instant?: boolean): Promise<void> {
@ -3384,6 +3384,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.updateFusionPalette(); this.updateFusionPalette();
} }
this.summonData = new PokemonSummonData(); this.summonData = new PokemonSummonData();
this.setSwitchOutStatus(false);
if (!this.battleData) { if (!this.battleData) {
this.resetBattleData(); this.resetBattleData();
} }
@ -3789,6 +3790,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.hideInfo(); this.hideInfo();
} }
this.scene.field.remove(this); this.scene.field.remove(this);
this.setSwitchOutStatus(true);
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true); this.scene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true);
} }
@ -3812,6 +3814,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const rootForm = getPokemonSpecies(this.species.getRootSpeciesId()); const rootForm = getPokemonSpecies(this.species.getRootSpeciesId());
return rootForm.getAbility(abilityIndex) === rootForm.getAbility(currentAbilityIndex); return rootForm.getAbility(abilityIndex) === rootForm.getAbility(currentAbilityIndex);
} }
/**
* Helper function to check if the player already owns the starter data of the Pokemon's
* current ability
* @param ownedAbilityAttrs the owned abilityAttr of this Pokemon's root form
* @returns true if the player already has it, false otherwise
*/
checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs: number): boolean {
if ((ownedAbilityAttrs & 1) > 0 && this.hasSameAbilityInRootForm(0)) {
return true;
}
if ((ownedAbilityAttrs & 2) > 0 && this.hasSameAbilityInRootForm(1)) {
return true;
}
if ((ownedAbilityAttrs & 4) > 0 && this.hasSameAbilityInRootForm(2)) {
return true;
}
return false;
}
} }
export default interface Pokemon { export default interface Pokemon {

View File

@ -268,7 +268,6 @@ export class GameMode implements GameModeConfig {
isFixedBattle(waveIndex: integer): boolean { isFixedBattle(waveIndex: integer): boolean {
const dummyConfig = new FixedBattleConfig(); const dummyConfig = new FixedBattleConfig();
return this.battleConfig.hasOwnProperty(waveIndex) || applyChallenges(this, ChallengeType.FIXED_BATTLES, waveIndex, dummyConfig); return this.battleConfig.hasOwnProperty(waveIndex) || applyChallenges(this, ChallengeType.FIXED_BATTLES, waveIndex, dummyConfig);
} }
/** /**

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "Kämpfe gewonnen!", "battlesWon": "{{count, number}} Kämpfe gewonnen!",
"joinTheDiscord": "Tritt dem Discord bei!", "joinTheDiscord": "Tritt dem Discord bei!",
"infiniteLevels": "Unendliche Level!", "infiniteLevels": "Unendliche Level!",
"everythingStacks": "Alles stapelt sich!", "everythingStacks": "Alles stapelt sich!",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "Battles Won!", "battlesWon": "{{count, number}} Battles Won!",
"joinTheDiscord": "Join the Discord!", "joinTheDiscord": "Join the Discord!",
"infiniteLevels": "Infinite Levels!", "infiniteLevels": "Infinite Levels!",
"everythingStacks": "Everything Stacks!", "everythingStacks": "Everything Stacks!",
@ -32,5 +32,17 @@
"alsoTryRadicalRed": "Also Try Radical Red!", "alsoTryRadicalRed": "Also Try Radical Red!",
"eeveeExpo": "Eevee Expo!", "eeveeExpo": "Eevee Expo!",
"ynoproject": "YNOproject!", "ynoproject": "YNOproject!",
"breedersInSpace": "Breeders in space!" "breedersInSpace": "Breeders in space!",
"halloween": {
"pumpkaboosAbout": "Pumpkaboos about!",
"mayContainSpiders": "May contain spiders!",
"spookyScaryDuskulls": "Spooky, Scary Duskulls!"
},
"xmas": {
"happyHolidays": "Happy Holidays!",
"delibirdSeason": "Delibird Season!"
},
"newYears": {
"happyNewYear": "Happy New Year!"
}
} }

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": Batallas ganadas!", "battlesWon": {{count, number}} Batallas ganadas!",
"joinTheDiscord": "¡Únete al Discord!", "joinTheDiscord": "¡Únete al Discord!",
"infiniteLevels": "¡Niveles infinitos!", "infiniteLevels": "¡Niveles infinitos!",
"everythingStacks": "¡Todo se acumula!", "everythingStacks": "¡Todo se acumula!",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "combats gagnés !", "battlesWon": "{{count, number}} combats gagnés !",
"joinTheDiscord": "Rejoins le Discord !", "joinTheDiscord": "Rejoins le Discord !",
"infiniteLevels": "Niveaux infinis !", "infiniteLevels": "Niveaux infinis !",
"everythingStacks": "Tout se cumule !", "everythingStacks": "Tout se cumule !",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "Battaglie Vinte!", "battlesWon": "{{count, number}} Battaglie Vinte!",
"joinTheDiscord": "Entra nel Discord!", "joinTheDiscord": "Entra nel Discord!",
"infiniteLevels": "Livelli Infiniti!", "infiniteLevels": "Livelli Infiniti!",
"everythingStacks": "Tutto si impila!", "everythingStacks": "Tutto si impila!",

View File

@ -1,10 +1,10 @@
{ {
"encounter": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", "encounter": "又しても 時が満ちた 様 である。\nこちらへ 至る 理由は 存知するな。\n$汝は この場所へ 引かれた……\nこの何度となく 至った場所。\n$けれども 数えられぬとも 限らぬ……\n正確に宣ふのたまう 現在の 循環は {{cycleCount}}回目 である。\n$各循環に 心… 意識… 両方も 元の有様に 戻る。\nなれども 故吾の 残影は 汝の 中に 存する。\n$未だに 成功せぬ ままでも\n異なる 存在を 感ずる。\n$御座在る者は 一人。\nなれども 感ずるは… もう 他人。\n$到頭 汝から いかめしい 挑戦は 我が目にかかるか?\n千歳 万歳 相まってた挑戦……\n$始もう。",
"encounter_female": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.", "encounter_female": "又しても 時が満ちた 様 である。\nこちらへ 至る 理由は 存知するな。\n$汝は この場所へ 引かれた……\nこの何度となく 至った場所。\n$けれども 数えられぬとも 限らぬ……\n正確に宣ふのたまう 現在の 循環は {{cycleCount}}回目 である。\n$各循環に 心… 意識… 両方も 元の有様に 戻る。\nなれども 故吾の 残影は 汝の 中に 存する。\n$未だに 成功せぬ ままでも\n異なる 存在を 感ずる。\n$御座在る者は 一人。\nなれども 感ずるは… もう 他人。\n$到頭 汝から いかめしい 挑戦は 我が目にかかるか?\n千歳 万歳 相まってた挑戦……\n$始もう。",
"firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.", "firstStageWin": "成る程。 感じた 存在は 正身(むざね) であった。\n自分を括る 必要 有らぬ 様である。\n$失望させぬが良い。",
"secondStageWin": "…Magnificent.", "secondStageWin": "……お見事でございます。",
"key_ordinal_one": "st", "key_ordinal_one": "",
"key_ordinal_two": "nd", "key_ordinal_two": "",
"key_ordinal_few": "rd", "key_ordinal_few": "",
"key_ordinal_other": "th" "key_ordinal_other": ""
} }

View File

@ -1,6 +1,6 @@
{ {
"ending": "@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.\n$@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.\n$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!\n$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.\n$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.\n$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?", "ending": "@c{shock}帰ってきた?@d{32} それなら…@d{96} 勝った っていうこと だろう?!\n@c{smile_ehalf}絶対 やれると思う べきだったな。\n$@c{smile_eclosed}もちろん、ずっと そんな気 がしたんだな。\n@c{smile}ついに 終わった だろう? ループを 断ち切った。\n$@c{smile_ehalf}キミの夢も 叶ったよな?\n一回も 負けなかった。\n$キミの しつくした事を 覚えるのは おれだけだ。\n@c{angry_mopen}忘れない ように するが…\n$@c{smile_wave_wink}なんつって!@d{64} @c{smile}一生 忘れない。@d{32}\nキミの伝説は いつまでも みんなの 心の中に 残っているから。\n$@c{smile_wave}とにかく、@d{64} そろそろ 遅くなる……@d{96} かな?\nこの場所で よく 分からない。\n$さあ、帰ろう。\n@c{smile_wave_wink}明日、昔のよしみで バトルでも しないか?",
"ending_female": "@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.\n$@c{smile}It's over.@d{64} You ended the loop.\n$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?\n$@c{serious_smile_fists}Your legend will always live on in our hearts.\n$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.\n$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.", "ending_female": "@c{smile}えぇ? 勝ちゃった?@d{96} @c{smile_eclosed}勝てるのが 分かる べきだったね。\nやっぱり 帰ってきた…\n$@c{smile}ついに終わった。@d{64} ループを 断ち切った。\n$@c{serious_smile_fists} アナタの夢も 叶ったよね?\n一回も 負けなかった\n$@c{neutral}アタシだけが アナタが できた事 を覚えていく、ね。@d{96}\nでもね、たぶん 大丈夫なの かなぁ\n$@c{serious_smile_fists}アナタの伝説は みんなの 心の中に\nずっと 残っているからね……\n$@c{smile_eclosed}じゃあ、こんなトコは もう飽きた だろう?\n帰ろうよ。\n$@c{serious_smile_fists}ふるさとに 着いたら、 また バトルしよう?\nやる気 あればね",
"ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.", "ending_endless": "現在のエンドまで やって来て おめでとう!\n更に多くの コンテンツは  近日公開予定です。",
"ending_name": "Devs" "ending_name": "開発者"
} }

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
"newGame": "はじめから", "newGame": "はじめから",
"settings": "設定", "settings": "設定",
"selectGameMode": "ゲームモードを 選んでください。", "selectGameMode": "ゲームモードを 選んでください。",
"logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要 ありません!", "logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要 ありません!",
"username": "ユーザー名", "username": "ユーザー名",
"password": "パスワード", "password": "パスワード",
"login": "ログイン", "login": "ログイン",
@ -14,7 +14,7 @@
"register": "登録", "register": "登録",
"emptyUsername": "ユーザー名を 空にする ことは できません", "emptyUsername": "ユーザー名を 空にする ことは できません",
"invalidLoginUsername": "入力されたユーザー名は無効です", "invalidLoginUsername": "入力されたユーザー名は無効です",
"invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむ必要が あります", "invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむことが 必要です",
"invalidLoginPassword": "入力したパスワードは無効です", "invalidLoginPassword": "入力したパスワードは無効です",
"invalidRegisterPassword": "パスワードは 6文字以上 でなければなりません", "invalidRegisterPassword": "パスワードは 6文字以上 でなければなりません",
"usernameAlreadyUsed": "入力したユーザー名は すでに 使用されています", "usernameAlreadyUsed": "入力したユーザー名は すでに 使用されています",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "Battles Won!", "battlesWon": "勝ったバトル:{{count, number}}回!",
"joinTheDiscord": "Join the Discord!", "joinTheDiscord": "Join the Discord!",
"infiniteLevels": "Infinite Levels!", "infiniteLevels": "Infinite Levels!",
"everythingStacks": "Everything Stacks!", "everythingStacks": "Everything Stacks!",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "전투에서 승리하세요!", "battlesWon": "{{count, number}} 전투에서 승리하세요!",
"joinTheDiscord": "디스코드에 가입하세요!", "joinTheDiscord": "디스코드에 가입하세요!",
"infiniteLevels": "무한한 레벨!", "infiniteLevels": "무한한 레벨!",
"everythingStacks": "모든 것이 누적됩니다!", "everythingStacks": "모든 것이 누적됩니다!",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "Batalhas Ganhas!", "battlesWon": "{{count, number}} Batalhas Ganhas!",
"joinTheDiscord": "Junte-se ao Discord!", "joinTheDiscord": "Junte-se ao Discord!",
"infiniteLevels": "Níveis Infinitos!", "infiniteLevels": "Níveis Infinitos!",
"everythingStacks": "Tudo Acumula!", "everythingStacks": "Tudo Acumula!",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "场胜利!", "battlesWon": "{{count, number}} 场胜利!",
"joinTheDiscord": "加入Discord", "joinTheDiscord": "加入Discord",
"infiniteLevels": "等级无限!", "infiniteLevels": "等级无限!",
"everythingStacks": "道具全部叠加!", "everythingStacks": "道具全部叠加!",

View File

@ -1,5 +1,5 @@
{ {
"battlesWon": "勝利場數!", "battlesWon": "{{count, number}} 勝利場數!",
"joinTheDiscord": "加入Discord", "joinTheDiscord": "加入Discord",
"infiniteLevels": "無限等級!", "infiniteLevels": "無限等級!",
"everythingStacks": "所有效果都能疊加!", "everythingStacks": "所有效果都能疊加!",

View File

@ -1761,7 +1761,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)),
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5), new WeightedModifierType(modifierTypes.FOCUS_BAND, 5),
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), new WeightedModifierType(modifierTypes.KINGS_ROCK, 3),
new WeightedModifierType(modifierTypes.LOCK_CAPSULE, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.LOCK_CAPSULE, (party: Pokemon[]) => party[0].scene.gameMode.isClassic ? 0 : 3),
new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24),
new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36),

View File

@ -40,7 +40,7 @@ export class EggLapsePhase extends Phase {
this.showSummary(); this.showSummary();
}, () => { }, () => {
this.hatchEggsRegular(eggsToHatch); this.hatchEggsRegular(eggsToHatch);
this.showSummary(); this.end();
} }
); );
}, 100, true); }, 100, true);

View File

@ -1,6 +1,6 @@
import BattleScene from "#app/battle-scene"; import BattleScene from "#app/battle-scene";
import { BattlerIndex, BattleType } from "#app/battle"; import { BattlerIndex, BattleType, ClassicFixedBossWaves } from "#app/battle";
import { modifierTypes } from "#app/modifier/modifier-type"; import { CustomModifierSettings, modifierTypes } from "#app/modifier/modifier-type";
import { BattleEndPhase } from "./battle-end-phase"; import { BattleEndPhase } from "./battle-end-phase";
import { NewBattlePhase } from "./new-battle-phase"; import { NewBattlePhase } from "./new-battle-phase";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
@ -42,8 +42,12 @@ export class VictoryPhase extends PokemonPhase {
} }
if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) {
this.scene.pushPhase(new EggLapsePhase(this.scene)); this.scene.pushPhase(new EggLapsePhase(this.scene));
if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_BOSS_2) {
// Should get Lock Capsule on 165 before shop phase so it can be used in the rewards shop
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.LOCK_CAPSULE));
}
if (this.scene.currentBattle.waveIndex % 10) { if (this.scene.currentBattle.waveIndex % 10) {
this.scene.pushPhase(new SelectModifierPhase(this.scene)); this.scene.pushPhase(new SelectModifierPhase(this.scene, undefined, undefined, this.getFixedBattleCustomModifiers()));
} else if (this.scene.gameMode.isDaily) { } else if (this.scene.gameMode.isDaily) {
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM)); this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM));
if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) {
@ -76,4 +80,18 @@ export class VictoryPhase extends PokemonPhase {
this.end(); this.end();
} }
/**
* If this wave is a fixed battle with special custom modifier rewards,
* will pass those settings to the upcoming {@linkcode SelectModifierPhase}`.
*/
getFixedBattleCustomModifiers(): CustomModifierSettings | undefined {
const gameMode = this.scene.gameMode;
const waveIndex = this.scene.currentBattle.waveIndex;
if (gameMode.isFixedBattle(waveIndex)) {
return gameMode.getFixedBattle(waveIndex).customModifierRewardSettings;
}
return undefined;
}
} }

View File

@ -0,0 +1,66 @@
import { getSplashMessages } from "#app/data/splash-messages";
import { describe, expect, it, vi, afterEach, beforeEach } from "vitest";
import * as Constants from "#app/constants";
describe("Data - Splash Messages", () => {
it("should contain at least 15 splash messages", () => {
expect(getSplashMessages().length).toBeGreaterThanOrEqual(15);
});
// make sure to adjust this test if the weight it changed!
it("should add contain 10 `battlesWon` splash messages", () => {
const battlesWonMessages = getSplashMessages().filter((message) => message === "splashMessages:battlesWon");
expect(battlesWonMessages).toHaveLength(10);
});
describe("Seasonal", () => {
beforeEach(() => {
vi.spyOn(Constants, "USE_SEASONAL_SPLASH_MESSAGES", "get").mockReturnValue(true);
});
afterEach(() => {
vi.useRealTimers(); // reset system time
});
it("should contain halloween messages from Sep 15 to Oct 31", () => {
testSeason(new Date("2024-09-15"), new Date("2024-10-31"), "halloween");
});
it("should contain xmas messages from Dec 1 to Dec 26", () => {
testSeason(new Date("2024-12-01"), new Date("2024-12-26"), "xmas");
});
it("should contain new years messages frm Jan 1 to Jan 31", () => {
testSeason(new Date("2024-01-01"), new Date("2024-01-31"), "newYears");
});
});
});
/**
* Helpoer method to test seasonal messages
* @param startDate The seasons start date
* @param endDate The seasons end date
* @param prefix the splash message prefix (e.g. `newYears` or `xmas`)
*/
function testSeason(startDate: Date, endDate: Date, prefix: string) {
const filterFn = (message: string) => message.startsWith(`splashMessages:${prefix}.`);
const beforeDate = new Date(startDate);
beforeDate.setDate(startDate.getDate() - 1);
const afterDate = new Date(endDate);
afterDate.setDate(endDate.getDate() + 1);
const dates: Date[] = [beforeDate, startDate, endDate, afterDate];
const [before, start, end, after] = dates.map((date) => {
vi.setSystemTime(date);
console.log("System time set to", date);
const count = getSplashMessages().filter(filterFn).length;
return count;
});
expect(before).toBe(0);
expect(start).toBeGreaterThanOrEqual(10); // make sure to adjust if weight is changed!
expect(end).toBeGreaterThanOrEqual(10); // make sure to adjust if weight is changed!
expect(after).toBe(0);
}

View File

@ -50,7 +50,7 @@ describe("Moves - Dragon Tail", () => {
await game.phaseInterceptor.to(BerryPhase); await game.phaseInterceptor.to(BerryPhase);
const isVisible = enemyPokemon.visible; const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.wildFlee; const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true); expect(!isVisible && hasFled).toBe(true);
// simply want to test that the game makes it this far without crashing // simply want to test that the game makes it this far without crashing
@ -72,7 +72,7 @@ describe("Moves - Dragon Tail", () => {
await game.phaseInterceptor.to(BerryPhase); await game.phaseInterceptor.to(BerryPhase);
const isVisible = enemyPokemon.visible; const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.wildFlee; const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true); expect(!isVisible && hasFled).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
}, TIMEOUT }, TIMEOUT
@ -97,9 +97,9 @@ describe("Moves - Dragon Tail", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
const isVisibleLead = enemyLeadPokemon.visible; const isVisibleLead = enemyLeadPokemon.visible;
const hasFledLead = enemyLeadPokemon.wildFlee; const hasFledLead = enemyLeadPokemon.switchOutStatus;
const isVisibleSec = enemySecPokemon.visible; const isVisibleSec = enemySecPokemon.visible;
const hasFledSec = enemySecPokemon.wildFlee; const hasFledSec = enemySecPokemon.switchOutStatus;
expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true); expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
@ -133,9 +133,9 @@ describe("Moves - Dragon Tail", () => {
await game.phaseInterceptor.to(BerryPhase); await game.phaseInterceptor.to(BerryPhase);
const isVisibleLead = enemyLeadPokemon.visible; const isVisibleLead = enemyLeadPokemon.visible;
const hasFledLead = enemyLeadPokemon.wildFlee; const hasFledLead = enemyLeadPokemon.switchOutStatus;
const isVisibleSec = enemySecPokemon.visible; const isVisibleSec = enemySecPokemon.visible;
const hasFledSec = enemySecPokemon.wildFlee; const hasFledSec = enemySecPokemon.switchOutStatus;
expect(!isVisibleLead && hasFledLead && !isVisibleSec && hasFledSec).toBe(true); expect(!isVisibleLead && hasFledLead && !isVisibleSec && hasFledSec).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
expect(secPokemon.hp).toBeLessThan(secPokemon.getMaxHp()); expect(secPokemon.hp).toBeLessThan(secPokemon.getMaxHp());

View File

@ -14,6 +14,8 @@ import { initStatsKeys } from "#app/ui/game-stats-ui-handler";
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
import { beforeAll, vi } from "vitest"; import { beforeAll, vi } from "vitest";
process.env.TZ = "UTC";
/** Mock the override import to always return default values, ignoring any custom overrides. */ /** Mock the override import to always return default values, ignoring any custom overrides. */
vi.mock("#app/overrides", async (importOriginal) => { vi.mock("#app/overrides", async (importOriginal) => {
const { defaultOverrides } = await importOriginal<typeof import("#app/overrides")>(); const { defaultOverrides } = await importOriginal<typeof import("#app/overrides")>();

View File

@ -381,17 +381,8 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr; const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
let playerOwnsThisAbility = false;
// Check if the player owns ability for the root form // Check if the player owns ability for the root form
if ((ownedAbilityAttrs & 1) > 0 && pokemon.hasSameAbilityInRootForm(0)) { const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
playerOwnsThisAbility = true;
}
if ((ownedAbilityAttrs & 2) > 0 && pokemon.hasSameAbilityInRootForm(1)) {
playerOwnsThisAbility = true;
}
if ((ownedAbilityAttrs & 4) > 0 && pokemon.hasSameAbilityInRootForm(2)) {
playerOwnsThisAbility = true;
}
if (missingDexAttrs || !playerOwnsThisAbility) { if (missingDexAttrs || !playerOwnsThisAbility) {
this.ownedIcon.setTint(0x808080); this.ownedIcon.setTint(0x808080);

View File

@ -267,18 +267,13 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false, this.scene.uiTheme)); this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false, this.scene.uiTheme));
this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true, this.scene.uiTheme)); this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true, this.scene.uiTheme));
/**
* If the opposing Pokemon only has 1 normal ability and is using the hidden ability it should have the same behavior
* if it had 2 normal abilities. This code checks if that is the case and uses the correct opponent Pokemon abilityIndex (2)
* for calculations so it aligns with where the hidden ability is stored in the starter data's abilityAttr (4)
*/
const opponentPokemonOneNormalAbility = (pokemon.species.getAbilityCount() === 2);
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr; const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
if (!rootFormHasHiddenAbility) { // Check if the player owns ability for the root form
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
if (!playerOwnsThisAbility) {
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme)); this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme)); this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme));
} else { } else {

View File

@ -13,7 +13,7 @@ import { RunEntry } from "../system/game-data";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { TrainerVariant } from "../field/trainer"; import { TrainerVariant } from "../field/trainer";
export type RunSelectCallback = (cursor: integer) => void; export type RunSelectCallback = (cursor: number) => void;
export const RUN_HISTORY_LIMIT: number = 25; export const RUN_HISTORY_LIMIT: number = 25;
@ -25,15 +25,15 @@ export const RUN_HISTORY_LIMIT: number = 25;
*/ */
export default class RunHistoryUiHandler extends MessageUiHandler { export default class RunHistoryUiHandler extends MessageUiHandler {
private readonly maxRows = 3;
private runSelectContainer: Phaser.GameObjects.Container; private runSelectContainer: Phaser.GameObjects.Container;
private runsContainer: Phaser.GameObjects.Container; private runsContainer: Phaser.GameObjects.Container;
private runSelectMessageBox: Phaser.GameObjects.NineSlice;
private runSelectMessageBoxContainer: Phaser.GameObjects.Container;
private runs: RunEntryContainer[]; private runs: RunEntryContainer[];
private runSelectCallback: RunSelectCallback | null; private runSelectCallback: RunSelectCallback | null;
private scrollCursor: integer = 0; private scrollCursor: number = 0;
private cursorObj: Phaser.GameObjects.NineSlice | null; private cursorObj: Phaser.GameObjects.NineSlice | null;
@ -74,8 +74,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler {
this.getUi().bringToTop(this.runSelectContainer); this.getUi().bringToTop(this.runSelectContainer);
this.runSelectContainer.setVisible(true); this.runSelectContainer.setVisible(true);
this.populateRuns(this.scene); this.populateRuns(this.scene).then(() => {
this.setScrollCursor(0); this.setScrollCursor(0);
this.setCursor(0); this.setCursor(0);
@ -83,6 +82,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler {
if (this.runs.length === 0) { if (this.runs.length === 0) {
this.clearCursor(); this.clearCursor();
} }
});
return true; return true;
} }
@ -122,13 +122,21 @@ export default class RunHistoryUiHandler extends MessageUiHandler {
success = this.setCursor(this.cursor - 1); success = this.setCursor(this.cursor - 1);
} else if (this.scrollCursor) { } else if (this.scrollCursor) {
success = this.setScrollCursor(this.scrollCursor - 1); success = this.setScrollCursor(this.scrollCursor - 1);
} else if (this.runs.length > 1) {
// wrap around to the bottom
success = this.setCursor(Math.min(this.runs.length - 1, this.maxRows - 1));
success = this.setScrollCursor(Math.max(0, this.runs.length - this.maxRows)) || success;
} }
break; break;
case Button.DOWN: case Button.DOWN:
if (this.cursor < 2) { if (this.cursor < Math.min(this.maxRows - 1, this.runs.length - this.scrollCursor - 1)) {
success = this.setCursor(this.cursor + 1); success = this.setCursor(this.cursor + 1);
} else if (this.scrollCursor < this.runs.length - 3) { } else if (this.scrollCursor < this.runs.length - this.maxRows) {
success = this.setScrollCursor(this.scrollCursor + 1); success = this.setScrollCursor(this.scrollCursor + 1);
} else if (this.runs.length > 1) {
// wrap around to the top
success = this.setCursor(0);
success = this.setScrollCursor(0) || success;
} }
break; break;
} }
@ -218,6 +226,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler {
override clear() { override clear() {
super.clear(); super.clear();
this.runSelectContainer.setVisible(false); this.runSelectContainer.setVisible(false);
this.setScrollCursor(0);
this.clearCursor(); this.clearCursor();
this.runSelectCallback = null; this.runSelectCallback = null;
this.clearRuns(); this.clearRuns();
@ -360,7 +369,7 @@ class RunEntryContainer extends Phaser.GameObjects.Container {
// The code here does not account for icon weirdness. // The code here does not account for icon weirdness.
const pokemonIconsContainer = this.scene.add.container(140, 17); const pokemonIconsContainer = this.scene.add.container(140, 17);
data.party.forEach((p: PokemonData, i: integer) => { data.party.forEach((p: PokemonData, i: number) => {
const iconContainer = this.scene.add.container(26 * i, 0); const iconContainer = this.scene.add.container(26 * i, 0);
iconContainer.setScale(0.75); iconContainer.setScale(0.75);
const pokemon = p.toPokemon(this.scene); const pokemon = p.toPokemon(this.scene);

View File

@ -49,15 +49,11 @@ export default class RunInfoUiHandler extends UiHandler {
private runResultContainer: Phaser.GameObjects.Container; private runResultContainer: Phaser.GameObjects.Container;
private runInfoContainer: Phaser.GameObjects.Container; private runInfoContainer: Phaser.GameObjects.Container;
private partyContainer: Phaser.GameObjects.Container; private partyContainer: Phaser.GameObjects.Container;
private partyHeldItemsContainer: Phaser.GameObjects.Container;
private statsBgWidth: integer; private statsBgWidth: integer;
private partyContainerHeight: integer;
private partyContainerWidth: integer;
private hallofFameContainer: Phaser.GameObjects.Container; private hallofFameContainer: Phaser.GameObjects.Container;
private endCardContainer: Phaser.GameObjects.Container; private endCardContainer: Phaser.GameObjects.Container;
private partyInfo: Phaser.GameObjects.Container[];
private partyVisibility: Boolean; private partyVisibility: Boolean;
private modifiersModule: any; private modifiersModule: any;
@ -863,7 +859,7 @@ export default class RunInfoUiHandler extends UiHandler {
private buttonCycleOption(button: Button) { private buttonCycleOption(button: Button) {
switch (button) { switch (button) {
case Button.CYCLE_FORM: case Button.CYCLE_FORM:
if (this.isVictory) { if (this.isVictory && this.pageMode !== RunInfoUiMode.HALL_OF_FAME) {
if (!this.endCardContainer || !this.endCardContainer.visible) { if (!this.endCardContainer || !this.endCardContainer.visible) {
this.createVictorySplash(); this.createVictorySplash();
this.endCardContainer.setVisible(true); this.endCardContainer.setVisible(true);
@ -877,7 +873,7 @@ export default class RunInfoUiHandler extends UiHandler {
} }
break; break;
case Button.CYCLE_SHINY: case Button.CYCLE_SHINY:
if (this.isVictory) { if (this.isVictory && this.pageMode !== RunInfoUiMode.ENDING_ART) {
if (!this.hallofFameContainer.visible) { if (!this.hallofFameContainer.visible) {
this.hallofFameContainer.setVisible(true); this.hallofFameContainer.setVisible(true);
this.pageMode = RunInfoUiMode.HALL_OF_FAME; this.pageMode = RunInfoUiMode.HALL_OF_FAME;
@ -888,7 +884,7 @@ export default class RunInfoUiHandler extends UiHandler {
} }
break; break;
case Button.CYCLE_ABILITY: case Button.CYCLE_ABILITY:
if (this.runInfo.modifiers.length !== 0) { if (this.runInfo.modifiers.length !== 0 && this.pageMode === RunInfoUiMode.MAIN) {
if (this.partyVisibility) { if (this.partyVisibility) {
this.showParty(false); this.showParty(false);
} else { } else {

View File

@ -3,11 +3,14 @@ import OptionSelectUiHandler from "./settings/option-select-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { TextStyle, addTextObject, getTextStyleOptions } from "./text";
import { getBattleCountSplashMessage, getSplashMessages } from "../data/splash-messages"; import { getSplashMessages } from "../data/splash-messages";
import i18next from "i18next"; import i18next from "i18next";
import { TimedEventDisplay } from "#app/timed-event-manager"; import { TimedEventDisplay } from "#app/timed-event-manager";
export default class TitleUiHandler extends OptionSelectUiHandler { export default class TitleUiHandler extends OptionSelectUiHandler {
/** If the stats can not be retrieved, use this fallback value */
private static readonly BATTLES_WON_FALLBACK: number = -99999999;
private titleContainer: Phaser.GameObjects.Container; private titleContainer: Phaser.GameObjects.Container;
private playerCountLabel: Phaser.GameObjects.Text; private playerCountLabel: Phaser.GameObjects.Text;
private splashMessage: string; private splashMessage: string;
@ -72,8 +75,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
.then(request => request.json()) .then(request => request.json())
.then(stats => { .then(stats => {
this.playerCountLabel.setText(`${stats.playerCount} ${i18next.t("menu:playersOnline")}`); this.playerCountLabel.setText(`${stats.playerCount} ${i18next.t("menu:playersOnline")}`);
if (this.splashMessage === getBattleCountSplashMessage()) { if (this.splashMessage === "splashMessages:battlesWon") {
this.splashMessageText.setText(getBattleCountSplashMessage().replace("{COUNT}", stats.battleCount.toLocaleString("en-US"))); this.splashMessageText.setText(i18next.t(this.splashMessage, { count: stats.battleCount }));
} }
}) })
.catch(err => { .catch(err => {
@ -86,7 +89,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
if (ret) { if (ret) {
this.splashMessage = Utils.randItem(getSplashMessages()); this.splashMessage = Utils.randItem(getSplashMessages());
this.splashMessageText.setText(this.splashMessage.replace("{COUNT}", "?")); this.splashMessageText.setText(i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK }));
const ui = this.getUi(); const ui = this.getUi();

View File

@ -139,7 +139,8 @@ const noTransitionModes = [
Mode.TEST_DIALOGUE, Mode.TEST_DIALOGUE,
Mode.AUTO_COMPLETE, Mode.AUTO_COMPLETE,
Mode.ADMIN, Mode.ADMIN,
Mode.MYSTERY_ENCOUNTER Mode.MYSTERY_ENCOUNTER,
Mode.RUN_INFO
]; ];
export default class UI extends Phaser.GameObjects.Container { export default class UI extends Phaser.GameObjects.Container {