From cb50f50d67232b147ead03ef46eff11f0b1aa94f Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 26 Sep 2024 23:23:29 -0700 Subject: [PATCH 01/38] types setup --- package.json | 4 +++- tsconfig.json | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tsconfig.json diff --git a/package.json b/package.json index 069819fc..c2ad5095 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,11 @@ }, "devDependencies": { "@eslint/js": "^9.11.0", + "@types/node": "^22.7.3", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.1.0", - "globals": "^15.9.0" + "globals": "^15.9.0", + "typescript": "^5.6.2" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..57dc6afc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "outDir": "./dummy", + "target": "ES6", + "lib": ["ES6"], + "checkJs": true, + "allowJs": true, + "skipLibCheck": true, + "moduleResolution": "node", + "strictNullChecks": true, + "allowSyntheticDefaultImports": true + }, + "exclude": ["node_modules"], + "include": ["./src/**/*.ts", "./src/**/*.js"] +} From 7e5cb5ee331711223742e4aa36cda82256594fac Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 26 Sep 2024 23:25:26 -0700 Subject: [PATCH 02/38] fix index errors ig --- src/index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 1eb63207..ad68039e 100644 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,7 @@ const { Client, Events, GatewayIntentBits } = require("discord.js"); const { createDjsClient } = require("discordbotlist"); const express = require("express"); const cors = require("cors"); -const axios = require("axios"); +const { default: axios } = require("axios"); const { runMessageCommand, runSlashCommand, @@ -124,6 +124,12 @@ client.on(Events.GuildCreate, (guild) => { // When the client is ready, run this code (only once) // We use 'c' for the event parameter to keep it separate from the already defined 'client' client.once(Events.ClientReady, (c) => { + if (!client.user) { + logger.error( + "Client user not found. Something went terribly wrong I'm so sorry I can't continue xd" + ); + return; + } logger.info(`Ready! Logged in as ${c.user.tag}`); client.user.setActivity(`/tutorial /help`); // log connected guilds @@ -159,8 +165,9 @@ client.once(Events.ClientReady, (c) => { // connect to discordbotlist.com if ( - process.env.STAGE === stageNames.BETA || - process.env.STAGE === stageNames.PROD + (process.env.STAGE === stageNames.BETA || + process.env.STAGE === stageNames.PROD) && + process.env.DBL_TOKEN ) { const dbl = createDjsClient(process.env.DBL_TOKEN, client); dbl.on("posted", (stats) => { From e33576698fdd13b9bb1ac4b017504a94011b0792 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 26 Sep 2024 23:36:29 -0700 Subject: [PATCH 03/38] more config --- tsconfig.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 57dc6afc..b70c8128 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,10 @@ { "compilerOptions": { "outDir": "./dummy", - "target": "ES6", - "lib": ["ES6"], + "target": "es2021" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, + "module": "commonjs", + "noEmit": true, + //"lib": ["ES6"], "checkJs": true, "allowJs": true, "skipLibCheck": true, From afe8f762e3718b75d40cb7ae57475393f052a89e Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 26 Sep 2024 23:44:14 -0700 Subject: [PATCH 04/38] eslint --- eslint.config.mjs | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index b5df5e52..09fe5199 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -34,6 +34,7 @@ export default [ "guard-for-in": "off", // TODO: enable but make better lol "no-continue": "off", // doesn't seem to work properly "no-await-in-loop": "off", // TODO: use promise.all + "jsdoc/no-undefined-types": 1, }, }, pluginJs.configs.recommended, diff --git a/package.json b/package.json index c2ad5095..58a9e772 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jsdoc": "^50.3.0", "globals": "^15.9.0", "typescript": "^5.6.2" } From a1b92f6ae637074dfe1f57fe190373f640a28120 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 27 Sep 2024 15:54:50 -0700 Subject: [PATCH 05/38] part 1 --- src/commands/battle/battleTower.js | 11 ++++++++--- src/handlers/commandHandler.js | 12 +++++++++++- src/services/battle.js | 8 ++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/commands/battle/battleTower.js b/src/commands/battle/battleTower.js index 2789d2dc..b89da930 100644 --- a/src/commands/battle/battleTower.js +++ b/src/commands/battle/battleTower.js @@ -10,15 +10,16 @@ const { setState, deleteState } = require("../../services/state"); const { buildBattleTowerSend } = require("../../services/battle"); const { getTrainer } = require("../../services/trainer"); const { battleTowerConfig } = require("../../config/npcConfig"); +const { ChatInputCommandInteraction, User } = require("discord.js"); /** * Creates the tower via encapsulating buildBattleTowerSend and giving it the required user data - * @param user user required for creating user specific tower. + * @param {User} user user required for creating user specific tower. * @returns Error or message to send. */ const battleTower = async (user) => { const trainer = await getTrainer(user); - if (trainer.err) { + if (trainer.err || !trainer.data) { return { err: trainer.err }; } @@ -53,7 +54,11 @@ const battleTowerMessageCommand = async (message) => { await message.channel.send(send); }; -// reads in slash-commands for the user-created tower and outputs results. +/** + * + * @param {ChatInputCommandInteraction} interaction + * @returns {Promise} + */ const battleTowerSlashCommand = async (interaction) => { const { send, err } = await battleTower(interaction.user); if (err) { diff --git a/src/handlers/commandHandler.js b/src/handlers/commandHandler.js index 93006d52..c8afe47e 100644 --- a/src/handlers/commandHandler.js +++ b/src/handlers/commandHandler.js @@ -6,7 +6,11 @@ * * commandHandler.js handles all commands and command types the user can use. */ -const { SlashCommandBuilder } = require("discord.js"); +const { + SlashCommandBuilder, + ChatInputCommandInteraction, + Client, +} = require("discord.js"); const path = require("node:path"); const { commandCategoryConfig, @@ -269,6 +273,12 @@ const runMessageCommand = async (message, client) => { } }; +/** + * + * @param {ChatInputCommandInteraction} interaction + * @param {Client} client + * @returns + */ const runSlashCommand = async (interaction, client) => { try { const query = new QueryBuilder(collectionNames.GUILDS) diff --git a/src/services/battle.js b/src/services/battle.js index d43df5f2..3694ab17 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -79,6 +79,7 @@ const { generateRandomPokemon } = require("./gacha"); const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); +const { User } = require("discord.js"); class NPC { constructor( @@ -3420,6 +3421,13 @@ const onBattleTowerAccept = async ({ stateId = null, user = null } = {}) => { return { err: null }; }; +/** + * + * @param {Object} param0 + * @param {string?=} param0.stateId + * @param {User?=} param0.user + * @returns + */ const buildBattleTowerSend = async ({ stateId = null, user = null } = {}) => { // get state const state = getState(stateId); From a772678eab4e987626f36edb201ac52da2a75958 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 28 Sep 2024 14:44:17 -0700 Subject: [PATCH 06/38] fix battle tower and typedef trainer --- src/commands/battle/battleTower.js | 2 +- src/services/trainer.js | 19 ++++++-- src/utils/utils.js | 14 +++++- types.js | 69 ++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 types.js diff --git a/src/commands/battle/battleTower.js b/src/commands/battle/battleTower.js index b89da930..6f7d5703 100644 --- a/src/commands/battle/battleTower.js +++ b/src/commands/battle/battleTower.js @@ -61,7 +61,7 @@ const battleTowerMessageCommand = async (message) => { */ const battleTowerSlashCommand = async (interaction) => { const { send, err } = await battleTower(interaction.user); - if (err) { + if (err || !send) { await interaction.reply(`${err}`); return { err }; } diff --git a/src/services/trainer.js b/src/services/trainer.js index 5182cd59..5c546cc9 100644 --- a/src/services/trainer.js +++ b/src/services/trainer.js @@ -37,7 +37,7 @@ const { getBackpackItemsString, } = require("../utils/trainerUtils"); const { getVoteMultiplier } = require("../config/socialConfig"); - +const types = require("../../types"); /* "user": { "username": "Mason", @@ -77,7 +77,10 @@ const initTrainer = async (user) => { const getTrainerFromId = async (userId) => { try { // check if trainer exists - const trainers = await findDocuments(collectionNames.USERS, { userId }); + const trainers = + /** @type{Array>} */ ( + await findDocuments(collectionNames.USERS, { userId }) + ); if (trainers.length === 0) { return { data: null, err: "Error finding trainer." }; } @@ -89,6 +92,12 @@ const getTrainerFromId = async (userId) => { } }; +/** + * + * @param {any} discordUser + * @param {boolean?} refresh + * @returns + */ const getTrainer = async (discordUser, refresh = true) => { // only keep desired fields const tmpUser = { @@ -102,7 +111,9 @@ const getTrainer = async (discordUser, refresh = true) => { let trainers; try { // check if trainer exists - trainers = await findDocuments(collectionNames.USERS, { userId: user.id }); + trainers = + /** @type{Array>} */ + (await findDocuments(collectionNames.USERS, { userId: user.id })); } catch (error) { logger.error(error); return { data: null, err: "Error finding trainer." }; @@ -111,7 +122,7 @@ const getTrainer = async (discordUser, refresh = true) => { let trainer; if (trainers.length === 0) { try { - trainer = await initTrainer(user); + trainer = /** @type{types.Trainer} */ (await initTrainer(user)); if (trainer === null) { return { data: null, err: "Error creating trainer." }; } diff --git a/src/utils/utils.js b/src/utils/utils.js index 529e3c80..5b88474b 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -129,6 +129,10 @@ const setTwoInline = (fields) => { return fields; }; +/** + * @param {Date?=} date + * @returns + */ const getFullUTCDate = (date = null) => { if (!date) { date = new Date(); @@ -137,6 +141,10 @@ const getFullUTCDate = (date = null) => { return Math.floor(time / 86400000); }; +/** + * @param {Date?=} date + * @returns + */ const getFullUTCWeek = (date = null) => { if (!date) { date = new Date(); @@ -145,7 +153,11 @@ const getFullUTCWeek = (date = null) => { return Math.floor(time / (86400000 * 7)); }; -// nice fortnite +/** + * nice fortnite + * @param {Date?=} date + * @returns + */ const getFullUTCFortnight = (date = null) => { if (!date) { date = new Date(); diff --git a/types.js b/types.js new file mode 100644 index 00000000..fc491546 --- /dev/null +++ b/types.js @@ -0,0 +1,69 @@ +const { ObjectId } = require("mongodb"); + +module.exports._typesOnly = true; + +/** + * @typedef {Object} CompactUser + * @property {string} id + * @property {string} username + * @property {string?} discriminator + * @property {string} avatar + */ + +/** + * @typedef {Object} UserVotingInfo + * @property {number} lastVoted + * @property {number} streak + * @property {number} rewards + */ + +/** + * @typedef {Object} UserTradeInfo + * @property {number} money + * @property {Array} pokemonIds + */ + +/** + * @typedef {Object} PartyInfo + * @property {Array} pokemonIds + * @property {number} rows + * @property {number} cols + */ + +/** + * @typedef {Object} Trainer + * + * Discord user info + * @property {string} userId + * @property {CompactUser} user + * @property {UserVotingInfo} voting + * + * Basic trainer info + * @property {number} level + * @property {number} exp + * @property {number} money + * @property {Object} backpack + * @property {Object} locations + * @property {UserTradeInfo} trade + * + * Rewards and time-gated stuff + * @property {number} lastDaily + * @property {boolean} claimedDaily + * @property {Object} purchasedShopItemsToday + * @property {Array} claimedLevelRewards + * @property {Object} defeatedNPCsToday + * @property {Object} defeatedNPCs + * @property {number} lastTowerStage + * + * Party info + * @property {PartyInfo} party + * @property {Object} savedParties + * + * Banner + * @property {number} beginnerRolls + * @property {Object} banners + * + * Mythic Pokemon + * @property {boolean} hasCelebi + * @property {boolean} usedTimeTravel + */ From 5a10132480d1fce2cdf497f093ad97b036a044b3 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 28 Sep 2024 16:45:52 -0700 Subject: [PATCH 07/38] fix types for battle commands --- src/commands/battle/partyAuto.js | 2 +- src/commands/battle/partyInfo.js | 2 +- src/commands/battle/partyLoad.js | 2 +- src/commands/battle/partyRemove.js | 2 +- src/services/battle.js | 16 ++++++++++++++++ types.js | 2 +- 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/commands/battle/partyAuto.js b/src/commands/battle/partyAuto.js index 70ddd791..0a7a23bf 100644 --- a/src/commands/battle/partyAuto.js +++ b/src/commands/battle/partyAuto.js @@ -20,7 +20,7 @@ const { buildPartyEmbed } = require("../../embeds/battleEmbeds"); const partyAuto = async (user, option) => { // get trainer const trainer = await getTrainer(user); - if (trainer.err) { + if (trainer.err || !trainer.data) { return { send: null, err: trainer.err }; } const { party } = trainer.data; diff --git a/src/commands/battle/partyInfo.js b/src/commands/battle/partyInfo.js index 3e5a2af7..a9a64611 100644 --- a/src/commands/battle/partyInfo.js +++ b/src/commands/battle/partyInfo.js @@ -27,7 +27,7 @@ const partyInfo = async (user) => { // get party pokemons const partyPokemons = await getPartyPokemons(trainer.data); - if (partyPokemons.err) { + if (partyPokemons.err || !partyPokemons.data) { return { send: null, err: partyPokemons.err }; } const partyPokemonsFiltered = partyPokemons.data.filter( diff --git a/src/commands/battle/partyLoad.js b/src/commands/battle/partyLoad.js index 3678195e..ecc79494 100644 --- a/src/commands/battle/partyLoad.js +++ b/src/commands/battle/partyLoad.js @@ -19,7 +19,7 @@ const { buildPartyEmbed } = require("../../embeds/battleEmbeds"); const partyLoad = async (user, preset) => { // get trainer const trainer = await getTrainer(user); - if (trainer.err) { + if (trainer.err || !trainer.data) { return { send: null, err: trainer.err }; } const { party } = trainer.data; diff --git a/src/commands/battle/partyRemove.js b/src/commands/battle/partyRemove.js index 58f534ad..88bb64d9 100644 --- a/src/commands/battle/partyRemove.js +++ b/src/commands/battle/partyRemove.js @@ -19,7 +19,7 @@ const { buildPartyEmbed } = require("../../embeds/battleEmbeds"); const partyRemove = async (user, option) => { // get trainer const trainer = await getTrainer(user); - if (trainer.err) { + if (trainer.err || !trainer.data) { return { send: null, err: trainer.err }; } const partyPokemon = trainer.data.party.pokemonIds; diff --git a/src/services/battle.js b/src/services/battle.js index 3694ab17..90214293 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -2044,6 +2044,22 @@ class Battle { difficulty; isPvp; */ + /** + * @param {Object} param0 + * @param {number?=} param0.moneyMultiplier + * @param {number?=} param0.expMultiplier + * @param {number?=} param0.pokemonExpMultiplier + * @param {number?=} param0.level + * @param {number?=} param0.equipmentLevel + * @param {Object?=} param0.rewards + * @param {string?=} param0.rewardString + * @param {Object?=} param0.dailyRewards + * @param {Function?=} param0.winCallback + * @param {Function?=} param0.loseCallback + * @param {string?=} param0.npcId + * @param {string?=} param0.difficulty + * @param {boolean?=} param0.isPvp + */ constructor({ moneyMultiplier = 1, expMultiplier = 1, diff --git a/types.js b/types.js index fc491546..e1465966 100644 --- a/types.js +++ b/types.js @@ -25,7 +25,7 @@ module.exports._typesOnly = true; /** * @typedef {Object} PartyInfo - * @property {Array} pokemonIds + * @property {Array} pokemonIds * @property {number} rows * @property {number} cols */ From ea4b8171073c391e13e9d6eaceee272228becb5e Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 28 Sep 2024 17:37:23 -0700 Subject: [PATCH 08/38] fix types for pokemon commands + guild type --- src/commands/pokemon/gacha.js | 2 +- src/commands/pokemon/list.js | 2 +- src/commands/pokemon/toggleSpawn.js | 6 +++++- src/commands/pokemon/train.js | 2 +- src/services/gacha.js | 15 +++++++++++---- src/services/pokemon.js | 8 ++++++++ types.js | 13 +++++++++++++ 7 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/commands/pokemon/gacha.js b/src/commands/pokemon/gacha.js index cd581f00..07840566 100644 --- a/src/commands/pokemon/gacha.js +++ b/src/commands/pokemon/gacha.js @@ -12,7 +12,7 @@ const { setState, deleteState } = require("../../services/state"); /** * Attempts to use a pokeball to spin the gacha for a random pokemon. * @param {Object} user User who initiated the command. - * @param {String} pokeball Pokeball to use. + * @param {number} page Pokeball to use. * @returns An embed with the new pokemon, or an error message. */ const gacha = async (user, page) => { diff --git a/src/commands/pokemon/list.js b/src/commands/pokemon/list.js index dfd428b2..c87bf11d 100644 --- a/src/commands/pokemon/list.js +++ b/src/commands/pokemon/list.js @@ -24,7 +24,7 @@ const { getUserId } = require("../../utils/utils"); * @param {Object} user User who initiated the command. * @param {Number} page Page of the list to display. * @param {String} filterBy Field to filter by. - * @param {String} filterValue Value to filter for equality. + * @param {String | boolean} filterValue Value to filter for equality. * @param {String} sortBy Field to sort by. * @param {boolean} descending Sort in descending order. * @returns Embed with list of Pokemon, and components for pagination/selection. diff --git a/src/commands/pokemon/toggleSpawn.js b/src/commands/pokemon/toggleSpawn.js index af1e4495..a7484778 100644 --- a/src/commands/pokemon/toggleSpawn.js +++ b/src/commands/pokemon/toggleSpawn.js @@ -2,6 +2,8 @@ const { collectionNames } = require("../../config/databaseConfig"); const { QueryBuilder } = require("../../database/mongoHandler"); const { getChannelId } = require("../../utils/utils"); const { logger } = require("../../log"); +const types = require("../../../types"); +const { Collection } = require("mongodb"); const toggleSpawn = async (user, guildId, channelId, member) => { // check if user can manage roles @@ -21,7 +23,9 @@ const toggleSpawn = async (user, guildId, channelId, member) => { guildId, }); - const guildRes = await query.findOne(); + const guildRes = /** @type{types.MongoCollection} */ ( + await query.findOne() + ); if (guildRes) { guildData = guildRes; } diff --git a/src/commands/pokemon/train.js b/src/commands/pokemon/train.js index b7a1c89f..8c724704 100644 --- a/src/commands/pokemon/train.js +++ b/src/commands/pokemon/train.js @@ -48,7 +48,7 @@ const train = async (user, pokemonId, location) => { // add exp & EVs const res = await trainPokemon(trainer.data, pokemon.data, locationId); - if (res.err) { + if (res.err || !res.data) { return { data: null, err: res.err }; } const { exp, evs } = res.data; diff --git a/src/services/gacha.js b/src/services/gacha.js index 332fb39a..ae7e6e65 100644 --- a/src/services/gacha.js +++ b/src/services/gacha.js @@ -465,6 +465,13 @@ const usePokeball = async (trainer, pokeballId, bannerIndex, quantity = 1) => { return await giveNewPokemons(trainer, pokemonIds); }; +/** + * @param {Object} param0 + * @param {number?=} param0.stateId + * @param {Object?=} param0.user + * @param {number?=} param0.page + * @returns + */ const buildBannerSend = async ({ stateId = null, user = null, @@ -487,11 +494,11 @@ const buildBannerSend = async ({ } // get trainer - let trainer = await getTrainer(user); - if (trainer.err) { - return { send: null, err: trainer.err }; + const trainerResult = await getTrainer(user); + if (trainerResult.err || !trainerResult.data) { + return { send: null, err: trainerResult.err }; } - trainer = trainer.data; + const trainer = trainerResult.data; const send = { embeds: [], diff --git a/src/services/pokemon.js b/src/services/pokemon.js index 612f08dc..b3e27a24 100644 --- a/src/services/pokemon.js +++ b/src/services/pokemon.js @@ -876,6 +876,14 @@ const buildPokemonAllInfoSend = async ({ return { send, err: null }; }; +/** + * @param {Object} param0 + * @param {Object?=} param0.user + * @param {string?=} param0.pokemonId + * @param {string?=} param0.tab + * @param {string?=} param0.action + * @returns + */ const buildPokemonInfoSend = async ({ user = null, pokemonId = null, diff --git a/types.js b/types.js index e1465966..3ccf6388 100644 --- a/types.js +++ b/types.js @@ -67,3 +67,16 @@ module.exports._typesOnly = true; * @property {boolean} hasCelebi * @property {boolean} usedTimeTravel */ + +/** + * @typedef {Object} Guild + * @property {string} guildId + * @property {number} lastCommand + * @property {boolean} spawnDisabled + * @property {Array} spawnDisabledChannels + */ + +/** + * @template T + * @typedef {import('mongodb').Collection & T} MongoCollection + */ From 06fd96ff5d632e4918a758a6a6e97f1ae666597d Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 28 Sep 2024 22:58:14 -0700 Subject: [PATCH 09/38] type other commands --- src/commands/deployCommands.js | 16 +++++++++++++--- src/commands/shop/buy.js | 3 ++- src/commands/shop/pokemart.js | 3 ++- src/commands/trainer/daily.js | 7 ++++--- src/services/shop.js | 11 +++++++++++ types.js | 2 -- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/commands/deployCommands.js b/src/commands/deployCommands.js index 9a6066b7..e461e942 100644 --- a/src/commands/deployCommands.js +++ b/src/commands/deployCommands.js @@ -35,9 +35,17 @@ for (const commandCategory in commandCategoryConfig) { } // Construct and prepare an instance of the REST module +if (!token) { + logger.error("No token provided."); + process.exit(1); +} const rest = new REST().setToken(token); // and deploy your commands! +if (!clientId) { + logger.error("No client ID provided."); + process.exit(1); +} (async () => { try { logger.info( @@ -46,9 +54,11 @@ const rest = new REST().setToken(token); logger.info(JSON.stringify(commands, null, 2)); // The put method is used to fully refresh all commands in the guild with the current set - const data = await rest.put(Routes.applicationCommands(clientId), { - body: commands, - }); + const data = /** @type{any} */ ( + await rest.put(Routes.applicationCommands(clientId), { + body: commands, + }) + ); logger.info( `Successfully reloaded ${data.length} application (/) commands.` diff --git a/src/commands/shop/buy.js b/src/commands/shop/buy.js index d574f586..a1a390c9 100644 --- a/src/commands/shop/buy.js +++ b/src/commands/shop/buy.js @@ -6,12 +6,13 @@ * * buy.js created for users to buy items. */ +const { User } = require("discord.js"); const { buyItem } = require("../../services/shop"); const { getTrainer } = require("../../services/trainer"); /** * Attempts to execute user's request to buy an item at a given quantity. - * @param {Object} user User who initiated the command. + * @param {User} user User who initiated the command. * @param {String} itemId ID of the item to buy. * @param {Number} quantity Quantity of the item to buy. * @returns Message confirming the purchase, or an error message. diff --git a/src/commands/shop/pokemart.js b/src/commands/shop/pokemart.js index b4e9355a..f591c9d3 100644 --- a/src/commands/shop/pokemart.js +++ b/src/commands/shop/pokemart.js @@ -8,11 +8,12 @@ */ const { setState, deleteState } = require("../../services/state"); const { buildShopSend } = require("../../services/shop"); +const { User } = require("discord.js"); /** * Parses the shop config, returning an interactive embed for the user to * browse the shop. - * @param {String} user User who initiated the command. + * @param {User} user User who initiated the command. * @returns Embed with shop options. */ const pokemart = async (user) => { diff --git a/src/commands/trainer/daily.js b/src/commands/trainer/daily.js index 08d55b04..2262834f 100644 --- a/src/commands/trainer/daily.js +++ b/src/commands/trainer/daily.js @@ -6,6 +6,7 @@ * * daily.js creates a system to display and grant the user their daily rewards. */ +const { User } = require("discord.js"); const { drawDaily } = require("../../services/gacha"); const { getTrainer } = require("../../services/trainer"); const { @@ -17,19 +18,19 @@ const { formatMoney } = require("../../utils/utils"); /** * Attempts to grant the user their daily rewards. If the user has * already claimed their daily rewards, returns an error message. - * @param {Object} user User who initiated the command. + * @param {User} user User who initiated the command. * @returns Embed with the user's daily rewards, or an error message. */ const daily = async (user) => { // get trainer const trainer = await getTrainer(user); - if (trainer.err) { + if (trainer.err || !trainer.data) { return { data: null, err: trainer.err }; } // draw daily rewards const rewards = await drawDaily(trainer.data); - if (rewards.err) { + if (rewards.err || !rewards.data) { return { data: null, err: rewards.err }; } const { money } = rewards.data; diff --git a/src/services/shop.js b/src/services/shop.js index 92e88e83..73c58978 100644 --- a/src/services/shop.js +++ b/src/services/shop.js @@ -38,6 +38,7 @@ const { addItems, getItems, } = require("../utils/trainerUtils"); +const { User } = require("discord.js"); const canBuyItem = (trainer, itemId, quantity) => { getOrSetDefault(trainer.purchasedShopItemsToday, itemId, 0); @@ -305,6 +306,16 @@ const buyItem = async (trainer, itemId, quantity) => { return { data: returnString, err: null }; }; +/** + * + * @param {Object} param0 + * @param {string?=} param0.stateId + * @param {User?=} param0.user + * @param {string?=} param0.view + * @param {string?=} param0.option + * @param {boolean?=} param0.back + * @returns + */ const buildShopSend = async ({ stateId = null, user = null, diff --git a/types.js b/types.js index 3ccf6388..79e93a1d 100644 --- a/types.js +++ b/types.js @@ -1,5 +1,3 @@ -const { ObjectId } = require("mongodb"); - module.exports._typesOnly = true; /** From 8c5ce240af83e61cc1d685bd484efc63266026ea Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Mon, 30 Sep 2024 19:23:10 -0700 Subject: [PATCH 10/38] remove items from backpack category config --- src/config/backpackConfig.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/config/backpackConfig.js b/src/config/backpackConfig.js index e7009c01..b2831680 100644 --- a/src/config/backpackConfig.js +++ b/src/config/backpackConfig.js @@ -15,36 +15,22 @@ const backpackItems = { MINT: "7", RAID_PASS: "8", }; - const backpackCategoryConfig = { [backpackCategories.POKEBALLS]: { name: "Pokeballs", emoji: "<:pokeball:1100296136931156008>", description: "Used to catch Pokemon! Use `/help gacha` command to learn more!", - items: [ - backpackItems.POKEBALL, - backpackItems.GREATBALL, - backpackItems.ULTRABALL, - backpackItems.MASTERBALL, - ], }, [backpackCategories.MATERIALS]: { name: "Materials", emoji: "<:materials:1112557472759160852>", description: "Used to upgrade Pokemon and equipment!", - items: [ - backpackItems.KNOWLEDGE_SHARD, - backpackItems.EMOTION_SHARD, - backpackItems.WILLPOWER_SHARD, - backpackItems.MINT, - ], }, [backpackCategories.CONSUMABLES]: { name: "Consumables", emoji: "<:raidpass:1150161526297206824>", description: "One-time use items for various purposes!", - items: [backpackItems.RAID_PASS], }, }; From 66eeba86689d36ca6d16075b43e87491dbbab3db Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Wed, 2 Oct 2024 23:39:15 -0700 Subject: [PATCH 11/38] make enums or something --- src/config/backpackConfig.js | 21 +++++++++++++-------- src/config/battleConfig.js | 1 - types.js | 7 +++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/config/backpackConfig.js b/src/config/backpackConfig.js index b2831680..a2c6fd66 100644 --- a/src/config/backpackConfig.js +++ b/src/config/backpackConfig.js @@ -1,10 +1,14 @@ -const backpackCategories = { +const types = require("../../types"); + +/** @typedef{types.Enum} BackpackCategoryEnum */ +const backpackCategories = Object.freeze({ POKEBALLS: "0", MATERIALS: "1", CONSUMABLES: "2", -}; +}); -const backpackItems = { +/** @typedef{types.Enum} BackpackItemEnum */ +const backpackItems = Object.freeze({ POKEBALL: "0", GREATBALL: "1", ULTRABALL: "2", @@ -14,8 +18,9 @@ const backpackItems = { WILLPOWER_SHARD: "6", MINT: "7", RAID_PASS: "8", -}; -const backpackCategoryConfig = { +}); + +const backpackCategoryConfig = Object.freeze({ [backpackCategories.POKEBALLS]: { name: "Pokeballs", emoji: "<:pokeball:1100296136931156008>", @@ -32,9 +37,9 @@ const backpackCategoryConfig = { emoji: "<:raidpass:1150161526297206824>", description: "One-time use items for various purposes!", }, -}; +}); -const backpackItemConfig = { +const backpackItemConfig = Object.freeze({ [backpackItems.POKEBALL]: { name: "Pokeball", emoji: "<:pokeball:1100296136931156008>", @@ -93,7 +98,7 @@ const backpackItemConfig = { description: "Used to start a raid!", category: backpackCategories.CONSUMABLES, }, -}; +}); module.exports = { backpackCategories, diff --git a/src/config/battleConfig.js b/src/config/battleConfig.js index 02fbbe70..bb810c27 100644 --- a/src/config/battleConfig.js +++ b/src/config/battleConfig.js @@ -350,7 +350,6 @@ const targetPatterns = { }; // TODO: is it worth having classes for these? - const effectTypes = { BUFF: "Buff", DEBUFF: "Debuff", diff --git a/types.js b/types.js index 79e93a1d..dd224ec4 100644 --- a/types.js +++ b/types.js @@ -1,5 +1,12 @@ module.exports._typesOnly = true; +// TODO: organize this lol + +/** + * @template T + * @typedef {T[keyof T]} Enum + */ + /** * @typedef {Object} CompactUser * @property {string} id From 70a4a5557fcb907fa9a904a66050fce3a83f4c44 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 3 Oct 2024 17:10:02 -0700 Subject: [PATCH 12/38] rename pokemon types --- src/config/battleConfig.js | 1003 +++++++++++++++++++----------------- 1 file changed, 519 insertions(+), 484 deletions(-) diff --git a/src/config/battleConfig.js b/src/config/battleConfig.js index bb810c27..aebbd4c2 100644 --- a/src/config/battleConfig.js +++ b/src/config/battleConfig.js @@ -3,7 +3,7 @@ /* eslint-disable no-shadow */ /* eslint-disable no-use-before-define */ /* eslint-disable no-param-reassign */ -const { types } = require("./pokemonConfig"); +const { types: pokemonTypes } = require("./pokemonConfig"); const battleEventNames = { BATTLE_BEGIN: "battleStart", @@ -68,153 +68,161 @@ steel = {'fire': .5, 'water': .5, 'electric': .5, 'ice': 2, 'rock': 2, 'steel': fairy = {'fire': .5, 'fighting': 2, 'poison': .5, 'dragon': 2, 'dark': 2, 'steel': .5} */ const typeAdvantages = { - [types.NORMAL]: { [types.ROCK]: 0.5, [types.GHOST]: 0, [types.STEEL]: 0.5 }, - [types.FIRE]: { - [types.FIRE]: 0.5, - [types.WATER]: 0.5, - [types.GRASS]: 2, - [types.ICE]: 2, - [types.BUG]: 2, - [types.ROCK]: 0.5, - [types.DRAGON]: 0.5, - [types.STEEL]: 2, - }, - [types.WATER]: { - [types.FIRE]: 2, - [types.WATER]: 0.5, - [types.GRASS]: 0.5, - [types.GROUND]: 2, - [types.ROCK]: 2, - [types.DRAGON]: 0.5, - }, - [types.ELECTRIC]: { - [types.WATER]: 2, - [types.ELECTRIC]: 0.5, - [types.GRASS]: 0.5, - [types.GROUND]: 0, - [types.FLYING]: 2, - [types.DRAGON]: 0.5, - }, - [types.GRASS]: { - [types.FIRE]: 0.5, - [types.WATER]: 2, - [types.GRASS]: 0.5, - [types.POISON]: 0.5, - [types.GROUND]: 2, - [types.FLYING]: 0.5, - [types.BUG]: 0.5, - [types.ROCK]: 2, - [types.DRAGON]: 0.5, - [types.STEEL]: 0.5, - }, - [types.ICE]: { - [types.FIRE]: 0.5, - [types.WATER]: 0.5, - [types.GRASS]: 2, - [types.ICE]: 0.5, - [types.GROUND]: 2, - [types.FLYING]: 2, - [types.DRAGON]: 2, - [types.STEEL]: 0.5, - }, - [types.FIGHTING]: { - [types.NORMAL]: 2, - [types.ICE]: 2, - [types.POISON]: 0.5, - [types.FLYING]: 0.5, - [types.PSYCHIC]: 0.5, - [types.BUG]: 0.5, - [types.ROCK]: 2, - [types.GHOST]: 0, - [types.DARK]: 2, - [types.STEEL]: 2, - [types.FAIRY]: 0.5, - }, - [types.POISON]: { - [types.GRASS]: 2, - [types.POISON]: 0.5, - [types.GROUND]: 0.5, - [types.ROCK]: 0.5, - [types.GHOST]: 0.5, - [types.STEEL]: 0, - [types.FAIRY]: 2, - }, - [types.GROUND]: { - [types.FIRE]: 2, - [types.ELECTRIC]: 2, - [types.GRASS]: 0.5, - [types.POISON]: 2, - [types.FLYING]: 0, - [types.BUG]: 0.5, - [types.ROCK]: 2, - [types.STEEL]: 2, - }, - [types.FLYING]: { - [types.ELECTRIC]: 0.5, - [types.GRASS]: 2, - [types.FIGHTING]: 2, - [types.BUG]: 2, - [types.ROCK]: 0.5, - [types.STEEL]: 0.5, - }, - [types.PSYCHIC]: { - [types.FIGHTING]: 2, - [types.POISON]: 2, - [types.PSYCHIC]: 0.5, - [types.DARK]: 0, - [types.STEEL]: 0.5, - }, - [types.BUG]: { - [types.FIRE]: 0.5, - [types.GRASS]: 2, - [types.FIGHTING]: 0.5, - [types.POISON]: 0.5, - [types.FLYING]: 0.5, - [types.PSYCHIC]: 2, - [types.GHOST]: 0.5, - [types.DARK]: 2, - [types.STEEL]: 0.5, - [types.FAIRY]: 0.5, - }, - [types.ROCK]: { - [types.FIRE]: 2, - [types.ICE]: 2, - [types.FIGHTING]: 0.5, - [types.GROUND]: 0.5, - [types.FLYING]: 2, - [types.BUG]: 2, - [types.STEEL]: 0.5, - }, - [types.GHOST]: { - [types.NORMAL]: 0, - [types.PSYCHIC]: 2, - [types.GHOST]: 2, - [types.DARK]: 0.5, - }, - [types.DRAGON]: { [types.DRAGON]: 2, [types.STEEL]: 0.5, [types.FAIRY]: 0 }, - [types.DARK]: { - [types.FIGHTING]: 0.5, - [types.PSYCHIC]: 2, - [types.GHOST]: 2, - [types.DARK]: 0.5, - [types.FAIRY]: 0.5, - }, - [types.STEEL]: { - [types.FIRE]: 0.5, - [types.WATER]: 0.5, - [types.ELECTRIC]: 0.5, - [types.ICE]: 2, - [types.ROCK]: 2, - [types.STEEL]: 0.5, - [types.FAIRY]: 2, - }, - [types.FAIRY]: { - [types.FIRE]: 0.5, - [types.FIGHTING]: 2, - [types.POISON]: 0.5, - [types.DRAGON]: 2, - [types.DARK]: 2, - [types.STEEL]: 0.5, + [pokemonTypes.NORMAL]: { + [pokemonTypes.ROCK]: 0.5, + [pokemonTypes.GHOST]: 0, + [pokemonTypes.STEEL]: 0.5, + }, + [pokemonTypes.FIRE]: { + [pokemonTypes.FIRE]: 0.5, + [pokemonTypes.WATER]: 0.5, + [pokemonTypes.GRASS]: 2, + [pokemonTypes.ICE]: 2, + [pokemonTypes.BUG]: 2, + [pokemonTypes.ROCK]: 0.5, + [pokemonTypes.DRAGON]: 0.5, + [pokemonTypes.STEEL]: 2, + }, + [pokemonTypes.WATER]: { + [pokemonTypes.FIRE]: 2, + [pokemonTypes.WATER]: 0.5, + [pokemonTypes.GRASS]: 0.5, + [pokemonTypes.GROUND]: 2, + [pokemonTypes.ROCK]: 2, + [pokemonTypes.DRAGON]: 0.5, + }, + [pokemonTypes.ELECTRIC]: { + [pokemonTypes.WATER]: 2, + [pokemonTypes.ELECTRIC]: 0.5, + [pokemonTypes.GRASS]: 0.5, + [pokemonTypes.GROUND]: 0, + [pokemonTypes.FLYING]: 2, + [pokemonTypes.DRAGON]: 0.5, + }, + [pokemonTypes.GRASS]: { + [pokemonTypes.FIRE]: 0.5, + [pokemonTypes.WATER]: 2, + [pokemonTypes.GRASS]: 0.5, + [pokemonTypes.POISON]: 0.5, + [pokemonTypes.GROUND]: 2, + [pokemonTypes.FLYING]: 0.5, + [pokemonTypes.BUG]: 0.5, + [pokemonTypes.ROCK]: 2, + [pokemonTypes.DRAGON]: 0.5, + [pokemonTypes.STEEL]: 0.5, + }, + [pokemonTypes.ICE]: { + [pokemonTypes.FIRE]: 0.5, + [pokemonTypes.WATER]: 0.5, + [pokemonTypes.GRASS]: 2, + [pokemonTypes.ICE]: 0.5, + [pokemonTypes.GROUND]: 2, + [pokemonTypes.FLYING]: 2, + [pokemonTypes.DRAGON]: 2, + [pokemonTypes.STEEL]: 0.5, + }, + [pokemonTypes.FIGHTING]: { + [pokemonTypes.NORMAL]: 2, + [pokemonTypes.ICE]: 2, + [pokemonTypes.POISON]: 0.5, + [pokemonTypes.FLYING]: 0.5, + [pokemonTypes.PSYCHIC]: 0.5, + [pokemonTypes.BUG]: 0.5, + [pokemonTypes.ROCK]: 2, + [pokemonTypes.GHOST]: 0, + [pokemonTypes.DARK]: 2, + [pokemonTypes.STEEL]: 2, + [pokemonTypes.FAIRY]: 0.5, + }, + [pokemonTypes.POISON]: { + [pokemonTypes.GRASS]: 2, + [pokemonTypes.POISON]: 0.5, + [pokemonTypes.GROUND]: 0.5, + [pokemonTypes.ROCK]: 0.5, + [pokemonTypes.GHOST]: 0.5, + [pokemonTypes.STEEL]: 0, + [pokemonTypes.FAIRY]: 2, + }, + [pokemonTypes.GROUND]: { + [pokemonTypes.FIRE]: 2, + [pokemonTypes.ELECTRIC]: 2, + [pokemonTypes.GRASS]: 0.5, + [pokemonTypes.POISON]: 2, + [pokemonTypes.FLYING]: 0, + [pokemonTypes.BUG]: 0.5, + [pokemonTypes.ROCK]: 2, + [pokemonTypes.STEEL]: 2, + }, + [pokemonTypes.FLYING]: { + [pokemonTypes.ELECTRIC]: 0.5, + [pokemonTypes.GRASS]: 2, + [pokemonTypes.FIGHTING]: 2, + [pokemonTypes.BUG]: 2, + [pokemonTypes.ROCK]: 0.5, + [pokemonTypes.STEEL]: 0.5, + }, + [pokemonTypes.PSYCHIC]: { + [pokemonTypes.FIGHTING]: 2, + [pokemonTypes.POISON]: 2, + [pokemonTypes.PSYCHIC]: 0.5, + [pokemonTypes.DARK]: 0, + [pokemonTypes.STEEL]: 0.5, + }, + [pokemonTypes.BUG]: { + [pokemonTypes.FIRE]: 0.5, + [pokemonTypes.GRASS]: 2, + [pokemonTypes.FIGHTING]: 0.5, + [pokemonTypes.POISON]: 0.5, + [pokemonTypes.FLYING]: 0.5, + [pokemonTypes.PSYCHIC]: 2, + [pokemonTypes.GHOST]: 0.5, + [pokemonTypes.DARK]: 2, + [pokemonTypes.STEEL]: 0.5, + [pokemonTypes.FAIRY]: 0.5, + }, + [pokemonTypes.ROCK]: { + [pokemonTypes.FIRE]: 2, + [pokemonTypes.ICE]: 2, + [pokemonTypes.FIGHTING]: 0.5, + [pokemonTypes.GROUND]: 0.5, + [pokemonTypes.FLYING]: 2, + [pokemonTypes.BUG]: 2, + [pokemonTypes.STEEL]: 0.5, + }, + [pokemonTypes.GHOST]: { + [pokemonTypes.NORMAL]: 0, + [pokemonTypes.PSYCHIC]: 2, + [pokemonTypes.GHOST]: 2, + [pokemonTypes.DARK]: 0.5, + }, + [pokemonTypes.DRAGON]: { + [pokemonTypes.DRAGON]: 2, + [pokemonTypes.STEEL]: 0.5, + [pokemonTypes.FAIRY]: 0, + }, + [pokemonTypes.DARK]: { + [pokemonTypes.FIGHTING]: 0.5, + [pokemonTypes.PSYCHIC]: 2, + [pokemonTypes.GHOST]: 2, + [pokemonTypes.DARK]: 0.5, + [pokemonTypes.FAIRY]: 0.5, + }, + [pokemonTypes.STEEL]: { + [pokemonTypes.FIRE]: 0.5, + [pokemonTypes.WATER]: 0.5, + [pokemonTypes.ELECTRIC]: 0.5, + [pokemonTypes.ICE]: 2, + [pokemonTypes.ROCK]: 2, + [pokemonTypes.STEEL]: 0.5, + [pokemonTypes.FAIRY]: 2, + }, + [pokemonTypes.FAIRY]: { + [pokemonTypes.FIRE]: 0.5, + [pokemonTypes.FIGHTING]: 2, + [pokemonTypes.POISON]: 0.5, + [pokemonTypes.DRAGON]: 2, + [pokemonTypes.DARK]: 2, + [pokemonTypes.STEEL]: 0.5, }, }; @@ -291,15 +299,15 @@ const calculateDamage = ( if (!source.battle.isWeatherNegated()) { const { weather } = source.battle; if (weather.weatherId === weatherConditions.SUN) { - if (move.type === types.FIRE) { + if (move.type === pokemonTypes.FIRE) { weatherMult = 1.5; - } else if (move.type === types.WATER) { + } else if (move.type === pokemonTypes.WATER) { weatherMult = 0.5; } } else if (weather.weatherId === weatherConditions.RAIN) { - if (move.type === types.FIRE) { + if (move.type === pokemonTypes.FIRE) { weatherMult = 0.5; - } else if (move.type === types.WATER) { + } else if (move.type === pokemonTypes.WATER) { weatherMult = 1.5; } } @@ -1002,7 +1010,7 @@ const effectConfig = { { power: 40, damageType: damageTypes.PHYSICAL, - type: types.UNKNOWN, + type: pokemonTypes.UNKNOWN, }, inflictedPokemon, inflictedPokemon, @@ -1068,7 +1076,7 @@ const effectConfig = { { power: moveData.power ? moveData.power * 1.5 : 40, damageType: damageTypes.PHYSICAL, - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, }, targetPokemon, sourcePokemon, @@ -1130,7 +1138,7 @@ const effectConfig = { { power: moveData.power ? moveData.power * 1.5 : 40, damageType: damageTypes.SPECIAL, - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, }, targetPokemon, sourcePokemon, @@ -1713,7 +1721,7 @@ const effectConfig = { // get damage multiplier let mult = targetPokemon.getTypeDamageMultiplier( - types.ROCK, + pokemonTypes.ROCK, targetPokemon ); if (mult >= 4) { @@ -2012,7 +2020,7 @@ const effectConfig = { } const { moveId } = args.damageInfo; const moveData = moveConfig[moveId]; - if (moveData.type !== types.ELECTRIC) { + if (moveData.type !== pokemonTypes.ELECTRIC) { return; } @@ -2047,26 +2055,26 @@ const effectConfig = { effectAdd(battle, _source, target) { battle.addToLog(`${target.name} lost its Flying type!`); // if pure flying, change to pure normal - if (target.type1 === types.FLYING && target.type2 === null) { - target.type1 = types.NORMAL; + if (target.type1 === pokemonTypes.FLYING && target.type2 === null) { + target.type1 = pokemonTypes.NORMAL; return { typeSlot: "type1", }; // else } - if (target.type1 === types.FLYING) { + if (target.type1 === pokemonTypes.FLYING) { target.type1 = null; return { typeSlot: "type1", }; } - if (target.type2 === types.FLYING && target.type1 === null) { - target.type2 = types.NORMAL; + if (target.type2 === pokemonTypes.FLYING && target.type1 === null) { + target.type2 = pokemonTypes.NORMAL; return { typeSlot: "type2", }; } - if (target.type2 === types.FLYING) { + if (target.type2 === pokemonTypes.FLYING) { target.type2 = null; return { typeSlot: "type2", @@ -2076,7 +2084,7 @@ const effectConfig = { }, effectRemove(battle, target, args) { battle.addToLog(`${target.name} regained its Flying type!`); - if (args.typeSlot) target[args.typeSlot] = types.FLYING; + if (args.typeSlot) target[args.typeSlot] = pokemonTypes.FLYING; }, }, absorbLight: { @@ -2505,7 +2513,7 @@ const effectConfig = { const moveConfig = { m6: { name: "Pay Day", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 45, accuracy: 100, cooldown: 0, @@ -2519,7 +2527,7 @@ const moveConfig = { }, "m6-1": { name: "Gold Rush", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 35, accuracy: 80, cooldown: 6, @@ -2533,7 +2541,7 @@ const moveConfig = { }, "m7-1": { name: "Red Hawk", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 80, accuracy: 100, cooldown: 3, @@ -2547,7 +2555,7 @@ const moveConfig = { }, m10: { name: "Scratch", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 0, @@ -2561,7 +2569,7 @@ const moveConfig = { }, m14: { name: "Swords Dance", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 4, @@ -2575,7 +2583,7 @@ const moveConfig = { }, m16: { name: "Gust", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 45, accuracy: 100, cooldown: 0, @@ -2589,7 +2597,7 @@ const moveConfig = { }, m17: { name: "Wing Attack", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 55, accuracy: 100, cooldown: 0, @@ -2603,7 +2611,7 @@ const moveConfig = { }, m21: { name: "Slam", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 90, accuracy: 80, cooldown: 2, @@ -2617,7 +2625,7 @@ const moveConfig = { }, m22: { name: "Vine Whip", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 55, accuracy: 100, cooldown: 0, @@ -2631,7 +2639,7 @@ const moveConfig = { }, m23: { name: "Stomp", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 90, accuracy: 100, cooldown: 3, @@ -2645,7 +2653,7 @@ const moveConfig = { }, m30: { name: "Horn Attack", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 75, accuracy: 100, cooldown: 2, @@ -2659,7 +2667,7 @@ const moveConfig = { }, m33: { name: "Tackle", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 0, @@ -2673,7 +2681,7 @@ const moveConfig = { }, m34: { name: "Body Slam", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 95, accuracy: 85, cooldown: 3, @@ -2687,7 +2695,7 @@ const moveConfig = { }, "m34-1": { name: "Groggy Slam", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 75, accuracy: 75, cooldown: 4, @@ -2701,7 +2709,7 @@ const moveConfig = { }, m35: { name: "Wrap", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 20, accuracy: 90, cooldown: 0, @@ -2715,7 +2723,7 @@ const moveConfig = { }, m36: { name: "Take Down", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 120, accuracy: 85, cooldown: 3, @@ -2729,7 +2737,7 @@ const moveConfig = { }, m38: { name: "Double-Edge", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 160, accuracy: 100, cooldown: 4, @@ -2743,7 +2751,7 @@ const moveConfig = { }, m40: { name: "Poison Sting", - type: types.POISON, + type: pokemonTypes.POISON, power: 20, accuracy: 100, cooldown: 0, @@ -2757,7 +2765,7 @@ const moveConfig = { }, m43: { name: "Leer", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 0, @@ -2771,7 +2779,7 @@ const moveConfig = { }, m46: { name: "Roar", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -2785,7 +2793,7 @@ const moveConfig = { }, m47: { name: "Sing", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 55, cooldown: 4, @@ -2799,7 +2807,7 @@ const moveConfig = { }, m50: { name: "Disable", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 2, @@ -2812,7 +2820,7 @@ const moveConfig = { }, m51: { name: "Acid", - type: types.POISON, + type: pokemonTypes.POISON, power: 50, accuracy: 100, cooldown: 0, @@ -2826,7 +2834,7 @@ const moveConfig = { }, m52: { name: "Ember", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 50, accuracy: 100, cooldown: 0, @@ -2840,7 +2848,7 @@ const moveConfig = { }, m53: { name: "Flamethrower", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 70, accuracy: 100, cooldown: 4, @@ -2854,7 +2862,7 @@ const moveConfig = { }, m55: { name: "Water Gun", - type: types.WATER, + type: pokemonTypes.WATER, power: 50, accuracy: 100, cooldown: 0, @@ -2867,7 +2875,7 @@ const moveConfig = { }, m56: { name: "Hydro Pump", - type: types.WATER, + type: pokemonTypes.WATER, power: 100, accuracy: 80, cooldown: 5, @@ -2881,7 +2889,7 @@ const moveConfig = { }, "m56-1": { name: "Hydro Artilery Pump", - type: types.WATER, + type: pokemonTypes.WATER, power: 90, accuracy: 80, cooldown: 6, @@ -2895,7 +2903,7 @@ const moveConfig = { }, "m56-2": { name: "Holy Pump", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 110, accuracy: 60, cooldown: 5, @@ -2909,7 +2917,7 @@ const moveConfig = { }, m57: { name: "Surf", - type: types.WATER, + type: pokemonTypes.WATER, power: 65, accuracy: 90, cooldown: 5, @@ -2923,7 +2931,7 @@ const moveConfig = { }, m58: { name: "Ice Beam", - type: types.ICE, + type: pokemonTypes.ICE, power: 70, accuracy: 90, cooldown: 4, @@ -2937,7 +2945,7 @@ const moveConfig = { }, m59: { name: "Blizzard", - type: types.ICE, + type: pokemonTypes.ICE, power: 70, accuracy: 70, cooldown: 6, @@ -2951,7 +2959,7 @@ const moveConfig = { }, m60: { name: "Psybeam", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 80, accuracy: 100, cooldown: 2, @@ -2965,7 +2973,7 @@ const moveConfig = { }, m63: { name: "Hyper Beam", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 160, accuracy: 90, cooldown: 6, @@ -2979,7 +2987,7 @@ const moveConfig = { }, m64: { name: "Peck", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 45, accuracy: 100, cooldown: 0, @@ -2992,7 +3000,7 @@ const moveConfig = { }, m65: { name: "Drill Peck", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 85, accuracy: 100, cooldown: 2, @@ -3006,7 +3014,7 @@ const moveConfig = { }, m68: { name: "Counter", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: null, accuracy: null, cooldown: 4, @@ -3020,7 +3028,7 @@ const moveConfig = { }, m70: { name: "Strength", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 80, accuracy: 100, cooldown: 2, @@ -3034,7 +3042,7 @@ const moveConfig = { }, m71: { name: "Absorb", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 40, accuracy: 100, cooldown: 0, @@ -3048,7 +3056,7 @@ const moveConfig = { }, m73: { name: "Leech Seed", - type: types.GRASS, + type: pokemonTypes.GRASS, power: null, accuracy: 90, cooldown: 3, @@ -3062,7 +3070,7 @@ const moveConfig = { }, m76: { name: "Solar Beam", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 120, accuracy: 100, cooldown: 5, @@ -3083,7 +3091,7 @@ const moveConfig = { }, m77: { name: "Poison Powder", - type: types.POISON, + type: pokemonTypes.POISON, power: null, accuracy: 70, cooldown: 0, @@ -3097,7 +3105,7 @@ const moveConfig = { }, m79: { name: "Sleep Powder", - type: types.GRASS, + type: pokemonTypes.GRASS, power: null, accuracy: 85, cooldown: 4, @@ -3111,7 +3119,7 @@ const moveConfig = { }, m81: { name: "String Shot", - type: types.BUG, + type: pokemonTypes.BUG, power: null, accuracy: 95, cooldown: 0, @@ -3125,7 +3133,7 @@ const moveConfig = { }, m84: { name: "Thunder Shock", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 50, accuracy: 100, cooldown: 0, @@ -3139,7 +3147,7 @@ const moveConfig = { }, m85: { name: "Thunderbolt", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 95, accuracy: 100, cooldown: 3, @@ -3153,7 +3161,7 @@ const moveConfig = { }, m86: { name: "Thunder Wave", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: null, accuracy: 90, cooldown: 3, @@ -3167,7 +3175,7 @@ const moveConfig = { }, m87: { name: "Thunder", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 95, accuracy: 70, cooldown: 5, @@ -3181,7 +3189,7 @@ const moveConfig = { }, "m87-1": { name: "Thunder Charge", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 110, accuracy: 80, cooldown: 5, @@ -3195,7 +3203,7 @@ const moveConfig = { }, m88: { name: "Rock Throw", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 60, accuracy: 70, cooldown: 0, @@ -3209,7 +3217,7 @@ const moveConfig = { }, m89: { name: "Earthquake", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 100, accuracy: 100, cooldown: 6, @@ -3223,7 +3231,7 @@ const moveConfig = { }, m91: { name: "Dig", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 100, accuracy: 100, cooldown: 3, @@ -3240,7 +3248,7 @@ const moveConfig = { }, m92: { name: "Toxic", - type: types.POISON, + type: pokemonTypes.POISON, power: null, accuracy: 90, cooldown: 2, @@ -3254,7 +3262,7 @@ const moveConfig = { }, m93: { name: "Confusion", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 50, accuracy: 100, cooldown: 0, @@ -3268,7 +3276,7 @@ const moveConfig = { }, m94: { name: "Psychic", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 65, accuracy: 90, cooldown: 4, @@ -3282,7 +3290,7 @@ const moveConfig = { }, m97: { name: "Agility", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -3296,7 +3304,7 @@ const moveConfig = { }, "m97-1": { name: "Ifrit Jambe", - type: types.FIRE, + type: pokemonTypes.FIRE, power: null, accuracy: null, cooldown: 5, @@ -3310,7 +3318,7 @@ const moveConfig = { }, m98: { name: "Quick Attack", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 0, @@ -3324,7 +3332,7 @@ const moveConfig = { }, m100: { name: "Teleport", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 0, @@ -3338,7 +3346,7 @@ const moveConfig = { }, m101: { name: "Night Shade", - type: types.GHOST, + type: pokemonTypes.GHOST, power: null, accuracy: null, cooldown: 2, @@ -3352,7 +3360,7 @@ const moveConfig = { }, m102: { name: "Mimic", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -3366,7 +3374,7 @@ const moveConfig = { }, m103: { name: "Screech", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 85, cooldown: 4, @@ -3380,7 +3388,7 @@ const moveConfig = { }, m105: { name: "Recover", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 2, @@ -3393,7 +3401,7 @@ const moveConfig = { }, m106: { name: "Harden", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -3407,7 +3415,7 @@ const moveConfig = { }, m108: { name: "Smokescreen", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 4, @@ -3421,7 +3429,7 @@ const moveConfig = { }, "m108-1": { name: "Rocket Smokescreen", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: 100, cooldown: 5, @@ -3435,7 +3443,7 @@ const moveConfig = { }, m110: { name: "Withdraw", - type: types.WATER, + type: pokemonTypes.WATER, power: null, accuracy: null, cooldown: 0, @@ -3449,7 +3457,7 @@ const moveConfig = { }, m111: { name: "Defense Curl", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -3463,7 +3471,7 @@ const moveConfig = { }, m113: { name: "Light Screen", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -3477,7 +3485,7 @@ const moveConfig = { }, m115: { name: "Reflect", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -3491,7 +3499,7 @@ const moveConfig = { }, m116: { name: "Focus Energy", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -3505,7 +3513,7 @@ const moveConfig = { }, m118: { name: "Metronome", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -3519,7 +3527,7 @@ const moveConfig = { }, m122: { name: "Lick", - type: types.GHOST, + type: pokemonTypes.GHOST, power: 20, accuracy: 100, cooldown: 0, @@ -3533,7 +3541,7 @@ const moveConfig = { }, m123: { name: "Smog", - type: types.POISON, + type: pokemonTypes.POISON, power: 35, accuracy: 70, cooldown: 0, @@ -3547,7 +3555,7 @@ const moveConfig = { }, m126: { name: "Fire Blast", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 90, accuracy: 85, cooldown: 5, @@ -3561,7 +3569,7 @@ const moveConfig = { }, m127: { name: "Waterfall", - type: types.WATER, + type: pokemonTypes.WATER, power: 100, accuracy: 100, cooldown: 3, @@ -3575,7 +3583,7 @@ const moveConfig = { }, m134: { name: "Kinesis", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 45, accuracy: 80, cooldown: 0, @@ -3589,7 +3597,7 @@ const moveConfig = { }, m135: { name: "Soft-Boiled", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -3603,7 +3611,7 @@ const moveConfig = { }, m136: { name: "High Jump Kick", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 150, accuracy: 70, cooldown: 4, @@ -3617,7 +3625,7 @@ const moveConfig = { }, m137: { name: "Glare", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 2, @@ -3631,7 +3639,7 @@ const moveConfig = { }, "m137-1": { name: "Rocket Glare", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: 80, cooldown: 3, @@ -3644,7 +3652,7 @@ const moveConfig = { }, m143: { name: "Sky Attack", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 200, accuracy: 90, cooldown: 6, @@ -3661,7 +3669,7 @@ const moveConfig = { }, m147: { name: "Spore", - type: types.GRASS, + type: pokemonTypes.GRASS, power: null, accuracy: null, cooldown: 3, @@ -3675,7 +3683,7 @@ const moveConfig = { }, m150: { name: "Splash", - type: types.WATER, + type: pokemonTypes.WATER, power: null, accuracy: null, cooldown: 0, @@ -3688,7 +3696,7 @@ const moveConfig = { }, m152: { name: "Crabhammer", - type: types.WATER, + type: pokemonTypes.WATER, power: 100, accuracy: 90, cooldown: 5, @@ -3702,7 +3710,7 @@ const moveConfig = { }, m153: { name: "Explosion", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 150, accuracy: 100, cooldown: 7, @@ -3716,7 +3724,7 @@ const moveConfig = { }, "m154-1": { name: "Paws of Fury", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 20, accuracy: 100, cooldown: 5, @@ -3730,7 +3738,7 @@ const moveConfig = { }, m156: { name: "Rest", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 4, @@ -3744,7 +3752,7 @@ const moveConfig = { }, m157: { name: "Rock Slide", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 75, accuracy: 90, cooldown: 5, @@ -3758,7 +3766,7 @@ const moveConfig = { }, m162: { name: "Super Fang", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -3772,7 +3780,7 @@ const moveConfig = { }, m167: { name: "Triple Kick", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 33, accuracy: 90, cooldown: 5, @@ -3786,7 +3794,7 @@ const moveConfig = { }, m168: { name: "Thief", - type: types.DARK, + type: pokemonTypes.DARK, power: 70, accuracy: 100, cooldown: 4, @@ -3800,7 +3808,7 @@ const moveConfig = { }, m175: { name: "Flail", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 0, @@ -3814,7 +3822,7 @@ const moveConfig = { }, m177: { name: "Aeroblast", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 100, accuracy: 90, cooldown: 6, @@ -3828,7 +3836,7 @@ const moveConfig = { }, "m177-1": { name: "Shadowblast", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 80, accuracy: 80, cooldown: 6, @@ -3842,7 +3850,7 @@ const moveConfig = { }, m182: { name: "Protect", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 5, @@ -3855,7 +3863,7 @@ const moveConfig = { }, "m182-1": { name: "Stretchy Defense", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 5, @@ -3869,7 +3877,7 @@ const moveConfig = { }, m183: { name: "Mach Punch", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 50, accuracy: 100, cooldown: 0, @@ -3883,7 +3891,7 @@ const moveConfig = { }, "m183-1": { name: "Mach Pistol", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 45, accuracy: 100, cooldown: 0, @@ -3897,7 +3905,7 @@ const moveConfig = { }, m186: { name: "Sweet Kiss", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: null, accuracy: 85, cooldown: 3, @@ -3911,7 +3919,7 @@ const moveConfig = { }, m187: { name: "Belly Drum", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 5, @@ -3925,7 +3933,7 @@ const moveConfig = { }, m188: { name: "Sludge Bomb", - type: types.POISON, + type: pokemonTypes.POISON, power: 65, accuracy: 100, cooldown: 4, @@ -3939,7 +3947,7 @@ const moveConfig = { }, m189: { name: "Mud Slap", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 35, accuracy: 100, cooldown: 0, @@ -3953,7 +3961,7 @@ const moveConfig = { }, m191: { name: "Spikes", - type: types.GROUND, + type: pokemonTypes.GROUND, power: null, accuracy: 100, cooldown: 5, @@ -3967,7 +3975,7 @@ const moveConfig = { }, m192: { name: "Zap Cannon", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 140, accuracy: 50, cooldown: 4, @@ -3981,7 +3989,7 @@ const moveConfig = { }, m194: { name: "Destiny Bond", - type: types.GHOST, + type: pokemonTypes.GHOST, power: null, accuracy: null, cooldown: 5, @@ -3995,7 +4003,7 @@ const moveConfig = { }, m195: { name: "Perish Song", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 7, @@ -4009,7 +4017,7 @@ const moveConfig = { }, m199: { name: "Lock-On", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -4023,7 +4031,7 @@ const moveConfig = { }, m200: { name: "Outrage", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: 130, accuracy: 100, cooldown: 5, @@ -4037,7 +4045,7 @@ const moveConfig = { }, m202: { name: "Giga Drain", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 90, accuracy: 100, cooldown: 3, @@ -4051,7 +4059,7 @@ const moveConfig = { }, m203: { name: "Endure", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 4, @@ -4064,7 +4072,7 @@ const moveConfig = { }, m204: { name: "Charm", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: null, accuracy: 100, cooldown: 2, @@ -4078,7 +4086,7 @@ const moveConfig = { }, m205: { name: "Rollout", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 40, accuracy: 90, cooldown: 0, @@ -4092,7 +4100,7 @@ const moveConfig = { }, m208: { name: "Milk Drink", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -4106,7 +4114,7 @@ const moveConfig = { }, "m208-1": { name: "Shuron Hakke", - type: types.POISON, + type: pokemonTypes.POISON, power: null, accuracy: null, cooldown: 4, @@ -4120,7 +4128,7 @@ const moveConfig = { }, m210: { name: "Fury Cutter", - type: types.BUG, + type: pokemonTypes.BUG, power: 40, accuracy: 90, cooldown: 0, @@ -4134,7 +4142,7 @@ const moveConfig = { }, m212: { name: "Mean Look", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 5, @@ -4148,7 +4156,7 @@ const moveConfig = { }, "m212-1": { name: "Slow Down!", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: null, accuracy: 75, cooldown: 5, @@ -4162,7 +4170,7 @@ const moveConfig = { }, m214: { name: "Sleep Talk", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -4176,7 +4184,7 @@ const moveConfig = { }, m215: { name: "Heal Bell", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 4, @@ -4190,7 +4198,7 @@ const moveConfig = { }, m216: { name: "Return", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 102, accuracy: 100, cooldown: 3, @@ -4204,7 +4212,7 @@ const moveConfig = { }, m219: { name: "Safeguard", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 5, @@ -4218,7 +4226,7 @@ const moveConfig = { }, m221: { name: "Sacred Fire", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 100, accuracy: 90, cooldown: 6, @@ -4232,7 +4240,7 @@ const moveConfig = { }, m223: { name: "Dynamic Punch", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 140, accuracy: 50, cooldown: 4, @@ -4246,7 +4254,7 @@ const moveConfig = { }, m224: { name: "Megahorn", - type: types.BUG, + type: pokemonTypes.BUG, power: 120, accuracy: 85, cooldown: 4, @@ -4260,7 +4268,7 @@ const moveConfig = { }, m226: { name: "Baton Pass", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -4274,7 +4282,7 @@ const moveConfig = { }, "m226-1": { name: "Attack Cuisine", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 4, @@ -4288,7 +4296,7 @@ const moveConfig = { }, m229: { name: "Rapid Spin", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 4, @@ -4302,7 +4310,7 @@ const moveConfig = { }, m231: { name: "Iron Tail", - type: types.STEEL, + type: pokemonTypes.STEEL, power: 100, accuracy: 75, cooldown: 6, @@ -4316,7 +4324,7 @@ const moveConfig = { }, m235: { name: "Synthesis", - type: types.GRASS, + type: pokemonTypes.GRASS, power: null, accuracy: null, cooldown: 2, @@ -4330,7 +4338,7 @@ const moveConfig = { }, m236: { name: "Moonlight", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: null, accuracy: null, cooldown: 2, @@ -4344,7 +4352,7 @@ const moveConfig = { }, m238: { name: "Cross Chop", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 80, accuracy: 80, cooldown: 4, @@ -4358,7 +4366,7 @@ const moveConfig = { }, m239: { name: "Twister", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: 40, accuracy: 100, cooldown: 0, @@ -4372,7 +4380,7 @@ const moveConfig = { }, m240: { name: "Rain Dance", - type: types.WATER, + type: pokemonTypes.WATER, power: null, accuracy: null, cooldown: 5, @@ -4385,7 +4393,7 @@ const moveConfig = { }, m241: { name: "Sunny Day", - type: types.FIRE, + type: pokemonTypes.FIRE, power: null, accuracy: null, cooldown: 5, @@ -4398,7 +4406,7 @@ const moveConfig = { }, m242: { name: "Crunch", - type: types.DARK, + type: pokemonTypes.DARK, power: 100, accuracy: 100, cooldown: 3, @@ -4412,7 +4420,7 @@ const moveConfig = { }, m243: { name: "Mirror Coat", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 4, @@ -4426,7 +4434,7 @@ const moveConfig = { }, m245: { name: "Extreme Speed", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 95, accuracy: 100, cooldown: 3, @@ -4440,7 +4448,7 @@ const moveConfig = { }, m246: { name: "Ancient Power", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 40, accuracy: 100, cooldown: 0, @@ -4454,7 +4462,7 @@ const moveConfig = { }, m247: { name: "Shadow Ball", - type: types.GHOST, + type: pokemonTypes.GHOST, power: 100, accuracy: 100, cooldown: 3, @@ -4468,7 +4476,7 @@ const moveConfig = { }, m248: { name: "Future Sight", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 100, accuracy: 100, cooldown: 5, @@ -4482,7 +4490,7 @@ const moveConfig = { }, m249: { name: "Rock Smash", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 45, accuracy: 80, cooldown: 0, @@ -4496,7 +4504,7 @@ const moveConfig = { }, m252: { name: "Fake Out", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 4, @@ -4510,7 +4518,7 @@ const moveConfig = { }, m257: { name: "Heat Wave", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 80, accuracy: 100, cooldown: 5, @@ -4524,7 +4532,7 @@ const moveConfig = { }, m258: { name: "Hail", - type: types.ICE, + type: pokemonTypes.ICE, power: null, accuracy: null, cooldown: 5, @@ -4537,7 +4545,7 @@ const moveConfig = { }, m262: { name: "Memento", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: 100, cooldown: 5, @@ -4551,7 +4559,7 @@ const moveConfig = { }, m266: { name: "Follow Me", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -4564,7 +4572,7 @@ const moveConfig = { }, m268: { name: "Charge", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: null, accuracy: null, cooldown: 0, @@ -4578,7 +4586,7 @@ const moveConfig = { }, m269: { name: "Taunt", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: 100, cooldown: 4, @@ -4592,7 +4600,7 @@ const moveConfig = { }, "m269-1": { name: "Reverse Taunt", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: 100, cooldown: 4, @@ -4606,7 +4614,7 @@ const moveConfig = { }, m270: { name: "Helping Hand", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -4620,7 +4628,7 @@ const moveConfig = { }, m273: { name: "Wish", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 2, @@ -4634,7 +4642,7 @@ const moveConfig = { }, m276: { name: "Superpower", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 130, accuracy: 100, cooldown: 4, @@ -4648,7 +4656,7 @@ const moveConfig = { }, m281: { name: "Yawn", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 4, @@ -4662,7 +4670,7 @@ const moveConfig = { }, m282: { name: "Knock Off", - type: types.DARK, + type: pokemonTypes.DARK, power: 75, accuracy: 100, cooldown: 3, @@ -4676,7 +4684,7 @@ const moveConfig = { }, m283: { name: "Endeavor", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 100, cooldown: 5, @@ -4690,7 +4698,7 @@ const moveConfig = { }, m284: { name: "Eruption", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 130, accuracy: 100, cooldown: 5, @@ -4704,7 +4712,7 @@ const moveConfig = { }, m288: { name: "Grudge", - type: types.GHOST, + type: pokemonTypes.GHOST, power: null, accuracy: null, cooldown: 4, @@ -4718,7 +4726,7 @@ const moveConfig = { }, m295: { name: "Luster Purge", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 80, accuracy: 80, cooldown: 5, @@ -4732,7 +4740,7 @@ const moveConfig = { }, m296: { name: "Mist Ball", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 55, accuracy: 80, cooldown: 5, @@ -4746,7 +4754,7 @@ const moveConfig = { }, m299: { name: "Blaze Kick", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 85, accuracy: 90, cooldown: 3, @@ -4760,7 +4768,7 @@ const moveConfig = { }, m303: { name: "Slack Off", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 3, @@ -4774,7 +4782,7 @@ const moveConfig = { }, m304: { name: "Hyper Voice", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 85, accuracy: 100, cooldown: 5, @@ -4788,7 +4796,7 @@ const moveConfig = { }, m305: { name: "Poison Fang", - type: types.POISON, + type: pokemonTypes.POISON, power: 50, accuracy: 100, cooldown: 2, @@ -4802,7 +4810,7 @@ const moveConfig = { }, m309: { name: "Meteor Mash", - type: types.STEEL, + type: pokemonTypes.STEEL, power: 90, accuracy: 90, cooldown: 4, @@ -4816,7 +4824,7 @@ const moveConfig = { }, m311: { name: "Weather Ball", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 0, @@ -4830,7 +4838,7 @@ const moveConfig = { }, m316: { name: "Aromatherapy", - type: types.GRASS, + type: pokemonTypes.GRASS, power: null, accuracy: null, cooldown: 5, @@ -4844,7 +4852,7 @@ const moveConfig = { }, m317: { name: "Rock Tomb", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 80, accuracy: 95, cooldown: 3, @@ -4858,7 +4866,7 @@ const moveConfig = { }, "m317-1": { name: "Holy Tomb", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 90, accuracy: 70, cooldown: 3, @@ -4872,7 +4880,7 @@ const moveConfig = { }, m322: { name: "Cosmic Power", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 4, @@ -4886,7 +4894,7 @@ const moveConfig = { }, m325: { name: "Shadow Punch", - type: types.GHOST, + type: pokemonTypes.GHOST, power: 75, accuracy: null, cooldown: 2, @@ -4900,7 +4908,7 @@ const moveConfig = { }, m330: { name: "Muddy Water", - type: types.WATER, + type: pokemonTypes.WATER, power: 50, accuracy: 70, cooldown: 5, @@ -4914,7 +4922,7 @@ const moveConfig = { }, m331: { name: "Bullet Seed", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 30, accuracy: 100, cooldown: 3, @@ -4928,7 +4936,7 @@ const moveConfig = { }, "m331-1": { name: "Ashura Bakkei", - type: types.DARK, + type: pokemonTypes.DARK, power: 15, accuracy: 80, cooldown: 5, @@ -4945,7 +4953,7 @@ const moveConfig = { }, m332: { name: "Aerial Ace", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 80, accuracy: null, cooldown: 2, @@ -4959,7 +4967,7 @@ const moveConfig = { }, m334: { name: "Iron Defense", - type: types.STEEL, + type: pokemonTypes.STEEL, power: null, accuracy: null, cooldown: 4, @@ -4973,7 +4981,7 @@ const moveConfig = { }, "m334-1": { name: "Titanium Defense", - type: types.STEEL, + type: pokemonTypes.STEEL, power: null, accuracy: null, cooldown: 5, @@ -4987,7 +4995,7 @@ const moveConfig = { }, "m334-2": { name: "Genetic Defense", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -5001,7 +5009,7 @@ const moveConfig = { }, m336: { name: "Howl", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 4, @@ -5015,7 +5023,7 @@ const moveConfig = { }, m340: { name: "Bounce", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 105, accuracy: 85, cooldown: 3, @@ -5032,7 +5040,7 @@ const moveConfig = { }, m344: { name: "Volt Tackle", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 80, accuracy: 80, cooldown: 4, @@ -5046,7 +5054,7 @@ const moveConfig = { }, m347: { name: "Calm Mind", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 4, @@ -5060,7 +5068,7 @@ const moveConfig = { }, m348: { name: "Leaf Blade", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 80, accuracy: 100, cooldown: 4, @@ -5074,7 +5082,7 @@ const moveConfig = { }, m349: { name: "Dragon Dance", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: null, accuracy: null, cooldown: 5, @@ -5088,7 +5096,7 @@ const moveConfig = { }, m352: { name: "Water Pulse", - type: types.WATER, + type: pokemonTypes.WATER, power: 60, accuracy: 100, cooldown: 4, @@ -5102,7 +5110,7 @@ const moveConfig = { }, m354: { name: "Psycho Boost", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 20, accuracy: 90, cooldown: 6, @@ -5116,7 +5124,7 @@ const moveConfig = { }, "m354-1": { name: "Psycho Boost - Attack", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 100, accuracy: 90, cooldown: 6, @@ -5130,7 +5138,7 @@ const moveConfig = { }, "m354-2": { name: "Psycho Boost - Defense", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -5144,7 +5152,7 @@ const moveConfig = { }, "m354-3": { name: "Psycho Boost - Speed", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 7, @@ -5158,7 +5166,7 @@ const moveConfig = { }, m355: { name: "Roost", - type: types.FLYING, + type: pokemonTypes.FLYING, power: null, accuracy: null, cooldown: 2, @@ -5172,7 +5180,7 @@ const moveConfig = { }, m359: { name: "Hammer Arm", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 80, accuracy: 90, cooldown: 4, @@ -5186,7 +5194,7 @@ const moveConfig = { }, m361: { name: "Healing Wish", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -5200,7 +5208,7 @@ const moveConfig = { }, m366: { name: "Tailwind", - type: types.FLYING, + type: pokemonTypes.FLYING, power: null, accuracy: null, cooldown: 6, @@ -5214,7 +5222,7 @@ const moveConfig = { }, m369: { name: "U-Turn", - type: types.BUG, + type: pokemonTypes.BUG, power: 70, accuracy: 100, cooldown: 2, @@ -5228,7 +5236,7 @@ const moveConfig = { }, m370: { name: "Close Combat", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 150, accuracy: 100, cooldown: 4, @@ -5242,7 +5250,7 @@ const moveConfig = { }, "m370-1": { name: "Gattling Combat", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 90, accuracy: 80, cooldown: 5, @@ -5256,7 +5264,7 @@ const moveConfig = { }, m387: { name: "Last Resort", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 80, accuracy: 100, cooldown: 3, @@ -5270,7 +5278,7 @@ const moveConfig = { }, m392: { name: "Aqua Ring", - type: types.WATER, + type: pokemonTypes.WATER, power: null, accuracy: null, cooldown: 5, @@ -5284,7 +5292,7 @@ const moveConfig = { }, m394: { name: "Flare Blitz", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 110, accuracy: 100, cooldown: 5, @@ -5298,7 +5306,7 @@ const moveConfig = { }, "m394-1": { name: "Dark Blitz", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 90, accuracy: 100, cooldown: 5, @@ -5312,7 +5320,7 @@ const moveConfig = { }, m396: { name: "Aura Sphere", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 100, accuracy: null, cooldown: 3, @@ -5326,7 +5334,7 @@ const moveConfig = { }, m398: { name: "Poison Jab", - type: types.POISON, + type: pokemonTypes.POISON, power: 100, accuracy: 100, cooldown: 3, @@ -5340,7 +5348,7 @@ const moveConfig = { }, m399: { name: "Dark Pulse", - type: types.DARK, + type: pokemonTypes.DARK, power: 65, accuracy: 100, cooldown: 4, @@ -5354,7 +5362,7 @@ const moveConfig = { }, m402: { name: "Seed Bomb", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 80, accuracy: 80, cooldown: 3, @@ -5368,7 +5376,7 @@ const moveConfig = { }, m403: { name: "Air Slash", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 65, accuracy: 90, cooldown: 4, @@ -5382,7 +5390,7 @@ const moveConfig = { }, m404: { name: "X-Scissor", - type: types.BUG, + type: pokemonTypes.BUG, power: 45, accuracy: 100, cooldown: 4, @@ -5396,7 +5404,7 @@ const moveConfig = { }, m405: { name: "Bug Buzz", - type: types.BUG, + type: pokemonTypes.BUG, power: 70, accuracy: 100, cooldown: 5, @@ -5410,7 +5418,7 @@ const moveConfig = { }, m406: { name: "Dragon Pulse", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: 70, accuracy: 100, cooldown: 4, @@ -5424,7 +5432,7 @@ const moveConfig = { }, m407: { name: "Dragon Rush", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: 120, accuracy: 75, cooldown: 3, @@ -5438,7 +5446,7 @@ const moveConfig = { }, m409: { name: "Drain Punch", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 90, accuracy: 100, cooldown: 3, @@ -5452,7 +5460,7 @@ const moveConfig = { }, m412: { name: "Energy Ball", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 100, accuracy: 100, cooldown: 3, @@ -5466,7 +5474,7 @@ const moveConfig = { }, m413: { name: "Brave Bird", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 110, accuracy: 80, cooldown: 5, @@ -5480,7 +5488,7 @@ const moveConfig = { }, m414: { name: "Earth Power", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 70, accuracy: 100, cooldown: 4, @@ -5494,7 +5502,7 @@ const moveConfig = { }, m416: { name: "Giga Impact", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 160, accuracy: 90, cooldown: 6, @@ -5508,7 +5516,7 @@ const moveConfig = { }, "m416-1": { name: "Galaxy Impact", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 150, accuracy: 90, cooldown: 6, @@ -5522,7 +5530,7 @@ const moveConfig = { }, m417: { name: "Nasty Plot", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: null, cooldown: 4, @@ -5536,7 +5544,7 @@ const moveConfig = { }, "m417-1": { name: "Uncontrollable Joy", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: null, accuracy: null, cooldown: 4, @@ -5550,7 +5558,7 @@ const moveConfig = { }, m418: { name: "Bullet Punch", - type: types.STEEL, + type: pokemonTypes.STEEL, power: 50, accuracy: 100, cooldown: 0, @@ -5564,7 +5572,7 @@ const moveConfig = { }, m420: { name: "Ice Shard", - type: types.ICE, + type: pokemonTypes.ICE, power: 50, accuracy: 100, cooldown: 0, @@ -5578,7 +5586,7 @@ const moveConfig = { }, m424: { name: "Fire Fang", - type: types.FIRE, + type: pokemonTypes.FIRE, power: 80, accuracy: 95, cooldown: 2, @@ -5592,7 +5600,7 @@ const moveConfig = { }, m425: { name: "Shadow Sneak", - type: types.GHOST, + type: pokemonTypes.GHOST, power: 40, accuracy: 100, cooldown: 0, @@ -5606,7 +5614,7 @@ const moveConfig = { }, m428: { name: "Zen Headbutt", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 120, accuracy: 90, cooldown: 4, @@ -5620,7 +5628,7 @@ const moveConfig = { }, m430: { name: "Flash Cannon", - type: types.STEEL, + type: pokemonTypes.STEEL, power: 65, accuracy: 100, cooldown: 4, @@ -5634,7 +5642,7 @@ const moveConfig = { }, m432: { name: "Defog", - type: types.FLYING, + type: pokemonTypes.FLYING, power: null, accuracy: null, cooldown: 4, @@ -5649,7 +5657,7 @@ const moveConfig = { // TODO: possibly rework this move m433: { name: "Trick Room", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -5663,7 +5671,7 @@ const moveConfig = { }, m435: { name: "Discharge", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 65, accuracy: 100, cooldown: 4, @@ -5677,7 +5685,7 @@ const moveConfig = { }, "m435-1": { name: "Volt Discharge", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 70, accuracy: 100, cooldown: 5, @@ -5691,7 +5699,7 @@ const moveConfig = { }, m437: { name: "Leaf Storm", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 100, accuracy: 90, cooldown: 5, @@ -5705,7 +5713,7 @@ const moveConfig = { }, m441: { name: "Gunk Shot", - type: types.POISON, + type: pokemonTypes.POISON, power: 100, accuracy: 80, cooldown: 3, @@ -5719,7 +5727,7 @@ const moveConfig = { }, m444: { name: "Stone Edge", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 80, accuracy: 80, cooldown: 4, @@ -5733,7 +5741,7 @@ const moveConfig = { }, m446: { name: "Stealth Rock", - type: types.ROCK, + type: pokemonTypes.ROCK, power: null, accuracy: null, cooldown: 5, @@ -5747,7 +5755,7 @@ const moveConfig = { }, m450: { name: "Bug Bite", - type: types.BUG, + type: pokemonTypes.BUG, power: 80, accuracy: 100, cooldown: 2, @@ -5761,7 +5769,7 @@ const moveConfig = { }, m453: { name: "Aqua Jet", - type: types.WATER, + type: pokemonTypes.WATER, power: 40, accuracy: 100, cooldown: 0, @@ -5775,7 +5783,7 @@ const moveConfig = { }, m469: { name: "Wide Guard", - type: types.ROCK, + type: pokemonTypes.ROCK, power: null, accuracy: null, cooldown: 5, @@ -5789,7 +5797,7 @@ const moveConfig = { }, m476: { name: "Rage Powder", - type: types.BUG, + type: pokemonTypes.BUG, power: null, accuracy: null, cooldown: 3, @@ -5803,7 +5811,7 @@ const moveConfig = { }, m479: { name: "Smack Down", - type: types.ROCK, + type: pokemonTypes.ROCK, power: 50, accuracy: 100, cooldown: 0, @@ -5817,7 +5825,7 @@ const moveConfig = { }, m482: { name: "Sludge Wave", - type: types.POISON, + type: pokemonTypes.POISON, power: 85, accuracy: 100, cooldown: 5, @@ -5831,7 +5839,7 @@ const moveConfig = { }, m483: { name: "Quiver Dance", - type: types.BUG, + type: pokemonTypes.BUG, power: null, accuracy: null, cooldown: 4, @@ -5845,7 +5853,7 @@ const moveConfig = { }, m484: { name: "Heavy Slam", - type: types.STEEL, + type: pokemonTypes.STEEL, power: null, accuracy: 100, cooldown: 3, @@ -5859,7 +5867,7 @@ const moveConfig = { }, m492: { name: "Foul Play", - type: types.DARK, + type: pokemonTypes.DARK, power: 105, accuracy: 100, cooldown: 3, @@ -5873,7 +5881,7 @@ const moveConfig = { }, m503: { name: "Scald", - type: types.WATER, + type: pokemonTypes.WATER, power: 100, accuracy: 100, cooldown: 3, @@ -5887,7 +5895,7 @@ const moveConfig = { }, m505: { name: "Heal Pulse", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 2, @@ -5901,7 +5909,7 @@ const moveConfig = { }, m506: { name: "Hex", - type: types.GHOST, + type: pokemonTypes.GHOST, power: 45, accuracy: 100, cooldown: 0, @@ -5915,7 +5923,7 @@ const moveConfig = { }, m521: { name: "Volt Switch", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 70, accuracy: 100, cooldown: 2, @@ -5929,7 +5937,7 @@ const moveConfig = { }, m523: { name: "Bulldoze", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 30, accuracy: 100, cooldown: 4, @@ -5943,7 +5951,7 @@ const moveConfig = { }, m525: { name: "Dragon Tail", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: 75, accuracy: 90, cooldown: 2, @@ -5957,7 +5965,7 @@ const moveConfig = { }, m526: { name: "Work Up", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 0, @@ -5971,7 +5979,7 @@ const moveConfig = { }, m527: { name: "Electroweb", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 50, accuracy: 90, cooldown: 5, @@ -5985,7 +5993,7 @@ const moveConfig = { }, m528: { name: "Wild Charge", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 100, accuracy: 100, cooldown: 4, @@ -5999,7 +6007,7 @@ const moveConfig = { }, m529: { name: "Drill Run", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 85, accuracy: 95, cooldown: 2, @@ -6013,7 +6021,7 @@ const moveConfig = { }, m534: { name: "Razor Shell", - type: types.WATER, + type: pokemonTypes.WATER, power: 90, accuracy: 95, cooldown: 4, @@ -6027,7 +6035,7 @@ const moveConfig = { }, m540: { name: "Psystrike", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 95, accuracy: 100, cooldown: 5, @@ -6041,7 +6049,7 @@ const moveConfig = { }, "m540-1": { name: "Divine Departure", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: 85, accuracy: 100, cooldown: 5, @@ -6055,7 +6063,7 @@ const moveConfig = { }, m542: { name: "Hurricane", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 70, accuracy: 70, cooldown: 5, @@ -6069,7 +6077,7 @@ const moveConfig = { }, "m542-1": { name: "Shadow Storm", - type: types.GHOST, + type: pokemonTypes.GHOST, power: 40, accuracy: 80, cooldown: 5, @@ -6083,7 +6091,7 @@ const moveConfig = { }, m564: { name: "Sticky Web", - type: types.BUG, + type: pokemonTypes.BUG, power: null, accuracy: null, cooldown: 5, @@ -6097,7 +6105,7 @@ const moveConfig = { }, m565: { name: "Fell Stinger", - type: types.BUG, + type: pokemonTypes.BUG, power: 100, accuracy: 100, cooldown: 4, @@ -6111,7 +6119,7 @@ const moveConfig = { }, m568: { name: "Noble Roar", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: 70, cooldown: 4, @@ -6125,7 +6133,7 @@ const moveConfig = { }, m573: { name: "Freeze-Dry", - type: types.ICE, + type: pokemonTypes.ICE, power: 90, accuracy: 90, cooldown: 3, @@ -6139,7 +6147,7 @@ const moveConfig = { }, m572: { name: "Petal Blizzard", - type: types.GRASS, + type: pokemonTypes.GRASS, power: 90, accuracy: 100, cooldown: 5, @@ -6153,7 +6161,7 @@ const moveConfig = { }, m574: { name: "Disarming Voice", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: 50, accuracy: null, cooldown: 0, @@ -6167,7 +6175,7 @@ const moveConfig = { }, m583: { name: "Play Rough", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: 95, accuracy: 90, cooldown: 3, @@ -6181,7 +6189,7 @@ const moveConfig = { }, m585: { name: "Moonblast", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: 80, accuracy: 70, cooldown: 5, @@ -6195,7 +6203,7 @@ const moveConfig = { }, m586: { name: "Boomburst", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 130, accuracy: 100, cooldown: 7, @@ -6209,7 +6217,7 @@ const moveConfig = { }, m605: { name: "Dazzling Gleam", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: 75, accuracy: 100, cooldown: 4, @@ -6222,7 +6230,7 @@ const moveConfig = { }, m618: { name: "Origin Pulse", - type: types.WATER, + type: pokemonTypes.WATER, power: 45, accuracy: 85, cooldown: 6, @@ -6236,7 +6244,7 @@ const moveConfig = { }, m619: { name: "Precipice Blades", - type: types.GROUND, + type: pokemonTypes.GROUND, power: 50, accuracy: 85, cooldown: 6, @@ -6250,7 +6258,7 @@ const moveConfig = { }, m620: { name: "Dragon Ascent", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 85, accuracy: 100, cooldown: 7, @@ -6264,7 +6272,7 @@ const moveConfig = { }, "m620-1": { name: "Bolo Breath", - type: types.DRAGON, + type: pokemonTypes.DRAGON, power: 95, accuracy: 100, cooldown: 6, @@ -6278,7 +6286,7 @@ const moveConfig = { }, m668: { name: "Strength Sap", - type: types.GRASS, + type: pokemonTypes.GRASS, power: null, accuracy: 100, cooldown: 3, @@ -6292,7 +6300,7 @@ const moveConfig = { }, m672: { name: "Toxic Thread", - type: types.POISON, + type: pokemonTypes.POISON, power: null, accuracy: 100, cooldown: 5, @@ -6306,7 +6314,7 @@ const moveConfig = { }, m710: { name: "Liquidation", - type: types.WATER, + type: pokemonTypes.WATER, power: 100, accuracy: 100, cooldown: 3, @@ -6320,7 +6328,7 @@ const moveConfig = { }, m719: { name: "10,000,000 Volt Thunderbolt", - type: types.ELECTRIC, + type: pokemonTypes.ELECTRIC, power: 100, accuracy: 100, cooldown: 6, @@ -6334,7 +6342,7 @@ const moveConfig = { }, m742: { name: "Double Iron Bash", - type: types.STEEL, + type: pokemonTypes.STEEL, power: 60, accuracy: 100, cooldown: 6, @@ -6348,7 +6356,7 @@ const moveConfig = { }, m814: { name: "Dual Wingbeat", - type: types.FLYING, + type: pokemonTypes.FLYING, power: 45, accuracy: 90, cooldown: 4, @@ -6362,7 +6370,7 @@ const moveConfig = { }, m876: { name: "Pound", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: 50, accuracy: 100, cooldown: 0, @@ -6375,7 +6383,7 @@ const moveConfig = { }, m20001: { name: "Democracy", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: 100, cooldown: 4, @@ -6389,7 +6397,7 @@ const moveConfig = { }, m20002: { name: "HM Master", - type: types.NORMAL, + type: pokemonTypes.NORMAL, power: null, accuracy: null, cooldown: 5, @@ -6403,7 +6411,7 @@ const moveConfig = { }, m20003: { name: "Rocket Thievery", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: null, cooldown: 7, @@ -6417,7 +6425,7 @@ const moveConfig = { }, m20004: { name: "Time Dilation", - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, power: null, accuracy: null, cooldown: 5, @@ -6431,7 +6439,7 @@ const moveConfig = { }, m20005: { name: "Stoke the Fire!", - type: types.FIRE, + type: pokemonTypes.FIRE, power: null, accuracy: null, cooldown: 4, @@ -6445,7 +6453,7 @@ const moveConfig = { }, m20006: { name: "Destructor Beam", - type: types.DARK, + type: pokemonTypes.DARK, power: 50, accuracy: 100, cooldown: 4, @@ -6459,7 +6467,7 @@ const moveConfig = { }, m20007: { name: "Scammed!", - type: types.DARK, + type: pokemonTypes.DARK, power: null, accuracy: null, cooldown: 6, @@ -6473,7 +6481,7 @@ const moveConfig = { }, m20008: { name: "All-In", - type: types.GHOST, + type: pokemonTypes.GHOST, power: null, accuracy: null, cooldown: 5, @@ -6487,7 +6495,7 @@ const moveConfig = { }, m20009: { name: "King of Hell", - type: types.DARK, + type: pokemonTypes.DARK, power: 50, accuracy: 90, cooldown: 6, @@ -6501,7 +6509,7 @@ const moveConfig = { }, m20010: { name: "Gear Fifth", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: null, accuracy: null, cooldown: 7, @@ -6515,7 +6523,7 @@ const moveConfig = { }, m20011: { name: "Dawn Rocket", - type: types.FAIRY, + type: pokemonTypes.FAIRY, power: 85, accuracy: null, cooldown: 2, @@ -6529,7 +6537,7 @@ const moveConfig = { }, m20012: { name: "Monkey God Gun", - type: types.FIGHTING, + type: pokemonTypes.FIGHTING, power: 80, accuracy: 100, cooldown: 6, @@ -6543,7 +6551,7 @@ const moveConfig = { }, m20013: { name: "ZA WATER!!!", - type: types.WATER, + type: pokemonTypes.WATER, power: null, accuracy: null, cooldown: 6, @@ -7217,7 +7225,10 @@ const moveExecutes = { m73(battle, source, _primaryTarget, allTargets, missedTargets) { for (const target of allTargets) { // check if target is grass type - if (target.type1 === types.GRASS || target.type2 === types.GRASS) { + if ( + target.type1 === pokemonTypes.GRASS || + target.type2 === pokemonTypes.GRASS + ) { battle.addToLog( `${target.name}'s Grass type renders it immune to Leech Seed!` ); @@ -7269,7 +7280,10 @@ const moveExecutes = { m77(battle, source, _primaryTarget, allTargets, missedTargets) { for (const target of allTargets) { // check if target is grass type - if (target.type1 === types.GRASS || target.type2 === types.GRASS) { + if ( + target.type1 === pokemonTypes.GRASS || + target.type2 === pokemonTypes.GRASS + ) { battle.addToLog( `${target.name}'s Grass type renders it immune to spore moves!` ); @@ -7287,7 +7301,10 @@ const moveExecutes = { m79(battle, source, _primaryTarget, allTargets, missedTargets) { for (const target of allTargets) { // check if target is grass type - if (target.type1 === types.GRASS || target.type2 === types.GRASS) { + if ( + target.type1 === pokemonTypes.GRASS || + target.type2 === pokemonTypes.GRASS + ) { battle.addToLog( `${target.name}'s Grass type renders it immune to spore moves!` ); @@ -7471,7 +7488,8 @@ const moveExecutes = { // if not miss or user poison, apply badly poisoned const miss = missedTargets.includes(target); const poisonType = - source.type1 === types.POISON || source.type2 === types.POISON; + source.type1 === pokemonTypes.POISON || + source.type2 === pokemonTypes.POISON; if (!miss || poisonType) { target.applyStatus(statusConditions.BADLY_POISON, source); } @@ -7529,8 +7547,11 @@ const moveExecutes = { target.boostCombatReadiness(source, 80); } // if user not fire, change second type to fire - if (source.type1 !== types.FIRE && source.type2 !== types.FIRE) { - source.type2 = types.FIRE; + if ( + source.type1 !== pokemonTypes.FIRE && + source.type2 !== pokemonTypes.FIRE + ) { + source.type2 = pokemonTypes.FIRE; } }, m98(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -7938,7 +7959,10 @@ const moveExecutes = { m147(battle, source, _primaryTarget, allTargets, missedTargets) { for (const target of allTargets) { // check if target is grass type - if (target.type1 === types.GRASS || target.type2 === types.GRASS) { + if ( + target.type1 === pokemonTypes.GRASS || + target.type2 === pokemonTypes.GRASS + ) { battle.addToLog( `${target.name}'s Grass type renders it immune to spore moves!` ); @@ -9537,13 +9561,13 @@ const moveExecutes = { let { type } = moveData; if (!battle.isWeatherNegated()) { if (battle.weather.weatherId === weatherConditions.SUN) { - type = types.FIRE; + type = pokemonTypes.FIRE; } else if (battle.weather.weatherId === weatherConditions.RAIN) { - type = types.WATER; + type = pokemonTypes.WATER; } else if (battle.weather.weatherId === weatherConditions.SANDSTORM) { - type = types.ROCK; + type = pokemonTypes.ROCK; } else if (battle.weather.weatherId === weatherConditions.HAIL) { - type = types.ICE; + type = pokemonTypes.ICE; } } const miss = missedTargets.includes(target); @@ -10225,8 +10249,14 @@ const moveExecutes = { for (const target of allTargets) { const miss = missedTargets.includes(target); // get max of fire, dark multiplier - const fireMultiplier = source.getTypeDamageMultiplier(types.FIRE, target); - const darkMultiplier = source.getTypeDamageMultiplier(types.DARK, target); + const fireMultiplier = source.getTypeDamageMultiplier( + pokemonTypes.FIRE, + target + ); + const darkMultiplier = source.getTypeDamageMultiplier( + pokemonTypes.DARK, + target + ); const damageToDeal = calculateDamage(moveData, source, target, miss, { type: Math.max(fireMultiplier, darkMultiplier), }); @@ -10934,7 +10964,8 @@ const moveExecutes = { // if not miss and target has flying type, apply loseFlying to target if ( !miss && - (target.type1 === types.FLYING || target.type2 === types.FLYING) + (target.type1 === pokemonTypes.FLYING || + target.type2 === pokemonTypes.FLYING) ) { target.addEffect("loseFlying", 1, source); } @@ -11385,7 +11416,8 @@ const moveExecutes = { for (const target of allTargets) { // see if target is water type const waterType = - target.type1 === types.WATER || target.type2 === types.WATER; + target.type1 === pokemonTypes.WATER || + target.type2 === pokemonTypes.WATER; const miss = !waterType && missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss, { type: waterType ? 2 : null, @@ -12303,7 +12335,7 @@ const abilityConfig = { // if electric, negate damage and heal 25% of max hp const moveData = moveConfig[args.damageInfo.moveId]; - if (moveData.type === types.ELECTRIC) { + if (moveData.type === pokemonTypes.ELECTRIC) { targetPokemon.battle.addToLog( `${targetPokemon.name} is healed by Volt Absorb!` ); @@ -12360,7 +12392,7 @@ const abilityConfig = { // if water, negate damage and heal 25% of max hp const moveData = moveConfig[args.damageInfo.moveId]; - if (moveData.type === types.WATER) { + if (moveData.type === pokemonTypes.WATER) { targetPokemon.battle.addToLog( `${targetPokemon.name} is healed by Water Absorb!` ); @@ -12484,7 +12516,7 @@ const abilityConfig = { // if fire, negate damage and grant atk up, spa up const moveData = moveConfig[args.damageInfo.moveId]; - if (moveData.type === types.FIRE) { + if (moveData.type === pokemonTypes.FIRE) { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Flash Fire was activated by the Fire attack!` ); @@ -12797,7 +12829,7 @@ const abilityConfig = { } const { moveType } = args; - if (moveType !== types.GROUND) { + if (moveType !== pokemonTypes.GROUND) { return; } args.multiplier = 0; @@ -13051,7 +13083,7 @@ const abilityConfig = { // check that enemy used non-ally move, and that move is electric const moveUser = args.user; const moveData = moveConfig[args.moveId]; - if (moveData.type !== types.ELECTRIC) { + if (moveData.type !== pokemonTypes.ELECTRIC) { return; } if (moveUser.teamName === initialArgs.pokemon.teamName) { @@ -13076,7 +13108,7 @@ const abilityConfig = { } const moveData = moveConfig[args.damageInfo.moveId]; - if (moveData.type !== types.ELECTRIC) { + if (moveData.type !== pokemonTypes.ELECTRIC) { return; } @@ -13472,7 +13504,7 @@ const abilityConfig = { { power, damageType: damageTypes.PHYSICAL, - type: types.PSYCHIC, + type: pokemonTypes.PSYCHIC, }, initialArgs.pokemon, targetPokemon, @@ -13533,7 +13565,10 @@ const abilityConfig = { // if move type === fire or ice, reduce damage by 50% const moveData = moveConfig[args.damageInfo.moveId]; - if (moveData.type === types.FIRE || moveData.type === types.ICE) { + if ( + moveData.type === pokemonTypes.FIRE || + moveData.type === pokemonTypes.ICE + ) { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Thick Fat reduces damage taken!` ); @@ -13929,7 +13964,7 @@ const abilityConfig = { // if move type === grass and hp < 1/3, increase damage by 50% const moveData = moveConfig[args.damageInfo.moveId]; if ( - moveData.type === types.GRASS && + moveData.type === pokemonTypes.GRASS && userPokemon.hp < userPokemon.maxHp / 3 ) { userPokemon.battle.addToLog( @@ -13977,7 +14012,7 @@ const abilityConfig = { // if move type === fire and hp < 1/3, increase damage by 50% const moveData = moveConfig[args.damageInfo.moveId]; if ( - moveData.type === types.FIRE && + moveData.type === pokemonTypes.FIRE && userPokemon.hp < userPokemon.maxHp / 3 ) { userPokemon.battle.addToLog( @@ -14025,7 +14060,7 @@ const abilityConfig = { // if move type === water and hp < 1/3, increase damage by 50% const moveData = moveConfig[args.damageInfo.moveId]; if ( - moveData.type === types.WATER && + moveData.type === pokemonTypes.WATER && userPokemon.hp < userPokemon.maxHp / 3 ) { userPokemon.battle.addToLog( @@ -14073,7 +14108,7 @@ const abilityConfig = { // if move type === bug and hp < 1/3, increase damage by 50% const moveData = moveConfig[args.damageInfo.moveId]; if ( - moveData.type === types.BUG && + moveData.type === pokemonTypes.BUG && userPokemon.hp < userPokemon.maxHp / 3 ) { userPokemon.battle.addToLog( @@ -14156,7 +14191,7 @@ const abilityConfig = { // if target is not flying, restrict combat readiness boosts for 2 turns if ( sourcePokemon.getTypeDamageMultiplier( - types.GROUND, + pokemonTypes.GROUND, targetPokemon ) !== 0 ) { @@ -14581,7 +14616,7 @@ const abilityConfig = { // check that enemy used non-ally move, and that move is water const moveUser = args.user; const moveData = moveConfig[args.moveId]; - if (moveData.type !== types.WATER) { + if (moveData.type !== pokemonTypes.WATER) { return; } if (moveUser.teamName === initialArgs.pokemon.teamName) { @@ -14606,7 +14641,7 @@ const abilityConfig = { } const moveData = moveConfig[args.damageInfo.moveId]; - if (moveData.type !== types.WATER) { + if (moveData.type !== pokemonTypes.WATER) { return; } @@ -14845,7 +14880,7 @@ const abilityConfig = { } const { moveId } = args.damageInfo; const moveData = moveConfig[moveId]; - if (!moveData || moveData.type !== types.STEEL) { + if (!moveData || moveData.type !== pokemonTypes.STEEL) { return; } From 237fd5b10303e89ec86e0bdc8871fffcc931e8e3 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 3 Oct 2024 17:31:53 -0700 Subject: [PATCH 13/38] begin battle refactor --- src/{config => battleEngine}/battleConfig.js | 27 ++++++++++++++++---- src/components/selectBattleMoveRow.js | 2 +- src/embeds/battleEmbeds.js | 5 +++- src/embeds/pokemonEmbeds.js | 2 +- src/services/battle.js | 4 +-- src/services/mythic.js | 2 +- src/services/spawn.js | 2 +- src/utils/battleUtils.js | 5 +++- src/utils/pokemonUtils.js | 2 +- 9 files changed, 37 insertions(+), 14 deletions(-) rename src/{config => battleEngine}/battleConfig.js (99%) diff --git a/src/config/battleConfig.js b/src/battleEngine/battleConfig.js similarity index 99% rename from src/config/battleConfig.js rename to src/battleEngine/battleConfig.js index aebbd4c2..f7ad955b 100644 --- a/src/config/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -3,9 +3,11 @@ /* eslint-disable no-shadow */ /* eslint-disable no-use-before-define */ /* eslint-disable no-param-reassign */ -const { types: pokemonTypes } = require("./pokemonConfig"); +const { types: pokemonTypes } = require("../config/pokemonConfig"); +const types = require("../../types"); -const battleEventNames = { +/** @typedef {types.Enum} BattleEventEnum */ +const battleEventNames = Object.freeze({ BATTLE_BEGIN: "battleStart", TURN_END: "turnEnd", TURN_BEGIN: "turnBegin", @@ -30,13 +32,14 @@ const battleEventNames = { CALCULATE_TYPE_MULTIPLIER: "calculateTypeMultiplier", CALCULATE_MISS: "calculateMiss", GET_ELIGIBLE_TARGETS: "getEligibleTargets", -}; +}); -const damageTypes = { +/** @typedef {types.Enum} DamageTypeEnum */ +const damageTypes = Object.freeze({ PHYSICAL: "Physical", SPECIAL: "Special", OTHER: "Other", -}; +}); const moveTiers = { BASIC: "Basic", @@ -244,6 +247,20 @@ const weatherConditions = { HAIL: "Hail", }; +/** + * @typedef {import("../services/battle").Battle} Battle + * @typedef {import("../services/battle").Pokemon} Pokemon + */ + +/** + * + * @param {*} move + * @param {Pokemon} source + * @param {Pokemon} target + * @param {boolean} miss + * @param {*} param4 + * @returns + */ const calculateDamage = ( move, source, diff --git a/src/components/selectBattleMoveRow.js b/src/components/selectBattleMoveRow.js index a8877dc2..b044f81e 100644 --- a/src/components/selectBattleMoveRow.js +++ b/src/components/selectBattleMoveRow.js @@ -8,7 +8,7 @@ */ const { ActionRowBuilder, StringSelectMenuBuilder } = require("discord.js"); const { eventNames } = require("../config/eventConfig"); -const { moveConfig } = require("../config/battleConfig"); +const { moveConfig } = require("../battleEngine/battleConfig"); const { typeConfig } = require("../config/pokemonConfig"); /** diff --git a/src/embeds/battleEmbeds.js b/src/embeds/battleEmbeds.js index 1ecab082..9d362542 100644 --- a/src/embeds/battleEmbeds.js +++ b/src/embeds/battleEmbeds.js @@ -7,7 +7,10 @@ * battleEmbeds.js Handles all embedded instructions for battles. */ const { EmbedBuilder } = require("discord.js"); -const { moveConfig, weatherConditions } = require("../config/battleConfig"); +const { + moveConfig, + weatherConditions, +} = require("../battleEngine/battleConfig"); const { buildPartyString, buildMoveString, diff --git a/src/embeds/pokemonEmbeds.js b/src/embeds/pokemonEmbeds.js index 7a13867c..53d6b47d 100644 --- a/src/embeds/pokemonEmbeds.js +++ b/src/embeds/pokemonEmbeds.js @@ -15,7 +15,7 @@ const { typeConfig, growthRateConfig, } = require("../config/pokemonConfig"); -const { moveConfig, abilityConfig } = require("../config/battleConfig"); +const { moveConfig, abilityConfig } = require("../battleEngine/battleConfig"); const { getWhitespace, getPBar, diff --git a/src/services/battle.js b/src/services/battle.js index 90214293..1bf76e95 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -28,7 +28,7 @@ const { abilityConfig, typeAdvantages, weatherConditions, -} = require("../config/battleConfig"); +} = require("../battleEngine/battleConfig"); const { buildBattleEmbed, buildPveListEmbed, @@ -3507,7 +3507,7 @@ const buildBattleTowerSend = async ({ stateId = null, user = null } = {}) => { module.exports = { Battle, // BattleEventHandler, - // Pokemon, + Pokemon, getStartTurnSend, buildPveSend, buildDungeonSend, diff --git a/src/services/mythic.js b/src/services/mythic.js index 5bc2651b..2515c323 100644 --- a/src/services/mythic.js +++ b/src/services/mythic.js @@ -13,7 +13,7 @@ const { backpackItemConfig, backpackItems, } = require("../config/backpackConfig"); -const { moveConfig } = require("../config/battleConfig"); +const { moveConfig } = require("../battleEngine/battleConfig"); const { collectionNames } = require("../config/databaseConfig"); const { eventNames } = require("../config/eventConfig"); const { getCelebiPool } = require("../config/gachaConfig"); diff --git a/src/services/spawn.js b/src/services/spawn.js index f69a31de..88da40e0 100644 --- a/src/services/spawn.js +++ b/src/services/spawn.js @@ -3,7 +3,7 @@ const { ButtonStyle } = require("discord.js"); const { buildButtonActionRow } = require("../components/buttonActionRow"); const { backpackItems } = require("../config/backpackConfig"); -const { typeAdvantages } = require("../config/battleConfig"); +const { typeAdvantages } = require("../battleEngine/battleConfig"); const { eventNames } = require("../config/eventConfig"); const { pokeballConfig } = require("../config/gachaConfig"); const { diff --git a/src/utils/battleUtils.js b/src/utils/battleUtils.js index 47cded80..82531624 100644 --- a/src/utils/battleUtils.js +++ b/src/utils/battleUtils.js @@ -6,7 +6,10 @@ * * battleUtils.js the lowest level of code for battles used by the battle.js */ -const { effectConfig, statusConditions } = require("../config/battleConfig"); +const { + effectConfig, + statusConditions, +} = require("../battleEngine/battleConfig"); const { difficultyConfig } = require("../config/npcConfig"); const { pokemonConfig, typeConfig } = require("../config/pokemonConfig"); const { getRewardsString, flattenRewards } = require("./trainerUtils"); diff --git a/src/utils/pokemonUtils.js b/src/utils/pokemonUtils.js index 067b71eb..c57fafdb 100644 --- a/src/utils/pokemonUtils.js +++ b/src/utils/pokemonUtils.js @@ -12,7 +12,7 @@ const { growthRateConfig, } = require("../config/pokemonConfig"); const { getPBar, getWhitespace } = require("./utils"); -const { abilityConfig } = require("../config/battleConfig"); +const { abilityConfig } = require("../battleEngine/battleConfig"); const { equipmentConfig, modifierSlotConfig, From d473eb6d2d3efee0335472bdbe673942bfc63183 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 3 Oct 2024 18:44:33 -0700 Subject: [PATCH 14/38] begin new move service --- src/battleEngine/battleConfig.js | 36 +++++------ src/battleEngine/battleTypes.js | 26 ++++++++ src/battleEngine/initialize.js | 12 ++++ src/battleEngine/moveService.js | 104 +++++++++++++++++++++++++++++++ src/battleEngine/moves.js | 59 ++++++++++++++++++ src/battleEngine/utils.js | 0 src/config/pokemonConfig.js | 7 ++- src/index.js | 5 ++ types.js | 5 ++ 9 files changed, 234 insertions(+), 20 deletions(-) create mode 100644 src/battleEngine/battleTypes.js create mode 100644 src/battleEngine/initialize.js create mode 100644 src/battleEngine/moveService.js create mode 100644 src/battleEngine/moves.js create mode 100644 src/battleEngine/utils.js diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index f7ad955b..22e4fc5f 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -41,11 +41,12 @@ const damageTypes = Object.freeze({ OTHER: "Other", }); -const moveTiers = { +/** @typedef {types.Enum} MoveTierEnum */ +const moveTiers = Object.freeze({ BASIC: "Basic", POWER: "Power", ULTIMATE: "Ultimate", -}; +}); // create pokemon type advantage matrix /* @@ -230,6 +231,7 @@ const typeAdvantages = { }; // unqiue status conditions +/** @typedef {types.Enum} StatusConditionEnum */ const statusConditions = { BURN: "Burn", FREEZE: "Freeze", @@ -247,16 +249,11 @@ const weatherConditions = { HAIL: "Hail", }; -/** - * @typedef {import("../services/battle").Battle} Battle - * @typedef {import("../services/battle").Pokemon} Pokemon - */ - /** * * @param {*} move - * @param {Pokemon} source - * @param {Pokemon} target + * @param {BattlePokemon} source + * @param {BattlePokemon} target * @param {boolean} miss * @param {*} param4 * @returns @@ -351,19 +348,22 @@ const calculateDamage = ( return Math.max(damage, 1); }; -const targetTypes = { +/** @typedef {types.Enum} TargetTypeEnum */ +const targetTypes = Object.freeze({ ALLY: "Ally", ENEMY: "Enemy", ANY: "Any", -}; -const targetPositions = { +}); +/** @typedef {types.Enum} TargetPositionEnum */ +const targetPositions = Object.freeze({ SELF: "Self", NON_SELF: "Non-self", ANY: "Any", FRONT: "Front", BACK: "Back", -}; -const targetPatterns = { +}); +/** @typedef {types.Enum} TargetPatternEnum */ +const targetPatterns = Object.freeze({ SINGLE: "Single", ALL: "All", ALL_EXCEPT_SELF: "All-except-self", @@ -372,9 +372,8 @@ const targetPatterns = { RANDOM: "Random", SQUARE: "Square", CROSS: "Cross", -}; +}); -// TODO: is it worth having classes for these? const effectTypes = { BUFF: "Buff", DEBUFF: "Debuff", @@ -2527,7 +2526,8 @@ const effectConfig = { }, }; -const moveConfig = { +/** @typedef{types.Keys} LegacyMoveIdEnum */ +const moveConfig = Object.freeze({ m6: { name: "Pay Day", type: pokemonTypes.NORMAL, @@ -6580,7 +6580,7 @@ const moveConfig = { description: "The user activates its Stand, ZA WATER!!!, forcing time to stop for all other Pokemon. This provides the user a buff granting it 2 extra turns.", }, -}; +}); const moveExecutes = { m6(battle, source, _primaryTarget, allTargets, missedTargets) { diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js new file mode 100644 index 00000000..9c229fcf --- /dev/null +++ b/src/battleEngine/battleTypes.js @@ -0,0 +1,26 @@ +/** + * @typedef {import("./moveService").MoveIdEnum} MoveIdEnum + * + * @typedef {import("../services/battle").Battle} Battle + * @typedef {import("../services/battle").Pokemon} BattlePokemon + * + * @typedef {import("./battleConfig").BattleEventEnum} BattleEventEnum + * @typedef {import("./battleConfig").DamageTypeEnum} DamageTypeEnum + * @typedef {import("./battleConfig").MoveTierEnum} MoveTierEnum + * @typedef {import("./battleConfig").StatusConditionEnum} StatusConditionEnum + * @typedef {import("./battleConfig").TargetTypeEnum} TargetTypeEnum + * @typedef {import("./battleConfig").TargetPositionEnum} TargetPositionEnum + * @typedef {import("./battleConfig").TargetPatternEnum} TargetPatternEnum + * + * @typedef {import("./moves").Move} Move + */ + +/** + * @callback MoveExecute + * @param {Object} param0 + * @param {Battle} param0.battle + * @param {BattlePokemon} param0.source + * @param {BattlePokemon} param0.primaryTarget + * @param {Array} param0.allTargets + * @param {Array?=} param0.missedTargets + */ diff --git a/src/battleEngine/initialize.js b/src/battleEngine/initialize.js new file mode 100644 index 00000000..43b4ae0c --- /dev/null +++ b/src/battleEngine/initialize.js @@ -0,0 +1,12 @@ +const { registerMoves, registerLegacyMoves } = require("./moveService"); +const { moveConfig, moveExecutes } = require("./battleConfig"); +const { movesToRegister } = require("./moves"); + +const initialize = () => { + registerMoves(movesToRegister); + registerLegacyMoves(moveConfig, moveExecutes); +}; + +module.exports = { + initialize, +}; diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js new file mode 100644 index 00000000..fd89c82b --- /dev/null +++ b/src/battleEngine/moveService.js @@ -0,0 +1,104 @@ +const { logger } = require("../log"); +const types = require("../../types"); + +/** + * @typedef{import("./battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum + * @typedef{types.Enum} NewMoveIdEnum + * @typedef{LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum + */ + +const moveIds = Object.freeze({ + TEST_MOVE: "999", + TEST_MOVE2: "998", +}); + +const allMoves = {}; + +/** + * @param {Array} moves + */ +const registerMoves = (moves) => { + moves.forEach((move) => { + allMoves[move.id] = move; + }); +}; + +/** + * @param {Object} moveConfig + * @param {Object} moveExecutes + */ +const registerLegacyMoves = (moveConfig, moveExecutes) => { + Object.entries(moveConfig).forEach(([moveId, move]) => { + const moveExecute = moveExecutes[moveId]; + if (!moveExecute) { + logger.warn( + `Move ${moveId} ${move.name} has no execute function. Proceeding without it.` + ); + return; + } + allMoves[moveId] = { + ...move, + execute: moveExecutes[moveId], + isLegacyMove: true, + }; + }); +}; + +/** + * @param {MoveIdEnum} moveId + * @returns {Move} + */ +const getMove = (moveId) => { + return allMoves[moveId]; +}; + +/** + * @param {Object} param0 + * @param {MoveIdEnum} param0.moveId + * @param {Object} param0.battle + * @param {Object} param0.source + * @param {Object} param0.primaryTarget + * @param {Array} param0.allTargets + * @param {Array?=} param0.missedTargets + * @returns + */ +const executeMove = ({ + moveId, + battle, + source, + primaryTarget, + allTargets, + missedTargets = [], +}) => { + const move = getMove(moveId); + if (!move) { + logger.error(`Move ${moveId} not found.`); + return; + } + + if (!move.isLegacyMove) { + move.execute({ + battle, + source, + primaryTarget, + allTargets, + missedTargets, + }); + } else { + const legacyMove = /** @type{any} */ (move); + legacyMove.execute( + battle, + source, + primaryTarget, + allTargets, + missedTargets + ); + } +}; + +module.exports = { + registerMoves, + registerLegacyMoves, + getMove, + executeMove, +}; diff --git a/src/battleEngine/moves.js b/src/battleEngine/moves.js new file mode 100644 index 00000000..4d2e869e --- /dev/null +++ b/src/battleEngine/moves.js @@ -0,0 +1,59 @@ +/** + * @typedef {import("../config/pokemonConfig").PokemonTypeEnum} PokemonTypeEnum + */ + +class Move { + /** + * @param {Object} param0 + * @param {MoveIdEnum} param0.id + * @param {string} param0.name + * @param {PokemonTypeEnum} param0.type + * @param {number} param0.power + * @param {number?} param0.accuracy + * @param {number} param0.cooldown + * @param {TargetTypeEnum} param0.targetType + * @param {TargetPositionEnum} param0.targetPosition + * @param {TargetPatternEnum} param0.targetPattern + * @param {MoveTierEnum} param0.tier + * @param {DamageTypeEnum} param0.damageType + * @param {string} param0.description + * @param {MoveExecute} param0.execute + */ + constructor({ + id, + name, + type, + power, + accuracy, + cooldown, + targetType, + targetPosition, + targetPattern, + tier, + damageType, + description, + execute, + }) { + this.id = id; + this.name = name; + this.type = type; + this.power = power; + this.accuracy = accuracy; + this.cooldown = cooldown; + this.targetType = targetType; + this.targetPosition = targetPosition; + this.targetPattern = targetPattern; + this.tier = tier; + this.damageType = damageType; + this.description = description; + this.execute = execute; + this.isLegacyMove = false; + } +} + +const movesToRegister = []; + +module.exports = { + Move, + movesToRegister, +}; diff --git a/src/battleEngine/utils.js b/src/battleEngine/utils.js new file mode 100644 index 00000000..e69de29b diff --git a/src/config/pokemonConfig.js b/src/config/pokemonConfig.js index cb3f7aef..5c2cfbdf 100644 --- a/src/config/pokemonConfig.js +++ b/src/config/pokemonConfig.js @@ -1,4 +1,7 @@ -const types = { +const jsTypes = require("../../types"); + +/** @typedef {jsTypes.Enum} PokemonTypeEnum */ +const types = Object.freeze({ NORMAL: 0, FIGHTING: 1, FLYING: 2, @@ -19,7 +22,7 @@ const types = { FAIRY: 17, UNKNOWN: 18, SHADOW: 19, -}; +}); const typeConfig = { 0: { diff --git a/src/index.js b/src/index.js index ad68039e..a78a1494 100644 --- a/src/index.js +++ b/src/index.js @@ -19,11 +19,16 @@ const { poll } = require("./utils/utils"); const { startSpawning, addGuild } = require("./services/spawn"); const { getStateCount } = require("./services/state"); const { cleanupRaids } = require("./services/raid"); +const { + initialize: initializeBattleData, +} = require("./battleEngine/initialize"); console.log(`STAGE: ${process.env.STAGE}`); const FFLAG_ENABLE_SPAWN = process.env.FFLAG_ENABLE_SPAWN === "1"; console.log(`FFLAG_ENABLE_SPAWN: ${FFLAG_ENABLE_SPAWN}`); +initializeBattleData(); + const corsOptions = { origin: true, credentials: true, diff --git a/types.js b/types.js index dd224ec4..d1bee560 100644 --- a/types.js +++ b/types.js @@ -7,6 +7,11 @@ module.exports._typesOnly = true; * @typedef {T[keyof T]} Enum */ +/** + * @template T + * @typedef {keyof T} Keys + */ + /** * @typedef {Object} CompactUser * @property {string} id From 4989fd032a55be534025415c83b1c12a993c71e1 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 3 Oct 2024 19:08:12 -0700 Subject: [PATCH 15/38] use new move service --- src/battleEngine/battleConfig.js | 627 +++++++++++++------------- src/components/selectBattleMoveRow.js | 4 +- src/embeds/battleEmbeds.js | 8 +- src/embeds/pokemonEmbeds.js | 7 +- src/services/battle.js | 46 +- 5 files changed, 356 insertions(+), 336 deletions(-) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 22e4fc5f..d50a8cbd 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -5,6 +5,7 @@ /* eslint-disable no-param-reassign */ const { types: pokemonTypes } = require("../config/pokemonConfig"); const types = require("../../types"); +const { getMove, executeMove } = require("./moveService"); /** @typedef {types.Enum} BattleEventEnum */ const battleEventNames = Object.freeze({ @@ -1074,7 +1075,7 @@ const effectConfig = { if (args.damageInfo.type !== "move") { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (!moveData || moveData.damageType !== damageTypes.PHYSICAL) { return; } @@ -1136,7 +1137,7 @@ const effectConfig = { if (args.damageInfo.type !== "move") { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (!moveData || moveData.damageType !== damageTypes.SPECIAL) { return; } @@ -1342,7 +1343,7 @@ const effectConfig = { // get move data const { moveId } = args.damageInfo; // eslint-disable-next-line no-use-before-define - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData) { return; } @@ -1358,13 +1359,14 @@ const effectConfig = { invulnPokemon.battle.addToLog( `${invulnPokemon.name} reflected the move back at ${sourcePokemon.name}!` ); - moveExecutes[moveId]( - invulnPokemon.battle, - invulnPokemon, - sourcePokemon, - [sourcePokemon], - [] - ); + executeMove({ + moveId: moveId, + battle: invulnPokemon.battle, + source: invulnPokemon, + primaryTarget: sourcePokemon, + allTargets: [sourcePokemon], + missedTargets: [], + }); } }, }; @@ -1396,7 +1398,7 @@ const effectConfig = { if (args.damageInfo.type !== "move") { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); let multiplier = 0.7; if (moveData.targetPattern === targetPatterns.SINGLE) { multiplier = 0.4; @@ -1857,7 +1859,7 @@ const effectConfig = { const disabledMoves = []; // disable ultimate moves for (const moveId of Object.keys(target.moveIds)) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.tier === moveTiers.ULTIMATE) { target.disableMove(moveId, source); disabledMoves.push(moveData.name); @@ -1878,7 +1880,7 @@ const effectConfig = { battle.addToLog(`${target.name}'s ultimate moves are now available!`); // enable ultimate moves for (const moveId of Object.keys(target.moveIds)) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.tier === moveTiers.ULTIMATE) { target.enableMove(moveId, args.source); } @@ -1895,7 +1897,7 @@ const effectConfig = { `${target.name} is silenced and can only use basic moves!` ); for (const moveId in target.moveIds) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.tier !== moveTiers.BASIC) { target.disableMove(moveId, source); } @@ -1907,7 +1909,7 @@ const effectConfig = { effectRemove(battle, target, args) { battle.addToLog(`${target.name} is no longer silenced!`); for (const moveId in target.moveIds) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.tier !== moveTiers.BASIC) { target.enableMove(moveId, args.source); } @@ -1923,7 +1925,7 @@ const effectConfig = { battle.addToLog(`${target.name} was taunted!`); // disable moves with no power for (const moveId of Object.keys(target.moveIds)) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData.power) { target.disableMove(moveId, source); } @@ -1936,7 +1938,7 @@ const effectConfig = { battle.addToLog(`${target.name} is no longer taunted!`); // enable moves with no power for (const moveId of Object.keys(target.moveIds)) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData.power) { target.enableMove(moveId, args.source); } @@ -1952,7 +1954,7 @@ const effectConfig = { battle.addToLog(`${target.name} was reverse-taunted!`); // disable moves with power for (const moveId of Object.keys(target.moveIds)) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.power) { target.disableMove(moveId, source); } @@ -1965,7 +1967,7 @@ const effectConfig = { battle.addToLog(`${target.name} is no longer reverse-taunted!`); // enable moves with no power for (const moveId of Object.keys(target.moveIds)) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.power) { target.enableMove(moveId, args.source); } @@ -1991,7 +1993,7 @@ const effectConfig = { // check that enemy used non-ally move const moveUser = args.user; - const moveData = moveConfig[args.moveId]; + const moveData = getMove(args.moveId); if (moveUser.teamName === initialArgs.pokemon.teamName) { return; } @@ -2035,7 +2037,7 @@ const effectConfig = { return; } const { moveId } = args.damageInfo; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (moveData.type !== pokemonTypes.ELECTRIC) { return; } @@ -2455,7 +2457,7 @@ const effectConfig = { dispellable: false, effectAdd(battle, _source, target, initialArgs) { const mimicMoveId = initialArgs.moveId; - const moveData = moveConfig[mimicMoveId]; + const moveData = getMove(mimicMoveId); if (!mimicMoveId) return; if (!moveData) return; // if target already knows move, ignore @@ -2518,7 +2520,7 @@ const effectConfig = { // if knows Gear Fifth, set cooldown to max const gearFifthMoveId = "m20010"; - const moveData = moveConfig[gearFifthMoveId]; + const moveData = getMove(gearFifthMoveId); if (target.moveIds[gearFifthMoveId]) { target.moveIds[gearFifthMoveId].cooldown = moveData.cooldown; } @@ -6585,7 +6587,7 @@ const moveConfig = Object.freeze({ const moveExecutes = { m6(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m6"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const damageToDeal = calculateDamage( moveData, @@ -6619,7 +6621,7 @@ const moveExecutes = { }, "m6-1": function (battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m6-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6658,7 +6660,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m7-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // if not miss, attempt to remove defUp, greaterDefUp, spdUp, greaterSpdUp @@ -6687,7 +6689,7 @@ const moveExecutes = { }, m10(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m10"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const damageToDeal = calculateDamage( moveData, @@ -6711,7 +6713,7 @@ const moveExecutes = { }, m16(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m16"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // if sprungUp, have 2x power const sprungUp = target.effectIds.sprungUp !== undefined; @@ -6727,7 +6729,7 @@ const moveExecutes = { }, m17(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m17"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const damageToDeal = calculateDamage( moveData, @@ -6743,7 +6745,7 @@ const moveExecutes = { }, m21(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m21"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6755,7 +6757,7 @@ const moveExecutes = { }, m22(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m22"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6767,7 +6769,7 @@ const moveExecutes = { }, m23(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m23"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6784,7 +6786,7 @@ const moveExecutes = { }, m30(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m30"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6796,7 +6798,7 @@ const moveExecutes = { }, m33(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m33"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6808,7 +6810,7 @@ const moveExecutes = { }, m34(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m34"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6831,7 +6833,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m34-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6848,7 +6850,7 @@ const moveExecutes = { }, m35(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m35"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6867,7 +6869,7 @@ const moveExecutes = { }, m36(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m36"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -6886,7 +6888,7 @@ const moveExecutes = { }, m38(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m38"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const damageToDeal = calculateDamage( @@ -6909,7 +6911,7 @@ const moveExecutes = { }, m40(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m40"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6964,7 +6966,7 @@ const moveExecutes = { }, m51(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m51"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6981,7 +6983,7 @@ const moveExecutes = { }, m52(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m52"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -6998,7 +7000,7 @@ const moveExecutes = { }, m53(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m53"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7015,7 +7017,7 @@ const moveExecutes = { }, m55(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m55"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7027,7 +7029,7 @@ const moveExecutes = { }, m56(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m56"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7050,7 +7052,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m56-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const multiplier = allTargets.length === 1 ? 1.5 : 1; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -7074,7 +7076,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m56-2"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7090,7 +7092,7 @@ const moveExecutes = { }, m57(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m57"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // deal less damage if more targets const numTargets = allTargets.length; const power = moveData.power - (numTargets - 1) * 5; @@ -7107,7 +7109,7 @@ const moveExecutes = { }, m58(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m58"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7124,7 +7126,7 @@ const moveExecutes = { }, m59(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m59"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7141,7 +7143,7 @@ const moveExecutes = { }, m60(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m60"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7158,7 +7160,7 @@ const moveExecutes = { }, m63(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m63"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7177,7 +7179,7 @@ const moveExecutes = { }, m64(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m64"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7189,7 +7191,7 @@ const moveExecutes = { }, m65(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m65"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7207,7 +7209,7 @@ const moveExecutes = { }, m70(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m70"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // 5% of atk true damage @@ -7222,7 +7224,7 @@ const moveExecutes = { }, m71(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m71"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -7263,7 +7265,7 @@ const moveExecutes = { }, m76(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m76"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if pokemon doesnt have "abosrb light" buff, apply it if ( source.effectIds.absorbLight === undefined && @@ -7351,7 +7353,7 @@ const moveExecutes = { }, m84(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m84"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7368,7 +7370,7 @@ const moveExecutes = { }, m85(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m85"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7398,7 +7400,7 @@ const moveExecutes = { }, m87(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m87"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // if sprungUp, have 2x power const sprungUp = target.effectIds.sprungUp !== undefined; @@ -7425,7 +7427,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m87-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // if sprungUp, have 2x power const sprungUp = target.effectIds.sprungUp !== undefined; @@ -7444,7 +7446,7 @@ const moveExecutes = { }, m88(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m88"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7456,7 +7458,7 @@ const moveExecutes = { }, m89(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m89"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // deal less damage if more targets const numTargets = allTargets.length; const power = moveData.power - (numTargets - 1) * 7; @@ -7481,7 +7483,7 @@ const moveExecutes = { m91(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m91"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if pokemon doesnt have "burrowed" buff, apply it if (source.effectIds.burrowed === undefined) { source.addEffect("burrowed", 1, source); @@ -7514,7 +7516,7 @@ const moveExecutes = { }, m93(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m93"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7531,7 +7533,7 @@ const moveExecutes = { }, m94(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m94"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7618,7 +7620,7 @@ const moveExecutes = { for (const target of allTargets) { // attempt to get targets ultimate move const ultimateMoveIds = Object.keys(target.moveIds).filter( - (moveId) => moveConfig[moveId].tier === moveTiers.ULTIMATE + (moveId) => getMove(moveId).tier === moveTiers.ULTIMATE ); if (ultimateMoveIds.length === 0) { battle.addToLog(`But if failed!`); @@ -7747,11 +7749,11 @@ const moveExecutes = { m118(battle, source) { // get random basic moves const basicMoves = Object.keys(moveConfig).filter( - (moveId) => moveConfig[moveId].tier === moveTiers.BASIC + (moveId) => getMove(moveId).tier === moveTiers.BASIC ); const randomMoveId = basicMoves[Math.floor(Math.random() * basicMoves.length)]; - const randomMoveData = moveConfig[randomMoveId]; + const randomMoveData = getMove(randomMoveId); battle.addToLog(`${source.name} used ${randomMoveData.name}!`); // get eligible targets @@ -7764,17 +7766,18 @@ const moveExecutes = { // get random target & use move const randomTarget = eligibleTargets[Math.floor(Math.random() * eligibleTargets.length)]; - moveExecutes[randomMoveId]( + executeMove({ + moveId: randomMoveId, battle, source, - randomTarget, - [randomTarget], - [] - ); + primaryTarget: randomTarget, + allTargets: [randomTarget], + missedTargets: [], + }); }, m122(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m122"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7791,7 +7794,7 @@ const moveExecutes = { }, m123(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m123"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7808,7 +7811,7 @@ const moveExecutes = { }, m126(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m126"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7825,7 +7828,7 @@ const moveExecutes = { }, m127(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m127"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7844,7 +7847,7 @@ const moveExecutes = { }, m134(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m134"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7882,7 +7885,7 @@ const moveExecutes = { }, m136(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m136"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7931,7 +7934,7 @@ const moveExecutes = { }, m143(battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m143"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let defeatedEnemy = false; // two turn move logic if (source.effectIds.skyCharge === undefined) { @@ -8008,7 +8011,7 @@ const moveExecutes = { }, m152(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m152"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // 5% of atk true damage @@ -8023,7 +8026,7 @@ const moveExecutes = { }, m153(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m153"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // power = base power + percent hp * 100 const power = moveData.power + (source.hp / source.maxHp) * 100; for (const target of allTargets) { @@ -8072,7 +8075,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m154-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8101,7 +8104,7 @@ const moveExecutes = { }, m156(battle, source, _primaryTarget, allTargets) { const moveId = "m156"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if source asleep or max HP, fail if ( @@ -8136,7 +8139,7 @@ const moveExecutes = { }, m157(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m157"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8164,7 +8167,7 @@ const moveExecutes = { }, m167(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m167"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); let damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8186,7 +8189,7 @@ const moveExecutes = { }, m168(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m168"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8229,7 +8232,7 @@ const moveExecutes = { }, m175(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m175"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // calculate power (lower hp = higher power) const n = Math.floor((source.hp / source.maxHp) * 100); @@ -8257,7 +8260,7 @@ const moveExecutes = { }, m177(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m177"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // filter out allTargets => just the primary target and up to 2 random other targets let damagedTargets = []; if (allTargets.length > 3) { @@ -8299,7 +8302,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m177-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); let type = source.getTypeDamageMultiplier(moveData.type, target); @@ -8330,7 +8333,7 @@ const moveExecutes = { }, m183(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m183"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8351,7 +8354,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m183-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8393,7 +8396,7 @@ const moveExecutes = { }, m188(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m188"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8410,7 +8413,7 @@ const moveExecutes = { }, m189(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m189"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8439,7 +8442,7 @@ const moveExecutes = { }, m192(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m192"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8493,7 +8496,7 @@ const moveExecutes = { }, m200(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m200"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if source doesn't have outrage, apply it if (source.effectIds.outrage === undefined) { source.addEffect("outrage", 2, source); @@ -8520,7 +8523,7 @@ const moveExecutes = { }, m202(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m202"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -8554,7 +8557,7 @@ const moveExecutes = { }, m205(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m205"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let targetHit = false; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -8624,7 +8627,7 @@ const moveExecutes = { }, m210(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m210"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let targetHit = false; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -8693,7 +8696,7 @@ const moveExecutes = { } const randomMoveId = sleepTalkMoves[Math.floor(Math.random() * sleepTalkMoves.length)]; - const randomMoveData = moveConfig[randomMoveId]; + const randomMoveData = getMove(randomMoveId); battle.addToLog(`${source.name} used ${randomMoveData.name}!`); // get valid targets @@ -8718,7 +8721,14 @@ const moveExecutes = { ); // use move against target battle.addToLog(`${randomMoveData.name} hit ${randomTarget.name}!`); - moveExecutes[randomMoveId](battle, source, randomTarget, targets, []); + executeMove({ + moveId: randomMoveId, + battle, + source, + primaryTarget: randomTarget, + allTargets: targets, + missedTargets: [], + }); // roll wakeup // sleep wakeup chance: 0 turns: 0%, 1 turn: 66%, 2 turns: 100% @@ -8747,7 +8757,7 @@ const moveExecutes = { }, m216(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m216"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // 20 less bp if source is damaged @@ -8768,7 +8778,7 @@ const moveExecutes = { }, m221(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m221"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // filter out allTargets => just the primary target and up to 2 random other targets let damagedTargets = []; if (allTargets.length > 3) { @@ -8801,7 +8811,7 @@ const moveExecutes = { }, m223(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m223"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8827,7 +8837,7 @@ const moveExecutes = { }, m224(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m224"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8871,7 +8881,7 @@ const moveExecutes = { }, m229(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m229"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8904,7 +8914,7 @@ const moveExecutes = { }, m231(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m231"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // check if user def is higher, if so apply effects before damage @@ -8974,7 +8984,7 @@ const moveExecutes = { }, m238(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m238"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -8991,7 +9001,7 @@ const moveExecutes = { }, m239(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m239"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9016,7 +9026,7 @@ const moveExecutes = { }, m242(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m242"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9033,7 +9043,7 @@ const moveExecutes = { }, m243(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m243"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply mirror coat 2 turn target.addEffect("mirrorCoat", 2, source); @@ -9041,7 +9051,7 @@ const moveExecutes = { }, m245(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m245"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9056,7 +9066,7 @@ const moveExecutes = { }, m246(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m246"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9106,7 +9116,7 @@ const moveExecutes = { }, m247(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m247"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9123,7 +9133,7 @@ const moveExecutes = { }, m248(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m248"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); if (miss) { @@ -9136,7 +9146,7 @@ const moveExecutes = { }, m249(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m249"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9153,7 +9163,7 @@ const moveExecutes = { }, m252(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m252"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9173,7 +9183,7 @@ const moveExecutes = { }, m257(battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m257"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get only target row const targetParty = battle.parties[primaryTarget.teamName]; @@ -9202,14 +9212,14 @@ const moveExecutes = { }, m258(battle, source, _primaryTarget, _allTargets, _missedTargets) { const moveId = "m258"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // hail weather battle.createWeather(weatherConditions.HAIL, source); }, m266(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m266"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply redirect for 1 turn target.addEffect("redirect", 1, source); @@ -9217,7 +9227,7 @@ const moveExecutes = { }, m262(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m262"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); if (!miss) { @@ -9231,7 +9241,7 @@ const moveExecutes = { }, m268(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m266"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply charge for 1 turn target.addEffect("charge", 1, source); @@ -9241,7 +9251,7 @@ const moveExecutes = { }, m269(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m269"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); if (miss) { @@ -9260,7 +9270,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m269-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); if (miss) { @@ -9273,7 +9283,7 @@ const moveExecutes = { }, m270(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m270"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply atk up and spa up 1 turn target.addEffect("atkUp", 1, source); @@ -9282,7 +9292,7 @@ const moveExecutes = { }, m273(battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m273"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // give delayed heal battle.addToLog(`${target.name} recieved ${source.name}'s wish!`); @@ -9293,7 +9303,7 @@ const moveExecutes = { }, m276(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m276"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9308,7 +9318,7 @@ const moveExecutes = { }, m281(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m281"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // if not miss apply yawn debuff const miss = missedTargets.includes(target); @@ -9324,7 +9334,7 @@ const moveExecutes = { }, m282(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m282"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -9356,7 +9366,7 @@ const moveExecutes = { }, m283(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m283"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); if (miss) { @@ -9378,7 +9388,7 @@ const moveExecutes = { }, m284(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m284"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // power = power * proportion source HP @@ -9394,7 +9404,7 @@ const moveExecutes = { }, m288(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m288"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply grudge for 1 turn target.addEffect("grudge", 1, source); @@ -9402,7 +9412,7 @@ const moveExecutes = { }, m295(battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m295"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // check if ally has mist ball on cooldown const allyPokemons = Object.values(battle.allPokemon).filter( @@ -9449,7 +9459,7 @@ const moveExecutes = { }, m296(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m296"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // check if ally has luster purge on cooldown const allyPokemons = Object.values(battle.allPokemon).filter( @@ -9487,7 +9497,7 @@ const moveExecutes = { }, m299(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m299"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9504,7 +9514,7 @@ const moveExecutes = { }, m303(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m303"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const _target of allTargets) { // heal 50% const healAmount = Math.floor(source.maxHp * 0.5); @@ -9519,7 +9529,7 @@ const moveExecutes = { }, m304(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m304"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9531,7 +9541,7 @@ const moveExecutes = { }, m305(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m305"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9548,7 +9558,7 @@ const moveExecutes = { }, m309(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m309"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // raise user atk for 2 turn source.addEffect("atkUp", 2, source); @@ -9573,7 +9583,7 @@ const moveExecutes = { }, m311(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m311"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { let { type } = moveData; if (!battle.isWeatherNegated()) { @@ -9604,7 +9614,7 @@ const moveExecutes = { }, m316(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m316"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // remove status conditions target.removeStatus(); @@ -9628,7 +9638,7 @@ const moveExecutes = { }, m317(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m317"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9651,7 +9661,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m317-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9678,7 +9688,7 @@ const moveExecutes = { }, m325(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m325"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // ignore miss const damageToDeal = calculateDamage(moveData, source, target, false); @@ -9690,7 +9700,7 @@ const moveExecutes = { }, m330(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m330"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9707,7 +9717,7 @@ const moveExecutes = { }, m331(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m331"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // loop 5 times, hitting random non-fainted target for (let i = 0; i < 5; i += 1) { allTargets = allTargets.filter((target) => !target.isFainted); @@ -9732,7 +9742,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m331-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if pokemon doesnt have "projectingSpirit" buff, apply it if (source.effectIds.projectingSpirit === undefined) { source.addEffect("projectingSpirit", 1, source); @@ -9763,7 +9773,7 @@ const moveExecutes = { }, m332(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m332"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // ignore miss const damageToDeal = calculateDamage(moveData, source, target, false); @@ -9791,7 +9801,7 @@ const moveExecutes = { allTargets, _missedTargets ) { - const moveData = moveConfig["m334-1"]; + const moveData = getMove("m334-1"); // put primary target at front of allTargets if (allTargets.includes(primaryTarget)) { allTargets = allTargets.filter((target) => target !== primaryTarget); @@ -9821,7 +9831,7 @@ const moveExecutes = { allTargets, _missedTargets ) { - const moveData = moveConfig["m334-2"]; + const moveData = getMove("m334-2"); for (const target of allTargets) { // sharply raise def & special def target.addEffect("greaterDefUp", 2, source); @@ -9832,7 +9842,7 @@ const moveExecutes = { }, m336(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m336"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // grant atk up 1 turn target.addEffect("atkUp", 1, source); @@ -9843,7 +9853,7 @@ const moveExecutes = { }, m340(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m340"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if pokemon doesnt have "sprungUp" buff, apply it if (source.effectIds.sprungUp === undefined) { source.addEffect("sprungUp", 1, source); @@ -9869,7 +9879,7 @@ const moveExecutes = { }, m344(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m344"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -9893,7 +9903,7 @@ const moveExecutes = { }, m347(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m347"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // spa, spd up 3 turns target.addEffect("spaUp", 3, source); @@ -9905,7 +9915,7 @@ const moveExecutes = { }, m348(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m348"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // 5% atk true damage @@ -9936,7 +9946,7 @@ const moveExecutes = { }, m352(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m352"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -9953,7 +9963,7 @@ const moveExecutes = { }, m354(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m354"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const allAllies = battle.parties[source.teamName].pokemons.filter( (p) => p && !p.isFainted @@ -10010,7 +10020,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m354-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const useAtk = source.atk > source.spa; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -10037,7 +10047,7 @@ const moveExecutes = { allTargets, _missedTargets ) { - const moveData = moveConfig["m354-2"]; + const moveData = getMove("m354-2"); for (const target of allTargets) { // get 25% def, spd as shield target.addEffect("shield", 3, source, { @@ -10053,7 +10063,7 @@ const moveExecutes = { _missedTargets ) { const moveId = "m354-3"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply extra turn buff for 1 (2) turn target.addEffect("extraTurn", 1, source); @@ -10077,7 +10087,7 @@ const moveExecutes = { }, m359(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m359"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10092,7 +10102,7 @@ const moveExecutes = { }, m361(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m361"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // fully heal source.giveHeal(target.maxHp, target, { @@ -10107,7 +10117,7 @@ const moveExecutes = { }, m366(battle, source, primaryTarget, allTargets, _missedTargets) { const moveId = "m366"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get only target row const targetParty = battle.parties[primaryTarget.teamName]; @@ -10129,7 +10139,7 @@ const moveExecutes = { }, m369(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m369"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const damageToDeal = calculateDamage( moveData, @@ -10157,7 +10167,7 @@ const moveExecutes = { }, m370(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m370"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10179,7 +10189,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m370-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10195,7 +10205,7 @@ const moveExecutes = { }, m387(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m387"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // calculate power = base power * moves on cooldown const numCooldownMoves = Object.values(source.moveIds).filter( @@ -10214,7 +10224,7 @@ const moveExecutes = { }, m392(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m392"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // add regeneration and def up target.addEffect("regeneration", 3, source, { @@ -10225,7 +10235,7 @@ const moveExecutes = { }, m394(battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m394"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -10261,7 +10271,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m394-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -10292,7 +10302,7 @@ const moveExecutes = { }, m396(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m398"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // ignore miss const damageToDeal = calculateDamage(moveData, source, target, false); @@ -10304,7 +10314,7 @@ const moveExecutes = { }, m398(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m398"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10321,7 +10331,7 @@ const moveExecutes = { }, m399(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m399"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10338,7 +10348,7 @@ const moveExecutes = { }, m402(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m402"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10355,7 +10365,7 @@ const moveExecutes = { }, m403(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m403"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10372,7 +10382,7 @@ const moveExecutes = { }, m404(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m404"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10397,7 +10407,7 @@ const moveExecutes = { }, m405(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m405"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // if not miss, 80% to spd down @@ -10414,7 +10424,7 @@ const moveExecutes = { }, m406(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m406"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10426,7 +10436,7 @@ const moveExecutes = { }, m407(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m407"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10443,7 +10453,7 @@ const moveExecutes = { }, m409(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m409"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -10462,7 +10472,7 @@ const moveExecutes = { }, m412(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m412"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10479,7 +10489,7 @@ const moveExecutes = { }, m413(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m413"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -10499,7 +10509,7 @@ const moveExecutes = { }, m414(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m414"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10516,7 +10526,7 @@ const moveExecutes = { }, m416(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m416"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10541,7 +10551,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m416-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10560,7 +10570,7 @@ const moveExecutes = { }, m417(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m417"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // sharply raise spatk target.addEffect("greaterSpaUp", 3, source); @@ -10577,7 +10587,7 @@ const moveExecutes = { _missedTargets ) { const moveId = "m417-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // sharply raise spatk, eva target.addEffect("greaterSpaUp", 2, source); @@ -10589,7 +10599,7 @@ const moveExecutes = { }, m418(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m418"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10604,7 +10614,7 @@ const moveExecutes = { }, m420(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m420"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10619,7 +10629,7 @@ const moveExecutes = { }, m424(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m424"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10640,7 +10650,7 @@ const moveExecutes = { }, m425(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m425"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10655,7 +10665,7 @@ const moveExecutes = { }, m428(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m428"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10672,7 +10682,7 @@ const moveExecutes = { }, m430(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m430"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10689,7 +10699,7 @@ const moveExecutes = { }, m432(battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m432"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const targets = Object.values(battle.allPokemon).filter((p) => battle.isPokemonHittable(p, moveId) ); @@ -10761,7 +10771,7 @@ const moveExecutes = { }, m435(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m435"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10784,7 +10794,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m435-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10824,7 +10834,7 @@ const moveExecutes = { }, m437(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m437"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10839,7 +10849,7 @@ const moveExecutes = { }, m441(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m441"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); let damageToDeal = calculateDamage( @@ -10870,7 +10880,7 @@ const moveExecutes = { }, m444(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m444"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10882,7 +10892,7 @@ const moveExecutes = { }, m446(battle, source, primaryTarget, allTargets, _missedTargets) { const moveId = "m446"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // stealth rock log battle.addToLog( `Sharp rocks were scattered on the ground near ${primaryTarget.teamName}'s side!` @@ -10894,7 +10904,7 @@ const moveExecutes = { }, m450(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m450"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10938,7 +10948,7 @@ const moveExecutes = { }, m453(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m453"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10953,7 +10963,7 @@ const moveExecutes = { }, m469(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m469"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply wide guard 3 turn target.addEffect("wideGuard", 3, source); @@ -10961,7 +10971,7 @@ const moveExecutes = { }, m476(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m476"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply redirect for 1 turn target.addEffect("redirect", 1, source); @@ -10969,7 +10979,7 @@ const moveExecutes = { }, m479(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m479"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10990,7 +11000,7 @@ const moveExecutes = { }, m482(battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m482"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get only target row const targetParty = battle.parties[primaryTarget.teamName]; @@ -11019,7 +11029,7 @@ const moveExecutes = { }, m483(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m483"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // boost spa, spd, spe target.addEffect("spaUp", 3, source); @@ -11032,7 +11042,7 @@ const moveExecutes = { }, m484(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m484"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { let speedPower = 0; if (source.getSpe() < 150) { @@ -11059,7 +11069,7 @@ const moveExecutes = { }, m492(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m492"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // use target's attack @@ -11074,7 +11084,7 @@ const moveExecutes = { }, m503(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m503"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11091,7 +11101,7 @@ const moveExecutes = { }, m505(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m505"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // heal 50% const healAmount = Math.floor(target.maxHp / 2); @@ -11103,7 +11113,7 @@ const moveExecutes = { }, m506(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m506"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11127,7 +11137,7 @@ const moveExecutes = { }, m521(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m521"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const damageToDeal = calculateDamage( moveData, @@ -11155,7 +11165,7 @@ const moveExecutes = { }, m523(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m523"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11172,7 +11182,7 @@ const moveExecutes = { }, m525(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m525"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11189,7 +11199,7 @@ const moveExecutes = { }, m526(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m526"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // give atk up and spa up 2 turns target.addEffect("atkUp", 2, source); @@ -11198,7 +11208,7 @@ const moveExecutes = { }, m527(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m527"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11215,7 +11225,7 @@ const moveExecutes = { }, m528(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m528"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let damageDealt = 0; for (const target of allTargets) { const damageToDeal = calculateDamage( @@ -11241,7 +11251,7 @@ const moveExecutes = { }, m529(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m529"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11253,7 +11263,7 @@ const moveExecutes = { }, m534(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m534"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss, { @@ -11272,7 +11282,7 @@ const moveExecutes = { }, m540(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m540"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss, { @@ -11297,7 +11307,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m540-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let targetsFainted = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11327,7 +11337,7 @@ const moveExecutes = { }, m542(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m542"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11350,7 +11360,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m542-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); let type = source.getTypeDamageMultiplier(moveData.type, target); @@ -11374,7 +11384,7 @@ const moveExecutes = { }, m564(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m564"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11387,7 +11397,7 @@ const moveExecutes = { }, m565(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m565"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // raise atk source.addEffect("greaterAtkUp", 3, source); @@ -11417,7 +11427,7 @@ const moveExecutes = { }, m568(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m568"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // if not miss or primary target, atk and spa down 1 turn @@ -11429,7 +11439,7 @@ const moveExecutes = { }, m573(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m573"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // see if target is water type const waterType = @@ -11452,7 +11462,7 @@ const moveExecutes = { }, m572(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m572"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // deal less damage if more targets const numTargets = allTargets.length; const power = moveData.power - (numTargets - 1) * 5; @@ -11469,7 +11479,7 @@ const moveExecutes = { }, m574(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m574"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // ignore miss const damageToDeal = calculateDamage(moveData, source, target, false); @@ -11481,7 +11491,7 @@ const moveExecutes = { }, m583(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m583"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11502,7 +11512,7 @@ const moveExecutes = { }, m585(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m585"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11519,7 +11529,7 @@ const moveExecutes = { }, m586(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m586"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11537,7 +11547,7 @@ const moveExecutes = { }, m605(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m605"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11549,7 +11559,7 @@ const moveExecutes = { }, m618(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m618"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // give user spa, spd up source.addEffect("spaUp", 1, source); @@ -11566,7 +11576,7 @@ const moveExecutes = { }, m619(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m619"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // give user atk, def up source.addEffect("atkUp", 1, source); @@ -11583,7 +11593,7 @@ const moveExecutes = { }, m620(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m620"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss, { @@ -11611,7 +11621,7 @@ const moveExecutes = { missedTargets ) { const moveId = "m620-1"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let hits = 0; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11640,7 +11650,7 @@ const moveExecutes = { }, m668(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m668"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // heal hp equal to targets atk @@ -11658,7 +11668,7 @@ const moveExecutes = { }, m672(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m672"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); // if not miss, badly poison and fully reduce cr @@ -11670,7 +11680,7 @@ const moveExecutes = { }, m710(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m710"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11687,7 +11697,7 @@ const moveExecutes = { }, m719(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m719"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // filter out allTargets => just the primary target and up to 2 random other targets let damagedTargets = []; if (allTargets.length > 3) { @@ -11720,7 +11730,7 @@ const moveExecutes = { }, m742(battle, source, primaryTarget, _allTargets, missedTargets) { const moveId = "m742"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get row targets const enemyParty = battle.parties[primaryTarget.teamName]; @@ -11766,7 +11776,7 @@ const moveExecutes = { }, m814(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m814"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11791,7 +11801,7 @@ const moveExecutes = { }, m876(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m876"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11803,7 +11813,7 @@ const moveExecutes = { }, m20001(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m20001"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // if primary target, increase cr to 100% and give spe up 2 turns if (target === primaryTarget) { @@ -11823,7 +11833,7 @@ const moveExecutes = { }, m20002(battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m20002"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // use all HM moves const hmMoveIds = ["m57", "m70", "m127", "m249"]; @@ -11836,7 +11846,7 @@ const moveExecutes = { if (move.cooldown > 0) { source.reduceMoveCooldown(moveId, move.cooldown, source); } else { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // else, use move and set cooldown battle.addToLog(`${source.name} used ${moveData.name}!`); // get target @@ -11847,7 +11857,14 @@ const moveExecutes = { target.position, moveId ); - moveExecutes[moveId](battle, source, target, targets, []); + executeMove({ + moveId, + battle, + source, + primaryTarget: target, + allTargets: targets, + missedTargets: [], + }); // set cd move.cooldown = moveData.cooldown; @@ -11857,7 +11874,7 @@ const moveExecutes = { }, m20003(battle, source, primaryTarget, _allTargets, _missedTargets) { const moveId = "m20003"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get random fainted enemy of primary target const enemyParty = primaryTarget.getEnemyParty(); @@ -11902,7 +11919,7 @@ const moveExecutes = { }, m20004(battle, source, _primaryTarget, _allTargets, _missedTargets) { const moveId = "m20004"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get all non-fainted, hitable pokemon const targets = Object.values(battle.allPokemon).filter((p) => battle.isPokemonHittable(p, moveId) @@ -11918,7 +11935,7 @@ const moveExecutes = { }, m20005(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m20005"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // heal 40% const healAmount = Math.floor(target.maxHp * 0.4); @@ -11933,7 +11950,7 @@ const moveExecutes = { }, m20006(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m20006"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -11955,7 +11972,7 @@ const moveExecutes = { }, m20007(battle, source, _primaryTarget, _allTargets, _missedTargets) { const moveId = "m20007"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // get all non-fainted, hitable pokemon const targets = Object.values(battle.allPokemon).filter((p) => battle.isPokemonHittable(p, moveId) @@ -11967,7 +11984,7 @@ const moveExecutes = { }, m20008(battle, source, primaryTarget, allTargets, _missedTargets) { const moveId = "m20008"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // if primary, reduce hp by 50% and apply greater def down, spd down 1 turns if (target === primaryTarget) { @@ -11992,7 +12009,7 @@ const moveExecutes = { }, m20009(battle, source, primaryTarget, _allTargets, missedTargets) { const moveId = "m20009"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (let i = 0; i < 3; i += 1) { let target = primaryTarget; if (primaryTarget.isFainted) { @@ -12021,7 +12038,7 @@ const moveExecutes = { }, m20010(battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m20010"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // if source not under 25% hp, do nothing if (source.hp > source.maxHp * 0.25) { @@ -12044,7 +12061,7 @@ const moveExecutes = { }, m20011(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m20011"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // ignore miss // def is min user, target spd @@ -12060,7 +12077,7 @@ const moveExecutes = { }, m20012(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m20012"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -12072,7 +12089,7 @@ const moveExecutes = { }, m20013(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m20013"; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); for (const target of allTargets) { // apply extra turn buff for 1 (2) turn target.addEffect("extraTurn", 1, source); @@ -12296,7 +12313,7 @@ const abilityConfig = { } // if physical, 30% chance to paralyze - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.damageType === damageTypes.PHYSICAL && Math.random() < 0.3 @@ -12351,7 +12368,7 @@ const abilityConfig = { } // if electric, negate damage and heal 25% of max hp - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.type === pokemonTypes.ELECTRIC) { targetPokemon.battle.addToLog( `${targetPokemon.name} is healed by Volt Absorb!` @@ -12408,7 +12425,7 @@ const abilityConfig = { } // if water, negate damage and heal 25% of max hp - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.type === pokemonTypes.WATER) { targetPokemon.battle.addToLog( `${targetPokemon.name} is healed by Water Absorb!` @@ -12532,7 +12549,7 @@ const abilityConfig = { } // if fire, negate damage and grant atk up, spa up - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.type === pokemonTypes.FIRE) { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Flash Fire was activated by the Fire attack!` @@ -12789,7 +12806,7 @@ const abilityConfig = { return; } const { moveId } = args.damageInfo; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData) { return; } @@ -12893,7 +12910,7 @@ const abilityConfig = { } // if physical, 30% chance to status - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.damageType === damageTypes.PHYSICAL && Math.random() < 0.3 @@ -13052,7 +13069,7 @@ const abilityConfig = { `${sourcePokemon.name}'s Natural Cure remedies ${target.name}!` ); target.removeStatus(); - const moveData = moveConfig[args.moveId]; + const moveData = getMove(args.moveId); if (moveData && moveData.tier === moveTiers.ULTIMATE) { for (const effectId in target.effectIds) { const effectData = effectConfig[effectId]; @@ -13099,7 +13116,7 @@ const abilityConfig = { // check that enemy used non-ally move, and that move is electric const moveUser = args.user; - const moveData = moveConfig[args.moveId]; + const moveData = getMove(args.moveId); if (moveData.type !== pokemonTypes.ELECTRIC) { return; } @@ -13124,7 +13141,7 @@ const abilityConfig = { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.type !== pokemonTypes.ELECTRIC) { return; } @@ -13275,7 +13292,7 @@ const abilityConfig = { } // if physical, 30% chance to poison - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.damageType === damageTypes.PHYSICAL && Math.random() < 0.3 @@ -13421,7 +13438,7 @@ const abilityConfig = { const targetMoveIds = targetPokemon.moveIds; const possibleMoves = Object.entries(targetMoveIds).filter( ([moveId, move]) => { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const currentCooldown = move.cooldown; return moveData.cooldown && currentCooldown === 0; } @@ -13435,7 +13452,7 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${initialArgs.pokemon.name} is exerting Pressure against ${ targetPokemon.name - }'s ${moveConfig[randomMove[0]].name}!` + }'s ${getMove(randomMove[0]).name}!` ); abilityData.affectedPokemons.push(targetPokemon); }, @@ -13482,7 +13499,7 @@ const abilityConfig = { return; } const { moveId } = args.damageInfo; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData) { return; } @@ -13581,7 +13598,7 @@ const abilityConfig = { } // if move type === fire or ice, reduce damage by 50% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.type === pokemonTypes.FIRE || moveData.type === pokemonTypes.ICE @@ -13890,7 +13907,7 @@ const abilityConfig = { } // if move is physical, 30% chance to lower attacker's attack for 1 turn - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.damageType === damageTypes.PHYSICAL && Math.random() < 0.3 @@ -13979,7 +13996,7 @@ const abilityConfig = { } // if move type === grass and hp < 1/3, increase damage by 50% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.type === pokemonTypes.GRASS && userPokemon.hp < userPokemon.maxHp / 3 @@ -14027,7 +14044,7 @@ const abilityConfig = { } // if move type === fire and hp < 1/3, increase damage by 50% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.type === pokemonTypes.FIRE && userPokemon.hp < userPokemon.maxHp / 3 @@ -14075,7 +14092,7 @@ const abilityConfig = { } // if move type === water and hp < 1/3, increase damage by 50% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.type === pokemonTypes.WATER && userPokemon.hp < userPokemon.maxHp / 3 @@ -14123,7 +14140,7 @@ const abilityConfig = { } // if move type === bug and hp < 1/3, increase damage by 50% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.type === pokemonTypes.BUG && userPokemon.hp < userPokemon.maxHp / 3 @@ -14304,7 +14321,7 @@ const abilityConfig = { } // if move type === punch, increase damage by 20% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.name.toLowerCase().includes("punch")) { userPokemon.battle.addToLog( `${userPokemon.name}'s Iron Fist increases the damage!` @@ -14476,7 +14493,7 @@ const abilityConfig = { } // if move has 60 base power or less, increase damage by 50% - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.power <= 70) { sourcePokemon.battle.addToLog( `${sourcePokemon.name}'s Technician is increasing its damage!` @@ -14522,7 +14539,7 @@ const abilityConfig = { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (!moveData) { return; } @@ -14578,7 +14595,7 @@ const abilityConfig = { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (!moveData) { return; } @@ -14632,7 +14649,7 @@ const abilityConfig = { // check that enemy used non-ally move, and that move is water const moveUser = args.user; - const moveData = moveConfig[args.moveId]; + const moveData = getMove(args.moveId); if (moveData.type !== pokemonTypes.WATER) { return; } @@ -14657,7 +14674,7 @@ const abilityConfig = { return; } - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if (moveData.type !== pokemonTypes.WATER) { return; } @@ -14896,7 +14913,7 @@ const abilityConfig = { return; } const { moveId } = args.damageInfo; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData || moveData.type !== pokemonTypes.STEEL) { return; } @@ -14994,7 +15011,7 @@ const abilityConfig = { } // if physical, 30% chance to poison - const moveData = moveConfig[args.damageInfo.moveId]; + const moveData = getMove(args.damageInfo.moveId); if ( moveData.damageType === damageTypes.PHYSICAL && Math.random() < 0.3 @@ -15182,7 +15199,7 @@ const abilityConfig = { } // make sure move is non-damaging - const moveData = moveConfig[args.moveId]; + const moveData = getMove(args.moveId); if (!moveData || moveData.damageType !== damageTypes.OTHER) { return; } @@ -15195,13 +15212,14 @@ const abilityConfig = { initialPokemon.battle.addToLog( `${initialPokemon.name}'s Magic Bounce reflects the move!` ); - moveExecutes[args.moveId]( + executeMove({ + moveId: args.moveId, battle, - initialPokemon, - sourcePokemon, - [sourcePokemon], - [] - ); + source: initialPokemon, + primaryTarget: sourcePokemon, + allTargets: [sourcePokemon], + missedTargets: [], + }); }, }; const listenerId = battle.eventHandler.registerListener( @@ -15241,7 +15259,7 @@ const abilityConfig = { // make sure move is non-damaging const { moveId } = args; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if (!moveData || moveData.damageType !== damageTypes.OTHER) { return; } @@ -16193,7 +16211,7 @@ const abilityConfig = { } const randomMoveId = validMoves[Math.floor(Math.random() * validMoves.length)]; - const randomMoveData = moveConfig[randomMoveId]; + const randomMoveData = getMove(randomMoveId); battle.addToLog( `${targetPokemon.name} countered with ${randomMoveData.name}!` ); @@ -16221,13 +16239,14 @@ const abilityConfig = { // use move against target battle.addToLog(`${randomMoveData.name} hit ${target.name}!`); // yes I know the targets are confusing - moveExecutes[randomMoveId]( - targetPokemon.battle, - targetPokemon, - target, - targets, - [] - ); + executeMove({ + moveId: randomMoveId, + battle: targetPokemon.battle, + source: targetPokemon, + primaryTarget: target, + allTargets: targets, + missedTargets: [], + }); }, }; const damageListener = { diff --git a/src/components/selectBattleMoveRow.js b/src/components/selectBattleMoveRow.js index b044f81e..70486f28 100644 --- a/src/components/selectBattleMoveRow.js +++ b/src/components/selectBattleMoveRow.js @@ -8,7 +8,7 @@ */ const { ActionRowBuilder, StringSelectMenuBuilder } = require("discord.js"); const { eventNames } = require("../config/eventConfig"); -const { moveConfig } = require("../battleEngine/battleConfig"); +const { getMove } = require("../battleEngine/moveService"); const { typeConfig } = require("../config/pokemonConfig"); /** @@ -30,7 +30,7 @@ const buildSelectBattleMoveRow = (battle, stateId, selectedMoveId = null) => { .addOptions( Object.keys(battle.activePokemon.moveIds).map((moveId) => { // TODO: remove moves on cooldown? - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const { cooldown } = battle.activePokemon.moveIds[moveId]; const cdString = cooldown > 0 ? `[COOLDOWN ${cooldown}] ` : ""; const { disabled } = battle.activePokemon.moveIds[moveId]; diff --git a/src/embeds/battleEmbeds.js b/src/embeds/battleEmbeds.js index 9d362542..5be74e07 100644 --- a/src/embeds/battleEmbeds.js +++ b/src/embeds/battleEmbeds.js @@ -7,10 +7,8 @@ * battleEmbeds.js Handles all embedded instructions for battles. */ const { EmbedBuilder } = require("discord.js"); -const { - moveConfig, - weatherConditions, -} = require("../battleEngine/battleConfig"); +const { weatherConditions } = require("../battleEngine/battleConfig"); +const { getMove } = require("../battleEngine/moveService"); const { buildPartyString, buildMoveString, @@ -258,7 +256,7 @@ const buildBattleEmbed = (battle) => { const buildBattleMovesetEmbed = (pokemon) => { const fields = Object.keys(pokemon.moveIds).map((moveId) => { const { cooldown } = pokemon.moveIds[moveId]; - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const { moveHeader, moveString } = buildMoveString(moveData, cooldown); return { name: moveHeader, diff --git a/src/embeds/pokemonEmbeds.js b/src/embeds/pokemonEmbeds.js index 53d6b47d..2a59eef8 100644 --- a/src/embeds/pokemonEmbeds.js +++ b/src/embeds/pokemonEmbeds.js @@ -15,7 +15,8 @@ const { typeConfig, growthRateConfig, } = require("../config/pokemonConfig"); -const { moveConfig, abilityConfig } = require("../battleEngine/battleConfig"); +const { abilityConfig } = require("../battleEngine/battleConfig"); +const { getMove } = require("../battleEngine/moveService"); const { getWhitespace, getPBar, @@ -362,7 +363,7 @@ const buildPokemonEmbed = ( // moves & abilities if (tab === "battle" || tab === "all") { const fields = getMoveIds(pokemon).map((moveId) => { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const { moveHeader, moveString } = buildMoveString(moveData); return { name: moveHeader, @@ -742,7 +743,7 @@ const buildSpeciesDexEmbed = (id, speciesData, tab, ownershipData) => { embed.setDescription(`No moves!`); } else { const fields = speciesData.moveIds.map((moveId) => { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const { moveHeader, moveString } = buildMoveString(moveData); return { name: moveHeader, diff --git a/src/services/battle.js b/src/services/battle.js index 1bf76e95..2e6c0e89 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -80,6 +80,7 @@ const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); const { User } = require("discord.js"); +const { getMove, executeMove } = require("../battleEngine/moveService"); class NPC { constructor( @@ -233,7 +234,7 @@ class NPC { // eslint-disable-next-line class-methods-use-this calculateHeuristic(moveId, source, targetsHit) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); let heuristic = 0; // special case: if asleep and sleep talk, use sleep talk @@ -573,19 +574,19 @@ class Pokemon { this.teamName = teamName; this.name = pokemonData.name; this.hp = pokemonData.remainingHp || pokemonData.stats[0]; - [this.maxHp] = this.pokemonData.stats; + [this.maxHp = 0] = this.pokemonData.stats; this.level = pokemonData.level; [ this.atk, - this.batk, + this.batk = 0, this.def, - this.bdef, + this.bdef = 0, this.spa, - this.bspa, + this.bspa = 0, this.spd, - this.bspd, + this.bspd = 0, this.spe, - this.bspe, + this.bspe = 0, ] = [ pokemonData.stats[1], pokemonData.stats[1], @@ -600,7 +601,7 @@ class Pokemon { ]; this.acc = 100; this.eva = 100; - [this.type1, this.type2 = null] = this.speciesData.type; + [this.type1 = null, this.type2 = null] = this.speciesData.type; // map effectId => effect data (duration, args) this.effectIds = {}; // map moveId => move data (cooldown, disabled) @@ -685,7 +686,7 @@ class Pokemon { return; } // make sure move exists and is not on cooldown & disabled - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); if ( !moveData || this.moveIds[moveId].cooldown > 0 || @@ -820,13 +821,14 @@ class Pokemon { ); // execute move - moveExecutes[moveId]( - this.battle, - this, + executeMove({ + moveId, + battle: this.battle, + source: this, primaryTarget, allTargets, - missedTargets - ); + missedTargets, + }); // after move event const eventArgs = { @@ -1065,7 +1067,7 @@ class Pokemon { } getTargets(moveId, targetPokemonId) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); // make sure target exists and is alive const target = this.battle.allPokemon[targetPokemonId]; if (!target) { @@ -1088,7 +1090,7 @@ class Pokemon { } getMisses(moveId, targetPokemons) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const misses = []; if (!moveData.accuracy) { return misses; @@ -1278,8 +1280,8 @@ class Pokemon { const freezeCheck = this.status.statusId === statusConditions.FREEZE && damageInfo.type === "move" && - moveConfig[damageInfo.moveId] !== undefined && - (moveConfig[damageInfo.moveId].type === types.FIRE || + getMove(damageInfo.moveId) !== undefined && + (getMove(damageInfo.moveId).type === types.FIRE || damageInfo.moveId === "m503"); if (freezeCheck) { if (this.removeStatus()) { @@ -1875,7 +1877,7 @@ class Pokemon { // disable move this.moveIds[moveId].disabled = true; - // this.battle.addToLog(`${this.name}'s ${moveConfig[moveId].name} was disabled!`); + // this.battle.addToLog(`${this.name}'s ${getMove(moveId).name} was disabled!`); } // eslint-disable-next-line no-unused-vars @@ -1892,7 +1894,7 @@ class Pokemon { // enable move this.moveIds[moveId].disabled = false; - // this.battle.addToLog(`${this.name}'s ${moveConfig[moveId].name} is no longer disabled!`); + // this.battle.addToLog(`${this.name}'s ${getMove(moveId).name} is no longer disabled!`); } tickEffectDurations() { @@ -1933,7 +1935,7 @@ class Pokemon { if (!silenced) { this.battle.addToLog( - `${this.name}'s ${moveConfig[moveId].name}'s cooldown was reduced by ${ + `${this.name}'s ${getMove(moveId).name}'s cooldown was reduced by ${ oldCooldown - newCooldown } turns!` ); @@ -2552,7 +2554,7 @@ class Battle { } getEligibleTargets(source, moveId) { - const moveData = moveConfig[moveId]; + const moveData = getMove(moveId); const eligibleTargets = []; const eventArgs = { user: source, From 3a52b973a734e87f916e7d97b47227fddc8f497c Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Thu, 3 Oct 2024 19:44:23 -0700 Subject: [PATCH 16/38] basic move migration --- src/battleEngine/battleConfig.js | 32 +++------------ src/battleEngine/battleTypes.js | 5 ++- src/battleEngine/moveService.js | 13 +------ src/battleEngine/moves.js | 67 +++++++++++++++++++++++++++++++- src/config/pokemonConfig.js | 13 ++++--- src/enums/battleEnums.js | 17 ++++++++ 6 files changed, 99 insertions(+), 48 deletions(-) create mode 100644 src/enums/battleEnums.js diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index d50a8cbd..61995405 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -272,6 +272,7 @@ const calculateDamage = ( power = null, type = null, moveType = null, + finalDamageMultipler = 1, } = {} ) => { /* eslint-disable-code-block no-param-reassign */ @@ -343,7 +344,8 @@ const calculateDamage = ( random * burn * weatherMult * - missMult + missMult * + finalDamageMultipler ); return Math.max(damage, 1); @@ -2642,20 +2644,6 @@ const moveConfig = Object.freeze({ description: "The target is slammed with a long tail, vines, etc., to inflict damage.", }, - m22: { - name: "Vine Whip", - type: pokemonTypes.GRASS, - power: 55, - accuracy: 100, - cooldown: 0, - targetType: targetTypes.ENEMY, - targetPosition: targetPositions.FRONT, - targetPattern: targetPatterns.SINGLE, - tier: moveTiers.BASIC, - damageType: damageTypes.PHYSICAL, - description: - "The target is struck with slender, whiplike vines to inflict damage.", - }, m23: { name: "Stomp", type: pokemonTypes.NORMAL, @@ -6755,18 +6743,6 @@ const moveExecutes = { }); } }, - m22(_battle, source, _primaryTarget, allTargets, missedTargets) { - const moveId = "m22"; - const moveData = getMove(moveId); - for (const target of allTargets) { - const miss = missedTargets.includes(target); - const damageToDeal = calculateDamage(moveData, source, target, miss); - source.dealDamage(damageToDeal, target, { - type: "move", - moveId, - }); - } - }, m23(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m23"; const moveData = getMove(moveId); @@ -16634,4 +16610,6 @@ module.exports = { weatherConditions, calculateDamage, abilityConfig, + damageTypes, + calculateDamage, }; diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js index 9c229fcf..f62ba6c3 100644 --- a/src/battleEngine/battleTypes.js +++ b/src/battleEngine/battleTypes.js @@ -1,5 +1,5 @@ /** - * @typedef {import("./moveService").MoveIdEnum} MoveIdEnum + * @typedef {import("../enums/battleEnums").MoveIdEnum} MoveIdEnum * * @typedef {import("../services/battle").Battle} Battle * @typedef {import("../services/battle").Pokemon} BattlePokemon @@ -17,10 +17,11 @@ /** * @callback MoveExecute + * @this {Move} * @param {Object} param0 * @param {Battle} param0.battle * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.primaryTarget * @param {Array} param0.allTargets - * @param {Array?=} param0.missedTargets + * @param {Array} param0.missedTargets */ diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index fd89c82b..91f03938 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -1,17 +1,6 @@ const { logger } = require("../log"); const types = require("../../types"); -/** - * @typedef{import("./battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum - * @typedef{types.Enum} NewMoveIdEnum - * @typedef{LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum - */ - -const moveIds = Object.freeze({ - TEST_MOVE: "999", - TEST_MOVE2: "998", -}); - const allMoves = {}; /** @@ -59,7 +48,7 @@ const getMove = (moveId) => { * @param {Object} param0.source * @param {Object} param0.primaryTarget * @param {Array} param0.allTargets - * @param {Array?=} param0.missedTargets + * @param {Array=} param0.missedTargets * @returns */ const executeMove = ({ diff --git a/src/battleEngine/moves.js b/src/battleEngine/moves.js index 4d2e869e..1ed27241 100644 --- a/src/battleEngine/moves.js +++ b/src/battleEngine/moves.js @@ -1,3 +1,15 @@ +const { types: pokemonTypes } = require("../config/pokemonConfig"); +const { + targetTypes, + targetPositions, + targetPatterns, + damageTypes, + moveTiers, + calculateDamage, +} = require("./battleConfig"); +const { getMove } = require("./moveService"); +const { moveIdEnum } = require("../enums/battleEnums"); + /** * @typedef {import("../config/pokemonConfig").PokemonTypeEnum} PokemonTypeEnum */ @@ -49,9 +61,62 @@ class Move { this.execute = execute; this.isLegacyMove = false; } + + /** + * @param {Object} param0 + * @param {BattlePokemon} param0.source + * @param {BattlePokemon} param0.primaryTarget + * @param {Array} param0.allTargets + * @param {Array=} param0.missedTargets + * @param {number=} param0.offTargetDamageMultiplier + */ + genericDealDamage = ({ + source, + primaryTarget, + allTargets, + missedTargets = [], + offTargetDamageMultiplier = 0.8, + }) => { + const moveData = getMove(this.id); + for (const target of allTargets) { + const miss = missedTargets.includes(target); + const damageToDeal = calculateDamage(moveData, source, target, miss, { + finalDamageMultiplier: + target === primaryTarget ? 1 : offTargetDamageMultiplier, + }); + source.dealDamage(damageToDeal, target, { + type: "move", + moveId: this.id, + }); + } + }; } -const movesToRegister = []; +const movesToRegister = [ + new Move({ + id: moveIdEnum.VINE_WHIP, + name: "Vine Whip", + type: pokemonTypes.GRASS, + power: 55, + accuracy: 100, + cooldown: 0, + targetType: targetTypes.ENEMY, + targetPosition: targetPositions.FRONT, + targetPattern: targetPatterns.SINGLE, + tier: moveTiers.BASIC, + damageType: damageTypes.PHYSICAL, + description: + "The target is struck with slender, whiplike vines to inflict damage.", + execute: function ({ source, primaryTarget, allTargets, missedTargets }) { + this.genericDealDamage({ + source, + primaryTarget, + allTargets, + missedTargets, + }); + }, + }), +]; module.exports = { Move, diff --git a/src/config/pokemonConfig.js b/src/config/pokemonConfig.js index 5c2cfbdf..1b25d0f4 100644 --- a/src/config/pokemonConfig.js +++ b/src/config/pokemonConfig.js @@ -1,4 +1,5 @@ const jsTypes = require("../../types"); +const { moveIdEnum } = require("../enums/battleEnums"); /** @typedef {jsTypes.Enum} PokemonTypeEnum */ const types = Object.freeze({ @@ -358,7 +359,7 @@ const pokemonConfig = { 65: 0.8, 34: 0.2, }, - moveIds: ["m22", "m33", "m79", "m202"], + moveIds: [moveIdEnum.VINE_WHIP, "m33", "m79", "m202"], battleEligible: true, rarity: rarities.RARE, growthRate: growthRates.MEDIUMSLOW, @@ -384,7 +385,7 @@ const pokemonConfig = { 65: 0.8, 34: 0.2, }, - moveIds: ["m22", "m79", "m188", "m202"], + moveIds: [moveIdEnum.VINE_WHIP, "m79", "m188", "m202"], battleEligible: true, rarity: rarities.EPIC, growthRate: growthRates.MEDIUMSLOW, @@ -404,7 +405,7 @@ const pokemonConfig = { 65: 0.8, 34: 0.2, }, - moveIds: ["m22", "m79", "m188", "m76"], + moveIds: [moveIdEnum.VINE_WHIP, "m79", "m188", "m76"], battleEligible: true, rarity: rarities.EPIC, growthRate: growthRates.MEDIUMSLOW, @@ -1956,7 +1957,7 @@ const pokemonConfig = { 34: 0.8, 82: 0.2, }, - moveIds: ["m22", "m51", "m202", "m398"], + moveIds: [moveIdEnum.VINE_WHIP, "m51", "m202", "m398"], battleEligible: true, rarity: rarities.COMMON, growthRate: growthRates.MEDIUMSLOW, @@ -3471,7 +3472,7 @@ const pokemonConfig = { basicMoveIds: [ "m16", "m17", - "m22", + moveIdEnum.VINE_WHIP, "m35", "m40", "m43", @@ -3634,7 +3635,7 @@ const pokemonConfig = { 65: 0.8, 102: 0.2, }, - moveIds: ["m22", "m77", "m73", "m202"], + moveIds: [moveIdEnum.VINE_WHIP, "m77", "m73", "m202"], battleEligible: true, rarity: rarities.RARE, growthRate: growthRates.MEDIUMSLOW, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js new file mode 100644 index 00000000..5ba92d94 --- /dev/null +++ b/src/enums/battleEnums.js @@ -0,0 +1,17 @@ +const types = require("../../types"); + +/** + * @typedef{import("../battleEngine/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum + * @typedef{types.Enum} NewMoveIdEnum + * @typedef{LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum + */ + +const moveIdEnum = Object.freeze({ + VINE_WHIP: "m22", + TEST_MOVE: "999", + TEST_MOVE2: "998", +}); + +module.exports = { + moveIdEnum, +}; From 9c63c2901f291a248af26e4ad4f14bcb32de2638 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 4 Oct 2024 00:44:05 -0700 Subject: [PATCH 17/38] init effects service --- src/battleEngine/battleConfig.js | 11 ++-- src/battleEngine/battleTypes.js | 47 +++++++++++++ src/battleEngine/effectService.js | 106 ++++++++++++++++++++++++++++++ src/battleEngine/effects.js | 73 ++++++++++++++++++++ src/battleEngine/moveService.js | 11 ++-- src/battleEngine/moves.js | 6 +- src/enums/battleEnums.js | 20 ++++-- src/services/battle.js | 2 +- 8 files changed, 258 insertions(+), 18 deletions(-) create mode 100644 src/battleEngine/effectService.js create mode 100644 src/battleEngine/effects.js diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 61995405..cc75bc40 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -377,12 +377,15 @@ const targetPatterns = Object.freeze({ CROSS: "Cross", }); -const effectTypes = { +/** @typedef {types.Enum} EffectTypeEnum */ +const effectTypes = Object.freeze({ BUFF: "Buff", DEBUFF: "Debuff", NEUTRAL: "Neutral", -}; -const effectConfig = { +}); + +/** @typedef {types.Keys} LegacyEffectIdEnum */ +const effectConfig = Object.freeze({ shield: { name: "Shield", description: "The target's takes shielded damage.", @@ -2528,7 +2531,7 @@ const effectConfig = { } }, }, -}; +}); /** @typedef{types.Keys} LegacyMoveIdEnum */ const moveConfig = Object.freeze({ diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js index f62ba6c3..9a930749 100644 --- a/src/battleEngine/battleTypes.js +++ b/src/battleEngine/battleTypes.js @@ -1,5 +1,6 @@ /** * @typedef {import("../enums/battleEnums").MoveIdEnum} MoveIdEnum + * @typedef {import("../enums/battleEnums").EffectIdEnum} EffectIdEnum * * @typedef {import("../services/battle").Battle} Battle * @typedef {import("../services/battle").Pokemon} BattlePokemon @@ -11,10 +12,56 @@ * @typedef {import("./battleConfig").TargetTypeEnum} TargetTypeEnum * @typedef {import("./battleConfig").TargetPositionEnum} TargetPositionEnum * @typedef {import("./battleConfig").TargetPatternEnum} TargetPatternEnum + * @typedef {import("./battleConfig").EffectTypeEnum} EffectTypeEnum * + * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} EffectAddBasicArgs + * @typedef {{battle: Battle, target: BattlePokemon}} EffectRemoveBasicArgs + * @typedef {typeof import("./effects").effectsToRegister} RegisteredEffects * @typedef {import("./moves").Move} Move */ +/** + * @template T + * @template U + * @typedef {import("./effects").Effect} Effect + */ +/** + * @template T + * @typedef {T extends Effect ? U : never} EffectInitialArgsType + **/ +/** + * @template T + * @typedef {T extends Effect ? V : never} EffectPropertiesType + */ + +/** + * + * @template T + * @template U + * @callback EffectAddCallback + * + * @param {Object} param0 + * @param {Battle} param0.battle + * @param {BattlePokemon} param0.source + * @param {BattlePokemon} param0.target + * @param {T} param0.initialArgs + * + * @returns {U} + * @this {Effect} + */ + +/** + * @template T + * @template U + * @callback EffectRemoveCallback + * @param {Object} param0 + * @param {Battle} param0.battle + * @param {BattlePokemon} param0.target + * @param {T} param0.initialArgs + * @param {U} param0.properties + * @this {Effect} + */ + /** * @callback MoveExecute * @this {Move} diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectService.js new file mode 100644 index 00000000..1235c0c5 --- /dev/null +++ b/src/battleEngine/effectService.js @@ -0,0 +1,106 @@ +const { logger } = require("../log"); +const types = require("../../types"); +const { effectIdEnum } = require("../enums/battleEnums"); + +const allEffects = {}; + +/** + * @param {Record>} effects + */ +const registerEffects = (effects) => { + Object.entries(effects).forEach(([effectId, effect]) => { + allEffects[effectId] = effect; + }); +}; + +/** + * @param {Record} effectConfig + */ +const registerLegacyEffects = (effectConfig) => { + Object.entries(effectConfig).forEach(([effectId, effect]) => { + allEffects[effectId] = { + ...effect, + isLegacyEffect: true, + }; + }); +}; + +/** + * @template {EffectIdEnum} K + * @param {K} effectId + * @returns {K extends keyof RegisteredEffects ? RegisteredEffects[K] : Effect} + */ +const getEffect = (effectId) => { + // @ts-ignore + return allEffects[effectId]; +}; + +const test = getEffect(effectIdEnum.TEST_EFFECT); + +/** + * @template {EffectIdEnum} K + * @param {Object} param0 + * @param {K} param0.effectId + * @param {Battle} param0.battle + * @param {BattlePokemon} param0.source + * @param {BattlePokemon} param0.target + * @param {K extends keyof RegisteredEffects ? EffectInitialArgsType : any} param0.initialArgs + */ +const applyEffect = ({ effectId, battle, source, target, initialArgs }) => { + const effect = getEffect(effectId); + if (!effect) { + logger.error(`Effect ${effectId} does not exist.`); + return; + } + if (!effect.isLegacyEffect) { + effect.effectAdd({ + battle, + source, + target, + initialArgs, + }); + } else { + const legacyEffect = /** @type {any} */ (effect); + legacyEffect.effectAdd(battle, source, target, initialArgs); + } +}; + +/** + * @template {EffectIdEnum} K + * @param {Object} param0 + * @param {K} param0.effectId + * @param {Battle} param0.battle + * @param {BattlePokemon} param0.target + * @param {K extends keyof RegisteredEffects ? EffectInitialArgsType : any} param0.initialArgs + * @param {K extends keyof RegisteredEffects ? EffectPropertiesType : any} param0.properties + */ +const removeEffect = ({ + effectId, + battle, + target, + initialArgs, + properties, +}) => { + const effect = getEffect(effectId); + if (!effect) { + logger.error(`Effect ${effectId} does not exist.`); + return; + } + if (!effect.isLegacyEffect) { + effect.effectRemove({ + battle, + target, + initialArgs, + properties, + }); + } else { + const legacyEffect = /** @type {any} */ (effect); + legacyEffect.effectRemove(battle, target, properties, initialArgs); + } +}; + +module.exports = { + registerEffects, + registerLegacyEffects, + getEffect, +}; diff --git a/src/battleEngine/effects.js b/src/battleEngine/effects.js new file mode 100644 index 00000000..13c18536 --- /dev/null +++ b/src/battleEngine/effects.js @@ -0,0 +1,73 @@ +const { effectTypes } = require("./battleConfig"); +const { effectIdEnum } = require("../enums/battleEnums"); + +/** + * @template T + * @template U + */ +class Effect { + /** + * @param {Object} param0 + * @param {EffectIdEnum} param0.id + * @param {string} param0.name + * @param {string} param0.description + * @param {EffectTypeEnum} param0.type + * @param {boolean} param0.dispellable + * @param {EffectAddCallback} param0.effectAdd + * @param {EffectRemoveCallback} param0.effectRemove + */ + constructor({ + id, + name, + description, + type, + dispellable, + effectAdd, + effectRemove, + }) { + this.id = id; + this.name = name; + this.description = description; + this.type = type; + this.dispellable = dispellable; + this.effectAdd = effectAdd; + this.effectRemove = effectRemove; + this.isLegacyEffect = false; + } +} + +const effectsToRegister = Object.freeze({ + [effectIdEnum.ATK_UP]: new Effect({ + id: effectIdEnum.ATK_UP, + name: "Atk. Up", + description: "The target's Attack increased.", + type: effectTypes.BUFF, + dispellable: true, + /** + * @param {EffectAddBasicArgs & {initialArgs: null}} args + */ + effectAdd: function ({ battle, target }) { + // if greaterAtkUp exists on target, remove atkUp and refresh greaterAtkUp + if (target.effectIds.greaterAtkUp) { + const currentDuration = target.effectIds.atkUp.duration; + delete target.effectIds.atkUp; + if (target.effectIds.greaterAtkUp.duration < currentDuration) { + target.effectIds.greaterAtkUp.duration = currentDuration; + } + } else { + battle.addToLog(`${target.name}'s Attack rose!`); + target.atk += Math.floor(target.batk * 0.5); + } + return {}; + }, + effectRemove: function ({ battle, target, initialArgs, properties }) { + battle.addToLog(`${target.name}'s Attack boost wore off!`); + target.atk -= Math.floor(target.batk * 0.5); + }, + }), +}); + +module.exports = { + Effect, + effectsToRegister, +}; diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 91f03938..3304cfdb 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -1,20 +1,19 @@ const { logger } = require("../log"); -const types = require("../../types"); const allMoves = {}; /** - * @param {Array} moves + * @param {{[K in MoveIdEnum]: Move}} moves */ const registerMoves = (moves) => { - moves.forEach((move) => { - allMoves[move.id] = move; + Object.entries(moves).forEach(([moveId, move]) => { + allMoves[moveId] = move; }); }; /** - * @param {Object} moveConfig - * @param {Object} moveExecutes + * @param {{[K in MoveIdEnum]: Object}} moveConfig + * @param {{[K in MoveIdEnum]: Function}} moveExecutes */ const registerLegacyMoves = (moveConfig, moveExecutes) => { Object.entries(moveConfig).forEach(([moveId, move]) => { diff --git a/src/battleEngine/moves.js b/src/battleEngine/moves.js index 1ed27241..21680f1e 100644 --- a/src/battleEngine/moves.js +++ b/src/battleEngine/moves.js @@ -92,8 +92,8 @@ class Move { }; } -const movesToRegister = [ - new Move({ +const movesToRegister = Object.freeze({ + [moveIdEnum.VINE_WHIP]: new Move({ id: moveIdEnum.VINE_WHIP, name: "Vine Whip", type: pokemonTypes.GRASS, @@ -116,7 +116,7 @@ const movesToRegister = [ }); }, }), -]; +}); module.exports = { Move, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index 5ba92d94..9aff0050 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -1,17 +1,29 @@ const types = require("../../types"); /** - * @typedef{import("../battleEngine/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum - * @typedef{types.Enum} NewMoveIdEnum - * @typedef{LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum + * @typedef {import("../battleEngine/battleConfig").LegacyEffectIdEnum} LegacyEffectIdEnum + * @typedef {types.Enum} NewEffectIdEnum + * @typedef {LegacyEffectIdEnum | NewEffectIdEnum} EffectIdEnum + */ + +const effectIdEnum = Object.freeze({ + TEST_EFFECT: "testEffect", + ATK_UP: "atkUp", +}); + +/** + * @typedef {import("../battleEngine/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum + * @typedef {types.Enum} NewMoveIdEnum + * @typedef {LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum */ const moveIdEnum = Object.freeze({ - VINE_WHIP: "m22", TEST_MOVE: "999", TEST_MOVE2: "998", + VINE_WHIP: "m22", }); module.exports = { moveIdEnum, + effectIdEnum, }; diff --git a/src/services/battle.js b/src/services/battle.js index 2e6c0e89..07867848 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -577,7 +577,7 @@ class Pokemon { [this.maxHp = 0] = this.pokemonData.stats; this.level = pokemonData.level; [ - this.atk, + this.atk = 0, this.batk = 0, this.def, this.bdef = 0, From e75f0883f34cd6df35fe9b06858f5094b3d7a3b3 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 4 Oct 2024 17:38:00 -0700 Subject: [PATCH 18/38] fix eslint --- eslint.config.mjs | 8 ++++++-- src/battleEngine/battleConfig.js | 2 +- src/battleEngine/battleTypes.js | 27 +++++++++++++++++---------- src/battleEngine/effectService.js | 24 ++++++++++-------------- src/battleEngine/effects.js | 7 ++++--- src/battleEngine/initialize.js | 8 +++++++- src/battleEngine/moveService.js | 21 +++++++++------------ src/battleEngine/moves.js | 12 ++++++------ 8 files changed, 60 insertions(+), 49 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 09fe5199..d37bf948 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,7 +14,7 @@ import pluginJs from "@eslint/js"; export default [ // mimic ESLintRC-style extends - ...compat.extends("airbnb", "prettier"), + ...compat.extends("airbnb", "prettier", "plugin:jsdoc/recommended"), { files: ["**/*.js"], languageOptions: { @@ -34,7 +34,11 @@ export default [ "guard-for-in": "off", // TODO: enable but make better lol "no-continue": "off", // doesn't seem to work properly "no-await-in-loop": "off", // TODO: use promise.all - "jsdoc/no-undefined-types": 1, + "jsdoc/no-undefined-types": "off", + "jsdoc/require-param-description": "off", // TODO: maybe add descriptions + "jsdoc/require-returns-description": "off", // TODO: maybe add descriptions + "jsdoc/valid-types": "off", // doesn't seem to work with TS types + "jsdoc/require-returns": "off", }, }, pluginJs.configs.recommended, diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index cc75bc40..0d462bd8 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -1365,7 +1365,7 @@ const effectConfig = Object.freeze({ `${invulnPokemon.name} reflected the move back at ${sourcePokemon.name}!` ); executeMove({ - moveId: moveId, + moveId, battle: invulnPokemon.battle, source: invulnPokemon, primaryTarget: sourcePokemon, diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js index 9a930749..36db7ca5 100644 --- a/src/battleEngine/battleTypes.js +++ b/src/battleEngine/battleTypes.js @@ -1,10 +1,8 @@ /** * @typedef {import("../enums/battleEnums").MoveIdEnum} MoveIdEnum * @typedef {import("../enums/battleEnums").EffectIdEnum} EffectIdEnum - * * @typedef {import("../services/battle").Battle} Battle * @typedef {import("../services/battle").Pokemon} BattlePokemon - * * @typedef {import("./battleConfig").BattleEventEnum} BattleEventEnum * @typedef {import("./battleConfig").DamageTypeEnum} DamageTypeEnum * @typedef {import("./battleConfig").MoveTierEnum} MoveTierEnum @@ -13,7 +11,6 @@ * @typedef {import("./battleConfig").TargetPositionEnum} TargetPositionEnum * @typedef {import("./battleConfig").TargetPatternEnum} TargetPatternEnum * @typedef {import("./battleConfig").EffectTypeEnum} EffectTypeEnum - * * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} EffectAddBasicArgs * @typedef {{battle: Battle, target: BattlePokemon}} EffectRemoveBasicArgs * @typedef {typeof import("./effects").effectsToRegister} RegisteredEffects @@ -25,13 +22,25 @@ * @template U * @typedef {import("./effects").Effect} Effect */ + /** * @template T * @typedef {T extends Effect ? U : never} EffectInitialArgsType - **/ + */ + /** * @template T - * @typedef {T extends Effect ? V : never} EffectPropertiesType + * @typedef {T extends Effect ? V | {} : never} EffectPropertiesType + */ + +/** + * @template {EffectIdEnum} K + * @typedef {K extends keyof RegisteredEffects ? EffectInitialArgsType : any} EffectInitialArgsTypeFromId + */ + +/** + * @template {EffectIdEnum} K + * @typedef {K extends keyof RegisteredEffects ? EffectPropertiesType : any} EffectPropertiesTypeFromId */ /** @@ -39,13 +48,11 @@ * @template T * @template U * @callback EffectAddCallback - * - * @param {Object} param0 + * @param {object} param0 * @param {Battle} param0.battle * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.target * @param {T} param0.initialArgs - * * @returns {U} * @this {Effect} */ @@ -54,7 +61,7 @@ * @template T * @template U * @callback EffectRemoveCallback - * @param {Object} param0 + * @param {object} param0 * @param {Battle} param0.battle * @param {BattlePokemon} param0.target * @param {T} param0.initialArgs @@ -65,7 +72,7 @@ /** * @callback MoveExecute * @this {Move} - * @param {Object} param0 + * @param {object} param0 * @param {Battle} param0.battle * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.primaryTarget diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectService.js index 1235c0c5..eb8d7e3f 100644 --- a/src/battleEngine/effectService.js +++ b/src/battleEngine/effectService.js @@ -1,6 +1,4 @@ const { logger } = require("../log"); -const types = require("../../types"); -const { effectIdEnum } = require("../enums/battleEnums"); const allEffects = {}; @@ -14,7 +12,7 @@ const registerEffects = (effects) => { }; /** - * @param {Record} effectConfig + * @param {Record} effectConfig */ const registerLegacyEffects = (effectConfig) => { Object.entries(effectConfig).forEach(([effectId, effect]) => { @@ -30,21 +28,17 @@ const registerLegacyEffects = (effectConfig) => { * @param {K} effectId * @returns {K extends keyof RegisteredEffects ? RegisteredEffects[K] : Effect} */ -const getEffect = (effectId) => { +const getEffect = (effectId) => // @ts-ignore - return allEffects[effectId]; -}; - -const test = getEffect(effectIdEnum.TEST_EFFECT); - + allEffects[effectId]; /** * @template {EffectIdEnum} K - * @param {Object} param0 + * @param {object} param0 * @param {K} param0.effectId * @param {Battle} param0.battle * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.target - * @param {K extends keyof RegisteredEffects ? EffectInitialArgsType : any} param0.initialArgs + * @param {EffectInitialArgsTypeFromId} param0.initialArgs */ const applyEffect = ({ effectId, battle, source, target, initialArgs }) => { const effect = getEffect(effectId); @@ -67,12 +61,12 @@ const applyEffect = ({ effectId, battle, source, target, initialArgs }) => { /** * @template {EffectIdEnum} K - * @param {Object} param0 + * @param {object} param0 * @param {K} param0.effectId * @param {Battle} param0.battle * @param {BattlePokemon} param0.target - * @param {K extends keyof RegisteredEffects ? EffectInitialArgsType : any} param0.initialArgs - * @param {K extends keyof RegisteredEffects ? EffectPropertiesType : any} param0.properties + * @param {EffectInitialArgsTypeFromId} param0.initialArgs + * @param {EffectPropertiesTypeFromId} param0.properties */ const removeEffect = ({ effectId, @@ -103,4 +97,6 @@ module.exports = { registerEffects, registerLegacyEffects, getEffect, + applyEffect, + removeEffect, }; diff --git a/src/battleEngine/effects.js b/src/battleEngine/effects.js index 13c18536..0ebbe0b7 100644 --- a/src/battleEngine/effects.js +++ b/src/battleEngine/effects.js @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ const { effectTypes } = require("./battleConfig"); const { effectIdEnum } = require("../enums/battleEnums"); @@ -7,7 +8,7 @@ const { effectIdEnum } = require("../enums/battleEnums"); */ class Effect { /** - * @param {Object} param0 + * @param {object} param0 * @param {EffectIdEnum} param0.id * @param {string} param0.name * @param {string} param0.description @@ -46,7 +47,7 @@ const effectsToRegister = Object.freeze({ /** * @param {EffectAddBasicArgs & {initialArgs: null}} args */ - effectAdd: function ({ battle, target }) { + effectAdd({ battle, target }) { // if greaterAtkUp exists on target, remove atkUp and refresh greaterAtkUp if (target.effectIds.greaterAtkUp) { const currentDuration = target.effectIds.atkUp.duration; @@ -60,7 +61,7 @@ const effectsToRegister = Object.freeze({ } return {}; }, - effectRemove: function ({ battle, target, initialArgs, properties }) { + effectRemove({ battle, target }) { battle.addToLog(`${target.name}'s Attack boost wore off!`); target.atk -= Math.floor(target.batk * 0.5); }, diff --git a/src/battleEngine/initialize.js b/src/battleEngine/initialize.js index 43b4ae0c..e9e68b37 100644 --- a/src/battleEngine/initialize.js +++ b/src/battleEngine/initialize.js @@ -1,8 +1,14 @@ +// @ts-nocheck +// TODO: remove ts-nocheck when all moves, effects, abilities, and events are migrated const { registerMoves, registerLegacyMoves } = require("./moveService"); -const { moveConfig, moveExecutes } = require("./battleConfig"); +const { registerEffects, registerLegacyEffects } = require("./effectService"); +const { effectsToRegister } = require("./effects"); +const { moveConfig, moveExecutes, effectConfig } = require("./battleConfig"); const { movesToRegister } = require("./moves"); const initialize = () => { + registerEffects(effectsToRegister); + registerLegacyEffects(effectConfig); registerMoves(movesToRegister); registerLegacyMoves(moveConfig, moveExecutes); }; diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 3304cfdb..417c8f34 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -12,7 +12,7 @@ const registerMoves = (moves) => { }; /** - * @param {{[K in MoveIdEnum]: Object}} moveConfig + * @param {{[K in MoveIdEnum]: object}} moveConfig * @param {{[K in MoveIdEnum]: Function}} moveExecutes */ const registerLegacyMoves = (moveConfig, moveExecutes) => { @@ -36,19 +36,16 @@ const registerLegacyMoves = (moveConfig, moveExecutes) => { * @param {MoveIdEnum} moveId * @returns {Move} */ -const getMove = (moveId) => { - return allMoves[moveId]; -}; +const getMove = (moveId) => allMoves[moveId]; /** - * @param {Object} param0 + * @param {object} param0 * @param {MoveIdEnum} param0.moveId - * @param {Object} param0.battle - * @param {Object} param0.source - * @param {Object} param0.primaryTarget - * @param {Array} param0.allTargets - * @param {Array=} param0.missedTargets - * @returns + * @param {object} param0.battle + * @param {object} param0.source + * @param {object} param0.primaryTarget + * @param {Array} param0.allTargets + * @param {Array=} param0.missedTargets */ const executeMove = ({ moveId, @@ -73,7 +70,7 @@ const executeMove = ({ missedTargets, }); } else { - const legacyMove = /** @type{any} */ (move); + const legacyMove = /** @type {any} */ (move); legacyMove.execute( battle, source, diff --git a/src/battleEngine/moves.js b/src/battleEngine/moves.js index 21680f1e..0c76a8ef 100644 --- a/src/battleEngine/moves.js +++ b/src/battleEngine/moves.js @@ -16,7 +16,7 @@ const { moveIdEnum } = require("../enums/battleEnums"); class Move { /** - * @param {Object} param0 + * @param {object} param0 * @param {MoveIdEnum} param0.id * @param {string} param0.name * @param {PokemonTypeEnum} param0.type @@ -63,20 +63,20 @@ class Move { } /** - * @param {Object} param0 + * @param {object} param0 * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.primaryTarget * @param {Array} param0.allTargets * @param {Array=} param0.missedTargets * @param {number=} param0.offTargetDamageMultiplier */ - genericDealDamage = ({ + genericDealDamage({ source, primaryTarget, allTargets, missedTargets = [], offTargetDamageMultiplier = 0.8, - }) => { + }) { const moveData = getMove(this.id); for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -89,7 +89,7 @@ class Move { moveId: this.id, }); } - }; + } } const movesToRegister = Object.freeze({ @@ -107,7 +107,7 @@ const movesToRegister = Object.freeze({ damageType: damageTypes.PHYSICAL, description: "The target is struck with slender, whiplike vines to inflict damage.", - execute: function ({ source, primaryTarget, allTargets, missedTargets }) { + execute({ source, primaryTarget, allTargets, missedTargets }) { this.genericDealDamage({ source, primaryTarget, From 9b5012cc848ed1346735418e8460055ee83ae8a2 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 4 Oct 2024 17:43:27 -0700 Subject: [PATCH 19/38] use getEffect --- src/battleEngine/battleConfig.js | 45 ++++++++++++++++--------------- src/battleEngine/effectService.js | 3 +++ src/services/battle.js | 34 +++++++++++++++-------- src/utils/battleUtils.js | 10 +++---- 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 0d462bd8..ab741ae3 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -6,6 +6,7 @@ const { types: pokemonTypes } = require("../config/pokemonConfig"); const types = require("../../types"); const { getMove, executeMove } = require("./moveService"); +const { getEffect } = require("./effectService"); /** @typedef {types.Enum} BattleEventEnum */ const battleEventNames = Object.freeze({ @@ -1737,7 +1738,7 @@ const effectConfig = Object.freeze({ // only allow buffs to trigger if ( args.effectId && - effectConfig[args.effectId].type !== effectTypes.BUFF + getEffect(args.effectId).type !== effectTypes.BUFF ) { return; } @@ -6918,7 +6919,7 @@ const moveExecutes = { for (const target of allTargets) { // remove all buffs for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.BUFF) { target.dispellEffect(effectId); } @@ -8099,7 +8100,7 @@ const moveExecutes = { for (const target of allTargets) { // remove all debuffs for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.DEBUFF) { target.removeEffect(effectId); } @@ -8181,7 +8182,7 @@ const moveExecutes = { if (!miss) { const possibleBuffs = Object.keys(target.effectIds).filter( (effectId) => { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); return ( effectData.type === effectTypes.BUFF && effectData.dispellable ); @@ -8565,7 +8566,7 @@ const moveExecutes = { let effects = 0; // remove all debuffs for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.DEBUFF) { if (target.dispellEffect(effectId)) { effects += 1; @@ -8879,7 +8880,7 @@ const moveExecutes = { ); for (const ally of allyTargets) { for (const effectId of Object.keys(ally.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type !== effectTypes.DEBUFF) { continue; } @@ -9321,7 +9322,7 @@ const moveExecutes = { // if not miss, remove all buffs if (!miss) { for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type !== effectTypes.BUFF) { continue; } @@ -9599,7 +9600,7 @@ const moveExecutes = { target.removeStatus(); // remove debuffs for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type !== effectTypes.DEBUFF) { continue; } @@ -10691,14 +10692,14 @@ const moveExecutes = { // if ally, dispell debuffs. if enemy, dispell buffs if (target.teamName === source.teamName) { for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.DEBUFF) { target.dispellEffect(effectId); } } } else { for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.BUFF) { target.dispellEffect(effectId); } @@ -10786,7 +10787,7 @@ const moveExecutes = { if (!miss) { const possibleDebuffs = Object.keys(source.effectIds).filter( (effectId) => { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); return ( effectData.type === effectTypes.DEBUFF && effectData.dispellable ); @@ -10896,7 +10897,7 @@ const moveExecutes = { if (!miss) { const possibleBuffs = Object.keys(target.effectIds).filter( (effectId) => { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); return ( effectData.type === effectTypes.BUFF && effectData.dispellable ); @@ -11098,7 +11099,7 @@ const moveExecutes = { let hasDebuff = false; for (const effect of Object.keys(target.effectIds)) { - const effectData = effectConfig[effect]; + const effectData = getEffect(effect); if (effectData.type === effectTypes.DEBUFF) { hasDebuff = true; break; @@ -11941,7 +11942,7 @@ const moveExecutes = { if (!miss) { // remove all buffs for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.BUFF) { target.dispellEffect(effectId); } @@ -11979,7 +11980,7 @@ const moveExecutes = { // increase all buff durations by 1 if dispellable battle.addToLog(`${target.name} is invigorated!`); for (const effectId of Object.keys(target.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.BUFF && effectData.dispellable) { target.effectIds[effectId].duration += 1; } @@ -12568,7 +12569,7 @@ const abilityConfig = { }, execute(initialArgs, args) { const { effectId } = args; - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (!effectData) { return; } @@ -12993,7 +12994,7 @@ const abilityConfig = { } const { effectId } = args; - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (!effectData || effectData.type !== effectTypes.DEBUFF) { return; } @@ -13051,7 +13052,7 @@ const abilityConfig = { const moveData = getMove(args.moveId); if (moveData && moveData.tier === moveTiers.ULTIMATE) { for (const effectId in target.effectIds) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (effectData.type === effectTypes.DEBUFF) { target.dispellEffect(effectId); } @@ -13730,7 +13731,7 @@ const abilityConfig = { return; } - const effectData = effectConfig[args.effectId]; + const effectData = getEffect(args.effectId); if ( !effectData || !effectData.dispellable || @@ -15287,7 +15288,7 @@ const abilityConfig = { } const { effectId } = args; - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (!effectData || effectData.type !== effectTypes.DEBUFF) { return; } @@ -16069,7 +16070,7 @@ const abilityConfig = { } const { effectId } = args; - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (!effectData) { return; } @@ -16098,7 +16099,7 @@ const abilityConfig = { // see if buff let isBuffed = false; for (const effectId of Object.keys(pokemon.effectIds)) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); if (!effectData) { continue; } diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectService.js index eb8d7e3f..a88b3adc 100644 --- a/src/battleEngine/effectService.js +++ b/src/battleEngine/effectService.js @@ -1,3 +1,5 @@ +// eslint-disable-next-line no-unused-vars +const { effectIdEnum } = require("../enums/battleEnums"); // TODO: remove after testing const { logger } = require("../log"); const allEffects = {}; @@ -31,6 +33,7 @@ const registerLegacyEffects = (effectConfig) => { const getEffect = (effectId) => // @ts-ignore allEffects[effectId]; + /** * @template {EffectIdEnum} K * @param {object} param0 diff --git a/src/services/battle.js b/src/services/battle.js index 07867848..61e28be3 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -16,12 +16,9 @@ const { getOrSetDefault, formatMoney } = require("../utils/utils"); const { pokemonConfig, types } = require("../config/pokemonConfig"); const { battleEventNames, - moveExecutes, - moveConfig, targetTypes, targetPatterns, targetPositions, - effectConfig, statusConditions, moveTiers, calculateDamage, @@ -79,8 +76,8 @@ const { generateRandomPokemon } = require("./gacha"); const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); -const { User } = require("discord.js"); const { getMove, executeMove } = require("../battleEngine/moveService"); +const { getEffect } = require("../battleEngine/effectService"); class NPC { constructor( @@ -1513,7 +1510,7 @@ class Pokemon { } duration = this.battle.activePokemon === this ? duration + 1 : duration; - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); // if effect already exists for longer or equal duration, do nothing (special case for shield) if ( @@ -1590,7 +1587,7 @@ class Pokemon { } dispellEffect(effectId) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); // if effect doesn't exist, do nothing if (!this.effectIds[effectId]) { @@ -1605,8 +1602,23 @@ class Pokemon { return this.removeEffect(effectId); } + /** + * @template {EffectIdEnum} K + * @param {K} effectId + * @returns {{ + * duration: number, + * source: Pokemon, + * initialArgs: K extends keyof RegisteredEffects ? EffectInitialArgsType : any, + * args: K extends keyof RegisteredEffects ? EffectPropertiesType : any + * } | undefined} + */ + getEffectInstance(effectId) { + // @ts-ignore + return this.effectIds[effectId]; + } + removeEffect(effectId) { - const effectData = effectConfig[effectId]; + const effectData = getEffect(effectId); // if effect doesn't exist, do nothing if (!this.effectIds[effectId]) { @@ -2047,15 +2059,15 @@ class Battle { isPvp; */ /** - * @param {Object} param0 + * @param {object} param0 * @param {number?=} param0.moneyMultiplier * @param {number?=} param0.expMultiplier * @param {number?=} param0.pokemonExpMultiplier * @param {number?=} param0.level * @param {number?=} param0.equipmentLevel - * @param {Object?=} param0.rewards + * @param {object?=} param0.rewards * @param {string?=} param0.rewardString - * @param {Object?=} param0.dailyRewards + * @param {object?=} param0.dailyRewards * @param {Function?=} param0.winCallback * @param {Function?=} param0.loseCallback * @param {string?=} param0.npcId @@ -3441,7 +3453,7 @@ const onBattleTowerAccept = async ({ stateId = null, user = null } = {}) => { /** * - * @param {Object} param0 + * @param {object} param0 * @param {string?=} param0.stateId * @param {User?=} param0.user * @returns diff --git a/src/utils/battleUtils.js b/src/utils/battleUtils.js index 82531624..ca614b89 100644 --- a/src/utils/battleUtils.js +++ b/src/utils/battleUtils.js @@ -6,14 +6,12 @@ * * battleUtils.js the lowest level of code for battles used by the battle.js */ -const { - effectConfig, - statusConditions, -} = require("../battleEngine/battleConfig"); +const { statusConditions } = require("../battleEngine/battleConfig"); const { difficultyConfig } = require("../config/npcConfig"); const { pokemonConfig, typeConfig } = require("../config/pokemonConfig"); const { getRewardsString, flattenRewards } = require("./trainerUtils"); const { getPBar, formatMoney } = require("./utils"); +const { getEffect } = require("../battleEngine/effectService"); const buildPartyString = ( pokemons, @@ -252,7 +250,9 @@ const buildBattlePokemonString = (pokemon) => { ) { shieldString = `${pokemon.effectIds[effectId].args.shield} HP `; } - return `${shieldString}${effectConfig[effectId].name} (${pokemon.effectIds[effectId].duration})`; + return `${shieldString}${getEffect(effectId).name} (${ + pokemon.effectIds[effectId].duration + })`; }) .join(", ")}`; } else { From 324b9e8473d7be64ab162319cf67181d3b8cfe1e Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 4 Oct 2024 18:34:35 -0700 Subject: [PATCH 20/38] effect add and remove --- src/battleEngine/battleConfig.js | 16 +++++++++-- src/battleEngine/battleTypes.js | 4 +-- src/battleEngine/effectService.js | 8 +++--- src/battleEngine/effects.js | 15 ++++++----- src/services/battle.js | 45 +++++++++++++++++++++++-------- 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index ab741ae3..67f7e669 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -6576,6 +6576,18 @@ const moveConfig = Object.freeze({ }, }); +/** + * @callback LegacyMoveExecute + * @param {Battle} battle + * @param {BattlePokemon} source + * @param {BattlePokemon} primaryTarget + * @param {BattlePokemon[]} allTargets + * @param {BattlePokemon[]} missedTargets + * @returns {void} + */ +/** + * @type {Record} + */ const moveExecutes = { m6(battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m6"; @@ -7539,8 +7551,8 @@ const moveExecutes = { "m97-1": function (_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply atkUp, speUp buff - target.addEffect("atkUp", 4, source); - target.addEffect("speUp", 4, source); + target.addEffect("atkUp", 4, source, undefined); + target.addEffect("speUp", 4, source, undefined); // boost combat readiness by 60 target.boostCombatReadiness(source, 80); diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js index 36db7ca5..91456fc2 100644 --- a/src/battleEngine/battleTypes.js +++ b/src/battleEngine/battleTypes.js @@ -48,25 +48,25 @@ * @template T * @template U * @callback EffectAddCallback + * @this {Effect} // not having any bugs out the template * @param {object} param0 * @param {Battle} param0.battle * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.target * @param {T} param0.initialArgs * @returns {U} - * @this {Effect} */ /** * @template T * @template U * @callback EffectRemoveCallback + * @this {Effect} * @param {object} param0 * @param {Battle} param0.battle * @param {BattlePokemon} param0.target * @param {T} param0.initialArgs * @param {U} param0.properties - * @this {Effect} */ /** diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectService.js index a88b3adc..94f742ec 100644 --- a/src/battleEngine/effectService.js +++ b/src/battleEngine/effectService.js @@ -42,6 +42,7 @@ const getEffect = (effectId) => * @param {BattlePokemon} param0.source * @param {BattlePokemon} param0.target * @param {EffectInitialArgsTypeFromId} param0.initialArgs + * @returns {EffectPropertiesTypeFromId | undefined} */ const applyEffect = ({ effectId, battle, source, target, initialArgs }) => { const effect = getEffect(effectId); @@ -50,16 +51,15 @@ const applyEffect = ({ effectId, battle, source, target, initialArgs }) => { return; } if (!effect.isLegacyEffect) { - effect.effectAdd({ + return effect.effectAdd({ battle, source, target, initialArgs, }); - } else { - const legacyEffect = /** @type {any} */ (effect); - legacyEffect.effectAdd(battle, source, target, initialArgs); } + const legacyEffect = /** @type {any} */ (effect); + return legacyEffect.effectAdd(battle, source, target, initialArgs); }; /** diff --git a/src/battleEngine/effects.js b/src/battleEngine/effects.js index 0ebbe0b7..98ab1de9 100644 --- a/src/battleEngine/effects.js +++ b/src/battleEngine/effects.js @@ -45,21 +45,22 @@ const effectsToRegister = Object.freeze({ type: effectTypes.BUFF, dispellable: true, /** - * @param {EffectAddBasicArgs & {initialArgs: null}} args + * @param {EffectAddBasicArgs & {initialArgs: any}} args */ effectAdd({ battle, target }) { // if greaterAtkUp exists on target, remove atkUp and refresh greaterAtkUp - if (target.effectIds.greaterAtkUp) { - const currentDuration = target.effectIds.atkUp.duration; - delete target.effectIds.atkUp; - if (target.effectIds.greaterAtkUp.duration < currentDuration) { - target.effectIds.greaterAtkUp.duration = currentDuration; + const greaterAtkUpInstance = target.getEffectInstance("greaterAtkUp"); + const thisInstance = target.getEffectInstance(effectIdEnum.ATK_UP); + if (greaterAtkUpInstance) { + const currentDuration = thisInstance?.duration ?? 0; + if (greaterAtkUpInstance.duration < currentDuration) { + greaterAtkUpInstance.duration = currentDuration; } + target.deleteEffectInstance(effectIdEnum.ATK_UP); } else { battle.addToLog(`${target.name}'s Attack rose!`); target.atk += Math.floor(target.batk * 0.5); } - return {}; }, effectRemove({ battle, target }) { battle.addToLog(`${target.name}'s Attack boost wore off!`); diff --git a/src/services/battle.js b/src/services/battle.js index 61e28be3..275b1ebb 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -77,7 +77,11 @@ const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); const { getMove, executeMove } = require("../battleEngine/moveService"); -const { getEffect } = require("../battleEngine/effectService"); +const { + getEffect, + applyEffect, + removeEffect, +} = require("../battleEngine/effectService"); class NPC { constructor( @@ -1503,6 +1507,13 @@ class Pokemon { return combatReadinessLost; } + /** + * @template {EffectIdEnum} K + * @param {K} effectId + * @param {number} duration + * @param {BattlePokemon} source + * @param {EffectInitialArgsTypeFromId} args + */ addEffect(effectId, duration, source, args) { // if faint, do nothing if (this.isFainted) { @@ -1510,7 +1521,6 @@ class Pokemon { } duration = this.battle.activePokemon === this ? duration + 1 : duration; - const effectData = getEffect(effectId); // if effect already exists for longer or equal duration, do nothing (special case for shield) if ( @@ -1567,7 +1577,13 @@ class Pokemon { this.effectIds[effectId].args = oldShield; } this.effectIds[effectId].args = - effectData.effectAdd(this.battle, source, this, args) || {}; + applyEffect({ + effectId, + battle: this.battle, + source, + target: this, + initialArgs: args, + }) || {}; if (this.effectIds[effectId] !== undefined) { // trigger after add effect events @@ -1617,20 +1633,27 @@ class Pokemon { return this.effectIds[effectId]; } - removeEffect(effectId) { - const effectData = getEffect(effectId); + /** + * Forcefully deletes the effect instance from this Pokemon. ONLY USE to bypass effect removal logic. + * @param {EffectIdEnum} effectId + */ + deleteEffectInstance(effectId) { + delete this.effectIds[effectId]; + } + removeEffect(effectId) { // if effect doesn't exist, do nothing if (!this.effectIds[effectId]) { return false; } - effectData.effectRemove( - this.battle, - this, - this.effectIds[effectId].args, - this.effectIds[effectId].initialArgs - ); + removeEffect({ + effectId, + battle: this.battle, + target: this, + properties: this.effectIds[effectId].args, + initialArgs: this.effectIds[effectId].initialArgs, + }); if (this.effectIds[effectId] !== undefined) { const afterRemoveArgs = { From 2c6c5fa22421231c33efbf68dfc826ea5656197f Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 4 Oct 2024 19:20:07 -0700 Subject: [PATCH 21/38] migrate one effect --- src/battleEngine/battleConfig.js | 23 ----------------------- src/battleEngine/moves.js | 3 +-- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 67f7e669..83378e1d 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -413,29 +413,6 @@ const effectConfig = Object.freeze({ battle.addToLog(`${target.name}'s shield was removed!`); }, }, - atkUp: { - name: "Atk. Up", - description: "The target's Attack increased.", - type: effectTypes.BUFF, - dispellable: true, - effectAdd(battle, _source, target) { - // if greaterAtkUp exists on target, remove atkUp and refresh greaterAtkUp - if (target.effectIds.greaterAtkUp) { - const currentDuration = target.effectIds.atkUp.duration; - delete target.effectIds.atkUp; - if (target.effectIds.greaterAtkUp.duration < currentDuration) { - target.effectIds.greaterAtkUp.duration = currentDuration; - } - } else { - battle.addToLog(`${target.name}'s Attack rose!`); - target.atk += Math.floor(target.batk * 0.5); - } - }, - effectRemove(battle, target) { - battle.addToLog(`${target.name}'s Attack boost wore off!`); - target.atk -= Math.floor(target.batk * 0.5); - }, - }, greaterAtkUp: { name: "Greater Atk. Up", description: "The target's Attack greatly increased.", diff --git a/src/battleEngine/moves.js b/src/battleEngine/moves.js index 0c76a8ef..06fb39ff 100644 --- a/src/battleEngine/moves.js +++ b/src/battleEngine/moves.js @@ -77,10 +77,9 @@ class Move { missedTargets = [], offTargetDamageMultiplier = 0.8, }) { - const moveData = getMove(this.id); for (const target of allTargets) { const miss = missedTargets.includes(target); - const damageToDeal = calculateDamage(moveData, source, target, miss, { + const damageToDeal = calculateDamage(this, source, target, miss, { finalDamageMultiplier: target === primaryTarget ? 1 : offTargetDamageMultiplier, }); From 53346f0af02be40caa466fbfc263b8a460419401 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 4 Oct 2024 20:04:55 -0700 Subject: [PATCH 22/38] migrate shield effect --- src/battleEngine/battleConfig.js | 26 -------------------------- src/battleEngine/battleTypes.js | 2 +- src/battleEngine/effectService.js | 13 +++++++++++++ src/battleEngine/effects.js | 27 +++++++++++++++++++++++++++ src/battleEngine/moveService.js | 12 ++++++++++++ src/battleEngine/moves.js | 14 ++++++++++---- src/enums/battleEnums.js | 1 + tsconfig.json | 5 ++--- 8 files changed, 66 insertions(+), 34 deletions(-) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 83378e1d..bc58e707 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -387,32 +387,6 @@ const effectTypes = Object.freeze({ /** @typedef {types.Keys} LegacyEffectIdEnum */ const effectConfig = Object.freeze({ - shield: { - name: "Shield", - description: "The target's takes shielded damage.", - type: effectTypes.BUFF, - dispellable: true, - effectAdd(battle, _source, target, initialArgs) { - const shield = initialArgs && initialArgs.shield; - if (!shield) { - return {}; - } - - let oldShield = - target.effectIds.shield && - target.effectIds.shield.args && - target.effectIds.shield.args.shield; - oldShield = oldShield || 0; - - const newShield = Math.max(oldShield, shield); - battle.addToLog(`${target.name} is shielded for ${newShield} damage!`); - initialArgs.shield = newShield; - return initialArgs; - }, - effectRemove(battle, target) { - battle.addToLog(`${target.name}'s shield was removed!`); - }, - }, greaterAtkUp: { name: "Greater Atk. Up", description: "The target's Attack greatly increased.", diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js index 91456fc2..f1515fea 100644 --- a/src/battleEngine/battleTypes.js +++ b/src/battleEngine/battleTypes.js @@ -30,7 +30,7 @@ /** * @template T - * @typedef {T extends Effect ? V | {} : never} EffectPropertiesType + * @typedef {T extends Effect ? V & {} : never} EffectPropertiesType */ /** diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectService.js index 94f742ec..ed42cf66 100644 --- a/src/battleEngine/effectService.js +++ b/src/battleEngine/effectService.js @@ -8,21 +8,33 @@ const allEffects = {}; * @param {Record>} effects */ const registerEffects = (effects) => { + let effectsRegistered = 0; Object.entries(effects).forEach(([effectId, effect]) => { allEffects[effectId] = effect; + effectsRegistered += 1; }); + logger.info(`Registered ${effectsRegistered} effects.`); }; /** * @param {Record} effectConfig */ const registerLegacyEffects = (effectConfig) => { + let effectsRegistered = 0; Object.entries(effectConfig).forEach(([effectId, effect]) => { + if (allEffects[effectId]) { + logger.warn( + `Effect ${effectId} ${allEffects[effectId].name} already exists. Continuing...` + ); + return; + } allEffects[effectId] = { ...effect, isLegacyEffect: true, }; + effectsRegistered += 1; }); + logger.info(`Registered ${effectsRegistered} legacy effects.`); }; /** @@ -84,6 +96,7 @@ const removeEffect = ({ return; } if (!effect.isLegacyEffect) { + // @ts-ignore effect.effectRemove({ battle, target, diff --git a/src/battleEngine/effects.js b/src/battleEngine/effects.js index 98ab1de9..898ab4a2 100644 --- a/src/battleEngine/effects.js +++ b/src/battleEngine/effects.js @@ -67,6 +67,33 @@ const effectsToRegister = Object.freeze({ target.atk -= Math.floor(target.batk * 0.5); }, }), + [effectIdEnum.SHIELD]: new Effect({ + id: effectIdEnum.SHIELD, + name: "Shield", + description: "The target's takes shielded damage.", + type: effectTypes.BUFF, + dispellable: true, + /** + * @param {EffectAddBasicArgs & {initialArgs: {shield: number}}} args + * @returns {{shield?: number}} + */ + effectAdd({ battle, target, initialArgs }) { + const shield = initialArgs && initialArgs.shield; + if (!shield) { + return {}; + } + + const oldShield = + target.getEffectInstance(effectIdEnum.SHIELD)?.args?.shield ?? 0; + const newShield = Math.max(oldShield, shield); + battle.addToLog(`${target.name} is shielded for ${newShield} damage!`); + initialArgs.shield = newShield; + return initialArgs; + }, + effectRemove({ battle, target }) { + battle.addToLog(`${target.name}'s shield was removed!`); + }, + }), }); module.exports = { diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 417c8f34..7c6109c4 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -6,9 +6,12 @@ const allMoves = {}; * @param {{[K in MoveIdEnum]: Move}} moves */ const registerMoves = (moves) => { + let movesRegistered = 0; Object.entries(moves).forEach(([moveId, move]) => { allMoves[moveId] = move; + movesRegistered += 1; }); + logger.info(`Registered ${movesRegistered} moves.`); }; /** @@ -16,7 +19,14 @@ const registerMoves = (moves) => { * @param {{[K in MoveIdEnum]: Function}} moveExecutes */ const registerLegacyMoves = (moveConfig, moveExecutes) => { + let movesRegistered = 0; Object.entries(moveConfig).forEach(([moveId, move]) => { + if (allMoves[moveId]) { + logger.warn( + `Move ${moveId} ${allMoves[moveId].name} already exists. Continuing...` + ); + return; + } const moveExecute = moveExecutes[moveId]; if (!moveExecute) { logger.warn( @@ -29,7 +39,9 @@ const registerLegacyMoves = (moveConfig, moveExecutes) => { execute: moveExecutes[moveId], isLegacyMove: true, }; + movesRegistered += 1; }); + logger.info(`Registered ${movesRegistered} legacy moves.`); }; /** diff --git a/src/battleEngine/moves.js b/src/battleEngine/moves.js index 06fb39ff..b411ffbf 100644 --- a/src/battleEngine/moves.js +++ b/src/battleEngine/moves.js @@ -79,10 +79,16 @@ class Move { }) { for (const target of allTargets) { const miss = missedTargets.includes(target); - const damageToDeal = calculateDamage(this, source, target, miss, { - finalDamageMultiplier: - target === primaryTarget ? 1 : offTargetDamageMultiplier, - }); + const damageToDeal = calculateDamage( + getMove(this.id), + source, + target, + miss, + { + finalDamageMultiplier: + target === primaryTarget ? 1 : offTargetDamageMultiplier, + } + ); source.dealDamage(damageToDeal, target, { type: "move", moveId: this.id, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index 9aff0050..6d054021 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -9,6 +9,7 @@ const types = require("../../types"); const effectIdEnum = Object.freeze({ TEST_EFFECT: "testEffect", ATK_UP: "atkUp", + SHIELD: "shield", }); /** diff --git a/tsconfig.json b/tsconfig.json index b70c8128..cd762d56 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,14 @@ { "compilerOptions": { "outDir": "./dummy", - "target": "es2021" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, + "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, "module": "commonjs", "noEmit": true, - //"lib": ["ES6"], + //"lib": ["es2020"], "checkJs": true, "allowJs": true, "skipLibCheck": true, "moduleResolution": "node", - "strictNullChecks": true, "allowSyntheticDefaultImports": true }, "exclude": ["node_modules"], From 117a2569fab2982e6d02a50e93e3f2ba141bad3f Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 5 Oct 2024 22:05:24 -0700 Subject: [PATCH 23/38] improve move registry --- eslint.config.mjs | 2 +- src/battleEngine/battleConfig.js | 1 - src/battleEngine/moveService.js | 49 ++++++++++++++++++++++++++++++++ types.js | 30 +++++++++++-------- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index d37bf948..a862fb8f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -34,7 +34,7 @@ export default [ "guard-for-in": "off", // TODO: enable but make better lol "no-continue": "off", // doesn't seem to work properly "no-await-in-loop": "off", // TODO: use promise.all - "jsdoc/no-undefined-types": "off", + "jsdoc/no-undefined-types": "silent", // doesn't seem to work with TS types "jsdoc/require-param-description": "off", // TODO: maybe add descriptions "jsdoc/require-returns-description": "off", // TODO: maybe add descriptions "jsdoc/valid-types": "off", // doesn't seem to work with TS types diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index bc58e707..1e31f253 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -16578,5 +16578,4 @@ module.exports = { calculateDamage, abilityConfig, damageTypes, - calculateDamage, }; diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 7c6109c4..4f7bd783 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -1,4 +1,5 @@ const { logger } = require("../log"); +const types = require("../../types"); const allMoves = {}; @@ -50,6 +51,52 @@ const registerLegacyMoves = (moveConfig, moveExecutes) => { */ const getMove = (moveId) => allMoves[moveId]; +/** + * @param {object} param0 + * @param {Record} param0.fieldFilter + * @param {Function} param0.customFilter + * @returns {types.PartialRecord} + */ +const getMoves = ({ fieldFilter, customFilter }) => { + if (customFilter) { + return Object.entries(allMoves).reduce((acc, [moveId, move]) => { + if (customFilter(move)) { + acc[moveId] = move; + } + return acc; + }, {}); + } + + if (fieldFilter) { + return Object.entries(allMoves).reduce((acc, [moveId, move]) => { + let isValid = true; + Object.entries(fieldFilter).forEach(([field, value]) => { + if (move[field] !== value) { + isValid = false; + } + }); + if (isValid) { + acc[moveId] = move; + } + return acc; + }, {}); + } + + return allMoves; +}; + +/** + * @param {object} param0 + * @param {Record} param0.fieldFilter + * @param {Function} param0.customFilter + * @returns {Array} + */ +const getMoveIds = ({ fieldFilter, customFilter }) => { + const moves = getMoves({ fieldFilter, customFilter }); + // @ts-ignore + return Object.keys(moves); +}; + /** * @param {object} param0 * @param {MoveIdEnum} param0.moveId @@ -97,5 +144,7 @@ module.exports = { registerMoves, registerLegacyMoves, getMove, + getMoves, + getMoveIds, executeMove, }; diff --git a/types.js b/types.js index d1bee560..d8f726d0 100644 --- a/types.js +++ b/types.js @@ -13,7 +13,13 @@ module.exports._typesOnly = true; */ /** - * @typedef {Object} CompactUser + * @template {string | number | symbol} T + * @template U + * @typedef {{ [K in T]?: U; }} PartialRecord + */ + +/** + * @typedef {object} CompactUser * @property {string} id * @property {string} username * @property {string?} discriminator @@ -21,27 +27,27 @@ module.exports._typesOnly = true; */ /** - * @typedef {Object} UserVotingInfo + * @typedef {object} UserVotingInfo * @property {number} lastVoted * @property {number} streak * @property {number} rewards */ /** - * @typedef {Object} UserTradeInfo + * @typedef {object} UserTradeInfo * @property {number} money * @property {Array} pokemonIds */ /** - * @typedef {Object} PartyInfo + * @typedef {object} PartyInfo * @property {Array} pokemonIds * @property {number} rows * @property {number} cols */ /** - * @typedef {Object} Trainer + * @typedef {object} Trainer * * Discord user info * @property {string} userId @@ -52,17 +58,17 @@ module.exports._typesOnly = true; * @property {number} level * @property {number} exp * @property {number} money - * @property {Object} backpack - * @property {Object} locations + * @property {object} backpack + * @property {object} locations * @property {UserTradeInfo} trade * * Rewards and time-gated stuff * @property {number} lastDaily * @property {boolean} claimedDaily - * @property {Object} purchasedShopItemsToday + * @property {object} purchasedShopItemsToday * @property {Array} claimedLevelRewards - * @property {Object} defeatedNPCsToday - * @property {Object} defeatedNPCs + * @property {object} defeatedNPCsToday + * @property {object} defeatedNPCs * @property {number} lastTowerStage * * Party info @@ -71,7 +77,7 @@ module.exports._typesOnly = true; * * Banner * @property {number} beginnerRolls - * @property {Object} banners + * @property {object} banners * * Mythic Pokemon * @property {boolean} hasCelebi @@ -79,7 +85,7 @@ module.exports._typesOnly = true; */ /** - * @typedef {Object} Guild + * @typedef {object} Guild * @property {string} guildId * @property {number} lastCommand * @property {boolean} spawnDisabled From e3b5747679c713ab8f74463ebe3d04b30d4ea696 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 5 Oct 2024 22:11:29 -0700 Subject: [PATCH 24/38] remove remaining move config usages --- src/battleEngine/battleConfig.js | 18 +++++++++--------- src/battleEngine/moveService.js | 8 ++++---- src/services/mythic.js | 10 +++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 1e31f253..4a58054e 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -5,7 +5,7 @@ /* eslint-disable no-param-reassign */ const { types: pokemonTypes } = require("../config/pokemonConfig"); const types = require("../../types"); -const { getMove, executeMove } = require("./moveService"); +const { getMove, getMoveIds, executeMove } = require("./moveService"); const { getEffect } = require("./effectService"); /** @typedef {types.Enum} BattleEventEnum */ @@ -2235,7 +2235,7 @@ const effectConfig = Object.freeze({ battle.addToLog( `${target.name} was hit by ${source.name}'s Future Sight!` ); - const damageCalc = calculateDamage(moveConfig.m248, source, target); + const damageCalc = calculateDamage(getMove("m248"), source, target); // calculate damage based on remaining duration. 0 => 100%, 1 => 50%, >= 2 => 25% let damageToDeal = damageCalc; if (remainingDuration === 0) { @@ -7517,7 +7517,7 @@ const moveExecutes = { } }, m98(_battle, source, _primaryTarget, allTargets, missedTargets) { - const moveData = moveConfig.m98; + const moveData = getMove("m98"); for (const target of allTargets) { const miss = missedTargets.includes(target); const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -7691,9 +7691,9 @@ const moveExecutes = { }, m118(battle, source) { // get random basic moves - const basicMoves = Object.keys(moveConfig).filter( - (moveId) => getMove(moveId).tier === moveTiers.BASIC - ); + const basicMoves = getMoveIds({ + fieldFilter: { tier: moveTiers.BASIC }, + }); const randomMoveId = basicMoves[Math.floor(Math.random() * basicMoves.length)]; const randomMoveData = getMove(randomMoveId); @@ -9618,7 +9618,7 @@ const moveExecutes = { } }, m322(_battle, source, _primaryTarget, allTargets, _missedTargets) { - const moveData = moveConfig.m322; + const moveData = getMove("m322"); for (const target of allTargets) { // raise def, spd target.addEffect("defUp", 3, source); @@ -9727,7 +9727,7 @@ const moveExecutes = { } }, m334(_battle, source, _primaryTarget, allTargets, _missedTargets) { - const moveData = moveConfig.m334; + const moveData = getMove("m334"); for (const target of allTargets) { // sharply raise def target.addEffect("greaterDefUp", 3, source); @@ -9877,7 +9877,7 @@ const moveExecutes = { } }, m349(_battle, source, _primaryTarget, allTargets, _missedTargets) { - const moveData = moveConfig.m349; + const moveData = getMove("m349"); for (const target of allTargets) { // raise attack and speed target.addEffect("atkUp", 3, source); diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 4f7bd783..0612a1b7 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -53,8 +53,8 @@ const getMove = (moveId) => allMoves[moveId]; /** * @param {object} param0 - * @param {Record} param0.fieldFilter - * @param {Function} param0.customFilter + * @param {Record=} param0.fieldFilter + * @param {Function=} param0.customFilter * @returns {types.PartialRecord} */ const getMoves = ({ fieldFilter, customFilter }) => { @@ -87,8 +87,8 @@ const getMoves = ({ fieldFilter, customFilter }) => { /** * @param {object} param0 - * @param {Record} param0.fieldFilter - * @param {Function} param0.customFilter + * @param {Record=} param0.fieldFilter + * @param {Function=} param0.customFilter * @returns {Array} */ const getMoveIds = ({ fieldFilter, customFilter }) => { diff --git a/src/services/mythic.js b/src/services/mythic.js index 2515c323..e9ff34bc 100644 --- a/src/services/mythic.js +++ b/src/services/mythic.js @@ -13,7 +13,6 @@ const { backpackItemConfig, backpackItems, } = require("../config/backpackConfig"); -const { moveConfig } = require("../battleEngine/battleConfig"); const { collectionNames } = require("../config/databaseConfig"); const { eventNames } = require("../config/eventConfig"); const { getCelebiPool } = require("../config/gachaConfig"); @@ -47,6 +46,7 @@ const { calculateAndUpdatePokemonStats, } = require("./pokemon"); const { getTrainer, updateTrainer } = require("./trainer"); +const { getMoves } = require("../battleEngine/moveService"); const getMythic = async (trainer, speciesId) => { const speciesData = pokemonConfig[speciesId]; @@ -259,25 +259,25 @@ const buildMewSend = async ({ user = null, tab = "basic" } = {}) => { selectIds = mythicConfig.basicMoveIds; // filter out moves that mew already knows selectIds = selectIds.filter((moveId) => !mew.moveIds.includes(moveId)); - selectConfig = moveConfig; + selectConfig = getMoves({}); break; case "power1": selectIds = mythicConfig.powerMoveIds; // filter out moves that mew already knows selectIds = selectIds.filter((moveId) => !mew.moveIds.includes(moveId)); - selectConfig = moveConfig; + selectConfig = getMoves({}); break; case "power2": selectIds = mythicConfig.powerMoveIds; // filter out moves that mew already knows selectIds = selectIds.filter((moveId) => !mew.moveIds.includes(moveId)); - selectConfig = moveConfig; + selectConfig = getMoves({}); break; case "ultimate": selectIds = mythicConfig.ultimateMoveIds; // filter out moves that mew already knows selectIds = selectIds.filter((moveId) => !mew.moveIds.includes(moveId)); - selectConfig = moveConfig; + selectConfig = getMoves({}); break; case "nature": selectIds = Object.keys(natureConfig); From da3ffa577a9476d0d5f8e4134c65831046071e4d Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sat, 5 Oct 2024 22:18:44 -0700 Subject: [PATCH 25/38] improve effect registry --- src/battleEngine/effectService.js | 46 +++++++++++++++++++++++++++++++ src/battleEngine/moveService.js | 11 ++++---- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectService.js index ed42cf66..6b63f529 100644 --- a/src/battleEngine/effectService.js +++ b/src/battleEngine/effectService.js @@ -1,6 +1,7 @@ // eslint-disable-next-line no-unused-vars const { effectIdEnum } = require("../enums/battleEnums"); // TODO: remove after testing const { logger } = require("../log"); +const types = require("../../types"); const allEffects = {}; @@ -46,6 +47,49 @@ const getEffect = (effectId) => // @ts-ignore allEffects[effectId]; +/** + * @param {Object} param0 + * @param {Record=} param0.fieldFilter + * @param {Function=} param0.customFilter + * @returns {types.PartialRecord>} + */ +const getEffects = ({ fieldFilter, customFilter }) => { + if (customFilter) { + return Object.entries(allEffects).reduce((acc, [effectId, effect]) => { + if (customFilter(effect)) { + acc[effectId] = effect; + } + return acc; + }, {}); + } + + if (fieldFilter) { + return Object.entries(allEffects).reduce((acc, [effectId, effect]) => { + Object.entries(fieldFilter).forEach(([field, value]) => { + if (effect[field] !== value) { + return acc; + } + }); + acc[effectId] = effect; + return acc; + }, {}); + } + + return { + ...allEffects, + }; +}; +/** + * @param {Object} param0 + * @param {Record=} param0.fieldFilter + * @param {Function=} param0.customFilter + * @returns {EffectIdEnum[]} + */ +const getEffectIds = ({ fieldFilter, customFilter }) => { + const effects = getEffects({ fieldFilter, customFilter }); + // @ts-ignore + return Object.keys(effects); +}; /** * @template {EffectIdEnum} K * @param {object} param0 @@ -113,6 +157,8 @@ module.exports = { registerEffects, registerLegacyEffects, getEffect, + getEffects, + getEffectIds, applyEffect, removeEffect, }; diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 0612a1b7..015cce46 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -69,20 +69,19 @@ const getMoves = ({ fieldFilter, customFilter }) => { if (fieldFilter) { return Object.entries(allMoves).reduce((acc, [moveId, move]) => { - let isValid = true; Object.entries(fieldFilter).forEach(([field, value]) => { if (move[field] !== value) { - isValid = false; + return acc; } }); - if (isValid) { - acc[moveId] = move; - } + acc[moveId] = move; return acc; }, {}); } - return allMoves; + return { + ...allMoves, + }; }; /** From 5eea95f0d9c02eb4ea0a266bdcbbdba97c52258c Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 16:51:53 -0700 Subject: [PATCH 26/38] small rename --- src/services/battle.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/battle.js b/src/services/battle.js index 275b1ebb..a6fcff94 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -607,7 +607,7 @@ class Pokemon { this.effectIds = {}; // map moveId => move data (cooldown, disabled) this.addMoves(pokemonData); - this.addAbility(pokemonData); + this.setAbility(pokemonData); this.status = { statusId: null, turns: 0, @@ -631,7 +631,7 @@ class Pokemon { }, {}); } - addAbility(pokemonData) { + setAbility(pokemonData) { this.ability = { abilityId: pokemonData.abilityId, }; @@ -675,7 +675,7 @@ class Pokemon { // set moves and ability this.addMoves(this.pokemonData); - this.addAbility(this.pokemonData); + this.setAbility(this.pokemonData); this.applyAbility(); this.battle.addToLog(`${oldName} transformed into ${this.name}!`); From 5879c98cf0763b20ebe13411be0bbf7fc05ab37d Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 17:14:28 -0700 Subject: [PATCH 27/38] effect registry refactor --- src/battleEngine/battleConfig.js | 467 +++++++++--------- .../{effectService.js => effectRegistry.js} | 68 +-- src/battleEngine/initialize.js | 2 +- src/battleEngine/moveService.js | 4 +- src/services/battle.js | 72 ++- src/utils/battleUtils.js | 2 +- 6 files changed, 289 insertions(+), 326 deletions(-) rename src/battleEngine/{effectService.js => effectRegistry.js} (59%) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 4a58054e..4d67bb2b 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -6,7 +6,7 @@ const { types: pokemonTypes } = require("../config/pokemonConfig"); const types = require("../../types"); const { getMove, getMoveIds, executeMove } = require("./moveService"); -const { getEffect } = require("./effectService"); +const { getEffect } = require("./effectRegistry"); /** @typedef {types.Enum} BattleEventEnum */ const battleEventNames = Object.freeze({ @@ -2347,7 +2347,7 @@ const effectConfig = Object.freeze({ `${targetPokemon.name}'s holds a grudge!` ); for (const enemyPokemon of enemyPokemons) { - enemyPokemon.addEffect("silenced", 2, targetPokemon); + enemyPokemon.applyEffect("silenced", 2, targetPokemon); } }, }; @@ -6661,7 +6661,7 @@ const moveExecutes = { m14(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply greater atk up for 3 turns - target.addEffect("greaterAtkUp", 3, source); + target.applyEffect("greaterAtkUp", 3, source); // gain 60% cr source.boostCombatReadiness(source, 60); } @@ -6723,7 +6723,7 @@ const moveExecutes = { // if not miss, flinch with 30% chance if (!miss && Math.random() < 0.3) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -6804,7 +6804,7 @@ const moveExecutes = { // if not miss, apply 1/8 hp DoT for 2 turns if (!miss) { - target.addEffect("dot", 2, source, { + target.applyEffect("dot", 2, source, { damage: Math.max(Math.floor(target.maxHp / 8), 1), }); } @@ -6874,7 +6874,7 @@ const moveExecutes = { const miss = missedTargets.includes(target); if (!miss) { // def down 2 turns - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } } }, @@ -6903,7 +6903,7 @@ const moveExecutes = { for (const target of allTargets) { const miss = missedTargets.includes(target); if (!miss) { - target.addEffect("disable", 1, source); + target.applyEffect("disable", 1, source); } } }, @@ -6920,7 +6920,7 @@ const moveExecutes = { // 20% chance spd down 1 turn if (!miss && Math.random() < 0.2) { - target.addEffect("spdDown", 1, source); + target.applyEffect("spdDown", 1, source); } } }, @@ -7029,8 +7029,8 @@ const moveExecutes = { }); // reduce atk and spa for 1 turn - target.addEffect("atkDown", 1, source); - target.addEffect("spaDown", 1, source); + target.applyEffect("atkDown", 1, source); + target.applyEffect("spaDown", 1, source); } }, m57(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -7097,7 +7097,7 @@ const moveExecutes = { // if not miss, 50% chance to confuse for 1 turn if (!miss && Math.random() < 0.5) { - target.addEffect("confused", 1, source); + target.applyEffect("confused", 1, source); } } }, @@ -7118,7 +7118,7 @@ const moveExecutes = { ); } // apply recharge to self - source.addEffect("recharge", 1, source); + source.applyEffect("recharge", 1, source); }, m64(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m64"; @@ -7147,7 +7147,7 @@ const moveExecutes = { m68(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply counter 2 turn - target.addEffect("counter", 2, source); + target.applyEffect("counter", 2, source); } }, m70(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -7203,7 +7203,7 @@ const moveExecutes = { } // apply leech seed 5 turns - target.addEffect("leechSeed", 5, source); + target.applyEffect("leechSeed", 5, source); } }, m76(battle, source, _primaryTarget, allTargets, missedTargets) { @@ -7215,7 +7215,7 @@ const moveExecutes = { !battle.isWeatherNegated() && battle.weather.weatherId !== weatherConditions.SUN ) { - source.addEffect("absorbLight", 1, source); + source.applyEffect("absorbLight", 1, source); // remove solar beam cd source.moveIds[moveId].cooldown = 0; } else { @@ -7289,7 +7289,7 @@ const moveExecutes = { } // sharply lower speed for 1 turn - target.addEffect("greaterSpeDown", 1, source); + target.applyEffect("greaterSpeDown", 1, source); // lower cr by 15% target.reduceCombatReadiness(source, 15); } @@ -7385,7 +7385,7 @@ const moveExecutes = { } // add spe down to user 1 turn - source.addEffect("speDown", 1, source); + source.applyEffect("speDown", 1, source); }, m88(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m88"; @@ -7429,7 +7429,7 @@ const moveExecutes = { const moveData = getMove(moveId); // if pokemon doesnt have "burrowed" buff, apply it if (source.effectIds.burrowed === undefined) { - source.addEffect("burrowed", 1, source); + source.applyEffect("burrowed", 1, source); // remove dig cd source.moveIds[moveId].cooldown = 0; } else { @@ -7470,7 +7470,7 @@ const moveExecutes = { // if not miss, 25% to confuse if (!miss && Math.random() < 0.25) { - target.addEffect("confused", 1, source); + target.applyEffect("confused", 1, source); } } }, @@ -7487,14 +7487,14 @@ const moveExecutes = { // if not miss, 60% chance to spd down 1 turn if (!miss && Math.random() < 0.6) { - target.addEffect("spdDown", 1, source); + target.applyEffect("spdDown", 1, source); } } }, m97(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply greaterSpeUp buff - target.addEffect("greaterSpeUp", 4, source); + target.applyEffect("greaterSpeUp", 4, source); // boost combat readiness by 80 target.boostCombatReadiness(source, 80); } @@ -7502,8 +7502,8 @@ const moveExecutes = { "m97-1": function (_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply atkUp, speUp buff - target.addEffect("atkUp", 4, source, undefined); - target.addEffect("speUp", 4, source, undefined); + target.applyEffect("atkUp", 4, source, undefined); + target.applyEffect("speUp", 4, source, undefined); // boost combat readiness by 60 target.boostCombatReadiness(source, 80); @@ -7572,7 +7572,7 @@ const moveExecutes = { const ultimateMoveId = ultimateMoveIds[0]; // replace move with ultimate move - source.addEffect("mimic", 3, source, { + source.applyEffect("mimic", 3, source, { moveId: ultimateMoveId, oldMoveId, }); @@ -7586,7 +7586,7 @@ const moveExecutes = { const miss = missedTargets.includes(target); if (!miss) { // greater def down for 2 turns - target.addEffect("greaterDefDown", 2, source); + target.applyEffect("greaterDefDown", 2, source); } } }, @@ -7604,9 +7604,9 @@ const moveExecutes = { m106(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // def up for 2 turns - target.addEffect("defUp", 2, source); + target.applyEffect("defUp", 2, source); // gain 15% def as shield - target.addEffect("shield", 2, source, { + target.applyEffect("shield", 2, source, { shield: Math.floor(target.getDef() * 0.15), }); } @@ -7616,22 +7616,22 @@ const moveExecutes = { const miss = missedTargets.includes(target); // if not miss, greater acc down 2 turns if (!miss) { - target.addEffect("greaterAccDown", 2, source); + target.applyEffect("greaterAccDown", 2, source); } } }, "m108-1": function (_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // greater eva up 2 turns - target.addEffect("greaterEvaUp", 2, source); + target.applyEffect("greaterEvaUp", 2, source); } }, m110(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // def up for 2 turns - target.addEffect("defUp", 2, source); + target.applyEffect("defUp", 2, source); // gain 15% def as shield - target.addEffect("shield", 2, source, { + target.applyEffect("shield", 2, source, { shield: Math.floor(target.getDef() * 0.15), }); } @@ -7639,9 +7639,9 @@ const moveExecutes = { m111(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // def up for 4 turns - target.addEffect("defUp", 4, source); + target.applyEffect("defUp", 4, source); // rollout 4 turns - target.addEffect("rollout", 4, source); + target.applyEffect("rollout", 4, source); } }, m113(battle, source, primaryTarget, allTargets) { @@ -7655,10 +7655,10 @@ const moveExecutes = { for (const target of allTargets) { if (targetRow.includes(target)) { // greater spd up for 3 turns - target.addEffect("greaterSpdUp", 3, source); + target.applyEffect("greaterSpdUp", 3, source); } else { // spd up for 3 turns - target.addEffect("spdUp", 3, source); + target.applyEffect("spdUp", 3, source); } } }, @@ -7673,17 +7673,17 @@ const moveExecutes = { for (const target of allTargets) { if (targetRow.includes(target)) { // greater def up for 3 turns - target.addEffect("greaterDefUp", 3, source); + target.applyEffect("greaterDefUp", 3, source); } else { // def up for 3 turns - target.addEffect("defUp", 3, source); + target.applyEffect("defUp", 3, source); } } }, m116(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // greater atk up for 1 turn - target.addEffect("greaterAtkUp", 1, source); + target.applyEffect("greaterAtkUp", 1, source); // boost 50% cr target.boostCombatReadiness(source, 50); @@ -7784,7 +7784,7 @@ const moveExecutes = { const flinchChance = source.atk > target.atk ? 0.2 + (source.atk / target.atk - 1) : 0.2; if (!miss && Math.random() < flinchChance) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -7801,7 +7801,7 @@ const moveExecutes = { // if not miss, apply accDown if (!miss) { - target.addEffect("accDown", 1, source); + target.applyEffect("accDown", 1, source); } } }, @@ -7881,7 +7881,7 @@ const moveExecutes = { let defeatedEnemy = false; // two turn move logic if (source.effectIds.skyCharge === undefined) { - source.addEffect("skyCharge", 1, source); + source.applyEffect("skyCharge", 1, source); // remove sky attack cd source.moveIds[moveId].cooldown = 0; } else { @@ -7915,7 +7915,7 @@ const moveExecutes = { // flinch chance = (30 + source speed/10) const flinchChance = Math.min(0.3 + source.getSpe() / 10 / 100, 0.75); if (Math.random() < flinchChance) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -8029,7 +8029,7 @@ const moveExecutes = { // if not miss, lower def 1 turn if (!miss) { - target.addEffect("defDown", 1, source); + target.applyEffect("defDown", 1, source); } } @@ -8093,7 +8093,7 @@ const moveExecutes = { // if not miss, 70% chance to flinch for 1 turn if (!miss && Math.random() < 0.7) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -8163,7 +8163,7 @@ const moveExecutes = { continue; } // apply buff to self - source.addEffect( + source.applyEffect( buffIdToSteal, buffToSteal.duration, buffToSteal.source, @@ -8265,13 +8265,13 @@ const moveExecutes = { m182(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply move invulnerable - target.addEffect("moveInvulnerable", 1, source); + target.applyEffect("moveInvulnerable", 1, source); } }, "m182-1": function (_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply super stretchy - target.addEffect("superStretchy", 1, source); + target.applyEffect("superStretchy", 1, source); } }, m183(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -8315,7 +8315,7 @@ const moveExecutes = { const miss = missedTargets.includes(target); if (!miss) { // apply confused for 3 turns - target.addEffect("confused", 3, source); + target.applyEffect("confused", 3, source); } } }, @@ -8367,7 +8367,7 @@ const moveExecutes = { // if not missed, acc down if (!miss) { - target.addEffect("accDown", 1, source); + target.applyEffect("accDown", 1, source); } } }, @@ -8379,7 +8379,7 @@ const moveExecutes = { for (const target of allTargets) { // if not miss, apply spikes 3 turns if (!missedTargets.includes(target)) { - target.addEffect("spikes", 3, source); + target.applyEffect("spikes", 3, source); } } }, @@ -8403,7 +8403,7 @@ const moveExecutes = { m194(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply destiny bond 1 turn to user - source.addEffect("destinyBond", 1, source, { + source.applyEffect("destinyBond", 1, source, { boundPokemon: target, }); } @@ -8412,7 +8412,7 @@ const moveExecutes = { for (const target of allTargets) { // if not miss, apply perish song 3 turns if (!missedTargets.includes(target)) { - target.addEffect("perishSong", 3, source); + target.applyEffect("perishSong", 3, source); } } @@ -8424,7 +8424,7 @@ const moveExecutes = { source.position ); for (const target of allyTargets) { - target.addEffect("perishSong", 3, source); + target.applyEffect("perishSong", 3, source); } // append ally targets to all targets @@ -8433,8 +8433,8 @@ const moveExecutes = { m199(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply redirect and greaterEvaDown 1 turn - target.addEffect("redirect", 1, source); - target.addEffect("greaterEvaDown", 1, source); + target.applyEffect("redirect", 1, source); + target.applyEffect("greaterEvaDown", 1, source); } }, m200(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -8442,7 +8442,7 @@ const moveExecutes = { const moveData = getMove(moveId); // if source doesn't have outrage, apply it if (source.effectIds.outrage === undefined) { - source.addEffect("outrage", 2, source); + source.applyEffect("outrage", 2, source); } for (const target of allTargets) { @@ -8461,7 +8461,7 @@ const moveExecutes = { // remove outrage source.removeEffect("outrage"); // confuse self - source.addEffect("confused", 2, source); + source.applyEffect("confused", 2, source); } }, m202(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -8486,7 +8486,7 @@ const moveExecutes = { m203(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply 1 turn immortality - target.addEffect("immortal", 1, source); + target.applyEffect("immortal", 1, source); } }, m204(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -8494,7 +8494,7 @@ const moveExecutes = { const miss = missedTargets.includes(target); if (!miss) { // greater atk down for 2 turns - target.addEffect("greaterAtkDown", 2, source); + target.applyEffect("greaterAtkDown", 2, source); } } }, @@ -8520,7 +8520,7 @@ const moveExecutes = { if (targetHit) { // add rollout for 1 turn - source.addEffect("rollout", 1, source); + source.applyEffect("rollout", 1, source); } }, m208(_battle, source, _primaryTarget, allTargets) { @@ -8555,11 +8555,11 @@ const moveExecutes = { const moveId = "m208-1"; for (const target of allTargets) { // give all stats up 1 turn - target.addEffect("atkUp", 1, source); - target.addEffect("defUp", 1, source); - target.addEffect("spaUp", 1, source); - target.addEffect("spdUp", 1, source); - target.addEffect("speUp", 1, source); + target.applyEffect("atkUp", 1, source); + target.applyEffect("defUp", 1, source); + target.applyEffect("spaUp", 1, source); + target.applyEffect("spdUp", 1, source); + target.applyEffect("speUp", 1, source); // heal source.giveHeal(Math.floor(source.maxHp * 0.25), source, { @@ -8590,7 +8590,7 @@ const moveExecutes = { if (targetHit) { // add fury cutter for 1 turn - source.addEffect("furyCutter", 1, source); + source.applyEffect("furyCutter", 1, source); } }, m212(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -8600,7 +8600,7 @@ const moveExecutes = { // if not miss, cr down 50% and restrict 2 turns if (!miss) { target.reduceCombatReadiness(source, 50); - target.addEffect("restricted", 2, source); + target.applyEffect("restricted", 2, source); } } }, @@ -8616,7 +8616,7 @@ const moveExecutes = { // if not miss, greater spe down 2 turns if (!miss) { - target.addEffect("greaterSpeDown", 2, source); + target.applyEffect("greaterSpeDown", 2, source); } } }, @@ -8716,7 +8716,7 @@ const moveExecutes = { m219(_battle, source, _primaryTarget, allTargets) { for (const target of allTargets) { // apply status immunity for 3 turns - target.addEffect("statusImmunity", 3, source); + target.applyEffect("statusImmunity", 3, source); } }, m221(_battle, source, primaryTarget, allTargets, missedTargets) { @@ -8773,7 +8773,7 @@ const moveExecutes = { moveId ); for (const enemyTarget of enemyTargets) { - enemyTarget.addEffect("confused", 2, source); + enemyTarget.applyEffect("confused", 2, source); } } } @@ -8803,7 +8803,7 @@ const moveExecutes = { return; } // apply effect to target - target.addEffect( + target.applyEffect( effectId, effect.duration, effect.source, @@ -8818,8 +8818,8 @@ const moveExecutes = { target.boostCombatReadiness(source, 100); // give greater atkup, defup 2 turns - target.addEffect("greaterAtkUp", 2, source); - target.addEffect("greaterDefUp", 2, source); + target.applyEffect("greaterAtkUp", 2, source); + target.applyEffect("greaterDefUp", 2, source); } }, m229(battle, source, _primaryTarget, allTargets, missedTargets) { @@ -8864,7 +8864,7 @@ const moveExecutes = { const beforeDamage = source.getDef() > target.getDef(); if (!miss && beforeDamage) { // apply def down 2 turns - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } const damageToDeal = calculateDamage(moveData, source, target, miss); source.dealDamage(damageToDeal, target, { @@ -8874,7 +8874,7 @@ const moveExecutes = { if (!miss && !beforeDamage) { // apply def down 2 turns - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } } }, @@ -8890,8 +8890,8 @@ const moveExecutes = { fraction = 0.33; // gain 2 turns spa, spd up - target.addEffect("spaUp", 2, source); - target.addEffect("spdUp", 2, source); + target.applyEffect("spaUp", 2, source); + target.applyEffect("spdUp", 2, source); } else { fraction = 0.25; } @@ -8955,7 +8955,7 @@ const moveExecutes = { // if not missed, 30% chance to flinch if (!miss && Math.random() < 0.3) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -8980,7 +8980,7 @@ const moveExecutes = { // if not miss, 85% chance to reduce def if (!miss && Math.random() < 0.85) { - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } } }, @@ -8989,7 +8989,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply mirror coat 2 turn - target.addEffect("mirrorCoat", 2, source); + target.applyEffect("mirrorCoat", 2, source); } }, m245(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -9039,19 +9039,19 @@ const moveExecutes = { ); switch (highestStatIndex + 1) { case 1: - source.addEffect("atkUp", 1, source); + source.applyEffect("atkUp", 1, source); break; case 2: - source.addEffect("defUp", 1, source); + source.applyEffect("defUp", 1, source); break; case 3: - source.addEffect("spaUp", 1, source); + source.applyEffect("spaUp", 1, source); break; case 4: - source.addEffect("spdUp", 1, source); + source.applyEffect("spdUp", 1, source); break; case 5: - source.addEffect("speUp", 1, source); + source.applyEffect("speUp", 1, source); break; default: break; @@ -9070,7 +9070,7 @@ const moveExecutes = { // if not miss, 85% chance to reduce sp def if (!miss && Math.random() < 0.85) { - target.addEffect("spdDown", 2, source); + target.applyEffect("spdDown", 2, source); } } }, @@ -9084,7 +9084,7 @@ const moveExecutes = { } // apply 2 turns future sight - target.addEffect("futureSight", 2, source); + target.applyEffect("futureSight", 2, source); } }, m249(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -9100,7 +9100,7 @@ const moveExecutes = { // if not miss, def down 2 turns 70% chance if (!miss && Math.random() < 0.7) { - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } } }, @@ -9117,7 +9117,7 @@ const moveExecutes = { // if not miss, flinch for 1 turn if (!miss) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } @@ -9165,7 +9165,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply redirect for 1 turn - target.addEffect("redirect", 1, source); + target.applyEffect("redirect", 1, source); } }, m262(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -9175,8 +9175,8 @@ const moveExecutes = { const miss = missedTargets.includes(target); if (!miss) { // give 2 turns greater atk, spa down - target.addEffect("greaterAtkDown", 2, source); - target.addEffect("greaterSpaDown", 2, source); + target.applyEffect("greaterAtkDown", 2, source); + target.applyEffect("greaterSpaDown", 2, source); } } // cause self to faint @@ -9187,9 +9187,9 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply charge for 1 turn - target.addEffect("charge", 1, source); + target.applyEffect("charge", 1, source); // apply spd up for 1 turn - target.addEffect("spdUp", 1, source); + target.applyEffect("spdUp", 1, source); } }, m269(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -9202,7 +9202,7 @@ const moveExecutes = { } // add taunt for 2 turns - target.addEffect("taunt", 2, source); + target.applyEffect("taunt", 2, source); } }, "m269-1": function ( @@ -9221,7 +9221,7 @@ const moveExecutes = { } // add reverse taunt for 2 turns - target.addEffect("reverseTaunt", 2, source); + target.applyEffect("reverseTaunt", 2, source); } }, m270(_battle, source, _primaryTarget, allTargets, _missedTargets) { @@ -9229,8 +9229,8 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply atk up and spa up 1 turn - target.addEffect("atkUp", 1, source); - target.addEffect("spaUp", 1, source); + target.applyEffect("atkUp", 1, source); + target.applyEffect("spaUp", 1, source); } }, m273(battle, source, _primaryTarget, allTargets, _missedTargets) { @@ -9239,7 +9239,7 @@ const moveExecutes = { for (const target of allTargets) { // give delayed heal battle.addToLog(`${target.name} recieved ${source.name}'s wish!`); - target.addEffect("delayedHeal", 1, source, { + target.applyEffect("delayedHeal", 1, source, { healAmount: Math.floor(source.maxHp * 0.5), }); } @@ -9256,8 +9256,8 @@ const moveExecutes = { }); } // reduce source atk and def - source.addEffect("atkDown", 1, source); - source.addEffect("defDown", 1, source); + source.applyEffect("atkDown", 1, source); + source.applyEffect("defDown", 1, source); }, m281(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m281"; @@ -9270,7 +9270,7 @@ const moveExecutes = { if (source.status.statusId === statusConditions.SLEEP) { target.applyStatus(statusConditions.SLEEP, source); } else { - target.addEffect("yawn", 1, source); + target.applyEffect("yawn", 1, source); } } } @@ -9350,7 +9350,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply grudge for 1 turn - target.addEffect("grudge", 1, source); + target.applyEffect("grudge", 1, source); } }, m295(battle, source, primaryTarget, allTargets, missedTargets) { @@ -9396,7 +9396,7 @@ const moveExecutes = { // if not miss or mist ball, spd down if (!miss || mistBallCooldown) { - target.addEffect("spdDown", 2, source); + target.applyEffect("spdDown", 2, source); } } }, @@ -9431,10 +9431,10 @@ const moveExecutes = { }); if (lusterPurgeCooldown) { - target.addEffect("atkDown", 2, source); - target.addEffect("spaDown", 2, source); + target.applyEffect("atkDown", 2, source); + target.applyEffect("spaDown", 2, source); } else if (!miss) { - target.addEffect("spaDown", 1, source); + target.applyEffect("spaDown", 1, source); } } }, @@ -9467,7 +9467,7 @@ const moveExecutes = { }); // apply def up 2 turns - source.addEffect("defUp", 2, source); + source.applyEffect("defUp", 2, source); } }, m304(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -9504,7 +9504,7 @@ const moveExecutes = { const moveData = getMove(moveId); // raise user atk for 2 turn - source.addEffect("atkUp", 2, source); + source.applyEffect("atkUp", 2, source); let fainted = false; for (const target of allTargets) { @@ -9592,7 +9592,7 @@ const moveExecutes = { // if not miss, spe down for 2 turns if (!miss) { - target.addEffect("speDown", 2, source); + target.applyEffect("speDown", 2, source); } } }, @@ -9614,17 +9614,17 @@ const moveExecutes = { }); // spe down 2 turns - target.addEffect("speDown", 2, source); + target.applyEffect("speDown", 2, source); } }, m322(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveData = getMove("m322"); for (const target of allTargets) { // raise def, spd - target.addEffect("defUp", 3, source); - target.addEffect("spdUp", 3, source); + target.applyEffect("defUp", 3, source); + target.applyEffect("spdUp", 3, source); // get 10% defenses as shield - target.addEffect("shield", 3, source, { + target.applyEffect("shield", 3, source, { shield: Math.floor(target.getDef() * 0.1 + target.getSpd() * 0.1), }); } @@ -9654,7 +9654,7 @@ const moveExecutes = { // if not miss, 50% acc down 2 turns if (!miss && Math.random() < 0.5) { - target.addEffect("accDown", 2, source); + target.applyEffect("accDown", 2, source); } } }, @@ -9688,7 +9688,7 @@ const moveExecutes = { const moveData = getMove(moveId); // if pokemon doesnt have "projectingSpirit" buff, apply it if (source.effectIds.projectingSpirit === undefined) { - source.addEffect("projectingSpirit", 1, source); + source.applyEffect("projectingSpirit", 1, source); // remove cd source.moveIds[moveId].cooldown = 0; } else { @@ -9730,9 +9730,9 @@ const moveExecutes = { const moveData = getMove("m334"); for (const target of allTargets) { // sharply raise def - target.addEffect("greaterDefUp", 3, source); + target.applyEffect("greaterDefUp", 3, source); // get 25% def as shield - target.addEffect("shield", 3, source, { + target.applyEffect("shield", 3, source, { shield: Math.floor(target.getDef() * 0.25), }); } @@ -9753,15 +9753,15 @@ const moveExecutes = { for (const target of allTargets) { // if primary target, greater def up, else def up if (target === primaryTarget) { - target.addEffect("greaterDefUp", 2, source); + target.applyEffect("greaterDefUp", 2, source); // get 25% def as shield - target.addEffect("shield", 2, source, { + target.applyEffect("shield", 2, source, { shield: Math.floor(source.getDef() * 0.25), }); } else { - target.addEffect("defUp", 2, source); + target.applyEffect("defUp", 2, source); // get 10% def as shield - target.addEffect("shield", 2, source, { + target.applyEffect("shield", 2, source, { shield: Math.floor(source.getDef() * 0.1), }); } @@ -9777,10 +9777,10 @@ const moveExecutes = { const moveData = getMove("m334-2"); for (const target of allTargets) { // sharply raise def & special def - target.addEffect("greaterDefUp", 2, source); - target.addEffect("greaterSpdUp", 2, source); + target.applyEffect("greaterDefUp", 2, source); + target.applyEffect("greaterSpdUp", 2, source); // lower spe - target.addEffect("speDown", 2, source); + target.applyEffect("speDown", 2, source); } }, m336(_battle, source, _primaryTarget, allTargets, _missedTargets) { @@ -9788,7 +9788,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // grant atk up 1 turn - target.addEffect("atkUp", 1, source); + target.applyEffect("atkUp", 1, source); // grant 15% CR target.boostCombatReadiness(source, 15); @@ -9799,7 +9799,7 @@ const moveExecutes = { const moveData = getMove(moveId); // if pokemon doesnt have "sprungUp" buff, apply it if (source.effectIds.sprungUp === undefined) { - source.addEffect("sprungUp", 1, source); + source.applyEffect("sprungUp", 1, source); // remove bounce cd source.moveIds[moveId].cooldown = 0; } else { @@ -9849,8 +9849,8 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // spa, spd up 3 turns - target.addEffect("spaUp", 3, source); - target.addEffect("spdUp", 3, source); + target.applyEffect("spaUp", 3, source); + target.applyEffect("spdUp", 3, source); // gain 50% cr target.boostCombatReadiness(source, 50); @@ -9880,8 +9880,8 @@ const moveExecutes = { const moveData = getMove("m349"); for (const target of allTargets) { // raise attack and speed - target.addEffect("atkUp", 3, source); - target.addEffect("speUp", 3, source); + target.applyEffect("atkUp", 3, source); + target.applyEffect("speUp", 3, source); // gain 50% cr target.boostCombatReadiness(source, 50); @@ -9900,7 +9900,7 @@ const moveExecutes = { // if not miss, confuse with 25% chance if (!miss && Math.random() < 0.25) { - target.addEffect("confused", 2, source); + target.applyEffect("confused", 2, source); } } }, @@ -9927,19 +9927,19 @@ const moveExecutes = { ); switch (highestStatIndex + 1) { case 1: - ally.addEffect("greaterAtkUp", 2, source); + ally.applyEffect("greaterAtkUp", 2, source); break; case 2: - ally.addEffect("greaterDefUp", 2, source); + ally.applyEffect("greaterDefUp", 2, source); break; case 3: - ally.addEffect("greaterSpaUp", 2, source); + ally.applyEffect("greaterSpaUp", 2, source); break; case 4: - ally.addEffect("greaterSpdUp", 2, source); + ally.applyEffect("greaterSpdUp", 2, source); break; case 5: - ally.addEffect("greaterSpeUp", 2, source); + ally.applyEffect("greaterSpeUp", 2, source); break; default: break; @@ -9978,9 +9978,9 @@ const moveExecutes = { // lower attack or special attack if (useAtk) { - source.addEffect("greaterAtkDown", 2, source); + source.applyEffect("greaterAtkDown", 2, source); } else { - source.addEffect("greaterSpaDown", 2, source); + source.applyEffect("greaterSpaDown", 2, source); } }, "m354-2": function ( @@ -9993,7 +9993,7 @@ const moveExecutes = { const moveData = getMove("m354-2"); for (const target of allTargets) { // get 25% def, spd as shield - target.addEffect("shield", 3, source, { + target.applyEffect("shield", 3, source, { shield: Math.floor(source.getDef() * 0.25 + source.getSpd() * 0.25), }); } @@ -10009,7 +10009,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply extra turn buff for 1 (2) turn - target.addEffect("extraTurn", 1, source); + target.applyEffect("extraTurn", 1, source); } }, m355(_battle, source, _primaryTarget, allTargets) { @@ -10025,7 +10025,7 @@ const moveExecutes = { ); // lose flying type - target.addEffect("loseFlying", 1, source); + target.applyEffect("loseFlying", 1, source); } }, m359(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -10041,7 +10041,7 @@ const moveExecutes = { } // source spe down 1 turns - source.addEffect("speDown", 1, source); + source.applyEffect("speDown", 1, source); }, m361(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m361"; @@ -10072,7 +10072,7 @@ const moveExecutes = { for (const target of allTargets) { // grant greater spe up for 2 turns - target.addEffect("greaterSpeUp", 2, source); + target.applyEffect("greaterSpeUp", 2, source); // grant 15% CR to backmost row if (boostTargets.includes(target)) { @@ -10121,8 +10121,8 @@ const moveExecutes = { } // lower self def, spd 1 turn - source.addEffect("defDown", 1, source); - source.addEffect("spdDown", 1, source); + source.applyEffect("defDown", 1, source); + source.applyEffect("spdDown", 1, source); }, "m370-1": function ( _battle, @@ -10143,8 +10143,8 @@ const moveExecutes = { } // sharply lower self def, spd 1 turn - source.addEffect("greaterDefDown", 1, source); - source.addEffect("greaterSpdDown", 1, source); + source.applyEffect("greaterDefDown", 1, source); + source.applyEffect("greaterSpdDown", 1, source); }, m387(_battle, source, _primaryTarget, allTargets, missedTargets) { const moveId = "m387"; @@ -10170,10 +10170,10 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // add regeneration and def up - target.addEffect("regeneration", 3, source, { + target.applyEffect("regeneration", 3, source, { healAmount: Math.floor(source.maxHp * 0.25), }); - target.addEffect("defUp", 2, source); + target.applyEffect("defUp", 2, source); } }, m394(battle, source, primaryTarget, allTargets, missedTargets) { @@ -10285,7 +10285,7 @@ const moveExecutes = { // 25% chance to flinch 1 turn if (!miss && Math.random() < 0.35) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -10319,7 +10319,7 @@ const moveExecutes = { // if not miss, 25% to flinch 1 turn if (!miss && Math.random() < 0.25) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -10355,7 +10355,7 @@ const moveExecutes = { const miss = missedTargets.includes(target); // if not miss, 80% to spd down if (!miss && Math.random() < 0.8) { - target.addEffect("spdDown", 2, source); + target.applyEffect("spdDown", 2, source); } const damageToDeal = calculateDamage(moveData, source, target, miss); @@ -10390,7 +10390,7 @@ const moveExecutes = { // if not miss, 30% chance to flinch if (!miss && Math.random() < 0.3) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -10426,7 +10426,7 @@ const moveExecutes = { // if not miss, 85% chance to reduce sp def if (!miss && Math.random() < 0.85) { - target.addEffect("spdDown", 2, source); + target.applyEffect("spdDown", 2, source); } } }, @@ -10463,7 +10463,7 @@ const moveExecutes = { // if not miss, 20% spd down 2 turns if (!miss && Math.random() < 0.2) { - target.addEffect("spdDown", 2, source); + target.applyEffect("spdDown", 2, source); } } }, @@ -10484,7 +10484,7 @@ const moveExecutes = { ); } // apply recharge to self - source.addEffect("recharge", 1, source); + source.applyEffect("recharge", 1, source); }, "m416-1": function ( _battle, @@ -10509,14 +10509,14 @@ const moveExecutes = { ); } // apply greater spe down to self - source.addEffect("greaterSpeDown", 1, source); + source.applyEffect("greaterSpeDown", 1, source); }, m417(_battle, source, _primaryTarget, allTargets, _missedTargets) { const moveId = "m417"; const moveData = getMove(moveId); for (const target of allTargets) { // sharply raise spatk - target.addEffect("greaterSpaUp", 3, source); + target.applyEffect("greaterSpaUp", 3, source); // boost cr 60% source.boostCombatReadiness(source, 60); @@ -10533,8 +10533,8 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // sharply raise spatk, eva - target.addEffect("greaterSpaUp", 2, source); - target.addEffect("greaterEvaUp", 2, source); + target.applyEffect("greaterSpaUp", 2, source); + target.applyEffect("greaterEvaUp", 2, source); // boost cr 60% source.boostCombatReadiness(source, 60); @@ -10587,7 +10587,7 @@ const moveExecutes = { } // if not miss, 10% chance to flinch for 1 turn if (!miss && Math.random() < 0.1) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -10619,7 +10619,7 @@ const moveExecutes = { // if not miss, flinch for 1 turn if (!miss) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -10636,7 +10636,7 @@ const moveExecutes = { // if not miss, 20% to spd down if (!miss && Math.random() < 0.2) { - target.addEffect("spdDown", 2, source); + target.applyEffect("spdDown", 2, source); } } }, @@ -10697,13 +10697,13 @@ const moveExecutes = { for (const target of targets) { const spe = target.getSpe(); if (spe > 1.25 * meanSpe) { - target.addEffect("greaterSpeDown", 3, source); + target.applyEffect("greaterSpeDown", 3, source); } else if (spe > meanSpe) { - target.addEffect("speDown", 3, source); + target.applyEffect("speDown", 3, source); } else if (spe > meanSpe * 0.75) { - target.addEffect("speUp", 3, source); + target.applyEffect("speUp", 3, source); } else { - target.addEffect("greaterSpeUp", 3, source); + target.applyEffect("greaterSpeUp", 3, source); } // append to allTargets if not already in @@ -10771,7 +10771,12 @@ const moveExecutes = { } // apply debuff to target - target.addEffect(debuffId, debuff.duration, source, debuff.initialArgs); + target.applyEffect( + debuffId, + debuff.duration, + source, + debuff.initialArgs + ); } } }, @@ -10788,7 +10793,7 @@ const moveExecutes = { } // apply greater spa down to user 2 turns - source.addEffect("greaterSpaDown", 2, source); + source.applyEffect("greaterSpaDown", 2, source); }, m441(_battle, source, primaryTarget, allTargets, missedTargets) { const moveId = "m441"; @@ -10842,7 +10847,7 @@ const moveExecutes = { ); for (const target of allTargets) { // give target stealthRock - target.addEffect("stealthRock", 3, source); + target.applyEffect("stealthRock", 3, source); } }, m450(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -10880,7 +10885,7 @@ const moveExecutes = { return; } // apply buff to self - source.addEffect( + source.applyEffect( buffIdToSteal, buffToSteal.duration, buffToSteal.source, @@ -10909,7 +10914,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply wide guard 3 turn - target.addEffect("wideGuard", 3, source); + target.applyEffect("wideGuard", 3, source); } }, m476(_battle, source, _primaryTarget, allTargets, _missedTargets) { @@ -10917,7 +10922,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply redirect for 1 turn - target.addEffect("redirect", 1, source); + target.applyEffect("redirect", 1, source); } }, m479(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -10937,7 +10942,7 @@ const moveExecutes = { (target.type1 === pokemonTypes.FLYING || target.type2 === pokemonTypes.FLYING) ) { - target.addEffect("loseFlying", 1, source); + target.applyEffect("loseFlying", 1, source); } } }, @@ -10975,9 +10980,9 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // boost spa, spd, spe - target.addEffect("spaUp", 3, source); - target.addEffect("spdUp", 3, source); - target.addEffect("speUp", 3, source); + target.applyEffect("spaUp", 3, source); + target.applyEffect("spdUp", 3, source); + target.applyEffect("speUp", 3, source); // boost cr 50% target.boostCombatReadiness(source, 50); @@ -11119,7 +11124,7 @@ const moveExecutes = { // if hit, reduce targets speed for 1 turn if (!miss) { - target.addEffect("speDown", 1, source); + target.applyEffect("speDown", 1, source); } } }, @@ -11145,8 +11150,8 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // give atk up and spa up 2 turns - target.addEffect("atkUp", 2, source); - target.addEffect("spaUp", 2, source); + target.applyEffect("atkUp", 2, source); + target.applyEffect("spaUp", 2, source); } }, m527(_battle, source, _primaryTarget, allTargets, missedTargets) { @@ -11162,7 +11167,7 @@ const moveExecutes = { // if hit, reduce targets speed for 2 turn if (!miss) { - target.addEffect("speDown", 2, source); + target.applyEffect("speDown", 2, source); } } }, @@ -11219,7 +11224,7 @@ const moveExecutes = { // if not miss, 50% to def down for 2 turns if (!miss && Math.random() < 0.5) { - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } } }, @@ -11291,7 +11296,7 @@ const moveExecutes = { // if hit, 30% chance to confuse target if (!miss && Math.random() < 0.3) { - target.addEffect("confused", 2, source); + target.applyEffect("confused", 2, source); } } }, @@ -11321,7 +11326,7 @@ const moveExecutes = { // if hit, 30% chance to flinch if (!miss && Math.random() < 0.3) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -11333,8 +11338,8 @@ const moveExecutes = { // if not miss, spe down and restrict 2 turns if (!miss) { - target.addEffect("speDown", 2, source); - target.addEffect("restricted", 2, source); + target.applyEffect("speDown", 2, source); + target.applyEffect("restricted", 2, source); } } }, @@ -11343,7 +11348,7 @@ const moveExecutes = { const moveData = getMove(moveId); // raise atk - source.addEffect("greaterAtkUp", 3, source); + source.applyEffect("greaterAtkUp", 3, source); let defeatedEnemy = false; for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11375,8 +11380,8 @@ const moveExecutes = { const miss = missedTargets.includes(target); // if not miss or primary target, atk and spa down 1 turn if (!miss || target === primaryTarget) { - target.addEffect("atkDown", 1, source); - target.addEffect("spaDown", 1, source); + target.applyEffect("atkDown", 1, source); + target.applyEffect("spaDown", 1, source); } } }, @@ -11445,11 +11450,11 @@ const moveExecutes = { // if not miss, 70% chance to lower atk if (!miss && Math.random() < 0.7) { - target.addEffect("atkDown", 1, source); + target.applyEffect("atkDown", 1, source); } // if not miss, 70% chance to lower spe if (!miss && Math.random() < 0.7) { - target.addEffect("speDown", 1, source); + target.applyEffect("speDown", 1, source); } } }, @@ -11466,7 +11471,7 @@ const moveExecutes = { // if not miss, sharply lower spatk if (!miss) { - target.addEffect("greaterSpaDown", 2, source); + target.applyEffect("greaterSpaDown", 2, source); } } }, @@ -11505,8 +11510,8 @@ const moveExecutes = { const moveData = getMove(moveId); // give user spa, spd up - source.addEffect("spaUp", 1, source); - source.addEffect("spdUp", 1, source); + source.applyEffect("spaUp", 1, source); + source.applyEffect("spdUp", 1, source); for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11522,8 +11527,8 @@ const moveExecutes = { const moveData = getMove(moveId); // give user atk, def up - source.addEffect("atkUp", 1, source); - source.addEffect("defUp", 1, source); + source.applyEffect("atkUp", 1, source); + source.applyEffect("defUp", 1, source); for (const target of allTargets) { const miss = missedTargets.includes(target); @@ -11553,8 +11558,8 @@ const moveExecutes = { // give user 100% cr, def down spd down 2 turns source.boostCombatReadiness(source, 100); - source.addEffect("defDown", 2, source); - source.addEffect("spdDown", 2, source); + source.applyEffect("defDown", 2, source); + source.applyEffect("spdDown", 2, source); }, "m620-1": function ( _battle, @@ -11605,7 +11610,7 @@ const moveExecutes = { // if not miss, apply atk down to target 2 turns if (!miss) { - target.addEffect("atkDown", 2, source); + target.applyEffect("atkDown", 2, source); } } }, @@ -11634,7 +11639,7 @@ const moveExecutes = { // if not miss, 85% chance to reduce def if (!miss && Math.random() < 0.85) { - target.addEffect("defDown", 2, source); + target.applyEffect("defDown", 2, source); } } }, @@ -11693,7 +11698,7 @@ const moveExecutes = { // if not miss, 30% chance to flinch if (!miss && Math.random() < 0.3) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } @@ -11713,7 +11718,7 @@ const moveExecutes = { // if not miss, 30% chance to flinch if (!miss && Math.random() < 0.3) { - target.addEffect("flinched", 1, source); + target.applyEffect("flinched", 1, source); } } }, @@ -11761,7 +11766,7 @@ const moveExecutes = { // if primary target, increase cr to 100% and give spe up 2 turns if (target === primaryTarget) { target.boostCombatReadiness(source, 100); - target.addEffect("speUp", 2, source); + target.applyEffect("speUp", 2, source); } else { // else, decrease cr by 30% and spe down 1 turn const miss = missedTargets.includes(target); @@ -11770,7 +11775,7 @@ const moveExecutes = { } target.reduceCombatReadiness(source, 30); - target.addEffect("speDown", 1, source); + target.applyEffect("speDown", 1, source); } } }, @@ -11870,7 +11875,7 @@ const moveExecutes = { for (const target of targets) { const effectId = target.teamName === source.teamName ? "speUp" : "speDown"; - target.addEffect(effectId, 2, source); + target.applyEffect(effectId, 2, source); } // source cr 100% @@ -11936,8 +11941,8 @@ const moveExecutes = { moveId, }); - target.addEffect("greaterDefDown", 1, source); - target.addEffect("greaterSpdDown", 1, source); + target.applyEffect("greaterDefDown", 1, source); + target.applyEffect("greaterSpdDown", 1, source); } // increase all buff durations by 1 if dispellable @@ -11997,7 +12002,7 @@ const moveExecutes = { moveId, }); // apply gear five buff for 3 turns - target.addEffect("gearFive", 3, source); + target.applyEffect("gearFive", 3, source); // boost cr 100% target.boostCombatReadiness(source, 100); } @@ -12035,7 +12040,7 @@ const moveExecutes = { const moveData = getMove(moveId); for (const target of allTargets) { // apply extra turn buff for 1 (2) turn - target.addEffect("extraTurn", 1, source); + target.applyEffect("extraTurn", 1, source); } }, }; @@ -12497,8 +12502,8 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Flash Fire was activated by the Fire attack!` ); - targetPokemon.addEffect("atkUp", 1, targetPokemon); - targetPokemon.addEffect("spaUp", 1, targetPokemon); + targetPokemon.applyEffect("atkUp", 1, targetPokemon); + targetPokemon.applyEffect("spaUp", 1, targetPokemon); args.damage = 0; args.maxDamage = Math.min(args.maxDamage, args.damage); } @@ -12646,7 +12651,7 @@ const abilityConfig = { battle.addToLog( `${sourcePokemon.name}'s Intimidate affects ${highestAtkPokemon.name}!` ); - highestAtkPokemon.addEffect("atkDown", 1, sourcePokemon); + highestAtkPokemon.applyEffect("atkDown", 1, sourcePokemon); // get pokemon with highest spe let highestSpePokemon = enemyPokemons[0]; @@ -12663,7 +12668,7 @@ const abilityConfig = { battle.addToLog( `${sourcePokemon.name}'s Intimidate affects ${highestSpePokemon.name}!` ); - highestSpePokemon.addEffect("atkDown", 1, sourcePokemon); + highestSpePokemon.applyEffect("atkDown", 1, sourcePokemon); }, }; const listenerId = battle.eventHandler.registerListener( @@ -12706,7 +12711,7 @@ const abilityConfig = { ); for (const pokemon of enemyPokemons) { - pokemon.addEffect("restricted", 1, sourcePokemon); + pokemon.applyEffect("restricted", 1, sourcePokemon); } }, }; @@ -13100,7 +13105,7 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Lightning Rod was activated by the Electric attack!` ); - targetPokemon.addEffect("spaUp", 1, targetPokemon); + targetPokemon.applyEffect("spaUp", 1, targetPokemon); }, }; const listenerId1 = battle.eventHandler.registerListener( @@ -13177,7 +13182,7 @@ const abilityConfig = { battle.addToLog( `${sourcePokemon.name}'s Illuminate affects ${pokemon.name}!` ); - pokemon.addEffect("evaDown", 1, sourcePokemon); + pokemon.applyEffect("evaDown", 1, sourcePokemon); } }, }; @@ -13709,7 +13714,7 @@ const abilityConfig = { `${initialArgs.pokemon.name}'s Pickup gains ${effectData.name}!` ); // apply buff to self - source.addEffect(args.effectId, 1, args.source, args.initialArgs); + source.applyEffect(args.effectId, 1, args.source, args.initialArgs); } }, }; @@ -13754,7 +13759,7 @@ const abilityConfig = { } if (ability.data.turn % 2 === 0) { pokemon.battle.addToLog(`${pokemon.name} is becoming lazy!`); - activePokemon.addEffect("recharge", 1, activePokemon); + activePokemon.applyEffect("recharge", 1, activePokemon); } ability.data.turn += 1; }, @@ -13804,7 +13809,7 @@ const abilityConfig = { pokemon.battle.addToLog( `${pokemon.name}'s Old Age is holding it back!` ); - activePokemon.addEffect("greaterSpeDown", 1, activePokemon); + activePokemon.applyEffect("greaterSpeDown", 1, activePokemon); } ability.data.turn += 1; }, @@ -13858,7 +13863,7 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Cute Charm affects ${sourcePokemon.name}!` ); - sourcePokemon.addEffect("atkDown", 1, targetPokemon); + sourcePokemon.applyEffect("atkDown", 1, targetPokemon); } }, }; @@ -14172,7 +14177,7 @@ const abilityConfig = { targetPokemon ) !== 0 ) { - targetPokemon.addEffect("restricted", 2, sourcePokemon); + targetPokemon.applyEffect("restricted", 2, sourcePokemon); } }, }; @@ -14635,7 +14640,7 @@ const abilityConfig = { ); args.damage = 0; args.maxDamage = 0; - targetPokemon.addEffect("spaUp", 1, targetPokemon); + targetPokemon.applyEffect("spaUp", 1, targetPokemon); }, }; const listenerId1 = battle.eventHandler.registerListener( @@ -15092,12 +15097,12 @@ const abilityConfig = { sourcePokemon.battle.addToLog( `${sourcePokemon.name}'s Moxie greatly increases its attack!` ); - sourcePokemon.addEffect("greaterAtkUp", 2, sourcePokemon); + sourcePokemon.applyEffect("greaterAtkUp", 2, sourcePokemon); } else { sourcePokemon.battle.addToLog( `${sourcePokemon.name}'s Moxie increases its attack!` ); - sourcePokemon.addEffect("atkUp", 2, sourcePokemon); + sourcePokemon.applyEffect("atkUp", 2, sourcePokemon); } // gain 10% combat readiness @@ -15260,7 +15265,7 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Competitive increases its special attack!` ); - targetPokemon.addEffect("greaterSpaUp", 2, targetPokemon); + targetPokemon.applyEffect("greaterSpaUp", 2, targetPokemon); }, }; const listenerId = battle.eventHandler.registerListener( @@ -15743,7 +15748,7 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${targetPokemon.name} suffers under ${initialArgs.pokemon.name}'s False Democracy!` ); - targetPokemon.addEffect("disable", 1, initialArgs.pokemon); + targetPokemon.applyEffect("disable", 1, initialArgs.pokemon); abilityData.affectedPokemons.push(targetPokemon); }, }; @@ -15962,7 +15967,7 @@ const abilityConfig = { execute(initialArgs, _args) { const { pokemon } = initialArgs; // add moneybags buff - pokemon.addEffect("moneyBags", 2, pokemon); + pokemon.applyEffect("moneyBags", 2, pokemon); }, }; @@ -15995,7 +16000,7 @@ const abilityConfig = { execute(initialArgs, _args) { const { pokemon } = initialArgs; // add immortality buff - pokemon.addEffect("immortal", 2, pokemon); + pokemon.applyEffect("immortal", 2, pokemon); }, }; @@ -16431,13 +16436,13 @@ const abilityConfig = { targetPokemon.battle.addToLog( `${targetPokemon.name}'s Cosmic Strength increases its special attack!` ); - targetPokemon.addEffect("greaterSpaUp", 1, targetPokemon); + targetPokemon.applyEffect("greaterSpaUp", 1, targetPokemon); } else if (effectId === "spaDown" || effectId === "greaterSpaDown") { // increase attack targetPokemon.battle.addToLog( `${targetPokemon.name}'s Cosmic Strength increases its attack!` ); - targetPokemon.addEffect("greaterAtkUp", 1, targetPokemon); + targetPokemon.applyEffect("greaterAtkUp", 1, targetPokemon); } }, }; diff --git a/src/battleEngine/effectService.js b/src/battleEngine/effectRegistry.js similarity index 59% rename from src/battleEngine/effectService.js rename to src/battleEngine/effectRegistry.js index 6b63f529..29cb3518 100644 --- a/src/battleEngine/effectService.js +++ b/src/battleEngine/effectRegistry.js @@ -65,11 +65,11 @@ const getEffects = ({ fieldFilter, customFilter }) => { if (fieldFilter) { return Object.entries(allEffects).reduce((acc, [effectId, effect]) => { - Object.entries(fieldFilter).forEach(([field, value]) => { + for (const [field, value] of Object.entries(fieldFilter)) { if (effect[field] !== value) { return acc; } - }); + } acc[effectId] = effect; return acc; }, {}); @@ -90,68 +90,6 @@ const getEffectIds = ({ fieldFilter, customFilter }) => { // @ts-ignore return Object.keys(effects); }; -/** - * @template {EffectIdEnum} K - * @param {object} param0 - * @param {K} param0.effectId - * @param {Battle} param0.battle - * @param {BattlePokemon} param0.source - * @param {BattlePokemon} param0.target - * @param {EffectInitialArgsTypeFromId} param0.initialArgs - * @returns {EffectPropertiesTypeFromId | undefined} - */ -const applyEffect = ({ effectId, battle, source, target, initialArgs }) => { - const effect = getEffect(effectId); - if (!effect) { - logger.error(`Effect ${effectId} does not exist.`); - return; - } - if (!effect.isLegacyEffect) { - return effect.effectAdd({ - battle, - source, - target, - initialArgs, - }); - } - const legacyEffect = /** @type {any} */ (effect); - return legacyEffect.effectAdd(battle, source, target, initialArgs); -}; - -/** - * @template {EffectIdEnum} K - * @param {object} param0 - * @param {K} param0.effectId - * @param {Battle} param0.battle - * @param {BattlePokemon} param0.target - * @param {EffectInitialArgsTypeFromId} param0.initialArgs - * @param {EffectPropertiesTypeFromId} param0.properties - */ -const removeEffect = ({ - effectId, - battle, - target, - initialArgs, - properties, -}) => { - const effect = getEffect(effectId); - if (!effect) { - logger.error(`Effect ${effectId} does not exist.`); - return; - } - if (!effect.isLegacyEffect) { - // @ts-ignore - effect.effectRemove({ - battle, - target, - initialArgs, - properties, - }); - } else { - const legacyEffect = /** @type {any} */ (effect); - legacyEffect.effectRemove(battle, target, properties, initialArgs); - } -}; module.exports = { registerEffects, @@ -159,6 +97,4 @@ module.exports = { getEffect, getEffects, getEffectIds, - applyEffect, - removeEffect, }; diff --git a/src/battleEngine/initialize.js b/src/battleEngine/initialize.js index e9e68b37..f964b701 100644 --- a/src/battleEngine/initialize.js +++ b/src/battleEngine/initialize.js @@ -1,7 +1,7 @@ // @ts-nocheck // TODO: remove ts-nocheck when all moves, effects, abilities, and events are migrated const { registerMoves, registerLegacyMoves } = require("./moveService"); -const { registerEffects, registerLegacyEffects } = require("./effectService"); +const { registerEffects, registerLegacyEffects } = require("./effectRegistry"); const { effectsToRegister } = require("./effects"); const { moveConfig, moveExecutes, effectConfig } = require("./battleConfig"); const { movesToRegister } = require("./moves"); diff --git a/src/battleEngine/moveService.js b/src/battleEngine/moveService.js index 015cce46..49b58b62 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/moveService.js @@ -69,11 +69,11 @@ const getMoves = ({ fieldFilter, customFilter }) => { if (fieldFilter) { return Object.entries(allMoves).reduce((acc, [moveId, move]) => { - Object.entries(fieldFilter).forEach(([field, value]) => { + for (const [field, value] of Object.entries(fieldFilter)) { if (move[field] !== value) { return acc; } - }); + } acc[moveId] = move; return acc; }, {}); diff --git a/src/services/battle.js b/src/services/battle.js index a6fcff94..ab3697d0 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -77,11 +77,7 @@ const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); const { getMove, executeMove } = require("../battleEngine/moveService"); -const { - getEffect, - applyEffect, - removeEffect, -} = require("../battleEngine/effectService"); +const { getEffect } = require("../battleEngine/effectRegistry"); class NPC { constructor( @@ -1512,13 +1508,18 @@ class Pokemon { * @param {K} effectId * @param {number} duration * @param {BattlePokemon} source - * @param {EffectInitialArgsTypeFromId} args + * @param {EffectInitialArgsTypeFromId} initialArgs */ - addEffect(effectId, duration, source, args) { + applyEffect(effectId, duration, source, initialArgs) { // if faint, do nothing if (this.isFainted) { return; } + const effect = getEffect(effectId); + if (!effect) { + logger.error(`Effect ${effectId} does not exist.`); + return; + } duration = this.battle.activePokemon === this ? duration + 1 : duration; @@ -1548,7 +1549,7 @@ class Pokemon { source, effectId, duration, - initialArgs: args, + initialArgs, canAdd: true, }; this.battle.eventHandler.emit( @@ -1571,19 +1572,25 @@ class Pokemon { this.effectIds[effectId] = { duration, source, - initialArgs: args, + initialArgs, }; if (oldShield) { this.effectIds[effectId].args = oldShield; } - this.effectIds[effectId].args = - applyEffect({ - effectId, - battle: this.battle, - source, - target: this, - initialArgs: args, - }) || {}; + + if (!effect.isLegacyEffect) { + this.effectIds[effectId].args = + effect.effectAdd({ + battle: this.battle, + source, + target: this, + initialArgs, + }) || {}; + } else { + const legacyEffect = /** @type {any} */ (effect); + this.effectIds[effectId].args = + legacyEffect.effectAdd(this.battle, source, this, initialArgs) || {}; + } if (this.effectIds[effectId] !== undefined) { // trigger after add effect events @@ -1592,7 +1599,7 @@ class Pokemon { source, effectId, duration, - initialArgs: args, + initialArgs: initialArgs, args: this.effectIds[effectId].args, }; this.battle.eventHandler.emit( @@ -1646,14 +1653,29 @@ class Pokemon { if (!this.effectIds[effectId]) { return false; } + const effect = getEffect(effectId); + if (!effect) { + logger.error(`Effect ${effectId} does not exist.`); + return; + } - removeEffect({ - effectId, - battle: this.battle, - target: this, - properties: this.effectIds[effectId].args, - initialArgs: this.effectIds[effectId].initialArgs, - }); + if (!effect.isLegacyEffect) { + // @ts-ignore + effect.effectRemove({ + battle: this.battle, + target: this, + properties: this.effectIds[effectId].args, + initialArgs: this.effectIds[effectId].initialArgs, + }); + } else { + const legacyEffect = /** @type {any} */ (effect); + legacyEffect.effectRemove( + this.battle, + this, + this.effectIds[effectId].args, + this.effectIds[effectId].initialArgs + ); + } if (this.effectIds[effectId] !== undefined) { const afterRemoveArgs = { diff --git a/src/utils/battleUtils.js b/src/utils/battleUtils.js index ca614b89..170bee5f 100644 --- a/src/utils/battleUtils.js +++ b/src/utils/battleUtils.js @@ -11,7 +11,7 @@ const { difficultyConfig } = require("../config/npcConfig"); const { pokemonConfig, typeConfig } = require("../config/pokemonConfig"); const { getRewardsString, flattenRewards } = require("./trainerUtils"); const { getPBar, formatMoney } = require("./utils"); -const { getEffect } = require("../battleEngine/effectService"); +const { getEffect } = require("../battleEngine/effectRegistry"); const buildPartyString = ( pokemons, From ad0906cca423d22807ba3ac3af66dbaeb2baae06 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 17:33:19 -0700 Subject: [PATCH 28/38] move battle data --- src/battleEngine/battleConfig.js | 4 ++-- src/battleEngine/battleTypes.js | 6 +++--- src/battleEngine/{ => data}/effectRegistry.js | 6 +++--- src/battleEngine/{ => data}/effects.js | 4 ++-- src/battleEngine/{ => data}/initialize.js | 2 +- src/battleEngine/{ => data}/moveService.js | 4 ++-- src/battleEngine/{ => data}/moves.js | 8 ++++---- src/components/selectBattleMoveRow.js | 2 +- src/embeds/battleEmbeds.js | 2 +- src/embeds/pokemonEmbeds.js | 2 +- src/index.js | 2 +- src/services/battle.js | 4 ++-- src/services/mythic.js | 2 +- src/utils/battleUtils.js | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) rename src/battleEngine/{ => data}/effectRegistry.js (93%) rename src/battleEngine/{ => data}/effects.js (96%) rename src/battleEngine/{ => data}/initialize.js (88%) rename src/battleEngine/{ => data}/moveService.js (97%) rename src/battleEngine/{ => data}/moves.js (92%) diff --git a/src/battleEngine/battleConfig.js b/src/battleEngine/battleConfig.js index 4d67bb2b..dc42adc6 100644 --- a/src/battleEngine/battleConfig.js +++ b/src/battleEngine/battleConfig.js @@ -5,8 +5,8 @@ /* eslint-disable no-param-reassign */ const { types: pokemonTypes } = require("../config/pokemonConfig"); const types = require("../../types"); -const { getMove, getMoveIds, executeMove } = require("./moveService"); -const { getEffect } = require("./effectRegistry"); +const { getMove, getMoveIds, executeMove } = require("./data/moveService"); +const { getEffect } = require("./data/effectRegistry"); /** @typedef {types.Enum} BattleEventEnum */ const battleEventNames = Object.freeze({ diff --git a/src/battleEngine/battleTypes.js b/src/battleEngine/battleTypes.js index f1515fea..d9f16bbc 100644 --- a/src/battleEngine/battleTypes.js +++ b/src/battleEngine/battleTypes.js @@ -13,14 +13,14 @@ * @typedef {import("./battleConfig").EffectTypeEnum} EffectTypeEnum * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} EffectAddBasicArgs * @typedef {{battle: Battle, target: BattlePokemon}} EffectRemoveBasicArgs - * @typedef {typeof import("./effects").effectsToRegister} RegisteredEffects - * @typedef {import("./moves").Move} Move + * @typedef {typeof import("./data/effects").effectsToRegister} RegisteredEffects + * @typedef {import("./data/moves").Move} Move */ /** * @template T * @template U - * @typedef {import("./effects").Effect} Effect + * @typedef {import("./data/effects").Effect} Effect */ /** diff --git a/src/battleEngine/effectRegistry.js b/src/battleEngine/data/effectRegistry.js similarity index 93% rename from src/battleEngine/effectRegistry.js rename to src/battleEngine/data/effectRegistry.js index 29cb3518..ba36ce52 100644 --- a/src/battleEngine/effectRegistry.js +++ b/src/battleEngine/data/effectRegistry.js @@ -1,7 +1,7 @@ // eslint-disable-next-line no-unused-vars -const { effectIdEnum } = require("../enums/battleEnums"); // TODO: remove after testing -const { logger } = require("../log"); -const types = require("../../types"); +const { effectIdEnum } = require("../../enums/battleEnums"); // TODO: remove after testing +const { logger } = require("../../log"); +const types = require("../../../types"); const allEffects = {}; diff --git a/src/battleEngine/effects.js b/src/battleEngine/data/effects.js similarity index 96% rename from src/battleEngine/effects.js rename to src/battleEngine/data/effects.js index 898ab4a2..ee40bbe5 100644 --- a/src/battleEngine/effects.js +++ b/src/battleEngine/data/effects.js @@ -1,6 +1,6 @@ /* eslint-disable no-param-reassign */ -const { effectTypes } = require("./battleConfig"); -const { effectIdEnum } = require("../enums/battleEnums"); +const { effectTypes } = require("../battleConfig"); +const { effectIdEnum } = require("../../enums/battleEnums"); /** * @template T diff --git a/src/battleEngine/initialize.js b/src/battleEngine/data/initialize.js similarity index 88% rename from src/battleEngine/initialize.js rename to src/battleEngine/data/initialize.js index f964b701..a41c841e 100644 --- a/src/battleEngine/initialize.js +++ b/src/battleEngine/data/initialize.js @@ -3,7 +3,7 @@ const { registerMoves, registerLegacyMoves } = require("./moveService"); const { registerEffects, registerLegacyEffects } = require("./effectRegistry"); const { effectsToRegister } = require("./effects"); -const { moveConfig, moveExecutes, effectConfig } = require("./battleConfig"); +const { moveConfig, moveExecutes, effectConfig } = require("../battleConfig"); const { movesToRegister } = require("./moves"); const initialize = () => { diff --git a/src/battleEngine/moveService.js b/src/battleEngine/data/moveService.js similarity index 97% rename from src/battleEngine/moveService.js rename to src/battleEngine/data/moveService.js index 49b58b62..92236ca7 100644 --- a/src/battleEngine/moveService.js +++ b/src/battleEngine/data/moveService.js @@ -1,5 +1,5 @@ -const { logger } = require("../log"); -const types = require("../../types"); +const { logger } = require("../../log"); +const types = require("../../../types"); const allMoves = {}; diff --git a/src/battleEngine/moves.js b/src/battleEngine/data/moves.js similarity index 92% rename from src/battleEngine/moves.js rename to src/battleEngine/data/moves.js index b411ffbf..3649d315 100644 --- a/src/battleEngine/moves.js +++ b/src/battleEngine/data/moves.js @@ -1,4 +1,4 @@ -const { types: pokemonTypes } = require("../config/pokemonConfig"); +const { types: pokemonTypes } = require("../../config/pokemonConfig"); const { targetTypes, targetPositions, @@ -6,12 +6,12 @@ const { damageTypes, moveTiers, calculateDamage, -} = require("./battleConfig"); +} = require("../battleConfig"); const { getMove } = require("./moveService"); -const { moveIdEnum } = require("../enums/battleEnums"); +const { moveIdEnum } = require("../../enums/battleEnums"); /** - * @typedef {import("../config/pokemonConfig").PokemonTypeEnum} PokemonTypeEnum + * @typedef {import("../../config/pokemonConfig").PokemonTypeEnum} PokemonTypeEnum */ class Move { diff --git a/src/components/selectBattleMoveRow.js b/src/components/selectBattleMoveRow.js index 70486f28..835e1aa3 100644 --- a/src/components/selectBattleMoveRow.js +++ b/src/components/selectBattleMoveRow.js @@ -8,7 +8,7 @@ */ const { ActionRowBuilder, StringSelectMenuBuilder } = require("discord.js"); const { eventNames } = require("../config/eventConfig"); -const { getMove } = require("../battleEngine/moveService"); +const { getMove } = require("../battleEngine/data/moveService"); const { typeConfig } = require("../config/pokemonConfig"); /** diff --git a/src/embeds/battleEmbeds.js b/src/embeds/battleEmbeds.js index 5be74e07..6f4c4093 100644 --- a/src/embeds/battleEmbeds.js +++ b/src/embeds/battleEmbeds.js @@ -8,7 +8,7 @@ */ const { EmbedBuilder } = require("discord.js"); const { weatherConditions } = require("../battleEngine/battleConfig"); -const { getMove } = require("../battleEngine/moveService"); +const { getMove } = require("../battleEngine/data/moveService"); const { buildPartyString, buildMoveString, diff --git a/src/embeds/pokemonEmbeds.js b/src/embeds/pokemonEmbeds.js index 2a59eef8..273ffb42 100644 --- a/src/embeds/pokemonEmbeds.js +++ b/src/embeds/pokemonEmbeds.js @@ -16,7 +16,7 @@ const { growthRateConfig, } = require("../config/pokemonConfig"); const { abilityConfig } = require("../battleEngine/battleConfig"); -const { getMove } = require("../battleEngine/moveService"); +const { getMove } = require("../battleEngine/data/moveService"); const { getWhitespace, getPBar, diff --git a/src/index.js b/src/index.js index a78a1494..fc523b96 100644 --- a/src/index.js +++ b/src/index.js @@ -21,7 +21,7 @@ const { getStateCount } = require("./services/state"); const { cleanupRaids } = require("./services/raid"); const { initialize: initializeBattleData, -} = require("./battleEngine/initialize"); +} = require("./battleEngine/data/initialize"); console.log(`STAGE: ${process.env.STAGE}`); const FFLAG_ENABLE_SPAWN = process.env.FFLAG_ENABLE_SPAWN === "1"; diff --git a/src/services/battle.js b/src/services/battle.js index ab3697d0..a7f786de 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -76,8 +76,8 @@ const { generateRandomPokemon } = require("./gacha"); const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); -const { getMove, executeMove } = require("../battleEngine/moveService"); -const { getEffect } = require("../battleEngine/effectRegistry"); +const { getMove, executeMove } = require("../battleEngine/data/moveService"); +const { getEffect } = require("../battleEngine/data/effectRegistry"); class NPC { constructor( diff --git a/src/services/mythic.js b/src/services/mythic.js index e9ff34bc..ff714bb2 100644 --- a/src/services/mythic.js +++ b/src/services/mythic.js @@ -46,7 +46,7 @@ const { calculateAndUpdatePokemonStats, } = require("./pokemon"); const { getTrainer, updateTrainer } = require("./trainer"); -const { getMoves } = require("../battleEngine/moveService"); +const { getMoves } = require("../battleEngine/data/moveService"); const getMythic = async (trainer, speciesId) => { const speciesData = pokemonConfig[speciesId]; diff --git a/src/utils/battleUtils.js b/src/utils/battleUtils.js index 170bee5f..92465789 100644 --- a/src/utils/battleUtils.js +++ b/src/utils/battleUtils.js @@ -11,7 +11,7 @@ const { difficultyConfig } = require("../config/npcConfig"); const { pokemonConfig, typeConfig } = require("../config/pokemonConfig"); const { getRewardsString, flattenRewards } = require("./trainerUtils"); const { getPBar, formatMoney } = require("./utils"); -const { getEffect } = require("../battleEngine/effectRegistry"); +const { getEffect } = require("../battleEngine/data/effectRegistry"); const buildPartyString = ( pokemons, From d6b90e35f69dab2376dfeafb974e9fefc8a54dff Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 17:34:35 -0700 Subject: [PATCH 29/38] more refactor --- src/{battleEngine => battle}/battleConfig.js | 0 src/{battleEngine => battle}/battleTypes.js | 0 src/{battleEngine => battle}/data/effectRegistry.js | 0 src/{battleEngine => battle}/data/effects.js | 0 src/{battleEngine => battle}/data/initialize.js | 0 src/{battleEngine => battle}/data/moveService.js | 0 src/{battleEngine => battle}/data/moves.js | 0 src/{battleEngine => battle}/utils.js | 0 src/components/selectBattleMoveRow.js | 2 +- src/embeds/battleEmbeds.js | 4 ++-- src/embeds/pokemonEmbeds.js | 4 ++-- src/enums/battleEnums.js | 4 ++-- src/index.js | 2 +- src/services/battle.js | 6 +++--- src/services/mythic.js | 2 +- src/services/spawn.js | 2 +- src/utils/battleUtils.js | 4 ++-- src/utils/pokemonUtils.js | 2 +- 18 files changed, 16 insertions(+), 16 deletions(-) rename src/{battleEngine => battle}/battleConfig.js (100%) rename src/{battleEngine => battle}/battleTypes.js (100%) rename src/{battleEngine => battle}/data/effectRegistry.js (100%) rename src/{battleEngine => battle}/data/effects.js (100%) rename src/{battleEngine => battle}/data/initialize.js (100%) rename src/{battleEngine => battle}/data/moveService.js (100%) rename src/{battleEngine => battle}/data/moves.js (100%) rename src/{battleEngine => battle}/utils.js (100%) diff --git a/src/battleEngine/battleConfig.js b/src/battle/battleConfig.js similarity index 100% rename from src/battleEngine/battleConfig.js rename to src/battle/battleConfig.js diff --git a/src/battleEngine/battleTypes.js b/src/battle/battleTypes.js similarity index 100% rename from src/battleEngine/battleTypes.js rename to src/battle/battleTypes.js diff --git a/src/battleEngine/data/effectRegistry.js b/src/battle/data/effectRegistry.js similarity index 100% rename from src/battleEngine/data/effectRegistry.js rename to src/battle/data/effectRegistry.js diff --git a/src/battleEngine/data/effects.js b/src/battle/data/effects.js similarity index 100% rename from src/battleEngine/data/effects.js rename to src/battle/data/effects.js diff --git a/src/battleEngine/data/initialize.js b/src/battle/data/initialize.js similarity index 100% rename from src/battleEngine/data/initialize.js rename to src/battle/data/initialize.js diff --git a/src/battleEngine/data/moveService.js b/src/battle/data/moveService.js similarity index 100% rename from src/battleEngine/data/moveService.js rename to src/battle/data/moveService.js diff --git a/src/battleEngine/data/moves.js b/src/battle/data/moves.js similarity index 100% rename from src/battleEngine/data/moves.js rename to src/battle/data/moves.js diff --git a/src/battleEngine/utils.js b/src/battle/utils.js similarity index 100% rename from src/battleEngine/utils.js rename to src/battle/utils.js diff --git a/src/components/selectBattleMoveRow.js b/src/components/selectBattleMoveRow.js index 835e1aa3..7f66551c 100644 --- a/src/components/selectBattleMoveRow.js +++ b/src/components/selectBattleMoveRow.js @@ -8,7 +8,7 @@ */ const { ActionRowBuilder, StringSelectMenuBuilder } = require("discord.js"); const { eventNames } = require("../config/eventConfig"); -const { getMove } = require("../battleEngine/data/moveService"); +const { getMove } = require("../battle/data/moveService"); const { typeConfig } = require("../config/pokemonConfig"); /** diff --git a/src/embeds/battleEmbeds.js b/src/embeds/battleEmbeds.js index 6f4c4093..4d43b9d2 100644 --- a/src/embeds/battleEmbeds.js +++ b/src/embeds/battleEmbeds.js @@ -7,8 +7,8 @@ * battleEmbeds.js Handles all embedded instructions for battles. */ const { EmbedBuilder } = require("discord.js"); -const { weatherConditions } = require("../battleEngine/battleConfig"); -const { getMove } = require("../battleEngine/data/moveService"); +const { weatherConditions } = require("../battle/battleConfig"); +const { getMove } = require("../battle/data/moveService"); const { buildPartyString, buildMoveString, diff --git a/src/embeds/pokemonEmbeds.js b/src/embeds/pokemonEmbeds.js index 273ffb42..048678a0 100644 --- a/src/embeds/pokemonEmbeds.js +++ b/src/embeds/pokemonEmbeds.js @@ -15,8 +15,8 @@ const { typeConfig, growthRateConfig, } = require("../config/pokemonConfig"); -const { abilityConfig } = require("../battleEngine/battleConfig"); -const { getMove } = require("../battleEngine/data/moveService"); +const { abilityConfig } = require("../battle/battleConfig"); +const { getMove } = require("../battle/data/moveService"); const { getWhitespace, getPBar, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index 6d054021..dc5eb9e6 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -1,7 +1,7 @@ const types = require("../../types"); /** - * @typedef {import("../battleEngine/battleConfig").LegacyEffectIdEnum} LegacyEffectIdEnum + * @typedef {import("../battle/battleConfig").LegacyEffectIdEnum} LegacyEffectIdEnum * @typedef {types.Enum} NewEffectIdEnum * @typedef {LegacyEffectIdEnum | NewEffectIdEnum} EffectIdEnum */ @@ -13,7 +13,7 @@ const effectIdEnum = Object.freeze({ }); /** - * @typedef {import("../battleEngine/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum + * @typedef {import("../battle/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum * @typedef {types.Enum} NewMoveIdEnum * @typedef {LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum */ diff --git a/src/index.js b/src/index.js index fc523b96..4c87c587 100644 --- a/src/index.js +++ b/src/index.js @@ -21,7 +21,7 @@ const { getStateCount } = require("./services/state"); const { cleanupRaids } = require("./services/raid"); const { initialize: initializeBattleData, -} = require("./battleEngine/data/initialize"); +} = require("./battle/data/initialize"); console.log(`STAGE: ${process.env.STAGE}`); const FFLAG_ENABLE_SPAWN = process.env.FFLAG_ENABLE_SPAWN === "1"; diff --git a/src/services/battle.js b/src/services/battle.js index a7f786de..b492a5c2 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -25,7 +25,7 @@ const { abilityConfig, typeAdvantages, weatherConditions, -} = require("../battleEngine/battleConfig"); +} = require("../battle/battleConfig"); const { buildBattleEmbed, buildPveListEmbed, @@ -76,8 +76,8 @@ const { generateRandomPokemon } = require("./gacha"); const { validateParty } = require("./party"); const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); -const { getMove, executeMove } = require("../battleEngine/data/moveService"); -const { getEffect } = require("../battleEngine/data/effectRegistry"); +const { getMove, executeMove } = require("../battle/data/moveService"); +const { getEffect } = require("../battle/data/effectRegistry"); class NPC { constructor( diff --git a/src/services/mythic.js b/src/services/mythic.js index ff714bb2..53eb8708 100644 --- a/src/services/mythic.js +++ b/src/services/mythic.js @@ -46,7 +46,7 @@ const { calculateAndUpdatePokemonStats, } = require("./pokemon"); const { getTrainer, updateTrainer } = require("./trainer"); -const { getMoves } = require("../battleEngine/data/moveService"); +const { getMoves } = require("../battle/data/moveService"); const getMythic = async (trainer, speciesId) => { const speciesData = pokemonConfig[speciesId]; diff --git a/src/services/spawn.js b/src/services/spawn.js index 88da40e0..da7a2a76 100644 --- a/src/services/spawn.js +++ b/src/services/spawn.js @@ -3,7 +3,7 @@ const { ButtonStyle } = require("discord.js"); const { buildButtonActionRow } = require("../components/buttonActionRow"); const { backpackItems } = require("../config/backpackConfig"); -const { typeAdvantages } = require("../battleEngine/battleConfig"); +const { typeAdvantages } = require("../battle/battleConfig"); const { eventNames } = require("../config/eventConfig"); const { pokeballConfig } = require("../config/gachaConfig"); const { diff --git a/src/utils/battleUtils.js b/src/utils/battleUtils.js index 92465789..973c2cc6 100644 --- a/src/utils/battleUtils.js +++ b/src/utils/battleUtils.js @@ -6,12 +6,12 @@ * * battleUtils.js the lowest level of code for battles used by the battle.js */ -const { statusConditions } = require("../battleEngine/battleConfig"); +const { statusConditions } = require("../battle/battleConfig"); const { difficultyConfig } = require("../config/npcConfig"); const { pokemonConfig, typeConfig } = require("../config/pokemonConfig"); const { getRewardsString, flattenRewards } = require("./trainerUtils"); const { getPBar, formatMoney } = require("./utils"); -const { getEffect } = require("../battleEngine/data/effectRegistry"); +const { getEffect } = require("../battle/data/effectRegistry"); const buildPartyString = ( pokemons, diff --git a/src/utils/pokemonUtils.js b/src/utils/pokemonUtils.js index c57fafdb..1c3ffc60 100644 --- a/src/utils/pokemonUtils.js +++ b/src/utils/pokemonUtils.js @@ -12,7 +12,7 @@ const { growthRateConfig, } = require("../config/pokemonConfig"); const { getPBar, getWhitespace } = require("./utils"); -const { abilityConfig } = require("../battleEngine/battleConfig"); +const { abilityConfig } = require("../battle/battleConfig"); const { equipmentConfig, modifierSlotConfig, From 450ae61ead1d4339aa37ae454825662cc406afff Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 17:50:13 -0700 Subject: [PATCH 30/38] more moving around --- src/battle/battleTypes.js | 16 +- src/battle/data/effects.js | 2 +- src/battle/data/initialize.js | 6 +- src/battle/data/moves.js | 2 +- src/battle/engine/battle.js | 3584 ++++++++++++++++++++++++ src/{battle => config}/battleConfig.js | 10 +- src/embeds/battleEmbeds.js | 2 +- src/embeds/pokemonEmbeds.js | 2 +- src/enums/battleEnums.js | 4 +- src/services/battle.js | 2 +- src/services/spawn.js | 2 +- src/utils/battleUtils.js | 2 +- src/utils/pokemonUtils.js | 2 +- 13 files changed, 3614 insertions(+), 22 deletions(-) create mode 100644 src/battle/engine/battle.js rename src/{battle => config}/battleConfig.js (99%) diff --git a/src/battle/battleTypes.js b/src/battle/battleTypes.js index d9f16bbc..5f73972c 100644 --- a/src/battle/battleTypes.js +++ b/src/battle/battleTypes.js @@ -3,14 +3,14 @@ * @typedef {import("../enums/battleEnums").EffectIdEnum} EffectIdEnum * @typedef {import("../services/battle").Battle} Battle * @typedef {import("../services/battle").Pokemon} BattlePokemon - * @typedef {import("./battleConfig").BattleEventEnum} BattleEventEnum - * @typedef {import("./battleConfig").DamageTypeEnum} DamageTypeEnum - * @typedef {import("./battleConfig").MoveTierEnum} MoveTierEnum - * @typedef {import("./battleConfig").StatusConditionEnum} StatusConditionEnum - * @typedef {import("./battleConfig").TargetTypeEnum} TargetTypeEnum - * @typedef {import("./battleConfig").TargetPositionEnum} TargetPositionEnum - * @typedef {import("./battleConfig").TargetPatternEnum} TargetPatternEnum - * @typedef {import("./battleConfig").EffectTypeEnum} EffectTypeEnum + * @typedef {import("../config/battleConfig").BattleEventEnum} BattleEventEnum + * @typedef {import("../config/battleConfig").DamageTypeEnum} DamageTypeEnum + * @typedef {import("../config/battleConfig").MoveTierEnum} MoveTierEnum + * @typedef {import("../config/battleConfig").StatusConditionEnum} StatusConditionEnum + * @typedef {import("../config/battleConfig").TargetTypeEnum} TargetTypeEnum + * @typedef {import("../config/battleConfig").TargetPositionEnum} TargetPositionEnum + * @typedef {import("../config/battleConfig").TargetPatternEnum} TargetPatternEnum + * @typedef {import("../config/battleConfig").EffectTypeEnum} EffectTypeEnum * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} EffectAddBasicArgs * @typedef {{battle: Battle, target: BattlePokemon}} EffectRemoveBasicArgs * @typedef {typeof import("./data/effects").effectsToRegister} RegisteredEffects diff --git a/src/battle/data/effects.js b/src/battle/data/effects.js index ee40bbe5..7e108a4e 100644 --- a/src/battle/data/effects.js +++ b/src/battle/data/effects.js @@ -1,5 +1,5 @@ /* eslint-disable no-param-reassign */ -const { effectTypes } = require("../battleConfig"); +const { effectTypes } = require("../../config/battleConfig"); const { effectIdEnum } = require("../../enums/battleEnums"); /** diff --git a/src/battle/data/initialize.js b/src/battle/data/initialize.js index a41c841e..b94030e6 100644 --- a/src/battle/data/initialize.js +++ b/src/battle/data/initialize.js @@ -3,7 +3,11 @@ const { registerMoves, registerLegacyMoves } = require("./moveService"); const { registerEffects, registerLegacyEffects } = require("./effectRegistry"); const { effectsToRegister } = require("./effects"); -const { moveConfig, moveExecutes, effectConfig } = require("../battleConfig"); +const { + moveConfig, + moveExecutes, + effectConfig, +} = require("../../config/battleConfig"); const { movesToRegister } = require("./moves"); const initialize = () => { diff --git a/src/battle/data/moves.js b/src/battle/data/moves.js index 3649d315..01598eb8 100644 --- a/src/battle/data/moves.js +++ b/src/battle/data/moves.js @@ -6,7 +6,7 @@ const { damageTypes, moveTiers, calculateDamage, -} = require("../battleConfig"); +} = require("../../config/battleConfig"); const { getMove } = require("./moveService"); const { moveIdEnum } = require("../../enums/battleEnums"); diff --git a/src/battle/engine/battle.js b/src/battle/engine/battle.js new file mode 100644 index 00000000..185eb238 --- /dev/null +++ b/src/battle/engine/battle.js @@ -0,0 +1,3584 @@ +/* eslint-disable no-case-declarations */ +// TODO: probably fix both +/* eslint-disable no-param-reassign */ +/* eslint-disable max-classes-per-file */ + +/** + * @file + * @author Elvis Wei + * @date 2023 + * @section Description + * + * battle.js Handles all battle interactions from the user at a base level down to creating the teams. + */ +const { v4: uuidv4 } = require("uuid"); +const { getOrSetDefault, formatMoney } = require("../../utils/utils"); +const { pokemonConfig, types } = require("../../config/pokemonConfig"); +const { + battleEventNames, + targetTypes, + targetPatterns, + targetPositions, + statusConditions, + moveTiers, + calculateDamage, + abilityConfig, + typeAdvantages, + weatherConditions, +} = require("../../config/battleConfig"); +const { + buildBattleEmbed, + buildPveListEmbed, + buildPveNpcEmbed, + buildDungeonListEmbed, + buildDungeonEmbed, + buildBattleTowerEmbed, +} = require("../../embeds/battleEmbeds"); +const { + buildSelectBattleMoveRow, +} = require("../../components/selectBattleMoveRow"); +const { buildButtonActionRow } = require("../../components/buttonActionRow"); +const { + buildBattleInfoActionRow, +} = require("../../components/battleInfoActionRow"); +const { + getTrainer, + addExpAndMoney, + updateTrainer, +} = require("../../services/trainer"); +const { + addPokemonExpAndEVs, + getPokemon, + calculatePokemonStats, +} = require("../../services/pokemon"); +const { logger } = require("../../log"); +const { + buildNextTurnActionRow, +} = require("../../components/battleNextTurnRow"); +const { deleteState } = require("../../services/state"); +const { + calculateEffectiveSpeed, + calculateEffectiveAccuracy, + calculateEffectiveEvasion, + getMoveIds, +} = require("../../utils/pokemonUtils"); +const { + npcConfig, + difficultyConfig, + dungeons, + dungeonConfig, + battleTowerConfig, +} = require("../../config/npcConfig"); +const { buildScrollActionRow } = require("../../components/scrollActionRow"); +const { getState } = require("../../services/state"); +const { eventNames } = require("../../config/eventConfig"); +const { + buildIdConfigSelectRow, +} = require("../../components/idConfigSelectRow"); +const { + drawIterable, + drawUniform, + drawDiscrete, +} = require("../../utils/gachaUtils"); +const { generateRandomPokemon } = require("../../services/gacha"); +const { validateParty } = require("../../services/party"); +const { addRewards, getRewardsString } = require("../../utils/trainerUtils"); +const { getIdFromTowerStage } = require("../../utils/battleUtils"); +const { getMove, executeMove } = require("../data/moveService"); +const { getEffect } = require("../data/effectRegistry"); + +class NPC { + constructor( + npcData, + difficulty, + // eslint-disable-next-line no-unused-vars + { userId = null, user = null, party = null } = {} + ) { + this.userId = userId || uuidv4(); + this.user = { + username: npcData.name, + discriminator: "0", + npc: this, + id: this.userId, + // data: npcData, + // difficulty: difficulty, + }; + + // this.setPokemon(npcData, difficulty); + } + + setPokemon(npcData, difficulty) { + const npcDifficultyData = npcData.difficulties[difficulty]; + this.party = { + rows: 3, + cols: 4, + pokemons: [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + }; + + // generate party + // generate numPokemon - 1 pokemon, then 1 for the ace + const { numPokemon } = npcDifficultyData; + const pokemons = []; + const pokemonIds = drawIterable( + npcDifficultyData.pokemonIds, + numPokemon - 1 + ); + for (const pokemonId of pokemonIds) { + const pokemon = generateRandomPokemon( + this.userId, + pokemonId, + drawUniform( + npcDifficultyData.minLevel, + npcDifficultyData.maxLevel, + 1 + )[0] + ); + // give random id + pokemon._id = uuidv4(); + pokemons.push(pokemon); + } + // push ace + const acePokemon = generateRandomPokemon( + this.userId, + npcDifficultyData.aceId, + npcDifficultyData.maxLevel + 1 + ); + acePokemon._id = uuidv4(); + pokemons.push(acePokemon); + + // put parties in random indices with no overlap + let i = 0; + while (i < numPokemon) { + const index = drawUniform(0, this.party.rows * this.party.cols - 1, 1)[0]; + if (this.party.pokemons[index] === null) { + this.party.pokemons[index] = pokemons[i]; + i += 1; + } + } + } + + action(battle) { + const { activePokemon } = battle; + if (activePokemon.userId !== this.userId) { + return; + } + + /* steps: + if cant move, skip turn + get all moves filtered by those with eligible targets and usable + for all considered moves, get the best move + best move/target: for all targets: + get how many targets would be hit by AoE + calculate heuristic + if move does damage, calculate damage that would be dealt + else, calculate heuristic = numTargets * source level * 1.5 + normalize heuristic by move accuracy, or *1.2 if move has no accuracy + normalize heuristic by move tier + choose best move & target based off heuristic + use move */ + + // if cant move, skip turn + if (!activePokemon.canMove()) { + activePokemon.skipTurn(); + return; + } + + // get all moves filtered by those with eligible targets and usable + const { moveIds } = activePokemon; + const validMoveIdsToTargets = {}; + Object.entries(moveIds).forEach(([moveId, move]) => { + if (move.disabled || move.cooldown > 0) { + return; + } + + const eligibleTargets = battle.getEligibleTargets(activePokemon, moveId); + if (eligibleTargets.length > 0) { + validMoveIdsToTargets[moveId] = eligibleTargets; + } + }); + + // if for some reason no moves exist, skip turn + if (Object.keys(validMoveIdsToTargets).length === 0) { + activePokemon.skipTurn(); + return; + } + + // for all considered moves, get the best move + let bestMoveId = null; + let bestTarget = null; + let bestHeuristic = -1; + for (const moveId in validMoveIdsToTargets) { + for (const target of validMoveIdsToTargets[moveId]) { + const source = activePokemon; + const targetsHit = source.getTargets(moveId, target.id); + const heuristic = this.calculateHeuristic(moveId, source, targetsHit); + if (heuristic > bestHeuristic) { + bestMoveId = moveId; + bestTarget = target; + bestHeuristic = heuristic; + } + } + } + + // use move + activePokemon.useMove(bestMoveId, bestTarget.id); + } + + // eslint-disable-next-line class-methods-use-this + calculateHeuristic(moveId, source, targetsHit) { + const moveData = getMove(moveId); + + let heuristic = 0; + // special case: if asleep and sleep talk, use sleep talk + if ( + source.status.statusId === statusConditions.SLEEP && + moveId === "m214" + ) { + return 1000000; + } + // special case: if move is rocket thievery and enemy team has no fainted pokemon, return 0 + if (moveId === "m20003") { + const enemyParty = source.getEnemyParty(); + if ( + enemyParty && + enemyParty.pokemons && + enemyParty.pokemons.filter((p) => p && p.isFainted).length === 0 + ) { + return 0; + } + } + // special case: if move is gear fifth, use if under 25% hp + if (moveId === "m20010") { + if (source.hp / source.maxHp > 0.25) { + return 0; + } + return 1000000; + } + + if (moveData.power !== null) { + // if move does damage, calculate damage that would be dealt + for (const target of targetsHit) { + const damage = calculateDamage(moveData, source, target, false); + heuristic += damage; + } + } else { + // else, calculate heuristic = numTargets * source level * 1.5 + heuristic = targetsHit.length * source.level * 1.5; + } + // normalize heuristic by move accuracy, or *1.2 if move has no accuracy + const { accuracy } = moveData; + heuristic *= accuracy === null ? 1.2 : accuracy / 100; + + // multiply heuristic by move tier. basic = 0.7, power = 1, ultimate = 1.5 + const moveTier = moveData.tier; + let tierMultiplier; + if (moveTier === moveTiers.BASIC) { + tierMultiplier = 0.7; + } else if (moveTier === moveTiers.POWER) { + tierMultiplier = 1; + } else { + tierMultiplier = 1.5; + } + heuristic *= tierMultiplier; + + // calculate nonce for small random variation + const nonce = Math.random(); + return heuristic + nonce; + } +} + +class DungeonNPC extends NPC { + constructor(dungeonData, difficulty) { + super(dungeonData, difficulty); + this.phaseNumber = 0; + this.phases = dungeonData.difficulties[difficulty].phases; + this.dungeonData = dungeonData; + this.difficulty = difficulty; + // suuper hacky, probably a better way to do this + this.user.nextPhase = (battle) => this.nextPhase(battle); + } + + // eslint-disable-next-line no-unused-vars + setPokemon(dungeonData, difficulty) { + const phase = this.phases[this.phaseNumber]; + if (phase === undefined) { + return; + } + this.party = { + rows: phase.rows, + cols: phase.cols, + pokemons: Array(phase.rows * phase.cols).fill(null), + }; + + // generate party + for (const pokemonData of phase.pokemons) { + const pokemon = generateRandomPokemon( + this.userId, + pokemonData.speciesId, + pokemonData.level + ); + // give random id + pokemon._id = uuidv4(); + this.party.pokemons[pokemonData.position - 1] = pokemon; + } + } + + // eslint-disable-next-line no-unused-vars + nextPhase(battle) { + this.phaseNumber += 1; + if (this.phaseNumber >= this.phases.length) { + return false; + } + this.setPokemon(this.dungeonData, this.difficulty); + + return { + trainer: this, + ...this.party, + }; + } +} + +class TowerNPC extends NPC { + constructor(towerData, difficulty) { + const npcData = npcConfig[towerData.npcId]; + super(npcData, difficulty); + } + + setPokemon(towerData, difficulty) { + const npcData = npcConfig[towerData.npcId]; + const npcDifficultyData = npcData.difficulties[difficulty]; + this.party = { + rows: 3, + cols: 4, + pokemons: [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + }; + + // generate party + // generate numPokemon - 1 pokemon, then 1 for the ace + const { numPokemon } = npcDifficultyData; + const pokemons = []; + const pokemonIds = drawIterable( + npcDifficultyData.pokemonIds, + numPokemon - 1 + ); + for (const pokemonId of pokemonIds) { + const pokemon = generateRandomPokemon( + this.userId, + pokemonId, + drawUniform(towerData.minLevel, towerData.maxLevel, 1)[0] + ); + // give random id + pokemon._id = uuidv4(); + pokemons.push(pokemon); + } + // push ace + const acePokemon = generateRandomPokemon( + this.userId, + npcDifficultyData.aceId, + towerData.maxLevel + 1 + ); + acePokemon._id = uuidv4(); + pokemons.push(acePokemon); + + // put parties in random indices with no overlap + let i = 0; + while (i < numPokemon) { + const index = drawUniform(0, this.party.rows * this.party.cols - 1, 1)[0]; + if (this.party.pokemons[index] === null) { + this.party.pokemons[index] = pokemons[i]; + i += 1; + } + } + } +} + +class RaidNPC extends NPC { + constructor(raidData, difficulty, raidUserId, boss) { + super(raidData, difficulty, { + userId: raidUserId, + }); + this.raidData = raidData; + this.difficulty = difficulty; + this.boss = boss; + } + + setPokemon(raidData, difficulty) { + const raidDifficultyData = raidData.difficulties[difficulty]; + this.party = { + rows: raidDifficultyData.rows, + cols: raidDifficultyData.cols, + pokemons: Array(raidDifficultyData.rows * raidDifficultyData.cols).fill( + null + ), + }; + + // generate party + for (const pokemonData of raidDifficultyData.pokemons) { + const pokemon = + pokemonData.speciesId === this.boss.speciesId + ? { ...this.boss } + : generateRandomPokemon( + this.userId, + pokemonData.speciesId, + pokemonData.level + ); + // give random id + pokemon._id = pokemon._id || uuidv4(); + this.party.pokemons[pokemonData.position - 1] = pokemon; + } + } +} + +class BattleEventHandler { + /* battle; + // event name => listenerIds + eventNames; + // listenerId => listener + eventListeners; */ + + constructor(battle) { + this.battle = battle; + this.eventNames = {}; + this.eventListeners = {}; + } + + registerListener(eventName, listener) { + // generate listener UUID + const listenerId = uuidv4(); + + getOrSetDefault(this.eventNames, eventName, []).push(listenerId); + this.eventListeners[listenerId] = listener; + // add listenerId and eventName to listener.initialargs + // eslint-disable-next-line no-param-reassign + listener.initialArgs = { + listenerId, + eventName, + ...listener.initialArgs, + }; + + return listenerId; + } + + unregisterListener(listenerId) { + const listener = this.eventListeners[listenerId]; + if (listener) { + const { eventName } = listener.initialArgs; + const listenerIds = this.eventNames[eventName]; + if (listenerIds) { + const index = listenerIds.indexOf(listenerId); + if (index > -1) { + listenerIds.splice(index, 1); + } + } + delete this.eventListeners[listenerId]; + } + } + + emit(eventName, args) { + const listenerIds = this.eventNames[eventName]; + if (listenerIds) { + for (const listenerId of listenerIds) { + const listener = this.eventListeners[listenerId]; + if (listener) { + listener.execute(listener.initialArgs, args); + } + } + } + } +} + +class Pokemon { + /* battle; + pokemonData; + speciesId; + speciesData; + id; + userId; + originalUserId; + teamName; + name; + level; + hp; + maxHp; + atk; + batk; + def; + bdef; + spa; + bspa; + spd; + bspd; + spe; + bspe; + acc; + eva; + type1; + type2; + // effectId => { duration, args } + effectIds; + // moveId => { cooldown, disabled } + moveIds; + // { statusId. tuns active } + status; + // { abilityId, args } + ability; + combatReadiness; + position; + isFainted; + targetable; + hittable; + incapacitated; + restricted; */ + + constructor(battle, trainer, pokemonData, teamName, position) { + this.battle = battle; + this.speciesId = pokemonData.speciesId; + this.speciesData = pokemonConfig[this.speciesId]; + // if battle has a set level, scale pokemon to that level + if (battle.level) { + battle.minLevel = Math.min(battle.minLevel, pokemonData.level); + pokemonData.level = battle.level; + } + // if battle has an equipment level, set all pokemons equipments to that level + if (battle.equipmentLevel) { + for (const equipment of Object.values(pokemonData.equipments)) { + equipment.level = battle.equipmentLevel; + } + } + pokemonData = calculatePokemonStats(pokemonData, this.speciesData); + this.pokemonData = pokemonData; + this.id = pokemonData._id.toString(); + this.userId = trainer.userId; + this.originalUserId = trainer.userId; + this.teamName = teamName; + this.name = pokemonData.name; + this.hp = pokemonData.remainingHp || pokemonData.stats[0]; + [this.maxHp = 0] = this.pokemonData.stats; + this.level = pokemonData.level; + [ + this.atk = 0, + this.batk = 0, + this.def, + this.bdef = 0, + this.spa, + this.bspa = 0, + this.spd, + this.bspd = 0, + this.spe, + this.bspe = 0, + ] = [ + pokemonData.stats[1], + pokemonData.stats[1], + pokemonData.stats[2], + pokemonData.stats[2], + pokemonData.stats[3], + pokemonData.stats[3], + pokemonData.stats[4], + pokemonData.stats[4], + pokemonData.stats[5], + pokemonData.stats[5], + ]; + this.acc = 100; + this.eva = 100; + [this.type1 = null, this.type2 = null] = this.speciesData.type; + // map effectId => effect data (duration, args) + this.effectIds = {}; + // map moveId => move data (cooldown, disabled) + this.addMoves(pokemonData); + this.setAbility(pokemonData); + this.status = { + statusId: null, + turns: 0, + }; + this.combatReadiness = 0; + this.position = position; + this.isFainted = false; + this.targetable = true; + this.hittable = true; + this.incapacitated = false; + this.restricted = false; + } + + addMoves(pokemonData) { + this.moveIds = getMoveIds(pokemonData).reduce((acc, moveId) => { + acc[moveId] = { + cooldown: 0, + disabled: false, + }; + return acc; + }, {}); + } + + setAbility(pokemonData) { + this.ability = { + abilityId: pokemonData.abilityId, + }; + } + + transformInto(speciesId, { abilityId = null, moveIds = [] } = {}) { + const oldName = this.name; + // remove ability + this.removeAbility(); + + // get old stat ratios (excpet hp) + const atkRatio = this.atk / this.batk; + const defRatio = this.def / this.bdef; + const spaRatio = this.spa / this.bspa; + const spdRatio = this.spd / this.bspd; + const speRatio = this.spe / this.bspe; + + // set new species values + this.speciesId = speciesId; + this.speciesData = pokemonConfig[this.speciesId]; + [this.type1, this.type2 = null] = this.speciesData.type; + this.type2 = this.speciesData.type[1] || null; + this.name = this.speciesData.name; + + // set pokemon data object + this.pokemonData.speciesId = this.speciesId; + this.pokemonData.name = this.speciesData.name; + this.pokemonData.moveIds = moveIds; + this.pokemonData.abilityId = + abilityId || drawDiscrete(this.speciesData.abilities, 1)[0]; + calculatePokemonStats(this.pokemonData, this.speciesData); + + // set stats (except hp) + [, this.atk, this.bdef, this.bspa, this.bspd, this.bspe] = + this.pokemonData.stats; + this.atk = Math.round(this.pokemonData.stats[1] * atkRatio); + this.def = Math.round(this.pokemonData.stats[2] * defRatio); + this.spa = Math.round(this.pokemonData.stats[3] * spaRatio); + this.spd = Math.round(this.pokemonData.stats[4] * spdRatio); + this.spe = Math.round(this.pokemonData.stats[5] * speRatio); + + // set moves and ability + this.addMoves(this.pokemonData); + this.setAbility(this.pokemonData); + this.applyAbility(); + + this.battle.addToLog(`${oldName} transformed into ${this.name}!`); + } + + useMove(moveId, targetPokemonId) { + // make sure pokemon can move + if (!this.canMove()) { + return; + } + // make sure move exists and is not on cooldown & disabled + const moveData = getMove(moveId); + if ( + !moveData || + this.moveIds[moveId].cooldown > 0 || + this.moveIds[moveId].disabled + ) { + return; + } + // check to see if target is valid + const primaryTarget = this.battle.allPokemon[targetPokemonId]; + if ( + !primaryTarget || + !this.battle.getEligibleTargets(this, moveId).includes(primaryTarget) + ) { + return; + } + + // clear battle log + this.battle.clearLog(); + + // reset combat readiness + this.combatReadiness = 0; + + let canUseMove = true; + + // check status conditions + if (this.status.statusId !== null) { + switch (this.status.statusId) { + case statusConditions.FREEZE: + // if move is flare blitz, thaw out + if (moveId === "m394" || moveId === "m394-1") { + this.removeStatus(); + break; + } + + // thaw chance (turns => chance): 0 => 0%, 1 => 40%, 2 => 80%, 3 => 100% + const thawChance = this.status.turns * 0.4; + const thawRoll = Math.random(); + if (thawRoll < thawChance) { + this.removeStatus(); + } else { + this.battle.addToLog(`${this.name} is frozen solid!`); + canUseMove = false; + } + break; + case statusConditions.PARALYSIS: + // 25% chance to be paralyzed + const paralysisRoll = Math.random(); + if (paralysisRoll < 0.25) { + this.battle.addToLog(`${this.name} is paralyzed and can't move!`); + canUseMove = false; + } + break; + case statusConditions.SLEEP: + // ignore if sleep talk is used + if (moveId === "m214") { + break; + } + // sleep wakeup chance: 0 turns: 0%, 1 turn: 66%, 2 turns: 100% + const wakeupChance = this.status.turns * 0.66; + const wakeupRoll = Math.random(); + if (wakeupRoll < wakeupChance) { + this.removeStatus(); + } else { + this.battle.addToLog(`${this.name} is fast asleep!`); + canUseMove = false; + } + break; + default: + break; + } + } + + if (canUseMove) { + const eventArgs = { + canUseMove, + source: this, + primaryTarget, + moveId, + }; + + // trigger before move events + this.battle.eventHandler.emit(battleEventNames.BEFORE_MOVE, eventArgs); + + canUseMove = eventArgs.canUseMove; + } + + // check if pokemon can use move make sure pokemon is alive + canUseMove = !!(canUseMove && !this.isFainted); + + // get move data and execute move + if (canUseMove) { + // see if move log should be silenced + const isSilenced = + moveData.silenceIf && moveData.silenceIf(this.battle, this); + // if pokemon alive, get all targets + const allTargets = this.getTargets(moveId, targetPokemonId); + if (!isSilenced) { + const targetString = + moveData.targetPattern === targetPatterns.ALL || + moveData.targetPattern === targetPatterns.ALL_EXCEPT_SELF || + moveData.targetPattern === targetPatterns.RANDOM || + moveData.targetPosition === targetPositions.SELF + ? "!" + : ` against ${primaryTarget.name}!`; + this.battle.addToLog( + `${this.name} used ${moveData.name}${targetString}` + ); + } + // calculate miss + const missedTargets = this.getMisses(moveId, allTargets); + // if misses, log miss + if (missedTargets.length > 0 && !isSilenced) { + this.battle.addToLog( + `Missed ${missedTargets.map((target) => target.name).join(", ")}!` + ); + } + + // set cooldown + this.moveIds[moveId].cooldown = moveData.cooldown; + + // trigger before execute move events + const executeEventArgs = { + source: this, + primaryTarget, + targets: allTargets, + missedTargets, + moveId, + }; + this.battle.eventHandler.emit( + battleEventNames.BEFORE_MOVE_EXECUTE, + executeEventArgs + ); + + // execute move + executeMove({ + moveId, + battle: this.battle, + source: this, + primaryTarget, + allTargets, + missedTargets, + }); + + // after move event + const eventArgs = { + source: this, + primaryTarget, + targets: allTargets, + missedTargets, + moveId, + }; + this.battle.eventHandler.emit(battleEventNames.AFTER_MOVE, eventArgs); + } + + // end turn + this.battle.nextTurn(); + } + + /** + * Called if the Pokemon is the active Pokemon but can't move. + * @returns null + */ + skipTurn() { + // make sure its this Pokemon's turn + if (this.battle.activePokemon !== this) { + return; + } + // make sure Pokemon can't move + if (this.canMove()) { + return; + } + + // clear battle log + this.battle.clearLog(); + + // reset combat readiness + this.combatReadiness = 0; + + // check pre-move status conditions that tick regardless of move + if (this.status.statusId !== null) { + switch (this.status.statusId) { + case statusConditions.FREEZE: + // thaw chance (turns => chance): 0 => 0%, 1 => 40%, 2 => 80%, 3 => 100% + const thawChance = this.status.turns * 0.4; + const thawRoll = Math.random(); + if (thawRoll < thawChance) { + this.removeStatus(); + } + break; + case statusConditions.SLEEP: + // sleep wakeup chance: 0 turns: 0%, 1 turn: 66%, 2 turns: 100% + const wakeupChance = this.status.turns * 0.66; + const wakeupRoll = Math.random(); + if (wakeupRoll < wakeupChance) { + this.removeStatus(); + } + break; + default: + break; + } + } + + // end turn + this.battle.nextTurn(); + } + + /** + * Check that the Pokemon may take a valid action. + * @returns True or False + */ + canMove() { + // make sure its this pokemon's turn + if (this.battle.activePokemon !== this) { + return false; + } + + // if fainted, return + if (this.isFainted) { + return false; + } + + // if incapacitated, return + if (this.incapacitated) { + return false; + } + + // for all non-disabled moves not on cooldown, check to see if valid targets exist + for (const moveId in this.moveIds) { + if (this.moveIds[moveId].disabled || this.moveIds[moveId].cooldown > 0) { + continue; + } + const eligibleTargets = this.battle.getEligibleTargets(this, moveId); + if (eligibleTargets.length > 0) { + return true; + } + } + + // if no valid targets exist, return false + return false; + } + + getTypeDamageMultiplier(moveType, targetPokemon) { + let mult = 1; + if (typeAdvantages[moveType]) { + let adv = typeAdvantages[moveType][targetPokemon.type1]; + if (adv !== undefined) { + mult *= adv; + } + + adv = typeAdvantages[moveType][targetPokemon.type2]; + if (adv !== undefined) { + mult *= adv; + } + } + + const eventArgs = { + source: this, + target: targetPokemon, + moveType, + multiplier: mult, + }; + this.battle.eventHandler.emit( + battleEventNames.CALCULATE_TYPE_MULTIPLIER, + eventArgs + ); + + return eventArgs.multiplier; + } + + getPatternTargets(targetParty, targetPattern, targetPosition, moveId = null) { + const targetRow = Math.floor((targetPosition - 1) / targetParty.cols); + const targetCol = (targetPosition - 1) % targetParty.cols; + const targets = []; + + switch (targetPattern) { + case targetPatterns.ALL: + // return all pokemon in party + for (const pokemon of targetParty.pokemons) { + if (this.battle.isPokemonHittable(pokemon, moveId)) { + targets.push(pokemon); + } + } + break; + case targetPatterns.ALL_EXCEPT_SELF: + // return all pokemon in party except self + for (const pokemon of targetParty.pokemons) { + if ( + this.battle.isPokemonHittable(pokemon, moveId) && + pokemon !== this + ) { + targets.push(pokemon); + } + } + break; + case targetPatterns.COLUMN: + // return all pokemon in column + for (const pokemon of targetParty.pokemons) { + if ( + this.battle.isPokemonHittable(pokemon, moveId) && + (pokemon.position - 1) % targetParty.cols === targetCol + ) { + targets.push(pokemon); + } + } + break; + case targetPatterns.ROW: + // return all pokemon in row + for (const pokemon of targetParty.pokemons) { + if ( + this.battle.isPokemonHittable(pokemon, moveId) && + Math.floor((pokemon.position - 1) / targetParty.cols) === targetRow + ) { + targets.push(pokemon); + } + } + break; + case targetPatterns.RANDOM: + // return random pokemon in party + const validPokemons = []; + for (const pokemon of targetParty.pokemons) { + if (this.battle.isPokemonHittable(pokemon, moveId)) { + validPokemons.push(pokemon); + } + } + targets.push( + validPokemons[Math.floor(Math.random() * validPokemons.length)] + ); + break; + case targetPatterns.SQUARE: + // if row index or column index within 1 of target, add to targets + for (const pokemon of targetParty.pokemons) { + if (this.battle.isPokemonHittable(pokemon, moveId)) { + const pokemonRow = Math.floor( + (pokemon.position - 1) / targetParty.cols + ); + const pokemonCol = (pokemon.position - 1) % targetParty.cols; + if ( + Math.abs(targetRow - pokemonRow) <= 1 && + Math.abs(targetCol - pokemonCol) <= 1 + ) { + targets.push(pokemon); + } + } + } + break; + case targetPatterns.CROSS: + // target manhattan distance <= 1, add to targets + for (const pokemon of targetParty.pokemons) { + if (this.battle.isPokemonHittable(pokemon, moveId)) { + const pokemonRow = Math.floor( + (pokemon.position - 1) / targetParty.cols + ); + const pokemonCol = (pokemon.position - 1) % targetParty.cols; + if ( + Math.abs(targetRow - pokemonRow) + + Math.abs(targetCol - pokemonCol) <= + 1 + ) { + targets.push(pokemon); + } + } + } + break; + default: + // default is single + + // get target pokemon + const targetPokemon = + targetParty.pokemons[targetRow * targetParty.cols + targetCol]; + if (this.battle.isPokemonHittable(targetPokemon, moveId)) { + targets.push(targetPokemon); + } + + break; + } + + return targets; + } + + getTargets(moveId, targetPokemonId) { + const moveData = getMove(moveId); + // make sure target exists and is alive + const target = this.battle.allPokemon[targetPokemonId]; + if (!target) { + return []; + } + + // get party of target + const targetParty = this.battle.parties[target.teamName]; + + const allTargets = []; + return [ + ...allTargets, + ...this.getPatternTargets( + targetParty, + moveData.targetPattern, + target.position, + moveId + ), + ]; + } + + getMisses(moveId, targetPokemons) { + const moveData = getMove(moveId); + const misses = []; + if (!moveData.accuracy) { + return misses; + } + for (const target of targetPokemons) { + let hitChance = + (moveData.accuracy * calculateEffectiveAccuracy(this.acc)) / + calculateEffectiveEvasion(target.eva); + const damageMult = this.getTypeDamageMultiplier(moveData.type, target); + if (damageMult >= 4) { + hitChance *= 1.4; + } else if (damageMult >= 2) { + hitChance *= 1.15; + } else if (damageMult === 0) { + hitChance = 0; + } else if (damageMult <= 0.25) { + hitChance *= 0.6; + } else if (damageMult <= 0.5) { + hitChance *= 0.8; + } + + // weather check + if (!this.battle.isWeatherNegated()) { + if (this.battle.weather.weatherId === weatherConditions.SUN) { + if ( + moveId === "m87" || + moveId === "m87-1" || + moveId === "m542" || + moveId === "m542-1" + ) { + hitChance *= 0.75; + } + } else if (this.battle.weather.weatherId === weatherConditions.RAIN) { + if ( + moveId === "m87" || + moveId === "m87-1" || + moveId === "m542" || + moveId === "m542-1" + ) { + hitChance = 150; + } + } else if (this.battle.weather.weatherId === weatherConditions.HAIL) { + if (moveId === "m59") { + hitChance = 150; + } + } + } + + const calculateMissArgs = { + target, + hitChance, + source: this, + }; + this.battle.eventHandler.emit( + battleEventNames.CALCULATE_MISS, + calculateMissArgs + ); + + if (Math.random() > calculateMissArgs.hitChance / 100) { + misses.push(target); + } + } + return misses; + } + + switchUsers(userId) { + if (this.userId === userId) { + return false; + } + // get user info from battle + const user = this.battle.users[userId]; + if (!user) { + return false; + } + const { teamName } = user; + if (!teamName) { + return false; + } + + // set teamName and userId + this.teamName = teamName; + this.userId = userId; + + this.battle.addToLog(`${this.name} switched to ${user.username}'s team!`); + + return true; + } + + // eslint-disable-next-line no-unused-vars + switchPositions(userId, newPosition, source) { + const currentIndex = this.position - 1; + const newIndex = newPosition - 1; + + // get user info from battle + const user = this.battle.users[userId]; + if (!user) { + return false; + } + const newTeamName = user.teamName; + if (!newTeamName) { + return false; + } + + const oldParty = this.battle.parties[this.teamName]; + const newParty = this.battle.parties[newTeamName]; + if (!newParty) { + return false; + } + + // check if position is valid + if (newPosition < 1 || newPosition > newParty.pokemons.length) { + return false; + } + // if pokemon exists in new position, return false + if (newParty.pokemons[newIndex]) { + return false; + } + + // if new team, attempt to switch teams + if (this.teamName !== newTeamName) { + if (!this.switchUsers(userId)) { + return false; + } + } + + // remove from old position + oldParty.pokemons[currentIndex] = null; + + // add to new position + newParty.pokemons[newIndex] = this; + this.position = newPosition; + + this.battle.addToLog( + `${this.name} switched to position to ${newPosition}!` + ); + + return true; + } + + dealDamage(damage, target, damageInfo) { + if (damage <= 0) { + return 0; + } + + // if pvp, deal 15% less damage + if (this.battle.isPvp) { + damage = Math.max(1, Math.floor(damage * 0.85)); + } + + const eventArgs = { + target, + damage, + source: this, + damageInfo, + }; + + this.battle.eventHandler.emit( + battleEventNames.BEFORE_DAMAGE_DEALT, + eventArgs + ); + damage = eventArgs.damage; + + const damageDealt = target.takeDamage(damage, this, damageInfo); + + if (damageDealt > 0) { + const afterDamageArgs = { + target, + damage: damageDealt, + source: this, + damageInfo, + }; + this.battle.eventHandler.emit( + battleEventNames.AFTER_DAMAGE_DEALT, + afterDamageArgs + ); + } + + return damageDealt; + } + + takeDamage(damage, source, damageInfo) { + if (this.isFainted) { + return 0; + } + + // if frozen and fire type or scald, thaw and deal 1.5x damage + const freezeCheck = + this.status.statusId === statusConditions.FREEZE && + damageInfo.type === "move" && + getMove(damageInfo.moveId) !== undefined && + (getMove(damageInfo.moveId).type === types.FIRE || + damageInfo.moveId === "m503"); + if (freezeCheck) { + if (this.removeStatus()) { + damage = Math.floor(damage * 1.5); + } + } + + // if shield, take shield damage and return 0 + const shieldData = this.effectIds.shield; + if (shieldData && shieldData.args && shieldData.args.shield) { + const shieldDamage = Math.min(damage, shieldData.args.shield); + shieldData.args.shield -= shieldDamage; + + this.battle.addToLog( + `${this.name} took ${shieldDamage} shield damage! (${shieldData.args.shield} left)` + ); + if (shieldData.args.shield <= 0) { + this.removeEffect("shield"); + } + return 0; + } + + const eventArgs = { + target: this, + damage, + source, + damageInfo, + maxDamage: Number.MAX_SAFE_INTEGER, + }; + + this.battle.eventHandler.emit( + battleEventNames.BEFORE_DAMAGE_TAKEN, + eventArgs + ); + damage = Math.min(eventArgs.damage, eventArgs.maxDamage); + + const oldHp = this.hp; + if (oldHp <= 0 || this.isFainted) { + return 0; + } + this.hp = Math.max(0, this.hp - damage); + const damageTaken = oldHp - this.hp; + this.battle.addToLog(`${this.name} took ${damageTaken} damage!`); + if (this.hp <= 0) { + this.hp = 0; + this.faint(source); + } + + if (damageTaken > 0) { + const afterDamageArgs = { + target: this, + damage: damageTaken, + source, + damageInfo, + }; + this.battle.eventHandler.emit( + battleEventNames.AFTER_DAMAGE_TAKEN, + afterDamageArgs + ); + } + + return damageTaken; + } + + takeFaint(source) { + if (this.isFainted) { + return; + } + // trigger before cause faint effects + const beforeCauseFaintArgs = { + target: this, + source, + canFaint: true, + }; + this.battle.eventHandler.emit( + battleEventNames.BEFORE_CAUSE_FAINT, + beforeCauseFaintArgs + ); + if (!beforeCauseFaintArgs.canFaint) { + return; + } + + this.faint(source); + } + + faint(source) { + // TODO: trigger before faint effects + + this.hp = 0; + this.isFainted = true; + this.removeAbility(); + this.battle.addToLog(`${this.name} fainted!`); + + // trigger after faint effects + const afterFaintArgs = { + target: this, + source, + }; + this.battle.eventHandler.emit(battleEventNames.AFTER_FAINT, afterFaintArgs); + } + + // eslint-disable-next-line no-unused-vars + beRevived(reviveHp, source) { + if (!this.isFainted) { + return; + } + if (reviveHp <= 0) { + return; + } + + this.hp = Math.min(this.maxHp, reviveHp); + this.isFainted = false; + this.battle.addToLog(`${this.name} was revived!`); + + // re-add ability + this.applyAbility(); + } + + applyAbility() { + const { abilityId } = this.ability; + const abilityData = abilityConfig[abilityId]; + if (!abilityData || !abilityData.abilityAdd) { + return; + } + + this.ability.data = abilityData.abilityAdd(this.battle, this, this); + this.ability.applied = true; + } + + removeAbility() { + // remove ability effects + const { abilityId } = this.ability; + const abilityData = abilityConfig[abilityId]; + if (abilityData && abilityData.abilityRemove) { + abilityData.abilityRemove(this.battle, this, this); + } + } + + giveHeal(heal, target, healInfo) { + const healGiven = target.takeHeal(heal, this, healInfo); + return healGiven; + } + + // eslint-disable-next-line no-unused-vars + takeHeal(heal, source, healInfo) { + const oldHp = this.hp; + if (oldHp <= 0 || this.isFainted) { + return 0; + } + this.hp = Math.min(this.maxHp, this.hp + heal); + const healTaken = this.hp - oldHp; + if (healTaken > 0) { + this.battle.addToLog(`${this.name} healed ${healTaken} HP!`); + } + return healTaken; + } + + boostCombatReadiness(source, amount, triggerEvents = true) { + // if faint, do nothing + if (this.isFainted) { + return 0; + } + + if (this.restricted) { + this.battle.addToLog( + `${this.name} is restricted and cannot gain combat readiness!` + ); + return 0; + } + + const beforeBoostArgs = { + target: this, + source, + amount, + }; + if (triggerEvents) { + this.battle.eventHandler.emit( + battleEventNames.BEFORE_CR_GAINED, + beforeBoostArgs + ); + } + + const oldCombatReadiness = this.combatReadiness; + this.combatReadiness = Math.min( + 100, + this.combatReadiness + beforeBoostArgs.amount + ); + const combatReadinessGained = this.combatReadiness - oldCombatReadiness; + this.battle.addToLog( + `${this.name} gained ${Math.round( + combatReadinessGained + )} combat readiness!` + ); + + if (combatReadinessGained > 0 && triggerEvents) { + const eventArgs = { + target: this, + source, + combatReadinessGained: amount, + }; + this.battle.eventHandler.emit( + battleEventNames.AFTER_CR_GAINED, + eventArgs + ); + } + + return combatReadinessGained; + } + + reduceCombatReadiness(source, amount) { + // if faint, do nothing + if (this.isFainted) { + return 0; + } + + const oldCombatReadiness = this.combatReadiness; + this.combatReadiness = Math.max(0, this.combatReadiness - amount); + const combatReadinessLost = oldCombatReadiness - this.combatReadiness; + this.battle.addToLog( + `${this.name} lost ${Math.round(combatReadinessLost)} combat readiness!` + ); + return combatReadinessLost; + } + + /** + * @template {EffectIdEnum} K + * @param {K} effectId + * @param {number} duration + * @param {BattlePokemon} source + * @param {EffectInitialArgsTypeFromId} initialArgs + */ + applyEffect(effectId, duration, source, initialArgs) { + // if faint, do nothing + if (this.isFainted) { + return; + } + const effect = getEffect(effectId); + if (!effect) { + logger.error(`Effect ${effectId} does not exist.`); + return; + } + + duration = this.battle.activePokemon === this ? duration + 1 : duration; + + // if effect already exists for longer or equal duration, do nothing (special case for shield) + if ( + this.effectIds[effectId] && + this.effectIds[effectId].duration >= duration && + effectId !== "shield" + ) { + return; + } + + // if effect exists, refresh duration + // TODO: should this be modified? + if (this.effectIds[effectId]) { + duration = Math.max(this.effectIds[effectId].duration, duration); + this.effectIds[effectId].duration = duration; + // shield special case + if (effectId !== "shield") { + return; + } + } + + // trigger before effect add events + const beforeAddArgs = { + target: this, + source, + effectId, + duration, + initialArgs, + canAdd: true, + }; + this.battle.eventHandler.emit( + battleEventNames.BEFORE_EFFECT_ADD, + beforeAddArgs + ); + if (!beforeAddArgs.canAdd) { + return; + } + duration = beforeAddArgs.duration; + + // special case for shield + let oldShield = {}; + if (effectId === "shield") { + // if shield already exists, keep note of it + if (this.effectIds[effectId]) { + oldShield = this.effectIds[effectId].args; + } + } + this.effectIds[effectId] = { + duration, + source, + initialArgs, + }; + if (oldShield) { + this.effectIds[effectId].args = oldShield; + } + + if (!effect.isLegacyEffect) { + this.effectIds[effectId].args = + effect.effectAdd({ + battle: this.battle, + source, + target: this, + initialArgs, + }) || {}; + } else { + const legacyEffect = /** @type {any} */ (effect); + this.effectIds[effectId].args = + legacyEffect.effectAdd(this.battle, source, this, initialArgs) || {}; + } + + if (this.effectIds[effectId] !== undefined) { + // trigger after add effect events + const afterAddArgs = { + target: this, + source, + effectId, + duration, + initialArgs: initialArgs, + args: this.effectIds[effectId].args, + }; + this.battle.eventHandler.emit( + battleEventNames.AFTER_EFFECT_ADD, + afterAddArgs + ); + } + } + + dispellEffect(effectId) { + const effectData = getEffect(effectId); + + // if effect doesn't exist, do nothing + if (!this.effectIds[effectId]) { + return false; + } + + // if effect is not dispellable, do nothing + if (!effectData.dispellable) { + return false; + } + + return this.removeEffect(effectId); + } + + /** + * @template {EffectIdEnum} K + * @param {K} effectId + * @returns {{ + * duration: number, + * source: Pokemon, + * initialArgs: K extends keyof RegisteredEffects ? EffectInitialArgsType : any, + * args: K extends keyof RegisteredEffects ? EffectPropertiesType : any + * } | undefined} + */ + getEffectInstance(effectId) { + // @ts-ignore + return this.effectIds[effectId]; + } + + /** + * Forcefully deletes the effect instance from this Pokemon. ONLY USE to bypass effect removal logic. + * @param {EffectIdEnum} effectId + */ + deleteEffectInstance(effectId) { + delete this.effectIds[effectId]; + } + + removeEffect(effectId) { + // if effect doesn't exist, do nothing + if (!this.effectIds[effectId]) { + return false; + } + const effect = getEffect(effectId); + if (!effect) { + logger.error(`Effect ${effectId} does not exist.`); + return; + } + + if (!effect.isLegacyEffect) { + // @ts-ignore + effect.effectRemove({ + battle: this.battle, + target: this, + properties: this.effectIds[effectId].args, + initialArgs: this.effectIds[effectId].initialArgs, + }); + } else { + const legacyEffect = /** @type {any} */ (effect); + legacyEffect.effectRemove( + this.battle, + this, + this.effectIds[effectId].args, + this.effectIds[effectId].initialArgs + ); + } + + if (this.effectIds[effectId] !== undefined) { + const afterRemoveArgs = { + target: this, + source: this.effectIds[effectId].source, + effectId, + duration: this.effectIds[effectId].duration, + initialArgs: this.effectIds[effectId].initialArgs, + args: this.effectIds[effectId].args, + }; + this.battle.eventHandler.emit( + battleEventNames.AFTER_EFFECT_REMOVE, + afterRemoveArgs + ); + } + + delete this.effectIds[effectId]; + return true; + } + + applyStatus(statusId, source, { startingTurns = 0 } = {}) { + // if faint, do nothing + if (this.isFainted) { + return; + } + + // if status already exists, do nothing + if (this.status.statusId) { + this.battle.addToLog(`${this.name} already has a status condition!`); + return; + } + + // trigger before apply status events + const beforeApplyArgs = { + target: this, + source, + statusId, + canApply: true, + }; + this.battle.eventHandler.emit( + battleEventNames.BEFORE_STATUS_APPLY, + beforeApplyArgs + ); + if (!beforeApplyArgs.canApply) { + return; + } + + let statusApplied = false; + switch (statusId) { + // TODO: other status effects + case statusConditions.BURN: + if (this.type1 === types.FIRE || this.type2 === types.FIRE) { + this.battle.addToLog( + `${this.name}'s Fire type renders it immune to burns!` + ); + break; + } + + this.status = { + statusId, + source, + turns: startingTurns, + }; + this.battle.addToLog(`${this.name} was burned!`); + statusApplied = true; + break; + case statusConditions.FREEZE: + if (this.type1 === types.ICE || this.type2 === types.ICE) { + this.battle.addToLog( + `${this.name}'s Ice type renders it immune to freezing!` + ); + break; + } + + if (this.battle.weather.weatherId === weatherConditions.SUN) { + this.battle.addToLog( + `${this.name} was protected from freezing by the sun!` + ); + break; + } + + this.status = { + statusId, + source, + turns: startingTurns, + }; + this.battle.addToLog(`${this.name} was frozen!`); + statusApplied = true; + break; + case statusConditions.PARALYSIS: + if (this.type1 === types.ELECTRIC || this.type2 === types.ELECTRIC) { + this.battle.addToLog( + `${this.name}'s Electric type renders it immune to paralysis!` + ); + break; + } + + // reduce speed by 45% + this.spe -= Math.floor(this.bspd * 0.45); + + this.status = { + statusId, + source, + turns: startingTurns, + }; + this.battle.addToLog(`${this.name} was paralyzed!`); + statusApplied = true; + break; + case statusConditions.POISON: + if (this.type1 === types.POISON || this.type2 === types.POISON) { + this.battle.addToLog( + `${this.name}'s Poison type renders it immune to poison!` + ); + break; + } + + this.status = { + statusId, + source, + turns: startingTurns, + }; + this.battle.addToLog(`${this.name} was poisoned!`); + statusApplied = true; + break; + case statusConditions.SLEEP: + this.status = { + statusId, + source, + turns: startingTurns, + }; + this.battle.addToLog(`${this.name} fell asleep!`); + statusApplied = true; + break; + case statusConditions.BADLY_POISON: + if (this.type1 === types.POISON || this.type2 === types.POISON) { + this.battle.addToLog( + `${this.name}'s Poison type renders it immune to poison!` + ); + break; + } + + this.status = { + statusId, + source, + turns: startingTurns, + }; + this.battle.addToLog(`${this.name} was badly poisoned!`); + statusApplied = true; + break; + default: + break; + } + + if (statusApplied) { + const afterStatusArgs = { + target: this, + source, + statusId, + }; + this.battle.eventHandler.emit( + battleEventNames.AFTER_STATUS_APPLY, + afterStatusArgs + ); + } + } + + tickStatus() { + // if status doesn't exist, do nothing + if (!this.status.statusId) { + return; + } + + switch (this.status.statusId) { + case statusConditions.POISON: + const damage = Math.round(this.maxHp / 6); + this.battle.addToLog(`${this.name} is hurt by poison!`); + this.takeDamage(damage, this.status.source, { + type: "statusCondition", + statusId: statusConditions.POISON, + }); + break; + case statusConditions.BURN: + const burnDamage = Math.round(this.maxHp / 8); + this.battle.addToLog(`${this.name} is hurt by its burn!`); + this.takeDamage(burnDamage, this.status.source, { + type: "statusCondition", + statusId: statusConditions.BURN, + }); + break; + case statusConditions.BADLY_POISON: + const badlyPoisonDamage = + Math.round(this.maxHp / 6) * 2 ** this.status.turns; + this.battle.addToLog(`${this.name} is hurt by poison!`); + this.takeDamage(badlyPoisonDamage, this.status.source, { + type: "statusCondition", + statusId: statusConditions.BADLY_POISON, + }); + break; + default: + break; + } + + this.status.turns += 1; + } + + removeStatus() { + // if status doesn't exist, do nothing + if (!this.status.statusId) { + return false; + } + + // TODO: trigger before remove status events + + switch (this.status.statusId) { + case statusConditions.BURN: + this.battle.addToLog(`${this.name} was cured of its burn!`); + break; + case statusConditions.FREEZE: + this.battle.addToLog(`${this.name} was thawed out!`); + break; + case statusConditions.PARALYSIS: + // restore speed + this.spe += Math.floor(this.bspd * 0.45); + + this.battle.addToLog(`${this.name} was cured of its paralysis!`); + break; + case statusConditions.POISON: + this.battle.addToLog(`${this.name} was cured of its poison!`); + break; + case statusConditions.SLEEP: + this.battle.addToLog(`${this.name} woke up!`); + break; + default: + break; + } + + this.status = { + statusId: null, + turns: 0, + }; + + return true; + } + + // eslint-disable-next-line no-unused-vars + disableMove(moveId, source) { + // check that move exists + if (!this.moveIds[moveId]) { + return; + } + + // if move already disabled, do nothing + if (this.moveIds[moveId].disabled) { + return; + } + + // disable move + this.moveIds[moveId].disabled = true; + // this.battle.addToLog(`${this.name}'s ${getMove(moveId).name} was disabled!`); + } + + // eslint-disable-next-line no-unused-vars + enableMove(moveId, source) { + // check that move exists + if (!this.moveIds[moveId]) { + return; + } + + // if move not disabled, do nothing + if (!this.moveIds[moveId].disabled) { + return; + } + + // enable move + this.moveIds[moveId].disabled = false; + // this.battle.addToLog(`${this.name}'s ${getMove(moveId).name} is no longer disabled!`); + } + + tickEffectDurations() { + for (const effectId in this.effectIds) { + this.effectIds[effectId].duration -= 1; + if (this.effectIds[effectId].duration <= 0) { + this.removeEffect(effectId); + } + } + } + + tickMoveCooldowns() { + for (const moveId in this.moveIds) { + if (this.moveIds[moveId].cooldown > 0) { + this.moveIds[moveId].cooldown -= 1; + } + } + } + + reduceMoveCooldown(moveId, amount, source, silenced = false) { + // check that move exists + if (!this.moveIds[moveId]) { + return; + } + + // check that the move is on cooldown + if (this.moveIds[moveId].cooldown === 0) { + return; + } + + // reduce cooldown + const oldCooldown = this.moveIds[moveId].cooldown; + this.moveIds[moveId].cooldown -= amount; + if (this.moveIds[moveId].cooldown < 0) { + this.moveIds[moveId].cooldown = 0; + } + const newCooldown = this.moveIds[moveId].cooldown; + + if (!silenced) { + this.battle.addToLog( + `${this.name}'s ${getMove(moveId).name}'s cooldown was reduced by ${ + oldCooldown - newCooldown + } turns!` + ); + } + return oldCooldown - newCooldown; + } + + effectiveSpeed() { + return calculateEffectiveSpeed(this.getSpe()); + } + + getRowAndColumn() { + const party = this.battle.parties[this.teamName]; + const row = Math.floor((this.position - 1) / party.cols); + const col = (this.position - 1) % party.cols; + return { row, col }; + } + + getPartyRowColumn() { + const party = this.battle.parties[this.teamName]; + return { party, ...this.getRowAndColumn() }; + } + + getEnemyParty() { + const teamNames = Object.keys(this.battle.parties); + const enemyTeamName = + teamNames[0] === this.teamName ? teamNames[1] : teamNames[0]; + return this.battle.parties[enemyTeamName]; + } + + getDef() { + let { def } = this; + + // if hail and ice, def * 1.5 + if ( + !this.battle.isWeatherNegated() && + this.battle.weather.weatherId === weatherConditions.HAIL && + (this.type1 === types.ICE || this.type2 === types.ICE) + ) { + def = Math.floor(def * 1.5); + } + + return def; + } + + getSpd() { + let { spd } = this; + + // if sandstorm and rock, spd * 1.5 + if ( + !this.battle.isWeatherNegated() && + this.battle.weather.weatherId === weatherConditions.SANDSTORM && + (this.type1 === types.ROCK || this.type2 === types.ROCK) + ) { + spd = Math.floor(spd * 1.5); + } + + return spd; + } + + getSpe() { + let { spe } = this; + + if (!this.battle.isWeatherNegated()) { + if (this.battle.weather.weatherId === weatherConditions.SUN) { + if (this.ability && this.ability.abilityId === "34") { + spe = Math.floor(spe * 1.5); + } + } else if (this.battle.weather.weatherId === weatherConditions.RAIN) { + if (this.ability && this.ability.abilityId === "33") { + spe = Math.floor(spe * 1.5); + } + } + } + + return spe; + } +} + +class Battle { + /* TODO: fix + moneyMultiplier; + expMultiplier; + pokemonExpMultiplier; + moneyReward; + expReward; + pokemonExpReward; + winCallback; + hasStarted; + userIds; + users; + teams; + activePokemon; + parties; + // { weatherId, duration, source } + weather; + log; + eventHandler; + turn; + winner; + ended; + minLevel; + level; + rewards; + rewardString; + dailyRewards; + npcId; + difficulty; + isPvp; */ + + /** + * @param {object} param0 + * @param {number?=} param0.moneyMultiplier + * @param {number?=} param0.expMultiplier + * @param {number?=} param0.pokemonExpMultiplier + * @param {number?=} param0.level + * @param {number?=} param0.equipmentLevel + * @param {object?=} param0.rewards + * @param {string?=} param0.rewardString + * @param {object?=} param0.dailyRewards + * @param {Function?=} param0.winCallback + * @param {Function?=} param0.loseCallback + * @param {string?=} param0.npcId + * @param {string?=} param0.difficulty + * @param {boolean?=} param0.isPvp + */ + constructor({ + moneyMultiplier = 1, + expMultiplier = 1, + pokemonExpMultiplier = 0.2, + level = null, + equipmentLevel = null, + rewards = null, + rewardString = null, + dailyRewards = null, + winCallback = null, + loseCallback = null, + npcId = null, + difficulty = null, + isPvp = false, + } = {}) { + // initial + this.baseMoney = 100; + this.baseExp = 50; + + this.moneyMultiplier = moneyMultiplier; + this.expMultiplier = expMultiplier; + this.pokemonExpMultiplier = pokemonExpMultiplier; + this.moneyReward = 0; + this.expReward = 0; + this.pokemonExpReward = 0; + this.hasStarted = false; + this.userIds = []; + // map userId to user + this.users = {}; + // map teamName to team + this.teams = {}; + this.activePokemon = null; + // map teamName to party + this.parties = {}; + // map pokemonId to pokemon + this.allPokemon = {}; + this.weather = { + weatherId: null, + duration: 0, + source: null, + }; + this.log = []; + this.eventHandler = new BattleEventHandler(this); + this.turn = 0; + this.winner = null; + this.ended = false; + if (equipmentLevel) { + if (equipmentLevel < 1 || equipmentLevel > 10) { + throw new Error("Invalid equipment level"); + } + this.equipmentLevel = equipmentLevel; + } + if (level) { + if (level < 1 || level > 100) { + throw new Error("Invalid level"); + } + this.level = level; + this.minLevel = level; + } + this.rewards = rewards; + this.rewardString = rewardString; + this.dailyRewards = dailyRewards; + this.npcId = npcId; + this.difficulty = difficulty; + this.winCallback = winCallback; + this.loseCallback = loseCallback; + this.isPvp = isPvp; + } + + addTeam(teamName, isNpc) { + this.teams[teamName] = { + name: teamName, + isNpc, + userIds: [], + }; + } + + addTrainer(trainer, pokemons, teamName, rows = 3, cols = 4) { + // if user already exists, return + if (this.users[trainer.userId]) { + return; + } + + this.teams[teamName].userIds.push(trainer.userId); + this.users[trainer.userId] = { + teamName, + ...trainer.user, + }; + this.userIds.push(trainer.userId); + + this.addPokemons(trainer, pokemons, teamName, rows, cols); + } + + addPokemons(trainer, pokemons, teamName, rows, cols) { + const partyPokemons = []; + for (const pokemonData of pokemons) { + if (pokemonData) { + const pokemonInstance = new Pokemon( + this, + trainer, + pokemonData, + teamName, + partyPokemons.length + 1 + ); + partyPokemons.push(pokemonInstance); + this.allPokemon[pokemonInstance.id] = pokemonInstance; + } else { + partyPokemons.push(null); + } + } + // TODO: modify if parties can have different num players + this.parties[teamName] = { + pokemons: partyPokemons, + rows, + cols, + }; + } + + increaseCombatReadiness() { + // get min ticks for a pokemon to be ready + const MAX_CR = 100; + let minTicks = Number.MAX_SAFE_INTEGER; + let minTicksPokemon = null; + for (const partyName in this.parties) { + const party = this.parties[partyName]; + for (const pokemon of party.pokemons) { + if (pokemon && !pokemon.isFainted) { + const requiredCr = MAX_CR - pokemon.combatReadiness; + const ticks = requiredCr / pokemon.effectiveSpeed(); + if (ticks < minTicks) { + minTicks = ticks; + minTicksPokemon = pokemon; + } + } + } + } + + // set active pokemon + if (minTicksPokemon) { + this.activePokemon = minTicksPokemon; + } else { + this.activePokemon = null; + return; + } + + // if no ticks, return + if (minTicks === 0) { + return; + } + + // increase combat readiness for all pokemon + for (const partyName in this.parties) { + const party = this.parties[partyName]; + for (const pokemon of party.pokemons) { + if (pokemon && !pokemon.isFainted) { + pokemon.combatReadiness = Math.min( + MAX_CR, + pokemon.combatReadiness + pokemon.effectiveSpeed() * minTicks + ); + } + } + } + } + + start() { + this.log.push("The battle begins!"); + this.turn = 0; + this.hasStarted = true; + + // sort all pokemon by speed descending + const sortedPokemon = Object.values(this.allPokemon); + sortedPokemon.sort((a, b) => b.getSpe() - a.getSpe()); + this.allPokemon = {}; + for (const pokemon of sortedPokemon) { + this.allPokemon[pokemon.id] = pokemon; + } + + // add all abilities + Object.entries(this.allPokemon).forEach(([, pokemon]) => { + if (pokemon.ability.applied) { + return; + } + pokemon.applyAbility(); + }); + + this.eventHandler.emit(battleEventNames.BATTLE_BEGIN, { + battle: this, + }); + + // begin turn + this.beginTurn(); + } + + beginTurn() { + // push cr + this.increaseCombatReadiness(); + + // tick move cooldowns + if (!this.activePokemon.isFainted) { + this.activePokemon.tickMoveCooldowns(); + } + + // begin turn + this.eventHandler.emit(battleEventNames.TURN_BEGIN); + + // log + const userIsNpc = this.isNpc(this.activePokemon.userId); + const userString = userIsNpc + ? this.users[this.activePokemon.userId].username + : `<@${this.activePokemon.userId}>`; + if (this.activePokemon.canMove()) { + this.log.push( + `**[Turn ${this.turn}] It is ${userString}'s ${this.activePokemon.name}'s turn.**` + ); + } else { + this.log.push( + `**[Turn ${this.turn}] ${userString}'s ${this.activePokemon.name} is unable to move.**` + ); + } + } + + nextTurn() { + // end turn logic + this.eventHandler.emit(battleEventNames.TURN_END); + + // tick status effects + if (!this.activePokemon.isFainted) { + this.activePokemon.tickStatus(); + } + + // tick effects + if (!this.activePokemon.isFainted) { + this.activePokemon.tickEffectDurations(); + } + + // tick weather + if (this.weather) { + this.tickWeather(); + } + + // increase turn and check for game end + this.turn += 1; + if (this.turn > 100) { + return this.endBattle(); + } + const undefeatedTeams = []; + for (const teamName in this.teams) { + let isTeamDefeated = true; + for (const pokemon of this.parties[teamName].pokemons) { + if (pokemon && !pokemon.isFainted) { + isTeamDefeated = false; + break; + } + } + if (!isTeamDefeated) { + undefeatedTeams.push(teamName); + } + } + if (undefeatedTeams.length === 1) { + [this.winner] = undefeatedTeams; + return this.endBattle(); + } + if (undefeatedTeams.length === 0) { + return this.endBattle(); + } + + // begin turn + this.beginTurn(); + } + + endBattle() { + if (this.winner) { + // get loser, and if any loser has next phase, initialize next phase. + const loser = + Object.keys(this.teams)[0] === this.winner + ? Object.keys(this.teams)[1] + : Object.keys(this.teams)[0]; + let nextPhase = false; + for (const userId of this.teams[loser].userIds) { + const user = this.users[userId]; + if (user.nextPhase !== undefined) { + const userNextPhase = user.nextPhase(this); + if (!userNextPhase) { + continue; + } + const { trainer, pokemons, rows, cols } = userNextPhase; + + // clear out all of user's pokemon + for (const [pokemonId, pokemon] of Object.entries(this.allPokemon)) { + if (pokemon.userId === userId) { + delete this.allPokemon[pokemonId]; + } + } + // add new pokemon + this.addPokemons(trainer, pokemons, loser, rows, cols); + nextPhase = true; + } + } + if (nextPhase) { + this.addToLog(`**[Phase Complete] A new battle phase begins!**`); + return this.start(); + } + + let winnerMentions = ""; + if (!this.teams[this.winner].isNpc) { + winnerMentions = this.teams[this.winner].userIds + .map((userId) => `<@${userId}>`) + .join(" "); + } + this.addToLog(`**Team ${this.winner} has won! ${winnerMentions}**`); + } else { + this.addToLog("**The battle has ended in a draw!**"); + } + this.ended = true; + + // if winner is npc, no rewards + if (this.teams[this.winner] && this.teams[this.winner].isNpc) { + this.addToLog("No rewards were given because the winner is an NPC."); + return; + } + + this.moneyReward = Math.floor(this.baseMoney * this.moneyMultiplier); + this.expReward = Math.floor(this.baseExp * this.expMultiplier); + // calculate pokemon exp by summing defeated pokemon's levels + this.pokemonExpReward = Math.floor( + Object.values(this.allPokemon).reduce((acc, pokemon) => { + if (pokemon.isFainted) { + return acc + (this.minLevel || pokemon.level); + } + return acc; + }, 0) * this.pokemonExpMultiplier + ); + + this.addToLog( + `Winners recieved ${formatMoney(this.moneyReward)}, ${ + this.expReward + } exp, and ${ + this.pokemonExpReward + } BASE Pokemon exp. Losers recieved half the amount.` + ); + } + + createWeather(weatherId, source) { + // calculate turns = 10 + number of non-fainted Pokemon + const duration = + 10 + + Object.values(this.allPokemon).reduce((acc, pokemon) => { + if (!pokemon.isFainted) { + return acc + 1; + } + return acc; + }, 0); + + // if weather exists and is same, refresh duration if possible + if (this.weather.weatherId === weatherId) { + if (this.weather.duration < duration) { + this.weather.duration = duration; + this.weather.source = source; + this.addToLog(`The weather intensified!`); + return true; + } + return false; + } + + // apply weather + this.weather = { + weatherId, + duration, + source, + }; + switch (weatherId) { + case weatherConditions.SUN: + this.addToLog(`The sunlight turned harsh!`); + break; + case weatherConditions.RAIN: + this.addToLog(`It started to rain!`); + break; + case weatherConditions.SANDSTORM: + this.addToLog(`A sandstorm kicked up!`); + break; + case weatherConditions.HAIL: + this.addToLog(`It started to hail!`); + break; + default: + break; + } + + return true; + } + + clearWeather() { + if (this.weather.weatherId) { + this.addToLog(`The weather cleared up!`); + } + this.weather = { + weatherId: null, + duration: 0, + source: null, + }; + } + + tickWeather() { + if (!this.weather.weatherId) { + return; + } + + if (!this.isWeatherNegated()) { + switch (this.weather.weatherId) { + case weatherConditions.SANDSTORM: + // tick weather for active Pokemon's team + this.addToLog(`The sandstorm rages!`); + for (const pokemon of this.parties[this.activePokemon.teamName] + .pokemons) { + // if pokemon not targetable, skip + if (this.isPokemonTargetable(pokemon) === false) { + continue; + } + // if pokemon not rock, steel, or ground, damage 1/16 of max hp + if ( + pokemon.type1 !== types.ROCK && + pokemon.type1 !== types.STEEL && + pokemon.type1 !== types.GROUND && + pokemon.type2 !== types.ROCK && + pokemon.type2 !== types.STEEL && + pokemon.type2 !== types.GROUND + ) { + pokemon.takeDamage( + Math.floor(pokemon.maxHp / 16), + this.weather.source, + { + type: "weather", + } + ); + } + } + break; + case weatherConditions.HAIL: + // tick weather for active Pokemon's team + this.addToLog(`The hail continues!`); + for (const pokemon of this.parties[this.activePokemon.teamName] + .pokemons) { + // if pokemon not targetable, skip + if (this.isPokemonTargetable(pokemon) === false) { + continue; + } + // if pokemon not ice, damage 1/16 of max hp + if (pokemon.type1 !== types.ICE && pokemon.type2 !== types.ICE) { + pokemon.takeDamage( + Math.floor(pokemon.maxHp / 16), + this.weather.source, + { + type: "weather", + } + ); + } + } + break; + default: + break; + } + } + + this.weather.duration -= 1; + if (this.weather.duration <= 0) { + this.clearWeather(); + } + } + + isWeatherNegated() { + // for all non-fainted pokemon, check if they have cloud nine or air lock + for (const pokemon of Object.values(this.allPokemon)) { + if (pokemon.isFainted) { + continue; + } + + if ( + pokemon.ability.abilityId === "13" || + pokemon.ability.abilityId === "76" + ) { + return true; + } + } + + return false; + } + + isNpc(userId) { + const user = this.users[userId]; + return user.npc !== undefined; + } + + getEligibleTargets(source, moveId) { + const moveData = getMove(moveId); + const eligibleTargets = []; + const eventArgs = { + user: source, + moveId, + eligibleTargets, + shouldReturn: false, + }; + this.eventHandler.emit(battleEventNames.GET_ELIGIBLE_TARGETS, eventArgs); + if (eventArgs.shouldReturn) { + return eligibleTargets; + } + + let targetParty = null; + // use target type to get party + switch (moveData.targetType) { + case targetTypes.ALLY: + targetParty = this.parties[source.teamName]; + break; + case targetTypes.ENEMY: + for (const teamName in this.parties) { + if (teamName !== source.teamName) { + targetParty = this.parties[teamName]; + break; + } + } + break; + default: + // return all pokemon + for (const teamName in this.parties) { + const party = this.parties[teamName]; + for (const pokemon of party.pokemons) { + if (this.isPokemonTargetable(pokemon, moveId)) { + eligibleTargets.push(pokemon); + } + } + } + return eligibleTargets; + } + + // use target position to get valid targets + let index; + switch (moveData.targetPosition) { + case targetPositions.SELF: + eligibleTargets.push(source); + break; + case targetPositions.NON_SELF: + for (const pokemon of targetParty.pokemons) { + if (this.isPokemonTargetable(pokemon, moveId) && pokemon !== source) { + eligibleTargets.push(pokemon); + } + } + break; + case targetPositions.FRONT: + // break up party into rows + // if pokemons exist in row, add to eligible targets + // if all pokemon in front row fainted or nonexistent, move to next row + + index = 0; + for (let i = 0; i < targetParty.rows; i += 1) { + let pokemonFound = false; + for (let j = 0; j < targetParty.cols; j += 1) { + const pokemon = targetParty.pokemons[index]; + if (this.isPokemonTargetable(pokemon, moveId)) { + pokemonFound = true; + eligibleTargets.push(pokemon); + } + index += 1; + } + if (pokemonFound) { + break; + } + } + break; + case targetPositions.BACK: + // break up party into rows + // if pokemons exist in back row, add to eligible targets + // if all pokemon in back row fainted or nonexistent, move to next row + index = targetParty.pokemons.length - 1; + for (let i = targetParty.rows - 1; i >= 0; i -= 1) { + let pokemonFound = false; + for (let j = targetParty.cols - 1; j >= 0; j -= 1) { + const pokemon = targetParty.pokemons[index]; + if (this.isPokemonTargetable(pokemon, moveId)) { + pokemonFound = true; + eligibleTargets.push(pokemon); + } + index -= 1; + } + if (pokemonFound) { + break; + } + } + break; + default: + // return all pokemon in party + for (const pokemon of targetParty.pokemons) { + if (this.isPokemonTargetable(pokemon, moveId)) { + eligibleTargets.push(pokemon); + } + } + break; + } + + return eligibleTargets; + } + + // eslint-disable-next-line class-methods-use-this + isPokemonTargetable(pokemon, moveId = null) { + if (!pokemon || pokemon.isFainted) { + return false; + } + + if (!pokemon.targetable) { + // special cases + // TODO: should this be an event listener? + + // eq + dig + if (moveId === "m89" && pokemon.effectIds.burrowed !== undefined) { + return true; + } + + // bounce + thunder or gust or smackdown + if ( + (moveId === "m87" || + moveId === "m87-1" || + moveId === "m16" || + moveId === "m479") && + pokemon.effectIds.sprungUp !== undefined + ) { + return true; + } + + return false; + } + + return true; + } + + // eslint-disable-next-line class-methods-use-this + isPokemonHittable(pokemon, moveId = null) { + if (!pokemon || pokemon.isFainted) { + return false; + } + + if (!pokemon.hittable) { + // special cases + // TODO: should this be an event listener? + + // eq + dig + if (moveId === "m89" && pokemon.effectIds.burrowed !== undefined) { + return true; + } + + // bounce + thunder or gust or smackdown + if ( + (moveId === "m87" || + moveId === "m87-1" || + moveId === "m16" || + moveId === "m479") && + pokemon.effectIds.sprungUp !== undefined + ) { + return true; + } + + return false; + } + + return true; + } + + clearLog() { + this.log = []; + } + + addToLog(message) { + this.log.push(message); + } +} + +const getStartTurnSend = async (battle, stateId) => { + // clip log to last 20 lines + if (battle.log.length > 20) { + battle.log = battle.log.slice(battle.log.length - 20); + } + let content = battle.log.join("\n"); + battle.clearLog(); + + const stateEmbed = buildBattleEmbed(battle); + + const components = []; + if (!battle.ended) { + const infoRow = buildBattleInfoActionRow( + battle, + stateId, + Object.keys(battle.teams).length + 1 + ); + components.push(infoRow); + + // check if active pokemon can move + // TODO: deal with NPC case + if ( + battle.activePokemon.canMove() && + !battle.isNpc(battle.activePokemon.userId) + ) { + const selectMoveComponent = buildSelectBattleMoveRow(battle, stateId); + components.push(selectMoveComponent); + } else { + const nextTurnComponent = buildNextTurnActionRow(stateId); + components.push(nextTurnComponent); + } + } else { + // if game ended, add rewards to trainers and pokemon + // for non-NPC teams, for all trainers, add rewards + try { + // if winner is NPC, no rewards + if (!battle.teams[battle.winner].isNpc) { + if (battle.rewardString) { + content += `\n${battle.rewardString}`; + } + const rewardRecipients = []; + content += `\n**The following Pokemon leveled up:**`; + for (const teamName in battle.teams) { + const team = battle.teams[teamName]; + if (team.isNpc) { + continue; + } + + const moneyReward = + teamName === battle.winner + ? battle.moneyReward + : Math.floor(battle.moneyReward / 2); + const expReward = + teamName === battle.winner + ? battle.expReward + : Math.floor(battle.expReward / 2); + const pokemonExpReward = + teamName === battle.winner + ? battle.pokemonExpReward + : Math.floor(battle.pokemonExpReward / 2); + + // TODO: optimize this it makes too many db calls + for (const userId of team.userIds) { + const user = battle.users[userId]; + // get trainer + const trainer = await getTrainer(user); + if (trainer.err) { + logger.warn( + `Failed to get trainer for user ${user.id} after battle` + ); + continue; + } + + // trigger battle win callback + if (battle.winCallback) { + await battle.winCallback(battle, trainer.data); + } + + // add trainer rewards + await addExpAndMoney(user, expReward, moneyReward); + const defeatedDifficultiesToday = + trainer.data.defeatedNPCsToday[battle.npcId]; + const defeatedDifficulties = + trainer.data.defeatedNPCs[battle.npcId]; + const allRewards = {}; + let modified = false; + // add battle rewards + if (battle.rewards) { + addRewards(trainer.data, battle.rewards, allRewards); + modified = true; + } + // add daily rewards + if ( + battle.dailyRewards && + (!defeatedDifficultiesToday || + !defeatedDifficultiesToday.includes(battle.difficulty)) + ) { + addRewards(trainer.data, battle.dailyRewards, allRewards); + getOrSetDefault( + trainer.data.defeatedNPCsToday, + battle.npcId, + [] + ).push(battle.difficulty); + modified = true; + } + // add to defeated difficulties if not already there + if ( + !defeatedDifficulties || + !defeatedDifficulties.includes(battle.difficulty) + ) { + getOrSetDefault(trainer.data.defeatedNPCs, battle.npcId, []).push( + battle.difficulty + ); + modified = true; + } + + // attempt to add rewards + if (modified) { + const { err } = await updateTrainer(trainer.data); + if (err) { + logger.warn( + `Failed to update daily trainer for user ${user.id} after battle` + ); + continue; + } else { + // this is kinda hacky there may be a better way to do this + rewardRecipients.push({ + username: user.username, + rewards: allRewards, + }); + } + } + + const levelUps = []; + // add pokemon rewards + for (const pokemon of Object.values(battle.allPokemon).filter( + (p) => p.originalUserId === trainer.data.userId + )) { + // get db pokemon + const dbPokemon = await getPokemon(trainer.data, pokemon.id); + if (dbPokemon.err) { + logger.warn(`Failed to get pokemon ${pokemon.id} after battle`); + continue; + } + + const oldLevel = dbPokemon.data.level; + const trainResult = await addPokemonExpAndEVs( + trainer.data, + dbPokemon.data, + pokemonExpReward + ); + if (trainResult.err) { + continue; + } + const newLevel = trainResult.data.level; + + if (newLevel > oldLevel) { + levelUps.push({ + pokemonName: dbPokemon.data.name, + oldLevel, + newLevel, + }); + } + } + if (levelUps.length > 0) { + content += `\n${user.username}'s Pokemon: ${levelUps + .map((l) => `${l.pokemonName} (${l.oldLevel} -> ${l.newLevel})`) + .join(", ")}`; + } + } + + if (rewardRecipients.length > 0) { + content += `\n**${rewardRecipients + .map((r) => r.username) + .join(", ")} received rewards for their victory:**`; + content += getRewardsString(rewardRecipients[0].rewards, false); + } + } + } else if (battle.loseCallback) { + await battle.loseCallback(battle); + } + } catch (err) { + logger.error(`Failed to add battle rewards: ${err}`); + } + + // re-add log to content + content += `\n${battle.log.join("\n")}`; + + // if state has an NPC id, add a replay button + const state = getState(stateId); + if (state && state.endBattleComponents) { + components.push(...state.endBattleComponents); + } else { + deleteState(stateId); + } + } + + return { + content, + embeds: [stateEmbed], + components, + }; +}; + +const buildPveSend = async ({ + stateId = null, + user = null, + view = "list", + option = null, + page = 1, +} = {}) => { + // get state + const state = getState(stateId); + + // get trainer + let trainer = await getTrainer(user); + if (trainer.err) { + return { send: null, err: trainer.err }; + } + trainer = trainer.data; + + const send = { + embeds: [], + components: [], + }; + const pageSize = 10; + const npcIds = Object.keys(npcConfig); + if (view === "list") { + const maxPages = Math.ceil(npcIds.length / pageSize); + if (page < 1 || page > maxPages) { + return { embed: null, err: `Invalid page!` }; + } + // get npc ids for page + const npcIdsForPage = npcIds.slice((page - 1) * pageSize, page * pageSize); + + // build list embed + const embed = buildPveListEmbed(npcIdsForPage, page); + send.embeds.push(embed); + + // build scroll buttons + const scrollData = { + stateId, + }; + const scrollRow = buildScrollActionRow( + page, + page === maxPages, + scrollData, + eventNames.PVE_SCROLL + ); + send.components.push(scrollRow); + + // build npc select menu + const npcSelectRowData = { + stateId, + }; + const npcSelectRow = buildIdConfigSelectRow( + npcIdsForPage, + npcConfig, + "Select an NPC to battle:", + npcSelectRowData, + eventNames.PVE_SELECT, + false + ); + send.components.push(npcSelectRow); + } else if (view === "npc") { + // validate npc id + const npcData = npcConfig[option]; + if (npcData === undefined) { + return { embed: null, err: `Invalid NPC!` }; + } + + // build npc embed + const embed = buildPveNpcEmbed(option); + send.embeds.push(embed); + + // build difficulty row + const difficultySelectData = { + stateId, + }; + const difficultyButtonConfigs = Object.keys(npcData.difficulties).map( + (difficulty) => ({ + label: difficultyConfig[difficulty].name, + disabled: false, + data: { + ...difficultySelectData, + difficulty, + }, + }) + ); + const difficultyRow = buildButtonActionRow( + difficultyButtonConfigs, + eventNames.PVE_ACCEPT + ); + send.components.push(difficultyRow); + + // build return button + const index = npcIds.indexOf(option); + const returnToPage = Math.floor(index / pageSize) + 1; + const returnData = { + stateId, + page: returnToPage, + }; + const returnButtonConfigs = [ + { + label: "Return", + disabled: false, + data: returnData, + }, + ]; + const returnRow = buildButtonActionRow( + returnButtonConfigs, + eventNames.PVE_SCROLL + ); + + state.npcId = option; + send.components.push(returnRow); + send.content = ""; + } else if (view === "battle") { + // validate npc id + const npcData = npcConfig[state.npcId]; + if (npcData === undefined) { + return { embed: null, err: `Invalid NPC!` }; + } + + // validate difficulty + const npcDifficultyData = npcData.difficulties[state.difficulty]; + if (npcDifficultyData === undefined) { + return { + embed: null, + err: `Difficulty doesn't exist for ${npcData.name}!`, + }; + } + + // get trainer + const trainerResult = await getTrainer(user); + if (trainerResult.err) { + return { embed: null, err: trainerResult.err }; + } + + // validate party + const validate = await validateParty(trainerResult.data); + if (validate.err) { + return { err: validate.err }; + } + + // add npc to battle + const npc = new NPC(npcData, state.difficulty); + npc.setPokemon(npcData, state.difficulty); + const rewardMultipliers = + npcDifficultyData.rewardMultipliers || + difficultyConfig[state.difficulty].rewardMultipliers; + const battle = new Battle({ + ...rewardMultipliers, + dailyRewards: npcDifficultyData.dailyRewards, + npcId: state.npcId, + difficulty: state.difficulty, + }); + battle.addTeam("NPC", true); + battle.addTrainer( + npc, + npc.party.pokemons, + "NPC", + npc.party.rows, + npc.party.cols + ); + battle.addTeam("Player", false); + battle.addTrainer(trainerResult.data, validate.data, "Player"); + + // start battle and add to state + battle.start(); + state.battle = battle; + + // add a replay button to state for later + // build difficulty row + const difficultySelectData = { + stateId, + difficulty: state.difficulty, + }; + const difficultyButtonConfigs = [ + { + label: "Replay", + disabled: false, + data: difficultySelectData, + }, + ]; + const difficultyRow = buildButtonActionRow( + difficultyButtonConfigs, + eventNames.PVE_ACCEPT + ); + + const returnData = { + stateId, + }; + const returnButtonConfigs = [ + { + label: "Return", + disabled: false, + data: returnData, + }, + ]; + const returnRow = buildButtonActionRow( + returnButtonConfigs, + eventNames.PVE_SELECT + ); + + state.endBattleComponents = [difficultyRow, returnRow]; + + return { + send: await getStartTurnSend(battle, stateId), + err: null, + }; + } + + return { send, err: null }; +}; + +const buildDungeonSend = async ({ + stateId = null, + user = null, + view = "list", + option = null, +} = {}) => { + // get state + const state = getState(stateId); + + // get trainer + let trainer = await getTrainer(user); + if (trainer.err) { + return { send: null, err: trainer.err }; + } + trainer = trainer.data; + + const send = { + embeds: [], + components: [], + }; + if (view === "list") { + // build list embed + const embed = buildDungeonListEmbed(); + send.embeds.push(embed); + + // build dungeon select menu + const dungeonSelectRowData = { + stateId, + }; + const dungeonSelectRow = buildIdConfigSelectRow( + Object.values(dungeons), + dungeonConfig, + "Select a Dungeon to battle:", + dungeonSelectRowData, + eventNames.DUNGEON_SELECT, + false + ); + send.components.push(dungeonSelectRow); + } else if (view === "dungeon") { + // validate npc id + const dungeonData = dungeonConfig[option]; + if (dungeonData === undefined) { + return { embed: null, err: `Invalid Dungeon!` }; + } + + // build npc embed + const embed = buildDungeonEmbed(option); + send.embeds.push(embed); + + // build difficulty row + const difficultySelectData = { + stateId, + }; + const difficultyButtonConfigs = Object.keys(dungeonData.difficulties).map( + (difficulty) => ({ + label: difficultyConfig[difficulty].name, + disabled: false, + data: { + ...difficultySelectData, + difficulty, + }, + }) + ); + const difficultyRow = buildButtonActionRow( + difficultyButtonConfigs, + eventNames.DUNGEON_ACCEPT + ); + send.components.push(difficultyRow); + + // build return button + const returnData = { + stateId, + }; + const returnButtonConfigs = [ + { + label: "Return", + disabled: false, + data: returnData, + }, + ]; + const returnRow = buildButtonActionRow( + returnButtonConfigs, + // im lazy and using the same event name + eventNames.DUNGEON_ACCEPT + ); + + state.dungeonId = option; + send.components.push(returnRow); + } else if (view === "battle") { + // validate npc id + const dungeonData = dungeonConfig[state.dungeonId]; + if (dungeonData === undefined) { + return { embed: null, err: `Invalid Dungeon!` }; + } + + // validate difficulty + const dungeonDifficultyData = dungeonData.difficulties[state.difficulty]; + if (dungeonDifficultyData === undefined) { + return { + embed: null, + err: `Difficulty doesn't exist for ${dungeonData.name}!`, + }; + } + + // get trainer + const trainerResult = await getTrainer(user); + if (trainerResult.err) { + return { embed: null, err: trainerResult.err }; + } + + // validate party + const validate = await validateParty(trainerResult.data); + if (validate.err) { + return { err: validate.err }; + } + + // add npc to battle + const npc = new DungeonNPC(dungeonData, state.difficulty); + npc.setPokemon(dungeonData, state.difficulty); + const rewardMultipliers = + dungeonDifficultyData.rewardMultipliers || + difficultyConfig[state.difficulty].rewardMultipliers; + const battle = new Battle({ + ...rewardMultipliers, + rewards: dungeonDifficultyData.rewards, + rewardString: dungeonDifficultyData.rewardString, + npcId: state.dungeonId, + difficulty: state.difficulty, + }); + battle.addTeam("Dungeon", true); + battle.addTrainer( + npc, + npc.party.pokemons, + "Dungeon", + npc.party.rows, + npc.party.cols + ); + battle.addTeam("Player", false); + battle.addTrainer(trainerResult.data, validate.data, "Player"); + + // start battle and add to state + battle.start(); + state.battle = battle; + + // add a replay button to state for later + // build difficulty row + const difficultySelectData = { + stateId, + difficulty: state.difficulty, + }; + const difficultyButtonConfigs = [ + { + label: "Replay", + disabled: false, + data: difficultySelectData, + }, + ]; + const difficultyRow = buildButtonActionRow( + difficultyButtonConfigs, + eventNames.DUNGEON_ACCEPT + ); + state.endBattleComponents = [difficultyRow]; + + return { + send: await getStartTurnSend(battle, stateId), + err: null, + }; + } + + return { send, err: null }; +}; + +const towerWinCallback = async (battle, trainer) => { + // validate tower stage + const towerStage = trainer.lastTowerStage + 1; + if (getIdFromTowerStage(towerStage) !== battle.npcId) { + battle.rewards = null; + return; + } + + // make sure that stage exists + const battleTowerData = battleTowerConfig[towerStage]; + if (!battleTowerData) { + battle.rewards = null; + return; + } + + // update trainer + trainer.lastTowerStage = towerStage; + const updateRes = await updateTrainer(trainer); + if (updateRes.err) { + battle.rewards = null; + return; + } + + // add rewards to battle + battle.rewards = { + ...battleTowerData.rewards, + }; +}; + +const onBattleTowerAccept = async ({ stateId = null, user = null } = {}) => { + // get state + const state = getState(stateId); + + // get battle tower stage data + const towerStage = state.towerStage || 1; + const battleTowerData = battleTowerConfig[towerStage]; + if (!battleTowerData) { + return { send: null, err: `Invalid Battle Tower stage!` }; + } + // validate npc id + const npcData = npcConfig[battleTowerData.npcId]; + if (npcData === undefined) { + return { embed: null, err: `Invalid NPC!` }; + } + // validate difficulty + const npcDifficultyData = npcData.difficulties[battleTowerData.difficulty]; + if (npcDifficultyData === undefined) { + return { + embed: null, + err: `Difficulty doesn't exist for ${npcData.name}!`, + }; + } + + // get trainer + const trainer = await getTrainer(user); + if (trainer.err) { + return { embed: null, err: trainer.err }; + } + // validate that last stage is correct + if (trainer.data.lastTowerStage !== towerStage - 1) { + return { embed: null, err: `You must complete the previous stage first!` }; + } + // validate party + const validate = await validateParty(trainer.data); + if (validate.err) { + return { err: validate.err }; + } + + // add npc to battle + const npc = new TowerNPC(battleTowerData, battleTowerData.difficulty); + npc.setPokemon(battleTowerData, battleTowerData.difficulty); + const rewardMultipliers = + npcDifficultyData.rewardMultipliers || + difficultyConfig[battleTowerData.difficulty].rewardMultipliers; + const battle = new Battle({ + ...rewardMultipliers, + npcId: getIdFromTowerStage(towerStage), + difficulty: battleTowerData.difficulty, + winCallback: towerWinCallback, + }); + battle.addTeam("Battle Tower", true); + battle.addTrainer( + npc, + npc.party.pokemons, + "Battle Tower", + npc.party.rows, + npc.party.cols + ); + battle.addTeam("Player", false); + battle.addTrainer(trainer.data, validate.data, "Player"); + + // start battle and add to state + battle.start(); + state.battle = battle; + + // build return button + const returnData = { + stateId, + page: towerStage, + }; + const returnButtonConfigs = [ + { + label: "Return", + disabled: false, + data: returnData, + }, + ]; + const returnRow = buildButtonActionRow( + returnButtonConfigs, + // im lazy and using the same event name + eventNames.TOWER_SCROLL + ); + state.endBattleComponents = [returnRow]; + + return { err: null }; +}; + +/** + * + * @param {object} param0 + * @param {string?=} param0.stateId + * @param {User?=} param0.user + * @returns + */ +const buildBattleTowerSend = async ({ stateId = null, user = null } = {}) => { + // get state + const state = getState(stateId); + + // get battle tower stage data + const towerStage = state.towerStage || 1; + const battleTowerData = battleTowerConfig[towerStage]; + if (!battleTowerData) { + return { send: null, err: `Invalid Battle Tower stage!` }; + } + const maxPages = Object.keys(battleTowerConfig).length; + + // get trainer + let trainer = await getTrainer(user); + if (trainer.err) { + return { send: null, err: trainer.err }; + } + trainer = trainer.data; + + const send = { + content: "", + embeds: [], + components: [], + }; + + const embed = buildBattleTowerEmbed(towerStage); + send.embeds.push(embed); + + // build scroll buttons + const scrollData = { + stateId, + }; + const scrollRow = buildScrollActionRow( + towerStage, + towerStage === maxPages, + scrollData, + eventNames.TOWER_SCROLL + ); + send.components.push(scrollRow); + + // build battle button + const battleData = { + stateId, + }; + const battleButtonConfigs = [ + { + label: "Battle", + disabled: towerStage !== trainer.lastTowerStage + 1, + data: battleData, + }, + ]; + const battleRow = buildButtonActionRow( + battleButtonConfigs, + eventNames.TOWER_ACCEPT + ); + send.components.push(battleRow); + + return { send, err: null }; +}; + +module.exports = { + Battle, + // BattleEventHandler, + Pokemon, + getStartTurnSend, + buildPveSend, + buildDungeonSend, + onBattleTowerAccept, + buildBattleTowerSend, + RaidNPC, +}; diff --git a/src/battle/battleConfig.js b/src/config/battleConfig.js similarity index 99% rename from src/battle/battleConfig.js rename to src/config/battleConfig.js index dc42adc6..26d36978 100644 --- a/src/battle/battleConfig.js +++ b/src/config/battleConfig.js @@ -3,10 +3,14 @@ /* eslint-disable no-shadow */ /* eslint-disable no-use-before-define */ /* eslint-disable no-param-reassign */ -const { types: pokemonTypes } = require("../config/pokemonConfig"); +const { types: pokemonTypes } = require("./pokemonConfig"); const types = require("../../types"); -const { getMove, getMoveIds, executeMove } = require("./data/moveService"); -const { getEffect } = require("./data/effectRegistry"); +const { + getMove, + getMoveIds, + executeMove, +} = require("../battle/data/moveService"); +const { getEffect } = require("../battle/data/effectRegistry"); /** @typedef {types.Enum} BattleEventEnum */ const battleEventNames = Object.freeze({ diff --git a/src/embeds/battleEmbeds.js b/src/embeds/battleEmbeds.js index 4d43b9d2..2e384395 100644 --- a/src/embeds/battleEmbeds.js +++ b/src/embeds/battleEmbeds.js @@ -7,7 +7,7 @@ * battleEmbeds.js Handles all embedded instructions for battles. */ const { EmbedBuilder } = require("discord.js"); -const { weatherConditions } = require("../battle/battleConfig"); +const { weatherConditions } = require("../config/battleConfig"); const { getMove } = require("../battle/data/moveService"); const { buildPartyString, diff --git a/src/embeds/pokemonEmbeds.js b/src/embeds/pokemonEmbeds.js index 048678a0..d8258a1f 100644 --- a/src/embeds/pokemonEmbeds.js +++ b/src/embeds/pokemonEmbeds.js @@ -15,7 +15,7 @@ const { typeConfig, growthRateConfig, } = require("../config/pokemonConfig"); -const { abilityConfig } = require("../battle/battleConfig"); +const { abilityConfig } = require("../config/battleConfig"); const { getMove } = require("../battle/data/moveService"); const { getWhitespace, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index dc5eb9e6..a826602c 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -1,7 +1,7 @@ const types = require("../../types"); /** - * @typedef {import("../battle/battleConfig").LegacyEffectIdEnum} LegacyEffectIdEnum + * @typedef {import("../config/battleConfig").LegacyEffectIdEnum} LegacyEffectIdEnum * @typedef {types.Enum} NewEffectIdEnum * @typedef {LegacyEffectIdEnum | NewEffectIdEnum} EffectIdEnum */ @@ -13,7 +13,7 @@ const effectIdEnum = Object.freeze({ }); /** - * @typedef {import("../battle/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum + * @typedef {import("../config/battleConfig").LegacyMoveIdEnum} LegacyMoveIdEnum * @typedef {types.Enum} NewMoveIdEnum * @typedef {LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum */ diff --git a/src/services/battle.js b/src/services/battle.js index b492a5c2..e6f8d767 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -25,7 +25,7 @@ const { abilityConfig, typeAdvantages, weatherConditions, -} = require("../battle/battleConfig"); +} = require("../config/battleConfig"); const { buildBattleEmbed, buildPveListEmbed, diff --git a/src/services/spawn.js b/src/services/spawn.js index da7a2a76..f69a31de 100644 --- a/src/services/spawn.js +++ b/src/services/spawn.js @@ -3,7 +3,7 @@ const { ButtonStyle } = require("discord.js"); const { buildButtonActionRow } = require("../components/buttonActionRow"); const { backpackItems } = require("../config/backpackConfig"); -const { typeAdvantages } = require("../battle/battleConfig"); +const { typeAdvantages } = require("../config/battleConfig"); const { eventNames } = require("../config/eventConfig"); const { pokeballConfig } = require("../config/gachaConfig"); const { diff --git a/src/utils/battleUtils.js b/src/utils/battleUtils.js index 973c2cc6..2f2d3f5b 100644 --- a/src/utils/battleUtils.js +++ b/src/utils/battleUtils.js @@ -6,7 +6,7 @@ * * battleUtils.js the lowest level of code for battles used by the battle.js */ -const { statusConditions } = require("../battle/battleConfig"); +const { statusConditions } = require("../config/battleConfig"); const { difficultyConfig } = require("../config/npcConfig"); const { pokemonConfig, typeConfig } = require("../config/pokemonConfig"); const { getRewardsString, flattenRewards } = require("./trainerUtils"); diff --git a/src/utils/pokemonUtils.js b/src/utils/pokemonUtils.js index 1c3ffc60..067b71eb 100644 --- a/src/utils/pokemonUtils.js +++ b/src/utils/pokemonUtils.js @@ -12,7 +12,7 @@ const { growthRateConfig, } = require("../config/pokemonConfig"); const { getPBar, getWhitespace } = require("./utils"); -const { abilityConfig } = require("../battle/battleConfig"); +const { abilityConfig } = require("../config/battleConfig"); const { equipmentConfig, modifierSlotConfig, From 67984bd89cb05b7e51fe3617dacdfd3a907500f5 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 18:04:07 -0700 Subject: [PATCH 31/38] some more changes --- eslint.config.mjs | 2 +- src/battle/data/effectRegistry.js | 5 +- src/battle/data/moveService.js | 1 + src/battle/engine/battle.js | 3584 ----------------------- src/battle/engine/events.js | 59 + src/battle/{battleTypes.js => types.js} | 0 src/battle/utils.js | 0 src/services/battle.js | 62 +- 8 files changed, 66 insertions(+), 3647 deletions(-) delete mode 100644 src/battle/engine/battle.js create mode 100644 src/battle/engine/events.js rename src/battle/{battleTypes.js => types.js} (100%) delete mode 100644 src/battle/utils.js diff --git a/eslint.config.mjs b/eslint.config.mjs index a862fb8f..ee5f0326 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -34,7 +34,7 @@ export default [ "guard-for-in": "off", // TODO: enable but make better lol "no-continue": "off", // doesn't seem to work properly "no-await-in-loop": "off", // TODO: use promise.all - "jsdoc/no-undefined-types": "silent", // doesn't seem to work with TS types + "jsdoc/no-undefined-types": "off", // doesn't seem to work with TS types "jsdoc/require-param-description": "off", // TODO: maybe add descriptions "jsdoc/require-returns-description": "off", // TODO: maybe add descriptions "jsdoc/valid-types": "off", // doesn't seem to work with TS types diff --git a/src/battle/data/effectRegistry.js b/src/battle/data/effectRegistry.js index ba36ce52..935b35f2 100644 --- a/src/battle/data/effectRegistry.js +++ b/src/battle/data/effectRegistry.js @@ -1,6 +1,7 @@ // eslint-disable-next-line no-unused-vars const { effectIdEnum } = require("../../enums/battleEnums"); // TODO: remove after testing const { logger } = require("../../log"); +// eslint-disable-next-line no-unused-vars const types = require("../../../types"); const allEffects = {}; @@ -48,7 +49,7 @@ const getEffect = (effectId) => allEffects[effectId]; /** - * @param {Object} param0 + * @param {object} param0 * @param {Record=} param0.fieldFilter * @param {Function=} param0.customFilter * @returns {types.PartialRecord>} @@ -80,7 +81,7 @@ const getEffects = ({ fieldFilter, customFilter }) => { }; }; /** - * @param {Object} param0 + * @param {object} param0 * @param {Record=} param0.fieldFilter * @param {Function=} param0.customFilter * @returns {EffectIdEnum[]} diff --git a/src/battle/data/moveService.js b/src/battle/data/moveService.js index 92236ca7..b02c5a3c 100644 --- a/src/battle/data/moveService.js +++ b/src/battle/data/moveService.js @@ -1,4 +1,5 @@ const { logger } = require("../../log"); +// eslint-disable-next-line no-unused-vars const types = require("../../../types"); const allMoves = {}; diff --git a/src/battle/engine/battle.js b/src/battle/engine/battle.js deleted file mode 100644 index 185eb238..00000000 --- a/src/battle/engine/battle.js +++ /dev/null @@ -1,3584 +0,0 @@ -/* eslint-disable no-case-declarations */ -// TODO: probably fix both -/* eslint-disable no-param-reassign */ -/* eslint-disable max-classes-per-file */ - -/** - * @file - * @author Elvis Wei - * @date 2023 - * @section Description - * - * battle.js Handles all battle interactions from the user at a base level down to creating the teams. - */ -const { v4: uuidv4 } = require("uuid"); -const { getOrSetDefault, formatMoney } = require("../../utils/utils"); -const { pokemonConfig, types } = require("../../config/pokemonConfig"); -const { - battleEventNames, - targetTypes, - targetPatterns, - targetPositions, - statusConditions, - moveTiers, - calculateDamage, - abilityConfig, - typeAdvantages, - weatherConditions, -} = require("../../config/battleConfig"); -const { - buildBattleEmbed, - buildPveListEmbed, - buildPveNpcEmbed, - buildDungeonListEmbed, - buildDungeonEmbed, - buildBattleTowerEmbed, -} = require("../../embeds/battleEmbeds"); -const { - buildSelectBattleMoveRow, -} = require("../../components/selectBattleMoveRow"); -const { buildButtonActionRow } = require("../../components/buttonActionRow"); -const { - buildBattleInfoActionRow, -} = require("../../components/battleInfoActionRow"); -const { - getTrainer, - addExpAndMoney, - updateTrainer, -} = require("../../services/trainer"); -const { - addPokemonExpAndEVs, - getPokemon, - calculatePokemonStats, -} = require("../../services/pokemon"); -const { logger } = require("../../log"); -const { - buildNextTurnActionRow, -} = require("../../components/battleNextTurnRow"); -const { deleteState } = require("../../services/state"); -const { - calculateEffectiveSpeed, - calculateEffectiveAccuracy, - calculateEffectiveEvasion, - getMoveIds, -} = require("../../utils/pokemonUtils"); -const { - npcConfig, - difficultyConfig, - dungeons, - dungeonConfig, - battleTowerConfig, -} = require("../../config/npcConfig"); -const { buildScrollActionRow } = require("../../components/scrollActionRow"); -const { getState } = require("../../services/state"); -const { eventNames } = require("../../config/eventConfig"); -const { - buildIdConfigSelectRow, -} = require("../../components/idConfigSelectRow"); -const { - drawIterable, - drawUniform, - drawDiscrete, -} = require("../../utils/gachaUtils"); -const { generateRandomPokemon } = require("../../services/gacha"); -const { validateParty } = require("../../services/party"); -const { addRewards, getRewardsString } = require("../../utils/trainerUtils"); -const { getIdFromTowerStage } = require("../../utils/battleUtils"); -const { getMove, executeMove } = require("../data/moveService"); -const { getEffect } = require("../data/effectRegistry"); - -class NPC { - constructor( - npcData, - difficulty, - // eslint-disable-next-line no-unused-vars - { userId = null, user = null, party = null } = {} - ) { - this.userId = userId || uuidv4(); - this.user = { - username: npcData.name, - discriminator: "0", - npc: this, - id: this.userId, - // data: npcData, - // difficulty: difficulty, - }; - - // this.setPokemon(npcData, difficulty); - } - - setPokemon(npcData, difficulty) { - const npcDifficultyData = npcData.difficulties[difficulty]; - this.party = { - rows: 3, - cols: 4, - pokemons: [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - ], - }; - - // generate party - // generate numPokemon - 1 pokemon, then 1 for the ace - const { numPokemon } = npcDifficultyData; - const pokemons = []; - const pokemonIds = drawIterable( - npcDifficultyData.pokemonIds, - numPokemon - 1 - ); - for (const pokemonId of pokemonIds) { - const pokemon = generateRandomPokemon( - this.userId, - pokemonId, - drawUniform( - npcDifficultyData.minLevel, - npcDifficultyData.maxLevel, - 1 - )[0] - ); - // give random id - pokemon._id = uuidv4(); - pokemons.push(pokemon); - } - // push ace - const acePokemon = generateRandomPokemon( - this.userId, - npcDifficultyData.aceId, - npcDifficultyData.maxLevel + 1 - ); - acePokemon._id = uuidv4(); - pokemons.push(acePokemon); - - // put parties in random indices with no overlap - let i = 0; - while (i < numPokemon) { - const index = drawUniform(0, this.party.rows * this.party.cols - 1, 1)[0]; - if (this.party.pokemons[index] === null) { - this.party.pokemons[index] = pokemons[i]; - i += 1; - } - } - } - - action(battle) { - const { activePokemon } = battle; - if (activePokemon.userId !== this.userId) { - return; - } - - /* steps: - if cant move, skip turn - get all moves filtered by those with eligible targets and usable - for all considered moves, get the best move - best move/target: for all targets: - get how many targets would be hit by AoE - calculate heuristic - if move does damage, calculate damage that would be dealt - else, calculate heuristic = numTargets * source level * 1.5 - normalize heuristic by move accuracy, or *1.2 if move has no accuracy - normalize heuristic by move tier - choose best move & target based off heuristic - use move */ - - // if cant move, skip turn - if (!activePokemon.canMove()) { - activePokemon.skipTurn(); - return; - } - - // get all moves filtered by those with eligible targets and usable - const { moveIds } = activePokemon; - const validMoveIdsToTargets = {}; - Object.entries(moveIds).forEach(([moveId, move]) => { - if (move.disabled || move.cooldown > 0) { - return; - } - - const eligibleTargets = battle.getEligibleTargets(activePokemon, moveId); - if (eligibleTargets.length > 0) { - validMoveIdsToTargets[moveId] = eligibleTargets; - } - }); - - // if for some reason no moves exist, skip turn - if (Object.keys(validMoveIdsToTargets).length === 0) { - activePokemon.skipTurn(); - return; - } - - // for all considered moves, get the best move - let bestMoveId = null; - let bestTarget = null; - let bestHeuristic = -1; - for (const moveId in validMoveIdsToTargets) { - for (const target of validMoveIdsToTargets[moveId]) { - const source = activePokemon; - const targetsHit = source.getTargets(moveId, target.id); - const heuristic = this.calculateHeuristic(moveId, source, targetsHit); - if (heuristic > bestHeuristic) { - bestMoveId = moveId; - bestTarget = target; - bestHeuristic = heuristic; - } - } - } - - // use move - activePokemon.useMove(bestMoveId, bestTarget.id); - } - - // eslint-disable-next-line class-methods-use-this - calculateHeuristic(moveId, source, targetsHit) { - const moveData = getMove(moveId); - - let heuristic = 0; - // special case: if asleep and sleep talk, use sleep talk - if ( - source.status.statusId === statusConditions.SLEEP && - moveId === "m214" - ) { - return 1000000; - } - // special case: if move is rocket thievery and enemy team has no fainted pokemon, return 0 - if (moveId === "m20003") { - const enemyParty = source.getEnemyParty(); - if ( - enemyParty && - enemyParty.pokemons && - enemyParty.pokemons.filter((p) => p && p.isFainted).length === 0 - ) { - return 0; - } - } - // special case: if move is gear fifth, use if under 25% hp - if (moveId === "m20010") { - if (source.hp / source.maxHp > 0.25) { - return 0; - } - return 1000000; - } - - if (moveData.power !== null) { - // if move does damage, calculate damage that would be dealt - for (const target of targetsHit) { - const damage = calculateDamage(moveData, source, target, false); - heuristic += damage; - } - } else { - // else, calculate heuristic = numTargets * source level * 1.5 - heuristic = targetsHit.length * source.level * 1.5; - } - // normalize heuristic by move accuracy, or *1.2 if move has no accuracy - const { accuracy } = moveData; - heuristic *= accuracy === null ? 1.2 : accuracy / 100; - - // multiply heuristic by move tier. basic = 0.7, power = 1, ultimate = 1.5 - const moveTier = moveData.tier; - let tierMultiplier; - if (moveTier === moveTiers.BASIC) { - tierMultiplier = 0.7; - } else if (moveTier === moveTiers.POWER) { - tierMultiplier = 1; - } else { - tierMultiplier = 1.5; - } - heuristic *= tierMultiplier; - - // calculate nonce for small random variation - const nonce = Math.random(); - return heuristic + nonce; - } -} - -class DungeonNPC extends NPC { - constructor(dungeonData, difficulty) { - super(dungeonData, difficulty); - this.phaseNumber = 0; - this.phases = dungeonData.difficulties[difficulty].phases; - this.dungeonData = dungeonData; - this.difficulty = difficulty; - // suuper hacky, probably a better way to do this - this.user.nextPhase = (battle) => this.nextPhase(battle); - } - - // eslint-disable-next-line no-unused-vars - setPokemon(dungeonData, difficulty) { - const phase = this.phases[this.phaseNumber]; - if (phase === undefined) { - return; - } - this.party = { - rows: phase.rows, - cols: phase.cols, - pokemons: Array(phase.rows * phase.cols).fill(null), - }; - - // generate party - for (const pokemonData of phase.pokemons) { - const pokemon = generateRandomPokemon( - this.userId, - pokemonData.speciesId, - pokemonData.level - ); - // give random id - pokemon._id = uuidv4(); - this.party.pokemons[pokemonData.position - 1] = pokemon; - } - } - - // eslint-disable-next-line no-unused-vars - nextPhase(battle) { - this.phaseNumber += 1; - if (this.phaseNumber >= this.phases.length) { - return false; - } - this.setPokemon(this.dungeonData, this.difficulty); - - return { - trainer: this, - ...this.party, - }; - } -} - -class TowerNPC extends NPC { - constructor(towerData, difficulty) { - const npcData = npcConfig[towerData.npcId]; - super(npcData, difficulty); - } - - setPokemon(towerData, difficulty) { - const npcData = npcConfig[towerData.npcId]; - const npcDifficultyData = npcData.difficulties[difficulty]; - this.party = { - rows: 3, - cols: 4, - pokemons: [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - ], - }; - - // generate party - // generate numPokemon - 1 pokemon, then 1 for the ace - const { numPokemon } = npcDifficultyData; - const pokemons = []; - const pokemonIds = drawIterable( - npcDifficultyData.pokemonIds, - numPokemon - 1 - ); - for (const pokemonId of pokemonIds) { - const pokemon = generateRandomPokemon( - this.userId, - pokemonId, - drawUniform(towerData.minLevel, towerData.maxLevel, 1)[0] - ); - // give random id - pokemon._id = uuidv4(); - pokemons.push(pokemon); - } - // push ace - const acePokemon = generateRandomPokemon( - this.userId, - npcDifficultyData.aceId, - towerData.maxLevel + 1 - ); - acePokemon._id = uuidv4(); - pokemons.push(acePokemon); - - // put parties in random indices with no overlap - let i = 0; - while (i < numPokemon) { - const index = drawUniform(0, this.party.rows * this.party.cols - 1, 1)[0]; - if (this.party.pokemons[index] === null) { - this.party.pokemons[index] = pokemons[i]; - i += 1; - } - } - } -} - -class RaidNPC extends NPC { - constructor(raidData, difficulty, raidUserId, boss) { - super(raidData, difficulty, { - userId: raidUserId, - }); - this.raidData = raidData; - this.difficulty = difficulty; - this.boss = boss; - } - - setPokemon(raidData, difficulty) { - const raidDifficultyData = raidData.difficulties[difficulty]; - this.party = { - rows: raidDifficultyData.rows, - cols: raidDifficultyData.cols, - pokemons: Array(raidDifficultyData.rows * raidDifficultyData.cols).fill( - null - ), - }; - - // generate party - for (const pokemonData of raidDifficultyData.pokemons) { - const pokemon = - pokemonData.speciesId === this.boss.speciesId - ? { ...this.boss } - : generateRandomPokemon( - this.userId, - pokemonData.speciesId, - pokemonData.level - ); - // give random id - pokemon._id = pokemon._id || uuidv4(); - this.party.pokemons[pokemonData.position - 1] = pokemon; - } - } -} - -class BattleEventHandler { - /* battle; - // event name => listenerIds - eventNames; - // listenerId => listener - eventListeners; */ - - constructor(battle) { - this.battle = battle; - this.eventNames = {}; - this.eventListeners = {}; - } - - registerListener(eventName, listener) { - // generate listener UUID - const listenerId = uuidv4(); - - getOrSetDefault(this.eventNames, eventName, []).push(listenerId); - this.eventListeners[listenerId] = listener; - // add listenerId and eventName to listener.initialargs - // eslint-disable-next-line no-param-reassign - listener.initialArgs = { - listenerId, - eventName, - ...listener.initialArgs, - }; - - return listenerId; - } - - unregisterListener(listenerId) { - const listener = this.eventListeners[listenerId]; - if (listener) { - const { eventName } = listener.initialArgs; - const listenerIds = this.eventNames[eventName]; - if (listenerIds) { - const index = listenerIds.indexOf(listenerId); - if (index > -1) { - listenerIds.splice(index, 1); - } - } - delete this.eventListeners[listenerId]; - } - } - - emit(eventName, args) { - const listenerIds = this.eventNames[eventName]; - if (listenerIds) { - for (const listenerId of listenerIds) { - const listener = this.eventListeners[listenerId]; - if (listener) { - listener.execute(listener.initialArgs, args); - } - } - } - } -} - -class Pokemon { - /* battle; - pokemonData; - speciesId; - speciesData; - id; - userId; - originalUserId; - teamName; - name; - level; - hp; - maxHp; - atk; - batk; - def; - bdef; - spa; - bspa; - spd; - bspd; - spe; - bspe; - acc; - eva; - type1; - type2; - // effectId => { duration, args } - effectIds; - // moveId => { cooldown, disabled } - moveIds; - // { statusId. tuns active } - status; - // { abilityId, args } - ability; - combatReadiness; - position; - isFainted; - targetable; - hittable; - incapacitated; - restricted; */ - - constructor(battle, trainer, pokemonData, teamName, position) { - this.battle = battle; - this.speciesId = pokemonData.speciesId; - this.speciesData = pokemonConfig[this.speciesId]; - // if battle has a set level, scale pokemon to that level - if (battle.level) { - battle.minLevel = Math.min(battle.minLevel, pokemonData.level); - pokemonData.level = battle.level; - } - // if battle has an equipment level, set all pokemons equipments to that level - if (battle.equipmentLevel) { - for (const equipment of Object.values(pokemonData.equipments)) { - equipment.level = battle.equipmentLevel; - } - } - pokemonData = calculatePokemonStats(pokemonData, this.speciesData); - this.pokemonData = pokemonData; - this.id = pokemonData._id.toString(); - this.userId = trainer.userId; - this.originalUserId = trainer.userId; - this.teamName = teamName; - this.name = pokemonData.name; - this.hp = pokemonData.remainingHp || pokemonData.stats[0]; - [this.maxHp = 0] = this.pokemonData.stats; - this.level = pokemonData.level; - [ - this.atk = 0, - this.batk = 0, - this.def, - this.bdef = 0, - this.spa, - this.bspa = 0, - this.spd, - this.bspd = 0, - this.spe, - this.bspe = 0, - ] = [ - pokemonData.stats[1], - pokemonData.stats[1], - pokemonData.stats[2], - pokemonData.stats[2], - pokemonData.stats[3], - pokemonData.stats[3], - pokemonData.stats[4], - pokemonData.stats[4], - pokemonData.stats[5], - pokemonData.stats[5], - ]; - this.acc = 100; - this.eva = 100; - [this.type1 = null, this.type2 = null] = this.speciesData.type; - // map effectId => effect data (duration, args) - this.effectIds = {}; - // map moveId => move data (cooldown, disabled) - this.addMoves(pokemonData); - this.setAbility(pokemonData); - this.status = { - statusId: null, - turns: 0, - }; - this.combatReadiness = 0; - this.position = position; - this.isFainted = false; - this.targetable = true; - this.hittable = true; - this.incapacitated = false; - this.restricted = false; - } - - addMoves(pokemonData) { - this.moveIds = getMoveIds(pokemonData).reduce((acc, moveId) => { - acc[moveId] = { - cooldown: 0, - disabled: false, - }; - return acc; - }, {}); - } - - setAbility(pokemonData) { - this.ability = { - abilityId: pokemonData.abilityId, - }; - } - - transformInto(speciesId, { abilityId = null, moveIds = [] } = {}) { - const oldName = this.name; - // remove ability - this.removeAbility(); - - // get old stat ratios (excpet hp) - const atkRatio = this.atk / this.batk; - const defRatio = this.def / this.bdef; - const spaRatio = this.spa / this.bspa; - const spdRatio = this.spd / this.bspd; - const speRatio = this.spe / this.bspe; - - // set new species values - this.speciesId = speciesId; - this.speciesData = pokemonConfig[this.speciesId]; - [this.type1, this.type2 = null] = this.speciesData.type; - this.type2 = this.speciesData.type[1] || null; - this.name = this.speciesData.name; - - // set pokemon data object - this.pokemonData.speciesId = this.speciesId; - this.pokemonData.name = this.speciesData.name; - this.pokemonData.moveIds = moveIds; - this.pokemonData.abilityId = - abilityId || drawDiscrete(this.speciesData.abilities, 1)[0]; - calculatePokemonStats(this.pokemonData, this.speciesData); - - // set stats (except hp) - [, this.atk, this.bdef, this.bspa, this.bspd, this.bspe] = - this.pokemonData.stats; - this.atk = Math.round(this.pokemonData.stats[1] * atkRatio); - this.def = Math.round(this.pokemonData.stats[2] * defRatio); - this.spa = Math.round(this.pokemonData.stats[3] * spaRatio); - this.spd = Math.round(this.pokemonData.stats[4] * spdRatio); - this.spe = Math.round(this.pokemonData.stats[5] * speRatio); - - // set moves and ability - this.addMoves(this.pokemonData); - this.setAbility(this.pokemonData); - this.applyAbility(); - - this.battle.addToLog(`${oldName} transformed into ${this.name}!`); - } - - useMove(moveId, targetPokemonId) { - // make sure pokemon can move - if (!this.canMove()) { - return; - } - // make sure move exists and is not on cooldown & disabled - const moveData = getMove(moveId); - if ( - !moveData || - this.moveIds[moveId].cooldown > 0 || - this.moveIds[moveId].disabled - ) { - return; - } - // check to see if target is valid - const primaryTarget = this.battle.allPokemon[targetPokemonId]; - if ( - !primaryTarget || - !this.battle.getEligibleTargets(this, moveId).includes(primaryTarget) - ) { - return; - } - - // clear battle log - this.battle.clearLog(); - - // reset combat readiness - this.combatReadiness = 0; - - let canUseMove = true; - - // check status conditions - if (this.status.statusId !== null) { - switch (this.status.statusId) { - case statusConditions.FREEZE: - // if move is flare blitz, thaw out - if (moveId === "m394" || moveId === "m394-1") { - this.removeStatus(); - break; - } - - // thaw chance (turns => chance): 0 => 0%, 1 => 40%, 2 => 80%, 3 => 100% - const thawChance = this.status.turns * 0.4; - const thawRoll = Math.random(); - if (thawRoll < thawChance) { - this.removeStatus(); - } else { - this.battle.addToLog(`${this.name} is frozen solid!`); - canUseMove = false; - } - break; - case statusConditions.PARALYSIS: - // 25% chance to be paralyzed - const paralysisRoll = Math.random(); - if (paralysisRoll < 0.25) { - this.battle.addToLog(`${this.name} is paralyzed and can't move!`); - canUseMove = false; - } - break; - case statusConditions.SLEEP: - // ignore if sleep talk is used - if (moveId === "m214") { - break; - } - // sleep wakeup chance: 0 turns: 0%, 1 turn: 66%, 2 turns: 100% - const wakeupChance = this.status.turns * 0.66; - const wakeupRoll = Math.random(); - if (wakeupRoll < wakeupChance) { - this.removeStatus(); - } else { - this.battle.addToLog(`${this.name} is fast asleep!`); - canUseMove = false; - } - break; - default: - break; - } - } - - if (canUseMove) { - const eventArgs = { - canUseMove, - source: this, - primaryTarget, - moveId, - }; - - // trigger before move events - this.battle.eventHandler.emit(battleEventNames.BEFORE_MOVE, eventArgs); - - canUseMove = eventArgs.canUseMove; - } - - // check if pokemon can use move make sure pokemon is alive - canUseMove = !!(canUseMove && !this.isFainted); - - // get move data and execute move - if (canUseMove) { - // see if move log should be silenced - const isSilenced = - moveData.silenceIf && moveData.silenceIf(this.battle, this); - // if pokemon alive, get all targets - const allTargets = this.getTargets(moveId, targetPokemonId); - if (!isSilenced) { - const targetString = - moveData.targetPattern === targetPatterns.ALL || - moveData.targetPattern === targetPatterns.ALL_EXCEPT_SELF || - moveData.targetPattern === targetPatterns.RANDOM || - moveData.targetPosition === targetPositions.SELF - ? "!" - : ` against ${primaryTarget.name}!`; - this.battle.addToLog( - `${this.name} used ${moveData.name}${targetString}` - ); - } - // calculate miss - const missedTargets = this.getMisses(moveId, allTargets); - // if misses, log miss - if (missedTargets.length > 0 && !isSilenced) { - this.battle.addToLog( - `Missed ${missedTargets.map((target) => target.name).join(", ")}!` - ); - } - - // set cooldown - this.moveIds[moveId].cooldown = moveData.cooldown; - - // trigger before execute move events - const executeEventArgs = { - source: this, - primaryTarget, - targets: allTargets, - missedTargets, - moveId, - }; - this.battle.eventHandler.emit( - battleEventNames.BEFORE_MOVE_EXECUTE, - executeEventArgs - ); - - // execute move - executeMove({ - moveId, - battle: this.battle, - source: this, - primaryTarget, - allTargets, - missedTargets, - }); - - // after move event - const eventArgs = { - source: this, - primaryTarget, - targets: allTargets, - missedTargets, - moveId, - }; - this.battle.eventHandler.emit(battleEventNames.AFTER_MOVE, eventArgs); - } - - // end turn - this.battle.nextTurn(); - } - - /** - * Called if the Pokemon is the active Pokemon but can't move. - * @returns null - */ - skipTurn() { - // make sure its this Pokemon's turn - if (this.battle.activePokemon !== this) { - return; - } - // make sure Pokemon can't move - if (this.canMove()) { - return; - } - - // clear battle log - this.battle.clearLog(); - - // reset combat readiness - this.combatReadiness = 0; - - // check pre-move status conditions that tick regardless of move - if (this.status.statusId !== null) { - switch (this.status.statusId) { - case statusConditions.FREEZE: - // thaw chance (turns => chance): 0 => 0%, 1 => 40%, 2 => 80%, 3 => 100% - const thawChance = this.status.turns * 0.4; - const thawRoll = Math.random(); - if (thawRoll < thawChance) { - this.removeStatus(); - } - break; - case statusConditions.SLEEP: - // sleep wakeup chance: 0 turns: 0%, 1 turn: 66%, 2 turns: 100% - const wakeupChance = this.status.turns * 0.66; - const wakeupRoll = Math.random(); - if (wakeupRoll < wakeupChance) { - this.removeStatus(); - } - break; - default: - break; - } - } - - // end turn - this.battle.nextTurn(); - } - - /** - * Check that the Pokemon may take a valid action. - * @returns True or False - */ - canMove() { - // make sure its this pokemon's turn - if (this.battle.activePokemon !== this) { - return false; - } - - // if fainted, return - if (this.isFainted) { - return false; - } - - // if incapacitated, return - if (this.incapacitated) { - return false; - } - - // for all non-disabled moves not on cooldown, check to see if valid targets exist - for (const moveId in this.moveIds) { - if (this.moveIds[moveId].disabled || this.moveIds[moveId].cooldown > 0) { - continue; - } - const eligibleTargets = this.battle.getEligibleTargets(this, moveId); - if (eligibleTargets.length > 0) { - return true; - } - } - - // if no valid targets exist, return false - return false; - } - - getTypeDamageMultiplier(moveType, targetPokemon) { - let mult = 1; - if (typeAdvantages[moveType]) { - let adv = typeAdvantages[moveType][targetPokemon.type1]; - if (adv !== undefined) { - mult *= adv; - } - - adv = typeAdvantages[moveType][targetPokemon.type2]; - if (adv !== undefined) { - mult *= adv; - } - } - - const eventArgs = { - source: this, - target: targetPokemon, - moveType, - multiplier: mult, - }; - this.battle.eventHandler.emit( - battleEventNames.CALCULATE_TYPE_MULTIPLIER, - eventArgs - ); - - return eventArgs.multiplier; - } - - getPatternTargets(targetParty, targetPattern, targetPosition, moveId = null) { - const targetRow = Math.floor((targetPosition - 1) / targetParty.cols); - const targetCol = (targetPosition - 1) % targetParty.cols; - const targets = []; - - switch (targetPattern) { - case targetPatterns.ALL: - // return all pokemon in party - for (const pokemon of targetParty.pokemons) { - if (this.battle.isPokemonHittable(pokemon, moveId)) { - targets.push(pokemon); - } - } - break; - case targetPatterns.ALL_EXCEPT_SELF: - // return all pokemon in party except self - for (const pokemon of targetParty.pokemons) { - if ( - this.battle.isPokemonHittable(pokemon, moveId) && - pokemon !== this - ) { - targets.push(pokemon); - } - } - break; - case targetPatterns.COLUMN: - // return all pokemon in column - for (const pokemon of targetParty.pokemons) { - if ( - this.battle.isPokemonHittable(pokemon, moveId) && - (pokemon.position - 1) % targetParty.cols === targetCol - ) { - targets.push(pokemon); - } - } - break; - case targetPatterns.ROW: - // return all pokemon in row - for (const pokemon of targetParty.pokemons) { - if ( - this.battle.isPokemonHittable(pokemon, moveId) && - Math.floor((pokemon.position - 1) / targetParty.cols) === targetRow - ) { - targets.push(pokemon); - } - } - break; - case targetPatterns.RANDOM: - // return random pokemon in party - const validPokemons = []; - for (const pokemon of targetParty.pokemons) { - if (this.battle.isPokemonHittable(pokemon, moveId)) { - validPokemons.push(pokemon); - } - } - targets.push( - validPokemons[Math.floor(Math.random() * validPokemons.length)] - ); - break; - case targetPatterns.SQUARE: - // if row index or column index within 1 of target, add to targets - for (const pokemon of targetParty.pokemons) { - if (this.battle.isPokemonHittable(pokemon, moveId)) { - const pokemonRow = Math.floor( - (pokemon.position - 1) / targetParty.cols - ); - const pokemonCol = (pokemon.position - 1) % targetParty.cols; - if ( - Math.abs(targetRow - pokemonRow) <= 1 && - Math.abs(targetCol - pokemonCol) <= 1 - ) { - targets.push(pokemon); - } - } - } - break; - case targetPatterns.CROSS: - // target manhattan distance <= 1, add to targets - for (const pokemon of targetParty.pokemons) { - if (this.battle.isPokemonHittable(pokemon, moveId)) { - const pokemonRow = Math.floor( - (pokemon.position - 1) / targetParty.cols - ); - const pokemonCol = (pokemon.position - 1) % targetParty.cols; - if ( - Math.abs(targetRow - pokemonRow) + - Math.abs(targetCol - pokemonCol) <= - 1 - ) { - targets.push(pokemon); - } - } - } - break; - default: - // default is single - - // get target pokemon - const targetPokemon = - targetParty.pokemons[targetRow * targetParty.cols + targetCol]; - if (this.battle.isPokemonHittable(targetPokemon, moveId)) { - targets.push(targetPokemon); - } - - break; - } - - return targets; - } - - getTargets(moveId, targetPokemonId) { - const moveData = getMove(moveId); - // make sure target exists and is alive - const target = this.battle.allPokemon[targetPokemonId]; - if (!target) { - return []; - } - - // get party of target - const targetParty = this.battle.parties[target.teamName]; - - const allTargets = []; - return [ - ...allTargets, - ...this.getPatternTargets( - targetParty, - moveData.targetPattern, - target.position, - moveId - ), - ]; - } - - getMisses(moveId, targetPokemons) { - const moveData = getMove(moveId); - const misses = []; - if (!moveData.accuracy) { - return misses; - } - for (const target of targetPokemons) { - let hitChance = - (moveData.accuracy * calculateEffectiveAccuracy(this.acc)) / - calculateEffectiveEvasion(target.eva); - const damageMult = this.getTypeDamageMultiplier(moveData.type, target); - if (damageMult >= 4) { - hitChance *= 1.4; - } else if (damageMult >= 2) { - hitChance *= 1.15; - } else if (damageMult === 0) { - hitChance = 0; - } else if (damageMult <= 0.25) { - hitChance *= 0.6; - } else if (damageMult <= 0.5) { - hitChance *= 0.8; - } - - // weather check - if (!this.battle.isWeatherNegated()) { - if (this.battle.weather.weatherId === weatherConditions.SUN) { - if ( - moveId === "m87" || - moveId === "m87-1" || - moveId === "m542" || - moveId === "m542-1" - ) { - hitChance *= 0.75; - } - } else if (this.battle.weather.weatherId === weatherConditions.RAIN) { - if ( - moveId === "m87" || - moveId === "m87-1" || - moveId === "m542" || - moveId === "m542-1" - ) { - hitChance = 150; - } - } else if (this.battle.weather.weatherId === weatherConditions.HAIL) { - if (moveId === "m59") { - hitChance = 150; - } - } - } - - const calculateMissArgs = { - target, - hitChance, - source: this, - }; - this.battle.eventHandler.emit( - battleEventNames.CALCULATE_MISS, - calculateMissArgs - ); - - if (Math.random() > calculateMissArgs.hitChance / 100) { - misses.push(target); - } - } - return misses; - } - - switchUsers(userId) { - if (this.userId === userId) { - return false; - } - // get user info from battle - const user = this.battle.users[userId]; - if (!user) { - return false; - } - const { teamName } = user; - if (!teamName) { - return false; - } - - // set teamName and userId - this.teamName = teamName; - this.userId = userId; - - this.battle.addToLog(`${this.name} switched to ${user.username}'s team!`); - - return true; - } - - // eslint-disable-next-line no-unused-vars - switchPositions(userId, newPosition, source) { - const currentIndex = this.position - 1; - const newIndex = newPosition - 1; - - // get user info from battle - const user = this.battle.users[userId]; - if (!user) { - return false; - } - const newTeamName = user.teamName; - if (!newTeamName) { - return false; - } - - const oldParty = this.battle.parties[this.teamName]; - const newParty = this.battle.parties[newTeamName]; - if (!newParty) { - return false; - } - - // check if position is valid - if (newPosition < 1 || newPosition > newParty.pokemons.length) { - return false; - } - // if pokemon exists in new position, return false - if (newParty.pokemons[newIndex]) { - return false; - } - - // if new team, attempt to switch teams - if (this.teamName !== newTeamName) { - if (!this.switchUsers(userId)) { - return false; - } - } - - // remove from old position - oldParty.pokemons[currentIndex] = null; - - // add to new position - newParty.pokemons[newIndex] = this; - this.position = newPosition; - - this.battle.addToLog( - `${this.name} switched to position to ${newPosition}!` - ); - - return true; - } - - dealDamage(damage, target, damageInfo) { - if (damage <= 0) { - return 0; - } - - // if pvp, deal 15% less damage - if (this.battle.isPvp) { - damage = Math.max(1, Math.floor(damage * 0.85)); - } - - const eventArgs = { - target, - damage, - source: this, - damageInfo, - }; - - this.battle.eventHandler.emit( - battleEventNames.BEFORE_DAMAGE_DEALT, - eventArgs - ); - damage = eventArgs.damage; - - const damageDealt = target.takeDamage(damage, this, damageInfo); - - if (damageDealt > 0) { - const afterDamageArgs = { - target, - damage: damageDealt, - source: this, - damageInfo, - }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_DAMAGE_DEALT, - afterDamageArgs - ); - } - - return damageDealt; - } - - takeDamage(damage, source, damageInfo) { - if (this.isFainted) { - return 0; - } - - // if frozen and fire type or scald, thaw and deal 1.5x damage - const freezeCheck = - this.status.statusId === statusConditions.FREEZE && - damageInfo.type === "move" && - getMove(damageInfo.moveId) !== undefined && - (getMove(damageInfo.moveId).type === types.FIRE || - damageInfo.moveId === "m503"); - if (freezeCheck) { - if (this.removeStatus()) { - damage = Math.floor(damage * 1.5); - } - } - - // if shield, take shield damage and return 0 - const shieldData = this.effectIds.shield; - if (shieldData && shieldData.args && shieldData.args.shield) { - const shieldDamage = Math.min(damage, shieldData.args.shield); - shieldData.args.shield -= shieldDamage; - - this.battle.addToLog( - `${this.name} took ${shieldDamage} shield damage! (${shieldData.args.shield} left)` - ); - if (shieldData.args.shield <= 0) { - this.removeEffect("shield"); - } - return 0; - } - - const eventArgs = { - target: this, - damage, - source, - damageInfo, - maxDamage: Number.MAX_SAFE_INTEGER, - }; - - this.battle.eventHandler.emit( - battleEventNames.BEFORE_DAMAGE_TAKEN, - eventArgs - ); - damage = Math.min(eventArgs.damage, eventArgs.maxDamage); - - const oldHp = this.hp; - if (oldHp <= 0 || this.isFainted) { - return 0; - } - this.hp = Math.max(0, this.hp - damage); - const damageTaken = oldHp - this.hp; - this.battle.addToLog(`${this.name} took ${damageTaken} damage!`); - if (this.hp <= 0) { - this.hp = 0; - this.faint(source); - } - - if (damageTaken > 0) { - const afterDamageArgs = { - target: this, - damage: damageTaken, - source, - damageInfo, - }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_DAMAGE_TAKEN, - afterDamageArgs - ); - } - - return damageTaken; - } - - takeFaint(source) { - if (this.isFainted) { - return; - } - // trigger before cause faint effects - const beforeCauseFaintArgs = { - target: this, - source, - canFaint: true, - }; - this.battle.eventHandler.emit( - battleEventNames.BEFORE_CAUSE_FAINT, - beforeCauseFaintArgs - ); - if (!beforeCauseFaintArgs.canFaint) { - return; - } - - this.faint(source); - } - - faint(source) { - // TODO: trigger before faint effects - - this.hp = 0; - this.isFainted = true; - this.removeAbility(); - this.battle.addToLog(`${this.name} fainted!`); - - // trigger after faint effects - const afterFaintArgs = { - target: this, - source, - }; - this.battle.eventHandler.emit(battleEventNames.AFTER_FAINT, afterFaintArgs); - } - - // eslint-disable-next-line no-unused-vars - beRevived(reviveHp, source) { - if (!this.isFainted) { - return; - } - if (reviveHp <= 0) { - return; - } - - this.hp = Math.min(this.maxHp, reviveHp); - this.isFainted = false; - this.battle.addToLog(`${this.name} was revived!`); - - // re-add ability - this.applyAbility(); - } - - applyAbility() { - const { abilityId } = this.ability; - const abilityData = abilityConfig[abilityId]; - if (!abilityData || !abilityData.abilityAdd) { - return; - } - - this.ability.data = abilityData.abilityAdd(this.battle, this, this); - this.ability.applied = true; - } - - removeAbility() { - // remove ability effects - const { abilityId } = this.ability; - const abilityData = abilityConfig[abilityId]; - if (abilityData && abilityData.abilityRemove) { - abilityData.abilityRemove(this.battle, this, this); - } - } - - giveHeal(heal, target, healInfo) { - const healGiven = target.takeHeal(heal, this, healInfo); - return healGiven; - } - - // eslint-disable-next-line no-unused-vars - takeHeal(heal, source, healInfo) { - const oldHp = this.hp; - if (oldHp <= 0 || this.isFainted) { - return 0; - } - this.hp = Math.min(this.maxHp, this.hp + heal); - const healTaken = this.hp - oldHp; - if (healTaken > 0) { - this.battle.addToLog(`${this.name} healed ${healTaken} HP!`); - } - return healTaken; - } - - boostCombatReadiness(source, amount, triggerEvents = true) { - // if faint, do nothing - if (this.isFainted) { - return 0; - } - - if (this.restricted) { - this.battle.addToLog( - `${this.name} is restricted and cannot gain combat readiness!` - ); - return 0; - } - - const beforeBoostArgs = { - target: this, - source, - amount, - }; - if (triggerEvents) { - this.battle.eventHandler.emit( - battleEventNames.BEFORE_CR_GAINED, - beforeBoostArgs - ); - } - - const oldCombatReadiness = this.combatReadiness; - this.combatReadiness = Math.min( - 100, - this.combatReadiness + beforeBoostArgs.amount - ); - const combatReadinessGained = this.combatReadiness - oldCombatReadiness; - this.battle.addToLog( - `${this.name} gained ${Math.round( - combatReadinessGained - )} combat readiness!` - ); - - if (combatReadinessGained > 0 && triggerEvents) { - const eventArgs = { - target: this, - source, - combatReadinessGained: amount, - }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_CR_GAINED, - eventArgs - ); - } - - return combatReadinessGained; - } - - reduceCombatReadiness(source, amount) { - // if faint, do nothing - if (this.isFainted) { - return 0; - } - - const oldCombatReadiness = this.combatReadiness; - this.combatReadiness = Math.max(0, this.combatReadiness - amount); - const combatReadinessLost = oldCombatReadiness - this.combatReadiness; - this.battle.addToLog( - `${this.name} lost ${Math.round(combatReadinessLost)} combat readiness!` - ); - return combatReadinessLost; - } - - /** - * @template {EffectIdEnum} K - * @param {K} effectId - * @param {number} duration - * @param {BattlePokemon} source - * @param {EffectInitialArgsTypeFromId} initialArgs - */ - applyEffect(effectId, duration, source, initialArgs) { - // if faint, do nothing - if (this.isFainted) { - return; - } - const effect = getEffect(effectId); - if (!effect) { - logger.error(`Effect ${effectId} does not exist.`); - return; - } - - duration = this.battle.activePokemon === this ? duration + 1 : duration; - - // if effect already exists for longer or equal duration, do nothing (special case for shield) - if ( - this.effectIds[effectId] && - this.effectIds[effectId].duration >= duration && - effectId !== "shield" - ) { - return; - } - - // if effect exists, refresh duration - // TODO: should this be modified? - if (this.effectIds[effectId]) { - duration = Math.max(this.effectIds[effectId].duration, duration); - this.effectIds[effectId].duration = duration; - // shield special case - if (effectId !== "shield") { - return; - } - } - - // trigger before effect add events - const beforeAddArgs = { - target: this, - source, - effectId, - duration, - initialArgs, - canAdd: true, - }; - this.battle.eventHandler.emit( - battleEventNames.BEFORE_EFFECT_ADD, - beforeAddArgs - ); - if (!beforeAddArgs.canAdd) { - return; - } - duration = beforeAddArgs.duration; - - // special case for shield - let oldShield = {}; - if (effectId === "shield") { - // if shield already exists, keep note of it - if (this.effectIds[effectId]) { - oldShield = this.effectIds[effectId].args; - } - } - this.effectIds[effectId] = { - duration, - source, - initialArgs, - }; - if (oldShield) { - this.effectIds[effectId].args = oldShield; - } - - if (!effect.isLegacyEffect) { - this.effectIds[effectId].args = - effect.effectAdd({ - battle: this.battle, - source, - target: this, - initialArgs, - }) || {}; - } else { - const legacyEffect = /** @type {any} */ (effect); - this.effectIds[effectId].args = - legacyEffect.effectAdd(this.battle, source, this, initialArgs) || {}; - } - - if (this.effectIds[effectId] !== undefined) { - // trigger after add effect events - const afterAddArgs = { - target: this, - source, - effectId, - duration, - initialArgs: initialArgs, - args: this.effectIds[effectId].args, - }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_EFFECT_ADD, - afterAddArgs - ); - } - } - - dispellEffect(effectId) { - const effectData = getEffect(effectId); - - // if effect doesn't exist, do nothing - if (!this.effectIds[effectId]) { - return false; - } - - // if effect is not dispellable, do nothing - if (!effectData.dispellable) { - return false; - } - - return this.removeEffect(effectId); - } - - /** - * @template {EffectIdEnum} K - * @param {K} effectId - * @returns {{ - * duration: number, - * source: Pokemon, - * initialArgs: K extends keyof RegisteredEffects ? EffectInitialArgsType : any, - * args: K extends keyof RegisteredEffects ? EffectPropertiesType : any - * } | undefined} - */ - getEffectInstance(effectId) { - // @ts-ignore - return this.effectIds[effectId]; - } - - /** - * Forcefully deletes the effect instance from this Pokemon. ONLY USE to bypass effect removal logic. - * @param {EffectIdEnum} effectId - */ - deleteEffectInstance(effectId) { - delete this.effectIds[effectId]; - } - - removeEffect(effectId) { - // if effect doesn't exist, do nothing - if (!this.effectIds[effectId]) { - return false; - } - const effect = getEffect(effectId); - if (!effect) { - logger.error(`Effect ${effectId} does not exist.`); - return; - } - - if (!effect.isLegacyEffect) { - // @ts-ignore - effect.effectRemove({ - battle: this.battle, - target: this, - properties: this.effectIds[effectId].args, - initialArgs: this.effectIds[effectId].initialArgs, - }); - } else { - const legacyEffect = /** @type {any} */ (effect); - legacyEffect.effectRemove( - this.battle, - this, - this.effectIds[effectId].args, - this.effectIds[effectId].initialArgs - ); - } - - if (this.effectIds[effectId] !== undefined) { - const afterRemoveArgs = { - target: this, - source: this.effectIds[effectId].source, - effectId, - duration: this.effectIds[effectId].duration, - initialArgs: this.effectIds[effectId].initialArgs, - args: this.effectIds[effectId].args, - }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_EFFECT_REMOVE, - afterRemoveArgs - ); - } - - delete this.effectIds[effectId]; - return true; - } - - applyStatus(statusId, source, { startingTurns = 0 } = {}) { - // if faint, do nothing - if (this.isFainted) { - return; - } - - // if status already exists, do nothing - if (this.status.statusId) { - this.battle.addToLog(`${this.name} already has a status condition!`); - return; - } - - // trigger before apply status events - const beforeApplyArgs = { - target: this, - source, - statusId, - canApply: true, - }; - this.battle.eventHandler.emit( - battleEventNames.BEFORE_STATUS_APPLY, - beforeApplyArgs - ); - if (!beforeApplyArgs.canApply) { - return; - } - - let statusApplied = false; - switch (statusId) { - // TODO: other status effects - case statusConditions.BURN: - if (this.type1 === types.FIRE || this.type2 === types.FIRE) { - this.battle.addToLog( - `${this.name}'s Fire type renders it immune to burns!` - ); - break; - } - - this.status = { - statusId, - source, - turns: startingTurns, - }; - this.battle.addToLog(`${this.name} was burned!`); - statusApplied = true; - break; - case statusConditions.FREEZE: - if (this.type1 === types.ICE || this.type2 === types.ICE) { - this.battle.addToLog( - `${this.name}'s Ice type renders it immune to freezing!` - ); - break; - } - - if (this.battle.weather.weatherId === weatherConditions.SUN) { - this.battle.addToLog( - `${this.name} was protected from freezing by the sun!` - ); - break; - } - - this.status = { - statusId, - source, - turns: startingTurns, - }; - this.battle.addToLog(`${this.name} was frozen!`); - statusApplied = true; - break; - case statusConditions.PARALYSIS: - if (this.type1 === types.ELECTRIC || this.type2 === types.ELECTRIC) { - this.battle.addToLog( - `${this.name}'s Electric type renders it immune to paralysis!` - ); - break; - } - - // reduce speed by 45% - this.spe -= Math.floor(this.bspd * 0.45); - - this.status = { - statusId, - source, - turns: startingTurns, - }; - this.battle.addToLog(`${this.name} was paralyzed!`); - statusApplied = true; - break; - case statusConditions.POISON: - if (this.type1 === types.POISON || this.type2 === types.POISON) { - this.battle.addToLog( - `${this.name}'s Poison type renders it immune to poison!` - ); - break; - } - - this.status = { - statusId, - source, - turns: startingTurns, - }; - this.battle.addToLog(`${this.name} was poisoned!`); - statusApplied = true; - break; - case statusConditions.SLEEP: - this.status = { - statusId, - source, - turns: startingTurns, - }; - this.battle.addToLog(`${this.name} fell asleep!`); - statusApplied = true; - break; - case statusConditions.BADLY_POISON: - if (this.type1 === types.POISON || this.type2 === types.POISON) { - this.battle.addToLog( - `${this.name}'s Poison type renders it immune to poison!` - ); - break; - } - - this.status = { - statusId, - source, - turns: startingTurns, - }; - this.battle.addToLog(`${this.name} was badly poisoned!`); - statusApplied = true; - break; - default: - break; - } - - if (statusApplied) { - const afterStatusArgs = { - target: this, - source, - statusId, - }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_STATUS_APPLY, - afterStatusArgs - ); - } - } - - tickStatus() { - // if status doesn't exist, do nothing - if (!this.status.statusId) { - return; - } - - switch (this.status.statusId) { - case statusConditions.POISON: - const damage = Math.round(this.maxHp / 6); - this.battle.addToLog(`${this.name} is hurt by poison!`); - this.takeDamage(damage, this.status.source, { - type: "statusCondition", - statusId: statusConditions.POISON, - }); - break; - case statusConditions.BURN: - const burnDamage = Math.round(this.maxHp / 8); - this.battle.addToLog(`${this.name} is hurt by its burn!`); - this.takeDamage(burnDamage, this.status.source, { - type: "statusCondition", - statusId: statusConditions.BURN, - }); - break; - case statusConditions.BADLY_POISON: - const badlyPoisonDamage = - Math.round(this.maxHp / 6) * 2 ** this.status.turns; - this.battle.addToLog(`${this.name} is hurt by poison!`); - this.takeDamage(badlyPoisonDamage, this.status.source, { - type: "statusCondition", - statusId: statusConditions.BADLY_POISON, - }); - break; - default: - break; - } - - this.status.turns += 1; - } - - removeStatus() { - // if status doesn't exist, do nothing - if (!this.status.statusId) { - return false; - } - - // TODO: trigger before remove status events - - switch (this.status.statusId) { - case statusConditions.BURN: - this.battle.addToLog(`${this.name} was cured of its burn!`); - break; - case statusConditions.FREEZE: - this.battle.addToLog(`${this.name} was thawed out!`); - break; - case statusConditions.PARALYSIS: - // restore speed - this.spe += Math.floor(this.bspd * 0.45); - - this.battle.addToLog(`${this.name} was cured of its paralysis!`); - break; - case statusConditions.POISON: - this.battle.addToLog(`${this.name} was cured of its poison!`); - break; - case statusConditions.SLEEP: - this.battle.addToLog(`${this.name} woke up!`); - break; - default: - break; - } - - this.status = { - statusId: null, - turns: 0, - }; - - return true; - } - - // eslint-disable-next-line no-unused-vars - disableMove(moveId, source) { - // check that move exists - if (!this.moveIds[moveId]) { - return; - } - - // if move already disabled, do nothing - if (this.moveIds[moveId].disabled) { - return; - } - - // disable move - this.moveIds[moveId].disabled = true; - // this.battle.addToLog(`${this.name}'s ${getMove(moveId).name} was disabled!`); - } - - // eslint-disable-next-line no-unused-vars - enableMove(moveId, source) { - // check that move exists - if (!this.moveIds[moveId]) { - return; - } - - // if move not disabled, do nothing - if (!this.moveIds[moveId].disabled) { - return; - } - - // enable move - this.moveIds[moveId].disabled = false; - // this.battle.addToLog(`${this.name}'s ${getMove(moveId).name} is no longer disabled!`); - } - - tickEffectDurations() { - for (const effectId in this.effectIds) { - this.effectIds[effectId].duration -= 1; - if (this.effectIds[effectId].duration <= 0) { - this.removeEffect(effectId); - } - } - } - - tickMoveCooldowns() { - for (const moveId in this.moveIds) { - if (this.moveIds[moveId].cooldown > 0) { - this.moveIds[moveId].cooldown -= 1; - } - } - } - - reduceMoveCooldown(moveId, amount, source, silenced = false) { - // check that move exists - if (!this.moveIds[moveId]) { - return; - } - - // check that the move is on cooldown - if (this.moveIds[moveId].cooldown === 0) { - return; - } - - // reduce cooldown - const oldCooldown = this.moveIds[moveId].cooldown; - this.moveIds[moveId].cooldown -= amount; - if (this.moveIds[moveId].cooldown < 0) { - this.moveIds[moveId].cooldown = 0; - } - const newCooldown = this.moveIds[moveId].cooldown; - - if (!silenced) { - this.battle.addToLog( - `${this.name}'s ${getMove(moveId).name}'s cooldown was reduced by ${ - oldCooldown - newCooldown - } turns!` - ); - } - return oldCooldown - newCooldown; - } - - effectiveSpeed() { - return calculateEffectiveSpeed(this.getSpe()); - } - - getRowAndColumn() { - const party = this.battle.parties[this.teamName]; - const row = Math.floor((this.position - 1) / party.cols); - const col = (this.position - 1) % party.cols; - return { row, col }; - } - - getPartyRowColumn() { - const party = this.battle.parties[this.teamName]; - return { party, ...this.getRowAndColumn() }; - } - - getEnemyParty() { - const teamNames = Object.keys(this.battle.parties); - const enemyTeamName = - teamNames[0] === this.teamName ? teamNames[1] : teamNames[0]; - return this.battle.parties[enemyTeamName]; - } - - getDef() { - let { def } = this; - - // if hail and ice, def * 1.5 - if ( - !this.battle.isWeatherNegated() && - this.battle.weather.weatherId === weatherConditions.HAIL && - (this.type1 === types.ICE || this.type2 === types.ICE) - ) { - def = Math.floor(def * 1.5); - } - - return def; - } - - getSpd() { - let { spd } = this; - - // if sandstorm and rock, spd * 1.5 - if ( - !this.battle.isWeatherNegated() && - this.battle.weather.weatherId === weatherConditions.SANDSTORM && - (this.type1 === types.ROCK || this.type2 === types.ROCK) - ) { - spd = Math.floor(spd * 1.5); - } - - return spd; - } - - getSpe() { - let { spe } = this; - - if (!this.battle.isWeatherNegated()) { - if (this.battle.weather.weatherId === weatherConditions.SUN) { - if (this.ability && this.ability.abilityId === "34") { - spe = Math.floor(spe * 1.5); - } - } else if (this.battle.weather.weatherId === weatherConditions.RAIN) { - if (this.ability && this.ability.abilityId === "33") { - spe = Math.floor(spe * 1.5); - } - } - } - - return spe; - } -} - -class Battle { - /* TODO: fix - moneyMultiplier; - expMultiplier; - pokemonExpMultiplier; - moneyReward; - expReward; - pokemonExpReward; - winCallback; - hasStarted; - userIds; - users; - teams; - activePokemon; - parties; - // { weatherId, duration, source } - weather; - log; - eventHandler; - turn; - winner; - ended; - minLevel; - level; - rewards; - rewardString; - dailyRewards; - npcId; - difficulty; - isPvp; */ - - /** - * @param {object} param0 - * @param {number?=} param0.moneyMultiplier - * @param {number?=} param0.expMultiplier - * @param {number?=} param0.pokemonExpMultiplier - * @param {number?=} param0.level - * @param {number?=} param0.equipmentLevel - * @param {object?=} param0.rewards - * @param {string?=} param0.rewardString - * @param {object?=} param0.dailyRewards - * @param {Function?=} param0.winCallback - * @param {Function?=} param0.loseCallback - * @param {string?=} param0.npcId - * @param {string?=} param0.difficulty - * @param {boolean?=} param0.isPvp - */ - constructor({ - moneyMultiplier = 1, - expMultiplier = 1, - pokemonExpMultiplier = 0.2, - level = null, - equipmentLevel = null, - rewards = null, - rewardString = null, - dailyRewards = null, - winCallback = null, - loseCallback = null, - npcId = null, - difficulty = null, - isPvp = false, - } = {}) { - // initial - this.baseMoney = 100; - this.baseExp = 50; - - this.moneyMultiplier = moneyMultiplier; - this.expMultiplier = expMultiplier; - this.pokemonExpMultiplier = pokemonExpMultiplier; - this.moneyReward = 0; - this.expReward = 0; - this.pokemonExpReward = 0; - this.hasStarted = false; - this.userIds = []; - // map userId to user - this.users = {}; - // map teamName to team - this.teams = {}; - this.activePokemon = null; - // map teamName to party - this.parties = {}; - // map pokemonId to pokemon - this.allPokemon = {}; - this.weather = { - weatherId: null, - duration: 0, - source: null, - }; - this.log = []; - this.eventHandler = new BattleEventHandler(this); - this.turn = 0; - this.winner = null; - this.ended = false; - if (equipmentLevel) { - if (equipmentLevel < 1 || equipmentLevel > 10) { - throw new Error("Invalid equipment level"); - } - this.equipmentLevel = equipmentLevel; - } - if (level) { - if (level < 1 || level > 100) { - throw new Error("Invalid level"); - } - this.level = level; - this.minLevel = level; - } - this.rewards = rewards; - this.rewardString = rewardString; - this.dailyRewards = dailyRewards; - this.npcId = npcId; - this.difficulty = difficulty; - this.winCallback = winCallback; - this.loseCallback = loseCallback; - this.isPvp = isPvp; - } - - addTeam(teamName, isNpc) { - this.teams[teamName] = { - name: teamName, - isNpc, - userIds: [], - }; - } - - addTrainer(trainer, pokemons, teamName, rows = 3, cols = 4) { - // if user already exists, return - if (this.users[trainer.userId]) { - return; - } - - this.teams[teamName].userIds.push(trainer.userId); - this.users[trainer.userId] = { - teamName, - ...trainer.user, - }; - this.userIds.push(trainer.userId); - - this.addPokemons(trainer, pokemons, teamName, rows, cols); - } - - addPokemons(trainer, pokemons, teamName, rows, cols) { - const partyPokemons = []; - for (const pokemonData of pokemons) { - if (pokemonData) { - const pokemonInstance = new Pokemon( - this, - trainer, - pokemonData, - teamName, - partyPokemons.length + 1 - ); - partyPokemons.push(pokemonInstance); - this.allPokemon[pokemonInstance.id] = pokemonInstance; - } else { - partyPokemons.push(null); - } - } - // TODO: modify if parties can have different num players - this.parties[teamName] = { - pokemons: partyPokemons, - rows, - cols, - }; - } - - increaseCombatReadiness() { - // get min ticks for a pokemon to be ready - const MAX_CR = 100; - let minTicks = Number.MAX_SAFE_INTEGER; - let minTicksPokemon = null; - for (const partyName in this.parties) { - const party = this.parties[partyName]; - for (const pokemon of party.pokemons) { - if (pokemon && !pokemon.isFainted) { - const requiredCr = MAX_CR - pokemon.combatReadiness; - const ticks = requiredCr / pokemon.effectiveSpeed(); - if (ticks < minTicks) { - minTicks = ticks; - minTicksPokemon = pokemon; - } - } - } - } - - // set active pokemon - if (minTicksPokemon) { - this.activePokemon = minTicksPokemon; - } else { - this.activePokemon = null; - return; - } - - // if no ticks, return - if (minTicks === 0) { - return; - } - - // increase combat readiness for all pokemon - for (const partyName in this.parties) { - const party = this.parties[partyName]; - for (const pokemon of party.pokemons) { - if (pokemon && !pokemon.isFainted) { - pokemon.combatReadiness = Math.min( - MAX_CR, - pokemon.combatReadiness + pokemon.effectiveSpeed() * minTicks - ); - } - } - } - } - - start() { - this.log.push("The battle begins!"); - this.turn = 0; - this.hasStarted = true; - - // sort all pokemon by speed descending - const sortedPokemon = Object.values(this.allPokemon); - sortedPokemon.sort((a, b) => b.getSpe() - a.getSpe()); - this.allPokemon = {}; - for (const pokemon of sortedPokemon) { - this.allPokemon[pokemon.id] = pokemon; - } - - // add all abilities - Object.entries(this.allPokemon).forEach(([, pokemon]) => { - if (pokemon.ability.applied) { - return; - } - pokemon.applyAbility(); - }); - - this.eventHandler.emit(battleEventNames.BATTLE_BEGIN, { - battle: this, - }); - - // begin turn - this.beginTurn(); - } - - beginTurn() { - // push cr - this.increaseCombatReadiness(); - - // tick move cooldowns - if (!this.activePokemon.isFainted) { - this.activePokemon.tickMoveCooldowns(); - } - - // begin turn - this.eventHandler.emit(battleEventNames.TURN_BEGIN); - - // log - const userIsNpc = this.isNpc(this.activePokemon.userId); - const userString = userIsNpc - ? this.users[this.activePokemon.userId].username - : `<@${this.activePokemon.userId}>`; - if (this.activePokemon.canMove()) { - this.log.push( - `**[Turn ${this.turn}] It is ${userString}'s ${this.activePokemon.name}'s turn.**` - ); - } else { - this.log.push( - `**[Turn ${this.turn}] ${userString}'s ${this.activePokemon.name} is unable to move.**` - ); - } - } - - nextTurn() { - // end turn logic - this.eventHandler.emit(battleEventNames.TURN_END); - - // tick status effects - if (!this.activePokemon.isFainted) { - this.activePokemon.tickStatus(); - } - - // tick effects - if (!this.activePokemon.isFainted) { - this.activePokemon.tickEffectDurations(); - } - - // tick weather - if (this.weather) { - this.tickWeather(); - } - - // increase turn and check for game end - this.turn += 1; - if (this.turn > 100) { - return this.endBattle(); - } - const undefeatedTeams = []; - for (const teamName in this.teams) { - let isTeamDefeated = true; - for (const pokemon of this.parties[teamName].pokemons) { - if (pokemon && !pokemon.isFainted) { - isTeamDefeated = false; - break; - } - } - if (!isTeamDefeated) { - undefeatedTeams.push(teamName); - } - } - if (undefeatedTeams.length === 1) { - [this.winner] = undefeatedTeams; - return this.endBattle(); - } - if (undefeatedTeams.length === 0) { - return this.endBattle(); - } - - // begin turn - this.beginTurn(); - } - - endBattle() { - if (this.winner) { - // get loser, and if any loser has next phase, initialize next phase. - const loser = - Object.keys(this.teams)[0] === this.winner - ? Object.keys(this.teams)[1] - : Object.keys(this.teams)[0]; - let nextPhase = false; - for (const userId of this.teams[loser].userIds) { - const user = this.users[userId]; - if (user.nextPhase !== undefined) { - const userNextPhase = user.nextPhase(this); - if (!userNextPhase) { - continue; - } - const { trainer, pokemons, rows, cols } = userNextPhase; - - // clear out all of user's pokemon - for (const [pokemonId, pokemon] of Object.entries(this.allPokemon)) { - if (pokemon.userId === userId) { - delete this.allPokemon[pokemonId]; - } - } - // add new pokemon - this.addPokemons(trainer, pokemons, loser, rows, cols); - nextPhase = true; - } - } - if (nextPhase) { - this.addToLog(`**[Phase Complete] A new battle phase begins!**`); - return this.start(); - } - - let winnerMentions = ""; - if (!this.teams[this.winner].isNpc) { - winnerMentions = this.teams[this.winner].userIds - .map((userId) => `<@${userId}>`) - .join(" "); - } - this.addToLog(`**Team ${this.winner} has won! ${winnerMentions}**`); - } else { - this.addToLog("**The battle has ended in a draw!**"); - } - this.ended = true; - - // if winner is npc, no rewards - if (this.teams[this.winner] && this.teams[this.winner].isNpc) { - this.addToLog("No rewards were given because the winner is an NPC."); - return; - } - - this.moneyReward = Math.floor(this.baseMoney * this.moneyMultiplier); - this.expReward = Math.floor(this.baseExp * this.expMultiplier); - // calculate pokemon exp by summing defeated pokemon's levels - this.pokemonExpReward = Math.floor( - Object.values(this.allPokemon).reduce((acc, pokemon) => { - if (pokemon.isFainted) { - return acc + (this.minLevel || pokemon.level); - } - return acc; - }, 0) * this.pokemonExpMultiplier - ); - - this.addToLog( - `Winners recieved ${formatMoney(this.moneyReward)}, ${ - this.expReward - } exp, and ${ - this.pokemonExpReward - } BASE Pokemon exp. Losers recieved half the amount.` - ); - } - - createWeather(weatherId, source) { - // calculate turns = 10 + number of non-fainted Pokemon - const duration = - 10 + - Object.values(this.allPokemon).reduce((acc, pokemon) => { - if (!pokemon.isFainted) { - return acc + 1; - } - return acc; - }, 0); - - // if weather exists and is same, refresh duration if possible - if (this.weather.weatherId === weatherId) { - if (this.weather.duration < duration) { - this.weather.duration = duration; - this.weather.source = source; - this.addToLog(`The weather intensified!`); - return true; - } - return false; - } - - // apply weather - this.weather = { - weatherId, - duration, - source, - }; - switch (weatherId) { - case weatherConditions.SUN: - this.addToLog(`The sunlight turned harsh!`); - break; - case weatherConditions.RAIN: - this.addToLog(`It started to rain!`); - break; - case weatherConditions.SANDSTORM: - this.addToLog(`A sandstorm kicked up!`); - break; - case weatherConditions.HAIL: - this.addToLog(`It started to hail!`); - break; - default: - break; - } - - return true; - } - - clearWeather() { - if (this.weather.weatherId) { - this.addToLog(`The weather cleared up!`); - } - this.weather = { - weatherId: null, - duration: 0, - source: null, - }; - } - - tickWeather() { - if (!this.weather.weatherId) { - return; - } - - if (!this.isWeatherNegated()) { - switch (this.weather.weatherId) { - case weatherConditions.SANDSTORM: - // tick weather for active Pokemon's team - this.addToLog(`The sandstorm rages!`); - for (const pokemon of this.parties[this.activePokemon.teamName] - .pokemons) { - // if pokemon not targetable, skip - if (this.isPokemonTargetable(pokemon) === false) { - continue; - } - // if pokemon not rock, steel, or ground, damage 1/16 of max hp - if ( - pokemon.type1 !== types.ROCK && - pokemon.type1 !== types.STEEL && - pokemon.type1 !== types.GROUND && - pokemon.type2 !== types.ROCK && - pokemon.type2 !== types.STEEL && - pokemon.type2 !== types.GROUND - ) { - pokemon.takeDamage( - Math.floor(pokemon.maxHp / 16), - this.weather.source, - { - type: "weather", - } - ); - } - } - break; - case weatherConditions.HAIL: - // tick weather for active Pokemon's team - this.addToLog(`The hail continues!`); - for (const pokemon of this.parties[this.activePokemon.teamName] - .pokemons) { - // if pokemon not targetable, skip - if (this.isPokemonTargetable(pokemon) === false) { - continue; - } - // if pokemon not ice, damage 1/16 of max hp - if (pokemon.type1 !== types.ICE && pokemon.type2 !== types.ICE) { - pokemon.takeDamage( - Math.floor(pokemon.maxHp / 16), - this.weather.source, - { - type: "weather", - } - ); - } - } - break; - default: - break; - } - } - - this.weather.duration -= 1; - if (this.weather.duration <= 0) { - this.clearWeather(); - } - } - - isWeatherNegated() { - // for all non-fainted pokemon, check if they have cloud nine or air lock - for (const pokemon of Object.values(this.allPokemon)) { - if (pokemon.isFainted) { - continue; - } - - if ( - pokemon.ability.abilityId === "13" || - pokemon.ability.abilityId === "76" - ) { - return true; - } - } - - return false; - } - - isNpc(userId) { - const user = this.users[userId]; - return user.npc !== undefined; - } - - getEligibleTargets(source, moveId) { - const moveData = getMove(moveId); - const eligibleTargets = []; - const eventArgs = { - user: source, - moveId, - eligibleTargets, - shouldReturn: false, - }; - this.eventHandler.emit(battleEventNames.GET_ELIGIBLE_TARGETS, eventArgs); - if (eventArgs.shouldReturn) { - return eligibleTargets; - } - - let targetParty = null; - // use target type to get party - switch (moveData.targetType) { - case targetTypes.ALLY: - targetParty = this.parties[source.teamName]; - break; - case targetTypes.ENEMY: - for (const teamName in this.parties) { - if (teamName !== source.teamName) { - targetParty = this.parties[teamName]; - break; - } - } - break; - default: - // return all pokemon - for (const teamName in this.parties) { - const party = this.parties[teamName]; - for (const pokemon of party.pokemons) { - if (this.isPokemonTargetable(pokemon, moveId)) { - eligibleTargets.push(pokemon); - } - } - } - return eligibleTargets; - } - - // use target position to get valid targets - let index; - switch (moveData.targetPosition) { - case targetPositions.SELF: - eligibleTargets.push(source); - break; - case targetPositions.NON_SELF: - for (const pokemon of targetParty.pokemons) { - if (this.isPokemonTargetable(pokemon, moveId) && pokemon !== source) { - eligibleTargets.push(pokemon); - } - } - break; - case targetPositions.FRONT: - // break up party into rows - // if pokemons exist in row, add to eligible targets - // if all pokemon in front row fainted or nonexistent, move to next row - - index = 0; - for (let i = 0; i < targetParty.rows; i += 1) { - let pokemonFound = false; - for (let j = 0; j < targetParty.cols; j += 1) { - const pokemon = targetParty.pokemons[index]; - if (this.isPokemonTargetable(pokemon, moveId)) { - pokemonFound = true; - eligibleTargets.push(pokemon); - } - index += 1; - } - if (pokemonFound) { - break; - } - } - break; - case targetPositions.BACK: - // break up party into rows - // if pokemons exist in back row, add to eligible targets - // if all pokemon in back row fainted or nonexistent, move to next row - index = targetParty.pokemons.length - 1; - for (let i = targetParty.rows - 1; i >= 0; i -= 1) { - let pokemonFound = false; - for (let j = targetParty.cols - 1; j >= 0; j -= 1) { - const pokemon = targetParty.pokemons[index]; - if (this.isPokemonTargetable(pokemon, moveId)) { - pokemonFound = true; - eligibleTargets.push(pokemon); - } - index -= 1; - } - if (pokemonFound) { - break; - } - } - break; - default: - // return all pokemon in party - for (const pokemon of targetParty.pokemons) { - if (this.isPokemonTargetable(pokemon, moveId)) { - eligibleTargets.push(pokemon); - } - } - break; - } - - return eligibleTargets; - } - - // eslint-disable-next-line class-methods-use-this - isPokemonTargetable(pokemon, moveId = null) { - if (!pokemon || pokemon.isFainted) { - return false; - } - - if (!pokemon.targetable) { - // special cases - // TODO: should this be an event listener? - - // eq + dig - if (moveId === "m89" && pokemon.effectIds.burrowed !== undefined) { - return true; - } - - // bounce + thunder or gust or smackdown - if ( - (moveId === "m87" || - moveId === "m87-1" || - moveId === "m16" || - moveId === "m479") && - pokemon.effectIds.sprungUp !== undefined - ) { - return true; - } - - return false; - } - - return true; - } - - // eslint-disable-next-line class-methods-use-this - isPokemonHittable(pokemon, moveId = null) { - if (!pokemon || pokemon.isFainted) { - return false; - } - - if (!pokemon.hittable) { - // special cases - // TODO: should this be an event listener? - - // eq + dig - if (moveId === "m89" && pokemon.effectIds.burrowed !== undefined) { - return true; - } - - // bounce + thunder or gust or smackdown - if ( - (moveId === "m87" || - moveId === "m87-1" || - moveId === "m16" || - moveId === "m479") && - pokemon.effectIds.sprungUp !== undefined - ) { - return true; - } - - return false; - } - - return true; - } - - clearLog() { - this.log = []; - } - - addToLog(message) { - this.log.push(message); - } -} - -const getStartTurnSend = async (battle, stateId) => { - // clip log to last 20 lines - if (battle.log.length > 20) { - battle.log = battle.log.slice(battle.log.length - 20); - } - let content = battle.log.join("\n"); - battle.clearLog(); - - const stateEmbed = buildBattleEmbed(battle); - - const components = []; - if (!battle.ended) { - const infoRow = buildBattleInfoActionRow( - battle, - stateId, - Object.keys(battle.teams).length + 1 - ); - components.push(infoRow); - - // check if active pokemon can move - // TODO: deal with NPC case - if ( - battle.activePokemon.canMove() && - !battle.isNpc(battle.activePokemon.userId) - ) { - const selectMoveComponent = buildSelectBattleMoveRow(battle, stateId); - components.push(selectMoveComponent); - } else { - const nextTurnComponent = buildNextTurnActionRow(stateId); - components.push(nextTurnComponent); - } - } else { - // if game ended, add rewards to trainers and pokemon - // for non-NPC teams, for all trainers, add rewards - try { - // if winner is NPC, no rewards - if (!battle.teams[battle.winner].isNpc) { - if (battle.rewardString) { - content += `\n${battle.rewardString}`; - } - const rewardRecipients = []; - content += `\n**The following Pokemon leveled up:**`; - for (const teamName in battle.teams) { - const team = battle.teams[teamName]; - if (team.isNpc) { - continue; - } - - const moneyReward = - teamName === battle.winner - ? battle.moneyReward - : Math.floor(battle.moneyReward / 2); - const expReward = - teamName === battle.winner - ? battle.expReward - : Math.floor(battle.expReward / 2); - const pokemonExpReward = - teamName === battle.winner - ? battle.pokemonExpReward - : Math.floor(battle.pokemonExpReward / 2); - - // TODO: optimize this it makes too many db calls - for (const userId of team.userIds) { - const user = battle.users[userId]; - // get trainer - const trainer = await getTrainer(user); - if (trainer.err) { - logger.warn( - `Failed to get trainer for user ${user.id} after battle` - ); - continue; - } - - // trigger battle win callback - if (battle.winCallback) { - await battle.winCallback(battle, trainer.data); - } - - // add trainer rewards - await addExpAndMoney(user, expReward, moneyReward); - const defeatedDifficultiesToday = - trainer.data.defeatedNPCsToday[battle.npcId]; - const defeatedDifficulties = - trainer.data.defeatedNPCs[battle.npcId]; - const allRewards = {}; - let modified = false; - // add battle rewards - if (battle.rewards) { - addRewards(trainer.data, battle.rewards, allRewards); - modified = true; - } - // add daily rewards - if ( - battle.dailyRewards && - (!defeatedDifficultiesToday || - !defeatedDifficultiesToday.includes(battle.difficulty)) - ) { - addRewards(trainer.data, battle.dailyRewards, allRewards); - getOrSetDefault( - trainer.data.defeatedNPCsToday, - battle.npcId, - [] - ).push(battle.difficulty); - modified = true; - } - // add to defeated difficulties if not already there - if ( - !defeatedDifficulties || - !defeatedDifficulties.includes(battle.difficulty) - ) { - getOrSetDefault(trainer.data.defeatedNPCs, battle.npcId, []).push( - battle.difficulty - ); - modified = true; - } - - // attempt to add rewards - if (modified) { - const { err } = await updateTrainer(trainer.data); - if (err) { - logger.warn( - `Failed to update daily trainer for user ${user.id} after battle` - ); - continue; - } else { - // this is kinda hacky there may be a better way to do this - rewardRecipients.push({ - username: user.username, - rewards: allRewards, - }); - } - } - - const levelUps = []; - // add pokemon rewards - for (const pokemon of Object.values(battle.allPokemon).filter( - (p) => p.originalUserId === trainer.data.userId - )) { - // get db pokemon - const dbPokemon = await getPokemon(trainer.data, pokemon.id); - if (dbPokemon.err) { - logger.warn(`Failed to get pokemon ${pokemon.id} after battle`); - continue; - } - - const oldLevel = dbPokemon.data.level; - const trainResult = await addPokemonExpAndEVs( - trainer.data, - dbPokemon.data, - pokemonExpReward - ); - if (trainResult.err) { - continue; - } - const newLevel = trainResult.data.level; - - if (newLevel > oldLevel) { - levelUps.push({ - pokemonName: dbPokemon.data.name, - oldLevel, - newLevel, - }); - } - } - if (levelUps.length > 0) { - content += `\n${user.username}'s Pokemon: ${levelUps - .map((l) => `${l.pokemonName} (${l.oldLevel} -> ${l.newLevel})`) - .join(", ")}`; - } - } - - if (rewardRecipients.length > 0) { - content += `\n**${rewardRecipients - .map((r) => r.username) - .join(", ")} received rewards for their victory:**`; - content += getRewardsString(rewardRecipients[0].rewards, false); - } - } - } else if (battle.loseCallback) { - await battle.loseCallback(battle); - } - } catch (err) { - logger.error(`Failed to add battle rewards: ${err}`); - } - - // re-add log to content - content += `\n${battle.log.join("\n")}`; - - // if state has an NPC id, add a replay button - const state = getState(stateId); - if (state && state.endBattleComponents) { - components.push(...state.endBattleComponents); - } else { - deleteState(stateId); - } - } - - return { - content, - embeds: [stateEmbed], - components, - }; -}; - -const buildPveSend = async ({ - stateId = null, - user = null, - view = "list", - option = null, - page = 1, -} = {}) => { - // get state - const state = getState(stateId); - - // get trainer - let trainer = await getTrainer(user); - if (trainer.err) { - return { send: null, err: trainer.err }; - } - trainer = trainer.data; - - const send = { - embeds: [], - components: [], - }; - const pageSize = 10; - const npcIds = Object.keys(npcConfig); - if (view === "list") { - const maxPages = Math.ceil(npcIds.length / pageSize); - if (page < 1 || page > maxPages) { - return { embed: null, err: `Invalid page!` }; - } - // get npc ids for page - const npcIdsForPage = npcIds.slice((page - 1) * pageSize, page * pageSize); - - // build list embed - const embed = buildPveListEmbed(npcIdsForPage, page); - send.embeds.push(embed); - - // build scroll buttons - const scrollData = { - stateId, - }; - const scrollRow = buildScrollActionRow( - page, - page === maxPages, - scrollData, - eventNames.PVE_SCROLL - ); - send.components.push(scrollRow); - - // build npc select menu - const npcSelectRowData = { - stateId, - }; - const npcSelectRow = buildIdConfigSelectRow( - npcIdsForPage, - npcConfig, - "Select an NPC to battle:", - npcSelectRowData, - eventNames.PVE_SELECT, - false - ); - send.components.push(npcSelectRow); - } else if (view === "npc") { - // validate npc id - const npcData = npcConfig[option]; - if (npcData === undefined) { - return { embed: null, err: `Invalid NPC!` }; - } - - // build npc embed - const embed = buildPveNpcEmbed(option); - send.embeds.push(embed); - - // build difficulty row - const difficultySelectData = { - stateId, - }; - const difficultyButtonConfigs = Object.keys(npcData.difficulties).map( - (difficulty) => ({ - label: difficultyConfig[difficulty].name, - disabled: false, - data: { - ...difficultySelectData, - difficulty, - }, - }) - ); - const difficultyRow = buildButtonActionRow( - difficultyButtonConfigs, - eventNames.PVE_ACCEPT - ); - send.components.push(difficultyRow); - - // build return button - const index = npcIds.indexOf(option); - const returnToPage = Math.floor(index / pageSize) + 1; - const returnData = { - stateId, - page: returnToPage, - }; - const returnButtonConfigs = [ - { - label: "Return", - disabled: false, - data: returnData, - }, - ]; - const returnRow = buildButtonActionRow( - returnButtonConfigs, - eventNames.PVE_SCROLL - ); - - state.npcId = option; - send.components.push(returnRow); - send.content = ""; - } else if (view === "battle") { - // validate npc id - const npcData = npcConfig[state.npcId]; - if (npcData === undefined) { - return { embed: null, err: `Invalid NPC!` }; - } - - // validate difficulty - const npcDifficultyData = npcData.difficulties[state.difficulty]; - if (npcDifficultyData === undefined) { - return { - embed: null, - err: `Difficulty doesn't exist for ${npcData.name}!`, - }; - } - - // get trainer - const trainerResult = await getTrainer(user); - if (trainerResult.err) { - return { embed: null, err: trainerResult.err }; - } - - // validate party - const validate = await validateParty(trainerResult.data); - if (validate.err) { - return { err: validate.err }; - } - - // add npc to battle - const npc = new NPC(npcData, state.difficulty); - npc.setPokemon(npcData, state.difficulty); - const rewardMultipliers = - npcDifficultyData.rewardMultipliers || - difficultyConfig[state.difficulty].rewardMultipliers; - const battle = new Battle({ - ...rewardMultipliers, - dailyRewards: npcDifficultyData.dailyRewards, - npcId: state.npcId, - difficulty: state.difficulty, - }); - battle.addTeam("NPC", true); - battle.addTrainer( - npc, - npc.party.pokemons, - "NPC", - npc.party.rows, - npc.party.cols - ); - battle.addTeam("Player", false); - battle.addTrainer(trainerResult.data, validate.data, "Player"); - - // start battle and add to state - battle.start(); - state.battle = battle; - - // add a replay button to state for later - // build difficulty row - const difficultySelectData = { - stateId, - difficulty: state.difficulty, - }; - const difficultyButtonConfigs = [ - { - label: "Replay", - disabled: false, - data: difficultySelectData, - }, - ]; - const difficultyRow = buildButtonActionRow( - difficultyButtonConfigs, - eventNames.PVE_ACCEPT - ); - - const returnData = { - stateId, - }; - const returnButtonConfigs = [ - { - label: "Return", - disabled: false, - data: returnData, - }, - ]; - const returnRow = buildButtonActionRow( - returnButtonConfigs, - eventNames.PVE_SELECT - ); - - state.endBattleComponents = [difficultyRow, returnRow]; - - return { - send: await getStartTurnSend(battle, stateId), - err: null, - }; - } - - return { send, err: null }; -}; - -const buildDungeonSend = async ({ - stateId = null, - user = null, - view = "list", - option = null, -} = {}) => { - // get state - const state = getState(stateId); - - // get trainer - let trainer = await getTrainer(user); - if (trainer.err) { - return { send: null, err: trainer.err }; - } - trainer = trainer.data; - - const send = { - embeds: [], - components: [], - }; - if (view === "list") { - // build list embed - const embed = buildDungeonListEmbed(); - send.embeds.push(embed); - - // build dungeon select menu - const dungeonSelectRowData = { - stateId, - }; - const dungeonSelectRow = buildIdConfigSelectRow( - Object.values(dungeons), - dungeonConfig, - "Select a Dungeon to battle:", - dungeonSelectRowData, - eventNames.DUNGEON_SELECT, - false - ); - send.components.push(dungeonSelectRow); - } else if (view === "dungeon") { - // validate npc id - const dungeonData = dungeonConfig[option]; - if (dungeonData === undefined) { - return { embed: null, err: `Invalid Dungeon!` }; - } - - // build npc embed - const embed = buildDungeonEmbed(option); - send.embeds.push(embed); - - // build difficulty row - const difficultySelectData = { - stateId, - }; - const difficultyButtonConfigs = Object.keys(dungeonData.difficulties).map( - (difficulty) => ({ - label: difficultyConfig[difficulty].name, - disabled: false, - data: { - ...difficultySelectData, - difficulty, - }, - }) - ); - const difficultyRow = buildButtonActionRow( - difficultyButtonConfigs, - eventNames.DUNGEON_ACCEPT - ); - send.components.push(difficultyRow); - - // build return button - const returnData = { - stateId, - }; - const returnButtonConfigs = [ - { - label: "Return", - disabled: false, - data: returnData, - }, - ]; - const returnRow = buildButtonActionRow( - returnButtonConfigs, - // im lazy and using the same event name - eventNames.DUNGEON_ACCEPT - ); - - state.dungeonId = option; - send.components.push(returnRow); - } else if (view === "battle") { - // validate npc id - const dungeonData = dungeonConfig[state.dungeonId]; - if (dungeonData === undefined) { - return { embed: null, err: `Invalid Dungeon!` }; - } - - // validate difficulty - const dungeonDifficultyData = dungeonData.difficulties[state.difficulty]; - if (dungeonDifficultyData === undefined) { - return { - embed: null, - err: `Difficulty doesn't exist for ${dungeonData.name}!`, - }; - } - - // get trainer - const trainerResult = await getTrainer(user); - if (trainerResult.err) { - return { embed: null, err: trainerResult.err }; - } - - // validate party - const validate = await validateParty(trainerResult.data); - if (validate.err) { - return { err: validate.err }; - } - - // add npc to battle - const npc = new DungeonNPC(dungeonData, state.difficulty); - npc.setPokemon(dungeonData, state.difficulty); - const rewardMultipliers = - dungeonDifficultyData.rewardMultipliers || - difficultyConfig[state.difficulty].rewardMultipliers; - const battle = new Battle({ - ...rewardMultipliers, - rewards: dungeonDifficultyData.rewards, - rewardString: dungeonDifficultyData.rewardString, - npcId: state.dungeonId, - difficulty: state.difficulty, - }); - battle.addTeam("Dungeon", true); - battle.addTrainer( - npc, - npc.party.pokemons, - "Dungeon", - npc.party.rows, - npc.party.cols - ); - battle.addTeam("Player", false); - battle.addTrainer(trainerResult.data, validate.data, "Player"); - - // start battle and add to state - battle.start(); - state.battle = battle; - - // add a replay button to state for later - // build difficulty row - const difficultySelectData = { - stateId, - difficulty: state.difficulty, - }; - const difficultyButtonConfigs = [ - { - label: "Replay", - disabled: false, - data: difficultySelectData, - }, - ]; - const difficultyRow = buildButtonActionRow( - difficultyButtonConfigs, - eventNames.DUNGEON_ACCEPT - ); - state.endBattleComponents = [difficultyRow]; - - return { - send: await getStartTurnSend(battle, stateId), - err: null, - }; - } - - return { send, err: null }; -}; - -const towerWinCallback = async (battle, trainer) => { - // validate tower stage - const towerStage = trainer.lastTowerStage + 1; - if (getIdFromTowerStage(towerStage) !== battle.npcId) { - battle.rewards = null; - return; - } - - // make sure that stage exists - const battleTowerData = battleTowerConfig[towerStage]; - if (!battleTowerData) { - battle.rewards = null; - return; - } - - // update trainer - trainer.lastTowerStage = towerStage; - const updateRes = await updateTrainer(trainer); - if (updateRes.err) { - battle.rewards = null; - return; - } - - // add rewards to battle - battle.rewards = { - ...battleTowerData.rewards, - }; -}; - -const onBattleTowerAccept = async ({ stateId = null, user = null } = {}) => { - // get state - const state = getState(stateId); - - // get battle tower stage data - const towerStage = state.towerStage || 1; - const battleTowerData = battleTowerConfig[towerStage]; - if (!battleTowerData) { - return { send: null, err: `Invalid Battle Tower stage!` }; - } - // validate npc id - const npcData = npcConfig[battleTowerData.npcId]; - if (npcData === undefined) { - return { embed: null, err: `Invalid NPC!` }; - } - // validate difficulty - const npcDifficultyData = npcData.difficulties[battleTowerData.difficulty]; - if (npcDifficultyData === undefined) { - return { - embed: null, - err: `Difficulty doesn't exist for ${npcData.name}!`, - }; - } - - // get trainer - const trainer = await getTrainer(user); - if (trainer.err) { - return { embed: null, err: trainer.err }; - } - // validate that last stage is correct - if (trainer.data.lastTowerStage !== towerStage - 1) { - return { embed: null, err: `You must complete the previous stage first!` }; - } - // validate party - const validate = await validateParty(trainer.data); - if (validate.err) { - return { err: validate.err }; - } - - // add npc to battle - const npc = new TowerNPC(battleTowerData, battleTowerData.difficulty); - npc.setPokemon(battleTowerData, battleTowerData.difficulty); - const rewardMultipliers = - npcDifficultyData.rewardMultipliers || - difficultyConfig[battleTowerData.difficulty].rewardMultipliers; - const battle = new Battle({ - ...rewardMultipliers, - npcId: getIdFromTowerStage(towerStage), - difficulty: battleTowerData.difficulty, - winCallback: towerWinCallback, - }); - battle.addTeam("Battle Tower", true); - battle.addTrainer( - npc, - npc.party.pokemons, - "Battle Tower", - npc.party.rows, - npc.party.cols - ); - battle.addTeam("Player", false); - battle.addTrainer(trainer.data, validate.data, "Player"); - - // start battle and add to state - battle.start(); - state.battle = battle; - - // build return button - const returnData = { - stateId, - page: towerStage, - }; - const returnButtonConfigs = [ - { - label: "Return", - disabled: false, - data: returnData, - }, - ]; - const returnRow = buildButtonActionRow( - returnButtonConfigs, - // im lazy and using the same event name - eventNames.TOWER_SCROLL - ); - state.endBattleComponents = [returnRow]; - - return { err: null }; -}; - -/** - * - * @param {object} param0 - * @param {string?=} param0.stateId - * @param {User?=} param0.user - * @returns - */ -const buildBattleTowerSend = async ({ stateId = null, user = null } = {}) => { - // get state - const state = getState(stateId); - - // get battle tower stage data - const towerStage = state.towerStage || 1; - const battleTowerData = battleTowerConfig[towerStage]; - if (!battleTowerData) { - return { send: null, err: `Invalid Battle Tower stage!` }; - } - const maxPages = Object.keys(battleTowerConfig).length; - - // get trainer - let trainer = await getTrainer(user); - if (trainer.err) { - return { send: null, err: trainer.err }; - } - trainer = trainer.data; - - const send = { - content: "", - embeds: [], - components: [], - }; - - const embed = buildBattleTowerEmbed(towerStage); - send.embeds.push(embed); - - // build scroll buttons - const scrollData = { - stateId, - }; - const scrollRow = buildScrollActionRow( - towerStage, - towerStage === maxPages, - scrollData, - eventNames.TOWER_SCROLL - ); - send.components.push(scrollRow); - - // build battle button - const battleData = { - stateId, - }; - const battleButtonConfigs = [ - { - label: "Battle", - disabled: towerStage !== trainer.lastTowerStage + 1, - data: battleData, - }, - ]; - const battleRow = buildButtonActionRow( - battleButtonConfigs, - eventNames.TOWER_ACCEPT - ); - send.components.push(battleRow); - - return { send, err: null }; -}; - -module.exports = { - Battle, - // BattleEventHandler, - Pokemon, - getStartTurnSend, - buildPveSend, - buildDungeonSend, - onBattleTowerAccept, - buildBattleTowerSend, - RaidNPC, -}; diff --git a/src/battle/engine/events.js b/src/battle/engine/events.js new file mode 100644 index 00000000..61db685d --- /dev/null +++ b/src/battle/engine/events.js @@ -0,0 +1,59 @@ +const { v4: uuidv4 } = require("uuid"); +const { getOrSetDefault } = require("../../utils/utils"); + +class BattleEventHandler { + // event name => listenerIds + eventNames; + // listenerId => listener + eventListeners; + + constructor() { + this.eventNames = {}; + this.eventListeners = {}; + } + + registerListener(eventName, listener) { + // generate listener UUID + const listenerId = uuidv4(); + + getOrSetDefault(this.eventNames, eventName, {})[listenerId] = listener; + this.eventListeners[listenerId] = listener; + // add listenerId and eventName to listener.initialargs + // eslint-disable-next-line no-param-reassign + listener.initialArgs = { + listenerId, + eventName, + ...listener.initialArgs, + }; + + return listenerId; + } + + unregisterListener(listenerId) { + const listener = this.eventListeners[listenerId]; + if (listener) { + const { eventName } = listener.initialArgs; + const listenerIds = this.eventNames[eventName]; + if (listenerIds) { + delete listenerIds[listenerId]; + } + delete this.eventListeners[listenerId]; + } + } + + emit(eventName, args) { + const listenerIds = this.eventNames[eventName]; + if (listenerIds) { + for (const listenerId of listenerIds) { + const listener = this.eventListeners[listenerId]; + if (listener) { + listener.execute(listener.initialArgs, args); + } + } + } + } +} + +module.exports = { + BattleEventHandler, +}; diff --git a/src/battle/battleTypes.js b/src/battle/types.js similarity index 100% rename from src/battle/battleTypes.js rename to src/battle/types.js diff --git a/src/battle/utils.js b/src/battle/utils.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/services/battle.js b/src/services/battle.js index e6f8d767..aac15b3b 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -78,6 +78,7 @@ const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); const { getMove, executeMove } = require("../battle/data/moveService"); const { getEffect } = require("../battle/data/effectRegistry"); +const { BattleEventHandler } = require("../battle/engine/events"); class NPC { constructor( @@ -447,64 +448,6 @@ class RaidNPC extends NPC { } } -class BattleEventHandler { - /* battle; - // event name => listenerIds - eventNames; - // listenerId => listener - eventListeners; */ - - constructor(battle) { - this.battle = battle; - this.eventNames = {}; - this.eventListeners = {}; - } - - registerListener(eventName, listener) { - // generate listener UUID - const listenerId = uuidv4(); - - getOrSetDefault(this.eventNames, eventName, []).push(listenerId); - this.eventListeners[listenerId] = listener; - // add listenerId and eventName to listener.initialargs - // eslint-disable-next-line no-param-reassign - listener.initialArgs = { - listenerId, - eventName, - ...listener.initialArgs, - }; - - return listenerId; - } - - unregisterListener(listenerId) { - const listener = this.eventListeners[listenerId]; - if (listener) { - const { eventName } = listener.initialArgs; - const listenerIds = this.eventNames[eventName]; - if (listenerIds) { - const index = listenerIds.indexOf(listenerId); - if (index > -1) { - listenerIds.splice(index, 1); - } - } - delete this.eventListeners[listenerId]; - } - } - - emit(eventName, args) { - const listenerIds = this.eventNames[eventName]; - if (listenerIds) { - for (const listenerId of listenerIds) { - const listener = this.eventListeners[listenerId]; - if (listener) { - listener.execute(listener.initialArgs, args); - } - } - } - } -} - class Pokemon { /* battle; pokemonData; @@ -2161,7 +2104,7 @@ class Battle { source: null, }; this.log = []; - this.eventHandler = new BattleEventHandler(this); + this.eventHandler = new BattleEventHandler(); this.turn = 0; this.winner = null; this.ended = false; @@ -3565,7 +3508,6 @@ const buildBattleTowerSend = async ({ stateId = null, user = null } = {}) => { module.exports = { Battle, - // BattleEventHandler, Pokemon, getStartTurnSend, buildPveSend, From 00ca09504b3e1deb72066faafddf4b266e0902b1 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 18:25:39 -0700 Subject: [PATCH 32/38] enum rename --- eslint.config.mjs | 1 + src/battle/engine/events.js | 16 ++- src/battle/types.js | 8 +- src/config/battleConfig.js | 266 ++++++++++++++++-------------------- src/enums/battleEnums.js | 29 ++++ src/enums/types.js | 4 + src/services/battle.js | 51 ++++--- 7 files changed, 195 insertions(+), 180 deletions(-) create mode 100644 src/enums/types.js diff --git a/eslint.config.mjs b/eslint.config.mjs index ee5f0326..af5e2c6a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -37,6 +37,7 @@ export default [ "jsdoc/no-undefined-types": "off", // doesn't seem to work with TS types "jsdoc/require-param-description": "off", // TODO: maybe add descriptions "jsdoc/require-returns-description": "off", // TODO: maybe add descriptions + "jsdoc/require-property-description": "off", // TODO: maybe add descriptions "jsdoc/valid-types": "off", // doesn't seem to work with TS types "jsdoc/require-returns": "off", }, diff --git a/src/battle/engine/events.js b/src/battle/engine/events.js index 61db685d..495dcf0f 100644 --- a/src/battle/engine/events.js +++ b/src/battle/engine/events.js @@ -3,9 +3,9 @@ const { getOrSetDefault } = require("../../utils/utils"); class BattleEventHandler { // event name => listenerIds - eventNames; + // eventNames; // listenerId => listener - eventListeners; + // eventListeners; constructor() { this.eventNames = {}; @@ -16,7 +16,7 @@ class BattleEventHandler { // generate listener UUID const listenerId = uuidv4(); - getOrSetDefault(this.eventNames, eventName, {})[listenerId] = listener; + getOrSetDefault(this.eventNames, eventName, new Set()).add(listenerId); this.eventListeners[listenerId] = listener; // add listenerId and eventName to listener.initialargs // eslint-disable-next-line no-param-reassign @@ -35,7 +35,7 @@ class BattleEventHandler { const { eventName } = listener.initialArgs; const listenerIds = this.eventNames[eventName]; if (listenerIds) { - delete listenerIds[listenerId]; + listenerIds.delete(listenerId); } delete this.eventListeners[listenerId]; } @@ -54,6 +54,14 @@ class BattleEventHandler { } } +/** + * @param {object} param0 + * @param {string} param0.eventName + * @param {Battle} param0.battle + * @param param0.callback + */ +const registerListenerFunction = ({ eventName, battle, callback }) => {}; + module.exports = { BattleEventHandler, }; diff --git a/src/battle/types.js b/src/battle/types.js index 5f73972c..0eb3c817 100644 --- a/src/battle/types.js +++ b/src/battle/types.js @@ -1,8 +1,9 @@ /** - * @typedef {import("../enums/battleEnums").MoveIdEnum} MoveIdEnum - * @typedef {import("../enums/battleEnums").EffectIdEnum} EffectIdEnum * @typedef {import("../services/battle").Battle} Battle * @typedef {import("../services/battle").Pokemon} BattlePokemon + */ + +/** * @typedef {import("../config/battleConfig").BattleEventEnum} BattleEventEnum * @typedef {import("../config/battleConfig").DamageTypeEnum} DamageTypeEnum * @typedef {import("../config/battleConfig").MoveTierEnum} MoveTierEnum @@ -11,6 +12,9 @@ * @typedef {import("../config/battleConfig").TargetPositionEnum} TargetPositionEnum * @typedef {import("../config/battleConfig").TargetPatternEnum} TargetPatternEnum * @typedef {import("../config/battleConfig").EffectTypeEnum} EffectTypeEnum + */ + +/** * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} EffectAddBasicArgs * @typedef {{battle: Battle, target: BattlePokemon}} EffectRemoveBasicArgs * @typedef {typeof import("./data/effects").effectsToRegister} RegisteredEffects diff --git a/src/config/battleConfig.js b/src/config/battleConfig.js index 26d36978..2d49a1cb 100644 --- a/src/config/battleConfig.js +++ b/src/config/battleConfig.js @@ -11,34 +11,7 @@ const { executeMove, } = require("../battle/data/moveService"); const { getEffect } = require("../battle/data/effectRegistry"); - -/** @typedef {types.Enum} BattleEventEnum */ -const battleEventNames = Object.freeze({ - BATTLE_BEGIN: "battleStart", - TURN_END: "turnEnd", - TURN_BEGIN: "turnBegin", - BEFORE_MOVE: "beforeMove", - BEFORE_MOVE_EXECUTE: "beforeMoveExecute", - AFTER_MOVE: "afterMove", - BEFORE_DAMAGE_DEALT: "beforeDamageDealt", - AFTER_DAMAGE_DEALT: "afterDamageDealt", - BEFORE_DAMAGE_TAKEN: "beforeDamageTaken", - AFTER_DAMAGE_TAKEN: "afterDamageTaken", - BEFORE_CR_GAINED: "beforeCRGained", - AFTER_CR_GAINED: "afterCRGained", - BEFORE_EFFECT_ADD: "beforeEffectAdd", - AFTER_EFFECT_ADD: "afterEffectAdd", - BEFORE_EFFECT_REMOVE: "beforeEffectRemove", - AFTER_EFFECT_REMOVE: "afterEffectRemove", - BEFORE_STATUS_APPLY: "beforeStatusApply", - AFTER_STATUS_APPLY: "afterStatusApply", - BEFORE_CAUSE_FAINT: "beforeCauseFaint", - BEFORE_FAINT: "beforeFaint", - AFTER_FAINT: "afterFaint", - CALCULATE_TYPE_MULTIPLIER: "calculateTypeMultiplier", - CALCULATE_MISS: "calculateMiss", - GET_ELIGIBLE_TARGETS: "getEligibleTargets", -}); +const { battleEventEnum } = require("../enums/battleEnums"); /** @typedef {types.Enum} DamageTypeEnum */ const damageTypes = Object.freeze({ @@ -1008,7 +981,7 @@ const effectConfig = Object.freeze({ }; battle.addToLog(`${target.name} became confused!`); const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_MOVE, + battleEventEnum.BEFORE_MOVE, listener ); return { @@ -1070,7 +1043,7 @@ const effectConfig = Object.freeze({ }; battle.addToLog(`${target.name} assumes a counter stance!`); const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -1132,7 +1105,7 @@ const effectConfig = Object.freeze({ }; battle.addToLog(`${target.name} puts up a reflective field!`); const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -1215,7 +1188,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -1258,7 +1231,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -1332,7 +1305,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -1382,7 +1355,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -1437,7 +1410,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -1473,7 +1446,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_STATUS_APPLY, + battleEventEnum.BEFORE_STATUS_APPLY, listener ); return { @@ -1534,7 +1507,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); battle.addToLog(`${target.name} is regenerating health!`); @@ -1575,7 +1548,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); battle.addToLog(`${target.name} is taking damage from a DoT effect!`); @@ -1623,7 +1596,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); battle.addToLog(`${target.name} is affected by a Leech Seed!`); @@ -1660,7 +1633,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); battle.addToLog(`${target.name} is taking extra turns!!`); @@ -1725,11 +1698,11 @@ const effectConfig = Object.freeze({ }; const crListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_CR_GAINED, + battleEventEnum.AFTER_CR_GAINED, listener ); const buffListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_EFFECT_ADD, + battleEventEnum.AFTER_EFFECT_ADD, listener ); return { @@ -1792,11 +1765,11 @@ const effectConfig = Object.freeze({ }; const crListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_CR_GAINED, + battleEventEnum.AFTER_CR_GAINED, crListener ); const beforeMoveListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_MOVE, + battleEventEnum.BEFORE_MOVE, beforeMoveListener ); return { @@ -1968,7 +1941,7 @@ const effectConfig = Object.freeze({ }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.GET_ELIGIBLE_TARGETS, + battleEventEnum.GET_ELIGIBLE_TARGETS, listener ); return { @@ -2013,7 +1986,7 @@ const effectConfig = Object.freeze({ }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -2356,7 +2329,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_FAINT, + battleEventEnum.AFTER_FAINT, listener ); return { @@ -2398,7 +2371,7 @@ const effectConfig = Object.freeze({ }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_FAINT, + battleEventEnum.AFTER_FAINT, listener ); return { @@ -12071,7 +12044,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -12126,7 +12099,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); return { @@ -12181,7 +12154,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -12225,7 +12198,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_STATUS_APPLY, + battleEventEnum.BEFORE_STATUS_APPLY, listener ); return { @@ -12281,7 +12254,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -12338,7 +12311,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -12395,7 +12368,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -12462,7 +12435,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_STATUS_APPLY, + battleEventEnum.BEFORE_STATUS_APPLY, listener ); return { @@ -12514,7 +12487,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -12565,7 +12538,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); @@ -12607,7 +12580,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); return { @@ -12676,7 +12649,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -12721,7 +12694,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); @@ -12779,7 +12752,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); @@ -12822,7 +12795,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.CALCULATE_TYPE_MULTIPLIER, + battleEventEnum.CALCULATE_TYPE_MULTIPLIER, listener ); return { @@ -12885,7 +12858,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -12932,7 +12905,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_STATUS_APPLY, + battleEventEnum.AFTER_STATUS_APPLY, listener ); return { @@ -12978,7 +12951,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); return { @@ -13034,7 +13007,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_MOVE, + battleEventEnum.AFTER_MOVE, listener ); return { @@ -13113,11 +13086,11 @@ const abilityConfig = { }, }; const listenerId1 = battle.eventHandler.registerListener( - battleEventNames.GET_ELIGIBLE_TARGETS, + battleEventEnum.GET_ELIGIBLE_TARGETS, listener1 ); const listenerId2 = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener2 ); return { @@ -13191,7 +13164,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -13257,7 +13230,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -13298,7 +13271,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); return { @@ -13335,7 +13308,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -13412,11 +13385,11 @@ const abilityConfig = { // add listener to after damage dealt and after damage taken const dealtListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_DEALT, + battleEventEnum.AFTER_DAMAGE_DEALT, listener ); const takenListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); @@ -13507,11 +13480,11 @@ const abilityConfig = { // add listener to after damage dealt and after damage taken const dealtListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_DEALT, + battleEventEnum.AFTER_DAMAGE_DEALT, listener ); const takenListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); @@ -13564,7 +13537,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); @@ -13621,7 +13594,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); @@ -13668,7 +13641,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); @@ -13724,7 +13697,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_EFFECT_REMOVE, + battleEventEnum.AFTER_EFFECT_REMOVE, listener ); @@ -13770,7 +13743,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); @@ -13820,7 +13793,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); @@ -13873,7 +13846,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); @@ -13912,7 +13885,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_STATUS_APPLY, + battleEventEnum.AFTER_STATUS_APPLY, listener ); @@ -13961,7 +13934,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14009,7 +13982,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14057,7 +14030,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14105,7 +14078,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14142,7 +14115,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -14186,7 +14159,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_DEALT, + battleEventEnum.AFTER_DAMAGE_DEALT, listener ); return { @@ -14228,7 +14201,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -14283,7 +14256,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14327,7 +14300,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14370,7 +14343,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14408,7 +14381,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.CALCULATE_MISS, + battleEventEnum.CALCULATE_MISS, listener ); return { @@ -14455,7 +14428,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14512,7 +14485,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -14567,7 +14540,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -14648,11 +14621,11 @@ const abilityConfig = { }, }; const listenerId1 = battle.eventHandler.registerListener( - battleEventNames.GET_ELIGIBLE_TARGETS, + battleEventEnum.GET_ELIGIBLE_TARGETS, listener1 ); const listenerId2 = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener2 ); return { @@ -14705,7 +14678,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); return { @@ -14768,15 +14741,15 @@ const abilityConfig = { battle.addToLog(`${target.name}'s Sheer Force is increasing its damage!`); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, damageListener ); const effectListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, effectAndStatusListener ); const statusListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_STATUS_APPLY, + battleEventEnum.BEFORE_STATUS_APPLY, effectAndStatusListener ); return { @@ -14830,7 +14803,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -14879,7 +14852,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -14923,7 +14896,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -14976,7 +14949,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, listener ); return { @@ -15018,7 +14991,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); return { @@ -15062,7 +15035,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); return { @@ -15114,7 +15087,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_FAINT, + battleEventEnum.AFTER_FAINT, listener ); return { @@ -15175,7 +15148,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_MOVE_EXECUTE, + battleEventEnum.BEFORE_MOVE_EXECUTE, listener ); return { @@ -15224,7 +15197,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_MOVE, + battleEventEnum.AFTER_MOVE, listener ); return { @@ -15273,7 +15246,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_EFFECT_ADD, + battleEventEnum.AFTER_EFFECT_ADD, listener ); return { @@ -15316,7 +15289,7 @@ const abilityConfig = { }, }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener ); return { @@ -15395,15 +15368,15 @@ const abilityConfig = { }; const turnListenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, turnListener ); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); const faintListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, faintListener ); return { @@ -15502,15 +15475,15 @@ const abilityConfig = { }; const afterDamageListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, afterDamageListener ); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); const faintListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, faintListener ); return { @@ -15599,15 +15572,15 @@ const abilityConfig = { }; const turnListenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, turnListener ); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); const faintListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, faintListener ); return { @@ -15690,15 +15663,15 @@ const abilityConfig = { }; const afterFaintListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_FAINT, + battleEventEnum.AFTER_FAINT, afterFaintListener ); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); const faintListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, faintListener ); return { @@ -15759,7 +15732,7 @@ const abilityConfig = { // add listener to after cr gain const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_CR_GAINED, + battleEventEnum.AFTER_CR_GAINED, listener ); @@ -15818,7 +15791,7 @@ const abilityConfig = { // add listener to after turn end const listenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_END, + battleEventEnum.TURN_END, listener ); @@ -15876,7 +15849,7 @@ const abilityConfig = { // add listener to after faint const listenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_FAINT, + battleEventEnum.AFTER_FAINT, listener ); @@ -15930,7 +15903,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, listener ); return { @@ -15976,7 +15949,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -16009,7 +15982,7 @@ const abilityConfig = { }; const listenerId = battle.eventHandler.registerListener( - battleEventNames.BATTLE_BEGIN, + battleEventEnum.BATTLE_BEGIN, listener ); return { @@ -16094,11 +16067,11 @@ const abilityConfig = { }; const listenerId1 = battle.eventHandler.registerListener( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, listener ); const listenerId2 = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, listener2 ); return { @@ -16257,15 +16230,15 @@ const abilityConfig = { }; const afterDamageListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, afterDamageListener ); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); const faintListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, faintListener ); return { @@ -16385,15 +16358,15 @@ const abilityConfig = { }; const afterDamageListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, afterDamageListener ); const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); const faintListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, faintListener ); return { @@ -16452,7 +16425,7 @@ const abilityConfig = { }; const debuffListenerId = battle.eventHandler.registerListener( - battleEventNames.AFTER_EFFECT_ADD, + battleEventEnum.AFTER_EFFECT_ADD, debuffListener ); return { @@ -16489,7 +16462,7 @@ const abilityConfig = { }; const damageListenerId = battle.eventHandler.registerListener( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, damageListener ); battle.addToLog( @@ -16552,7 +16525,7 @@ const abilityConfig = { }; const turnListenerId = battle.eventHandler.registerListener( - battleEventNames.TURN_BEGIN, + battleEventEnum.TURN_BEGIN, turnListener ); return { @@ -16573,7 +16546,6 @@ const abilityConfig = { module.exports = { typeAdvantages, - battleEventNames, moveConfig, moveExecutes, moveTiers, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index a826602c..414c7903 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -24,7 +24,36 @@ const moveIdEnum = Object.freeze({ VINE_WHIP: "m22", }); +/** @typedef {types.Enum} BattleEventEnum */ +const battleEventEnum = Object.freeze({ + BATTLE_BEGIN: "battleStart", + TURN_END: "turnEnd", + TURN_BEGIN: "turnBegin", + BEFORE_MOVE: "beforeMove", + BEFORE_MOVE_EXECUTE: "beforeMoveExecute", + AFTER_MOVE: "afterMove", + BEFORE_DAMAGE_DEALT: "beforeDamageDealt", + AFTER_DAMAGE_DEALT: "afterDamageDealt", + BEFORE_DAMAGE_TAKEN: "beforeDamageTaken", + AFTER_DAMAGE_TAKEN: "afterDamageTaken", + BEFORE_CR_GAINED: "beforeCRGained", + AFTER_CR_GAINED: "afterCRGained", + BEFORE_EFFECT_ADD: "beforeEffectAdd", + AFTER_EFFECT_ADD: "afterEffectAdd", + BEFORE_EFFECT_REMOVE: "beforeEffectRemove", + AFTER_EFFECT_REMOVE: "afterEffectRemove", + BEFORE_STATUS_APPLY: "beforeStatusApply", + AFTER_STATUS_APPLY: "afterStatusApply", + BEFORE_CAUSE_FAINT: "beforeCauseFaint", + BEFORE_FAINT: "beforeFaint", + AFTER_FAINT: "afterFaint", + CALCULATE_TYPE_MULTIPLIER: "calculateTypeMultiplier", + CALCULATE_MISS: "calculateMiss", + GET_ELIGIBLE_TARGETS: "getEligibleTargets", +}); + module.exports = { moveIdEnum, effectIdEnum, + battleEventEnum, }; diff --git a/src/enums/types.js b/src/enums/types.js new file mode 100644 index 00000000..5742caab --- /dev/null +++ b/src/enums/types.js @@ -0,0 +1,4 @@ +/** + * @typedef {import("./battleEnums").MoveIdEnum} MoveIdEnum + * @typedef {import("./battleEnums").EffectIdEnum} EffectIdEnum + */ diff --git a/src/services/battle.js b/src/services/battle.js index aac15b3b..9f2ce386 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -15,7 +15,6 @@ const { v4: uuidv4 } = require("uuid"); const { getOrSetDefault, formatMoney } = require("../utils/utils"); const { pokemonConfig, types } = require("../config/pokemonConfig"); const { - battleEventNames, targetTypes, targetPatterns, targetPositions, @@ -26,6 +25,7 @@ const { typeAdvantages, weatherConditions, } = require("../config/battleConfig"); +const { battleEventEnum } = require("../enums/battleEnums"); const { buildBattleEmbed, buildPveListEmbed, @@ -708,7 +708,7 @@ class Pokemon { }; // trigger before move events - this.battle.eventHandler.emit(battleEventNames.BEFORE_MOVE, eventArgs); + this.battle.eventHandler.emit(battleEventEnum.BEFORE_MOVE, eventArgs); canUseMove = eventArgs.canUseMove; } @@ -756,7 +756,7 @@ class Pokemon { moveId, }; this.battle.eventHandler.emit( - battleEventNames.BEFORE_MOVE_EXECUTE, + battleEventEnum.BEFORE_MOVE_EXECUTE, executeEventArgs ); @@ -778,7 +778,7 @@ class Pokemon { missedTargets, moveId, }; - this.battle.eventHandler.emit(battleEventNames.AFTER_MOVE, eventArgs); + this.battle.eventHandler.emit(battleEventEnum.AFTER_MOVE, eventArgs); } // end turn @@ -889,7 +889,7 @@ class Pokemon { multiplier: mult, }; this.battle.eventHandler.emit( - battleEventNames.CALCULATE_TYPE_MULTIPLIER, + battleEventEnum.CALCULATE_TYPE_MULTIPLIER, eventArgs ); @@ -1085,7 +1085,7 @@ class Pokemon { source: this, }; this.battle.eventHandler.emit( - battleEventNames.CALCULATE_MISS, + battleEventEnum.CALCULATE_MISS, calculateMissArgs ); @@ -1188,7 +1188,7 @@ class Pokemon { }; this.battle.eventHandler.emit( - battleEventNames.BEFORE_DAMAGE_DEALT, + battleEventEnum.BEFORE_DAMAGE_DEALT, eventArgs ); damage = eventArgs.damage; @@ -1203,7 +1203,7 @@ class Pokemon { damageInfo, }; this.battle.eventHandler.emit( - battleEventNames.AFTER_DAMAGE_DEALT, + battleEventEnum.AFTER_DAMAGE_DEALT, afterDamageArgs ); } @@ -1253,7 +1253,7 @@ class Pokemon { }; this.battle.eventHandler.emit( - battleEventNames.BEFORE_DAMAGE_TAKEN, + battleEventEnum.BEFORE_DAMAGE_TAKEN, eventArgs ); damage = Math.min(eventArgs.damage, eventArgs.maxDamage); @@ -1278,7 +1278,7 @@ class Pokemon { damageInfo, }; this.battle.eventHandler.emit( - battleEventNames.AFTER_DAMAGE_TAKEN, + battleEventEnum.AFTER_DAMAGE_TAKEN, afterDamageArgs ); } @@ -1297,7 +1297,7 @@ class Pokemon { canFaint: true, }; this.battle.eventHandler.emit( - battleEventNames.BEFORE_CAUSE_FAINT, + battleEventEnum.BEFORE_CAUSE_FAINT, beforeCauseFaintArgs ); if (!beforeCauseFaintArgs.canFaint) { @@ -1320,7 +1320,7 @@ class Pokemon { target: this, source, }; - this.battle.eventHandler.emit(battleEventNames.AFTER_FAINT, afterFaintArgs); + this.battle.eventHandler.emit(battleEventEnum.AFTER_FAINT, afterFaintArgs); } // eslint-disable-next-line no-unused-vars @@ -1399,7 +1399,7 @@ class Pokemon { }; if (triggerEvents) { this.battle.eventHandler.emit( - battleEventNames.BEFORE_CR_GAINED, + battleEventEnum.BEFORE_CR_GAINED, beforeBoostArgs ); } @@ -1422,10 +1422,7 @@ class Pokemon { source, combatReadinessGained: amount, }; - this.battle.eventHandler.emit( - battleEventNames.AFTER_CR_GAINED, - eventArgs - ); + this.battle.eventHandler.emit(battleEventEnum.AFTER_CR_GAINED, eventArgs); } return combatReadinessGained; @@ -1496,7 +1493,7 @@ class Pokemon { canAdd: true, }; this.battle.eventHandler.emit( - battleEventNames.BEFORE_EFFECT_ADD, + battleEventEnum.BEFORE_EFFECT_ADD, beforeAddArgs ); if (!beforeAddArgs.canAdd) { @@ -1542,11 +1539,11 @@ class Pokemon { source, effectId, duration, - initialArgs: initialArgs, + initialArgs, args: this.effectIds[effectId].args, }; this.battle.eventHandler.emit( - battleEventNames.AFTER_EFFECT_ADD, + battleEventEnum.AFTER_EFFECT_ADD, afterAddArgs ); } @@ -1630,7 +1627,7 @@ class Pokemon { args: this.effectIds[effectId].args, }; this.battle.eventHandler.emit( - battleEventNames.AFTER_EFFECT_REMOVE, + battleEventEnum.AFTER_EFFECT_REMOVE, afterRemoveArgs ); } @@ -1659,7 +1656,7 @@ class Pokemon { canApply: true, }; this.battle.eventHandler.emit( - battleEventNames.BEFORE_STATUS_APPLY, + battleEventEnum.BEFORE_STATUS_APPLY, beforeApplyArgs ); if (!beforeApplyArgs.canApply) { @@ -1779,7 +1776,7 @@ class Pokemon { statusId, }; this.battle.eventHandler.emit( - battleEventNames.AFTER_STATUS_APPLY, + battleEventEnum.AFTER_STATUS_APPLY, afterStatusArgs ); } @@ -2247,7 +2244,7 @@ class Battle { pokemon.applyAbility(); }); - this.eventHandler.emit(battleEventNames.BATTLE_BEGIN, { + this.eventHandler.emit(battleEventEnum.BATTLE_BEGIN, { battle: this, }); @@ -2265,7 +2262,7 @@ class Battle { } // begin turn - this.eventHandler.emit(battleEventNames.TURN_BEGIN); + this.eventHandler.emit(battleEventEnum.TURN_BEGIN); // log const userIsNpc = this.isNpc(this.activePokemon.userId); @@ -2285,7 +2282,7 @@ class Battle { nextTurn() { // end turn logic - this.eventHandler.emit(battleEventNames.TURN_END); + this.eventHandler.emit(battleEventEnum.TURN_END); // tick status effects if (!this.activePokemon.isFainted) { @@ -2562,7 +2559,7 @@ class Battle { eligibleTargets, shouldReturn: false, }; - this.eventHandler.emit(battleEventNames.GET_ELIGIBLE_TARGETS, eventArgs); + this.eventHandler.emit(battleEventEnum.GET_ELIGIBLE_TARGETS, eventArgs); if (eventArgs.shouldReturn) { return eligibleTargets; } From 6a3af139fe3fee0c37189540c2e3c2ebd3d9ba2d Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 18:54:17 -0700 Subject: [PATCH 33/38] begin event listener migration --- src/battle/engine/events.js | 23 ++++++++++++----------- src/battle/types.js | 7 ++++++- src/enums/battleEnums.js | 31 +++++++++++++++++++++++++++++++ src/enums/types.js | 6 ++++++ src/services/battle.js | 13 +++++++++++++ 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/battle/engine/events.js b/src/battle/engine/events.js index 495dcf0f..9c8c1595 100644 --- a/src/battle/engine/events.js +++ b/src/battle/engine/events.js @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ const { v4: uuidv4 } = require("uuid"); const { getOrSetDefault } = require("../../utils/utils"); @@ -19,12 +20,12 @@ class BattleEventHandler { getOrSetDefault(this.eventNames, eventName, new Set()).add(listenerId); this.eventListeners[listenerId] = listener; // add listenerId and eventName to listener.initialargs - // eslint-disable-next-line no-param-reassign listener.initialArgs = { listenerId, eventName, ...listener.initialArgs, }; + listener.eventName = eventName; return listenerId; } @@ -32,7 +33,7 @@ class BattleEventHandler { unregisterListener(listenerId) { const listener = this.eventListeners[listenerId]; if (listener) { - const { eventName } = listener.initialArgs; + const { eventName } = listener.eventName; const listenerIds = this.eventNames[eventName]; if (listenerIds) { listenerIds.delete(listenerId); @@ -47,21 +48,21 @@ class BattleEventHandler { for (const listenerId of listenerIds) { const listener = this.eventListeners[listenerId]; if (listener) { - listener.execute(listener.initialArgs, args); + // migration harness + if (listener.isNewListener) { + listener.execute({ + ...(args || {}), + eventName, + }); + } else { + listener.execute(listener.initialArgs, args); + } } } } } } -/** - * @param {object} param0 - * @param {string} param0.eventName - * @param {Battle} param0.battle - * @param param0.callback - */ -const registerListenerFunction = ({ eventName, battle, callback }) => {}; - module.exports = { BattleEventHandler, }; diff --git a/src/battle/types.js b/src/battle/types.js index 0eb3c817..2904d12c 100644 --- a/src/battle/types.js +++ b/src/battle/types.js @@ -4,7 +4,6 @@ */ /** - * @typedef {import("../config/battleConfig").BattleEventEnum} BattleEventEnum * @typedef {import("../config/battleConfig").DamageTypeEnum} DamageTypeEnum * @typedef {import("../config/battleConfig").MoveTierEnum} MoveTierEnum * @typedef {import("../config/battleConfig").StatusConditionEnum} StatusConditionEnum @@ -83,3 +82,9 @@ * @param {Array} param0.allTargets * @param {Array} param0.missedTargets */ + +/** + * @template {BattleEventEnum} K + * @callback BattleEventListenerCallback + * @param {BattleEventArgs} args + */ diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index 414c7903..acad10b5 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-unused-vars const types = require("../../types"); /** @@ -52,6 +53,36 @@ const battleEventEnum = Object.freeze({ GET_ELIGIBLE_TARGETS: "getEligibleTargets", }); +/** + * @template {BattleEventEnum} K + * @typedef {{ + * [battleEventEnum.BATTLE_BEGIN]: {}, + * [battleEventEnum.TURN_END]: {}, + * [battleEventEnum.TURN_BEGIN]: {}, + * [battleEventEnum.BEFORE_MOVE]: {canUseMove: boolean, source: BattlePokemon, primaryTarget: BattlePokemon, moveId: MoveIdEnum}, + * [battleEventEnum.BEFORE_MOVE_EXECUTE]: any, + * [battleEventEnum.AFTER_MOVE]: any, + * [battleEventEnum.BEFORE_DAMAGE_DEALT]: any, + * [battleEventEnum.AFTER_DAMAGE_DEALT]: any, + * [battleEventEnum.BEFORE_DAMAGE_TAKEN]: any, + * [battleEventEnum.AFTER_DAMAGE_TAKEN]: any, + * [battleEventEnum.BEFORE_CR_GAINED]: any, + * [battleEventEnum.AFTER_CR_GAINED]: any, + * [battleEventEnum.BEFORE_EFFECT_ADD]: any, + * [battleEventEnum.AFTER_EFFECT_ADD]: any, + * [battleEventEnum.BEFORE_EFFECT_REMOVE]: any, + * [battleEventEnum.AFTER_EFFECT_REMOVE]: any, + * [battleEventEnum.BEFORE_STATUS_APPLY]: any, + * [battleEventEnum.AFTER_STATUS_APPLY]: any, + * [battleEventEnum.BEFORE_CAUSE_FAINT]: any, + * [battleEventEnum.BEFORE_FAINT]: any, + * [battleEventEnum.AFTER_FAINT]: any, + * [battleEventEnum.CALCULATE_TYPE_MULTIPLIER]: any, + * [battleEventEnum.CALCULATE_MISS]: any, + * [battleEventEnum.GET_ELIGIBLE_TARGETS]: any, + * }[K] & { eventName: K }} BattleEventArgs + */ + module.exports = { moveIdEnum, effectIdEnum, diff --git a/src/enums/types.js b/src/enums/types.js index 5742caab..f986ac03 100644 --- a/src/enums/types.js +++ b/src/enums/types.js @@ -1,4 +1,10 @@ /** * @typedef {import("./battleEnums").MoveIdEnum} MoveIdEnum * @typedef {import("./battleEnums").EffectIdEnum} EffectIdEnum + * @typedef {import("./battleEnums").BattleEventEnum} BattleEventEnum + */ + +/** + * @template {BattleEventEnum} K + * @typedef {import("./battleEnums").BattleEventArgs} BattleEventArgs */ diff --git a/src/services/battle.js b/src/services/battle.js index 9f2ce386..95f80af2 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -2722,6 +2722,19 @@ class Battle { return true; } + /** + * @template {BattleEventEnum} K + * @param {object} param0 + * @param {K} param0.eventName + * @param { BattleEventListenerCallback } param0.callback + */ + registerListenerFunction({ eventName, callback }) { + return this.eventHandler.registerListener(eventName, { + isNewListener: true, + execute: callback, + }); + } + clearLog() { this.log = []; } From d930c8d1e0fa6b8b9e93cef28ad947d61478bf01 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 19:38:09 -0700 Subject: [PATCH 34/38] conditional event improvements --- src/battle/engine/eventConditions.js | 10 ++++ src/battle/engine/events.js | 34 ++++++++----- src/battle/types.js | 7 +++ src/config/battleConfig.js | 73 +++++++++++++++++----------- src/enums/battleEnums.js | 2 +- src/services/battle.js | 19 +++++++- 6 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 src/battle/engine/eventConditions.js diff --git a/src/battle/engine/eventConditions.js b/src/battle/engine/eventConditions.js new file mode 100644 index 00000000..a1c81cb5 --- /dev/null +++ b/src/battle/engine/eventConditions.js @@ -0,0 +1,10 @@ +/** + * @param {Battle} battle + * @param {BattlePokemon} pokemon + */ +const getIsActivePokemonCallback = (battle, pokemon) => () => + battle.activePokemon === pokemon; + +module.exports = { + getIsActivePokemonCallback, +}; diff --git a/src/battle/engine/events.js b/src/battle/engine/events.js index 9c8c1595..91ebd25a 100644 --- a/src/battle/engine/events.js +++ b/src/battle/engine/events.js @@ -44,20 +44,28 @@ class BattleEventHandler { emit(eventName, args) { const listenerIds = this.eventNames[eventName]; - if (listenerIds) { - for (const listenerId of listenerIds) { - const listener = this.eventListeners[listenerId]; - if (listener) { - // migration harness - if (listener.isNewListener) { - listener.execute({ - ...(args || {}), - eventName, - }); - } else { - listener.execute(listener.initialArgs, args); - } + if (!listenerIds) { + return; + } + for (const listenerId of listenerIds) { + const listener = this.eventListeners[listenerId]; + if (!listener) { + continue; + } + const fullArgs = { + ...(args || {}), + eventName, + }; + // migration harness + if (listener.isNewListener) { + if ( + !listener.conditionCallback || + listener.conditionCallback(fullArgs) + ) { + listener.execute(fullArgs); } + } else { + listener.execute(listener.initialArgs, args); } } } diff --git a/src/battle/types.js b/src/battle/types.js index 2904d12c..0c016994 100644 --- a/src/battle/types.js +++ b/src/battle/types.js @@ -88,3 +88,10 @@ * @callback BattleEventListenerCallback * @param {BattleEventArgs} args */ + +/** + * @template {BattleEventEnum} K + * @callback BattleEventListenerConditionCallback + * @param {BattleEventArgs} args + * @returns {boolean} + */ diff --git a/src/config/battleConfig.js b/src/config/battleConfig.js index 2d49a1cb..dfe1c706 100644 --- a/src/config/battleConfig.js +++ b/src/config/battleConfig.js @@ -12,6 +12,9 @@ const { } = require("../battle/data/moveService"); const { getEffect } = require("../battle/data/effectRegistry"); const { battleEventEnum } = require("../enums/battleEnums"); +const { + getIsActivePokemonCallback, +} = require("../battle/engine/eventConditions"); /** @typedef {types.Enum} DamageTypeEnum */ const damageTypes = Object.freeze({ @@ -12022,7 +12025,31 @@ const moveExecutes = { }, }; -const abilityConfig = { +/** + * @callback LegacyAbilityAdd + * @param {Battle} battle + * @param {BattlePokemon} source + * @param {BattlePokemon} target + */ + +/** + * @callback LegacyAbilityRemove + * @param {Battle} battle + * @param {BattlePokemon} source + * @param {BattlePokemon} target + */ + +/** @typedef {types.Keys} LegacyAbilityIdEnum */ + +/** + * @type {Record} + */ +const abilityConfig = Object.freeze({ 2: { name: "Drizzle", description: "At the start of battle, stir up a rainy storm.", @@ -14969,33 +14996,21 @@ const abilityConfig = { name: "Regenerator", description: "After the user's turn, heal 15% of its max HP.", abilityAdd(battle, _source, target) { - const listener = { - initialArgs: { - pokemon: target, - }, - execute(initialArgs, _args) { - const { battle } = initialArgs.pokemon; - const { activePokemon } = battle; - if (activePokemon !== initialArgs.pokemon) { - return; - } - - // heal 15% of max hp - battle.addToLog( - `${activePokemon.name}'s Regenerator restores its health!` - ); - const healAmount = Math.floor(activePokemon.maxHp * 0.15); - activePokemon.giveHeal(healAmount, activePokemon, { - type: "regenerator", - }); - }, - }; - const listenerId = battle.eventHandler.registerListener( - battleEventEnum.TURN_END, - listener - ); return { - listenerId, + listenerId: battle.registerListenerFunction({ + eventName: battleEventEnum.TURN_END, + callback: ({ activePokemon }) => { + // heal 15% of max hp + battle.addToLog( + `${activePokemon.name}'s Regenerator restores its health!` + ); + const healAmount = Math.floor(activePokemon.maxHp * 0.15); + activePokemon.giveHeal(healAmount, activePokemon, { + type: "regenerator", + }); + }, + conditionCallback: getIsActivePokemonCallback(battle, target), + }), }; }, abilityRemove(battle, _source, target) { @@ -15004,7 +15019,7 @@ const abilityConfig = { return; } const abilityData = ability.data; - battle.eventHandler.unregisterListener(abilityData.listenerId); + battle.unregisterListener(abilityData.listenerId); }, }, 145: { @@ -16542,7 +16557,7 @@ const abilityConfig = { battle.eventHandler.unregisterListener(abilityData.turnListenerId); }, }, -}; +}); module.exports = { typeAdvantages, diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index acad10b5..a3be3e42 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -57,7 +57,7 @@ const battleEventEnum = Object.freeze({ * @template {BattleEventEnum} K * @typedef {{ * [battleEventEnum.BATTLE_BEGIN]: {}, - * [battleEventEnum.TURN_END]: {}, + * [battleEventEnum.TURN_END]: {activePokemon: BattlePokemon}, * [battleEventEnum.TURN_BEGIN]: {}, * [battleEventEnum.BEFORE_MOVE]: {canUseMove: boolean, source: BattlePokemon, primaryTarget: BattlePokemon, moveId: MoveIdEnum}, * [battleEventEnum.BEFORE_MOVE_EXECUTE]: any, diff --git a/src/services/battle.js b/src/services/battle.js index 95f80af2..1d5370b4 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -2282,7 +2282,9 @@ class Battle { nextTurn() { // end turn logic - this.eventHandler.emit(battleEventEnum.TURN_END); + this.eventHandler.emit(battleEventEnum.TURN_END, { + activePokemon: this.activePokemon, + }); // tick status effects if (!this.activePokemon.isFainted) { @@ -2727,14 +2729,27 @@ class Battle { * @param {object} param0 * @param {K} param0.eventName * @param { BattleEventListenerCallback } param0.callback + * @param { BattleEventListenerConditionCallback= } param0.conditionCallback function that returns true if the event should be executed. If undefined, always execute for event. + * @returns {string} listenerId */ - registerListenerFunction({ eventName, callback }) { + registerListenerFunction({ eventName, callback, conditionCallback }) { return this.eventHandler.registerListener(eventName, { isNewListener: true, execute: callback, + conditionCallback, }); } + /** + * @param {string} listenerId + */ + unregisterListener(listenerId) { + if (!listenerId) { + return; + } + this.eventHandler.unregisterListener(listenerId); + } + clearLog() { this.log = []; } From 0e57f0224277b9e67652e3904d0c4a87966a356f Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Sun, 6 Oct 2024 19:44:12 -0700 Subject: [PATCH 35/38] begin event emit migration --- src/enums/battleEnums.js | 7 ++++++- src/enums/types.js | 5 +++++ src/services/battle.js | 11 ++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index a3be3e42..cac19b09 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -80,7 +80,12 @@ const battleEventEnum = Object.freeze({ * [battleEventEnum.CALCULATE_TYPE_MULTIPLIER]: any, * [battleEventEnum.CALCULATE_MISS]: any, * [battleEventEnum.GET_ELIGIBLE_TARGETS]: any, - * }[K] & { eventName: K }} BattleEventArgs + * }[K]} BattleEventArgsWithoutEventName + */ + +/** + * @template {BattleEventEnum} K + * @typedef {BattleEventArgsWithoutEventName & { eventName: K }} BattleEventArgs */ module.exports = { diff --git a/src/enums/types.js b/src/enums/types.js index f986ac03..c4c5d8b1 100644 --- a/src/enums/types.js +++ b/src/enums/types.js @@ -4,6 +4,11 @@ * @typedef {import("./battleEnums").BattleEventEnum} BattleEventEnum */ +/** + * @template {BattleEventEnum} K + * @typedef {import("./battleEnums").BattleEventArgsWithoutEventName} BattleEventArgsWithoutEventName + */ + /** * @template {BattleEventEnum} K * @typedef {import("./battleEnums").BattleEventArgs} BattleEventArgs diff --git a/src/services/battle.js b/src/services/battle.js index 1d5370b4..1631f562 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -2282,7 +2282,7 @@ class Battle { nextTurn() { // end turn logic - this.eventHandler.emit(battleEventEnum.TURN_END, { + this.emitEvent(battleEventEnum.TURN_END, { activePokemon: this.activePokemon, }); @@ -2750,6 +2750,15 @@ class Battle { this.eventHandler.unregisterListener(listenerId); } + /** + * @template {BattleEventEnum} K + * @param {K} eventName + * @param {BattleEventArgsWithoutEventName} args + */ + emitEvent(eventName, args) { + this.eventHandler.emit(eventName, args || {}); + } + clearLog() { this.log = []; } From d11e95bb2bd6ebc00307d9652c9bc2344a741104 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 11 Oct 2024 20:17:57 -0700 Subject: [PATCH 36/38] create ability registry --- src/battle/data/abilityRegistry.js | 101 +++++++++++++++++++++++++++++ src/enums/battleEnums.js | 13 +++- src/enums/types.js | 1 + src/services/battle.js | 2 +- 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/battle/data/abilityRegistry.js diff --git a/src/battle/data/abilityRegistry.js b/src/battle/data/abilityRegistry.js new file mode 100644 index 00000000..66f7ab38 --- /dev/null +++ b/src/battle/data/abilityRegistry.js @@ -0,0 +1,101 @@ +// eslint-disable-next-line no-unused-vars +const { abilityIdEnum } = require("../../enums/battleEnums"); // TODO: remove after testing +const { logger } = require("../../log"); +// eslint-disable-next-line no-unused-vars +const types = require("../../../types"); + +const allAbilities = {}; + +/** + * @param {Record>} abilities + */ +const registerAbilities = (abilities) => { + let abilitiesRegistered = 0; + Object.entries(abilities).forEach(([abilityId, ability]) => { + allAbilities[abilityId] = ability; + abilitiesRegistered += 1; + }); + logger.info(`Registered ${abilitiesRegistered} abilities.`); +}; + +/** + * @param {Record} abilityConfig + */ +const registerLegacyAbilities = (abilityConfig) => { + let abilitiesRegistered = 0; + Object.entries(abilityConfig).forEach(([abilityId, ability]) => { + if (allAbilities[abilityId]) { + logger.warn( + `Ability ${abilityId} ${allAbilities[abilityId].name} already exists. Continuing...` + ); + return; + } + allAbilities[abilityId] = { + ...ability, + isLegacyAbility: true, + }; + abilitiesRegistered += 1; + }); + logger.info(`Registered ${abilitiesRegistered} legacy abilities.`); +}; + +/** + * @template {EffectIdEnum} K + * @param {K} abilityId + * @returns {K extends keyof RegisteredEffects ? RegisteredEffects[K] : Effect} + */ +const getAbility = (abilityId) => + // @ts-ignore + allAbilities[abilityId]; + +/** + * @param {object} param0 + * @param {Record=} param0.fieldFilter + * @param {Function=} param0.customFilter + * @returns {types.PartialRecord>} + */ +const getAbilities = ({ fieldFilter, customFilter }) => { + if (customFilter) { + return Object.entries(allAbilities).reduce((acc, [abilityId, ability]) => { + if (customFilter(ability)) { + acc[abilityId] = ability; + } + return acc; + }, {}); + } + + if (fieldFilter) { + return Object.entries(allAbilities).reduce((acc, [abilityId, ability]) => { + for (const [field, value] of Object.entries(fieldFilter)) { + if (ability[field] !== value) { + return acc; + } + } + acc[abilityId] = ability; + return acc; + }, {}); + } + + return { + ...allAbilities, + }; +}; +/** + * @param {object} param0 + * @param {Record=} param0.fieldFilter + * @param {Function=} param0.customFilter + * @returns {EffectIdEnum[]} + */ +const getAbilityIds = ({ fieldFilter, customFilter }) => { + const abilities = getAbilities({ fieldFilter, customFilter }); + // @ts-ignore + return Object.keys(abilities); +}; + +module.exports = { + registerAbilities, + registerLegacyAbilities, + getAbility, + getAbilities, + getAbilityIds, +}; diff --git a/src/enums/battleEnums.js b/src/enums/battleEnums.js index cac19b09..fb8e6d56 100644 --- a/src/enums/battleEnums.js +++ b/src/enums/battleEnums.js @@ -6,7 +6,6 @@ const types = require("../../types"); * @typedef {types.Enum} NewEffectIdEnum * @typedef {LegacyEffectIdEnum | NewEffectIdEnum} EffectIdEnum */ - const effectIdEnum = Object.freeze({ TEST_EFFECT: "testEffect", ATK_UP: "atkUp", @@ -18,13 +17,22 @@ const effectIdEnum = Object.freeze({ * @typedef {types.Enum} NewMoveIdEnum * @typedef {LegacyMoveIdEnum | NewMoveIdEnum} MoveIdEnum */ - const moveIdEnum = Object.freeze({ TEST_MOVE: "999", TEST_MOVE2: "998", VINE_WHIP: "m22", }); +/** + * @typedef {import("../config/battleConfig").LegacyAbilityIdEnum} LegacyAbilityIdEnum + * @typedef {types.Enum} NewAbilityIdEnum + * @typedef {LegacyAbilityIdEnum | NewAbilityIdEnum} AbilityIdEnum + */ +const abilityIdEnum = Object.freeze({ + TEST_ABILITY: "testAbility", + REGENERATOR: "144", +}); + /** @typedef {types.Enum} BattleEventEnum */ const battleEventEnum = Object.freeze({ BATTLE_BEGIN: "battleStart", @@ -91,5 +99,6 @@ const battleEventEnum = Object.freeze({ module.exports = { moveIdEnum, effectIdEnum, + abilityIdEnum, battleEventEnum, }; diff --git a/src/enums/types.js b/src/enums/types.js index c4c5d8b1..509a8832 100644 --- a/src/enums/types.js +++ b/src/enums/types.js @@ -1,6 +1,7 @@ /** * @typedef {import("./battleEnums").MoveIdEnum} MoveIdEnum * @typedef {import("./battleEnums").EffectIdEnum} EffectIdEnum + * @typedef {import("./battleEnums").AbilityIdEnum} AbilityIdEnum * @typedef {import("./battleEnums").BattleEventEnum} BattleEventEnum */ diff --git a/src/services/battle.js b/src/services/battle.js index 1631f562..4864e51a 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -2741,7 +2741,7 @@ class Battle { } /** - * @param {string} listenerId + * @param {string?=} listenerId */ unregisterListener(listenerId) { if (!listenerId) { From 23e1860fb6f57fdba6980c72c4fcf7cc64fe74e0 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 11 Oct 2024 21:17:47 -0700 Subject: [PATCH 37/38] ability class and types --- src/battle/data/abilities.js | 59 ++++++++++++++++++++++++++++++++++++ src/battle/types.js | 36 ++++++++++++++++++++++ src/config/battleConfig.js | 34 ++------------------- 3 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 src/battle/data/abilities.js diff --git a/src/battle/data/abilities.js b/src/battle/data/abilities.js new file mode 100644 index 00000000..37c12c4b --- /dev/null +++ b/src/battle/data/abilities.js @@ -0,0 +1,59 @@ +/* eslint-disable no-param-reassign */ +const { abilityIdEnum, battleEventEnum } = require("../../enums/battleEnums"); +const { getIsActivePokemonCallback } = require("../engine/eventConditions"); + +/** + * @template T + */ +class Ability { + /** + * @param {object} param0 + * @param {AbilityIdEnum} param0.id + * @param {string} param0.name + * @param {string} param0.description + * @param {AbilityAddCallback} param0.abilityAdd + * @param {AbilityRemoveCallback} param0.abilityRemove + */ + constructor({ id, name, description, abilityAdd, abilityRemove }) { + this.id = id; + this.name = name; + this.description = description; + this.abilityAdd = abilityAdd; + this.abilityRemove = abilityRemove; + this.isLegacyAbility = false; + } +} + +const abilitiesToRegister = Object.freeze({ + [abilityIdEnum.REGENERATOR]: new Ability({ + id: abilityIdEnum.REGENERATOR, + name: "Regenerator", + description: "After the user's turn, heal 15% of its max HP.", + abilityAdd({ battle, target }) { + return { + listenerId: battle.registerListenerFunction({ + eventName: battleEventEnum.TURN_END, + callback: ({ activePokemon }) => { + // heal 15% of max hp + battle.addToLog( + `${activePokemon.name}'s Regenerator restores its health!` + ); + const healAmount = Math.floor(activePokemon.maxHp * 0.15); + activePokemon.giveHeal(healAmount, activePokemon, { + type: "regenerator", + }); + }, + conditionCallback: getIsActivePokemonCallback(battle, target), + }), + }; + }, + abilityRemove({ battle, properties }) { + battle.unregisterListener(properties.listenerId); + }, + }), +}); + +module.exports = { + Ability, + abilitiesToRegister, +}; diff --git a/src/battle/types.js b/src/battle/types.js index 0c016994..893fc51d 100644 --- a/src/battle/types.js +++ b/src/battle/types.js @@ -16,7 +16,9 @@ /** * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} EffectAddBasicArgs * @typedef {{battle: Battle, target: BattlePokemon}} EffectRemoveBasicArgs + * @typedef {{battle: Battle, source: BattlePokemon, target: BattlePokemon}} AbilityAddBasicArgs * @typedef {typeof import("./data/effects").effectsToRegister} RegisteredEffects + * @typedef {typeof import("./data/abilities").abilitiesToRegister} RegisteredAbilities * @typedef {import("./data/moves").Move} Move */ @@ -46,6 +48,21 @@ * @typedef {K extends keyof RegisteredEffects ? EffectPropertiesType : any} EffectPropertiesTypeFromId */ +/** + * @template T + * @typedef {import("./data/abilities").Ability} Ability + */ + +/** + * @template T + * @typedef {T extends Ability ? U & { abilityId: AbilityIdEnum } : never} AbilityPropertiesType + */ + +/** + * @template {AbilityIdEnum} K + * @typedef {K extends keyof RegisteredAbilities ? AbilityPropertiesType : any} AbilityPropertiesTypeFromId + */ + /** * * @template T @@ -83,6 +100,25 @@ * @param {Array} param0.missedTargets */ +/** + * @template T + * @callback AbilityAddCallback + * @this {Ability} + * @param {AbilityAddBasicArgs} param0 + * @returns {T} + */ + +/** + * @template T + * @callback AbilityRemoveCallback + * @this {Ability} + * @param {object} param0 + * @param {Battle} param0.battle + * @param {BattlePokemon} param0.source + * @param {BattlePokemon} param0.target + * @param {T} param0.properties + */ + /** * @template {BattleEventEnum} K * @callback BattleEventListenerCallback diff --git a/src/config/battleConfig.js b/src/config/battleConfig.js index dfe1c706..c7feac49 100644 --- a/src/config/battleConfig.js +++ b/src/config/battleConfig.js @@ -12039,10 +12039,10 @@ const moveExecutes = { * @param {BattlePokemon} target */ -/** @typedef {types.Keys} LegacyAbilityIdEnum */ +/** @typedef {types.Keys} LegacyAbilityIdEnum */ /** - * @type {Record { - // heal 15% of max hp - battle.addToLog( - `${activePokemon.name}'s Regenerator restores its health!` - ); - const healAmount = Math.floor(activePokemon.maxHp * 0.15); - activePokemon.giveHeal(healAmount, activePokemon, { - type: "regenerator", - }); - }, - conditionCallback: getIsActivePokemonCallback(battle, target), - }), - }; - }, - abilityRemove(battle, _source, target) { - const { ability } = target; - if (!ability || ability.abilityId !== "144" || !ability.data) { - return; - } - const abilityData = ability.data; - battle.unregisterListener(abilityData.listenerId); - }, - }, 145: { name: "Big Pecks", description: "Immune to Def. Down and Greater Def. Down.", From 9eecbe77d57211ab7af9d338f459c38d9844a785 Mon Sep 17 00:00:00 2001 From: Elvis Wei Date: Fri, 11 Oct 2024 21:50:57 -0700 Subject: [PATCH 38/38] ability migration --- src/battle/data/abilities.js | 18 ++++++++++++ src/battle/data/abilityRegistry.js | 13 +++++---- src/battle/data/initialize.js | 8 ++++++ src/battle/types.js | 2 +- src/embeds/pokemonEmbeds.js | 6 ++-- src/services/battle.js | 45 +++++++++++++++++++++++++----- src/utils/pokemonUtils.js | 6 ++-- 7 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/battle/data/abilities.js b/src/battle/data/abilities.js index 37c12c4b..4bf04893 100644 --- a/src/battle/data/abilities.js +++ b/src/battle/data/abilities.js @@ -1,5 +1,6 @@ /* eslint-disable no-param-reassign */ const { abilityIdEnum, battleEventEnum } = require("../../enums/battleEnums"); +const { logger } = require("../../log"); const { getIsActivePokemonCallback } = require("../engine/eventConditions"); /** @@ -22,6 +23,23 @@ class Ability { this.abilityRemove = abilityRemove; this.isLegacyAbility = false; } + + // eslint-disable-next-line jsdoc/require-returns-check + /** + * @param {BattlePokemon} pokemon + * @returns {{ abilityId: AbilityIdEnum, data: T, applied: boolean }=} + */ + getAbilityInstance(pokemon) { + const abilityInstance = pokemon.ability; + if (abilityInstance?.abilityId !== this.id) { + logger.error( + `Ability ${this.id} not found on Pokemon ${pokemon.id} ${pokemon.name}. Real Ability ID: ${abilityInstance?.abilityId}` + ); + return; + } + + return /** @type {any} */ (abilityInstance); + } } const abilitiesToRegister = Object.freeze({ diff --git a/src/battle/data/abilityRegistry.js b/src/battle/data/abilityRegistry.js index 66f7ab38..f76ca90f 100644 --- a/src/battle/data/abilityRegistry.js +++ b/src/battle/data/abilityRegistry.js @@ -7,7 +7,7 @@ const types = require("../../../types"); const allAbilities = {}; /** - * @param {Record>} abilities + * @param {Record>} abilities */ const registerAbilities = (abilities) => { let abilitiesRegistered = 0; @@ -19,7 +19,7 @@ const registerAbilities = (abilities) => { }; /** - * @param {Record} abilityConfig + * @param {Record} abilityConfig */ const registerLegacyAbilities = (abilityConfig) => { let abilitiesRegistered = 0; @@ -40,9 +40,9 @@ const registerLegacyAbilities = (abilityConfig) => { }; /** - * @template {EffectIdEnum} K + * @template {AbilityIdEnum} K * @param {K} abilityId - * @returns {K extends keyof RegisteredEffects ? RegisteredEffects[K] : Effect} + * @returns {K extends keyof RegisteredAbilities ? RegisteredAbilities[K] : Ability?} */ const getAbility = (abilityId) => // @ts-ignore @@ -52,7 +52,7 @@ const getAbility = (abilityId) => * @param {object} param0 * @param {Record=} param0.fieldFilter * @param {Function=} param0.customFilter - * @returns {types.PartialRecord>} + * @returns {types.PartialRecord>} */ const getAbilities = ({ fieldFilter, customFilter }) => { if (customFilter) { @@ -80,11 +80,12 @@ const getAbilities = ({ fieldFilter, customFilter }) => { ...allAbilities, }; }; + /** * @param {object} param0 * @param {Record=} param0.fieldFilter * @param {Function=} param0.customFilter - * @returns {EffectIdEnum[]} + * @returns {AbilityIdEnum[]} */ const getAbilityIds = ({ fieldFilter, customFilter }) => { const abilities = getAbilities({ fieldFilter, customFilter }); diff --git a/src/battle/data/initialize.js b/src/battle/data/initialize.js index b94030e6..c1e5768f 100644 --- a/src/battle/data/initialize.js +++ b/src/battle/data/initialize.js @@ -7,14 +7,22 @@ const { moveConfig, moveExecutes, effectConfig, + abilityConfig, } = require("../../config/battleConfig"); const { movesToRegister } = require("./moves"); +const { + registerAbilities, + registerLegacyAbilities, +} = require("./abilityRegistry"); +const { abilitiesToRegister } = require("./abilities"); const initialize = () => { registerEffects(effectsToRegister); registerLegacyEffects(effectConfig); registerMoves(movesToRegister); registerLegacyMoves(moveConfig, moveExecutes); + registerAbilities(abilitiesToRegister); + registerLegacyAbilities(abilityConfig); }; module.exports = { diff --git a/src/battle/types.js b/src/battle/types.js index 893fc51d..8a2bb1a4 100644 --- a/src/battle/types.js +++ b/src/battle/types.js @@ -55,7 +55,7 @@ /** * @template T - * @typedef {T extends Ability ? U & { abilityId: AbilityIdEnum } : never} AbilityPropertiesType + * @typedef {T extends Ability ? U : never} AbilityPropertiesType */ /** diff --git a/src/embeds/pokemonEmbeds.js b/src/embeds/pokemonEmbeds.js index d8258a1f..d6ee2823 100644 --- a/src/embeds/pokemonEmbeds.js +++ b/src/embeds/pokemonEmbeds.js @@ -15,7 +15,7 @@ const { typeConfig, growthRateConfig, } = require("../config/pokemonConfig"); -const { abilityConfig } = require("../config/battleConfig"); +const { getAbility } = require("../battle/data/abilityRegistry"); const { getMove } = require("../battle/data/moveService"); const { getWhitespace, @@ -380,7 +380,7 @@ const buildPokemonEmbed = ( } // add ability field - const abilityData = abilityConfig[pokemon.abilityId]; + const abilityData = getAbility(pokemon.abilityId); embed.addFields({ name: `Ability: ${getAbilityName(pokemon.abilityId)}`, value: abilityData ? abilityData.description : "Not yet implemented!", @@ -762,7 +762,7 @@ const buildSpeciesDexEmbed = (id, speciesData, tab, ownershipData) => { // display: ability strings const fields = getAbilityOrder(speciesData.abilities).map((abilityId) => { const abilityProbability = speciesData.abilities[abilityId]; - const abilityData = abilityConfig[abilityId]; + const abilityData = getAbility(abilityId); const abilityHeader = `${getAbilityName(abilityId)} (${Math.floor( abilityProbability * 100 )}%)`; diff --git a/src/services/battle.js b/src/services/battle.js index 4864e51a..578155aa 100644 --- a/src/services/battle.js +++ b/src/services/battle.js @@ -21,7 +21,6 @@ const { statusConditions, moveTiers, calculateDamage, - abilityConfig, typeAdvantages, weatherConditions, } = require("../config/battleConfig"); @@ -78,6 +77,7 @@ const { addRewards, getRewardsString } = require("../utils/trainerUtils"); const { getIdFromTowerStage } = require("../utils/battleUtils"); const { getMove, executeMove } = require("../battle/data/moveService"); const { getEffect } = require("../battle/data/effectRegistry"); +const { getAbility } = require("../battle/data/abilityRegistry"); const { BattleEventHandler } = require("../battle/engine/events"); class NPC { @@ -1342,21 +1342,52 @@ class Pokemon { applyAbility() { const { abilityId } = this.ability; - const abilityData = abilityConfig[abilityId]; + const abilityData = getAbility(/** @type {AbilityIdEnum} */ (abilityId)); if (!abilityData || !abilityData.abilityAdd) { + // TODO: not all abilities are implemented + // logger.error(`Ability ${abilityId} does not exist.`); return; } - this.ability.data = abilityData.abilityAdd(this.battle, this, this); + if (!abilityData.isLegacyAbility) { + this.ability.data = abilityData.abilityAdd({ + battle: this.battle, + source: this, + target: this, + }); + } else { + const legacyAbility = /** @type {any} */ (abilityData); + legacyAbility.abilityAdd(this.battle, this, this); + } this.ability.applied = true; } removeAbility() { // remove ability effects - const { abilityId } = this.ability; - const abilityData = abilityConfig[abilityId]; - if (abilityData && abilityData.abilityRemove) { - abilityData.abilityRemove(this.battle, this, this); + const { abilityId, applied } = this.ability; + const abilityData = getAbility(/** @type {AbilityIdEnum} */ (abilityId)); + if (!abilityData || !abilityData.abilityRemove) { + // TODO: not all abilities are implemented + // logger.error(`Ability ${abilityId} does not exist.`); + return; + } + if (!abilityId || !applied) { + logger.error( + `Ability ${abilityId} is not applied to Pokemon ${this.id} ${this.name}.` + ); + return; + } + + if (!abilityData.isLegacyAbility) { + abilityData.abilityRemove({ + battle: this.battle, + source: this, + target: this, + properties: this.ability.data, + }); + } else { + const legacyAbility = /** @type {any} */ (abilityData); + legacyAbility.abilityRemove(this.battle, this, this); } } diff --git a/src/utils/pokemonUtils.js b/src/utils/pokemonUtils.js index 067b71eb..7ad08180 100644 --- a/src/utils/pokemonUtils.js +++ b/src/utils/pokemonUtils.js @@ -12,7 +12,7 @@ const { growthRateConfig, } = require("../config/pokemonConfig"); const { getPBar, getWhitespace } = require("./utils"); -const { abilityConfig } = require("../config/battleConfig"); +const { getAbility } = require("../battle/data/abilityRegistry"); const { equipmentConfig, modifierSlotConfig, @@ -175,8 +175,8 @@ const calculateEffectiveEvasion = (evasion) => { }; const getAbilityName = (abilityId) => { - if (abilityConfig[abilityId]) { - return `#${abilityId} ${abilityConfig[abilityId].name}`; + if (getAbility(abilityId)) { + return `#${abilityId} ${getAbility(abilityId).name}`; } return `#${abilityId}`; };