diff --git a/chat-plugins/dexsearch.js b/chat-plugins/dexsearch.js index 950e85b3b5..722477b107 100644 --- a/chat-plugins/dexsearch.js +++ b/chat-plugins/dexsearch.js @@ -11,13 +11,153 @@ const RESULTS_MAX_LENGTH = 10; -exports.commands = { - ds: 'dexsearch', - dsearch: 'dexsearch', - dexsearch: function (target, room, user, connection, cmd, message) { - if (!this.canBroadcast()) return; +if (!process.send) { + // This is the parent + Tools.dexsearchProcess = require('child_process').fork('dexsearch.js', {cwd: __dirname}); - if (!target) return this.parse('/help dexsearch'); + let resolvers = {}; + let queryId = 1; + let queryChild = function (query) { + let localQueryId = queryId++; + query.id = localQueryId; + return new Promise((resolve, reject) => { + resolvers[localQueryId] = resolve; + Tools.dexsearchProcess.send(query); + }); + }; + Tools.dexsearchProcess.on("message", message => resolvers[message.id](message.value)); + + exports.commands = { + ds: 'dexsearch', + dsearch: 'dexsearch', + dexsearch: function (target, room, user, connection, cmd, message) { + if (!this.canBroadcast()) return; + + if (!target) return this.parse('/help dexsearch'); + + queryChild({ + target: target, + cmd: 'dexsearch', + canAll: (!this.broadcasting || room.isPersonal), + message: (this.broadcasting ? "" : message), + }).then(message => { + if (message.reply) { + this.sendReplyBox(message.reply); + } else if (message.parse) { + this.parse(message.parse); + } + }); + }, + + dexsearchhelp: ["/dexsearch [parameter], [parameter], [parameter], ... - Searches for Pok\u00e9mon that fulfill the selected criteria", + "Search categories are: type, tier, color, moves, ability, gen, recovery, priority, stat.", + "Valid colors are: green, red, blue, white, brown, yellow, purple, pink, gray and black.", + "Valid tiers are: Uber/OU/BL/UU/BL2/RU/BL3/NU/BL4/PU/NFE/LC/CAP.", + "Types must be followed by ' type', e.g., 'dragon type'.", + "Inequality ranges use the characters '>=' for '≥' and '<=' for '≤', e.g., 'hp <= 95' searches all Pok\u00e9mon with HP less than or equal to 95.", + "Parameters can be excluded through the use of '!', e.g., '!water type' excludes all water types.", + "The parameter 'mega' can be added to search for Mega Evolutions only, and the parameter 'NFE' can be added to search not-fully evolved Pok\u00e9mon only.", + "Parameters separated with '|' will be searched as alternatives for each other, e.g., 'trick | switcheroo' searches for all Pok\u00e9mon that learn either Trick or Switcheroo.", + "The order of the parameters does not matter."], + + rollpokemon: 'randompokemon', + randpoke: 'randompokemon', + randompokemon: function (target, room, user, connection, cmd, message) { + let targets = target.split(","); + let targetsBuffer = []; + let qty; + for (let i = 0; i < targets.length; i++) { + if (!targets[i]) continue; + let num = Number(targets[i]); + if (Number.isInteger(num)) { + if (qty) return this.errorReply("Only specify the number of Pok\u00e9mon once."); + qty = num; + if (qty < 1 || 15 < qty) return this.errorReply("Number of random Pok\u00e9mon must be between 1 and 15."); + targetsBuffer.push("random" + qty); + } else { + targetsBuffer.push(targets[i]); + } + } + if (!qty) targetsBuffer.push("random1"); + + queryChild({ + target: targetsBuffer.join(","), + cmd: 'randpoke', + canAll: (!this.broadcasting || room.isPersonal), + message: (this.broadcasting ? "" : message), + }).then(message => { + if (message.reply) { + this.sendReplyBox(message.reply); + } else if (message.parse) { + this.parse(message.parse); + } + }); + }, + randompokemonhelp: ["/randompokemon - Generates random Pok\u00e9mon based on given search conditions.", + "/randompokemon uses the same parameters as /dexsearch (see '/help ds').", + "Adding a number as a parameter returns that many random Pok\u00e9mon, e.g., '/randpoke 6' returns 6 random Pok\u00e9mon."], + + ms: 'movesearch', + msearch: 'movesearch', + movesearch: function (target, room, user, connection, cmd, message) { + if (!this.canBroadcast()) return; + + if (!target) return this.parse('/help movesearch'); + + queryChild({ + target: target, + cmd: 'movesearch', + canAll: (!this.broadcasting || room.isPersonal), + message: (this.broadcasting ? "" : message), + }).then(message => { + if (message.reply) { + this.sendReplyBox(message.reply); + } else if (message.parse) { + this.parse(message.parse); + } + }); + }, + movesearchhelp: ["/movesearch [parameter], [parameter], [parameter], ... - Searches for moves that fulfill the selected criteria.", + "Search categories are: type, category, flag, status inflicted, type boosted, and numeric range for base power, pp, and accuracy.", + "Types must be followed by ' type', e.g., 'dragon type'.", + "Stat boosts must be preceded with 'boosts ', e.g., 'boosts attack' searches for moves that boost the attack stat.", + "Inequality ranges use the characters '>' and '<' though they behave as '≥' and '≤', e.g., 'bp > 100' searches for all moves equal to and greater than 100 base power.", + "Parameters can be excluded through the use of '!', e.g., !water type' excludes all water type moves.", + "Valid flags are: authentic (bypasses substitute), bite, bullet, contact, defrost, powder, pulse, punch, secondary, snatch, sound", + "If a Pok\u00e9mon is included as a parameter, moves will be searched from it's movepool.", + "The order of the parameters does not matter."], + + isearch: 'itemsearch', + itemsearch: function (target, room, user, connection, cmd, message) { + if (!target) return this.parse('/help itemsearch'); + if (!this.canBroadcast()) return; + + queryChild({ + target: target, + cmd: 'itemsearch', + canAll: (!this.broadcasting || room.isPersonal), + message: (this.broadcasting ? "" : message), + }).then(message => { + if (message.reply) { + this.sendReplyBox(message.reply); + } else if (message.parse) { + this.parse(message.parse); + } + }); + }, + itemsearchhelp: ["/itemsearch [move description] - finds items that match the given key words.", + "Command accepts natural language. (tip: fewer words tend to work better)", + "Searches with \"fling\" in them will find items with the specified Fling behavior.", + "Searches with \"natural gift\" in them will find items with the specified Natural Gift behavior."], + }; +} else { + // This is the child + + global.Tools = require('../tools.js'); + global.toId = Tools.getId; + Tools.includeData(); + + let runDexsearch = function (target, cmd, canAll, message) { let searches = []; let allTiers = {'uber':1, 'ou':1, 'bl':1, 'uu':1, 'bl2':1, 'ru':1, 'bl3':1, 'nu':1, 'bl4':1, 'pu':1, 'nfe':1, 'lc uber':1, 'lc':1, 'cap':1}; let allColours = {'green':1, 'red':1, 'blue':1, 'white':1, 'brown':1, 'yellow':1, 'purple':1, 'pink':1, 'gray':1, 'black':1}; @@ -33,20 +173,19 @@ exports.commands = { if (group[cat] === undefined) continue; if (group[cat][param] === undefined) continue; if (group[cat][param] === isNotSearch) { - this.sendReplyBox("A search cannot both include and exclude '" + param + "'."); + return "A search cannot both include and exclude '" + param + "'."; } else { - this.sendReplyBox("The search included '" + (isNotSearch ? "!" : "") + param + "' more than once."); + return "The search included '" + (isNotSearch ? "!" : "") + param + "' more than once."; } - return false; } - return true; + return false; }; let andGroups = target.split(','); for (let i = 0; i < andGroups.length; i++) { let orGroup = {abilities: {}, tiers: {}, colors: {}, gens: {}, moves: {}, types: {}, stats: {}, skip: false}; let parameters = andGroups[i].split("|"); - if (parameters.length > 3) return this.sendReply("No more than 3 alternatives for each parameter may be used."); + if (parameters.length > 3) return {reply: "No more than 3 alternatives for each parameter may be used."}; for (let j = 0; j < parameters.length; j++) { let isNotSearch = false; target = parameters[j].trim().toLowerCase(); @@ -57,24 +196,27 @@ exports.commands = { let targetAbility = Tools.getAbility(target); if (targetAbility.exists) { - if (!validParameter("abilities", targetAbility, isNotSearch)) return; + let invalid = validParameter("abilities", targetAbility, isNotSearch); + if (invalid) return {reply: invalid}; orGroup.abilities[targetAbility] = !isNotSearch; continue; } if (target in allTiers) { if (target === "cap") { - if (parameters.length > 1) return this.sendReplyBox("The parameter 'cap' cannot have alternative parameters"); + if (parameters.length > 1) return {reply: "The parameter 'cap' cannot have alternative parameters"}; capSearch = !isNotSearch; } - if (!validParameter("tiers", target, isNotSearch)) return; + let invalid = validParameter("tiers", target, isNotSearch); + if (invalid) return {reply: invalid}; orGroup.tiers[target] = !isNotSearch; continue; } if (target in allColours) { target = target.charAt(0).toUpperCase() + target.slice(1); - if (!validParameter("colors", target, isNotSearch)) return; + let invalid = validParameter("colors", target, isNotSearch); + if (invalid) return {reply: invalid}; orGroup.colors[target] = !isNotSearch; continue; } @@ -82,14 +224,15 @@ exports.commands = { if (target.substr(0, 3) === 'gen' && Number.isInteger(parseFloat(target.substr(3)))) target = target.substr(3).trim(); let targetInt = parseInt(target); if (0 < targetInt && targetInt < 7) { - if (!validParameter("gens", target, isNotSearch)) return; + let invalid = validParameter("gens", target, isNotSearch); + if (invalid) return {reply: invalid}; orGroup.gens[target] = !isNotSearch; continue; } if (target === 'all') { - if (this.broadcasting && !room.isPersonal) return this.sendReplyBox("A search with the parameter 'all' cannot be broadcast."); - if (parameters.length > 1) return this.sendReplyBox("The parameter 'all' cannot have alternative parameters"); + if (!canAll) return {reply: "A search with the parameter 'all' cannot be broadcast."}; + if (parameters.length > 1) return {reply: "The parameter 'all' cannot have alternative parameters"}; showAll = true; orGroup.skip = true; break; @@ -103,17 +246,18 @@ exports.commands = { } if (target === 'megas' || target === 'mega') { - if (parameters.length > 1) return this.sendReplyBox("The parameter 'mega' cannot have alternative parameters"); + if (parameters.length > 1) return {reply: "The parameter 'mega' cannot have alternative parameters"}; megaSearch = !isNotSearch; orGroup.skip = true; break; } if (target === 'recovery') { - if (parameters.length > 1) return this.sendReplyBox("The parameter 'recovery' cannot have alternative parameters"); + if (parameters.length > 1) return {reply: "The parameter 'recovery' cannot have alternative parameters"}; let recoveryMoves = ["recover", "roost", "moonlight", "morningsun", "synthesis", "milkdrink", "slackoff", "softboiled", "wish", "healorder"]; for (let k = 0; k < recoveryMoves.length; k++) { - if (!validParameter("moves", recoveryMoves[k], isNotSearch)) return; + let invalid = validParameter("moves", recoveryMoves[k], isNotSearch); + if (invalid) return {reply: invalid}; if (isNotSearch) { let bufferObj = {moves: {}}; bufferObj.moves[recoveryMoves[k]] = false; @@ -127,12 +271,13 @@ exports.commands = { } if (target === 'priority') { - if (parameters.length > 1) return this.sendReplyBox("The parameter 'priority' cannot have alternative parameters"); + if (parameters.length > 1) return {reply: "The parameter 'priority' cannot have alternative parameters"}; for (let move in Tools.data.Movedex) { let moveData = Tools.getMove(move); if (moveData.category === "Status" || moveData.id === "bide") continue; if (moveData.priority > 0) { - if (!validParameter("moves", move, isNotSearch)) return; + let invalid = validParameter("moves", move, isNotSearch); + if (invalid) return {reply: invalid}; if (isNotSearch) { let bufferObj = {moves: {}}; bufferObj.moves[move] = false; @@ -148,7 +293,8 @@ exports.commands = { let targetMove = Tools.getMove(target); if (targetMove.exists) { - if (!validParameter("moves", targetMove.id, isNotSearch)) return; + let invalid = validParameter("moves", targetMove.id, isNotSearch); + if (invalid) return {reply: invalid}; orGroup.moves[targetMove.id] = !isNotSearch; continue; } @@ -157,7 +303,8 @@ exports.commands = { if (typeIndex >= 0) { target = target.charAt(0).toUpperCase() + target.substring(1, typeIndex); if (target in Tools.data.TypeChart) { - if (!validParameter("types", target, isNotSearch)) return; + let invalid = validParameter("types", target, isNotSearch); + if (invalid) return {reply: invalid}; orGroup.types[target] = !isNotSearch; continue; } @@ -165,7 +312,7 @@ exports.commands = { let inequality = target.search(/>|<|=/); if (inequality >= 0) { - if (isNotSearch) return this.sendReplyBox("You cannot use the negation symbol '!' in stat ranges."); + if (isNotSearch) return {reply: "You cannot use the negation symbol '!' in stat ranges."}; if (target.charAt(inequality + 1) === '=') { inequality = target.substr(inequality, 2); } else { @@ -193,7 +340,7 @@ exports.commands = { case '=': direction = 'equal'; break; } } else { - return this.sendReplyBox("No value given to compare with '" + Tools.escapeHTML(target) + "'."); + return {reply: "No value given to compare with '" + Tools.escapeHTML(target) + "'."}; } switch (toId(stat)) { case 'attack': stat = 'atk'; break; @@ -204,18 +351,18 @@ exports.commands = { case 'spdef': stat = 'spd'; break; case 'speed': stat = 'spe'; break; } - if (!(stat in allStats)) return this.sendReplyBox("'" + Tools.escapeHTML(target) + "' did not contain a valid stat."); + if (!(stat in allStats)) return {reply: "'" + Tools.escapeHTML(target) + "' did not contain a valid stat."}; if (!orGroup.stats[stat]) orGroup.stats[stat] = {}; - if (orGroup.stats[stat][direction]) return this.sendReplyBox("Invalid stat range for " + stat + "."); + if (orGroup.stats[stat][direction]) return {reply: "Invalid stat range for " + stat + "."}; orGroup.stats[stat][direction] = num; continue; } - return this.sendReplyBox("'" + Tools.escapeHTML(target) + "' could not be found in any of the search categories."); + return {reply: "'" + Tools.escapeHTML(target) + "' could not be found in any of the search categories."}; } searches.push(orGroup); } - if (showAll && searches.length === 0 && megaSearch === null) return this.sendReplyBox("No search parameters other than 'all' were found. Try '/help dexsearch' for more information on this command."); + if (showAll && searches.length === 0 && megaSearch === null) return {reply: "No search parameters other than 'all' were found. Try '/help dexsearch' for more information on this command."}; let dex = {}; for (let pokemon in Tools.data.Pokedex) { @@ -356,7 +503,7 @@ exports.commands = { results = Tools.shuffle(results).slice(0, randomOutput); } - let resultsStr = this.broadcasting ? "" : ("" + Tools.escapeHTML(message) + ":
"); + let resultsStr = (message === "" ? message : "" + Tools.escapeHTML(message) + ":
"); if (results.length > 1) { if (showAll || results.length <= RESULTS_MAX_LENGTH + 5) { results.sort(); @@ -365,55 +512,14 @@ exports.commands = { resultsStr += results.slice(0, RESULTS_MAX_LENGTH).join(", ") + ", and " + (results.length - RESULTS_MAX_LENGTH) + " more. Redo the search with 'all' as a search parameter to show all results."; } } else if (results.length === 1) { - return CommandParser.commands.data.call(this, results[0], room, user, connection, 'dt'); + return {parse: '/dt ' + results[0]}; } else { resultsStr += "No Pokémon found."; } - return this.sendReplyBox(resultsStr); - }, - dexsearchhelp: ["/dexsearch [parameter], [parameter], [parameter], ... - Searches for Pok\u00e9mon that fulfill the selected criteria", - "Search categories are: type, tier, color, moves, ability, gen, recovery, priority, stat.", - "Valid colors are: green, red, blue, white, brown, yellow, purple, pink, gray and black.", - "Valid tiers are: Uber/OU/BL/UU/BL2/RU/BL3/NU/BL4/PU/NFE/LC/CAP.", - "Types must be followed by ' type', e.g., 'dragon type'.", - "Inequality ranges use the characters '>=' for '≥' and '<=' for '≤', e.g., 'hp <= 95' searches all Pok\u00e9mon with HP less than or equal to 95.", - "Parameters can be excluded through the use of '!', e.g., '!water type' excludes all water types.", - "The parameter 'mega' can be added to search for Mega Evolutions only, and the parameter 'NFE' can be added to search not-fully evolved Pok\u00e9mon only.", - "Parameters separated with '|' will be searched as alternatives for each other, e.g., 'trick | switcheroo' searches for all Pok\u00e9mon that learn either Trick or Switcheroo.", - "The order of the parameters does not matter."], + return {reply: resultsStr}; + }; - rollpokemon: 'randompokemon', - randpoke: 'randompokemon', - randompokemon: function (target, room, user, connection, cmd, message) { - let targets = target.split(","); - let targetsBuffer = []; - let qty; - for (let i = 0; i < targets.length; i++) { - if (!targets[i]) continue; - let num = Number(targets[i]); - if (Number.isInteger(num)) { - if (qty) return this.errorReply("Only specify the number of Pok\u00e9mon once."); - qty = num; - if (qty < 1 || 15 < qty) return this.errorReply("Number of random Pok\u00e9mon must be between 1 and 15."); - targetsBuffer.push("random" + qty); - } else { - targetsBuffer.push(targets[i]); - } - } - if (!qty) targetsBuffer.push("random1"); - - CommandParser.commands.dexsearch.call(this, targetsBuffer.join(","), room, user, connection, "randpoke", message); - }, - randompokemonhelp: ["/randompokemon - Generates random Pok\u00e9mon based on given search conditions.", - "/randompokemon uses the same parameters as /dexsearch (see '/help ds').", - "Adding a number as a parameter returns that many random Pok\u00e9mon, e.g., '/randpoke 6' returns 6 random Pok\u00e9mon."], - - ms: 'movesearch', - msearch: 'movesearch', - movesearch: function (target, room, user, connection, cmd, message) { - if (!this.canBroadcast()) return; - - if (!target) return this.parse('/help movesearch'); + let runMovesearch = function (target, cmd, canAll, message) { let targets = target.split(','); let searches = {}; let allCategories = {'physical':1, 'special':1, 'status':1}; @@ -437,9 +543,9 @@ exports.commands = { let typeIndex = target.indexOf(' type'); if (typeIndex >= 0) { target = target.charAt(0).toUpperCase() + target.substring(1, typeIndex); - if (!(target in Tools.data.TypeChart)) return this.sendReplyBox("Type '" + Tools.escapeHTML(target) + "' not found."); + if (!(target in Tools.data.TypeChart)) return {reply: "Type '" + Tools.escapeHTML(target) + "' not found."}; if (!searches['type']) searches['type'] = {}; - if ((searches['type'][target] && isNotSearch) || (searches['type'][target] === false && !isNotSearch)) return this.sendReplyBox('A search cannot both exclude and include a type.'); + if ((searches['type'][target] && isNotSearch) || (searches['type'][target] === false && !isNotSearch)) return {reply: 'A search cannot both exclude and include a type.'}; searches['type'][target] = !isNotSearch; continue; } @@ -447,7 +553,7 @@ exports.commands = { if (target in allCategories) { target = target.charAt(0).toUpperCase() + target.substr(1); if (!searches['category']) searches['category'] = {}; - if ((searches['category'][target] && isNotSearch) || (searches['category'][target] === false && !isNotSearch)) return this.sendReplyBox('A search cannot both exclude and include a category.'); + if ((searches['category'][target] && isNotSearch) || (searches['category'][target] === false && !isNotSearch)) return {reply: 'A search cannot both exclude and include a category.'}; searches['category'][target] = !isNotSearch; continue; } @@ -455,13 +561,13 @@ exports.commands = { if (target === 'bypassessubstitute') target = 'authentic'; if (target in allFlags) { if (!searches['flags']) searches['flags'] = {}; - if ((searches['flags'][target] && isNotSearch) || (searches['flags'][target] === false && !isNotSearch)) return this.sendReplyBox('A search cannot both exclude and include \'' + target + '\'.'); + if ((searches['flags'][target] && isNotSearch) || (searches['flags'][target] === false && !isNotSearch)) return {reply: 'A search cannot both exclude and include \'' + target + '\'.'}; searches['flags'][target] = !isNotSearch; continue; } if (target === 'all') { - if (this.broadcasting && !room.isPersonal) return this.sendReplyBox("A search with the parameter 'all' cannot be broadcast."); + if (!canAll) return {reply: "A search with the parameter 'all' cannot be broadcast."}; showAll = true; continue; } @@ -470,14 +576,14 @@ exports.commands = { if (!searches['recovery']) { searches['recovery'] = !isNotSearch; } else if ((searches['recovery'] && isNotSearch) || (searches['recovery'] === false && !isNotSearch)) { - return this.sendReplyBox('A search cannot both exclude and include recovery moves.'); + return {reply: 'A search cannot both exclude and include recovery moves.'}; } continue; } let template = Tools.getTemplate(target); if (template.exists) { - if (Object.keys(lsetData).length) return this.sendReplyBox("A search can only include one Pok\u00e9mon learnset."); + if (Object.keys(lsetData).length) return {reply: "A search can only include one Pok\u00e9mon learnset."}; if (!template.learnset) template = Tools.getTemplate(template.baseSpecies); lsetData = template.learnset; targetMon = template.name; @@ -492,7 +598,7 @@ exports.commands = { let inequality = target.search(/>|<|=/); if (inequality >= 0) { - if (isNotSearch) return this.sendReplyBox("You cannot use the negation symbol '!' in quality ranges."); + if (isNotSearch) return {reply: "You cannot use the negation symbol '!' in quality ranges."}; inequality = target.charAt(inequality); let targetParts = target.replace(/\s/g, '').split(inequality); let numSide, propSide, direction; @@ -513,7 +619,7 @@ exports.commands = { case '=': direction = 'equal'; break; } } else { - return this.sendReplyBox("No value given to compare with '" + Tools.escapeHTML(target) + "'."); + return {reply: "No value given to compare with '" + Tools.escapeHTML(target) + "'."}; } let prop = targetParts[propSide]; switch (toId(targetParts[propSide])) { @@ -521,17 +627,17 @@ exports.commands = { case 'bp': prop = 'basePower'; break; case 'acc': prop = 'accuracy'; break; } - if (!(prop in allProperties)) return this.sendReplyBox("'" + Tools.escapeHTML(target) + "' did not contain a valid property."); + if (!(prop in allProperties)) return {reply: "'" + Tools.escapeHTML(target) + "' did not contain a valid property."}; if (!searches['property']) searches['property'] = {}; if (direction === 'equal') { - if (searches['property'][prop]) return this.sendReplyBox("Invalid property range for " + prop + "."); + if (searches['property'][prop]) return {reply: "Invalid property range for " + prop + "."}; searches['property'][prop] = {}; searches['property'][prop]['less'] = parseFloat(targetParts[numSide]); searches['property'][prop]['greater'] = parseFloat(targetParts[numSide]); } else { if (!searches['property'][prop]) searches['property'][prop] = {}; if (searches['property'][prop][direction]) { - return this.sendReplyBox("Invalid property range for " + prop + "."); + return {reply: "Invalid property range for " + prop + "."}; } else { searches['property'][prop][direction] = parseFloat(targetParts[numSide]); } @@ -547,11 +653,11 @@ exports.commands = { } else if (target === "-") { sign = 'less'; } else { - return this.sendReplyBox("Priority type '" + target + "' not recognized."); + return {reply: "Priority type '" + target + "' not recognized."}; } if (!searches['property']) searches['property'] = {}; if (searches['property']['priority']) { - return this.sendReplyBox("Priority cannot be set with both shorthand and inequality range."); + return {reply: "Priority cannot be set with both shorthand and inequality range."}; } else { searches['property']['priority'] = {}; searches['property']['priority'][sign] = (sign === 'less' ? -1 : 1); @@ -572,9 +678,9 @@ exports.commands = { case 'evasiveness': target = 'evasion'; break; default: target = target.substr(7); } - if (!(target in allBoosts)) return this.sendReplyBox("'" + Tools.escapeHTML(target.substr(7)) + "' is not a recognized stat."); + if (!(target in allBoosts)) return {reply: "'" + Tools.escapeHTML(target.substr(7)) + "' is not a recognized stat."}; if (!searches['boost']) searches['boost'] = {}; - if ((searches['boost'][target] && isNotSearch) || (searches['boost'][target] === false && !isNotSearch)) return this.sendReplyBox('A search cannot both exclude and include a stat boost.'); + if ((searches['boost'][target] && isNotSearch) || (searches['boost'][target] === false && !isNotSearch)) return {reply: 'A search cannot both exclude and include a stat boost.'}; searches['boost'][target] = !isNotSearch; continue; } @@ -595,23 +701,23 @@ exports.commands = { if (target in allStatus) { if (!searches['status']) searches['status'] = {}; - if ((searches['status'][target] && isNotSearch) || (searches['status'][target] === false && !isNotSearch)) return this.sendReplyBox('A search cannot both exclude and include a status.'); + if ((searches['status'][target] && isNotSearch) || (searches['status'][target] === false && !isNotSearch)) return {reply: 'A search cannot both exclude and include a status.'}; searches['status'][target] = !isNotSearch; continue; } if (target in allVolatileStatus) { if (!searches['volatileStatus']) searches['volatileStatus'] = {}; - if ((searches['volatileStatus'][target] && isNotSearch) || (searches['volatileStatus'][target] === false && !isNotSearch)) return this.sendReplyBox('A search cannot both exclude and include a volitile status.'); + if ((searches['volatileStatus'][target] && isNotSearch) || (searches['volatileStatus'][target] === false && !isNotSearch)) return {reply: 'A search cannot both exclude and include a volitile status.'}; searches['volatileStatus'][target] = !isNotSearch; continue; } - return this.sendReplyBox("'" + Tools.escapeHTML(oldTarget) + "' could not be found in any of the search categories."); + return {reply: "'" + Tools.escapeHTML(oldTarget) + "' could not be found in any of the search categories."}; } if (showAll && !Object.keys(searches).length && !targetMon) { - return this.sendReplyBox("No search parameters other than 'all' were found. Try '/help movesearch' for more information on this command."); + return {reply: "No search parameters other than 'all' were found. Try '/help movesearch' for more information on this command."}; } let dex = {}; @@ -743,7 +849,7 @@ exports.commands = { if (targetMon) { resultsStr += "Matching moves found in learnset for " + targetMon + ":
"; } else { - resultsStr += this.broadcasting ? "" : ("" + Tools.escapeHTML(message) + ":
"); + resultsStr += (message === "" ? message : "" + Tools.escapeHTML(message) + ":
"); } if (results.length > 0) { if (showAll || results.length <= RESULTS_MAX_LENGTH + 5) { @@ -755,23 +861,10 @@ exports.commands = { } else { resultsStr += "No moves found."; } - return this.sendReplyBox(resultsStr); - }, - movesearchhelp: ["/movesearch [parameter], [parameter], [parameter], ... - Searches for moves that fulfill the selected criteria.", - "Search categories are: type, category, flag, status inflicted, type boosted, and numeric range for base power, pp, and accuracy.", - "Types must be followed by ' type', e.g., 'dragon type'.", - "Stat boosts must be preceded with 'boosts ', e.g., 'boosts attack' searches for moves that boost the attack stat.", - "Inequality ranges use the characters '>' and '<' though they behave as '≥' and '≤', e.g., 'bp > 100' searches for all moves equal to and greater than 100 base power.", - "Parameters can be excluded through the use of '!', e.g., !water type' excludes all water type moves.", - "Valid flags are: authentic (bypasses substitute), bite, bullet, contact, defrost, powder, pulse, punch, secondary, snatch, sound", - "If a Pok\u00e9mon is included as a parameter, moves will be searched from it's movepool.", - "The order of the parameters does not matter."], - - isearch: 'itemsearch', - itemsearch: function (target, room, user, connection, cmd, message) { - if (!target) return this.parse('/help itemsearch'); - if (!this.canBroadcast()) return; + return {reply: resultsStr}; + }; + let runItemsearch = function (target, cmd, canAll, message) { let showAll = false; target = target.trim(); @@ -852,7 +945,7 @@ exports.commands = { searchedWords.push(newWord); } - if (searchedWords.length === 0) return this.sendReplyBox("No distinguishing words were used. Try a more specific search."); + if (searchedWords.length === 0) return {reply: "No distinguishing words were used. Try a more specific search."}; if (searchedWords.indexOf('fling') >= 0) { let basePower = 0; let effect; @@ -873,13 +966,13 @@ exports.commands = { case 'badly': wordEff = 'tox'; break; } if (wordEff && effect) { - if (!(wordEff === 'psn' && effect === 'tox')) return this.sendReplyBox("Only specify fling effect once."); + if (!(wordEff === 'psn' && effect === 'tox')) return {reply: "Only specify fling effect once."}; } else if (wordEff) { effect = wordEff; } else { if (searchedWords[k].substr(searchedWords[k].length - 2) === 'bp' && searchedWords[k].length > 2) searchedWords[k] = searchedWords[k].substr(0, searchedWords[k].length - 2); if (Number.isInteger(Number(searchedWords[k]))) { - if (basePower) return this.sendReplyBox("Only specify a number for base power once."); + if (basePower) return {reply: "Only specify a number for base power once."}; basePower = parseInt(searchedWords[k]); } } @@ -898,7 +991,7 @@ exports.commands = { if (item.fling.status === effect || item.fling.volatileStatus === effect) foundItems.push(item.name); } } - if (foundItems.length === 0) return this.sendReplyBox('No items inflict ' + basePower + 'bp damage when used with Fling.'); + if (foundItems.length === 0) return {reply: 'No items inflict ' + basePower + 'bp damage when used with Fling.'}; } else if (target.search(/natural ?gift/i) >= 0) { let basePower = 0; let type = ""; @@ -906,12 +999,12 @@ exports.commands = { for (let k = 0; k < searchedWords.length; k++) { searchedWords[k] = searchedWords[k].charAt(0).toUpperCase() + searchedWords[k].slice(1); if (searchedWords[k] in Tools.data.TypeChart) { - if (type) return this.sendReplyBox("Only specify natural gift type once."); + if (type) return {reply: "Only specify natural gift type once."}; type = searchedWords[k]; } else { if (searchedWords[k].substr(searchedWords[k].length - 2) === 'bp' && searchedWords[k].length > 2) searchedWords[k] = searchedWords[k].substr(0, searchedWords[k].length - 2); if (Number.isInteger(Number(searchedWords[k]))) { - if (basePower) return this.sendReplyBox("Only specify a number for base power once."); + if (basePower) return {reply: "Only specify a number for base power once."}; basePower = parseInt(searchedWords[k]); } } @@ -929,7 +1022,7 @@ exports.commands = { if (item.naturalGift.type === type) foundItems.push(item.name); } } - if (foundItems.length === 0) return this.sendReplyBox('No berries inflict ' + basePower + 'bp damage when used with Natural Gift.'); + if (foundItems.length === 0) return {reply: 'No berries inflict ' + basePower + 'bp damage when used with Natural Gift.'}; } else { let bestMatched = 0; for (let n in Tools.data.Items) { @@ -972,7 +1065,7 @@ exports.commands = { } } - let resultsStr = this.broadcasting ? "" : ("" + Tools.escapeHTML(message) + ":
"); + let resultsStr = (message === "" ? message : "" + Tools.escapeHTML(message) + ":
"); if (foundItems.length > 0) { if (showAll || foundItems.length <= RESULTS_MAX_LENGTH + 5) { foundItems.sort(); @@ -983,10 +1076,31 @@ exports.commands = { } else { resultsStr += "No items found. Try a more general search"; } - return this.sendReplyBox(resultsStr); - }, - itemsearchhelp: ["/itemsearch [move description] - finds items that match the given key words.", - "Command accepts natural language. (tip: fewer words tend to work better)", - "Searches with \"fling\" in them will find items with the specified Fling behavior.", - "Searches with \"natural gift\" in them will find items with the specified Natural Gift behavior."], -}; + return {reply: resultsStr}; + }; + + process.on('message', message => { + let response = {id: message.id}; + switch (message.cmd) { + case 'randpoke': + //falls through + case 'dexsearch': + response.value = runDexsearch(message.target, message.cmd, message.canAll, message.message); + break; + case 'movesearch': + response.value = runMovesearch(message.target, message.cmd, message.canAll, message.message); + break; + case 'itemsearch': + response.value = runItemsearch(message.target, message.cmd, message.canAll, message.message); + break; + } + + process.send(response); + }); + + process.on('disconnect', () => { + process.exit(); + }); + + require('../repl.js').start('dexsearch', cmd => eval(cmd)); +} diff --git a/chat-plugins/info.js b/chat-plugins/info.js index bf34efb8f5..885c6b73c5 100644 --- a/chat-plugins/info.js +++ b/chat-plugins/info.js @@ -1291,6 +1291,9 @@ exports.commands = { buf += "" + process.process.pid + " - Validator " + (i++) + "
"; } } + if (Tools.dexsearchProcess) { + buf += "" + Tools.dexsearchProcess.pid + " - Dexsearch
"; + } this.sendReplyBox(buf); },