diff --git a/bot2022.db/000225.ldb b/bot2022.db/000225.ldb new file mode 100644 index 0000000..87a48b4 Binary files /dev/null and b/bot2022.db/000225.ldb differ diff --git a/bot2022.db/000227.ldb b/bot2022.db/000227.ldb new file mode 100644 index 0000000..e01ddb9 Binary files /dev/null and b/bot2022.db/000227.ldb differ diff --git a/bot2022.db/000228.log b/bot2022.db/000228.log new file mode 100644 index 0000000..da7bde2 Binary files /dev/null and b/bot2022.db/000228.log differ diff --git a/bot2022.db/CURRENT b/bot2022.db/CURRENT new file mode 100644 index 0000000..72f074e --- /dev/null +++ b/bot2022.db/CURRENT @@ -0,0 +1 @@ +MANIFEST-000226 diff --git a/bot2022.db/LOCK b/bot2022.db/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/bot2022.db/LOG b/bot2022.db/LOG new file mode 100644 index 0000000..87fa3ba --- /dev/null +++ b/bot2022.db/LOG @@ -0,0 +1,5 @@ +2022/01/12-03:39:23.660 73e8 Recovering log #224 +2022/01/12-03:39:23.660 73e8 Level-0 table #227: started +2022/01/12-03:39:23.664 73e8 Level-0 table #227: 643 bytes OK +2022/01/12-03:39:23.669 73e8 Delete type=0 #224 +2022/01/12-03:39:23.670 73e8 Delete type=3 #222 diff --git a/bot2022.db/LOG.old b/bot2022.db/LOG.old new file mode 100644 index 0000000..da12ac9 --- /dev/null +++ b/bot2022.db/LOG.old @@ -0,0 +1,14 @@ +2022/01/12-03:38:41.098 7054 Recovering log #221 +2022/01/12-03:38:41.099 7054 Level-0 table #223: started +2022/01/12-03:38:41.102 7054 Level-0 table #223: 1867 bytes OK +2022/01/12-03:38:41.109 7054 Delete type=0 #221 +2022/01/12-03:38:41.109 7054 Delete type=3 #219 +2022/01/12-03:38:41.109 5828 Compacting 4@0 + 1@1 files +2022/01/12-03:38:41.112 5828 Generated table #225@0: 24 keys, 1492 bytes +2022/01/12-03:38:41.112 5828 Compacted 4@0 + 1@1 files => 1492 bytes +2022/01/12-03:38:41.114 5828 compacted to: files[ 0 1 0 0 0 0 0 ] +2022/01/12-03:38:41.114 5828 Delete type=2 #210 +2022/01/12-03:38:41.114 5828 Delete type=2 #212 +2022/01/12-03:38:41.114 5828 Delete type=2 #215 +2022/01/12-03:38:41.115 5828 Delete type=2 #220 +2022/01/12-03:38:41.115 5828 Delete type=2 #223 diff --git a/bot2022.db/MANIFEST-000226 b/bot2022.db/MANIFEST-000226 new file mode 100644 index 0000000..393e953 Binary files /dev/null and b/bot2022.db/MANIFEST-000226 differ diff --git a/index.js b/index.js index 1748882..29b81d8 100644 --- a/index.js +++ b/index.js @@ -3,11 +3,11 @@ require('dotenv').config(); globalThis.gBot = require('./src/Bot'); const level = require('level'); -const DiscordClient = require('./src/DiscordClient'); +const MPPClient = require('./src/MPPClient'); -globalThis.db = level("./bot2019.db"); +globalThis.db = level("./bot2022.db"); -let sendChat = DiscordClient.sendChat; +let sendChat = MPPClient.sendChat; db.getPokemon = function(id, cb) { var key = "pokemon collection~"+id; @@ -197,4 +197,4 @@ db.setFruits = function(num_fruits) { } -gBot.start(process.env.DISCORD_TOKEN); +gBot.start(process.env.MPPCLONE_TOKEN); diff --git a/package-lock.json b/package-lock.json index 28cc025..ae5db57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "booger", "version": "1.0.0", "license": "ISC", "dependencies": { diff --git a/src/Bot.js b/src/Bot.js index b1423ec..b03911d 100644 --- a/src/Bot.js +++ b/src/Bot.js @@ -1,15 +1,17 @@ -const { sendChat } = require('./DiscordClient'); -const DiscordClient = require('./DiscordClient'); +const { sendChat } = require('./MPPClient'); +// const DiscordClient = require('./DiscordClient'); +const MPPClient = require('./MPPClient'); const StaticEventEmitter = require('./StaticEventEmitter'); module.exports = class Bot extends StaticEventEmitter { static start(token) { - DiscordClient.start(token); + MPPClient.start(token); this.bindEventListeners(); this.commands = new Map(); this.admin = [ - "314868372686766080" + "2ffc3744fbc1bc6c6ef4a330", + "a41651bf8a46bafc5548dad6" ]; this.prefix = "/"; // never change this @@ -18,10 +20,10 @@ module.exports = class Bot extends StaticEventEmitter { static bindEventListeners() { this.on("chat.receive", msg => { - console.log(msg.author.username + ": " + msg.content); + console.log(msg.p.name + ": " + msg.a); let m = { - referer: msg.author, + referer: msg.p, }; @@ -29,72 +31,67 @@ module.exports = class Bot extends StaticEventEmitter { }); this.on("chat.send", msg => { - DiscordClient.sendChat(msg); + MPPClient.sendChat(msg); }); } static runCommand(msg) { - let role; - msg.member.guild.roles.cache.forEach(r => { - if (r.name.toString() == msg.member.user.id.toString()) { - role = r; - } - }); - msg.p = { - name: msg.author.username, - _id: msg.author.id, - color: role.color, - id: msg.author.id - } - msg.a = msg.content; + // let role; + // msg.member.guild.roles.cache.forEach(r => { + // if (r.name.toString() == msg.member.user.id.toString()) { + // role = r; + // } + // }); + // msg.p = { + // name: msg.author.username, + // _id: msg.author.id, + // color: role.color, + // id: msg.author.id + // } + // msg.a = msg.content; if(msg.a[0] == "∕" && msg.p.id !== client.participantId) { msg.a[0] = "/"; } function findParticipantByName(name) { - // if(!name || name.trim() == "") return undefined; - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id) && client.ppl[id].name === name) { - // return client.ppl[id]; - // } - // } + if(!name || name.trim() == "") return undefined; + for(var id in client.ppl) { + if(client.ppl.hasOwnProperty(id) && client.ppl[id].name === name) { + return client.ppl[id]; + } + } return undefined; }; + function findParticipantByNameCaseInsensitive(name) { if(!name || name.trim() == "") return undefined; var part = findParticipantByName(name); if(!part) { name_lc = name.toLowerCase(); - DiscordClient.client.guilds.cache.get('841331769051578413').members.cache.forEach(p => { - if (p.user.username.toLowerCase() === name_lc) { - part = p.user; - part.name = p.user.name; - part._id = p.user.id; - DiscordClient.client.guilds.cache.get('841331769051578413').roles.cache.forEach(r => { - if (r.name === part._id) { - part.color = r.color; - } - }); + MPPClient.client.ppl.forEach(p => { + if(p.name.toLowerCase() === name_lc) { + part = p; } }); } return part; }; + function findParticipantByNameFuzzy(name) { if(!name || name.trim() == "") return undefined; name = name.toLowerCase(); var part = findParticipantByNameCaseInsensitive(name); - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id) && client.ppl[id].name.toLowerCase().indexOf(name) === 0) { - // part = client.ppl[id]; - // break; - // } - // } - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id) && client.ppl[id].name.toLowerCase().indexOf(name) !== -1) { - // part = client.ppl[id]; - // break; - // } - // } + for(var id in MPPClient.client.ppl) { + if(client.ppl.hasOwnProperty(id) && client.ppl[id].name.toLowerCase().indexOf(name) === 0) { + part = client.ppl[id]; + break; + } + } + for(var id in MPPClient.client.ppl) { + if(client.ppl.hasOwnProperty(id) && client.ppl[id].name.toLowerCase().indexOf(name) !== -1) { + part = MPPClient.client.ppl[id]; + break; + } + } return part; }; if (msg.cmd.startsWith("give_")) { diff --git a/src/Commands.js b/src/Commands.js index 382500a..4d0aa97 100644 --- a/src/Commands.js +++ b/src/Commands.js @@ -1,4 +1,4 @@ -const DiscordClient = require("./DiscordClient"); +const MPPClient = require("./MPPClient"); const Color = require('./Color'); const crypto = require('crypto'); const fs = require('fs'); @@ -17,11 +17,14 @@ module.exports = (bot) => { var fish = ["Angelfish", "Arapaima", "Arowana", "Barbel Steed", "Barred Knifejaw", "Bitterling", "Black Bass", "Blowfish", "Blue Marlin", "Bluegill", "Brook Trout", "Butterflyfish", "Can", "Carp", "Catfish", "Char", "Cherry Salmon", "Clownfish", "Coelacanth", "Crawfish", "Crucian Carp", "Dab", "Dace", "Dorado", "Eel", "Football fish", "Freshwater Goby", "Frog", "Gar", "Giant Snakehead", "Giant Trevally", "Goldfish", "Guppy", "Hammerhead Shark", "Horse Mackerel", "Jellyfish", "Key", "Killifish", "King Salmon", "Koi", "Large Bass", "Loach", "Lobster", "Mitten Crab", "Moray Eel", "Napoleonfish", "Neon Tetra", "Nibble Fish", "Oarfish", "Ocean Sunfish", "Octopus", "Olive Flounder", "Pale Chub", "Pike", "Piranha", "Pond Smelt", "Popeyed Goldfish", "Puffer Fish", "Rainbow Trout", "Ray", "Red Snapper", "Ribbon Eel", "Saddled Bichir", "Salmon", "Saw Shark", "Sea Bass", "Sea Butterfly", "Seahorse", "Shark", "Small Bass", "Softshell Turtle", "Squid", "Stringfish", "Surgeonfish", "Sweetfish", "Tadpole", "Tuna", "Whale Shark", "Yellow Perch", "Zebra Turkeyfish"]; var fish_without_images = ["Blowfish", "Brook Trout", "Butterflyfish", "Can", "Giant Trevally", "Key", "Large Bass", "Lobster", "Mitten Crab", "Moray Eel", "Napoleonfish", "Neon Tetra", "Nibble Fish", "Oarfish", "Pike", "Ray", "Ribbon Eel", "Saddled Bichir", "Saw Shark", "Small Bass", "Softshell Turtle", "Surgeonfish", "Tadpole", "Whale Shark"]; var newfish = require("./newfish.json"); - var pokedex = []; + var pokedex = require("./pokedex.json"); + var untakeable = ['bed', 'chair', 'couch', 'desk', 'dining table', 'fireplace', 'floor lamp', 'flower pot', 'food cart', 'garden', 'grand piano', 'lamp', 'lawn mower', 'mailbox', 'potted plant', 'refrigerator', 'sink', 'sofa', 'stairs', 'stool', 'table', 'television', 'toilet', 'tv', 'vase', 'wardrobe', 'window', 'workbench']; - var sendChat = DiscordClient.sendChat; + var sendChat = MPPClient.sendChat; var blockHelpUntil = 0; + var locations = ["sleep", "outside", "inside"] + function underline(text) { var result = ""; for(var i = 0; i < text.length; i++) { @@ -72,8 +75,8 @@ module.exports = (bot) => { } function startupSound() { - // client.sendArray([{m: "n", t: Date.now()+client.serverTimeOffset, - // n: [{n:"e6",v:0.1},{d:50, n:"c7",v:0.2}]}]); + MPPClient.client.sendArray([{m: "n", t: Date.now()+MPPClient.client.serverTimeOffset, + n: [{n:"e6",v:0.1},{d:50, n:"c7",v:0.2}]}]); } function rando(arr) { @@ -275,12 +278,12 @@ module.exports = (bot) => { }; function findParticipantByName(name) { - // if(!name || name.trim() == "") return undefined; - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id) && client.ppl[id].name === name) { - // return client.ppl[id]; - // } - // } + if(!name || name.trim() == "") return undefined; + for(var id in MPPClient.client.ppl) { + if(MPPClient.client.ppl.hasOwnProperty(id) && MPPClient.client.ppl[id].name === name) { + return MPPClient.client.ppl[id]; + } + } return undefined; }; @@ -289,18 +292,11 @@ module.exports = (bot) => { var part = findParticipantByName(name); if(!part) { name_lc = name.toLowerCase(); - DiscordClient.client.guilds.cache.get('841331769051578413').members.cache.forEach(p => { - if (p.user.username.toLowerCase() === name_lc) { - part = p.user; - part.name = p.user.name; - part._id = p.user.id; - DiscordClient.client.guilds.cache.get('841331769051578413').roles.cache.forEach(r => { - if (r.name === part._id) { - part.color = r.color; - } - }); + for (let p of Object.values(MPPClient.client.ppl)) { + if(p.name.toLowerCase() === name_lc) { + part = p; } - }); + } } return part; }; @@ -309,18 +305,38 @@ module.exports = (bot) => { if(!name || name.trim() == "") return undefined; name = name.toLowerCase(); var part = findParticipantByNameCaseInsensitive(name); - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id) && client.ppl[id].name.toLowerCase().indexOf(name) === 0) { - // part = client.ppl[id]; - // break; - // } - // } - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id) && client.ppl[id].name.toLowerCase().indexOf(name) !== -1) { - // part = client.ppl[id]; - // break; - // } - // } + for(var id in MPPClient.client.ppl) { + if(MPPClient.client.ppl.hasOwnProperty(id) && MPPClient.client.ppl[id].name.toLowerCase().indexOf(name) === 0) { + part = MPPClient.client.ppl[id]; + break; + } + } + for(var id in MPPClient.client.ppl) { + if(MPPClient.client.ppl.hasOwnProperty(id) && MPPClient.client.ppl[id].name.toLowerCase().indexOf(name) !== -1) { + part = MPPClient.client.ppl[id]; + break; + } + } + + // new + if (!part) { + // for (var id in MPPClient.client.ppl) { + // let p = MPPClient.client.ppl[id]; + // if (!p) continue; + // if (p._id.toLowerCase().includes(name.toLowerCase())) { + // part = p; + // break; + // } + // } + for (var p of Object.values(MPPClient.client.ppl)) { + if (!p) continue; + if (p._id.toLowerCase().includes(name.toLowerCase())) { + part = p; + break; + } + } + } + return part; }; @@ -361,23 +377,44 @@ module.exports = (bot) => { db.put("look.outside.◍Sand", "We don't talk about that."); }, 6000); + function randomizeAllColors() { + for (let p of Object.values(MPPClient.client.ppl)) { + MPPClient.client.sendArray([{m:'setcolor', id:p._id, color:randomColor()}]); + } + } + + function swapAllColors() { + let colorList = []; + for (let p of Object.values(MPPClient.client.ppl)) { + colorList.push(p.color); + } + let index = 0; + let lastColor = colorList[colorList.length - 1]; + for (let p of Object.values(MPPClient.client.ppl)) { + MPPClient.client.sendArray([{m:'setcolor', id:p._id, color:lastColor}]); + lastColor = colorList[index]; + } + } bot.addCommand(['help', 'about', 'commands'], 0, msg => { if (Date.now() < blockHelpUntil) return; blockHelpUntil = Date.now() + 10000; //sendChat("This is a test to see what leveldb is like. Commands: /put , /get , /del , /read [ []] \t"+underline("Fishing")+": \t/fish, /cast (starts fishing), /reel (stops fishing), /caught [name] (shows fish you've caught), /eat (eats one of your fish), /give [name] (gives fish to someone else), /steal [name] (steals fish from someone else)"); // sendChat(underline("Fishing")+": \t/fish, /cast (starts fishing), /reel (stops fishing), /caught [name] (shows fish you've caught), /eat (eats one of your fish), /give [name] (gives fish to someone else), /give_[number] [name] (give up to 100 at a time), /pick (picks fruit from the tree), /look [object] (look at surroundings), /yeet [item] (yeet items into surroundings), /take [object] (take items from surroundings)"); - sendChat(underline("Fishing")+": \t/fish, /cast (starts fishing), /reel (stops fishing), /caught [name] (shows fish you've caught), /eat (eats one of your fish), /give [name] (gives fish to someone else), ~~/give_[number] [name] (give up to 100 at a time)~~, /pick (picks fruit from the tree), /look [object] (look at surroundings), /yeet [item] (yeet items into surroundings), /take [object] (take items from surroundings)"); + // sendChat(underline("Fishing")+": \t/fish, /cast (starts fishing), /reel (stops fishing), /caught [name] (shows fish you've caught), /eat (eats one of your fish), /give [name] (gives fish to someone else), ~~/give_[number] [name] (give up to 100 at a time)~~, /pick (picks fruit from the tree), /look [object] (look at surroundings), /yeet [item] (yeet items into surroundings), /take [object] (take items from surroundings)"); + sendChat(underline("Fishing")+": /fish, /cast (starts fishing), /reel (stops fishing), /caught [name] (shows fish you've caught), /eat (eats one of your fish), /give [name] (gives fish to someone else), ~~/give_[number] [name] (give up to 100 at a time)~~, /pick (picks fruit from the tree), /look [object] (look at surroundings), /yeet [item] (yeet items into surroundings), /take [object] (take items from surroundings)"); }, false); bot.addCommand('qmyid', 0, (msg, admin) => { if (!admin) return; - console.log(DiscordClient.client.user.id); + console.log(MPPClient.client.user.id); + sendChat(`Friend ${msg.p.name}: Your ID is ${msg.p.id}`); }, false); bot.addCommand('name', 0, (msg, admin) => { if (!admin) return; - DiscordClient.client.guilds.cache.get('841331769051578413').members.cache.get(DiscordClient.client.user.id).setNickname(msg.argcat()); + // DiscordClient.client.guilds.cache.get('841331769051578413').members.cache.get(DiscordClient.client.user.id).setNickname(msg.argcat()); + MPPClient.client.sendArray([{m:'userset', set: {name: msg.argcat()}}]); }, false); bot.addCommand('ch', 0, (msg, admin) => { @@ -419,13 +456,14 @@ module.exports = (bot) => { }, false); bot.addCommand(['ppl'], 0, msg => { - var list = "sorry :("; - // for(var id in client.ppl) { - // if(client.ppl.hasOwnProperty(id)) { - // list += ", " + client.ppl[id].name; - // } - // } - list = list.substr(2); + // var list = "sorry :("; + var list = ""; + for(var id in MPPClient.client.ppl) { + if(MPPClient.client.ppl.hasOwnProperty(id)) { + list += ", " + MPPClient.client.ppl[id].name; + } + } + // list = list.substring(0, list.length - 2); sendChat("ppl: " + list); return; }, false); @@ -444,7 +482,8 @@ module.exports = (bot) => { }, false); bot.addCommand(['pokedex', 'dex'], 0, msg => { - var pkmn = pokedex[msg.args[0]]; + // var pkmn = pokedex[msg.args[0]]; + var pkmn = pokedex.find(pk => pk.id == msg.argcat() || pk.name.toLowerCase() == msg.argcat().toLowerCase()); if(pkmn && pkmn.id) { var text = pkmn.id + ", " + pkmn.name + " ("; var n = 0; @@ -454,6 +493,7 @@ module.exports = (bot) => { ++n; } text += ") (\"" + pkmn.classification + "\")"; + sendChat(text); } }, false); @@ -609,7 +649,7 @@ module.exports = (bot) => { bot.addCommand('put', 0, (msg, admin) => { if (!admin) return; - db.put(args[0], msg.argcat(1), function(err) { + db.put(msg.args[0], msg.argcat(1), function(err) { if(err) { sendChat("our friend " + msg.p.name + " put ERR: " + err); } else { @@ -647,9 +687,9 @@ module.exports = (bot) => { var count = 0; var result_count = 0; db.createReadStream({ - start: args[0] || undefined, - end: args[1] || undefined, - reverse: args[2] === "reverse" || undefined + start: msg.args[0] || undefined, + end: msg.args[1] || undefined, + reverse: msg.args[2] === "reverse" || undefined }) .on("data", function(data) { ++count; @@ -668,6 +708,7 @@ module.exports = (bot) => { }, false); bot.addCommand('startup_sound', 0, msg => { + startupSound(); return; }, false); @@ -805,13 +846,19 @@ module.exports = (bot) => { var color = "#"+rrggbbrand()+rrggbbrand()+rrggbbrand(); // client.sendArray([{m: "admin message", password: "amogus", // msg: {m: "color", _id: msg.p._id, color: color}}]); - DiscordClient.client.guilds.cache.get("841331769051578413").roles.cache.forEach(r => { - if (r.name == msg.p._id) { - r.edit({ - color: color - }); - } - }) + // DiscordClient.client.guilds.cache.get("841331769051578413").roles.cache.forEach(r => { + // if (r.name == msg.p._id) { + // r.edit({ + // color: color + // }); + // } + // }) + + MPPClient.client.sendArray([{ + m: 'setcolor', + _id: msg.p._id, + color: color + }]); sendChat("Our friend " + msg.p.name+" ate his/her "+food+" and it made him/her turn "+(new Color(color).getName().toLowerCase())+"."); } @@ -831,7 +878,7 @@ module.exports = (bot) => { bot.addCommand(['go'], 0, msg => { db.getLocation(msg.p._id, location => { var target = msg.argcat().toLowerCase().trim(); - if(!["outside", "sleep"].includes(target)) { + if(!locations.includes(target)) { sendChat("Where is "+target+"?"); return; } @@ -980,7 +1027,17 @@ module.exports = (bot) => { } } else { - sendChat("Guy/girl "+msg.p.name+": doing that whilst "+location+" is currently prohibited."); + if (location == 'inside') { + sendChat(`Excuse me, ${msg.p.name}: Yeeting ${location} is most certainly rude.`); + return; + } + if (!['inside', 'outside'].includes(location)) { + sendChat("Guy/girl "+msg.p.name+": doing that whilst at "+location+" is currently prohibited."); + } else if (location == 'sleep') { + sendChat("Guy/girl "+msg.p.name+": doing that whilst "+location+"ing is currently prohibited."); + } else { + sendChat("Guy/girl "+msg.p.name+": doing that whilst "+location+" is currently prohibited."); + } } }); return; @@ -989,7 +1046,7 @@ module.exports = (bot) => { bot.addCommand('grow_fruit', 0, (msg, admin) => { if (!admin) return; - var how_many = ~~args[0]; + var how_many = ~~msg.args[0]; if(!how_many) how_many = 1; db.getFruits(function(num_fruits) { db.setFruits(num_fruits + how_many); @@ -1016,6 +1073,10 @@ module.exports = (bot) => { } else { var fish = entry.key; fish = fish.substr(fish.indexOf("◍")+1); + if (['bed'].includes(fish.toLowerCase()) || location == 'sleep') { + sendChat("Friend "+msg.p.name+": You can't take the " + fish + "."); + return; + } myfish.push(fish); db.putFish(msg.p._id, myfish); db.del(entry.key); @@ -1029,7 +1090,7 @@ module.exports = (bot) => { bot.addCommand('give', 0, msg => { var thief = msg.p; - var victim = findParticipantByNameFuzzy(args[0]); + var victim = findParticipantByNameFuzzy(msg.args[0]); if(!victim) { sendChat("Friend " +thief.name+" missed"); return; @@ -1038,7 +1099,7 @@ module.exports = (bot) => { sendChat("Friendly friend " +thief.name+" fudged"); return; } - var target_fish = argcat(1); + var target_fish = msg.argcat(1); db.getFish(thief._id, function(thief_fish) { db.getFish(victim._id, function(victim_fish) { if(victim_fish.length >= TOO_MANY_FISH) { @@ -1074,6 +1135,7 @@ module.exports = (bot) => { setInterval(function() { db.put("look.outside.◍Sand", "We don't talk about that."); + db.put("look.sleep.◍Bed", "*snuggles*"); }, 6000); var FISHING_CHANCE = 0.02; @@ -1094,22 +1156,34 @@ module.exports = (bot) => { db.del(winner); var user_id = winner.substr(8); var part; - DiscordClient.client.guilds.cache.get('841331769051578413').members.cache.forEach(p => { - if (p.user.id === user_id) { - part = p.user; - part.name = p.user.username; - part._id = p.user.id; + // DiscordClient.client.guilds.cache.get('841331769051578413').members.cache.forEach(p => { + // if (p.user.id === user_id) { + // part = p.user; + // part.name = p.user.username; + // part._id = p.user.id; + // if(typeof part !== 'undefined') { + // catchSomething(part); + // } + // } + // }); + for (let p of Object.values(MPPClient.client.ppl)) { + if (p.id === user_id) { + part = p; + part.name = p.name; + part._id = p.id; if(typeof part !== 'undefined') { catchSomething(part); } } - }); + } } }); }, 5000); setInterval(function() { - return; // stop auto-fishing + // return; // stop auto-fishing + + var client = MPPClient.client; if(!client.isConnected()) return; @@ -1119,7 +1193,7 @@ module.exports = (bot) => { var key = "fishing~"+part._id; db.get(key, function(err, value) { if(!value) { - sendChat("/fish"); + sendChat("/fish", true); } else { db.getFish(part._id, function(myfish) { if(!myfish.length) return; @@ -1131,7 +1205,7 @@ module.exports = (bot) => { else dest = client.ppl[i]; } if(dest && dest.id !== client.participantId) { - sendChat("/give "+dest.name.split(" ")[0]); + sendChat("/give "+dest.name.split(" ")[0], true); } }); /*if(findParticipantByNameFuzzy("potato")) { @@ -1473,7 +1547,7 @@ module.exports = (bot) => { }); }, false); - bot.addCommand(['/hug'], 0, msg => { + bot.addCommand(['hug'], 0, msg => { var part = findParticipantByNameFuzzy(msg.argcat()); if (part) { let hug = rando("a squeeze", "an affectionate hug", @@ -1490,7 +1564,41 @@ module.exports = (bot) => { } }, false); - bot.addCommand(['/give'], 0, msg => { + bot.addCommand(['kiss'], 0, msg => { + var part = findParticipantByNameFuzzy(msg.argcat()); + if (part) { + let kiss = rando("a kiss", "a sloppy smooch", + "a deep, passionate french kiss", `a big grandma kiss on the cheek`, "a big smooch on the cheek", "a new kiss", "a lasting mark on their face", "caring kiss"); + sendChat(`Our friend ` + msg.p.name + ` gave ` + part.name + ' ' + kiss); + } else { + db.getLocation(msg.p._id, location => { + var message = "Friend " + msg.p.name + " missed and the kiss went everywhere."; + if (location == 'outside' && Math.random() < 0.25) { + message += " Some of it went into the water and love was felt by the fish inside."; + } + sendChat(message); + }); + } + }); + + bot.addCommand(['bonk'], 0, msg => { + var part = findParticipantByNameFuzzy(msg.argcat()); + if (part) { + let bonk = rando("a bonk", "a slap", + "a deep, passionate bonk", `a "normal" bonk`, "a bonk on the cheek", "a new bonk", "a lasting mark on their face", "caring bonk"); + sendChat(`Our friend ` + msg.p.name + ` gave ` + part.name + ' ' + bonk); + } else { + db.getLocation(msg.p._id, location => { + var message = "Friend " + msg.p.name + " missed and the bonk went everywhere."; + if (location == 'outside' && Math.random() < 0.25) { + message += " Some of it went into the water and love was felt by the fish inside."; + } + sendChat(message); + }); + } + }); + + bot.addCommand(['give'], 0, msg => { var thief = msg.p; var victim = findParticipantByNameFuzzy(msg.args[0]); if (!victim) { @@ -1503,7 +1611,7 @@ module.exports = (bot) => { } var target_fish = msg.argcat(1); db.getFish(thief._id, function (thief_fish) { - db.getFish(victim_id, function (victim_fish) { + db.getFish(victim._id, function (victim_fish) { if (victim_fish.length >= TOO_MANY_FISH) { sendChat("Friend " + victim.name + " is carrying too much."); return; diff --git a/src/MPPClient.js b/src/MPPClient.js new file mode 100644 index 0000000..07f81ae --- /dev/null +++ b/src/MPPClient.js @@ -0,0 +1,79 @@ +// const Discord = require('discord.js'); +const Client = require('./mppt-client.js'); + +module.exports = class MPPClient { + static start(token) { + this.client = new Client('wss://mppclone.com:8443', token); + this.client.start(); + this.client.setChannel('✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧'); + this.bindEventListeners(); + } + + static bindEventListeners() { + this.client.on('hi', () => { + process.stdout.write("\n********************************START********************************\n"); + }); + + // this.client.on('messageUpdate', msg => { + // if (msg.system) return; + // if (msg.author.bot) return; + // this.handleMessage(msg); + // }); + + this.client.on('a', msg => { + // if (msg.system) return; + // if (msg.author.bot) return; + // if (msg.p.tag) + this.handleMessage(msg); + }); + + // this.client.on('guildMemberAdd', async guildMember => { + // let role; + // guildMember.guild.roles.cache.forEach(r => { + // if (r.name.toString() == guildMember.user.id.toString()) { + // role = r; + // } + // }); + + // if (typeof role !== 'undefined') console.log('found role: ' + role.name); + // if (!role) console.log('creating role'); + // if (!role) role = await guildMember.guild.roles.create({ + // data: { + // name: guildMember.user.id, + // color: Math.floor(Math.random()*16777215).toString(16) + // }, + // reason: guildMember.user.id + // }); + + // if (role) guildMember.roles.add(role); + // }); + } + + static handleMessage(msg) { + msg.args = msg.a.split(' '); + msg.cmd = msg.a.startsWith(gBot.prefix) ? msg.args[0].substring(gBot.prefix.length).trim() : ""; + msg.args = msg.args.slice(1); + msg.argcat = function(start, end) { + var parts = msg.args.slice(start || 0, end || undefined); + var result = ""; + for(var i = 0; i < parts.length; i++) { + result += parts[i]; + if(i + 1 < parts.length) { + result += " "; + } + } + return result; + }; + msg.rank = gBot.getRank(msg.p._id); + + gBot.emit('chat.receive', msg); + } + + static sendChat(str, skip = false) { + // DiscordClient.client.channels.cache.get('841331769658703954').send(str); + MPPClient.client.sendArray([{ + m:'a', + message: skip == true ? str : `\u034f${str}` + }]); + } +} diff --git a/src/RateLimit.js b/src/RateLimit.js new file mode 100644 index 0000000..d5fca89 --- /dev/null +++ b/src/RateLimit.js @@ -0,0 +1,69 @@ + +var RateLimit = function(interval_ms) { + this._interval_ms = interval_ms || 0; // (0 means no limit) + this._after = 0; +}; + +RateLimit.prototype.attempt = function(time) { + var time = time || Date.now(); + if(time < this._after) return false; + this._after = time + this._interval_ms; + return true; +}; + +RateLimit.prototype.setInterval = function(interval_ms) { + this._after += interval_ms - this._interval_ms; + this._interval_ms = interval_ms; +}; + + + +var RateLimitChain = function(num, interval_ms) { + this.setNumAndInterval(num, interval_ms); +}; + +RateLimitChain.prototype.attempt = function(time) { + var time = time || Date.now(); + for(var i = 0; i < this._chain.length; i++) { + if(this._chain[i].attempt(time)) return true; + } + return false; +}; + +RateLimitChain.prototype.setNumAndInterval = function(num, interval_ms) { + this._chain = []; + for(var i = 0; i < num; i++) { + this._chain.push(new RateLimit(interval_ms)); + } +}; + + + +var DataRateLimit = function(limit, interval_ms) { + this._limit = limit; + this._interval_ms = interval_ms || 0; // 0: per-attempt + this._after = 0; + this._size = 0; +}; + +DataRateLimit.prototype.attempt = function(size, time) { + var time = time || Date.now(); + + if(time >= this._after) { + this._size = 0; + this._after = time + this._interval_ms; + } + if(this._size + size <= this._limit) { + this._size += size; + return true; + } else { + return false; + } +}; + + + +var exports = typeof module !== "undefined" ? module.exports : this; +exports.RateLimit = RateLimit; +exports.RateLimitChain = RateLimitChain; +exports.DataRateLimit = DataRateLimit; \ No newline at end of file diff --git a/src/mppt-client.js b/src/mppt-client.js new file mode 100644 index 0000000..b0ebebf --- /dev/null +++ b/src/mppt-client.js @@ -0,0 +1,315 @@ + +if(typeof module !== "undefined") { + module.exports = Client; + WebSocket = require("ws"); + EventEmitter = require("events").EventEmitter; +} else { + this.Client = Client; +} + + +function mixin(obj1, obj2) { + for(var i in obj2) { + if(obj2.hasOwnProperty(i)) { + obj1[i] = obj2[i]; + } + } +}; + + +function Client(uri, token) { + EventEmitter.call(this); + this.uri = uri; + this.ws = undefined; + this.serverTimeOffset = 0; + this.user = undefined; + this.participantId = undefined; + this.channel = undefined; + this.ppl = {}; + this.connectionTime = undefined; + this.connectionAttempts = 0; + this.desiredChannelId = undefined; + this.desiredChannelSettings = undefined; + this.pingInterval = undefined; + this.canConnect = false; + this.noteBuffer = []; + this.noteBufferTime = 0; + this.noteFlushInterval = undefined; + this.token = token; + + this.bindEventListeners(); + + this.emit("status", "(Offline mode)"); +}; + +mixin(Client.prototype, EventEmitter.prototype); + +Client.prototype.constructor = Client; + +Client.prototype.isSupported = function() { + return typeof WebSocket === "function"; +}; + +Client.prototype.isConnected = function() { + return this.isSupported() && this.ws && this.ws.readyState === WebSocket.OPEN; +}; + +Client.prototype.isConnecting = function() { + return this.isSupported() && this.ws && this.ws.readyState === WebSocket.CONNECTING; +}; + +Client.prototype.start = function() { + this.canConnect = true; + this.connect(); +}; + +Client.prototype.stop = function() { + this.canConnect = false; + this.ws.close(); +}; + +Client.prototype.connect = function() { + if(!this.canConnect || !this.isSupported() || this.isConnected() || this.isConnecting()) + return; + this.emit("status", "Connecting..."); + this.ws = new WebSocket(this.uri); + this.ws.binaryType = "arraybuffer"; + var self = this; + this.ws.addEventListener("close", function(evt) { + self.user = undefined; + self.participantId = undefined; + self.channel = undefined; + self.setParticipants([]); + clearInterval(self.pingInterval); + clearInterval(self.noteFlushInterval); + + self.emit("disconnect"); + self.emit("status", "Offline mode"); + + // reconnect! + if(self.connectionTime) { + self.connectionTime = undefined; + self.connectionAttempts = 0; + } else { + ++self.connectionAttempts; + } + var ms_lut = [50, 2950, 7000, 10000]; + var idx = self.connectionAttempts; + if(idx >= ms_lut.length) idx = ms_lut.length - 1; + var ms = ms_lut[idx]; + setTimeout(self.connect.bind(self), ms); + }); + this.ws.addEventListener("open", function(evt) { + self.connectionTime = Date.now(); + self.sendArray([{m: "hi", token: self.token}]); + self.pingInterval = setInterval(function() { + self.sendArray([{m: "t", e: Date.now()}]); + }, 20000); + //self.sendArray([{m: "t", e: Date.now()}]); + self.noteBuffer = []; + self.noteBufferTime = 0; + self.noteFlushInterval = setInterval(function() { + if(self.noteBufferTime && self.noteBuffer.length > 0) { + self.sendArray([{m: "n", t: self.noteBufferTime + self.serverTimeOffset, n: self.noteBuffer}]); + self.noteBufferTime = 0; + self.noteBuffer = []; + } + }, 200); + + self.emit("connect"); + self.emit("status", "Joining channel..."); + }); + this.ws.addEventListener("message", function(evt) { + if(typeof evt.data !== 'string') return; + var transmission = JSON.parse(evt.data); + for(var i = 0; i < transmission.length; i++) { + var msg = transmission[i]; + self.emit(msg.m, msg); + } + }); +}; + +Client.prototype.bindEventListeners = function() { + var self = this; + this.on("hi", function(msg) { + self.user = msg.u; + self.receiveServerTime(msg.t, msg.e || undefined); + if(self.desiredChannelId) { + self.setChannel(); + } + }); + this.on("t", function(msg) { + self.receiveServerTime(msg.t, msg.e || undefined); + }); + this.on("ch", function(msg) { + self.desiredChannelId = msg.ch._id; + self.channel = msg.ch; + if(msg.p) self.participantId = msg.p; + self.setParticipants(msg.ppl); + }); + this.on("p", function(msg) { + self.participantUpdate(msg); + self.emit("participant update", self.findParticipantById(msg.id)); + }); + this.on("m", function(msg) { + if(self.ppl.hasOwnProperty(msg.id)) { + self.participantUpdate(msg); + } + }); + this.on("bye", function(msg) { + self.removeParticipant(msg.p); + }); +}; + +Client.prototype.send = function(raw) { + if(this.isConnected()) this.ws.send(raw); +}; + +Client.prototype.sendArray = function(arr) { + this.send(JSON.stringify(arr)); +}; + +Client.prototype.setChannel = function(id, set) { + this.desiredChannelId = id || this.desiredChannelId || "lobby"; + this.desiredChannelSettings = set || this.desiredChannelSettings || undefined; + this.sendArray([{m: "ch", _id: this.desiredChannelId, set: this.desiredChannelSettings}]); +}; + +Client.prototype.offlineChannelSettings = { + lobby: true, + visible: false, + chat: false, + crownsolo: false, + color:"#ecfaed" +}; + +Client.prototype.getChannelSetting = function(key) { + if(!this.isConnected() || !this.channel || !this.channel.settings) { + return this.offlineChannelSettings[key]; + } + return this.channel.settings[key]; +}; + +Client.prototype.offlineParticipant = { + name: "", + color: "#777" +}; + +Client.prototype.getOwnParticipant = function() { + return this.findParticipantById(this.participantId); +}; + +Client.prototype.setParticipants = function(ppl) { + // remove participants who left + for(var id in this.ppl) { + if(!this.ppl.hasOwnProperty(id)) continue; + var found = false; + for(var j = 0; j < ppl.length; j++) { + if(ppl[j].id === id) { + found = true; + break; + } + } + if(!found) { + this.removeParticipant(id); + } + } + // update all + for(var i = 0; i < ppl.length; i++) { + this.participantUpdate(ppl[i]); + } +}; + +Client.prototype.countParticipants = function() { + var count = 0; + for(var i in this.ppl) { + if(this.ppl.hasOwnProperty(i)) ++count; + } + return count; +}; + +Client.prototype.participantUpdate = function(update) { + var part = this.ppl[update.id] || null; + if(part === null) { + part = update; + this.ppl[part.id] = part; + this.emit("participant added", part); + this.emit("count", this.countParticipants()); + } else { + if(update.x) part.x = update.x; + if(update.y) part.y = update.y; + if(update.color) part.color = update.color; + if(update.name) part.name = update.name; + } +}; + +Client.prototype.removeParticipant = function(id) { + if(this.ppl.hasOwnProperty(id)) { + var part = this.ppl[id]; + delete this.ppl[id]; + this.emit("participant removed", part); + this.emit("count", this.countParticipants()); + } +}; + +Client.prototype.findParticipantById = function(id) { + return this.ppl[id] || this.offlineParticipant; +}; + +Client.prototype.isOwner = function() { + return this.channel && this.channel.crown && this.channel.crown.participantId === this.participantId; +}; + +Client.prototype.preventsPlaying = function() { + return this.isConnected() && !this.isOwner() && this.getChannelSetting("crownsolo") === true; +}; + +Client.prototype.receiveServerTime = function(time, echo) { + var self = this; + var now = Date.now(); + var target = time - now; + //console.log("Target serverTimeOffset: " + target); + var duration = 1000; + var step = 0; + var steps = 50; + var step_ms = duration / steps; + var difference = target - this.serverTimeOffset; + var inc = difference / steps; + var iv; + iv = setInterval(function() { + self.serverTimeOffset += inc; + if(++step >= steps) { + clearInterval(iv); + //console.log("serverTimeOffset reached: " + self.serverTimeOffset); + self.serverTimeOffset=target; + } + }, step_ms); + // smoothen + + //this.serverTimeOffset = time - now; // mostly time zone offset ... also the lags so todo smoothen this + // not smooth: + //if(echo) this.serverTimeOffset += echo - now; // mostly round trip time offset +}; + +Client.prototype.startNote = function(note, vel) { + if(this.isConnected()) { + var vel = typeof vel === "undefined" ? undefined : +vel.toFixed(3); + if(!this.noteBufferTime) { + this.noteBufferTime = Date.now(); + this.noteBuffer.push({n: note, v: vel}); + } else { + this.noteBuffer.push({d: Date.now() - this.noteBufferTime, n: note, v: vel}); + } + } +}; + +Client.prototype.stopNote = function(note) { + if(this.isConnected()) { + if(!this.noteBufferTime) { + this.noteBufferTime = Date.now(); + this.noteBuffer.push({n: note, s: 1}); + } else { + this.noteBuffer.push({d: Date.now() - this.noteBufferTime, n: note, s: 1}); + } + } +}; \ No newline at end of file diff --git a/src/pokedex.json b/src/pokedex.json new file mode 100644 index 0000000..3209348 --- /dev/null +++ b/src/pokedex.json @@ -0,0 +1,2485 @@ +[ + { + "id": "1", + "name": "Bulbasaur", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 45, + "attack": 49, + "defense": 49, + "specialAttack": 65, + "specialDefense": 65, + "speed": 45 + }, + "classification": "Seed Pokémon" + }, + { + "id": "2", + "name": "Ivysaur", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 62, + "defense": 63, + "specialAttack": 80, + "specialDefense": 80, + "speed": 60 + }, + "classification": "Seed Pokémon" + }, + { + "id": "3", + "name": "Venusaur", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 82, + "defense": 83, + "specialAttack": 100, + "specialDefense": 100, + "speed": 80 + }, + "classification": "Seed Pokémon" + }, + { + "id": "4", + "name": "Charmander", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 39, + "attack": 52, + "defense": 43, + "specialAttack": 60, + "specialDefense": 50, + "speed": 65 + }, + "classification": "Lizard Pokémon" + }, + { + "id": "5", + "name": "Charmeleon", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 58, + "attack": 64, + "defense": 58, + "specialAttack": 80, + "specialDefense": 65, + "speed": 80 + }, + "classification": "Flame Pokémon" + }, + { + "id": "6", + "name": "Charizard", + "type": [ + "Fire", + "Flying" + ], + "baseStatistics": { + "hitPoints": 78, + "attack": 84, + "defense": 78, + "specialAttack": 109, + "specialDefense": 85, + "speed": 100 + }, + "classification": "Flame Pokémon" + }, + { + "id": "7", + "name": "Squirtle", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 44, + "attack": 48, + "defense": 65, + "specialAttack": 50, + "specialDefense": 64, + "speed": 43 + }, + "classification": "Tiny Turtle Pokémon" + }, + { + "id": "8", + "name": "Wartortle", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 59, + "attack": 63, + "defense": 80, + "specialAttack": 65, + "specialDefense": 80, + "speed": 58 + }, + "classification": "Turtle Pokémon" + }, + { + "id": "9", + "name": "Blastoise", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 79, + "attack": 83, + "defense": 100, + "specialAttack": 85, + "specialDefense": 105, + "speed": 78 + }, + "classification": "Shellfish Pokémon" + }, + { + "id": "10", + "name": "Caterpie", + "type": [ + "Bug" + ], + "baseStatistics": { + "hitPoints": 45, + "attack": 30, + "defense": 35, + "specialAttack": 20, + "specialDefense": 20, + "speed": 45 + }, + "classification": "Worm Pokémon" + }, + { + "id": "11", + "name": "Metapod", + "type": [ + "Bug" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 20, + "defense": 55, + "specialAttack": 25, + "specialDefense": 25, + "speed": 30 + }, + "classification": "Cocoon Pokémon" + }, + { + "id": "12", + "name": "Butterfree", + "type": [ + "Bug", + "Flying" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 45, + "defense": 50, + "specialAttack": 90, + "specialDefense": 80, + "speed": 70 + }, + "classification": "Butterfly Pokémon" + }, + { + "id": "13", + "name": "Weedle", + "type": [ + "Bug", + "Poison" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 35, + "defense": 30, + "specialAttack": 20, + "specialDefense": 20, + "speed": 50 + }, + "classification": "Hairy Bug Pokémon" + }, + { + "id": "14", + "name": "Kakuna", + "type": [ + "Bug", + "Poison" + ], + "baseStatistics": { + "hitPoints": 45, + "attack": 25, + "defense": 50, + "specialAttack": 25, + "specialDefense": 25, + "speed": 35 + }, + "classification": "Cocoon Pokémon" + }, + { + "id": "15", + "name": "Beedrill", + "type": [ + "Bug", + "Poison" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 90, + "defense": 40, + "specialAttack": 45, + "specialDefense": 80, + "speed": 75 + }, + "classification": "Poison Bee Pokémon" + }, + { + "id": "16", + "name": "Pidgey", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 45, + "defense": 40, + "specialAttack": 35, + "specialDefense": 35, + "speed": 56 + }, + "classification": "Tiny Bird Pokémon" + }, + { + "id": "17", + "name": "Pidgeotto", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 63, + "attack": 60, + "defense": 55, + "specialAttack": 50, + "specialDefense": 50, + "speed": 71 + }, + "classification": "Bird Pokémon" + }, + { + "id": "18", + "name": "Pidgeot", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 83, + "attack": 80, + "defense": 75, + "specialAttack": 70, + "specialDefense": 70, + "speed": 101 + }, + "classification": "Bird Pokémon" + }, + { + "id": "19", + "name": "Rattata", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 56, + "defense": 35, + "specialAttack": 25, + "specialDefense": 35, + "speed": 72 + }, + "classification": "Mouse Pokémon" + }, + { + "id": "20", + "name": "Raticate", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 81, + "defense": 60, + "specialAttack": 50, + "specialDefense": 70, + "speed": 97 + }, + "classification": "Mouse Pokémon" + }, + { + "id": "21", + "name": "Spearow", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 60, + "defense": 30, + "specialAttack": 31, + "specialDefense": 31, + "speed": 70 + }, + "classification": "Tiny Bird Pokémon" + }, + { + "id": "22", + "name": "Fearow", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 90, + "defense": 65, + "specialAttack": 61, + "specialDefense": 61, + "speed": 100 + }, + "classification": "Beak Pokémon" + }, + { + "id": "23", + "name": "Ekans", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 60, + "defense": 44, + "specialAttack": 40, + "specialDefense": 54, + "speed": 55 + }, + "classification": "Snake Pokémon" + }, + { + "id": "24", + "name": "Arbok", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 95, + "defense": 69, + "specialAttack": 65, + "specialDefense": 79, + "speed": 80 + }, + "classification": "Cobra Pokémon" + }, + { + "id": "25", + "name": "Pikachu", + "type": [ + "Electric" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 55, + "defense": 30, + "specialAttack": 50, + "specialDefense": 50, + "speed": 90 + }, + "classification": "Mouse Pokémon" + }, + { + "id": "26", + "name": "Raichu", + "type": [ + "Electric" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 90, + "defense": 55, + "specialAttack": 90, + "specialDefense": 80, + "speed": 110 + }, + "classification": "Mouse Pokémon" + }, + { + "id": "27", + "name": "Sandshrew", + "type": [ + "Ground" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 75, + "defense": 85, + "specialAttack": 20, + "specialDefense": 30, + "speed": 40 + }, + "classification": "Mouse Pokémon" + }, + { + "id": "28", + "name": "Sandslash", + "type": [ + "Ground" + ], + "baseStatistics": { + "hitPoints": 75, + "attack": 100, + "defense": 110, + "specialAttack": 45, + "specialDefense": 55, + "speed": 65 + }, + "classification": "Mouse Pokémon" + }, + { + "id": "29", + "name": "Nidoran♀", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 47, + "defense": 52, + "specialAttack": 40, + "specialDefense": 40, + "speed": 41 + }, + "classification": "Poison Pin Pokémon" + }, + { + "id": "30", + "name": "Nidorina", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 70, + "attack": 62, + "defense": 67, + "specialAttack": 55, + "specialDefense": 55, + "speed": 56 + }, + "classification": "Poison Pin Pokémon" + }, + { + "id": "31", + "name": "Nidoqueen", + "type": [ + "Poison", + "Ground" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 92, + "defense": 87, + "specialAttack": 75, + "specialDefense": 85, + "speed": 76 + }, + "classification": "Drill Pokémon" + }, + { + "id": "32", + "name": "Nidoran♂", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 46, + "attack": 57, + "defense": 40, + "specialAttack": 40, + "specialDefense": 40, + "speed": 50 + }, + "classification": "Poison Pin Pokémon" + }, + { + "id": "33", + "name": "Nidorino", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 61, + "attack": 72, + "defense": 57, + "specialAttack": 55, + "specialDefense": 55, + "speed": 65 + }, + "classification": "Poison Pin Pokémon" + }, + { + "id": "34", + "name": "Nidoking", + "type": [ + "Poison", + "Ground" + ], + "baseStatistics": { + "hitPoints": 81, + "attack": 102, + "defense": 77, + "specialAttack": 85, + "specialDefense": 75, + "speed": 85 + }, + "classification": "Drill Pokémon" + }, + { + "id": "35", + "name": "Clefairy", + "type": [ + "Fairy" + ], + "baseStatistics": { + "hitPoints": 70, + "attack": 45, + "defense": 48, + "specialAttack": 60, + "specialDefense": 65, + "speed": 35 + }, + "classification": "Fairy Pokémon" + }, + { + "id": "36", + "name": "Clefable", + "type": [ + "Fairy" + ], + "baseStatistics": { + "hitPoints": 95, + "attack": 70, + "defense": 73, + "specialAttack": 95, + "specialDefense": 90, + "speed": 60 + }, + "classification": "Fairy Pokémon" + }, + { + "id": "37", + "name": "Vulpix", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 38, + "attack": 41, + "defense": 40, + "specialAttack": 50, + "specialDefense": 65, + "speed": 65 + }, + "classification": "Fox Pokémon" + }, + { + "id": "38", + "name": "Ninetales", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 73, + "attack": 76, + "defense": 75, + "specialAttack": 81, + "specialDefense": 100, + "speed": 100 + }, + "classification": "Fox Pokémon" + }, + { + "id": "39", + "name": "Jigglypuff", + "type": [ + "Normal", + "Fairy" + ], + "baseStatistics": { + "hitPoints": 115, + "attack": 45, + "defense": 20, + "specialAttack": 45, + "specialDefense": 25, + "speed": 20 + }, + "classification": "Balloon Pokémon" + }, + { + "id": "40", + "name": "Wigglytuff", + "type": [ + "Normal", + "Fairy" + ], + "baseStatistics": { + "hitPoints": 140, + "attack": 70, + "defense": 45, + "specialAttack": 85, + "specialDefense": 50, + "speed": 45 + }, + "classification": "Balloon Pokémon" + }, + { + "id": "41", + "name": "Zubat", + "type": [ + "Poison", + "Flying" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 45, + "defense": 35, + "specialAttack": 30, + "specialDefense": 30, + "speed": 40 + }, + "classification": "Bat Pokémon" + }, + { + "id": "42", + "name": "Golbat", + "type": [ + "Poison", + "Flying" + ], + "baseStatistics": { + "hitPoints": 75, + "attack": 80, + "defense": 70, + "specialAttack": 65, + "specialDefense": 75, + "speed": 90 + }, + "classification": "Bat Pokémon" + }, + { + "id": "43", + "name": "Oddish", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 45, + "attack": 50, + "defense": 55, + "specialAttack": 75, + "specialDefense": 65, + "speed": 30 + }, + "classification": "Weed Pokémon" + }, + { + "id": "44", + "name": "Gloom", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 65, + "defense": 70, + "specialAttack": 85, + "specialDefense": 75, + "speed": 40 + }, + "classification": "Weed Pokémon" + }, + { + "id": "45", + "name": "Vileplume", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 75, + "attack": 80, + "defense": 85, + "specialAttack": 110, + "specialDefense": 90, + "speed": 50 + }, + "classification": "Flower Pokémon" + }, + { + "id": "46", + "name": "Paras", + "type": [ + "Bug", + "Grass" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 70, + "defense": 55, + "specialAttack": 45, + "specialDefense": 55, + "speed": 25 + }, + "classification": "Mushroom Pokémon" + }, + { + "id": "47", + "name": "Parasect", + "type": [ + "Bug", + "Grass" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 95, + "defense": 80, + "specialAttack": 60, + "specialDefense": 80, + "speed": 30 + }, + "classification": "Mushroom Pokémon" + }, + { + "id": "48", + "name": "Venonat", + "type": [ + "Bug", + "Poison" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 55, + "defense": 50, + "specialAttack": 40, + "specialDefense": 55, + "speed": 45 + }, + "classification": "Insect Pokémon" + }, + { + "id": "49", + "name": "Venomoth", + "type": [ + "Bug", + "Poison" + ], + "baseStatistics": { + "hitPoints": 70, + "attack": 65, + "defense": 60, + "specialAttack": 90, + "specialDefense": 75, + "speed": 90 + }, + "classification": "Poison Moth Pokémon" + }, + { + "id": "50", + "name": "Diglett", + "type": [ + "Ground" + ], + "baseStatistics": { + "hitPoints": 10, + "attack": 55, + "defense": 25, + "specialAttack": 35, + "specialDefense": 45, + "speed": 95 + }, + "classification": "Mole Pokémon" + }, + { + "id": "51", + "name": "Dugtrio", + "type": [ + "Ground" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 100, + "defense": 50, + "specialAttack": 50, + "specialDefense": 70, + "speed": 120 + }, + "classification": "Mole Pokémon" + }, + { + "id": "52", + "name": "Meowth", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 45, + "defense": 35, + "specialAttack": 40, + "specialDefense": 40, + "speed": 90 + }, + "classification": "Scratch Cat Pokémon" + }, + { + "id": "53", + "name": "Persian", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 70, + "defense": 60, + "specialAttack": 65, + "specialDefense": 65, + "speed": 115 + }, + "classification": "Classy Cat Pokémon" + }, + { + "id": "54", + "name": "Psyduck", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 52, + "defense": 48, + "specialAttack": 65, + "specialDefense": 50, + "speed": 55 + }, + "classification": "Duck Pokémon" + }, + { + "id": "55", + "name": "Golduck", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 82, + "defense": 78, + "specialAttack": 95, + "specialDefense": 80, + "speed": 85 + }, + "classification": "Duck Pokémon" + }, + { + "id": "56", + "name": "Mankey", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 80, + "defense": 35, + "specialAttack": 35, + "specialDefense": 45, + "speed": 70 + }, + "classification": "Pig Monkey Pokémon" + }, + { + "id": "57", + "name": "Primeape", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 105, + "defense": 60, + "specialAttack": 60, + "specialDefense": 70, + "speed": 95 + }, + "classification": "Pig Monkey Pokémon" + }, + { + "id": "58", + "name": "Growlithe", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 70, + "defense": 45, + "specialAttack": 70, + "specialDefense": 50, + "speed": 60 + }, + "classification": "Puppy Pokémon" + }, + { + "id": "59", + "name": "Arcanine", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 110, + "defense": 80, + "specialAttack": 100, + "specialDefense": 80, + "speed": 95 + }, + "classification": "Legendary Pokémon" + }, + { + "id": "60", + "name": "Poliwag", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 50, + "defense": 40, + "specialAttack": 40, + "specialDefense": 40, + "speed": 90 + }, + "classification": "Tadpole Pokémon" + }, + { + "id": "61", + "name": "Poliwhirl", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 65, + "defense": 65, + "specialAttack": 50, + "specialDefense": 50, + "speed": 90 + }, + "classification": "Tadpole Pokémon" + }, + { + "id": "62", + "name": "Poliwrath", + "type": [ + "Water", + "Fighting" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 95, + "defense": 95, + "specialAttack": 70, + "specialDefense": 90, + "speed": 70 + }, + "classification": "Tadpole Pokémon" + }, + { + "id": "63", + "name": "Abra", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 25, + "attack": 20, + "defense": 15, + "specialAttack": 105, + "specialDefense": 55, + "speed": 90 + }, + "classification": "Psi Pokémon" + }, + { + "id": "64", + "name": "Kadabra", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 35, + "defense": 30, + "specialAttack": 120, + "specialDefense": 70, + "speed": 105 + }, + "classification": "Psi Pokémon" + }, + { + "id": "65", + "name": "Alakazam", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 50, + "defense": 45, + "specialAttack": 135, + "specialDefense": 95, + "speed": 120 + }, + "classification": "Psi Pokémon" + }, + { + "id": "66", + "name": "Machop", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 70, + "attack": 80, + "defense": 50, + "specialAttack": 35, + "specialDefense": 35, + "speed": 35 + }, + "classification": "Superpower Pokémon" + }, + { + "id": "67", + "name": "Machoke", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 100, + "defense": 70, + "specialAttack": 50, + "specialDefense": 60, + "speed": 45 + }, + "classification": "Superpower Pokémon" + }, + { + "id": "68", + "name": "Machamp", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 130, + "defense": 80, + "specialAttack": 65, + "specialDefense": 85, + "speed": 55 + }, + "classification": "Superpower Pokémon" + }, + { + "id": "69", + "name": "Bellsprout", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 75, + "defense": 35, + "specialAttack": 70, + "specialDefense": 30, + "speed": 40 + }, + "classification": "Flower Pokémon" + }, + { + "id": "70", + "name": "Weepinbell", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 90, + "defense": 50, + "specialAttack": 85, + "specialDefense": 45, + "speed": 55 + }, + "classification": "Flycatcher Pokémon" + }, + { + "id": "71", + "name": "Victreebel", + "type": [ + "Grass", + "Poison" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 105, + "defense": 65, + "specialAttack": 100, + "specialDefense": 70, + "speed": 70 + }, + "classification": "Flycatcher Pokémon" + }, + { + "id": "72", + "name": "Tentacool", + "type": [ + "Water", + "Poison" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 40, + "defense": 35, + "specialAttack": 50, + "specialDefense": 100, + "speed": 70 + }, + "classification": "Jellyfish Pokémon" + }, + { + "id": "73", + "name": "Tentacruel", + "type": [ + "Water", + "Poison" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 70, + "defense": 65, + "specialAttack": 80, + "specialDefense": 120, + "speed": 100 + }, + "classification": "Jellyfish Pokémon" + }, + { + "id": "74", + "name": "Geodude", + "type": [ + "Rock", + "Ground" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 80, + "defense": 100, + "specialAttack": 30, + "specialDefense": 30, + "speed": 20 + }, + "classification": "Rock Pokémon" + }, + { + "id": "75", + "name": "Graveler", + "type": [ + "Rock", + "Ground" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 95, + "defense": 115, + "specialAttack": 45, + "specialDefense": 45, + "speed": 35 + }, + "classification": "Rock Pokémon" + }, + { + "id": "76", + "name": "Golem", + "type": [ + "Rock", + "Ground" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 120, + "defense": 130, + "specialAttack": 55, + "specialDefense": 65, + "speed": 45 + }, + "classification": "Megaton Pokémon" + }, + { + "id": "77", + "name": "Ponyta", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 85, + "defense": 55, + "specialAttack": 65, + "specialDefense": 65, + "speed": 90 + }, + "classification": "Fire Horse Pokémon" + }, + { + "id": "78", + "name": "Rapidash", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 100, + "defense": 70, + "specialAttack": 80, + "specialDefense": 80, + "speed": 105 + }, + "classification": "Fire Horse Pokémon" + }, + { + "id": "79", + "name": "Slowpoke", + "type": [ + "Water", + "Psychic" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 65, + "defense": 65, + "specialAttack": 40, + "specialDefense": 40, + "speed": 15 + }, + "classification": "Dopey Pokémon" + }, + { + "id": "80", + "name": "Slowbro", + "type": [ + "Water", + "Psychic" + ], + "baseStatistics": { + "hitPoints": 95, + "attack": 75, + "defense": 110, + "specialAttack": 100, + "specialDefense": 80, + "speed": 30 + }, + "classification": "Hermit Crab Pokémon" + }, + { + "id": "81", + "name": "Magnemite", + "type": [ + "Electric", + "Steel" + ], + "baseStatistics": { + "hitPoints": 25, + "attack": 35, + "defense": 70, + "specialAttack": 95, + "specialDefense": 55, + "speed": 45 + }, + "classification": "Magnet Pokémon" + }, + { + "id": "82", + "name": "Magneton", + "type": [ + "Electric", + "Steel" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 60, + "defense": 95, + "specialAttack": 120, + "specialDefense": 70, + "speed": 70 + }, + "classification": "Magnet Pokémon" + }, + { + "id": "83", + "name": "Farfetch'd", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 52, + "attack": 65, + "defense": 55, + "specialAttack": 58, + "specialDefense": 62, + "speed": 60 + }, + "classification": "Wild Duck Pokémon" + }, + { + "id": "84", + "name": "Doduo", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 85, + "defense": 45, + "specialAttack": 35, + "specialDefense": 35, + "speed": 75 + }, + "classification": "Twin Bird Pokémon" + }, + { + "id": "85", + "name": "Dodrio", + "type": [ + "Normal", + "Flying" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 110, + "defense": 70, + "specialAttack": 60, + "specialDefense": 60, + "speed": 110 + }, + "classification": "Triple Bird Pokémon" + }, + { + "id": "86", + "name": "Seel", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 45, + "defense": 55, + "specialAttack": 45, + "specialDefense": 70, + "speed": 45 + }, + "classification": "Sea Lion Pokémon" + }, + { + "id": "87", + "name": "Dewgong", + "type": [ + "Water", + "Ice" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 70, + "defense": 80, + "specialAttack": 70, + "specialDefense": 95, + "speed": 70 + }, + "classification": "Sea Lion Pokémon" + }, + { + "id": "88", + "name": "Grimer", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 80, + "defense": 50, + "specialAttack": 40, + "specialDefense": 50, + "speed": 25 + }, + "classification": "Sludge Pokémon" + }, + { + "id": "89", + "name": "Muk", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 105, + "attack": 105, + "defense": 75, + "specialAttack": 65, + "specialDefense": 100, + "speed": 50 + }, + "classification": "Sludge Pokémon" + }, + { + "id": "90", + "name": "Shellder", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 65, + "defense": 100, + "specialAttack": 45, + "specialDefense": 25, + "speed": 40 + }, + "classification": "Bivalve Pokémon" + }, + { + "id": "91", + "name": "Cloyster", + "type": [ + "Water", + "Ice" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 95, + "defense": 180, + "specialAttack": 85, + "specialDefense": 45, + "speed": 70 + }, + "classification": "Bivalve Pokémon" + }, + { + "id": "92", + "name": "Gastly", + "type": [ + "Ghost", + "Poison" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 35, + "defense": 30, + "specialAttack": 100, + "specialDefense": 35, + "speed": 80 + }, + "classification": "Gas Pokémon" + }, + { + "id": "93", + "name": "Haunter", + "type": [ + "Ghost", + "Poison" + ], + "baseStatistics": { + "hitPoints": 45, + "attack": 50, + "defense": 45, + "specialAttack": 115, + "specialDefense": 55, + "speed": 95 + }, + "classification": "Gas Pokémon" + }, + { + "id": "94", + "name": "Gengar", + "type": [ + "Ghost", + "Poison" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 65, + "defense": 60, + "specialAttack": 130, + "specialDefense": 75, + "speed": 110 + }, + "classification": "Shadow Pokémon" + }, + { + "id": "95", + "name": "Onix", + "type": [ + "Rock", + "Ground" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 45, + "defense": 160, + "specialAttack": 30, + "specialDefense": 45, + "speed": 70 + }, + "classification": "Rock Snake Pokémon" + }, + { + "id": "96", + "name": "Drowzee", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 48, + "defense": 45, + "specialAttack": 43, + "specialDefense": 90, + "speed": 42 + }, + "classification": "Hypnosis Pokémon" + }, + { + "id": "97", + "name": "Hypno", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 85, + "attack": 73, + "defense": 70, + "specialAttack": 73, + "specialDefense": 115, + "speed": 67 + }, + "classification": "Hypnosis Pokémon" + }, + { + "id": "98", + "name": "Krabby", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 105, + "defense": 90, + "specialAttack": 25, + "specialDefense": 25, + "speed": 50 + }, + "classification": "River Crab Pokémon" + }, + { + "id": "99", + "name": "Kingler", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 130, + "defense": 115, + "specialAttack": 50, + "specialDefense": 50, + "speed": 75 + }, + "classification": "Pincer Pokémon" + }, + { + "id": "100", + "name": "Voltorb", + "type": [ + "Electric" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 30, + "defense": 50, + "specialAttack": 55, + "specialDefense": 55, + "speed": 100 + }, + "classification": "Ball Pokémon" + }, + { + "id": "101", + "name": "Electrode", + "type": [ + "Electric" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 50, + "defense": 70, + "specialAttack": 80, + "specialDefense": 80, + "speed": 140 + }, + "classification": "Ball Pokémon" + }, + { + "id": "102", + "name": "Exeggcute", + "type": [ + "Grass", + "Psychic" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 40, + "defense": 80, + "specialAttack": 60, + "specialDefense": 45, + "speed": 40 + }, + "classification": "Egg Pokémon" + }, + { + "id": "103", + "name": "Exeggutor", + "type": [ + "Grass", + "Psychic" + ], + "baseStatistics": { + "hitPoints": 95, + "attack": 95, + "defense": 85, + "specialAttack": 125, + "specialDefense": 75, + "speed": 55 + }, + "classification": "Coconut Pokémon" + }, + { + "id": "104", + "name": "Cubone", + "type": [ + "Ground" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 50, + "defense": 95, + "specialAttack": 40, + "specialDefense": 50, + "speed": 35 + }, + "classification": "Lonely Pokémon" + }, + { + "id": "105", + "name": "Marowak", + "type": [ + "Ground" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 80, + "defense": 110, + "specialAttack": 50, + "specialDefense": 80, + "speed": 45 + }, + "classification": "Bone Keeper Pokémon" + }, + { + "id": "106", + "name": "Hitmonlee", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 120, + "defense": 53, + "specialAttack": 35, + "specialDefense": 110, + "speed": 87 + }, + "classification": "Kicking Pokémon" + }, + { + "id": "107", + "name": "Hitmonchan", + "type": [ + "Fighting" + ], + "baseStatistics": { + "hitPoints": 50, + "attack": 105, + "defense": 79, + "specialAttack": 35, + "specialDefense": 110, + "speed": 76 + }, + "classification": "Punching Pokémon" + }, + { + "id": "108", + "name": "Lickitung", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 55, + "defense": 75, + "specialAttack": 60, + "specialDefense": 75, + "speed": 30 + }, + "classification": "Licking Pokémon" + }, + { + "id": "109", + "name": "Koffing", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 65, + "defense": 95, + "specialAttack": 60, + "specialDefense": 45, + "speed": 35 + }, + "classification": "Poison Gas Pokémon" + }, + { + "id": "110", + "name": "Weezing", + "type": [ + "Poison" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 90, + "defense": 120, + "specialAttack": 85, + "specialDefense": 70, + "speed": 60 + }, + "classification": "Poison Gas Pokémon" + }, + { + "id": "111", + "name": "Rhyhorn", + "type": [ + "Ground", + "Rock" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 85, + "defense": 95, + "specialAttack": 30, + "specialDefense": 30, + "speed": 25 + }, + "classification": "Spikes Pokémon" + }, + { + "id": "112", + "name": "Rhydon", + "type": [ + "Ground", + "Rock" + ], + "baseStatistics": { + "hitPoints": 105, + "attack": 130, + "defense": 120, + "specialAttack": 45, + "specialDefense": 45, + "speed": 40 + }, + "classification": "Drill Pokémon" + }, + { + "id": "113", + "name": "Chansey", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 250, + "attack": 5, + "defense": 5, + "specialAttack": 35, + "specialDefense": 105, + "speed": 50 + }, + "classification": "Egg Pokémon" + }, + { + "id": "114", + "name": "Tangela", + "type": [ + "Grass" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 55, + "defense": 115, + "specialAttack": 100, + "specialDefense": 40, + "speed": 60 + }, + "classification": "Vine Pokémon" + }, + { + "id": "115", + "name": "Kangaskhan", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 105, + "attack": 95, + "defense": 80, + "specialAttack": 40, + "specialDefense": 80, + "speed": 90 + }, + "classification": "Parent Pokémon" + }, + { + "id": "116", + "name": "Horsea", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 40, + "defense": 70, + "specialAttack": 70, + "specialDefense": 25, + "speed": 60 + }, + "classification": "Dragon Pokémon" + }, + { + "id": "117", + "name": "Seadra", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 65, + "defense": 95, + "specialAttack": 95, + "specialDefense": 45, + "speed": 85 + }, + "classification": "Dragon Pokémon" + }, + { + "id": "118", + "name": "Goldeen", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 45, + "attack": 67, + "defense": 60, + "specialAttack": 35, + "specialDefense": 50, + "speed": 63 + }, + "classification": "Goldfish Pokémon" + }, + { + "id": "119", + "name": "Seaking", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 92, + "defense": 65, + "specialAttack": 65, + "specialDefense": 80, + "speed": 68 + }, + "classification": "Goldfish Pokémon" + }, + { + "id": "120", + "name": "Staryu", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 45, + "defense": 55, + "specialAttack": 70, + "specialDefense": 55, + "speed": 85 + }, + "classification": "Star Shape Pokémon" + }, + { + "id": "121", + "name": "Starmie", + "type": [ + "Water", + "Psychic" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 75, + "defense": 85, + "specialAttack": 100, + "specialDefense": 85, + "speed": 115 + }, + "classification": "Mysterious Pokémon" + }, + { + "id": "122", + "name": "Mr. Mime", + "type": [ + "Psychic", + "Fairy" + ], + "baseStatistics": { + "hitPoints": 40, + "attack": 45, + "defense": 65, + "specialAttack": 100, + "specialDefense": 120, + "speed": 90 + }, + "classification": "Barrier Pokémon" + }, + { + "id": "123", + "name": "Scyther", + "type": [ + "Bug", + "Flying" + ], + "baseStatistics": { + "hitPoints": 70, + "attack": 110, + "defense": 80, + "specialAttack": 55, + "specialDefense": 80, + "speed": 105 + }, + "classification": "Mantis Pokémon" + }, + { + "id": "124", + "name": "Jynx", + "type": [ + "Ice", + "Psychic" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 50, + "defense": 35, + "specialAttack": 115, + "specialDefense": 95, + "speed": 95 + }, + "classification": "Human Shape Pokémon" + }, + { + "id": "125", + "name": "Electabuzz", + "type": [ + "Electric" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 83, + "defense": 57, + "specialAttack": 95, + "specialDefense": 85, + "speed": 105 + }, + "classification": "Electric Pokémon" + }, + { + "id": "126", + "name": "Magmar", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 95, + "defense": 57, + "specialAttack": 100, + "specialDefense": 85, + "speed": 93 + }, + "classification": "Spitfire Pokémon" + }, + { + "id": "127", + "name": "Pinsir", + "type": [ + "Bug" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 125, + "defense": 100, + "specialAttack": 55, + "specialDefense": 70, + "speed": 85 + }, + "classification": "Stag Beetle Pokémon" + }, + { + "id": "128", + "name": "Tauros", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 75, + "attack": 100, + "defense": 95, + "specialAttack": 40, + "specialDefense": 70, + "speed": 110 + }, + "classification": "Wild Bull Pokémon" + }, + { + "id": "129", + "name": "Magikarp", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 20, + "attack": 10, + "defense": 55, + "specialAttack": 15, + "specialDefense": 20, + "speed": 80 + }, + "classification": "Fish Pokémon" + }, + { + "id": "130", + "name": "Gyarados", + "type": [ + "Water", + "Flying" + ], + "baseStatistics": { + "hitPoints": 95, + "attack": 125, + "defense": 79, + "specialAttack": 60, + "specialDefense": 100, + "speed": 81 + }, + "classification": "Atrocious Pokémon" + }, + { + "id": "131", + "name": "Lapras", + "type": [ + "Water", + "Ice" + ], + "baseStatistics": { + "hitPoints": 130, + "attack": 85, + "defense": 80, + "specialAttack": 85, + "specialDefense": 95, + "speed": 60 + }, + "classification": "Transport Pokémon" + }, + { + "id": "132", + "name": "Ditto", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 48, + "attack": 48, + "defense": 48, + "specialAttack": 48, + "specialDefense": 48, + "speed": 48 + }, + "classification": "Transform Pokémon" + }, + { + "id": "133", + "name": "Eevee", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 55, + "attack": 55, + "defense": 50, + "specialAttack": 45, + "specialDefense": 65, + "speed": 55 + }, + "classification": "Evolution Pokémon" + }, + { + "id": "134", + "name": "Vaporeon", + "type": [ + "Water" + ], + "baseStatistics": { + "hitPoints": 130, + "attack": 65, + "defense": 60, + "specialAttack": 110, + "specialDefense": 95, + "speed": 65 + }, + "classification": "Bubble Jet Pokémon" + }, + { + "id": "135", + "name": "Jolteon", + "type": [ + "Electric" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 65, + "defense": 60, + "specialAttack": 110, + "specialDefense": 95, + "speed": 130 + }, + "classification": "Thunderbolt Pokémon" + }, + { + "id": "136", + "name": "Flareon", + "type": [ + "Fire" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 130, + "defense": 60, + "specialAttack": 95, + "specialDefense": 110, + "speed": 65 + }, + "classification": "Flame Pokémon" + }, + { + "id": "137", + "name": "Porygon", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 65, + "attack": 60, + "defense": 70, + "specialAttack": 85, + "specialDefense": 75, + "speed": 40 + }, + "classification": "Virtual Pokémon" + }, + { + "id": "138", + "name": "Omanyte", + "type": [ + "Rock", + "Water" + ], + "baseStatistics": { + "hitPoints": 35, + "attack": 40, + "defense": 100, + "specialAttack": 90, + "specialDefense": 55, + "speed": 35 + }, + "classification": "Spiral Pokémon" + }, + { + "id": "139", + "name": "Omastar", + "type": [ + "Rock", + "Water" + ], + "baseStatistics": { + "hitPoints": 70, + "attack": 60, + "defense": 125, + "specialAttack": 115, + "specialDefense": 70, + "speed": 55 + }, + "classification": "Spiral Pokémon" + }, + { + "id": "140", + "name": "Kabuto", + "type": [ + "Rock", + "Water" + ], + "baseStatistics": { + "hitPoints": 30, + "attack": 80, + "defense": 90, + "specialAttack": 55, + "specialDefense": 45, + "speed": 55 + }, + "classification": "Shellfish Pokémon" + }, + { + "id": "141", + "name": "Kabutops", + "type": [ + "Rock", + "Water" + ], + "baseStatistics": { + "hitPoints": 60, + "attack": 115, + "defense": 105, + "specialAttack": 65, + "specialDefense": 70, + "speed": 80 + }, + "classification": "Shellfish Pokémon" + }, + { + "id": "142", + "name": "Aerodactyl", + "type": [ + "Rock", + "Flying" + ], + "baseStatistics": { + "hitPoints": 80, + "attack": 105, + "defense": 65, + "specialAttack": 60, + "specialDefense": 75, + "speed": 130 + }, + "classification": "Fossil Pokémon" + }, + { + "id": "143", + "name": "Snorlax", + "type": [ + "Normal" + ], + "baseStatistics": { + "hitPoints": 160, + "attack": 110, + "defense": 65, + "specialAttack": 65, + "specialDefense": 110, + "speed": 30 + }, + "classification": "Sleeping Pokémon" + }, + { + "id": "144", + "name": "Articuno", + "type": [ + "Ice", + "Flying" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 85, + "defense": 100, + "specialAttack": 95, + "specialDefense": 125, + "speed": 85 + }, + "classification": "Freeze Pokémon" + }, + { + "id": "145", + "name": "Zapdos", + "type": [ + "Electric", + "Flying" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 90, + "defense": 85, + "specialAttack": 125, + "specialDefense": 90, + "speed": 100 + }, + "classification": "Electric Pokémon" + }, + { + "id": "146", + "name": "Moltres", + "type": [ + "Fire", + "Flying" + ], + "baseStatistics": { + "hitPoints": 90, + "attack": 100, + "defense": 90, + "specialAttack": 125, + "specialDefense": 85, + "speed": 90 + }, + "classification": "Flame Pokémon" + }, + { + "id": "147", + "name": "Dratini", + "type": [ + "Dragon" + ], + "baseStatistics": { + "hitPoints": 41, + "attack": 64, + "defense": 45, + "specialAttack": 50, + "specialDefense": 50, + "speed": 50 + }, + "classification": "Dragon Pokémon" + }, + { + "id": "148", + "name": "Dragonair", + "type": [ + "Dragon" + ], + "baseStatistics": { + "hitPoints": 61, + "attack": 84, + "defense": 65, + "specialAttack": 70, + "specialDefense": 70, + "speed": 70 + }, + "classification": "Dragon Pokémon" + }, + { + "id": "149", + "name": "Dragonite", + "type": [ + "Dragon", + "Flying" + ], + "baseStatistics": { + "hitPoints": 91, + "attack": 134, + "defense": 95, + "specialAttack": 100, + "specialDefense": 100, + "speed": 80 + }, + "classification": "Dragon Pokémon" + }, + { + "id": "150", + "name": "Mewtwo", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 106, + "attack": 110, + "defense": 90, + "specialAttack": 154, + "specialDefense": 90, + "speed": 130 + }, + "classification": "Genetic Pokémon" + }, + { + "id": "151", + "name": "Mew", + "type": [ + "Psychic" + ], + "baseStatistics": { + "hitPoints": 100, + "attack": 100, + "defense": 100, + "specialAttack": 100, + "specialDefense": 100, + "speed": 100 + }, + "classification": "New Species Pokémon" + } +] \ No newline at end of file