diff --git a/server/chat-plugins/othermetas.ts b/server/chat-plugins/othermetas.ts index 9784ea9488..a835b1a9c4 100644 --- a/server/chat-plugins/othermetas.ts +++ b/server/chat-plugins/othermetas.ts @@ -1,4 +1,9 @@ -// Other Metas plugin by Spandan +/** + * Other Metagames chat plugin + * Lets users see elements of Pokemon in various Other Metagames. + * Originally by Spandan. + * @author Kris +*/ import {Utils} from '../../lib/utils'; @@ -42,12 +47,12 @@ function getMegaStone(stone: string, mod = 'gen8'): Item | null { export const commands: ChatCommands = { om: 'othermetas', othermetas(target, room, user) { - if (!this.runBroadcast()) return; + this.runBroadcast(); target = toID(target); let buffer = ``; if (target === 'all' && this.broadcasting) { - return this.sendReplyBox(`You cannot broadcast information about all Other Metagames at once.`); + throw new Chat.ErrorMessage(`You cannot broadcast information about all Other Metagames at once.`); } if (!target || target === 'all') { @@ -78,30 +83,28 @@ export const commands: ChatCommands = { mnm: 'mixandmega', mixandmega(target, room, user) { - if (!this.runBroadcast()) return; if (!toID(target) || !target.includes('@')) return this.parse('/help mixandmega'); + this.runBroadcast(); let dex = Dex; const sep = target.split('@'); const stoneName = sep.slice(1).join('@').trim().split(','); const mod = stoneName[1]; - if (mod && toID(mod) in Dex.dexes) dex = Dex.mod(toID(mod)); + if (mod) { + if (toID(mod) in Dex.dexes) { + dex = Dex.mod(toID(mod)); + } else { + throw new Chat.ErrorMessage(`A mod by the name of '${mod.trim()}' does not exist.`); + } + if (dex === Dex.dexes['ssb']) { + throw new Chat.ErrorMessage(`The SSB mod supports custom elements for Mega Stones that have the capability of crashing the server.`); + } + } const stone = getMegaStone(stoneName[0], mod); const species = dex.getSpecies(sep[0]); if (!stone || (dex.gen >= 8 && ['redorb', 'blueorb'].includes(stone.id))) { - return this.errorReply(`Error: Mega Stone not found.`); - } - if (!species.exists) return this.errorReply(`Error: Pokemon not found.`); - const banlist = Dex.getFormat('gen8mixandmega').banlist; - if (banlist.includes(stone.name)) { - this.errorReply(`Warning: ${stone.name} is banned from Mix and Mega.`); - } - // Fake Pokemon and Mega Stones - if (species.isNonstandard === "CAP") { - this.errorReply(`Warning: ${species.name} is not a real Pokemon and is therefore not usable in Mix and Mega.`); - } - if (stone.isNonstandard === "CAP") { - this.errorReply(`Warning: ${stone.name} is a fake mega stone created by the CAP Project and is restricted to the CAP ${stone.megaEvolves}.`); + throw new Chat.ErrorMessage(`Error: Mega Stone not found.`); } + if (!species.exists) throw new Chat.ErrorMessage(`Error: Pok\u00e9mon not found.`); let baseSpecies = dex.getSpecies(stone.megaEvolves); let megaSpecies = dex.getSpecies(stone.megaStone); if (stone.id === 'redorb') { // Orbs do not have 'Item.megaStone' or 'Item.megaEvolves' properties. @@ -174,27 +177,36 @@ export const commands: ChatCommands = { }).join(" |  ") + ''); }, mixandmegahelp: [ - `/mnm @ [, generation] - Shows the Mix and Mega evolved Pokemon's type and stats.`, + `/mnm @ [, generation] - Shows the Mix and Mega evolved Pok\u00e9mon's type and stats.`, ], orb: 'stone', megastone: 'stone', stone(target) { - if (!this.runBroadcast()) return; const sep = target.split(','); let dex = Dex; - if (sep[1] && toID(sep[1]) in Dex.dexes) dex = Dex.mod(toID(sep[1])); + if (sep[1]) { + if (toID(sep[1]) in Dex.dexes) { + dex = Dex.mod(toID(sep[1])); + } else { + throw new Chat.ErrorMessage(`A mod by the name of '${sep[1].trim()}' does not exist.`); + } + if (dex === Dex.dexes['ssb']) { + throw new Chat.ErrorMessage(`The SSB mod supports custom elements for Mega Stones that have the capability of crashing the server.`); + } + } const targetid = toID(sep[0]); if (!targetid) return this.parse('/help stone'); + this.runBroadcast(); const stone = getMegaStone(targetid, sep[1]); if (stone && dex.gen >= 8 && ['redorb', 'blueorb'].includes(stone.id)) { - return this.errorReply("The Orbs do not exist in Gen 8 and later."); + throw new Chat.ErrorMessage("The Orbs do not exist in Gen 8 and later."); } const stones = []; if (!stone) { const species = dex.getSpecies(targetid.replace(/(?:mega[xy]?|primal)$/, '')); - if (!species.exists) return this.errorReply(`Error: Mega Stone not found.`); - if (!species.otherFormes) return this.errorReply(`Error: Mega Evolution not found.`); + if (!species.exists) throw new Chat.ErrorMessage(`Error: Mega Stone not found.`); + if (!species.otherFormes) throw new Chat.ErrorMessage(`Error: Mega Evolution not found.`); for (const poke of species.otherFormes) { if (!/(?:-Primal|-Mega(?:-[XY])?)$/.test(poke)) continue; const megaPoke = dex.getSpecies(poke); @@ -203,26 +215,15 @@ export const commands: ChatCommands = { if (!flag) continue; stones.push(getMegaStone(flag, sep[1])); } - if (!stones.length) return this.errorReply(`Error: Mega Evolution not found.`); + if (!stones.length) throw new Chat.ErrorMessage(`Error: Mega Evolution not found.`); } const toDisplay = (stones.length ? stones : [stone]); - const ruleTable = Dex.getRuleTable(Dex.getFormat('gen8mixandmega')); for (const aStone of toDisplay) { if (!aStone) return; - if (ruleTable.isBanned(`${aStone.name === 'Dragon Ascent' ? 'move' : 'item'}:${aStone.name}`)) { - this.errorReply(`Warning: ${aStone.name} is banned from Mix and Mega.`); - } - if (aStone.name === 'Dragon Ascent') { - this.errorReply(`Warning: Only Pokemon with access to Dragon Ascent can mega evolve with Mega Rayquaza's traits.`); - } - // Fake Mega Stones - if (aStone.isNonstandard === 'CAP') { - this.errorReply(`Warning: ${aStone.name} is a fake mega stone created by the CAP Project and is restricted to the CAP ${aStone.megaEvolves}.`); - } let baseSpecies = dex.getSpecies(aStone.megaEvolves); let megaSpecies = dex.getSpecies(aStone.megaStone); if (dex.gen >= 8 && ['redorb', 'blueorb'].includes(aStone.id)) { - return this.errorReply("The Orbs do not exist in Gen 8 and later."); + throw new Chat.ErrorMessage("The Orbs do not exist in Gen 8 and later."); } if (aStone.id === 'redorb') { // Orbs do not have 'Item.megaStone' or 'Item.megaEvolves' properties. megaSpecies = dex.getSpecies("Groudon-Primal"); @@ -294,13 +295,13 @@ export const commands: ChatCommands = { this.sendReply(`|raw|Gen: ${details["Gen"]} |  Weight: ${details["Weight"]}`); } }, - stonehelp: [`/stone [, generation] - Shows the changes that a mega stone/orb applies to a Pokemon.`], + stonehelp: [`/stone [, generation] - Shows the changes that a mega stone/orb applies to a Pok\u00e9mon.`], 350: '350cup', '350cup'(target, room, user) { - if (!this.runBroadcast()) return; const args = target.split(','); if (!toID(args[0])) return this.parse('/help 350cup'); + this.runBroadcast(); let dex = Dex; if (args[1] && toID(args[1]) in Dex.dexes) { dex = Dex.dexes[toID(args[1])]; @@ -312,7 +313,7 @@ export const commands: ChatCommands = { if (!species.exists || species.gen > dex.gen) { const monName = species.gen > dex.gen ? species.name : args[0].trim(); const additionalReason = species.gen > dex.gen ? ` in Generation ${dex.gen}` : ``; - return this.errorReply(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); + throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); } const bst = species.bst; species.bst = 0; @@ -337,9 +338,9 @@ export const commands: ChatCommands = { ts7: 'tiershift', ts8: 'tiershift', tiershift(target, room, user, connection, cmd) { - if (!this.runBroadcast()) return; const args = target.split(','); if (!toID(args[0])) return this.parse('/help tiershift'); + this.runBroadcast(); const targetGen = parseInt(cmd[cmd.length - 1]); if (targetGen && !args[1]) args[1] = `gen${targetGen}`; let dex = Dex; @@ -353,7 +354,7 @@ export const commands: ChatCommands = { if (!species.exists || species.gen > dex.gen) { const monName = species.gen > dex.gen ? species.name : args[0].trim(); const additionalReason = species.gen > dex.gen ? ` in Generation ${dex.gen}` : ``; - return this.errorReply(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); + throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); } const boosts: {[tier in TierShiftTiers]: number} = { UU: 10, @@ -382,7 +383,7 @@ export const commands: ChatCommands = { }, tiershifthelp: [ `/ts OR /tiershift [, generation] - Shows the base stats that a Pok\u00e9mon would have in Tier Shift.`, - `Alternatively, you can use /ts[gen number] to see a Pokemon's stats in that generation.`, + `Alternatively, you can use /ts[gen number] to see a Pok\u00e9mon's stats in that generation.`, ], scale: 'scalemons', @@ -395,9 +396,9 @@ export const commands: ChatCommands = { scale7: 'scalemons', scale8: 'scalemons', scalemons(target, room, user, connection, cmd) { - if (!this.runBroadcast()) return; const args = target.split(','); if (!args.length || !toID(args[0])) return this.parse(`/help scalemons`); + this.runBroadcast(); const targetGen = parseInt(cmd[cmd.length - 1]); if (targetGen && !args[1]) args[1] = `gen${targetGen}`; let dex = Dex; @@ -411,7 +412,7 @@ export const commands: ChatCommands = { if (!species.exists || species.gen > dex.gen) { const monName = species.gen > dex.gen ? species.name : args[0].trim(); const additionalReason = species.gen > dex.gen ? ` in Generation ${dex.gen}` : ``; - return this.errorReply(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); + throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); } const bstNoHP = species.bst - species.baseStats.hp; const scale = (dex.gen !== 1 ? 600 : 500) - species.baseStats['hp']; @@ -427,7 +428,7 @@ export const commands: ChatCommands = { }, scalemonshelp: [ `/scale OR /scalemons [, gen] - Shows the base stats that a Pok\u00e9mon would have in Scalemons.`, - `Alternatively, you can use /scale[gen number] to see a Pokemon's scaled stats in that generation.`, + `Alternatively, you can use /scale[gen number] to see a Pok\u00e9mon's scaled stats in that generation.`, ], flip: 'flipped', @@ -440,9 +441,9 @@ export const commands: ChatCommands = { flip7: 'flipped', flip8: 'flipped', flipped(target, room, user, connection, cmd) { - if (!this.runBroadcast()) return; const args = target.split(','); if (!args[0]) return this.parse(`/help flipped`); + this.runBroadcast(); const mon = args[0]; let mod = args[1]; const targetGen = parseInt(cmd[cmd.length - 1]); @@ -457,7 +458,7 @@ export const commands: ChatCommands = { if (!species.exists || species.gen > dex.gen) { const monName = species.gen > dex.gen ? species.name : mon.trim(); const additionalReason = species.gen > dex.gen ? ` in Generation ${dex.gen}` : ``; - return this.errorReply(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); + throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); } if (dex.gen === 1) { const flippedStats: {[k: string]: number} = { @@ -481,8 +482,8 @@ export const commands: ChatCommands = { this.sendReply(`|raw|${Chat.getDataPokemonHTML(species, dex.gen)}`); }, flippedhelp: [ - `/flip OR /flipped [, gen] - Shows the base stats that a Pokemon would have in Flipped.`, - `Alternatively, you can use /flip[gen number] to see a Pokemon's stats in that generation.`, + `/flip OR /flipped [, gen] - Shows the base stats that a Pok\u00e9mon would have in Flipped.`, + `Alternatively, you can use /flip[gen number] to see a Pok\u00e9mon's stats in that generation.`, ], ns: 'natureswap', @@ -493,7 +494,6 @@ export const commands: ChatCommands = { ns7: 'natureswap', ns8: 'natureswap', natureswap(target, room, user, connection, cmd) { - if (!this.runBroadcast()) return; const args = target.split(','); const nature = args[0]; const pokemon = args[1]; @@ -507,14 +507,15 @@ export const commands: ChatCommands = { dex = Dex.mod(format.mod); } if (!toID(nature) || !toID(pokemon)) return this.parse(`/help natureswap`); + this.runBroadcast(); const natureObj = dex.getNature(nature); - if (dex.gen < 3) return this.errorReply(`Error: Natures don't exist prior to Generation 3.`); - if (!natureObj.exists) return this.errorReply(`Error: Nature ${nature} not found.`); + if (dex.gen < 3) throw new Chat.ErrorMessage(`Error: Natures don't exist prior to Generation 3.`); + if (!natureObj.exists) throw new Chat.ErrorMessage(`Error: Nature ${nature} not found.`); const species = Utils.deepClone(dex.getSpecies(pokemon)); if (!species.exists || species.gen > dex.gen) { const monName = species.gen > dex.gen ? species.name : args[0].trim(); const additionalReason = species.gen > dex.gen ? ` in Generation ${dex.gen}` : ``; - return this.errorReply(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); + throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${monName}' not found${additionalReason}.`); } if (natureObj.minus && natureObj.plus) { const swap = species.baseStats[natureObj.minus]; @@ -525,8 +526,8 @@ export const commands: ChatCommands = { this.sendReply(`|raw|${Chat.getDataPokemonHTML(species, dex.gen)}`); }, natureswaphelp: [ - `/ns OR /natureswap , [, gen] - Shows the base stats that a Pokemon would have in Nature Swap.`, - `Alternatively, you can use /ns[gen number] to see a Pokemon's stats in that generation.`, + `/ns OR /natureswap , [, gen] - Shows the base stats that a Pok\u00e9mon would have in Nature Swap.`, + `Alternatively, you can use /ns[gen number] to see a Pok\u00e9mon's stats in that generation.`, ], ce: 'crossevolve', @@ -539,11 +540,11 @@ export const commands: ChatCommands = { const species = Dex.getSpecies(pokes[0]); const crossSpecies = Dex.getSpecies(pokes[1]); - if (!species.exists) return this.errorReply(`Error: Pokemon '${pokes[0]}' not found.`); - if (!crossSpecies.exists) return this.errorReply(`Error: Pokemon '${pokes[1]}' not found.`); + if (!species.exists) throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${pokes[0]}' not found.`); + if (!crossSpecies.exists) throw new Chat.ErrorMessage(`Error: Pok\u00e9mon '${pokes[1]}' not found.`); - if (!species.evos.length) return this.errorReply(`Error: ${species.name} does not evolve.`); - if (!crossSpecies.prevo) return this.errorReply(`Error: ${crossSpecies.name} does not have a prevolution.`); + if (!species.evos.length) throw new Chat.ErrorMessage(`Error: ${species.name} does not evolve.`); + if (!crossSpecies.prevo) throw new Chat.ErrorMessage(`Error: ${crossSpecies.name} does not have a prevolution.`); let setStage = 1; let crossStage = 1; @@ -561,7 +562,7 @@ export const commands: ChatCommands = { } } if (setStage + 1 !== crossStage) { - return this.errorReply(`Error: Cross evolution must follow evolutionary stages. (${species.name} is Stage ${setStage} and can only cross evolve to Stage ${setStage + 1})`); + throw new Chat.ErrorMessage(`Error: Cross evolution must follow evolutionary stages. (${species.name} is Stage ${setStage} and can only cross evolve to Stage ${setStage + 1})`); } const mixedSpecies = Utils.deepClone(species); mixedSpecies.abilities = Utils.deepClone(crossSpecies.abilities); @@ -613,7 +614,7 @@ export const commands: ChatCommands = { }).join(" |  ") + ''); }, crossevolvehelp: [ - "/crossevo , - Shows the type and stats for the Cross Evolved Pokemon.", + "/crossevo , - Shows the type and stats for the Cross Evolved Pok\u00e9mon.", ], showevo(target, user, room) { @@ -622,14 +623,10 @@ export const commands: ChatCommands = { if (!targetid) return this.parse('/help showevo'); const evo = Dex.getSpecies(target); if (!evo.exists) { - return this.errorReply(`Error: Pok\u00e9mon ${target} not found.`); + throw new Chat.ErrorMessage(`Error: Pok\u00e9mon ${target} not found.`); } if (!evo.prevo) { - return this.errorReply(`Error: ${evo.name} is not an evolution.`); - } - // Fake Pokemon - if (evo.isNonstandard === 'CAP') { - this.errorReply(`Warning: ${evo.name} is a fake Pok\u00e9mon created by the CAP Project and is restricted to CAP.`); + throw new Chat.ErrorMessage(`Error: ${evo.name} is not an evolution.`); } const prevoSpecies = Dex.getSpecies(evo.prevo); const deltas = Utils.deepClone(evo); diff --git a/server/chat.ts b/server/chat.ts index a7d8b6fcbd..75aa1f23aa 100644 --- a/server/chat.ts +++ b/server/chat.ts @@ -492,9 +492,11 @@ export class CommandContext extends MessageContext { } catch (err) { if (err.name?.endsWith('ErrorMessage')) { this.errorReply(err.message); + this.update(); return false; } if (err.name.endsWith('Interruption')) { + this.update(); return; } Monitor.crashlog(err, 'A chat command', {