/*jshint laxbreak:true,shadow:true,undef:true,evil:true,trailing:true,proto:true,withstmt:true,-W030*/ /*global sys, module, SESSION, script, safaribot, require, updateModule, staffchannel, sachannel, pokedex */ var MemoryHash = require("memoryhash.js").MemoryHash; var utilities = require('utilities.js'); var html_escape = utilities.html_escape; var Bot = require('bot.js').Bot; function Safari() { /* Variables */ { var safari = this; var safariUpdating = false; var needsUpdating = false; var safchan; var defaultChannel = "Safari"; var safaribot = new Bot("Tauros"); var tutorbot = new Bot("Kangaskhan"); var daycarebot = new Bot("Chansey"); var closedMessage = "±PA: Ding-dong! The Safari Game is over! Please return to the front counter while an update is applied!"; var openedMessage = "±Attendant: Welcome to the Safari Zone! You can catch all the Pokémon you want in the park! We'll call you on the PA when you run out of time or an update is needed!"; var wildPokemonMessage = "A {2}wild {0} appeared! (BST: {1})"; var separator = "*** *********************************************************** ***"; var lastCheckedRepo = 0; var scriptHashCode = null; var saveFiles = "scriptdata/safarisaves.txt"; var saveBackupFile1 = "scriptdata/safari/savesBackup1.txt"; var saveBackupFile2 = "scriptdata/safari/savesBackup2.txt"; var saveBackupFile3 = "scriptdata/safari/savesBackup3.txt"; var saveBackupFile4 = "scriptdata/safari/savesBackup4.txt"; var saveBackupFile5 = "scriptdata/safari/savesBackup5.txt"; var deletedSaveFiles = "scriptdata/safari/deletedsafarisaves.txt"; var themesFile = "scriptdata/safari/themes.txt"; var decorationsFile = "scriptdata/safari/decorations.txt"; var missionsFile = "scriptdata/safari/missions.txt"; var fortunesFile = "scriptdata/safari/fortunes.txt"; var tradeLog = "scriptdata/safaritrades.txt"; var rareTradeLog = "scriptdata/safari/raretrades.txt"; var shopLog = "scriptdata/safarishoplog.txt"; var auctionLog = "scriptdata/safariauctions.txt"; var altLog = "scriptdata/safarialtlog.txt"; var lostLog = "scriptdata/safaricommands.txt"; var mythLog = "scriptdata/safari/mythlog.txt"; var eventLog = "scriptdata/safari/eventlog.txt"; var giftLog = "scriptdata/safari/giftlog.txt"; var crossLog = "scriptdata/safari/crosslog.txt"; var miscLog = "scriptdata/safari/misclog.txt"; var questLog = "scriptdata/safari/questlog.txt"; var tradebansFile = "scriptdata/safaribans.txt"; var saltbansFile = "scriptdata/safarisalt.txt"; var permFile = "scriptdata/safariobjects.txt"; var configFile = "scriptdata/safari/config.txt"; var rafflePlayersFile = "scriptdata/safari/raffleplayers.txt"; var idnumFile = "scriptdata/safari/idnum.txt"; var permObj; var configObj; var tradeBans; var saltBans; var rawPlayers; var backupPlayers1; var backupPlayers2; var backupPlayers3; var backupPlayers4; var backupPlayers5; var cookedPlayers; var rafflePlayers; var mAuctionsData; var mAuctionsMarket; var lastEscapedMons; var npcShop; var idnumList; var lastIdAssigned; var rafflePrizeObj = null; var marketData = {}; var dayCareEnabled = true; var needsPechaCleared = []; var bakingDebug = false; var npcMatchAlive = []; var buyLuckyPossible = false; var npcBetCap = 200; var eliteCounterPicks = []; var eliteStrongCounterPicks = []; var vbdebug = false; var officialVolleyballTeam1 = "Paranormal Phanpies"; var officialVolleyballTeam2 = "Gloomy Oddishes"; var officialVolleyballWins1 = 0; var officialVolleyballWins2 = 0; var globalWildItems = {}; var customWildItems = {}; var skillData = {}; var skillUnlocks = {}; var currentDay = 1; var ghostSprite = "data:image/png;base64,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"; var bonusClues = []; var bonusCluesInteract = []; var highestDexNum = 1026; // should be 1 more than the actual dex num var cageMode = false; var cage = ""; var langPack = null; var starters = [1, 4, 7]; var maxConsecutiveCombo = 9999; var defaultRareBST = 550; var nextClod = 0; var auraHours = 24; // mostly any formes that contain significant type/BST differences from their base forme var noDailyBonusForms = [328350, 66015, 197087, 131551, 262623, 328159, 66091, 66282, 131730, 66310, 131846, 197382, 262918, 328454, 393990, 459526, 66500, 66560, 132096]; var teraCrowns = { "Normal": "data:image/png;base64,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", "Fighting": "data:image/png;base64,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", "Flying": "data:image/png;base64,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", "Poison": "data:image/png;base64,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", "Ground": "data:image/png;base64,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", "Rock": "data:image/png;base64,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", "Bug": "data:image/png;base64,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", "Ghost": "data:image/png;base64,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", "Steel": "data:image/png;base64,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", "Fire": "data:image/png;base64,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", "Water": "data:image/png;base64,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", "Grass": "data:image/png;base64,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", "Electric": "data:image/png;base64,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", "Psychic": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAYCAYAAAAMAljuAAAAAXNSR0IArs4c6QAADONJREFUaEPtmmt4VNW5x/977cvckswMQ25AgqCAEWwKRSMIGKKlRYvVVqpFwT5PPAeLtpWqh+dY2kM1EWmDtmgBsVatttqm9KYYBRoSaCkBSUUuETUgCZCQMJlkMpc9+7bOs9eeGTKTTIrafmjL+jLsdV/vb72X9QYO8dJXXTuf642Vc/3G2uyNS7oS9Rd+/3kS6Lu55vbTWZxY8uL9zydW4QYuF9nw+vvkYKDQoNjgoPoPuQtg/ik0uj9XXSg47RsA+sVOF51b8tKDDUMCkdfX/TcV7E9jfyeg8lYfanyiTekgoIYOGvKD6ioMXf9E8/2rDya8JVd1hAiH16219Eec0zctVYcEQlftEOTx4knIaj729wCqDsQn+LiC0OQotEg/iC6zKQyQjzvVv8U4AgOcTYRY7IUaib6T85O7SgceLMVkmQ3Rl/7aAsl2KXqiwOFeQEvCi48TwblEkM/kWgLe3w0aNvuk99OhR2XooeA/QtH+LWAwg0MopGIfiE1AVJXfHVFze0lGINENOy6Cz30MAAdNB4IKcKgnVRg6ge3+MnAX+yxBt/oRW9sE8ANMG6VQe3tAVQ08rHqD/mdrRkKI/CgXiFOKf3L03cCJ8VM3Lv9waB9S2/wNQsR1AwnQriCMY6amWLXCzZMhzB4LaJYJgmCHtusEtN8dtr5DVr0SCg97qyWBB7my+Fwfk1e/DONAPMATrCbOawNfMRFcnhv0dB/0+vdA+2Mgk/OAbDvo4TPsO7E/UlbAxqlvnWK/pOJi8CWF4PtlaNuOgJ6MWfOOdYMb7YbR0gUEZCiwfJt95jggKMM41AV47SATPexbbvGzdgk8uBwbyKxikEn50NsC0Ha3gnZb59Xjcko/vHleOCQg35nS1EH7vjm+atGTQwKJ1jbv4IlYnjqZDn5KHpCTBS7LBi7LoqvVtVg85lsaR0MKaCgGevg0tC3vQzsbGhaIqTnS6htARrtT+tFjXVDW7AKNxsCNyob0nc+Cy7YnBU4jMpT/3QIyoxjiHVdAfX4P9PpjVnuBE/bHb4JxpAPyM3+FY8U8cAVZKfOrm/ZAbzgGYeHlEG6+HHJNPdDcCV2wNNj189tgnOqD8uAWYFoB7A9UQN9zHPL6Juu8BdmQVl4LLsduzRsHIFe/Dr3VhCYOeW5eIsCYEQBHU4HoZxvGP7Jo7iAg9Om3nLLP4edFLr6S1YWfXgAyypucxPQX2o526H9ptzZYPgr81QXgsrKTfbR9J6Bt2D8sEAiAtOo6cNk2JiDDIBCvLgI30gl182Hobx6FuLQMfOlo6I0noLecAj+tEPy0cdC2tDAIth/fAKO1G8raXdZey8dDXPRpKE/sAT9vLPjJhaCnAlD3nAKfYwc/ewyoyiF2/xaQ60sg3VQC5fE/W9oQL/affQn6MT/UqkaQKXmQvj0LWlM7tKf3sR5S9bUghW7QtiC0hmMgo3LYurQziNj3tmc+c5EbkATASI1adV6NtQbe8ZXWPMhULOnUo79oWgJH9gvpQRVvquYIF4yOIPQjndAbz1qL0jjpsGUCyCgBwoISkEm50Fu7oa7ePTwQojMgZlFWbYeuiwyGc821MFr9UNY0WsAcdsRWbE+aSPGuK6G9cgC0Nwbpf2aDTMlFbNV20NNB2NbewI6kPLITth9cBxpWEP3+DlB/FDwvgZ+aB7gFdgEGASFWOGr/6RczAuFLCyB+ayZoIAJl1Q7LVJqXckEJaFsA+oFOID5P8vCGDuTmADkJzYk/JxIdiIIu+ezXxj586wtJINGX95WLzqxXQIT8QVIsdoP/VC5oTEdsaxtwMgicDQM6D8ga0Buy3iqUwrbWFKAEdfNRy4wMV3gbpO+WWUAeaQKoFaVJq6+x6h7azdpJoQvqS4eg72yzZiPWwXgOICUe8MuugN5wAvrBDkjLr4JWd5yZT3HhJFavvnxkyF2QBRMgfeESKBubYTR3JvvYN10P43gA+g/2gkz2gb93OvR9HVCf+RvERZPBl4+F+svD0Bvj+8l0xsSFzZaYKU2YtkHdBUA1Yl1nZP+tE1YtbODk2ubHKMUK0XHO5AwcZPA8xPnjWFWs4SRovwp8GADaIkBYST4cuTwJtu/PYQvL3643VxkeCEcGA3GJsNdUwOgMQXl4L/g5oyHeNonNQ88q0M3goaEdiOkMCLudj1oAjeN+ZtJiD+2CMH8cM0/K080w3k6LEuO7IjdcxIAYR/wMYOJ5xE8vzAhEur8MZJIPSs0eGK3D+0jw1NKWorgH0ONRSrpUeMsJdcid4Dis4RrL73hsAmdbofYErJuXNoAQwLvzWZg2PzDnHkCWAc9o8PaclJ5GjhOe55exuvCSGuh/Z7/m/nI3fBO8JwuRjW9Ac4pw3VgGYWweIq81IbTuVTaXc3EFnAvKQDwu9q23dyP03ZcQO21FPdlfmgXH3Z9n/1beOQ7/A8/Cu/I22OdMQaDqFag7Dw15MRyLK5C9uGLINuVoG/zf2AT7lRPgrboT8s4DCFTVInfDUggXF6H7v9aBnsiQ7otfFE7MBi9lQ490sExFkniGayoKkAnH3ceGPzfrlvKZUa7Wrukj04HYK6bD9fDXobd3IHhnFSBkA1kjB4Ezl3Q/twzE7UTsmTrE6uJhcIYNJICYAAYWoy+M7rvXgfPHw0jzKG4XHHMvg2vhNeBzPYjV7UffE79jw2z5PrhfXM7+HVj9CuQdh+C+dwGcN5adF5DQC/VQPzSFa63n/b9KZALie2wxpGmTGHT9neNDn4wDeKcXvOhh7YbWBy1k+t3h3mH0AxvBrZ43H21OOvWds5fcNSYUekZKszTZVfdAum4alB1HID/3evLmxzUNCU10XjQGwsIZ4GdMgr73KMJVtRlQWNUJIObND7/WZOZUYPSGoe96G7QvCoUSCMW5rM4IRmAG23yBB+5n74MR6If/jrXWPDA1zbq5HdevZCYz68szkb30ekT+sBuhJ1+z+pmqPqBkxTUksPIFyHvfT16wvK1VGYEkQPdtfBWx31phcCIz57A7rNklD0Ckc+ESJVAjflDVylggNeplmSndoPPytz66zWxOAvl1+bKs0v4zAZdGU4xd1gN3wnbLHDP+sIj39CD67J+g7zrKvvnZk+CovBZkxIjkcbXtbyOyzhJEppIAYrZ3f31d0ulJXPxlX5QPX/ViKAdOIFCzmQExi3fzQ+yNMhwQqaQIvh8vhdEbgn/ZT0C7+hgQ6TOXwHHFJejb+AY+DhDpU+Pgq6mE1taFwH2bQEMyA+L6wnQIkh3K6/sBe1zjE5KNZyjUUDuo+WpMA0J4Gu7l+n0T6p5kIVtKLmt7eeW+SwOB6QOFSCQvnMtuBj+lGKTIB+KzBK888XvrQiy/yQLl74HR7mePo2jtbpBg5CMBSWhcYhB1O5H71N1sPbmxGdFdLXDMuQz2iqmQGw4i+OivWFemIU8thTCxCB3zViZvumdtJaTLxzGfo9TtBx2ZA+eNM9mYM19dzUya6UPOV0OC1bXgBALfL1Ywf2ZCMTVbLMqDc34ZaE8v+u7dDMQvVPrhDU2DEuoEoanZbonHH7xvVFtCTAdSP7dy+cSewOPpQNj3SCsl4VpyDaRbZlvO3Sx2O5Tf7EL4543Wd/zlmu6L0jeYriHpQMxt268ugfc7t8eHnkvV+O9ZD/3908MCofku+KorIRTHb2x8X8H1rzJBflQNMYEwgRXnwffDymSQkThv+EfboO5tywgElEBXw9DDqcGARLDE+2b1i0MC+c1nvzbhqjPB9waiErzF4ETTPlo22BSka8WXIZVZKROlqQXhNZuRLtB0AIOAmA654nJWHas/OChISG5wjA+uz00F8blhBMNQfr8Hemdv0nab4GxXl4DzZEHesi9lHi7LDmluKWyl42Cc7UX0zy3QD52wjjdhNISJo6A1vctMGuI+Rpw/lZkirfEwy58JZZfC6AhAb/6AjTNNJ/HkQJw/i+XIaGc/lIb3oHdYUerfK1rYD0NJhKBUp5wyqmBrTZLSoPT7wStvbfPKapFpzIScQnDJ8PYcEHNR2zzLssW2vsV+Pw6QgZvPpFHpf85K+JJEfXp7+jyZ2hP1fCKVEQeSXp8IBhLzSqII4nHDoPF3WyKZmMFUDQJkOvlgO6jBBu7J31Y9Y2CfQUAOz1j0gVeWL+btPvBZI2H+xe9CsSTA273gHanvr48sG2r+BVWFGjxp+veGgm3VycQi09yBE/564ULpqlYaBQfiHDGBNV0AAnBEgJBVyH4zOe3zBhOPukyzpUW6w3meUjdX+5WkIqcA+dO8ygWXBSJ/tHmL6mSJuzsWcvzH/+8TITeHG+N1DbIk5w1giI59vF6APvoU7e2fr3HK9Nwt30umxlMWqp9XuaxU8yi++rU//SQLXhh7fhLoXbL+LiPQEx7x6sqXEyP+H28pnxj2SBepAAAAAElFTkSuQmCC", "Ice": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAYCAYAAAAMAljuAAAAAXNSR0IArs4c6QAAB7dJREFUaEPtmm1wVNUZx3/3nrs3m92weeHNBAJMNGBoNS2l09IZbFCrBa107Dh+cIZxRooVp7bVmdoPjJYpncFWqYNDfaG0UKpjYXBaO1arU96sDraKBYpIxSiQkBhIQkI2u3t3757O2bO72b3ZRUmy2FLOl90999zznHN+5/k/z7l3DdJlZYdceDJBS1+MRzZfZnRl6i9+lm4FFr4vb6vu6vQ9M692Y8aKkWvu0Y/kewfi1CJ53Ofj509cchFMKXBc/46sFTaPI1lcc6pzwe/m1e4sCOThk3JZhcmTeyIQTaMyRzkiF0hKCDvgylF2diHcnl6DqW4U228n3JNvB56aOzdeEMiPd0hrxudoiyaZ/FoM4hJGCySBhpFIXgirOQZzkFBmQr0PooOR/RtmBZpze82TLHVhY7c85DO5vCcJ+x2Ie1qINOE6S3dzQq044HraORLiLkTT18dgKhdGFxKm2xpKPBJ5d21DoKkokF90yBmT/LQChpKaviTsS2jJyWxwBaTegntrdDdreuB4YjiQfkffd1Gm8vfRJRaERLZOdn/4bsPGeU0fFpSsZ3rldy3B2twuel14MwakO7GTUC/g7mrdal0vHHfBSWvbGUfXOyWQqMkCGmxoS8DxrOrCRAE3BqFSwAkXXhqA/rT9z5RBqIDuevs4H+6nxlGbVpaMPdF+4p5Vc6Y8VgzIDkvQ4h1ctQm9Et50IJyWoKsCutXuQf0ZtGCuDWEX/hIuDZDrAnBnDfy+H7b0a7tNNqyoAX/ORJVnrzwJR+Pw6GSt196S28f5gFFmaKkyPImN2d6x86dz6hYMA/KklIGaAbol+HMHqBzj5gqIAhv6YdAFtTDNZbrVvhi8PAgxF1ZM0HVLOyE8xrNUMeraclgWgq0D8IcBbeCxiVAj4FAU9jpwmQ1f8sMhB1b1wMoaXbct3T4zrH85cNDJOv4Yj3Z4d0pV7GERG2TCjTn7D45/+Prm1JJlm2w8JZeMK2OTih1eINcGwDbhgzg0+TSEPTG4MzTUUnlKow1qAzxw6vwA+VoAbg/BkTg82D00FlX3yiC0J4aA3NaZP6+M4g3JeemYTBIwrgCMjMX4qe7bf9Y0YVMWyOZe2VJm8awwmFwszzWT8PUg/D0Kh+JwU3m+HqpsaplalBLNUG2Uq/1wRwVsC8NzEVhRqTfI2n4tp4XKgyG41AYvEOHR8jHHkY5h4wSo2FesqEtJJ9Hl9HTeuqqpfqfx9Gm5Gsn9/ozOFjl45AJRi6ACaW75NIA8VAVTLbirB8JFkogMkDeU5uaUdR4JKwUQn4obBeKXV4HU70hnB4ZhPGRc/dI/VzN91v29ZzLNvKKVqRdsbYIftsa4b4qPL4byo1O7I7ijFVyK3T+6KQsEN1VZfK/eYtNHCX7blWBDo80Mv8k1B6JF7T7R4Ofy4PAt2nJgrKPc0Dqpb5YAy4KoOoyp8jFZp0BEDYzvp5StZf3WlnDTNVsTdvkEtaSFiwbiJJJUWQYTyvItfJpAvnUoRnei8Ak0A2TFsXxN+1tfTt48ur3iuVtQZmsgqrhJSTSe/Bgg8oiQ5q3bm4N7s6Gm5YV9S/smTlsP+T5Wbgl+MgVcE/pcmF+h830hNDjXFan6nQPQXK6zhOVHx95TCnnImgab5qDJA8ccdqcXWLXLk6YGX8pDSucRQ54hTPCns8/cMcQT4GQ8xQtfhQjJdbuuqHhFXcoCuWXdlooPPt/S61qBvHCngLzWpNPeu47qh47fHg/zQxrIq/2C9d06v/71dG3thvfOD5DF4wX31Pk4OJjk7vcjKdsKyJJJFocHJW8MuKw7T0Bsn8B3lkQhHCmiPCbhQFlw/IuNhjp+DwFRPxZt2/uPjvqZc3MhKiCLqvTBa/cZGEj3u6BSt9rRpz8rBFw1Tn//8+nSA/lNV4wqYbBpVjmVwuD1M24KzEy/yVcrBfvCSe5tdbJAVNzJLd1xyQu9KuKNMOalkx9DaolS3nG2IiVEYiBlvj3TMv+4Y3bgm5l787LjRc+99YOOqbPWeIGo37kn4Wk2rJ6qW/2oDTzynKof8USLzMorWQqIKl+u9LFaDSinnE7IFIyjMZkF4u32cDjJ8lZn5ONMAwiWffI8301CVJ2gc4qwxJLts8s3FwRy49MvN7Y3zv+3vqiDYMjvNSiYYsMjaSD3tUF7qukId9rZN1be1em2YE7A4nA0yTtRHZRdU1Dng5urfVQJaIvpnd/jao9YELKpMA28anI6KdndPwIPGQGI3EnE1KuI1FK5Kna4cWTd683jsm9oh50fv7LjxLFIsLpeAVFyNVwXNaC69KY8kU1eSg9E2c0E7YwHKiCqJNNJX+a5T+a6N8h7+Z+rJwvLwO8b3VuiwWhGuow9u64Mzssd03Ag248fiVRMuNT2xbXhEjy1PQen+C9qmp/OjnZgYZ2D7Nx1ZXn2waKqyANyy5aDduu0aZGkaZmhwCc70Ix2YP8r9wf8AuMsz6POdR5KtmIO4UlX+Cu3GqnXT6mSZ2Lhn976Rldd4/PlPvGiX5jfSQT8//f/PpnY0WZEbWHUnuuKF2nfoV5VjK+eFI7xy4hkYVKKL2yfbe8tCOSG599e3t8w03n1s8FfjZH9i92cZQUWH44sHXDlwF9nB57NNPsPLtncbnXpdb0AAAAASUVORK5CYII=", "Dragon": "data:image/png;base64,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", "Dark": "data:image/png;base64,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", "Fairy": "data:image/png;base64,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", "Stellar": "data:image/png;base64,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" }; var base64Ribbons = { "winningRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABg1BMVEUAAABISFBISFBISFBHR1BISFBISFBISFBERE1ISFBISFBISFBFRU1HR1BHR1BDQ05ISFBISFBISFBISFBISFBISFBISFBISFBISFBISFBHR1BISFBGSE5GR01HSE5ISFBGR01ISFBISFBISFBISFBISFBISFBFRU1DQ0txcXmLi5NLS1VxcXje3qv19ZudnV1JSlCIiGbz827x8Vzr5Gual15TVFBVVVFLS0+cnVfn4WvHeefNlMLh22ro6F5KSlDJxmPTm8LBZv+2kpqqrlJJSVDLzFvy7GtYSW18Up7IyF309GCnq1J/W5XFdOupqV3KymCOkFaRjV/s5W2mplzBvWiXfYdVSmOUlVakomGthKOhXs2MjlakoGWmbbyrgqPAu2jExGCTk1u/umi/vGTAwV/AwGBOTlGVllrDw2BRUVF4eFZJSFJMR1lZTWiXXryYXr1eTm6sY9qtY9xiT3XAaPjBaPliT3TCaPu5Zu5kT3e2ZeqkYdFaTWlyU4tHSE////+9/yFBAAAAJ3RSTlMAAAOK1iMGf/zDIhXvxJXtD8UgwGNnajvdPOACx8fHr8BiEAEwQCFRUvzDAAAAAWJLR0SAZb2eaAAAAAd0SU1FB+UCAQwwHf17t9oAAAFkSURBVCjPY2BgYGRkYlbXYGFlBAJWFg11ZiZGRgYQYGRj59DU0ubkYmTk4tTW0uRgZ4NKcPPo6OrpG/Dy8fEa6Ovp6vBwwyT4DY2MTUzNzM3NTE2MjQz5YRICghaWVtY2tnZ2tjbWVpYWggJQCUYhYXsHRydnFxdnJ0cHe2EhqOVAGRFXN3dnD09PD2d3N1cRmDhQRtTVy9vH18/P18fby1UULg6S8A8IDAoODgoM8EeTCAkNC4+ICA8LDUGTiIyKDouJCYuOikSTiI2LT0hMTIiPi0WREHNNSk5JTUtLTUlOchVDkhCXSM/IBIOMdAlxJAlGSamsbDDIkpJEeAMkI5iTCwY5gsjiDAzSMnn5BUCQnycjzYAiIVtYVAwERYWy6BIlpWVAUFqCIVFeWlFZWVFajiFRVV1TUlJTXYUuIVdbVy8vX19XK4cmIVivoKikpKhQL4gqoayiqgZKPmqqKsoQEQDhh1GbWc+A3wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0wMVQxMjo0ODoyNSswMDowMI8o2sMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMDFUMTI6NDg6MjUrMDA6MDD+dWJ/AAAAAElFTkSuQmCC", "victoryRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB3VBMVEUAAABISFBISFBISFBHR1BISFBISFBISFBERE1ISFBISFBISFBFRU1HR1BHR1BDQ05ISFBISFBISFBISFBISFBISFBFRVBGRlBISFBGRlBISFBISFBISFBFSE9FR05ISFBISFBISFBISFBISFBJSFBISFBISFBISFBISFBISFBISFBISFBISFBISFBISFBFRU1DQ0txcXmLi5NLS1VxcXje3qv19ZudnV1JSlCIiGbz827x8Vzr5Gual15TVFBVVVFLS0+cnVfn4WvHeefNlMLh22ro6F5KSlDJxmPTm8LBZv+2kpqqrlJJSVDLzFvy7GtYSW18Up7IyF309GCnq1J/W5XFdOupqV3KymCOkFaRjV/s5W2mplzBvWiXfYdVSmOUlVakomGthKOhXs2YfYeMjlaEhVi6tWelbLyof6O2sWa6ul91dVbBwWDBwWG8t2iDgFxOUE5PUU5kZFSwsF5HSU5cTWxsUoJISU6UXbijYc1OR01OR1pGR05tUoKzZeS6Zu6aU2O+XqROSVV3VZLCafvBaPmbV4XOXpNQSVB4VZPDafzAaPhFSE/JYKNPSVV5VZTFaf+5Zu5RSU6fUlNRSU1tUoWnYdViT3WgUlNISFBISFFJSFFJSFD///9nG11tAAAAL3RSTlMAAAOK1iMGf/zDIhXvxJXtD8UgwGNn7e+A/jP9OP39OSrfP53tr908WvZiEPR1OihCwzMAAAABYktHRJ6fsqMLAAAAB3RJTUUH5QIBDDALCa8CiwAAAaRJREFUKM9jYGBgZGRi1jdgYWUEAlYWA31mJkZGBhBgZGPnMDQy5uRiZOTiNDYy5GBng0pw85iYmplb8PLx8VqYm5ma8HDDJPgtraxtbO3s7e1sbaytLPlhEgKCDo5Ozi6ubm6uLs5Ojg6CAlAJRiFhdw9PL28fH28vTw93YSGo5UAZEV8/f++AwMAAb38/XxGYOFBG1DcoOCQ0LCw0JDjIVxQuDpIIj4iMio6OiowIR5IAGRUTGxefkBAfFxuDMApseWJSckpqakpyUiLCcqBz3dPSMzKzsrOzMjPS09zhzuUWy8nNyy8oLCoqLMjPy80Rg/tcvLgkN620rLy8rDQtt6RYHCbBJiFZnFNRVllVVVlWkVMsKQELREYp6eqa2rr6hob6utqaamkpuLNkZBubmlta29paW5qbGmVlYB5hlJFr7+js6u7p6e7q7GiXI0JCvrexr3/CxEmTJk7o72vslYdLKChOnjJ12vQZM6ZPmzpzsqICXIJRSVll1uw5qqpzZs+aq6aOFOyMjBqawlra2lrCOrp6CHGwlDwXKPlwAYUh4gD/cG9+zeI+4wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0wMVQxMjo0ODowOCswMDowMKzavH4AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMDFUMTI6NDg6MDgrMDA6MDDdhwTCAAAAAElFTkSuQmCC", "abilityRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB8lBMVEUAAACNjZ5paXWIiJiCgpGOjp6QkKFubntGRk5ISFBoaHRKSlKHh5ZjY2+Hh5djY2+Hh5ZkZHDCwtqHh5Z1dYNjY29GRk5CQkxISFBDQ1BDQ09kZHGGhpZlZXOHh5dmZnNtbXpGRk5ISFBHSE9GSE5ISFBER0pISFBVTGFwcXxISFBFR0tgY2p/f45ISFBFR0xPUFhISFBISFBJSFFJSFFISFBISFBFR0xISFBISFBISFBBQUlDQ0tjY2mTk5dfX2bQ0NK+vsGRkJZdXWbV1dfFxcdXUmOOVLhfX2jQ0L3///+tr7GOaZTPidljY23Nzaz09Hz6+sWtrbZ/flPo52GPj3L393rw8Fzz823Z2YJublJ7e1WLi1X09F/w8GDw8F/z817c3F7GxlyMjFf19WHy8mDz82B9fUvn51bz9F/x81xTU0mZmTvDpInSnrrTnrtRUUqGdVOsasjDcPHCb/DDb/BSU0yUj0Di22Pu523t5mxQUFpPUEaamzrb21Xg4GFNSVdsUYNxcUt4eFd+V5qeYMZzU41GRVJERU9YTGaaXsGnYtKfYMdoUXxERkuOW7LDafy8Z/KrY9hsUoFbXWVGR02QW7XDaf2fX8puUoW6Zu/BaPrBaPmIWKqpYti/aParYttOSllKSFKBV6CAV5/////pDa/rAAAAO3RSTlMAAAAAAAAAAAAAAABi/lrzXfECWhTpFOsU6+vpWvNb7ZmZT+1kEqM7/oYPw/EqFfR2DInF2SVq25QuQO2pQ4gAAAABYktHRKUuuUovAAAAB3RJTUUH5QIBDCwL79hf1gAAAc5JREFUKM9jYAADRiYeXmsbG2teHiZGBiTAzMLHb2sHBLb8fCzMCGFGFgFBewdHJydHB3tBARZGqBQzKxOfoLOLq5u7u5uri7MgHxMrRIZNSJjfw9PL28fX18fby9ODX1iIDSzBLiLq5x8QGBQcEhIcFBjg7ycqwg6W4BATDw0Lj4iMio6OiowIDwsVF+MAS3BKSMbExsUnJCYlJSbEx8XGSEpwQiWkklPi4uJS09JSgVRKshRUgkNMKj0jMwsKMjPSpaBGsYtIZ+fk5uWDQV5uTrY01HI2IRnZgsKi4pLS0pLiosICWRmoc4EelJMvK6+orKqqrCgvk5eDeRAYJFwK1TW1dfX1dbU11QpcjPDQ4uRQbGjMbmpubspubFDk4ISHIqeScktrW3tHR3tba4uyElyCk1Ols6u7p7evr7enu6tThRMmw6mq1j9h4qTJU6ZMnjRxQr+aKidMg/rUadMnzNDQ1NSYMWH6tKnqUC2cWtozZ82eM1dHV1dn7pzZs2Zqa0El9PRnzpu/YKEBA4PBwgXz583U14NKGBoZL1q8xESdm1vdZMniRcZGhlAJTk5TM/OpFkCa02KquZkpJ8JZnJyWVqZAHqeplSVUGABOKHi34xtu/QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0wMVQxMjo0NDowNiswMDowMObZJ60AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMDFUMTI6NDQ6MDYrMDA6MDCXhJ8RAAAAAElFTkSuQmCC", "pairAbilityRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABiVBMVEUAAABISFBISFBISFBISFBISFBISFBISFBISFBHR09HR1BISFBISFBISFBGRk5GR05ISFBISE9ISFBISFBISFBISFBFR0xGRk5tUYWIh43Ce/CGhozu7e/NhvpFRU2Gho/w8PP8+/zapP3w8PKIiJGGhont7bf6+tfQ0NOtrq/z87jq6aT082HLy3RhYGx7UZvy8lxGR01iT3S3eOHMheLXoL6oqVZ+WZXFdOvWn8FjT3W2d8nMhNvXoL+op1qQi2Ds5W1GRlCFhFfm5WD19GHCwlxZWVKUlFfz82GEhFbi4l/Pz2CkpFyLi1rOzmDq6mBHR1B5eVa6ul/DwmGwiKF7e1Zzc1a5uWCodLRCQk10c1egbLRFRU51dX1HRlBjT3Z9fYXR0dlISFBMSVVKRlR4dYLS0tnu7vFJSFGHWaaYW7+1ks7v8fH8/PyMW62iXc3Ln+f8/frBwcObXsS2YuzZpPu5u7tQUFinYdTEaP6barxTVVlJSFKnYdWYXcBNSVh6VZZPSlr///8sxbaCAAAAFnRSTlMAAA+zDqIMpa+lpaNiZ/DfN9083vE9i7JGowAAAAFiS0dEgouz/0QAAAAHdElNRQflChUGLx5LtOYhAAABlklEQVQoz12Q+VcBURiG7zXDIGXNRamULKU0KrK0TKEs2UqLFGlfaber/7wZNzncX55z3ud857vfCwAAEPIItZrgdQnaD0KSjzQaxBcIMElsIBRQSKvTaZFQiEkJOMPmIjQyqtePjoyNYSIRZyBJofEJw+SkYcponOI4MY4oEgIeH02bzBar1WKemcE0TSM+DxBIO2ubm7fb5+cWFjBts1pEANqxuLTsdK2suJxuN+by0qKDBrTH61tdW9/YWF9zuzFXfV4PDQhmc8sfCG5vBwM7O5j+rU2GYJczoXAkursbjcXjMY6RcIhhl7PfTSRTe/v7e6l0GjOZ4L7LHihmDg6Pjg4Pjo8xGXHndCqROTnJZE9PsxwTuJJ2iQNnuVw2f36ez+ZyZwNkt15J4eLy6vrm5vrq8qIg6eSsGby9u394fHp6fLi/ux38zznxXCy9vL6+lIrPfeLt/ePz6+vz4/2tT5Qr1drQUK1aKfeKeqPZkspk0lazUe+d+P6RKyBUyH++eycKtJLNWaOkC71CNYxbGFb9iV/wg1zJdtls7wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0xMC0yMVQwNjo0NzoyNCswMDowMEANrGoAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMTAtMjFUMDY6NDc6MjQrMDA6MDAxUBTWAAAAAElFTkSuQmCC", "worldAbilityRibbon": "data:image/png;base64,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", "doubleAbilityRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABklBMVEUAAABISFBISFBISFBISFBISFBISFBISFBHR09ISFBISFBISFBGR01ISFBGRlBISFBISFBISFBGRlFFRVBGRlBISFBISFBISFBHSE5GR01ISFBGR01GR01HSE5GR01HSE9ISFBJSFFISFBISFBISFBISFBERExGRk5/f4WIiI3w8PCGhozu7u/6+vqGho/w8PP8/Pz9/f2Ghont7bf6+tfQ0NOtr6+Ih43q6aT082HLy3RhYGx7UZtiT3S3eOHMheLXoL6oqVZ+WZXFdOtjT3W2d8nMhNvXoL+op1qQi2Ds5W2FhFfm5WD19GHCwlxZWVKUlFdGRk+EhFbi4l/Pz2CkpFyLi1lvbz1bW0R4eFa6ul/Dw2BbW0VwcD2QkFakpD1aWkRzc1a5uV/CwmCcnDx/f1RGRlBHR090dFZJSVBISFBFRVBHRlFJSFFHR1BtblVXTGSNW65oUXtLR1ddTm2lYc+cX8KZXr63ZuukYc+fYMfAaPi5Zu2yZOO7Z/F0VI1kT3e4Zu1yU4taTWlHSE////9ENugSAAAAJnRSTlMAAA+zDqIMpaULpBXwo/40/Tj9/f06EjvTxgLH1MfDHsUiATBAIdhoURcAAAABYktHRIUV12rnAAAAB3RJTUUH5QIBDC4hBlX0ggAAAXNJREFUKM9jYIAARkYmZjU1ZiZGRgYUwMjIwqquoaHOyoIqw8jIxq6uqaWlqc7OhiwDFOdQ19bR1dXRVudAkgGaw66up29gaGigr6fODjcNbL6RsYmpmZmpibERwh5GTi51cwtLK2sbG2srSwtzdS5OqAQ3j62dvYOjk7Ozk6ODvZ0tDzdcwsXVzd3D08vL08PdzdUFLsHJy+ft4+vnHxDg7+fr483HCzOKkV8gMCg4JDQsLDQkOChQgB/uLEGh8IjIqGggiIqMiRUShHmEUVA4Lj4hMSk5OSkxJiVVBCEhmpYYF56eERWVkR6bmpklCpcQY03LzsnNy8/Py81Jy2IVg0swiolLFBQWFRcXFRZIiIshBxajZElpWXl5WWmJJCNKwEtJu1RUVlVVVrhIS6FElJS0bXVNbW1NtS0Wibr6+josEg2NTTIyTY0N6BKyzU1y8vJyTc2yaBIKLXKKjIyKci0KqBJKyiqqQHcyqqooK0FEAEyvTDQwOuhFAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTAxVDEyOjQ2OjMwKzAwOjAwD3PFSQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0wMVQxMjo0NjozMCswMDowMH4uffUAAAAASUVORK5CYII=", "multiAbilityRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACFlBMVEUAAABISFBHSE5GSE5ISFBFR0tISFBISFBISFBISFBJSFFISFBISFBISFBISFBERE9ISFBGRlBHSE5GR01HSE5OSlhISFBGSE5HSE5OSllHSE5HR1BFRVBISFBISFBISFBISFBISFBHSE5FR05FR05ER09JSFJGR01GSFBGSFBISFBISFBFSFBFSFBISFBzU4xMTFVPT1JOTlFHR09MS1SsZNtERE2hoZbg4Hijo1VPT1avrrLKhPewsF2+vltcXFmysrX5+PnBgepxcVNgYFq1tbj6+vn39/fXw+VRUlq6vKf294bw8K2dnqN2cX9dTG6rlbrnz6zq2H6xsVxbSnKkW9lbTWqfYMezZObAZf3PitioqViaf4jcrbFgTXOpkoPkyY/q1YSurlpmZ1DS1VlUVU+7vVjz9F7U1F99fVdtblJZWURYWE2qqlvCwmC/v2Czn4CHh0qFhTpTU06Tk1rBwGGmcrSEhDqGhkpFRVCIiFmurlF1dTlOTlCRkFumc7N2djmZmU5XV1NHR1BMTFBMTk5MTE5GR05LS1CRXbJKTE5JSFFzU41NSVdcTWtoUXxISE+wWHVUSVJsUoKUXbhXTGRYTWmsXKFSSlVuUoSmYdCYXr2aV4PNXpVQSVB4VZS4ZuutY9yaV4bOXpLCaPq9Z/SfVW/MYKZQSVV/V51ZTWibWIm0V2dQSU5KSFNzTEpZSk7///8+cWnsAAAAL3RSTlMAAAAAJ9ssdnNwxiPOOjj9N/1K/UTlAslJ6U39/SXPZWQHh/v9/c+o/d93/f1vMJBCIQsAAAABYktHRLE0Y55SAAAAB3RJTUUH5QIBDC8arkUs5wAAAblJREFUKM9jYAACRjBgYWVlgbAYoICRkY2dg4OTS1+fi5ODg50NLsPIzWNgaGRsYmpqYmxkaMDDDZfgNTO3sLSytrGxtrK0MDfjhUvw8dva2Ts4Ojk5Otjb2fLzMcKsEBB0dnF1c/fwcHdzdXEWFIBawsgkJOzp5e3j6+fn6+Pt5SksxASVEBH1DwgMCg4JDQ0JDgoM8BcVgUiIiYeFR0RGRcfExsZER0VGhIeJi0F0SEjGxSckJiWnpCQnJSbEx0lKQI1ilhJOTUvPyMzKysxIT0sVlmJmhLlKOjsnNy+/oCA/LzcnWxrmKqA/ZAqLiktKy8pKS4rLKwT5ED6vrKquqa2rr6+rbWhsakb4XFaupbWtvaOzs6O9ratZThYuwSjf3dMb1tff3xfW29MtjwhdRoUJEydNnjJ16pTJkyZOUEBIKCpNmz5j5qzZs2fNnDF9mpIiInSV58ydN3/BwoUL5s+bO0cZ4So+lUWL581fsnTpkvnzFi9SQZJQXbZ8hf7KVatW6q9YvkwVWcfqNWvXqamrq61bu2Y1sg6N9Rs0tYCJgE1Lc8N6DSQJbR1dPUjK0dPV0QZLAAAU7Xgoj9JfBgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0wMVQxMjo0NzoxNiswMDowMMFEnDAAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMDFUMTI6NDc6MTYrMDA6MDCwGSSMAAAAAElFTkSuQmCC", "coolRibbonMaster": "data:image/png;base64,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", "coolRibbonUltra": "data:image/png;base64,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", "coolRibbonGreat": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB4FBMVEUAAAA4Nz85OEBmZXaBgJV1dId0c4aWla7///84Nz84Nz84Nz84Nz84Nz83Nj44Nz84Nz84Nz84Nz85OEBpaHlKSVT///9KSVRzcoRCQUo2Nz83Nz8+Nj40Nz84Nz84Nz84Nz84Nz84Nz80MzxoaXo2Nz9UU2A4Nz80N0A4Nz8zMjk4Nz84Nz85Nz84Nz84Nz80N0A4Nz84Nz84Nz84Nz84Nz86OUFCQUtlZGnKyr7Ly7/MzL85OEC+v6Xq7Krx9lTx9VLn61U5OT/GyUPn7D7U2TPf40KrrzI6OT/AxD6qrhuvsx/Lz0aNkBW8wDyPkw6bnh/Q1ES5vTzDx0O2ujSFiAqgoyjM0Dg5OD+prDHe4zieohaDhgeHigl7fRnm6zze4zmusSyLjhFJSTGhpSPp7j7o7UTY3Dg6OURIRzKhpCTb3zTs8UA1NT9JTDJpax+ytSdONDxqLjk2NUBPTy1NNDymJTC9Ii2fJjFKMj68Ii1gMDpiMjbJJCzLHyt7LDY2Nj7KHyy/Ji1hMjY3NT3Namb4NSzZISdyLjhHS1d8e4Do5OT+tK/Fd3hMMTo7Nj25Uk7/bWP45uSfpKgyMztuNznONCv6MybZRDtDQEg7Nz+VNjQ5Nz80Nz84Nz////9Iz2tEAAAANnRSTlMAAAAAAAAAAAACXmdmXKX8TvFoZ178AqVc7cZ2+Ph2ErNiHsD9SGIT49wkDIrFJWrbZS4+PxmIp/b+AAAAAWJLR0Sf6LWTnQAAAAd0SU1FB+UCBwkbHN+s5xIAAAGJSURBVCjPY2AAA0YkwIAEGBk5ubh5gICbixNZhpGRl4/fDAz4+XiRZBgFBM0tLK2sra0sLcwFBZAkhGxs7ewdHB0d7O1sbYSQJLidnF1c3dzd3VxdnJ24kSU8PL28fXx9fby9PD1QJfz8AwKDggID/P3QJIJDQsPCw8NCQ4JRJSIio6JjYmNjoqMiI5AkmIQj4uITEpOSEhPi4yKEmeASzCKiySmpaenpaakpyaIizHAJFjHxjMys7Jyc7KzMDHExFrgEK5uEZG5efkFBfl6upAQbKyKw2DmkCouKS0qKi0qlONiRA1Fapqy8orKyoqpaVg45EOUVamrr6hsa6hubmhXkGREaFFta29o7Ojs72ttaWxRhWhgZlZS7unt6+1RU+np7uruUlaAyjIyq/RMmTpo8RU1tyuRJEyf0q8Ik1DWmTps+Y6amlpbmzBnTp03VUIdKaOvozpo9e44QI6PQnNmzZ+nqaMOMYtTTN5g7z5CR0XDeXAN9PURKAbKMjE1MGRlNTYyNoMIApRhgvFeH+3IAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMDdUMDk6Mjc6MjQrMDA6MDAZtneIAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTA3VDA5OjI3OjI0KzAwOjAwaOvPNAAAAABJRU5ErkJggg==", "beautyRibbonMaster": "data:image/png;base64,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", "beautyRibbonUltra": "data:image/png;base64,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", "beautyRibbonGreat": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB4FBMVEUAAAA4Nz85OEBmZXaBgJV1dId0c4aWla7///84Nz84Nz84Nz84Nz84Nz83Nj44Nz84Nz84Nz84Nz85OEBpaHlKSVT///9KSVRzcoRCQUo3Nj04Nz46OkU3NTw4Nz84Nz84Nz84Nz84Nz80MztpaHg4Nj1UU2A4Nz83NTs4Nz8zMjk4Nz84Nz84OEA4Nz84Nz83NTs4Nz84Nz84Nz84Nz84Nz86OUFCQUtlZGnKyr7Ly7/MzL85OEC+v6Xq7Krx9lTx9VLn61U5OT/GyUPn7D7U2TPf40KrrzI6OT/AxD6qrhuvsx/Lz0aNkBW8wDyPkw6bnh/Q1ES5vTzDx0O2ujSFiAqgoyjM0Dg5OD+prDHe4zieohaDhgeHigl7fRnm6zze4zmusSyLjhFJSTGhpSPp7j7o7UTY3Dg6OURIRzKhpCTb3zTs8UA2NT5LSjBpax+ytSc+QVNFTW02NUBPTy0+QFJXaaJdc7dVZpw8PlJdc7ZDSWM+SmZeesNhesNLVXs2NT1hecNbdrs+SWU2Nj6Am81jl/RfgdJJUnRKSVJ7e4Dl5unE2fyNnsQ8P1E4OEJphbqNt/7q7/ijoaIzMjpDUnJVgM1elvZkjtdBQUk5OEJLZZc4N0A3NTs4Nz/////jddL2AAAANnRSTlMAAAAAAAAAAAACXmdmXKX8TvFoZ178AqVc7cZ2+Ph2ErNiHsD9SGIT49wkDIrFJWrbZS4+PxmIp/b+AAAAAWJLR0Sf6LWTnQAAAAd0SU1FB+UCBwkbAbyqi8sAAAGJSURBVCjPY2AAA0YkwIAEGBk5ubh5gICbixNZhpGRl4/fDAz4+XiRZBgFBM0tLK2sra0sLcwFBZAkhGxs7ewdHB0d7O1sbYSQJLidnF1c3dzd3VxdnJ24kSU8PL28fXx9fby9PD1QJfz8AwKDggID/P3QJIJDQsPCw8NCQ4JRJSIio6JjYmNjoqMiI5AkmIQj4uITEpOSEhPi4yKEmeASzCKiySmpaenpaakpyaIizHAJFjHxjMys7Jyc7KzMDHExFrgEK5uEZG5efkFBfl6upAQbKyKw2DmkCouKS0qKi0qlONiRA1Fapqy8orKyoqpaVg45EOUVamrr6hsa6hubmhXkGREaFFta29o7Ojs72ttaWxRhWhgZlZS7unt6+1RU+np7uruUlaAyjIyq/RMmTpo8RU1tyuRJEyf0q8Ik1DWmTps+Y6amlpbmzBnTp03VUIdKaOvozpo9e44QI6PQnNmzZ+nqaMOMYtTTN5g7z5CR0XDeXAN9PURKAbKMjE1MGRlNTYyNoMIApRhgvFeH+3IAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMDdUMDk6MjY6NDkrMDA6MDBRzHTxAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTA3VDA5OjI2OjQ5KzAwOjAwIJHMTQAAAABJRU5ErkJggg==", "cuteRibbonMaster": "data:image/png;base64,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", "cuteRibbonUltra": "data:image/png;base64,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", "cuteRibbonGreat": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB11BMVEUAAAA4Nz85OEBmZXaBgJV1dId0c4aWla7///84Nz84Nz84Nz84Nz84Nz83Nj44Nz84Nz84Nz84Nz85OEBpaHlKSVT///9KSVRzcoRCQUo2Nj43Nz8/OUI1NT04Nz84Nz84Nz84Nz84Nz80MztoaHk2Nj5UU2A4Nz80NT04Nz8zMjk4Nz84Nz85OEA4Nz84Nz80NT04Nz84Nz84Nz84Nz84Nz86OUFCQUtlZGnKyr7Ly7/MzL85OEC+v6Xq7Krx9lTx9VLn61U5OT/GyUPn7D7U2TPf40KrrzI6OT/AxD6qrhuvsx/Lz0aNkBW8wDyPkw6bnh/Q1ES5vTzDx0O2ujSFiAqgoyjM0Dg5OD+prDHe4zieohaDhgeHigl7fRnm6zze4zmusSyLjhFJSTGhpSPp7j7o7UTY3Dg6OURIRzKhpCTb3zTs8UA1NT9JSzFpax+ytSdOQElrS1c2NUBPTy1NP0inY3G+bHugYG5LPUm9bHthR1FgSVPIdIPMcYF7Ul02NT2+cH9fSVM3Nj7Hm6XulqbXeotzTlpHSVR8e4Do5uf52d7CnaZNPkc7OECzhZD5t8P37/CfoaQyMjpqUlzFgI/QjptDQUmPZXE5Nz81NT04Nz/////Ebh+QAAAANnRSTlMAAAAAAAAAAAACXmdmXKX8TvFoZ178AqVc7cZ2+Ph2ErNiHsD9SGIT49wkDIrFJWrbZS4+PxmIp/b+AAAAAWJLR0SccbzCJwAAAAd0SU1FB+UCBwkbLPl1174AAAGJSURBVCjPY2AAA0YkwIAEGBk5ubh5gICbixNZhpGRl4/fDAz4+XiRZBgFBM0tLK2sra0sLcwFBZAkhGxs7ewdHB0d7O1sbYSQJLidnF1c3dzd3VxdnJ24kSU8PL28fXx9fby9PD1QJfz8AwKDggID/P3QJIJDQsPCw8NCQ4JRJSIio6JjYmNjoqMiI5AkmIQj4uITEpOSEhPi4yKEmeASzCKiySmpaenpaakpyaIizHAJFjHxjMys7Jyc7KzMDHExFrgEK5uEZG5efkFBfl6upAQbKyKw2DmkCouKS0qKi0qlONiRA1Fapqy8orKyoqpaVg45EOUVamrr6hsa6usamxTkGREaFJtbWtvaOzra21pbmhVhWhgZlZQ7u7p7elVUenu6uzqVlaAyjIyqff0TJk6arKY2edLECf19qjAJdY0pU1unTdfU0tKcPq116hQNdaiEto5u34wZM4UYGYVmzpjRp6ujDTOKUU/fYNZsQ0ZGw9mzDPT1ECkFyDIyNjFlZDQ1MTaCCgMActBgL/R7788AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMDdUMDk6Mjc6NDErMDA6MDCN4VGoAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTA3VDA5OjI3OjQxKzAwOjAw/LzpFAAAAABJRU5ErkJggg==", "smartRibbonMaster": "data:image/png;base64,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", "smartRibbonUltra": "data:image/png;base64,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", "smartRibbonGreat": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB3VBMVEUAAAA4Nz85OEBmZXaBgJV1dId0c4aWla7///84Nz84Nz84Nz84Nz84Nz83Nj44Nz84Nz84Nz84Nz85OEBpaHlKSVT///9KSVRzcoRCQUo3NT44Nj85Pj82ND44Nz84Nz84Nz84Nz84Nz80MzxpZ3k3NT9UU2A4Nz81Mz44Nz8zMjk4Nz84Nz85OD84Nz84Nz81Mz44Nz84Nz84Nz84Nz84Nz86OUFCQUtlZGnKyr7Ly7/MzL85OEC+v6Xq7Krx9lTx9VLn61U5OT/GyUPn7D7U2TPf40KrrzI6OT/AxD6qrhuvsx/Lz0aNkBW8wDyPkw6bnh/Q1ES5vTzDx0O2ujSFiAqgoyjM0Dg5OD+prDHe4zieohaDhgeHigl7fRnm6zze4zmusSyLjhFJSTGhpSPp7j7o7UTY3Dg6OURIRzKhpCTb3zTs8UA2ND9LSTJpax+ytSc/TkJIbUY2NUBPTy0/TUFcrUxlxU9bpUs9S0NkxE9FY0NOYEZyzlRo1FBOfkc2NT1uxFNOXkY3Nj2ox4mq72963VZLdkZKRlZ8e4Dn6Obi+cqiw5A9TT85Oj+Ss3PJ+Znx9+uhn6YzMTtZakyTxWOu7m6g0HFCQkg6OkBxj1Y4OD82Mz44Nz/////Y6Rl7AAAANnRSTlMAAAAAAAAAAAACXmdmXKX8TvFoZ178AqVc7cZ2+Ph2ErNiHsD9SGIT49wkDIrFJWrbZS4+PxmIp/b+AAAAAWJLR0Sen7KjCwAAAAd0SU1FB+UCBwkcHefqQUMAAAGJSURBVCjPY2AAA0YkwIAEGBk5ubh5gICbixNZhpGRl4/fDAz4+XiRZBgFBM0tLK2sra0sLcwFBZAkhGxs7ewdHB0d7O1sbYSQJLidnF1c3dzd3VxdnJ24kSU8PL28fXx9fby9PD1QJfz8AwKDggID/P3QJIJDQsPCw8NCQ4JRJSIio6JjYmNjoqMiI5AkmIQj4uITEpOSEhPi4yKEmeASzCKiySmpaenpaakpyaIizHAJFjHxjMys7Jyc7KzMDHExFrgEK5uEZG5efkFBfl6upAQbKyKw2DmkCouKS0qKi0qlONiRA1Fapqy8orKyoqpaVg45EOUVamrr6hsa6usamxTkGREaFJtbWtvaOzra21pbmhVhWhgZlZQ7u7p7elVUenu6uzqVlaAyjIyqff0TJk6arKY2edLECf19qjAJdY0pU6dNn6GppaU5Y/q0qVM01KES2jq6M2fNmi3EyCg0e9asmbo62jCjGPX0DebMNWRkNJw7x0BfD5FSgCwjYxNTRkZTE2MjqDAAiUZgf6mHXPAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMDdUMDk6Mjg6MjYrMDA6MDB/Ij0sAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTA3VDA5OjI4OjI2KzAwOjAwDn+FkAAAAABJRU5ErkJggg==", "toughRibbonMaster": "data:image/png;base64,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", "toughRibbonUltra": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAChVBMVEUAAAA4Nz91dId5eItRUF02NT1ycYMrKi86OUKGhZtvboA+PUY+PUdnZneBgJVMS1d2dYk4Nz84Nz84Nz84Nz84Nz83Nj44Nz84Nz84Nz84Nz94d4tQUFw1NT03Nj92dYhcW2o2Nj82Nj8/Pkc4Nz8zMz84Nz84Nz84Nz84N0BLSlZLSlU7OkQ3Nz9zcoU+PUY2NTxUU2A3Nj1paHn///9HRlE4Nz83Nj44Nz84Nz80ND9oZ3k2NT9YV2Q4Nz80Mz9UU2B5eIw4Nz84Nz84Nz5TUl44Nz82NT0zMz56eY02NT1RUF15eIs6OUFCQUtlZGnKyr7Ly7/MzL85OEC+v6Xq7Krx9lTx9VLn61U5OT/GyUPn7D7U2TPf40KrrzI6OT/AxD6qrhuvsx/Lz0aNkBVGREVGQz+8wDyPkw6bnh/Q1ES5vTw3N0Ohjz/jxkBqYT9KRT85OD/Dx0O2ujSFiAqgoyjM0DirmED/3kDuzUCJdkA2Nj+prDHe4zieohaDhgeHignwz0CKd0A2NT57fRnm6zze4zmusSyLjhE0Mz+ikEDv0UDqykC5m0BjWD9HSDOhpSPp7j7o7UTY3DhBPj9OST6ehkCmjEBjWT8zM0BISDKhpCTb3zTs8UCchT5PSj87OkI4Nz44Nz82NUBJSjJpax+ytSdAP0lMRz5oXEBPTy1LRj5BP0OhiD+2mUCag0BJREC1mEBeVD84OENhWT/DpEDDokB3aD82NT26nUBgWD83Nj/Ms0D000DSsUBwYkBIR1Z8cD/qzED93UDDqkBLRkA7Oj+3oj/62UCgjj82NkNsYz/Uu0r62kTWvEBBPj1PTlecjEqSgj5DQkszMj1SUV7///8XBxbuAAAATXRSTlMAAAAAAAAAAAAAAAAAAAAAAAJeZ2ZcpfxO8Wg+nIwHLt7K3T49/D4u3p3t/KGcT2hjwmNeA5QSsGIewP1IYhPj3CQMisVoJWrbZT4/GQ6m6FoAAAABYktHRNbntWqpAAAAB3RJTUUH5QIHCR0qRkzVDQAAAfNJREFUKM9jYAADRiTAgAQYGQWFhEWAQFhIEFmGkVFUTNwXDMTFRJFkGCUk/fwDAoOCAgP8/SQlkCSkgkNCw8IjIsLDQkOCpSASTMwsrIzCkVHRMbFxcbEx0VGRwoysLMxMDGzSMrJywvEJiUnJKSnJSYkJ8cJysjLSbAzyCqlpikrx6RmZWdnZWZkZ6fFKimmpCvIMyjm5efkFhUXFJaVlZaUlxUWFBfl5uTnKDCqq5RWVVdU1tXX1DQ31dbU11VWVFeWqKmCJxqbmlta29o6O9rbWluamRrCEWmdXd09vX/+EiZMmT540cUJ/X29Pd1enGoO6xpSp06bPmDlr9py5c+fMnjVzxvR586doqDMwqmlqLShcuGjxkqXLli1dsnjRwkJtHV01RmA4sevpGxguX7Fy8apVi1euXm5oZGzCAQ4vTi5ubtM1a9et37Bh/cZNm015ePmYYGFlZr5l67btO3Zs37Zzl7kZIyLULXbv2btv/4ED+/ft3bPbAhbujIyWVgcPHT5y1Nr66JHDhw5aWUJlGBltjh2vOHHylK3tqZMnKo4fs4FJ2NmfPnP23HkHR0eH8+fOnjltbweVcHJ2uXDx0mVXfn7Xy5cuXnBxdoIZxejm7nHlqqeAgOfVKx7uboiUAmSpe3n7MDH5eHupQ4UBo7KtPzthhYgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMDdUMDk6Mjk6MzkrMDA6MDCqAiZlAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTA3VDA5OjI5OjM5KzAwOjAw21+e2QAAAABJRU5ErkJggg==", "toughRibbonGreat": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB2lBMVEUAAAA4Nz85OEBmZXaBgJV1dId0c4aWla7///84Nz84Nz84Nz84Nz84Nz83Nj44Nz84Nz84Nz84Nz85OEBpaHlKSVT///9KSVRzcoRCQUo2NT43Nz8+Oz81ND84Nz84Nz84Nz84Nz84Nz80MzxoZ3o2NT9UU2A4Nz80Mz84Nz8zMjk4Nz84Nz85OD84Nz84Nz80Mz84Nz84Nz84Nz84Nz84Nz86OUFCQUtlZGnKyr7Ly7/MzL85OEC+v6Xq7Krx9lTx9VLn61U5OT/GyUPn7D7U2TPf40KrrzI6OT/AxD6qrhuvsx/Lz0aNkBW8wDyPkw6bnh/Q1ES5vTzDx0O2ujSFiAqgoyjM0Dg5OD+prDHe4zieohaDhgeHigl7fRnm6zze4zmusSyLjhFJSTGhpSPp7j7o7UTY3Dg6OURIRzKhpCTb3zTs8UA1ND9JSjJpax+ytSdNRz9oXEA2NUBPTy1MRj+hiEC2mUCag0BJREC1mEBeVD9hWDzDpD/DokB3aD82NT26nT9gVzw3Nj3Lu3H01EPSsT1wYkBIR1Z8e4Do6OX88LjCtYFLRT07OT63plr/53X49eefoKcyMjttYz/Lsj721j7Wvk1DQUg7OT+Tgz80ND84Nz////8TOeGoAAAANnRSTlMAAAAAAAAAAAACXmdmXKX8TvFoZ178AqVc7cZ2+Ph2ErNiHsD9SGIT49wkDIrFJWrbZS4+PxmIp/b+AAAAAWJLR0SdBrvysQAAAAd0SU1FB+UCBwkdGmCV5aEAAAGJSURBVCjPY2AAA0YkwIAEGBk5ubh5gICbixNZhpGRl4/fDAz4+XiRZBgFBM0tLK2sra0sLcwFBZAkhGxs7ewdHB0d7O1sbYSQJLidnF1c3dzd3VxdnJ24kSU8PL28fXx9fby9PD1QJfz8AwKDggID/P3QJIJDQsPCw8NCQ4JRJSIio6JjYmNjoqMiI5AkmIQj4uITEpOSEhPi4yKEmeASzCKiySmpaenpaakpyaIizHAJFjHxjMys7Jyc7KzMDHExFrgEK5uEZG5efkFBfl6upAQbKyKw2DmkCouKS0qKi0qlONiRA1Fapqy8orKyoqpaVg45EOUVamrr6hsa6usamxTkGREaFJtbWtvaOzra21pbmhVhWhgZlZQ7u7p7elVUenu6uzqVlaAyjIyqff0TJk6arKY2edLECf19qjAJdY0pU6dNn6GppaU5Y/q0qVM01KES2jq6M2fNihBiZBSKmDVrpq6ONswoRj19g9lzDBkZDefMNtDXQ6QUIMvI2MSUkdHUxNgIKgwAZ/lf9Xz18+IAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDItMDdUMDk6Mjk6MjMrMDA6MDDC2Hm1AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTA3VDA5OjI5OjIzKzAwOjAws4XBCQAAAABJRU5ErkJggg==", "coolnessMasterRibbon": "data:image/png;base64,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", "beautyMasterRibbon": "data:image/png;base64,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", "cutenessMasterRibbon": "data:image/png;base64,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", "clevernessMasterRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH5QIBDCwxKdSGZAAABZFJREFUSMe9lVtsFNcdxn9nZvZ+t72L19gGanBJbDCBpDQXSJoSqCAoUaBpK0VVXpAqlVRqqzZRxVPVqqmiNi+V+lBVrUjUNCoxiVAakkKC21RGJsY42A7ExsYY4/Xu2nudmb3NnD50bVxAQn1o/9KRRme+//n+R/q+88H/uMTyhxB3BUsp/2vsTaabTU3Ak8A+oG1959rl/ysXwIFn9wKsreN3A6FbBxDLGxIksmlVe/h3ew/u2NfobZb9fx+6cvafQ7+3LOsPwOJSY326qMPhOPTQjm3P3/9Q19rrC9P2+739b2Tn9ReEEMVl3NJEqqYAfOvgi1+23kq/JAfN1+XgtePyyM8O223t8RPAJmBVfd237gvtf/vpKz+Q52fflgPGH+WfEz+Uew71GMBuody8qbZ0lcbWINlUcXNbT0Qx7Dxj2bOE/TH2fK9bONeYT772q5NbWuLNhmVZMplM+w79ZH/r9q+tZ6Y6RC63gNAkLd1hD7DJG3B9YOTLAGhSSg3Ylp3Tn/A3uffbDhspwZYWKXOWz/QRahtNdn2nq3XX/geolKt8cGIAvXOGgfkFGn0hBAqKFEiHTTDqfa6Ur3qllKeAc5qiKN/eu//xX+w80BW7lBoFB9h1BdhSsljIMZ9MYQk3pjNHFQtdFphfLGPRQIM3jBBg2TaaW+XZIw9v6Qh3bun7y9gL77975kXN43E/euD5XbHupyKkT0xSo4YQAolEFQohX4BaxGLsXIILg9PUqhalYo1YYws+j3dZMQoCTVUJb/Dw2J57iHpWR/tO9z+s6Lrx4ekzp8zLiVEsrYosg6jbQ1EUVgUbsaddxBfa6FroojuzldhiJ5WrDmKBCMqyJAVCgHBbTCQuc7rvtG6apQ9VYHJ2Nrk5tsm7MRDzkJzKE++IoKoKxWyJc+9M0lGO86Pv3s+2rTY93QqPbN3AyJk0o+PTRFr9OD0apllh8mKC5s4QE8Nz9L7af8IsVl/WHB6tkJ03+tMzhadbuiI4HC76jn2Gy62RS5SRMyWeOXIfvnAGu27OUGOCg/si/PiXI2QWSvhjDkrFKg3NATx+J+mZAtmkeVZzqbpm1yRW1c6auSqVDLiyXrxGHAoqmlnC6UsQjsBK49tAU9RB3B1GKa/GmdQIqxIrlaeckejZCrZlL4KCZtdsgEvXL+T1tjUl3xcDTr7x3GZMAxxu+Ou7A2TSJnF/EFmnUYRgMWXw4PYWdu/agm0LnC6bN3r7uTpe4can+QJwWdoSta6CdGbOaB4fTESjPul/YndEseUCLneBGhafj6VZsyaE06UihMAoVOj76Bqd9wZpiBi4vAaKyPGn1y7Ujr8+OjMzkj5WLdeOCiGqS07W9az5fT1r/nawwfXmjev6xta1EbJJnZ6eJnLZMu/0fk57WwAp4dpMnua2EJ0bQhg5BX/EycRYkqHz85dnL6W+CUwApX/L9+ZLWfrqV9Z9Onwx2Xvy5FUEEl8oSCFVY+eOFr70SCtVAZYqePDRNrY/EEXPaAQbfdTKFd57b5KLY+m3uu6NjgghSkv+UFc+15NTGapVe2ouoT/WvTHavG59ANUZoJgReJ3QutpPPOZDw4lVDRGKelDQ+fijaV759cDgXKLwUipl5IQQy3mg8h/lBOzsfLI4PjWV29neEgi3t7sIRNxozhAQQNX8ePxevMEKFT3LmVPT/Pzl/snzw4nDwPDKw28jEMIGJB6PY+rKZGbok8H5zlym3OZxSuF2ldE0E9sqkMssMnrhBkePjspXf/PJx8MXk4cdDvUfti1vS7vbsu+WCVYpivL1DR2Rp9Z3RO6JxbwBaUuRTBn58SuZsYkrmeNSymNA6g69dyZYGXlSSoIBF/lC2Qe0AA11yCIw6/c7jWKxwi1Jd3eCW4nuVnc6+P9W/wLvvIWw3v47qAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0wMVQxMjo0NDo0NCswMDowMPUMOH4AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMDFUMTI6NDQ6NDQrMDA6MDCEUYDCAAAAAElFTkSuQmCC", "toughnessMasterRibbon": "data:image/png;base64,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", "contestStarRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH5QIBDC0H/3UivAAABmxJREFUSMd1lltsVNcVhr+9z5k5M56bZ2zPMPgCvmGwuTigOOA0kEgkgGhStSlRK9JWqSpVkVq1fUgfWlVVH/pUqQ9VpT708kTUqiQqlzYFlYSUYAUHsGkwYMAXfPfcPLbn5jNzztl9ADtOQn5pS0tLa/9rSWvt9W/BIwghVk00KVGA4zish1KKx8UL+dBWjvpcrFh/4ZEzDhwCdOAj4BZgrydcRyCATqAXsIFzwOw6LvTPVBNtjjf9+dvPff1IwOvj8u2r8+/97/LJ5WL+twr1wOXSAYFVtgA2ur36D9t6615tfzrWYOZs+v8+fi47k/8OkFxNoq2Sy4eOF18/8upPf3XsR6K3bReHuvf7m2KNT40mJntTixldKdnjOGKvUvbOuubAL1/4ybZvvfDjjmDb07W07qthObnS8uBa5oaQYmi1cF0BEoHtOLom9Z7uhi1SKIVlWwQML68d+Cr1kQ09J0d+39O7vx5lw9mLt4i+VMvWAzGUUtgVB02XxLb6pBDsUY46KaRm4djoKGU4qBcbfVXHG/2+/QG3wWqrHMehnFmkQ4Q5djjK4WMKbMj5qhiqK5NIZ6mtDj7sHwrdaxBpefK1UjbRVlyYPAGc1YUQ3z1SH/vN8eYGn4XAJYDVFEphLRcwU4s4TQVyiwrlCMqVEtkFcHklkVAATQhQCqEZNB/4fgTh/sr0R28eTAy984ZepWs9h+ujvm3VAe4s5alY1tpwCSFxhfz4yjX0XyswXfFhmXmGpgrEvtxAMORFSrk2WVbZQUg3gVgnsc5DvvS993tkoWJ9cDW9uFJ2HFwC8sX82uxKKRGhAH2J25y/tMT2rv20t27i2n8qpK5D2O9Hrr4BwCxKhDRwLJPsxDXTMguXNWBsfsXc1R2p7oh73YyWYFvLbnRNY2YxwZnhf3I9/W++dnwXu3YGCfhsasMG4wMOxWwOLSzRqyTF/Ao3LxjonkMUUvd5cOkP561y4dearmlmzrYbgz7j+d3BIINOhklR5F76ARcmz9H57BQvvdJK+xY/Pn+cQHgHLZsqPP9MhBpLMHjepJDUKQ5LxFwNS2wiffs0mbH+Pwmpvadbtq3563yds/tjvDOQpW5vHUd/YFEuLbCjUM1g/yQ1fovWHfuoCnRgWVO4PBpTI1PMTud4/RvdBKqqCIZdnD5zn9+d/gft3SnmPjairXubkcBOb9DzXKglTF+7wcDiEho5aqMmDRsV2/a0MJ2qIAjiOPNUypOUSzkm5y269rTQ2KiIRE0cFrg3k2Fze5ra5iDekOfwrQvDzRIQoXjQJXWNQGMIY08LA4MpvD6DSlkj5NfxhEKYpVFmx66QnZ9B2TbeYJhwyEV5xY0v5OFSXwJncyuekAvHpQjU+d2AksBYenxhwjItDMMgFKrj4pUC46NZIrEAi2lBpVjm9o2bXHz3Jn19k0w9SFIxV8gkNKqjAYbvJPnXpSQVoVBuh+JikYWpxWFgTgJmpVRJV/IWHuFh+V6B8M7d/OX0DDOTaVq3hVnKSRLZCF3dXbRuaWFipop80Uv79hqmp+b449kZvFtauXv1LmbFpJAtUjGtFFDRpCatcrFc9Ad9R1/53suG2zaYLkyQKJe4cnUWO19g+/Yo+55pJV5fR2xjDZvbwghpcn1wir+en6Por0Y4AhlRxDrqGHj7ZjI1mvmZEGJCe7S3RzITWaOSs77kb/LKwQ9vgA6hrjg3xnJcHV5kYiRJjbdEYi7F2XfT/HfI4XbWxt8eJzWSITmXwhMx6H/zRvH+5fFfoHhLINbWtXJs53YqmT4Y2RGIx3dEyU4tkZvPE22vxeMNEmrdyukTQ9wbNdh68DBzC0n8G6uYuTuLMhTx7iiJOykGTt08Y1fsn6+KlLa2tw29UFwqTSyn8j2BqK/GsRSO5eD1eiklTOo31FMf28qGzc1kcilGR0aRmmBhPkvFKZOeyDJ46tYH+Uz+DSHE3KrgiPVyKTWJYzudLo/rm5oun2je27TvyaNPRELeEOVJh9pQHYVynmQ+QbSthtxynr5T/XPjAxN9lml/aFv234BZqeso2/4kwSeyqVBKUNsSIT2WcQO9Hr/n5XjDhqP18Y1Vtz6+877QcDd1NhxITKZS2fns2ZWi+RZwzRvy2qWl0pr8rv8gfOqn8NnztjoB0AV0P9JwN/Ak0N7x1Bbg0/Gf4+QL8LjgL8Jjq32E/wM+hO8D1cDR7wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMi0wMVQxMjo0NTowMiswMDowMP1UaIAAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDItMDFUMTI6NDU6MDIrMDA6MDCMCdA8AAAAAElFTkSuQmCC", "legendRibbon": "data:image/png;base64,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", "towerMasterRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACylBMVEUAAAABAQH///8VEgkAAQDDq2UeGxMAAAMCAQIDAgIGBQMGBQQDAwIAAAEJCAcGBQUCAgIAAAAHCAgREhEREhIREhEQEBAQEA8QERAHBwcAAAAAAABGQCc8NyEAAABaUzJLQiEAAAAlJBwfHRQAAAAAAAAoKCcfHx8AAAAAAAApKSggIB8oKCYfHx4AAAAODg12dHFvbWoKCgoAAAACAgI+MT42KzcAAAAAAAAAAAAwLRshHQsAAAAICAYzLhkuKRQDAwIYFxMPDgwAAAAAAAAMCwdRTDKjm2tEQCkGBQUAAAAyLBeCdEEoJBYAAAACAgNuYjgAAAADAwR0aD53bEV6ck0CAgN9dlUAAAAAAABMSDI9OywAAAAAAAAiIBZxZjtqZksbGhQAAAAJCwgAAAAAAAEyLBiAbjR4aTYpJhgAAAAFBAUCAgMAAACklFSjk1WpnWixqoG2s5G0sIuspHWkl2DPtFjJqkfSuV/f0Yfo4qjo4qnc0IvOuWaTjXeKdHd4W296YHZ7Y3x6Yn6PfoeQinXV0szDo9qbbMGdbsGcbsGbbMDIqdvNy8TU0srDo9ibbb+db8Ccbb/IqdrMysLEo9jMysPV0svFpdnJqtzNy8OxksaebsKeb8KxlMSFYJuabb6abL6AXJV9cVJtWWlsWGVzYz2zroKqonCJg119d1G1r4Hk3afg2J2OejzSvWzfz4nm3KDo36Lj1o/OvXOFczfRsU/PtF7j0IPt46P07rTt5abWxHrZwGmMeTzKq07UtlfTu2nczYjk2pvi1prez43ZwnKPfkXNr1TRsU7Jrlrm1Ibu4qHl2Zzu5affzIGQg03Ps13Mrk/Iq1TZwW7h0Ijs4aPy6q/k1Y+TiVjYv2rGq1PQsE3VuF3hzX/j157v5KS1pWTVu2HHqEzLrVDUumfdyoPq4ae0sIe8o0/Nrk3Ttlq/sG/////919lhAAAAbnRSTlMAAAAAAAAAAAAAAAAAAAAAAB2Xp6ampqamkxg58+8x8u4l3dYeBrStAwWzq7WtAYj9+38Nf/r1cgsg3c4SW+PcTcSxIRyB4/7WazXN/bgfZ/lDZ/f392j4REfp2i09rffxni8BClfH/fm4GoByESsXtTwAAAABYktHRAJmC3xkAAAAB3RJTUUH5QIBDC86lSsMLwAAAVVJREFUKM9jYAABRkEhYRFRMXFxCWFJKUYGBGCUlsnLLygsKi4plZVDlZAvK6+orKquqVVAlVBUqqtvaGxqbmlVVkGRUFVra+/o7Oru6VXXQJHQ1OrrnzBx4qTJU7QxJKaCJaahSajqTJ8Bkpg5SxdVQk/fYPaciRPnzjM0YkKRYDA2MZ2/YOEiM3MLBhQJRgZLq8VLli6ztgGxGVD02NotX2HvwIwmzMLAKue4cpWTMxuaDnYXV7fVa9a6e3jaMHAgiXN6ea9bv2Hjps1bfHz9kHVw+W/dtn3Hzl279+wNCESW4A7at//AwUOHjxw9Fowq4X/8xMlTp8+cPXc+BE3iwsVLl69cvXb9RiiqRNjNW7fv3L135v6D8AhkCZ7IqIePHj95+uz5i+gYFH9oxMbFv3z1+k1CYlIyaiDypqSmpWdkRmoyoAQiKBz4srJzcvkZBKDiADzjecS9QiUdAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTAxVDEyOjQ3OjU0KzAwOjAw0pGD4wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0wMVQxMjo0Nzo1NCswMDowMKPMO18AAAAASUVORK5CYII=", "earthRibbon": "data:image/png;base64,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", "galarLeagueChampionRibbon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH5QIBDjoNGaebWgAABM1JREFUSMeVlltM01ccxz/n31JaSim9ABIuURTachFhxIDbEIeiqJtz85bpTHwwS1yyvfiyly3bHnXJss1syxJNpktwuKiJLkbidODGplGGMiOXiHIr9ka5lNLS9uxhkjCg4H7JSU5Ofuf7yfme3+/kwHOEEGJmuhXYNWft/4UQYsEBJFospstWq7kFMCyS959QFmFZpZQOKaVKSglgq6urrtixo74UWC2lREqpSCntwLJ4IosBXs/JTr+WkWE+AZQrirJl86b16dvrqww6nWYrUGq1pn6em5v+ixAcVJSFLVPHU5dS/rlqVWb00MGad36+cmdnV7dfrlubQ5LGR9ka2+GMjLRDb7xWknm2sdnT1+dqiQeI6//FH99Dr0/89MzJwzLoa5AP7p6SU84vZGSsUT7sOCfHnSflxR8OSItZ9yWgxLsH1VyAlJKc7HS8fkF7e3ff6Pj0lu2bqyy5ZieBQJCIzCDLOsbk01Y+OnZtoO2+9+jmug3DplQ9g0OueQCxACBTrVYdtViMJimRCWqlpuGbV/NWJKnwNk9CRJJSFmJU62P3+3/1+8dV19QqVczj9U1MT0c+E0L0PSuKuIB0jSbh5J5da7ft31vHlO8hxfYkvLcCBCeWIbXJaMZvkbFumvYBK3rLi1y41ML3Zy5dDwZDB4UQA7MB8ywCAtFo7Gpf/0hSUX5yeW2VQdGai1CnGdGvsKLNSsaUF0OvU2POWsPt9gH57XfnT4+OBo4AQ3MtmlemzxI8fv/EsV+b7/VPRDOYCmswJAXRKz2YNJ0ka4PEkssIR9TcbPnN43L7jwPOJRtt5mjPEofu/j16435XjNhkP+HRbjTTT0iIuYgmriQsDfT0PORu2+ObQM+M+Gx75lk05xSxsbFJXSgkduZlBRWTdhyp0hHT2ZgSGfT39nLqdJts/qPzuEDcRswXX7TR9u1+k6tNTeWZBa+oPjgf5oX0HgrTp1A0Y6S42tB1eahaXiGuWjvLfG4PirLwoxDvDmho/MliszteMRRsxKl7CXfKfpp+j3D5Sgdl4TSqpZntuXaKHYXVQoj02XsXBcyK0qI1a+2D0SxUhiwKC+2Ew8GxiXDIn+soJDvZxMpAlKri0gKgIp7IgoDKykpMRsOmHEelvtsVJjFBwawJ4vN6Orwj/ns+rRo0CTDspXqlTZtqtWz86sSJ5we0tramFtjsNTGTA89oCEOSGvWUC7fHfd/r9bY9CQdAr4PxCcoTjThs9pp3jxyxLgmYeYuA1UWlFUW9ASPT0RhWQwJBXz9er69jZMTX8cjvhRQDRGMs801QWVJqA8qllCzZaAX5+RgN+toVxesMXe4YQihkGhPwDfeGQuFIF9FYV89T5yTmFFAEYthHTb4jKcVk2rh3396lLerq7jYW2Oy10eRsHrc1EXY9IMuk4Bx47AeeAP29gwO+KXMKt0fcXHjQRq5Ki8Nu33C24axlrt5CfZDncntWNX79Yedw+53ridb8l7XrPykaGhwYBFxAZHBoaKAl6Mv+uP1GZ5tn6HrFyKP1rjH/ciAf8MatSyEEiqKYgHogV1PyNkBtYVGRMy0t7Rz/dr6wWq1nSkpK3ED9Wzl2gGygXlEUy6K/jYV+CKrEZIA9wI5ZqduAA4vtm4l/AL5Y8ny5Y79UAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTAxVDE0OjU4OjA3KzAwOjAwO/nMigAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0wMVQxNDo1ODowNyswMDowMEqkdDYAAAAASUVORK5CYII=", "battleMemoryRibbon": "data:image/png;base64,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", "battleMemoryRibbonGold": "data:image/png;base64,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", "spiritDuelsChampion": "data:image/png;base64,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", "spiritDuelsSupporter": "data:image/png;base64,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" }; var medalCap = 40; var medalIcons = { // Spring Open Tag Battle Tournament, Best Catcher #1~3 "300": base64Ribbons.pairAbilityRibbon, "285": base64Ribbons.doubleAbilityRibbon, "291": base64Ribbons.abilityRibbon, // Arena Tycoon #1~3 "1": base64Ribbons.toughRibbonMaster, "2": base64Ribbons.toughRibbonUltra, "3": base64Ribbons.toughRibbonGreat, // Pyramid Adventurer #1~3 "4": base64Ribbons.smartRibbonMaster, "5": base64Ribbons.smartRibbonUltra, "6": base64Ribbons.smartRibbonGreat, // Spirit Duels "7": base64Ribbons.spiritDuelsSupporter, "8": base64Ribbons.spiritDuelsChampion, // Tower Tycoon #1~3 "9": base64Ribbons.multiAbilityRibbon, "10": base64Ribbons.victoryRibbon, "11": base64Ribbons.winningRibbon, // Contest Champion #1~3 "299": base64Ribbons.coolRibbonMaster, "281": base64Ribbons.coolRibbonUltra, "287": base64Ribbons.coolRibbonGreat, // Photographer #1~3 "301": base64Ribbons.beautyRibbonMaster, "280": base64Ribbons.beautyRibbonUltra, "303": base64Ribbons.beautyRibbonGreat, // Collector Fanatic #1~3 "302": base64Ribbons.cuteRibbonMaster, "282": base64Ribbons.cuteRibbonUltra, "292": base64Ribbons.cuteRibbonGreat, // Consecutive Weeks #1 & Top Three "17": base64Ribbons.battleMemoryRibbonGold, "37": base64Ribbons.battleMemoryRibbon, // Celebrity Easy ~ Abyssal "253": base64Ribbons.coolnessMasterRibbon, "252": base64Ribbons.beautyMasterRibbon, "251": base64Ribbons.cutenessMasterRibbon, "249": base64Ribbons.clevernessMasterRibbon, "258": base64Ribbons.toughnessMasterRibbon, "255": base64Ribbons.contestStarRibbon, // League "248": base64Ribbons.galarLeagueChampionRibbon, // Monotation Battle Event "243": base64Ribbons.towerMasterRibbon, // Halloween/Summertime Trials "173": base64Ribbons.earthRibbon, "256": base64Ribbons.earthRibbon, // unused "999": base64Ribbons.legendRibbon }; var playerTemplate = { // when adding any object that has variable keys, remember to exclude them from sanitisation! id: "", casedName: "", nameColor: "", idnum: 0, created: 0, tutorialFinished: 0, pokemon: [], party: [], helds: [], berries: { petayaCombo: 0, pecha: false }, money: 0, costume: "none", tutorial: { inTutorial: false, step: 0, privateWildPokemon: null, viewedContestRules: false, viewedRules: false }, balls: { safari: 0, great: 0, ultra: 0, master: 0, myth: 0, level: 0, quick: 0, luxury: 0, mono: 0, love: 0, mirror: 0, inver: 0, photo: 0, spirit: 0, uturn: 0, lightning: 0, cherish: 0, heavy: 0, bait: 0, golden: 0, rock: 0, stick: 0, premier: 0, spy: 0, clone: 0, gacha: 0, rare: 0, dust: 0, mega: 0, spray: 0, amulet: 0, honey: 0, eviolite: 0, soothe: 0, crown: 0, scarf: 0, battery: 0, itemfinder: 0, permfinder: 0, pearl: 0, stardust: 0, starpiece: 0, bigpearl: 0, nugget: 0, bignugget: 0, cometshard: 0, gem: 0, box: 4, salt: 0, entry: 0, silver: 0, pack: 0, fragment: 0, egg: 0, bright: 0, philosopher: 0, philosopherpebble: 0, materia: 0, water: 0, cherry: 0, blkapricorn: 0, whtapricorn: 0, pnkapricorn: 0, ylwapricorn: 0, bluapricorn: 0, redapricorn: 0, grnapricorn: 0, coupon: 0, fossil: 0, form: 0, burn: 0, soda: 0, cookie: 0, shady: 0, lens: 0, mail: 0, crystal: 0, scale: 0, mushroom: 0, brush: 0, dew: 0, hdew: 0, ldew: 0, easteregg: 0, candybag: 0, celebrityTicket: 0, pokeblock: 0, ash: 0, oran: 0, pecha: 0, razz: 0, bluk: 0, leppa: 0, nanab: 0, pinap: 0, tamato: 0, petaya: 0, watmel: 0, miracle: 0, platinum: 0, deluxe: 0, lucky: 0, moonshard: 0, sunshard: 0, dummy: 0, dummy2: 0, dummy3: 0, battlepoint: 0, terashard: 0, terajewel: 0, teraorb: 0, gigantamix: 0, maxmushroom: 0, maxhoney: 0, wishingpiece: 0, wishingstar: 0 }, eventFlags: { "144": 0, "145": 0, "146": 0 }, deluxeBait: { "commons": { "rate": 0, "list": [] }, "uncommons": { "rate": 0, "list": [] }, "rares": { "rate": 0, "list": [] }, "inedible": 0 }, aviData: { "style": "f", "tone": "1", "hair": "bru" }, celebBetEarned: 0, decorations: {}, firstCelebrityRun: true, ticketCelebrityRun: false, medals: [], medalRecords: { "Best Catcher": { first: 0, stayfirst: false, topthree: 0, staytopthree: false }, "Contest Champion": { first: 0, stayfirst: false, topthree: 0, staytopthree: false }, "Photographer": { first: 0, stayfirst: false, topthree: 0, staytopthree: false }, "Collector Fanatic": { first: 0, stayfirst: false, topthree: 0, staytopthree: false }, "Arena Tycoon": { first: 0, stayfirst: false, topthree: 0, staytopthree: false }, "Pyramid Adventurer": { first: 0, stayfirst: false, topthree: 0, staytopthree: false }, "Tower Tycoon": { first: 0, stayfirst: false, topthree: 0, staytopthree: false } }, recentQuests: [], volleyballRecords: { spikes: 0, blocks: 0, sets: 0, digs: 0, points: 0, pointsGiven: 0 }, volleyballTeam: "none", costumeInfo: { preschooler: { level: 1, skills: [], exp: 0 }, breeder: { level: 1, skills: [], exp: 0 }, pokefan: { level: 1, skills: [], exp: 0 }, explorer: { level: 1, skills: [], exp: 0 }, chef: { level: 1, skills: [], exp: 0 }, fisherman: { level: 1, skills: [], exp: 0 }, battle: { level: 1, skills: [], exp: 0 }, scientist: { level: 1, skills: [], exp: 0 }, backpacker: { level: 1, skills: [], exp: 0 }, rich: { level: 1, skills: [], exp: 0 }, ninja: { level: 1, skills: [], exp: 0 }, rocket: { level: 1, skills: [], exp: 0 }, flower: { level: 1, skills: [], exp: 0 }, journalist: { level: 1, skills: [], exp: 0 }, admin: { level: 1, skills: [], } }, records: { gachasUsed: 0, masterballsWon: 0, jackpotsWon: 0, contestsWon: 0, pokesCaught: 0, pokesNotCaught: 0, catchInkay: 0, pokesCloned: 0, pokesEvolved: 0, pokeSoldEarnings: 0, luxuryEarnings: 0, luxurySilver: 0, pawnEarnings: 0, pawnComet: 0, rocksThrown: 0, rocksHit: 0, rocksMissed: 0, rocksBounced: 0, rocksDodged: 0, rocksHitBy: 0, rocksWalletHit: 0, rocksWalletHitBy: 0, rocksCaught: 0, rocksDodgedWindow: 0, rocksMissedWindow: 0, rocksWalletEarned: 0, rocksWalletLost: 0, rocksWindowEarned: 0, rocksWindowLost: 0, rocksItemfinderHit: 0, rocksChargesGained: 0, rocksChargesLost: 0, baitUsed: 0, deluxeBaitUsed: 0, goldenBaitUsed: 0, goldenBaitWeak: 0, baitAttracted: 0, baitNothing: 0, baitWater: 0, catchQuick: 0, catchClone: 0, catchSpy: 0, catchHeavy: 0, catchLuxury: 0, catchMono: 0, catchMyth: 0, catchSpirit: 0, catchLightning: 0, catchMirror: 0, catchLove: 0, catchSwitch: 0, catchInvert: 0, catchLevel: 0, catchPhoto: 0, catchFriend: 0, catchCherish: 0, photosTaken: 0, itemsFound: 0, collectorEarnings: 0, collectorGiven: 0, scientistEarnings: 0, scientistGiven: 0, arenaWon: 0, arenaLost: 0, arenaPoints:0, towerHighest: 0, towerHighestNew: 0, towerEarnings: 0, towerSilver: 0, towerBP: 0, towerTotal: 0, towerBosses: 0, towerSecretBosses: 0, towerFinalBosses: 0, consecutiveLogins: 0, gemsUsed: 0, megaEvolutions: 0, devolutions: 0, devolutionDust: 0, wonderTrades: 0, wonderStarter: 0, factionMVPs: 0, factionWins: 0, pokeRaceWins: 0, pokeRaceEarnings: 0, underdogRaceWins: 0, favoriteRaceWins: 0, factoryFirst: 0, factorySecond: 0, factoryThird: 0, quizFirst: 0, quizSecond: 0, quizThird: 0, topQuizScore: 0, bingoWon: 0, packsOpened: 0, pokesStolen: 0, notBaitedCaught: 0, pokeRaceSilver: 0, //fullyPlayedContests: 0, pyramidLeaderScore: 0, pyramidHelperScore: 0, pyramidLeaderClears: 0, pyramidHelperClears: 0, pyramidTotalScore: 0, pyramidMoney: 0, pyramidSilver: 0, gymsLost: 0, gymsCleared: 0, allGymsCleared: 0, eliteCleared: 0, eliteLost: 0, eggsHatched: 0, brightEggsHatched: 0, rareHatched: 0, transmutations: 0, transmutationsMade: 0, philosopherTransmutations: 0, philosopherTransmutationsCost: 0, journalPoints: 0, journalSubmitted: 0, burnReceived: 0, burnGiven: 0, burnAuraReceivedHeal: 0, burnAuraGaveHeal: 0, shadyUsed: 0, mongerAuctionsWon: 0, cookiesEaten: 0, mushroomsEaten: 0, scalesUsed: 0, mailsSent: 0, crystalsUsed: 0, photosRetouched: 0, missionPoints: 0, missionCleared: 0, celebrityScore: 0, celebrityScoreEasy: 0, celebrityScoreHard: 0, celebrityScoreExpert: 0, celebrityScoreSuperExpert: 0, celebrityScoreAbyssal: 0, idolUnlocked: 0, idolActivated: 0, casesSolved: 0, fastestCaseSolved: 0, medalsWon: 0, wildsScared: 0, goodJournalSubmission: 0, scientistPhotoSubmission: 0, teraMonsCaught: 0, teraOrbsDeployed: 0, teraOrbsSucceeded: 0 }, missionPoints: 0, photos: [], hideLB: [], removedFromLB: false, secretBase: [], secretBaseCache: [], baseValue: 0, costumes: [], savedParties: [], ninjaParty: [], megaTimers: [], starter: null, starter2: [], lastLogin: null, consecutiveLogins: 1, lastStreak: 1, lastViewedRules : 0, tradeBlacklist: [], playerBlacklist: [], lastSold: {}, locked: false, altlog: [], tradeban: 0, truesalt: 0, srate: 1, trackers: [], auctionWarns: 0, auctionWarnCooldown: 0, burnLastUsed: 0, scaleColor: null, scaleDeadline: 0, mushroomTheme: null, mushroomDeadline: 0, zcrystalUser: null, zcrystalDeadline: 0, cooldowns: { ball: 0, bait: 0, rock: 0, gacha: 0, itemfinder: 0, stick: 0, costume: 0, auction: 0, shopEdit: 0, lastBaits: [], nubTaunt: 0, baseView: 0, unown: 0, burn: 0, price: 0, unsell: 0, daycare: 0, detective: 0, buyFromPlayer: 0 }, cherished: [], freebaits: 0, celebrityRegion: "kanto", shop: {}, quests: { collector: { requests: [], reward: 0, cooldown: 0, deadline: null }, scientist: { cooldown: 0, photo: 0, pokemon: 0 }, arena: { cooldown: 0 }, tower: { cooldown: 0, bonusPower: 0 }, wonder: { cooldown: 0 }, pyramid: { cooldown: 0, bonusStamina: 0, hazards: [] }, baking: { cooldown: 0 }, alchemist: { cooldown: 0 }, decor: { cooldown: 0 }, league: { registered: false, cooldown: 0, week: 0, badges: [], eliteCurrent: false, eliteCurrentUsed: false, eliteNext: false }, journal: { cooldown: 0, trackedRequests: {} }, arborist: { cooldown: 0 }, detective: { cooldown: 0, lastGuesses: [] } }, altTimeline: { lead: 0, buff: 1, cooldown: 0 }, npcBets: { giovanni: 0, koga: 0, sabrina: 0, erika: 0, chuck: 0, clair: 0, jasmine: 0, pryce: 0, norman: 0, flannery: 0, winona: 0, wallace: 0, fantina: 0, volkner: 0, roark: 0, candice: 0 }, missions: [], trials: { name: "", missions: [], pastIDs: [], currentIDs: [], level: 1, points: 0, bonusPointsReceived: false }, spiritDuels: { rank: 0, rankName: "Grunt", team: "None", exp: 0, box: [], skills: [], skillChoices: {}, roundStreak: 0, prizePoints: 0 }, bonusLogin: { index: 0, name: "" }, story: { box: [], party: [], inStory: false, state: "Reading", clearedChapters: [], currentPokemon: { id: 0, bst: 1, multiplier: 1 }, chapter: 0, step: 0, savePoints: { "0": 0, "1": 0, "2": 0 }, variables: { poke1: 0, poke2: 0, poke3: 0, poke4: 0, poke5: 0, poke6: 0, champion: "" } }, hiddenQuiz: { lastPlayed: [], points: 1000, responseData: { hit: 0, tried: 0, hitRate: 0, obscureHit: 0, obscureTried: 0, obscureHitRate: 0, responseSpeed: 0, obscureResponseSpeed: 0, obscureAnswerTime: 0, answerTime: 0 } }, nextSpawn: { pokemon: {}, amt: 1, appearAs: {} }, notifications: [], notificationSources: [], notificationData: { towerWaiting: false, lastTowerParty: [], daycarePoke: 0, daycareWaiting: false, daycareHungry: false, collectorWaiting: false, leagueWaiting: false, lastLeagueParty: [], missionWaiting: true, arenaWaiting: false, lastArenaTrainer: "", lastArenaParty: [], scientistWaiting: false, wonderWaiting: false, pyramidWaiting: false, alchemistWaiting: false, journalWaiting: false, detectiveWaiting: false, arboristWaiting: false, costumeWaiting: false }, inbox: [], unreadInbox: [], rockTargets: [], fortune: { name: null, prop: null, val: 0, desc: null, deadline: 0, limit: 0 }, pokeskills: {}, pokeskillsArr: [ /* example "25": { "a": { "active": true, "expiration": [int], "level": 3, "effect": [str] } } */ ], excludeFromEconomy: false, options: { visible: true, flashme: false, smallBox: false, trading: true, favoriteBall: "safari", alwaysShowMasterBall: true, alwaysShowCherishBall: true, sellPrompt: false, leadAbilityMessages: true, dexOptional: ["stats", "effectiveness", "trivia"], pokeskillsDisabled: false, cherishOff: false, monoSecondary: false, autoForfeitThrow: true, showLeadMessage: false, canSellFormes: true, showEvoMessages: true, broadcastEvoMessages: true, anyNotifications: true, questNotifications: true, persistentBait: false, receiveWeeklyMedals: true, showContestCaptures: true, androidTextFlow: false, showConsecutiveCombo: true, flashRares: true, textViewOnly: false, baitScramble: true }, spawnlessThrows: 0, burningAura: false, brilliantAura: false, auraExpiry: 0, offlineSales: {}, consecutiveCombo: 0, pokeFlashList: [], teraActive: false }; // template end /* Item Variables */ var itemCap = 999; var moneyCap = 99999999; var editableItemProps = { fullName: "string", plural: "string", icon: "number", price: ["number", "array"], aliases: "array", ballBonus: "number", bonusRate: "number", maxRate: "number", maxBonus: "number", cooldown: "number", bstBonus: "number", minBstBonus: "number", shinyBonus: "number", successRate: "number", bounceRate: "number", successCD: "number", failCD: "number", targetCD: "number", bounceCD: "number", throwCD: "number", duration: "number", charges: "number", minVar: "number", maxVar: "number", tradeReq: "number", threshold: "number", legendaryChance: "number", shinyChance: "number", tradable: "boolean", cap: "number" }; var itemData; var loadApricornRecipes = function () { return ( { "luxury": { "reward": "20@luxury", "ingredients": { "blkapricorn": 10, "pnkapricorn": 20, "ylwapricorn": 10 }, "showAmt": "luxury" }, "mono": { "reward": "20@mono", "ingredients": { "blkapricorn": 20, "whtapricorn": 20 }, "showAmt": "mono" }, "myth": { "reward": "20@myth", "ingredients": { "redapricorn": 5, "pnkapricorn": 20, "grnapricorn": 15 }, "showAmt": "myth" }, "quick": { "reward": "20@quick", "ingredients": { "bluapricorn": 20, "ylwapricorn": 20 }, "showAmt": "quick" }, "clone": { "reward": "20@clone", "ingredients": { "whtapricorn": 10, "blkapricorn": 10, "grnapricorn": 10, "redapricorn": 10 }, "showAmt": "clone" }, "level": { "reward": "20@level", "ingredients": { "whtapricorn": 15, "grnapricorn": 15, "pnkapricorn": 10 }, "showAmt": "level" }, "spy": { "reward": "20@spy", "ingredients": { "whtapricorn": 5, "bluapricorn": 20, "blkapricorn": 15 }, "showAmt": "spy" }, "love": { "reward": "20@love", "ingredients": { "pnkapricorn": 20, "bluapricorn": 10, "blkapricorn": 10 }, "showAmt": "love" }, "lightning": { "reward": "20@lightning", "ingredients": { "ylwapricorn": 20, "whtapricorn": 20, "dew": 25 }, "showAmt": "lightning" }, "heavy": { "reward": "20@heavy", "ingredients": { "blkapricorn": 20, "bluapricorn": 10, "redapricorn": 10, "dew": 15 }, "showAmt": "heavy" }, "photo": { "reward": "20@photo", "ingredients": { "redapricorn": 20, "whtapricorn": 10, "blkapricorn": 10, "hdew": 10 }, "showAmt": "photo" }, "mirror": { "reward": "20@mirror", "ingredients": { "bluapricorn": 20, "ylwapricorn": 10, "pnkapricorn": 10, "dew": 20 }, "showAmt": "mirror" }, "uturn": { "reward": "20@uturn", "ingredients": { "redapricorn": 5, "grnapricorn": 20, "whtapricorn": 10, "bluapricorn": 5, "hdew": 20 }, "showAmt": "uturn" }, "inver": { "reward": "20@inver", "ingredients": { "bluapricorn": 20, "redapricorn": 20, "dew": 40, "hdew": 40 }, "showAmt": "inver" }, "cherish": { "reward": "@cherish", "ingredients": { "pnkapricorn": 20, "ldew": 5 }, "showAmt": "cherish" } } ); } var gymData = {}; var heldCodes = { "1": "oran", "2": "pecha", "3": "razz", "4": "bluk", "5": "leppa", "6": "tamato", "7": "pinap", "8": "nanab", "9": "petaya", "10": "watmel", "11": "miracle", "12": "platinum" }; var updateItemData = function() { itemData = { //Balls safari: {name: "safari", fullName: "Safari Ball", type: "ball", icon: 309, price: 30, ballBonus: 1, cooldown: 6000, aliases:["safariball", "safari", "safari ball"], tradable: false}, great: {name: "great", fullName: "Great Ball", type: "ball", icon: 306, price: 75, ballBonus: 1.5, cooldown: 9000, aliases:["greatball", "great", "great ball"], tradable: false}, ultra: {name: "ultra", fullName: "Ultra Ball", type: "ball", icon: 307, price: 180, ballBonus: 2, cooldown: 12000, aliases:["ultraball", "ultra", "ultra ball"], tradable: false}, master: {name: "master", fullName: "Master Ball", type: "ball", icon: 308, price: 10000, ballBonus: 255, cooldown: 90000, aliases:["masterball", "master", "master ball"], tradable: true, cap: 1, special: true}, myth: {name: "myth", fullName: "Myth Ball", type: "ball", icon: 368, price: 500, ballBonus: 1, bonusRate: 2.25, cooldown: 15000, aliases:["mythball", "myth", "myth ball"], tradable: true}, level: {name: "level", fullName: "Level Ball", type: "ball", icon: 310, price: 500, ballBonus: 1, bonusRate: 0.4, maxBonus: 3, cooldown: 10000, aliases:["levelball", "level", "level ball"], tradable: true}, quick: {name: "quick", fullName: "Quick Ball", type: "ball", icon: 326, price: 500, ballBonus: 1.1, bonusRate: 3, cooldown: 12000, aliases:["quickball", "quick", "quick ball"], tradable: true}, luxury: {name: "luxury", fullName: "Luxury Ball", type: "ball", icon: 324, price: 500, ballBonus: 1.25, cooldown: 10000, aliases:["luxuryball", "luxury", "luxury ball"], tradable: true}, premier: {name: "premier", fullName: "Premier Ball", type: "ball", icon: 318, price: 500, ballBonus: 1.5, bonusRate: 3, maxBonus: 4, cooldown: 10000, aliases:["premierball", "premier", "premier ball"], tradable: true}, spy: {name: "spy", fullName: "Spy Ball", type: "ball", icon: 312, price: 500, ballBonus: 1.5, bonusRate: 0.25, cooldown: 16000, aliases:["spyball", "spy", "spy ball"], tradable: true}, clone: {name: "clone", fullName: "Clone Ball", type: "ball", icon: 320, price: 500, ballBonus: 1, bonusRate: 0.05, cooldown: 12000, aliases:["cloneball", "clone", "clone ball"], tradable: true}, mono: {name: "mono", fullName: "Mono Ball", type: "ball", icon: 327, price: 321, ballBonus: 1, bonusRate: 2, cooldown: 9000, aliases:["monoball", "mono", "mono ball"], tradable: true}, love: {name: "love", fullName: "Love Ball", type: "ball", icon: 314, price: 500, ballBonus: 1, bonusRate: 2.5, maxBonus: 2.5, cooldown: 10000, aliases:["loveball", "love", "love ball"], tradable: true}, spirit: {name: "spirit", fullName: "Spirit Ball", type: "ball", icon: 327, price: 321, ballBonus: 1.5, bonusRate: 0.5, cooldown: 6000, aliases:["spiritball", "spirit", "spirit ball"], tradable: false, cap: 10, special: true}, lightning: {name: "lightning", fullName: "Lightning Ball", type: "ball", icon: 319, price: 500, ballBonus: 1.2, bonusRate: 10, cooldown: 12000, aliases:["lightningball", "lightning", "lightning ball"], tradable: true}, heavy: {name: "heavy", fullName: "Heavy Ball", type: "ball", icon: 315, price: 500, ballBonus: 1.2, bonusRate: 10, cooldown: 10000, aliases:["heavyball", "heavy", "heavy ball"], tradable: true}, photo: {name: "photo", fullName: "Photo Ball", type: "ball", icon: 317, price: 500, ballBonus: 1, bonusRate: 5, cooldown: 10000, aliases:["photoball", "photo", "photo ball"], tradable: true}, mirror: {name: "mirror", fullName: "Mirror Ball", type: "ball", icon: 323, price: 500, ballBonus: 1, bonusRate: 1, maxBonus: 16, cooldown: 12000, aliases:["mirrorball", "mirror", "mirror ball"], tradable: true}, uturn: {name: "uturn", fullName: "Switch Ball", type: "ball", icon: 311, price: 500, ballBonus: 1, bonusRate: 2, maxBonus: 2, cooldown: 16000, aliases:["switchball", "switch", "uturn", "switch ball"], tradable: true}, inver: {name: "inver", fullName: "Inver Ball", type: "ball", icon: 322, price: 500, ballBonus: 1.5, bonusRate: 1, cooldown: 12000, aliases:["inverball", "inver", "invert", "inver ball"], tradable: true}, cherish: {name: "cherish", fullName: "Cherish Ball", type: "ball", icon: 328, price: 500, ballBonus: 3, bonusRate: 1, cooldown: 18000, aliases:["cherishball", "cherish", "cherish ball"], tradable: false, special: true}, //Other Items //Seasonal change. Rock icon is 206, Snowball is 334 rock: {name: "rock", fullName: "Rock", type: "items", icon: 206, price: 50, successRate: 0.65, bounceRate: 0.1, targetCD: 7000, bounceCD: 11000, throwCD: 15000, aliases:["rock", "rocks", "snow", "snowball", "snowballs"], tradable: false, cap: 9999}, bait: {name: "bait", fullName: "Bait", type: "items", icon: 8017, price: 129, successRate: 0.4, failCD: 13, successCD: 70, aliases:["bait"], tradable: false}, golden: {name: "golden", fullName: "Golden Bait", type: "items", icon: 8016, price: 750, successRate: 0.75, failCD: 20, successCD: 25, minBstBonus: 10, bstBonus: 8, shinyBonus: 0, aliases:["goldenbait", "golden bait", "golden"], tradable: false}, deluxe: {name: "deluxe", fullName: "Deluxe Bait", type: "items", icon: 8016, price: 200, successRate: 1, failCD: 0, successCD: 6, minBstBonus: 10, bstBonus: 8, shinyBonus: 0, aliases:["deluxebait", "deluxe bait", "deluxe"], tradable: false}, gacha: {name: "gacha", fullName: "Gachapon Ticket", type: "items", icon: 132, price: 218, cooldown: 8000, aliases:["gacha", "gachapon", "gachapon ticket", "gachaponticket"], tradable: false}, spray: {name: "spray", fullName: "Devolution Spray", type: "items", icon: 137, price: 5000, aliases:["spray", "devolution", "devolution spray", "devolutionspray"], tradable: true}, mega: {name: "mega", fullName: "Mega Stone", type: "items", icon: 2001, price: 10000, aliases:["mega", "mega stone", "megastone"], duration: 3, tradable: true}, stick: {name: "stick", fullName: "Stick", type: "items", icon: 164, price: 99999, cooldown: 20000, aliases:["stick","sticks"], tradable: false, cap: 1}, itemfinder: {name: "itemfinder", fullName: "Itemfinder Charge", type: "items", icon: 69, price: 50, cooldown: 8000, charges: 30, aliases:["itemfinder", "finder", "item finder"], tradable: false}, permfinder: {name: "permfinder", fullName: "Itemfinder Bonus Charges", type: "items", icon: 0, price: 50, aliases:["permfinder"], tradable: false}, dust: {name: "dust", fullName: "Candy Dust", type: "items", icon: 24, price: 100, aliases:["dust", "candydust", "candy dust"], tradable: false, cap: 9999}, salt: {name: "salt", fullName: "Salt", type: "items", icon: 127, price: 1000, aliases: ["salt", "nacl"], tradable: false, invisible: true}, burn: {name: "burn", fullName: "Burn Heal", type: "items", icon: 54, price: 5000, cooldown: 3600000, threshold: 96, aliases: ["burn", "burnheal", "burn heal"], tradable: false}, dummy: {name: "dummy", fullName: "Dummy", type: "items", icon: 50, price: 1000, aliases: ["dummy"], tradable: false, invisible: true}, dummy2: {name: "dummy2", fullName: "Dummy2", type: "items", icon: 50, price: 1000, aliases: ["dummy2"], tradable: false, invisible: true}, dummy3: {name: "dummy3", fullName: "Dummy3", type: "items", icon: 50, price: 1000, aliases: ["dummy3"], tradable: false, invisible: true}, silver: {name: "silver", fullName: "Silver Coin", type: "items", icon: 273, price: 300, aliases: ["silver", "silver coin", "silvercoin"], tradable: false, cap: 9999}, shady: {name: "shady", fullName: "Shady Coin", type: "items", icon: 300, price: 500, aliases: ["shady", "shady coin", "shadycoin"], tradable: false, cap: 9999}, battlepoint: {name: "battlepoint", fullName: "Battle Point", type: "items", icon: 0, price: 0, aliases: ["battlepoint", "bp", "battle point", "battlepoints", "battle points"], tradable: false, cap: 9999}, entry: {name: "entry", fullName: "Raffle Entry", type: "items", icon: 333, price: 300, aliases: ["entry", "raffle", "raffleentry", "raffle entry"], tradable: false, cap: 50}, coupon: {name: "coupon", fullName: "Decor Coupon", type: "items", icon: 58, price: 15000, aliases: ["coupon", "decor coupon", "decorcoupon", "decoupon"], tradable: false}, fossil: {name: "fossil", fullName: "Helix Fossil", type: "items", icon: 207, price: 5000, bonusRate: 0.1, aliases: ["fossil", "helixfossil", "helix fossil"], tradable: true}, mail: {name: "mail", fullName: "Mail", type: "items", icon: 214, price: 1000, aliases: ["mail"], tradable: true }, crystal: {name: "crystal", fullName: "Z-Crystal", type: "consumable", icon: 3000, price: 10000, duration: 40, aliases: ["crystal", "z-crystal", "zcrystal", "z crystal"], tradable: false }, scale: {name: "scale", fullName: "Prism Scale", type: "consumable", icon: 232, price: 3000, duration: 10, aliases: ["scale", "prism", "prism scale", "prismscale"], tradable: false }, mushroom: {name: "mushroom", fullName: "Big Mushroom", type: "consumable", icon: 47, price: 3000, duration: 10, aliases: ["mushroom", "big mushroom", "bigmushroom", "tiny mushroom", "tinymushroom", "shroom"], tradable: true }, brush: {name: "brush", fullName: "Photo Brush", type: "consumable", icon: 175, price: 3000, aliases: ["brush", "photo brush", "photobrush"], tradable: false }, pokeblock: {name: "pokeblock", fullName: "Pokéblock", type: "consumable", icon: 53, price: 3000, aliases: ["block", "poke block", "noms", "pokéblock", "poké block", "pokeblock"], tradable: false}, //Consumables (for useItem) rare: {name: "rare", fullName: "Rare Candy", type: "consumable", icon: 117, price: 5000, charges: 230, minVar: 0, maxVar: 30, aliases:["rare", "rarecandy", "rare candy", "candy"], tradable: true}, gem: {name: "gem", fullName: "Ampere Gem", type: "consumable", icon: 245, price: 1000, charges: 20, aliases:["gem", "ampere", "ampere gem", "amperegem"], tradable: true}, pack: {name: "pack", fullName: "Prize Pack", type: "consumable", icon: 59, price: 5000, aliases:["prize", "pack", "prizepack", "prize pack"], tradable: true}, egg: {name: "egg", fullName: "Egg", type: "consumable", icon: 94, price: 5000, aliases:["egg"], tradable: true}, bright: {name: "bright", fullName: "Bright Egg", type: "consumable", icon: 94, price: 50000, shinyChance: 32, legendaryChance: 128, aliases:["bright", "bright egg", "brightegg"], tradable: true}, water: {name: "water", fullName: "Fresh Water", type: "consumable", icon: 73, price: 3000, bonusRate: 0.1, aliases:["fresh", "fresh water", "water"], tradable: true}, cherry: {name: "cherry", fullName: "Cherry Delight", type: "consumable", icon: 341, price: 5000, bonusRate: 10, aliases: ["cherry", "delight", "cherry delight", "cherrydelight"], tradable: true}, soda: {name: "soda", fullName: "Soda Pop", type: "consumable", icon: 130, price: 5000, bonusRate: 0.5, aliases: ["soda", "soda pop", "sodapop"], tradable: true, cap: 9999}, form: {name: "form", fullName: "Event Form", type: "consumable", icon: 333, price: 5000, aliases: ["form", "event form", "event form"], tradable: true}, cookie: {name: "cookie", fullName: "Fortune Cookie", type: "consumable", icon: 88, price: 5000, aliases: ["cookie", "fortune cookie", "fortunecookie", "fortune"], tradable: false}, easteregg: {name: "easteregg", fullName: "Easter Egg", type: "consumable", icon: 88, price: 5000, aliases: ["egg", "easter egg", "easteregg", "easter", "rainbowegg"], tradable: false, cap: 9999}, //candybag: {name: "candybag", fullName: "Candy Bag", type: "consumable", icon: 88, price: 5000, aliases: ["bag", "candy bag", "candybag", "halloween", "treat", "treats"], tradable: false, cap: 9999}, celebrityTicket: {name: "celebrityTicket", fullName: "Celebrity Ticket", type: "consumable", icon: 132, price: 5000, aliases: ["celebrityticket", "celebrity ticket", "celebrity"], tradable: true}, teraorb: {name: "teraorb", fullName: "Tera Orb", type: "consumable", icon: 132, price: 9999, aliases: ["teraorb", "tera orb", "orb"], tradable: false, bonusRate: 0.1, cap: 1}, maxmushroom: {name: "maxmushroom", fullName: "Max Mushroom", type: "consumable", icon: 132, price: 9999, aliases: ["maxmushroom", "max mushroom", "maxshroom"], tradable: false}, lucky: {name: "lucky", fullName: "Lucky Coin", type: "valuables", icon: 272, price: 0, aliases: ["lucky", "luckycoin", "lucky coin", "luckycoins"], tradable: false}, //Alchemy related items materia: {name: "materia", fullName: "Prima Materia", type: "alchemy", icon: 93, price: 2000, aliases: ["materia", "prima", "primamateria", "prima materia"], threshold: 400, tradable: true}, fragment: {name: "fragment", fullName: "Ball Fragment", type: "alchemy", icon: 120, price: 2000, aliases:["fragment", "ball fragment", "ballfragment"], threshold: 5, tradable: true}, philosopher: {name: "philosopher", fullName: "Philosopher's Stone", type: "alchemy", icon: 252, price: 10000, aliases: ["philosopher's stone", "philosopher'sstone", "philosophersstone", "philosopherstone", "philosophers stone", "philosopher stone", "philosopher", "stone", "philosopher's", "philosopher"], tradable: true }, philosopherpebble: {name: "philosopherpebble", fullName: "Philosopher's Pebble", type: "alchemy", icon: 161, price: 2000, aliases: ["philosopher's pebble", "philosopher'spebble", "philosopherspebble", "philosopherpebble", "philosophers pebble", "philosopher pebble", "pebble"], tradable: true }, ash: {name: "ash", fullName: "Sacred Ash", type: "alchemy", icon: 124, price: 10000, aliases: ["sacred ash", "sacredash", "ash", "sacred", "ash ketchum"], tradable: true }, gigantamix: {name: "gigantamix", fullName: "Gigantamix", type: "alchemy", icon: 132, price: 9999, aliases: ["gigantamix", "dynamix", "mix", "spice"], tradable: false}, wishingpiece: {name: "wishingpiece", fullName: "Wishing Piece", type: "alchemy", icon: 132, price: 9999, aliases: ["wishingpiece", "wishing piece", "wish piece", "piece", "wishpiece", "wish", "wpiece"], tradable: false}, //Poké Ball related items dew: {name: "dew", fullName: "Mystical Dew", type: "alchemy", icon: 8017, price: 9999, aliases: ["dew", "mdew", "mysticdew", "mysticaldew", "mystical dew"], threshold: 400, tradable: false}, hdew: {name: "hdew", fullName: "Harmonic Dew", type: "alchemy", icon: 162, price: 9999, aliases: ["hdew", "harmonicdew", "harmdew", "harmonic dew"], threshold: 400, tradable: false}, ldew: {name: "ldew", fullName: "Legendary Dew", type: "alchemy", icon: 131, price: 9999, aliases: ["ldew", "legendarydew", "legenddew", "legendary dew"], threshold: 400, tradable: false}, blkapricorn: {name: "blkapricorn", fullName: "Black Apricorn", type: "alchemy", icon: 59, price: 1000, aliases:["blackapricorn", "black apricorn", "blkapricorn"], tradable: true}, whtapricorn: {name: "whtapricorn", fullName: "White Apricorn", type: "alchemy", icon: 133, price: 1000, aliases:["whiteapricorn", "white apricorn", "whtapricorn"], tradable: true}, grnapricorn: {name: "grnapricorn", fullName: "Green Apricorn", type: "alchemy", icon: 59, price: 1000, aliases:["grnapricorn", "green apricorn", "grnapricorn"], tradable: true}, redapricorn: {name: "redapricorn", fullName: "Red Apricorn", type: "alchemy", icon: 133, price: 1000, aliases:["redapricorn", "red apricorn"], tradable: true}, bluapricorn: {name: "bluapricorn", fullName: "Blue Apricorn", type: "alchemy", icon: 59, price: 1000, aliases:["bluapricorn", "blue apricorn", "blueapricorn"], tradable: true}, ylwapricorn: {name: "ylwapricorn", fullName: "Yellow Apricorn", type: "alchemy", icon: 133, price: 1000, aliases:["ylwapricorn", "yellow apricorn", "yellowapricorn"], tradable: true}, pnkapricorn: {name: "pnkapricorn", fullName: "Pink Apricorn", type: "alchemy", icon: 59, price: 1000, aliases:["pnkapricorn", "pink apricorn", "pinkapricorn"], tradable: true}, //Berries oran: {name: "oran", fullName: "Oran Berry", type: "berry", icon: 8007, price: 1000, aliases:["oran", "oran berry", "oranberry"], rate: 0.25, rate2: 50, tradable: true}, pecha: {name: "pecha", fullName: "Pecha Berry", type: "berry", icon: 8003, price: 1000, aliases:["pecha", "pecha berry", "pechaberry"], rate: 1.5, tradable: true}, razz: {name: "razz", fullName: "Razz Berry", type: "berry", icon: 8016, price: 1000, aliases:["razz", "razz berry", "razzberry"], tradable: true}, bluk: {name: "bluk", fullName: "Bluk Berry", type: "berry", icon: 8017, price: 1000, aliases:["bluk", "bluk berry", "blukberry"], tradable: true}, leppa: {name: "leppa", fullName: "Leppa Berry", type: "berry", icon: 8006, price: 1000, aliases:["leppa", "leppa berry", "leppaberry"], tradable: true}, tamato: {name: "tamato", fullName: "Tamato Berry", type: "berry", icon: 8026, price: 1000, aliases:["tamato", "tamato berry", "tamatoberry"], tradable: true}, pinap: {name: "pinap", fullName: "Pinap Berry", type: "berry", icon: 8020, price: 1000, aliases:["pinap", "pinap berry", "pinapberry"], tradable: true, rate: 18}, nanab: {name: "nanab", fullName: "Nanab Berry", type: "berry", icon: 8018, price: 1000, aliases:["nanab", "nanab berry", "nanabberry"], tradable: true}, watmel: {name: "watmel", fullName: "Watmel Berry", type: "berry", icon: 8033, price: 1000, aliases:["watmel", "watmel berry", "watmelberry"], tradable: true, rate: 0.25}, petaya: {name: "petaya", fullName: "Petaya Berry", type: "berry", icon: 8056, price: 1000, aliases:["petaya", "petaya berry", "petayaberry"], tradable: true}, miracle: {name: "miracle", fullName: "Miracle Berry", type: "berry", icon: 59, price: 1000, aliases:["miracle", "miracle berry", "miracleberry"], tradable: true}, platinum: {name: "platinum", fullName: "Platinum Berry", type: "berry", icon: 59, price: 1000, aliases:["platinum", "platinum berry", "platinumberry"], tradable: true}, maxhoney: {name: "maxhoney", fullName: "Max Honey", type: "berry", icon: 132, price: 9999, aliases: ["maxhoney", "max honey"], tradable: false}, //Perks amulet: {name: "amulet", fullName: "Amulet Coin", type: "perk", icon: 42, price: 5000, bonusRate: 0.03, maxRate: 0.3, aliases:["amulet", "amuletcoin", "amulet coin", "coin"], tradable: true, tradeReq: 10}, honey: {name: "honey", fullName: "Honey", type: "perk", icon: 82, price: 1000, bonusRate: 0.03, maxRate: 0.3, aliases:["honey"], tradable: true}, soothe: {name: "soothe", fullName: "Soothe Bell", type: "perk", icon: 35, price: 5000, bonusRate: 0.03, maxRate: 0.3, aliases:["soothe", "soothebell", "soothe bell", "bell"], tradable: true}, crown: {name: "crown", fullName: "Relic Crown", type: "perk", icon: 278, price: 5000, bonusRate: 0.01, maxRate: 0.1, aliases:["crown", "reliccrown", "relic crown", "relic"], tradable: true, tradeReq: 10}, scarf: {name: "scarf", fullName: "Silk Scarf", type: "perk", icon: 31, price: 5000, bonusRate: 0.03, maxRate: 0.3, aliases:["scarf", "silkscarf", "silk scarf", "silk"], tradable: true}, battery: {name: "battery", fullName: "Cell Battery", type: "perk", icon: 241, price: 2000, bonusRate: 2, maxRate: 20, aliases:["battery", "cellbattery", "cell battery", "cell"], tradable: true}, eviolite: {name: "eviolite", fullName: "Eviolite", type: "perk", icon: 233, price: 2000, bonusRate: 8, maxRate: 80, threshold: 420, aliases:["eviolite"], tradable: true}, lens: {name: "lens", fullName: "Zoom Lens", type: "perk", icon: 41, price: 30000, cooldown: 10000, bonusRate: 1, maxRate: 10, threshold: 2000, aliases:["lens", "zoom lens", "zoom", "zoomlens"], tradable: false }, box: {name: "box", fullName: "Box", type: "perk", icon: 175, price: [0, 0, 0, 0, 100000, 200000, 400000, 600000, 800000, 1000000], bonusRate: 120, aliases:["box", "boxes"], tradable: false}, //Valuables pearl: {name: "pearl", fullName: "Pearl", type: "valuables", icon: 111, price: 500, aliases:["pearl"], tradable: true}, stardust: {name: "stardust", fullName: "Stardust", type: "valuables", icon: 135, price: 750, aliases:["stardust"], tradable: true}, bigpearl: {name: "bigpearl", fullName: "Big Pearl", type: "valuables", icon: 46, price: 1500, aliases:["bigpearl", "big pearl"], tradable: true}, starpiece: {name: "starpiece", fullName: "Star Piece", type: "valuables", icon: 134, price: 3000, aliases:["starpiece", "star piece"], tradable: true}, nugget: {name: "nugget", fullName: "Nugget", type: "valuables", icon: 108, price: 4000, aliases:["nugget"], tradable: true}, bignugget: {name: "bignugget", fullName: "Big Nugget", type: "valuables", icon: 269, price: 10000, aliases:["bignugget", "big nugget"], tradable: true}, cometshard: {name: "cometshard", fullName: "Comet Shard", type: "valuables", icon: 271, price: 15000, aliases:["cometshard", "comet", "cshard", "comet shard"], tradable: true}, moonshard: {name: "moonshard", fullName: "Moon Shard", type: "alchemy", icon: 271, price: 3000, aliases:["moonshard", "moon", "moon shard", "mshard"], tradable: true}, sunshard: {name: "sunshard", fullName: "Sun Shard", type: "alchemy", icon: 271, price: 3000, aliases:["sunshard", "sun", "sun shard", "sshard"], tradable: true}, terashard: {name: "terashard", fullName: "Tera Shard", type: "alchemy", icon: 271, price: 9999, aliases:["terashard", "tera", "tera shard", "tshard"], tradable: false}, terajewel: {name: "terajewel", fullName: "Tera Jewel", type: "alchemy", icon: 271, price: 9999, aliases:["terajewel", "tera jewel", "tjewel", "jewel"], tradable: false}, wishingstar: {name: "wishingstar", fullName: "Wishing Star", type: "alchemy", icon: 271, price: 9999, aliases:["wishingstar", "wishing star", "wish star", "wishstar", "wstar"], tradable: false} }; }; var editableCostumeProps = { fullName: "string", icon: "number", aliases: "array", rate: "number", rate2: "number", bonusChance: "number", acqReq: "number", acqReq2: "number", record: "string", record2: "string", noAcq: "string", thresh1: "number", thresh2: "number", thresh3: "number", changeRate: "number", specialAcq: "boolean" }; var costumeData = { preschooler: { icon: 401, name: "preschooler", fullName: "Preschooler", aliases: ["preschooler", "pre schooler"], acqReq: 1, record: "pokesCaught", rate: 1.30, thresh1: 25, thresh2: 50, thresh3: 90, changeRate: 0.1, rate2: 1.15, effect: "A master in friendship. Strengthens the bond between a trainer and their Starter Pokémon to increase catch rate at the beginning of an adventure. Also increases the limit of Eviolites that can be used. [Exp. Up item: Eviolite]", noAcq: "Catch your first Pokémon", expTypes: ["daycareplay", "bait", "wincontest", "catch"], expItem: "eviolite", skills: { preschoolerPack1: [2, 2], typeMatchupHelper: [3, 3], ballHint: [4, 4], fasterFinder: [5, 5], preschoolerPack2: [6, 6], preschoolerPack3: [7, 7], preschoolerPack4: [9, 9], preschoolerPack5: [11, 11], preschoolerPack6: [13, 13], preschoolerPack7: [15, 15], preschoolerPack8: [17, 17], preschoolerPack9: [19, 19], preschoolerPack10: [20, 20] } }, breeder: { icon: 379, name: "breeder", fullName: "PokeBreeder", aliases: ["pokébreeder", "breeder", "pokebreeder", "poke breeder", "pokemonbreeder", "pokemon breeder"], acqReq: 15, record: "pokesEvolved", rate: 0.9, effect: "A master in evolution. Taps into years of experience in order to reduce the number of Rare Candies required for evolution. [Exp. Up item: Soothe Bell]", effect2: "Has slightly increased cooldown when throwing Poké Balls other than Safari or Love.", noAcq: "Evolve {0} more Pokémon", expTypes: ["daycareplay", "bait", "wincontest", "catch"], expItem: "soothe", skills: { evolveCheap: [2, 5], extraDust: [3, 7], loveBallBoost: [4, 8], pokeblockBoost: [5, 9], daycarePlay: [10, 15], catchNormal: [12, 17], extraLoveBall: [20, 20] } }, pokefan: { icon: 398, name: "pokefan", fullName: "PokeFan", aliases: ["pokéfan", "pokefan", "poke fan"], acqReq: 200, record: "collectorGiven", rate: 1.2, rate2: -10, effect: "A master in Pokémon. Aficionados of Pokémon tend to stick together and help each other out, granting a bonus payout when finding Pokémon for the Collector's collection. [Exp. Up item: Eviolite]", noAcq: "Turn in {0} more Pokémon to the Collector", effect2: "Has slightly reduced move power in Auto Battles.", expTypes: ["daycareplay", "catchlowbst", "wincontest", "catch", "wintrivia"], expItem: "eviolite", skills: { botdboost: [2, 2], catchLowBST: [3, 5], lowPhotoCD: [6, 10], useLowBST: [11, 13], mirrorBallBoost: [12, 15], finderBasedOnLead: [18, 19], pokefanPack: [20, 20] } }, explorer: { icon: 373, name: "explorer", fullName: "Explorer", aliases: ["explorer"], acqReq: 500, record: "itemsFound", rate: 0.1, rate2: 0.5, effect: "A master in scavenging. Uses knowledge from past finds to slightly increase the likelihood of finding an item with Itemfinder. Rarely, you can even find multiple items or exclusive items! [Exp. Up item: Relic Crown]", noAcq: "Find {0} more items", effect2: "Has slightly increased cooldown when throwing Poké Balls other than Safari or Heavy.", expTypes: ["findrare", "wincontest", "catch", "winmafia"], expItem: "crown", skills: { finderBasedOnLead: [2, 3], heavyBallBoost: [4, 8], catchRockGround: [6, 9], betterPyrItems: [10, 15], betterFinder: [14, 18], pyrStaminaBoost: [20, 20] } }, chef: { icon: 423, name: "chef", fullName: "Chef", aliases: ["chef"], acqReq: 500, record: "baitNothing", rate: 12, effect: "A master in cooking. After years of throwing bait that even a Garbodor wouldn't eat, all it took was a simple dash of seasoning and some ketchup to help make the bait more irresistable to Pokémon with a type disadvantage against their lead. [Exp. Up item: Honey]", effect2: "Has slightly increased cooldown when sucessfully catching.", noAcq: "Fail to attract {0} more Pokémon with Bait", expTypes: ["bait", "wincontest", "catch", "wintour"], expItem: "honey", skills: { pokeblockBoost: [3, 3], premierBallBoost: [8, 12], bakingDiscount: [10, 10], higherBakingYield: [10, 13], catchFire: [11, 16], moreEdibleDeluxe: [14, 18], extraApricornsFromContest: [16, 19], superChef: [20, 20] } }, battle: { icon: 386, name: "battle", fullName: "Battle Girl", aliases: ["battle girl", "battle", "battlegirl"], acqReq: 100, record: "arenaPoints", rate: 20, effect: "A master in fighting. Through rigorous training, people and Pokémon can become stronger without limit. Utilizing powerful offensive techniques, attacks deal more damage in NPC Auto-Battles. [Exp. Up item: Cell Battery]", effect2: "Has slightly increased cooldown when sucessfully catching.", noAcq: "Accumulate {0} more Arena Points", expTypes: ["wintour", "arenasilver", "fighttower", "catch"], expItem: "battery", skills: { extraTourRare: [2, 2], quickBallBoost: [5, 9], battleBoost: [8, 13], extraTourMega: [10, 10], fasterArena: [11, 14], catchFighting: [12, 17], lightningBallBoost: [15, 18], kiai: [20, 20] } }, scientist: { icon: 431, name: "scientist", fullName: "Scientist", aliases: ["scientist"], acqReq: 6, record: "pokesCloned", acqReq2: 50, record2: "scientistEarnings", rate: 0.02, bonusChance: 0.05, effect: "A master in genetics. Recent breakthroughs in science allow easier modification of DNA, granting an increased success rate in cloning, a small chance to clone multiple times in a single attempt, and the ability to clone very rare Pokémon! [Exp. Up item: Cell Battery]", effect2: "Has less success befriending Pokémon in the daycare.", noAcq: "Clone {0} more Pokémon and obtain {1} more Silver Coins from the Scientist Quest", expTypes: ["soda", "clonepoke", "scientist", "catch", "wintrivia"], expItem: "battery", skills: { extraTriviaSoda: [4, 9], extraScientistSilver: [5, 7], cloneBallBoost: [6, 10], catchElectric: [9, 12], tripleChance: [15, 19], cloneBallBoost2: [20, 20], } }, ninja: { icon: 434, name: "ninja", fullName: "Ninja Boy", aliases: ["ninja boy", "ninja", "ninjaboy"], acqReq: 10, specialAcq: true, rate: 0.15, thresh: 499, effect: "A master in ninjutsu. Able to lurk amongst the shadows and create diversions to sneak past a small number of Trainers in the Battle Tower. [Exp. Up item: Silk Scarf]", effect2: "Is less adept at catching with a legendary Pokémon as a lead.", noAcq: "Reach Floor 11 of Battle Tower using a team of Pokémon with <500 BST (without using Battle Girl costume)", expTypes: ["fighttower", "wincontest", "catch", "winmafia", "catchlowbst"], expItem: "scarf", skills: { towerLoot: [2, 4], ninjaSniper: [5, 7], catchPoison: [7, 11], ninjaPack1: [10, 10], mythBallBoost: [11, 14], spyBallBoost: [11, 14], permanentStealthThrow: [13, 16], ninjaDoubleThrow: [20, 20] } }, rocket: { icon: 999, name: "rocket", fullName: "Rocket", aliases: ["rocket"], acqReq: 100, record: "notBaitedCaught", acqReq2: 150000, record2: "pokeSoldEarnings", rate: 0.05, rate2: 0.03, effect: "A master in deception. Years of trickery have granted a small chance to steal back a Pokémon given to NPCs! [Exp. Up item: Amulet Coin]", effect2: "Has less success befriending Pokémon in the Daycare.", noAcq: "Catch {0} Pokémon attracted by other players and earn ${1} more from selling Pokémon", expTypes: ["stealpoke", "arenasilver", "wincontest", "catch", "winmafia"], expItem: "amulet", skills: { extraScientistSilver: [2, 3], spyBallBoost: [4, 5], extraMafiaShady: [5, 6], ninjaSniper: [7, 9], catchDark: [9, 12], betterGacha: [13, 16], extraSellProfit: [15, 19], blackMarketAccess: [20, 20], } }, flower: { icon: 439, name: "flower", fullName: "Flower Girl", aliases: ["flower", "flowergirl", "flower girl"], acqReq: 25, record: "catchMono", acqReq2: 15, record2: "mushroomsEaten", rate: 20, rate2: 0.05, effect: "A master in simplicity. Understanding Pokémon is the key to making them stronger. Single-type Pokémon perform better for this trainer! Also reduces cooldown while using Mono Balls. [Exp. Up item: Eviolite]", noAcq: "Catch {0} Pokémon with Mono Balls and consume {1} Mushrooms", effect2: "Has higher cooldown using the Itemfinder.", expTypes: ["daycareplay", "bait", "wincontest", "catch"], expItem: "eviolite", skills: { catchGrass: [2, 3], berryCatcher: [4, 4], monoBallBoost: [4, 10], pokeblockBoost: [5, 8], finderBasedOnLead: [7, 12], extraApricornsFromContest: [10, 11], catchSing: [12, 15], extendedMushroom: [16, 18], flowerPack: [20, 20] } }, fisherman: { icon: 359, name: "fisherman", fullName: "Fisherman", aliases: ["fisher", "fisherman", "fisher man"], acqReq: 80, record: "baitWater", rate: 0.3, effect: "A master in angling. Superb technique at handling fishing rods allows them to reel back Poké Balls that failed to catch a Pokémon! [Exp. Up item: Soothe Bell]", effect2: "Has higher cooldown using the Itemfinder.", noAcq: "Bait {0} more pure Water-type Pokémon", expTypes: ["bait", "catchwater", "wincontest", "catch"], expItem: "soothe", skills: { catchWater: [2, 3], switchBallBoost: [4, 10], reducedCatchFailCD: [6, 8], catchSplash: [10, 11], extraDust: [13, 16], fisherPack: [18, 18], reelOtherPokeballs: [20, 20] } }, backpacker: { icon: 372, name: "backpacker", fullName: "Backpacker", aliases: ["backpacker", "back packer"], acqReq: 36, record: "wonderStarter", rate: 1.2, effect: "A master in traveling. Can easily fit more Honey, Silk Scarves and Cell Batteries into the bag to benefit more from its effects! [Exp. Up item: Relic Crown]", effect2: "Carrying a heavy backpack makes you throw Poké Balls slower.", noAcq: "Obtain {0} more starters from Wonder Trade", expTypes: ["journal", "catchhighbst", "wincontest", "catch"], expItem: "crown", skills: { catchRockClimb: [3, 5], fasterPhotos: [5, 11], catchIce: [6, 9], haggler: [10, 10], extraApricornsFromContest: [11, 14], levelBallBoost: [13, 16], megaPacker: [20, 20] } }, rich: { icon: 395, name: "rich", fullName: "Rich Girl", aliases: ["rich", "rich girl", "rich boy"], acqReq: 200000, record: "pawnComet", acqReq2: 50000, record2: "luxuryEarnings", rate: 1.2, effect: "A master in money. Personal wealth grants the experience necessary to use more Amulet Coins, Relic Crowns and Soothe Bells than normal people! [Exp. Up item: Amulet Coin]", effect2: "Peasant balls (Safari Balls) are more likely to fail.", noAcq: "Obtain ${0} more by pawning Comet Shards and earn ${1} more from Luxury Balls", expTypes: ["bait", "wincontest", "findrare", "catch"], expItem: "amulet", skills: { botdboost: [3, 5], luxuryBallBoost: [6, 7], extraBuyBonus: [10, 10], extraSellProfit: [11, 14], lowUltraCD: [11, 14], catchFairy: [12, 18], betterFinder: [17, 19], megaPacker: [20, 20] } }, journalist: { icon: 327, name: "journalist", fullName: "Journalist", aliases: ["journalist", "journal", "photographer", "photo"], acqReq: 20, record: "goodJournalSubmission", acqReq2: 5, record2: "scientistPhotoSubmission", rate: 3, rate2: 1, effect: "A master in photography. Keen spacial awareness grants you the best photo opportunities, allowing you to take higher quality photos than usual! [Exp. Up item: Silk Scarf]", effect2: "The Monger probably won't want anything to do with a nosy journalist...", noAcq: "Complete {0} more Journal requests worth at least 50 points and submit {1} more photos to the Scientist", expTypes: ["journal", "wincontest", "takephoto", "catch"], expItem: "scarf", skills: { fasterPhotos: [3, 6], photoBallBoost: [6, 7], lowPhotoCD: [8, 11], catchGhost: [10, 13], smartPhoto: [11, 14], moreJournalPoints: [15, 18], revealAction: [16, 19], revealMood: [16, 19], scientistPhotoGuarantee: [20, 20] } }, inver: { icon: 387, name: "inver", fullName: "Inver", aliases: ["inver"], acqReq: 25, record: "catchInvert", acqReq2: 15, record2: "catchInkay", effect: "A master in type matchups. Possesses a mystical power that inverts type effectiveness, making super-effective moves not very effective, and vice versa.", noAcq: "Catch {0} more Pokémon with an Inver Ball and {1} more Inkay", expTypes: ["none"], skills: { } }, admin: { icon: 167, name: "admin", fullName: "Admin", aliases: ["admin"], effect: "pwrful", expTypes: [], skills: {} } //triathlete: {icon: 361, name: "triathlete", fullName: "Triathlete", aliases: ["triathlete"], acqReq: 50, record: fullyPlayedContests, rate: 0.01, thresh1: 5, thresh2: 8, thresh3: 13, effect: "A master in endurance. Even after playing in the Safari Zone all day, extensive training allows a quick and alert response when a wild Pokémon appears.", noAcq: "{0}"}, //guitarist: {icon: 428, name: "guitarist", fullName: "Guitarist", aliases: ["guitarist"], acqReq: 30, record: "gemsUsed", rate: 5, effect: "A master in melody. ", noAcq: "Use {0} more Ampere Gems"}, //sightseer: {icon: 816, name: "sightseer", fullName: "Sightseer", aliases: ["sightseer"], acqReq: 8, record: "rareHatched", acqReq2: 512, record2: "goldenBaitUsed", rate: 0.05, effect: "A master in tourism. Figured how to bait Shiny Pokemon more effectively after exploring the planet several times.", noAcq: "Match ${0} more rare Pokémon from eggs and use ${1} more Golden Bait" } }; var defaultItemData; var defaultCostumeData; var base64icons = { itemfinder: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAALHRFWHRDcmVhdGlvbiBUaW1lAEZyaSAyMSBOb3YgMjAxNCAyMDozMDo0NCAtMDAwMKEIypIAAAAHdElNRQfeCxUUHwrCV61vAAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAABExJREFUeNqFVutPHFUU/93ZpWyAamOMgIm2Pgrl0ZVHq9t+EAPtZiHWRGOaSFPoF+tnYrJGv+jXEhL/Ah8gaRPb2KSNFqqtSiOQxnYXpFUxatwEAbWBpkuz7GOuc+7OnblzZ0hPcrMze8+5v/M7rzuMr/UDMHCwb5EXiyZIrl9uYUABuWwOkZoIpPjf8+g+mnHsps4/wyLVoUD9sAS5du1jGMw632CIxU7w2YkmFqkho5JQ5Jw7RvRMdgSi2h2w7TjPg1n/qU4ZscQvfGrqI0tvD+5m5sHMEmZmP8UL8QXe2b1gnViBzY2iMJSyubGJA72/BtrtP7zANzcKUIWYGfLlbuZz7HiyFeuZBVgu4YuTjRjuq8e+nnkee3lRAEpRPdXt9H0ZifC2ijBM08TDO9uw/lcaO3Y+h2KphKOf/I5SyUTY2n/skUfR0T3Ppy82ODkIsqN3nYkENaYuPs0O9byFQqGA7U9EUTI5urrexA9fNrDZid1iZe+toa6uDgePEDNDePn9had8dlfffxfcBtNzyqjqCDn+xt88Xygn/rtzu6xCqPB49tLrGV4omLhxZS/LZe85h7x45A9hl88XhTMrKyuYvmBVX802j31YxpSYOeh24rkW88HBQew7NMZ//KbVUjBtZs8y0o8lfuahUEjRNz32YTWmVE2V1ZWe97KRIcDn5uaE1/sP3+JSp2Tlk/Zqa2uRTCYxNDQUYA8vkAriemaIUm5qahUHxuNxdHR0OKxHR0fF79LSEtLptB0Jcsz0VJ+TI/VPl5kLsrq6KkKxtvYfOHedGR4eAYWMAIkd5Wh5eRk3r7aI8PpyFMSMmrmlJSqMJcjMpT1MTotyMbwjioEAx8fHnYIAQpBAdL6hAhATV8p5kQklY6pGFYQ8pSKiFkgm38bAwAAMw0B7e7voO5qF7ml2ddGSTOiZ2DQ373VCVlkZtvbdvtD7RDawdK4866qdfUMy0WeZKsRm5lKjwyZIn5gFsSIutB/2T2WKaZVQ1sX1VNePYCuR5xuqZ/n7NN5D4to4dWpEeFVfX2+DhnxMdWYquFPW9r6vjyg3IyMfIpVKiZKluLe1taGzZ47fuNLsTAT9vpEA6dRNWFmi68kjYVk9upEEEcZ2I6pM/DdvlQBv+udrvPb8Q0QFr6aYiEQue79cDHqM6WA1BHo4dH0CocY+dmwAVX3v4YPr1Th5fh3HB0+IcRWp2e7vIypTOphyQx7Somcq71zWmyNiQkLh7u8/LkYTRYFyKnNDg7iz5yfu6yMq03Nnz4ghSV1OiybC9FeNztWh95EqFHIq8d7eXkxOTjrRCOyjy2ceZ78t3sadO/+K9e3ZXYyG5IOqTTSmxYZYSQBP1QXd8cRMV96qjyjcdIUQSDQaxdjYmBisiURCgFLYWfm7zvVUvSoe/F2X89y0uxuaRSuobE+f/kyEXQCpt+pWot+8QaHreuVP53NAyuxEedr/DwgKwzi3jMhsAAAAAElFTkSuQmCC", gacha: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAA3NCSVQICAjb4U/gAAAAG1BMVEX////4+Piw2OjgoNh4kKi4cMCISKhwSGAwMDAYBeQdAAAACXRSTlMA//////////83ApvUAAAACXBIWXMAAArwAAAK8AFCrDSYAAAAIHRFWHRTb2Z0d2FyZQBNYWNyb21lZGlhIEZpcmV3b3JrcyBNWLuRKiQAAAB0SURBVHicpYwxCoAwDEUzdhXxApncxRt4AZHGveLHWTyAiyXHtlWLHQX/9B68hOjHNGfeM35FwVwnnmRNopjFPZ3O1mK8RWHLVsBd6AwwFIVg4ySlwNUx8+ijLNe9F9tUcnPowreHyUypiTvkZfIZk8n4007iyyLPYqOktAAAAABJRU5ErkJggg==", //myth: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAARhJREFUSEvNlT0OgzAMhTlGj8CYo3TsETp2ZOzI2JGRscfoyNiRsUdgzObqIb0qGOenqkAdLCCJ32eHOK5EpNrSNhVH4P8BcM6JZSVbm8yAotPzKJZxPgWKAuAcEw7Hx66es4tBTADF/asVWA6UgqwAC3HvxcMyEABikH0Bet+/2aJYFosMSn+s/skUt7ZpfwCiyJ0azoeR4/126VZHdpVBfR7mE0ERfNNiwhTHOl0TJiAUvZ8O4lsneBLOyBFxuDYLQDUigqa5fhwhTtNi4Td8rIo262AcprnA4KQz0BCswfq+f5QBmEUIgQiFMK4tJp7sB0gXjpYgxzCfEs82HF7HFNLPn67r8PrdrOGUdKzcmv/oybkoU/NvdoVkS0HgewQAAAAASUVORK5CYII=", box: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXZwQWcAAAAgAAAAIACH+pydAAAA5UlEQVRIx92VwQ2DMAxFMwLHHjtCRuHIGDly7AgdgREyAkdGYIxuEGQJV6ljOwZSVerhCxGF//wTYbsYo/um3M8B3vuEag4A09f6eOsMzGxOZYWJ5tPk0zz3rDld12DVyhEET3y3JGMBtWMBEMK4dEsc0nO884CaOVc5gkBorgJwA0g7c65ygFUBsBElwaRjCeF2DIDKP6SwvJDDCVDwIZcsDN0H+FICdp0AmiVY+o4FtEkA5rukBPRvLrqmmCAzlxKYW0UOKxIIdyA1PfMs0C5Z66jmwVEk2wGn2nXLKef+f+hf1QY4PRz+Bnq4AwAAAABJRU5ErkJggg==", entry: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAAD2AAAA9gAXp4RY0AAAJ4SURBVEhL7ZNdSFNhGMe96y7qou6jXSXM7Xy8mzbmsmIO23Duq02jSIlJC0zMjwKR1ILIzYQRCRUGEnoq6CJtQiCCYZQFod2sKKplQREFQRc7+/ec7c0RzsrwKvaD5+KMvf/zPM/7OyVF/h9MkjgmScIAf1w/zBQqCcJCst2D2Yjru1kwrM9LGBN9TBKRCO9Dpr8eOHMA6A3iXrMTJtHYw/+2NioEYTszGHYEqsoxENiDTF8IGIoAH1PI0t8A0G9KowMui6lVp9Nt4Ed/jyzLpUwUfW1O6+tOVyXUc43AzQvApQ5gYRZIPgGezQHnm4Cr3dkXjx6qhtvCjvOIwlgshi2yKPZQ6PRwgx2fuvdnO8TDBJBRgZlbwI1BINYMTCtANAxVieFRixvD9XvhqWRxHrUSzQqPzTQ5RTt+0eEFpq7luuyjfX/+QBMM0QTtQGKE1nQMaSWKpVN+dLmsCNt3KrJQ5meMbeRxeczmvBVvuvy0VwqcuAJ8+5rrWrvMeEtuEip18CjUswcRtJnhs5mey3JZablev5XH5clawciKMbLiIh3upVLT1PWD3PjazrXueXCGjElTnaQ7YaIRPGYljJEVHrLi9G5k3tHhRTcw76Qug8D7V8DcxLIdPyt5og7KYccX0nFRNuqreVRhmGQcn7nthKqFP6bgtm1A3SbgLV3oCO+W1/2IE9qdUPC4LBpbecSfcTnk+OT1GqiXGeDbDHipUjTBS9rxaIisqF22goLX/hFptx30mqJ3Y+T4nV10qVVI0wRLT33obLLiiL1idSv+Fu1wbY0c19aVToUQcJvhcbGsFfpCVvwrmv9Mlla3okiRXykp+QE8HqM/vT34MQAAAABJRU5ErkJggg==", egg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAB5QTFRF////MDAwPJxXbcSHhqtvt+O4v6pv38qP9+qw+Pj4/Tk5SAAAAAF0Uk5TAEDm2GYAAABuSURBVBjTY2AgFwgKItiMHTMF4BzhiJmNcAlji45gmBRjcESHMYwj0dHRYWyIxGmFchgzOjqCO9oh6hiTTTuM4Rwn44iOjjIoR0nJoqMsDWqckJJ6WYoiA4yTluYE4zCmATlwxwmmpQli9wKJAABtjxkjucRGhQAAAABJRU5ErkJggg==", bright: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAB5QTFRF/vPSMDAw4tGw+rV8+ZZE/dzA8uLB+q1t+/v7////3kFdYAAAAAp0Uk5T////////////ALLMLM8AAABySURBVBjTldChDYAwFATQQ4CmAVNHPgIL+WEAkgbdpBt0hg5RW9ltEfALAtNzT13ukD9BDZR6kRB1wWBjI0i8wmmBs2BBD4C3D9oHaQIcOn1j3sEFgS3gHxizwtONPJrTL0cWEAVBIqIgpVkRqf8JlR9c2wmHPYayHNYAAAAASUVORK5CYII=", pack: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAA3NCSVQICAjb4U/gAAAAJ1BMVEX////w8PDw6Ijo0FD4yDjgqDjAqFComEDYcDiIaDg4YABYUDgwMDDaxm2rAAAADXRSTlMA////////////////LQRBrQAAAAlwSFlzAAAK8AAACvABQqw0mAAAACB0RVh0U29mdHdhcmUATWFjcm9tZWRpYSBGaXJld29ya3MgTVi7kSokAAAAw0lEQVR4nH3PMQrCQBAFUA8wjeQGAQ+gA2vEY0jA3kJQGxsXJZ2FE0iXIg7JEey0SLpAICmcQ5nd1ZRO9R87w/JHo38jUg8ZAirkh4YoZvmyKYk5vzu1y6oHxfYQcM9cElkILpkr9wJJqvIncmYgt1QhKh2ZrbpZnPqsC7MFM3+utY7sCfjhCs/8SgzaSbhGxNIh8OmKeHibvXZKQY+dtkcOF4ejpzaoLhGYj7rxGHGrHxbQeX0+C7iqkpn86y4y9B7mAwpHT1Z1wCFpAAAAAElFTkSuQmCC", fragment: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAJFBMVEUAAAAxMTGcakFKSmq9i1rerHu0rM32xZzu7v+Ui6zVze7///+kAd76AAAAAXRSTlMAQObYZgAAAAFiS0dECx/XxMAAAAAJb0ZGcwAAAAMAAAADAHeTl6MAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAHdElNRQfeCxISIDTCst7zAAAACXZwQWcAAAAeAAAAHgD4T+E9AAAAoUlEQVQY02NgwAcYBQURHCElJQRPWEnFBc5jNHJxCREUgHKSjUNDXAOhUmJpIi6hhVCOxCwTl3Aoh7FrhqGzMVSP2MrmiWaGMInM5pkzLCEyEm0rm6dlNkM5q8CciWBVWatmWKalzQBrYkzLAnPAmqAciCbGaSubZ07LnAnlZCI4DJIzm6elZUItYpScYTlzJsRokLcbBQUbBWDeEwAjVAAAUGwxQVWp15AAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDMtMDJUMTc6MTk6MzYtMDY6MDDWjJb5AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE0LTExLTE4VDE4OjMyOjUyLTA2OjAwZ+O8ygAAAABJRU5ErkJggg==", mono: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTE5NkM1NDcwMDM5MTFFNjhENTlFMkEzNzYxNTAxMUMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTE5NkM1NDYwMDM5MTFFNjhENTlFMkEzNzYxNTAxMUMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmRpZDpEQjE4NzA3RDM5MDBFNjExOTBGRUJBQkUxRDhCNTE0RiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEQjE4NzA3RDM5MDBFNjExOTBGRUJBQkUxRDhCNTE0RiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Puecb4wAAAAqUExURfj4+Hp6es3NzZqamlZWVqamptra2oKCgrq6uk1NTTAwMHt7e2ZmZgAAAAV/T5sAAAAOdFJOU/////////////////8ARcDcyAAAAJdJREFUeNqskkkSwyAMBAW20AL5/3cjhALYKZ+SPlHThcBj4PUA/CDU+RKq1ZkqhFINSHehlI8QHMaF1gzghpmp6SYOgMyDtkQ/N3/y2poPc8ExxWLL7yKwfAniS35OIWZEZMyhU5YwEFB63Nc6r5tEAEAGafuOAqnv8LigbpWYSak4qJcSsUSMeq8dnXvtzz/qD4/hLcAAPMwYmRr6Be0AAAAASUVORK5CYII=", coupon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAMFBMVEUAAAAwMDDo6PDI4PiYyPhIgMhokNiQkNhwsOi4uMBwcIigkHjQ0NhAiChQsCj////Sw72SAAAAAXRSTlMAQObYZgAAAAFiS0dEDxi6ANkAAAAHdElNRQflBQ0LHzvA039nAAAAf0lEQVQY02NgIA0IIrOVBJDYCI6giZKSIowdYhwE4wi6pQc7QdUJGoeVlrpCOILGJqVhoR1gTYzGxsbhqZGrF0E4JiatUWtWTlIEKRN2dnbSWjNrIli/iInLDZ2TEDYDo8vbnYsOQu1n9L07cyLcLXK3EWwG2YsINgMjEpt0AAA7jR3+N85b6QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wNS0xM1QxMTozMTo1NiswMDowMJbZ/qsAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDUtMTNUMTE6MzE6NTYrMDA6MDDnhEYXAAAAAElFTkSuQmCC", //level: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsAAAA7AAWrWiQkAAAMfSURBVDhPHVNLb1tFFP7mzn3Y9zqOnSh+NNdOGlMnNU0TaEGBigKqigSIih1Cyjp/ggX8gGy6YMGiO4QUdVN1gRJVQgpFIEUgQQhBVGmpUzskjt/3+r7vDOOe0RzNnNE3c+Y73yEAyJ07b6eyUqrKg7AaMWwEhLzDCCjlgMRZwBl/wKT4PiXYH/DT+vb2kS9wL4ETZmrm00vFzMaMi6tJn+uHPJbPohAlN8CsF6CpIPSz8tD2g58afecbydd+/PbRoxFdu3x5uTaf+2JtZfHNazlTr2qT0mkQgFoW1iwHi1GE6XyKlmsX9KyhVIaOU2zY1pPPnzUbEkdYzWWNq6ZZUNXlZSiVBawmDdwUeRcyCuTrJZjXK5gr57E0m1WmkupbEuMr90wzIyVIvDGX1XSZ+YgkB6HdQaHVRk6LQVZnoa5WoJZyYFEAHnmoFVK6mUmsX8xP3qXvvb5470bNlJPJJBC5YKEDNiGBzE9BGYPEcHpteNYAcRjA0Ch50feKLZe/Ko1CTls9G3bnHE+fvcDeeQe/ihocyzIC34U76MIbdhEHHsA5zhyOQUCoRIhCFyoXv4oho9e3cBIp8NIl9GIFZ50uwl4LKvcRM6BpxTgechx2Yjxvj+D5AaSEYQQjLYP9noTJuRre/fATvP/Rx0iXl/B7K8LBeYyDdow/2hy/tRh64gGJ0pCDuFJ6wnhQuJALywvzMFIpCEGIKcQh0u5EKv7sUxx5BibLC7h0pYqELpgEdgj4pjy0hve9wL91ZfXatGc72Hv8A0aCHFUluHnrBpi4KY4j6HpSrEXq9cZg5LkPkWBbdKlUjoQQFyfSRqVYnKHJBEUqrcMsFTCTn4asSFA1BYEQzt9/PUHz+GTLspzvvt/5+YSWXqF27OvdQa9b7HTbeQGUs1NpAsG4PbJhDS38+7QeH+z/0288b251z9tf9z0c1ut1ISNh67dvG33qvKbJ2kqxVFhPaok3iGgMLkrDGAvtkbvz3+n5Qxayx5aPo93d3ehlY4zd2L4UHP3ywVqGhuSu2H4mTpRxXOBd4TbFHze3t/escWgcB4D/Aa3LeHVESOsoAAAAAElFTkSuQmCC", blkapricorn: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kKBwQSCrGq9k4AAAF6SURBVEjH7ZUhc4MwFMd/9CaYAwcSOZm6dg5Zu49RyzfY1WH7MbBI3A5H5GRl4qjEMdELayGBdpvcu+MueSH/f97/vbzAvy2YZ3MKIXoAKaV3PR+bWZ+zJxv4R74D4DWjT5OA9/0WgLIokeHOgAP0S0SeC1yfFABvR0mxFwBESXxDkqYpAFmWOUlWY0dZlJRFOczTJBjGhtSAmy/Pc6eMq7Gmh/oybhoJwH6X3JXMOI6tJJMcpEnAoT6TJgHr0VrTyEGeqqoGf1VVCCFQSs0nWQjRb6IzmwjW6+nJax3gh9wAj6MA+ut8rFwhG4mMHcsT/st2ViYhxHKZ1jpgE50HklpfkrwEfvc9+OyeQX/P2yAxof8NwUAy1Kb+FcEkB23bTn6yVYejdSz3IiFE7zpxFEXWaJRSaK1RSk1utFWitm0Jw3Di11qjtca15y6JpJRe13UPa911nbUfeTM13fu+b41kfHIX+CzB9TtgIzLAD7XrJaKfPDj/tmhf5K2wqRPvPAwAAAAASUVORK5CYII=", whtapricorn: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kKBwQSFEulyy0AAAGDSURBVEjH7VUteMIwEH3lQyA7N9wqwR1yrpXMIdkUm8RVTyEBVzlQbG5VgCxqwzVqRXaK4jo5l6n0Y03Sdj+Sp3KX9L171y854IQSGKokEXEAYIwZx3EeYv9HICK+X475fjnmRMTdnp3Fi0GLCyxf3jgRcZ240gER8dWoDwBI3ncAgLsZw8MtAQCaFy0E/hzXs+gbSafT0bqp5ROBP0fgz7PYscxsLUTzCMNQ28ZavqdT1gAA7MItAKDvWJVaO/IWShHJgWOZmLIG2Me5RLILt1J7BK4u2yh1QESczANc+lRWrhItc1HTHRYtEngKYkyeg0IBlYu6qkoyD5mIqLqMXAdJwHY9bKbDLB5MHtG2mr++yXVV0na9bB0n6Z8EpH+wWfvSoShOKpGtXqPyt4iI+MhbKAms5pnSTRQniJMU98Mb6UYrW7RZ+7C7PSkfJyniJIXum0otYowZgeZwEYK1r3yPjKJX1en2lE7ylevICwWO54BKSBCXzYVKA+NfB84JeXwBn+m8UXJeocMAAAAASUVORK5CYII=", ylwapricorn: "data:image/png;base64,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", redapricorn: "data:image/png;base64,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", bluapricorn: "data:image/png;base64,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", grnapricorn: "data:image/png;base64,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", pnkapricorn: "data:image/png;base64,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", brush:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAASBQTFRFkGgY////BQUFEQsPCwsLEAsPERQUExMTAxAGExYULCMWEhcTExkUEhASEhgTExgTEhkTExoUFB8VMi0kPS4RIyQmLiUMFSYZFxURHGQqIGEvIVctIVcuIVkvIVovIl0wIl4wImAxJGcxJGkyJGsyJW80JjEnJjEoJnA1J3M2KXA8LigTMJtHMZ9JMaFKMaJKMnVNMqVMM6RMM6dMM6hNPS4RQDYaQkJCSXBSTDkPUnFZXEocXEojZk4bgXyAhIaEin2HiriWj2kTkGgYlGsNlo2Vl7SHo3sjpHYHrHwGrX0IsX8GsYACspwutYMGt4YMuYUGuYkNwp0tyZtdypYRy68vy9XN29jb46sV8L8x8b4z9M06/882/9k+/+c///JCeaP/DAAAABd0Uk5TAADBwcbQ0trc3+Dh4+Tl5efq6/L5/PxuAN84AAAAtklEQVQoz3WRRQLCQBAEF3d3d3ddIEGDu7v8/xdwTpo5Th16uoaI/gxBS3O8B4EplXUgYExWPBQAfazqpSDDEC35KAjXRgp+Cq7ShHMBCs5VhfNBCnooXeUQBQUTrYqbguaKzrkhBkrkvUtXwnfF1dSWYn8k5UvkrO3BZJyW8e1y9vlivTnYBNqbzetuthzqhOB0v+22LCsAmc/reZyywg863499nQWvZVYMwxEEfmsCgYhgAOcLypgZUUeZxsMAAAAASUVORK5CYII=", dew:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHZSURBVEhLY/z//z8DLQETlKYZGPoWEBUHlo4TwYp+fFgA5nMIJIDp4/vzGcEMPACvBYaGhv9BhhkYBDDoa3yHiiLAzv2nGR7c7GM4f/48TotwWgAy3MJhA1aD0cHMGZE4LcFqAbLhhgaKUFEGhvMX7oPpizc4Ge4/eMAgJS7BYGb4H8y/dqGG4eD+xRiWYI1kULDADDcwYIBjEJ+HhxMsp6igwPDu43u4Zf9Z7KG6UQGGBaAIBYU5IQAyVIhfEEyDLNPWcYUnBmSA1QeEwh3kapgPQDTIkp/ff0BlUQGGBbCkCAKgML9wgQGOQfyL1/+C5ZB9AAPIemEAZ0YDuRIEFqx4ATYYbDhQ7MtXZrA4sg8+frjA8OffF7A4OsCwAJaJQMEEMhBGwywEgYQICSgL4hMQUJL9BdeLDPD6AGY4OgD5CmTwjx8/oSIMDKoqCEuRAdZ84BF48H+AhzhWw5+/eI5iMCh44iPcGY6ces+wZK4lcfng5YMCsOEgV6JjkOEgQ2EYBED54/eXOWA2OsBbVIgrTIDyIC5FB+5OZgyebkYMGRkWpBUVMAAr7PgFgNkYCEApRkX2JwM39z+wqyfP3sRw9WwbeYUdMqBZcU0NgDOZUgsMdQsYGAAGYQVLYeEoTgAAAABJRU5ErkJggg==", easteregg:"data:image/png;base64,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", fragment: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAA9xJREFUSEvVlVtoHFUYx//nzMzOzuzs7uxldrdJt4nkKkGa4CU22pKSiKWt9qEF8cGCIMWCKIh4e/VBUeuLQn0QxYqgBBR60dYqpYFCFZU0bu8xJrHJZuJms9nN7ro7c+bIrhpzWaotzYPf4zmH73e+//ed/yFY4yBrnB//K4BoGMa9lmUJmUzm9N/K3KoK5Egk8v66sPFoZmHhzfGJ8edvCcDr9W4OhUJPed3uPlWWQ7qxHpPJXz9KnB/ee9MAwzA6/V7/6xTOXR5F0RXJVVXBIQRtHffg+MnP+03T/OZmAbGtPfdfCCiewPTszLIB9Olh/JKcci5fPq8BKC4DcM4JIYRfZ2Tduq7v33L3pqedcqkxPT+36mhzRxc/8fWxL03T3LF0c7HJTRsa9/n8/giltGjZjJWZnUkmJ4cNw2ju6el5aee27RtPHz9JfkoMQxZdsB2GMitDlRQwcOzd9ySee/HZd1Op1P6agJ39D57t7ujqHr82DkIp2jvvgBbQealQJJcSCQwN/YimYAMsx0a6kIFIBeiKrwrLUxtdm3v5G2+98nY6nX6mJuCRh3Zfmpoca2OOU92v6MU5R0DxQaISREGASEUIlMIlSKCELuahkTDCdfX44MN3Ds/MzOyqCdj1wPYxc2aqgZJ/nkZQ1WF4gsv0rlyAOQyWY8FiNnKlPKJN7Qgbdfh04L2R6enplpqA9uaWs0FN617ZPYEI1SUHTrUi5gAiJZAEEW5RhktwwWUEEW9o44c+Pnhqdna2ryYgXh8/UB8wnti08U7f0JVhlKxy9VylikoiDg7H4WCcgXOnKtvvdgklVsZ9/VsBUcfRI59kr/58tSmXy6Vqjmlbc9sLnc0tr83NZaCIMgQqoGSXkS0tVGVZGYQQSFTE7sce50lzjmQz8/jixMDLo6Ojr9Z8aLFYbM/6sDHgEsVVySq3DgTD2NLXxxljuDD4PXFRqToOgdtb4fZ4oXk1DA99h8NHP3u4UCgcqSRZaXZiNBrtDfv9h/yqZ11FGp/bC7/PDyMWhVfxIJfNIW3+Vu1HybZgqy6oSgDgBJIiQ5JEPjJxZf7U4Fe9lmWdq+mmsiy3NNbHD7RGGnb4VY1WGro0ysxGgXL4QgZi4SgvForIL+SJZdmYy6X5hDl25lxiaBuA/HXtWpbl1g3xDWcMPRJ2STIoswEKKG4/Ar5wlalpKgIhnY+bU/j2h8FSMpncUygUjv1ns1NVtU7TtEYAt9VF4wclQVCY4+Qdxq85nI1wQi7mi/OJbDZ7MZVKJQD8OX5/xY1+OC4AlYex6JZLk9UyzRsFrJquf1tYc8Af2HZ+KMPEwe8AAAAASUVORK5CYII=", celebrityTicket: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAA5VJREFUSEvt1EtsG1UYBeAzHo/rjB1nmji2G1smrilNGoqd1I5xGlG3QrSRWKSqqLrpAsSCRTdFICEEywJSJbLpEiEBG6iEmg3QRVBC26gksY2dpo1Tk4dLQmI7fsQeP2fmDrKlAAXyKNAFEnc9Ot/ov+f+FB7zoR5zPv4H4Hb1DMkycfoDoeN/Ne6/PSKPy/WKRKQPzlkK3El7A/PqDUQmAuHOPyKPBPT29rawtNSqYulZHyfhWDOPaFqEXqtAewuLi7epcWGjemrs3j1+E9oV4PE4D0gSzE6H9horMtw7r+mx9lUSgZkY2nutaDJoEBuJQKvT4JMf6eH5lHzhdji8UkO2Bbxeh1kQ6MFj/dqzTVTDc+dOs2CSLDKZDHKpIhZCa9DpWbDaPViajsOso0DULL5epq/OJOTXa8iWgMfjuvKUXWV+oa9l0MbRKG3wmJ5LwKRj0PGEARRF1ZH54CqK+QqUShr7DxvQKFQQT1bxZUw5vLxWPf8nwON2DREiO4fet/oUP+8BpynhfnQd391ZRiJXQaOaRt9BI7psJlAUUNgo4+7NB/WRuwcOoLSaRWkhAVmtweVpavxXYLMVl05YuMMvs8x6kAcRKMRW0xirheerIATo7nQjk47gTJ8dCoWiHixWJQSv34d1rxJEIhiJqzCdU4GIpJPyeY90KGV5tt8o4WRbFdGUiFxBhOvUkygIIkbDMSTyAtotHZhbmoMgVtGsVWCgx4h9+n11hGJktPRoEPm0LLw98iBLiPyW/4fwx/VLft7bfevNp6WjFb4Ala0VrbZm+L+J1v+kq98KLddQr8LsUhyB+Tj4sgwTp4TPYYHJrEeGb4BsruCNd5fGJIEKTQWDF3//FijfoUPaNoPqszM2adCkVyLPqLE4k4AkEqi1KtidJjS1aupIMpNEOi9jv42DoGrEYlrEtxOp4buR8sqUP3hhy5fsdTjMXQZ8OGAhZ5lyET/lCNqfMaJSFJCNF2BzGOsIx+0FMZbw+bUCMlLpxq1x/qpCkIY3O7/tqqghdj115bydDJZzPMzHD4JPl7D4fQzdXVa0vWjEex+tg5cr2Zk7hdMyjZWJiVB0p3X/UE1r42KamOuXn6WOrmV4EEmGTs3gZobF6LoC1YrcWZTo5OTkZGqn4G1XhafHMfvFS/aO0MKicCkgZ2X5t1bsNnjHXeRxHxkloEJTU/6HWvGvAY8atNX3u9qm/wT77wO/AD8uiACRHoDsAAAAAElFTkSuQmCC", battlepoint: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAIVBMVEUAAAAwMDD4+PjwcHD4sLDwoKC4qKhwUFCYcHCwYGD///8C+1qZAAAAAXRSTlMAQObYZgAAAAFiS0dECmjQ9FYAAAAHdElNRQflCxYGCShoBXbCAAAAcElEQVQY02NgIBcwCiBxhBQRPEYlJUM4T1hJyRgmx+jspGKiBOUJqRgbOympBEJ1KBsBFYI5wk5KymZlKqYgZYxANcrmHcUQCWMgqGiHSRgbwyQYQxESDAyCoXAJCG+6Kdw1QF4gwtGCocheECTJ9wCggxVEFBenTwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0xMS0yMlQwNjowOTozNSswMDowMHrq0NsAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMTEtMjJUMDY6MDk6MzUrMDA6MDALt2hnAAAAAElFTkSuQmCC", teraorb: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAABGxJREFUSEvNlVtoHGUUx//fzGxmL9lr2ma3be5ubJNS7SWRlKLWSwPRBxHUl4KUIoogoqB9KD5YrfqgCMYHQdQHKRQvD9WKtVRQaulDxSbUhIRcmzSbnZ1kMtmd++X7ZDca2m16eSl4nuZyzv/H+f5zzhDc5SB3WR//C4AYj8c7o9HoVsEXugAaBI9h3TYHZFn+G4Byq1O4XQfNTY1NZ7fXd7SuD9YRgMHmXIi0BrprsBFlTB+dGnvZcq2vbwa5KaAhs+ndbZmON+vD6wMc4WDyFtwgg0OBoAvUeiEwMJQcDQNzl8+MX508AECuBq0JaExv/qC7ddfhx3t7kWnK4JfTpwEfWNaWYTACynyEHYKgF6jo5a7OYcy+cn4iN7kPgHstZC1A5xO7ewd7dj7AH3j9YCX3/Lk/8Ov3Z1DDAliABo8AlmNhoxurvJfm8iiaRVwuDR9XNa3cyWpUA6LtrdmxPRu76puyzTh4+AV41EP/R/2wr2qVIo/5WNA8WJyFOp6vQOenc3BcBzK3yGb1/A5VVQf/I1wHSCaTfQ9t2XNKCAWI71O0NLdiaWEJqqEghRgopfBAUSgFgKCBWvgwXRMFucBELwDdNogM5cicPP/eDQBRFPu6ex7+IU3CvKVaECyukuMyF8VaHQkxgQQJg4JBoaxsCUKKidnxGVCeMi9BCUc5pgra0vxi/kHHcYbK9asdvPL2h1OWojYPfvEj0hszsAUH2/beD7kgQ9Yk8CIYVEIII3CYBzACc7IIXVk5Oh8UOm8ivDnCsl3Nhe9O/HQvgOVVwDuffL6YbtudOvVpP6SpERx69SDykoQLP59DnI9CRQlIAVpOQ8DkoC6p8HUPgrnSqQcPTshnO5/aTrr338feOHTsLcdxjq0CMpnGz5576eiLm9q24NLvJxEo5eDPWmxBXSBMBAsLIUiaTILJMJYu5hlHuUot4yhjG3jS8Wg7enp3MIHnMTQwTvvf/zLLGJu+1uSappbs4a337DrKajiQaQmpUAJGyUDJ1lAielkUXU8+QlrbOxGL1aJufQJiWISiSpAWhrBUkPHnxeHl0eGJ1ybHpr+6zoPyTSwWyzbHGkaDAZHEw3EQtWwlgcO5sDibla/3Pt2HzS1txHVcGIYOo1iEKi/iyvgQctNzKPjKs/l8/lvGGFlxrCqSyeQ3HZn2Z4p2CWlrHUgVIJiKgJN9wjMOlDEU7eWKAiEEi+GiNzEzta5s7ppz8O/DcGdrx6UUF2sv+iXUBqNMqOGhuyZgUBJxghCoAIvZ4EUe4VikspMUS9WHciP7DMO4eLtVgWg02pNtaDsRMcRGT6SgrocN8XoQnlSGzbUdMAb4HoVjWdA408yZ0hFJkj6uPpGbbtNIJJJOJBJ/NXD1GeIB8boUAmIA4FaGxzFdFNVlSJziS4r0mKZpv1WL32DyGgmBdDq9PxaKPS+A28+Di4KVv3nfZ5ReMHz7+MzczMnyvltL/E4A1XUN5Q0BYLIyW3cQt/uj3YHErVPuOuAfnyAONX8MrRYAAAAASUVORK5CYII=", terashard: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAA6ZJREFUSEvNlHtMm2UUxp+X3ug6BCuFuamELCFBtsFwZCadOoXUsFkv7azWIWMRJRmCA7Rb7SQZ09WZRQloZIqTId06ulmcmzRlYC0ukdV2glMyP7uvmwMyoVCLXNZufQ2JJl64tE2W+P598vzOec95HoKb/MhN1kfUAJVKpeXxeLcYDIa9AMYB3AmAC4D9e9NRARISRJknPq0/k53z6KLWI0cnWTfjWL4yJ8vQfMjRYbXKogZQSgkhRFxXV8eqs0VxAkka5XKHELpBCWexmNbUd/r1+jeTAVz7CxLxBFu2PLPr7ZrK3XHxSwloAEGfnQRuANzrIoSEIpRqm/a0HDZWRwWIj49f3tC4v6+fPSOMFSyCKk+O20Ve4gsGIIrhgD+eQa9wJqCreXePyWTaDSAU0QQ6ne7Yxk2rFLqaYiQnpuN52TKyTvY6nfI6SAgUgpH11Ce8RAztZ7/RaDT3AwiGDRCLxZs6raaWfRY9/0rvz8jKuofce8diuvnZRlxke3BtfIDcJspBW8fJH16q1Gycnp6+NPNNYQEyMzPTtmsrzg2KuoVu3iTZsDoXlk4LCvOUSOfeh48PFCJt9YNIWSFDRcGOfJvNZglrB39eDae8vNzVLfSsuDw2TDpLCjDC68H3yQ9hdJDB59sbUPzcVnipC4m/P0FLS8sSAPjDAswUVb22yyQtyFW+Z/6IZElewbn+FCirnRji2BBrvI6pX0fBDDiwMjXXbzZYi1yuXnMkPpDstB4fWJe6hGf/ag1Ou/h4JGeMviA9iLG4u4iz6yzsF1uRGvuUr/mDtm0Mwxz5d/TMu4PEpKR9j9uMmp8OSTHu52OHYgT5S+spPykPgcut5OpIPzzcMpQUVzzMsqx1tlybD5B4sPHwhQ8FqbfGuNaSqnyAAyf8cb8hOTiJCz/6KDsU8lqP1xadP//dqblCc06ASqWSa59sMjeY+Zw16wP45TMBJmVMMGH6lP2dt/Y2e73eLgBjACbmS+Q5Adu2lp14dVWt3J8UhLibgecxCTa/KFW43e5/LHGhuJ8VoFAotLXF+jfEMctIrHsQoz4fLfyyer/ltGXnjP0XEl3oipZ8fayjby0/W0LbryImhaCFZ/+2qKpECiAQifhsTuaq1erelrK6u0NNE/ANDcPxgJtuePnpbAB9kXb/H4BSqVQfqHy/mTCUc7L/i2Em5P6kvavd5HQ6eyLtfFYny+VyfUZGRrrRaGzzeDxHAUxFKxx2VPzvAX8AWGNsKO5B/fwAAAAASUVORK5CYII=", terajewel: "data:image/png;base64,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", //candybag:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAMUSURBVEhLtVRfSFNRGP/uvLkJ1YwMJvOlcLVrmyQ0yUjYS/VmPSib4Hv02mNlb6unqBB8qMfaU4q2qF6DBgY9KCUuUCIhcf0BkxXMbXo6v2/33N0771KhfnA45/tzvv/naEII+p/wmPuuMDw8LHp7e0Uqldp1VDtmYBiG0DSi3EjE5FTRurRGgZkV8vl8NDs7KzXc0dABDH+6fJxKB5op/Hje5DqxZhymr6fbWZ7L5dydwEH9CofDglKDwjDCAud6uX0p3WOxbpFMJiXLKXcQWHbj9bJGKxKJ8J3QqYgknTIHEYvF9mxcrUaBOaaoUCjwHgx28L4XoAfGE9kLOQzj4+NWYx1N1m4PCbNhNDk5SaOjN2UUkm+2D2dd16lSqTCvszNEi4uL1iSl02kxsjztaLrloKenR8wNdVoTszBUJDUWFaGRrtUCaUQbT30mp5oRdqtExWKRjc8PbvCC9E3eQ5s7GH+9qls0ByUv5vuCTAOOHpwPbvLeZF64km2myISXz4Bb5FezOkcO7pakFwaL/AAVHA4enC1bxrsmfBQIBqi9I0DX3+1zNR6VziHHuiF11N2H/SXeAcsBUlM1h3G0ZvVLntf0cpOrcQDy/Eqepj43MY2S9ge2+CcAXZuiW0f5AAWPNKac1Ud+5pmX3l7a4DO4KIuK3H4XZUOjOYNoNMoaMAblLrOmbsbXS1XXimsv6a9yrQpqtNlBPB7fNi3dU/u5DIoDZ4g8JycFPEQOG3ff62zc3+onf3NVG7qqMOxgbGxMuzOnMwM4J2t4JNDGzUM2uBCb8nLaWCelQRX5i98dPAw/19aZrs/aanJ6qdokKDySU4DmAXi5yKQoJ1hNDKKFI0Ruh914IpHg3Woynnni46ilcPGVl75VWuhQWyvTfwOC+SAfp7qrGoyz4y/CaKHGAKJ5vuzhN4CoAZUVGoiyfM//4H/Jbhz3Xsbu08DAwHYHAJxMXyjTCX/1VStpVn4boYNb1Nai0bUZne71VbgP9rIg8lAoRJlMxpwhFwcK1kORqkrFfgbqaVUWOxo6+Dcg+gOPizMCRY/02QAAAABJRU5ErkJggg==", // Not necessary since they're already in PO /*oran:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAHlBMVEUwMDBQUIDg6PiYoLi4wPhQoPBY0PhoaLhQeMj////bPOk9AAAACnRSTlP///////////8AsswszwAAAAFiS0dECfHZpewAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAC1SURBVHjaY5zJgABMDAQ439MRnO+PlAqAFAuInc4syCcOlUkPLfyrJADhfA8VfG9w1qUAzClVZGC+ZHy2HKLsAwOD3dnrNwqABnwv+PifkcWF4S9Y5sFHxv8MfxgU3EEchX/vPwBVPIDIALUpALEBWIaB4QBQgPkDkMM5mYHBAciWByv7r3yAgcFInmEmyG2dz5PAdoFlOCcA6QUMMyeAXdDZuYAhYeZPBgZGcBikQ+QZiQsQAE9FMeRhR/RDAAAAAElFTkSuQmCC", pecha:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAHlBMVEUwMDD4wJj44JjokHD4sHBgyFCoWEAwcEBYuED////NKy3MAAAACnRSTlP///////////8AsswszwAAAAFiS0dECfHZpewAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAC8SURBVHjaY5zJgABMSGwGFgiVzsA5Acr5XqD0/z9U2fciQQbGjwUQThE/w4f/UAO+Kwgw/P/AIA7mFDIwMggyMHxAMlqQuQDI+W4A1PAebilQAwOQqw7iMINFmRXAMmBTGfTBykoNlICCRh8gehYwMykpfYAY0O17AWL4TZCMqheYxwxyNWc3A8MFA4a/Z2eCvDDz+wH1TgbWAoh/ODewV/Ayn9CEuG1mZz/zj50MDIyQAPleAPI2I87QAQBTqTAHPO/jbwAAAABJRU5ErkJggg==", razz:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAHlBMVEUgICAgICApWjlBrEGDQWKD7kHNOUH/SmL/e4P/1dVWLmjkAAAAAXRSTlMAQObYZgAAAKlJREFUeF6Fz9EJwjAQBuBkgwRUfa2lA7ROIB73bksHKB6XDCCJLmDqBu221iSggODd08fP/XDi96gPtD4nSK1k32bswrDuyjoFz+l6aspCRdwCHaq2qiOMo6avupQ49pf2UKT7yc6Pcq8itsHOAeAYsXFmdEykYsLoyTh8Q95dIPQZo50ceoYIs5wAUyyQiJ4RIFWviJfVIlcD8iAymIBVhmTQ+utl8W9emSkrCLdOHMIAAAAASUVORK5CYII=", bluk:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAIVBMVEUwMDB4yEA4QHiwcOiAWMhAmEDgkPhwULhAcEA4QGD///+wgHtsAAAAC3RSTlP/////////////AEpPAfIAAAABYktHRApo0PRWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAA9ElEQVR42mNcxYAATAzYOPeQOP87H0A59+79e87QwMDAAhKdLfhBxsUULPPvuQJD3tHfDWDOuyuBPPsbNtiBOP8W/P938AAzy58PQD0/bI/sZ2A5y8DwQYCJgYPFgUHegZmVtQGo7IfC71ClzRrWN+2Ayp6s+2r/VWqJgMIPoEyToQ/DnwAGAQYOIAdoPlD3P4bwQ0BltSYfeIDuCL0HMmCZw28Xhw2q/w68B3K+b2LY8Jv1A4PDfwbGVQx3P6xmYA34zSopBXRbDUNAqDfDuk1cIIfWM2xgYFj94LoAyD9yCQwbPJ+DAoYRHDrXuhYwwDlQAAD05FG2LWOr4QAAAABJRU5ErkJggg==", leppa:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAJFBMVEUwMDBAmEBY0FAwcED4kDD42DDo4GDgcDDISDCwoGCYUDj///8b2L15AAAADHRSTlP//////////////wAS387OAAAAAWJLR0QLH9fEwAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAL5JREFUeNpj3M2AAEwMWDlfAxhYoExXBkYuGMdV6f+HX1DOV6H/H97vhunh/8i/G2ZA0Ef+10AKouy/wL3dCKPf7XZ1hXN+uyopQexx5Wb6KvheUBTEcXX58/kcowADYwATw1dXgUP/+N9/+PcAqCcEaNj93+vfvV6/Aajs/wf7A9zcYKO/JjAwWHTAvMDMwCzwH8IBKnNhYGCEyegz/GP4sQHC4S6494BBASazyoCBIxumhzuLgRuiioEROagApsI4ymcug3UAAAAASUVORK5CYII=", tamato:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAHlBMVEUwMDBw2FAwoFD4kDCAODAwYFDISDD40DDgcDD////8XnhNAAAACnRSTlP///////////8AsswszwAAAAFiS0dECfHZpewAAAAJcEhZcwAADsQAAA7EAZUrDhsAAADsSURBVHjaY5zJgABMDBicPwzbQRSzLwPD96NdzBf+KkBkDj8P2ASWAnE2KBwIMGKFcoIvMDD8/w1ksDB8P/CYgZF5jyvIhJlhzOUGCoxCbKycM1kYNrAwmM1iemfyPwhotOMbBdZfas4vWR/pMoFsPMDwmeE/z0mgaawBe4BaLzAogYz+/ZTZGMhjZQgAyfzjvWDAwOB3bwNIZgHD3wesxvc+QWQ+MDPrMTz5+xzogt8xN3iALmYI/QuU4VxwDmgQg0xFC1CGYeb3gjQGhjkzQQ5lYOCcmc7iwMkA4YAkr4kiAoRzlgOQZEQOKgDt60WTFllkeQAAAABJRU5ErkJggg==", pinap:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAJFBMVEUgICAgICBKakFSvVpzajF73lqci0rNvUrmtDH21TH/5jn/7osLb5hNAAAAAXRSTlMAQObYZgAAAMNJREFUeF5tzz0KwkAQBeDsDbIIKnbZ4AEST5DOOuABYjHIpo8Qq4AscwOtLfIzOUDAuZy7q5rGV72PBwMT/IkI5YxFtJMz4jQN3xAyV+oDsXnk21glvq+oSg9KRQ6bkluVb3PphlHz82inxIFvFVexKtyBJfNUEkATvMEX6ga0i+jPJddEvQdxxS3R3aMepycBnIyDpo7scHUHBGiLAa+ZRbAG0ndEDD0MGgA0HsKgzb4IfNYni+z72hKw8d1Lyl+f8wLnIkNyZI3KywAAAABJRU5ErkJggg==", nanab:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAG1BMVEUgICAgICCsnDnFWoPezUr2c5z/nM3/xeb/7ntkXFPfAAAAAXRSTlMAQObYZgAAALBJREFUeF5NjzEOgzAMRckNcGm7FzF0bSxgBjFkraLQA1RObhDPlRg4dpMQBJ7+U9634mIfKI4RqjzBdIbmVx6N67oT1FCr9ZGdZRovYwKoZLMOAspCACCiXJfYqIzRaKRSUbqFPBPK5xDL2FqrDcoId7bWdDR/k2bJtI4c5QXovOEEAtEx9X6Djtlr9u/0FRdyz7RBa7HjZMUNocNWbyCqD3vzCimLlB6yiHDcDzn/AZrYK5a3h8QkAAAAAElFTkSuQmCC", watmel:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAIVBMVEUwMDCY0FhwqDD4wKiw6ICIwEjwqJD42MhYkDjYkHj///+Khd/PAAAAC3RSTlP/////////////AEpPAfIAAAABYktHRApo0PRWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAA/klEQVR42mNcxYAALCDiWwID1wIgzQRiJwqVqyVAON8ShY1ufxJLACtLFOAV+PueUQ0k801RMOzvAQY34wSQTCALg+JFBgZVkEz6kV/3GAXF/p4tA3J+f2B5v4HhnjHInm/8DPf/MvxjuKfLkgDUo/f+gpCpKUgHkLP/n8BXJYZ7DLOAXMa/ggy2Z88qsCUAOaKCAmAHCoJckP92QzIDwynm/wsYmLgKHj+6d+/BBYZf/xYwMTAzBDK8NwD7h4lhOsOG3yBNbK9AXtD9z+HwimH+R5AMV1mBwgEGhl/TF4BkprcI/BRL+PcK7FOu6QXujHyvgIHAuAopQBiRgwoARb5Pn3gDHL4AAAAASUVORK5CYII=", petaya:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAJFBMVEUwMDCISDBgMBDQuJCgiGD40MDAeFDwoHjA6IiAuGhooFD///8kKe9AAAAADHRSTlP//////////////wAS387OAAAAAWJLR0QLH9fEwAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAO1JREFUeNpj3M2AAExIbAYWKB3wlXsDTOYruwkrTCbgh/1/BQjnK4Mqy3kmXYgBgQGMf5UEGV2BnK8BwgoCTxgZLhmAZEwVmBkEHf4pCIA4igxMTDLM7x+C9HAvlme8xDBLWeA0SOY/0++g80ofmEAy34oYDNY4MTK9ZgVy/gMtc3rE99pJAeIcpkdOR1yUb4Nc8IlJQeFnAsOrDUAO039ZBgb2hf9/RwOVce4SvcDAkKRsCnbbC8G9DP8/GrIEAJV9bWQTeM+wTuAfyHMRgkmffu82ejkb5B8mtp3f/Rjug+xg3O0K9DwUMCIHFQDqz0IsBPHRKAAAAABJRU5ErkJggg==",*/ miracle:"data:image/png;base64,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", platinum:"data:image/png;base64,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", bait: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAbVJREFUSEu1la1Ww0AQhaeuFgeusTj6CJHI4gqukrqWN8BxcNjaOiqRkcjwCBtXHLJxcO5ubs9kuvmhp13TzJ7mfjN3ZjcDOfManEp/NBr9FkVxoHc0AII6uVmeyGrsxEJaAVaEL2MfglzreSnTt+H/ABBx97Og8eUk/3Fyt3U+pHiWiXxvSrmcDP2+W/asYC8+nQTAeuMhz7sQZo8BBHEsAPC8+9z6WNsUtcgDrlORm8qGqoKPYVIThy2ePw8gxKhKV9IIeL9KZHwRALAHiwBkq8X5bMXxTmsFFMYfNWy5EEnTkDn9p0WdU1Tzf72RPMtkDDXVD+w9JEGcfYD/vc5Bzf/KewvQDddnYeWy7nMAAP2nRd4e03AtjN7cls73yEIOesAKdGPxsu4BBWN7OCuNY7q352Uh8vTqp8fbo8YUmWsgp4zQbkB1emvNrQ4agLHsOcL4bbXI+h/zHgBWwT5wr7PJ/hJL0n3DrPfMlA3tmqDoQYtBdKYQ1zGytvePBkenSI8py7e2ILYN1cJ8bgTYOWfMCvqIt95FqMJOh4bGroVeFfBPTV+zmEjb3tHf5L6gswP+APGgYSg2QW5BAAAAAElFTkSuQmCC", //bait:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKtSURBVEhL7ZQ/T5NRFMZ/tKVCkYpAJYIULKHyR4ImgJGJaEwYDNHFOBkHw+Bg4lfQz+BmGFxMdNDEyTjIICwOCBFNDCB/BKHQQimFIm/B57aQlNJWwuTAL3lyz9ue95x7zzn35Zh/kbe7HoZmKV/6InVKTmmPAWkraR6dV9LHpElA2kmRRzoSZqcmWIBnxHjJn4T9hDj9CvxBuicVsKTfe6QD2HfXTDz2wsObKs21GoqWm3AEHfKPUUSJSlsgjxnptbTKI6X8lLDSsO2u6XT6Cuipc9HVUQyXVfmSOShUQF8hNM1CwxDUf9bzhH7fJKh3NpOv7idbkwO9FXiunIJiJZgahhddEFXw3ndQ0woxhVvXfiNK/FQvROGOFnOefaQnMDV/29dMWXtrm628tJS89QUGhkeZmLaw4lBbDacdWhvbsDucjA0NcncUwlbmBOklMqPnKXZgi8z/ZHbsK7+mp2k8X82Ndh/dHT4u+X3k2+3kx9dxbkdxKFmuWc/WA6LhIKuhBSzLwlNRhbfJT03LBSoqvbjdbqzYmnxCxKzknGYjawKDy+XC7/djr/RrltSElutw1k9dvZ9IJML45AxzEdjOkSF9TGul+xc1E5vbaqK1xfpKgPIT2snCOMx+h5XfjI6MMLW4RmBZtddE9as3usam/t8SUVLImMCr4E4dPU/Xasuxjc3aIBxaYjkohYKJ4MHFHUJhmJSPhgy5HypBidRwEqpcFvZ87cwmj/hGjNXVGOFwUksqiwk+vwE6E9MwKFcNMJMmSCpZ78FVTVOjDFU/I7pf/JD6E0+ckRaT5n6yJShT2fv0Z485YreU+ul8L2nz+jowqArdkmlusgp7kFwjrENwzhhV8FyObmMbFuCBmmq+O2bXu4fITK4EqbyRTH/2uC2tJM1j/m/gL3sX4DsdYIE/AAAAAElFTkSuQmCC", golden: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAblJREFUSEu1VS1Xw0AQ3LpG4opLJDiKoo5aHLKVSJA4KkHiikQWyU+oDK51tYlrHTJx8ObSydtuLx/00TPJXXszOzN7uY4ceXT+Cz8Mw580TffwDiYAoC7ubhHJez8RS1JLYEG4GesA5Ph4yGU07f6NACCrOHAYQZJJFudy/tZ1c4LP5yKbz1x6t8V68thSAcGD0ye3MVu/OJL4NXXzyejaPQGOAQK8Z/HazbVNXotAkMxEsmhXwfJrswMOWzBgEQbmUKWVVBKs7nMJBgUA7MEgAarV4Hy34NhTr2AL7HJQZDfRmQyHReX0nxY1dpH2H97L7FtkfCI6D6xdLqPS+0Lleq9FvQq0/+weS6AD12dhvAibz4FTsPWf3sMeG7gGRjYXVz2XkSXZy6BUoILFZp0BAX1rOCuVbUpwGTyLxJOie+D/9qABGEMTsstI2kjA06vD5UEDoa96tjCetRZZ/33eaxXMgWuNIYNg1k/LwKz3rJSBNnVQZZtaEl0pwPUcVdvvjyb2dpFuU8pnuHqzDVT/xvdKAtvnnFNBG/DabxFU2O7QpL7rsZUC/qnqNvOB1K0dfCe3JTo6wS+CaG4oM8AU8wAAAABJRU5ErkJggg==", //golden:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL0SURBVEhL7ZTLaxNRFMa/mUyaNmkeNU1faqWthmotKkWwdSOCCAq1bgRxIYhuuhAUwZUbd/4BrhQ3LgRdKLrxgdiFqQsRfFAK9mEfSWxSU5OmaWaSzIzfTVoaY9KqKxf+4Ms9c+eeOfee+Sb4z0ZIK+Pv0EVZqfdUH1VFrRKgsoXw77lPvSqEiFJmkXxUWTY6gdjpIxHcHPS5Njll+fSNSPzoZZfXv6tBzqkmhp9PYOQpYrkMznHZY7G2GMvKWI5LO1qUwbOHa7r6e+oce1tlJWvLWEZdqsPiNqR2U4IlpGHo5TLUJVw0DbxmzmIhdQ15ZSylb2er0t+9zXro2D47jnQ7YOPKXM5A7+4atCkK6hc1eL5r8BkKbBJizNEKqT9TqUXRa2ccvuN8uN/pxHQcGJtLIWcY8Dc74fFwQTSFWCKDcBK4cjeOZNo8xbwHhfQ1Sgvke/7i+lbvnoEm2dPhgCJL0ANB6MEcoLOnLRIkZhmd9UjnZISfRXDw6gwWlvSyBUpbJKzna7HLsjOWgnUqDimsQmlrhW1/B2wHOqD422HxOGGtrUJ1rYI6NwtWajQpe8uq8MaiCnxLA6oBuL1Ac1NBdXSk2wFkDJhJFZrBk5nCqeUpX7uWYhHY+MOeQ6byVm+kGDdSbP7ypyg+c9SNPyzwZmQRo+NZRKa5O/GG9QRnw1SQ4vVsHPPTWYRCvOLlOs8vX+DDhInJGRXR2SWkxpZgRuaAxFe6nGK8PJHEPO9NB9P4OEH78uVXovRDowHR6apWNtfZYLHLJhzcgsx/IF3LIJvUoMY0xKeSmAqpGAtlERjTMT6XGdYNPGHuVP4pRVT8Dk50O3y926vQ42dVGr+W3hTHpe3Zljh3buLtZA733okZNFDzIiilUgGv3Srdof36rTzjhT4P7FWFpaLdtwIJJOku7no4nTUHOCW+ZNrtVyoVEPRSW0Tgb7DeliXJJWLBl4XMeS2b/98Rux7KT1ZgvQLFPKTE+1nlJEX//OffB/gBN6gIBPq9GlYAAAAASUVORK5CYII=", deluxe: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAbhJREFUSEu1VS1Xw0AQ3LrWgatsZXCtAxmJRFJHJMhK+Ak4kKkLrsjKk8W1rpGJow5c6+DN5U3f9nr5oI+uSe6Sm9md2U1acuJo/Rd+r9f7yfP8AO9oAgDq5KJFX+JhJi5JJYELwsPYByDj7WErty/tvxEAZGoyi/H9tZHFbCmv8ciuCW6MyPp9K92btt3Pxg0rIHi/W+SYrQuS5Om5WN8ZewU4AgS438w/7VrL5JUIBPF0JWfnnb0K0g+zBw5ZEJAIgTWq0pWUEtxHiQyvB/Yg5EGQANlqcN674DhTWQGB8aImmwWPEoZF5tSfEtV2kdYf2ptkLuHoSrQf2Juk4532SAD6N5oDrT+7xyXQhutZMKu4fg5AQP0pEeRxDdfA8Ca4DK1HLsmBB6xAG4vD2gMC+vYwK6VtSvBB0JFlWgwX5KFUAEZoQnYZSWsJOL3aXA4aCH3Zs4VxrZTI1d+nva6CPnCv1mQQhBfRzjBXe2ZKQ+s6yDtoPhKdKcD1Glm73x9N7O0i3aYsn+bqw66h+hnvSwncPueaFTQBr/wWoQq3OzSp77PQqAK+VPY384FU7R39T25KdHKCXwI4eyjpKIZJAAAAAElFTkSuQmCC", //deluxe:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAALnSURBVEhL7ZVLSFRhFMf/18YZcRgznUk0MjCawtdIQpBkiZS0kHYFEdQyqFWUuM11QbQNWkQLQTGjkh5E2kBJpOkI5jscyde8ncl5OXO//nfuDDTm+GjVwj/87j3fvfc7h+983zkXu9pKUvK+XeWTGvKJmImJpDRKnKr572oggiiOO5J2iotkZ5JnUSfPwGEsgINDB+rh5VXACBduIIxu2sq4lZixwm+6ExP/UFbyvqEkXYVW0leZrkkaU0sRTFfzmKJpvjiHQuihwzJtZfyBrOAxrw9JmjIGcH9BRTBaVIPcJjTWaHHKCByL8MUYcFgLVLqB6nGg4jNwlM+Kl+HnW19i8nbkGUCHf6JBKPIOm0TPWYjbpWo67udCPC+DeF0L0UEeaSBq1X3oVWdnUCrnsuu0Ix7sCcsxbyKAvOYSkflWEZiwCMcoxPt6iKHjEHNNhSLcdkFMntCK8/kbB0hPURRTkoSbV66P66xv7ukiDjWloflWrPV+hfRMg9yuMlSZK1GQvxcaIaCLxaDdQ0cZDnxagCwznFIZOtu7HJGZj30Ij74CYjZER98hPumDbDdCdltgOnAEhv1F0OTkILy8jIAsYy3pY70ybnLoJWmfAQJ3gK4i5GiqYairh6H5MqTScuwrt8BQXIyFwUFMcRW/kvPWK2OAMTtgm+Ch8FnhnxvGmn0EmGABW58CP77BOdCPaZsNNn7rYQ1HM5wfZm9DtZQI6LNXgfBIHO6fMrJCQcQ8TqwuzmN1aRF2rxcL/ig8c8AMvxvhFruAWc59orpQlSlAHZtOYTwEvTTFguM6pXAEEd8q/B6VpWAMLjp2s9iGOIFxvrOUlR71NuEhqc2aXUcJ+0szDQvRJB6lK0AmCZOGEHCJt06Sps0C5LFgG7nEbmWZZ8ihxGNVVqLkI65SHuXO0FRqPU1btWulayodFAeBW9nAScVWxHQ8YLfoTw5fkL+cK9rJ/+AuURaSUhvpU81d/b8CfgP1NCLta2jYiQAAAABJRU5ErkJggg==", pokeblock:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACw4AAAsOAUC+4UEAAAToSURBVEhLtVRLbBtVFD3z88zY43jsOHHTOG7SfKhoWtImhQBddNks2FQChESREIuKNUJICAmVDRIblqgS6g6x6AIJofKTEP0IlUKqflIoaVTUpvnasRPb8/G8+XBnnExCYZsjvZk3b94959773r1cEATYTfCb713DrgvEKeI4jp4f8idP/iqJYjbNKUwXkO4VVeUwBP4VRdEm5WRKbDRtf32jdt+wrYtuy77hMfNu4PrzaC6ZmqY5ly5dckO+mHdrMjU1Jed7+sYKPcVjsqK94HL8i3KqK6f3jSR5qYO3ywb8lo21moGGIqMiBqitPnSs8sOm77k/sGb9D7exPOMatWv3rny/TLwRcSzw2htvTYw9d+LjYqn/WIfelRFTWaw7EhaaDAu3/kL97jzklARTkNDs6IDVuwdM4GHbFox6FWZ1ldlriw9ZefHbzOqN83ev/3wz5I3PoMVrb7uF0ROrqYHMEpdDxeFhMQ+MhhWIsLs6UensQr3Yh3o+j4YfoGIyLJoBFlwNS4leqZw5OFTNHXqT8fJHm7TbERx//Z311uTpjOEGUAUBOUWALosQAx51q4XA99GdlPHYcomUoUFrhu3AtBhc5sJ3/ZAONMHQzLnW7NWvlZA3Fhh59b3g0eFTcGkj73EQKTaJ56MBIUCC59CnJPC46aC20QQjct8L4If2JI6QP6SSeIzMnMNfl78Kb82OFJGnDm0kGzAysiiSuuOhajNUTRcrBsPtqoWVpg3LsuF6HmTWQt7YgOIwcFsCEW30iBALePRXYw76WwYygRtvCW0iJ2nB9sgByn1oJvoMA24Dx+wy8swIUxHtjzyMrNqIBThaK7keTiUYJgITCuXyX/A232TCkVWanMnV18GbBp3PNmEksONzW4A8bFE6FudWkV5eQ941IURxP4Gt0Mgg4CRUlByaShJBQgQySWB/J5Ci+SZigbyeQPeADr+7A9kEjwK5LNNfng43qvKYmByktYaq4k5PD24PDaJ+oATuaAnK+F7IR2hOt20LsUAhK2PkUAHKRB/8oTw692XRX9LQu0dFPpeArklI09BoqHkN8kAB/pEhuBP7gdEedAoMB+/NIu22yIctb3YIhImTVBFSfyeEo/tQeLoLo4NpjO9PYiLlYky0MDak4eBTOnoPFNA1UkC2qEPOKuAFDsUHj5BdLFO0dEabjCFigbAeokyEKVFIiAotTTnKrlTQ+2AJe+8/Qr8uoljQ0JFVoYbOEHFIwNENWe/J4++J0ah9BK5jtVl3CPi2fUei01cFETL1G5ETACoqzFUQUKPj7BYksV2A7c7bRkggEml1eB8asgP1+ndlgVnvt//ujMDe+KB54/KCykzoxNKRSEAN77Zhg3ktyM8PgkuQ6LZJlAqRUqtWl5H+6cul9LVvPk3OzZxOgZ1v76A9W61i4syZpG66U4lU6l0lk3u2e3Scyw4egF934FBJtDJpOKKPpq9gjgJrhpVfK8O7dcWVFma/EKrLn1N7/DM1P1+fnp6mZtDmjQXCsMfHxyWtOJriA6NgNmovK3rnS3sOjx9K94+oQmkYgaKhFkiYrdZh3ps2pVtXfxfXFj+TGf/jb8efqeHs2bhw/lfgCfCTk5O653kljksoSKcuCkktGwiibzD3GgzjE2F95Zebw8NVXLgQ1/kW/iOwW9g+sV0B8A+ESjQpudvRVgAAAABJRU5ErkJggg==", moonshard:"data:image/png;base64,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", sunshard:"data:image/png;base64,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", gigantamix: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAwtJREFUSEvV1V9MU1ccwPHv6eW2gApIbcOf2YLIZpSHKQR5MMok0Rk1PmmAxti5ZIuAxLhXNCbjaQ9O67/4oNaQAmo0MxqjJhg0xighYw93cYp1tBEHlDagUNc2vWdpeTKBaas+eJ9Ocs/5fX733N/5HcEnfsQnjs97AVJKbbZEhBAV70pwTkBKaQIO+33+piFfD+2HTqBiTMaLEaXtUDMl9jpsdttJYL8QIjJrEnNlIKVc0ffooXb71G7Mdhtbtm5kgSmenP46onD92i2CPj8b9pylenVNhRDiz5SBE64jWvz3A2yrs5NVVEWmdWZH/h3TePOyn6s9PpRVP9Pcui9N4PhpLX6vnaqvJzAXzEfXlSRgMMQJjkzR/0ceyto2mlt+TA/o6+vXDre1UlfsY1HeNNnKZBIIx3MZn5hHz7Cd/e0uqqur0gO6PF1aIBBgfeUyrrgOENfVJKAYYnyz8ycGno9gsVhocDSkDkQiEek65qKsrIyKr5aRk5NHZnb2zD8Ih3n1agLtyV94vV5a97ZiMplmrcg5y3Tw6aA8+utRnLud6DIGugEQSCQgkyMlQ6XjfAfNe1so/7I8dcB9xo3zOydWay7RqJ7YHELB0eRXGFUjUgg8ni7qGxvSA7o93TS1NDH22M/jGw9gcQ5XXGfYvHwNNXW1ZKwqwn3rMvWO+vQBh8PBAqOK/8Ijzv5yjJdiioPrdlFgtvLG+w+eNVMfBtTch1q9kJ7FQcKTEc7f/Y0fTJWUTGcSKlTobTR/GLCyM8jrv4cZyBglM3c+q0ML6VafUh0tpLR0CQMfAzg3eodt5kq+3biJydO93FFfcDNriO+t6z8OMO7zsTKjCCVL5bnFjR5aSmCimEJ7afqAlHKws6Nzaf6ifOYFYgy5e8l/kShVCH1hoMRZy7RFJTQeonFn4zMhRHmq3dQSi8UuXuq+VBvzjjCsh2fOWqLZ6VBsyEYtK2B7/fZeVVV3CCECKQGJyVJKC2CNRqOa3+d/a73NbsNoNCb699hcwRML3uvKfNe1+H/vP3/gP/L4WCgm0k9oAAAAAElFTkSuQmCC", maxmushroom: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAABTZJREFUSEu1lXtQVGUYh5+DsCyoy7K4iArKyoIJpgaCpWhMzgjeUrGayBktUsdBZ0rHRtNEsmxwphynwkxNxWQwTBMcb6mNGiqKY4po6i7IRRC5HGAXF5bFPc05ggMo1T9+M+ef73zf+7y33/cKvOAlvGD7/C+AJElG4FA3Z2YJgmD+LwefAUiSpAd8gSBRFLPFOlGxcXjfQQ5nHsQFidkJc4h9N17Z1/no0Ol0M4EioFoQhJrO0C4AxXhTS1bmwQMxYkMDthtm5esXNYqggQaMAw2oCq5T5bST39YEJ45C1CsQGY5OqyUhfs4Z+qjf6QzpDjBl7skwBqj64rfpO7zGTkDzajRtgjt5R27hVLniZm1RHIyMH0lvewPi+bOIuWepSllDeauVhHlzzYIgBHdE8RQg59lsMpuOb/yeRU2tqB47IXERZ366xKP6ZsLeCEZv0OF87KTydjXmvFJ6e3sQ8+FYctNPEV12nrRQA7Erl2IMNgZ31KczoHDHl6lhw0zlDL9wCZ+PViAYDJxLvwhSLyYmjqfkagU3Tt7BZ7AWa62NYeMNePdXUXLoFKPiRpD39y2u6HuzNCX5piAII+QougCmBAaHLRtkIPrTlXhqvSH7D2zhY8nddQa1l5aI2eFKBB2rlys82LmXgJo7lI+LpDovn88slZw2330uILustOzNbQuWM8Tait99E7qAAIbPfBs3Hz0P79VRVVyn2HZYGmm1NDDu4SVKnU7u2Zup8g+mtK+KD7ZuNBuNxmdrIF+UJCntRm5u0tlv9tAsNhLZz4/mktv012hx1Wlw9dYogLZ6C22ihapHTXgMCeFKdSUeOi9Gz596JnrWrH/tIndgU1lpWVJ5QTFfrFrN+sgZSBYHXh6ttEluCsBVcNDYrELQuJGcf5i1qV/hFxpgDgoKGtejDtrVGtpZXLKIklcn01BZQVJULEMbXRRAsZeTLZdP0Me3v33rjq2ymntUtVJkSZJigKy0b9P0iFYQm3A4wU3niavOk0nh0ZxI38n7IycqgN0F54idn4hx4pinxezpyRDaPb+wdPFS/cdRk7GYrFhMFoa7VWFqqkEbEUbq1VOsmBbPKN8Bip3r1Q/4+shB9ub8Zge6v0ddopEBYWmbNxdG7c3BsDkV07bT2G1aBk8LwZa1H//GCu44HUR+sgzBR6cApBqRpO1pzFu4kLUp61F7alDba4l5aw5TEt6ThSZDlXoogMt5eYX7V6whrkFF3wmj6eNopeJWOd4GH6pOHiMicQn68JH08m0H1IqYCorIztrO6OkL8Gvxxsezlmu+HpxO30bkquUkzE34XBCEFBnwtHMSJ8UxUTuI+JBomusfoPEPQZIcVFzMJmJxEl4vD3sSQZ1I3V8V3P4xBeP6DTj8QnE1F3FtdxqeQ3zZhJ2cozlPAB3FkSQpGwjNzMg0ZmZksmHk6wQFB+I+wAd7jUhNfjF+kYGoNFqcFht1d2vI/2EdLmoNggD3BwajGhrIyYZ7/HwiJ0cQBPkJ7zpw2mfBEiBp6msx+l1LluGr7weOFprv16Huq0Zqg4YSEWvlQ66fzcbfMA5BreaYtQjfKRHExczYMnjMS7INZT13okmSVJj7+7mwkmPHmRs1Xjloq7Ziq7HJLY2ttpaSP38lpdWK2s0DydWdLYcyZKHNkrtKEAS5u3oGtGvD1JEur8cwyS+EAfWtyqUH3ipOV92VU3HzqaH217O7HnqcyZ3SRX159bp9B37B6fJEyS5OJ9MnTO6Sih6F1tOPzvtyKz/nXJdU9GTnH8ZWRjcQM6T3AAAAAElFTkSuQmCC", maxhoney: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAABH5JREFUSEu1lX9M1GUcx18Pvzk40pNDIEExlDEQTUUM2SlgCOYOWWaThr+mqadpm7PW+jH9B2mVbrFTyjamiVrNJpQGKaFkIgchyClo6O6YP8ATLgkV7ziedqSECsx++Gzfv57n83l9nvf3+bw/gqe8xFPOzz8CSCkLgeceKWqHEGLHYIUOCpBSegLh/QJ1zeZm3bqlK0EInKUFBgaRk5ONKiQoXQhRNBBkKIDeUGnQVVVW/R13w8rKlNmYTpwBVxf8k1/gm5IjLFyxnOHBAVvuH9QLISwPggYEOKVoNjdrv9u1hzlTYkHhBfZuQlX+1O7aS+SiRHBx4GtqpKTTj/2Fh7B6u7PotUXOr0kIMW5AwH1Ztjml2LlqI1nxM7lZcxb1CFciVi7hZPZuNDk6OPwTGGuQYeHYk+dx714XrjYHhWd/JSQygoT01HNCiGgn5KEbSCmjDJUGY1VxKYlCiSI2lDEd93C0mrH7jqTF2s2Ylk4oroXMydhmTMFUXg1u4OnWw+jpcWg3b6Ho2JHBAfpP9cY5EdFc21mAZuZkOPALMk4FC7JAoUBcaQM3VwhWY8j9nElrXsb96FGEpQViYtEWHKCotHhwwP4v9xlD/FQkuPlB7qFeKeXW5dj++J26/IN0WW8xevpERidqaNEfQO2vwuXHehwvRSADQsjYo+f7uopBAfraspO6iuJSkr1H4E43o5KmIN2fwVRazpiUqXjUGZA3WxGxqTh8FNhKzuBll1R63cZaf4FLE8ey7t1Nax/0Rt8/ePByDuftxveunfnzYnHp6eH25St4B0dx/byR8b91wMUmSI+BhGTKc/JASqauyuRyfSOGOy1o31idrlar+3qiP8CoTU6N+jg5g6BpwSg7PZEHi2C4KyxeBWV1iLI68PfDtiKJ2vyDTHtrMbS1ICtOcylGQ8kFI2vXr40WQpx77JlKKY2vaGZH5a3eiLLpEh5fVUB8OHKJFhsumMtO9zbX+LRErhaVoYoLw+tEAzTWIOIn0XjHndLhYmhAWuTEqM1vbiJu1FiwO2CYD1LpRZV+L5GZycAdfJsb6YlIwvLZt4ysuQopz0OYkvM/GDieOmFowJLEtKhZngGEWrvocnQzLiOFlqqzaDZo4NQxZIMdx7MRSE0CdkcPDoUH5mOnCDSa2Tc1EOVIlWXZmtXxQoimgSTSWa/d0O98+31u11/Gw2Fn3rIs/HGFhjIChAseyih6/FWYKqppbm2jU3aDjzfXuzrwXDzXsixtwUIRpj7e3/Qe7WRte3t7YXtbO5YGM8dztrHhvXeo/iSXyROCuHXRhsrLh4I2EwWtTUjAU0o+yNnKjIy0cf0rH9LsnJtmk1nmZa1n1l0FL2aFgek6P5/uoDw6hLExkZbMda/f6Ffp/IGSP+ZF/a8mpey160Obssn+aCMy/ws+rL5C6NJXLZnzMxeKEOVDUvzbgbNNO1erK5w2ATFMiTb/a4rqavps4EnG7ZAjU0qpA5wQ53Rje+72pvDw8D6v/8+AXqP7CxIw0LT6XwBPkmSoM38C+cLpKOBgFesAAAAASUVORK5CYII=", wishingpiece: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAA4xJREFUSEvtlU1oXFUYhp9z596ZMUkTMtP8Nc0krYnQH3emBUGQruzGghsbBAMWio1ZSNRNScCdRIi1aWZEl0IdxFWzEMSNgorNQhdOk9KkYCcxnUySO3+ZyZ37d+RebcikE+oPBRd+8G3OOfd7v/c97/2O4DGHeMz1+W8ASClf1HX9hr6p+4Qj0QiRSOQccPdPBZaEENV6ajySgZRyJJvNxScmPmR+YdWvcfJ4p59eDJ4e5NTpUwkhxBt/G8ArDnxw9uz50IHWpzGdbr+GY2376YXi/koi8Ta9fb11m92XgSdLNpu7MTx8iVDzC34xyyqRXV1me/sXkAfQGp4EO8PU+69x5sxzt4QQJ/eyqAvwQJbL49MUt1S/c6Nqcf+3LzFNFUETrqsQeiKKY27gGinmb8/9NYD9ZMnpt8nlM1StALgQ1pogEMKxKrjl75m/87N3yWNCiMRuFjUMpJQnVlf11MWLl2g/PISey++c1TdSZNc3kYqCcCEYbEYoGrZTwXa2OdJR4rPkx0QireeEELMPPnwI4NrVeOq7H7OY8giqqmJZtn+2VFwhs7aA7YYREgKBEGqgEccxfADF3mDwmaNcuTJONBrdqbsXYDE+He//9PP7iGALXR1tKEoAx3FwbEgvf4VhNuywCqkt2OYWjU1d/lo5d5PZ2RkGnhqoD7B4Z1EOX5iE4DGkbXtS0xc7hGVLH8SLXH4FfT2FJIjlagSERnNTzx8s8z9x7eqbNY6qYeABvHz+MlI7SiTag2laVC2TQ53tuK7LVqWMlCrlcoGAMCgWMjS3xKgaZR9A2gWM0lyNo2oAqtWqnHxviuQXKRqbu+noPobjOuT0HJa1RUtrF5X8JsXiCoqqEes5jmk5rK0u+ACa1oCR/9pz1I5lH7rk+PS1VPyTHwiGD9La3k9Aa6Ckr2EaBVStgWJlxXfRwUgf4ZZOinqGylYG1b1F7HAbM4kpYr2xWSGEN6tqp6ln07mbc6mxsUkKJYEWbseRYapm0XeOl66QCAQdnSfYLCxjVdZQ3DIT468yNPTSN8C3Qoh397NpyJs96XvpkaW79xgdeQvUARw17HetVOdBGqB2o4R6saSBsNdJTL/jXewS8KwQYn3fH233hpQyBbSPvj7alk6n/a2Zj2Y8+kvJ68n+5PWkvzb0ypCX3rge+CfT9HnAy90RB/aO5vjezutKVK+Df7v2yAfnf4DfAaL8qSgps+ZcAAAAAElFTkSuQmCC", wishingstar: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAA9xJREFUSEvFlX1M1HUcx1/fO7hHzgu4B8FEyLuWQcMtoCW5RfOBGTsHK+2iTOcflaAtehD+cGD+kctm2YS14XzYYCx1mjooFy1nmz2s6ESYgZdRPB9wx8MBx8Hxa782icaB5cb8bt8/v5/X3u/P5/v+CBb5iEWuz/0DSJKUPEvdhBDCfS9qwyqQJGmX1+srr6k+DdIUBXsK+oCKeQBXhBBX5oPPAcjFgcPZG7eo+4cSiYi0YDFcxZnv/Ps6NjlmaiUkJHD006MyfM18Cv8FkG3p6vY15ee/jn/Uis6wFqXWjjTtJ9B/hoExF8lLNtKvTSJSayMU7GPDU61sfeFZbHabbKEMkoEzZw7gyMeVTefOf8HExEokhQ5ddDZWhcSTwzdp0XjY4OnhyAN20KYilHrGB+sZ89VTXLJDVrhfCFG2IMDr9TW96HyVgQED05JAKKM51jHKR+m5DCmnWOVtxNLbyllLOggFoWAHoWAncZY+LtZdnGNXuB6UFb5WWPrd98Mkapayrz3AyXXrMY4PEBmawK+OJtbvIdd1mpKEHHJ6G6g3Pc6NYAM7d6xhd+HuFCFE8x0VYQFAaUpyJlt12fTExWEf1TDtbyPW76PDtByVepLmmJVYW66hjDJjHtZzRvcNhz7cS2Zmxt0BsoJbDR5slhwmo2PYdfU2BXYDwZFfiTHl8VLbn/wYP41rcoAsKRahC+JLuklaxmNs2+50CyHsYRXIU9TbO9jkfPkd9v+2morU5Wx3t/NetBKFysr4yA8IhQaD2cFhVzEH0otwtI1w3RqkU7uE7q46jp96C/vD9hlnwk5R3eXbxARSyfT6GA/2oKCdS/HrCIXGIEJHWsBPwnQn/piHMHT+xFdLV0GEifbfj3HufMXCgPJPyptOnHIRFbWatWMeGhhhhdCzufdnquKzQG3C4f2WWtsz2IUemmupM9mI1MQwPnKd6qo3FwTYgGuOTQ5zt8fM88NWWvU+GiNAo03koPsSLY88zZfGFHoGh0k3KEn9pZLy2DgCko+9xXtwOvPm74HcGEmSymqqa0oPvn+CJyaDqCUzN4xG1vuHeHAqhMWYj0c/TG3UNBMBD2l/fI35lc0Ybcvk4nImbZn9m8ONqVlW4b7ltl2uOkvlZxeoGI3gA3MSkVMTmEz5jEl9ZAQaOT7uIzf3Ud4tKUKlUqUAngWjYvYXlyRJtutzwCYHX3d3ALV6GSpdCiGtnn0Nh6jLy/p/YRcucu9E99tFB3C5ukBEMKWAN57bwM6SbXI0yJb897ieB+Lwer0XvAPef6J6RYJsi/1ui+j+rcx7WY/h3iy6gr8Ac5+jKDtush0AAAAASUVORK5CYII=" }; var base64costumes = { rocket: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFYAAABWCAMAAABiiJHFAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzAzQUIwNkRDOTVFMTFFNTlGMTZCRDZGRUJDNjRENjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzAzQUIwNkVDOTVFMTFFNTlGMTZCRDZGRUJDNjRENjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMDNBQjA2QkM5NUUxMUU1OUYxNkJENkZFQkM2NEQ2NyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMDNBQjA2Q0M5NUUxMUU1OUYxNkJENkZFQkM2NEQ2NyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnEdDvwAAABgUExURdZfRmh5e0JOTygtLqOtt/zm411sbTtFR+XUz3qGh4KVldfLyJinqmF5hVQ0LjM9P/He2m5DO7uwrZ5SRJqSj25+ga24usrAv42dn+p7ZlJfYL7G0bCloN7j7EVUVQAAAF8gpikAAAAgdFJOU/////////////////////////////////////////8AXFwb7QAABTJJREFUeNrsmOlyozoQRrVgCQG2AFvAaBz5/d9yuoVYxOIFz/1za76qVJxKcmhard7I4z8R+Yf9h/2/YjlzFuQYk/xvYbW2gvYSQlgn2V/AcimMUsZ4KBjsmHNS8++wXAuVKeAqOgpMdpzzL7DcqU5RYbJshkVZ/YL7DKtF1lH46sDgGCvYC+4TrKNZJihA11QhtX7qYPKEqoxQCF1SqbCIfcbdxUpqhFMeuqYyrZ9z97BSgAPB3hWT+tjVL7hknwoRS9eaUz/GWkql2ISCB0bqPncHK4x121A3g+rdMCM7PgDHvobum7uNZcLZLShbUvfM3cWKJdOumfvmbmO1syJmOrkJ/czah3RiEKbvHeanvoWMCHkVxZ4gP44E4PIVAKR5+HwwbtHeGRK+ZF/O5PiQI7dszgWH+GgzU+4azD+QGMObcmnnsSZmCYEfyrfOvyqjUcIZcuLRfGtzBaWQdhRDOKTHxeU9ghVlbs6uS5IOzyqkA8ms45oq64PhCFaldW1dkSSF+d0Y7pigKiuSQlHX/BSdEvyQb29lfbu5pAFzf34KZjqVoIqC6t8/8LROH6q8qmxVLdq8bZKfU8dN0hWem1DuH6CO9QkivYPyMs3b/CeTJgkqoMkpjmMfNTihzU+nU5nWN3NrUIDz39u2MQexolZ1Wp68yjJN8zxvQXme4ufaHm2W1O1e16dJJSrFJ4Fn6uOt3a02t/IUq0Qs2Hv7omO83UW9wNZ5iian9Lu2WSzMvdcpKjffYeV94YTWY2/fzg62XjoXVLNvsQ89HVsfCml6F99POjA/tJ6Zp21b3/M0V/L7AcoXCZWXJVwGuM1p2r409Q1sXx+1A17pL1jtvh/3oI7JUCpV7gPrzvjXWGxxXcByS7EPe1kcX2L7Mm41n5dwLVGvStl+VyPtMH7MGpwBC8Kx+tM+QQco1lpf2MdORM71EdY3HGaiwuw8ciOq/KBZ4kxbQ+mcio2j3KB+gOU2U8yaBbXvSFfktztGoHad6meniDr0unOxt9tmmPRBfiZbUQN6pv0YXmBxzIc5X7Bt6ATvj3KXG2M19VRsN91rsf2ONMZahUsJA43cMDu8g93gknjYh0k/M/S5B0bJ/dEkwjqjPqAupt9dbL+bCjHAXnvhySxFojXK7BbI7UOD0GVL126ASZxdfSp0m1h/zS4VSMs1dkiWAU2i1cQ8vcjFoV+q65X8QpGL7BPQHDu4eollcXqRen44VSD2InqFhVSKq81hPzZiuYjTy9xaVv2KVbEoXXorGc4tuOJBg0l8XiOVza2dsKS3+hqwfD7A4gpC+InQWTJe2yi54HFMIdZjybWqpL6MWB1P3JyF1YZVRUGGhIh5QE5UjofLIuwF6lf4WA3YwJW9uZ4rTDJgtYWwmsV/OA05ZMBLj4X/rrwT/PP11Pb0lc35nYktmiRg7RCwISKXtYsjrBojDHyAhz51af75+HJwbAJnFo+1wvplAVjAPDVeJ8ALXgn5NYbYVS/qLh+XJc4yitMQ6VsiSAYCOq0+E+jHFnbSlS+TC+fjsWibZR06gQuYZZumMxhTDNuCF1hyWecsrkesAZEHM0VRNE0BGdHbG14u5lbBzsrzr5eNZkn3ntD2DCLWZgUYm2TmfBbwi+GvY+zFG4lN2OQGvtEKw/8zjy2EzhK0FX+CSNCPfezojmqvtcPdk8c2irMiUMFcLaeGeelc/+qX67W6POsYufDYgnEHRcFjz3z648hcjs6t+Fs7MEhmArAUoiNQz7MMv/ZCxd/ZA3r9EWAAv0eboM/sqZQAAAAASUVORK5CYII=" }; var base64trainers = { collector: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAEXRFWHRTb2Z0d2FyZQBKVEwtRGV2J4CxQ84AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAtUExURQAAALi40AAAAEAwKEhQiFB4yHBQQHhAQJB4ULB4UMBIQNiYcNi4SOjo+PjQuDxaNFYAAAACdFJOUwAAdpPNOAAAAAFiS0dEAmYLfGQAAAENSURBVCjPndGxasMwEAbgpG8g+wmsprSmIRCuNXhNlRcI1gskINAbFPIA8WA62CC4NbRLhw5dHDBkytChU9duWdpS0DNULpbktf0HDR/c6bgbDP6TISEkBDAvcRCmiNCDMEGUCjykCSql0qmFsBISpVhVFuJKtFlhZAGTFtIeKAAwjS0EiEoIKUv3C0U0JWVkIdhBm9tnB3Wzbpp17YCcf7/p4/uHKyEXe22y8z3qXOd3nxtXMmnG+etLcbm3c0z0dlwU9El3cCKPDwtK+fZr2YFaZofH+8NClB3gGf9N3MGQjnjGGM+obUpOr+ecz8EPNrq54hkwDwGbUcpmfskBRoTEZe8u1NwkiCz8NT+hdlqy1rCCiAAAAABJRU5ErkJggg==", scientist: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAKlBMVEX///8AAAAWIy0gaKgpQ08tSWZWbHVxSC2Mia/IyNTPlm/js4/u7vf3z7YQMY+8AAAAAXRSTlMAQObYZgAAAPhJREFUKM9jYCASMAoKCqDwRdLSHJFFRNxS3FIckRSopLm4pDkhlDA6gQRUEAJCKmkpbmlOinABJRfHrGUiLkowPruKi9TduwtdnApgAotcZO/eveiiBRdYKHgXCASlYAIMgloua2+5LBJEWFtyxi3ljDuSU6XOrL11ZiGS08XX3r17qxDZc1W31i5H9hxLo3ihhAOSgMyJwzY9B5F0zMk4GtN2EqFHYoZka8TEzkaEQJvk0ZiJGXAB9l0CMid6DjKuhvtll9iMEz2diXABhkLGzhmdMwTEkayZOaNzJrLDGDVndE5CEXARFHRBEVASACLkqBMEI+IAAPcBSGPvQC5SAAAAAElFTkSuQmCC", arena: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAKlBMVEX///8AAAAvLi9JSU1KKRJpUGlxSC2OKBGQiHPCuK3Plm/hUEru7vf3z7bCjtuyAAAAAXRSTlMAQObYZgAAAPFJREFUKM+Nj68OwjAYxFsEuiVsYYBZESTI9QmwuMkSBLM4MCQ4JHKiCU1mIPxR04ipySV7giV9FwZLu08guKSiv9xd7kPolzCtRSDpcR74gHQmnHPmt4nvnzECEzyAgDYWm+nwJrK1CfZNtKD7ZB/D9LA24LoPRtlrP2vB1M+y184C1Fu4WkdXsGyTp9X4AbYvdK0bOK6fal3N4f2bKoUJ5MRu5IXAoNTyrlRrGZ+FFPKUW0N5FDKRg9JYcEGFKCRNDBhG2FslMXEvBlxwWJYOGRqAiaMoVSEmYIeQcAfyROzJGAJJsIDgU2crm9rm/aU3xJBFi/RJuaoAAAAASUVORK5CYII=", wonder: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTIzRTMwQTlGNDdCMTFFNTg2MjdDNzEwNjcyQTIzRTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTIzRTMwQUFGNDdCMTFFNTg2MjdDNzEwNjcyQTIzRTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5MjNFMzBBN0Y0N0IxMUU1ODYyN0M3MTA2NzJBMjNFOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5MjNFMzBBOEY0N0IxMUU1ODYyN0M3MTA2NzJBMjNFOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pqi2bP0AAAAwUExURfjwaNCYcPj4+Ki40GAoIPjQuPig8NiYAIhIiNhwUHhIMCgoKLg4GPDAAAAAAAAAACeUxKwAAAAQdFJOU////////////////////wDgI10ZAAABAklEQVR42oSTC7KDIAxFTfhY8sH97/Zd1LYq8HoHNZAzkoSwbD+0PObaNAdUlyadAerNvU7/ILt/WVzGgLjvBD4yBU4NgYv/SnwAvQP6BNTMvn5MtAfKG9lN7bawUrDe1Ixui02kOU6ZSA+QfQgz6oFNibgSUa6cSQd1UEksKaUsnEWHlSS4i6Wcqa+kHHFSMkt0RChXQLAYY0TwqkgHJmC5AErMKEBcfcWIMJnPQM8tNAS0WvQAeYSJ+aPUenClHB7VYcsFDhjTlrPiDHmxMVCLe2Fu7zoGcNo4BgA2A1ZkihzXCQDilV8YV/89C7SlyK2nn1ev1uP55/L+vN2d/gQYAF8VM2lHYAE7AAAAAElFTkSuQmCC", tower: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAMFBMVEX///8AAAApQ08pcIFClKpJSU1Lq9BmLg9ubm5xSC2jycjPlm/cmhnork/u7vf3z7a4KRbKAAAAAXRSTlMAQObYZgAAASdJREFUKM9jYCAXMCkbKaDwVVxcnJBFlFOcTdyMEHxGsxRjY7dkAYQON5CKFIQeZbc0F5e0FIQeWZc0IHC5COMryZi4paWlOB9UgvI9dxqbOKk4G8+eAhZhMsvcZGysDcLTkkHmMroBBZT2/1YCCqSAbGZ0SVE0Wvz/v5WykJsL2CkizkbSD///l9uobOIIcaexkeR/IJiobAxxq5KhAqP07/0bBZiEIbYYAyUCJSeKApUag20BCrC2npwTEQBigZQIGzIwtp69EyEAYoH1CDBIxNy9e7SRgRHiX2UBzg7xs3cKOyYwQvyryDB/YaR44VSpnwxCUO9Kbwzt6AiV3ogIQtETHR09gQJIgRpz5sxRAeR4EJ0oGYgSUZIHZSaiCDAKMAoQG8sAZEpOkN/TuWMAAAAASUVORK5CYII=", pyramid: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAKlBMVEX///8AAAAuIhI0bC9KKRJLQyttZStxSC2JUC+mhS7EpUzPlm/UbjH3z7bDGGp+AAAAAXRSTlMAQObYZgAAAShJREFUKM+NkT9Lw0AYxu++Qd9W26Zbi36Ajo5KsWbooBCwZCrIYcZCByGT4J8TXYJp8LJ1zLuHFI7uDhm7Fb+Ld/R6yeDgs92P5/1z70PI3wKl+pumiFmjBhxUimqGqXYsK4tzK5Seo6rD10dZfr/ZLhQzDVK0NT1EIRBXtiRP9ZTMNw4K23yKuPQnsCfOaitzz/PlpBeZrSAEKaEAs1tbROGPUuGI2R4w1tmMF7s5YwcwO1psduPztgWtIYQFtIYGUOBnCUDwyc1Y0n11E86D+OHRbNp9EuL+RYgrAyhXIAkU4A0DXMaChLH4ANaXIkBMxN27AXLE9AnZSJop0HQ1iPs2ieaJLrno2yMf3wyUTq+rGLwBwMCrYqDlnJBOWYtOf4IC+Z9+Ab4jceSsepITAAAAAElFTkSuQmCC", alchemist: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAtUExURXCYOOCoiPjQyPj4+ChQEHhIMKjIiLiwoOCImPjgyPCoqJBAUNBgYAAAAP///2PjC4AAAAAPdFJOU///////////////////ANTcmKEAAAEJSURBVHjavJPRcoUgDESDiEI22///3C7aVrTc6dyX7jiDuocQSLCPP2T/DeDQSwCoh27IACC8hlQ9MAO+/QcxAPUC6gSADLhHuEMgZoD8U5gAh794ba1qGIgRWJZoUizLFFCAw++EQjyAM4NI3U8xZmFXAI/MDjAfxAMA9TOjAxrciTtAejjal6APcgQ0XWtESS3pKWK1Cu8RVEoHWYpeddS4RxCB0CpmKZkpfoDPoyaMji3nDU778YdqrmbkJpFm6+9qsq0mJme5tjZOgETl4K4cmCaANqJOqiq2KkJOOorqmDj9fZ8Bfadd+75fe7i3PUtKZEqFry4O+wFinP+8WT058r27+SnAAFJzMLawCe3JAAAAAElFTkSuQmCC", arborist: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAEXRFWHRTb2Z0d2FyZQBKVEwtRGV2J4CxQ84AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAAHsIAAB7CAW7QdT4AAAI7SURBVHic7VcrsKMwFL1UVVYiIyuRSNZVPomMrETilnVIZGVkZWXdRiKRyEhkZR3LCRO2yyPlO/PEvszcKfeXe5KcfOrUdU1f2Xb0xe0bgJUDzq8f1qRUsU+2mClrfP3z9zIAQ4WEKqys5cxzhoBtAsAUPvKTtbNS3D8BGQMwiQMofvCOb4sbcIh7N0uzAZjirscmdYi4OSBGAaCzqe0oci1z8nZjozffVaFGi3N3ryUoqlHCLjoHAMImc7bsJABIBJvz7DoJnPRcEtVTC76RN7QtZ81ALEQdBCHti5ZckEdRWqXkvhZ8Iw/5607CRNblJaIo9+mpbi25uH1KH6Jdij37oMxvCHnOmj4CZ/U5YDo1RWzyGrfJOZBy7mAUZvQVlXQ+J/T0iO4Z7wQ67PCjIR55yF/NgRNvSeipC93CoFlbRn1iQocdfsShIW8KB3ZTpz8nt+VAJelZ3P/xQ4ddg2ni5izDWwBm9HqbySuJa0Eq/1u8/w0/4obyFwEwa38Xod5W3vmi1zaJ0q44fqHDDj/iEP+av3wG2N5BZ9iKZlr9qAVCsuwEOuxmuRCPPOSvAvBQqjbrjX0dhu0Fk+VJc+YfOoGOBj/iDC+QvwkJmX/S66tBFWV37Rp5tSMO8Zu8CXEfmFGkUlLVnPMfKe+K9a/tWyzIbW7DOAhaG2PO6hcROoHoUd5iXURec13QCHTY4e/nbPssbwiHIhhl5CedQNfFZbnts7x/l5uTbeiItfkWv4q//5r9NwD+ANjtjs7HxchXAAAAAElFTkSuQmCC", decor: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACpQTFRFAAAARi1GbUtQLy4v98+2iXVrz5Zv7HV1rUxLcUgt7u73o8nIbS8rAAAARr4BKgAAAA50Uk5T/////////////////wBFwNzIAAAA70lEQVR42pyTgXLEIAhEV1Eiav7/d7vYa3s34nXaHaNkeAOSAO5fhL8BWDoCQMpUekGebLprTmtDBECWfxGCCEg/QAoASJL6maLSRAB4iCUGiAB49CUa2ABT8fxJHodaDEgp8gYQ+klIBPhX1g4H0BX7HW6BlC+gyA54lVYesqc6v08vzbQol3nJEeC3NPM7xoBgtKHKDbIDJNAz2hgNoyMh+JsjDzQK6B0WAD13tOtqoKnFdkBznxc1SZQQUMxrcnkMRD3JBAwxPcmhq3PzFC2fgcpX1CNAX4U/x8liVwIvU7GP3n2/Gb3/TfeHAAMAaGMlUpM6VNUAAAAASUVORK5CYII=", league: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRF////AAAASTAoZi4PaVBpamqObS8rcUgtjImvkIhztKbDw4tQyMjUzOPR47OPqukgZwAAAAF0Uk5TAEDm2GYAAADqSURBVCjPY2DADhglJQVQ+WfOHEQWAfLPnJmIpGDmmTlz5sxEKGGcM3GipORJhIDkTOnduzfOROiRnL7v3bvXlXABxi2z3wHBTm8B3AL7QAIv4QIM0nWh796FPt+IsHa7IBBUIzlVTlB6o+BDJKdLg8zYiOy5fTNnvkb2HKP4632FyAKyCwUFpS4iKVi79Jpt1C2EEtlbrYutItYilEjdEF1mFdi7EGHrQpCAFNxexucCooutAhnr4J57JyC6dFUgkIIpEWQ0XLpKWEAQyV6ztLRkFJemJYqloQoIMKIKKAkAEXJcCoIRcQAA4RtXiYv0j40AAAAASUVORK5CYII=", celebrity: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAEXRFWHRTb2Z0d2FyZQBKVEwtRGV2J4CxQ84AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAAHsIAAB7CAW7QdT4AAAIySURBVHic7VctlKNADA6rkJVI1lWOHImsRCI5xzlWHe4411OH3FNbiURWIpFI3FWOrMT15hs6lJ9tly28t/febd7Lg2Qy+ZJMMqXG6XSij6QH+mD6DOBqDzz++LIo0J/vL9Mr8PTzNjjLD6ch37J3zYWOoAW0VjTiCYHMCuAQ71rgV4M7r8Fu8QDgNPbcSQ5h954gHpYEvyeIf/seQBau41ApjqO1MLZbHhLssW9KFSZXAE7BjIkRKGTosR4wb/5FhHsgiZrsu+RvavXc7ceDjTVOYSsH+2f1zPKcwq1v/Pr2Mq8CAADwa+A6KA2qn3f3wFpcQIQQvay1PCSt1+BdO/j7/fT1fRWwY99A+SJ/1QMJ44x8i/WrI2Xou6DYh/3wM6sJkTkcg3fhhhzHJjdNKE2iliFDj/XWdm/OH0NdhS6tRE51ue/pIEPfpSnZXw2gsupeEEVVNdVISzoUF/DhO9ZBsJ8CfnMMs7p/IQnRKCzLpEiOp+9d+gDAW5lx16YbAH6O7x5DgAPwmEVKTtOKuGy6SlZcM2ToFZjDVICL/hbobJFZ+Ryod854y+qmlHqsoxG71ZkVgCc/MNBgNt+054tsC1FSIuddM2R+Hk3YwR77vAkfKG/2gHaCM0aZzex8y7GCHJdTnhUy/aYKtRuQ563VEajjcmxjdg/AiXZ0+fxpwEHqKeU398yZguE0FGE0WuPJdtT9i0zB8GICCG6+cH00NEO+Bn73/4LPv2b/TQB/AZGta/TPiHWYAAAAAElFTkSuQmCC", journal: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAC0UlEQVR42sRXTUgbQRT+RqIQWKkIC7G6lKZgvDSrPVVy8O+giLQ3b7aFQsFjQSgUeuilUih4FDxVKzkIHhqkxEuMB2sthWq8mEACZVMUAouHgAdDXw+bGWej1tlkre802fdmv2++9968LCMi3KQF6tnEGLuQNRGxayXAgaPhdjDNcIOXLeH3QiTgBZwDd+kVFEsOKAAwzYBxtwNMMwQRVRJNquCmrkngAQHOT18sBdClV8A0A6auXZqmuggAALW24Dpim1RPLxs/qYipKiObqgreuuAwjyLuoUuvOIB6R9XhgBdLAeAwD3hQQJlApmDD1DUn3zBE7mUVqGyBSbG+1cBeqeyAV09GZUuA76wuY6S7U/ym1haYuoa9Utk/AjK4bDurywCAd7PvMdLd6SpCXxX4+Pb5OWAOzq2WRO2ehtsQABZnppD+8RvBvvFzvmDfON7Ev7tIKN0xKsOI34KLM1Po6R/9Z+zB9jqefviETMFWupKVFCAilinYWEqkroxdSqSUwT21IREx52J5hSePhtHTP4qD7XUAEOulRApzyX1Pw4ip/h9gjJEZNZ22zOwBAF6O3QcAzCX3nW6R/L4qwMGNWEQ820h/w4PXnwEAtv0YQ4MPXX7ViajcBfzl1lYWAJDL5XG8m8TxbhK5XN7lk4n4MowmpicFAJc/Hl9AW+8Y2nrHEI8vCOk5iYnpSaVh5Oke4OAAsJneBADc+jqLwunRhTEN1wBjjE5+fsHJnSG0twdFJxixCMLNIWymZ8HXiJ0VH2OMbPsEwRfPrqwFJQWCvzbOpYNbuDkk1rLsfE9DCpz1/tna2speWmQ8/7X7GkqBGTVdeTViEVhbWVjIumqB+3is7zehbAODAyicHmFtfkVIH24OuYrRtza8rKrX5ldARIyIGCfyX76MZPllk1NxrQSGb/+pKqP23LdhVPt5Vltk9XyW1UXAb2vCDdvfAQBACm6irmjgEAAAAABJRU5ErkJggg==", monger: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAEXRFWHRTb2Z0d2FyZQBKVEwtRGV2J4CxQ84AAAAJcEhZcwAAHsIAAB7CAW7QdT4AAAAnUExURQCAQIigcP///wAAACgwMEBQYFhgeGhwcHhAQICAgIiQkNiYcPjQuP/pZaEAAAADdFJOUwAAAPp2xN4AAAABYktHRAMRDEzyAAAA50lEQVQoz53RMW7CQBAFUJIq7bC5ALmBZaNYSWV59gTeDwS3xBcgyFI6RINETZEDIA5gCqRNSYHwHiominc3KVDEL59mNF+azsOfdP4FPSJmTohauCEuimKS0G0L98U5kwsgv+E1sdAfnAGBA+BjDQ8ivKxLqBZ6tGCUM5XOuz8TokJTTGaVhX0EQMUWKM7CJqPAgshzrfPMFRP6YA515YCejTF14EH0WZudB2LGeienDp4gtxs5ttXvTm8YQ70fLehFA9lqb1eWkRyC47m70lfM8M+GUiH14XHKaemDCIhCsnDFb3/lCyD4aJ/LXtT8AAAAAElFTkSuQmCC", baking: "data:image/png;base64,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", idol: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAIABgAFL1MG+DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUBFwog6bVmlgAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAADzklEQVRYw+2XX2hbdRTHP5mdZFGsoctY3Zx58GUxaR1JCaQ0TbaXOERKhJWwDbOtD1aWIWGla9gm64NF3epDxL4s0mn9E5A0lOEKHaSLLqWsZbaJrT6IoWwV22JapnfSrrs+9N5LsnYuN7aC6Hm5957zze+c3/d3ft/8fvC//ddNU8qPjH6vuJY/2x1TPV5ZKYnNxxofBhHVFqJRk1xvs+BxukgvzHB7bKIgvqPahKV8G/3JQXIj6aKLKFuP5MCKrxo8Thf9EhvFFLGpWAZ2VJvYCOymYmd/e2yC9MIMmUgUj9OFx+lSMPJ3JhJVGNLbLA9t1pKbMBOJ4gsGChLnmy8Y4PPOMHqbZX0YkGcPkBtJ4wsGqN26E4D0wgytZXdoLbtDemEGgNqtO/EFA+RG0vljmNalB+QEAP3JQXoqtij+noot9CcHCzDArPT85W8XIM8e4Eysh8Re6ypMYq+VM7EeZJ3IjaQNUkgoqYB8+mW7PncLgOlryVV42SdjZLHS2yz3jH7vEyU3Yf7s+5ODXD/aik6nY37CWYAzNZ9HEARqP3rnwW24DNwraQkkRSv4Btgden0VVvZZP/n0wTHuS0WoZyA/uWyL2asMHdq/yj90aD+L2asAWMq3KTtDUkONKgbyBaRtPAGg7O+TR4KM9V1R1l1e+7G+K5w8EmT08EFFM6SxtG3jCQ2gVcVAtjtWkBzg4vQS7DuA9dTbcKqLJvvzK/7m83IyfNL/hdSI2kwkutRR5Qa4X3QBEm1i/swvTi8p8dFQCIDfnsoB0LzvAABNz2wG4HJd4+zLX0cdwKLE8rLaJhTbxhN0VLnR2yyc/fKaEphKpTDY7RjsdmpOXKDmxAUMdjuvTn+Px+mio8rNaChkyESis8Cz2e7Ycik6oOmocrPng7fWDM4ODwMwNzDA3MAAzt5L+IIBOTkA72d/jV6ua5wq6UAiN6HeZuHm8XPMT/SReKOTXQ4HU6kUuxyOla13+jQAOp0OgF6XS8G4PwzytOmVRx56NI9SwJvHzwEw2dXC7ub3MIfbufTzH2sO9lqlls2aZT57TKCyvk59AWvJryw+2e4Y9d/0sl2r47uhYSr2WOj86gYA73rr+f3uvLL9zMcayX77A5nAWY3qJfiLEy9Gv5fnmg4rvu3aFep9t+4C0NDQgNHvBeBJ64tFFaDG9EANIE52tYiTXS2iOdwumsPtoiAIoiAIIiAa/V6bhMUcbi/fiLvEC3IBgBOojMfjYjweFyXt0G3YvUCyn14a/lF+TwGPvxn/OD8u/BM3qi9kpcy7kIj8G+1P0GOYn/wsWwkAAAAASUVORK5CYII=", //detective: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAArCAYAAAAZvYo3AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpSoVBzuIKGSoThZERRy1CkWoEGqFVh1MXvoHTRqSFBdHwbXg4M9i1cHFWVcHV0EQ/AFxc3NSdJES70sKLWK88Hgf591zeO8+QKiXmWZ1jAOabpupRFzMZFfF0CsCGEYI3RBkZhlzkpSEb33dUzfVXYxn+ff9Wb1qzmJAQCSeZYZpE28QT2/aBud94ggryirxOfGYSRckfuS64vEb54LLAs+MmOnUPHGEWCy0sdLGrGhqxFPEUVXTKV/IeKxy3uKslauseU/+wnBOX1nmOq0hJLCIJUgQoaCKEsqwEaNdJ8VCis7jPv5B1y+RSyFXCYwcC6hAg+z6wf/g92yt/OSElxSOA50vjvMxAoR2gUbNcb6PHadxAgSfgSu95a/UgZlP0mstLXoE9G0DF9ctTdkDLneAgSdDNmVXCtIS8nng/Yy+KQv03wI9a97cmuc4fQDSNKvkDXBwCIwWKHvd591d7XP7t6c5vx/k63JuaqyPYQAAAAZiS0dEAAAA/wAAR9uPkgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+UBHQcJHjvMFswAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAACfElEQVRYw92YTWgTQRTHf1NUFBTbSvxIUaEXtwdLEOlBD8aKkAqCN3vxJNUoKOqpx9wsguAHaGjxopd4FFEDgtGDHoqYj4ONgsUUUqqlNkVFvWQ9bJ50hs3HQiFT321mZ97u//fevJlZ5bou7TQ1EokEmjBZbfzFAx1KteJnPJsFoIM225qgisM9uxqPL8+4QYjYRyCoYtPM8UKkHgl7CIjyoIpbtYlczgUYiUSUnQQkRhKzlSLRzM//lwOz5RlNubTNXBgHZQeBesrrKWnWfnj+MACXH31q6QPaT8DMfjN7TWUn72R8269undMJ1vrFZLwTz9tFoO55QHJDlI0lUgAkkqMNHSbiYwCMJoYBOHTxLgBD3V3/3nk1k7F4NxTLF/RYiaL1TgyA38W09lz6Ky+Tvn6GolHLd8N6NlcsArDdcTwS0ahHYO6zTqjWb84TWx53u1eB1Orbpw4AcOHBGwCKyUuBHDvxG/j5cV1X2Umg/MerhHt29wDQG9qkDXz21ovp06lvDR0e6+v2sn6/o/VPz38H4EOpDMCT9wvKzlVw9sQRAJ6/ntSUCxmxM/fe6Ted0/u0cTJPSIjfKzfvW1oH+rf1aw+OHhwAIPtxWsuJ3pCnqHT9mjbejPnS4jrNT7332FcHtnR2aueCr7+8WG7uCmkkHuc2avOOR35o2b60OA/A1g2OWREtvReILVQqyq8umOv5i/pp9Fe1thArlAraul89u6FUvPLanQAMhqsNzwuD4b1a+8Wspy3fpHLafx7YEe4DYKrWTqdTvruaUsoFiMWGa/P8Sa0eAnKPn0inXL9+04SIkDD7rSWg5Eelau333nJlK/IBbSfwF++WDDjFt6YdAAAAAElFTkSuQmCC" detective: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QkXACELuedkbQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAALVBMVEUAAAAAAAAvLi9GLUZJSU1KKRJxSC2LZjGQcUiRU2qwxuDPlm/u7vf3z7b///+TrKvBAAAAAXRSTlMAQObYZgAAAAFiS0dEDm+9ME8AAADrSURBVCjPY2DADpiUlJRQ+CouLi7IIkpAvouTAqoCZCUQPkKAyQUqIICiwslJSQGhQkVFRRshoOIEAnu1lbbBXaWira19dzdcgCFbae/evXdvJ8EFGNW23b17NzsJbguD9h7tu5tOb0I4nW2P7N2LpxMQAox7gVpuCyD5ThoosBHZ+4W5u6+JKyAJtAumCVYgCYhHNK6SaC1EKCiXEJ0Z2FgOV8JUppg6M0woHS4gnciYahwmIAa3R3ajGFAgUfoi3F0CYqHGoYmMCJcxioUqhSYiuZRRKVUpTAlFQFFQCEUgVACMEUAQiokBAKcRQwoZaIiAAAAAAElFTkSuQmCC" }; var base64acetrainer = { "f": { "1": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "2": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "3": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", } }, "m": { "1": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "2": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "3": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", } } }; var base64acetrainer_overworld = { "f": { "1": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "2": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "3": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", } }, "m": { "1": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "2": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", }, "3": { "blk": "", "blu": "", "bru": "", "bld": "", "pnk": "", "plt": "", } } }; var storyBase64 = { "eir": "", "naru": "", "lance": "", "cynthia": "", "steven": "", "blue": "" }; var gachaItems = { safari: 95, great: 50, ultra: 30, bluapricorn: 20, grnapricorn: 29, pnkapricorn: 25, bait: 90, rock: 120, gem: 9, dust: 60, wild: 70, gacha: 1, master: 1, amulet: 1, soothe: 1, scarf: 1, battery: 1, pearl: 15, stardust: 12, bigpearl: 9, starpiece: 5, nugget: 4, bignugget: 1, whtapricorn: 45 }; var finderItems = { crown: 1, honey: 1, eviolite: 1, fragment: 1, rare: 4, recharge: 10, pnkapricorn: 20, rock: 1, bait: 20, pearl: 10, stardust: 7, bigpearl: 3, grnapricorn: 15, gacha: 16, hint: 15, blkapricorn: 30 }; var economyData = { // mostly listing the main ways i can think of that money is generated/lost/moved, there are plenty of other misc things like luxury balls, window repairs etc not listed here but can be added if desired npcShop: [0, 0, 0], // placeholders, too lazy to copy 14 blank values in playerShop: [0, 0, 0], playerTrade: [0, 0, 0], playerAuction: [0, 0, 0], playerSell: [0, 0, 0], playerPawn: [0, 0, 0], collector: [0, 0, 0], questFee: [0, 0, 0], total: [0, 0, 0] }; var finderMissRate = 0.70; var safariHints = [ "Did you know? Not only can you see your bag using /bag, but you can also see certain portions of it by using a more specific command! Trainers can use " + link("/bag wallet") + ", " + link("/bag balls") + ", " + link("/bag apricorns") + ", " + link("/bag perks") + ", " + link("/bag pawnables") + ", and " + link("/bag rare") + " to see specific rows!", "Like going on adventures with your friends? Use your teamwork to clear the Pyramid! Type " + link("/quest pyramid") + " to get started!", "Bored? Why not challenge the Arena! Type " + link("/quest arena:help") + " to see the strong trainers there!", "Look where you're walking while itemfindering!", "Use different kinds of Poké Balls to catch different kinds of Pokémon! Type " + link("/itemhelp balls") + " to find out more!", "Remember to catch what you bait!", "Challenge your rivals with " + link("/challenge2") + "! Team up for a Tag Battle with " + link("/challenge3") + "!", "The Gym Leaders of legendary renown are waiting to battle you in " + link("/quest celebrity") + "!", "...Oh, it's just an ad. \"Test your knowledge in " + link("/cjoin Trivia", "#Trivia") + "!\" it says.", "...Oh, it's just an ad. \"Test your bluffing skills in " + link("/cjoin Mafia", "#Mafia") + "!\" it says.", "...Oh, it's just an ad. \"Test your verbosity in " + link("/cjoin Hangman", "#Hangman") + "!\" it says.", "Every day you play, you can bait 5 times without experiencing the full cooldown! Isn't that neat?", "Logging in 31 days in a row gives you a Master Ball!", "You can read about the latest Safari updates in the Official Safari Discord!", "Having trouble finding asleep? Try " + link("/findd Mareep") + "!" ]; var teraPity = [ ["2@terashard", 1], ["@terashard", 0.75], ["@terashard", 0.5], ["@terashard", 0.5], ["@cometshard", 0.15], ["@fragment", 1], ["@crystal", 0.5] ]; var packItems = { amulet: 2, crown: 2, scarf: 4, soothe: 4, battery: 4, honey: 6, eviolite: 6, rare: 3, spray: 2, nugget: 6, pnkapricorn: 8, grnapricorn: 6, bluapricorn: 3, rock: 8, bait: 8, silver: 9, gem: 6, gacha: 12, mega: 1 }; var fortuneData = [ { name: "amulet", val: {"0.01": 4, "0.02": 3, "0.03": 2, "0.04": 1}, deadline: [15, 45], interval: 3, desc: "You will receive {Percent} more money when selling Pokémon for {Time}" }, { name: "crown", val: {"0.01": 4, "0.02": 3, "0.03": 2, "0.04": 1}, deadline: [30, 60], interval: 3, desc: "You will receive {Percent} more money when pawning valuables for {Time}" }, { name: "discount", val: {"0.02": 4, "0.03": 3, "0.04": 2, "0.05": 1}, deadline: [10, 30], interval: 2, desc: "Items from /buy will be {Percent} cheaper for {Time} (except for Silver Coin items)" } ]; var defaultCrystalBuff = 0.06; var zCrystalData = { "Normal" : { name: "Normalium Z", effect: "evolution", chance: 0.12, description: "have a {0}% chance to automatically evolve a Pokémon caught" }, "Fairy" : { name: "Fairium Z", effect: "evolution", chance: 0.12, description: "have a {0}% chance to automatically evolve a Pokémon caught" }, "Fighting" : { name: "Fightinium Z", effect: "double", chance: 0.8, description: "have a {0}% of throwing 2 balls at once" }, "Bug" : { name: "Buginium Z", effect: "double", chance: 0.8, description: "have a {0}% of throwing 2 balls at once" }, "Flying" : { name: "Flyinium Z", effect: "priority", chance: 0.75, rate: [2, 5], description: "have a {0}% chance of getting increased priority when throwing balls" }, "Electric" : { name: "Electrium Z", effect: "priority", chance: 0.75, rate: [2, 5], description: "have a {0}% chance of getting increased priority when throwing balls" }, "Poison" : { name: "Poisonium Z", effect: "gacha", chance: 0.45, description: "have a {0}% chance to use a free Gachapon Ticket when throwing a ball" }, "Rock" : { name: "Rockium Z", effect: "gacha", chance: 0.45, description: "have a {0}% chance to use a free Gachapon Ticket when throwing a ball" }, "Ground" : { name: "Groundium Z", effect: "silver", chance: 0.48, rate: [1, 4], description: "have a {0}% chance to gain some Silver Coins when successfully catching a Pokémon" }, "Steel" : { name: "Steelium Z", effect: "silver", chance: 0.48, rate: [1, 4], description: "have a {0}% chance to gain some Silver Coins when successfully catching a Pokémon" }, "Ghost" : { name: "Ghostium Z", effect: "photo", chance: 0.9, description: "have a {0}% chance to automatically take a photo when throwing balls" }, "Fire" : { name: "Firium Z", effect: "photo", chance: 0.9, description: "have a {0}% chance to automatically take a photo when throwing balls" }, "Grass" : { name: "Grassium Z", effect: "cooldown", chance: 0.9, rate: 0.5, description: "have a {0}% chance of having half cooldown after throwing balls" }, "Psychic" : { name: "Psychium Z", effect: "cooldown", chance: 0.9, rate: 0.5, description: "have a {0}% chance of having half cooldown after throwing balls" }, "Water" : { name: "Waterium Z", effect: "fisherman", chance: 0.85, description: "have a {0}% chance to retrieve the ball you threw if it fails to catch" }, "Ice" : { name: "Icium Z", effect: "fisherman", chance: 0.85, description: "have a {0}% chance to retrieve the ball you threw if it fails to catch" }, "Dragon" : { name: "Dragonium Z", effect: "clone", chance: 0.09, description: "have a {0}% chance to clone a Pokémon caught" }, "Dark" : { name: "Darkinium Z", effect: "clone", chance: 0.09, description: "have a {0}% chance to clone a Pokémon caught" }, //specials "GuardianDeity": { name: "Guardian of Alola", special: [785, 786, 787, 788], effect: "golden", chance: 1, npcBuff: 0.12, description: "increase the likelihood of high BST Pokémon appearing from Golden Baits " }, }; var abilityEffects = { "237": "[Lead] Any failed Ball throws are instantly refunded to you at a 100% chance.", // Ball Fetch "34": "[Lead] Increases your catch rate by 1.3x during Weather Institute [Sunny] Contests.", // Chlorophyll "181": "[Lead] Increases your catch rate by an amount equal to 2 buffs if your lead is nerfed during a Contest.", // Competitive "14": "[Lead] Your chance of finding a held item after catching a Pokémon (if that species can hold an item) is increased.
- If the held item rate is more than or equal to 50%: +10% chance
- If the held item rate is between 20% and 49%: +20% chance
- If the held item rate is 19% or below: 2x chance
For wild held Berries in Contests:
- If the Berry rate is more than or equal to 50%: +10% chance
- If the Berry rate is 49% or below: +20% chance", // Compound Eyes "128": 181, // Defiant "87": "[Lead] Increases your catch rate by 1.3x during Weather Institute [Rain] Contests.", // Dry Skin "122": 34, // Flower Gift "119": 14, // Frisk "182": "[Lead] You gain greatly increased priority when throwing Balls. Does not work for taking photos.", // Gale Wings "93": 87, // Hydration "115": "[Lead] Increases your catch rate by 1.3x during Weather Institute [Hail] Contests.", // Ice Body "248": 115, // Ice Face "35": "[Lead] Your chance of successfully baiting is increased by 30%.", // Illuminate "150": "[Lead] Your lead Pokémon will transform into any current wild Pokémon.
1. Viewing your party will display your lead's transformed sprite. The transformed Pokémon's original species name is still shown, however. Shininess is dependent on the wild Pokémon; even if your lead is Shiny, it will transform into a regular colour if the wild Pokémon is not Shiny.
2. The transformation is temporary and will wear off after the wild Pokémon is gone.
3. Your catch rate is calculated as if your lead is the actual transformed Pokémon. During a Contest, your lead will be buffed/nerfed depending on whether the current wild Pokémon is buffed/nerfed. For all other non-catch rate-related purposes, your lead is still considered its original species. The wild Pokémon's Ability will not be copied.", // Imposter "151": "[Lead] Reveals a wild Pokémon's true form to you if it is disguised as another Pokémon.", // Infiltrator "22": "[Lead] The wild Pokémon's BST is decreased (only for you) by 30% if your lead Pokémon has a higher BST, and 10% if not.", // Intimidate "51": "[Lead] Increases your chance of taking higher quality photos.", // Keen Eye "102": 34, // Leaf Guard "187": 14, // Magician "104": "[Lead] You will completely ignore the effects of any wild Pokémon's Ability.", // Mold Breaker "256": 104, // Neutralizing Gas "12": "[Lead] You will ignore the effects of Contest buffs and nerfs.", // Oblivious "20": 12, // Own Tempo "124": 14, // Pickpocket "158": "[Lead] You will gain increased priority when taking photos.", // Prankster "44": 87, // Rain Dish "159": "[Lead] Increases your catch rate by 1.3x during Weather Institute [Sandstorm] Contests.", // Sand Force "146": 159, // Sand Rush "8": 159, // Sand Veil "113": "[Lead] If your lead Pokémon is Normal or Fighting-type, their Normal or Fighting-type will be calculated as 1x effectiveness against wild Ghost-types, except during inverted type matchups.", // Scrappy "86": "[Lead] Your type modifier against wild Pokémon is doubled if super-effective and halved if not very effective. For example, a resistance of 0.5x is amplified to become 0.25x, and a 2x type advantage is amplified to become 4x. Neutral or full immunity matchups are not affected.", // Simple "92": "[Lead] Prevents you from losing Consecutive Catch Combo even when failing a capture.", // Skill Link "221": 115, // Slush Rush "97": "[Lead] Increases your catch rate by 1.3x when trying to catch Pokémon baited by other people.", // Sniper "81": 115, // Snow Cloak "94": 34, // Solar Power "3": "[Lead] You gain slightly increased priority when throwing Balls. Does not work for taking photos.", // Speed Boost "225": "[Lead] Your lead's type effectiveness against wild Pokémon is calculated as if it has Steel as a 3rd type.", // Steelworker "1": "[Lead] Your chance of successfully baiting is decreased by 20%.", // Stench "105": 14, // Super Luck "33": 87, // Swift Swim "101": "[Lead] If you throw a Ball with a weaker bonus multiplier than the Great Ball, that Ball's bonus multiplier will be boosted to the same level as the Great Ball.", // Technician "164": 104, // Teravolt "110": "[Lead] If your type effectiveness multiplier against the wild Pokémon is less than or equal to 0.5x, the multiplier is doubled.", // Tinted Lens "163": 104, // Turboblaze "109": 12, // Unaware "282": 12, // Good as Gold "289": 97, // Opportunist "267": 35, // Lingering Aroma "265": 182, // Quick Draw "144": "[Lead] Upon successfully switching out with a Switch Ball, the Switch Ball will not be consumed.", // Regenerator "296": "[Lead] You will completely ignore the effects of any wild Pokémon's Ability. When using Big Mushrooms, you will automatically consume 2 at once, gaining double the amount of spawns for the same theme.", // Mycelium Might "276": "[Lead] Your Itemfinder gains 5 or 10 charges after catching a Flying-type Pokémon.", // Wind Power "292": "[Lead] Only activates when you are in a Contest. Your lead Pokémon's BST increases by 3.64% for each empty slot in your party, up to a maximum of 18.2% if it is the only Pokémon in your party. Once this Ability activates, you cannot modify your party in any way, and you will be unable to use Switch Balls. Forfeiting a Contest will deactivate this Ability.", // Supreme Overlord "280": 34, // Protosynthesis "281": 87, // Quark Drive "287": 34, // Orihalcum Pulse "288": 87, // Hadron Engine "278": "[Lead] Only activates when you are in a Contest. If your lead has this Ability and a Dondozo is in the second slot of your party, your lead's BST is calculated by taking the higher of each individual stat between your lead and Dondozo. Additionally, your lead will inherit all of Dondozo's Ability effects, if any. Once this Ability activates, you cannot modify your party in any way, and you will be unable to use Switch Balls. Forfeiting a Contest will deactivate this Ability.", // Commander "60": 14, // Sticky Hold "53": 14, // Pickup "197": 97, "298": 35, // Supersweet Syrup "305": 12, // Teraform Zero "16": "[Wild] The wild Pokémon's type changes into your lead Pokémon's type whenever it breaks out of a Ball. If your lead Pokémon has two types, one is chosen at random. If it breaks out of a Spy Ball, the type will be listed as unknown.", // Color Change "126": "[Wild] The wild Pokémon's catch rate is calculated using inverted type effectiveness. This does not stack with the Inver costume, Inver Balls, or the Inverted Type Matchup Contest rule.", // Contrary "129": "[Wild] The wild Pokémon loses 5 BST whenever it breaks out of a Ball, up to a maximum of 25.", // Defeatist "222": "[Wild] The wild Pokémon has a 50% chance of fleeing immediately after each failed capture (once Ball preparation phase is over).", // Emergency Exit "26": "[Wild] The wild Pokémon gains an immunity to the Ground-type. This immunity is typeless and therefore has no effect on Resistance Mode or inverted type effectiveness.", // Levitate "153": "[Wild] The wild Pokémon gains 5 BST whenever it breaks out of a Ball, up to a maximum of 25.", // Moxie "46": "[Wild] The wild Pokémon causes you to use up 1 extra Ball whenever you catch it. This does not work on rare, special Balls.", // Pressure "50": "[Wild] The wild Pokémon flees after fewer failed captures than usual.", // Run Away "207": 222, // Wimp Out "25": "[Wild] The wild Pokémon will be instantly caught by any Ball if your lead has at least 2x effectiveness against it. If you have less than 2x effectiveness, the catch rate will be calculated as if it were immune to your lead.", // Wonder Guard "199": "[Wild] The wild Pokémon prevents the use of Quick Balls and Lightning Balls against it.", // Dazzling "214": 199, // Queenly Majesty "295": 199, // Armor Tail "240": "[Wild] The wild Pokémon prevents the use of Mirror Balls against it.", // Mirror Armor "283": "[Wild] The wild Pokémon decreases your lead Pokémon's BST by 25%.", // Vessel of Ruin "284": 283, // Sword of Ruin "285": 283, // Tablets of Ruin "286": 283, // Beads of Ruin "23": "[Wild] Prevents the use of Switch Balls if your lead Pokémon is not Ghost-type.", // Shadow Tag "71": "[Wild] Prevents the use of Switch Balls if your lead Pokémon is neither Flying-type nor Ghost-type.", // Arena Trap "42": "[Wild] Prevents the use of Switch Balls if your lead Pokémon is Steel-type and not Ghost-type.", // Magnet Pull "127": "[Wild] Negates the effects of Contest buffs on your lead Pokémon.", // Unnerve "271": "[Wild] If you have an Aura that has remained unpurified for at least 1 hour, capturing a Pokémon with this Ability will purify your Aura if you have any Salt in your bag. This consumes up to 5 Salts and reduces the remaining duration of your Aura depending on how much Salt was consumed.
- 1 to 2 Salts: 75% reduction
- 3 to 4 Salts: 50% reduction
- 5 Salts: 25% reduction", // Purifying Salt "304": "[Wild] All type matchups against the wild Pokémon are not-very-effective.", "268": "[Misc] When growing Berries in the Daycare, a Pokémon with this Ability will produce 1.5x more Berries in 25% less time than usual. Does not stack with Ripen.", // Seed Sower "247": "[Misc] When growing Berries in the Daycare, a Pokémon with this Ability will produce 1.5x more Berries in 25% less time than usual. Does not stack with Seed Sower.", // Ripen "139": "[Misc] When growing Berries in the Daycare, a Pokémon with this Ability will produce 1.8x more Berries than usual.", // Harvest "82": "[Misc] When growing Berries in the Daycare, a Pokémon with this Ability will produce 70% fewer Berries, but in 60% less time than usual.", // Gluttony "167": "[Misc] When growing Berries in the Daycare, a Pokémon with this Ability will produce 20% fewer Berries, but in 55% less time than usual." // Cheek Pouch }; var ultraPokes = { "851993": { "types": ["Electric", "???"], "name": "Pikachu-Seventh Hat", "stats": [35, 55, 40, 50, 50, 90], "abilities": [ "Static", "Lightning Rod" ], "tier": "SM PU", "height": 0.4, "weight": 6 }, "131124": { "types": ["Steel", "???"], "name": "Galarian Meowth", "stats": [50, 65, 55, 40, 40, 40], "abilities": [ "Pickup", "Tough Claws", "Unnerve" ], "tier": "SM LC", "height": 0.4, "weight": 7.5, "moves": [ 133, 372, 213, 34, 343, 242, 174, 399, 91, 38, 203, 263, 252, 175, 492, 154, 45, 441, 360, 468, 304, 334, 442, 231, 232, 319, 417, 400, 371, 6, 579, 182, 240, 156, 514, 496, 10, 103, 402, 247, 421, 163, 214, 173, 180, 727, 164, 241, 207, 14, 269, 168, 37, 634, 87, 85, 253, 369, 526 ] }, "65613": { "types": ["Psychic", "???"], "name": "Galarian Ponyta", "stats": [50, 85, 55, 65, 65, 90], "abilities": [ "Run Away", "Pastel Veil", "Anticipation" ], "tier": "SM LC", "height": 0.8, "weight": 24, "moves": [ 97, 502, 213, 34, 340, 347, 204, 93, 563, 38, 24, 203, 263, 565, 248, 45, 361, 505, 624, 32, 95, 286, 231, 67, 234, 573, 579, 182, 60, 94, 156, 496, 214, 173, 23, 500, 164, 129, 33, 39, 36, 37, 528, 428 ] }, "65614": { "types": ["Psychic", "Fairy"], "name": "Galarian Rapidash", "stats": [65, 100, 70, 80, 80, 105], "abilities": [ "Run Away", "Pastel Veil", "Anticipation" ], "tier": "SM PU", "height": 1.7, "weight": 80, "moves": [ 37, 24, 95, 38, 32, 234, 97, 502, 213, 226, 34, 340, 347, 204, 93, 563, 529, 203, 263, 565, 248, 416, 45, 361, 505, 624, 63, 286, 231, 67, 478, 224, 599, 573, 6, 579, 182, 60, 94, 633, 427, 98, 156, 496, 214, 661, 173, 23, 500, 164, 129, 14, 33, 39, 36, 634, 433, 528, 472, 428 ] }, "65619": { "types": ["Fighting", "???"], "name": "Galarian Farfetch'd", "stats": [52, 95, 55, 58, 62, 55], "abilities": [ "Steadfast", "Scrappy" ], "tier": "SM LC", "height": 0.8, "weight": 42, "moves": [ 372, 213, 34, 413, 280, 664, 370, 68, 343, 174, 432, 197, 38, 203, 263, 364, 515, 175, 116, 210, 270, 282, 348, 43, 400, 64, 398, 182, 98, 501, 156, 514, 279, 249, 496, 28, 493, 143, 21, 214, 173, 630, 211, 164, 241, 276, 14, 634, 526 ] }, "65646": { "types": ["Poison", "Fairy"], "name": "Galarian Weezing", "stats": [65, 90, 120, 85, 70, 60], "abilities": [ "Levitate", "Neutralizing Gas", "Misty Surge" ], "tier": "SM LU", "height": 3, "weight": 16, "moves": [ 60, 220, 288, 180, 174, 254, 256, 255, 312, 583, 372, 213, 584, 664, 499, 399, 563, 432, 194, 458, 203, 153, 263, 565, 126, 53, 416, 360, 114, 257, 63, 262, 599, 315, 371, 579, 139, 182, 240, 156, 496, 103, 120, 247, 214, 124, 188, 482, 123, 108, 173, 721, 164, 241, 33, 269, 168, 87, 85, 92, 390, 253, 607, 474, 261, 472 ] }, "65658": { "types": ["Ice", "Psychic"], "name": "Galarian Mr. Mime", "stats": [50, 65, 65, 90, 90, 100], "abilities": [ "Vital Spirit", "Screen Cleaner", "Ice Body" ], "tier": "SM PU", "height": 1.4, "weight": 56.8, "moves": [ 502, 213, 419, 226, 59, 34, 280, 347, 204, 109, 93, 383, 563, 24, 409, 227, 203, 412, 263, 252, 374, 411, 492, 568, 248, 416, 447, 385, 258, 270, 63, 95, 58, 8, 420, 333, 196, 334, 113, 478, 25, 5, 118, 102, 243, 599, 417, 371, 1, 471, 384, 182, 60, 94, 633, 473, 240, 229, 278, 115, 156, 272, 496, 219, 103, 247, 285, 214, 173, 76, 666, 500, 164, 389, 241, 269, 298, 168, 87, 85, 86, 321, 271, 433, 253, 472, 428 ] }, "65758": { "types": ["Ghost", "???"], "name": "Galarian Corsola", "stats": [60, 55, 100, 65, 100, 30], "abilities": [ "Weak Armor", "Cursed Body" ], "tier": "SM PU", "height": 0.6, "weight": 0.5, "moves": [ 133, 246, 310, 213, 59, 34, 362, 523, 347, 109, 174, 194, 91, 50, 414, 89, 203, 263, 202, 288, 258, 106, 114, 457, 506, 56, 58, 333, 196, 334, 113, 668, 243, 267, 101, 408, 182, 94, 240, 115, 156, 350, 157, 317, 496, 219, 201, 503, 103, 120, 247, 214, 173, 180, 446, 666, 444, 650, 164, 241, 57, 33, 634, 352, 250, 261 ] }, "65799": { "types": ["Dark", "Normal"], "name": "Galarian Zigzagoon", "stats": [38, 30, 41, 30, 41, 60], "abilities": [ "Pickup", "Gluttony", "Quick Feet" ], "tier": "SM LC", "height": 0.4, "weight": 17.5, "moves": [ 372, 213, 560, 59, 34, 68, 91, 38, 203, 263, 313, 374, 447, 441, 29, 270, 304, 58, 196, 231, 282, 43, 122, 341, 600, 371, 42, 182, 501, 240, 156, 514, 496, 28, 184, 103, 402, 247, 214, 555, 173, 164, 241, 57, 129, 33, 36, 269, 168, 87, 85, 86, 271, 250, 526 ] }, "65800": { "types": ["Dark", "Normal"], "name": "Galarian Linoone", "stats": [78, 70, 61, 50, 61, 100], "abilities": [ "Pickup", "Gluttony", "Quick Feet" ], "tier": "SM PU", "height": 0.5, "weight": 32.5, "moves": [ 600, 501, 282, 372, 213, 560, 59, 707, 34, 68, 91, 38, 203, 263, 313, 374, 154, 416, 447, 441, 29, 270, 468, 63, 304, 58, 196, 231, 43, 122, 341, 400, 371, 42, 182, 240, 156, 514, 496, 28, 184, 103, 402, 247, 421, 214, 555, 173, 666, 164, 241, 57, 129, 415, 33, 36, 269, 168, 634, 87, 85, 86, 271, 250, 526 ] }, "131399": { "types": ["Normal", "???"], "name": "Spinda", "stats": [60, 60, 60, 60, 60, 60], "abilities": [ "Own Tempo", "Tangled Feet", "Contrary" ], "tier": "SM PU", "height": 1.1, "weight": 5 }, "196935": { "types": ["Normal", "???"], "name": "Spinda", "stats": [60, 60, 60, 60, 60, 60], "abilities": [ "Own Tempo", "Tangled Feet", "Contrary" ], "tier": "SM PU", "height": 1.1, "weight": 5 }, "262471": { "types": ["Normal", "???"], "name": "Spinda", "stats": [60, 60, 60, 60, 60, 60], "abilities": [ "Own Tempo", "Tangled Feet", "Contrary" ], "tier": "SM PU", "height": 1.1, "weight": 5 }, "393695": { "types": ["Electric", "Ghost"], "name": "Rotom-Pokédex", "stats": [50, 75, 131, 143, 131, 106], "height": 0.3, "weight": 0.3, "abilities": ["Levitate"], "tier": "SM Ubers" }, "66090": { "types": ["Ice", "???"], "name": "Galarian Darumaka", "stats": [70, 90, 45, 15, 45, 50], "abilities": [ "Hustle", "Inner Focus" ], "tier": "SM LC", "height": 0.7, "weight": 40, "moves": [ 213, 419, 187, 44, 59, 280, 91, 227, 203, 263, 126, 424, 7, 83, 53, 172, 394, 374, 116, 264, 568, 447, 360, 359, 29, 257, 58, 423, 8, 510, 25, 5, 315, 181, 602, 182, 156, 157, 317, 496, 214, 173, 76, 164, 241, 276, 33, 36, 269, 168, 37, 253, 369, 261, 526, 281, 428 ] }, "131627": { "types": ["Ice", "???"], "name": "Galarian Darmanitan", "stats": [105, 140, 55, 30, 55, 95], "abilities": [ "Gorilla Tactics", "Zen Mode" ], "tier": "SM Ubers", "height": 1.7, "weight": 120, "moves": [ 264, 359, 36, 172, 281, 568, 510, 602, 213, 419, 187, 44, 59, 707, 34, 280, 339, 523, 91, 89, 227, 203, 263, 126, 424, 7, 83, 53, 394, 374, 411, 116, 416, 447, 360, 29, 257, 63, 58, 423, 8, 556, 334, 442, 25, 5, 315, 371, 181, 182, 94, 156, 179, 157, 317, 496, 214, 173, 76, 444, 164, 241, 276, 33, 269, 168, 37, 253, 369, 261, 526, 428 ] }, "197163": { "types": ["Ice", "Fire"], "name": "Galarian Darmanitan-Zen", "stats": [105, 160, 55, 30, 55, 135], "abilities": [ "Zen Mode" ], "tier": "SM Ubers", "height": 1.7, "weight": 120, "moves": [ 213, 419, 187, 44, 59, 707, 34, 280, 339, 523, 91, 89, 227, 203, 263, 126, 424, 7, 83, 53, 394, 374, 411, 116, 416, 447, 360, 29, 257, 63, 58, 423, 8, 556, 334, 442, 25, 5, 315, 371, 181, 182, 94, 156, 179, 157, 317, 496, 214, 173, 76, 444, 164, 241, 276, 33, 269, 168, 37, 253, 369, 261, 526, 428, 52, 326 ] }, "66098": { "types": ["Ground", "Ghost"], "name": "Galarian Yamask", "stats": [38, 55, 85, 30, 65, 30], "abilities": [ "Wandering Spirit" ], "tier": "SM LC", "height": 0.5, "weight": 1.5, "moves": [ 502, 310, 213, 664, 347, 562, 174, 399, 194, 50, 414, 89, 203, 412, 263, 313, 470, 114, 506, 286, 334, 212, 262, 417, 101, 371, 471, 182, 94, 240, 156, 157, 317, 496, 219, 201, 247, 285, 21, 214, 173, 164, 168, 390, 271, 433, 261, 472, 428 ] }, "66154": { "types": ["Ground", "Steel"], "name": "Galarian Stunfisk", "stats": [109, 81, 99, 66, 84, 32], "abilities": [ "Mimicry" ], "tier": "SM PU", "height": 0.7, "weight": 20.5, "moves": [ 310, 213, 20, 340, 523, 68, 242, 174, 91, 414, 89, 203, 263, 90, 175, 430, 492, 423, 334, 232, 319, 330, 341, 189, 220, 371, 182, 240, 513, 156, 279, 157, 317, 496, 201, 503, 103, 214, 188, 482, 710, 173, 180, 446, 727, 666, 444, 164, 389, 57, 33, 86, 253, 55, 281 ] }, "131817": { "types": ["Rock", "???"], "name": "Lycanroc-Dusk", "stats": [75, 117, 65, 55, 65, 110], "abilities": [ "Tough Claws" ], "tier": "SM UU", "height": 0.8, "weight": 25, "moves": [ 306, 389, 501, 98, 203, 179, 416, 201, 350, 422, 424, 541, 116, 370, 526, 579, 665, 632, 213, 44, 280, 339, 585, 68, 343, 242, 104, 529, 414, 497, 283, 263, 218, 237, 336, 304, 334, 442, 231, 387, 43, 316, 200, 182, 156, 216, 46, 431, 397, 157, 88, 317, 496, 28, 184, 214, 555, 173, 446, 666, 444, 164, 207, 14, 33, 269, 37, 92, 428 ] }, "66336": { "types": ["Psychic", "Steel"], "name": "Necrozma-Dusk Mane", "stats": [97, 157, 127, 113, 109, 77], "abilities": [ "Prism Armor" ], "tier": "SM Ubers", "height": 3.8, "weight": 460 }, "131872": { "types": ["Psychic", "Ghost"], "name": "Necrozma-Dawn Wings", "stats": [97, 113, 109, 157, 127, 77], "abilities": [ "Prism Armor" ], "height": 4.2, "weight": 350, "tier": "SM Ubers" }, "197408": { "types": ["Psychic", "Dragon"], "name": "Ultra Necrozma", "stats": [97, 167, 97, 167, 97, 129], "abilities": [ "Neuroforce" ], "tier": "SM Ubers", "height": 7.5, "weight": 230 }, "803": { "types": ["Poison", "???"], "name": "Poipole", "stats": [67, 73, 67, 73, 67, 73], "abilities": [ "Beast Boost" ], "tier": "SM PU", "height": 0.6, "weight": 1.8, "moves": [ 42, 203, 390, 51, 204, 585, 343, 406, 497, 263, 591, 218, 31, 380, 45, 441, 270, 237, 231, 417, 64, 398, 182, 156, 216, 496, 324, 214, 188, 482, 289, 173, 164, 92, 253, 607, 474 ] }, "804": { "types": ["Poison", "Dragon"], "name": "Naganadel", "stats": [73, 73, 73, 127, 73, 121], "abilities": [ "Beast Boost" ], "tier": "SM Ubers", "height": 3.6, "weight": 150, "moves": [ 407, 42, 416, 129, 372, 440, 506, 715, 191, 203, 349, 390, 51, 512, 332, 314, 403, 502, 204, 585, 399, 104, 434, 337, 406, 525, 497, 263, 591, 126, 53, 19, 218, 31, 380, 45, 441, 257, 270, 237, 63, 231, 636, 141, 417, 200, 64, 398, 182, 156, 216, 496, 421, 351, 324, 143, 507, 214, 188, 482, 661, 555, 289, 173, 164, 366, 168, 634, 85, 92, 253, 369, 607, 474, 404, 343 ] }, "805": { "types": ["Rock", "Steel"], "name": "Stakataka", "stats": [61, 131, 211, 53, 101, 13], "abilities": [ "Beast Boost" ], "tier": "SM LU", "height": 5.5, "weight": 820, "moves": [ 106, 23, 25, 34, 203, 484, 535, 624, 707, 727, 502, 475, 117, 20, 335, 664, 523, 38, 89, 263, 430, 218, 416, 356, 360, 237, 571, 334, 442, 113, 277, 478, 393, 182, 278, 115, 156, 216, 350, 397, 157, 88, 317, 272, 496, 219, 201, 285, 214, 479, 173, 446, 666, 444, 164, 276, 33, 36, 477, 92, 433, 469, 472, 428 ] }, "806": { "types": ["Fire", "Ghost"], "name": "Blacephalon", "stats": [53, 127, 53, 151, 79, 107], "abilities": [ "Beast Boost" ], "tier": "SM OU", "height": 1.8, "weight": 13, "moves": [ 83, 109, 510, 95, 573, 7, 76, 120, 350, 203, 227, 428, 495, 310, 347, 585, 399, 104, 52, 153, 263, 126, 481, 488, 53, 374, 492, 218, 257, 237, 63, 282, 387, 113, 277, 702, 101, 315, 220, 371, 182, 94, 473, 511, 278, 156, 216, 496, 247, 421, 214, 479, 173, 180, 500, 164, 241, 207, 269, 168, 259, 92, 271, 253, 261 ] }, "807": { "types": ["Electric", "???"], "name": "Zeraora", "stats": [88, 112, 75, 102, 80, 143], "abilities": [ "Volt Absorb" ], "tier": "SM UU", "height": 1.5, "weight": 44.5, "moves": [ 512, 332, 97, 372, 396, 299, 340, 280, 664, 339, 347, 268, 370, 585, 435, 104, 409, 530, 497, 588, 486, 527, 283, 203, 263, 252, 206, 7, 374, 411, 264, 218, 154, 416, 447, 270, 237, 468, 63, 231, 282, 636, 67, 490, 25, 5, 200, 6, 703, 579, 602, 182, 98, 501, 156, 216, 279, 179, 496, 184, 10, 351, 163, 214, 555, 289, 173, 209, 164, 276, 129, 269, 634, 87, 85, 9, 86, 92, 521, 528, 526 ] }, "808": { "types": ["Steel", "???"], "name": "Meltan", "stats": [46, 65, 65, 55, 35, 34], "abilities": [ "Magnet Pull" ], "tier": "SM PU", "height": 0.2, "weight": 8, "moves": [ 151, 203, 263, 430, 360, 106, 29, 334, 182, 156, 496, 214, 173, 727, 164, 39, 85, 84, 86, 92 ] }, "809": { "types": ["Steel", "???"], "name": "Melmetal", "stats": [135, 143, 143, 80, 65, 34], "abilities": [ "Iron Fist" ], "tier": "SM OU", "height": 2.5, "weight": 800, "moves": [ 151, 707, 34, 280, 664, 643, 435, 705, 223, 89, 588, 203, 263, 430, 416, 360, 106, 29, 484, 624, 63, 58, 8, 334, 442, 25, 5, 182, 156, 157, 317, 496, 120, 214, 173, 76, 727, 164, 276, 39, 87, 85, 9, 84, 86, 92 ] }, "810": { "types": ["Grass", "???"], "name": "Grookey", "stats": [50, 65, 50, 40, 40, 65], "tier": "SM LC", "height": 0.3, "abilities": ["Overgrow", "Grassy Surge"], "weight": 5, "moves": [ 512, 372, 213, 716, 409, 283, 203, 412, 263, 252, 206, 374, 116, 202, 447, 520, 45, 74, 359, 282, 73, 67, 345, 25, 5, 267, 182, 75, 156, 496, 10, 103, 21, 214, 173, 76, 630, 70, 164, 241, 129, 14, 269, 253, 369, 452, 526, 388 ] }, "811": { "name": "Thwackey", "stats": [70, 85, 70, 55, 60, 80], "tier": "SM PU", "abilities": ["Overgrow", "Grassy Surge"], "types": ["Grass", "???"], "height": 0.7, "weight": 14, "moves": [ 512, 372, 213, 716, 458, 409, 283, 203, 412, 263, 206, 374, 116, 202, 447, 520, 45, 282, 67, 345, 25, 5, 182, 75, 156, 496, 10, 103, 21, 214, 173, 76, 630, 164, 241, 129, 14, 269, 253, 369, 452, 526, 252, 74, 359, 73, 267, 70, 388 ] }, "812": { "name": "Rillaboom", "stats": [100, 125, 90, 60, 70, 85 ], "tier": "SM OU", "abilities": ["Overgrow", "Grassy Surge"], "types": ["Grass", "???"], "height": 2.1, "weight": 90, "moves": [ 512, 372, 213, 707, 34, 561, 716, 280, 664, 339, 523, 331, 643, 458, 409, 709, 414, 89, 283, 203, 412, 263, 206, 374, 411, 116, 338, 202, 416, 447, 520, 593, 45, 624, 63, 304, 282, 437, 67, 345, 25, 5, 341, 574, 182, 75, 156, 496, 184, 10, 103, 21, 214, 555, 173, 76, 630, 666, 164, 241, 276, 129, 14, 269, 253, 369, 452, 526, 252, 74, 359, 73, 267, 70, 388 ] }, "813": { "name": "Scorbunny", "stats": [ 50, 71, 40, 40, 40, 69 ], "tier": "SM LC", "abilities": [ "Blaze", "Libero" ], "types": [ "Fire", "???" ], "height": 0.3, "weight": 4.5, "moves": [ 512, 97, 502, 372, 213, 226, 299, 340, 68, 38, 24, 486, 52, 203, 263, 126, 424, 519, 488, 53, 116, 45, 441, 29, 257, 136, 67, 490, 25, 341, 315, 182, 98, 156, 179, 496, 28, 214, 173, 164, 389, 241, 162, 129, 33, 269, 369, 526 ] }, "814": { "name": "Raboot", "stats": [ 65, 86, 60, 55, 60, 94 ], "tier": "SM PU", "abilities": [ "Blaze", "Libero" ], "types": [ "Fire", "???" ], "height": 0.6, "weight": 9, "moves": [ 512, 97, 502, 372, 213, 226, 299, 340, 339, 68, 38, 24, 486, 52, 203, 263, 126, 424, 519, 488, 53, 394, 116, 45, 441, 29, 257, 67, 490, 25, 341, 315, 182, 98, 156, 179, 496, 214, 173, 164, 241, 129, 33, 269, 369, 526, 136, 28, 389, 162 ] }, "815": { "name": "Cinderace", "stats": [ 80, 116, 75, 65, 75, 119 ], "tier": "SM OU", "abilities": [ "Blaze", "Libero" ], "types": [ "Fire", "???" ], "height": 1.4, "weight": 33, "moves": [ 512, 97, 502, 372, 213, 226, 307, 299, 340, 339, 68, 739, 38, 24, 486, 52, 203, 263, 364, 126, 424, 519, 7, 83, 488, 53, 394, 411, 116, 416, 45, 441, 29, 257, 270, 63, 442, 67, 490, 25, 341, 315, 182, 711, 98, 156, 279, 179, 496, 247, 214, 555, 173, 164, 241, 129, 33, 269, 369, 526, 428, 136, 28, 389, 162 ] }, "816": { "name": "Sobble", "stats": [ 50, 40, 40, 70, 40, 70 ], "tier": "SM LC", "abilities": [ "Torrent", "Sniper" ], "types": [ "Water", "???" ], "height": 0.3, "weight": 4, "moves": [ 453, 392, 213, 226, 20, 340, 291, 104, 203, 263, 591, 45, 114, 420, 113, 668, 54, 330, 341, 1, 182, 240, 115, 156, 496, 219, 214, 173, 487, 164, 389, 57, 129, 671, 369, 55, 518, 352, 311, 250, 526 ] }, "817": { "name": "Drizzile", "stats": [ 65, 60, 55, 95, 55, 90 ], "tier": "SM PU", "abilities": [ "Torrent", "Sniper" ], "types": [ "Water", "???" ], "height": 0.7, "weight": 11.5, "moves": [ 213, 226, 20, 340, 291, 203, 263, 374, 45, 113, 668, 330, 341, 1, 182, 240, 115, 156, 496, 219, 214, 173, 487, 164, 389, 57, 129, 671, 369, 55, 518, 352, 311, 250, 526, 453, 392, 104, 591, 114, 420, 54 ] }, "818": { "name": "Inteleon", "stats": [ 70, 85, 65, 125, 65, 120 ], "tier": "SM NU", "abilities": [ "Torrent", "Sniper" ], "types": [ "Water", "???" ], "height": 1.9, "weight": 45.2, "moves": [ 512, 97, 403, 213, 226, 20, 59, 340, 715, 399, 291, 203, 263, 374, 116, 416, 45, 308, 56, 63, 58, 333, 196, 113, 668, 118, 330, 341, 1, 182, 240, 115, 156, 496, 219, 503, 247, 214, 729, 173, 487, 164, 389, 57, 129, 14, 671, 369, 127, 55, 518, 352, 311, 250, 526, 453, 392, 104, 591, 114, 420, 54 ] }, "819": { "name": "Skwovet", "stats": [ 70, 55, 55, 35, 35, 25 ], "tier": "SM LC", "abilities": [ "Cheek Pouch", "Gluttony" ], "types": [ "Normal", "???" ], "height": 0.3, "weight": 2.5, "moves": [ 133, 372, 213, 584, 187, 44, 34, 664, 331, 68, 242, 111, 91, 203, 263, 374, 360, 304, 231, 387, 341, 371, 182, 156, 205, 496, 402, 214, 173, 255, 254, 740, 164, 162, 256, 33, 541, 39, 168, 253 ] }, "820": { "name": "Greedent", "stats": [ 120, 95, 95, 55, 75, 20 ], "tier": "SM PU", "abilities": [ "Cheek Pouch", "Gluttony" ], "types": [ "Normal", "???" ], "height": 0.6, "weight": 6, "moves": [ 133, 372, 213, 584, 44, 707, 34, 664, 331, 68, 343, 242, 91, 89, 203, 263, 424, 374, 416, 360, 63, 304, 423, 231, 341, 371, 182, 665, 156, 496, 402, 214, 173, 255, 254, 666, 740, 164, 162, 276, 256, 14, 33, 541, 39, 168, 422, 253, 528, 187, 111, 387, 205 ] }, "821": { "name": "Rookidee", "stats": [ 38, 47, 35, 33, 35, 57 ], "tier": "SM LC", "abilities": [ "Keen Eye", "Unnerve", "Big Pecks" ], "types": [ "Flying", "???" ], "height": 0.2, "weight": 1.8, "moves": [ 97, 403, 372, 213, 413, 432, 65, 203, 263, 313, 19, 116, 31, 468, 43, 417, 371, 64, 365, 658, 182, 156, 514, 279, 179, 249, 355, 496, 28, 184, 143, 214, 173, 180, 164, 207, 129, 366, 269, 168, 369, 526 ] }, "822": { "name": "Corvisquire", "stats": [ 68, 67, 55, 43, 55, 77 ], "tier": "SM PU", "abilities": [ "Keen Eye", "Unnerve", "Big Pecks" ], "types": [ "Flying", "???" ], "height": 0.8, "weight": 16, "moves": [ 97, 403, 372, 213, 413, 65, 203, 263, 313, 19, 116, 31, 468, 43, 417, 371, 64, 365, 658, 182, 156, 514, 279, 179, 496, 184, 214, 173, 164, 207, 129, 269, 168, 369, 526, 432, 249, 355, 28, 143, 180, 366 ] }, "823": { "name": "Corviknight", "stats": [ 98, 87, 105, 53, 85, 67 ], "tier": "SM OU", "abilities": [ "Pressure", "Unnerve", "Mirror Armor" ], "types": [ "Flying", "Steel" ], "height": 2.2, "weight": 75, "moves": [ 97, 403, 372, 213, 707, 34, 413, 339, 65, 203, 263, 313, 430, 19, 116, 31, 416, 484, 468, 542, 63, 334, 442, 43, 113, 319, 417, 371, 64, 365, 658, 182, 115, 156, 514, 279, 179, 496, 184, 103, 214, 173, 727, 211, 164, 207, 129, 269, 168, 369, 526, 432, 249, 355, 28, 143, 180, 366 ] }, "824": { "name": "Blipbug", "stats": [ 25, 20, 20, 25, 45, 45 ], "tier": "SM LC", "abilities": [ "Swarm", "Compound Eyes", "Telepathy" ], "types": [ "Bug", "???" ], "height": 4, "weight": 8, "moves": [ 571, 105, 605, 522, 48 ] }, "825": { "name": "Dottler", "stats": [ 50, 35, 80, 50, 90, 30 ], "tier": "SM PU", "abilities": [ "Swarm", "Compound Eyes", "Telepathy" ], "types": [ "Bug", "Psychic" ], "height": 0.4, "weight": 19.5, "moves": [ 502, 213, 707, 405, 347, 93, 203, 412, 263, 248, 385, 270, 286, 334, 141, 113, 478, 371, 384, 182, 94, 633, 473, 115, 156, 496, 219, 247, 285, 214, 173, 76, 500, 522, 164, 271, 433, 472, 428, 571, 105, 605, 48 ] }, "826": { "name": "Orbeetle", "stats": [ 60, 45, 110, 80, 120, 90 ], "tier": "SM PU", "abilities": [ "Swarm", "Frisk", "Telepathy" ], "types": [ "Bug", "Psychic" ], "height": 0.4, "weight": 40.8, "moves": [ 495, 97, 502, 213, 226, 707, 405, 347, 109, 93, 203, 412, 263, 248, 202, 416, 385, 270, 63, 95, 286, 334, 141, 113, 277, 478, 243, 371, 384, 182, 60, 94, 633, 427, 473, 115, 156, 496, 219, 247, 285, 214, 173, 76, 500, 522, 164, 271, 433, 369, 472, 428, 571, 105, 605, 48 ] }, "827": { "name": "Nickit", "stats": [ 40, 28, 28, 47, 52, 50 ], "tier": "SM LC", "abilities": [ "Run Away", "Unburden", "Stakeout" ], "types": [ "Dark", "???" ], "height": 0.6, "weight": 8.9, "moves": [ 97, 372, 213, 226, 251, 91, 203, 263, 313, 492, 468, 336, 282, 341, 417, 400, 579, 182, 98, 501, 156, 496, 103, 214, 555, 173, 164, 389, 129, 541, 39, 269, 168, 259 ] }, "828": { "name": "Thievul", "stats": [ 70, 58, 58, 87, 92, 90 ], "tier": "SM PU", "abilities": [ "Run Away", "Unburden", "Stakeout" ], "types": [ "Dark", "???" ], "height": 1.2, "weight": 19.9, "moves": [ 512, 97, 372, 213, 226, 251, 242, 399, 91, 203, 263, 313, 424, 492, 416, 447, 468, 63, 423, 341, 417, 400, 600, 579, 182, 94, 98, 156, 496, 103, 247, 421, 214, 555, 173, 164, 389, 129, 541, 39, 269, 168, 422, 369, 336, 282, 501, 259 ] }, "829": { "name": "Gossifleur", "stats": [ 40, 40, 60, 40, 60, 10 ], "tier": "SM LC", "abilities": [ "Cotton Down", "Regenerator", "Effect Spore" ], "types": [ "Grass", "???" ], "height": 0.4, "weight": 2.2, "moves": [ 312, 213, 331, 204, 203, 412, 263, 202, 447, 593, 74, 270, 304, 625, 437, 536, 73, 113, 345, 77, 654, 182, 229, 75, 156, 496, 47, 79, 214, 173, 76, 78, 164, 241, 230, 235, 388 ] }, "830": { "name": "Eldegoss", "stats": [ 60, 50, 90, 80, 120, 60 ], "tier": "SM PU", "abilities": [ "Cotton Down", "Regenerator", "Effect Spore" ], "types": [ "Grass", "???" ], "height": 0.5, "weight": 2.5, "moves": [ 312, 213, 331, 204, 538, 178, 203, 412, 263, 202, 416, 447, 593, 270, 63, 304, 625, 437, 536, 113, 345, 654, 182, 229, 75, 156, 496, 402, 47, 214, 173, 76, 164, 241, 230, 235, 311, 74, 73, 77, 79, 78, 388 ] }, "831": { "name": "Wooloo", "stats": [ 42, 40, 55, 40, 45, 48 ], "tier": "SM LC", "abilities": [ "Fluffy", "Run Away", "Bulletproof" ], "types": [ "Normal", "???" ], "height": 0.6, "weight": 6, "moves": [ 97, 213, 383, 538, 68, 111, 38, 24, 486, 203, 263, 45, 470, 385, 29, 371, 182, 156, 179, 496, 214, 173, 23, 164, 207, 33, 36, 86, 528 ] }, "832": { "name": "Dubwool", "stats": [ 72, 80, 100, 60, 90, 88 ], "tier": "SM PU", "abilities": [ "Fluffy", "Run Away", "Bulletproof" ], "types": [ "Normal", "???" ], "height": 1.3, "weight": 43, "moves": [ 97, 213, 226, 707, 34, 340, 383, 538, 111, 38, 24, 486, 203, 263, 416, 45, 470, 385, 29, 63, 387, 25, 371, 182, 156, 514, 179, 496, 214, 173, 164, 14, 33, 36, 86, 528, 428, 68, 23, 207 ] }, "833": { "types": ["Water", "???"], "name": "Chewtle", "stats": [50, 64, 50, 38, 38, 44], "abilities": [ "Strong Jaw", "Shell Armor", "Swift Swim" ], "tier": "SM LC", "height": 0.3, "weight": 8.5, "moves": [ 372, 213, 44, 34, 68, 291, 525, 203, 263, 380, 29, 56, 423, 730, 668, 341, 371, 182, 240, 156, 279, 496, 130, 214, 173, 164, 57, 33, 55, 250 ] }, "834": { "types": ["Water", "Rock"], "name": "Drednaw", "stats": [90, 115, 90, 48, 68, 74], "abilities": [ "Strong Jaw", "Shell Armor", "Swift Swim" ], "tier": "SM PU", "height": 1, "weight": 115.5, "moves": [ 372, 213, 44, 59, 707, 34, 523, 68, 242, 91, 291, 414, 89, 203, 263, 206, 416, 29, 457, 624, 56, 63, 58, 423, 334, 231, 730, 668, 224, 330, 341, 371, 398, 182, 240, 534, 156, 279, 350, 397, 157, 317, 496, 201, 328, 503, 184, 214, 661, 173, 446, 666, 444, 164, 276, 57, 14, 33, 634, 127, 55, 250, 525, 380, 130 ] }, "835": { "types": ["Electric", "???"], "name": "Yamper", "stats": [59, 45, 50, 40, 50, 26], "abilities": [ "Ball Fetch", "Rattled" ], "tier": "SM LC", "height": 0.3, "weight": 13.5, "moves": [ 213, 44, 268, 204, 242, 91, 435, 38, 486, 203, 263, 424, 488, 270, 336, 575, 579, 182, 156, 46, 496, 28, 214, 555, 173, 209, 164, 129, 33, 39, 87, 85, 422, 86, 253, 521, 528 ] }, "836": { "types": ["Electric", "???"], "name": "Boltund", "stats": [69, 90, 60, 90, 60, 121], "abilities": [ "Strong Jaw", "Competitive" ], "tier": "SM PU", "height": 1, "weight": 34, "moves": [ 97, 213, 44, 339, 268, 204, 242, 91, 587, 588, 589, 486, 203, 263, 424, 116, 416, 270, 63, 304, 575, 579, 182, 665, 156, 46, 496, 214, 555, 173, 209, 164, 129, 33, 39, 87, 85, 422, 86, 253, 521, 528, 435, 38, 488, 336, 28 ] }, "837": { "types": ["Rock", "???"], "name": "Rolycoly", "stats": [30, 40, 50, 40, 50, 30], "abilities": [ "Steam Engine", "Heatproof", "Flash Fire" ], "tier": "SM LC", "height": 0.3, "weight": 12, "moves": [ 246, 213, 335, 91, 203, 153, 263, 360, 535, 510, 334, 442, 189, 182, 229, 115, 156, 350, 397, 157, 317, 496, 201, 328, 120, 214, 479, 108, 173, 191, 446, 444, 164, 33, 261 ] }, "838": { "types": ["Rock", "Fire"], "name": "Carkol", "stats": [80, 60, 90, 60, 70, 50], "abilities": [ "Steam Engine", "Flame Body", "Flash Fire" ], "tier": "SM PU", "height": 1.1, "weight": 78, "moves": [ 246, 213, 707, 659, 91, 203, 263, 126, 83, 488, 53, 394, 360, 535, 257, 484, 624, 510, 334, 442, 315, 182, 229, 115, 156, 350, 397, 157, 317, 496, 201, 328, 503, 120, 214, 479, 108, 173, 191, 446, 444, 164, 33, 261, 335, 153, 189 ] }, "839": { "types": ["Rock", "Fire"], "name": "Coalossal", "stats": [110, 80, 120, 80, 90, 30], "abilities": [ "Steam Engine", "Flame Body", "Flash Fire" ], "tier": "SM PU", "height": 2.8, "weight": 310.5, "moves": [ 246, 213, 707, 34, 523, 659, 91, 414, 89, 203, 263, 126, 7, 83, 488, 53, 394, 416, 360, 535, 257, 484, 624, 63, 510, 334, 442, 25, 5, 315, 182, 229, 115, 156, 350, 397, 157, 317, 496, 201, 328, 503, 120, 214, 479, 108, 173, 76, 191, 446, 444, 164, 33, 732, 261, 335, 153, 189 ] }, "840": { "types": ["Grass", "Dragon"], "name": "Applin", "stats": [40, 40, 80, 40, 40, 20], "abilities": [ "Ripen", "Gluttony", "Bulletproof" ], "tier": "SM LC", "height": 0.2, "weight": 0.5, "moves": [ 310, 213, 111, 434, 278, 205, 389, 110 ] }, "841": { "types": ["Grass", "Dragon"], "name": "Flapple", "stats": [70, 110, 80, 95, 60, 70], "abilities": [ "Ripen", "Gluttony", "Hustle" ], "tier": "SM PU", "height": 0.3, "weight": 1, "moves": [ 491, 512, 403, 310, 213, 331, 225, 434, 349, 406, 407, 203, 412, 263, 19, 116, 202, 416, 447, 719, 74, 484, 63, 334, 73, 200, 182, 278, 156, 496, 184, 402, 214, 173, 76, 164, 241, 239, 369, 17, 110, 111, 205, 389 ] }, "842": { "types": ["Grass", "Dragon"], "name": "Appletun", "stats": [110, 85, 80, 100, 80, 30], "abilities": [ "Ripen", "Gluttony", "Thick Fat" ], "tier": "SM PU", "height": 0.4, "weight": 13, "moves": [ 133, 718, 310, 213, 707, 34, 523, 331, 174, 434, 406, 89, 203, 412, 263, 202, 416, 447, 74, 360, 29, 484, 624, 63, 334, 73, 113, 200, 371, 182, 105, 278, 115, 156, 496, 219, 402, 214, 173, 76, 23, 164, 241, 276, 230, 110, 111, 205, 389 ] }, "843": { "types": ["Ground", "???"], "name": "Silicobra", "stats": [52, 57, 75, 35, 50, 46], "abilities": [ "Sand Spit", "Shed Skin", "Sand Veil" ], "tier": "SM LC", "height": 2.2, "weight": 7.6, "moves": [ 213, 584, 664, 523, 489, 91, 407, 529, 414, 89, 203, 263, 137, 29, 387, 107, 341, 189, 342, 182, 156, 496, 28, 201, 328, 103, 21, 214, 173, 164, 35 ] }, "844": { "types": ["Ground", "???"], "name": "Sandaconda", "stats": [72, 107, 125, 65, 70, 71], "abilities": [ "Sand Spit", "Shed Skin", "Sand Veil" ], "tier": "SM PU", "height": 3.8, "weight": 65.5, "moves": [ 213, 707, 664, 523, 489, 91, 529, 414, 89, 203, 263, 424, 416, 137, 29, 624, 542, 63, 334, 442, 107, 341, 200, 182, 156, 350, 157, 317, 496, 28, 201, 328, 103, 130, 21, 214, 173, 446, 444, 164, 35, 428, 584, 407, 387, 189, 342 ] }, "845": { "types": ["Flying", "Water"], "name": "Cramorant", "stats": [70, 85, 55, 85, 95, 85], "abilities": [ "Gulp Missile" ], "tier": "SM PU", "height": 0.8, "weight": 18, "moves": [ 332, 97, 403, 133, 392, 372, 213, 584, 59, 413, 432, 291, 65, 203, 263, 297, 19, 31, 416, 542, 56, 63, 58, 196, 668, 64, 365, 182, 240, 156, 179, 355, 496, 503, 214, 173, 255, 211, 254, 164, 276, 57, 256, 168, 37, 634, 253, 55, 311, 250 ] }, "66381": { "types": ["Flying", "Water"], "name": "Cramorant-Gulping", "stats": [70, 85, 55, 85, 95, 85], "abilities": [ "Gulp Missile" ], "tier": "SM PU", "height": 0.8, "weight": 18 }, "131917": { "types": ["Flying", "Water"], "name": "Cramorant-Gorging", "stats": [70, 85, 55, 85, 95, 85], "abilities": [ "Gulp Missile" ], "tier": "SM PU", "height": 0.8, "weight": 18 }, "846": { "types": ["Water", "???"], "name": "Arrokuda", "stats": [41, 63, 40, 40, 30, 66], "abilities": [ "Swift Swim", "Propeller Tail" ], "tier": "SM LC", "height": 0.5, "weight": 1, "moves": [ 367, 97, 453, 372, 213, 44, 340, 280, 370, 242, 291, 38, 529, 203, 263, 116, 31, 423, 636, 668, 400, 64, 398, 182, 665, 240, 156, 496, 503, 163, 214, 173, 164, 129, 37, 634, 127, 250 ] }, "847": { "types": ["Water", "???"], "name": "Barraskewda", "stats": [61, 123, 60, 60, 50, 136], "abilities": [ "Swift Swim", "Propeller Tail" ], "tier": "SM OU", "height": 1.3, "weight": 30, "moves": [ 97, 453, 372, 213, 44, 340, 280, 370, 242, 291, 38, 529, 203, 263, 116, 31, 416, 56, 63, 423, 636, 668, 64, 398, 182, 665, 240, 156, 496, 503, 184, 214, 173, 164, 57, 129, 634, 127, 250, 367, 400, 163, 37 ] }, "848": { "types": ["Electric", "Poison"], "name": "Toxel", "stats": [40, 38, 35, 54, 35, 40], "abilities": [ "Rattled", "Static", "Klutz" ], "tier": "SM LC", "height": 0.4, "weight": 11, "moves": [ 51, 213, 584, 227, 283, 203, 263, 175, 45, 319, 575, 602, 182, 156, 496, 214, 173, 164, 671 ] }, "849": { "types": ["Electric", "Poison"], "name": "Toxtricity", "stats": [75, 98, 70, 114, 70, 75], "abilities": [ "Punk Rock", "Plus", "Technician" ], "tier": "SM LU", "height": 1.6, "weight": 40, "moves": [ 51, 491, 213, 584, 561, 268, 435, 409, 587, 486, 227, 203, 263, 7, 175, 374, 416, 45, 441, 506, 63, 304, 43, 25, 5, 574, 575, 717, 371, 398, 182, 156, 496, 184, 103, 508, 351, 214, 188, 482, 555, 173, 209, 500, 164, 207, 129, 269, 671, 634, 87, 85, 9, 84, 86, 92, 253, 474, 521, 528, 283, 319, 602 ] }, "66385": { "types": ["Electric", "Poison"], "name": "Toxtricity-Low Key", "stats": [75, 98, 70, 114, 70, 75], "abilities": [ "Punk Rock", "Minus", "Technician" ], "tier": "SM LU", "height": 1.6, "weight": 40, "moves": [ 51, 491, 213, 584, 561, 268, 435, 409, 587, 486, 227, 203, 263, 7, 175, 374, 416, 45, 441, 506, 63, 304, 43, 597, 25, 5, 574, 575, 717, 371, 398, 182, 156, 496, 184, 103, 351, 214, 188, 482, 555, 173, 209, 500, 164, 207, 129, 269, 671, 634, 87, 85, 9, 84, 86, 92, 253, 607, 521, 528, 283, 319, 602 ] }, "850": { "types": ["Fire", "Bug"], "name": "Sizzlipede", "stats": [50, 65, 45, 50, 50, 45], "abilities": [ "Flash Fire", "White Smoke", "Flame Body" ], "tier": "SM LC", "height": 0.7, "weight": 1, "moves": [ 404, 213, 44, 664, 450, 405, 659, 489, 242, 111, 52, 203, 263, 657, 83, 172, 535, 257, 282, 141, 656, 438, 182, 156, 205, 496, 503, 21, 214, 108, 173, 522, 164, 241, 474, 35 ] }, "851": { "types": ["Fire", "Bug"], "name": "Centiskorch", "stats": [100, 115, 65, 90, 90, 65], "abilities": [ "Flash Fire", "White Smoke", "Flame Body" ], "tier": "SM PU", "height": 3, "weight": 120, "moves": [ 213, 44, 664, 450, 405, 659, 489, 242, 52, 203, 263, 126, 424, 657, 83, 53, 172, 394, 416, 535, 257, 63, 517, 141, 656, 573, 315, 438, 182, 156, 496, 503, 21, 214, 108, 173, 76, 164, 241, 422, 474, 261, 35, 404, 111, 282, 205, 522 ] }, "852": { "types": ["Fighting", "???"], "name": "Clobbopus", "stats": [50, 68, 60, 50, 50, 32], "abilities": [ "Limber", "Technician" ], "tier": "SM LC", "height": 0.6, "weight": 4, "moves": [ 213, 20, 34, 280, 362, 339, 509, 370, 197, 291, 203, 263, 364, 411, 8, 43, 668, 5, 330, 341, 220, 371, 602, 182, 156, 514, 279, 179, 249, 496, 69, 214, 173, 487, 66, 164, 389, 276, 269, 127, 526 ] }, "853": { "types": ["Fighting", "???"], "name": "Grapploct", "stats": [80, 118, 90, 70, 80, 42], "abilities": [ "Limber", "Technician" ], "tier": "SM PU", "height": 1.6, "weight": 39, "moves": [ 213, 20, 34, 280, 362, 664, 339, 370, 197, 91, 291, 409, 203, 263, 364, 411, 416, 56, 63, 8, 43, 668, 5, 330, 341, 190, 736, 371, 182, 156, 514, 279, 179, 249, 496, 184, 214, 173, 666, 66, 164, 276, 57, 269, 606, 127, 250, 526, 509, 220, 602, 69, 487, 389 ] }, "854": { "types": ["Ghost", "???"], "name": "Sinistea", "stats": [40, 45, 45, 74, 54, 50], "abilities": [ "Weak Armor", "Cursed Body" ], "tier": "SM LC", "height": 0.1, "weight": 0.2, "moves": [ 502, 312, 583, 310, 226, 399, 203, 263, 492, 202, 506, 286, 72, 262, 118, 417, 371, 578, 182, 94, 473, 156, 496, 247, 504, 214, 173, 500, 164, 389, 271, 261, 110, 472 ] }, "66390": { "types": ["Ghost", "???"], "name": "Sinistea-Antique", "stats": [40, 45, 45, 74, 54, 50], "abilities": [ "Weak Armor", "Cursed Body" ], "tier": "SM LC", "height": 0.1, "weight": 0.2, "moves": [ 502, 312, 583, 310, 226, 399, 203, 263, 492, 202, 506, 286, 72, 262, 118, 417, 371, 578, 182, 94, 473, 156, 496, 247, 504, 214, 173, 500, 164, 389, 271, 261, 110, 472 ] }, "855": { "types": ["Ghost", "???"], "name": "Polteageist", "stats": [60, 65, 65, 134, 114, 70], "abilities": [ "Weak Armor", "Cursed Body" ], "tier": "SM LU", "height": 0.2, "weight": 0.4, "moves": [ 502, 312, 583, 310, 226, 174, 399, 203, 263, 492, 202, 416, 506, 63, 286, 113, 72, 262, 118, 417, 371, 578, 182, 94, 473, 115, 156, 496, 120, 247, 504, 214, 173, 500, 650, 164, 389, 735, 271, 261, 110, 472 ] }, "66391": { "types": ["Ghost", "???"], "name": "Polteageist-Antique", "stats": [60, 65, 65, 134, 114, 70], "abilities": [ "Weak Armor", "Cursed Body" ], "tier": "SM LU", "height": 0.2, "weight": 0.4, "moves": [ 502, 312, 583, 310, 226, 174, 399, 203, 263, 492, 202, 416, 506, 63, 286, 113, 72, 262, 118, 417, 371, 578, 182, 94, 473, 115, 156, 496, 120, 247, 504, 214, 173, 500, 650, 164, 389, 735, 271, 261, 110, 472 ] }, "856": { "types": ["Psychic", "???"], "name": "Hatenna", "stats": [42, 30, 45, 56, 53, 39], "abilities": [ "Healer", "Anticipation", "Magic Bounce" ], "tier": "SM LC", "height": 0.4, "weight": 3.4, "moves": [ 495, 312, 583, 213, 226, 347, 204, 93, 399, 563, 586, 564, 203, 263, 202, 361, 505, 270, 286, 722, 113, 345, 573, 575, 580, 579, 182, 60, 94, 473, 511, 156, 496, 219, 285, 214, 173, 500, 164, 86 ] }, "857": { "types": ["Psychic", "???"], "name": "Hattrem", "stats": [57, 40, 65, 86, 73, 49], "abilities": [ "Healer", "Anticipation", "Magic Bounce" ], "tier": "SM PU", "height": 0.6, "weight": 4.8, "moves": [ 312, 213, 226, 664, 347, 204, 93, 399, 563, 586, 564, 203, 263, 202, 361, 505, 270, 286, 722, 113, 345, 573, 580, 579, 182, 60, 94, 473, 156, 496, 219, 285, 214, 173, 500, 164, 86, 495, 583, 575, 511 ] }, "858": { "types": ["Psychic", "Fairy"], "name": "Hatterene", "stats": [57, 90, 95, 136, 103, 29], "abilities": [ "Healer", "Anticipation", "Magic Bounce" ], "tier": "SM UU", "height": 2.1, "weight": 5.1, "moves": [ 312, 213, 226, 664, 347, 204, 93, 399, 563, 586, 564, 203, 263, 248, 202, 416, 385, 361, 505, 270, 63, 286, 722, 113, 345, 733, 478, 573, 580, 579, 384, 438, 182, 60, 94, 427, 473, 156, 496, 219, 247, 421, 285, 214, 173, 500, 164, 14, 86, 433, 472, 495, 583, 575, 511 ] }, "859": { "types": ["Dark", "Fairy"], "name": "Impidimp", "stats": [45, 45, 30, 55, 40, 50], "abilities": [ "Prankster", "Frisk", "Pickpocket" ], "tier": "SM LC", "height": 0.4, "weight": 5.5, "moves": [ 372, 213, 44, 585, 399, 563, 564, 409, 203, 263, 252, 313, 260, 492, 141, 67, 25, 5, 118, 417, 579, 182, 156, 514, 279, 496, 214, 173, 164, 389, 207, 269, 168, 86, 259, 271, 253 ] }, "860": { "types": ["Dark", "Fairy"], "name": "Morgrem", "stats": [65, 60, 45, 75, 55, 70], "abilities": [ "Prankster", "Frisk", "Pickpocket" ], "tier": "SM PU", "height": 0.8, "weight": 12.5, "moves": [ 372, 213, 44, 585, 399, 563, 564, 409, 203, 263, 252, 313, 724, 260, 492, 141, 113, 67, 25, 5, 118, 417, 579, 182, 115, 156, 514, 279, 496, 421, 214, 173, 164, 389, 207, 269, 168, 634, 86, 259, 271, 253 ] }, "861": { "types": ["Dark", "Fairy"], "name": "Grimmsnarl", "stats": [95, 120, 65, 95, 75, 60], "abilities": [ "Prankster", "Frisk", "Pickpocket" ], "tier": "SM UU", "height": 1.5, "weight": 61, "moves": [ 372, 213, 44, 707, 34, 280, 339, 585, 242, 643, 399, 563, 564, 409, 203, 263, 252, 313, 724, 7, 260, 411, 116, 492, 416, 359, 63, 8, 141, 113, 67, 490, 25, 5, 118, 417, 579, 384, 602, 438, 182, 115, 156, 514, 279, 496, 184, 421, 214, 173, 720, 666, 164, 389, 276, 207, 269, 168, 634, 9, 86, 259, 271, 253, 472 ] }, "862": { "types": ["Dark", "Normal"], "name": "Obstagoon", "stats": [93, 90, 101, 60, 81, 95], "abilities": [ "Reckless", "Guts", "Defiant" ], "tier": "SM LU", "height": 1.6, "weight": 46, "moves": [ 600, 501, 282, 372, 213, 560, 59, 707, 34, 280, 339, 370, 68, 238, 440, 91, 38, 203, 263, 313, 7, 374, 116, 154, 416, 447, 441, 29, 270, 468, 63, 304, 58, 8, 196, 334, 231, 43, 122, 67, 25, 5, 341, 400, 723, 371, 42, 182, 240, 156, 514, 279, 179, 496, 28, 184, 103, 402, 247, 421, 214, 555, 173, 666, 66, 164, 241, 57, 129, 415, 33, 36, 269, 168, 634, 87, 85, 9, 86, 271, 250, 526, 404, 541 ] }, "863": { "types": ["Steel", "???"], "name": "Perrserker", "stats": [70, 110, 100, 50, 60, 50], "abilities": [ "Battle Armor", "Tough Claws", "Steely Spirit" ], "tier": "SM PU", "height": 0.8, "weight": 28, "moves": [ 180, 38, 175, 400, 343, 133, 372, 213, 226, 34, 370, 242, 399, 91, 203, 263, 252, 374, 492, 154, 416, 45, 441, 360, 484, 468, 63, 304, 334, 442, 231, 368, 232, 319, 417, 371, 6, 579, 182, 240, 156, 514, 496, 10, 103, 402, 247, 421, 163, 214, 173, 727, 164, 241, 207, 14, 269, 168, 37, 634, 87, 85, 253, 369, 526, 174 ] }, "864": { "types": ["Ghost", "???"], "name": "Cursola", "stats": [60, 95, 50, 145, 130, 30], "abilities": [ "Weak Armor", "Perish Body" ], "tier": "SM PU", "height": 1, "weight": 0.4, "moves": [ 114, 109, 267, 194, 352, 457, 133, 246, 310, 213, 59, 34, 362, 523, 347, 174, 91, 50, 414, 89, 203, 263, 202, 416, 288, 258, 106, 506, 56, 63, 58, 333, 196, 334, 141, 113, 668, 243, 101, 195, 42, 408, 182, 94, 240, 115, 156, 279, 350, 157, 317, 496, 219, 201, 503, 103, 120, 247, 214, 173, 180, 446, 666, 444, 650, 164, 241, 57, 33, 634, 250, 261, 722, 55 ] }, "865": { "types": ["Fighting", "???"], "name": "Sirfetch'd", "stats": [62, 135, 95, 68, 82, 65], "abilities": [ "Steadfast", "Scrappy" ], "tier": "SM NU", "height": 0.8, "weight": 117, "moves": [ 68, 98, 175, 501, 174, 343, 400, 493, 38, 364, 143, 372, 213, 34, 413, 280, 664, 370, 432, 197, 203, 263, 515, 646, 116, 210, 270, 334, 282, 348, 43, 725, 64, 398, 182, 156, 514, 279, 249, 496, 28, 21, 214, 173, 630, 211, 164, 241, 276, 14, 634, 526 ] }, "866": { "types": ["Ice", "Psychic"], "name": "Mr. Rime", "stats": [80, 85, 75, 110, 100, 70], "abilities": [ "Tangled Feet", "Screen Cleaner", "Ice Body" ], "tier": "SM PU", "height": 1.5, "weight": 58.2, "moves": [ 252, 109, 471, 321, 495, 502, 213, 419, 226, 59, 335, 34, 280, 347, 204, 93, 383, 563, 24, 409, 227, 203, 412, 263, 313, 374, 411, 492, 568, 248, 416, 447, 385, 258, 270, 63, 95, 58, 8, 420, 333, 196, 334, 113, 478, 25, 5, 118, 102, 243, 599, 417, 371, 1, 384, 182, 60, 94, 633, 473, 240, 229, 278, 115, 156, 272, 496, 219, 103, 247, 285, 303, 214, 173, 76, 666, 500, 164, 389, 241, 269, 298, 168, 87, 85, 86, 271, 433, 253, 472, 428 ] }, "867": { "types": ["Ground", "Ghost"], "name": "Runerigus", "stats": [58, 95, 145, 50, 105, 30], "abilities": [ "Wandering Spirit" ], "tier": "SM PU", "height": 1.6, "weight": 66.6, "moves": [ 262, 502, 133, 310, 213, 707, 664, 523, 347, 562, 174, 399, 194, 50, 406, 414, 89, 203, 412, 263, 313, 416, 447, 470, 385, 114, 506, 63, 286, 334, 212, 417, 101, 371, 578, 471, 384, 182, 94, 240, 156, 279, 350, 157, 317, 496, 219, 201, 328, 184, 247, 421, 285, 21, 214, 173, 446, 444, 164, 269, 168, 390, 271, 433, 261, 472, 428 ] }, "868": { "types": ["Fairy", "???"], "name": "Milcery", "stats": [45, 40, 40, 50, 61, 34], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM LC", "height": 0.2, "weight": 0.3, "moves": [ 609, 151, 312, 583, 213, 560, 204, 563, 564, 203, 494, 263, 374, 270, 387, 599, 182, 105, 156, 496, 214, 173, 500, 164, 186, 230, 33 ] }, "869": { "types": ["Fairy", "???"], "name": "Alcremie", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5, "moves": [ 609, 151, 312, 583, 213, 347, 204, 563, 708, 564, 409, 227, 203, 412, 494, 263, 313, 374, 202, 416, 270, 63, 286, 113, 345, 478, 118, 599, 573, 579, 182, 94, 473, 105, 156, 496, 219, 214, 173, 76, 500, 164, 186, 230, 33, 161, 472, 560, 387 ] }, "66405": { "types": ["Fairy", "???"], "name": "Alcremie-Vanilla Cream Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "131941": { "types": ["Fairy", "???"], "name": "Alcremie-Vanilla Cream Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "197477": { "types": ["Fairy", "???"], "name": "Alcremie-Vanilla Cream Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "263013": { "types": ["Fairy", "???"], "name": "Alcremie-Vanilla Cream Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "328549": { "types": ["Fairy", "???"], "name": "Alcremie-Vanilla Cream Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "394085": { "types": ["Fairy", "???"], "name": "Alcremie-Vanilla Cream Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "459621": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "525157": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "590693": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "656229": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "721765": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "787301": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "852837": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Cream Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "918373": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "983909": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1049445": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1114981": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1180517": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1246053": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1311589": { "types": ["Fairy", "???"], "name": "Alcremie-Matcha Cream Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1377125": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1442661": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1508197": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1573733": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1639269": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1704805": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1770341": { "types": ["Fairy", "???"], "name": "Alcremie-Mint Cream Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1835877": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1901413": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "1966949": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2032485": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2098021": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2163557": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2229093": { "types": ["Fairy", "???"], "name": "Alcremie-Lemon Cream Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2294629": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2360165": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2425701": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2491237": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2556773": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2622309": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2687845": { "types": ["Fairy", "???"], "name": "Alcremie-Salted Cream Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2753381": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2818917": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2884453": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "2949989": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3015525": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3081061": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3146597": { "types": ["Fairy", "???"], "name": "Alcremie-Ruby Swirl Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3212133": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3277669": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3343205": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3408741": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3474277": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3539813": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3605349": { "types": ["Fairy", "???"], "name": "Alcremie-Caramel Swirl Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3670885": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Strawberry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3736421": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Berry", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3801957": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Love", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3867493": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Star", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3933029": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Clover", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "3998565": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Flower", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "4064101": { "types": ["Fairy", "???"], "name": "Alcremie-Rainbow Swirl Ribbon", "stats": [65, 60, 75, 110, 121, 64], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM PU", "height": 0.3, "weight": 0.5 }, "4129637": { "types": ["Fairy", "???"], "name": "Alcremie-Finalé", "stats": [130, 65, 85, 115, 127, 58], "abilities": [ "Sweet Veil", "Aroma Veil" ], "tier": "SM Ubers", "height": 69.1, "weight": 420.7 }, "870": { "types": ["Fighting", "???"], "name": "Falinks", "stats": [65, 100, 100, 70, 60, 75], "abilities": [ "Battle Armor", "Defiant" ], "tier": "SM PU", "height": 3, "weight": 62, "moves": [ 97, 372, 251, 280, 339, 370, 68, 203, 263, 206, 646, 411, 116, 416, 29, 270, 63, 334, 442, 224, 731, 371, 398, 182, 156, 514, 279, 179, 157, 249, 317, 496, 103, 214, 661, 173, 164, 276, 14, 33, 634, 428 ] }, "871": { "types": ["Electric", "???"], "name": "Pincurchin", "stats": [48, 101, 95, 91, 85, 15], "abilities": [ "Lightning Rod", "Electric Surge" ], "tier": "SM PU", "height": 0.3, "weight": 1, "moves": [ 367, 372, 213, 362, 61, 268, 174, 435, 588, 203, 263, 31, 506, 56, 668, 262, 330, 371, 64, 42, 398, 182, 240, 105, 156, 179, 496, 503, 120, 214, 173, 209, 191, 164, 389, 57, 634, 87, 85, 84, 86, 390, 607, 474, 55, 672 ] }, "872": { "types": ["Ice", "Bug"], "name": "Snom", "stats": [30, 25, 35, 45, 30, 20], "abilities": [ "Shield Dust", "Ice Scales" ], "tier": "SM LC", "height": 0.3, "weight": 3.8, "moves": [ 213, 450, 405, 203, 263, 565, 333, 196, 243, 181, 182, 156, 496, 214, 173, 522, 164 ] }, "873": { "types": ["Ice", "Bug"], "name": "Frosmoth", "stats": [70, 65, 60, 125, 90, 65], "abilities": [ "Shield Dust", "Ice Scales" ], "tier": "SM PU", "height": 1.3, "weight": 42, "moves": [ 512, 403, 213, 62, 635, 419, 59, 405, 347, 563, 432, 203, 263, 297, 202, 416, 258, 270, 542, 63, 58, 333, 196, 286, 571, 141, 113, 54, 579, 181, 182, 483, 115, 156, 496, 219, 214, 173, 522, 78, 164, 366, 369, 311, 469, 450, 565, 243 ] }, "874": { "types": ["Rock", "???"], "name": "Stonjourner", "stats": [100, 125, 135, 20, 20, 70], "abilities": [ "Power Spot" ], "tier": "SM PU", "height": 2.5, "weight": 520, "moves": [ 246, 372, 213, 335, 707, 34, 664, 523, 174, 414, 89, 203, 263, 416, 356, 535, 484, 63, 286, 334, 25, 182, 156, 350, 397, 157, 88, 317, 496, 219, 201, 328, 120, 214, 173, 446, 23, 666, 444, 164, 276, 469, 472 ] }, "875": { "types": ["Ice", "???"], "name": "Eiscue", "stats": [75, 80, 110, 65, 90, 50], "abilities": [ "Ice Face" ], "tier": "SM PU", "height": 1.4, "weight": 89, "moves": [ 97, 133, 392, 213, 635, 419, 187, 59, 362, 291, 38, 203, 263, 568, 258, 29, 457, 56, 58, 8, 556, 333, 196, 334, 442, 668, 54, 181, 182, 115, 156, 179, 496, 214, 173, 487, 164, 57, 33, 127, 311, 250, 428 ] }, "66411": { "types": ["Ice", "???"], "name": "Eiscue-Noice", "stats": [75, 80, 70, 65, 50, 130], "abilities": [ "Ice Face" ], "tier": "SM PU", "height": 1.4, "weight": 89 }, "876": { "types": ["Psychic", "Normal"], "name": "Indeedee", "stats": [60, 65, 55, 105, 95, 95], "abilities": [ "Inner Focus", "Synchronize", "Psychic Surge" ], "tier": "SM LU", "height": 0.9, "weight": 28, "moves": [ 495, 502, 312, 213, 347, 563, 586, 564, 409, 227, 203, 412, 326, 263, 252, 248, 270, 304, 286, 387, 345, 478, 118, 573, 6, 580, 579, 471, 384, 182, 60, 94, 633, 244, 473, 156, 496, 247, 214, 173, 500, 164, 129, 161, 271, 433, 472, 428 ] }, "66412": { "types": ["Psychic", "Normal"], "name": "Indeedee-F", "stats": [70, 55, 65, 95, 105, 85], "abilities": [ "Own Tempo", "Synchronize", "Psychic Surge" ], "tier": "SM NU", "height": 0.9, "weight": 28, "moves": [ 502, 312, 213, 226, 347, 563, 586, 564, 409, 203, 412, 263, 252, 266, 248, 470, 385, 361, 505, 270, 304, 286, 113, 345, 118, 573, 6, 580, 579, 182, 60, 94, 633, 375, 244, 473, 115, 156, 496, 219, 247, 214, 173, 500, 164, 129, 271, 428 ] }, "877": { "types": ["Electric", "Dark"], "name": "Morpeko", "stats": [58, 95, 58, 70, 58, 97], "abilities": [ "Hunger Switch" ], "tier": "SM PU", "height": 0.3, "weight": 3, "moves": [ 97, 372, 213, 714, 44, 280, 331, 268, 242, 399, 588, 486, 527, 203, 263, 252, 313, 424, 260, 374, 492, 423, 43, 417, 200, 600, 371, 658, 182, 665, 511, 98, 229, 156, 279, 496, 184, 402, 214, 555, 173, 209, 666, 164, 162, 207, 129, 39, 269, 168, 37, 87, 85, 422, 9, 84, 86, 321, 259, 253, 521, 528 ] }, "66413": { "types": ["Electric", "Dark"], "name": "Morpeko-Hangry", "stats": [58, 95, 58, 70, 58, 97], "abilities": [ "Hunger Switch" ], "tier": "SM PU", "height": 0.3, "weight": 3 }, "878": { "types": ["Steel", "???"], "name": "Cufant", "stats": [72, 80, 49, 40, 49, 40], "abilities": [ "Sheer Force", "Heavy Metal" ], "tier": "SM LC", "height": 1.2, "weight": 100, "moves": [ 213, 584, 707, 34, 280, 664, 523, 174, 111, 91, 38, 414, 203, 263, 90, 374, 45, 624, 334, 442, 25, 341, 579, 438, 182, 156, 350, 157, 249, 317, 205, 496, 103, 21, 214, 173, 446, 727, 23, 666, 70, 164, 276, 207, 33, 18, 526, 428 ] }, "879": { "types": ["Steel", "???"], "name": "Copperajah", "stats": [122, 130, 69, 80, 69, 30], "abilities": [ "Sheer Force", "Heavy Metal" ], "tier": "SM NU", "height": 3, "weight": 650, "moves": [ 213, 707, 34, 280, 664, 523, 91, 414, 89, 203, 263, 430, 374, 416, 45, 535, 484, 624, 63, 334, 442, 25, 341, 200, 371, 579, 438, 182, 156, 279, 350, 157, 249, 317, 205, 496, 184, 103, 214, 555, 173, 446, 727, 23, 666, 444, 70, 164, 276, 33, 269, 526, 428, 584, 174, 111, 38, 90, 21, 207, 18 ] }, "880": { "types": ["Electric", "Dragon"], "name": "Dracozolt", "stats": [90, 100, 90, 80, 70, 75], "abilities": [ "Volt Absorb", "Hustle", "Sand Rush" ], "tier": "SM OU", "height": 1.8, "weight": 190, "moves": [ 332, 246, 34, 737, 715, 664, 523, 268, 435, 434, 337, 406, 407, 525, 414, 89, 486, 203, 263, 126, 424, 83, 53, 416, 624, 63, 231, 67, 25, 5, 200, 365, 182, 240, 156, 350, 157, 317, 496, 21, 214, 173, 23, 666, 444, 164, 241, 33, 269, 87, 85, 422, 9, 84, 86, 528 ] }, "881": { "types": ["Electric", "Ice"], "name": "Arctozolt", "stats": [90, 100, 90, 90, 80, 55], "abilities": [ "Volt Absorb", "Static", "Slush Rush" ], "tier": "SM LU", "height": 2.3, "weight": 215, "moves": [ 246, 419, 59, 34, 737, 523, 268, 435, 497, 486, 203, 263, 568, 416, 258, 56, 63, 304, 58, 423, 556, 333, 196, 231, 67, 25, 5, 371, 365, 181, 182, 240, 156, 350, 157, 317, 496, 21, 214, 173, 666, 444, 164, 57, 269, 87, 85, 422, 9, 84, 86, 528 ] }, "882": { "types": ["Water", "Dragon"], "name": "Dracovish", "stats": [90, 90, 100, 70, 80, 75], "abilities": [ "Water Absorb", "Strong Jaw", "Sand Rush" ], "tier": "SM Ubers", "height": 2.3, "weight": 215, "moves": [ 246, 44, 34, 362, 664, 523, 242, 291, 434, 225, 406, 407, 414, 89, 203, 263, 738, 416, 56, 63, 423, 442, 141, 668, 67, 25, 200, 182, 665, 240, 156, 350, 157, 317, 496, 503, 214, 173, 23, 666, 444, 164, 162, 57, 33, 127, 55, 250, 428 ] }, "883": { "types": ["Water", "Ice"], "name": "Arctovish", "stats": [90, 90, 100, 80, 90, 55], "abilities": [ "Water Absorb", "Ice Body", "Slush Rush" ], "tier": "SM PU", "height": 2, "weight": 175, "moves": [ 246, 635, 419, 44, 59, 34, 362, 242, 291, 203, 263, 738, 568, 416, 258, 56, 63, 58, 423, 556, 333, 196, 334, 442, 668, 181, 182, 665, 240, 156, 350, 157, 317, 496, 214, 173, 444, 164, 162, 57, 127, 55, 250, 428 ] }, "884": { "types": ["Steel", "Dragon"], "name": "Duraludon", "stats": [70, 95, 115, 120, 50, 85], "abilities": [ "Light Metal", "Heavy Metal", "Stalwart" ], "tier": "SM NU", "height": 1.8, "weight": 40, "moves": [ 213, 707, 34, 715, 280, 399, 434, 337, 406, 525, 203, 263, 430, 492, 416, 360, 484, 468, 63, 334, 442, 636, 43, 113, 368, 232, 319, 243, 400, 200, 182, 115, 156, 157, 249, 317, 496, 184, 103, 163, 214, 555, 173, 76, 446, 727, 666, 444, 164, 14, 87, 85, 86 ] }, "885": { "types": ["Dragon", "Ghost"], "name": "Dreepy", "stats": [28, 60, 30, 40, 30, 82], "abilities": [ "Clear Body", "Infiltrator", "Cursed Body" ], "tier": "SM LC", "height": 0.5, "weight": 2, "moves": [ 310, 213, 226, 44, 109, 174, 50, 104, 434, 525, 203, 263, 288, 270, 571, 182, 98, 156, 496, 214, 173, 164, 389, 129, 86 ] }, "886": { "types": ["Dragon", "Ghost"], "name": "Drakloak", "stats": [68, 80, 50, 60, 50, 102], "abilities": [ "Clear Body", "Infiltrator", "Cursed Body" ], "tier": "SM PU", "height": 1.4, "weight": 11, "moves": [ 512, 97, 502, 372, 310, 213, 226, 251, 44, 715, 362, 291, 38, 458, 434, 349, 406, 407, 203, 263, 126, 53, 270, 506, 56, 571, 387, 199, 200, 578, 182, 665, 98, 156, 496, 503, 247, 214, 173, 211, 164, 57, 129, 36, 168, 87, 85, 86, 369, 261, 109, 174, 50, 104, 525, 288, 389 ] }, "887": { "types": ["Dragon", "Ghost"], "name": "Dragapult", "stats": [88, 120, 75, 100, 75, 142], "abilities": [ "Clear Body", "Infiltrator", "Cursed Body" ], "tier": "SM OU", "height": 3, "weight": 50, "moves": [ 512, 97, 502, 372, 310, 213, 226, 251, 44, 34, 715, 362, 291, 38, 458, 434, 225, 337, 349, 734, 406, 407, 203, 263, 126, 53, 19, 416, 270, 506, 56, 63, 571, 387, 113, 199, 200, 578, 182, 665, 98, 115, 156, 496, 503, 247, 214, 173, 76, 211, 164, 389, 57, 129, 36, 168, 87, 85, 86, 161, 369, 261, 109, 174, 50, 104, 525, 288 ] }, "888": { "types": ["Fairy", "???"], "name": "Zacian", "stats": [92, 120, 115, 80, 115, 138], "abilities": [ "Intrepid Sword" ], "tier": "SM Ubers", "height": 2.8, "weight": 110, "moves": [ 727, 97, 403, 372, 44, 280, 664, 370, 242, 91, 203, 263, 206, 424, 411, 116, 416, 270, 336, 63, 304, 423, 286, 442, 231, 636, 232, 572, 579, 182, 665, 427, 98, 501, 156, 514, 279, 179, 496, 533, 184, 163, 214, 555, 173, 630, 164, 129, 14, 541, 422, 528, 526 ] }, "66424": { "types": ["Fairy", "Steel"], "name": "Zacian-Crowned Sword", "stats": [92, 150, 115, 80, 115, 148], "abilities": [ "Intrepid Sword" ], "tier": "SM Ubers", "height": 2.8, "weight": 355, "moves": [ 727, 97, 403, 372, 712, 44, 280, 664, 370, 242, 91, 203, 263, 206, 424, 411, 116, 416, 270, 336, 63, 304, 423, 286, 231, 636, 232, 572, 579, 182, 665, 427, 98, 501, 156, 514, 279, 179, 496, 533, 184, 163, 214, 555, 173, 630, 164, 129, 14, 541, 422, 528, 526 ] }, "889": { "types": ["Fighting", "???"], "name": "Zamazenta", "stats": [92, 120, 115, 80, 115, 138], "abilities": [ "Dauntless Shield" ], "tier": "SM Ubers", "height": 2.9, "weight": 210, "moves": [ 727, 97, 44, 370, 242, 563, 91, 203, 263, 424, 430, 411, 116, 416, 385, 270, 336, 63, 304, 423, 286, 334, 442, 231, 636, 113, 368, 232, 572, 371, 579, 384, 182, 665, 98, 115, 156, 514, 279, 179, 496, 219, 184, 163, 214, 555, 173, 76, 164, 129, 541, 422, 469, 528, 526 ] }, "66425": { "types": ["Fighting", "Steel"], "name": "Zamazenta-Crowned Shield", "stats": [92, 120, 140, 80, 140, 128], "abilities": [ "Dauntless Shield" ], "tier": "SM Ubers", "height": 2.9, "weight": 785, "moves": [ 727, 97, 713, 44, 370, 242, 563, 91, 203, 263, 424, 430, 411, 116, 416, 385, 270, 336, 63, 304, 423, 286, 334, 231, 636, 113, 368, 232, 572, 371, 579, 384, 182, 665, 98, 115, 156, 514, 279, 179, 496, 219, 184, 163, 214, 555, 173, 76, 164, 129, 541, 422, 469, 528, 526 ] }, "890": { "types": ["Poison", "Dragon"], "name": "Eternatus", "stats": [140, 85, 95, 145, 95, 130], "abilities": [ "Pressure" ], "tier": "SM Ubers", "height": 20, "weight": 950, "moves": [ 97, 372, 664, 109, 322, 440, 434, 349, 406, 525, 728, 203, 726, 263, 53, 430, 19, 416, 63, 113, 573, 371, 398, 342, 182, 105, 115, 156, 496, 184, 103, 247, 214, 188, 482, 173, 76, 164, 92, 390, 607, 474 ] }, "66426": { "types": ["Poison", "Dragon"], "name": "Eternatus-Eternamax", "stats": [255, 115, 250, 125, 250, 130], "abilities": [ "Pressure" ], "tier": "SM Ubers", "height": 100, "weight": 0 }, "131564": { "types": ["Grass", "???"], "name": "Shaymin-Lilac", "stats": [100, 100, 100, 100, 100, 100], "abilities": [ "Natural Cure" ], "tier": "SM OU", "height": 0.2, "weight": 2.1 }, "197100": { "types": ["Grass", "???"], "name": "Shaymin-Sunflower", "stats": [100, 100, 100, 100, 100, 100], "abilities": [ "Natural Cure" ], "tier": "SM OU", "height": 0.2, "weight": 2.1 }, "262636": { "types": ["Grass", "???"], "name": "Shaymin-Felicia", "stats": [100, 100, 100, 100, 100, 100], "abilities": [ "Natural Cure" ], "tier": "SM OU", "height": 0.2, "weight": 2.1 }, "65615": { "types": ["Psychic", "???"], "name": "Galarian Slowpoke", "stats": [90, 65, 65, 40, 40, 15], "abilities": [ "Gluttony", "Own Tempo" ], "tier": "SM LC", "height": 1.2, "weight": 36, "moves": [movenum("Tackle"), movenum("Curse"), movenum("Growl"), movenum("Acid"), movenum("Yawn"), movenum("Confusion"), movenum("Disable"), movenum("Water Pulse"), movenum("Headbutt"), movenum("Zen Headbutt"), movenum("Amnesia"), movenum("Surf"), movenum("Slack Off"), movenum("Psychic"), movenum("Psych Up"), movenum("Rain Dance"), movenum("Heal Pulse"), movenum("Pay Day"), movenum("Thunder Wave"), movenum("Dig"), movenum("Light Screen"), movenum("Safeguard"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Icy Wind"), movenum("Attract"), movenum("Sunny Day"), movenum("Hail"), movenum("Whirlpool"), movenum("Facade"), movenum("Swift"), movenum("Imprison"), movenum("Dive"), movenum("Weather Ball"), movenum("Mud Shot"), movenum("Brine"), movenum("Trick Room"), movenum("Wonder Room"), movenum("Round"), movenum("Bulldoze"), movenum("Psychic Terrain"), movenum("Body Slam"), movenum("Flamethrower"), movenum("Hydro Pump"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Earthquake"), movenum("Fire Blast"), movenum("Tri Attack"), movenum("Substitute"), movenum("Psyshock"), movenum("Endure"), movenum("Sleep Talk"), movenum("Iron Tail"), movenum("Shadow Ball"), movenum("Future Sight"), movenum("Trick"), movenum("Skill Swap"), movenum("Calm Mind"), movenum("Grass Knot"), movenum("Foul Play"), movenum("Stored Power"), movenum("Scald"), movenum("Liquidation"), movenum("Belch"), movenum("Belly Drum"), movenum("Block"), movenum("Stomp")] }, "131152": { "types": ["Poison", "Psychic"], "name": "Galarian Slowbro", "stats": [95, 100, 95, 100, 70, 30], "abilities": [ "Quick Draw", "Own Tempo" ], "tier": "SM LU", "height": 1.6, "weight": 70.5, "moves": [movenum("Shell Side Arm"), movenum("Withdraw"), movenum("Tackle"), movenum("Curse"), movenum("Growl"), movenum("Acid"), movenum("Yawn"), movenum("Confusion"), movenum("Disable"), movenum("Water Pulse"), movenum("Headbutt"), movenum("Zen Headbutt"), movenum("Amnesia"), movenum("Surf"), movenum("Slack Off"), movenum("Psychic"), movenum("Psych Up"), movenum("Rain Dance"), movenum("Heal Pulse"), movenum("Mega Punch"), movenum("Mega Kick"), movenum("Pay Day"), movenum("Ice Punch"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Thunder Wave"), movenum("Dig"), movenum("Light Screen"), movenum("Safeguard"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Icy Wind"), movenum("Attract"), movenum("Sunny Day"), movenum("Hail"), movenum("Whirlpool"), movenum("Facade"), movenum("Swift"), movenum("Brick Break"), movenum("Imprison"), movenum("Dive"), movenum("Weather Ball"), movenum("Mud Shot"), movenum("Brine"), movenum("Fling"), movenum("Drain Punch"), movenum("Avalanche"), movenum("Trick Room"), movenum("Wonder Room"), movenum("Venoshock"), movenum("Round"), movenum("Bulldoze"), movenum("Razor Shell"), movenum("Psychic Terrain"), movenum("Brutal Swing"), movenum("Body Slam"), movenum("Flamethrower"), movenum("Hydro Pump"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Earthquake"), movenum("Fire Blast"), movenum("Tri Attack"), movenum("Substitute"), movenum("Sludge Bomb"), movenum("Psyshock"), movenum("Endure"), movenum("Sleep Talk"), movenum("Iron Tail"), movenum("Shadow Ball"), movenum("Future Sight"), movenum("Trick"), movenum("Skill Swap"), movenum("Muddy Water"), movenum("Iron Defense"), movenum("Calm Mind"), movenum("Poison Jab"), movenum("Focus Blast"), movenum("Nasty Plot"), movenum("Grass Knot"), movenum("Sludge Wave"), movenum("Foul Play"), movenum("Stored Power"), movenum("Scald"), movenum("Liquidation"), movenum("Belch"), movenum("Belly Drum"), movenum("Block"), movenum("Stomp")] }, "891": { "types": ["Fighting", "???"], "name": "Kubfu", "stats": [60, 90, 60, 53, 50, 72], "abilities": [ "Inner Focus", "No Guard" ], "tier": "SM PU", "height": 0.6, "weight": 12, "moves": [movenum("Rock Smash"), movenum("Leer"), movenum("Endure"), movenum("Focus Energy"), movenum("Aerial Ace"), movenum("Scary Face"), movenum("Headbutt"), movenum("Brick Break"), movenum("Detect"), movenum("Bulk Up"), movenum("Iron Head"), movenum("Dynamic Punch"), movenum("Counter"), movenum("Close Combat"), movenum("Focus Punch"), movenum("Mega Punch"), movenum("Mega Kick"), movenum("Fire Punch"), movenum("Ice Punch"), movenum("Thunder Punch"), movenum("Dig"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Attract"), movenum("Facade"), movenum("Helping Hand"), movenum("Revenge"), movenum("U-turn"), movenum("Low Sweep"), movenum("Round"), movenum("Acrobatics"), movenum("Retaliate"), movenum("Body Slam"), movenum("Low Kick"), movenum("Substitute"), movenum("Reversal"), movenum("Sleep Talk"), movenum("Superpower"), movenum("Zen Headbutt"), movenum("Work Up")] }, "892": { "types": ["Fighting", "Dark"], "name": "Urshifu", "stats": [100, 130, 100, 63, 60, 97], "abilities": [ "Unseen Fist" ], "tier": "SM Ubers", "height": 1.9, "weight": 105, "moves": [movenum("Wicked Blow"), movenum("Sucker Punch"), movenum("Rock Smash"), movenum("Leer"), movenum("Endure"), movenum("Focus Energy"), movenum("Aerial Ace"), movenum("Scary Face"), movenum("Headbutt"), movenum("Brick Break"), movenum("Detect"), movenum("Bulk Up"), movenum("Iron Head"), movenum("Dynamic Punch"), movenum("Counter"), movenum("Close Combat"), movenum("Focus Punch"), movenum("Mega Punch"), movenum("Mega Kick"), movenum("Fire Punch"), movenum("Ice Punch"), movenum("Thunder Punch"), movenum("Giga Impact"), movenum("Dig"), movenum("Rest"), movenum("Rock Slide"), movenum("Snore"), movenum("Protect"), movenum("Attract"), movenum("Beat Up"), movenum("Facade"), movenum("Swift"), movenum("Helping Hand"), movenum("Revenge"), movenum("Rock Tomb"), movenum("U-turn"), movenum("Payback"), movenum("Assurance"), movenum("Fling"), movenum("Drain Punch"), movenum("Low Sweep"), movenum("Round"), movenum("Acrobatics"), movenum("Retaliate"), movenum("Snarl"), movenum("False Swipe"), movenum("Body Slam"), movenum("Low Kick"), movenum("Substitute"), movenum("Reversal"), movenum("Sleep Talk"), movenum("Crunch"), movenum("Taunt"), movenum("Superpower"), movenum("Iron Defense"), movenum("Aura Sphere"), movenum("Poison Jab"), movenum("Dark Pulse"), movenum("Focus Blast"), movenum("Zen Headbutt"), movenum("Stone Edge"), movenum("Foul Play"), movenum("Work Up"), movenum("Darkest Lariat"), movenum("Throat Chop"), movenum("Body Press")] }, "66428": { "types": ["Fighting", "Water"], "name": "Urshifu-Rapid Strike", "stats": [100, 130, 100, 63, 60, 97], "abilities": [ "Unseen Fist" ], "tier": "SM OU", "height": 1.9, "weight": 105, "moves": [movenum("Rock Smash"), movenum("Leer"), movenum("Endure"), movenum("Focus Energy"), movenum("Aerial Ace"), movenum("Scary Face"), movenum("Headbutt"), movenum("Brick Break"), movenum("Detect"), movenum("Bulk Up"), movenum("Iron Head"), movenum("Dynamic Punch"), movenum("Counter"), movenum("Close Combat"), movenum("Focus Punch"), movenum("Surging Strikes"), movenum("Aqua Jet"), movenum("Mega Punch"), movenum("Mega Kick"), movenum("Fire Punch"), movenum("Ice Punch"), movenum("Thunder Punch"), movenum("Giga Impact"), movenum("Dig"), movenum("Rest"), movenum("Rock Slide"), movenum("Snore"), movenum("Protect"), movenum("Attract"), movenum("Rain Dance"), movenum("Whirlpool"), movenum("Facade"), movenum("Swift"), movenum("Helping Hand"), movenum("Revenge"), movenum("Dive"), movenum("Rock Tomb"), movenum("Brine"), movenum("U-turn"), movenum("Drain Punch"), movenum("Low Sweep"), movenum("Round"), movenum("Acrobatics"), movenum("Retaliate"), movenum("False Swipe"), movenum("Body Slam"), movenum("Low Kick"), movenum("Waterfall"), movenum("Substitute"), movenum("Reversal"), movenum("Sleep Talk"), movenum("Taunt"), movenum("Superpower"), movenum("Iron Defense"), movenum("Aura Sphere"), movenum("Poison Jab"), movenum("Focus Blast"), movenum("Zen Headbutt"), movenum("Stone Edge"), movenum("Scald"), movenum("Work Up"), movenum("Liquidation"), movenum("Body Press")] }, "893": { "types": ["Dark", "Grass"], "name": "Zarude", "stats": [105, 120, 105, 70, 95, 105], "abilities": [ "Leaf Guard" ], "tier": "SM LU", "height": 1.8, "weight": 70, "moves": [movenum("Bind"), movenum("Scratch"), movenum("Leer"), movenum("Vine Whip"), movenum("Growth"), movenum("Fury Swipes"), movenum("Scary Face"), movenum("Grass Knot"), movenum("Bite"), movenum("U-turn"), movenum("Swagger"), movenum("Energy Ball"), movenum("Synthesis"), movenum("Hammer Arm"), movenum("Thrash"), movenum("Power Whip"), movenum("Jungle Healing"), movenum("Mega Punch"), movenum("Mega Kick"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Magical Leaf"), movenum("Solar Beam"), movenum("Solar Blade"), movenum("Dig"), movenum("Rest"), movenum("Rock Slide"), movenum("Thief"), movenum("Snore"), movenum("Protect"), movenum("Giga Drain"), movenum("Sunny Day"), movenum("Facade"), movenum("Swift"), movenum("Revenge"), movenum("Brick Break"), movenum("Rock Tomb"), movenum("Bullet Seed"), movenum("Mud Shot"), movenum("Payback"), movenum("Assurance"), movenum("Fling"), movenum("Drain Punch"), movenum("Round"), movenum("Acrobatics"), movenum("Snarl"), movenum("Grassy Terrain"), movenum("Brutal Swing"), movenum("Stomping Tantrum"), movenum("Body Slam"), movenum("Low Kick"), movenum("Substitute"), movenum("Endure"), movenum("Sleep Talk"), movenum("Encore"), movenum("Iron Tail"), movenum("Crunch"), movenum("Taunt"), movenum("Superpower"), movenum("Hyper Voice"), movenum("Bulk Up"), movenum("Close Combat"), movenum("Dark Pulse"), movenum("Seed Bomb"), movenum("Nasty Plot"), movenum("Darkest Lariat"), movenum("Throat Chop")] }, "66429": { "types": ["Dark", "Grass"], "name": "Zarude-Dada", "stats": [105, 120, 105, 70, 95, 105], "abilities": [ "Leaf Guard" ], "tier": "SM LU", "height": 1.8, "weight": 70 }, "65735": { "types": ["Poison", "Psychic"], "name": "Galarian Slowking", "stats": [95, 65, 80, 110, 110, 30], "abilities": [ "Curious Medicine", "Own Tempo" ], "tier": "SM OU", "height": 1.8, "weight": 79.5, "moves": [movenum("Eerie Spell"), movenum("Power Gem"), movenum("Nasty Plot"), movenum("Swagger"), movenum("Tackle"), movenum("Curse"), movenum("Growl"), movenum("Acid"), movenum("Yawn"), movenum("Confusion"), movenum("Disable"), movenum("Water Pulse"), movenum("Headbutt"), movenum("Zen Headbutt"), movenum("Amnesia"), movenum("Surf"), movenum("Slack Off"), movenum("Psychic"), movenum("Psych Up"), movenum("Rain Dance"), movenum("Heal Pulse"), movenum("Mega Punch"), movenum("Mega Kick"), movenum("Pay Day"), movenum("Ice Punch"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Thunder Wave"), movenum("Dig"), movenum("Light Screen"), movenum("Safeguard"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Icy Wind"), movenum("Attract"), movenum("Sunny Day"), movenum("Hail"), movenum("Whirlpool"), movenum("Facade"), movenum("Swift"), movenum("Brick Break"), movenum("Imprison"), movenum("Dive"), movenum("Weather Ball"), movenum("Mud Shot"), movenum("Brine"), movenum("Fling"), movenum("Drain Punch"), movenum("Avalanche"), movenum("Trick Room"), movenum("Wonder Room"), movenum("Venoshock"), movenum("Round"), movenum("Hex"), movenum("Bulldoze"), movenum("Razor Shell"), movenum("Psychic Terrain"), movenum("Body Slam"), movenum("Flamethrower"), movenum("Hydro Pump"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Earthquake"), movenum("Fire Blast"), movenum("Tri Attack"), movenum("Substitute"), movenum("Sludge Bomb"), movenum("Psyshock"), movenum("Endure"), movenum("Sleep Talk"), movenum("Iron Tail"), movenum("Shadow Ball"), movenum("Future Sight"), movenum("Trick"), movenum("Skill Swap"), movenum("Muddy Water"), movenum("Iron Defense"), movenum("Calm Mind"), movenum("Focus Blast"), movenum("Grass Knot"), movenum("Sludge Wave"), movenum("Foul Play"), movenum("Stored Power"), movenum("Scald"), movenum("Venom Drench"), movenum("Liquidation"), movenum("Belch"), movenum("Belly Drum"), movenum("Block"), movenum("Stomp")] }, "65680": { "types": ["Psychic", "Flying"], "name": "Galarian Articuno", "stats": [90, 85, 85, 125, 100, 95], "abilities": [ "Competitive" ], "tier": "SM NU", "height": 1.7, "weight": 50.9, "moves": [movenum("Gust"), movenum("Psycho Shift"), movenum("Confusion"), movenum("Reflect"), movenum("Hypnosis"), movenum("Agility"), movenum("Ancient Power"), movenum("Tailwind"), movenum("Psycho Cut"), movenum("Recover"), movenum("Freezing Glare"), movenum("Dream Eater"), movenum("Hurricane"), movenum("Mind Reader"), movenum("Future Sight"), movenum("Trick Room"), movenum("Fly"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Light Screen"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Steel Wing"), movenum("Facade"), movenum("Swift"), movenum("Imprison"), movenum("U-turn"), movenum("Power Swap"), movenum("Guard Swap"), movenum("Round"), movenum("Air Slash"), movenum("Psychic"), movenum("Substitute"), movenum("Psyshock"), movenum("Endure"), movenum("Sleep Talk"), movenum("Shadow Ball"), movenum("Skill Swap"), movenum("Hyper Voice"), movenum("Calm Mind"), movenum("Brave Bird"), movenum("Stored Power"), movenum("Ally Switch")] }, "65681": { "types": ["Fighting", "Flying"], "name": "Galarian Zapdos", "stats": [90, 125, 90, 85, 90, 100], "abilities": [ "Defiant" ], "tier": "SM OU", "height": 1.6, "weight": 58.2, "moves": [movenum("Peck"), movenum("Focus Energy"), movenum("Rock Smash"), movenum("Light Screen"), movenum("Pluck"), movenum("Agility"), movenum("Ancient Power"), movenum("Brick Break"), movenum("Drill Peck"), movenum("Quick Guard"), movenum("Thunderous Kick"), movenum("Bulk Up"), movenum("Counter"), movenum("Detect"), movenum("Close Combat"), movenum("Reversal"), movenum("Mega Kick"), movenum("Fly"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Screech"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Steel Wing"), movenum("Facade"), movenum("Swift"), movenum("Revenge"), movenum("Bounce"), movenum("U-turn"), movenum("Payback"), movenum("Assurance"), movenum("Low Sweep"), movenum("Round"), movenum("Acrobatics"), movenum("Retaliate"), movenum("Stomping Tantrum"), movenum("Low Kick"), movenum("Substitute"), movenum("Endure"), movenum("Sleep Talk"), movenum("Taunt"), movenum("Superpower"), movenum("Blaze Kick"), movenum("Brave Bird"), movenum("Hurricane"), movenum("Throat Chop")] }, "65682": { "types": ["Dark", "Flying"], "name": "Galarian Moltres", "stats": [90, 85, 90, 100, 125, 90], "abilities": [ "Berserk" ], "tier": "SM UU", "height": 2.0, "weight": 66.0, "moves": [movenum("Gust"), movenum("Leer"), movenum("Payback"), movenum("Safeguard"), movenum("Wing Attack"), movenum("Agility"), movenum("Ancient Power"), movenum("Sucker Punch"), movenum("Air Slash"), movenum("After You"), movenum("Fiery Wrath"), movenum("Nasty Plot"), movenum("Hurricane"), movenum("Endure"), movenum("Memento"), movenum("Sky Attack"), movenum("Fly"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Steel Wing"), movenum("Facade"), movenum("Swift"), movenum("Imprison"), movenum("U-turn"), movenum("Assurance"), movenum("Round"), movenum("Hex"), movenum("Snarl"), movenum("Substitute"), movenum("Sleep Talk"), movenum("Shadow Ball"), movenum("Taunt"), movenum("Hyper Voice"), movenum("Dark Pulse"), movenum("Brave Bird"), movenum("Foul Play")] }, "894": { "types": ["Electric", "???"], "name": "Regieleki", "stats": [80, 100, 50, 100, 50, 200], "abilities": [ "Transistor" ], "tier": "SM OU", "height": 1.2, "weight": 145, "moves": [movenum("Thunder Shock"), movenum("Rapid Spin"), movenum("Electroweb"), movenum("Ancient Power"), movenum("Shock Wave"), movenum("Thunder Wave"), movenum("Extreme Speed"), movenum("Thunder Cage"), movenum("Thunderbolt"), movenum("Magnet Rise"), movenum("Thrash"), movenum("Lock-On"), movenum("Zap Cannon"), movenum("Hyper Beam"), movenum("Explosion"), movenum("Giga Impact"), movenum("Screech"), movenum("Light Screen"), movenum("Reflect"), movenum("Self-Destruct"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Rain Dance"), movenum("Facade"), movenum("Swift"), movenum("Bounce"), movenum("Assurance"), movenum("Round"), movenum("Acrobatics"), movenum("Volt Switch"), movenum("Electric Terrain"), movenum("Eerie Impulse"), movenum("Body Slam"), movenum("Thunder"), movenum("Agility"), movenum("Substitute"), movenum("Endure"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Wild Charge")] }, "895": { "types": ["Dragon", "???"], "name": "Regidrago", "stats": [200, 100, 50, 100, 50, 80], "abilities": [ "Dragon's Maw" ], "tier": "SM LU", "height": 2.1, "weight": 200, "moves": [movenum("Twister"), movenum("Vise Grip"), movenum("Bite"), movenum("Ancient Power"), movenum("Dragon Breath"), movenum("Focus Energy"), movenum("Crunch"), movenum("Dragon Claw"), movenum("Hammer Arm"), movenum("Dragon Dance"), movenum("Thrash"), movenum("Laser Focus"), movenum("Dragon Energy"), movenum("Hyper Beam"), movenum("Explosion"), movenum("Giga Impact"), movenum("Light Screen"), movenum("Reflect"), movenum("Self-Destruct"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Facade"), movenum("Thunder Fang"), movenum("Fire Fang"), movenum("Round"), movenum("Breaking Swipe"), movenum("Body Slam"), movenum("Substitute"), movenum("Reversal"), movenum("Outrage"), movenum("Endure"), movenum("Sleep Talk"), movenum("Dragon Pulse"), movenum("Draco Meteor")] }, "896": { "types": ["Ice", "???"], "name": "Glastrier", "stats": [100, 145, 130, 65, 110, 30], "abilities": [ "Chilling Neigh" ], "tier": "SM LU", "height": 2.2, "weight": 800, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Double Kick"), movenum("Avalanche"), movenum("Stomp"), movenum("Torment"), movenum("Mist"), movenum("Icicle Crash"), movenum("Take Down"), movenum("Iron Defense"), movenum("Thrash"), movenum("Taunt"), movenum("Double-Edge"), movenum("Swords Dance"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Icy Wind"), movenum("Hail"), movenum("Facade"), movenum("Icicle Spear"), movenum("Mud Shot"), movenum("Payback"), movenum("Assurance"), movenum("Round"), movenum("Bulldoze"), movenum("Snarl"), movenum("Smart Strike"), movenum("Stomping Tantrum"), movenum("Body Slam"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Substitute"), movenum("Outrage"), movenum("Endure"), movenum("Sleep Talk"), movenum("Megahorn"), movenum("Crunch"), movenum("Uproar"), movenum("Superpower"), movenum("Close Combat"), movenum("Heavy Slam"), movenum("High Horsepower"), movenum("Throat Chop"), movenum("Body Press")] }, "897": { "types": ["Ghost", "???"], "name": "Spectrier", "stats": [100, 65, 60, 145, 80, 130], "abilities": [ "Grim Neigh" ], "tier": "SM OU", "height": 2.0, "weight": 44.5, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Double Kick"), movenum("Hex"), movenum("Stomp"), movenum("Confuse Ray"), movenum("Haze"), movenum("Shadow Ball"), movenum("Take Down"), movenum("Agility"), movenum("Thrash"), movenum("Disable"), movenum("Double-Edge"), movenum("Nasty Plot"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Will-O-Wisp"), movenum("Facade"), movenum("Swift"), movenum("Mud Shot"), movenum("Payback"), movenum("Assurance"), movenum("Psycho Cut"), movenum("Round"), movenum("Bulldoze"), movenum("Snarl"), movenum("Phantom Force"), movenum("Stomping Tantrum"), movenum("Body Slam"), movenum("Substitute"), movenum("Endure"), movenum("Sleep Talk"), movenum("Crunch"), movenum("Uproar"), movenum("Taunt"), movenum("Calm Mind"), movenum("Dark Pulse"), movenum("Foul Play")] }, "898": { "types": ["Grass", "Psychic"], "name": "Calyrex", "stats": [100, 80, 80, 80, 80, 80], "abilities": [ "Unnerve" ], "tier": "SM PU", "height": 1.1, "weight": 7.7, "moves": [movenum("Pound"), movenum("Mega Drain"), movenum("Confusion"), movenum("Growth"), movenum("Life Dew"), movenum("Giga Drain"), movenum("Psyshock"), movenum("Helping Hand"), movenum("Aromatherapy"), movenum("Energy Ball"), movenum("Psychic"), movenum("Leech Seed"), movenum("Heal Pulse"), movenum("Solar Beam"), movenum("Future Sight"), movenum("Agility"), movenum("Pay Day"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Magical Leaf"), movenum("Solar Blade"), movenum("Light Screen"), movenum("Reflect"), movenum("Safeguard"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Sunny Day"), movenum("Facade"), movenum("Swift"), movenum("Imprison"), movenum("Bullet Seed"), movenum("Power Swap"), movenum("Guard Swap"), movenum("Speed Swap"), movenum("Trick Room"), movenum("Wonder Room"), movenum("Magic Room"), movenum("Round"), movenum("Draining Kiss"), movenum("Grassy Terrain"), movenum("Psychic Terrain"), movenum("Metronome"), movenum("Tri Attack"), movenum("Substitute"), movenum("Endure"), movenum("Sleep Talk"), movenum("Baton Pass"), movenum("Encore"), movenum("Trick"), movenum("Skill Swap"), movenum("Calm Mind"), movenum("Seed Bomb"), movenum("Zen Headbutt"), movenum("Leaf Storm"), movenum("Grass Knot"), movenum("Stored Power"), movenum("Ally Switch"), movenum("Pollen Puff")] }, "66434": { "types": ["Psychic", "Ice"], "name": "Calyrex-Ice Rider", "stats": [100, 165, 150, 85, 130, 50], "abilities": [ "As One" ], "tier": "SM Ubers", "height": 2.4, "weight": 809.1, "moves": [movenum("Pound"), movenum("Mega Drain"), movenum("Confusion"), movenum("Growth"), movenum("Life Dew"), movenum("Giga Drain"), movenum("Psyshock"), movenum("Helping Hand"), movenum("Aromatherapy"), movenum("Energy Ball"), movenum("Psychic"), movenum("Leech Seed"), movenum("Heal Pulse"), movenum("Solar Beam"), movenum("Future Sight"), movenum("Glacial Lance"), movenum("Tackle"), movenum("Tail Whip"), movenum("Double Kick"), movenum("Avalanche"), movenum("Stomp"), movenum("Torment"), movenum("Mist"), movenum("Icicle Crash"), movenum("Take Down"), movenum("Iron Defense"), movenum("Thrash"), movenum("Taunt"), movenum("Double-Edge"), movenum("Swords Dance"), movenum("Agility"), movenum("Pay Day"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Magical Leaf"), movenum("Solar Blade"), movenum("Light Screen"), movenum("Reflect"), movenum("Safeguard"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Icy Wind"), movenum("Sunny Day"), movenum("Hail"), movenum("Facade"), movenum("Swift"), movenum("Imprison"), movenum("Bullet Seed"), movenum("Icicle Spear"), movenum("Mud Shot"), movenum("Payback"), movenum("Assurance"), movenum("Power Swap"), movenum("Guard Swap"), movenum("Speed Swap"), movenum("Trick Room"), movenum("Wonder Room"), movenum("Magic Room"), movenum("Round"), movenum("Bulldoze"), movenum("Snarl"), movenum("Draining Kiss"), movenum("Grassy Terrain"), movenum("Psychic Terrain"), movenum("Smart Strike"), movenum("Stomping Tantrum"), movenum("Body Slam"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Metronome"), movenum("Tri Attack"), movenum("Substitute"), movenum("Outrage"), movenum("Endure"), movenum("Sleep Talk"), movenum("Megahorn"), movenum("Baton Pass"), movenum("Encore"), movenum("Crunch"), movenum("Uproar"), movenum("Trick"), movenum("Superpower"), movenum("Skill Swap"), movenum("Calm Mind"), movenum("Close Combat"), movenum("Seed Bomb"), movenum("Zen Headbutt"), movenum("Leaf Storm"), movenum("Grass Knot"), movenum("Heavy Slam"), movenum("Stored Power"), movenum("Ally Switch"), movenum("High Horsepower"), movenum("Throat Chop"), movenum("Pollen Puff"), movenum("Body Press")] }, "131970": { "types": ["Psychic", "Ghost"], "name": "Calyrex-Shadow Rider", "stats": [100, 85, 80, 165, 100, 150], "abilities": [ "As One" ], "tier": "SM Ubers", "height": 2.4, "weight": 53.6, "moves": [movenum("Pound"), movenum("Mega Drain"), movenum("Confusion"), movenum("Growth"), movenum("Life Dew"), movenum("Giga Drain"), movenum("Psyshock"), movenum("Helping Hand"), movenum("Aromatherapy"), movenum("Energy Ball"), movenum("Psychic"), movenum("Leech Seed"), movenum("Heal Pulse"), movenum("Solar Beam"), movenum("Future Sight"), movenum("Tackle"), movenum("Tail Whip"), movenum("Double Kick"), movenum("Stomp"), movenum("Take Down"), movenum("Thrash"), movenum("Taunt"), movenum("Double-Edge"), movenum("Astral Barrage"), movenum("Hex"), movenum("Confuse Ray"), movenum("Haze"), movenum("Shadow Ball"), movenum("Agility"), movenum("Disable"), movenum("Nasty Plot"), movenum("Pay Day"), movenum("Hyper Beam"), movenum("Giga Impact"), movenum("Magical Leaf"), movenum("Solar Blade"), movenum("Light Screen"), movenum("Reflect"), movenum("Safeguard"), movenum("Rest"), movenum("Snore"), movenum("Protect"), movenum("Scary Face"), movenum("Sunny Day"), movenum("Will-O-Wisp"), movenum("Facade"), movenum("Swift"), movenum("Imprison"), movenum("Bullet Seed"), movenum("Mud Shot"), movenum("Payback"), movenum("Assurance"), movenum("Power Swap"), movenum("Guard Swap"), movenum("Speed Swap"), movenum("Psycho Cut"), movenum("Trick Room"), movenum("Wonder Room"), movenum("Magic Room"), movenum("Round"), movenum("Bulldoze"), movenum("Snarl"), movenum("Phantom Force"), movenum("Draining Kiss"), movenum("Grassy Terrain"), movenum("Psychic Terrain"), movenum("Stomping Tantrum"), movenum("Body Slam"), movenum("Metronome"), movenum("Tri Attack"), movenum("Substitute"), movenum("Endure"), movenum("Sleep Talk"), movenum("Baton Pass"), movenum("Encore"), movenum("Crunch"), movenum("Uproar"), movenum("Trick"), movenum("Skill Swap"), movenum("Calm Mind"), movenum("Dark Pulse"), movenum("Seed Bomb"), movenum("Zen Headbutt"), movenum("Leaf Storm"), movenum("Grass Knot"), movenum("Foul Play"), movenum("Stored Power"), movenum("Ally Switch"), movenum("Pollen Puff")] }, "65594": { "types": ["Fire", "Rock"], "name": "Hisuian Growlithe", "stats": [60, 75, 45, 65, 50, 55], "abilities": [ "Intimidate", "Flash Fire", "Rock Head" ], "tier": "SM LC", "height": 0.8, "weight": 22.7, "moves": [movenum("Tackle"), movenum("Ember"), movenum("Bite"), movenum("Fire Fang"), movenum("Rock Slide"), movenum("Crunch"), movenum("Double-Edge"), movenum("Flare Blitz"), movenum("Rock Smash"), movenum("Aerial Ace"), movenum("Swift"), movenum("Rest"), movenum("Iron Tail"), movenum("Snarl"), movenum("Wild Charge"), movenum("Outrage"), movenum("Play Rough"), movenum("Flamethrower")] }, "65595": { "types": ["Fire", "Rock"], "name": "Hisuian Arcanine", "stats": [95, 115, 80, 95, 80, 90], "abilities": [ "Intimidate", "Flash Fire", "Rock Head" ], "tier": "SM OU", "height": 2.2, "weight": 182.9, "moves": [movenum("Tackle"), movenum("Ember"), movenum("Bite"), movenum("Fire Fang"), movenum("Rock Slide"), movenum("Raging Fury"), movenum("Crunch"), movenum("Double-Edge"), movenum("Flare Blitz"), movenum("Thunder Fang"), movenum("Rock Smash"), movenum("Bulldoze"), movenum("Aerial Ace"), movenum("Swift"), movenum("Rest"), movenum("Iron Head"), movenum("Iron Tail"), movenum("Snarl"), movenum("Giga Impact"), movenum("Wild Charge"), movenum("Stone Edge"), movenum("Outrage"), movenum("Play Rough"), movenum("Hyper Beam"), movenum("Flamethrower")] }, "65636": { "types": ["Electric", "Grass"], "name": "Hisuian Voltorb", "stats": [40, 30, 50, 55, 55, 100], "abilities": [ "Soundproof", "Static", "Aftermath" ], "tier": "SM LC", "height": 0.5, "weight": 13.0, "moves": [movenum("Thunder Shock"), movenum("Tackle"), movenum("Thunder Wave"), movenum("Spark"), movenum("Energy Ball"), movenum("Thunderbolt"), movenum("Thunder"), movenum("Self-Destruct"), movenum("Ice Ball"), movenum("Swift"), movenum("Magical Leaf"), movenum("Rest"), movenum("Charge Beam"), movenum("Wild Charge")] }, "65637": { "types": ["Electric", "Grass"], "name": "Hisuian Electrode", "stats": [60, 50, 70, 80, 80, 150], "abilities": [ "Soundproof", "Static", "Aftermath" ], "tier": "SM OU", "height": 1.2, "weight": 74.3, "moves": [movenum("Thunder Shock"), movenum("Tackle"), movenum("Thunder Wave"), movenum("Spark"), movenum("Energy Ball"), movenum("Thunderbolt"), movenum("Thunder"), movenum("Chloroblast"), movenum("Self-Destruct"), movenum("Ice Ball"), movenum("Swift"), movenum("Magical Leaf"), movenum("Rest"), movenum("Charge Beam"), movenum("Giga Impact"), movenum("Wild Charge"), movenum("Hyper Beam")] }, "65693": { "types": ["Fire", "Ghost"], "name": "Hisuian Typhlosion", "stats": [73, 84, 78, 119, 85, 95], "abilities": [ "Blaze", "Frisk" ], "tier": "SM OU", "height": 1.6, "weight": 69.7, "moves": [movenum("Quick Attack"), movenum("Hex"), movenum("Ember"), movenum("Rollout"), movenum("Flame Wheel"), movenum("Swift"), movenum("Flamethrower"), movenum("Infernal Parade"), movenum("Shadow Ball"), movenum("Overheat"), movenum("Rock Smash"), movenum("Bulldoze"), movenum("Aerial Ace"), movenum("Ominous Wind"), movenum("Calm Mind"), movenum("Rest"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Drain Punch"), movenum("Rock Slide"), movenum("Shadow Claw"), movenum("Iron Tail"), movenum("Mystical Fire"), movenum("Giga Impact"), movenum("Wild Charge"), movenum("Hyper Beam")] }, "65747": { "types": ["Dark", "Poison"], "name": "Hisuian Qwilfish", "stats": [65, 95, 85, 55, 55, 85], "abilities": [ "Poison Point", "Swift Swim", "Intimidate" ], "tier": "SM LC", "height": 0.5, "weight": 3.9, "moves": [movenum("Poison Sting"), movenum("Spikes"), movenum("Pin Missile"), movenum("Barb Barrage"), movenum("Water Pulse"), movenum("Dark Pulse"), movenum("Poison Jab"), movenum("Aqua Tail"), movenum("Double-Edge"), movenum("Self-Destruct"), movenum("Ice Ball"), movenum("Swift"), movenum("Rest"), movenum("Icy Wind"), movenum("Sludge Bomb"), movenum("Shadow Ball"), movenum("Ice Beam")] }, "65751": { "types": ["Poison", "Fighting"], "name": "Hisuian Sneasel", "stats": [55, 95, 55, 35, 75, 115], "abilities": [ "Inner Focus", "Keen Eye", "Pickpocket" ], "tier": "SM LC", "height": 0.9, "weight": 28.0, "moves": [movenum("Quick Attack"), movenum("Rock Smash"), movenum("Swift"), movenum("Slash"), movenum("Poison Jab"), movenum("Swords Dance"), movenum("Close Combat"), movenum("False Swipe"), movenum("Aerial Ace"), movenum("Focus Energy"), movenum("Bulk Up"), movenum("Calm Mind"), movenum("Rest"), movenum("Drain Punch"), movenum("X-Scissor"), movenum("Shadow Claw"), movenum("Iron Tail"), movenum("Shadow Ball"), movenum("Snarl")] }, "66019": { "types": ["Steel", "Dragon"], "name": "Dialga-Origin", "stats": [100, 100, 120, 150, 120, 90], "abilities": [ "Pressure", "Telepathy" ], "tier": "SM Ubers", "height": 7.0, "weight": 848.7, "moves": [movenum("Twister"), movenum("Ancient Power"), movenum("Slash"), movenum("Dragon Claw"), movenum("Power Gem"), movenum("Dragon Pulse"), movenum("Earth Power"), movenum("Iron Tail"), movenum("Flash Cannon"), movenum("Roar of Time"), movenum("Rock Smash"), movenum("Bulldoze"), movenum("Aerial Ace"), movenum("Stealth Rock"), movenum("Swift"), movenum("Bulk Up"), movenum("Rest"), movenum("Rock Slide"), movenum("Shadow Claw"), movenum("Iron Head"), movenum("Giga Impact"), movenum("Stone Edge"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Ice Beam"), movenum("Draco Meteor"), movenum("Steel Beam")] }, "66020": { "types": ["Water", "Dragon"], "name": "Palkia-Origin", "stats": [90, 100, 100, 150, 120, 120], "abilities": [ "Pressure", "Telepathy" ], "tier": "SM Ubers", "height": 6.3, "weight": 659.0, "moves": [movenum("Twister"), movenum("Ancient Power"), movenum("Slash"), movenum("Dragon Claw"), movenum("Power Gem"), movenum("Dragon Pulse"), movenum("Earth Power"), movenum("Aqua Tail"), movenum("Hydro Pump"), movenum("Spacial Rend"), movenum("Rock Smash"), movenum("Bulldoze"), movenum("Aerial Ace"), movenum("Swift"), movenum("Bulk Up"), movenum("Rest"), movenum("Rock Slide"), movenum("Shadow Claw"), movenum("Water Pulse"), movenum("Giga Impact"), movenum("Stone Edge"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Ice Beam"), movenum("Draco Meteor")] }, "66039": { "types": ["Water", "Dark"], "name": "Hisuian Samurott", "stats": [90, 108, 80, 100, 65, 85], "abilities": [ "Torrent", "Sharpness" ], "tier": "SM OU", "height": 1.5, "weight": 94.6, "moves": [movenum("Night Slash"), movenum("Tackle"), movenum("Aqua Jet"), movenum("Swords Dance"), movenum("Water Pulse"), movenum("Ceaseless Edge"), movenum("Slash"), movenum("Aqua Tail"), movenum("Dark Pulse"), movenum("Hydro Pump"), movenum("False Swipe"), movenum("Rock Smash"), movenum("Aerial Ace"), movenum("Focus Energy"), movenum("Rest"), movenum("Poison Jab"), movenum("Psycho Cut"), movenum("X-Scissor"), movenum("Iron Tail"), movenum("Icy Wind"), movenum("Giga Impact"), movenum("Megahorn"), movenum("Hyper Beam"), movenum("Ice Beam")] }, "66085": { "types": ["Grass", "Fighting"], "name": "Hisuian Lilligant", "stats": [70, 105, 75, 50, 75, 105], "abilities": [ "Chlorophyll", "Hustle", "Leaf Guard" ], "tier": "SM OU", "height": 1.3, "weight": 20.0, "moves": [movenum("Absorb"), movenum("Rock Smash"), movenum("Leafage"), movenum("Stun Spore"), movenum("Poison Powder"), movenum("Energy Ball"), movenum("Sleep Powder"), movenum("Drain Punch"), movenum("Recover"), movenum("Leaf Blade"), movenum("Victory Dance"), movenum("Leaf Storm"), movenum("Petal Dance"), movenum("Close Combat"), movenum("Aerial Ace"), movenum("Magical Leaf"), movenum("Focus Energy"), movenum("Rest"), movenum("Baby-Doll Eyes"), movenum("Poison Jab"), movenum("Giga Impact"), movenum("Hyper Beam")] }, "131622": { "types": ["Water", "???"], "name": "Basculin-White Striped", "stats": [70, 92, 65, 80, 55, 98], "abilities": [ "Swift Swim", "Adaptability", "Mold Breaker" ], "tier": "SM LC", "height": 1.0, "weight": 18.0, "moves": [movenum("Tackle"), movenum("Aqua Jet"), movenum("Bite"), movenum("Zen Headbutt"), movenum("Crunch"), movenum("Wave Crash"), movenum("Double-Edge"), movenum("Ice Fang"), movenum("Swift"), movenum("Rest"), movenum("Icy Wind"), movenum("Aqua Tail"), movenum("Ice Beam")] }, "66106": { "types": ["Normal", "Ghost"], "name": "Hisuian Zorua", "stats": [40, 65, 40, 80, 40, 65], "abilities": [ "Illusion" ], "tier": "SM LC", "height": 0.7, "weight": 12.5, "moves": [movenum("Shadow Sneak"), movenum("Snarl"), movenum("Swift"), movenum("Bitter Malice"), movenum("Slash"), movenum("Shadow Claw"), movenum("Nasty Plot"), movenum("Aerial Ace"), movenum("Calm Mind"), movenum("Rest"), movenum("Sludge Bomb"), movenum("Shadow Ball"), movenum("Dark Pulse")] }, "66107": { "types": ["Normal", "Ghost"], "name": "Hisuian Zoroark", "stats": [60, 105, 60, 120, 60, 105], "abilities": [ "Illusion" ], "tier": "SM OU", "height": 1.6, "weight": 73.0, "moves": [movenum("Shadow Sneak"), movenum("Snarl"), movenum("Swift"), movenum("Bitter Malice"), movenum("Slash"), movenum("Shadow Claw"), movenum("Shadow Ball"), movenum("Nasty Plot"), movenum("Extrasensory"), movenum("Rock Smash"), movenum("Aerial Ace"), movenum("Ominous Wind"), movenum("Calm Mind"), movenum("Rest"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Flamethrower"), movenum("Dark Pulse")] }, "66164": { "types": ["Psychic", "Flying"], "name": "Hisuian Braviary", "stats": [110, 83, 70, 112, 70, 65], "abilities": [ "Keen Eye", "Sheer Force", "Tinted Lens" ], "tier": "SM OU", "height": 1.7, "weight": 43.4, "moves": [movenum("Quick Attack"), movenum("Aerial Ace"), movenum("Twister"), movenum("Slash"), movenum("Air Slash"), movenum("Esper Wing"), movenum("Roost"), movenum("Double-Edge"), movenum("Brave Bird"), movenum("Hurricane"), movenum("Rock Smash"), movenum("Swift"), movenum("Ominous Wind"), movenum("Power Shift"), movenum("Bulk Up"), movenum("Rest"), movenum("Zen Headbutt"), movenum("Rock Slide"), movenum("Shadow Claw"), movenum("Mystical Fire"), movenum("Dazzling Gleam"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Psychic")] }, "66241": { "types": ["Dragon", "Steel"], "name": "Hisuian Sliggoo", "stats": [58, 75, 83, 83, 113, 40], "abilities": [ "Sap Sipper", "Shell Armor", "Gooey" ], "tier": "SM PU", "height": 0.7, "weight": 76.8, "moves": [movenum("Bubble"), movenum("Acid Spray"), movenum("Acid Armor"), movenum("Water Pulse"), movenum("Iron Head"), movenum("Dragon Pulse"), movenum("Shelter"), movenum("Hydro Pump"), movenum("Rest"), movenum("Rock Slide"), movenum("Iron Tail"), movenum("Sludge Bomb"), movenum("Outrage"), movenum("Thunderbolt"), movenum("Ice Beam"), movenum("Draco Meteor"), movenum("Steel Beam")] }, "66242": { "types": ["Dragon", "Steel"], "name": "Hisuian Goodra", "stats": [80, 100, 100, 110, 150, 60], "abilities": [ "Sap Sipper", "Shell Armor", "Gooey" ], "tier": "SM OU", "height": 2.7, "weight": 641.5, "moves": [movenum("Bubble"), movenum("Acid Spray"), movenum("Acid Armor"), movenum("Water Pulse"), movenum("Iron Head"), movenum("Dragon Pulse"), movenum("Shelter"), movenum("Hydro Pump"), movenum("Rock Smash"), movenum("Bulldoze"), movenum("Rest"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Rock Slide"), movenum("Iron Tail"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Ice Beam"), movenum("Draco Meteor"), movenum("Steel Beam")] }, "66249": { "types": ["Ice", "Rock"], "name": "Hisuian Avalugg", "stats": [95, 127, 184, 34, 36, 38], "abilities": [ "Strong Jaw", "Ice Body", "Sturdy" ], "tier": "SM OU", "height": 1.4, "weight": 278.6, "moves": [movenum("Tackle"), movenum("Rock Slide"), movenum("Powder Snow"), movenum("Ice Shard"), movenum("Bite"), movenum("Iron Defense"), movenum("Crunch"), movenum("Earth Power"), movenum("Mountain Gale"), movenum("Blizzard"), movenum("Double-Edge"), movenum("Ice Fang"), movenum("Ice Ball"), movenum("Rock Smash"), movenum("Bulldoze"), movenum("Stealth Rock"), movenum("Power Shift"), movenum("Rest"), movenum("Iron Head"), movenum("Water Pulse"), movenum("Icy Wind"), movenum("Flash Cannon"), movenum("Giga Impact"), movenum("High Horsepower"), movenum("Stone Edge"), movenum("Hyper Beam"), movenum("Ice Beam")] }, "66260": { "types": ["Grass", "Fighting"], "name": "Hisuian Decidueye", "stats": [88, 112, 80, 95, 95, 60], "abilities": [ "Overgrow", "Scrappy" ], "tier": "SM OU", "height": 1.6, "weight": 36.9, "moves": [movenum("Gust"), movenum("Rock Smash"), movenum("Leafage"), movenum("Roost"), movenum("Aerial Ace"), movenum("Magical Leaf"), movenum("Air Slash"), movenum("Aura Sphere"), movenum("Leaf Blade"), movenum("Triple Arrows"), movenum("Brave Bird"), movenum("Leaf Storm"), movenum("False Swipe"), movenum("Spikes"), movenum("Swift"), movenum("Focus Energy"), movenum("Bulk Up"), movenum("Rest"), movenum("Psycho Cut"), movenum("Shadow Claw"), movenum("Energy Ball"), movenum("Giga Impact"), movenum("Hyper Beam")] }, "899": { "types": ["Normal", "Psychic"], "name": "Wyrdeer", "stats": [103, 105, 72, 105, 75, 65], "abilities": [ "Intimidate", "Frisk", "Sap Sipper" ], "tier": "SM OU", "height": 1.8, "weight": 95.1, "moves": [movenum("Tackle"), movenum("Confusion"), movenum("Hypnosis"), movenum("Calm Mind"), movenum("Psyshield Bash"), movenum("Extrasensory"), movenum("Zen Headbutt"), movenum("Double-Edge"), movenum("Bulldoze"), movenum("Swift"), movenum("Rest"), movenum("Iron Tail"), movenum("Charge Beam"), movenum("Energy Ball"), movenum("Shadow Ball"), movenum("Giga Impact"), movenum("Wild Charge"), movenum("High Horsepower"), movenum("Megahorn"), movenum("Hyper Beam"), movenum("Thunderbolt"), movenum("Psychic")] }, "900": { "types": ["Bug", "Rock"], "name": "Kleavor", "stats": [70, 130, 95, 45, 75, 85], "abilities": [ "Swarm", "Sheer Force", "Sharpness" ], "tier": "SM OU", "height": 1.8, "weight": 89.0, "moves": [movenum("Quick Attack"), movenum("Silver Wind"), movenum("Aerial Ace"), movenum("Double Hit"), movenum("Stealth Rock"), movenum("Air Slash"), movenum("Swords Dance"), movenum("Stone Axe"), movenum("X-Scissor"), movenum("Close Combat"), movenum("False Swipe"), movenum("Rock Smash"), movenum("Swift"), movenum("Ominous Wind"), movenum("Focus Energy"), movenum("Calm Mind"), movenum("Rest"), movenum("Psycho Cut"), movenum("Rock Slide"), movenum("Giga Impact"), movenum("Stone Edge"), movenum("Hyper Beam")] }, "901": { "types": ["Normal", "Ground"], "name": "Ursaluna", "stats": [130, 140, 105, 45, 80, 50], "abilities": [ "Guts", "Bulletproof", "Unnerve" ], "tier": "SM OU", "height": 2.4, "weight": 290.0, "moves": [movenum("Tackle"), movenum("Baby-Doll Eyes"), movenum("Bulldoze"), movenum("Slash"), movenum("Play Rough"), movenum("High Horsepower"), movenum("Double-Edge"), movenum("Headlong Rush"), movenum("Rock Smash"), movenum("Aerial Ace"), movenum("Swift"), movenum("Focus Energy"), movenum("Bulk Up"), movenum("Rest"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Rock Slide"), movenum("Shadow Claw"), movenum("Earth Power"), movenum("Giga Impact"), movenum("Stone Edge"), movenum("Hyper Beam")] }, "902": { "types": ["Water", "Ghost"], "name": "Basculegion", "stats": [120, 112, 65, 80, 75, 78], "abilities": [ "Swift Swim", "Adaptability", "Mold Breaker" ], "tier": "SM OU", "height": 3.0, "weight": 110.0, "moves": [movenum("Tackle"), movenum("Aqua Jet"), movenum("Bite"), movenum("Hex"), movenum("Zen Headbutt"), movenum("Crunch"), movenum("Shadow Ball"), movenum("Wave Crash"), movenum("Double-Edge"), movenum("Ice Fang"), movenum("Swift"), movenum("Ominous Wind"), movenum("Calm Mind"), movenum("Rest"), movenum("Water Pulse"), movenum("Icy Wind"), movenum("Giga Impact"), movenum("Aqua Tail"), movenum("Hyper Beam"), movenum("Ice Beam"), movenum("Psychic")] }, "66438": { "types": ["Water", "Ghost"], "name": "Basculegion-F", "stats": [120, 92, 65, 100, 75, 78], "abilities": [ "Swift Swim", "Adaptability", "Mold Breaker" ], "tier": "SM OU", "height": 3.0, "weight": 110.0, "moves": [movenum("Tackle"), movenum("Aqua Jet"), movenum("Bite"), movenum("Hex"), movenum("Zen Headbutt"), movenum("Crunch"), movenum("Shadow Ball"), movenum("Wave Crash"), movenum("Double-Edge"), movenum("Ice Fang"), movenum("Swift"), movenum("Ominous Wind"), movenum("Calm Mind"), movenum("Rest"), movenum("Water Pulse"), movenum("Icy Wind"), movenum("Giga Impact"), movenum("Aqua Tail"), movenum("Hyper Beam"), movenum("Ice Beam"), movenum("Psychic")] }, "903": { "types": ["Poison", "Fighting"], "name": "Sneasler", "stats": [80, 130, 60, 40, 80, 120], "abilities": [ "Pressure", "Poison Touch", "Unburden" ], "tier": "SM OU", "height": 1.3, "weight": 43.0, "moves": [movenum("Quick Attack"), movenum("Rock Smash"), movenum("Swift"), movenum("Dire Claw"), movenum("Slash"), movenum("Poison Jab"), movenum("Swords Dance"), movenum("Close Combat"), movenum("False Swipe"), movenum("Aerial Ace"), movenum("Power Shift"), movenum("Focus Energy"), movenum("Bulk Up"), movenum("Calm Mind"), movenum("Rest"), movenum("Drain Punch"), movenum("X-Scissor"), movenum("Shadow Claw"), movenum("Iron Tail"), movenum("Shadow Ball"), movenum("Snarl"), movenum("Giga Impact"), movenum("Hyper Beam")] }, "904": { "types": ["Dark", "Poison"], "name": "Overqwil", "stats": [85, 115, 95, 65, 65, 85], "abilities": [ "Poison Point", "Swift Swim", "Intimidate" ], "tier": "SM OU", "height": 2.5, "weight": 60.5, "moves": [movenum("Poison Sting"), movenum("Spikes"), movenum("Pin Missile"), movenum("Barb Barrage"), movenum("Water Pulse"), movenum("Dark Pulse"), movenum("Poison Jab"), movenum("Aqua Tail"), movenum("Double-Edge"), movenum("Self-Destruct"), movenum("Ice Ball"), movenum("Swift"), movenum("Rest"), movenum("Icy Wind"), movenum("Sludge Bomb"), movenum("Shadow Ball"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Ice Beam")] }, "905": { "types": ["Fairy", "Flying"], "name": "Enamorus", "stats": [74, 115, 70, 135, 80, 106], "abilities": [ "Cute Charm", "Contrary" ], "tier": "SM OU", "height": 1.6, "weight": 48.0, "moves": [movenum("Tackle"), movenum("Bite"), movenum("Twister"), movenum("Draining Kiss"), movenum("Iron Defense"), movenum("Extrasensory"), movenum("Crunch"), movenum("Moonblast"), movenum("Springtide Storm"), movenum("Rock Smash"), movenum("Power Shift"), movenum("Calm Mind"), movenum("Rest"), movenum("Zen Headbutt"), movenum("Mystical Fire"), movenum("Sludge Bomb"), movenum("Earth Power"), movenum("Dazzling Gleam"), movenum("Giga Impact"), movenum("Play Rough"), movenum("Hyper Beam"), movenum("Psychic")] }, "66441": { "types": ["Fairy", "Flying"], "name": "Enamorus-Therian", "stats": [74, 115, 110, 135, 100, 46], "abilities": [ "Overcoat" ], "tier": "SM OU", "height": 1.6, "weight": 48.0, "moves": [movenum("Tackle"), movenum("Bite"), movenum("Twister"), movenum("Draining Kiss"), movenum("Iron Defense"), movenum("Extrasensory"), movenum("Crunch"), movenum("Moonblast"), movenum("Springtide Storm"), movenum("Rock Smash"), movenum("Power Shift"), movenum("Calm Mind"), movenum("Rest"), movenum("Zen Headbutt"), movenum("Mystical Fire"), movenum("Sludge Bomb"), movenum("Earth Power"), movenum("Dazzling Gleam"), movenum("Giga Impact"), movenum("Play Rough"), movenum("Hyper Beam"), movenum("Psychic")] }, "65664": { "types": ["Fighting", "???"], "name": "Paldean Tauros", "stats": [75, 110, 105, 30, 70, 100], "abilities": ["Intimidate", "Anger Point", "Cud Chew"], "tier": "SM NU", "height": 1.4, "weight": 115, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Work Up"), movenum("Double Kick"), movenum("Assurance"), movenum("Headbutt"), movenum("Scary Face"), movenum("Zen Headbutt"), movenum("Raging Bull"), movenum("Rest"), movenum("Swagger"), movenum("Thrash"), movenum("Double-Edge"), movenum("Close Combat"), movenum("Take Down"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rock Slide"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Drill Run"), movenum("Surf"), movenum("Reversal"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Curse"), movenum("Endeavor")] }, "131200": { "types": ["Fighting", "Fire"], "name": "Paldean Tauros-Blaze", "stats": [75, 110, 105, 30, 70, 100], "abilities": ["Intimidate", "Anger Point", "Cud Chew"], "tier": "SM LU", "height": 1.4, "weight": 85, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Work Up"), movenum("Double Kick"), movenum("Flame Charge"), movenum("Headbutt"), movenum("Scary Face"), movenum("Zen Headbutt"), movenum("Raging Bull"), movenum("Rest"), movenum("Swagger"), movenum("Thrash"), movenum("Flare Blitz"), movenum("Close Combat"), movenum("Take Down"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rock Slide"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Drill Run"), movenum("Will-O-Wisp"), movenum("Flamethrower"), movenum("Reversal"), movenum("Fire Blast"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Curse"), movenum("Endeavor")] }, "196736": { "types": ["Fighting", "Water"], "name": "Paldean Tauros-Aqua", "stats": [75, 110, 105, 30, 70, 100], "abilities": ["Intimidate", "Anger Point", "Cud Chew"], "tier": "SM LU", "height": 1.4, "weight": 110, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Work Up"), movenum("Double Kick"), movenum("Aqua Jet"), movenum("Headbutt"), movenum("Scary Face"), movenum("Zen Headbutt"), movenum("Raging Bull"), movenum("Rest"), movenum("Swagger"), movenum("Thrash"), movenum("Wave Crash"), movenum("Close Combat"), movenum("Take Down"), movenum("Protect"), movenum("Water Pulse"), movenum("Thief"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rock Slide"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Drill Run"), movenum("Liquidation"), movenum("Surf"), movenum("Reversal"), movenum("Hydro Pump"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Curse"), movenum("Endeavor")] }, "65730": { "types": ["Poison", "Ground"], "name": "Paldean Wooper", "stats": [55, 45, 45, 25, 25, 15], "abilities": ["Poison Point", "Water Absorb", "Unaware"], "tier": "SM LC", "height": 0.4, "weight": 11, "moves": [movenum("Mud Shot"), movenum("Tail Whip"), movenum("Tackle"), movenum("Poison Tail"), movenum("Toxic Spikes"), movenum("Slam"), movenum("Yawn"), movenum("Poison Jab"), movenum("Sludge Wave"), movenum("Amnesia"), movenum("Toxic"), movenum("Earthquake"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Water Pulse"), movenum("Low Kick"), movenum("Acid Spray"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Venoshock"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Waterfall"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Spikes"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Liquidation"), movenum("Stealth Rock"), movenum("Surf"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Hydro Pump"), movenum("Sludge Bomb"), movenum("Stone Edge"), movenum("Tera Blast"), movenum("After You"), movenum("Ancient Power"), movenum("Counter"), movenum("Curse"), movenum("Double Kick"), movenum("Haze"), movenum("Mist"), movenum("Recover"), movenum("Spit Up"), movenum("Stockpile"), movenum("Swallow")] }, "906": { "types": ["Grass", "???"], "name": "Sprigatito", "stats": [40, 61, 54, 45, 45, 65], "abilities": ["Overgrow", "", "Protean"], "tier": "SM LC", "height": 0.4, "weight": 4.1, "moves": [movenum("Scratch"), movenum("Tail Whip"), movenum("Leafage"), movenum("Bite"), movenum("Hone Claws"), movenum("Magical Leaf"), movenum("Quick Attack"), movenum("Seed Bomb"), movenum("U-turn"), movenum("Worry Seed"), movenum("Slash"), movenum("Energy Ball"), movenum("Play Rough"), movenum("Take Down"), movenum("Charm"), movenum("Fake Tears"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Acrobatics"), movenum("Disarming Voice"), movenum("Trailblaze"), movenum("Facade"), movenum("Swift"), movenum("Endure"), movenum("Bullet Seed"), movenum("Shadow Claw"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Giga Drain"), movenum("Helping Hand"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Grass Pledge"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Copycat"), movenum("Leech Seed"), movenum("Petal Blizzard"), movenum("Sucker Punch")] }, "907": { "types": ["Grass", "???"], "name": "Floragato", "stats": [61, 80, 63, 60, 63, 83], "abilities": ["Overgrow", "Protean"], "tier": "SM PU", "height": 0.9, "weight": 12.2, "moves": [movenum("Scratch"), movenum("Tail Whip"), movenum("Leafage"), movenum("Bite"), movenum("Hone Claws"), movenum("Magical Leaf"), movenum("Quick Attack"), movenum("Seed Bomb"), movenum("U-turn"), movenum("Worry Seed"), movenum("Slash"), movenum("Energy Ball"), movenum("Play Rough"), movenum("Leaf Storm"), movenum("Take Down"), movenum("Charm"), movenum("Fake Tears"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Disarming Voice"), movenum("Trailblaze"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Bullet Seed"), movenum("Shadow Claw"), movenum("Thunder Punch"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Giga Drain"), movenum("Helping Hand"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Grass Pledge"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Copycat"), movenum("Leech Seed"), movenum("Petal Blizzard"), movenum("Sucker Punch")] }, "908": { "types": ["Grass", "Dark"], "name": "Meowscarada", "stats": [76, 110, 70, 81, 70, 123], "abilities": ["Overgrow", "Protean"], "tier": "SM OU", "height": 1.5, "weight": 31.2, "moves": [movenum("Leafage"), movenum("Scratch"), movenum("Tail Whip"), movenum("Flower Trick"), movenum("Double Team"), movenum("Trick"), movenum("Bite"), movenum("Hone Claws"), movenum("Magical Leaf"), movenum("Quick Attack"), movenum("Seed Bomb"), movenum("U-turn"), movenum("Worry Seed"), movenum("Slash"), movenum("Night Slash"), movenum("Energy Ball"), movenum("Play Rough"), movenum("Knock Off"), movenum("Grassy Terrain"), movenum("Leaf Storm"), movenum("Take Down"), movenum("Charm"), movenum("Fake Tears"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Thief"), movenum("Disarming Voice"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Bullet Seed"), movenum("Brick Break"), movenum("Shadow Claw"), movenum("Foul Play"), movenum("Thunder Punch"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Spikes"), movenum("Toxic Spikes"), movenum("Dark Pulse"), movenum("Skill Swap"), movenum("Power Gem"), movenum("Substitute"), movenum("Giga Drain"), movenum("Aura Sphere"), movenum("Shadow Ball"), movenum("Helping Hand"), movenum("Pollen Puff"), movenum("Nasty Plot"), movenum("Grass Pledge"), movenum("Giga Impact"), movenum("Frenzy Plant"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Copycat"), movenum("Leech Seed"), movenum("Petal Blizzard"), movenum("Sucker Punch")] }, "909": { "types": ["Fire", "???"], "name": "Fuecoco", "stats": [67, 45, 59, 63, 43, 36], "abilities": ["Blaze", "Unaware"], "tier": "SM LC", "height": 0.4, "weight": 9.8, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Ember"), movenum("Round"), movenum("Bite"), movenum("Incinerate"), movenum("Yawn"), movenum("Snarl"), movenum("Roar"), movenum("Flamethrower"), movenum("Hyper Voice"), movenum("Fire Blast"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Disarming Voice"), movenum("Fire Spin"), movenum("Facade"), movenum("Flame Charge"), movenum("Endure"), movenum("Sunny Day"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Will-O-Wisp"), movenum("Crunch"), movenum("Heat Wave"), movenum("Encore"), movenum("Helping Hand"), movenum("Fire Pledge"), movenum("Outrage"), movenum("Overheat"), movenum("Flare Blitz"), movenum("Tera Blast"), movenum("Belch"), movenum("Curse"), movenum("Slack Off")] }, "910": { "types": ["Fire", "???"], "name": "Crocalor", "stats": [81, 55, 78, 90, 58, 49], "abilities": ["Blaze", "Unaware"], "tier": "SM NU", "height": 1, "weight": 30.7, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Ember"), movenum("Lick"), movenum("Round"), movenum("Bite"), movenum("Yawn"), movenum("Incinerate"), movenum("Snarl"), movenum("Roar"), movenum("Flamethrower"), movenum("Hyper Voice"), movenum("Will-O-Wisp"), movenum("Fire Blast"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Disarming Voice"), movenum("Fire Spin"), movenum("Facade"), movenum("Flame Charge"), movenum("Endure"), movenum("Sunny Day"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Crunch"), movenum("Heat Wave"), movenum("Helping Hand"), movenum("Fire Pledge"), movenum("Outrage"), movenum("Overheat"), movenum("Flare Blitz"), movenum("Tera Blast"), movenum("Belch"), movenum("Curse"), movenum("Encore"), movenum("Slack Off")] }, "911": { "types": ["Fire", "Ghost"], "name": "Skeledirge", "stats": [104, 75, 100, 110, 76, 66], "abilities": ["Blaze", "Unaware"], "tier": "SM OU", "height": 1.6, "weight": 326.5, "moves": [movenum("Ember"), movenum("Tackle"), movenum("Leer"), movenum("Sing"), movenum("Torch Song"), movenum("Lick"), movenum("Round"), movenum("Scary Face"), movenum("Bite"), movenum("Incinerate"), movenum("Snarl"), movenum("Roar"), movenum("Flamethrower"), movenum("Shadow Ball"), movenum("Hyper Voice"), movenum("Will-O-Wisp"), movenum("Hex"), movenum("Fire Blast"), movenum("Overheat"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Disarming Voice"), movenum("Fire Spin"), movenum("Facade"), movenum("Flame Charge"), movenum("Night Shade"), movenum("Endure"), movenum("Sunny Day"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Shadow Claw"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Imprison"), movenum("Substitute"), movenum("Crunch"), movenum("Heat Wave"), movenum("Encore"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Fire Pledge"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Blast Burn"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Belch"), movenum("Curse"), movenum("Slack Off")] }, "912": { "types": ["Water", "???"], "name": "Quaxly", "stats": [55, 65, 45, 50, 45, 50], "abilities": ["Torrent", "Moxie"], "tier": "SM LC", "height": 0.5, "weight": 6.1, "moves": [movenum("Pound"), movenum("Growl"), movenum("Water Gun"), movenum("Work Up"), movenum("Wing Attack"), movenum("Aqua Jet"), movenum("Double Hit"), movenum("Aqua Cutter"), movenum("Air Slash"), movenum("Focus Energy"), movenum("Acrobatics"), movenum("Liquidation"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Disarming Voice"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Air Cutter"), movenum("Endure"), movenum("Rain Dance"), movenum("Rest"), movenum("Encore"), movenum("Surf"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Misty Terrain"), movenum("Hydro Pump"), movenum("Water Pledge"), movenum("Brave Bird"), movenum("Tera Blast"), movenum("Detect"), movenum("Last Resort"), movenum("Rapid Spin"), movenum("Roost")] }, "913": { "types": ["Water", "???"], "name": "Quaxwell", "stats": [70, 85, 65, 65, 60, 65], "abilities": ["Torrent", "Moxie"], "tier": "SM PU", "height": 1.2, "weight": 21.5, "moves": [movenum("Double Hit"), movenum("Pound"), movenum("Growl"), movenum("Water Gun"), movenum("Work Up"), movenum("Wing Attack"), movenum("Aqua Jet"), movenum("Water Pulse"), movenum("Low Sweep"), movenum("Aqua Cutter"), movenum("Air Slash"), movenum("Focus Energy"), movenum("Acrobatics"), movenum("Liquidation"), movenum("Feather Dance"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Disarming Voice"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Air Cutter"), movenum("Endure"), movenum("Rain Dance"), movenum("Rest"), movenum("Encore"), movenum("Surf"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Misty Terrain"), movenum("Hydro Pump"), movenum("Water Pledge"), movenum("Brave Bird"), movenum("Tera Blast"), movenum("Detect"), movenum("Last Resort"), movenum("Rapid Spin"), movenum("Roost")] }, "914": { "types": ["Water", "Fighting"], "name": "Quaquaval", "stats": [85, 120, 80, 85, 75, 85], "abilities": ["Torrent", "Moxie"], "tier": "SM OU", "height": 1.8, "weight": 61.9, "moves": [movenum("Double Hit"), movenum("Pound"), movenum("Growl"), movenum("Water Gun"), movenum("Counter"), movenum("Aqua Step"), movenum("Work Up"), movenum("Wing Attack"), movenum("Aqua Jet"), movenum("Low Sweep"), movenum("Water Pulse"), movenum("Aqua Cutter"), movenum("Air Slash"), movenum("Focus Energy"), movenum("Mega Kick"), movenum("Acrobatics"), movenum("Liquidation"), movenum("Feather Dance"), movenum("Close Combat"), movenum("Wave Crash"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Low Kick"), movenum("Disarming Voice"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Icy Wind"), movenum("Air Cutter"), movenum("Fling"), movenum("Endure"), movenum("Rain Dance"), movenum("Brick Break"), movenum("U-turn"), movenum("Bulk Up"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Substitute"), movenum("Encore"), movenum("Surf"), movenum("Ice Spinner"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Reversal"), movenum("Misty Terrain"), movenum("Hydro Pump"), movenum("Water Pledge"), movenum("Giga Impact"), movenum("Hydro Cannon"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Brave Bird"), movenum("Tera Blast"), movenum("Detect"), movenum("Last Resort"), movenum("Rapid Spin"), movenum("Roost")] }, "915": { "types": ["Normal", "???"], "name": "Lechonk", "stats": [54, 45, 40, 35, 45, 35], "abilities": ["Aroma Veil", "Gluttony", "Thick Fat"], "tier": "SM LC", "height": 0.5, "weight": 10.2, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Disarming Voice"), movenum("Echoed Voice"), movenum("Mud Shot"), movenum("Covet"), movenum("Dig"), movenum("Headbutt"), movenum("Yawn"), movenum("Take Down"), movenum("Work Up"), movenum("Uproar"), movenum("Double-Edge"), movenum("Mud-Slap"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Rest"), movenum("Iron Head"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Tera Blast"), movenum("Endeavor"), movenum("Spit Up"), movenum("Stockpile"), movenum("Stuff Cheeks"), movenum("Swallow")] }, "916": { "types": ["Normal", "???"], "name": "Oinkologne", "stats": [110, 100, 75, 59, 80, 65], "abilities": ["Lingering Aroma", "Gluttony", "Thick Fat"], "tier": "SM PU", "height": 1, "weight": 120, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Disarming Voice"), movenum("Echoed Voice"), movenum("Mud Shot"), movenum("Covet"), movenum("Dig"), movenum("Headbutt"), movenum("Take Down"), movenum("Yawn"), movenum("Work Up"), movenum("Uproar"), movenum("Double-Edge"), movenum("Earth Power"), movenum("Belch"), movenum("Mud-Slap"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Energy Ball"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Endeavor"), movenum("Spit Up"), movenum("Stockpile"), movenum("Stuff Cheeks"), movenum("Swallow")] }, "66452": { "types": ["Normal", "???"], "name": "Oinkologne-F", "stats": [115, 90, 70, 59, 90, 65], "abilities": ["Aroma Veil", "Gluttony", "Thick Fat"], "tier": "SM PU", "height": 1, "weight": 120, "moves": [movenum("Tackle"), movenum("Tail Whip"), movenum("Disarming Voice"), movenum("Echoed Voice"), movenum("Mud Shot"), movenum("Covet"), movenum("Dig"), movenum("Headbutt"), movenum("Take Down"), movenum("Yawn"), movenum("Work Up"), movenum("Uproar"), movenum("Double-Edge"), movenum("Earth Power"), movenum("Belch"), movenum("Mud-Slap"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Energy Ball"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Endeavor"), movenum("Spit Up"), movenum("Stockpile"), movenum("Stuff Cheeks"), movenum("Swallow")] }, "917": { "types": ["Bug", "???"], "name": "Tarountula", "stats": [35, 41, 45, 29, 40, 20], "abilities": ["Insomnia", "Stakeout"], "tier": "SM LC", "height": 0.3, "weight": 4, "moves": [movenum("Tackle"), movenum("String Shot"), movenum("Struggle Bug"), movenum("Assurance"), movenum("Feint"), movenum("Bug Bite"), movenum("Block"), movenum("Counter"), movenum("Headbutt"), movenum("Sticky Web"), movenum("Gastro Acid"), movenum("Circle Throw"), movenum("Throat Chop"), movenum("Skitter Smack"), movenum("Take Down"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("False Swipe"), movenum("Shadow Claw"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Poison Jab"), movenum("Rest"), movenum("Spikes"), movenum("Toxic Spikes"), movenum("Leech Life"), movenum("Substitute"), movenum("X-Scissor"), movenum("Giga Drain"), movenum("Bug Buzz"), movenum("Tera Blast"), movenum("First Impression"), movenum("Lunge"), movenum("Memento"), movenum("Sucker Punch")] }, "918": { "types": ["Bug", "???"], "name": "Spidops", "stats": [60, 79, 92, 52, 86, 35], "abilities": ["Insomnia", "Stakeout"], "tier": "SM NU", "height": 1, "weight": 16.5, "moves": [movenum("Tackle"), movenum("String Shot"), movenum("Silk Trap"), movenum("Struggle Bug"), movenum("Assurance"), movenum("Feint"), movenum("Bug Bite"), movenum("Block"), movenum("Counter"), movenum("Headbutt"), movenum("Sticky Web"), movenum("Gastro Acid"), movenum("Circle Throw"), movenum("Throat Chop"), movenum("Skitter Smack"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Aerial Ace"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("False Swipe"), movenum("Brick Break"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Spikes"), movenum("Toxic Spikes"), movenum("Leech Life"), movenum("Substitute"), movenum("X-Scissor"), movenum("Giga Drain"), movenum("Reversal"), movenum("Giga Impact"), movenum("Bug Buzz"), movenum("Tera Blast"), movenum("First Impression"), movenum("Lunge"), movenum("Memento"), movenum("Sucker Punch")] }, "919": { "types": ["Bug", "???"], "name": "Nymble", "stats": [33, 46, 40, 21, 25, 45], "abilities": ["Swarm", "Tinted Lens"], "tier": "SM LC", "height": 0.2, "weight": 1, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Struggle Bug"), movenum("Astonish"), movenum("Assurance"), movenum("Double Kick"), movenum("Screech"), movenum("Endure"), movenum("Bug Bite"), movenum("Feint"), movenum("Agility"), movenum("Sucker Punch"), movenum("First Impression"), movenum("Take Down"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("U-turn"), movenum("Sleep Talk"), movenum("Rest"), movenum("Leech Life"), movenum("Substitute"), movenum("X-Scissor"), movenum("Bug Buzz"), movenum("Tera Blast"), movenum("Counter"), movenum("Skitter Smack")] }, "920": { "types": ["Bug", "Dark"], "name": "Lokix", "stats": [71, 102, 78, 52, 55, 92], "abilities": ["Swarm", "Tinted Lens"], "tier": "SM UU", "height": 1, "weight": 17.5, "moves": [movenum("Low Kick"), movenum("Detect"), movenum("Tackle"), movenum("Leer"), movenum("Lunge"), movenum("Struggle Bug"), movenum("Astonish"), movenum("Assurance"), movenum("Double Kick"), movenum("Screech"), movenum("Endure"), movenum("Bug Bite"), movenum("Feint"), movenum("Agility"), movenum("Throat Chop"), movenum("Sucker Punch"), movenum("First Impression"), movenum("Bounce"), movenum("Axe Kick"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Aerial Ace"), movenum("Low Sweep"), movenum("Fling"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Brick Break"), movenum("U-turn"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Dark Pulse"), movenum("Leech Life"), movenum("Substitute"), movenum("X-Scissor"), movenum("Reversal"), movenum("Giga Impact"), movenum("Bug Buzz"), movenum("Tera Blast"), movenum("Counter"), movenum("Skitter Smack")] }, "921": { "types": ["Electric", "???"], "name": "Pawmi", "stats": [45, 50, 20, 40, 25, 60], "abilities": ["Static", "Natural Cure", "Iron Fist"], "tier": "SM LC", "height": 0.3, "weight": 2.5, "moves": [movenum("Scratch"), movenum("Growl"), movenum("Thunder Shock"), movenum("Quick Attack"), movenum("Charge"), movenum("Nuzzle"), movenum("Dig"), movenum("Bite"), movenum("Spark"), movenum("Thunder Wave"), movenum("Entrainment"), movenum("Slam"), movenum("Discharge"), movenum("Agility"), movenum("Wild Charge"), movenum("Take Down"), movenum("Charm"), movenum("Protect"), movenum("Thunder Fang"), movenum("Thief"), movenum("Charge Beam"), movenum("Facade"), movenum("Metal Claw"), movenum("Swift"), movenum("Fling"), movenum("Endure"), movenum("Volt Switch"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Rest"), movenum("Eerie Impulse"), movenum("Substitute"), movenum("Crunch"), movenum("Encore"), movenum("Thunderbolt"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Electric Terrain"), movenum("Thunder"), movenum("Tera Blast"), movenum("Fake Out"), movenum("Mach Punch"), movenum("Sweet Kiss"), movenum("Wish")] }, "922": { "types": ["Electric", "Fighting"], "name": "Pawmo", "stats": [60, 75, 40, 50, 40, 85], "abilities": ["Volt Absorb", "Iron Fist"], "tier": "SM PU", "height": 0.4, "weight": 6.5, "moves": [movenum("Scratch"), movenum("Growl"), movenum("Arm Thrust"), movenum("Thunder Shock"), movenum("Quick Attack"), movenum("Charge"), movenum("Nuzzle"), movenum("Dig"), movenum("Bite"), movenum("Spark"), movenum("Thunder Wave"), movenum("Slam"), movenum("Entrainment"), movenum("Discharge"), movenum("Agility"), movenum("Wild Charge"), movenum("Take Down"), movenum("Charm"), movenum("Protect"), movenum("Thunder Fang"), movenum("Low Kick"), movenum("Thief"), movenum("Charge Beam"), movenum("Facade"), movenum("Metal Claw"), movenum("Swift"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Volt Switch"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Thunder Punch"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Rest"), movenum("Eerie Impulse"), movenum("Substitute"), movenum("Crunch"), movenum("Encore"), movenum("Thunderbolt"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Electric Terrain"), movenum("Thunder"), movenum("Tera Blast"), movenum("Fake Out"), movenum("Mach Punch"), movenum("Sweet Kiss"), movenum("Wish")] }, "923": { "types": ["Electric", "Fighting"], "name": "Pawmot", "stats": [70, 115, 70, 70, 60, 105], "abilities": ["Volt Absorb", "Iron Fist"], "tier": "SM UU", "height": 0.9, "weight": 41, "moves": [movenum("Wild Charge"), movenum("Scratch"), movenum("Growl"), movenum("Revival Blessing"), movenum("Thunder Shock"), movenum("Quick Attack"), movenum("Charge"), movenum("Nuzzle"), movenum("Dig"), movenum("Bite"), movenum("Spark"), movenum("Arm Thrust"), movenum("Thunder Wave"), movenum("Slam"), movenum("Entrainment"), movenum("Close Combat"), movenum("Discharge"), movenum("Agility"), movenum("Double Shock"), movenum("Take Down"), movenum("Charm"), movenum("Protect"), movenum("Thunder Fang"), movenum("Low Kick"), movenum("Thief"), movenum("Charge Beam"), movenum("Facade"), movenum("Metal Claw"), movenum("Swift"), movenum("Rock Tomb"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Volt Switch"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Brick Break"), movenum("Bulk Up"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Electro Ball"), movenum("Metronome"), movenum("Grass Knot"), movenum("Rest"), movenum("Body Press"), movenum("Eerie Impulse"), movenum("Substitute"), movenum("Crunch"), movenum("Encore"), movenum("Thunderbolt"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Electric Terrain"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast"), movenum("Fake Out"), movenum("Mach Punch"), movenum("Sweet Kiss"), movenum("Wish")] }, "924": { "types": ["Normal", "???"], "name": "Tandemaus", "stats": [50, 50, 45, 40, 45, 75], "abilities": ["Run Away", "Pickup", "Own Tempo"], "tier": "SM LC", "height": 0.3, "weight": 1.8, "moves": [movenum("Pound"), movenum("Baby-Doll Eyes"), movenum("Echoed Voice"), movenum("Helping Hand"), movenum("Super Fang"), movenum("Double Hit"), movenum("Bullet Seed"), movenum("Encore"), movenum("Play Rough"), movenum("Hyper Voice"), movenum("Charm"), movenum("Beat Up"), movenum("Copycat"), movenum("Population Bomb"), movenum("Take Down"), movenum("Fake Tears"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Water Pulse"), movenum("Low Kick"), movenum("Thief"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Mud Shot"), movenum("Low Sweep"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Crunch"), movenum("Baton Pass"), movenum("Tera Blast"), movenum("After You"), movenum("Bite"), movenum("Feint"), movenum("Switcheroo"), movenum("Tickle")] }, "925": { "types": ["Normal", "???"], "name": "Maushold", "stats": [74, 75, 70, 65, 75, 111], "abilities": ["Friend Guard", "Cheek Pouch", "Technician"], "tier": "SM UU", "height": 0.3, "weight": 2.8, "moves": [movenum("Follow Me"), movenum("Tidy Up"), movenum("Pound"), movenum("Baby-Doll Eyes"), movenum("Echoed Voice"), movenum("Helping Hand"), movenum("Super Fang"), movenum("Double Hit"), movenum("Bullet Seed"), movenum("Encore"), movenum("Play Rough"), movenum("Hyper Voice"), movenum("Charm"), movenum("Beat Up"), movenum("Copycat"), movenum("Population Bomb"), movenum("Take Down"), movenum("Fake Tears"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Water Pulse"), movenum("Low Kick"), movenum("Thief"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Mud Shot"), movenum("Low Sweep"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Crunch"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("After You"), movenum("Baton Pass"), movenum("Bite"), movenum("Feint"), movenum("Switcheroo"), movenum("Tickle")] }, "66461": { "types": ["Normal", "???"], "name": "Maushold-Three", "stats": [74, 75, 70, 65, 75, 111], "abilities": ["Friend Guard", "Cheek Pouch", "Technician"], "tier": "SM UU", "height": 0.3, "weight": 2.3, "moves": [movenum("Follow Me"), movenum("Tidy Up"), movenum("Pound"), movenum("Baby-Doll Eyes"), movenum("Echoed Voice"), movenum("Helping Hand"), movenum("Super Fang"), movenum("Double Hit"), movenum("Bullet Seed"), movenum("Encore"), movenum("Play Rough"), movenum("Hyper Voice"), movenum("Charm"), movenum("Beat Up"), movenum("Copycat"), movenum("Population Bomb"), movenum("Take Down"), movenum("Fake Tears"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Water Pulse"), movenum("Low Kick"), movenum("Thief"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Mud Shot"), movenum("Low Sweep"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Crunch"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("After You"), movenum("Baton Pass"), movenum("Bite"), movenum("Feint"), movenum("Switcheroo"), movenum("Tickle")] }, "926": { "types": ["Fairy", "???"], "name": "Fidough", "stats": [37, 55, 70, 30, 55, 65], "abilities": ["Own Tempo", "Klutz"], "tier": "SM LC", "height": 0.3, "weight": 10.9, "moves": [movenum("Tackle"), movenum("Growl"), movenum("Lick"), movenum("Tail Whip"), movenum("Covet"), movenum("Bite"), movenum("Baby-Doll Eyes"), movenum("Play Rough"), movenum("Work Up"), movenum("Baton Pass"), movenum("Roar"), movenum("Double-Edge"), movenum("Charm"), movenum("Crunch"), movenum("Last Resort"), movenum("Take Down"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Trailblaze"), movenum("Facade"), movenum("Snarl"), movenum("Mud Shot"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Dazzling Gleam"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Helping Hand"), movenum("Misty Terrain"), movenum("Tera Blast"), movenum("Copycat"), movenum("Howl"), movenum("Sweet Scent"), movenum("Wish"), movenum("Yawn")] }, "927": { "types": ["Fairy", "???"], "name": "Dachsbun", "stats": [57, 80, 115, 50, 80, 95], "abilities": ["Well-Baked Body", "Aroma Veil"], "tier": "SM NU", "height": 0.5, "weight": 14.9, "moves": [movenum("Tackle"), movenum("Growl"), movenum("Lick"), movenum("Tail Whip"), movenum("Covet"), movenum("Bite"), movenum("Baby-Doll Eyes"), movenum("Play Rough"), movenum("Work Up"), movenum("Baton Pass"), movenum("Roar"), movenum("Double-Edge"), movenum("Charm"), movenum("Crunch"), movenum("Last Resort"), movenum("Take Down"), movenum("Agility"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Trailblaze"), movenum("Facade"), movenum("Snarl"), movenum("Mud Shot"), movenum("Draining Kiss"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Dazzling Gleam"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Substitute"), movenum("Helping Hand"), movenum("Misty Terrain"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Copycat"), movenum("Howl"), movenum("Sweet Scent"), movenum("Wish"), movenum("Yawn")] }, "928": { "types": ["Grass", "Normal"], "name": "Smoliv", "stats": [41, 35, 45, 58, 51, 30], "abilities": ["Early Bird", "Harvest"], "tier": "SM LC", "height": 0.3, "weight": 6.5, "moves": [movenum("Tackle"), movenum("Sweet Scent"), movenum("Absorb"), movenum("Growth"), movenum("Razor Leaf"), movenum("Helping Hand"), movenum("Flail"), movenum("Mega Drain"), movenum("Grassy Terrain"), movenum("Seed Bomb"), movenum("Energy Ball"), movenum("Leech Seed"), movenum("Terrain Pulse"), movenum("Charm"), movenum("Protect"), movenum("Trailblaze"), movenum("Facade"), movenum("Swift"), movenum("Magical Leaf"), movenum("Endure"), movenum("Sunny Day"), movenum("Bullet Seed"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Rest"), movenum("Substitute"), movenum("Giga Drain"), movenum("Earth Power"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Memento"), movenum("Strength Sap"), movenum("Synthesis"), movenum("Weather Ball")] }, "929": { "types": ["Grass", "Normal"], "name": "Dolliv", "stats": [52, 53, 60, 78, 78, 33], "abilities": ["Early Bird", "Harvest"], "tier": "SM PU", "height": 0.6, "weight": 11.9, "moves": [movenum("Tackle"), movenum("Sweet Scent"), movenum("Absorb"), movenum("Growth"), movenum("Razor Leaf"), movenum("Helping Hand"), movenum("Flail"), movenum("Mega Drain"), movenum("Grassy Terrain"), movenum("Seed Bomb"), movenum("Energy Ball"), movenum("Leech Seed"), movenum("Terrain Pulse"), movenum("Charm"), movenum("Protect"), movenum("Trailblaze"), movenum("Facade"), movenum("Swift"), movenum("Magical Leaf"), movenum("Endure"), movenum("Sunny Day"), movenum("Bullet Seed"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Rest"), movenum("Substitute"), movenum("Giga Drain"), movenum("Earth Power"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Memento"), movenum("Strength Sap"), movenum("Synthesis"), movenum("Weather Ball")] }, "930": { "types": ["Grass", "Normal"], "name": "Arboliva", "stats": [78, 69, 90, 125, 109, 39], "abilities": ["Seed Sower", "Harvest"], "tier": "SM LU", "height": 1.4, "weight": 48.2, "moves": [movenum("Mirror Coat"), movenum("Safeguard"), movenum("Tackle"), movenum("Sweet Scent"), movenum("Absorb"), movenum("Growth"), movenum("Razor Leaf"), movenum("Helping Hand"), movenum("Flail"), movenum("Mega Drain"), movenum("Grassy Terrain"), movenum("Seed Bomb"), movenum("Energy Ball"), movenum("Leech Seed"), movenum("Terrain Pulse"), movenum("Petal Blizzard"), movenum("Petal Dance"), movenum("Charm"), movenum("Protect"), movenum("Trailblaze"), movenum("Facade"), movenum("Swift"), movenum("Magical Leaf"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Bullet Seed"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Metronome"), movenum("Grass Knot"), movenum("Rest"), movenum("Substitute"), movenum("Giga Drain"), movenum("Hyper Voice"), movenum("Encore"), movenum("Pollen Puff"), movenum("Earth Power"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Memento"), movenum("Strength Sap"), movenum("Synthesis"), movenum("Weather Ball")] }, "931": { "types": ["Normal", "Flying"], "name": "Squawkabilly", "stats": [82, 96, 51, 45, 51, 92], "abilities": ["Intimidate", "Hustle", "Guts"], "tier": "SM PU", "height": 0.6, "weight": 2.4, "moves": [movenum("Growl"), movenum("Peck"), movenum("Mimic"), movenum("Quick Attack"), movenum("Torment"), movenum("Aerial Ace"), movenum("Fury Attack"), movenum("Taunt"), movenum("Uproar"), movenum("Copycat"), movenum("Fly"), movenum("Facade"), movenum("Swagger"), movenum("Brave Bird"), movenum("Roost"), movenum("Reversal"), movenum("Take Down"), movenum("Fake Tears"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Air Cutter"), movenum("Endure"), movenum("Sunny Day"), movenum("U-turn"), movenum("Foul Play"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Rest"), movenum("Substitute"), movenum("Tailwind"), movenum("Hyper Voice"), movenum("Heat Wave"), movenum("Helping Hand"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Double-Edge"), movenum("Final Gambit"), movenum("Flatter"), movenum("Parting Shot")] }, "66467": { "types": ["Normal", "Flying"], "name": "Squawkabilly-Blue", "stats": [82, 96, 51, 45, 51, 92], "abilities": ["Intimidate", "Hustle", "Guts"], "tier": "SM PU", "height": 0.6, "weight": 2.4, "moves": [movenum("Growl"), movenum("Peck"), movenum("Mimic"), movenum("Quick Attack"), movenum("Torment"), movenum("Aerial Ace"), movenum("Fury Attack"), movenum("Taunt"), movenum("Uproar"), movenum("Copycat"), movenum("Fly"), movenum("Facade"), movenum("Swagger"), movenum("Brave Bird"), movenum("Roost"), movenum("Reversal"), movenum("Take Down"), movenum("Fake Tears"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Air Cutter"), movenum("Endure"), movenum("Sunny Day"), movenum("U-turn"), movenum("Foul Play"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Rest"), movenum("Substitute"), movenum("Tailwind"), movenum("Hyper Voice"), movenum("Heat Wave"), movenum("Helping Hand"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Double-Edge"), movenum("Final Gambit"), movenum("Flatter"), movenum("Parting Shot")] }, "132003": { "types": ["Normal", "Flying"], "name": "Squawkabilly-Yellow", "stats": [82, 96, 51, 45, 51, 92], "abilities": ["Intimidate", "Hustle", "Sheer Force"], "tier": "SM PU", "height": 0.6, "weight": 2.4, "moves": [movenum("Growl"), movenum("Peck"), movenum("Mimic"), movenum("Quick Attack"), movenum("Torment"), movenum("Aerial Ace"), movenum("Fury Attack"), movenum("Taunt"), movenum("Uproar"), movenum("Copycat"), movenum("Fly"), movenum("Facade"), movenum("Swagger"), movenum("Brave Bird"), movenum("Roost"), movenum("Reversal"), movenum("Take Down"), movenum("Fake Tears"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Air Cutter"), movenum("Endure"), movenum("Sunny Day"), movenum("U-turn"), movenum("Foul Play"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Rest"), movenum("Substitute"), movenum("Tailwind"), movenum("Hyper Voice"), movenum("Heat Wave"), movenum("Helping Hand"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Double-Edge"), movenum("Final Gambit"), movenum("Flatter"), movenum("Parting Shot")] }, "197539": { "types": ["Normal", "Flying"], "name": "Squawkabilly-White", "stats": [82, 96, 51, 45, 51, 92], "abilities": ["Intimidate", "Hustle", "Sheer Force"], "tier": "SM PU", "height": 0.6, "weight": 2.4, "moves": [movenum("Growl"), movenum("Peck"), movenum("Mimic"), movenum("Quick Attack"), movenum("Torment"), movenum("Aerial Ace"), movenum("Fury Attack"), movenum("Taunt"), movenum("Uproar"), movenum("Copycat"), movenum("Fly"), movenum("Facade"), movenum("Swagger"), movenum("Brave Bird"), movenum("Roost"), movenum("Reversal"), movenum("Take Down"), movenum("Fake Tears"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Air Cutter"), movenum("Endure"), movenum("Sunny Day"), movenum("U-turn"), movenum("Foul Play"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Rest"), movenum("Substitute"), movenum("Tailwind"), movenum("Hyper Voice"), movenum("Heat Wave"), movenum("Helping Hand"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Double-Edge"), movenum("Final Gambit"), movenum("Flatter"), movenum("Parting Shot")] }, "932": { "types": ["Rock", "???"], "name": "Nacli", "stats": [55, 55, 75, 35, 35, 25], "abilities": ["Purifying Salt", "Sturdy", "Clear Body"], "tier": "SM LC", "height": 0.4, "weight": 16, "moves": [movenum("Tackle"), movenum("Harden"), movenum("Rock Throw"), movenum("Mud Shot"), movenum("Smack Down"), movenum("Rock Polish"), movenum("Headbutt"), movenum("Iron Defense"), movenum("Recover"), movenum("Rock Slide"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Take Down"), movenum("Protect"), movenum("Facade"), movenum("Bulldoze"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Flash Cannon"), movenum("Iron Head"), movenum("Power Gem"), movenum("Substitute"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Tera Blast"), movenum("Ancient Power"), movenum("Curse"), movenum("Fissure")] }, "933": { "types": ["Rock", "???"], "name": "Naclstack", "stats": [60, 60, 100, 35, 65, 35], "abilities": ["Purifying Salt", "Sturdy", "Clear Body"], "tier": "SM LU", "height": 0.6, "weight": 105, "moves": [movenum("Tackle"), movenum("Harden"), movenum("Salt Cure"), movenum("Rock Throw"), movenum("Mud Shot"), movenum("Smack Down"), movenum("Rock Polish"), movenum("Headbutt"), movenum("Iron Defense"), movenum("Recover"), movenum("Rock Slide"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Take Down"), movenum("Protect"), movenum("Facade"), movenum("Bulldoze"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Iron Head"), movenum("Power Gem"), movenum("Substitute"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Ancient Power"), movenum("Curse"), movenum("Fissure")] }, "934": { "types": ["Rock", "???"], "name": "Garganacl", "stats": [100, 100, 130, 45, 90, 35], "abilities": ["Purifying Salt", "Sturdy", "Clear Body"], "tier": "SM OU", "height": 2.3, "weight": 240, "moves": [movenum("Block"), movenum("Wide Guard"), movenum("Rock Blast"), movenum("Tackle"), movenum("Harden"), movenum("Hammer Arm"), movenum("Rock Throw"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Rock Polish"), movenum("Headbutt"), movenum("Salt Cure"), movenum("Recover"), movenum("Rock Slide"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Explosion"), movenum("Take Down"), movenum("Protect"), movenum("Facade"), movenum("Bulldoze"), movenum("Fling"), movenum("Avalanche"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Iron Head"), movenum("Power Gem"), movenum("Substitute"), movenum("Iron Defense"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Ancient Power"), movenum("Curse"), movenum("Fissure")] }, "935": { "types": ["Fire", "???"], "name": "Charcadet", "stats": [40, 50, 40, 50, 40, 35], "abilities": ["Flash Fire", "Flame Body"], "tier": "SM LC", "height": 0.6, "weight": 10.5, "moves": [movenum("Ember"), movenum("Leer"), movenum("Astonish"), movenum("Clear Smog"), movenum("Fire Spin"), movenum("Will-O-Wisp"), movenum("Night Shade"), movenum("Flame Charge"), movenum("Incinerate"), movenum("Lava Plume"), movenum("Take Down"), movenum("Protect"), movenum("Confuse Ray"), movenum("Facade"), movenum("Endure"), movenum("Sunny Day"), movenum("Sleep Talk"), movenum("Substitute"), movenum("Heat Wave"), movenum("Flamethrower"), movenum("Helping Hand"), movenum("Fire Blast"), movenum("Overheat"), movenum("Flare Blitz"), movenum("Tera Blast"), movenum("Destiny Bond"), movenum("Disable"), movenum("Spite")] }, "936": { "types": ["Fire", "Psychic"], "name": "Armarouge", "stats": [85, 60, 100, 125, 80, 75], "abilities": ["Flash Fire", "Weak Armor"], "tier": "SM OU", "height": 1.5, "weight": 85, "moves": [movenum("Mystical Fire"), movenum("Wide Guard"), movenum("Ember"), movenum("Leer"), movenum("Astonish"), movenum("Psyshock"), movenum("Clear Smog"), movenum("Fire Spin"), movenum("Will-O-Wisp"), movenum("Night Shade"), movenum("Flame Charge"), movenum("Incinerate"), movenum("Lava Plume"), movenum("Calm Mind"), movenum("Ally Switch"), movenum("Flamethrower"), movenum("Expanding Force"), movenum("Armor Cannon"), movenum("Take Down"), movenum("Protect"), movenum("Acid Spray"), movenum("Psybeam"), movenum("Confuse Ray"), movenum("Facade"), movenum("Stored Power"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Rest"), movenum("Taunt"), movenum("Flash Cannon"), movenum("Dark Pulse"), movenum("Substitute"), movenum("Iron Defense"), movenum("Trick"), movenum("Aura Sphere"), movenum("Shadow Ball"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Energy Ball"), movenum("Psychic"), movenum("Helping Hand"), movenum("Psychic Terrain"), movenum("Fire Blast"), movenum("Overheat"), movenum("Focus Blast"), movenum("Trick Room"), movenum("Flare Blitz"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Destiny Bond"), movenum("Disable"), movenum("Spite")] }, "937": { "types": ["Fire", "Ghost"], "name": "Ceruledge", "stats": [75, 125, 80, 60, 100, 85], "abilities": ["Flash Fire", "Weak Armor"], "tier": "SM OU", "height": 1.6, "weight": 62, "moves": [movenum("Night Slash"), movenum("Shadow Sneak"), movenum("Quick Guard"), movenum("Solar Blade"), movenum("Leer"), movenum("Ember"), movenum("Astonish"), movenum("Shadow Claw"), movenum("Clear Smog"), movenum("Fire Spin"), movenum("Will-O-Wisp"), movenum("Night Shade"), movenum("Flame Charge"), movenum("Incinerate"), movenum("Lava Plume"), movenum("Swords Dance"), movenum("Ally Switch"), movenum("Bitter Blade"), movenum("Psycho Cut"), movenum("Flare Blitz"), movenum("Take Down"), movenum("Protect"), movenum("Confuse Ray"), movenum("Facade"), movenum("Hex"), movenum("Stored Power"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("False Swipe"), movenum("Brick Break"), movenum("Bulk Up"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Dragon Claw"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Iron Head"), movenum("Substitute"), movenum("Iron Defense"), movenum("X-Scissor"), movenum("Shadow Ball"), movenum("Heat Wave"), movenum("Flamethrower"), movenum("Helping Hand"), movenum("Fire Blast"), movenum("Phantom Force"), movenum("Overheat"), movenum("Close Combat"), movenum("Tera Blast"), movenum("Destiny Bond"), movenum("Disable"), movenum("Spite")] }, "938": { "types": ["Electric", "???"], "name": "Tadbulb", "stats": [61, 31, 41, 59, 35, 45], "abilities": ["Own Tempo", "Static", "Damp"], "tier": "SM LC", "height": 0.3, "weight": 0.4, "moves": [movenum("Tackle"), movenum("Mud-Slap"), movenum("Thunder Shock"), movenum("Water Gun"), movenum("Charge"), movenum("Spark"), movenum("Mud Shot"), movenum("Flail"), movenum("Discharge"), movenum("Weather Ball"), movenum("Electric Terrain"), movenum("Sucker Punch"), movenum("Zap Cannon"), movenum("Protect"), movenum("Water Pulse"), movenum("Acid Spray"), movenum("Confuse Ray"), movenum("Chilling Water"), movenum("Charge Beam"), movenum("Swift"), movenum("Endure"), movenum("Volt Switch"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Thunder Wave"), movenum("Rest"), movenum("Eerie Impulse"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Thunderbolt"), movenum("Wild Charge"), movenum("Thunder"), movenum("Tera Blast"), movenum("Muddy Water"), movenum("Parabolic Charge"), movenum("Soak")] }, "939": { "types": ["Electric", "???"], "name": "Bellibolt", "stats": [109, 64, 91, 103, 83, 45], "abilities": ["Electromorphosis", "Static", "Damp"], "tier": "SM LU", "height": 1.2, "weight": 113, "moves": [movenum("Slack Off"), movenum("Tackle"), movenum("Mud-Slap"), movenum("Thunder Shock"), movenum("Water Gun"), movenum("Charge"), movenum("Spark"), movenum("Mud Shot"), movenum("Flail"), movenum("Discharge"), movenum("Weather Ball"), movenum("Electric Terrain"), movenum("Sucker Punch"), movenum("Zap Cannon"), movenum("Protect"), movenum("Water Pulse"), movenum("Acid Spray"), movenum("Confuse Ray"), movenum("Chilling Water"), movenum("Charge Beam"), movenum("Swift"), movenum("Endure"), movenum("Volt Switch"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Thunder Wave"), movenum("Rest"), movenum("Eerie Impulse"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Thunderbolt"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast"), movenum("Muddy Water"), movenum("Parabolic Charge"), movenum("Soak")] }, "940": { "types": ["Electric", "Flying"], "name": "Wattrel", "stats": [40, 40, 35, 55, 40, 70], "abilities": ["Wind Power", "Volt Absorb", "Competitive"], "tier": "SM LC", "height": 0.4, "weight": 3.6, "moves": [movenum("Peck"), movenum("Growl"), movenum("Thunder Shock"), movenum("Quick Attack"), movenum("Pluck"), movenum("Spark"), movenum("Uproar"), movenum("Roost"), movenum("Dual Wingbeat"), movenum("Agility"), movenum("Volt Switch"), movenum("Discharge"), movenum("Take Down"), movenum("Protect"), movenum("Acrobatics"), movenum("Charge Beam"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Air Cutter"), movenum("Endure"), movenum("U-turn"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Thunder Wave"), movenum("Rest"), movenum("Eerie Impulse"), movenum("Fly"), movenum("Substitute"), movenum("Tailwind"), movenum("Thunderbolt"), movenum("Electric Terrain"), movenum("Wild Charge"), movenum("Hurricane"), movenum("Brave Bird"), movenum("Thunder"), movenum("Tera Blast"), movenum("Endeavor"), movenum("Feather Dance"), movenum("Spit Up"), movenum("Stockpile"), movenum("Swallow"), movenum("Weather Ball")] }, "941": { "types": ["Electric", "Flying"], "name": "Kilowattrel", "stats": [70, 70, 60, 105, 60, 125], "abilities": ["Wind Power", "Volt Absorb", "Competitive"], "tier": "SM LU", "height": 1.4, "weight": 38.6, "moves": [movenum("Peck"), movenum("Growl"), movenum("Electro Ball"), movenum("Thunder Shock"), movenum("Quick Attack"), movenum("Pluck"), movenum("Spark"), movenum("Uproar"), movenum("Roost"), movenum("Dual Wingbeat"), movenum("Agility"), movenum("Volt Switch"), movenum("Discharge"), movenum("Hurricane"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Charge Beam"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Air Cutter"), movenum("Endure"), movenum("U-turn"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Thunder Wave"), movenum("Rest"), movenum("Eerie Impulse"), movenum("Fly"), movenum("Substitute"), movenum("Tailwind"), movenum("Thunderbolt"), movenum("Electric Terrain"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Brave Bird"), movenum("Thunder"), movenum("Tera Blast"), movenum("Endeavor"), movenum("Feather Dance"), movenum("Spit Up"), movenum("Stockpile"), movenum("Swallow"), movenum("Weather Ball")] }, "942": { "types": ["Dark", "???"], "name": "Maschiff", "stats": [60, 78, 60, 40, 51, 51, ], "abilities": ["Intimidate", "Run Away", "Stakeout"], "tier": "SM LC", "height": 0.5, "weight": 16, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Scary Face"), movenum("Lick"), movenum("Snarl"), movenum("Hone Claws"), movenum("Bite"), movenum("Roar"), movenum("Headbutt"), movenum("Payback"), movenum("Crunch"), movenum("Swagger"), movenum("Reversal"), movenum("Jaw Lock"), movenum("Double-Edge"), movenum("Take Down"), movenum("Charm"), movenum("Fake Tears"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Dark Pulse"), movenum("Substitute"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Tera Blast"), movenum("Destiny Bond"), movenum("Endeavor"), movenum("Retaliate")] }, "943": { "types": ["Dark", "???"], "name": "Mabosstiff", "stats": [80, 120, 90, 60, 70, 85], "abilities": ["Intimidate", "Guard Dog", "Stakeout"], "tier": "SM NU", "height": 1.1, "weight": 61, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Scary Face"), movenum("Comeuppance"), movenum("Lick"), movenum("Snarl"), movenum("Hone Claws"), movenum("Bite"), movenum("Roar"), movenum("Headbutt"), movenum("Payback"), movenum("Crunch"), movenum("Swagger"), movenum("Reversal"), movenum("Jaw Lock"), movenum("Double-Edge"), movenum("Outrage"), movenum("Take Down"), movenum("Charm"), movenum("Fake Tears"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Dark Pulse"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Destiny Bond"), movenum("Endeavor"), movenum("Retaliate")] }, "944": { "types": ["Poison", "Normal"], "name": "Shroodle", "stats": [40, 65, 35, 40, 35, 75], "abilities": ["Unburden", "Pickpocket", "Prankster"], "tier": "SM LC", "height": 0.2, "weight": 0.7, "moves": [movenum("Scratch"), movenum("Leer"), movenum("Acid Spray"), movenum("Bite"), movenum("Fury Swipes"), movenum("Switcheroo"), movenum("Poison Fang"), movenum("Flatter"), movenum("Slash"), movenum("U-turn"), movenum("Poison Jab"), movenum("Taunt"), movenum("Substitute"), movenum("Knock Off"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Acrobatics"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Mud Shot"), movenum("Fling"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Metronome"), movenum("Rest"), movenum("Swords Dance"), movenum("Encore"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Nasty Plot"), movenum("Sludge Bomb"), movenum("Tera Blast"), movenum("Copycat"), movenum("Cross Poison"), movenum("Parting Shot"), movenum("Super Fang"), movenum("Swagger"), movenum("Toxic")] }, "945": { "types": ["Poison", "Normal"], "name": "Grafaiai", "stats": [63, 95, 65, 80, 72, 110], "abilities": ["Unburden", "Poison Touch", "Prankster"], "tier": "SM UU", "height": 0.7, "weight": 27.2, "moves": [movenum("Scratch"), movenum("Leer"), movenum("Doodle"), movenum("Acid Spray"), movenum("Fury Swipes"), movenum("Switcheroo"), movenum("Poison Fang"), movenum("Flatter"), movenum("Slash"), movenum("U-turn"), movenum("Poison Jab"), movenum("Taunt"), movenum("Substitute"), movenum("Knock Off"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Poison Tail"), movenum("Mud Shot"), movenum("Low Sweep"), movenum("Fling"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Shadow Claw"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Metronome"), movenum("Rest"), movenum("Swords Dance"), movenum("X-Scissor"), movenum("Encore"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Nasty Plot"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Tera Blast"), movenum("Copycat"), movenum("Cross Poison"), movenum("Parting Shot"), movenum("Super Fang"), movenum("Swagger"), movenum("Toxic"), movenum("Bite")] }, "946": { "types": ["Grass", "Ghost"], "name": "Bramblin", "stats": [40, 65, 30, 45, 35, 60], "abilities": ["Wind Rider", "Infiltrator"], "tier": "SM LC", "height": 0.6, "weight": 0.6, "moves": [movenum("Astonish"), movenum("Defense Curl"), movenum("Rollout"), movenum("Absorb"), movenum("Rapid Spin"), movenum("Bullet Seed"), movenum("Infestation"), movenum("Hex"), movenum("Mega Drain"), movenum("Disable"), movenum("Phantom Force"), movenum("Giga Drain"), movenum("Curse"), movenum("Pain Split"), movenum("Power Whip"), movenum("Scary Face"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Rest"), movenum("Spikes"), movenum("Substitute"), movenum("Shadow Ball"), movenum("Energy Ball"), movenum("Grassy Terrain"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Beat Up"), movenum("Block"), movenum("Leech Seed"), movenum("Shadow Sneak"), movenum("Strength Sap")] }, "947": { "types": ["Grass", "Ghost"], "name": "Brambleghast", "stats": [55, 115, 70, 80, 70, 90], "abilities": ["Wind Rider", "Infiltrator"], "tier": "SM UU", "height": 1.2, "weight": 6, "moves": [movenum("Astonish"), movenum("Defense Curl"), movenum("Rollout"), movenum("Absorb"), movenum("Rapid Spin"), movenum("Bullet Seed"), movenum("Infestation"), movenum("Hex"), movenum("Mega Drain"), movenum("Disable"), movenum("Phantom Force"), movenum("Giga Drain"), movenum("Curse"), movenum("Pain Split"), movenum("Power Whip"), movenum("Scary Face"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Trailblaze"), movenum("Pounce"), movenum("Facade"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Rest"), movenum("Spikes"), movenum("Substitute"), movenum("Shadow Ball"), movenum("Energy Ball"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Beat Up"), movenum("Block"), movenum("Leech Seed"), movenum("Shadow Sneak"), movenum("Strength Sap")] }, "948": { "types": ["Ground", "Grass"], "name": "Toedscool", "stats": [40, 40, 35, 50, 100, 70], "abilities": ["Mycelium Might"], "tier": "SM LC", "height": 0.9, "weight": 33, "moves": [movenum("Wrap"), movenum("Mud-Slap"), movenum("Absorb"), movenum("Poison Powder"), movenum("Stun Spore"), movenum("Supersonic"), movenum("Tackle"), movenum("Mega Drain"), movenum("Screech"), movenum("Mud Shot"), movenum("Hex"), movenum("Seed Bomb"), movenum("Spore"), movenum("Growth"), movenum("Giga Drain"), movenum("Earth Power"), movenum("Power Whip"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Confuse Ray"), movenum("Trailblaze"), movenum("Swift"), movenum("Magical Leaf"), movenum("Venoshock"), movenum("Endure"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Spikes"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Energy Ball"), movenum("Grassy Terrain"), movenum("Sludge Bomb"), movenum("Leaf Storm"), movenum("Trick Room"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Acupressure"), movenum("Knock Off"), movenum("Leech Seed"), movenum("Mirror Coat"), movenum("Rage Powder"), movenum("Rapid Spin"), movenum("Tickle"), movenum("Toxic")] }, "949": { "types": ["Ground", "Grass"], "name": "Toedscruel", "stats": [80, 70, 65, 80, 120, 100, ], "abilities": ["Mycelium Might"], "tier": "SM UU", "height": 1.9, "weight": 58, "moves": [movenum("Reflect Type"), movenum("Wrap"), movenum("Mud-Slap"), movenum("Absorb"), movenum("Stun Spore"), movenum("Poison Powder"), movenum("Supersonic"), movenum("Tackle"), movenum("Mega Drain"), movenum("Screech"), movenum("Mud Shot"), movenum("Hex"), movenum("Seed Bomb"), movenum("Spore"), movenum("Growth"), movenum("Giga Drain"), movenum("Earth Power"), movenum("Power Whip"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Confuse Ray"), movenum("Trailblaze"), movenum("Swift"), movenum("Magical Leaf"), movenum("Venoshock"), movenum("Endure"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Spikes"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Energy Ball"), movenum("Grassy Terrain"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Acupressure"), movenum("Knock Off"), movenum("Leech Seed"), movenum("Mirror Coat"), movenum("Rage Powder"), movenum("Rapid Spin"), movenum("Tickle"), movenum("Toxic")] }, "950": { "types": ["Rock", "???"], "name": "Klawf", "stats": [70, 100, 115, 35, 55, 75], "abilities": ["Anger Shell", "Shell Armor", "Regenerator"], "tier": "SM NU", "height": 1.3, "weight": 79, "moves": [movenum("Vise Grip"), movenum("Rock Throw"), movenum("Harden"), movenum("Rock Smash"), movenum("Rock Tomb"), movenum("Metal Claw"), movenum("Protect"), movenum("Rock Blast"), movenum("X-Scissor"), movenum("Swords Dance"), movenum("Flail"), movenum("Rock Slide"), movenum("High Horsepower"), movenum("Iron Defense"), movenum("Guillotine"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Bulldoze"), movenum("Mud Shot"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("Brick Break"), movenum("Shadow Claw"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Power Gem"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Reversal"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Ancient Power"), movenum("Crabhammer"), movenum("Endeavor"), movenum("Knock Off")] }, "951": { "types": ["Grass", "???"], "name": "Capsakid", "stats": [50, 62, 40, 62, 40, 50], "abilities": ["Chlorophyll", "Insomnia", "Klutz"], "tier": "SM LC", "height": 0.3, "weight": 3, "moves": [movenum("Leafage"), movenum("Leer"), movenum("Bite"), movenum("Growth"), movenum("Razor Leaf"), movenum("Sunny Day"), movenum("Bullet Seed"), movenum("Headbutt"), movenum("Zen Headbutt"), movenum("Crunch"), movenum("Seed Bomb"), movenum("Solar Beam"), movenum("Take Down"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Magical Leaf"), movenum("Endure"), movenum("Sandstorm"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Helping Hand"), movenum("Grassy Terrain"), movenum("Leaf Storm"), movenum("Tera Blast"), movenum("Ingrain"), movenum("Leech Seed"), movenum("Rage Powder"), movenum("Rollout"), movenum("Worry Seed")] }, "952": { "types": ["Grass", "Fire"], "name": "Scovillain", "stats": [65, 108, 65, 108, 65, 75], "abilities": ["Chlorophyll", "Insomnia", "Moody"], "tier": "SM PU", "height": 0.9, "weight": 15, "moves": [movenum("Fire Fang"), movenum("Leafage"), movenum("Leer"), movenum("Spicy Extract"), movenum("Flamethrower"), movenum("Bite"), movenum("Growth"), movenum("Razor Leaf"), movenum("Sunny Day"), movenum("Bullet Seed"), movenum("Headbutt"), movenum("Zen Headbutt"), movenum("Worry Seed"), movenum("Crunch"), movenum("Seed Bomb"), movenum("Solar Beam"), movenum("Overheat"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Magical Leaf"), movenum("Endure"), movenum("Sandstorm"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Will-O-Wisp"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Helping Hand"), movenum("Grassy Terrain"), movenum("Fire Blast"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Ingrain"), movenum("Leech Seed"), movenum("Rage Powder"), movenum("Rollout")] }, "953": { "types": ["Bug", "???"], "name": "Rellor", "stats": [41, 50, 60, 31, 58, 30], "abilities": ["Compound Eyes", "Shed Skin"], "tier": "SM LC", "height": 0.2, "weight": 1, "moves": [movenum("Tackle"), movenum("Defense Curl"), movenum("Sand Attack"), movenum("Struggle Bug"), movenum("Rollout"), movenum("Mud Shot"), movenum("Bug Bite"), movenum("Take Down"), movenum("Dig"), movenum("Lunge"), movenum("Mud-Slap"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Sleep Talk"), movenum("Rest"), movenum("Leech Life"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Iron Defense"), movenum("X-Scissor"), movenum("Sludge Bomb"), movenum("Bug Buzz"), movenum("Tera Blast"), movenum("Cosmic Power"), movenum("Memento"), movenum("Recover"), movenum("Weather Ball")] }, "954": { "types": ["Bug", "Psychic"], "name": "Rabsca", "stats": [75, 50, 85, 115, 100, 45], "abilities": ["Synchronize", "Telepathy"], "tier": "SM PU", "height": 0.3, "weight": 3.5, "moves": [movenum("Tackle"), movenum("Defense Curl"), movenum("Confusion"), movenum("Safeguard"), movenum("Psych Up"), movenum("Revival Blessing"), movenum("Sand Attack"), movenum("Struggle Bug"), movenum("Rollout"), movenum("Psybeam"), movenum("Bug Bite"), movenum("Take Down"), movenum("Extrasensory"), movenum("Lunge"), movenum("Speed Swap"), movenum("Power Swap"), movenum("Guard Swap"), movenum("Bug Buzz"), movenum("Psychic"), movenum("Mud-Slap"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Stored Power"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Psyshock"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Imprison"), movenum("Leech Life"), movenum("Skill Swap"), movenum("Power Gem"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Iron Defense"), movenum("X-Scissor"), movenum("Trick"), movenum("Shadow Ball"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Earth Power"), movenum("Psychic Terrain"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Cosmic Power"), movenum("Memento"), movenum("Recover"), movenum("Weather Ball")] }, "955": { "types": ["Psychic", "???"], "name": "Flittle", "stats": [30, 35, 30, 55, 30, 75], "abilities": ["Anticipation", "Frisk", "Speed Boost"], "tier": "SM LC", "height": 0.2, "weight": 1.5, "moves": [movenum("Peck"), movenum("Growl"), movenum("Confusion"), movenum("Baby-Doll Eyes"), movenum("Disarming Voice"), movenum("Quick Attack"), movenum("Psybeam"), movenum("Pluck"), movenum("Agility"), movenum("Uproar"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Swift"), movenum("Stored Power"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Psyshock"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Reflect"), movenum("Light Screen"), movenum("Rest"), movenum("Skill Swap"), movenum("Substitute"), movenum("Trick"), movenum("Psychic"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Psychic Terrain"), movenum("Trick Room"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Hypnosis"), movenum("Roost")] }, "956": { "types": ["Psychic", "???"], "name": "Espathra", "stats": [95, 60, 60, 101, 60, 105], "abilities": ["Opportunist", "Frisk", "Speed Boost"], "tier": "SM Ubers", "height": 1.9, "weight": 90, "moves": [movenum("Drill Peck"), movenum("Feather Dance"), movenum("Peck"), movenum("Growl"), movenum("Lumina Crash"), movenum("Confusion"), movenum("Baby-Doll Eyes"), movenum("Disarming Voice"), movenum("Quick Attack"), movenum("Psybeam"), movenum("Pluck"), movenum("Agility"), movenum("Uproar"), movenum("Dazzling Gleam"), movenum("Psychic"), movenum("Last Resort"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Low Kick"), movenum("Confuse Ray"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Aerial Ace"), movenum("Hex"), movenum("Swift"), movenum("Stored Power"), movenum("Night Shade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Psyshock"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Foul Play"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Reflect"), movenum("Light Screen"), movenum("Rest"), movenum("Flash Cannon"), movenum("Skill Swap"), movenum("Substitute"), movenum("Trick"), movenum("Shadow Ball"), movenum("Hyper Voice"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Psychic Terrain"), movenum("Giga Impact"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Brave Bird"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Hypnosis"), movenum("Roost")] }, "957": { "types": ["Fairy", "Steel"], "name": "Tinkatink", "stats": [50, 45, 45, 35, 64, 58], "abilities": ["Mold Breaker", "Own Tempo", "Pickpocket"], "tier": "SM LC", "height": 0.4, "weight": 8.9, "moves": [movenum("Astonish"), movenum("Fairy Wind"), movenum("Baby-Doll Eyes"), movenum("Metal Claw"), movenum("Covet"), movenum("Rock Smash"), movenum("Draining Kiss"), movenum("Sweet Kiss"), movenum("Brutal Swing"), movenum("Slam"), movenum("Flash Cannon"), movenum("Play Rough"), movenum("Fake Out"), movenum("Flatter"), movenum("Skitter Smack"), movenum("Knock Off"), movenum("Fake Tears"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Metronome"), movenum("Thunder Wave"), movenum("Rest"), movenum("Rock Slide"), movenum("Swords Dance"), movenum("Skill Swap"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Encore"), movenum("Helping Hand"), movenum("Stone Edge"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Feint"), movenum("Ice Hammer"), movenum("Quash")] }, "958": { "types": ["Fairy", "Steel"], "name": "Tinkatuff", "stats": [65, 55, 55, 45, 82, 78], "abilities": ["Mold Breaker", "Own Tempo", "Pickpocket"], "tier": "SM PU", "height": 0.7, "weight": 59.1, "moves": [movenum("Astonish"), movenum("Fairy Wind"), movenum("Baby-Doll Eyes"), movenum("Metal Claw"), movenum("Covet"), movenum("Rock Smash"), movenum("Draining Kiss"), movenum("Sweet Kiss"), movenum("Brutal Swing"), movenum("Slam"), movenum("Flash Cannon"), movenum("Play Rough"), movenum("Fake Out"), movenum("Flatter"), movenum("Skitter Smack"), movenum("Knock Off"), movenum("Fake Tears"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Brick Break"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Metronome"), movenum("Thunder Wave"), movenum("Rest"), movenum("Rock Slide"), movenum("Swords Dance"), movenum("Skill Swap"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Encore"), movenum("Helping Hand"), movenum("Stone Edge"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Feint"), movenum("Ice Hammer"), movenum("Quash")] }, "959": { "types": ["Fairy", "Steel"], "name": "Tinkaton", "stats": [85, 75, 77, 70, 105, 94], "abilities": ["Mold Breaker", "Own Tempo", "Pickpocket"], "tier": "SM UU", "height": 0.7, "weight": 112.8, "moves": [movenum("Astonish"), movenum("Fairy Wind"), movenum("Gigaton Hammer"), movenum("Baby-Doll Eyes"), movenum("Metal Claw"), movenum("Covet"), movenum("Rock Smash"), movenum("Draining Kiss"), movenum("Sweet Kiss"), movenum("Brutal Swing"), movenum("Slam"), movenum("Flash Cannon"), movenum("Play Rough"), movenum("Fake Out"), movenum("Flatter"), movenum("Skitter Smack"), movenum("Knock Off"), movenum("Fake Tears"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Brick Break"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Metronome"), movenum("Thunder Wave"), movenum("Rest"), movenum("Rock Slide"), movenum("Swords Dance"), movenum("Skill Swap"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Encore"), movenum("Helping Hand"), movenum("Stone Edge"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Feint"), movenum("Ice Hammer"), movenum("Quash")] }, "960": { "types": ["Water", "???"], "name": "Wiglett", "stats": [10, 55, 25, 35, 25, 95], "abilities": ["Gooey", "Rattled", "Sand Veil"], "tier": "SM LC", "height": 1.2, "weight": 1.8, "moves": [movenum("Water Gun"), movenum("Sand Attack"), movenum("Mud-Slap"), movenum("Wrap"), movenum("Aqua Jet"), movenum("Slam"), movenum("Water Pulse"), movenum("Headbutt"), movenum("Dig"), movenum("Sucker Punch"), movenum("Throat Chop"), movenum("Liquidation"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Swift"), movenum("Mud Shot"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Surf"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Ice Beam"), movenum("Hydro Pump"), movenum("Blizzard"), movenum("Tera Blast"), movenum("Final Gambit"), movenum("Memento")] }, "961": { "types": ["Water", "???"], "name": "Wugtrio", "stats": [35, 100, 50, 50, 70, 120], "abilities": ["Gooey", "Rattled", "Sand Veil"], "tier": "SM PU", "height": 1.2, "weight": 5.4, "moves": [movenum("Water Gun"), movenum("Sand Attack"), movenum("Mud-Slap"), movenum("Wrap"), movenum("Aqua Jet"), movenum("Slam"), movenum("Water Pulse"), movenum("Headbutt"), movenum("Triple Dive"), movenum("Dig"), movenum("Sucker Punch"), movenum("Throat Chop"), movenum("Liquidation"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Swift"), movenum("Mud Shot"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Substitute"), movenum("Surf"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Ice Beam"), movenum("Hydro Pump"), movenum("Blizzard"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Final Gambit"), movenum("Memento")] }, "962": { "types": ["Dark", "Flying"], "name": "Bombirdier", "stats": [70, 103, 85, 60, 85, 82], "abilities": ["Big Pecks", "Keen Eye", "Rocky Payload"], "tier": "SM NU", "height": 1.5, "weight": 42.9, "moves": [movenum("Wing Attack"), movenum("Leer"), movenum("Peck"), movenum("Memento"), movenum("Hone Claws"), movenum("Thief"), movenum("Rock Throw"), movenum("Whirlwind"), movenum("Pluck"), movenum("Torment"), movenum("Rock Tomb"), movenum("Payback"), movenum("Dual Wingbeat"), movenum("Rock Slide"), movenum("Knock Off"), movenum("Parting Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Facade"), movenum("Aerial Ace"), movenum("Snarl"), movenum("Icy Wind"), movenum("Air Cutter"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("U-turn"), movenum("Foul Play"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Rock Blast"), movenum("Taunt"), movenum("Dark Pulse"), movenum("Fly"), movenum("Power Gem"), movenum("Substitute"), movenum("Drill Run"), movenum("Tailwind"), movenum("Stealth Rock"), movenum("Hyper Voice"), movenum("Heat Wave"), movenum("Nasty Plot"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Brave Bird"), movenum("Tera Blast"), movenum("Feather Dance"), movenum("Power Trip"), movenum("Roost"), movenum("Sky Attack"), movenum("Sucker Punch")] }, "963": { "types": ["Water", "???"], "name": "Finizen", "stats": [70, 45, 40, 45, 40, 75], "abilities": ["Water Veil"], "tier": "SM LC", "height": 1.3, "weight": 60.2, "moves": [movenum("Water Gun"), movenum("Supersonic"), movenum("Astonish"), movenum("Focus Energy"), movenum("Aqua Jet"), movenum("Double Hit"), movenum("Dive"), movenum("Charm"), movenum("Acrobatics"), movenum("Encore"), movenum("Aqua Tail"), movenum("Mist"), movenum("Hydro Pump"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Water Pulse"), movenum("Disarming Voice"), movenum("Chilling Water"), movenum("Facade"), movenum("Swift"), movenum("Icy Wind"), movenum("Draining Kiss"), movenum("Fling"), movenum("Endure"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Waterfall"), movenum("Rest"), movenum("Substitute"), movenum("Liquidation"), movenum("Surf"), movenum("Helping Hand"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Tera Blast"), movenum("Boomburst"), movenum("Bounce"), movenum("Counter"), movenum("Haze"), movenum("Tickle")] }, "964": { "types": ["Water", "???"], "name": "Palafin", "stats": [100, 70, 72, 53, 62, 100], "abilities": ["Zero to Hero"], "tier": "SM Ubers", "height": 1.3, "weight": 60.2, "moves": [movenum("Jet Punch"), movenum("Water Gun"), movenum("Supersonic"), movenum("Flip Turn"), movenum("Astonish"), movenum("Focus Energy"), movenum("Aqua Jet"), movenum("Double Hit"), movenum("Dive"), movenum("Charm"), movenum("Acrobatics"), movenum("Encore"), movenum("Aqua Tail"), movenum("Mist"), movenum("Hydro Pump"), movenum("Focus Punch"), movenum("Wave Crash"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Water Pulse"), movenum("Disarming Voice"), movenum("Chilling Water"), movenum("Facade"), movenum("Swift"), movenum("Icy Wind"), movenum("Draining Kiss"), movenum("Fling"), movenum("Endure"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Drain Punch"), movenum("Waterfall"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Iron Head"), movenum("Substitute"), movenum("Liquidation"), movenum("Aura Sphere"), movenum("Hyper Voice"), movenum("Surf"), movenum("Helping Hand"), movenum("Reversal"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Giga Impact"), movenum("Outrage"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Close Combat"), movenum("Tera Blast"), movenum("Boomburst"), movenum("Bounce"), movenum("Counter"), movenum("Haze"), movenum("Tickle")] }, "66500": { "types": ["Water", "???"], "name": "Palafin-Hero", "stats": [100, 160, 97, 106, 87, 100], "abilities": ["Zero to Hero"], "tier": "SM Ubers", "height": 1.8, "weight": 97.4, "moves": [movenum("Jet Punch"), movenum("Water Gun"), movenum("Supersonic"), movenum("Flip Turn"), movenum("Astonish"), movenum("Focus Energy"), movenum("Aqua Jet"), movenum("Double Hit"), movenum("Dive"), movenum("Charm"), movenum("Acrobatics"), movenum("Encore"), movenum("Aqua Tail"), movenum("Mist"), movenum("Hydro Pump"), movenum("Focus Punch"), movenum("Wave Crash"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Water Pulse"), movenum("Disarming Voice"), movenum("Chilling Water"), movenum("Facade"), movenum("Swift"), movenum("Icy Wind"), movenum("Draining Kiss"), movenum("Fling"), movenum("Endure"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Drain Punch"), movenum("Waterfall"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Iron Head"), movenum("Substitute"), movenum("Liquidation"), movenum("Aura Sphere"), movenum("Hyper Voice"), movenum("Surf"), movenum("Helping Hand"), movenum("Reversal"), movenum("Ice Beam"), movenum("Blizzard"), movenum("Giga Impact"), movenum("Outrage"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Close Combat"), movenum("Tera Blast"), movenum("Boomburst"), movenum("Bounce"), movenum("Counter"), movenum("Haze"), movenum("Tickle")] }, "965": { "types": ["Steel", "Poison"], "name": "Varoom", "stats": [45, 70, 63, 30, 45, 47], "abilities": ["Overcoat", "Slow Start"], "tier": "SM LC", "height": 1, "weight": 35, "moves": [movenum("Lick"), movenum("Poison Gas"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Sludge Bomb"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "966": { "types": ["Steel", "Poison"], "name": "Revavroom", "stats": [80, 119, 90, 54, 67, 90], "abilities": ["Overcoat", "Filter"], "tier": "SM LU", "height": 1.8, "weight": 120, "moves": [movenum("Magnet Rise"), movenum("Lick"), movenum("Poison Gas"), movenum("Shift Gear"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Heavy Slam"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "66502": { // credit to https://www.reddit.com/r/stunfisk/comments/168educ/starmobiles_base_stats_one_step_closer_to_being/ for reverse-engineering the starmobiles' theoretical base stats from raw stats "types": ["Dark", "???"], "name": "Segin Starmobile", "stats": [326, 79, 106, 76, 84, 311], "abilities": ["Intimidate"], "tier": "SM Ubers", "height": 5.4, "weight": 360, "moves": [movenum("Magnet Rise"), movenum("Lick"), movenum("Poison Gas"), movenum("Shift Gear"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Heavy Slam"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "132038": { "types": ["Fire", "???"], "name": "Schedar Starmobile", "stats": [324, 86, 107, 48, 90, 239], "abilities": ["Speed Boost"], "tier": "SM Ubers", "height": 5.4, "weight": 360, "moves": [movenum("Magnet Rise"), movenum("Lick"), movenum("Poison Gas"), movenum("Shift Gear"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Heavy Slam"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "197574": { "types": ["Poison", "???"], "name": "Navi Starmobile", "stats": [329, 87, 118, 71, 103, 196], "abilities": ["Toxic Debris"], "tier": "SM Ubers", "height": 5.4, "weight": 360, "moves": [movenum("Magnet Rise"), movenum("Lick"), movenum("Poison Gas"), movenum("Shift Gear"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Heavy Slam"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "263110": { "types": ["Fairy", "???"], "name": "Ruchbah Starmobile", "stats": [320, 85, 115, 65, 85, 127], "abilities": ["Misty Surge"], "tier": "SM Ubers", "height": 5.4, "weight": 360, "moves": [movenum("Magnet Rise"), movenum("Lick"), movenum("Poison Gas"), movenum("Shift Gear"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Heavy Slam"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "328646": { "types": ["Fighting", "???"], "name": "Caph Starmobile", "stats": [306, 81, 103, 67, 72, 113], "abilities": ["Stamina"], "tier": "SM Ubers", "height": 5.4, "weight": 360, "moves": [movenum("Magnet Rise"), movenum("Lick"), movenum("Poison Gas"), movenum("Shift Gear"), movenum("Smog"), movenum("Taunt"), movenum("Assurance"), movenum("Sludge"), movenum("Gyro Ball"), movenum("Headbutt"), movenum("Screech"), movenum("Iron Head"), movenum("Swagger"), movenum("Poison Jab"), movenum("Uproar"), movenum("Spin Out"), movenum("Gunk Shot"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acid Spray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Heavy Slam"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Haze"), movenum("Parting Shot"), movenum("Self-Destruct"), movenum("Torment"), movenum("Toxic")] }, "967": { "types": ["Dragon", "Normal"], "name": "Cyclizar", "stats": [70, 95, 65, 85, 65, 121], "abilities": ["Shed Skin", "Regenerator"], "tier": "SM Ubers", "height": 1.6, "weight": 63, "moves": [movenum("Tackle"), movenum("Growl"), movenum("Rapid Spin"), movenum("Taunt"), movenum("Breaking Swipe"), movenum("Quick Attack"), movenum("Bite"), movenum("U-turn"), movenum("Shed Tail"), movenum("Dragon Claw"), movenum("Shift Gear"), movenum("Dragon Pulse"), movenum("Double-Edge"), movenum("Dragon Rush"), movenum("Take Down"), movenum("Agility"), movenum("Mud-Slap"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Acrobatics"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Aerial Ace"), movenum("Mud Shot"), movenum("Dragon Tail"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Iron Head"), movenum("Substitute"), movenum("Crunch"), movenum("Hyper Voice"), movenum("Ice Spinner"), movenum("Thunderbolt"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Aqua Tail"), movenum("Iron Tail"), movenum("Knock Off"), movenum("Power Whip")] }, "968": { "types": ["Steel", "???"], "name": "Orthworm", "stats": [70, 85, 145, 60, 55, 65, ], "abilities": ["Earth Eater", "Sand Veil"], "tier": "SM OU", "height": 2.5, "weight": 310, "moves": [movenum("Tackle"), movenum("Wrap"), movenum("Harden"), movenum("Mud-Slap"), movenum("Smack Down"), movenum("Bulldoze"), movenum("Iron Head"), movenum("Take Down"), movenum("Dig"), movenum("Sandstorm"), movenum("Iron Defense"), movenum("Iron Tail"), movenum("Earthquake"), movenum("Shed Tail"), movenum("Protect"), movenum("Facade"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Endure"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rock Blast"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Coil"), movenum("Curse"), movenum("Metal Burst")] }, "969": { "types": ["Poison", "Rock"], "name": "Glimmet", "stats": [48, 35, 42, 105, 60, 60], "abilities": ["Toxic Debris", "Corrosion"], "tier": "SM LC", "height": 0.7, "weight": 8, "moves": [movenum("Rock Throw"), movenum("Harden"), movenum("Smack Down"), movenum("Acid Spray"), movenum("Ancient Power"), movenum("Rock Polish"), movenum("Stealth Rock"), movenum("Venoshock"), movenum("Sandstorm"), movenum("Self-Destruct"), movenum("Rock Slide"), movenum("Power Gem"), movenum("Acid Armor"), movenum("Sludge Wave"), movenum("Protect"), movenum("Confuse Ray"), movenum("Facade"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Rock Blast"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Spikes"), movenum("Toxic Spikes"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Iron Defense"), movenum("Sludge Bomb"), movenum("Stone Edge"), movenum("Tera Blast"), movenum("Explosion"), movenum("Memento"), movenum("Toxic")] }, "970": { "types": ["Poison", "Rock"], "name": "Glimmora", "stats": [83, 55, 90, 130, 81, 86], "abilities": ["Toxic Debris", "Corrosion"], "tier": "SM OU", "height": 1.5, "weight": 45, "moves": [movenum("Spiky Shield"), movenum("Toxic Spikes"), movenum("Rock Throw"), movenum("Harden"), movenum("Smack Down"), movenum("Mortal Spin"), movenum("Acid Spray"), movenum("Ancient Power"), movenum("Rock Polish"), movenum("Stealth Rock"), movenum("Venoshock"), movenum("Sandstorm"), movenum("Self-Destruct"), movenum("Rock Slide"), movenum("Power Gem"), movenum("Acid Armor"), movenum("Sludge Wave"), movenum("Protect"), movenum("Confuse Ray"), movenum("Facade"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Rock Blast"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Spikes"), movenum("Flash Cannon"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Iron Defense"), movenum("Energy Ball"), movenum("Earth Power"), movenum("Sludge Bomb"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Explosion"), movenum("Memento"), movenum("Toxic")] }, "971": { "types": ["Ghost", "???"], "name": "Greavard", "stats": [50, 61, 60, 30, 55, 34], "abilities": ["Pickup", "Fluffy"], "tier": "SM LC", "height": 0.6, "weight": 35, "moves": [movenum("Tackle"), movenum("Growl"), movenum("Lick"), movenum("Tail Whip"), movenum("Bite"), movenum("Roar"), movenum("Headbutt"), movenum("Dig"), movenum("Rest"), movenum("Crunch"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Phantom Force"), movenum("Charm"), movenum("Double-Edge"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Confuse Ray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Hex"), movenum("Snarl"), movenum("Mud Shot"), movenum("Night Shade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Psychic Fangs"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Substitute"), movenum("Trick"), movenum("Shadow Ball"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Destiny Bond"), movenum("Disable"), movenum("Howl"), movenum("Memento"), movenum("Shadow Sneak"), movenum("Yawn")] }, "972": { "types": ["Ghost", "???"], "name": "Houndstone", "stats": [72, 101, 100, 50, 97, 68], "abilities": ["Sand Rush", "Fluffy"], "tier": "SM Ubers", "height": 2, "weight": 15, "moves": [movenum("Tackle"), movenum("Growl"), movenum("Last Respects"), movenum("Lick"), movenum("Tail Whip"), movenum("Bite"), movenum("Roar"), movenum("Headbutt"), movenum("Dig"), movenum("Rest"), movenum("Crunch"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Phantom Force"), movenum("Charm"), movenum("Double-Edge"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Confuse Ray"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Hex"), movenum("Snarl"), movenum("Mud Shot"), movenum("Night Shade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Psychic Fangs"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Body Press"), movenum("Substitute"), movenum("Will-O-Wisp"), movenum("Trick"), movenum("Shadow Ball"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Destiny Bond"), movenum("Disable"), movenum("Howl"), movenum("Memento"), movenum("Shadow Sneak"), movenum("Yawn")] }, "973": { "types": ["Flying", "Fighting"], "name": "Flamigo", "stats": [82, 115, 74, 75, 64, 90], "abilities": ["Scrappy", "Tangled Feet", "Costar"], "tier": "SM LU", "height": 1.6, "weight": 37, "moves": [movenum("Peck"), movenum("Copycat"), movenum("Double Kick"), movenum("Detect"), movenum("Wing Attack"), movenum("Focus Energy"), movenum("Low Kick"), movenum("Feint"), movenum("Payback"), movenum("Roost"), movenum("Air Slash"), movenum("Mega Kick"), movenum("Wide Guard"), movenum("Throat Chop"), movenum("Brave Bird"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Water Pulse"), movenum("Acrobatics"), movenum("Thief"), movenum("Pounce"), movenum("Chilling Water"), movenum("Facade"), movenum("Aerial Ace"), movenum("Low Sweep"), movenum("Air Cutter"), movenum("Fling"), movenum("Endure"), movenum("U-turn"), movenum("Bulk Up"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Fly"), movenum("Substitute"), movenum("Liquidation"), movenum("Tailwind"), movenum("Reversal"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Close Combat"), movenum("Tera Blast"), movenum("Double Team"), movenum("Quick Guard"), movenum("Sky Attack")] }, "974": { "types": ["Ice", "???"], "name": "Cetoddle", "stats": [108, 68, 45, 30, 40, 43], "abilities": ["Thick Fat", "Snow Cloak", "Sheer Force"], "tier": "SM LC", "height": 1.2, "weight": 45, "moves": [movenum("Tackle"), movenum("Powder Snow"), movenum("Growl"), movenum("Echoed Voice"), movenum("Ice Shard"), movenum("Rest"), movenum("Take Down"), movenum("Flail"), movenum("Avalanche"), movenum("Bounce"), movenum("Body Slam"), movenum("Amnesia"), movenum("Ice Spinner"), movenum("Double-Edge"), movenum("Blizzard"), movenum("Charm"), movenum("Protect"), movenum("Ice Fang"), movenum("Water Pulse"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Icy Wind"), movenum("Endure"), movenum("Rain Dance"), movenum("Snowscape"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Body Press"), movenum("Substitute"), movenum("Liquidation"), movenum("Hyper Voice"), movenum("Heavy Slam"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Ice Beam"), movenum("Earthquake"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Belly Drum"), movenum("Entrainment"), movenum("Icicle Crash"), movenum("Superpower"), movenum("Yawn")] }, "975": { "types": ["Ice", "???"], "name": "Cetitan", "stats": [170, 113, 65, 45, 55, 73], "abilities": ["Thick Fat", "Slush Rush", "Sheer Force"], "tier": "SM NU", "height": 4.5, "weight": 700, "moves": [movenum("Tackle"), movenum("Powder Snow"), movenum("Growl"), movenum("Echoed Voice"), movenum("Ice Shard"), movenum("Rest"), movenum("Take Down"), movenum("Flail"), movenum("Avalanche"), movenum("Bounce"), movenum("Body Slam"), movenum("Amnesia"), movenum("Ice Spinner"), movenum("Double-Edge"), movenum("Blizzard"), movenum("Charm"), movenum("Protect"), movenum("Ice Fang"), movenum("Water Pulse"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Icy Wind"), movenum("Endure"), movenum("Rain Dance"), movenum("Snowscape"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Body Press"), movenum("Substitute"), movenum("Liquidation"), movenum("Hyper Voice"), movenum("Heavy Slam"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Ice Beam"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Belly Drum"), movenum("Entrainment"), movenum("Icicle Crash"), movenum("Superpower"), movenum("Yawn")] }, "976": { "types": ["Water", "Psychic"], "name": "Veluza", "stats": [90, 102, 73, 78, 65, 70], "abilities": ["Mold Breaker", "Sharpness"], "tier": "SM NU", "height": 2.5, "weight": 90, "moves": [movenum("Tackle"), movenum("Aqua Jet"), movenum("Pluck"), movenum("Water Pulse"), movenum("Focus Energy"), movenum("Slash"), movenum("Aqua Cutter"), movenum("Fillet Away"), movenum("Night Slash"), movenum("Psycho Cut"), movenum("Liquidation"), movenum("Crunch"), movenum("Final Gambit"), movenum("Take Down"), movenum("Agility"), movenum("Protect"), movenum("Ice Fang"), movenum("Chilling Water"), movenum("Icy Wind"), movenum("Stored Power"), movenum("Endure"), movenum("Rain Dance"), movenum("Snowscape"), movenum("Zen Headbutt"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Waterfall"), movenum("Rest"), movenum("Substitute"), movenum("Drill Run"), movenum("Psychic"), movenum("Surf"), movenum("Ice Beam"), movenum("Psychic Terrain"), movenum("Hydro Pump"), movenum("Blizzard"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Recover"), movenum("Thrash")] }, "977": { "types": ["Water", "???"], "name": "Dondozo", "stats": [150, 100, 115, 65, 65, 35], "abilities": ["Unaware", "Oblivious", "Water Veil"], "tier": "SM OU", "height": 12, "weight": 220, "moves": [movenum("Tackle"), movenum("Supersonic"), movenum("Water Gun"), movenum("Tickle"), movenum("Flail"), movenum("Rest"), movenum("Sleep Talk"), movenum("Dive"), movenum("Noble Roar"), movenum("Soak"), movenum("Body Slam"), movenum("Aqua Tail"), movenum("Rain Dance"), movenum("Order Up"), movenum("Heavy Slam"), movenum("Double-Edge"), movenum("Wave Crash"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Ice Fang"), movenum("Water Pulse"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Avalanche"), movenum("Endure"), movenum("Zen Headbutt"), movenum("Waterfall"), movenum("Stomping Tantrum"), movenum("Rock Slide"), movenum("Body Press"), movenum("Substitute"), movenum("Crunch"), movenum("Liquidation"), movenum("Surf"), movenum("Hydro Pump"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Curse"), movenum("Fissure"), movenum("Thrash"), movenum("Yawn")] }, "978": { "types": ["Dragon", "Water"], "name": "Tatsugiri", "stats": [68, 50, 60, 120, 95, 82], "abilities": ["Commander", "Storm Drain"], "tier": "SM LU", "height": 0.3, "weight": 8, "moves": [movenum("Water Gun"), movenum("Splash"), movenum("Harden"), movenum("Helping Hand"), movenum("Water Pulse"), movenum("Soak"), movenum("Taunt"), movenum("Memento"), movenum("Muddy Water"), movenum("Nasty Plot"), movenum("Mirror Coat"), movenum("Dragon Pulse"), movenum("Take Down"), movenum("Protect"), movenum("Chilling Water"), movenum("Facade"), movenum("Icy Wind"), movenum("Endure"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Rest"), movenum("Dragon Dance"), movenum("Substitute"), movenum("Surf"), movenum("Baton Pass"), movenum("Hydro Pump"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Counter"), movenum("Rapid Spin")] }, "66514": { "types": ["Dragon", "Water"], "name": "Tatsugiri-Droopy", "stats": [68, 50, 60, 120, 95, 82], "abilities": ["Commander", "Storm Drain"], "tier": "SM LU", "height": 0.3, "weight": 8, "moves": [movenum("Water Gun"), movenum("Splash"), movenum("Harden"), movenum("Helping Hand"), movenum("Water Pulse"), movenum("Soak"), movenum("Taunt"), movenum("Memento"), movenum("Muddy Water"), movenum("Nasty Plot"), movenum("Mirror Coat"), movenum("Dragon Pulse"), movenum("Take Down"), movenum("Protect"), movenum("Chilling Water"), movenum("Facade"), movenum("Icy Wind"), movenum("Endure"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Rest"), movenum("Dragon Dance"), movenum("Substitute"), movenum("Surf"), movenum("Baton Pass"), movenum("Hydro Pump"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Counter"), movenum("Rapid Spin")] }, "132050": { "types": ["Dragon", "Water"], "name": "Tatsugiri-Stretchy", "stats": [68, 50, 60, 120, 95, 82], "abilities": ["Commander", "Storm Drain"], "tier": "SM LU", "height": 0.3, "weight": 8, "moves": [movenum("Water Gun"), movenum("Splash"), movenum("Harden"), movenum("Helping Hand"), movenum("Water Pulse"), movenum("Soak"), movenum("Taunt"), movenum("Memento"), movenum("Muddy Water"), movenum("Nasty Plot"), movenum("Mirror Coat"), movenum("Dragon Pulse"), movenum("Take Down"), movenum("Protect"), movenum("Chilling Water"), movenum("Facade"), movenum("Icy Wind"), movenum("Endure"), movenum("Rain Dance"), movenum("Sleep Talk"), movenum("Rest"), movenum("Dragon Dance"), movenum("Substitute"), movenum("Surf"), movenum("Baton Pass"), movenum("Hydro Pump"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Counter"), movenum("Rapid Spin")] }, "979": { "types": ["Fighting", "Ghost"], "name": "Annihilape", "stats": [110, 115, 80, 50, 90, 90], "abilities": ["Vital Spirit", "Inner Focus", "Defiant"], "tier": "SM Ubers", "height": 1.2, "weight": 56, "moves": [movenum("Counter"), movenum("Fling"), movenum("Scratch"), movenum("Leer"), movenum("Focus Energy"), movenum("Shadow Punch"), movenum("Fury Swipes"), movenum("Low Kick"), movenum("Seismic Toss"), movenum("Swagger"), movenum("Cross Chop"), movenum("Assurance"), movenum("Thrash"), movenum("Rage Fist"), movenum("Close Combat"), movenum("Screech"), movenum("Stomping Tantrum"), movenum("Outrage"), movenum("Final Gambit"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Thief"), movenum("Facade"), movenum("Bulldoze"), movenum("Swift"), movenum("Rock Tomb"), movenum("Low Sweep"), movenum("Night Shade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Brick Break"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Drain Punch"), movenum("Metronome"), movenum("Poison Jab"), movenum("Rest"), movenum("Rock Slide"), movenum("Taunt"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Shadow Ball"), movenum("Stealth Rock"), movenum("Encore"), movenum("Thunderbolt"), movenum("Helping Hand"), movenum("Reversal"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Phantom Force"), movenum("Giga Impact"), movenum("Overheat"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast"), movenum("Beat Up"), movenum("Curse"), movenum("Night Slash"), movenum("Spite"), movenum("Covet")] }, "980": { "types": ["Poison", "Ground"], "name": "Clodsire", "stats": [130, 75, 60, 45, 100, 20], "abilities": ["Poison Point", "Water Absorb", "Unaware"], "tier": "SM OU", "height": 1.8, "weight": 223, "moves": [movenum("Poison Sting"), movenum("Tail Whip"), movenum("Amnesia"), movenum("Toxic Spikes"), movenum("Mud Shot"), movenum("Poison Tail"), movenum("Slam"), movenum("Yawn"), movenum("Poison Jab"), movenum("Sludge Wave"), movenum("Megahorn"), movenum("Toxic"), movenum("Earthquake"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Protect"), movenum("Water Pulse"), movenum("Low Kick"), movenum("Acid Spray"), movenum("Trailblaze"), movenum("Chilling Water"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Venoshock"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Waterfall"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Spikes"), movenum("Iron Head"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Liquidation"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Surf"), movenum("Helping Hand"), movenum("Earth Power"), movenum("Hydro Pump"), movenum("Sludge Bomb"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("After You"), movenum("Ancient Power"), movenum("Counter"), movenum("Curse"), movenum("Double Kick"), movenum("Haze"), movenum("Mist"), movenum("Recover"), movenum("Spit Up"), movenum("Stockpile"), movenum("Swallow"), movenum("Tackle")] }, "981": { "types": ["Normal", "Psychic"], "name": "Farigiraf", "stats": [120, 90, 70, 110, 70, 60], "abilities": ["Cud Chew", "Armor Tail", "Sap Sipper"], "tier": "SM NU", "height": 3.2, "weight": 160, "moves": [movenum("Power Swap"), movenum("Guard Swap"), movenum("Astonish"), movenum("Tackle"), movenum("Growl"), movenum("Confusion"), movenum("Assurance"), movenum("Stomp"), movenum("Psybeam"), movenum("Agility"), movenum("Double Hit"), movenum("Twin Beam"), movenum("Crunch"), movenum("Baton Pass"), movenum("Nasty Plot"), movenum("Psychic"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Confuse Ray"), movenum("Thief"), movenum("Trailblaze"), movenum("Charge Beam"), movenum("Facade"), movenum("Bulldoze"), movenum("Swift"), movenum("Stored Power"), movenum("Night Shade"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Psyshock"), movenum("Zen Headbutt"), movenum("Foul Play"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Imprison"), movenum("Skill Swap"), movenum("Iron Head"), movenum("Substitute"), movenum("Trick"), movenum("Shadow Ball"), movenum("Hyper Voice"), movenum("Energy Ball"), movenum("Thunderbolt"), movenum("Amnesia"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Psychic Terrain"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast"), movenum("Ally Switch"), movenum("Beat Up"), movenum("Double Kick"), movenum("Future Sight"), movenum("Mean Look"), movenum("Mirror Coat"), movenum("Uproar"), movenum("Wish"), movenum("Level up knowing Twin Beam →")] }, "982": { "types": ["Normal", "???"], "name": "Dudunsparce", "stats": [125, 100, 80, 85, 75, 55], "abilities": ["Serene Grace", "Run Away", "Rattled"], "tier": "SM NU", "height": 3.6, "weight": 39.2, "moves": [movenum("Flail"), movenum("Defense Curl"), movenum("Mud-Slap"), movenum("Rollout"), movenum("Glare"), movenum("Screech"), movenum("Ancient Power"), movenum("Drill Run"), movenum("Yawn"), movenum("Hyper Drill"), movenum("Roost"), movenum("Dragon Rush"), movenum("Coil"), movenum("Double-Edge"), movenum("Endeavor"), movenum("Hurricane"), movenum("Boomburst"), movenum("Take Down"), movenum("Agility"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Chilling Water"), movenum("Facade"), movenum("Poison Tail"), movenum("Bulldoze"), movenum("Hex"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Stored Power"), movenum("Dragon Tail"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Air Slash"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Poison Jab"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Substitute"), movenum("Tailwind"), movenum("Shadow Ball"), movenum("Stealth Rock"), movenum("Hyper Voice"), movenum("Heavy Slam"), movenum("Ice Spinner"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Amnesia"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Earth Power"), movenum("Ice Beam"), movenum("Fire Blast"), movenum("Blizzard"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Aqua Tail"), movenum("Astonish"), movenum("Bite"), movenum("Curse"), movenum("Headbutt"), movenum("Last Resort")] }, "66518": { "types": ["Normal", "???"], "name": "Dudunsparce-Three Segment", "stats": [125, 100, 80, 85, 75, 55], "abilities": ["Serene Grace", "Run Away", "Rattled"], "tier": "SM NU", "height": 4.5, "weight": 47.4, "moves": [movenum("Flail"), movenum("Defense Curl"), movenum("Mud-Slap"), movenum("Rollout"), movenum("Glare"), movenum("Screech"), movenum("Ancient Power"), movenum("Drill Run"), movenum("Yawn"), movenum("Hyper Drill"), movenum("Roost"), movenum("Dragon Rush"), movenum("Coil"), movenum("Double-Edge"), movenum("Endeavor"), movenum("Hurricane"), movenum("Boomburst"), movenum("Take Down"), movenum("Agility"), movenum("Scary Face"), movenum("Protect"), movenum("Thief"), movenum("Pounce"), movenum("Chilling Water"), movenum("Facade"), movenum("Poison Tail"), movenum("Bulldoze"), movenum("Hex"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Stored Power"), movenum("Dragon Tail"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Air Slash"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Poison Jab"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Substitute"), movenum("Tailwind"), movenum("Shadow Ball"), movenum("Stealth Rock"), movenum("Hyper Voice"), movenum("Heavy Slam"), movenum("Ice Spinner"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Amnesia"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Earth Power"), movenum("Ice Beam"), movenum("Fire Blast"), movenum("Blizzard"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Aqua Tail"), movenum("Astonish"), movenum("Bite"), movenum("Curse"), movenum("Headbutt"), movenum("Last Resort")] }, "983": { "types": ["Dark", "Steel"], "name": "Kingambit", "stats": [100, 135, 120, 60, 85, 50], "abilities": ["Defiant", "Supreme Overlord", "Pressure"], "tier": "SM OU", "height": 2, "weight": 120, "moves": [movenum("Metal Burst"), movenum("Metal Claw"), movenum("Fury Cutter"), movenum("Scratch"), movenum("Leer"), movenum("Kowtow Cleave"), movenum("Torment"), movenum("Scary Face"), movenum("Assurance"), movenum("Metal Sound"), movenum("Slash"), movenum("Night Slash"), movenum("Iron Defense"), movenum("Retaliate"), movenum("Iron Head"), movenum("Swords Dance"), movenum("Guillotine"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Thief"), movenum("Facade"), movenum("Aerial Ace"), movenum("Snarl"), movenum("Rock Tomb"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Dig"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("Shadow Claw"), movenum("Foul Play"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Flash Cannon"), movenum("Dark Pulse"), movenum("Substitute"), movenum("X-Scissor"), movenum("Stealth Rock"), movenum("Reversal"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Headbutt"), movenum("Mean Look"), movenum("Quick Guard"), movenum("Sucker Punch")] }, "984": { "types": ["Ground", "Fighting"], "name": "Great Tusk", "stats": [115, 131, 131, 53, 53, 87], "abilities": ["Protosynthesis"], "tier": "SM OU", "height": 2.2, "weight": 320, "moves": [movenum("Sunny Day"), movenum("Horn Attack"), movenum("Defense Curl"), movenum("Rollout"), movenum("Bulldoze"), movenum("Taunt"), movenum("Rapid Spin"), movenum("Brick Break"), movenum("Stomping Tantrum"), movenum("Knock Off"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Close Combat"), movenum("Endeavor"), movenum("Megahorn"), movenum("Head Smash"), movenum("Headlong Rush"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Facade"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Endure"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Psyshock"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Iron Head"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Ice Spinner"), movenum("Play Rough"), movenum("Earth Power"), movenum("Reversal"), movenum("Stone Edge"), movenum("Hyper Beam"), movenum("Tera Blast")] }, "985": { "types": ["Fairy", "Psychic"], "name": "Scream Tail", "stats": [115, 65, 99, 65, 115, 111], "abilities": ["Protosynthesis"], "tier": "SM UU", "height": 1.2, "weight": 8, "moves": [movenum("Sunny Day"), movenum("Pound"), movenum("Sing"), movenum("Disable"), movenum("Howl"), movenum("Noble Roar"), movenum("Bite"), movenum("Body Slam"), movenum("Rest"), movenum("Play Rough"), movenum("Hyper Voice"), movenum("Psychic Fangs"), movenum("Crunch"), movenum("Wish"), movenum("Gyro Ball"), movenum("Perish Song"), movenum("Boomburst"), movenum("Take Down"), movenum("Fake Tears"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Water Pulse"), movenum("Psybeam"), movenum("Facade"), movenum("Rock Tomb"), movenum("Stored Power"), movenum("Fling"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Snowscape"), movenum("Psyshock"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Bulk Up"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Drain Punch"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Metronome"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Stomping Tantrum"), movenum("Imprison"), movenum("Substitute"), movenum("Trick"), movenum("Stealth Rock"), movenum("Psychic"), movenum("Encore"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Amnesia"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Baton Pass"), movenum("Ice Beam"), movenum("Psychic Terrain"), movenum("Misty Terrain"), movenum("Fire Blast"), movenum("Blizzard"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast")] }, "986": { "types": ["Grass", "Dark"], "name": "Brute Bonnet", "stats": [111, 127, 99, 79, 99, 55], "abilities": ["Protosynthesis"], "tier": "SM UU", "height": 1.2, "weight": 21, "moves": [movenum("Sunny Day"), movenum("Absorb"), movenum("Growth"), movenum("Astonish"), movenum("Stun Spore"), movenum("Mega Drain"), movenum("Synthesis"), movenum("Clear Smog"), movenum("Payback"), movenum("Thrash"), movenum("Giga Drain"), movenum("Sucker Punch"), movenum("Spore"), movenum("Ingrain"), movenum("Rage Powder"), movenum("Solar Beam"), movenum("Scary Face"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Hex"), movenum("Magical Leaf"), movenum("Venoshock"), movenum("Endure"), movenum("Bullet Seed"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Body Press"), movenum("Dark Pulse"), movenum("Substitute"), movenum("Crunch"), movenum("Energy Ball"), movenum("Pollen Puff"), movenum("Earth Power"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Outrage"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Close Combat"), movenum("Tera Blast")] }, "987": { "types": ["Ghost", "Fairy"], "name": "Flutter Mane", "stats": [55, 55, 55, 135, 135, 135], "abilities": ["Protosynthesis"], "tier": "SM Ubers", "height": 1.4, "weight": 4, "moves": [movenum("Sunny Day"), movenum("Confuse Ray"), movenum("Spite"), movenum("Astonish"), movenum("Psybeam"), movenum("Mean Look"), movenum("Memento"), movenum("Wish"), movenum("Dazzling Gleam"), movenum("Shadow Ball"), movenum("Mystical Fire"), movenum("Power Gem"), movenum("Psyshock"), movenum("Phantom Force"), movenum("Pain Split"), movenum("Moonblast"), movenum("Perish Song"), movenum("Charm"), movenum("Fake Tears"), movenum("Protect"), movenum("Disarming Voice"), movenum("Charge Beam"), movenum("Hex"), movenum("Swift"), movenum("Magical Leaf"), movenum("Icy Wind"), movenum("Draining Kiss"), movenum("Stored Power"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Imprison"), movenum("Dark Pulse"), movenum("Substitute"), movenum("Hyper Voice"), movenum("Energy Ball"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Misty Terrain"), movenum("Giga Impact"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast")] }, "988": { "types": ["Bug", "Fighting"], "name": "Slither Wing", "stats": [85, 135, 79, 85, 105, 81], "abilities": ["Protosynthesis"], "tier": "SM UU", "height": 3.2, "weight": 92, "moves": [movenum("Sunny Day"), movenum("Gust"), movenum("Ember"), movenum("Bug Bite"), movenum("Poison Powder"), movenum("Stun Spore"), movenum("Flame Charge"), movenum("Stomp"), movenum("Low Sweep"), movenum("Morning Sun"), movenum("Lunge"), movenum("Superpower"), movenum("Bulk Up"), movenum("Dual Wingbeat"), movenum("First Impression"), movenum("Whirlwind"), movenum("Leech Life"), movenum("Thrash"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Trailblaze"), movenum("Facade"), movenum("Aerial Ace"), movenum("Endure"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Substitute"), movenum("Will-O-Wisp"), movenum("Giga Drain"), movenum("Heat Wave"), movenum("Heavy Slam"), movenum("Reversal"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Bug Buzz"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Close Combat"), movenum("Tera Blast")] }, "989": { "types": ["Electric", "Ground"], "name": "Sandy Shocks", "stats": [85, 81, 97, 121, 85, 101], "abilities": ["Protosynthesis"], "tier": "SM UU", "height": 2.3, "weight": 60, "moves": [movenum("Sunny Day"), movenum("Thunder Wave"), movenum("Electric Terrain"), movenum("Supersonic"), movenum("Thunder Shock"), movenum("Spark"), movenum("Bulldoze"), movenum("Charge Beam"), movenum("Tri Attack"), movenum("Screech"), movenum("Heavy Slam"), movenum("Metal Sound"), movenum("Discharge"), movenum("Earth Power"), movenum("Mirror Coat"), movenum("Gravity"), movenum("Zap Cannon"), movenum("Magnetic Flux"), movenum("Take Down"), movenum("Protect"), movenum("Facade"), movenum("Swift"), movenum("Mud Shot"), movenum("Endure"), movenum("Volt Switch"), movenum("Sandstorm"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Body Press"), movenum("Spikes"), movenum("Flash Cannon"), movenum("Eerie Impulse"), movenum("Power Gem"), movenum("Substitute"), movenum("Iron Defense"), movenum("Stealth Rock"), movenum("Thunderbolt"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast")] }, "990": { "types": ["Ground", "Steel"], "name": "Iron Treads", "stats": [90, 112, 120, 72, 70, 106], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 0.9, "weight": 240, "moves": [movenum("Electric Terrain"), movenum("Horn Attack"), movenum("Defense Curl"), movenum("Rollout"), movenum("Bulldoze"), movenum("Rapid Spin"), movenum("Iron Head"), movenum("Stomping Tantrum"), movenum("Knock Off"), movenum("Earthquake"), movenum("Heavy Slam"), movenum("Wild Charge"), movenum("Endeavor"), movenum("Megahorn"), movenum("Giga Impact"), movenum("Steel Roller"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Facade"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Volt Switch"), movenum("Sandstorm"), movenum("Smart Strike"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Electro Ball"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Iron Defense"), movenum("Stealth Rock"), movenum("Ice Spinner"), movenum("Earth Power"), movenum("Stone Edge"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Steel Beam"), movenum("Tera Blast")] }, "991": { "types": ["Ice", "Water"], "name": "Iron Bundle", "stats": [56, 80, 114, 124, 60, 136], "abilities": ["Quark Drive"], "tier": "SM Ubers", "height": 0.6, "weight": 11, "moves": [movenum("Electric Terrain"), movenum("Present"), movenum("Powder Snow"), movenum("Whirlpool"), movenum("Take Down"), movenum("Drill Peck"), movenum("Helping Hand"), movenum("Freeze-Dry"), movenum("Flip Turn"), movenum("Ice Beam"), movenum("Agility"), movenum("Snowscape"), movenum("Hydro Pump"), movenum("Aurora Veil"), movenum("Blizzard"), movenum("Protect"), movenum("Water Pulse"), movenum("Acrobatics"), movenum("Thief"), movenum("Chilling Water"), movenum("Facade"), movenum("Swift"), movenum("Icy Wind"), movenum("Air Cutter"), movenum("Fling"), movenum("Avalanche"), movenum("Endure"), movenum("Rain Dance"), movenum("U-turn"), movenum("Body Slam"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Encore"), movenum("Ice Spinner"), movenum("Play Rough"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast")] }, "992": { "types": ["Fighting", "Electric"], "name": "Iron Hands", "stats": [154, 140, 108, 50, 68, 50], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 1.8, "weight": 380.7, "moves": [movenum("Electric Terrain"), movenum("Sand Attack"), movenum("Tackle"), movenum("Focus Energy"), movenum("Arm Thrust"), movenum("Fake Out"), movenum("Whirlwind"), movenum("Thunder Punch"), movenum("Slam"), movenum("Force Palm"), movenum("Seismic Toss"), movenum("Charge"), movenum("Wild Charge"), movenum("Close Combat"), movenum("Detect"), movenum("Heavy Slam"), movenum("Belly Drum"), movenum("Focus Punch"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Facade"), movenum("Bulldoze"), movenum("Rock Tomb"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Volt Switch"), movenum("Brick Break"), movenum("Body Slam"), movenum("Fire Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Drain Punch"), movenum("Metronome"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Iron Defense"), movenum("Thunderbolt"), movenum("Play Rough"), movenum("Reversal"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast")] }, "993": { "types": ["Dark", "Flying"], "name": "Iron Jugulis", "stats": [94, 80, 86, 122, 80, 108], "abilities": ["Quark Drive"], "tier": "SM UU", "height": 1.3, "weight": 111, "moves": [movenum("Electric Terrain"), movenum("Work Up"), movenum("Focus Energy"), movenum("Tri Attack"), movenum("Air Cutter"), movenum("Roar"), movenum("Assurance"), movenum("Dragon Breath"), movenum("Snarl"), movenum("Crunch"), movenum("Hyper Voice"), movenum("Air Slash"), movenum("Knock Off"), movenum("Dark Pulse"), movenum("Outrage"), movenum("Dragon Pulse"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Acrobatics"), movenum("Charge Beam"), movenum("Facade"), movenum("Rock Tomb"), movenum("Dragon Tail"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Flash Cannon"), movenum("Fly"), movenum("Iron Head"), movenum("Substitute"), movenum("Tailwind"), movenum("Heat Wave"), movenum("Flamethrower"), movenum("Earth Power"), movenum("Fire Blast"), movenum("Hydro Pump"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hurricane"), movenum("Tera Blast")] }, "994": { "types": ["Fire", "Poison"], "name": "Iron Moth", "stats": [80, 70, 60, 140, 110, 110], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 1.2, "weight": 36, "moves": [movenum("Electric Terrain"), movenum("Gust"), movenum("Whirlwind"), movenum("Ember"), movenum("Acid Spray"), movenum("Struggle Bug"), movenum("Fire Spin"), movenum("Take Down"), movenum("Lunge"), movenum("Screech"), movenum("Discharge"), movenum("Sludge Wave"), movenum("Fiery Dance"), movenum("Metal Sound"), movenum("Morning Sun"), movenum("Hurricane"), movenum("Bug Buzz"), movenum("Overheat"), movenum("Agility"), movenum("Protect"), movenum("Acrobatics"), movenum("Confuse Ray"), movenum("Pounce"), movenum("Charge Beam"), movenum("Facade"), movenum("Swift"), movenum("Flame Charge"), movenum("Venoshock"), movenum("Endure"), movenum("Sunny Day"), movenum("U-turn"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Toxic Spikes"), movenum("Flash Cannon"), movenum("Substitute"), movenum("Heat Wave"), movenum("Energy Ball"), movenum("Psychic"), movenum("Flamethrower"), movenum("Helping Hand"), movenum("Fire Blast"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Solar Beam"), movenum("Tera Blast")] }, "995": { "types": ["Rock", "Electric"], "name": "Iron Thorns", "stats": [100, 134, 110, 70, 84, 72], "abilities": ["Quark Drive"], "tier": "SM UU", "height": 1.6, "weight": 303, "moves": [movenum("Electric Terrain"), movenum("Rock Throw"), movenum("Fire Fang"), movenum("Ice Fang"), movenum("Iron Defense"), movenum("Thunder Fang"), movenum("Screech"), movenum("Rock Tomb"), movenum("Bite"), movenum("Charge"), movenum("Rock Slide"), movenum("Sandstorm"), movenum("Wild Charge"), movenum("Pin Missile"), movenum("Earthquake"), movenum("Stealth Rock"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Charge Beam"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Metal Claw"), movenum("Fling"), movenum("Dragon Tail"), movenum("Endure"), movenum("Volt Switch"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Brick Break"), movenum("Body Slam"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Electro Ball"), movenum("Rock Blast"), movenum("Dragon Claw"), movenum("Thunder Wave"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Spikes"), movenum("Eerie Impulse"), movenum("Iron Head"), movenum("Dragon Dance"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Heavy Slam"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Earth Power"), movenum("Ice Beam"), movenum("Fire Blast"), movenum("Blizzard"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Tera Blast")] }, "996": { "types": ["Dragon", "Ice"], "name": "Frigibax", "stats": [65, 75, 45, 35, 45, 55], "abilities": ["Thermal Exchange", "Ice Body"], "tier": "SM LC", "height": 0.5, "weight": 17, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Dragon Tail"), movenum("Icy Wind"), movenum("Dragon Breath"), movenum("Focus Energy"), movenum("Bite"), movenum("Ice Fang"), movenum("Dragon Claw"), movenum("Take Down"), movenum("Ice Beam"), movenum("Crunch"), movenum("Icicle Crash"), movenum("Protect"), movenum("Facade"), movenum("Avalanche"), movenum("Endure"), movenum("Rain Dance"), movenum("Snowscape"), movenum("Dig"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Swords Dance"), movenum("Substitute"), movenum("Dragon Pulse"), movenum("Helping Hand"), movenum("Blizzard"), movenum("Outrage"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Aqua Tail"), movenum("Dragon Rush"), movenum("Freeze-Dry"), movenum("Icicle Spear")] }, "997": { "types": ["Dragon", "Ice"], "name": "Arctibax", "stats": [90, 95, 66, 45, 65, 62], "abilities": ["Thermal Exchange", "Ice Body"], "tier": "SM PU", "height": 0.8, "weight": 30, "moves": [movenum("Tackle"), movenum("Leer"), movenum("Dragon Tail"), movenum("Icy Wind"), movenum("Dragon Breath"), movenum("Focus Energy"), movenum("Bite"), movenum("Ice Fang"), movenum("Take Down"), movenum("Ice Beam"), movenum("Crunch"), movenum("Icicle Crash"), movenum("Scary Face"), movenum("Protect"), movenum("Facade"), movenum("Aerial Ace"), movenum("Avalanche"), movenum("Endure"), movenum("Rain Dance"), movenum("Snowscape"), movenum("Dig"), movenum("Brick Break"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Dragon Claw"), movenum("Rest"), movenum("Swords Dance"), movenum("Iron Head"), movenum("Substitute"), movenum("Dragon Pulse"), movenum("Helping Hand"), movenum("Blizzard"), movenum("Outrage"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Aqua Tail"), movenum("Dragon Rush"), movenum("Freeze-Dry"), movenum("Icicle Spear")] }, "998": { "types": ["Dragon", "Ice"], "name": "Baxcalibur", "stats": [115, 145, 92, 75, 86, 87], "abilities": ["Thermal Exchange", "Ice Body"], "tier": "SM OU", "height": 2.1, "weight": 210, "moves": [movenum("Snowscape"), movenum("Breaking Swipe"), movenum("Ice Shard"), movenum("Tackle"), movenum("Leer"), movenum("Dragon Tail"), movenum("Glaive Rush"), movenum("Icy Wind"), movenum("Dragon Breath"), movenum("Focus Energy"), movenum("Bite"), movenum("Ice Fang"), movenum("Dragon Claw"), movenum("Take Down"), movenum("Ice Beam"), movenum("Crunch"), movenum("Icicle Crash"), movenum("Scary Face"), movenum("Protect"), movenum("Thunder Fang"), movenum("Facade"), movenum("Aerial Ace"), movenum("Bulldoze"), movenum("Avalanche"), movenum("Endure"), movenum("Rain Dance"), movenum("Dig"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Dragon Dance"), movenum("Substitute"), movenum("Dragon Pulse"), movenum("Helping Hand"), movenum("Blizzard"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Aqua Tail"), movenum("Dragon Rush"), movenum("Freeze-Dry"), movenum("Icicle Spear")] }, "999": { "types": ["Ghost", "???"], "name": "Gimmighoul", "stats": [45, 30, 70, 75, 70, 10], "abilities": ["Rattled"], "tier": "SM LC", "height": 0.3, "weight": 5, "moves": [movenum("Astonish"), movenum("Tackle"), movenum("Take Down"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Hex"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Rest"), movenum("Power Gem"), movenum("Substitute"), movenum("Shadow Ball"), movenum("Nasty Plot"), movenum("Tera Blast")] }, "66535": { "types": ["Ghost", "???"], "name": "Gimmighoul-Roaming", "stats": [45, 30, 25, 75, 45, 80], "abilities": ["Run Away"], "tier": "SM LC", "height": 0.1, "weight": 0.1, "moves": [movenum("Astonish"), movenum("Tackle"), movenum("Take Down"), movenum("Protect"), movenum("Confuse Ray"), movenum("Thief"), movenum("Hex"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Rest"), movenum("Power Gem"), movenum("Substitute"), movenum("Shadow Ball"), movenum("Nasty Plot"), movenum("Tera Blast")] }, "1000": { "types": ["Steel", "Ghost"], "name": "Gholdengo", "stats": [87, 60, 95, 133, 91, 84], "abilities": ["Good as Gold"], "tier": "SM OU", "height": 1.2, "weight": 30, "moves": [movenum("Tackle"), movenum("Astonish"), movenum("Night Shade"), movenum("Confuse Ray"), movenum("Substitute"), movenum("Metal Sound"), movenum("Shadow Ball"), movenum("Recover"), movenum("Power Gem"), movenum("Make It Rain"), movenum("Nasty Plot"), movenum("Memento"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Thief"), movenum("Charge Beam"), movenum("Hex"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Sandstorm"), movenum("Psyshock"), movenum("Thunder Punch"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Thunder Wave"), movenum("Rest"), movenum("Flash Cannon"), movenum("Iron Head"), movenum("Trick"), movenum("Psychic"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Thunder"), movenum("Steel Beam"), movenum("Tera Blast")] }, "1001": { "types": ["Dark", "Grass"], "name": "Wo-Chien", "stats": [85, 85, 100, 95, 135, 70], "abilities": ["Tablets of Ruin"], "tier": "SM UU", "height": 1.5, "weight": 74.2, "moves": [movenum("Absorb"), movenum("Spite"), movenum("Mean Look"), movenum("Tickle"), movenum("Payback"), movenum("Poison Powder"), movenum("Stun Spore"), movenum("Mega Drain"), movenum("Leech Seed"), movenum("Growth"), movenum("Ingrain"), movenum("Dark Pulse"), movenum("Giga Drain"), movenum("Ruination"), movenum("Foul Play"), movenum("Power Whip"), movenum("Grassy Terrain"), movenum("Knock Off"), movenum("Leaf Storm"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Trailblaze"), movenum("Facade"), movenum("Hex"), movenum("Snarl"), movenum("Magical Leaf"), movenum("Mud Shot"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Bullet Seed"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Reflect"), movenum("Light Screen"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Body Press"), movenum("Substitute"), movenum("Energy Ball"), movenum("Pollen Puff"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast")] }, "1002": { "types": ["Dark", "Ice"], "name": "Chien-Pao", "stats": [80, 120, 80, 90, 65, 135, ], "abilities": ["Sword of Ruin"], "tier": "SM Ubers", "height": 1.9, "weight": 152.2, "moves": [movenum("Spite"), movenum("Powder Snow"), movenum("Mean Look"), movenum("Icy Wind"), movenum("Payback"), movenum("Mist"), movenum("Haze"), movenum("Ice Shard"), movenum("Swords Dance"), movenum("Snowscape"), movenum("Night Slash"), movenum("Dark Pulse"), movenum("Icicle Crash"), movenum("Ruination"), movenum("Sucker Punch"), movenum("Sacred Sword"), movenum("Recover"), movenum("Throat Chop"), movenum("Sheer Cold"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Ice Fang"), movenum("Acrobatics"), movenum("Facade"), movenum("Aerial Ace"), movenum("Hex"), movenum("Snarl"), movenum("Avalanche"), movenum("Endure"), movenum("Rain Dance"), movenum("False Swipe"), movenum("Brick Break"), movenum("Psychic Fangs"), movenum("Sleep Talk"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Crunch"), movenum("Ice Spinner"), movenum("Blizzard"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast")] }, "1003": { "types": ["Dark", "Ground"], "name": "Ting-Lu", "stats": [155, 110, 125, 55, 80, 45], "abilities": ["Vessel of Ruin"], "tier": "SM OU", "height": 2.7, "weight": 699.7, "moves": [movenum("Mean Look"), movenum("Sand Tomb"), movenum("Spite"), movenum("Spikes"), movenum("Payback"), movenum("Stomp"), movenum("Bulldoze"), movenum("Whirlwind"), movenum("Taunt"), movenum("Thrash"), movenum("Dark Pulse"), movenum("Stomping Tantrum"), movenum("Ruination"), movenum("Throat Chop"), movenum("Rock Slide"), movenum("Memento"), movenum("Earthquake"), movenum("Fissure"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Facade"), movenum("Hex"), movenum("Snarl"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Endure"), movenum("Sunny Day"), movenum("Sandstorm"), movenum("Dig"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Body Press"), movenum("Substitute"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Earth Power"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Tera Blast")] }, "1004": { "types": ["Dark", "Fire"], "name": "Chi-Yu", "stats": [55, 80, 80, 135, 120, 100], "abilities": ["Beads of Ruin"], "tier": "SM Ubers", "height": 0.4, "weight": 4.9, "moves": [movenum("Ember"), movenum("Spite"), movenum("Mean Look"), movenum("Flame Wheel"), movenum("Payback"), movenum("Will-O-Wisp"), movenum("Flame Charge"), movenum("Incinerate"), movenum("Confuse Ray"), movenum("Nasty Plot"), movenum("Dark Pulse"), movenum("Lava Plume"), movenum("Ruination"), movenum("Bounce"), movenum("Swagger"), movenum("Inferno"), movenum("Memento"), movenum("Overheat"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Spin"), movenum("Facade"), movenum("Hex"), movenum("Snarl"), movenum("Endure"), movenum("Sunny Day"), movenum("Zen Headbutt"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Crunch"), movenum("Heat Wave"), movenum("Psychic"), movenum("Flamethrower"), movenum("Fire Blast"), movenum("Giga Impact"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Tera Blast")] }, "1005": { "types": ["Dragon", "Dark"], "name": "Roaring Moon", "stats": [105, 139, 71, 55, 101, 119], "abilities": ["Protosynthesis"], "tier": "SM OU", "height": 2, "weight": 380, "moves": [movenum("Sunny Day"), movenum("Jaw Lock"), movenum("Breaking Swipe"), movenum("Scale Shot"), movenum("Dragon Breath"), movenum("Leer"), movenum("Bite"), movenum("Focus Energy"), movenum("Incinerate"), movenum("Headbutt"), movenum("Scary Face"), movenum("Dragon Claw"), movenum("Zen Headbutt"), movenum("Flamethrower"), movenum("Night Slash"), movenum("Dragon Dance"), movenum("Dragon Rush"), movenum("Fly"), movenum("Throat Chop"), movenum("Roost"), movenum("Double-Edge"), movenum("Take Down"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Acrobatics"), movenum("Fire Spin"), movenum("Facade"), movenum("Aerial Ace"), movenum("Snarl"), movenum("Metal Claw"), movenum("Dragon Tail"), movenum("Endure"), movenum("Dig"), movenum("Brick Break"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Air Slash"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Rock Slide"), movenum("Taunt"), movenum("Body Press"), movenum("Dark Pulse"), movenum("Iron Head"), movenum("Substitute"), movenum("X-Scissor"), movenum("Crunch"), movenum("Tailwind"), movenum("Dragon Pulse"), movenum("Hyper Voice"), movenum("Heat Wave"), movenum("Fire Blast"), movenum("Hydro Pump"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "1006": { "types": ["Fairy", "Fighting"], "name": "Iron Valiant", "stats": [74, 130, 90, 120, 60, 116], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 1.4, "weight": 35, "moves": [movenum("Electric Terrain"), movenum("Disable"), movenum("Double Team"), movenum("Shadow Sneak"), movenum("Fury Cutter"), movenum("Hypnosis"), movenum("Feint"), movenum("Future Sight"), movenum("Dazzling Gleam"), movenum("Psycho Cut"), movenum("Night Slash"), movenum("Leaf Blade"), movenum("Moonblast"), movenum("Close Combat"), movenum("Knock Off"), movenum("Destiny Bond"), movenum("Wide Guard"), movenum("Quick Guard"), movenum("Spirit Break"), movenum("Agility"), movenum("Protect"), movenum("Low Kick"), movenum("Psybeam"), movenum("Confuse Ray"), movenum("Charge Beam"), movenum("Aerial Ace"), movenum("Hex"), movenum("Swift"), movenum("Magical Leaf"), movenum("Icy Wind"), movenum("Stored Power"), movenum("Fling"), movenum("Endure"), movenum("Psyshock"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("Shadow Claw"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Drain Punch"), movenum("Reflect"), movenum("Light Screen"), movenum("Metronome"), movenum("Grass Knot"), movenum("Thunder Wave"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Imprison"), movenum("Skill Swap"), movenum("Substitute"), movenum("X-Scissor"), movenum("Trick"), movenum("Liquidation"), movenum("Aura Sphere"), movenum("Shadow Ball"), movenum("Hyper Voice"), movenum("Energy Ball"), movenum("Psychic"), movenum("Encore"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Reversal"), movenum("Psychic Terrain"), movenum("Misty Terrain"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Tera Blast")] }, "1007": { "types": ["Fighting", "Dragon"], "name": "Koraidon", "stats": [100, 135, 115, 85, 100, 135], "abilities": ["Orichalcum Pulse"], "tier": "SM Ubers", "height": 2.5, "weight": 303, "moves": [movenum("Sunny Day"), movenum("Breaking Swipe"), movenum("Rock Smash"), movenum("Ancient Power"), movenum("Drain Punch"), movenum("Brick Break"), movenum("Agility"), movenum("Dragon Claw"), movenum("Flamethrower"), movenum("Collision Course"), movenum("Screech"), movenum("Counter"), movenum("Outrage"), movenum("Close Combat"), movenum("Flare Blitz"), movenum("Giga Impact"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Mud Shot"), movenum("Flame Charge"), movenum("Low Sweep"), movenum("Dragon Tail"), movenum("Endure"), movenum("Dig"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Crunch"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Heavy Slam"), movenum("Helping Hand"), movenum("Reversal"), movenum("Fire Blast"), movenum("Wild Charge"), movenum("Overheat"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "66543": { "types": ["Fighting", "Dragon"], "name": "Koraidon-Limited", "stats": [100, 135, 115, 85, 100, 135], "abilities": ["Orichalcum Pulse"], "tier": "SM Ubers", "height": 3.5, "weight": 303, "moves": [movenum("Sunny Day"), movenum("Breaking Swipe"), movenum("Rock Smash"), movenum("Ancient Power"), movenum("Drain Punch"), movenum("Brick Break"), movenum("Agility"), movenum("Dragon Claw"), movenum("Flamethrower"), movenum("Collision Course"), movenum("Screech"), movenum("Counter"), movenum("Outrage"), movenum("Close Combat"), movenum("Flare Blitz"), movenum("Giga Impact"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Mud Shot"), movenum("Flame Charge"), movenum("Low Sweep"), movenum("Dragon Tail"), movenum("Endure"), movenum("Dig"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Crunch"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Heavy Slam"), movenum("Helping Hand"), movenum("Reversal"), movenum("Fire Blast"), movenum("Wild Charge"), movenum("Overheat"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "132079": { "types": ["Fighting", "Dragon"], "name": "Koraidon-Sprinting", "stats": [100, 135, 115, 85, 100, 135], "abilities": ["Orichalcum Pulse"], "tier": "SM Ubers", "height": 3.5, "weight": 303, "moves": [movenum("Sunny Day"), movenum("Breaking Swipe"), movenum("Rock Smash"), movenum("Ancient Power"), movenum("Drain Punch"), movenum("Brick Break"), movenum("Agility"), movenum("Dragon Claw"), movenum("Flamethrower"), movenum("Collision Course"), movenum("Screech"), movenum("Counter"), movenum("Outrage"), movenum("Close Combat"), movenum("Flare Blitz"), movenum("Giga Impact"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Mud Shot"), movenum("Flame Charge"), movenum("Low Sweep"), movenum("Dragon Tail"), movenum("Endure"), movenum("Dig"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Crunch"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Heavy Slam"), movenum("Helping Hand"), movenum("Reversal"), movenum("Fire Blast"), movenum("Wild Charge"), movenum("Overheat"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "197615": { "types": ["Fighting", "Dragon"], "name": "Koraidon-Swimming", "stats": [100, 135, 115, 85, 100, 135], "abilities": ["Orichalcum Pulse"], "tier": "SM Ubers", "height": 3.5, "weight": 303, "moves": [movenum("Sunny Day"), movenum("Breaking Swipe"), movenum("Rock Smash"), movenum("Ancient Power"), movenum("Drain Punch"), movenum("Brick Break"), movenum("Agility"), movenum("Dragon Claw"), movenum("Flamethrower"), movenum("Collision Course"), movenum("Screech"), movenum("Counter"), movenum("Outrage"), movenum("Close Combat"), movenum("Flare Blitz"), movenum("Giga Impact"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Mud Shot"), movenum("Flame Charge"), movenum("Low Sweep"), movenum("Dragon Tail"), movenum("Endure"), movenum("Dig"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Crunch"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Heavy Slam"), movenum("Helping Hand"), movenum("Reversal"), movenum("Fire Blast"), movenum("Wild Charge"), movenum("Overheat"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "263151": { "types": ["Fighting", "Dragon"], "name": "Koraidon-Gliding", "stats": [100, 135, 115, 85, 100, 135], "abilities": ["Orichalcum Pulse"], "tier": "SM Ubers", "height": 3.5, "weight": 303, "moves": [movenum("Sunny Day"), movenum("Breaking Swipe"), movenum("Rock Smash"), movenum("Ancient Power"), movenum("Drain Punch"), movenum("Brick Break"), movenum("Agility"), movenum("Dragon Claw"), movenum("Flamethrower"), movenum("Collision Course"), movenum("Screech"), movenum("Counter"), movenum("Outrage"), movenum("Close Combat"), movenum("Flare Blitz"), movenum("Giga Impact"), movenum("Take Down"), movenum("Mud-Slap"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Low Kick"), movenum("Acrobatics"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Mud Shot"), movenum("Flame Charge"), movenum("Low Sweep"), movenum("Dragon Tail"), movenum("Endure"), movenum("Dig"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Bulk Up"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Iron Head"), movenum("Substitute"), movenum("Crunch"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Heavy Slam"), movenum("Helping Hand"), movenum("Reversal"), movenum("Fire Blast"), movenum("Wild Charge"), movenum("Overheat"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "1008": { "types": ["Electric", "Dragon"], "name": "Miraidon", "stats": [100, 85, 100, 135, 115, 135], "abilities": ["Hadron Engine"], "tier": "SM Ubers", "height": 3.5, "weight": 240, "moves": [movenum("Electric Terrain"), movenum("Thunder Shock"), movenum("Dragon Breath"), movenum("Shock Wave"), movenum("Charge"), movenum("Parabolic Charge"), movenum("Discharge"), movenum("Agility"), movenum("Dragon Pulse"), movenum("Electro Drift"), movenum("Metal Sound"), movenum("Mirror Coat"), movenum("Outrage"), movenum("Thunder"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Confuse Ray"), movenum("Charge Beam"), movenum("Facade"), movenum("Snarl"), movenum("Dragon Tail"), movenum("Endure"), movenum("Volt Switch"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dragon Claw"), movenum("Dazzling Gleam"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Flash Cannon"), movenum("Eerie Impulse"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "66544": { "types": ["Electric", "Dragon"], "name": "Miraidon-Low Power", "stats": [100, 85, 100, 135, 115, 135], "abilities": ["Hadron Engine"], "tier": "SM Ubers", "height": 2.8, "weight": 240, "moves": [movenum("Electric Terrain"), movenum("Thunder Shock"), movenum("Dragon Breath"), movenum("Shock Wave"), movenum("Charge"), movenum("Parabolic Charge"), movenum("Discharge"), movenum("Agility"), movenum("Dragon Pulse"), movenum("Electro Drift"), movenum("Metal Sound"), movenum("Mirror Coat"), movenum("Outrage"), movenum("Thunder"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Confuse Ray"), movenum("Charge Beam"), movenum("Facade"), movenum("Snarl"), movenum("Dragon Tail"), movenum("Endure"), movenum("Volt Switch"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dragon Claw"), movenum("Dazzling Gleam"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Flash Cannon"), movenum("Eerie Impulse"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "132080": { "types": ["Electric", "Dragon"], "name": "Miraidon-Drive", "stats": [100, 85, 100, 135, 115, 135], "abilities": ["Hadron Engine"], "tier": "SM Ubers", "height": 2.8, "weight": 240, "moves": [movenum("Electric Terrain"), movenum("Thunder Shock"), movenum("Dragon Breath"), movenum("Shock Wave"), movenum("Charge"), movenum("Parabolic Charge"), movenum("Discharge"), movenum("Agility"), movenum("Dragon Pulse"), movenum("Electro Drift"), movenum("Metal Sound"), movenum("Mirror Coat"), movenum("Outrage"), movenum("Thunder"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Confuse Ray"), movenum("Charge Beam"), movenum("Facade"), movenum("Snarl"), movenum("Dragon Tail"), movenum("Endure"), movenum("Volt Switch"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dragon Claw"), movenum("Dazzling Gleam"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Flash Cannon"), movenum("Eerie Impulse"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "197616": { "types": ["Electric", "Dragon"], "name": "Miraidon-Aquatic", "stats": [100, 85, 100, 135, 115, 135], "abilities": ["Hadron Engine"], "tier": "SM Ubers", "height": 2.8, "weight": 240, "moves": [movenum("Electric Terrain"), movenum("Thunder Shock"), movenum("Dragon Breath"), movenum("Shock Wave"), movenum("Charge"), movenum("Parabolic Charge"), movenum("Discharge"), movenum("Agility"), movenum("Dragon Pulse"), movenum("Electro Drift"), movenum("Metal Sound"), movenum("Mirror Coat"), movenum("Outrage"), movenum("Thunder"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Confuse Ray"), movenum("Charge Beam"), movenum("Facade"), movenum("Snarl"), movenum("Dragon Tail"), movenum("Endure"), movenum("Volt Switch"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dragon Claw"), movenum("Dazzling Gleam"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Flash Cannon"), movenum("Eerie Impulse"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "263152": { "types": ["Electric", "Dragon"], "name": "Miraidon-Glide", "stats": [100, 85, 100, 135, 115, 135], "abilities": ["Hadron Engine"], "tier": "SM Ubers", "height": 2.8, "weight": 240, "moves": [movenum("Electric Terrain"), movenum("Thunder Shock"), movenum("Dragon Breath"), movenum("Shock Wave"), movenum("Charge"), movenum("Parabolic Charge"), movenum("Discharge"), movenum("Agility"), movenum("Dragon Pulse"), movenum("Electro Drift"), movenum("Metal Sound"), movenum("Mirror Coat"), movenum("Outrage"), movenum("Thunder"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Acrobatics"), movenum("Confuse Ray"), movenum("Charge Beam"), movenum("Facade"), movenum("Snarl"), movenum("Dragon Tail"), movenum("Endure"), movenum("Volt Switch"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Reflect"), movenum("Light Screen"), movenum("Dragon Claw"), movenum("Dazzling Gleam"), movenum("Thunder Wave"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Flash Cannon"), movenum("Eerie Impulse"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "1009": { "types": ["Water", "Dragon"], "name": "Walking Wake", "stats": [99, 83, 91, 125, 83, 109], "abilities": ["Protosynthesis"], "tier": "SM OU", "height": 3.5, "weight": 280, "moves": [movenum("Roar"), movenum("Leer"), movenum("Twister"), movenum("Aqua Jet"), movenum("Sunny Day"), movenum("Hone Claws"), movenum("Bite"), movenum("Water Pulse"), movenum("Noble Roar"), movenum("Dragon Breath"), movenum("Breaking Swipe"), movenum("Dragon Rush"), movenum("Hydro Steam"), movenum("Dragon Pulse"), movenum("Outrage"), movenum("Flamethrower"), movenum("Hydro Pump"), movenum("Take Down"), movenum("Agility"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Low Kick"), movenum("Chilling Water"), movenum("Facade"), movenum("Snarl"), movenum("Swift"), movenum("Mud Shot"), movenum("Dragon Tail"), movenum("Endure"), movenum("Rain Dance"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Waterfall"), movenum("Dragon Claw"), movenum("Rest"), movenum("Dragon Dance"), movenum("Substitute"), movenum("Crunch"), movenum("Liquidation"), movenum("Surf"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast")] }, "1010": { "types": ["Grass", "Psychic"], "name": "Iron Leaves", "stats": [90, 130, 88, 70, 108, 104], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 1.5, "weight": 125, "moves": [movenum("Quick Attack"), movenum("Leer"), movenum("Helping Hand"), movenum("Work Up"), movenum("Electric Terrain"), movenum("Quash"), movenum("Magical Leaf"), movenum("Retaliate"), movenum("Quick Guard"), movenum("Night Slash"), movenum("Swords Dance"), movenum("Sacred Sword"), movenum("Leaf Blade"), movenum("Psyblade"), movenum("Close Combat"), movenum("Imprison"), movenum("Megahorn"), movenum("Ally Switch"), movenum("Solar Blade"), movenum("Take Down"), movenum("Agility"), movenum("Scary Face"), movenum("Protect"), movenum("Trailblaze"), movenum("Facade"), movenum("Aerial Ace"), movenum("Swift"), movenum("Endure"), movenum("Smart Strike"), movenum("False Swipe"), movenum("Brick Break"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Grass Knot"), movenum("Rest"), movenum("Taunt"), movenum("Substitute"), movenum("Iron Defense"), movenum("X-Scissor"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Reversal"), movenum("Grassy Terrain"), movenum("Psychic Terrain"), movenum("Wild Charge"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast")] }, "66437": { "types": ["Normal", "Ground"], "name": "Ursaluna-Bloodmoon", "stats": [113, 70, 120, 135, 65, 52], "abilities": ["Mind's Eye"], "tier": "SM OU", "height": 2.7, "weight": 333.0, "moves": [movenum("Headlong Rush"), movenum("Scratch"), movenum("Leer"), movenum("Lick"), movenum("Moonlight"), movenum("Fury Swipes"), movenum("Payback"), movenum("Harden"), movenum("Slash"), movenum("Play Nice"), movenum("Scary Face"), movenum("Rest"), movenum("Snore"), movenum("Earth Power"), movenum("Moonblast"), movenum("Hammer Arm"), movenum("Blood Moon"), movenum("Take Down"), movenum("Protect"), movenum("Low Kick"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Metal Claw"), movenum("Swift"), movenum("Mud Shot"), movenum("Rock Tomb"), movenum("Fling"), movenum("Avalanche"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Dig"), movenum("Brick Break"), movenum("Shadow Claw"), movenum("Body Slam"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Stomping Tantrum"), movenum("Rock Slide"), movenum("Taunt"), movenum("Swords Dance"), movenum("Body Press"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Crunch"), movenum("Hyper Voice"), movenum("Heavy Slam"), movenum("Calm Mind"), movenum("Helping Hand"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Roar"), movenum("Smack Down"), movenum("Vacuum Wave"), movenum("High Horsepower"), movenum("Uproar"), movenum("Focus Punch")] }, "1011": { "types": ["Grass", "Dragon"], "name": "Dipplin", "stats": [80, 80, 110, 95, 80, 40], "abilities": ["Supersweet Syrup", "Gluttony", "Sticky Hold"], "tier": "SM PU", "height": 0.4, "weight": 4.4, "moves": [movenum("Recycle"), movenum("Withdraw"), movenum("Astonish"), movenum("Sweet Scent"), movenum("Infestation"), movenum("Double Hit"), movenum("Dragon Tail"), movenum("Growth"), movenum("Dragon Breath"), movenum("Protect"), movenum("Bullet Seed"), movenum("Syrup Bomb"), movenum("Dragon Pulse"), movenum("Recover"), movenum("Energy Ball"), movenum("Substitute"), movenum("Take Down"), movenum("Pounce"), movenum("Facade"), movenum("Endure"), movenum("Sunny Day"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Reflect"), movenum("Grass Knot"), movenum("Rest"), movenum("Giga Drain"), movenum("Pollen Puff"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Outrage"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Gyro Ball"), movenum("Bug Bite"), movenum("Grassy Glide"), movenum("Defense Curl"), movenum("Rollout"), movenum("Sucker Punch")] }, "1012": { "types": ["Grass", "Ghost"], "name": "Poltchageist", "stats": [40, 45, 45, 74, 54, 50], "abilities": ["Hospitality", "Heatproof"], "tier": "SM LC", "height": 0.1, "weight": 1.1, "moves": [movenum("Astonish"), movenum("Withdraw"), movenum("Stun Spore"), movenum("Absorb"), movenum("Life Dew"), movenum("Foul Play"), movenum("Mega Drain"), movenum("Hex"), movenum("Rage Powder"), movenum("Giga Drain"), movenum("Shadow Ball"), movenum("Memento"), movenum("Leaf Storm"), movenum("Protect"), movenum("Magical Leaf"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Rest"), movenum("Imprison"), movenum("Substitute"), movenum("Iron Defense"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Phantom Force"), movenum("Trick Room"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Spite"), movenum("Scald"), movenum("Uproar"), movenum("Poltergeist")] }, "66548": { "types": ["Grass", "Ghost"], "name": "Poltchageist-Artisan", "stats": [40, 45, 45, 74, 54, 50], "abilities": ["Hospitality", "Heatproof"], "tier": "SM LC", "height": 0.1, "weight": 1.1, "moves": [movenum("Astonish"), movenum("Withdraw"), movenum("Stun Spore"), movenum("Absorb"), movenum("Life Dew"), movenum("Foul Play"), movenum("Mega Drain"), movenum("Hex"), movenum("Rage Powder"), movenum("Giga Drain"), movenum("Shadow Ball"), movenum("Memento"), movenum("Leaf Storm"), movenum("Protect"), movenum("Magical Leaf"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Rest"), movenum("Imprison"), movenum("Substitute"), movenum("Iron Defense"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Phantom Force"), movenum("Trick Room"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Spite"), movenum("Scald"), movenum("Uproar"), movenum("Poltergeist")] }, "1013": { "types": ["Grass", "Ghost"], "name": "Sinistcha", "stats": [71, 60, 106, 121, 80, 70], "abilities": ["Hospitality", "Heatproof"], "tier": "SM OU", "height": 0.2, "weight": 2.2, "moves": [movenum("Astonish"), movenum("Withdraw"), movenum("Stun Spore"), movenum("Matcha Gotcha"), movenum("Absorb"), movenum("Life Dew"), movenum("Foul Play"), movenum("Mega Drain"), movenum("Hex"), movenum("Rage Powder"), movenum("Strength Sap"), movenum("Shadow Ball"), movenum("Memento"), movenum("Leaf Storm"), movenum("Protect"), movenum("Magical Leaf"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Rest"), movenum("Imprison"), movenum("Substitute"), movenum("Iron Defense"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Phantom Force"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Spite"), movenum("Scald"), movenum("Uproar"), movenum("Poltergeist")] }, "66549": { "types": ["Grass", "Ghost"], "name": "Sinistcha-Masterpiece", "stats": [71, 60, 106, 121, 80, 70], "abilities": ["Hospitality", "Heatproof"], "tier": "SM OU", "height": 0.2, "weight": 2.2, "moves": [movenum("Astonish"), movenum("Withdraw"), movenum("Stun Spore"), movenum("Matcha Gotcha"), movenum("Absorb"), movenum("Life Dew"), movenum("Foul Play"), movenum("Mega Drain"), movenum("Hex"), movenum("Rage Powder"), movenum("Strength Sap"), movenum("Shadow Ball"), movenum("Memento"), movenum("Leaf Storm"), movenum("Protect"), movenum("Magical Leaf"), movenum("Night Shade"), movenum("Endure"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Rest"), movenum("Imprison"), movenum("Substitute"), movenum("Iron Defense"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Calm Mind"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Phantom Force"), movenum("Trick Room"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Spite"), movenum("Scald"), movenum("Uproar"), movenum("Poltergeist")] }, "1014": { "types": ["Poison", "Fighting"], "name": "Okidogi", "stats": [88, 128, 115, 58, 86, 80], "abilities": ["Toxic Chain", "Guard Dog"], "tier": "SM OU", "height": 1.8, "weight": 92.2, "moves": [movenum("Bite"), movenum("Low Kick"), movenum("Bulk Up"), movenum("Howl"), movenum("Poison Fang"), movenum("Force Palm"), movenum("Counter"), movenum("Poison Jab"), movenum("Brutal Swing"), movenum("Crunch"), movenum("Superpower"), movenum("Giga Impact"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Fire Fang"), movenum("Thunder Fang"), movenum("Ice Fang"), movenum("Thief"), movenum("Facade"), movenum("Poison Tail"), movenum("Snarl"), movenum("Metal Claw"), movenum("Rock Tomb"), movenum("Low Sweep"), movenum("Fling"), movenum("Endure"), movenum("Dig"), movenum("Brick Break"), movenum("Shadow Claw"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Fire Punch"), movenum("Thunder Punch"), movenum("Ice Punch"), movenum("Sleep Talk"), movenum("Drain Punch"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Body Press"), movenum("Iron Head"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Reversal"), movenum("Sludge Bomb"), movenum("Outrage"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Close Combat"), movenum("Tera Blast"), movenum("Roar"), movenum("Toxic"), movenum("Spite"), movenum("Knock Off"), movenum("High Horsepower"), movenum("Uproar"), movenum("Focus Punch"), movenum("Lash Out")] }, "1015": { "types": ["Poison", "Psychic"], "name": "Munkidori", "stats": [88, 75, 66, 130, 90, 106], "abilities": ["Toxic Chain", "Frisk"], "tier": "SM OU", "height": 1.0, "weight": 12.2, "moves": [movenum("Scratch"), movenum("Confusion"), movenum("Fake Out"), movenum("Flatter"), movenum("Helping Hand"), movenum("Psybeam"), movenum("Clear Smog"), movenum("Psychic"), movenum("Sludge Wave"), movenum("Nasty Plot"), movenum("Future Sight"), movenum("Parting Shot"), movenum("Mud-Slap"), movenum("Protect"), movenum("Acid Spray"), movenum("Confuse Ray"), movenum("Thief"), movenum("Trailblaze"), movenum("Facade"), movenum("Hex"), movenum("Swift"), movenum("Stored Power"), movenum("Night Shade"), movenum("Fling"), movenum("Venoshock"), movenum("Endure"), movenum("Psyshock"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Sleep Talk"), movenum("Light Screen"), movenum("Metronome"), movenum("Grass Knot"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Imprison"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Trick"), movenum("Shadow Ball"), movenum("Calm Mind"), movenum("Baton Pass"), movenum("Psychic Terrain"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Hyper Beam"), movenum("Tera Blast"), movenum("Toxic"), movenum("Spite"), movenum("Uproar"), movenum("Poltergeist"), movenum("Lash Out")] }, "1016": { "types": ["Poison", "Fairy"], "name": "Fezandipiti", "stats": [88, 91, 82, 70, 125, 99], "abilities": ["Toxic Chain", "Technician"], "tier": "SM OU", "height": 1.4, "weight": 30.1, "moves": [movenum("Double Kick"), movenum("Peck"), movenum("Poison Gas"), movenum("Disarming Voice"), movenum("Quick Attack"), movenum("Attract"), movenum("Wing Attack"), movenum("Cross Poison"), movenum("Tail Slap"), movenum("Beat Up"), movenum("Flatter"), movenum("Roost"), movenum("Moonblast"), movenum("Take Down"), movenum("Charm"), movenum("Agility"), movenum("Protect"), movenum("Acid Spray"), movenum("Acrobatics"), movenum("Thief"), movenum("Facade"), movenum("Poison Tail"), movenum("Aerial Ace"), movenum("Hex"), movenum("Swift"), movenum("Icy Wind"), movenum("Air Cutter"), movenum("Venoshock"), movenum("Endure"), movenum("U-turn"), movenum("Shadow Claw"), movenum("Air Slash"), movenum("Sleep Talk"), movenum("Light Screen"), movenum("Dazzling Gleam"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Dark Pulse"), movenum("Fly"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Tailwind"), movenum("Shadow Ball"), movenum("Heat Wave"), movenum("Play Rough"), movenum("Calm Mind"), movenum("Nasty Plot"), movenum("Sludge Bomb"), movenum("Giga Impact"), movenum("Hurricane"), movenum("Hyper Beam"), movenum("Brave Bird"), movenum("Tera Blast"), movenum("Toxic"), movenum("Spite"), movenum("Uproar"), movenum("Dual Wingbeat"), movenum("Lash Out")] }, "1017": { "types": ["Grass", "???"], "name": "Ogerpon", "stats": [80, 120, 84, 60, 96, 110], "abilities": ["Defiant", "Embody Aspect"], // Embody Aspect only applies to Terastal form but this mechanic is unimplemented in Safari, so we'll just have it as a regular second ability "tier": "SM OU", "height": 1.2, "weight": 39.8, "moves": [movenum("Vine Whip"), movenum("Leech Seed"), movenum("Quick Attack"), movenum("Follow Me"), movenum("Double Kick"), movenum("Counter"), movenum("Retaliate"), movenum("Horn Leech"), movenum("Focus Energy"), movenum("Growth"), movenum("Slam"), movenum("Low Sweep"), movenum("Ivy Cudgel"), movenum("Throat Chop"), movenum("Synthesis"), movenum("Spiky Shield"), movenum("Power Whip"), movenum("Superpower"), movenum("Wood Hammer"), movenum("Take Down"), movenum("Charm"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Trailblaze"), movenum("Facade"), movenum("Magical Leaf"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Spikes"), movenum("Substitute"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Encore"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Reversal"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Knock Off"), movenum("Solar Blade"), movenum("Grassy Glide"), movenum("Lash Out")] }, "66553": { "types": ["Grass", "Water"], "name": "Ogerpon-Wellspring", "stats": [80, 120, 84, 60, 96, 110], "abilities": ["Water Absorb", "Embody Aspect"], // Embody Aspect only applies to Terastal form but this mechanic is unimplemented in Safari, so we'll just have it as a regular second ability "tier": "SM OU", "height": 1.2, "weight": 39.8, "moves": [movenum("Vine Whip"), movenum("Leech Seed"), movenum("Quick Attack"), movenum("Follow Me"), movenum("Double Kick"), movenum("Counter"), movenum("Retaliate"), movenum("Horn Leech"), movenum("Focus Energy"), movenum("Growth"), movenum("Slam"), movenum("Low Sweep"), movenum("Ivy Cudgel"), movenum("Throat Chop"), movenum("Synthesis"), movenum("Spiky Shield"), movenum("Power Whip"), movenum("Superpower"), movenum("Wood Hammer"), movenum("Take Down"), movenum("Charm"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Trailblaze"), movenum("Facade"), movenum("Magical Leaf"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Spikes"), movenum("Substitute"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Encore"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Reversal"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Knock Off"), movenum("Solar Blade"), movenum("Grassy Glide"), movenum("Lash Out")] }, "132089": { "types": ["Grass", "Fire"], "name": "Ogerpon-Hearthflame", "stats": [80, 120, 84, 60, 96, 110], "abilities": ["Mold Breaker", "Embody Aspect"], // Embody Aspect only applies to Terastal form but this mechanic is unimplemented in Safari, so we'll just have it as a regular second ability "tier": "SM OU", "height": 1.2, "weight": 39.8, "moves": [movenum("Vine Whip"), movenum("Leech Seed"), movenum("Quick Attack"), movenum("Follow Me"), movenum("Double Kick"), movenum("Counter"), movenum("Retaliate"), movenum("Horn Leech"), movenum("Focus Energy"), movenum("Growth"), movenum("Slam"), movenum("Low Sweep"), movenum("Ivy Cudgel"), movenum("Throat Chop"), movenum("Synthesis"), movenum("Spiky Shield"), movenum("Power Whip"), movenum("Superpower"), movenum("Wood Hammer"), movenum("Take Down"), movenum("Charm"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Trailblaze"), movenum("Facade"), movenum("Magical Leaf"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Spikes"), movenum("Substitute"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Encore"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Reversal"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Knock Off"), movenum("Solar Blade"), movenum("Grassy Glide"), movenum("Lash Out")] }, "197625": { "types": ["Grass", "Rock"], "name": "Ogerpon-Cornerstone", "stats": [80, 120, 84, 60, 96, 110], "abilities": ["Sturdy", "Embody Aspect"], // Embody Aspect only applies to Terastal form but this mechanic is unimplemented in Safari, so we'll just have it as a regular second ability "tier": "SM OU", "height": 1.2, "weight": 39.8, "moves": [movenum("Vine Whip"), movenum("Leech Seed"), movenum("Quick Attack"), movenum("Follow Me"), movenum("Double Kick"), movenum("Counter"), movenum("Retaliate"), movenum("Horn Leech"), movenum("Focus Energy"), movenum("Growth"), movenum("Slam"), movenum("Low Sweep"), movenum("Ivy Cudgel"), movenum("Throat Chop"), movenum("Synthesis"), movenum("Spiky Shield"), movenum("Power Whip"), movenum("Superpower"), movenum("Wood Hammer"), movenum("Take Down"), movenum("Charm"), movenum("Scary Face"), movenum("Protect"), movenum("Low Kick"), movenum("Trailblaze"), movenum("Facade"), movenum("Magical Leaf"), movenum("Rock Tomb"), movenum("Fling"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Sandstorm"), movenum("False Swipe"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("U-turn"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Grass Knot"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Swords Dance"), movenum("Spikes"), movenum("Substitute"), movenum("Giga Drain"), movenum("Energy Ball"), movenum("Encore"), movenum("Play Rough"), movenum("Helping Hand"), movenum("Reversal"), movenum("Grassy Terrain"), movenum("Giga Impact"), movenum("Leaf Storm"), movenum("Solar Beam"), movenum("Tera Blast"), movenum("Knock Off"), movenum("Solar Blade"), movenum("Grassy Glide"), movenum("Lash Out")] }, "1018": { "types": ["Steel", "Dragon"], "name": "Archaludon", "stats": [90, 105, 130, 125, 65, 85], "abilities": ["Stamina", "Sturdy", "Stalwart"], "tier": "SM OU", "height": 2.0, "weight": 60, "moves": [movenum("Leer"), movenum("Metal Claw"), movenum("Electro Shot"), movenum("Rock Smash"), movenum("Hone Claws"), movenum("Metal Sound"), movenum("Breaking Swipe"), movenum("Dragon Tail"), movenum("Iron Defense"), movenum("Focus Energy"), movenum("Dragon Claw"), movenum("Flash Cannon"), movenum("Metal Burst"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Facade"), movenum("Snarl"), movenum("Rock Tomb"), movenum("Endure"), movenum("Brick Break"), movenum("Foul Play"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Reflect"), movenum("Light Screen"), movenum("Thunder Wave"), movenum("Rest"), movenum("Rock Slide"), movenum("Swords Dance"), movenum("Body Press"), movenum("Dark Pulse"), movenum("Iron Head"), movenum("Substitute"), movenum("Aura Sphere"), movenum("Dragon Pulse"), movenum("Stealth Rock"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Outrage"), movenum("Thunder"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Roar"), movenum("Smack Down"), movenum("Gyro Ball"), movenum("Double-Edge"), movenum("Meteor Beam"), movenum("Hard Press"), movenum("Dragon Cheer"), movenum("Mirror Coat"), movenum("Night Slash"), movenum("Slash")] }, "1019": { "types": ["Grass", "Dragon"], "name": "Hydrapple", "stats": [106, 80, 110, 120, 80, 44], "abilities": ["Supersweet Syrup", "Regenrator", "Sticky Hold"], "tier": "SM OU", "height": 1.8, "weight": 93, "moves": [movenum("Withdraw"), movenum("Sweet Scent"), movenum("Recycle"), movenum("Astonish"), movenum("Yawn"), movenum("Double Hit"), movenum("Infestation"), movenum("Fickle Beam"), movenum("Dragon Tail"), movenum("Growth"), movenum("Dragon Breath"), movenum("Protect"), movenum("Bullet Seed"), movenum("Syrup Bomb"), movenum("Dragon Pulse"), movenum("Recover"), movenum("Energy Ball"), movenum("Substitute"), movenum("Power Whip"), movenum("Take Down"), movenum("Pounce"), movenum("Facade"), movenum("Magical Leaf"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Seed Bomb"), movenum("Reflect"), movenum("Grass Knot"), movenum("Rest"), movenum("Body Press"), movenum("Giga Drain"), movenum("Heavy Slam"), movenum("Pollen Puff"), movenum("Earth Power"), movenum("Grassy Terrain"), movenum("Nasty Plot"), movenum("Hydro Pump"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Outrage"), movenum("Leaf Storm"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Gyro Ball"), movenum("Bug Bite"), movenum("Uproar"), movenum("Grassy Glide"), movenum("Double-Edge"), movenum("Breaking Swipe"), movenum("Curse"), movenum("Dragon Cheer"), movenum("Defense Curl"), movenum("Rollout"), movenum("Sucker Punch")] }, "1020": { "types": ["Fire", "Dragon"], "name": "Gouging Fire", "stats": [105, 115, 121, 65, 93, 91], "abilities": ["Protosynthesis"], "tier": "SM OU", "height": 3.5, "weight": 590, "moves": [movenum("Stomp"), movenum("Leer"), movenum("Incinerate"), movenum("Sunny Day"), movenum("Ancient Power"), movenum("Double Kick"), movenum("Noble Roar"), movenum("Fire Fang"), movenum("Howl"), movenum("Bite"), movenum("Dragon Claw"), movenum("Crush Claw"), movenum("Morning Sun"), movenum("Burning Bulwark"), movenum("Dragon Rush"), movenum("Fire Blast"), movenum("Lava Plume"), movenum("Outrage"), movenum("Flare Blitz"), movenum("Raging Fury"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Thunder Fang"), movenum("Fire Spin"), movenum("Facade"), movenum("Bulldoze"), movenum("Snarl"), movenum("Flame Charge"), movenum("Dragon Tail"), movenum("Endure"), movenum("Smart Strike"), movenum("Psychic Fangs"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Iron Head"), movenum("Dragon Dance"), movenum("Substitute"), movenum("Crunch"), movenum("Dragon Pulse"), movenum("Heat Wave"), movenum("Flamethrower"), movenum("Reversal"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Overheat"), movenum("Hyper Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Roar"), movenum("Heat Crash"), movenum("Weather Ball"), movenum("Scale Shot"), movenum("Double-Edge"), movenum("Temper Flare"), movenum("Scorching Sands"), movenum("Breaking Swipe"), movenum("Dragon Cheer")] }, "1021": { "types": ["Electric", "Dragon"], "name": "Raging Bolt", "stats": [125, 73, 91, 137, 89, 75], "abilities": ["Protosynthesis"], "tier": "SM OU", "height": 5.2, "weight": 480, "moves": [movenum("Twister"), movenum("Sunny Day"), movenum("Shock Wave"), movenum("Stomp"), movenum("Ancient Power"), movenum("Charge"), movenum("Dragon Breath"), movenum("Electric Terrain"), movenum("Discharge"), movenum("Dragon Tail"), movenum("Calm Mind"), movenum("Thunderclap"), movenum("Dragon Hammer"), movenum("Rising Voltage"), movenum("Dragon Pulse"), movenum("Zap Cannon"), movenum("Body Press"), movenum("Thunder"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Thunder Fang"), movenum("Charge Beam"), movenum("Facade"), movenum("Snarl"), movenum("Endure"), movenum("Volt Switch"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Electro Ball"), movenum("Thunder Wave"), movenum("Stomping Tantrum"), movenum("Rest"), movenum("Taunt"), movenum("Eerie Impulse"), movenum("Substitute"), movenum("Crunch"), movenum("Hyper Voice"), movenum("Heavy Slam"), movenum("Thunderbolt"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Giga Impact"), movenum("Outrage"), movenum("Hyper Beam"), movenum("Solar Beam"), movenum("Draco Meteor"), movenum("Tera Blast"), movenum("Roar"), movenum("Weather Ball"), movenum("Double-Edge"), movenum("Supercell Slam"), movenum("Electroweb"), movenum("Breaking Swipe"), movenum("Dragon Cheer")] }, "1022": { "types": ["Rock", "Psychic"], "name": "Iron Boulder", "stats": [90, 120, 80, 68, 108, 124], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 1.5, "weight": 162.5, "moves": [movenum("Horn Attack"), movenum("Leer"), movenum("Rock Throw"), movenum("Electric Terrain"), movenum("Quick Attack"), movenum("Slash"), movenum("Agility"), movenum("Psycho Cut"), movenum("Counter"), movenum("Rock Tomb"), movenum("Sacred Sword"), movenum("Mighty Cleave"), movenum("Swords Dance"), movenum("Megahorn"), movenum("Quick Guard"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Take Down"), movenum("Scary Face"), movenum("Protect"), movenum("Facade"), movenum("Aerial Ace"), movenum("Bulldoze"), movenum("Endure"), movenum("Sandstorm"), movenum("Psyshock"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("Air Slash"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rock Blast"), movenum("Poison Jab"), movenum("Rest"), movenum("Taunt"), movenum("Iron Head"), movenum("Substitute"), movenum("Iron Defense"), movenum("X-Scissor"), movenum("Psychic"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Hyper Beam"), movenum("Close Combat"), movenum("Tera Blast"), movenum("Solar Blade"), movenum("Double-Edge"), movenum("Meteor Beam"), movenum("Throat Chop")] }, "1023": { "types": ["Steel", "Psychic"], "name": "Iron Crown", "stats": [90, 72, 100, 122, 108, 98], "abilities": ["Quark Drive"], "tier": "SM OU", "height": 1.6, "weight": 156, "moves": [movenum("Leer"), movenum("Electric Terrain"), movenum("Confusion"), movenum("Metal Claw"), movenum("Smart Strike"), movenum("Slash"), movenum("Iron Defense"), movenum("Psyshock"), movenum("Psycho Cut"), movenum("Flash Cannon"), movenum("Sacred Sword"), movenum("Tachyon Cutter"), movenum("Future Sight"), movenum("Volt Switch"), movenum("Quick Guard"), movenum("Metal Burst"), movenum("Hyper Beam"), movenum("Take Down"), movenum("Agility"), movenum("Scary Face"), movenum("Protect"), movenum("Facade"), movenum("Bulldoze"), movenum("Stored Power"), movenum("Endure"), movenum("Brick Break"), movenum("Zen Headbutt"), movenum("Air Slash"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Rest"), movenum("Swords Dance"), movenum("Iron Head"), movenum("Substitute"), movenum("X-Scissor"), movenum("Psychic"), movenum("Heavy Slam"), movenum("Calm Mind"), movenum("Giga Impact"), movenum("Focus Blast"), movenum("Steel Beam"), movenum("Tera Blast"), movenum("Gravity"), movenum("Solar Blade"), movenum("Double-Edge"), movenum("Supercell Slam"), movenum("Expanding Force"), movenum("Metal Sound"), movenum("Psychic Noise")] }, "1024": { "types": ["Normal", "???"], "name": "Terapagos", "stats": [90, 65, 85, 65, 85, 60], "abilities": ["Tera Shift"], "tier": "SM Ubers", "height": 0.2, "weight": 6.5, "moves": [movenum("Withdraw"), movenum("Tri Attack"), movenum("Rapid Spin"), movenum("Ancient Power"), movenum("Headbutt"), movenum("Protect"), movenum("Earth Power"), movenum("Heavy Slam"), movenum("Tera Starstorm"), movenum("Double-Edge"), movenum("Rock Polish"), movenum("Gyro Ball"), movenum("Take Down"), movenum("Water Pulse"), movenum("Facade"), movenum("Stored Power"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Dark Pulse"), movenum("Iron Head"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Aura Sphere"), movenum("Dragon Pulse"), movenum("Stealth Rock"), movenum("Energy Ball"), movenum("Surf"), movenum("Ice Spinner"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Ice Beam"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Bug Buzz"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Thunder"), movenum("Solar Beam"), movenum("Roar"), movenum("Toxic"), movenum("Gravity"), movenum("Heat Crash"), movenum("Weather Ball"), movenum("Supercell Slam"), movenum("Scorching Sands"), movenum("Meteor Beam")] }, "66560": { "types": ["Normal", "???"], "name": "Terapagos-Terastal", "stats": [95, 95, 110, 105, 110, 85], "abilities": ["Tera Shell"], "tier": "SM Ubers", "height": 0.3, "weight": 16.0, "moves": [movenum("Withdraw"), movenum("Tri Attack"), movenum("Rapid Spin"), movenum("Ancient Power"), movenum("Headbutt"), movenum("Protect"), movenum("Earth Power"), movenum("Heavy Slam"), movenum("Tera Starstorm"), movenum("Double-Edge"), movenum("Rock Polish"), movenum("Gyro Ball"), movenum("Take Down"), movenum("Water Pulse"), movenum("Facade"), movenum("Stored Power"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Dark Pulse"), movenum("Iron Head"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Aura Sphere"), movenum("Dragon Pulse"), movenum("Stealth Rock"), movenum("Energy Ball"), movenum("Surf"), movenum("Ice Spinner"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Ice Beam"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Bug Buzz"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Thunder"), movenum("Solar Beam"), movenum("Roar"), movenum("Toxic"), movenum("Gravity"), movenum("Heat Crash"), movenum("Weather Ball"), movenum("Supercell Slam"), movenum("Scorching Sands"), movenum("Meteor Beam")] }, "132096": { "types": ["Stellar", "???"], "name": "Terapagos-Stellar", "stats": [160, 105, 110, 130, 110, 85], "abilities": ["Teraform Zero"], "tier": "SM Ubers", "height": 1.7, "weight": 77.0, "moves": [movenum("Withdraw"), movenum("Tri Attack"), movenum("Rapid Spin"), movenum("Ancient Power"), movenum("Headbutt"), movenum("Protect"), movenum("Earth Power"), movenum("Heavy Slam"), movenum("Tera Starstorm"), movenum("Double-Edge"), movenum("Rock Polish"), movenum("Gyro Ball"), movenum("Take Down"), movenum("Water Pulse"), movenum("Facade"), movenum("Stored Power"), movenum("Endure"), movenum("Sunny Day"), movenum("Rain Dance"), movenum("Zen Headbutt"), movenum("Body Slam"), movenum("Sleep Talk"), movenum("Dazzling Gleam"), movenum("Rest"), movenum("Rock Slide"), movenum("Body Press"), movenum("Flash Cannon"), movenum("Dark Pulse"), movenum("Iron Head"), movenum("Power Gem"), movenum("Substitute"), movenum("Crunch"), movenum("Aura Sphere"), movenum("Dragon Pulse"), movenum("Stealth Rock"), movenum("Energy Ball"), movenum("Surf"), movenum("Ice Spinner"), movenum("Flamethrower"), movenum("Thunderbolt"), movenum("Calm Mind"), movenum("Ice Beam"), movenum("Wild Charge"), movenum("Earthquake"), movenum("Stone Edge"), movenum("Giga Impact"), movenum("Bug Buzz"), movenum("Hyper Beam"), movenum("Flare Blitz"), movenum("Thunder"), movenum("Solar Beam"), movenum("Roar"), movenum("Toxic"), movenum("Gravity"), movenum("Heat Crash"), movenum("Weather Ball"), movenum("Supercell Slam"), movenum("Scorching Sands"), movenum("Meteor Beam")] }, "1025": { "types": ["Poison", "Ghost"], "name": "Pecharunt", "stats": [88, 88, 160, 88, 88, 88], "abilities": ["Poison Puppeteer"], "tier": "SM OU", "height": 0.3, "weight": 0.3, "moves": [movenum("Smog"), movenum("Poison Gas"), movenum("Memento"), movenum("Astonish"), movenum("Defense Curl"), movenum("Rollout"), movenum("Mean Look"), movenum("Withdraw"), movenum("Destiny Bond"), movenum("Fake Tears"), movenum("Parting Shot"), movenum("Shadow Ball"), movenum("Malignant Chain"), movenum("Toxic"), movenum("Nasty Plot"), movenum("Recover"), movenum("Protect"), movenum("Acid Spray"), movenum("Hex"), movenum("Night Shade"), movenum("Venoshock"), movenum("Endure"), movenum("Foul Play"), movenum("Sleep Talk"), movenum("Rest"), movenum("Imprison"), movenum("Gunk Shot"), movenum("Substitute"), movenum("Sludge Bomb"), movenum("Phantom Force"), movenum("Tera Blast"), movenum("Spite"), movenum("Poltergeist"), movenum("Sludge Wave"), movenum("Curse")] } /* template "": { "types": ["", ""], "name": "", "stats": [], "abilities": [], "tier": "SM OU", "height": 0, "weight": 0, "moves": [] }, */ }; var ultraAbilities = { "233": { "name": "Neuroforce" }, "234": { "name": "Intrepid Sword" }, "235": { "name": "Dauntless Shield" }, "236": { "name": "Libero" }, "237": { "name": "Ball Fetch" }, "238": { "name": "Cotton Down" }, "239": { "name": "Propeller Tail" }, "240": { "name": "Mirror Armor" }, "241": { "name": "Gulp Missile" }, "242": { "name": "Stalwart" }, "243": { "name": "Steam Engine" }, "244": { "name": "Punk Rock" }, "245": { "name": "Sand Spit" }, "246": { "name": "Ice Scales" }, "247": { "name": "Ripen" }, "248": { "name": "Ice Face" }, "249": { "name": "Power Spot" }, "250": { "name": "Mimicry" }, "251": { "name": "Screen Cleaner" }, "252": { "name": "Steely Spirit" }, "253": { "name": "Perish Body" }, "254": { "name": "Wandering Spirit" }, "255": { "name": "Gorilla Tactics" }, "256": { "name": "Neutralizing Gas" }, "257": { "name": "Pastel Veil" }, "258": { "name": "Hunger Switch" }, "259": { "name": "Unseen Fist" }, "260": { "name": "Transistor" }, "261": { "name": "Dragon's Maw" }, "262": { "name": "Chilling Neigh" }, "263": { "name": "Grim Neigh" }, "264": { "name": "As One" }, "265": { "name": "Quick Draw" }, "266": { "name": "Curious Medicine" }, "267": { "name": "Lingering Aroma" }, "268": { "name": "Seed Sower" }, "269": { "name": "Thermal Exchange" }, "270": { "name": "Anger Shell" }, "271": { "name": "Purifying Salt" }, "272": { "name": "Well-Baked Body" }, "273": { "name": "Wind Rider" }, "274": { "name": "Guard Dog" }, "275": { "name": "Rocky Payload" }, "276": { "name": "Wind Power" }, "277": { "name": "Zero to Hero" }, "278": { "name": "Commander" }, "279": { "name": "Electromorphosis" }, "280": { "name": "Protosynthesis" }, "281": { "name": "Quark Drive" }, "282": { "name": "Good as Gold" }, "283": { "name": "Vessel of Ruin" }, "284": { "name": "Sword of Ruin" }, "285": { "name": "Tablets of Ruin" }, "286": { "name": "Beads of Ruin" }, "287": { "name": "Orichalcum Pulse" }, "288": { "name": "Hadron Engine" }, "289": { "name": "Opportunist" }, "290": { "name": "Cud Chew" }, "291": { "name": "Sharpness" }, "292": { "name": "Supreme Overlord" }, "293": { "name": "Costar" }, "294": { "name": "Toxic Debris" }, "295": { "name": "Armor Tail" }, "296": { "name": "Mycelium Might" }, "297": { "name": "Earth Eater" }, "298": { "name": "Supersweet Syrup" }, "299": { "name": "Hospitality" }, "300": { "name": "Toxic Chain" }, "301": { "name": "Embody Aspect" }, "302": { "name": "Mind's Eye" }, "303": { "name": "Tera Shift" }, "304": { "name": "Tera Shell" }, "305": { "name": "Teraform Zero" }, "306": { "name": "Poison Puppeteer" } } var ultraMoves = { "1": { "learned": [ 60, 61, 62, 186, 115, 122, 175, 176, 468, 439, 486 ] }, "5": { "learned": [ 65612, 149, 215, 461, 236, 237, 271, 280, 281, 282, 475, 330, 385, 427, 428, 446, 447, 448, 453, 454, 459, 460, 494, 531, 532, 533, 534, 537, 538, 539, 552, 553, 554, 555, 559, 560, 571, 579, 613, 614, 619, 620, 621, 660, 674, 675, 695, 701, 706, 727, 66281, 759, 760, 765, 766, 776, 783, 784, 787, 799, 802 ] }, "6": { "learned": [ 78, 133, 470, 471, 135, 134, 136, 196, 197, 700, 150, 446, 677, 678, 66214, 725, 726, 727, 187, 188, 189 ] }, "7": { "learned": [ 65610, 65611, 65612, 65624, 65625, 727, 66281, 784, 735 ] }, "8": { "learned": [ 65563, 65564, 65624, 65625, 784, 788, 735, 739 ] }, "9": { "learned": [ 65624, 65625, 727, 66281, 784, 785, 735, 739, 740 ] }, "10": { "learned": [ 65586, 65587 ] }, "13": { "learned": [ 1, 2, 3, 158, 159, 160 ] }, "14": { "learned": [ 659, 78, 118, 119, 338, 365, 744 ] }, "16": { "learned": [ 146, 717 ] }, "17": { "learned": [ 227, 4, 5 ] }, "18": { "learned": [ 293, 294, 295 ] }, "19": { "learned": [ 738 ] }, "20": { "learned": [ 130, 760, 727, 759, 764, 793, 796 ] }, "21": { "learned": [ 532, 533, 534 ] }, "23": { "learned": [ 759, 760 ] }, "24": { "learned": [ 25, 26, 65562, 133, 470, 471, 134, 136, 196, 197, 700, 427, 428, 725, 726, 727 ] }, "25": { "learned": [ 149, 215, 461, 271, 280, 281, 282, 475, 330, 385, 427, 428, 446, 447, 448, 453, 454, 460, 486, 494, 531, 532, 533, 534, 537, 538, 539, 552, 553, 554, 555, 559, 560, 571, 613, 614, 619, 620, 622, 623, 660, 674, 675, 695, 799, 701, 706, 727, 66281, 763, 765, 766, 776, 783, 784, 802 ] }, "28": { "learned": [ 74, 75, 76, 111, 112, 464, 138, 139 ] }, "29": { "learned": [ 751, 752, 12, 15, 16, 17, 18, 65555, 65556, 21, 22, 65563, 65564, 65573, 65574, 41, 42, 169, 43, 44, 45, 182, 46, 47, 48, 49, 65588, 65589, 69, 70, 71, 72, 73, 65610, 65611, 65612, 81, 82, 84, 85, 88, 89, 65624, 65625, 90, 91, 92, 93, 98, 99, 102, 65639, 109, 110, 118, 119, 120, 121, 137, 233, 474, 140, 144, 145, 146, 585, 586 ] }, "33": { "learned": [ 37, 38, 65573, 65574, 111, 112, 464, 161, 162, 246, 247, 248, 273, 274, 275, 374, 375, 376, 626, 716 ] }, "34": { "learned": [ 799, 65563, 65564, 65573, 65574, 65586, 65587, 65588, 65589, 483, 484, 485, 486, 487, 488, 531, 553, 555, 621, 623, 626, 641, 642, 645, 660, 696, 697, 698, 699, 716, 717, 718, 760, 793, 794, 797 ] }, "35": { "learned": [ 224, 345, 346, 686, 687, 793 ] }, "36": { "learned": [ 65610, 65611, 65612 ] }, "37": { "learned": [ 108, 463, 214, 32, 33, 111, 112, 464 ] }, "40": { "learned": [ 592, 593 ] }, "42": { "learned": [ 28, 65564, 91, 139, 151, 416 ] }, "43": { "learned": [ 107, 144, 145, 641, 642, 645, 721, 209, 210 ] }, "44": { "learned": [ 143, 443, 444, 445, 446, 554, 555, 610, 611, 612, 543, 544, 545 ] }, "45": { "learned": [ 115, 187, 188, 189 ] }, "48": { "learned": [ 258, 259, 260, 298 ] }, "50": { "learned": [ 174, 302, 442, 686, 687, 708, 709 ] }, "51": { "learned": [ 108, 463, 535, 536 ] }, "52": { "learned": [ 554, 555, 661 ] }, "54": { "learned": [ 65563, 65564, 249 ] }, "55": { "learned": [ 222, 226, 245, 341, 342, 422, 423, 458, 592, 593, 704, 705, 706, 721, 751, 752, 231, 232 ] }, "56": { "learned": [ 79, 80, 199, 99, 108, 463, 112, 464, 115, 143, 147, 148, 149, 151, 194, 195, 222, 248, 295, 306, 341, 342, 365, 384, 422, 423, 446, 550, 635, 706, 713, 747, 748, 752, 780, 781 ] }, "57": { "learned": [ 706 ] }, "58": { "learned": [ 65563, 65564 ] }, "60": { "learned": [ 102, 103, 65639, 120, 121, 150, 280, 281, 282, 475, 480, 481, 482, 488, 577, 578, 579 ] }, "62": { "learned": [ 131 ] }, "63": { "learned": [ 724, 730, 743, 750, 756, 763, 765, 768, 772, 777, 802 ] }, "64": { "learned": [ 15, 86, 87 ] }, "66": { "learned": [ 701, 155, 156, 157 ] }, "67": { "learned": [ 60, 61, 62, 186, 240, 65639, 647, 727, 749, 750, 760, 763, 782, 783, 784, 802, 522, 523 ] }, "68": { "learned": [ 165, 166, 766 ] }, "69": { "learned": [ 65563, 65564, 65610, 65611, 65612, 440, 674, 675 ] }, "70": { "learned": [ 749, 750, 759, 760 ] }, "71": { "learned": [ 102, 103, 65639, 187, 188, 189, 273, 274, 275 ] }, "72": { "learned": [ 102, 65639, 273, 274, 275, 787, 65624, 65625 ] }, "73": { "learned": [ 346, 470 ] }, "75": { "learned": [ 83 ] }, "76": { "learned": [ 65574, 587, 695 ] }, "79": { "learned": [ 46, 47 ] }, "81": { "learned": [ 684, 685 ] }, "83": { "learned": [ 65641, 721, 147, 148, 149, 151, 250, 330, 334, 435, 494, 554, 555, 635, 662, 663, 725, 726, 727, 791 ] }, "84": { "learned": [ 65610, 65611, 65612, 100, 101, 137, 233, 474, 403, 404, 405 ] }, "86": { "learned": [ 65610, 65611, 65612, 757, 758 ] }, "87": { "learned": [ 685 ] }, "88": { "learned": [ 111, 112, 464, 138, 139, 140, 141, 142, 246, 247, 248, 696, 697 ] }, "90": { "learned": [ 446, 749, 750 ] }, "91": { "learned": [ 65555, 65556, 65563, 65564, 65573, 65574, 65588, 65589, 65610, 65611, 65612, 65624, 65625, 346, 622, 623 ] }, "93": { "learned": [ 151, 380, 381, 561, 577, 578, 579, 7, 8, 9, 63 ] }, "95": { "learned": [ 338, 574, 575, 576 ] }, "97": { "learned": [ 27, 28, 50, 51, 65586, 65587, 150, 37, 38, 41, 42, 169, 121, 151, 225, 243, 244, 245, 309, 310, 380, 381, 405, 519, 520, 521, 589, 605, 606, 619, 620, 627, 628, 745, 777, 791, 792, 801, 802 ] }, "98": { "learned": [ 118, 119, 198, 430, 509, 510 ] }, "100": { "learned": [ 65562 ] }, "101": { "learned": [ 64, 65, 442 ] }, "103": { "learned": [ 65587, 124, 167, 168, 258, 259, 260, 636, 637, 39, 40, 65586, 151, 163, 164, 174, 186, 275, 416, 453, 454, 509, 510, 535, 536, 537, 556, 569, 605, 606, 624, 625, 679, 680, 681, 688, 689, 736, 737, 738, 767, 768, 771, 778, 798 ] }, "107": { "learned": [ 40, 440 ] }, "108": { "learned": [ 244 ] }, "109": { "learned": [ 150, 456, 457 ] }, "111": { "learned": [ 440, 767, 768, 759, 760 ] }, "112": { "learned": [ 137, 233, 474 ] }, "113": { "learned": [ 91 ] }, "114": { "learned": [ 65625 ] }, "115": { "learned": [ 65573, 65574, 172, 478 ] }, "116": { "learned": [ 83, 107, 128, 236, 359, 151, 448, 519, 520, 521, 619, 620, 675, 721, 216, 217 ] }, "117": { "learned": [ 65610, 65611, 65612 ] }, "118": { "learned": [ 480, 481, 482, 683, 685, 719 ] }, "120": { "learned": [ 425, 426, 39, 40, 65624, 65625, 462, 463, 482, 485, 526, 582, 583, 584, 615, 622, 623, 645, 721, 773, 797, 801 ] }, "129": { "learned": [ 65573, 65574, 65588, 63, 64, 65, 104, 105, 65641, 494, 509, 510, 519, 520, 521, 527, 528, 546, 547, 561, 566, 567, 570, 571, 587, 595, 596, 610, 611, 612, 627, 628, 629, 630, 638, 639, 640, 643, 644, 646, 66182, 131718, 647, 649, 661, 662, 663, 677, 678, 66214, 694, 695, 701, 714, 715, 716, 717, 718, 722, 723, 724, 742, 743, 745, 757, 758, 767, 768, 772, 773, 777, 780, 785, 791, 792, 802 ] }, "133": { "learned": [ 202, 258, 259, 260, 360, 27, 28, 163, 164, 206, 272, 385, 449, 450, 468, 517, 518, 684, 685, 719, 780, 799 ] }, "138": { "learned": [ 49 ] }, "139": { "learned": [ 92, 93, 94 ] }, "141": { "learned": [ 140, 141, 27, 28, 452, 649 ] }, "143": { "learned": [ 722, 723, 724, 731, 732, 733, 741, 785, 792 ] }, "146": { "learned": [ 107 ] }, "151": { "learned": [ 72, 73 ] }, "154": { "learned": [ 4, 5, 6, 46, 47, 65586, 65587, 619, 620 ] }, "156": { "learned": [ 801, 486, 785, 786, 787, 788 ] }, "157": { "learned": [ 7, 8, 793 ] }, "161": { "learned": [ 380, 381, 480, 481, 482, 606, 801 ] }, "162": { "learned": [ 65563, 65564, 777 ] }, "163": { "learned": [ 65586, 65587, 98, 99, 252, 253, 254, 341, 342 ] }, "168": { "learned": [ 25, 197, 559 ] }, "172": { "learned": [ 126, 467, 240, 244 ] }, "174": { "learned": [ 65563, 65564, 104, 105, 65641, 710, 711, 769, 770, 776 ] }, "175": { "learned": [ 143, 446, 290, 291, 292, 632 ] }, "179": { "learned": [ 62, 66, 67, 68, 127, 128, 151, 211, 213, 244, 416, 428, 475, 550, 555, 617, 638, 639, 640, 647, 675, 679, 680, 681, 701, 718, 727, 760, 773, 802 ] }, "180": { "learned": [ 724, 771, 65641, 747, 748, 765, 769, 770, 778, 781, 792, 793 ] }, "182": { "learned": [ 486 ] }, "184": { "learned": [ 126, 467, 240, 244, 66182, 131718, 721, 59, 95, 208, 151, 211, 220, 221, 306, 310, 362, 448, 538, 539, 614, 638, 639, 641, 642, 645, 675, 687, 696, 697, 750, 791, 792, 800, 66336, 197408, 131872 ] }, "187": { "learned": [ 298, 335 ] }, "188": { "learned": [ 570, 769 ] }, "189": { "learned": [ 535, 536, 537, 757, 758 ] }, "191": { "learned": [ 28, 65564, 151, 214 ] }, "192": { "learned": [ 801, 25, 26, 65562, 35, 36, 39, 40, 52, 53, 63, 64, 65, 79, 80, 199, 88, 89, 92, 93, 94, 96, 97, 100, 101, 109, 110, 111, 112, 464, 113, 242, 115, 120, 121, 122, 125, 466, 128, 130, 131, 135, 136, 143, 147, 148, 149, 150, 170, 171, 172, 173, 174, 175, 176, 468, 179, 180, 190, 424, 196, 197, 200, 429, 203, 206, 209, 210, 216, 217, 239, 241, 249, 250 ] }, "193": { "learned": [ 183, 184 ] }, "194": { "learned": [ 592, 593, 605, 606 ] }, "195": { "learned": [ 298, 358 ] }, "196": { "learned": [ 728, 729, 730, 65563, 65564, 65588, 65589, 747, 748, 751, 752, 768, 772, 773, 780, 788, 792, 795, 65555, 65556, 740, 741 ] }, "197": { "learned": [ 255, 256, 257, 16, 17, 18, 21, 22, 25, 26, 65562, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 39, 40, 41, 42, 169, 52, 53, 56, 57, 61, 62, 186, 66, 67, 68, 83, 106, 123, 212, 125, 466, 126, 467, 142, 144, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 172, 173, 174, 175, 176, 468, 177, 178, 190, 424, 198, 430, 207, 472, 209, 210, 214, 215, 461, 220, 221, 473, 225, 227, 228, 229, 234, 236, 239, 240, 243, 244, 245, 246, 247, 248, 249, 250, 251 ] }, "200": { "learned": [ 15, 1, 2, 7, 8, 65639, 65641, 127, 773, 776, 791, 794, 795, 799, 800, 66336, 197408, 131872, 802, 66281 ] }, "201": { "learned": [ 6, 745, 66281 ] }, "202": { "learned": [ 722, 723, 724, 751, 752, 761, 762, 763, 764, 778, 798, 65624, 65625 ] }, "203": { "learned": [ 66281 ] }, "204": { "learned": [ 241, 124, 131, 151, 238, 385, 478, 66214, 684, 685, 703, 719, 742, 743, 759, 760, 764, 786 ] }, "205": { "learned": [ 65563, 65564, 759, 760, 767, 768 ] }, "210": { "learned": [ 543, 544, 545, 473 ] }, "211": { "learned": [ 225 ] }, "212": { "learned": [ 163, 164, 701 ] }, "213": { "learned": [ 597, 598, 636, 637, 765 ] }, "215": { "learned": [ 65573, 65574, 764, 801 ] }, "217": { "learned": [ 298 ] }, "219": { "learned": [ 26 ] }, "220": { "learned": [ 65573, 65574, 65624, 65625, 65641, 747, 748, 764, 765, 768, 769, 770, 779, 781, 793 ] }, "223": { "learned": [ 122 ] }, "224": { "learned": [ 86, 87, 128, 131, 151, 638, 639, 640, 647, 797 ] }, "225": { "learned": [ 4, 5, 6, 333, 58, 59, 130, 131, 151, 248, 249, 250 ] }, "226": { "learned": [ 12, 35, 36, 39, 40, 182, 206, 225, 380, 381, 385, 453, 454, 480, 481, 482, 494, 509, 510, 527, 528, 686, 687, 719, 801 ] }, "227": { "learned": [ 37, 38, 93, 94, 124, 143, 151, 271, 272, 302, 385, 446, 453, 454, 480, 481, 482, 494, 548, 549, 571, 577, 578, 579, 588, 589, 682, 683, 719, 764, 765, 801 ] }, "228": { "learned": [ 111, 112, 464 ] }, "229": { "learned": [ 776 ] }, "230": { "learned": [ 12, 15, 48, 49, 114, 465, 124, 151, 165, 166, 187, 188, 189, 204, 205, 238, 241, 251, 25, 26, 65562 ] }, "231": { "learned": [ 65555, 65556, 65563, 65564, 65573, 65574, 65588, 65589, 65639, 155, 156, 157, 728, 729, 730, 744, 745, 66281, 746, 757, 758, 766, 776, 777, 782, 783, 784, 791, 734, 735, 779 ] }, "232": { "learned": [ 447, 485, 569 ] }, "235": { "learned": [ 755, 756, 781, 787 ] }, "238": { "learned": [ 701 ] }, "239": { "learned": [ 690 ] }, "240": { "learned": [ 338, 556, 643, 756 ] }, "241": { "learned": [ 647, 695 ] }, "242": { "learned": [ 42, 169, 139, 244, 245, 610, 611, 612, 712, 9, 41, 151, 197, 306, 23 ] }, "243": { "learned": [ 65563, 65564, 144, 771 ] }, "246": { "learned": [ 150, 324 ] }, "247": { "learned": [ 38 ] }, "248": { "learned": [ 65562, 103, 124, 151, 164, 344, 380, 381, 494, 555, 606, 686, 687, 786, 791, 792, 800, 66336, 197408, 131872 ] }, "249": { "learned": [ 749, 750, 787 ] }, "250": { "learned": [ 592, 593, 689, 728, 729, 730, 746 ] }, "251": { "learned": [ 559, 560, 632, 65589, 115, 151, 416, 584, 624, 625, 634, 635, 675, 688, 689, 757, 758, 778 ] }, "252": { "learned": [ 574, 575, 576 ] }, "253": { "learned": [ 746, 65586, 65587, 65588, 65589, 186, 340, 416, 688, 689, 728, 729, 730, 766, 776, 780, 782, 783, 784, 734, 735, 66281, 779 ] }, "257": { "learned": [ 110, 65641, 275, 757, 758, 773, 776, 780, 792, 799, 800, 66336, 197408, 131872, 731, 732, 733 ] }, "258": { "learned": [ 145, 337 ] }, "260": { "learned": [ 427, 428, 481 ] }, "261": { "learned": [ 661 ] }, "262": { "learned": [ 37, 38, 225 ] }, "263": { "learned": [ 755, 756 ] }, "264": { "learned": [ 782, 783, 784, 65563, 65564, 65610, 65611, 65612, 65625, 727, 739, 740, 66281, 759, 760, 766, 787, 802 ] }, "268": { "learned": [ 125, 466, 239, 243, 618 ] }, "269": { "learned": [ 674, 125, 126, 589, 763 ] }, "270": { "learned": [ 223, 224, 1, 2, 3, 4, 5, 6, 7, 8, 9, 65624, 65625, 131, 182, 206, 225, 243, 244, 245, 249, 250, 303, 350, 479, 262623, 131551, 197087, 328159, 66015, 622, 623, 632, 634, 635, 643, 644, 646, 66182, 131718, 66214, 728, 729, 730, 742, 743, 757, 758, 761, 762, 763, 777, 780, 781, 791, 792, 418, 419 ] }, "271": { "learned": [ 555, 623, 742, 743, 764, 765, 769, 770, 778, 788, 792, 801 ] }, "272": { "learned": [ 65573, 65574, 741, 759, 760, 764, 769, 770, 777, 781, 793 ] }, "276": { "learned": [ 62, 59, 65610, 65611, 65612, 65639, 727, 754, 766, 780, 784, 791, 797, 802, 775 ] }, "277": { "learned": [ 65562, 730, 747, 748, 751, 752, 755, 756, 764, 765, 772, 773, 779, 786, 788, 793, 796 ] }, "278": { "learned": [ 65562, 65625, 755, 756, 769, 770, 771, 800, 66336, 197408, 131872 ] }, "279": { "learned": [ 128, 151, 248, 256, 257, 275, 342, 355, 356, 477, 416, 447, 448, 475, 532, 533, 534, 539, 552, 553, 559, 560, 563, 570, 571, 589, 597, 598, 619, 620, 638, 639, 640, 647, 675, 701, 745, 750, 760, 766, 784, 787, 794, 802 ] }, "280": { "learned": [ 638, 639, 640, 647 ] }, "281": { "learned": [ 765 ] }, "282": { "learned": [ 799, 280, 281, 282, 722, 723, 724, 766, 65556, 65563, 65564, 65588, 65589, 65639, 727, 731, 732, 733, 747, 748, 754, 762, 763, 765, 768, 775, 781, 788, 791, 793, 798, 800, 66336, 197408, 131872, 802 ] }, "283": { "learned": [ 757, 758, 744, 745, 66281, 761, 762, 763, 725, 726, 727, 734, 735, 749, 750, 766, 768, 774, 775, 776, 777, 780, 784, 791, 794 ] }, "285": { "learned": [ 677, 678, 66214, 769, 770 ] }, "286": { "learned": [ 151, 35, 36, 65, 79, 80, 199, 94, 65641, 163, 164, 176, 468, 177, 178, 249, 250, 251, 350, 385, 426, 478, 561, 592, 593, 622, 623, 710, 711, 724, 742, 743, 765, 800, 66336, 197408, 131872, 801 ] }, "287": { "learned": [ 25, 26, 65562 ] }, "289": { "learned": [ 727, 747, 748, 765, 766, 778, 779, 795, 802 ] }, "291": { "learned": [ 537, 689, 728, 729, 730, 752, 768 ] }, "299": { "learned": [ 256, 6, 151, 494, 620, 727, 802 ] }, "300": { "learned": [ 399, 400 ] }, "304": { "learned": [ 65588, 65589, 59, 310, 479, 262623, 131551, 197087, 328159, 66015, 582, 583, 584, 773, 776, 784, 785, 791, 800, 66336, 197408, 131872, 744, 745, 66281 ] }, "305": { "learned": [ 757, 758 ] }, "307": { "learned": [ 493 ] }, "308": { "learned": [ 493 ] }, "309": { "learned": [ 448, 447 ] }, "310": { "learned": [ 273, 274, 275, 328, 329, 330, 361, 362 ] }, "311": { "learned": [ 437, 4, 5, 6, 7, 8, 9, 37, 38, 65573, 65574, 79, 80, 199, 91, 131, 133, 470, 471, 135, 134, 136, 196, 197, 700, 144, 145, 146, 151, 186, 225, 244, 245, 251, 272, 279, 315, 320, 321, 324, 337, 338, 340, 350, 406, 423, 449, 450, 459, 460, 466, 467, 526, 535, 536, 537, 556, 641, 642, 643, 644, 645, 646, 66182, 131718, 695, 698, 699, 706, 730, 756 ] }, "312": { "learned": [ 416, 761, 314 ] }, "313": { "learned": [ 38, 65574, 65589, 151, 215, 461, 559, 560, 677, 678, 66214, 682, 683, 686, 687, 719, 742, 743, 758 ] }, "314": { "learned": [ 274 ] }, "321": { "learned": [ 677, 678, 66214, 780 ] }, "322": { "learned": [ 151, 177, 178, 375, 376, 384, 576, 800, 66336, 197408, 131872 ] }, "324": { "learned": [ 738, 742, 743, 751, 752, 753, 754, 755, 756, 772, 773, 779, 780, 792, 795, 800, 66336, 197408, 131872, 801 ] }, "326": { "learned": [ 554, 555 ] }, "328": { "learned": [ 645, 31, 34, 151, 212, 220, 221, 473, 227, 246, 247, 248, 259, 260, 305, 306, 337, 338, 340, 343, 344, 377, 379, 423, 452, 524, 525, 526, 529, 530, 660, 703, 719, 749, 750 ] }, "329": { "learned": [ 646, 66182, 131718 ] }, "330": { "learned": [ 55, 60, 61, 62, 186, 80, 118, 119, 130, 151, 199, 246, 247, 248, 271, 272, 341, 342, 349, 350, 449, 450, 564, 565, 593, 647, 689, 747, 748, 768, 781 ] }, "331": { "learned": [ 753, 754, 764, 787, 504, 505 ] }, "332": { "learned": [ 175 ] }, "333": { "learned": [ 124, 144, 151, 225, 362, 364, 365, 378, 460, 461, 471, 478, 614, 615, 646, 66182, 131718, 699, 712, 713 ] }, "334": { "learned": [ 65586, 65587, 112, 464, 185, 208, 213, 344, 369, 452, 555, 744, 745, 66281, 747, 748, 751, 752, 772, 773, 781, 785, 786, 787, 788, 791, 798, 799, 65610, 65611, 65612 ] }, "335": { "learned": [ 679, 680, 65625, 735, 740, 748, 765, 766, 769, 770, 771, 776, 780, 781, 795, 797 ] }, "336": { "learned": [ 243, 447, 448 ] }, "339": { "learned": [ 260 ] }, "340": { "learned": [ 761, 762, 763, 777, 794, 802 ] }, "341": { "learned": [ 768, 31, 34, 79, 80, 199, 111, 112, 464, 151, 183, 184, 213, 263, 264, 271, 272, 298, 341, 342, 345, 346, 347, 348, 349, 350, 422, 423, 449, 450, 529, 530, 564, 565, 616, 617, 688, 689, 698, 699, 704, 705, 706, 721, 746, 747, 748, 750, 767, 769, 770 ] }, "343": { "learned": [ 743, 213, 65555, 65556, 65563, 65564, 65573, 65574, 311, 312, 722, 723, 724, 725, 726, 727, 728, 729, 730, 741, 742, 744, 745, 66281, 746, 747, 748, 757, 758, 761, 762, 763, 764, 765, 777, 778 ] }, "345": { "learned": [ 103, 65639, 151, 700, 708, 709, 743, 753, 754 ] }, "347": { "learned": [ 25, 26, 103, 163, 164, 730 ] }, "348": { "learned": [ 151, 251, 549 ] }, "349": { "learned": [ 95, 208, 142, 151, 254, 350, 380, 635, 643, 644, 646, 66182, 131718, 715, 758, 780, 800, 66336, 197408, 131872 ] }, "350": { "learned": [ 31, 34, 68, 140, 141, 142, 151, 185, 226, 248, 305, 306, 337, 338, 345, 346, 369, 377, 473, 530, 534, 564, 565, 566, 567, 639, 688, 689, 696, 697, 698, 699, 745, 66281 ] }, "351": { "learned": [ 65555, 65556, 65588, 65589, 65611, 65612, 65624, 65625, 734, 735, 736, 737, 738, 766, 776, 777, 780, 784, 791, 792, 795, 797, 799, 800, 66336, 197408, 131872, 801 ] }, "352": { "learned": [ 65588, 65589, 728, 729, 730, 747, 748, 751, 752, 768, 780, 784 ] }, "356": { "learned": [ 765, 769, 770, 774, 777, 786, 788, 796, 797 ] }, "358": { "learned": [ 96, 97, 114, 465 ] }, "359": { "learned": [ 99, 143, 486, 579, 98 ] }, "360": { "learned": [ 302 ] }, "362": { "learned": [ 728, 729, 730, 747, 748, 769, 770, 781 ] }, "364": { "learned": [ 662, 663, 522, 523, 701 ] }, "366": { "learned": [ 724, 742, 743, 764, 773, 780, 792, 798 ] }, "367": { "learned": [ 211, 118, 119, 686, 687 ] }, "368": { "learned": [ 589, 632 ] }, "369": { "learned": [ 257, 428 ] }, "370": { "learned": [ 783, 784, 62, 83, 128, 151, 257, 319, 342, 359, 428, 533, 534, 553, 560, 589, 612, 619, 620, 623, 626, 627, 628, 675, 679, 680, 681, 696, 697, 701, 727, 745, 66281, 760, 768, 787, 791, 794, 795 ] }, "371": { "learned": [ 273, 442, 427, 428, 506, 717, 718, 66281 ] }, "372": { "learned": [ 128, 252, 253, 254, 41, 42, 169, 50, 51, 65586, 65587, 68, 115, 123, 212, 151, 211, 214, 223, 224, 225, 226, 255, 256, 257, 274, 275, 303, 416, 427, 428, 434, 435, 451, 452, 453, 454, 482, 545, 550, 556, 559, 560, 566, 567, 570, 571, 597, 598, 599, 600, 601, 620, 627, 628, 629, 630, 641, 642, 649, 688, 689, 696, 697, 701, 706, 727, 745, 766, 767, 768, 777, 781, 785, 795 ] }, "374": { "learned": [ 782 ] }, "380": { "learned": [ 143, 65624, 65625, 747, 748 ] }, "381": { "learned": [ 385 ] }, "383": { "learned": [ 133, 470, 471, 135, 134, 136, 196, 197, 700 ] }, "384": { "learned": [ 63, 64, 65, 137, 233, 474, 151, 282, 337, 338, 343, 344, 436, 437, 488, 494, 517, 518, 555, 561, 563, 577, 578, 579, 786, 787 ] }, "385": { "learned": [ 137, 233, 474, 151, 282, 343, 344, 436, 437, 488, 494, 517, 518, 555, 563, 574, 575, 576, 577, 578, 579, 686, 687, 703, 719, 786, 787, 788, 801 ] }, "386": { "learned": [ 763 ] }, "387": { "learned": [ 115, 744, 745, 66281, 765, 65555, 65556, 65588, 65589, 742, 743, 772, 773, 774, 775, 777, 778, 791, 798, 799, 801, 802, 25, 26, 65562 ] }, "388": { "learned": [ 753, 754, 764, 722, 723, 724, 755, 756, 761, 762, 763, 787, 793 ] }, "389": { "learned": [ 122, 273, 274, 439, 618, 708, 709, 355 ] }, "390": { "learned": [ 151, 416, 616, 617 ] }, "392": { "learned": [ 484 ] }, "393": { "learned": [ 796, 736, 737, 738, 774, 797, 799, 800, 66336, 197408, 131872, 801 ] }, "394": { "learned": [ 255, 146, 151, 250, 643, 758, 513, 514 ] }, "396": { "learned": [ 692, 106, 107, 251, 257, 380, 381, 385, 428, 475, 647, 784, 802 ] }, "398": { "learned": [ 92 ] }, "400": { "learned": [ 675, 754, 98, 99, 252, 253, 273, 274, 275, 588, 589 ] }, "401": { "learned": [ 65563, 65564, 65573, 65574, 728, 729, 730, 782, 783, 784 ] }, "402": { "learned": [ 65588, 65589, 722, 723, 724, 753, 754, 755, 756, 761, 762, 763, 764, 766, 787, 733 ] }, "403": { "learned": [ 144, 149, 151, 177, 249, 250, 275, 291, 329, 330, 359, 373, 380, 381, 475, 567, 625, 638, 639, 640, 647, 681, 724 ] }, "405": { "learned": [ 151, 451, 452, 751, 752, 767, 768 ] }, "406": { "learned": [ 65639, 782, 783, 784, 799, 800, 66336, 197408, 131872 ] }, "407": { "learned": [ 329, 694, 695, 714, 715 ] }, "408": { "learned": [ 151, 599, 600, 601 ] }, "409": { "learned": [ 62, 302, 480, 481, 482, 617, 631, 727, 756, 760, 766, 778, 783, 784, 794, 799, 739, 740 ] }, "411": { "learned": [ 237 ] }, "412": { "learned": [ 475, 587 ] }, "413": { "learned": [ 144, 145, 146, 151, 225, 279, 521, 701 ] }, "414": { "learned": [ 29, 30, 32, 33, 60, 61, 62, 186, 3, 460, 744, 745, 66281, 749, 750, 800, 66336, 197408, 131872, 65610, 65611, 65612 ] }, "415": { "learned": [ 742, 743 ] }, "416": { "learned": [ 147, 148, 531, 561, 743, 745, 66281, 778 ] }, "417": { "learned": [ 633, 634, 635, 65, 80, 94, 150, 163, 164, 337, 342, 344, 434, 435, 479, 262623, 131551, 197087, 328159, 66015, 480, 481, 527, 528, 574, 575, 576, 641, 677, 678, 66214, 682, 683, 686, 687, 711 ] }, "419": { "learned": [ 65563, 65564, 65574, 615 ] }, "420": { "learned": [ 65563, 65564 ] }, "421": { "learned": [ 563, 348 ] }, "422": { "learned": [ 151, 721, 734, 735 ] }, "423": { "learned": [ 151, 550, 613, 614, 734, 735 ] }, "424": { "learned": [ 151, 734, 735 ] }, "425": { "learned": [ 722, 723, 724 ] }, "427": { "learned": [ 103, 121, 123, 212, 124, 141, 151, 215, 461, 251, 375, 376, 380, 381, 480, 481, 482, 510, 527, 528, 561, 612, 679, 680, 681, 724, 754, 786, 792 ] }, "428": { "learned": [ 65573, 65574, 65639, 627, 744, 745, 66281, 759, 760, 761, 762, 763, 773, 777, 793, 797, 801, 802, 65555, 65556, 735, 739, 740, 774, 775 ] }, "430": { "learned": [ 377, 606 ] }, "432": { "learned": [ 333, 334, 519, 520, 521, 532, 533, 534, 587, 661, 662, 663, 701, 714, 715, 166, 255, 256, 257, 313, 314, 351, 358, 384, 455, 479, 262623, 131551, 197087, 328159, 66015, 495, 496, 497, 527, 528, 546, 547, 561, 615, 635, 637, 641, 642, 643, 644, 645, 666, 671, 707, 716, 717, 721, 731, 732, 733, 741, 742, 743, 764, 773, 780, 785, 792 ] }, "435": { "learned": [ 695, 736 ] }, "437": { "learned": [ 724, 151, 272, 460, 470, 556, 640, 709 ] }, "438": { "learned": [ 103, 65639, 763, 130, 151, 315, 407, 346, 536, 537, 711 ] }, "440": { "learned": [ 68, 72, 73, 123, 212, 141, 151, 254, 416, 454, 545, 569, 748, 754, 758, 793 ] }, "441": { "learned": [ 65588, 65589, 747, 748, 757, 758, 766, 793, 731, 732, 733 ] }, "442": { "learned": [ 65639, 555, 628, 727, 744, 745, 66281, 749, 750, 759, 760, 768, 776, 777, 781, 782, 783, 784, 785, 793, 794, 799, 800, 66336, 197408, 131872, 802, 65612, 735, 739, 740, 774, 775 ] }, "444": { "learned": [ 140 ] }, "446": { "learned": [ 65563, 65564, 65586, 65587, 749, 750, 769, 770, 784 ] }, "450": { "learned": [ 795, 742, 743, 753, 754, 794 ] }, "453": { "learned": [ 298, 580, 581 ] }, "457": { "learned": [ 95, 208 ] }, "468": { "learned": [ 619, 620, 692, 693, 65563, 65564, 65586, 65587 ] }, "469": { "learned": [ 713, 95, 208 ] }, "471": { "learned": [ 96, 97 ] }, "472": { "learned": [ 65574, 124, 742, 743, 751, 752, 755, 756, 786, 788, 792, 238, 779 ] }, "475": { "learned": [ 568, 569 ] }, "477": { "learned": [ 65562, 39, 40, 101, 113, 242, 137, 233, 474, 205, 251, 413, 65949, 131485, 476, 599, 600, 601, 649, 654, 655, 677, 678, 66214, 682, 683, 686, 687, 700, 703, 708, 709, 710, 711, 719, 720, 742, 743, 764, 765, 774, 778, 779, 781, 785, 786, 787, 788, 792, 793, 800, 66336, 197408, 131872 ] }, "478": { "learned": [ 65562, 742, 743, 751, 752, 755, 756, 765, 778, 786, 788, 792, 779 ] }, "479": { "learned": [ 749, 750 ] }, "482": { "learned": [ 45 ] }, "484": { "learned": [ 379, 622, 112, 464, 151, 241, 248, 365, 377, 378, 382, 383, 450, 462, 473, 483, 484, 485, 558, 598, 614, 713, 721, 776, 791, 322, 323, 387, 388, 389 ] }, "486": { "learned": [ 135, 151, 403, 404, 405, 642, 644, 694, 695, 702, 737, 738, 777, 796, 801 ] }, "487": { "learned": [ 751 ] }, "490": { "learned": [ 427, 66281 ] }, "491": { "learned": [ 588, 589, 705, 706, 455, 559, 560 ] }, "492": { "learned": [ 65573, 65574, 65625, 757, 758, 793, 795, 802, 66281 ] }, "493": { "learned": [ 381, 527, 528 ] }, "495": { "learned": [ 742, 743, 747, 748, 755, 756, 769, 770, 777, 778, 779, 801 ] }, "496": { "learned": [ 679, 680 ] }, "500": { "learned": [ 65562, 38, 65574, 65, 79, 80, 199, 113, 242, 122, 150, 151, 163, 164, 206, 337, 338, 344, 385, 439, 440, 479, 262623, 131551, 197087, 328159, 66015, 480, 481, 482, 488, 574, 575, 576, 577, 578, 579, 605, 606, 686, 687, 707, 719, 730, 764, 785, 786, 787, 788, 801 ] }, "501": { "learned": [ 447 ] }, "502": { "learned": [ 381, 577, 578, 579, 65562, 35, 36, 39, 40, 50, 51, 65586, 65587, 92, 93, 94, 65641, 113, 242, 121, 122, 124, 137, 233, 474, 150, 196, 251, 292, 302, 337, 338, 355, 356, 477, 375, 376, 380, 385, 425, 426, 436, 437, 439, 442, 462, 468, 478, 479, 262623, 131551, 197087, 328159, 66015, 480, 481, 482, 488, 517, 518, 527, 528, 531, 574, 575, 576, 582, 583, 584, 600, 601, 607, 608, 609, 622, 623, 649, 677, 678, 66214, 682, 683, 686, 687, 694, 695, 702, 703, 708, 709, 710, 711, 719, 742, 743, 764, 765, 781, 786, 793, 800, 66336, 197408, 131872, 107, 106, 237, 65611, 65612, 96, 97, 199, 200, 429, 203, 205, 325, 326, 353, 354, 358, 413, 65949, 131485, 433, 476, 523, 648, 654, 655, 669, 670, 671, 328350, 779 ] }, "503": { "learned": [ 243 ] }, "504": { "learned": [ 7, 8, 9 ] }, "506": { "learned": [ 442, 708, 709, 31, 34, 151, 169, 211, 292, 302, 343, 344, 416, 434, 435, 436, 437, 543, 544, 545, 570, 571, 591, 710, 711, 724, 748, 778, 781, 792, 793, 802 ] }, "510": { "learned": [ 757, 758 ] }, "512": { "learned": [ 123, 291, 428 ] }, "514": { "learned": [ 65588, 65589, 718, 766 ] }, "515": { "learned": [ 83 ] }, "518": { "learned": [ 728, 729 ] }, "519": { "learned": [ 725, 726 ] }, "520": { "learned": [ 722, 723, 773 ] }, "522": { "learned": [ 415 ] }, "523": { "learned": [ 60, 103 ] }, "525": { "learned": [ 329, 4, 5, 690 ] }, "526": { "learned": [ 27, 28, 50, 51 ] }, "527": { "learned": [ 766, 777, 785, 795, 796, 801, 65610, 65611, 65612 ] }, "529": { "learned": [ 28, 65564, 95, 208, 225, 745, 768, 795 ] }, "530": { "learned": [ 754, 62, 726, 727, 735, 739, 740, 66281, 753, 759, 760, 768, 782, 783, 784, 787, 794, 799 ] }, "533": { "learned": [ 501, 502, 503 ] }, "534": { "learned": [ 98, 99, 80, 141, 151, 199, 589, 565 ] }, "535": { "learned": [ 6, 112, 464, 143, 151, 257, 324, 383, 485, 486, 623, 643, 721, 727, 776, 791, 799 ] }, "536": { "learned": [ 764 ] }, "538": { "learned": [ 626 ] }, "541": { "learned": [ 151, 263, 264, 434, 435, 678, 66214, 745, 190, 424 ] }, "542": { "learned": [ 384, 6, 12, 142, 145, 151, 163, 164, 169, 226, 230, 249, 334, 373, 521, 627, 628, 663, 724 ] }, "549": { "learned": [ 66182, 131718 ] }, "555": { "learned": [ 612, 635 ] }, "556": { "learned": [ 584, 361, 362, 478, 582, 583 ] }, "560": { "learned": [ 161, 162, 417 ] }, "561": { "learned": [ 784 ] }, "562": { "learned": [ 562, 563, 358 ] }, "563": { "learned": [ 65573, 439, 702 ] }, "564": { "learned": [ 12, 25, 26, 65562, 35, 36, 65573, 65574, 39, 40, 151, 173, 174, 175, 176, 468, 183, 184, 238, 298, 303, 420, 421, 480, 481, 482, 716, 719, 728, 729, 730, 755, 756, 761, 762, 763, 778, 788, 801 ] }, "565": { "learned": [ 785 ] }, "568": { "learned": [ 471, 646, 66182, 131718, 698 ] }, "571": { "learned": [ 632 ] }, "572": { "learned": [ 89, 65625, 517, 518, 711 ] }, "573": { "learned": [ 6, 35, 36, 37, 38, 77, 78, 122, 136, 146, 151, 175, 176, 468, 250, 282, 380, 381, 467, 494, 555, 607, 608, 609, 637, 643, 700, 710, 711, 719, 791 ] }, "574": { "learned": [ 643, 644, 646, 66182, 131718 ] }, "575": { "learned": [ 172 ] }, "578": { "learned": [ 622, 724, 94, 151, 442, 487, 563, 778, 802 ] }, "579": { "learned": [ 39, 52, 65588, 58, 59, 241, 25, 26, 65562, 35, 36, 77, 78, 151, 172, 173, 174, 175, 176, 468, 182, 333, 334, 403, 404, 405, 421, 427, 428, 480, 481, 482, 547, 572, 573, 677, 678, 66214, 696, 697, 700, 716, 719, 728, 729, 730, 742, 743, 744, 745, 66281, 786, 788 ] }, "580": { "learned": [ 172 ] }, "583": { "learned": [ 416, 742, 743 ] }, "584": { "learned": [ 513, 514, 633, 634, 635 ] }, "586": { "learned": [ 113, 242, 173, 174, 440, 788, 479, 262623, 131551, 197087, 328159, 66015 ] }, "587": { "learned": [ 26, 137, 233, 474, 145, 151, 243, 343, 344, 479, 262623, 131551, 197087, 328159, 66015, 587, 642, 737, 738, 777, 785, 801 ] }, "588": { "learned": [ 601, 618, 26, 151, 243, 479, 262623, 131551, 197087, 328159, 66015, 702 ] }, "593": { "learned": [ 151, 251, 346, 420, 421, 546, 547, 549, 591, 754, 46, 47, 540, 541, 542, 672, 673 ] }, "599": { "learned": [ 65574, 151, 183, 184, 439, 546, 547, 707, 721 ] }, "600": { "learned": [ 725, 726, 727 ] }, "601": { "learned": [ 742, 743 ] }, "602": { "learned": [ 65624, 65625, 194, 708 ] }, "604": { "learned": [ 511, 512 ] }, "605": { "learned": [ 685, 736, 737, 738, 684, 742, 743, 751, 752 ] }, "607": { "learned": [ 543, 73, 109, 110, 151, 211, 452, 453, 454, 535, 536, 537, 568, 569, 617 ] }, "608": { "learned": [ 744, 745, 66281 ] }, "609": { "learned": [ 65639, 113, 242, 142, 143, 492, 494, 648 ] }, "624": { "learned": [ 241, 31, 34, 51, 65587, 62, 68, 77, 78, 95, 208, 99, 111, 112, 464, 127, 128, 151, 195, 214, 221, 473, 248, 260, 306, 383, 449, 450, 486, 529, 530, 533, 534, 553, 626, 660, 697, 713, 718, 760, 787, 794, 799, 231, 232 ] }, "625": { "learned": [ 252, 253, 254, 420, 421, 459, 460 ] }, "626": { "learned": [ 131872, 197408 ] }, "630": { "learned": [ 77, 78, 83, 151, 251, 253, 254, 274, 275, 421, 465, 470, 475, 549, 558, 640, 679, 680, 681, 724, 763, 781, 798 ] }, "631": { "learned": [ 66336, 197408 ] }, "633": { "learned": [ 63, 64, 65, 79, 80, 199, 122, 124, 150, 151, 251, 282, 337, 338, 343, 344, 437, 439, 577, 578, 579, 678, 66214, 96, 97, 203, 653, 654, 655 ] }, "634": { "learned": [ 214, 626, 27, 28, 65563, 65564, 31, 34, 52, 53, 65588, 65589, 62, 68, 78, 83, 105, 65641, 106, 107, 118, 119, 127, 128, 151, 197, 211, 222, 243, 254, 264, 275, 329, 330, 405, 434, 435, 452, 454, 475, 510, 526, 531, 539, 545, 553, 556, 560, 571, 596, 614, 625, 631, 635, 675, 687, 701, 66281, 768, 795, 802, 15, 20, 65556, 22, 24, 47, 57, 157, 168, 207, 472, 217, 229, 234, 262, 297, 335, 336, 354, 359, 386, 395, 402, 432, 455, 491, 512, 514, 516, 542, 603, 604, 720 ] }, "635": { "learned": [ 460, 582, 583, 584, 698, 699, 712, 713 ] }, "636": { "learned": [ 116, 117, 230, 123, 212, 328, 329, 330, 610, 611, 612, 615, 624, 625, 659, 660, 15, 18, 22, 25, 26, 65562, 36, 38, 65574, 40, 55, 59, 65, 83, 94, 105, 65641, 106, 107, 113, 242, 133, 470, 471, 135, 134, 136, 196, 197, 700, 142, 144, 145, 146, 151, 153, 154, 157, 164, 178, 181, 182, 199, 205, 215, 461, 217, 229, 236, 237, 243, 244, 245, 249, 250, 251, 254, 257, 262, 267, 269, 277, 282, 286, 291, 301, 303, 308, 310, 326, 337, 338, 350, 358, 359, 362, 373, 376, 380, 381, 386, 392, 395, 398, 402, 405, 407, 409, 416, 417, 421, 424, 428, 429, 444, 445, 447, 468, 469, 475, 477, 478, 480, 481, 482, 492, 493, 494, 504, 505, 510, 523, 526, 528, 531, 538, 539, 542, 548, 549, 555, 571, 573, 576, 579, 589, 609, 617, 620, 628, 638, 639, 640, 643, 644, 646, 66182, 131718, 648, 654, 655, 666, 675, 679, 680, 681, 689, 693, 701, 706, 715, 716, 717, 720, 724, 738, 66281, 752, 754, 758, 763, 766, 768, 773, 784, 795 ] }, "640": { "learned": [ 357 ] }, "643": { "learned": [ 62, 68, 143, 151, 260, 466, 477, 486, 553, 623, 675, 760, 787, 794 ] }, "644": { "learned": [ 131 ] }, "646": { "learned": [ 83, 328, 329, 330, 610, 611, 612, 632 ] }, "650": { "learned": [ 426, 43, 44, 45, 182, 69, 70, 71, 187, 188, 189, 592, 593 ] }, "651": { "learned": [ 171 ] }, "654": { "learned": [ 12, 45, 151, 251, 420, 421, 548, 549, 590, 591, 755, 756, 764 ] }, "656": { "learned": [ 167, 168, 313, 595, 596 ] }, "657": { "learned": [ 758 ] }, "658": { "learned": [ 393, 394, 395, 725, 726, 727 ] }, "659": { "learned": [ 250, 498, 499, 500 ] }, "660": { "learned": [ 26, 63, 64, 65, 137, 233, 474, 150, 151, 436, 437, 494, 527, 528, 561, 595, 596, 786, 801 ] }, "661": { "learned": [ 214, 638, 639, 640, 647, 716 ] }, "664": { "learned": [ 26, 72, 73, 99, 183, 184, 237, 298, 303, 425, 426, 465, 598, 620, 631, 711 ] }, "665": { "learned": [ 58, 59, 142, 151, 196, 208, 303, 309, 310, 403, 404, 405, 528, 550, 696, 697, 745, 66281, 773, 791, 800, 203 ] }, "666": { "learned": [ 246, 247, 248, 3, 30, 31, 33, 34, 50, 51, 65586, 65587, 67, 68, 95, 208, 99, 103, 65639, 106, 108, 463, 111, 112, 464, 113, 242, 128, 143, 151, 185, 194, 195, 206, 221, 473, 222, 241, 244, 260, 264, 294, 295, 305, 306, 324, 337, 338, 340, 348, 365, 369, 376, 377, 378, 379, 383, 423, 438, 445, 446, 449, 450, 452, 460, 465, 466, 467, 483, 484, 485, 486, 508, 525, 526, 530, 531, 534, 537, 538, 545, 552, 553, 558, 569, 591, 611, 612, 618, 621, 626, 631, 632, 639, 660, 675, 696, 697, 703, 706, 713, 718, 721, 727, 744, 745, 66281, 749, 750, 776, 780, 784, 794, 797, 20, 65556, 24, 27, 28, 75, 76, 65611, 65612, 85, 154, 157, 168, 203, 210, 217, 219, 232, 289, 297, 299, 476, 301, 323, 327, 332, 357, 386, 389, 400, 409, 411, 432, 498, 499, 500, 505, 586, 650, 651, 652, 673, 734, 735, 775 ] }, "668": { "learned": [ 140, 141, 318, 319, 9, 55, 62, 79, 80, 199, 90, 91, 98, 99, 116, 117, 230, 131, 134, 139, 151, 184, 186, 195, 211, 224, 226, 245, 260, 278, 279, 321, 340, 342, 348, 365, 369, 382, 484, 537, 550, 614, 647, 686, 687, 688, 689, 693, 721, 730, 746, 781, 87, 160, 283, 284, 370, 395, 400, 419, 489, 490, 493, 503, 580, 581, 594, 779 ] }, "671": { "learned": [ 706, 311, 312, 504, 505, 669, 670, 671, 702 ] }, "467": { "learned": [ 393695 ] }, // Give Shadow Force to Rotom-Pokedex "702": { "name": "Mind Blown", "learned": [806], "type": "Fire", "power": 150 }, "703": { "name": "Plasma Fists", "learned": [807], "type": "Electric", "power": 100 }, "704": { "name": "Photon Geyser", "learned": [800, 66336, 131872, 197408], "type": "Psychic", "power": 100 }, "705": { "name": "Double Iron Bash", "learned": [809], "type": "Steel", "power": 60 }, "706": { "name": "Clangorous Soul", "learned": [784], "type": "Dragon", "power": "---" }, "707": { "name": "Body Press", "learned": [ 486, 9, 31, 34, 80, 95, 108, 112, 131, 143, 149, 151, 185, 208, 226, 227, 241, 248, 260, 65800, 304, 305, 306, 320, 321, 324, 344, 365, 369, 376, 377, 379, 437, 450, 462, 463, 464, 473, 483, 484, 485, 524, 525, 526, 555, 131627, 558, 563, 569, 598, 614, 623, 643, 644, 646, 66182, 131718, 701, 703, 706, 713, 719, 721, 750, 760, 776, 781, 784, 799, 805, 809, 812, 820, 823, 825, 826, 832, 834, 838, 839, 842, 844, 861, 862, 867, 874, 878, 879, 884, 892, 66428, 896, 66434 ], "type": "Fighting", "power": 80 }, "708": { "name": "Decorate", "learned": [869], "type": "Fairy", "power": "---", }, "709": { "name": "Drum Beating", "learned": [812], "type": "Grass", "power": 80 }, "710": { "name": "Snap Trap", "learned": [getForm(618, 1)], "type": "Grass", "power": 35 }, "711": { "name": "Pyro Ball", "learned": [815], "type": "Fire", "power": 120 }, "712": { "name": "Behemoth Blade", "learned": [getForm(888, 1)], "type": "Steel", "power": 100 }, "713": { "name": "Behemoth Bash", "learned": [getForm(889, 1)], "type": "Steel", "power": 100 }, "714": { "name": "Aura Wheel", "learned": [877, getForm(877, 1)], "type": "Electric", "power": 110 }, "715": { "name": "Breaking Swipe", "learned": [ 884, 6, 95, 65639, 112, 147, 148, 149, 151, 208, 230, 248, 254, 330, 334, 350, 373, 380, 381, 384, 445, 464, 483, 484, 487, 610, 611, 612, 635, 643, 644, 646, 66182, 131718, 695, 697, 706, 718, 758, 780, 784, 800, 66336, 197408, 131872, 804, 818, 880, 886, 887, 895 ], "type": "Dragon", "power": 60 }, "716": { "name": "Branch Poke", "learned": [708, 709, 810, 811, 812], "type": "Grass", "power": 40 }, "717": { "name": "Overdrive", "learned": [849, getForm(849, 1)], "type": "Electric", "power": 80 }, "718": { "name": "Apple Acid", "learned": [842], "type": "Grass", "power": 80 }, "719": { "name": "Grav Apple", "learned": [841], "type": "Grass", "power": 80 }, "720": { "name": "Spirit Break", "learned": [861], "type": "Fairy", "power": 75 }, "721": { "name": "Strange Steam", "learned": [getForm(110, 1)], "type": "Fairy", "power": 90 }, "722": { "name": "Life Dew", "learned": [ 35, 36, 113, 131, 150, 151, 175, 176, 222, 242, 250, 251, 280, 281, 282, 350, 385, 448, 468, 475, 531, 856, 857, 858, 898, 66434, 131970, 7, 8, 9, 90, 91, 315, 407, 406, 704, 705, 706, 728, 729, 730 ], "type": "Water", "power": "---" }, "723": { "name": "Obstruct", "learned": [862], "type": "Dark", "power": "---" }, "724": { "name": "False Surrender", "learned": [860, 861], "type": "Dark", "power": 80 }, "725": { "name": "Meteor Assault", "learned": [865], "type": "Fighting", "power": 150 }, "726": { "name": "Eternabeam", "learned": [890, getForm(890, 1)], "type": "Dragon", "power": 160 }, "727": { "name": "Steel Beam", "learned": [ 65563, 65564, 65586, 65587, 131124, 81, 82, 208, 212, 227, 303, 304, 305, 306, 374, 375, 376, 379, 385, 436, 437, 448, 462, 483, 485, 530, 589, 597, 598, 599, 600, 601, 66154, 624, 625, 632, 638, 649, 679, 680, 681, 707, 773, 777, 791, 797, 798, 801, 805, 808, 809, 823, 863, 878, 879, 884, 888, 66424, 889, 66425 ], "type": "Steel", "power": 140 }, "728": { "name": "Dynamax Cannon", "learned": [890, getForm(890, 1)], "type": "Dragon", "power": 100 }, "729": { "name": "Snipe Shot", "learned": [818], "type": "Water", "power": 80 }, "730": { "name": "Jaw Lock", "learned": [833, 834], "type": "Dark", "power": 80 }, "731": { "name": "No Retreat", "learned": [870], "type": "Fighting", "power": "---" }, "732": { "name": "Tar Shot", "learned": [839], "type": "Rock", "power": "---" }, "733": { "name": "Magic Powder", "learned": [858], "type": "Psychic", "power": "---" }, "734": { "name": "Dragon Darts", "learned": [887], "type": "Dragon", "power": 50 }, "735": { "name": "Teatime", "learned": [855], "type": "Normal", "power": "---" }, "736": { "name": "Octolock", "learned": [853], "type": "Fighting", "power": "---" }, "737": { "name": "Bolt Beak", "learned": [880, 881], "type": "Electric", "power": 85 }, "738": { "name": "Fishious Rend", "learned": [882, 883], "type": "Water", "power": 85 }, "739": { "name": "Court Change", "learned": [815], "type": "Normal", "power": "---" }, "740": { "name": "Stuff Cheeks", "learned": [ 819, 820 ], "type": "Normal", "power": "---" }, "741": { "name": "Wicked Blow", "learned": [892], "type": "Dark", "power": 75 }, "742": { "name": "Surging Strikes", "learned": [66428], "type": "Water", "power": 25 }, "743": { "name": "Freezing Glare", "learned": [65680], "type": "Psychic", "power": 90 }, "744": { "name": "Thunderous Kick", "learned": [65681], "type": "Fighting", "power": 90 }, "745": { "name": "Fiery Wrath", "learned": [65682], "type": "Dark", "power": 90 }, "746": { "name": "Thunder Cage", "learned": [894], "type": "Electric", "power": 80 }, "747": { "name": "Dragon Energy", "learned": [895], "type": "Dragon", "power": 150 }, "748": { "name": "Glacial Lance", "learned": [66434], "type": "Ice", "power": 120 }, "749": { "name": "Astral Barrage", "learned": [131970], "type": "Ghost", "power": 120 }, "750": { "name": "Shell Side Arm", "learned": [131152], "type": "Poison", "power": 90 }, "751": { "name": "Eerie Spell", "learned": [65735], "type": "Psychic", "power": 80 }, "752": { "name": "Terrain Pulse", "learned": [ 3, 9, 103, 65639, 108, 115, 143, 151, 206, 295, 448, 463, 486, 66154, 692, 693, 703, 716, 719, 765, 770, 772, 773, 876, 66412 ], "type": "Normal", "power": 50 }, "753": { "name": "Burning Jealousy", "learned": [ 37, 38, 65589, 65641, 126, 136, 146, 151, 324, 435, 442, 467, 485, 510, 555, 131627, 570, 571, 607, 608, 609, 631, 727, 776, 778, 828, 859, 860, 861, 864 ], "type": "Fire", "power": 70 }, "754": { "name": "Flip Turn", "learned": [ 7, 8, 9, 55, 116, 117, 118, 119, 120, 121, 134, 141, 151, 230, 260, 318, 319, 350, 550, 647, 690, 691, 692, 693, 728, 729, 730, 746, 847 ], "type": "Water", "power": 60 }, "755": { "name": "Grassy Glide", "learned": [ 1, 2, 3, 43, 44, 45, 102, 103, 65639, 114, 151, 182, 251, 252, 253, 254, 270, 271, 272, 273, 274, 275, 315, 406, 407, 420, 421, 459, 460, 465, 470, 546, 547, 548, 549, 556, 640, 708, 709, 710, 711, 722, 723, 724, 753, 754, 761, 762, 763, 764, 781, 810, 811, 812, 829, 830, 831, 832, 840, 841, 842, 865, 893 ], "type": "Grass", "power": 60 }, "756": { "name": "Rising Voltage", "learned": [ 25, 26, 65562, 81, 82, 125, 135, 145, 151, 170, 171, 243, 309, 310, 403, 404, 405, 462, 466, 479, 262623, 131551, 197087, 328159, 66015, 587, 595, 596, 599, 600, 601, 642, 644, 694, 695, 702, 736, 737, 738, 777, 796, 807, 835, 836, 849, 66385, 871, 877, 880, 881, 894 ], "type": "Electric", "power": 70 }, "757": { "name": "Coaching", "learned": [ 62, 66, 67, 68, 106, 107, 65681, 151, 214, 237, 256, 257, 447, 448, 453, 454, 475, 532, 533, 534, 538, 539, 559, 560, 619, 620, 638, 639, 640, 647, 674, 675, 701, 759, 760, 766, 783, 784, 794, 795, 802, 807, 815, 852, 853, 865, 870, 889, 66425, 891, 892, 66428 ], "type": "Fighting", "power": "---" }, "758": { "name": "Scorching Sands", "learned": [ 6, 27, 28, 31, 34, 38, 50, 65586, 51, 65587, 59, 78, 95, 104, 105, 65641, 111, 112, 126, 136, 146, 151, 208, 244, 250, 324, 328, 329, 330, 343, 344, 383, 443, 444, 445, 449, 450, 464, 467, 485, 494, 529, 530, 551, 552, 553, 622, 623, 631, 643, 660, 718, 721, 727, 769, 770, 776, 815, 838, 839, 843, 844, 850, 851 ], "type": "Ground", "power": 70 }, "759": { "name": "Dual Wingbeat", "learned": [ 6, 12, 35, 36, 83, 65619, 123, 142, 144, 65680, 145, 65681, 146, 65682, 149, 151, 163, 164, 169, 176, 177, 178, 206, 212, 225, 226, 227, 249, 250, 251, 278, 279, 291, 329, 330, 333, 334, 373, 380, 381, 415, 416, 468, 484, 487, 519, 520, 521, 527, 528, 561, 566, 567, 587, 621, 627, 628, 629, 630, 635, 637, 643, 644, 646, 66182, 131718, 661, 662, 663, 701, 714, 715, 717, 722, 723, 724, 738, 742, 743, 792, 794, 804, 821, 822, 823, 841, 845, 865, 873 ], "type": "Flying", "power": 40 }, "760": { "name": "Meteor Beam", "learned": [ 35, 36, 95, 112, 121, 138, 139, 140, 141, 142, 151, 185, 208, 213, 222, 65758, 306, 337, 338, 345, 346, 347, 348, 369, 375, 376, 377, 379, 384, 385, 437, 464, 524, 525, 526, 558, 564, 565, 566, 567, 605, 606, 689, 696, 697, 698, 699, 703, 719, 791, 792, 793, 797, 800, 66336, 197408, 131872, 805, 834, 837, 838, 839, 864, 874, 880, 881, 882, 883, 890 ], "type": "Rock", "power": 120 }, "761": { "name": "Skitter Smack", "learned": [ 53, 65589, 92, 93, 94, 151, 206, 213, 224, 290, 291, 292, 350, 355, 356, 422, 423, 451, 452, 477, 510, 543, 544, 545, 551, 552, 553, 557, 558, 570, 571, 595, 596, 607, 608, 609, 616, 617, 632, 636, 637, 704, 705, 706, 708, 709, 710, 711, 718, 724, 736, 737, 738, 751, 752, 757, 758, 767, 768, 795, 802, 833, 834, 843, 844, 850, 851, 853, 872, 873 ], "type": "Bug", "power": 70 }, "762": { "name": "Triple Axel", "learned": [ 65563, 65564, 65574, 65658, 144, 151, 215, 225, 237, 281, 282, 350, 427, 428, 461, 471, 475, 478, 572, 573, 615, 728, 729, 730, 762, 763, 795, 866, 873 ], "type": "Ice", "power": 20 }, "763": { "name": "Corrosive Gas", "learned": [ 45, 73, 92, 93, 94, 109, 110, 65646, 151, 434, 435, 454, 568, 569, 758, 793, 799 ], "type": "Poison", "power": "---" }, "764": { "name": "Expanding Force", "learned": [ 65562, 40, 64, 65, 65613, 65614, 79, 65615, 80, 131152, 103, 121, 122, 65658, 124, 65680, 150, 151, 177, 178, 196, 199, 65735, 251, 280, 281, 282, 343, 344, 375, 376, 385, 436, 437, 475, 480, 481, 482, 488, 494, 517, 518, 527, 528, 555, 561, 574, 575, 576, 577, 578, 579, 605, 606, 677, 678, 66214, 686, 687, 765, 791, 792, 800, 806, 825, 826, 856, 857, 858, 866, 876, 66412, 898, 66434, 131970 ], "type": "Psychic", "power": 80 }, "765": { "name": "Poltergeist", "learned": [ 92, 93, 94, 65641, 151, 292, 302, 355, 356, 442, 477, 478, 479, 262623, 131551, 197087, 328159, 66015, 487, 562, 66098, 563, 592, 593, 607, 608, 609, 622, 623, 708, 709, 710, 711, 724, 769, 770, 781, 792, 802, 854, 855, 864, 867 ], "type": "Ghost", "power": 110 }, "766": { "name": "Scale Shot", "learned": [ 6, 116, 117, 118, 119, 130, 147, 148, 149, 151, 206, 211, 230, 249, 254, 318, 319, 330, 349, 350, 369, 380, 381, 384, 443, 444, 445, 483, 484, 550, 553, 610, 611, 612, 621, 635, 643, 644, 646, 66182, 131718, 690, 691, 694, 695, 696, 697, 718, 746, 757, 758, 776, 780, 782, 783, 784, 804, 818, 833, 834, 843, 844, 846, 847, 895 ], "type": "Dragon", "power": 25 }, "767": { "name": "Lash Out", "learned": [ 52, 65588, 131124, 53, 65589, 128, 130, 65682, 151, 197, 215, 248, 65799, 65800, 274, 275, 302, 342, 434, 435, 452, 453, 454, 461, 509, 510, 551, 552, 553, 555, 131627, 559, 560, 566, 567, 570, 571, 618, 66154, 621, 624, 625, 626, 629, 630, 641, 642, 674, 675, 686, 687, 696, 697, 717, 727, 66281, 750, 776, 780, 827, 828, 859, 860, 861, 862, 863, 877, 892, 893, 896, 897, 66434, 131970 ], "type": "Dark", "power": 75 }, "768": { "name": "Steel Roller", "learned": [ 27, 65563, 28, 65564, 39, 40, 91, 108, 143, 151, 183, 184, 208, 211, 213, 241, 304, 305, 306, 320, 321, 362, 363, 364, 365, 375, 376, 379, 436, 437, 462, 463, 485, 543, 544, 545, 577, 578, 579, 597, 598, 599, 600, 601, 777, 781, 791, 797, 799, 801, 805, 809, 878, 879, 884 ], "type": "Steel", "power": 130 }, "769": { "name": "Misty Explosion", "learned": [ 35, 36, 39, 40, 65646, 151, 183, 184, 282, 518, 682, 683, 684, 685, 700, 703, 716, 719, 730, 801, 858, 869 ], "type": "Fairy", "power": 100 }, "770": { "name": "Jungle Healing", "learned": [893], "type": "Grass", "power": "---" }, "771": { "name": "Dire Claw", "learned": [903], "type": "Poison", "power": 80 }, "772": { "name": "Psyshield Bash", "learned": [234, 899], "type": "Psychic", "power": 70 }, "773": { "name": "Power Shift", "learned": [], "type": "Normal", "power": "---" }, "774": { "name": "Stone Axe", "learned": [900], "type": "Rock", "power": 65 }, "775": { "name": "Mystical Power", "learned": [480, 481, 482], "type": "Psychic", "power": 70 }, "776": { "name": "Raging Fury", "learned": [65595], "type": "Fire", "power": 120 }, "777": { "name": "Wave Crash", "learned": [902, 66438], "type": "Water", "power": 120 }, "778": { "name": "Chloroblast", "learned": [65637], "type": "Grass", "power": 150 }, "779": { "name": "Mountain Gale", "learned": [66249], "type": "Ice", "power": 100 }, "780": { "name": "Victory Dance", "learned": [66085], "type": "Fighting", "power": "---" }, "781": { "name": "Headlong Rush", "learned": [389, 901], "type": "Ground", "power": 120 }, "782": { "name": "Barb Barrage", "learned": [65747, 904], "type": "Poison", "power": 60 }, "783": { "name": "Esper Wing", "learned": [66164], "type": "Psychic", "power": 80 }, "784": { "name": "Bitter Malice", "learned": [66106, 66107], "type": "Ghost", "power": 75 }, "785": { "name": "Shelter", "learned": [704, 705, 706, 66241, 66242], "type": "Steel", "power": "---" }, "786": { "name": "Triple Arrows", "learned": [66260], "type": "Fighting", "power": 90 }, "787": { "name": "Infernal Parade", "learned": [65693], "type": "Ghost", "power": 60 }, "788": { "name": "Ceaseless Edge", "learned": [66039], "type": "Dark", "power": 65 }, "789": { "name": "Springtide Storm", "learned": [905, 66441], "type": "Fairy", "power": 100 }, "790": { "name": "Bleakwind Storm", "learned": [641, 66177], "type": "Flying", "power": 100 }, "791": { "name": "Wildbolt Storm", "learned": [642, 66178], "type": "Electric", "power": 100 }, "792": { "name": "Sandsear Storm", "learned": [645, 66181], "type": "Ground", "power": 100 }, "793": { "name": "Lunar Blessing", "learned": [488], "type": "Psychic", "power": "---" }, "794": { "name": "Take Heart", "learned": [489, 490], "type": "Psychic", "power": "---" }, "795": { "name": "Tera Blast", "learned": [], // literally every mon or something "type": "Normal", "power": 80 }, "796": { "name": "Silk Trap", "learned": [919], "type": "Bug", "power": "---" }, "797": { "name": "Axe Kick", "learned": [308, 66085, 921], "type": "Fighting", "power": 120 }, "798": { "name": "Last Respects", "learned": [925], "type": "Ghost", "power": 50 }, "799": { "name": "Lumina Crash", "learned": [927], "type": "Psychic", "power": 80 }, "800": { "name": "Order Up", "learned": [931], "type": "Dragon", "power": 80 }, "801": { "name": "Jet Punch", "learned": [934, 66470], "type": "Water", "power": 60 }, "802": { "name": "Spicy Extract", "learned": [939], "type": "Grass", "power": "---" }, "803": { "name": "Spin Out", "learned": [942, 943], "type": "Steel", "power": 100 }, "804": { "name": "Population Bomb", "learned": [945, 946, 66482], "type": "Normal", "power": 20 }, "805": { "name": "Ice Spinner", "learned": [947, 948], // TM lines unadded "type": "Ice", "power": 80 }, "806": { "name": "Glaive Rush", "learned": [951], "type": "Dragon", "power": 120 }, "807": { "name": "Revival Blessing", "learned": [923, 956], "type": "Normal", "power": "---" }, "808": { "name": "Salt Cure", "learned": [963, 964, 965], "type": "Rock", "power": 40 }, "809": { "name": "Triple Dive", "learned": [930], "type": "Water", "power": 30 }, "810": { "name": "Mortal Spin", "learned": [967], "type": "Poison", "power": 30 }, "811": { "name": "Doodle", "learned": [969], "type": "Normal", "power": "---" }, "812": { "name": "Fillet Away", "learned": [932], "type": "Normal", "power": "---" }, "813": { "name": "Kowtow Cleave", "learned": [1008], "type": "Dark", "power": 85 }, "814": { "name": "Flower Trick", "learned": [908], "type": "Grass", "power": 70 }, "815": { "name": "Torch Song", "learned": [911], "type": "Fire", "power": 80 }, "816": { "name": "Aqua Step", "learned": [914], "type": "Water", "power": 80 }, "817": { "name": "Raging Bull", "learned": [128, 65664, 131200, 196736], "type": "Normal", "power": 90 }, "818": { "name": "Make It Rain", "learned": [977], "type": "Steel", "power": 120 }, "819": { "name": "Ruination", "learned": [994, 995, 996, 997], "type": "Dark", "power": "---" }, "820": { "name": "Collision Course", "learned": [998, 66534, 132070, 197606, 263142], "type": "Fighting", "power": 100 }, "821": { "name": "Electro Drift", "learned": [999, 66535, 132071, 197607, 263143], "type": "Electric", "power": 100 }, "822": { "name": "Shed Tail", "learned": [944, 953], "type": "Normal", "power": "---" }, "823": { "name": "Chilly Reception", "learned": [199, 65735], "type": "Ice", "power": "---" }, "824": { "name": "Tidy Up", "learned": [946, 66482], "type": "Normal", "power": "---" }, "825": { "name": "Snowscape", "learned": [902, 66438, 947, 948, 932, 950, 951, 952, 982, 992, 995], // all pokemon with hail before unadded "type": "", "power": "---" }, "826": { "name": "Pounce", "learned": [], // way too many "type": "Bug", "power": 50 }, "827": { "name": "Trailblaze", "learned": [], // way too many "type": "Grass", "power": 50 }, "828": { "name": "Chilling Water", "learned": [], // way too many "type": "Water", "power": 50 }, "829": { "name": "Hyper Drill", "learned": [206, 917, 66453], "type": "Normal", "power": 100 }, "830": { "name": "Twin Beam", "learned": [203, 928], "type": "Psychic", "power": 40 }, "831": { "name": "Rage Fist", "learned": [57, 1010], "type": "Ghost", "power": 50 }, "832": { "name": "Armor Cannon", "learned": [1004], "type": "Fire", "power": 120 }, "833": { "name": "Bitter Blade", "learned": [1005], "type": "Fire", "power": 90 }, "834": { "name": "Double Shock", "learned": [956], "type": "Electric", "power": 120 }, "835": { "name": "Gigaton Hammer", "learned": [1002], "type": "Steel", "power": 160 }, "836": { "name": "Comeuppance", "learned": [228, 229, 430, 66106, 66107, 973], "type": "Dark", "power": "---" }, "837": { "name": "Aqua Cutter", "learned": [475, 912, 913, 914, 932], "type": "Water", "power": 70 }, "838": { "name": "Blazing Torque", "learned": [132038], "type": "Fire", "power": 80 }, "839": { "name": "Wicked Torque", "learned": [66502], "type": "Dark", "power": 80 }, "840": { "name": "Noxious Torque", "learned": [197574], "type": "Poison", "power": 100 }, "841": { "name": "Combat Torque", "learned": [328646], "type": "Fighting", "power": 100 }, "842": { "name": "Magical Torque", "learned": [263110], "type": "Fairy", "power": 100 }, "843": { "name": "Hydro Steam", "learned": [1009], "type": "Water", "power": 80 }, "844": { "name": "Psyblade", "learned": [1010], "type": "Psychic", "power": 80 }, "845": { "name": "Syrup Bomb", "learned": [1011], "type": "Grass", "power": 60 }, "846": { "name": "Matcha Gotcha", "learned": [1013], "type": "Grass", "power": 80 }, "847": { "name": "Ivy Cudgel", "learned": [1017, 66553, 132089, 197625], "type": "Grass", "power": 100 }, "848": { "name": "Blood Moon", "learned": [66437], "type": "Normal", "power": 140 }, "849": { "name": "Electro Shot", "learned": [1018], "type": "Electric", "power": 130 }, "850": { "name": "Tera Starstorm", "learned": [132096], "type": "Stellar", "power": 120 }, "851": { "name": "Fickle Beam", "learned": [1019], "type": "Dragon", "power": 80 }, "852": { "name": "Burning Bulwark", "learned": [1020], "type": "Fire", "power": "---" }, "853": { "name": "Thunderclap", "learned": [1021], "type": "Electric", "power": 70 }, "854": { "name": "Mighty Cleave", "learned": [1022], "type": "Rock", "power": 95 }, "855": { "name": "Tachyon Cutter", "learned": [1023], "type": "Steel", "power": 50 }, "856": { "name": "Hard Press", "learned": [], "type": "Steel", "power": 100 }, "857": { "name": "Dragon Cheer", "learned": [], "type": "Dragon", "power": "---" }, "858": { "name": "Alluring Voice", "learned": [], "type": "Fairy", "power": 80 }, "859": { "name": "Temper Flare", "learned": [], "type": "Fire", "power": 75 }, "860": { "name": "Supercell Slam", "learned": [], "type": "Electric", "power": 100 }, "861": { "name": "Psychic Noise", "learned": [], "type": "Psychic", "power": 75 }, "862": { "name": "Upper Hand", "learned": [], "type": "Fighting", "power": 65 }, "863": { "name": "Malignant Chain", "learned": [1025], "type": "Poison", "power": 100 } /* template "": { "name": "", "learned": [], "type": "", "power": 0 }, */ }; var moveBlacklist = { // move: [pokemon list] 307: [255, 256, 390, 391, 498, 499, 653, 654, 725, 726], 308: [258, 289, 393, 394, 501, 502, 656, 657, 728, 729], 338: [252, 253, 387, 388, 495, 496, 650, 651, 722, 723], 431: [3, 9, 31, 34, 55, 56, 57, 59, 62, 66, 67, 68, 104, 105, 106, 107, 113, 115, 125, 126, 127, 139, 141, 143, 150, 151, 154, 157, 160, 181, 210, 217, 242, 243, 244, 245, 248, 254, 257, 260, 272, 288, 289, 295, 296, 297, 306, 335, 377, 378, 379, 383, 387, 388, 389, 390, 391, 392, 395, 408, 409, 446, 448, 452, 453, 454, 460, 466, 467, 473, 485, 486, 487, 491, 493], 457: [299, 476], 512: [59], 571: [401], 608: [133, 134, 135, 136, 196, 197, 470, 471, 700], 610: [133, 134, 135, 136, 196, 197, 470, 471, 700] }; var updatedAbilities = { "109": [26, 256, 1], // Koffing and Weezing get new abilities in Gen 8 "110": [26, 256, 1], "275": [34, 273, 124], // Shiftry gets Wind Rider > Early Bird in Gen 9 "395": [67, 181] // Empoleon gets Competitive > Defiant in Gen 9 }; var updatedStats = { "65708": [20, 40, 15, 35, 35, 60], // Pichu-Spiky Eared's stats are incorrect on PO "681": [60, 50, 140, 50, 140, 60], // Aegislash was nerfed in Gen 8 (Sword & Shield) "66217": [60, 140, 50, 140, 50, 60], "488": [120, 70, 110, 75, 120, 85] // Cresselia's defences nerfed in Gen 9 }; function getCrystalEffect(num) { for (var x in zCrystalData) { if (!(zCrystalData[x].special)) { continue; } if (zCrystalData[x].special.contains(num)) { return x; } } return type1(num); }; var itemHelp, perkHelp, ballHelp, berryHelp; var allBalls; updateItemData(); var updateItemHelp = function() { itemHelp = { silver: "Rare coins that can be used to purchase valuable items. Obtained from quests and Contests.", bait: "A tasty treat used to attract wild Pokémon. Has " + an(itemData.bait.successRate*100) + "% success rate with an approximate " + itemData.bait.successCD + " second cooldown on success, and an approximate " + itemData.bait.failCD + " second cooldown on failure. Use with \"/bait\".", golden: "A premium bait used to attract wild Pokémon. Has " + an(itemData.golden.successRate*100) + "% success rate and can be used more often than normal Baits. Use with \"/gbait\".", deluxe: "A customizable bait. Made from /quest baking.", gacha: "A ticket that allows you to try the Gachapon Machine to get a random reward! " + cdSeconds("gacha") + " Use with \"/gacha\".", rock: "A small rock that can be thrown at another player for amusement. " + cdSeconds("rock", "throwCD") + " Use with \"/rock [Player]\". Can also be used to scare unwanted Pokémon away with \"/rockscare\". You can only scare Pokémon that haven't been interacted with for " + plural(rockScareThreshold/1000, "second") + ".", rare: "Can be smashed and transformed into around " + (itemData.rare.charges + Math.floor(itemData.rare.maxVar/2)) + " Candy Dusts. Use with \"/use rare\". Found with Itemfinder and obtained in Pyramid.", dust: "What you obtain after smashing a Rare Candy into powder. Has the power to evolve Pokémon. Use with \"/evolve [Pokémon]\".", spray: "A spray that affects the genetic code of a Pokémon, making them devolve and generating some Candy Dust. Use with \"/spray [Pokémon]\". Obtained from Prize Packs and Pyramid.", mega: "A mysterious stone that allows certain Pokémon to undergo a powerful transformation. It is said to wear off in approximately " + itemData.mega.duration + " days. Use with \"/mega [Pokémon]\". Obtained from Official Events, Pyramid, Alchemy, and Prize Packs.", valuables: "The items Pearl, Stardust, Big Pearl, Star Piece, Nugget, Big Nugget and Comet Shard can be sold for a varying amount of money. Sell with \"/pawn [Item]\". Obtained from Gachapon, found with Itemfinder, and rewarded from Contests.", itemfinder: "Itemfinder: An experimental machine that can help find rare items! By default, it can only hold " + itemData.itemfinder.charges + " charges. These charges are reset every day. Use with \"/finder\".", gem: "An electrically charged gem created by a famous Ampharos in Olivine City. It is said to be able to recharge the Itemfinder, giving it " + itemData.gem.charges + " more uses for the day! Use with \"/use gem\". Obtained from Gachapon and quests.", box: "Increases number of Pokémon that can be owned by " + itemData.box.bonusRate + " each. Can only acquire by purchasing.", stick: "Legendary Stick of the almighty Farfetch'd that provides a never ending wave of prods and pokes unto your enemies and other nefarious evil-doers. " + cdSeconds("stick") + " Use with \"/stick [Player]\".", salt: "A pile of salt that makes the holder increasingly unlucky the more they have.", burn: "A potion used to purify Burning Aura into Brilliant Aura, in exchange, you also get Brilliant Aura. Use /burn [player] to give it to someone who may need it. Obtainable via Gachapon.", entry: "A Raffle Entry that can win a spectacular prize if you own the correct one at the time of drawing. Simply hold onto your ticket safely until the time of the drawing. Nothing more is needed on your part!", coupon: "A coupon holding a special offer to those interested in decorating their Secret Base. Can be traded for a Decoration at the Decor.", fossil: "A rare fossil often desired by archaeologists. Use it to enter the Pyramid without paying the entry fee by typing \"/quest pyramid:fossil:Name1:Name2\". You also receive " + (itemData.fossil.bonusRate * 100) + "% more points during that run.", form: "A form that you can fill to start an event. Use it with \"/use form\" for more details.", pack: "A wonderful package that could contain equally wonderful prizes! Use with \"/use pack:[Amount to Use]\". Obtained from Official Events, Pyramid, and Detective", fragment: "A fragment of a broken Poké Ball. Collecting " + itemData.fragment.threshold + " is said to be enough to form a Master Ball! Obtained from Itemfinder and when obtaining a Master Ball while having one already.", materia: "A basic substance required for various alchemic creations. Obtained from Alchemy quest.", philosopher: "A legendary pink gem that is said to be capable of performing outstanding transformations. Obtained from League quest.", philosopherpebble: "An incompletely crystallized Philosopher's Stone. It still holds a fraction of the complete Stone's transformative power, so maybe you can make use of it somehow... Obtained from the weekly Celebrity leaderboards.", ash: "An extremely rare material useful in transmutation. Obtained from clearing Celebrity quest on Normal or harder.", egg: "An egg that seems to have a non-legendary Pokémon inside. Use with \"/use egg:[Amount to Use]\". Obtained from Pyramid quest.", bright: "A mysterious egg that gives birth to a Pokémon when hatched. Small chance that this Pokémon will be shiny or even legendary! Use with \"/use bright:[Amount to Use]\". Obtained from Pyramid quest.", water: "Water with high mineral content that increases your stamina at Pyramid by " + (itemData.water.bonusRate * 100) + "%. Use with \"/use water\".", soda: "Carbonated drink that reduces remaining cooldown for a quest, auction, burn heal or costume change to " + (itemData.soda.bonusRate * 100) + "%. Use with \"/use soda:[Quest]\". Obtained from Trivia event games.", cookie: "A delicious cookie that brings good luck in the form of a random buff for a limited time when eaten. Use with \"/use cookie\" (using another one will replace the previous effect).", cherry: "A tasty treat that keeps you energized during a Tower Challenge allowing you to deal more damage. Use with \"/use cherry\". Obtained from Alchemy.", blkapricorn: "An acorn-shaped fruit that can be crafted into a Poké Ball. Has a very strong flavor. Found with Itemfinder.", whtapricorn: "An acorn-shaped fruit that can be crafted into a Poké Ball. Has a very weak flavor. Obtained from Gachapon.", shady: "A suspicious coin that's only accepted by some mongers. Obtained from Mafia Event games.", mail: "An envelope that can be used to send a message to another player's inbox. Use it with \"/mail Name:Message\".", crystal: "A mysterious crystal that gives a bonus based on the active Pokémon's primary type for " + itemData.crystal.duration + " minutes. Type \"/use crystal\" for more details.", scale: "A mysterious scale that shines in rainbow colors. Use with \"/use scale\" to make your active Pokémon's color count as a different one for " + itemData.scale.duration + " minutes.", mushroom: "A large and rare mushroom. Eating one with \"/use mushroom\" makes you think you are in a different theme for the next " + itemData.mushroom.duration + " Pokémon that you bait.", brush: "A soft brush ideal for editing photos. Type \"/use brush\" for more details.", pokeblock: "A yummy nom. Feed it to your Pokémon in daycare!", dew: "Can be used to make special Poké Balls. Use /quest arborist for more details.", hdew: "Can be used to make special Poké Balls. Use /quest arborist for more details.", ldew: "A mysterious substance radiating an aura of light. Can be used to create extremely rare Poké Balls. Use /quest arborist for more details.", easteregg: "A colorful ovaloid with surprising goodies inside. Can be used with /use easteregg, /use easteregg:10, /use easteregg:100, and /use easteregg:1000. Don't open until Easter!", //candybag: "A basket full of goodies. Can be used with /use candybag, /use candybag:10, /use candybag:100, and /use candybag:1000. Don't open until Halloween!", lucky: "Coins used to bet on which Celebrity you think will win the tournament.", celebrityTicket: "A ticket to battle the Celebrities. It can be used with /use celebrityticket to make your next challenge a reward run, and /use celebrityticket:[region] to pick a region to challenge next!", moonshard: "A shard that holds the power of the moon. Can unlock special Pokémon skills. Use /quest idol for more details.", sunshard: "A shard that holds the power of the sun. Can unlock special Pokémon skills. Use /quest idol for more details.", battlepoint: "A unit of currency used in the Battle Tower shop. Obtainable from the Tower quest.", terashard: "A shard broken off from a Tera Jewel, which holds some of the mysterious Terastal energy normally found only in Paldea. Obtained from wild Terastallized Pokémon.", terajewel: "A brilliant jewel that holds some of the mysterious Terastal energy normally found only in Paldea. The energy is said to contain the power to crystallize, but it is too unstable to be of use in its current state.", teraorb: "An orb that allows you to wield the Terastal energy of the Tera Jewel contained within it. What effect might it have? You can choose to activate or deactivate the orb with \"/use orb\". When activated, you will automatically deploy the orb on the next Pokémon you bait.", gigantamix: "???", maxmushroom: "???", maxhoney: "???", wishingpiece: "???", wishingstar: "???" }; perkHelp = { amulet: "When holding this charm, " + itemData.amulet.bonusRate * 100 + "% more money is obtained when selling a Pokémon to the store (Max Rate: " + itemData.amulet.maxRate * 100 + "% | With Rich Girl Costume: " + itemData.amulet.maxRate * 100 * costumeData["rich"].rate + "%). Obtained from Gachapon.", soothe: "A bell with a comforting chime that calms the owner and their Pokémon. Reduces delay after a successful catch by " + toFixed(itemData.soothe.bonusRate * 100, 2) + "% (Max Rate: " + itemData.soothe.maxRate * 100 + "% | With Rich Girl Costume: " + itemData.soothe.maxRate * 100 * costumeData["rich"].rate + "%). Obtained from Gachapon.", scarf: "A fashionable scarf made of the finest silk. Wearing it allows you to lead a more luxurious life and grants you " + itemData.scarf.bonusRate * 100 + "% more money from Luxury Balls (Max Rate: " + itemData.scarf.maxRate * 100 + "% | With Backpacker Costume: " + itemData.scarf.maxRate * 100 * costumeData["backpacker"].rate + "%). Obtained from Gachapon.", battery: " A high-capacity battery that can increase the uses of Item Finder by " + itemData.battery.bonusRate + " per day. (Max Rate: " + itemData.battery.maxRate + " | With Backpacker Costume: " + itemData.battery.maxRate * costumeData["backpacker"].rate + "). Obtained from Gachapon.", honey: "Sweet-smelling Combee Honey that, when applied to bait, increases the chance of a Pokémon being attracted by " + itemData.honey.bonusRate * 100 + "% (Max Rate: " + itemData.honey.maxRate * 100 + "% | With Backpacker Costume: " + itemData.honey.maxRate * 100 * costumeData["backpacker"].rate + "%). Found with Itemfinder.", crown: "A rare crown with mysterious properties that brings good fortune to its owner. Increases rate of pawned items by " + itemData.crown.bonusRate * 100 + "% (Max Rate: " + itemData.crown.maxRate * 100 + "% | With Rich Girl Costume: " + itemData.crown.maxRate * 100 * costumeData["rich"].rate + "%). Found with Itemfinder.", eviolite: "A mysterious gem that automatically powers up Pokémon with 420 BST or less by " + itemData.eviolite.bonusRate + ". (Max Rate: " + itemData.eviolite.maxRate + " | With Preschooler Costume: " + itemData.eviolite.maxRate * costumeData["preschooler"].rate2 + "). Found with Itemfinder.", lens: "A device that allows user to take photos of wild Pokémon. Chance for better quality photos increase for each additional " + itemData.lens.fullName + ". Obtained from Journal quest." }; ballHelp = { safari: "A standard issue Poké Ball used to catch Pokémon. " + cdSeconds("safari"), great: "A Poké Ball that has a slightly increased catch rate. " + cdSeconds("great"), ultra: "A high functioning Poké Ball that has a better catch rate than a Great Ball. " + cdSeconds("ultra"), master: "An extremely rare Poké Ball that never fails to catch. " + cdSeconds("master") + " Obtained from Gachapon and Alchemist.", premier: "A plain Poké Ball gifted to you for your patronage. It works better when a Normal-type or a single-type Pokémon is active. " + cdSeconds("premier") + " Obtained by purchasing a lot of Poké Balls from the shop and Pyramid.", luxury: "A comfortable Poké Ball with an increased catch rate that is said to make one wealthy. " + cdSeconds("luxury") + " Obtained from Arborist and Itemfinder.", myth: "An ancient Poké Ball that ignores modern era catch modifiers. Said to be particularly effective against rare Pokémon. " + cdSeconds("myth") + " Obtained from Arborist.", quick: "A somewhat different Poké Ball that tends to get better priority during throws. " + cdSeconds("quick") + " Obtained from Arborist and Pyramid.", level: "A slickly designed Poké Ball that raises the stat levels of the lead Pokémon. " + cdSeconds("level") + " Obtained from Arborist and Pyramid.", clone: "A mysterious Poké Ball with a very low catch rate that can duplicate a pokémon's D.N.A. " + cdSeconds("clone") + " Obtained from Arborist and Pyramid.", spy: "A stealthy Poké Ball that cannot be tracked. A successful snag with this ball allows for quick follow-up action, but it has low priority. " + cdSeconds("spy") + ". Obtained from Arborist and Pyramid.", mono: "A monochromatic Poké Ball that enables your active Pokémon to use only one of their types. " + cdSeconds("mono") + " Obtained from Arborist.", lightning: "A Poké Ball with a lightning bolt design that comes out in a flash. " + cdSeconds("lightning") + " Obtained from Arborist.", heavy: "An industrial Poké Ball that works better against heavier Pokémon and takes type less into consideration. " + cdSeconds("heavy") + " Cooldown is decreased when throwing this Ball on heavier Pokémon. Obtained from Arborist.", photo: "A Poké Ball riddled with memory chips capable of identifying Pokémon stored in the camera and catching them with higher likelihood. " + cdSeconds("photo") + " Obtained from Arborist.", mirror: "A Poké Ball with a reflective surface that enables the lead Pokémon to catch based on its similarities to the wild. Doubly effective in Similarity Mode. " + cdSeconds("mirror") + " Obtained from Arborist.", love: "A Poké Ball with a pink heart design that works better if the lead is in the same egg group as the target. It also increases the well-being of Pokémon in the daycare. " + cdSeconds("love") + " Obtained from Arborist.", uturn: "A Poké Ball with a dynamic design that enables the lead Pokémon to switch out after a successful catch. Catch rate is increased if the lead Pokémon is capable of learning " + readable([369, 521, 754, 226, 100, 600, 822, 823].map(moveOff), "or") + ". " + cdSeconds("uturn") + " Obtained from Arborist.", inver: "A mysterious Poké Ball that reverses the type advantage " + cdSeconds("inver") + " Obtained from Arborist.", spirit: "A magical Poké Ball that can capture the Spirits of Pokémon. " + cdSeconds("spirit") + " Obtained during Spirit Duels events. This Ball will return to you if it fails to catch a Pokémon. (Max capacity: 10)", cherish: "A homey Poké Ball that forever marks the caught Pokémon as being cherished by its owner. " + cdSeconds("cherish") + " Obtained from Arborist. This Ball will return to you if it fails to catch a Pokémon." }; berryHelp = { oran:"A berry that can be used as an ingredient in bait. Can also restore 50 HP to the holder during rotation batttles. Give berries with /giveitem [berry].", pecha:"A berry that can be used as an ingredient in bait. Can also help a Pokémon overcome a nerf in Contests. Give berries with /giveitem [berry].", razz:"A berry that can be used as an ingredient in bait. No known use yet. Give berries with /giveitem [berry].", bluk:"A berry that can be used as an ingredient in bait. No known use yet. Give berries with /giveitem [berry].", leppa:"A berry that can be used as an ingredient in bait. No known use yet. Give berries with /giveitem [berry].", tamato:"An uncommon berry that can be used as an ingredient in bait. No known use yet. Give berries with /giveitem [berry].", pinap:"An uncommon berry that can be used as an ingredient in bait. Allows its holder to fight better in the Battle Tower if its BST is unique on its team. Give berries with /giveitem [berry].", nanab:"An uncommon berry that can be used as an ingredient in bait. No known use yet. Give berries with /giveitem [berry].", watmel:"An uncommon berry that can be used as an ingredient in bait. When hatching a Pokémon that shares an Egg Group with its holder, it increases the chance of the hatched Pokémon to be Shiny. If multiple Pokémon in your party are holding this berry and have the same Egg Group as the hatched Pokémon, only the first holder's berry will take effect. Give berries with /giveitem [berry].", petaya:"An uncommon berry that can be used as an ingredient in bait. Can help a Pokémon evolve when it assists in captures. Give berries with /giveitem [berry].", miracle:"A rare berry that can be used as an ingredient in bait. Allows its holder to survive a KO-ing move in rotation battles. Give berries with /giveitem [berry].", platinum:"A rare berry that can be used as an ingredient in bait. As an ingredient, it is unmatched, and it can cause the bait to attract rare Pokémon forms." }; allBalls = ["safari", "great", "ultra", "myth", "luxury", "quick", "heavy", "spy", "clone", "premier", "mono", "spirit", "lightning", "level", "photo", "mirror", "uturn", "love", "inver", "cherish", "master"]; }; updateItemHelp(); var currentItems = Object.keys(itemData); var retiredItems = []; var allItems = currentItems.concat(retiredItems, "permfinder"); var allCostumes = Object.keys(costumeData); var decorations = {}; var missionsData = {}; var trialsData = {}; var SECRET_BASE_WIDTH = 7; var SECRET_BASE_HEIGHT = 5; /* Wild Pokemon Variables */ var shinyChance = 1024; //Chance for Shiny Pokémon var currentPokemon = null; var currentTypeOverride = null; var spiritSpawn = false, wildSpirit = false; var canSpawnTera = false, wildTera = false; var currentExtraBST = 0; var currentPokemonCount = 1; var lastPokemonCount = 1; var currentDisplay = null; var currentDisplayBST = 0; var currentPokemonAction = null; var currentPokemonMood = null; var currentPokemonMoodRate = null; var wildAbilityBoost = 5; var wildAbilityBoostLimit = wildAbilityBoost * 5; var wildBallThrows = {}; var currentThrowers = []; var restrictedThrowers = []; var restrictedThrowBoost = 1.5; var restrictedThrowTime = 0; var restrictedThrowTimeLimit = 300; var restrictedThrowLimit = 5; var maxThrows = 10; var pokeblockThrows = 0; var currentThrows; var throwAttempts; var preparationPhase = 0; var preparationThrows = {}; var bufferThrows = {}; var preparationFirst = null; var rockScareThreshold = 15000; var lastWild = 0; var lastWildAction = 0; var wildEvent = false; var currentBaiter = null; var resolvingThrows = false; var catchTierChance = [0.20, 0.18, 0.14, 0.10, 0.065, 0.0275]; var tiers = ["SM LC", "SM PU", "SM NU", "SM LU", "SM UU", "SM OU", "SM Ubers"]; /* Leaderboard Variables */ var celebrityPKs = {}; var recentPlayers = {}; var leaderboards = {}; var monthlyLeaderboards = {}; var lastLeaderboards; var lastLeaderboardUpdate; //Adding a variable that already exists on player.records here will automatically make it available as a leaderboard //To add stuff not on player.records, you must add an exception on this.updateLeaderboards() var leaderboardTypes = { // totalPokes: { desc: "by Pokémon owned", alts: ["totalpokes"], alias: "owned" }, pokesCaught: { desc: "by successful catches", alts: ["caught", "pokescaught"], alias: "caught" }, // pokesEvolved: { desc: "by successful evolutions", alts: ["evolve", "evolved", "pokesevolved"], alias: "evolved" }, // bst: { desc: "by total BST of Pokémon owned", alts: [], alias: "bst" }, contestsWon: { desc: "by Contests won", alts: ["contest", "contests", "contestswon"], alias: "contest" }, earnings: { desc: "by money earned from NPC, quests, events and Luxury Balls", alts: ["earned", "earnings", "money", "$"], alias: "earnings", isMoney: true }, // money: { desc: "by money", alts: ["$"], alias: "money", isMoney: true }, // pokeSoldEarnings: { desc: "by money gained with selling Pokémon", alts: ["sold", "sell", "pokesoldearnings"], alias: "sold", isMoney: true }, // luxuryEarnings: { desc: "by money gained with Luxury Balls", alts: ["luxury", "luxuryball", "luxury ball", "luxuryearnings"], alias: "luxury", isMoney: true }, // consecutiveLogins: { desc: "by longest streak of consecutive days login", alts: ["login", "logins", "consecutivelogins"], alias: "login" }, pokesCloned: { desc: "by successful clones", alts: ["clone", "clones", "cloned", "clone ball", "pokescloned"], alias: "cloned" }, // gachasUsed: { desc: "by tickets used for Gachapon", alts: ["gacha", "gachasused"], alias: "gacha" }, // itemsFound: { desc: "by items found with Itemfinder", alts: ["found", "itemsfound", "items found"], alias: "found" }, collectorEarnings: { desc: "by money received from the Collector", alts: ["collector", "collector money", "collectormoney", "collector $", "collectorearnings"], alias: "collector", isMoney: true }, // collectorGiven: { desc: "by Pokémon given to the Collector", alts: ["collector", "collector pokémon", "collectorpokémon", "collector pokemon", "collector poke", "collectorpoke", "collectorgiven"], alias: "collector" }, towerHighest: { desc: "by best old Battle Tower run", alts: ["old tower", "oldbattletower", "old battle tower", "oldtowerhighest"], alias: "old tower" }, towerHighestNew: { desc: "by best Battle Tower run", alts: ["tower", "battletower", "battle tower", "towerhighest"], alias: "tower" }, arenaPoints: { desc: "by Arena points", alts: ["points", "arena", "arenapoints", "arena points"], alias: "arena points" }, salt: { desc: "by saltiest players", alts: ["salt", "salty"], alias: "salt" }, pokesStolen: { desc: "by Pokémon stolen from NPCs", alts: ["stolen", "pokesstolen"], alias: "stolen" }, // topQuizScore: { desc: "by best score in Quiz", alts: ["quiz", "score", "quizscore", "quiz score", "topquizscore"], alias: "quiz" }, celebrityScore: { desc: "by best Normal Celebrity score", alts: ["celebrity", "celebrityscore", "celebrity score", "celeb", "celebrity normal", "celebrityscore normal", "celebrity score normal", "celeb normal"], alias: "celebrity" }, celebrityScoreEasy: { desc: "by best Easy Celebrity score", alts: ["celebrity easy", "celebrityscore easy", "celebrity score easy", "celeb easy"], alias: "celebrity easy" }, celebrityScoreHard: { desc: "by best Hard Celebrity score", alts: ["celebrity hard", "celebrityscore hard", "celebrity score hard", "celeb hard"], alias: "celebrity hard" }, celebrityScoreExpert: { desc: "by best Expert Celebrity score", alts: ["celebrity expert", "celebrityscore expert", "celebrity score expert", "celeb expert"], alias: "celebrity expert" }, celebrityScoreSuperExpert: { desc: "by best Super Expert Celebrity score", alts: ["celebrity super expert", "celebrityscore super expert", "celebrity score super expert", "celeb super expert"], alias: "celebrity super expert" }, celebrityScoreAbyssal: { desc: "by best Abyssal Celebrity score", alts: ["celebrity abyssal", "celebrityscore abyssal", "celebrity score abyssal", "celeb abyssal"], alias: "celebrity abyssal" }, pyramidScore: { desc: "by best Pyramid score", alts: ["pyramid", "pyramidscore", "pyramid score", "pyr"], alias: "pyramid" }, // pyramidTotalScore: { desc: "by total Pyramid points", alts: ["pyramidtotal", "pyramid total", "pyramidtotalscore"], alias: "pyramid total" }, pyramidFinished: { desc: "by cleared Pyramid runs", alts: ["pyramidfinished", "pyramid finished"], alias: "pyramid finished" }, eliteCleared: { desc: "by cleared Elite Four challenges", alts: ["elite", "elite four", "elitefour", "elite4", "e4", "elite 4", "e 4", "elite cleared", "elite four cleared", "elite 4 cleared"], alias: "elite four cleared" }, baseValue: { desc: "by most valuable Secret Base", alts: ["base", "secretbase", "secret base",], alias: "base" }, journalPoints: { desc: "by most photo points", alts: ["photos", "photo", "photo points", "photo points"], alias: "photo" }, missionPoints: { desc: "by most mission points earned", alts: ["missions", "mission", "mission points", "mission point"], alias: "mission" }, casesSolved: { desc: "by most Detective cases solved", alts: ["detective", "cases", "detective cases", "detective solved", "detectivesolved", "cases solved", "casessolved"], alias: "detective" }, fastestCaseSolved: { desc: "by fastest Detective case solved", alts: ["detective fastest", "detectivefastest", "detective speed", "detectivespeed", "detective speedrun", "detectivespeedrun"], alias: "detective fastest" } }; var monthlyLeaderboardTypes = { pokesCaught: { desc: "by successful catches during this week", alts: ["caught weekly"], alias: "caught weekly", lastAlias: "caught last", file: "scriptdata/safari/weeklyPokesCaught.txt", lastDesc: "by successful catches during the last week", reward: true }, contestsWon: { desc: "by Contests won during this week", alts: ["contest weekly", "contests weekly"], alias: "contest weekly", lastAlias: "contest last", file: "scriptdata/safari/weeklyContestsWon.txt", lastDesc: "by Contests won during the last week", reward: true }, collectorEarnings: { desc: "by money received from the Collector during this week", alts: ["collector weekly", "collector money weekly", "collectormoney weekly", "collector $ weekly"], alias: "collector weekly", lastAlias: "collector last",isMoney: true, file: "scriptdata/safari/weeklyCollectorEarnings.txt", lastDesc: "by money received from the Collector during the last week", reward: true }, arenaPoints: { desc: "by Arena points won this week", alts: ["arena weekly"], alias: "arena weekly", lastAlias: "arena last", file: "scriptdata/safari/weeklyArenaPoints.txt", lastDesc: "by Arena points won during the last week", reward: true }, towerHighestNew: { desc: "by best Battle Tower run this week", alts: ["tower weekly", "battletower weekly", "battle tower weekly", "towerhighest weekly"], alias: "tower weekly", lastAlias: "tower last", file: "scriptdata/safari/weeklyTowerHighest.txt", lastDesc: "by best Battle Tower run during the last week", reward: true }, journalPoints: { desc: "by Photo points won this week", alts: ["photo weekly", "journal weekly"], alias: "photo weekly", lastAlias: "photo last", file: "scriptdata/safari/weeklyPhotoPoints.txt", lastDesc: "by Photo points won during the last week", reward: true }, pyramidScore: { desc: "by Pyramid score this week", alts: ["pyramid weekly", "pyr weekly"], alias: "pyramid weekly", lastAlias: "pyramid last", file: "scriptdata/safari/weeklyPyramidScore.txt", lastDesc: "by Pyramid points won during the last week", reward: true }, celebrityScore: { desc: "by best Normal Celebrity score this week", alts: ["celebrity weekly", "celebrityscore weekly", "celebrity score weekly", "celeb weekly", "celebrity normal weekly", "celebrityscore normal weekly", "celebrity score normal weekly", "celeb normal weekly"], alias: "celebrity weekly", lastAlias: "celebrity normal last", file: "scriptdata/safari/weeklyCelebrityScore.txt", lastDesc: "by best Normal Celebrity score during the last week", reward: true }, celebrityScoreEasy: { desc: "by best Easy Celebrity score this week", alts: ["celebrity easy weekly", "celebrityscore easy weekly", "celebrity score easy weekly", "celeb easy weekly"], alias: "celebrity easy weekly", lastAlias: "celebrity easy last", file: "scriptdata/safari/weeklyCelebrityScoreEasy.txt", lastDesc: "by best Easy Celebrity score during the last week", reward: false }, celebrityScoreHard: { desc: "by best Hard Celebrity score this week", alts: ["celebrity hard weekly", "celebrityscore hard weekly", "celebrity score hard weekly", "celeb hard weekly"], alias: "celebrity hard weekly", lastAlias: "celebrity hard last", file: "scriptdata/safari/weeklyCelebrityScoreHard.txt", lastDesc: "by best Hard Celebrity score during the last week", reward: true }, celebrityScoreExpert: { desc: "by best Expert Celebrity score this week", alts: ["celebrity expert weekly", "celebrityscore expert weekly", "celebrity score expert weekly", "celeb expert weekly"], alias: "celebrity expert weekly", lastAlias: "celebrity expert last", file: "scriptdata/safari/weeklyCelebrityScoreExpert.txt", lastDesc: "by best Expert Celebrity score during the last week", reward: true }, celebrityScoreSuperExpert: { desc: "by best Super Expert Celebrity score this week", alts: ["celebrity super expert weekly", "celebrityscore super expert weekly", "celebrity score super expert weekly", "celeb super expert weekly"], alias: "celebrity super expert weekly", lastAlias: "celebrity super expert last", file: "scriptdata/safari/weeklyCelebrityScoreSuperExpert.txt", lastDesc: "by best Super Expert Celebrity score during the last week", reward: true }, celebrityScoreAbyssal: { desc: "by best Abyssal Celebrity score this week", alts: ["celebrity abyssal weekly", "celebrityscore abyssal weekly", "celebrity score abyssal weekly", "celeb abyssal weekly"], alias: "celebrity abyssal weekly", lastAlias: "celebrity abyssal last", file: "scriptdata/safari/weeklyCelebrityScoreAbyssal.txt", lastDesc: "by best Abyssal Celebrity score during the last week", reward: true } }; /* Contest Variables */ var contestDuration = 300; //Contest lasts for 5 minutes var contestCount = 0; var contestExtension = 0; var contestExtensionLimit = 600; // event extension limit, 10 minutes var contestMidPoint = false; //whether the contest is half over (checked after wild caught); var contestCatchers = {}; var contestActivity = {}; var contestComboPlayers = []; var contestBroadcast = true; //Determines whether Tohjo gets notified var contestCooldownLength = 1800; //1 contest every 30 minutes var contestCooldown = (SESSION.global() && SESSION.global().safariContestCooldown ? SESSION.global().safariContestCooldown : contestCooldownLength); var contestForfeited = []; var contestantsCount = {}; var contestantsWild = []; var contestCombo = 0; var currentTheme; var currentThemeAlter; var currentThemeEffect; var currentThemeFlavor; var currentThemeSecondary; var chosenThemes; var contestVotes; var contestVotingCooldown = 4; var contestVotingCount = (SESSION.global() && SESSION.global().contestVotingCount ? SESSION.global().contestVotingCount : contestVotingCooldown); var generations = ["None", "Kanto", "Johto", "Hoenn", "Sinnoh", "Unova", "Kalos", "Alola", "Galar", "Paldea"]; var nextTheme; var currentRules; var nextRules; var RULES_NERF = 0.95; var RULES_BUFF = 0.25; var RULES_NERF_CAP = 0.5; var defaultRules = { "onlyTypes": { //Picks one of the random sets and excludes all types not in that array "chance": 0, "sets": [ ["Water", "Fire", "Grass"], ["Dark", "Fighting", "Psychic"] ] }, "excludeTypes": { //onlyTypes has priority over excludeTypes; if a set from onlyTypes is used, excludeTypes will be skipped "Normal" : 0.015, "Fighting" : 0.06, "Flying" : 0.06, "Poison" : 0.06, "Ground" : 0.06, "Rock" : 0.06, "Bug" : 0.06, "Ghost" : 0.06, "Steel" : 0.06, "Fire" : 0.06, "Water" : 0.06, "Grass" : 0.06, "Electric" : 0.06, "Psychic" : 0.06, "Ice" : 0.06, "Dragon" : 0.06, "Dark" : 0.06, "Fairy" : 0.06 }, "bonusTypes": { //Excluded types have priority over bonus types. If a type is picked by both, it will only count for exclude. "Normal" : 0.06, "Fighting" : 0.06, "Flying" : 0.06, "Poison" : 0.06, "Ground" : 0.06, "Rock" : 0.06, "Bug" : 0.06, "Ghost" : 0.06, "Steel" : 0.06, "Fire" : 0.06, "Water" : 0.06, "Grass" : 0.06, "Electric" : 0.06, "Psychic" : 0.06, "Ice" : 0.06, "Dragon" : 0.06, "Dark" : 0.06, "Fairy" : 0.06 }, "excludeBalls": { "safari": 0, "great": 0.02, "ultra": 0.08, "master": 0.10, "myth": 0.125, "luxury": 0.125, "quick": 0.15, "level": 0.125, "spy": 0.125, "clone": 0.125, "premier": 0.125, "mono": 0.10 }, "buffBalls": {}, "bst": { //Both min and max are optional. It's possible to have only one of them in this object "minChance": 0, "min": [230, 400], "maxChance": 0.12, "max": [490, 535] }, "noLegendaries": { "chance": 0.07 }, "shiny": { "nerf": 0, "buff": 0.05 }, "singleType": { "nerf": 0, "buff": 0.13 }, "dualType": { "nerf": 0, "buff": 0 }, "inver": { "chance": 0.12 }, "invertedBST": { "chance": 0.08 }, "defensive": { "chance": 0.10 }, "generation": { "kanto": {"nerf": 0.03, "buff": 0.03}, "johto": {"nerf": 0.03, "buff": 0.03}, "hoenn": {"nerf": 0.03, "buff": 0.03}, "sinnoh": {"nerf": 0.03, "buff": 0.03}, "unova": {"nerf": 0.03, "buff": 0.03}, "kalos": {"nerf": 0.03, "buff": 0.03} }, "color": { "red": {"nerf": 0.025, "buff": 0.025}, "blue": {"nerf": 0.025, "buff": 0.025}, "green": {"nerf": 0.025, "buff": 0.025}, "yellow": {"nerf": 0.025, "buff": 0.025}, "purple": {"nerf": 0.025, "buff": 0.025}, "pink": {"nerf": 0.025, "buff": 0.025}, "brown": {"nerf": 0.025, "buff": 0.025}, "black": {"nerf": 0.025, "buff": 0.025}, "gray": {"nerf": 0.025, "buff": 0.025}, "white": {"nerf": 0.025, "buff": 0.025} }, "rewards": { "sets": { "defaultSet": { "gacha": 10 }, "whtapricorn": { "gacha": 10, "whtapricorn": 10 }, "blkapricorn": { "gacha": 10, "blkapricorn": 6 }, "grnapricorn": { "gacha": 10, "grnapricorn": 10 }, "bluapricorn": { "gacha": 10, "bluapricorn": 8 }, "ylwapricorn": { "gacha": 10, "ylwapricorn": 12 }, "redapricorn": { "gacha": 10, "redapricorn": 6 }, "pnkapricorn": { "gacha": 10, "pnkapricorn": 8 }, "silver": { "gacha": 15, "silver": 3 }, "gem": { "gacha": 10, "gem": 2 }, "nugget": { "gacha": 10, "nugget": 1 }, "dust": { "gacha": 10, "rare": 1 }, "rock": { "gacha": 15, "rock": 20 }, "fragment": { "fragment": 1 } }, "chance": { "whtapricorn": 0.11, "blkapricorn": 0.11, "bluapricorn": 0.11, "pnkapricorn": 0.11, "ylwapricorn": 0.10, "grnapricorn": 0.10, "redapricorn": 0.10, "silver": 0.05, "gem": 0.05, "nugget": 0.05, "dust": 0.05, "rock": 0.05, "fragment": 0.01 } } }; /* Theme Syntax: forest: { types: ["Grass", "Bug"], //Types that will be included. Pokémon only needs to match one of these types excludeTypes: [], //Types that will be excluded even if it matches the type above include: [16, 17, 18, 25, 163, 164], //Pokémon that do not match any of the criteria above, but will be included anyway exclude: [492, 649], //Pokémon that matches all of the previous criteria, but will be excluded anyway, editBST: { "289": 600 }, //Makes a pokémon count as a different BST for this theme. In the example, Pokémon #289 (Slaking) will be considered a 600 BST Pokémon for this theme. ceilBST: 600, //Choose a different maximum BST for pokémon to spawn. Optional, defaults to 600. floorBST: 300 //Choose a different minimum BST for pokémon to spawn. Optional, defaults to 300. } */ var contestThemes = {}; /* Events Variables */ var currentEvent; var currentGame; var stopEventJoins = false; var watchCountMax = 3; /* Bait Variables */ var lastBaiters = []; var lastBaitersAmount = 3; //Amount of people that need to bait before you can var lastBaitersDecay = lastBaitersDecayTime = 15; //Seconds before the first entry in lastBaiters is purged var successfulBaitCount = 15; var nextGachaSpawn = 0; var baitCooldownLength = 0; var baitCooldown = (SESSION.global() && SESSION.global().safariBaitCooldown ? SESSION.global().safariBaitCooldown : baitCooldownLength); var releaseCooldownLength = 180; //1 release every 3 minutes var releaseCooldown = (SESSION.global() && SESSION.global().safariReleaseCooldown ? SESSION.global().safariReleaseCooldown : releaseCooldownLength); var goldenBaitCooldown = (SESSION.global() && SESSION.global().safariGoldenBaitCooldown ? SESSION.global().safariGoldenBaitCooldown: 0); var deluxeBaitCooldown = (SESSION.global() && SESSION.global().safariDeluxeBaitCooldown? SESSION.global().safariDeluxeBaitCooldown: 0); /* Pokemon Variables */ var effectiveness = { "Stellar": { "Normal": 1.2, "Fighting": 1.2, "Flying": 1.2, "Poison": 1.2, "Ground": 1.2, "Rock": 1.2, "Bug": 1.2, "Ghost": 1.2, "Steel": 1.2, "Fire": 1.2, "Water": 1.2, "Grass": 1.2, "Electric": 1.2, "Psychic": 1.2, "Ice": 1.2, "Dragon": 1.2, "Dark": 1.2, "Fairy": 1.2 }, "Normal": { "Rock": 0.5, "Ghost": 0, "Steel": 0.5 }, "Fighting": { "Normal": 2, "Flying": 0.5, "Poison": 0.5, "Rock": 2, "Bug": 0.5, "Ghost": 0, "Steel": 2, "Psychic": 0.5, "Ice": 2, "Dark": 2, "Fairy": 0.5, "Stellar": 2 }, "Flying": { "Fighting": 2, "Rock": 0.5, "Bug": 2, "Steel": 0.5, "Grass": 2, "Electric": 0.5 }, "Poison": { "Poison": 0.5, "Ground": 0.5, "Rock": 0.5, "Ghost": 0.5, "Steel": 0, "Grass": 2, "Fairy": 2 }, "Ground": { "Flying": 0, "Poison": 2, "Rock": 2, "Bug": 0.5, "Steel": 2, "Fire": 2, "Grass": 0.5, "Electric": 2 }, "Rock": { "Fighting": 0.5, "Flying": 2, "Ground": 0.5, "Bug": 2, "Steel": 0.5, "Fire": 2, "Ice": 2 }, "Bug": { "Fighting": 0.5, "Flying": 0.5, "Poison": 0.5, "Ghost": 0.5, "Steel": 0.5, "Fire": 0.5, "Grass": 2, "Psychic": 2, "Dark": 2, "Fairy": 0.5 }, "Ghost": { "Normal": 0, "Ghost": 2, "Psychic": 2, "Dark": 0.5, "Stellar": 0 }, "Steel": { "Rock": 2, "Steel": 0.5, "Fire": 0.5, "Water": 0.5, "Electric": 0.5, "Ice": 2, "Fairy": 2 }, "Fire": { "Rock": 0.5, "Bug": 2, "Steel": 2, "Fire": 0.5, "Water": 0.5, "Grass": 2, "Ice": 2, "Dragon": 0.5 }, "Water": { "Ground": 2, "Rock": 2, "Fire": 2, "Water": 0.5, "Grass": 0.5, "Dragon": 0.5 }, "Grass": { "Flying": 0.5, "Poison": 0.5, "Ground": 2, "Rock": 2, "Bug": 0.5, "Steel": 0.5, "Fire": 0.5, "Water": 2, "Grass": 0.5, "Dragon": 0.5 }, "Electric": { "Flying": 2, "Ground": 0, "Water": 2, "Grass": 0.5, "Electric": 0.5, "Dragon": 0.5 }, "Psychic": { "Fighting": 2, "Poison": 2, "Steel": 0.5, "Psychic": 0.5, "Dark": 0 }, "Ice": { "Flying": 2, "Ground": 2, "Steel": 0.5, "Fire": 0.5, "Water": 0.5, "Grass": 2, "Ice": 0.5, "Dragon": 2 }, "Dragon": { "Steel": 0.5, "Dragon": 2, "Fairy": 0 }, "Dark": { "Fighting": 0.5, "Ghost": 2, "Psychic": 2, "Dark": 0.5, "Fairy": 0.5 }, "Fairy": { "Fighting": 2, "Poison": 0.5, "Steel": 0.5, "Fire": 0.5, "Dragon": 2, "Dark": 2 } }; var immuneMultiplier = 0.15; var pokeInfo = { species: function(poke) { return poke & ((1 << 16) - 1); }, forme: function(poke) { return poke >> 16; }, shiny: function(poke) { return typeof poke === "string"; }, readableNum: function(poke) { var ret = []; ret += pokeInfo.species(poke); if (pokeInfo.forme(poke) > 0) { ret += "-"; ret += pokeInfo.forme(poke); } return ret; }, icon: function(p, shinyBG, noLink) { var p2 = p; var pcheck = p2%65536; var uic; var ret; //Pumpkaboo/Gourgeist icon hack if (pcheck === 710 || pcheck === 711) { p2 = pcheck; } if (ultraPokes.hasOwnProperty(p2+"")) { var species = pokeInfo.species(p2), form = pokeInfo.forme(p2); var key = species + (form > 0 ? "-" + form : ""); uic = resources.icons.get(key); if (!(uic)) { uic = 'icon:' + p2%65536; } ret = ''; } else { ret = ''; } return noLink ? ret : "" + ret + ""; }, sprite: function(pk, noLink) { var shiny = pokeInfo.shiny(pk); var ret; if (ultraPokes.hasOwnProperty(pk+"")) { var species = pokeInfo.species(pk), form = pokeInfo.forme(pk); var key = species + (form > 0 ? "-" + form : ""); if (shiny) { ret = ""; } else { ret = ""; } return noLink ? ret : "" + ret + ""; } ret = []; ret += "" : ""; } else if (pk === "66337") { return noLink ? "" : ""; } else { ret += "&shiny=true"; } } // ret += "&gen=7'>"; /* Start of temporary hack due to windows client bug with shiny sprites. Enable the line above and remove this block once the client can properly show shiny sprites for non-gen 7 Pokémon*/ var g = 6, id = parseInt(pk, 10), sp = pokeInfo.species(id); var withback = false; if ((sp >= 722 && sp < 803) || [131730, 66254, 131790, 458777, 524313, 589849, 655385, 720921, 786457, 65555, 65556, 65562, 65563, 65564, 65573, 65574, 65586, 65587, 65588, 65589, 65610, 65611, 65612, 65624, 65625, 65639, 65641].contains(id)) { g = 7; if (shiny) { withback = false; } } // ret += "&gen="+g+"'>"; ret += "&gen="+g+(withback ? "&back=true" : "") +"'>"; /* End of temporary hack */ return noLink ? ret : "" + ret + ""; }, valid: function(pk) { if (ultraPokes.hasOwnProperty(pk+"")) { return true; } return pokePlain(pk) !== "Missingno"; }, calcForme: function(base, forme) { return parseInt(base,10) + parseInt(forme << 16, 10); } }; var wildForms = { "201": 27, "412": 2, "413": 2, "422": 1, "423": 1, "550": 1, "585": 3, "586": 3, "666": 17, "669": 4, "670": 4, "671": 4, "678": 1, "710": 3, "711": 3, "741": 3, "745": 1, "849": 1, "869": 62, "876": 1, "916": 1, "931": 3, "978": 2 }; var noShinySprite = [ // These Pokemon have no shiny sprites at all, so they should not be allowed to be shiny. This list includes Pikachu forms (except Pikachu-Cosplay), Castform forms, Rotom-Pokedex, Alcremie forms, and Eternamax Eternatus 65561,131097,196633,262169,327705,458777,524313,589849,655385,720921,786457,851993,65887,131423,262495,393695,66405,131941,197477,263013,328549,394085,459621,525157,590693,656229,721765,787301,852837,918373,983909,1049445,1114981,1180517,1246053,1311589,1377125,1442661,1508197,1573733,1639269,1704805,1770341,1835877,1901413,1966949,2032485,2098021,2163557,2229093,2294629,2360165,2425701,2491237,2556773,2622309,2687845,2753381,2818917,2884453,2949989,3015525,3081061,3146597,3212133,3277669,3343205,3408741,3474277,3539813,3605349,3670885,3736421,3801957,3867493,3933029,3998565,4064101,66426 ]; var contestMoves = {"1":"Tough","2":"Tough","3":"Cute","4":"Tough","5":"Tough","6":"Clever","7":"Tough","8":"Beautiful","9":"Cool","10":"Tough","11":"Tough","12":"Cool","13":"Cool","14":"Beautiful","15":"Cool","16":"Clever","17":"Cool","18":"Clever","19":"Clever","20":"Tough","21":"Tough","22":"Cool","23":"Tough","24":"Cool","25":"Cool","26":"Cool","27":"Cool","28":"Cute","29":"Tough","30":"Cool","31":"Cool","32":"Cool","33":"Tough","34":"Tough","35":"Tough","36":"Tough","37":"Tough","38":"Tough","39":"Cute","40":"Clever","41":"Cool","42":"Cool","43":"Cool","44":"Tough","45":"Cute","46":"Cool","47":"Cute","48":"Clever","49":"Cool","50":"Clever","51":"Clever","52":"Cute","53":"Beautiful","54":"Beautiful","55":"Cute","56":"Beautiful","57":"Beautiful","58":"Beautiful","59":"Beautiful","60":"Beautiful","61":"Beautiful","62":"Beautiful","63":"Cool","64":"Cool","65":"Cool","66":"Cool","67":"Tough","68":"Tough","69":"Tough","70":"Tough","71":"Clever","72":"Clever","73":"Clever","74":"Beautiful","75":"Cool","76":"Cool","77":"Clever","78":"Clever","79":"Clever","80":"Beautiful","81":"Clever","82":"Cool","83":"Beautiful","84":"Cool","85":"Cool","86":"Cool","87":"Cool","88":"Tough","89":"Tough","90":"Tough","91":"Tough","92":"Clever","93":"Clever","94":"Clever","95":"Clever","96":"Beautiful","97":"Cool","98":"Cool","99":"Tough","100":"Cool","101":"Clever","102":"Cute","103":"Clever","104":"Cool","105":"Clever","106":"Tough","107":"Cute","108":"Clever","109":"Clever","110":"Cute","111":"Cute","112":"Cool","113":"Beautiful","114":"Beautiful","115":"Clever","116":"Cool","117":"Tough","118":"Cute","119":"Clever","120":"Beautiful","121":"Cute","122":"Cute","123":"Tough","124":"Tough","125":"Tough","126":"Beautiful","127":"Tough","128":"Tough","129":"Cool","130":"Tough","131":"Cool","132":"Tough","133":"Cute","134":"Clever","135":"Cute","136":"Cool","137":"Tough","138":"Clever","139":"Clever","140":"Cute","141":"Clever","142":"Beautiful","143":"Cool","144":"Clever","145":"Cute","146":"Cool","147":"Beautiful","148":"Beautiful","149":"Clever","150":"Cute","151":"Tough","152":"Tough","153":"Beautiful","154":"Tough","155":"Tough","156":"Cute","157":"Tough","158":"Cool","159":"Cute","160":"Beautiful","161":"Beautiful","162":"Tough","163":"Cool","164":"Cute","165":"Tough","166":"Clever","167":"Cool","168":"Tough","169":"Clever","170":"Clever","171":"Clever","172":"Beautiful","173":"Cute","174":"Tough","175":"Cute","176":"Beautiful","177":"Cool","178":"Beautiful","179":"Cool","180":"Tough","181":"Beautiful","182":"Cute","183":"Cool","184":"Tough","185":"Clever","186":"Cute","187":"Cute","188":"Tough","189":"Cute","190":"Tough","191":"Clever","192":"Cool","193":"Clever","194":"Clever","195":"Beautiful","196":"Beautiful","197":"Cool","198":"Tough","199":"Clever","200":"Cool","201":"Tough","202":"Clever","203":"Tough","204":"Cute","205":"Cute","206":"Cool","207":"Cute","208":"Cute","209":"Cool","210":"Cool","211":"Cool","212":"Beautiful","213":"Cute","214":"Cute","215":"Beautiful","216":"Cute","217":"Cute","218":"Cute","219":"Beautiful","220":"Clever","221":"Beautiful","222":"Tough","223":"Cool","224":"Cool","225":"Cool","226":"Cute","227":"Cute","228":"Clever","229":"Cool","230":"Cute","231":"Cool","232":"Cool","233":"Cool","234":"Beautiful","235":"Clever","236":"Beautiful","237":"Clever","238":"Cool","239":"Cool","240":"Beautiful","241":"Beautiful","242":"Tough","243":"Beautiful","244":"Clever","245":"Cool","246":"Tough","247":"Clever","248":"Clever","249":"Tough","250":"Beautiful","251":"Clever","252":"Cute","253":"Cute","254":"Tough","255":"Tough","256":"Tough","257":"Beautiful","258":"Beautiful","259":"Tough","260":"Clever","261":"Beautiful","262":"Tough","263":"Cute","264":"Tough","265":"Tough","266":"Cute","267":"Beautiful","268":"Clever","269":"Clever","270":"Clever","271":"Clever","272":"Cute","273":"Cute","274":"Cute","275":"Clever","276":"Tough","277":"Beautiful","278":"Clever","279":"Tough","280":"Cool","281":"Cute","282":"Clever","283":"Tough","284":"Beautiful","285":"Clever","286":"Clever","287":"Cute","288":"Tough","289":"Clever","290":"Clever","291":"Beautiful","292":"Tough","293":"Clever","294":"Beautiful","295":"Clever","296":"Clever","297":"Beautiful","298":"Cute","299":"Beautiful","300":"Cute","301":"Beautiful","302":"Clever","303":"Cute","304":"Cool","305":"Clever","306":"Cool","307":"Beautiful","308":"Beautiful","309":"Cool","310":"Clever","311":"Beautiful","312":"Clever","313":"Cute","314":"Cool","315":"Beautiful","316":"Clever","317":"Clever","318":"Beautiful","319":"Clever","320":"Clever","321":"Cute","322":"Cool","323":"Beautiful","324":"Beautiful","325":"Clever","326":"Cool","327":"Cool","328":"Clever","329":"Beautiful","330":"Tough","331":"Cool","332":"Cool","333":"Beautiful","334":"Tough","335":"Cute","336":"Cool","337":"Cool","338":"Cool","339":"Beautiful","340":"Cute","341":"Tough","342":"Clever","343":"Cute","344":"Cool","345":"Beautiful","346":"Cute","347":"Clever","348":"Cool","349":"Cool","350":"Tough","351":"Cool","352":"Beautiful","353":"Cool","354":"Clever","355":"Cool","356":"Clever","357":"Cute","358":"Tough","359":"Tough","360":"Beautiful","361":"Cute","362":"Tough","363":"Cool","364":"Clever","365":"Cute","366":"Cool","367":"Tough","368":"Beautiful","369":"Cute","370":"Tough","371":"Tough","372":"Beautiful","373":"Cute","374":"Tough","375":"Cool","376":"Cool","377":"Cute","378":"Tough","379":"Cool","380":"Tough","381":"Cute","382":"Cute","383":"Cool","384":"Beautiful","385":"Cute","386":"Cool","387":"Cute","388":"Beautiful","389":"Clever","390":"Clever","391":"Cool","392":"Beautiful","393":"Cute","394":"Cool","395":"Cool","396":"Beautiful","397":"Tough","398":"Tough","399":"Cool","400":"Beautiful","401":"Cute","402":"Tough","403":"Cool","404":"Beautiful","405":"Cute","406":"Clever","407":"Tough","408":"Beautiful","409":"Tough","410":"Cool","411":"Cool","412":"Beautiful","413":"Cute","414":"Clever","415":"Cool","416":"Tough","417":"Cute","418":"Tough","419":"Cool","420":"Beautiful","421":"Cute","422":"Cool","423":"Cool","424":"Beautiful","425":"Clever","426":"Clever","427":"Cool","428":"Clever","429":"Cute","430":"Clever","431":"Tough","432":"Beautiful","433":"Cute","434":"Clever","435":"Cool","436":"Tough","437":"Cute","438":"Tough","439":"Tough","440":"Cool","441":"Tough","442":"Tough","443":"Cool","444":"Tough","445":"Beautiful","446":"Cool","447":"Clever","448":"Clever","449":"Beautiful","450":"Tough","451":"Beautiful","452":"Tough","453":"Beautiful","454":"Clever","455":"Clever","456":"Clever","457":"Tough","458":"Clever","459":"Cool","460":"Beautiful","461":"Beautiful","462":"Tough","463":"Tough","464":"Clever","465":"Cool","466":"Beautiful","467":"Cool","468":"Cute","469":"Tough","470":"Clever","471":"Clever","472":"Clever","473":"Beautiful","474":"Beautiful","475":"Beautiful","476":"Clever","477":"Clever","478":"Clever","479":"Tough","480":"Cool","481":"Beautiful","482":"Tough","483":"Beautiful","484":"Tough","485":"Clever","486":"Cool","487":"Cute","488":"Cool","489":"Tough","490":"Clever","491":"beautiful","492":"Clever","493":"Cute","494":"Cute","495":"Cute","496":"Beautiful","497":"beautiful","498":"Tough","499":"Beautiful","500":"Clever","501":"Cool","502":"Clever","503":"Tough","504":"Tough","505":"Beautiful","506":"Clever","507":"Tough","508":"Clever","509":"Cool","510":"Tough","511":"Clever","512":"Cool","513":"Clever","514":"Cool","515":"Tough","516":"Cool","517":"Beautiful","518":"Beautiful","519":"Beautiful","520":"Beautiful","521":"Cool","522":"Cute","523":"Tough","524":"Beautiful","525":"Tough","526":"Tough","527":"beautiful","528":"Tough","529":"Tough","530":"Tough","531":"Cute","532":"Tough","533":"Cool","534":"Cool","535":"Tough","536":"Cool","537":"Tough","538":"Cute","539":"Cool","540":"Cool","541":"Cute","542":"Tough","543":"Tough","544":"Clever","545":"Cool","546":"Cool","547":"beautiful","548":"beautiful","549":"beautiful","550":"Beautiful","551":"beautiful","552":"beautiful","553":"beautiful","554":"Beautiful","555":"Tough","556":"beautiful","557":"Cool","558":"beautiful","559":"Cool","560":"Cute","561":"Tough","562":"Clever","563":"Beautiful","564":"Cute","565":"Beautiful","566":"Tough","567":"Clever","568":"Beautiful","569":"Beautiful","570":"Beautiful","571":"Cute","572":"Beautiful","573":"Beautiful","574":"Tough","575":"Cute","576":"Cool","577":"Clever","578":"Cool","579":"Cute","580":"Cute","581":"Cute","582":"Cool","583":"Beautiful","584":"Tough","585":"Cute","586":"Cute","587":"Clever","588":"Clever","589":"Clever","590":"Clever","591":"Cool","592":"Beautiful","593":"Beautiful","594":"Beautiful","595":"Cool","596":"Beautiful","597":"Clever","598":"Cool","599":"Beautiful","600":"Cool","601":"Clever","602":"Tough","603":"Tough","604":"Tough","605":"Tough","606":"Clever","607":"Clever","608":"Cute","609":"Cute","610":"Cool","611":"Beautiful","612":"Beautiful","613":"Beautiful","614":"Clever","615":"Cute","616":"Beautiful","617":"Tough","618":"Beautiful","619":"Cool","620":"Beautiful","621":"Tough"}; var evolutions = { "1": { "evo": 2, "candies": 207 }, "2": { "evo": 3, "candies": 656 }, "65561": { "evo": -1, "candies": 0 }, "131097": { "evo": -1, "candies": 0 }, "196633": { "evo": -1, "candies": 0 }, "262169": { "evo": -1, "candies": 0 }, "327705": { "evo": -1, "candies": 0 }, "393241": { "evo": -1, "candies": 0 }, "458777": { "evo": -1, "candies": 0 }, "524313": { "evo": -1, "candies": 0 }, "589849": { "evo": -1, "candies": 0 }, "655385": { "evo": -1, "candies": 0 }, "720921": { "evo": -1, "candies": 0 }, "786457": { "evo": -1, "candies": 0 }, "851993": { "evo": -1, "candies": 0 }, "4": { "evo": 5, "candies": 207 }, "5": { "evo": 6, "candies": 668 }, "7": { "evo": 8, "candies": 207 }, "8": { "evo": 9, "candies": 663 }, "10": { "evo": 11, "candies": 105 }, "11": { "evo": 12, "candies": 332 }, "13": { "evo": 14, "candies": 105 }, "14": { "evo": 15, "candies": 332 }, "16": { "evo": 17, "candies": 178 }, "17": { "evo": 18, "candies": 402 }, "19": { "evo": 20, "candies": 347 }, "65555": { "evo": 65556, "candies": 347 }, "21": { "evo": 22, "candies": 371 }, "23": { "evo": 24, "candies": 368 }, "25": { "evo": 26, "candies": 407 }, "27": { "evo": 28, "candies": 378 }, "65563": { "evo": 65564, "candies": 378 }, "29": { "evo": 30, "candies": 186 }, "30": { "evo": 31, "candies": 424 }, "32": { "evo": 33, "candies": 186 }, "33": { "evo": 34, "candies": 424 }, "35": { "evo": 36, "candies": 406 }, "37": { "evo": 38, "candies": 424 }, "65573": { "evo": 65574, "candies": 424 }, "39": { "evo": 40, "candies": 365 }, "41": { "evo": 42, "candies": 232 }, "42": { "evo": 169, "candies": 449 }, "43": { "evo": 44, "candies": 201 }, "44": { "evo": [45, 182], "candies": 412 }, "46": { "evo": 47, "candies": 340 }, "48": { "evo": 49, "candies": 378 }, "50": { "evo": 51, "candies": 340 }, "65586": { "evo": 65587, "candies": 340 }, "52": { "evo": 53, "candies": 370 }, "65588": { "evo": 65589, "candies": 370 }, "54": { "evo": 55, "candies": 420 }, "56": { "evo": 57, "candies": 382 }, "58": { "evo": 59, "candies": 466 }, "60": { "evo": 61, "candies": 196 }, "61": { "evo": [62, 186], "candies": 424 }, "63": { "evo": 64, "candies": 204 }, "64": { "evo": 65, "candies": 575 }, "66": { "evo": 67, "candies": 207 }, "67": { "evo": 68, "candies": 424 }, "69": { "evo": 70, "candies": 199 }, "70": { "evo": 71, "candies": 412 }, "72": { "evo": 73, "candies": 433 }, "74": { "evo": 75, "candies": 199 }, "65610": { "evo": 65611, "candies": 199 }, "75": { "evo": 76, "candies": 416 }, "65611": { "evo": 65612, "candies": 416 }, "77": { "evo": 78, "candies": 420 }, "79": { "evo": [80, 199], "candies": 412 }, "81": { "evo": 82, "candies": 237 }, "82": { "evo": 462, "candies": 449 }, "84": { "evo": 85, "candies": 386 }, "86": { "evo": 87, "candies": 399 }, "88": { "evo": 89, "candies": 420 }, "65624": { "evo": 65625, "candies": 420 }, "90": { "evo": 91, "candies": 441 }, "92": { "evo": 93, "candies": 207 }, "93": { "evo": 94, "candies": 575 }, "95": { "evo": 208, "candies": 428 }, "96": { "evo": 97, "candies": 406 }, "98": { "evo": 99, "candies": 399 }, "100": { "evo": 101, "candies": 403 }, "102": { "evo": 103, "candies": 437 }, "104": { "evo": 105, "candies": 357 }, "108": { "evo": 463, "candies": 433 }, "109": { "evo": 110, "candies": 412 }, "111": { "evo": 112, "candies": 247 }, "112": { "evo": 464, "candies": 449 }, "113": { "evo": 242, "candies": 621 }, "114": { "evo": 465, "candies": 449 }, "116": { "evo": 117, "candies": 224 }, "117": { "evo": 230, "candies": 454 }, "118": { "evo": 119, "candies": 378 }, "120": { "evo": 121, "candies": 437 }, "123": { "evo": 212, "candies": 420 }, "125": { "evo": 466, "candies": 454 }, "126": { "evo": 467, "candies": 454 }, "129": { "evo": 130, "candies": 454 }, "133": { "evo": [470, 471, 135, 134, 136, 196, 197, 700], "candies": 656 }, "137": { "evo": 233, "candies": 263 }, "138": { "evo": 139, "candies": 416 }, "140": { "evo": 141, "candies": 416 }, "147": { "evo": 148, "candies": 214 }, "148": { "evo": 149, "candies": 699 }, "152": { "evo": 153, "candies": 207 }, "153": { "evo": 154, "candies": 656 }, "155": { "evo": 156, "candies": 207 }, "156": { "evo": 157, "candies": 668 }, "158": { "evo": 159, "candies": 207 }, "159": { "evo": 160, "candies": 663 }, "161": { "evo": 162, "candies": 349 }, "163": { "evo": 164, "candies": 371 }, "165": { "evo": 166, "candies": 328 }, "167": { "evo": 168, "candies": 328 }, "170": { "evo": 171, "candies": 386 }, "172": { "evo": 25, "candies": 163 }, "65708": { "evo": -1, "candies": 0 }, "173": { "evo": 35, "candies": 165 }, "174": { "evo": 39, "candies": 138 }, "175": { "evo": 176, "candies": 207 }, "176": { "evo": 468, "candies": 627 }, "177": { "evo": 178, "candies": 395 }, "179": { "evo": 180, "candies": 186 }, "180": { "evo": 181, "candies": 428 }, "183": { "evo": 184, "candies": 353 }, "187": { "evo": 188, "candies": 173 }, "188": { "evo": 189, "candies": 386 }, "190": { "evo": 424, "candies": 405 }, "191": { "evo": 192, "candies": 357 }, "193": { "evo": 469, "candies": 433 }, "194": { "evo": 195, "candies": 361 }, "198": { "evo": 430, "candies": 424 }, "200": { "evo": 429, "candies": 416 }, "204": { "evo": 205, "candies": 391 }, "207": { "evo": 472, "candies": 428 }, "209": { "evo": 210, "candies": 378 }, "215": { "evo": 461, "candies": 428 }, "216": { "evo": 217, "candies": 420 }, "218": { "evo": 219, "candies": 344 }, "220": { "evo": 221, "candies": 230 }, "221": { "evo": 473, "candies": 445 }, "223": { "evo": 224, "candies": 403 }, "228": { "evo": 229, "candies": 420 }, "231": { "evo": 232, "candies": 420 }, "233": { "evo": 474, "candies": 449 }, "236": { "evo": [107, 106, 237], "candies": 382 }, "238": { "evo": 124, "candies": 382 }, "239": { "evo": 125, "candies": 250 }, "240": { "evo": 126, "candies": 252 }, "246": { "evo": 247, "candies": 209 }, "247": { "evo": 248, "candies": 699 }, "252": { "evo": 253, "candies": 207 }, "253": { "evo": 254, "candies": 663 }, "255": { "evo": 256, "candies": 207 }, "256": { "evo": 257, "candies": 663 }, "258": { "evo": 259, "candies": 207 }, "259": { "evo": 260, "candies": 669 }, "261": { "evo": 262, "candies": 353 }, "263": { "evo": 264, "candies": 353 }, "265": { "evo": [266, 268], "candies": 105 }, "266": { "evo": 267, "candies": 332 }, "268": { "evo": 269, "candies": 323 }, "270": { "evo": 271, "candies": 173 }, "271": { "evo": 272, "candies": 552 }, "273": { "evo": 274, "candies": 173 }, "274": { "evo": 275, "candies": 403 }, "276": { "evo": 277, "candies": 361 }, "278": { "evo": 279, "candies": 361 }, "280": { "evo": 281, "candies": 142 }, "281": { "evo": [282, 475], "candies": 435 }, "283": { "evo": 284, "candies": 348 }, "285": { "evo": 286, "candies": 386 }, "287": { "evo": 288, "candies": 224 }, "288": { "evo": 289, "candies": 781 }, "290": { "evo": [291, 292], "candies": 291 }, "293": { "evo": 294, "candies": 184 }, "294": { "evo": 295, "candies": 412 }, "296": { "evo": 297, "candies": 398 }, "298": { "evo": 183, "candies": 128 }, "299": { "evo": 476, "candies": 441 }, "300": { "evo": 301, "candies": 319 }, "304": { "evo": 305, "candies": 219 }, "305": { "evo": 306, "candies": 610 }, "307": { "evo": 308, "candies": 344 }, "309": { "evo": 310, "candies": 399 }, "315": { "evo": 407, "candies": 433 }, "316": { "evo": 317, "candies": 392 }, "318": { "evo": 319, "candies": 386 }, "320": { "evo": 321, "candies": 420 }, "322": { "evo": 323, "candies": 575 }, "325": { "evo": 326, "candies": 395 }, "328": { "evo": 329, "candies": 173 }, "329": { "evo": 330, "candies": 598 }, "331": { "evo": 332, "candies": 399 }, "333": { "evo": 334, "candies": 412 }, "339": { "evo": 340, "candies": 393 }, "341": { "evo": 342, "candies": 393 }, "343": { "evo": 344, "candies": 420 }, "345": { "evo": 346, "candies": 416 }, "347": { "evo": 348, "candies": 416 }, "349": { "evo": [350], "candies": 454 }, "353": { "evo": 354, "candies": 382 }, "355": { "evo": 356, "candies": 232 }, "356": { "evo": 477, "candies": 441 }, "360": { "evo": 202, "candies": 340 }, "361": { "evo": [362, 478], "candies": 403 }, "363": { "evo": 364, "candies": 209 }, "364": { "evo": 365, "candies": 445 }, "366": { "evo": [367, 368], "candies": 407 }, "371": { "evo": 372, "candies": 214 }, "372": { "evo": 373, "candies": 699 }, "374": { "evo": 375, "candies": 214 }, "375": { "evo": 376, "candies": 699 }, "387": { "evo": 388, "candies": 207 }, "388": { "evo": 389, "candies": 656 }, "390": { "evo": 391, "candies": 207 }, "391": { "evo": 392, "candies": 668 }, "393": { "evo": 394, "candies": 207 }, "394": { "evo": 395, "candies": 663 }, "396": { "evo": 397, "candies": 173 }, "397": { "evo": 398, "candies": 407 }, "399": { "evo": 400, "candies": 344 }, "401": { "evo": 402, "candies": 323 }, "403": { "evo": 404, "candies": 185 }, "404": { "evo": 405, "candies": 439 }, "406": { "evo": 315, "candies": 204 }, "408": { "evo": 409, "candies": 416 }, "410": { "evo": 411, "candies": 416 }, "412": { "evo": [413, 414], "candies": 356 }, "415": { "evo": 416, "candies": 398 }, "418": { "evo": 419, "candies": 416 }, "420": { "evo": 421, "candies": 378 }, "422": { "evo": 423, "candies": 399 }, "425": { "evo": 426, "candies": 418 }, "427": { "evo": 428, "candies": 403 }, "431": { "evo": 432, "candies": 380 }, "433": { "evo": 358, "candies": 357 }, "434": { "evo": 435, "candies": 402 }, "436": { "evo": 437, "candies": 420 }, "438": { "evo": 185, "candies": 344 }, "439": { "evo": 122, "candies": 386 }, "440": { "evo": 113, "candies": 230 }, "443": { "evo": 444, "candies": 209 }, "444": { "evo": 445, "candies": 699 }, "446": { "evo": 143, "candies": 454 }, "447": { "evo": 448, "candies": 604 }, "449": { "evo": 450, "candies": 441 }, "451": { "evo": 452, "candies": 420 }, "453": { "evo": 454, "candies": 412 }, "456": { "evo": 457, "candies": 386 }, "458": { "evo": 226, "candies": 391 }, "459": { "evo": 460, "candies": 415 }, "495": { "evo": 496, "candies": 211 }, "496": { "evo": 497, "candies": 660 }, "498": { "evo": 499, "candies": 213 }, "499": { "evo": 500, "candies": 660 }, "501": { "evo": 502, "candies": 211 }, "502": { "evo": 503, "candies": 660 }, "504": { "evo": 505, "candies": 353 }, "506": { "evo": 507, "candies": 189 }, "507": { "evo": 508, "candies": 420 }, "509": { "evo": 510, "candies": 375 }, "511": { "evo": 512, "candies": 418 }, "513": { "evo": 514, "candies": 418 }, "515": { "evo": 516, "candies": 418 }, "517": { "evo": 518, "candies": 409 }, "519": { "evo": 520, "candies": 183 }, "520": { "evo": 521, "candies": 410 }, "522": { "evo": 523, "candies": 417 }, "524": { "evo": 525, "candies": 199 }, "525": { "evo": 526, "candies": 433 }, "527": { "evo": 528, "candies": 357 }, "529": { "evo": 530, "candies": 427 }, "532": { "evo": 533, "candies": 207 }, "533": { "evo": 534, "candies": 424 }, "535": { "evo": 536, "candies": 196 }, "536": { "evo": 537, "candies": 428 }, "540": { "evo": 541, "candies": 194 }, "541": { "evo": 542, "candies": 420 }, "543": { "evo": 544, "candies": 184 }, "544": { "evo": 545, "candies": 407 }, "546": { "evo": 547, "candies": 403 }, "548": { "evo": 549, "candies": 403 }, "551": { "evo": 552, "candies": 179 }, "552": { "evo": 553, "candies": 597 }, "554": { "evo": 555, "candies": 403 }, "557": { "evo": 558, "candies": 399 }, "559": { "evo": 560, "candies": 410 }, "562": { "evo": 563, "candies": 406 }, "564": { "evo": 565, "candies": 416 }, "566": { "evo": 567, "candies": 652 }, "568": { "evo": 569, "candies": 398 }, "570": { "evo": 571, "candies": 428 }, "572": { "evo": 573, "candies": 395 }, "574": { "evo": 575, "candies": 199 }, "575": { "evo": 576, "candies": 412 }, "577": { "evo": 578, "candies": 189 }, "578": { "evo": 579, "candies": 412 }, "580": { "evo": 581, "candies": 397 }, "582": { "evo": 583, "candies": 201 }, "583": { "evo": 584, "candies": 449 }, "585": { "evo": 586, "candies": 399 }, "588": { "evo": 589, "candies": 416 }, "590": { "evo": 591, "candies": 390 }, "592": { "evo": 593, "candies": 403 }, "595": { "evo": 596, "candies": 396 }, "597": { "evo": 598, "candies": 411 }, "599": { "evo": 600, "candies": 224 }, "600": { "evo": 601, "candies": 437 }, "602": { "evo": 603, "candies": 207 }, "603": { "evo": 604, "candies": 433 }, "605": { "evo": 606, "candies": 407 }, "607": { "evo": 608, "candies": 189 }, "608": { "evo": 609, "candies": 598 }, "610": { "evo": 611, "candies": 209 }, "611": { "evo": 612, "candies": 621 }, "613": { "evo": 614, "candies": 407 }, "616": { "evo": 617, "candies": 416 }, "619": { "evo": 620, "candies": 428 }, "622": { "evo": 623, "candies": 555 }, "624": { "evo": 625, "candies": 412 }, "627": { "evo": 628, "candies": 428 }, "629": { "evo": 630, "candies": 428 }, "633": { "evo": 634, "candies": 214 }, "634": { "evo": 635, "candies": 699 }, "636": { "evo": 637, "candies": 462 }, "650": { "evo": 651, "candies": 207 }, "651": { "evo": 652, "candies": 663 }, "653": { "evo": 654, "candies": 209 }, "654": { "evo": 655, "candies": 668 }, "656": { "evo": 657, "candies": 207 }, "657": { "evo": 658, "candies": 663 }, "659": { "evo": 660, "candies": 355 }, "661": { "evo": 662, "candies": 195 }, "662": { "evo": 663, "candies": 624 }, "664": { "evo": 665, "candies": 109 }, "665": { "evo": [666, 66202, 131738, 197274, 262810, 328346, 393882, 459418, 524954, 590490, 656026, 721562, 787098, 852634, 918170, 983706, 1049242, 1114778], "candies": 345 }, "667": { "evo": 668, "candies": 426 }, "669": { "evo": 670, "candies": 189 }, "670": { "evo": 671, "candies": 464 }, "328350": { "evo": -1, "candies": 0 }, "672": { "evo": 673, "candies": 446 }, "674": { "evo": 675, "candies": 416 }, "677": { "evo": [678, 66214], "candies": 391 }, "679": { "evo": 680, "candies": 228 }, "680": { "evo": 681, "candies": 598 }, "682": { "evo": 683, "candies": 388 }, "684": { "evo": 685, "candies": 403 }, "686": { "evo": 687, "candies": 405 }, "688": { "evo": 689, "candies": 420 }, "690": { "evo": 691, "candies": 415 }, "692": { "evo": 693, "candies": 420 }, "694": { "evo": 695, "candies": 404 }, "696": { "evo": 697, "candies": 438 }, "698": { "evo": 699, "candies": 651 }, "704": { "evo": 705, "candies": 231 }, "705": { "evo": 706, "candies": 699 }, "708": { "evo": 709, "candies": 398 }, "710": { "evo": 711, "candies": 415 }, "712": { "evo": 713, "candies": 432 }, "714": { "evo": 715, "candies": 449 }, "722": { "evo": 723, "candies": 214 }, "723": { "evo": 724, "candies": 663 }, "725": { "evo": 726, "candies": 214 }, "726": { "evo": 727, "candies": 663 }, "728": { "evo": 729, "candies": 214 }, "729": { "evo": 730, "candies": 663 }, "731": { "evo": 732, "candies": 181 }, "732": { "evo": 733, "candies": 407 }, "734": { "evo": 735, "candies": 351 }, "736": { "evo": 737, "candies": 204 }, "737": { "evo": 738, "candies": 420 }, "739": { "evo": 740, "candies": 402 }, "742": { "evo": 743, "candies": 390 }, "744": { "evo": [745, 66281], "candies": 409 }, "747": { "evo": 748, "candies": 416 }, "749": { "evo": 750, "candies": 420 }, "751": { "evo": 752, "candies": 381 }, "753": { "evo": 754, "candies": 403 }, "755": { "evo": 756, "candies": 340 }, "757": { "evo": 758, "candies": 403 }, "759": { "evo": 760, "candies": 420 }, "761": { "evo": 762, "candies": 148 }, "762": { "evo": 763, "candies": 428 }, "767": { "evo": 768, "candies": 445 }, "769": { "evo": 770, "candies": 403 }, "772": { "evo": 773, "candies": 599 }, "782": { "evo": 783, "candies": 214 }, "783": { "evo": 784, "candies": 699 }, "789": { "evo": 790, "candies": 39999 }, "790": { "evo": [791, 792], "candies": 9999999 }, "803": { "evo": 804, "candies": 9999999 }, "808": { "evo": 809, "candies": 9999999 }, "810": { "evo": 811, "candies": 207 }, "811": { "evo": 812, "candies": 656 }, "813": { "evo": 814, "candies": 207 }, "814": { "evo": 815, "candies": 656 }, "816": { "evo": 817, "candies": 207 }, "817": { "evo": 818, "candies": 656 }, "819": { "evo": 820, "candies": 310 }, "821": { "evo": 822, "candies": 178 }, "822": { "evo": 823, "candies": 402 }, "824": { "evo": 825, "candies": 130 }, "825": { "evo": 826, "candies": 323 }, "827": { "evo": 828, "candies": 375 }, "829": { "evo": 830, "candies": 420 }, "831": { "evo": 832, "candies": 350 }, "833": { "evo": 834, "candies": 406 }, "835": { "evo": 836, "candies": 432 }, "837": { "evo": 838, "candies": 270 }, "838": { "evo": 839, "candies": 610 }, "840": { "evo": [841, 842], "candies": 510 }, "843": { "evo": 844, "candies": 480 }, "846": { "evo": 847, "candies": 433 }, "848": { "evo": [849, 66385], "candies": 480 }, "850": { "evo": 851, "candies": 498 }, "852": { "evo": 853, "candies": 444 }, "854": { "evo": 855, "candies": 496 }, "856": { "evo": 857, "candies": 290 }, "857": { "evo": 858, "candies": 412 }, "859": { "evo": 860, "candies": 290 }, "860": { "evo": 861, "candies": 412 }, "65799": { "evo": 65800, "candies": 353 }, "65800": { "evo": 862, "candies": 508 }, "131124": { "evo": 863, "candies": 451 }, "65758": { "evo": 864, "candies": 680 }, "65619": { "evo": 865, "candies": 617 }, "65658": { "evo": 866, "candies": 629 }, "66098": { "evo": 867, "candies": 470 }, "868": { "evo": [869, 66405, 131941, 197477, 263013, 328549, 394085, 459621, 525157, 590693, 656229, 721765, 787301, 852837, 918373, 983909, 1049445, 1114981, 1180517, 1246053, 1311589, 1377125, 1442661, 1508197, 1573733, 1639269, 1704805, 1770341, 1835877, 1901413, 1966949, 2032485, 2098021, 2163557, 2229093, 2294629, 2360165, 2425701, 2491237, 2556773, 2622309, 2687845, 2753381, 2818917, 2884453, 2949989, 3015525, 3081061, 3146597, 3212133, 3277669, 3343205, 3408741, 3474277, 3539813, 3605349, 3670885, 3736421, 3801957, 3867493, 3933029, 3998565, 4064101], "candies": 410 }, "872": { "evo": 873, "candies": 346 }, "878": { "evo": 879, "candies": 470 }, "885": { "evo": 886, "candies": 214 }, "886": { "evo": 887, "candies": 699 }, "65613": { "evo": 65614, "candies": 540 }, "66090": { "evo": 131627, "candies": 530 }, "65615": { "evo": [131152, 65735], "candies": 412 }, "891": { "evo": [892, 66428], "candies": 999999 }, "65594": { "evo": 65595, "candies": 466 }, "65636": { "evo": 65637, "candies": 403 }, "65747": { "evo": 904, "candies": 420 }, "65751": { "evo": 903, "candies": 428 }, "131622": { "evo": [902, 66438], "candies": 478 }, "66106": { "evo": 66107, "candies": 428 }, "66241": { "evo": 66242, "candies": 699 }, "65730": { "evo": 980, "candies": 340 }, "906": { "evo": 907, "candies": 207 }, "907": { "evo": 908, "candies": 656 }, "909": { "evo": 910, "candies": 207 }, "910": { "evo": 911, "candies": 656 }, "912": { "evo": 913, "candies": 207 }, "913": { "evo": 914, "candies": 656 }, "915": { "evo": [916, 66452], "candies": 351 }, "917": { "evo": 918, "candies": 328 }, "919": { "evo": 920, "candies": 378 }, "921": { "evo": 922, "candies": 199 }, "922": { "evo": 923, "candies": 412 }, "924": { "evo": [66461], "candies": 397 }, "926": { "evo": 927, "candies": 402 }, "928": { "evo": 929, "candies": 204 }, "929": { "evo": 930, "candies": 433 }, "932": { "evo": 933, "candies": 270 }, "933": { "evo": 934, "candies": 610 }, "935": { "evo": [936, 937], "candies": 498 }, "938": { "evo": 939, "candies": 410 }, "940": { "evo": 941, "candies": 410 }, "942": { "evo": 943, "candies": 407 }, "944": { "evo": 945, "candies": 406 }, "946": { "evo": 947, "candies": 444 }, "948": { "evo": 949, "candies": 433 }, "951": { "evo": 952, "candies": 406 }, "953": { "evo": 954, "candies": 397 }, "955": { "evo": 956, "candies": 404 }, "957": { "evo": 958, "candies": 207 }, "958": { "evo": 959, "candies": 424 }, "960": { "evo": 961, "candies": 340 }, "963": { "evo": 964, "candies": 375 }, "965": { "evo": 966, "candies": 470 }, "969": { "evo": 970, "candies": 498 }, "971": { "evo": 972, "candies": 409 }, "974": { "evo": 975, "candies": 651 }, "996": { "evo": 997, "candies": 214 }, "997": { "evo": 998, "candies": 699 }, "999": { "evo": 1000, "candies": 462 }, "66535": { "evo": -1, "candies": 0 }, "1012": { "evo": 1013, "candies": 496 }, "66548": { "evo": 66549, "candies": 496 }, "66390": { "evo": 66391, "candies": 496 }, "1011": {"evo": 1019, "candies": 449 } }; for (var i = 0; i < 100; i++) { // tandemaus evolutions[924].evo.unshift(925); } var devolutions = { "2": 1, "3": 2, "5": 4, "6": 5, "8": 7, "9": 8, "11": 10, "12": 11, "14": 13, "15": 14, "17": 16, "18": 17, "20": 19, "22": 21, "24": 23, "65562": 25, "26": 25, "28": 27, "30": 29, "31": 30, "33": 32, "34": 33, "36": 35, "38": 37, "40": 39, "42": 41, "169": 42, "44": 43, "45": 44, "182": 44, "47": 46, "49": 48, "51": 50, "53": 52, "55": 54, "57": 56, "59": 58, "61": 60, "62": 61, "186": 61, "64": 63, "65": 64, "67": 66, "68": 67, "70": 69, "71": 70, "73": 72, "75": 74, "76": 75, "78": 77, "80": 79, "199": 79, "82": 81, "462": 82, "85": 84, "87": 86, "89": 88, "91": 90, "93": 92, "94": 93, "208": 95, "97": 96, "99": 98, "101": 100, "103": 102, "105": 104, "463": 108, "110": 109, "112": 111, "464": 112, "242": 113, "465": 114, "117": 116, "230": 117, "119": 118, "121": 120, "212": 123, "466": 125, "467": 126, "130": 129, "470": 133, "471": 133, "135": 133, "134": 133, "136": 133, "196": 133, "197": 133, "700": 133, "233": 137, "139": 138, "141": 140, "148": 147, "149": 148, "153": 152, "154": 153, "156": 155, "157": 156, "159": 158, "160": 159, "162": 161, "164": 163, "166": 165, "168": 167, "171": 170, "25": 172, "65561": -1, "131097": -1, "196633": -1, "262169": -1, "327705": -1, "393241": -1, "458777": -1, "524313": -1, "589849": -1, "655385": -1, "720921": -1, "786457": -1, "851993": -1, "35": 173, "39": 174, "176": 175, "468": 176, "178": 177, "180": 179, "181": 180, "184": 183, "188": 187, "189": 188, "424": 190, "192": 191, "469": 193, "195": 194, "430": 198, "429": 200, "205": 204, "472": 207, "210": 209, "461": 215, "217": 216, "219": 218, "221": 220, "473": 221, "224": 223, "229": 228, "232": 231, "474": 233, "107": 236, "106": 236, "237": 236, "124": 238, "125": 239, "126": 240, "247": 246, "248": 247, "253": 252, "254": 253, "256": 255, "257": 256, "259": 258, "260": 259, "262": 261, "264": 263, "266": 265, "268": 265, "267": 266, "269": 268, "271": 270, "272": 271, "274": 273, "275": 274, "277": 276, "279": 278, "281": 280, "282": 281, "475": 281, "284": 283, "286": 285, "288": 287, "289": 288, "291": 290, "292": 290, "294": 293, "295": 294, "297": 296, "183": 298, "476": 299, "301": 300, "305": 304, "306": 305, "308": 307, "310": 309, "407": 315, "317": 316, "319": 318, "321": 320, "323": 322, "326": 325, "329": 328, "330": 329, "332": 331, "334": 333, "340": 339, "342": 341, "344": 343, "346": 345, "348": 347, "350": 349, "354": 353, "356": 355, "477": 356, "202": 360, "362": 361, "478": 361, "364": 363, "365": 364, "367": 366, "368": 366, "372": 371, "373": 372, "375": 374, "376": 375, "388": 387, "389": 388, "391": 390, "392": 391, "394": 393, "395": 394, "397": 396, "398": 397, "400": 399, "402": 401, "404": 403, "405": 404, "315": 406, "409": 408, "411": 410, "413": 412, "414": 412, "416": 415, "419": 418, "421": 420, "423": 422, "426": 425, "428": 427, "432": 431, "358": 433, "435": 434, "437": 436, "185": 438, "122": 439, "113": 440, "444": 443, "445": 444, "143": 446, "448": 447, "450": 449, "452": 451, "454": 453, "457": 456, "226": 458, "460": 459, "496": 495, "497": 496, "499": 498, "500": 499, "502": 501, "503": 502, "505": 504, "507": 506, "508": 507, "510": 509, "512": 511, "514": 513, "516": 515, "518": 517, "520": 519, "521": 520, "523": 522, "525": 524, "526": 525, "528": 527, "530": 529, "533": 532, "534": 533, "536": 535, "537": 536, "541": 540, "542": 541, "544": 543, "545": 544, "547": 546, "549": 548, "552": 551, "553": 552, "555": 554, "66091": 554, "558": 557, "560": 559, "563": 562, "565": 564, "567": 566, "569": 568, "571": 570, "573": 572, "575": 574, "576": 575, "578": 577, "579": 578, "581": 580, "583": 582, "584": 583, "586": 585, "589": 588, "591": 590, "593": 592, "596": 595, "598": 597, "600": 599, "601": 600, "603": 602, "604": 603, "606": 605, "608": 607, "609": 608, "611": 610, "612": 611, "614": 613, "617": 616, "620": 619, "623": 622, "625": 624, "628": 627, "630": 629, "634": 633, "635": 634, "637": 636, "651": 650, "652": 651, "654": 653, "655": 654, "657": 656, "658": 657, "660": 659, "662": 661, "663": 662, "665": 664, "666": 665, "668": 667, "670": 669, "328350": -1, "671": 670, "673": 672, "675": 674, "678": 677, "680": 679, "681": 680, "683": 682, "685": 684, "687": 686, "689": 688, "691": 690, "693": 692, "695": 694, "697": 696, "699": 698, "705": 704, "706": 705, "709": 708, "711": 710, "713": 712, "715": 714, "723": 722, "724": 723, "726": 725, "727": 726, "729": 728, "730": 729, "732": 731, "733": 732, "735": 734, "737": 736, "738": 737, "740": 739, "743": 742, "745": 744, "131817": -1, "66281": 744, "748": 747, "750": 749, "752": 751, "754": 753, "756": 755, "758": 757, "760": 759, "762": 761, "763": 762, "768": 767, "770": 769, "773": 772, "783": 782, "784": 783, "790": 789, "791": 790, "792": 790, "804": 803, "811": 810, "812": 811, "814": 813, "815": 814, "817": 816, "818": 817, "820": 819, "822": 821, "823": 822, "825": 824, "826": 825, "828": 827, "830": 829, "832": 831, "834": 833, "836": 835, "838": 837, "839": 838, "841": 840, "842": 840, "844": 843, "847": 846, "849": 848, "66385": 848, "851": 850, "853": 852, "855": 854, "857": 856, "858": 857, "860": 859, "861": 860, "862": 65800, "65800": 65799, "863": 131124, "864": 65758, "865": 65619, "866": 65658, "867": 66098, "869": 868, "873": 872, "879": 878, "886": 885, "887": 886, "65614": 65613, "197163": 66090, "131627": 66090, "131152": 65615, "892": 891, "66428": 891, "65735": 65615, "555": 554, "66091": 554, "131627": 66090, "197163": 66090, "131152": 65615, "65611": 65610, "65595": 65594, "65637": 65636, "65693": 156, "66039": 502, "66085": 548, "66107": 66106, "66164": 627, "66241": 704, "66242": 66241, "66249": 712, "66260": 723, "899": 234, "900": 123, "901": 217, "902": 131622, "66438": 131622, "903": 65751, "904": 65747, "907": 906, "908": 907, "910": 909, "911": 910, "913": 912, "914": 913, "916": 915, "66452": 915, "918": 917, "920": 919, "922": 921, "923": 922, "925": 924, "66461": 924, "927": 926, "929": 928, "930": 929, "933": 932, "934": 933, "936": 935, "937": 935, "939": 938, "941": 940, "943": 942, "945": 944, "947": 946, "949": 948, "952": 951, "954": 953, "956": 955, "958": 957, "959": 958, "961": 960, "964": 963, "66500": 963, "966": 965, "970": 969, "972": 971, "975": 974, "979": 57, "980": 65730, "981": 203, "982": 206, "66518": 206, "983": 625, "997": 996, "998": 997, "1000": 999, "1011": 840, "1013": 1012, "66549": 66548, "66391": 66390, "66437": 217, "1018": 884, "1019": 1011 }; var megaEvolutions = { "3":[65539],"6":[65542, 131078],"9":[65545],"15":[65551],"18":[65554],"65":[65601],"80":[65616],"94":[65630],"115":[65651],"127":[65663],"130":[65666],"142":[65678],"150":[65686, 131222],"181":[65717],"208":[65744],"212":[65748],"214":[65750],"229":[65765],"248":[65784],"254":[65790],"257":[65793],"260":[65796],"282":[65818],"302":[65838],"303":[65839],"306":[65842],"308":[65844],"310":[65846],"319":[65855],"323":[65859],"334":[65870],"354":[65890],"359":[65895],"362":[65898],"373":[65909],"376":[65912],"380":[65916],"381":[65917],"382":[65918],"383":[65919],"384":[65920],"428":[65964],"445":[65981],"448":[65984],"460":[65996],"475":[66011],"531":[66067],"719":[66255] }; var megaPokemon = [ 65539,65542,131078,65545,65551,65554,65601,65616,65630,65651,65663,65666,65678,65686,131222,65717,65744,65748,65750,65765,65784,65790,65793,65796,65818,65838,65839,65842,65844,65846,65855,65859,65870,65890,65895,65898,65909,65912,65916,65917,65918,65919,65920,65964,65981,65984,65996,66011,66067,66255 ]; var eggdata = {1:["Monster","Grass"],2:["Monster","Grass"],3:["Monster","Grass"],4:["Monster","Dragon"],5:["Monster","Dragon"],6:["Monster","Dragon"],7:["Monster","Water1"],8:["Monster","Water1"],9:["Monster","Water1"],10:["Bug"],11:["Bug"],12:["Bug"],13:["Bug"],14:["Bug"],15:["Bug"],16:["Flying"],17:["Flying"],18:["Flying"],19:["Field"],20:["Field"],21:["Flying"],22:["Flying"],23:["Field"],24:["Field"],25:["Field","Fairy"],26:["Field","Fairy"],27:["Field"],28:["Field"],29:["Monster","Field"],30:["Undiscovered"],31:["Undiscovered"],32:["Monster","Field"],33:["Monster","Field"],34:["Monster","Field"],35:["Fairy"],36:["Fairy"],37:["Field"],38:["Field"],39:["Fairy"],40:["Fairy"],41:["Flying"],42:["Flying"],43:["Grass"],44:["Grass"],45:["Grass"],46:["Bug","Grass"],47:["Bug","Grass"],48:["Bug"],49:["Bug"],50:["Field"],51:["Field"],52:["Field"],53:["Field"],54:["Water1","Field"],55:["Water1","Field"],56:["Field"],57:["Field"],58:["Field"],59:["Field"],60:["Water1"],61:["Water1"],62:["Water1"],63:["Human-Like"],64:["Human-Like"],65:["Human-Like"],66:["Human-Like"],67:["Human-Like"],68:["Human-Like"],69:["Grass"],70:["Grass"],71:["Grass"],72:["Water3"],73:["Water3"],74:["Mineral"],75:["Mineral"],76:["Mineral"],77:["Field"],78:["Field"],79:["Monster","Water1"],80:["Monster","Water1"],81:["Mineral"],82:["Mineral"],83:["Flying","Field"],84:["Flying"],85:["Flying"],86:["Water1","Field"],87:["Water1","Field"],88:["Amorphous"],89:["Amorphous"],90:["Water3"],91:["Water3"],92:["Amorphous"],93:["Amorphous"],94:["Amorphous"],95:["Mineral"],96:["Human-Like"],97:["Human-Like"],98:["Water3"],99:["Water3"],100:["Mineral"],101:["Mineral"],102:["Grass"],103:["Grass"],104:["Monster"],105:["Monster"],106:["Human-Like"],107:["Human-Like"],108:["Monster"],109:["Amorphous"],110:["Amorphous"],111:["Monster","Field"],112:["Monster","Field"],113:["Fairy"],114:["Grass"],115:["Monster"],116:["Water1","Dragon"],117:["Water1","Dragon"],118:["Water2"],119:["Water2"],120:["Water3"],121:["Water3"],122:["Human-Like"],123:["Bug"],124:["Human-Like"],125:["Human-Like"],126:["Human-Like"],127:["Bug"],128:["Field"],129:["Water2","Dragon"],130:["Water2","Dragon"],131:["Monster","Water1"],132:["Ditto"],133:["Field"],134:["Field"],135:["Field"],136:["Field"],137:["Mineral"],138:["Water1","Water3"],139:["Water1","Water3"],140:["Water1","Water3"],141:["Water1","Water3"],142:["Flying"],143:["Monster"],144:["Undiscovered"],145:["Undiscovered"],146:["Undiscovered"],147:["Water1","Dragon"],148:["Water1","Dragon"],149:["Water1","Dragon"],150:["Undiscovered"],151:["Undiscovered"],152:["Grass"],153:["Grass"],154:["Grass"],155:["Field"],156:["Field"],157:["Field"],158:["Monster"],159:["Monster"],160:["Monster"],161:["Field"],162:["Field"],163:["Flying"],164:["Flying"],165:["Bug"],166:["Bug"],167:["Bug"],168:["Bug"],169:["Flying"],170:["Water2"],171:["Water2"],172:["Undiscovered"],173:["Undiscovered"],174:["Undiscovered"],175:["Undiscovered"],176:["Fairy","flying"],177:["Flying"],178:["Flying"],179:["Monster","Field"],180:["Monster","Field"],181:["Monster","Field"],182:["Grass"],183:["Water1","Fairy"],184:["Water1","Fairy"],185:["Mineral"],186:["Water1"],187:["Fairy","Grass"],188:["Fairy","Grass"],189:["Fairy","Grass"],190:["Field"],191:["Grass"],192:["Grass"],193:["Bug"],194:["Water1","Field"],195:["Water1","Field"],196:["Field"],197:["Field"],198:["Flying"],199:["Monster","Water1"],200:["Amorphous"],201:["Undiscovered"],202:["Amorphous"],203:["Field"],204:["Bug"],205:["Bug"],206:["Field"],207:["Bug"],208:["Mineral"],209:["Field","Fairy"],210:["Field","Fairy"],211:["Water2"],212:["Bug"],213:["Bug"],214:["Bug"],215:["Field"],216:["Field"],217:["Field"],218:["Amorphous"],219:["Amorphous"],220:["Field"],221:["Field"],222:["Water1","Water3"],223:["Water1","Water2"],224:["Water1","Water2"],225:["Water1","Field"],226:["Water1"],227:["Flying"],228:["Field"],229:["Field"],230:["Water1","Dragon"],231:["Field"],232:["Field"],233:["Mineral"],234:["Field"],235:["Field"],236:["Undiscovered"],237:["Human-Like"],238:["Undiscovered"],239:["Undiscovered"],240:["Undiscovered"],241:["Field"],242:["Fairy"],243:["Undiscovered"],244:["Undiscovered"],245:["Undiscovered"],246:["Monster"],247:["Monster"],248:["Monster"],249:["Undiscovered"],250:["Undiscovered"],251:["Undiscovered"],252:["Monster","Dragon"],253:["Monster","Dragon"],254:["Monster","Dragon"],255:["Field"],256:["Field"],257:["Field"],258:["Monster","Water1"],259:["Monster","Water1"],260:["Monster","Water1"],261:["Field"],262:["Field"],263:["Field"],264:["Field"],265:["Bug"],266:["Bug"],267:["Bug"],268:["Bug"],269:["Bug"],270:["Water1","Grass"],271:["Water1","Grass"],272:["Water1","Grass"],273:["Grass","Field"],274:["Grass","Field"],275:["Grass","Field"],276:["Flying"],277:["Flying"],278:["Water1","Flying"],279:["Water1","Flying"],280:["Amorphous","Human-Like"],281:["Amorphous","Human-Like"],282:["Amorphous","Human-Like"],283:["Water1","bug"],284:["Water1","bug"],285:["Fairy","Grass"],286:["Fairy","Grass"],287:["Field"],288:["Field"],289:["Field"],290:["Bug"],291:["Bug"],292:["Mineral"],293:["Monster","Field"],294:["Monster","Field"],295:["Monster","Field"],296:["Human-Like"],297:["Human-Like"],298:["Undiscovered"],299:["Mineral"],300:["Field","Fairy"],301:["Field","Fairy"],302:["Human-Like"],303:["Field","Fairy"],304:["Monster"],305:["Monster"],306:["Monster"],307:["Human-Like"],308:["Human-Like"],309:["Field"],310:["Field"],311:["Fairy"],312:["Fairy"],313:["Grass","Human-Like"],314:["Grass","Human-Like"],315:["Fairy","Grass"],316:["Amorphous"],317:["Amorphous"],318:["Water2"],319:["Water2"],320:["Water2","Field"],321:["Water2","Field"],322:["Field"],323:["Field"],324:["Field"],325:["Field"],326:["Field"],327:["Field","Human-Like"],328:["Bug","Dragon"],329:["Bug","Dragon"],330:["Bug","Dragon"],331:["Grass","Human-Like"],332:["Grass","Human-Like"],333:["Flying","Dragon"],334:["Flying","Dragon"],335:["Field"],336:["Field","Dragon"],337:["Mineral"],338:["Mineral"],339:["Water2"],340:["Water2"],341:["Water1","Water3"],342:["Water1","Water3"],343:["Mineral"],344:["Mineral"],345:["Water3"],346:["Water3"],347:["Water3"],348:["Water3"],349:["Water1","Dragon"],350:["Water1","Dragon"],351:["Fairy","Amorphous"],352:["Field"],353:["Amorphous"],354:["Amorphous"],355:["Amorphous"],356:["Amorphous"],357:["Grass","Monster"],358:["Amorphous"],359:["Field"],360:["Undiscovered"],361:["Fairy","Mineral"],362:["Fairy","Mineral"],363:["Water1","Field"],364:["Water1","Field"],365:["Water1","Field"],366:["Water1"],367:["Water1"],368:["Water1"],369:["Water1","Water2"],370:["Water2"],371:["Dragon"],372:["Dragon"],373:["Dragon"],374:["Mineral"],375:["Mineral"],376:["Mineral"],377:["Undiscovered"],378:["Undiscovered"],379:["Undiscovered"],380:["Undiscovered"],381:["Undiscovered"],382:["Undiscovered"],383:["Undiscovered"],384:["Undiscovered"],385:["Undiscovered"],386:["Undiscovered"],387:["Grass","Monster"],388:["Grass","Monster"],389:["Grass","Monster"],390:["Field","Human-Like"],391:["Field","Human-Like"],392:["Field","Human-Like"],393:["Water1","Field"],394:["Water1","Field"],395:["Water1","Field"],396:["Flying"],397:["Flying"],398:["Flying"],399:["Water1","Field"],400:["Water1","Field"],401:["Bug"],402:["Bug"],403:["Field"],404:["Field"],405:["Field"],406:["Undiscovered"],407:["Fairy","Grass"],408:["Monster"],409:["Monster"],410:["Monster"],411:["Monster"],412:["Bug"],413:["Bug"],414:["Bug"],415:["Bug"],416:["Bug"],417:["Field","Fairy"],418:["Water1","Field"],419:["Water1","Field"],420:["Fairy","Grass"],421:["Fairy","Grass"],422:["Water1","Amorphous"],423:["Water1","Amorphous"],424:["Field"],425:["Amorphous"],426:["Amorphous"],427:["Field","Human-Like"],428:["Field","Human-Like"],429:["Amorphous"],430:["Flying"],431:["Field"],432:["Field"],433:["Undiscovered"],434:["Field"],435:["Field"],436:["Mineral"],437:["Mineral"],438:["Undiscovered"],439:["Undiscovered"],440:["Undiscovered"],441:["Flying"],442:["Amorphous"],443:["Monster","Dragon"],444:["Monster","Dragon"],445:["Monster","Dragon"],446:["Undiscovered"],447:["Undiscovered"],448:["Field","Human-Like"],449:["Field"],450:["Field"],451:["Bug","Water3"],452:["Bug","Water3"],453:["Human-Like"],454:["Human-Like"],455:["Grass"],456:["Water2"],457:["Water2"],458:["Undiscovered"],459:["Monster","Grass"],460:["Monster","Grass"],461:["Field"],462:["Mineral"],463:["Monster"],464:["Monster","Field"],465:["Grass"],466:["Human-Like"],467:["Human-Like"],468:["Flying","Fairy"],469:["Bug"],470:["Field"],471:["Field"],472:["Bug"],473:["Field"],474:["Mineral"],475:["Amorphous","Human-Like"],476:["Mineral"],477:["Amorphous"],478:["Fairy","Mineral"],479:["Amorphous"],480:["Undiscovered"],481:["Undiscovered"],482:["Undiscovered"],483:["Undiscovered"],484:["Undiscovered"],485:["Undiscovered"],486:["Undiscovered"],487:["Undiscovered"],488:["Undiscovered"],489:["Water1","Fairy"],490:["Water1","Fairy"],491:["Undiscovered"],492:["Undiscovered"],493:["Undiscovered"],494:["Undiscovered"],495:["Field","Grass"],496:["Field","Grass"],497:["Field","Grass"],498:["Field"],499:["Field"],500:["Field"],501:["Field"],502:["Field"],503:["Field"],504:["Field"],505:["Field"],506:["Field"],507:["Field"],508:["Field"],509:["Field"],510:["Field"],511:["Field"],512:["Field"],513:["Field"],514:["Field"],515:["Field"],516:["Field"],517:["Field"],518:["Field"],519:["Flying"],520:["Flying"],521:["Flying"],522:["Field"],523:["Field"],524:["Mineral"],525:["Mineral"],526:["Mineral"],527:["Field","Flying"],528:["Field","Flying"],529:["Field"],530:["Field"],531:["Fairy"],532:["Human-Like"],533:["Human-Like"],534:["Human-Like"],535:["Water1"],536:["Water1"],537:["Water1"],538:["Human-Like"],539:["Human-Like"],540:["Bug"],541:["Bug"],542:["Bug"],543:["Bug"],544:["Bug"],545:["Bug"],546:["Grass","Fairy"],547:["Grass","Fairy"],548:["Grass"],549:["Grass"],550:["Water2"],551:["Field"],552:["Field"],553:["Field"],554:["Field"],555:["Field"],556:["Grass"],557:["Bug","Mineral"],558:["Bug","Mineral"],559:["Field","Dragon"],560:["Field","Dragon"],561:["Flying"],562:["Mineral","Amorphous"],563:["Mineral","Amorphous"],564:["Water1","Water3"],565:["Water1","Water3"],566:["Flying","Water3"],567:["Flying","Water3"],568:["Mineral"],569:["Mineral"],570:["Field"],571:["Field"],572:["Field"],573:["Field"],574:["Human-Like"],575:["Human-Like"],576:["Human-Like"],577:["Amorphous"],578:["Amorphous"],579:["Amorphous"],580:["Water1","Flying"],581:["Water1","Flying"],582:["Mineral"],583:["Mineral"],584:["Mineral"],585:["Field"],586:["Field"],587:["Field"],588:["Bug"],589:["Bug"],590:["Grass"],591:["Grass"],592:["Amorphous"],593:["Amorphous"],594:["Water1","Water2"],595:["Bug"],596:["Bug"],597:["Grass","Mineral"],598:["Grass","Mineral"],599:["Mineral"],600:["Mineral"],601:["Mineral"],602:["Amorphous"],603:["Amorphous"],604:["Amorphous"],605:["Human-Like"],606:["Human-Like"],607:["Amorphous"],608:["Amorphous"],609:["Amorphous"],610:["Monster","Dragon"],611:["Monster","Dragon"],612:["Monster","Dragon"],613:["Field"],614:["Field"],615:["Mineral"],616:["Bug"],617:["Bug"],618:["Water1","Amorphous"],619:["Field","Human-Like"],620:["Field","Human-Like"],621:["Dragon","Monster"],622:["Mineral"],623:["Mineral"],624:["Human-Like"],625:["Human-Like"],626:["Field"],627:["Flying"],628:["Flying"],629:["Flying"],630:["Flying"],631:["Field"],632:["Bug"],633:["Dragon"],634:["Dragon"],635:["Dragon"],636:["Bug"],637:["Bug"],638:["Undiscovered"],639:["Undiscovered"],640:["Undiscovered"],641:["Undiscovered"],642:["Undiscovered"],643:["Undiscovered"],644:["Undiscovered"],645:["Undiscovered"],646:["Undiscovered"],647:["Undiscovered"],648:["Undiscovered"],649:["Undiscovered"],650:["Field"],651:["Field"],652:["Field"],653:["Field"],654:["Field"],655:["Field"],656:["Water1"],657:["Water1"],658:["Water1"],659:["Field"],660:["Field"],661:["Flying"],662:["Flying"],663:["Flying"],664:["Bug"],665:["Bug"],666:["Bug"],667:["Field"],668:["Field"],669:["Fairy"],670:["Fairy"],671:["Fairy"],672:["Field"],673:["Field"],674:["Field","Human-Like"],675:["Field","Human-Like"],676:["Field"],677:["Field"],678:["Field"],679:["Mineral"],680:["Mineral"],681:["Mineral"],682:["Fairy"],683:["Fairy"],684:["Fairy"],685:["Fairy"],686:["Water1","Water2"],687:["Water1","Water2"],688:["Water3"],689:["Water3"],690:["Water1","Dragon"],691:["Water1","Dragon"],692:["Water1","Water3"],693:["Water1","Water3"],694:["Monster","Dragon"],695:["Monster","Dragon"],696:["Monster","Dragon"],697:["Monster","Dragon"],698:["Monster"],699:["Monster"],700:["Field"],701:["Human-Like","Flying"],702:["Field","Fairy"],703:["Fairy","Mineral"],704:["Dragon"],705:["Dragon"],706:["Dragon"],707:["Mineral"],708:["Grass","Amorphous"],709:["Grass","Amorphous"],710:["Amorphous"],711:["Amorphous"],712:["Monster","Mineral"],713:["Monster","Mineral"],714:["Flying","Dragon"],715:["Flying","Dragon"],716:["Undiscovered"],717:["Undiscovered"],718:["Undiscovered"],719:["Undiscovered"],720:["Undiscovered"],721:["Undiscovered"],722:["Flying"],723:["Flying"],724:["Flying"],725:["Field"],726:["Field"],727:["Field"],728:["Water1","Field"],729:["Water1","Field"],730:["Water1","Field"],731:["Flying"],732:["Flying"],733:["Flying"],734:["Field"],735:["Field"],736:["Bug"],737:["Bug"],738:["Bug"],739:["Water3"],740:["Water3"],741:["Flying"],742:["Bug","Fairy"],743:["Bug","Fairy"],744:["Field"],745:["Field"],746:["Water2"],747:["Water1"],748:["Water1"],749:["Field"],750:["Field"],751:["Water1","Bug"],752:["Water1","Bug"],753:["Grass"],754:["Grass"],755:["Grass"],756:["Grass"],757:["Monster","Dragon"],758:["Monster","Dragon"],759:["Field"],760:["Field"],761:["Grass"],762:["Grass"],763:["Grass"],764:["Grass"],765:["Field"],766:["Field"],767:["Bug","Water3"],768:["Bug","Water3"],769:["Amorphous"],770:["Amorphous"],771:["Water1"],772:["Undiscovered"],773:["Undiscovered"],774:["Mineral"],775:["Field"],776:["Monster","Dragon"],777:["Field","Fairy"],778:["Amorphous"],779:["Water2"],780:["Monster","Dragon"],781:["Mineral"],782:["Dragon"],783:["Dragon"],784:["Dragon"],785:["Undiscovered"],786:["Undiscovered"],787:["Undiscovered"],788:["Undiscovered"],789:["Undiscovered"],790:["Undiscovered"],791:["Undiscovered"],792:["Undiscovered"],793:["Undiscovered"],794:["Undiscovered"],795:["Undiscovered"],796:["Undiscovered"],797:["Undiscovered"],798:["Undiscovered"],799:["Undiscovered"],800:["Undiscovered"],801:["Undiscovered"],802:["Undiscovered"],803:["Undiscovered"],804:["Undiscovered"],805:["Undiscovered"],806:["Undiscovered"],807:["Undiscovered"],808:["Undiscovered"],809:["Undiscovered"],810:["Field","Grass"],811:["Field","Grass"],812:["Field","Human-Like"],813:["Field","Human-Like"],814:["Field","Human-Like"],815:["Field","Human-Like"],816:["Field","Water1"],817:["Field","Water1"],818:["Field","Water1"],819:["Field"],820:["Field"],821:["Flying"],822:["Flying"],823:["Flying"],824:["Bug"],825:["Bug"],826:["Bug"],827:["Field"],828:["Field"],829:["Grass"],830:["Grass"],831:["Field"],832:["Field"],833:["Monster","Water1"],834:["Monster","Water1"],835:["Field"],836:["Field"],837:["Mineral"],838:["Mineral"],839:["Mineral"],840:["Grass","Dragon"],841:["Grass","Dragon"],842:["Grass","Dragon"],843:["Field","Dragon"],844:["Field","Dragon"],845:["Water1","Flying"],846:["Water2"],847:["Water2"],848:["Undiscovered"],849:["Human-Like"],850:["Bug"],851:["Bug"],852:["Water1","Human-Like"],853:["Water1","Human-Like"],854:["Mineral","Amorphous"],855:["Mineral","Amorphous"],856:["Fairy"],857:["Fairy"],858:["Fairy"],859:["Fairy","Human-Like"],860:["Fairy","Human-Like"],861:["Fairy","Human-Like"],862:["Field"],863:["Field"],864:["Water1","Water3"],865:["Flying","Field"],866:["Human-Like"],867:["Mineral","Amorphous"],868:["Fairy","Amorphous"],869:["Fairy","Amorphous"],870:["Fairy","Mineral"],871:["Water1","Amorphous"],872:["Bug"],873:["Bug"],874:["Mineral"],875:["Water1","Field"],876:["Fairy"],877:["Field","Fairy"],878:["Field","Mineral"],879:["Field","Mineral"],880:["Undiscovered"],881:["Undiscovered"],882:["Undiscovered"],883:["Undiscovered"],884:["Mineral","Dragon"],885:["Amorphous","Dragon"],886:["Amorphous","Dragon"],887:["Amorphous","Dragon"],888:["Undiscovered"],889:["Undiscovered"],890:["Undiscovered"],891:["Undiscovered"],892:["Undiscovered"],893:["Undiscovered"],894:["Undiscovered"],895:["Undiscovered"],896:["Undiscovered"],897:["Undiscovered"],898:["Undiscovered"],899:["Field"],900:["Bug"],901:["Field"],902:["Water2"],903:["Field"],904:["Water2"],905:["Undiscovered"],"906":["Field","Grass"],"907":["Field","Grass"],"908":["Field","Grass"],"909":["Field"],"910":["Field"],"911":["Field"],"912":["Flying","Water 1"],"913":["Flying","Water 1"],"914":["Flying","Water 1"],"915":["Field"],"916":["Field"],"917":["Bug"],"918":["Bug"],"919":["Bug"],"920":["Bug"],"921":["Field"],"922":["Field"],"923":["Field"],"924":["Field","Fairy"],"925":["Field","Fairy"],"926":["Field","Mineral"],"927":["Field","Mineral"],"928":["Grass"],"929":["Grass"],"930":["Grass"],"931":["Flying"],"932":["Mineral"],"933":["Mineral"],"934":["Mineral"],"935":["Human-Like"],"936":["Human-Like"],"937":["Human-Like"],"938":["Water 1"],"939":["Water 1"],"940":["Water 1","Flying"],"941":["Water 1","Flying"],"942":["Field"],"943":["Field"],"944":["Field"],"945":["Field"],"946":["Grass"],"947":["Grass"],"948":["Grass"],"949":["Grass"],"950":["Water 3"],"951":["Grass"],"952":["Grass"],"953":["Bug"],"954":["Bug"],"955":["Flying"],"956":["Flying"],"957":["Fairy"],"958":["Fairy"],"959":["Fairy"],"960":["Water 3"],"961":["Water 3"],"962":["Flying"],"963":["Field","Water 2"],"964":["Field","Water 2"],"965":["Mineral"],"966":["Mineral"],"967":["Field"],"968":["Field"],"969":["Mineral"],"970":["Mineral"],"971":["Field"],"972":["Field"],"973":["Flying"],"974":["Field"],"975":["Field"],"976":["Water 2"],"977":["Water 2"],"978":["Water 2"],"979":["Field"],"980":["Water 1","Field"],"981":["Field"],"982":["Field"],"983":["Human-Like"],"984":["Undiscovered"],"985":["Undiscovered"],"986":["Undiscovered"],"987":["Undiscovered"],"988":["Undiscovered"],"989":["Undiscovered"],"990":["Undiscovered"],"991":["Undiscovered"],"992":["Undiscovered"],"993":["Undiscovered"],"994":["Undiscovered"],"995":["Undiscovered"],"996":["Dragon","Mineral"],"997":["Dragon","Mineral"],"998":["Dragon","Mineral"],"999":["Undiscovered"],"1000":["Undiscovered"],"1001":["Undiscovered"],"1002":["Undiscovered"],"1003":["Undiscovered"],"1004":["Undiscovered"],"1005":["Undiscovered"],"1006":["Undiscovered"],"1007":["Undiscovered"],"1008":["Undiscovered"],"1009":["Undiscovered"],"1010":["Undiscovered"],"1011":["Grass","Dragon"],"1012":["Mineral","Amorphous"],"1013":["Mineral","Amorphous"],"1014":["Undiscovered"],"1015":["Undiscovered"],"1016":["Undiscovered"],"1017":["Undiscovered"],"1018":["Mineral","Dragon"],"1019":["Grass","Dragon"],"1020":["Undiscovered"],"1021":["Undiscovered"],"1022":["Undiscovered"],"1023":["Undiscovered"],"1024":["Undiscovered"],"1025":["Undiscovered"]}; var legendaries = [ 144,145,146,150,151,243,244,245,249,250,251,377,378,379,380,381,382,383,384,385,386,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,638,639,640,641,642,643,644,645,646,647,648,649,716,717,718,719,720,721,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,888,889,890,891,892,893,894,895,896,897,898,905,1001,1002,1003,1004,1007,1008,984,985,986,987,988,989,990,991,992,993,994,995,1005,1006,1009,1010,1014,1015,1016,1017,1020,1021,1022,1023,1024,1025 ]; var ultraBeasts = [ // Have their BST set to 600 773,772,785,786,787,788,790,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809 ]; var paradoxPokemon = [ 984,985,986,987,988,989,990,991,992,993,994,995,1005,1006,1009,1010,1020,1021,1022,1023 ]; var regionalEvos = [ // Have their BST set to 560 862,863,864,865,866,867,899,900,901,902,903,904,979,980,981,982,66518,983,1011,1018,1019 ]; var pokeColors = { "red": [4, 5, 6, 45, 46, 47, 98, 99, 100, 101, 118, 119, 124, 126, 129, 136, 165, 166, 168, 193, 212, 218, 219, 224, 225, 233, 240, 250, 255, 256, 257, 265, 308, 318, 323, 338, 341, 342, 262495, 380, 383, 386, 401, 402, 131485, 467, 474, 479, 498, 499, 500, 513, 514, 538, 543, 545, 553, 554, 555, 557, 558, 560, 616, 617, 621, 624, 625, 628, 631, 653, 654, 655, 661, 662, 663, 697, 717, 725, 726, 727, 741, 66281, 66310, 131846, 776, 787, 794, 66337, 826, 850, 851, 889, 131484, 131657, 590490, 1049242, 721562, 1245850, 65682, 909,910,911,935,936,950,961,66514,988,991,1004,1007,66543,132079,197615,263151,132089], "blue": [1377125, 1442661, 1508197, 1573733, 1639269, 1704805, 1770341, 7, 8, 9, 65564, 29, 30, 31, 65574, 43, 44, 65588, 65589, 55, 60, 61, 62, 72, 73, 114, 116, 117, 130, 131, 134, 138, 139, 144, 147, 148, 158, 159, 160, 170, 171, 183, 184, 189, 194, 195, 202, 214, 230, 231, 245, 258, 259, 260, 276, 277, 283, 284, 294, 295, 298, 307, 319, 320, 321, 333, 334, 340, 131423, 358, 360, 363, 364, 365, 366, 367, 371, 373, 374, 375, 376, 378, 381, 382, 393, 394, 395, 403, 404, 405, 408, 409, 65958, 65959, 443, 444, 445, 447, 448, 453, 454, 456, 457, 458, 465, 471, 482, 489, 490, 501, 502, 503, 515, 516, 524, 525, 526, 527, 528, 535, 536, 537, 539, 66091, 564, 565, 580, 588, 603, 604, 605, 615, 633, 634, 635, 638, 642, 656, 657, 658, 678, 686, 687, 692, 693, 698, 699, 712, 713, 716, 728, 729, 730, 738, 746, 747, 748, 328454, 393990, 775, 789, 790, 131872, 816, 817, 818, 821, 822, 824, 845, 853, 875, 881, 883, 888, 787098, 1114778, 524954, 912,913,914,66467,937,963,964,66500,969,970,977,993,66535,1008,66544,132080,197616,263152,1009,66553,1023,1024], "yellow": [14, 15, 25, 26, 27, 28, 38, 52, 131124, 53, 54, 77, 78, 96, 97, 103, 125, 135, 145, 146, 155, 156, 157, 172, 181, 191, 192, 203, 206, 213, 239, 243, 267, 279, 291, 296, 310, 311, 312, 322, 337, 385, 414, 415, 416, 433, 466, 480, 488, 494, 540, 542, 559, 563, 566, 567, 595, 596, 612, 619, 647, 694, 695, 702, 66277, 742, 743, 197382, 778, 785, 66336, 197408, 807, 825, 835, 836, 4064101, 3670885, 3736421, 3801957, 3867493, 3933029, 3998565, 4784997, 4850533, 4916069, 4981605, 5047141, 5112677, 5178213, 870, 877, 878, 894, 197274, 1835877, 1901413, 1966949, 2032485, 2098021, 2163557, 2229093,921,922,923,926,132003,938,941,948,955,956,132050,982,66518,994,1000,1021], "green": [918373, 983909, 1049445, 1114981, 1180517, 1, 2, 3, 10, 11, 69, 70, 71, 65624, 65625, 123, 152, 153, 154, 167, 177, 178, 182, 186, 188, 246, 248, 251, 252, 253, 254, 269, 270, 271, 272, 286, 309, 315, 316, 329, 330, 331, 332, 346, 352, 357, 384, 387, 388, 389, 406, 407, 412, 413, 436, 437, 455, 469, 470, 492, 495, 496, 497, 511, 512, 541, 546, 547, 548, 549, 550, 556, 568, 569, 577, 578, 579, 610, 611, 66154, 622, 623, 640, 641, 650, 651, 652, 701, 718, 737, 751, 752, 764, 262918, 781, 797, 810, 811, 812, 829, 830, 833, 834, 840, 841, 842, 843, 844, 1246053, 1311589, 879, 880, 882, 885, 886, 887, 895, 898, 66121, 328346, 983706, 459418, 902, 906,907,908,918,928,929,930,931,939,951,952,954,967,995,1005,1010,1011,1012,1013,1017,1019], "black": [65542, 65555, 65556, 143, 197, 198, 201, 215, 228, 229, 303, 325, 336, 344, 353, 354, 355, 356, 430, 441, 446, 461, 477, 487, 491, 522, 523, 561, 562, 608, 609, 644, 664, 665, 66254, 131790, 731, 732, 733, 757, 758, 771, 796, 799, 800, 837, 838, 839, 893, 66428, 897, 131970, 904,940,949,983,1014,1015,1016], "brown": [3212133, 3277669, 3343205, 3408741, 3408741, 3474277, 3539813, 3605349, 13, 16, 17, 18, 20, 21, 22, 65562, 37, 50, 51, 56, 57, 58, 59, 63, 64, 65, 74, 75, 76, 83, 84, 85, 104, 105, 106, 107, 115, 120, 127, 128, 133, 140, 141, 149, 161, 162, 163, 164, 185, 216, 217, 220, 221, 234, 237, 244, 263, 273, 274, 275, 285, 287, 289, 292, 297, 324, 327, 328, 343, 349, 377, 390, 391, 392, 396, 397, 398, 399, 400, 65949, 418, 419, 427, 428, 438, 449, 450, 473, 485, 504, 505, 506, 534, 551, 552, 586, 606, 618, 626, 629, 630, 645, 659, 660, 667, 668, 672, 673, 679, 680, 681, 688, 689, 690, 691, 696, 708, 709, 710, 711, 721, 722, 723, 724, 734, 735, 744, 745, 131817, 749, 750, 769, 770, 774, 819, 820, 827, 828, 846, 847, 852, 863, 4195173, 4260709, 4326245, 4391781, 4457317, 4522853, 4588389, 65948, 197193, 131738, 393882, 918170, 852634, 900, 901,66452,927,932,933,934,942,946,947,953,980,981,999,1001,1003,1020], "purple": [19, 23, 24, 32, 33, 34, 41, 42, 48, 49, 88, 89, 90, 91, 92, 93, 94, 65641, 109, 110, 121, 132, 142, 150, 169, 190, 196, 205, 207, 210, 226, 236, 268, 301, 302, 314, 317, 326, 345, 65916, 65917, 421, 422, 423, 424, 425, 426, 429, 434, 435, 442, 451, 452, 472, 484, 509, 510, 574, 575, 576, 620, 649, 704, 705, 706, 714, 715, 720, 739, 197349, 755, 756, 761, 762, 763, 459526, 788, 792, 803, 804, 823, 848, 849, 854, 855, 861, 866, 871, 876, 890, 262810, 65680, 984,1025], "gray": [66, 67, 68, 65610, 65611, 65612, 81, 82, 95, 65646, 111, 112, 200, 204, 208, 211, 223, 227, 232, 247, 261, 262, 290, 299, 304, 305, 306, 313, 339, 347, 348, 351, 361, 362, 369, 379, 410, 411, 431, 432, 462, 464, 476, 507, 508, 519, 520, 521, 529, 530, 532, 533, 544, 570, 571, 572, 573, 589, 597, 598, 599, 600, 601, 632, 639, 646, 677, 703, 707, 736, 767, 768, 772, 773, 777, 782, 783, 784, 801, 802, 805, 808, 809, 814, 862, 867, 874, 891, 892, 66428, 656026, 899, 903,915,916,919,920,943,944,945,965,966,976,979,986,987,989,990,992,996,997,998,197625,1022], "white": [12, 65563, 65573, 65613, 65614, 86, 87, 65658, 175, 176, 179, 65758, 235, 249, 65799, 264, 266, 278, 280, 281, 282, 288, 335, 65887, 359, 372, 417, 459, 460, 468, 475, 478, 483, 486, 493, 66067, 66090, 197163, 131627, 581, 582, 583, 584, 587, 590, 591, 592, 593, 602, 607, 613, 614, 627, 636, 637, 643, 648, 666, 669, 670, 671, 674, 675, 676, 66214, 684, 685, 740, 765, 766, 780, 791, 793, 795, 798, 806, 813, 815, 831, 832, 864, 865, 868, 869, 66405, 131941, 197477, 263013, 328549, 394085, 872, 873, 884, 896, 66434, 66202,917,924,925,66461,197539,960,962,971,972,974,975,1002,1006,1018], "pink": [2753381, 2818917, 2884453, 2949989, 3015525, 3081061, 3146597, 459621, 525157, 590693, 656229, 721765, 787301, 852837, 35, 36, 39, 40, 79, 80, 102, 108, 113, 122, 137, 151, 173, 174, 180, 187, 199, 209, 222, 238, 241, 242, 293, 300, 350, 368, 370, 420, 65957, 439, 440, 463, 481, 517, 518, 531, 585, 594, 682, 683, 700, 719, 131813, 753, 754, 759, 760, 779, 786, 856, 857, 858, 859, 860, 3605349, 3670885, 3736421, 3801957, 3867493, 3933029, 3998565, 65615, 131152, 65735, 1180314, 905,957,958,959,968,973,978,985] }; /* Quest Variables */ var arenaOpponents = { nub: { name: "Trainer Nub", party: [19,50,316,582,116,27,194,207,200,570,489,"235",236,624,513,511,417,147], power: [10, 60], postArgs: { rewardAmt: 0, moneyReward: 5, cooldown: 0.1667, noRecords: true, taunt: true }, desc: "Arena NPC" }, pink: { name: "Trainer Pink", party: ["36",80,222,700,594,706,65838,472,205,423,308,620,368,429,510,151], power: [60, 130], postArgs: { rewardAmt: 1, cooldown: 1 }, desc: "Arena NPC" }, mustard: { name: "Trainer Mustard", party: [65,131743,38,203,"26",560,297,563,145,71,479,65964,15,28,135], power: [90, 170], postArgs: { rewardAmt: 2, cooldown: 1.25 }, desc: "Arena NPC" }, lorekeeper: { name: "Trainer Lorekeeper", party: [65844,376,887,214,867,748,62,719,31,642,"776",702,812,342,836], power: [110, 150], postArgs: { rewardAmt: 3, cooldown: 1.5 }, desc: "Arena NPC" }, cyan: { name: "Trainer Cyan", party: [448,202,539,476,635,593,376,"171",65959,445,66091,214,378,658,465], power: [110, 200], postArgs: { rewardAmt: 3, cooldown: 1.75 }, desc: "Arena NPC" }, crimson: { name: "Trainer Crimson", party: [131078,101,625,"663",212,342,553,538,721,149,45,197087,168,571,213, 308, 702, 702], power: [150, 300], postArgs: { rewardAmt: 6, cooldown: 2.5 }, desc: "Arena NPC" }, rainbow: { name: "Trainer Rainbow", party: [721,483,66023,462,272,442,411,227,429,563,323,208,630,373,65790,230,"59",66217], power: [200, 380], postArgs: { rewardAmt: 10, cooldown: 5 }, desc: "Arena NPC" } }; var recipeData = {}; var eliteData = []; var eliteHall = []; var pyrBonusMons = []; var journalDoneDeadline = 3*60*1000; /* Photo Variables */ var photoQuality = ["Disastrous", "Terrible", "Awful", "Bad", "Poor", "Okay", "Good", "Great", "Superb", "Excellent", "Perfect"]; var photoActions = { Any: ["eating", "playing", "dancing", "sleeping", "jumping", "singing", "whistling", "running", "standing still", "sitting", "walking", "stretching", "crying", "laughing", "yawning", "attacking", "smiling", "protecting itself"], Positive: ["eating", "playing", "dancing", "sleeping", "singing", "whistling", "stretching", "smiling"], Neutral: ["jumping", "walking", "standing still", "sitting", "laughing", "yawning"], Negative: ["running", "crying", "attacking", "protecting itself"], Normal: ["spinning", "screaming"], Fighting: ["punching", "kicking"], Flying: ["flying", "flapping their wings"], Poison: ["exhaling poison", "releasing some gas"], Ground: ["digging a hole", "throwing sand"], Rock: ["digging a hole", "throwing rocks"], Bug: ["hiding", "spreading powder"], Ghost: ["vanishing", "cursing"], Steel: ["hardening", "charging"], Fire: ["overheating", "breathing fire"], Water: ["swimming", "diving"], Grass: ["releasing spores", "releasing petals"], Electric: ["releasing sparks", "charging"], Psychic: ["meditating", "teleporting"], Ice: ["swimming", "cooling"], Dragon: ["howling", "rampaging"], Dark: ["taunting", "hiding"], Fairy: ["winking", "casting a spell"] }; var photoMood = { Positive: ["happy", "relaxed", "confident", "lively", "docile"], Neutral: ["indifferent", "calm", "distracted", "lazy"], Negative: ["angry", "anxious", "scared", "tired", "sad"] }; /* Misc Variables */ var stopQuests = {"collector": false, "scientist": false, "arena": false, "wonder": false, "tower": false, "pyramid": false, "alchemist": false, "arborist": false, "decoration": false, "league": false, "celebrity": false, "journal": false, "monger": false, "baking": false, "idol": false, "detective": false}; var tradeRequests = {}; var challengeRequests = {}; var challengeRequests2 = []; var pyramidRequests = {}; var bakingRequests = {}; var currentBattles = []; var currentPyramids = []; var currentBakings = []; var currentAuctions = []; var lastContests = []; var gachaJackpotAmount = 200; //Jackpot for gacha tickets. Number gets divided by 10 later. var gachaJackpot = (SESSION.global() && SESSION.global().safariGachaJackpot ? SESSION.global().safariGachaJackpot : gachaJackpotAmount); var allTrackers = (SESSION.global() && SESSION.global().allTrackers ? SESSION.global().allTrackers : ["blinky"]); var dailyBoost; var scientistQuest; var photographQuest; var ccatch = "ccatch"; var ccatch2 = "ccatchh"; var pendingActiveChanges = {}; var allowedSharedIPNames = []; var forbiddenNames = ["on", "off", "cancel", "name"]; var TYPE_NULL_NAME = "TypeNullName"; var colorTranslations = { "darkorchid": "#9932CC", "darkgreen": "#006400", "darkorange": "#FF8C00", "goldenrod": "#DAA520", "lightblue": "#ADD8E6", "crimson": "#DC143C", "peru": "#CD853F", "palevioletred": "#DB7093", "orangered": "#FF4500", "tomato": "#FF6347" }; var resources = { sprites: {}, shiny: {}, icons: {}, $: { sprites: { file: "scriptdata/safari/sprites.txt", url: Config.base_url + "scriptdata/safari/sprites.txt", type: "MemoryHash" }, shiny: { file: "scriptdata/safari/shiny.txt", url: Config.base_url + "scriptdata/safari/shiny.txt", type: "MemoryHash" }, icons: { file: "scriptdata/safari/icons.txt", url: Config.base_url + "scriptdata/safari/icons.txt", type: "MemoryHash" }, u: { file: "scriptdata/safari/u.txt", url: Config.base_url + "scriptdata/safari/u.txt", type: "MemoryHash" } } }; } /* Safari Functions */ function loadResource(r) { var resource = resources.$[r]; var file = resource.file; if (resource.type === "MemoryHash") { resources[r] = new MemoryHash(file); return null; } else if (resource.type === "JavaScript") { resources[r] = require(file); return null; } else { try { resources[r] = JSON.parse(sys.getFileContent(file)); return null; } catch (err) { return "An error occurred while loading Safari resource '" + resource.file.split("/").pop() + "'. (Error: " + err + ")"; } } } function downloadResource(r) { var resource = resources.$[r]; try { sys.webCall(resource.url, function (resp) { sys.writeToFile(resource.file, resp); ret = loadResource(r); if (ret !== null) { safaribot.sendAll(ret, staffchannel); } }); return null; } catch (err) { return "Couldn't download '" + resource.file.split("/").pop() + "'. (Error: " + err + ")"; } } function getAvatar(src) { if (SESSION.users(src)) { return SESSION.users(src).safari; } } function getAvatarOff(name) { var id = sys.id(name); var player; if (id) { player = getAvatar(id); } if (!player) { player = rawPlayers.get(name.toLowerCase()); if (player) { player = JSON.parse(player); } } return player; } function hasSave(name) { if (Object.keys(rawPlayers.hash).contains(name.toLowerCase())) { return true; } return false; } function isPlaying(name) { var id = sys.id(name); if (!id) { return false; } if (!getAvatar(id)) { return false; } if (!sys.isInChannel(id, safchan)) { return false; } return true; } function currentThemeName(obj) { if (obj.hasOwnProperty("day" + currentDay + "name") && !obj.useDefaultName) { return obj["day" + currentDay + "name"]; } return obj.name; } function themeName(obj) { if (Array.isArray(obj)) { return readable(obj.map(function(x){ if (x === "none") { return "Default"; } else { return currentThemeName(contestThemes[x]); } }), "or"); } if (obj in contestThemes) { return currentThemeName(contestThemes[obj]); } return "Default"; } function getAllThemeNames(commandLink) { return Object.keys(contestThemes).filter(function(e) { return e !== "none"; }).map(function(e) { return (commandLink ? link("/" + commandLink + " " + e, contestThemes[e].name) : contestThemes[e].name); }); } function loadLastId () { try { return parseInt(permObj.get("lastIdAssigned"), 10); } catch (err) { return 0; } } function validPlayers (scope, src, tar, selfmsg) { var self = (scope === "self" || scope === "both"); if (self) { var player = getAvatar(src); if (!player) { safaribot.sendHtmlMessage(src, "You need to enter the game first! Type /start for that. If you already had a save previously but can no longer find/access it, please contact a {0}!".format(link("/cauth", "Safari Admin")), safchan); return false; } } var other = (scope === "target" || scope === "both"); if (other) { var targetId = sys.id(tar); if (!targetId || !sys.isInChannel(targetId, safchan)) { safaribot.sendMessage(src, "No such person!", safchan); return false; } var allowShared = allowedSharedIPNames.contains(sys.name(src)) || allowedSharedIPNames.contains(tar.toLowerCase()) || sys.ip(src) === "::1%0"; if ((targetId == src || (sys.ip(targetId) === sys.ip(src) && !allowShared)) && selfmsg) { safaribot.sendMessage(src, selfmsg, safchan); return false; } var target = getAvatar(targetId); if (!target) { safaribot.sendMessage(src, "This person doesn't have a Safari Save!", safchan); return false; } } return true; } function canLosePokemon(src, data, verb, cantBecause, loseCount, ignoreShop) { if (!validPlayers("self", src)) { return; } if (!cantBecause) { /*if (contestCount > 0) { safaribot.sendMessage(src, "You can't " + verb + " a Pokémon during a contest!", safchan); return false; }*/ if (safari.isBattling(sys.name(src))) { safaribot.sendMessage(src, "You can't " + verb + " a Pokémon during a battle!", safchan); return; } if (currentEvent && currentEvent.isInEvent(sys.name(src))) { safaribot.sendMessage(src, "You can't " + verb + " a Pokémon during an event!", safchan); return; } } var amt = loseCount || 1; var player = getAvatar(src); if (player.pokemon.length <= amt) { safaribot.sendMessage(src, "You can't " + verb + " your remaining Pokémon!", safchan); return false; } var input = typeNull(data).split(":"); var info = getInputPokemon(input[0]); var id = info.id; if (!info.num) { safaribot.sendMessage(src, "Invalid Pokémon!", safchan); return false; } if (player.pokemon.indexOf(id) == -1) { safaribot.sendMessage(src, "You do not have that Pokémon!", safchan); return false; } var count = countRepeated(player.pokemon, id); if (amt > 1 && amt > count) { safaribot.sendMessage(src, "You do not have " + amt + " of that Pokémon!", safchan); return false; } if (player.party.length == 1 && player.party[0] === id && count <= 1) { safaribot.sendMessage(src, "You can't " + verb + " the only Pokémon in your party!", safchan); return false; } var pCount = countRepeated(player.party, id); if (amt > 1 && pCount === player.party.length && amt >= pCount && count <= amt) { safaribot.sendMessage(src, "You can't " + verb + " all the Pokémon in your party!", safchan); return false; } if (player.starter === id && count <= amt) { safaribot.sendMessage(src, "You can't " + verb + " your starter Pokémon!", safchan); return false; } if (player.story.box.contains(id) && count <= amt) { safaribot.sendMessage(src, "You can't " + verb + " " + poke(id) + "! It is required in Story Mode!", safchan); return false; } if (!ignoreShop && info.input in player.shop && player.shop[info.input].limit >= count) { safaribot.sendMessage(src, "You need to remove " + info.name + " from your shop before you can " + verb + " it!", safchan); return false; } if (pokeInfo.forme(info.num) > 0 && isMega(info.num)) { safaribot.sendMessage(src, "You can't " + verb + " a Pokémon while they are Mega Evolved!", safchan); return false; } return true; } function chance(value) { return Math.random() < value; } function cantBecause(src, action, arr, item, silent) { var player = getAvatar(src); if (arr.contains("tutorial")) { if (player.tutorial.inTutorial) { if (!silent) safaribot.sendHtmlMessage(src, "You cannot " + action + " at this stage in the tutorial! If you forgot what to do, use " + link("/tutorial") + ".", safchan); return true; } } if (item) { if (!(item in itemData)) { if (!silent) safaribot.sendMessage(src, item + " is not a valid item!", safchan); return true; } if (arr.contains("item")) { if (player.balls[item] < 1) { if (!silent) safaribot.sendMessage(src, "You don't have any " + finishName(item) + "!", safchan); return true; } } } if (arr.contains("contest")) { if (contestCount > 0 && !contestForfeited.contains(player.idnum)) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " during a Contest!", safchan); return true; } } /*if (arr.contains("distortion")) { if ((contestCount > 0 && !contestForfeited.contains(player.idnum)) && currentThemeEffect == "distortion") { if (!silent) safaribot.sendMessage(src, "You can't " + action + " during the twisted dimensions!", safchan); return true; } }*/ if (arr.contains("wild")) { if ((currentPokemon && contestCount === 0) || (currentPokemon && contestCount > 0 && !contestForfeited.contains(player.idnum))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " while a Wild Pokemon is out!", safchan); return true; } } if (arr.contains("precontest")) { if (contestCooldown <= 181) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " with less than 3 minutes before the next Contest starts!", safchan); return true; } } if (arr.contains("auction")) { if (safari.isInAuction(sys.name(src))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " while participating in an auction!", safchan); return true; } } if (arr.contains("battle")) { if (safari.isBattling(sys.name(src))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " during a battle!", safchan); return true; } } if (arr.contains("story")) { if ((contestCount > 0 && !contestForfeited.contains(player.idnum))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " during Story Mode!", safchan); return true; } } if (arr.contains("event") && currentEvent) { if (currentEvent.isInEvent(sys.name(src))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " during an event!", safchan); return true; } } if (arr.contains("event") && currentGame) { if (currentGame.playerInGame(sys.name(src))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " during an event!", safchan); return true; } } if (arr.contains("pyramid")) { for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(sys.name(src))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " while inside the Pyramid!", safchan); return true; } } } if (arr.contains("baking")) { for (var p in currentBakings) { if (currentBakings[p].isInKitchen(sys.name(src))) { if (!silent) safaribot.sendMessage(src, "You can't " + action + " while Baking!", safchan); return true; } } } return false; } function tutorMsg(src, mess) { if (![".", "?", "!"].contains(mess.charAt(mess.length-1))) { mess = mess + "."; } tutorbot.sendHtmlMessage(src, toColor(mess, "DarkOrchid"), safchan); } function advanceTutorial(src, step) { var player = getAvatar(src); player.tutorial.step = step; safari.progressTutorial(src, step); } function welcomePack(src, complete) { var player = getAvatar(src); player.tutorial.inTutorial = false; var rew = []; if (complete) { var cash = 300; player.money = cash; var giftpack = { safari: 30, great: 5, ultra: 1, dust: 100, itemfinder: 30, golden: 5}; for (var e in giftpack) { if (giftpack.hasOwnProperty(e)) { player.balls[e] = giftpack[e]; rew.push(plural(giftpack[e], e)); } } player.balls.permfinder = 0; player.records.goldenBaitWeak = 5; var johto; switch (player.starter) { case 1: johto = 155; player.pokemon.push(155); break; case 4: johto = 158; player.pokemon.push(158); break; case 7: johto = 152; player.pokemon.push(152); break; } safaribot.sendMessage(src, "You received $" + cash + ", " + rew.join(", ") + ", and " + an(pokePlain(johto)) + "!", safchan); tutorbot.sendHtmlMessage(src, toColor("One last advice: You can use " + link("/gbait") + " to use " + an(finishName("golden")) + ", a special and more efficient bait!", "DarkOrchid"), safchan); //sys.sendAll("", safchan); //tutorbot.sendHtmlAll(toColor(Congratulations to " + html_escape(sys.name(src)) + " on completing the tutorial!", "DarkOrchid"), safchan); //sys.sendAll("", safchan); } else { var cash = 300; player.money = cash; var giftpack = { safari: 30, great: 5, itemfinder: 15 }; for (var e in giftpack) { if (giftpack.hasOwnProperty(e)) { player.balls[e] = giftpack[e]; rew.push(plural(giftpack[e], e)); } } safaribot.sendMessage(src, "You received $" + cash + ", " + readable(rew, "and") + "!", safchan); } } function resetVars(saveContest) { preparationPhase = 0; preparationThrows = {}; preparationFirst = null; baitCooldown = sys.rand(4,7); goldenBaitCooldown = sys.rand(8,10); deluxeBaitCooldown = 1; currentPokemon = null; currentTypeOverride = null; currentExtraBST = 0; currentDisplay = null; currentDisplayBST = 0; wildEvent = false; wildTera = false; wildBallThrows = {}; bufferThrows = {}; currentThrowers = []; currentPokemonCount = 1; lastPokemonCount = 1; currentBaiter = null; if (!saveContest) { currentTheme = null; currentThemeAlter = false; if (currentThemeEffect) { if (currentThemeEffect == "portal") { safaribot.sendHtmlAll("The portal was sealed!", safchan); } else if (currentThemeEffect == "past") { safaribot.sendHtmlAll("The timeline was reverted!", safchan); } else if (currentThemeEffect == "distortion") { safaribot.sendHtmlAll("The twisted dimensions returned to normal!", safchan); } else if (["rain","hail","sand","sun"].contains(currentThemeEffect)) { safaribot.sendHtmlAll("The effects of the weather wore off!", safchan); } } currentThemeEffect = false; currentThemeSecondary = false; chosenThemes = null; currentThemeFlavor = null; contestVotes = null; } } function getMaxThrows(num, count, shiny, thrown, boost) { var amt = maxThrows; var bst = getBST(num); if (inclusive(bst, 0, 360)) { amt += 7; } else if (inclusive(bst, 360, 420)) { amt += 5; } else if (inclusive(bst, 420, 520)) { amt += 3; } else if (!isRare(num)) { amt += 1; } //else { // amt -= 1; //} amt += 3 * count; amt -= 3 * shiny; var pokeblockVal = Math.round((pokeblockThrows >= 3 ? 10 : (pokeblockThrows == 2 ? 8 : pokeblockThrows == 1 ? 5 : 0)) * (boost ? 1.5 : 1)); return Math.floor(Math.max(maxThrows, amt) * 0.5) + pokeblockVal - thrown; } /* Message Functions */ function sendAll(mess, html, system, bypass, rareWild) { var players = sys.playersOfChannel(safchan).filter(function(x) { if (rareWild) { // special case for event/rare wild mons to give people a chance to forfeit/quit or whatever to catch it if they want return true; } var name = sys.name(x); var isBypassed = bypass && bypass === name.toLowerCase(); if (currentEvent && currentEvent.isInEvent(name) && !isBypassed) { return false; } if (currentGame && currentGame.playerInGame(name) && !isBypassed) { return false; } for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(name) && !isBypassed) { return false; } } for (var p in currentBakings) { if (currentBakings[p].isInKitchen(name) && !isBypassed) { return false; } } for (var p in currentBattles) { if (currentBattles[p].battle2 && currentBattles[p].isInBattle(name) && !isBypassed) { return false; } } var player = getAvatar(x); if (player && player.tutorial.inTutorial && !isBypassed) { return false; } return true; }); if (system) { for (var e in players) { sys.sendHtmlMessage(players[e], mess, safchan); } } else if (html) { for (var e in players) { safaribot.sendHtmlMessage(players[e], mess, safchan); } } else { for (var e in players) { safaribot.sendMessage(players[e], mess, safchan); } } } /* Time Functions */ function now() { return new Date().getTime(); } function timeLeft(time) { return Math.floor((time - now())/1000) + 1; } function timeLeftString(time) { return utilities.getTimeString(timeLeft(time)); } function timeString(time, full) { return utilities.getTimeString(time, full) || "0 seconds"; } function getDay(time) { return Math.floor(time / (1000 * 60 * 60 * 24)); } function hours(number) { return number * 60 * 60 * 1000; } function cdSeconds(item, value) { value = value || "cooldown"; return "Cooldown: " + plural(itemData[item][value] / 1000, "second") + "."; } /* Data Type Functions */ function add(arr) { var result = 0; for (var e in arr) { result += arr[e]; } return result; } function countRepeated(arr, val) { var count = 0; arr.forEach(function(x) { count += x === val ? 1 : 0; }); return count; } function countArray(arr, item) { var first = arr.indexOf(item); var last = arr.lastIndexOf(item); var count = last - first + (first === -1 ? 0 : 1); return count; } function randomSample(hash) { var cum = 0; var val = Math.random(); var psum = 0.0; var x; var count = 0; for (x in hash) { if (hash.hasOwnProperty(x)) { psum += hash[x]; count += 1; } } if (psum === 0.0) { var j = 0; for (x in hash) { if (hash.hasOwnProperty(x)) { cum = (++j) / count; if (cum >= val) { return x; } } } } else { for (x in hash) { if (hash.hasOwnProperty(x)) { cum += hash[x] / psum; if (cum >= val) { return x; } } } } } function randomSampleObj(hash, returnId) { var sampled = {}; for (var e in hash) { sampled[e] = hash[e].chance; } if (returnId) { return randomSample(sampled); } else { return hash[randomSample(sampled)]; } } function isInRange(value, range) { // Range is a string with the syntax ">500", "<500" or "400~500" var v; if (range[0] === ">") { v = parseInt(range.substr(1), 10); if (!isNaN(v)) { return value >= v; } } else if (range[0] === "<") { v = parseInt(range.substr(1), 10); if (!isNaN(v)) { return value <= v; } } else if (range.indexOf("~") !== -1) { v = range.split("~"); var v1 = parseFloat(v[0]), v2 = parseFloat(v[1]); if (!isNaN(v1) && !isNaN(v2)) { return value >= v1 && value <= v2; } } return false; } function getRange(input) { var range = input.split("-"), lower, upper; if (range.length > 1) { lower = parseInt(range[0], 10); upper = parseInt(range[1], 10); } else { lower = 0; upper = parseInt(range[0], 10); } if (isNaN(lower) || isNaN(upper)) { return null; } if (lower === 0) { lower = 1; } return { lower: lower, upper: upper}; } function getArrayRange(arr, lower, upper) { var result = arr.concat(); if (lower >= 0) { return result.slice(Math.max(lower-1, 0), upper); } else { return result.slice(-upper, -lower); } } function countDuplicates(arr, val) { var out = 0; for (var i in arr) { if (arr[i] === val) { out++; } } return out; } function getCherished(mon, player) { if (!getAvatarOff(player)) { return 0; } return (Math.min(countDuplicates(getAvatarOff(player).cherished, pokeInfo.species(getInputPokemon(poke(mon)).num)), 10)); } function removeDuplicates(arr, onlyNumbers) { if (onlyNumbers) { var result = []; for (var x = 0; x < arr.length; x++) { if (!result.contains(arr[x])) { result.push(arr[x]); } } return result; } else { var result = {}; for (var x in arr) { result[arr[x]] = 1; } return Object.keys(result); } } function removeNonDuplicates(arr) { var hit = []; for (var j = arr.length; j--;) { if (hit.contains(arr[j])) { continue; } hit.push(arr.splice(j, 1)[0]); } return arr; } function lookupMoveLearners(h) { var outList = []; if (safari.moveLearners.hasOwnProperty(h+"")) { outList = safari.moveLearners[h+""]; } else if (moveOff(h)) { for (var e = 1; e < highestDexNum - 1; e++) { outList.push(e); } outList = outList.filter(function(x){ return (canLearnMove(x, h)); }); safari.moveLearners[h+""] = outList; permObj.add("moveLearners", JSON.stringify(safari.moveLearners)); } return outList; } function getAlternateEvolutions(num) { /* this doesn't accurately return ingame alt evos, only safari-based ones e.g. spewpa only evolving into base vivillon etc. stuff like floette that each share a base species, but evolve from different variants of that same base species are also not included here for best results, concatenate with getAllForms() */ if (devolutions.hasOwnProperty(num+"")) { var entry = devolutions[num+""]; if (evolutions.hasOwnProperty(entry+"") && Array.isArray(evolutions[entry+""].evo)) { return evolutions[entry].evo.filter(function(e) { return e !== num }); } } return []; } function getAllForms(num, excludeSelf) { num = parseInt(num); var species = pokeInfo.species(num), ret = [], currentForm = 1; var currentId = species; if (currentId === 351) { // special case for castform which has a mislabeled form number (castform-sunny is 351-4 instead of 351-3) ret = [351, 65887, 131423, 262495]; } else { while (pokeInfo.valid(currentId)) { ret.push(currentId); currentId = species + 65536 * currentForm++; } } return ret.filter(function(e) { return excludeSelf ? e !== num : true }); } function ignoresWildAbilities(player) { var abilities = [104, 163, 164, 256, 296]; // Mold Breaker, Turboblaze, Teravolt, Neutralizing Gas, Mycelium Might var leader = safari.getEffectiveLead(player, true); for (var a in abilities) { if (canHaveAbility(leader, abilities[a])) { return abilities[a]; } } return false; } function compare(a,b) { if (a.sort < b.sort) { return -1; } else if (a.sort > b.sort) { return 1; } else { return 0; } } function countProps(obj) { //Stolen from http://stackoverflow.com/a/3849480 var count = 0; for (var k in obj) { if (obj.hasOwnProperty(k)) { count++; } } return count; } function objectEquals(v1, v2) { if (typeof(v1) !== typeof(v2)) { return false; } if (typeof(v1) === "function") { return v1.toString() === v2.toString(); } if (v1 instanceof Object && v2 instanceof Object) { if (countProps(v1) !== countProps(v2)) { return false; } var r = true; for (var k in v1) { r = objectEquals(v1[k], v2[k]); if (!r) { return false; } } return true; } else { return v1 === v2; } } function inclusive(value, lower, upper) { return lower <= value && value <= upper; } function toCommandData(str, props) { var out = {}; var data = str.split(str.indexOf(":::") !== -1 && str.indexOf(":::") <= str.search(/:(?!:)/) ? ":::" : ":"); for (var p = 0; p < props.length; p++) { out[props[p]] = data.length > p && data[p].trim() !== "" ? data[p].trim() : null; } return out; } function toUserNames(str) { var data = str.split(","); for (var p = 0; p < data.length; p++) { data[p] = data[p].trim(); } return data; } function weatherMessage() { if (currentThemeEffect == "rain") { safaribot.sendHtmlAll(pokeInfo.icon(186) + " Looks like it's raining!", safchan); } else if (currentThemeEffect == "sunny") { safaribot.sendHtmlAll(pokeInfo.icon(38) + " Looks like it's sunny!", safchan); } else if (currentThemeEffect == "hail") { safaribot.sendHtmlAll(pokeInfo.icon(460) + " Looks like it's snowing!", safchan); } else if (currentThemeEffect == "sandstorm") { safaribot.sendHtmlAll(pokeInfo.icon(450) + " Looks like it's a sandstorm!", safchan); } } /* Formatting Functions */ function cap(string, sentence) { if (sentence) { return string.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); }); } else { return string.charAt(0).toUpperCase() + string.slice(1); } } function plural(qty, string, forceNumber, forceRawString) { var input = forceRawString ? string : getInput(string); var q = parseFloat(qty); var plur = isNaN(q) || Math.abs(q) !== 1; qty = addComma(qty); if (input) { if (input.type === "poke") { if (forceNumber) { return qty + " " + input.name; } else if (plur) { return toWord(qty) + " " + input.name; } else { return an(input.name); } } if (input.type === "item" && !isNaN(q)) { return qty + " " + (plur ? (itemData[input.id].plural ? itemData[input.id].plural : es(input.name)) : input.name); } } return qty + " " + (plur ? es(string) : string); } function toWord(num) { //Realistically shouldn't need more than this. Also add comma will mess up anything 1000+ return ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"][num] || num; } function capitalizeFirst(str) { return str.charAt(0).toUpperCase() + str.substring(1); } function an(string) { var vowels = "aeiou"; string = string + ""; //For the numbers if (vowels.indexOf(string.charAt(0).toLowerCase()) > -1 || string.charAt(0) === "8" || string === "11" || string === "18" || string.toLowerCase().indexOf("x ") === 0) { string = "an " + string; } else { string = "a " + string; } return string; } function es(string) { var end = string.charAt(string.length-1); //Last character would be the quotes var preEnd = string.charAt(string.length-2); var lasttwo = preEnd + end; if (["ch", "sh", "ss"].contains(lasttwo) || ["x", "z"].contains(end)) { return string + "es"; } if (end === "s") { return string; } if (end === "y" && !["a", "e", "i", "o", "u"].contains(preEnd)) { return string.slice(0, -1) + "ies"; } return string + "s"; } function readable(arr, last_delim) { if (!Array.isArray(arr)) { return arr; } if (arr.length > 1) { return arr.slice(0, arr.length - 1).join(", ") + " " + (last_delim ? last_delim : "and") + " " + arr.slice(-1)[0]; } else if (arr.length == 1) { return arr[0]; } else { return ""; } } function getOrdinal(n) { //Shamelessly stolen from http://stackoverflow.com/questions/23291256/ext-form-field-number-formatting-the-value var s=["th","st","nd","rd"], v=n%100; return n+(s[(v-20)%10]||s[v]||s[0]); } function addSign(num) { return num >= 0 ? "+" + num : num + ""; } function addComma(num) { if (typeof num !== "number") { return num; } return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } function stripComma(string) { return string.replace(/(?!=\d),(?=\d)/g, ""); } function link(string, string2, setmsg, color) { string2 = string2 || string; return "" + html_escape(string2) + ""; } function toColor(str, color) { if (colorTranslations.hasOwnProperty(color.toLowerCase())) { color = colorTranslations[color.toLowerCase()]; } return "" + str + ""; } function addFlashTag(name) { return "" + name + ""; } function addColorTag(name) { return "" + name + ""; } /*function toFlashing(message, name) { //Totally not stolen from tours var newmessage = message; var htmlname = html_escape(name); var regex = new RegExp(htmlname, "gi"); var newregex1 = ""; if (sys.os(sys.id(name)) === "android") { newregex1 = "" + htmlname.toCorrectCase() + ""; } else if (sys.os(sys.id(name)) === "webclient") { newregex1 = "" + htmlname.toCorrectCase() + ""; } else { newregex1 = "" + htmlname.toCorrectCase() + ""; } newmessage = message.replace(regex,newregex1); return newmessage; }*/ function toFlashing(message, name) { //Totally not stolen from tours var newmessage = message; var flashtag = ""; var htmlname = html_escape(name); var regex = new RegExp(flashtag+htmlname+flashtag, "gi"); var newregex1 = ""; if (sys.os(sys.id(name)) !== "android") { newregex1 = "" + htmlname.toCorrectCase() + ""; } else { newregex1 = "" + htmlname.toCorrectCase() + ""; } var flashregex = new RegExp(flashtag,"g"); newmessage = message.replace(regex,newregex1).replace(flashregex,""); return newmessage; } function toColored(message, name) { var newmessage = message; var flashtag = ""; var htmlname = html_escape(name); var regex = flashtag+htmlname+flashtag; var newregex1 = ""; var color = sys.id(name) ? script.getColor(sys.id(name)) : "#000000"; if (sys.os(sys.id(name)) !== "android") { newregex1 = "" + htmlname + ""; } else { newregex1 = "" + htmlname + ""; } var flashregex = new RegExp(flashtag,"g"); newmessage = message.replace(regex,newregex1).replace(flashregex,""); return newmessage; } function percentage(num, denom, places) { places = places || 2; if (typeof num !== "number" || typeof denom !== "number") { return "??.??%"; } if (denom === 0) { return "0.00%"; } return (num/denom*100).toFixed(places) + "%"; } function toFixed(num, digits) { return (+num).toFixed(digits).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1'); } function escapeRegExp(str) { //From http://stackoverflow.com/a/6969486 return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); } function itemsLeft(player, item, itemName) { var amt = player.balls[item]; if (amt === 0) { return "You have no more " + es(finishName(item)) + " left! "; } else { return "You still have " + plural(amt, finishName(item)) + " left! "; } } function typeNull(str, output) { return str.replace(/(type: null)|(type:null)/gi, (output || "772")); } /* Pokemon Functions */ function getInputPokemon(info) { /* Use this function for every time you need information about a Pokémon typed by a player (don't use for pokémon picked from player.pokemon). Returns an object with the following properties: -num: Int; Pokémon's number; Same that you would get with sys.pokeNum() -id: Int or String; Pokémon's id that can be added to player.pokemon -shiny: Boolean; If the pokémon is shiny or not -name: String; Pokémon's name (including Shiny prefix). Same as poke() -input: String; What you need to type to get to that result. Used for trade */ var shiny = false, id, num, name; info = info.replace(/flabebe|flabébe|flabebé/gi, "flabébé").toLowerCase(); info = info.replace(/pokédex|pokedex/gi, "pokédex").toLowerCase(); info = info.replace(/alcremie-finale/gi, "alcremie-finalé").toLowerCase(); if ((info.length > 1 && (info[0] == "*" || info[info.length-1] == "*")) || info.indexOf("shiny ") === 0) { shiny = true; info = info.replace("*", ""); info = info.replace("shiny ", ""); } var arr = info.split("-"); if (arr.length === 2 && !isNaN(arr[0]) && !isNaN(arr[1])) { num = parseInt(arr[0], 10) + (parseInt(arr[1], 10) * 65536); } else { num = parseInt(info, 10); if (isNaN(num)) { num = getPokeNum(info); } } id = shiny ? num + "" : num; name = pokePlain(num); if (name.toLowerCase() == "missingno") { num = null; } return { num: num, id: id, shiny: shiny, name: poke(id), input: (shiny ? "*" : "") + name, type: "poke" }; } function getInputMove(src, data) { var num = parseInt(data, 10), name; if (isNaN(num)) { num = movenum(data); } name = moveOff(num); safaribot.sendMessage( src,"Move #" + num + ": " + name + ".",safchan ); return; } function getInputAbility(src, data) { var num = parseInt(data, 10), name; if (isNaN(num)) { num = abilitynum(data); } name = abilityOff(num); safaribot.sendMessage( src,"Ability #" + num + ": " + name + ".",safchan ); return; } function getPokemonInfo(info) { var shiny = false, id = info; if (typeof info == "string") { shiny = true; id = parseInt(info, 10); if (isNaN(id)) { id = null; } } return [id, shiny]; } function poke(num, multi) { var shiny = false, name; if (typeof num === "string") { num = parseInt(num, 10); shiny = true; } if (isNaN(num)) { return null; } if (ultraPokes.hasOwnProperty(num+"")) { name = ultraPokes[num+""].name; } else { name = sys.pokemon(num); } if (permObj.get("usingLangPack") == "true" && multi) { if (!langPack) { langPack = sys.getFileContent("scriptdata/safari/" + permObj.get("langPackFile")).split("\n"); } name = poke(num).replace(poke(pokeInfo.species(num)), langPack[pokeInfo.species(num) - 1].trim()); } return name ? (shiny ? "Shiny " : "") + name : null; } function pokePlain(num, multi) { var name; if (typeof num === "string") { num = parseInt(num, 10); } if (isNaN(num)) { return "Missingno"; } if (ultraPokes.hasOwnProperty(num+"")) { name = ultraPokes[num+""].name; } else { name = sys.pokemon(num); } if (permObj.get("usingLangPack") == "true" && multi) { if (!langPack) { langPack = sys.getFileContent("scriptdata/safari/" + permObj.get("langPackFile")).split("\n"); } name = pokePlain(num).replace(pokePlain(pokeInfo.species(num)), langPack[pokeInfo.species(num) - 1].trim()); } return name ? name : "Missingno"; } function getForm(num, amt) { if (!(amt)) { amt = 1; } return Math.round(num + (65536 * amt)); } function movenum(name) { if (name.toLowerCase() === "vise grip") { name = "Vice Grip"; } var out = sys.moveNum(name); if (out == "undefined" || (!out)) { var hit = false; for (var i in ultraMoves) { if (!(ultraMoves[i].hasOwnProperty("name"))) { continue } if (ultraMoves[i].name.toLowerCase() == name.toLowerCase()) { out = parseInt(i, 10); break; } } } return out; }; function moveOff(id) { if (id < 702) { var move = sys.move(id); // Vice Grip is renamed to Vise Grip in Gen 8 return move === "Vice Grip" ? "Vise Grip" : move; } if (ultraMoves.hasOwnProperty(id+"")) { return ultraMoves[id+""].name; } return false; }; function getMoveBP(move) { if (ultraMoves.hasOwnProperty(move+"")) { if (ultraMoves[move+""].hasOwnProperty("power")) { return ultraMoves[move+""].power; } } return pokedex.getMoveBP(move); }; function moveType(move) { if (ultraMoves.hasOwnProperty(move+"")) { if (ultraMoves[move+""].hasOwnProperty("type")) { return ultraMoves[move+""].type; } } return sys.moveType(move); }; function getPokeNum(name) { var out = sys.pokeNum(name); if (!(out)) { for (var a in ultraPokes) { if (ultraPokes[a].name.toLowerCase() == name.toLowerCase()) { out = parseInt(a, 10); } } } return out; }; function type1(pokeNum) { if (ultraPokes.hasOwnProperty(pokeNum)) { return ultraPokes[pokeNum+""].types[0]; } return sys.type(sys.pokeType1(pokeNum)); } function type2(pokeNum) { if (ultraPokes.hasOwnProperty(pokeNum)) { return ultraPokes[pokeNum+""].types[1]; } return sys.type(sys.pokeType2(pokeNum)); } function hasType(pokeNum, type) { return type1(pokeNum) == type || type2(pokeNum) == type; } function getHeight(pokeNum) { if (ultraPokes.hasOwnProperty(pokeNum)) { return ultraPokes[pokeNum+""].height; } return pokedex.getHeight(pokeNum); } function getWeight(pokeNum) { if (ultraPokes.hasOwnProperty(pokeNum)) { return ultraPokes[pokeNum+""].weight; } return pokedex.getWeight(pokeNum); } function fetchMoves(num) { var out = []; var moves = []; var id = parseInt(num, 10); var species = pokeInfo.species(id); if (ultraPokes.hasOwnProperty(num + "") && ultraPokes[num + ""].hasOwnProperty("moves")) { out = ultraPokes[num+""].moves; } else if (species >= 803) { // Some in-battle and cosmetic forms do not have moves explicitly defined, so default to moves for base species of new Pokemon return fetchMoves(species); } else { moves = pokedex.getAllMoves(id); if (typeof moves === "undefined" || moves.length === 0) { if (getInputPokemon(""+id).name === "Missingno") { moves = [33]; // Tackle safaribot.sendAll("No moves found in fetchNum with parameter: " + num, staffchannel); } else moves = fetchMoves(species); } } var mn = 0; for (var i in ultraMoves) { mn = parseInt(i, 10); if (out.contains(mn)) { continue; } if (ultraMoves[i].hasOwnProperty("learned")) { if (ultraMoves[i].learned.contains(num)) { out.push(mn); } } } if (moves) { for (var i = 0; i < moves.length; i++) { var moveNum = moves[i]; if (Object.keys(moveBlacklist).contains(moveNum) && moveBlacklist[moveNum].contains(id)) { // if mon is listed under that move's blacklist moves.splice(i, 1); // override pokedex.getAllMoves by deleting it here i--; } } } if ((!moves) || (moves && moves.length == 0)) { return removeDuplicates(out); } if (out.length > 0) { moves = moves.concat(out); } return removeDuplicates(moves); } function canLearnMove(num, moveNum) { var id = parseInt(num, 10); var moves = fetchMoves(id); var move = moveNum + ""; if (!moves) { moves = fetchMoves(pokeInfo.species(id)); } return moves.contains(move); } function abilityOff(id) { if (id < 233) { return sys.ability(id); } if (ultraAbilities.hasOwnProperty(id+"")) { return ultraAbilities[id+""].name; } return false; } function abilitynum(name) { var out = sys.abilityNum(name); if (out == "undefined" || (!out)) { for (var i in ultraAbilities) { if (!(ultraAbilities[i].hasOwnProperty("name"))) { continue } if (ultraAbilities[i].name.toLowerCase() == name.toLowerCase()) { out = parseInt(i, 10); break; } } } return out; } function getPokeAbility(pokeNum, num) { if (isMega(pokeNum) && num !== 0) { return 0; } if (ultraPokes.hasOwnProperty(pokeNum+"")) { if (!(ultraPokes[pokeNum + ""].hasOwnProperty("abilities"))) { return 0; } var abilities = ultraPokes[pokeNum + ""].abilities.map(function(a) { return abilitynum(a); }); if (num < abilities.length && num >= 0) { return abilities[num]; } else { return 0; } } if (updatedAbilities.hasOwnProperty(pokeNum + "")) { var abilities = updatedAbilities[pokeNum + ""]; if (num < abilities.length && num >= 0) { return abilities[num]; } else { return 0; } } return sys.pokeAbility(pokeNum, num) || 0; } function canHaveAbility(num, abilityNum) { return [0,1,2].map(function(x) { return getPokeAbility(num, x); }).contains(abilityNum); } function getStats(pokeNum) { if (ultraPokes.hasOwnProperty(pokeNum+"")) { return ultraPokes[pokeNum+""].stats.slice(0); // slice so the returned array can be modified without affecting ultraPokes } if (updatedStats.hasOwnProperty(pokeNum + "")) { return updatedStats[pokeNum + ""].slice(0); } return sys.pokeBaseStats(pokeNum); } function getStatsNamed(pokeNum) { var st, out = {}; if (ultraPokes.hasOwnProperty(pokeNum+"")) { st = ultraPokes[pokeNum+""].stats; } else if (updatedStats.hasOwnProperty(pokeNum + "")) { st = updatedStats[pokeNum + ""]; } else { st = sys.pokeBaseStats(pokeNum); } out["HP"] = st[0]; out["Attack"] = st[1]; out["Defense"] = st[2]; out["Special Attack"] = st[3]; out["Special Defense"] = st[4]; out["Speed"] = st[5]; return out; } function getBST(pokeNum) { return add(getStats(pokeNum)); } function getPrice(pokeNum, shiny, perkBonus, fortuneBonus, costumeBonus) { return Math.round(getBST(pokeNum) * (getBST(pokeNum) >= 600 ? 2 : 1) * (getBST(pokeNum) >= 660 ? 4 : 1) * (shiny ? 5 : 1) * (isLegendary(pokeNum) ? 10 : 1) * (perkBonus ? perkBonus : 1) * (fortuneBonus ? fortuneBonus : 1) * (costumeBonus ? costumeBonus : 1)); } function isMega(num) { return megaPokemon.indexOf(parseInt(num)) !== -1; } function isLegendary(num) { return legendaries.indexOf(pokeInfo.species(num)) !== -1; } function getInput(info) { var input = getInputPokemon(info); if (!input.num) { if (info[0] == "@") { info = info.substr(1); } info = itemAlias(info, true); if (!(allItems)) { return false; } if (allItems.indexOf(info) === -1) { return false; } input = { input: "@" + info, id: info, name: finishName(info), type: "item" }; } return input; } function hasPokeInShop(src, remove) { var player = getAvatar(src); if (player.shop) { var isInShop = []; for (var e = 0; e < player.party.length; e++) { var id = player.party[e]; id += typeof id === "string" ? "*" : ""; var input = getInputPokemon(id); id = input.input; var count = countRepeated(player.pokemon, input.id); if (player.shop.hasOwnProperty(id)) { var lim = player.shop[id].limit; if (lim > 0 && count - lim < countRepeated(player.party, input.id)) { if (!isInShop.contains(input.name)) { isInShop.push(input.name); if (remove) { delete player.shop[id]; } } } } } if (isInShop.length > 0) { if (remove) { safaribot.sendMessage(src, "In order to participate in the event, " + readable(isInShop, "and") + " " + (isInShop.length === 1 ? "was" : "were") + " removed from your shop because it is also in your party!", safchan); return false; } else { safaribot.sendMessage(src, "You need to remove " + readable(isInShop, "and") + " from your shop before you start this challenge!", safchan); return true; } } } return false; } function isRare(id, excludeMega) { if (typeof id == "string") { return true; } if (isLegendary(id)) { return true; } if (regionalEvos.contains(id)) { return true; } if (paradoxPokemon.contains(id)) { return true; } var base = pokeInfo.species(id), form = pokeInfo.forme(id); if (form > 0 && (!(base in wildForms) || form > wildForms[base])) { if (isMega(id) && excludeMega) { return false; } return true; } return false; } function findLink(id) { return link("/findd " + poke(id), poke(id)); } function typeIcon(type, string) { var text = "#fefefe"; var colors = { Normal: { bg: "#a8a878" }, Fighting: {bg: "#c03028" }, Flying: { bg: "#a890f0" }, Poison: { bg: "#a040a0" }, Ground: { bg: "#e0c068" }, Rock: { bg: "#b8a038" }, Bug: { bg: "#a8b820" }, Ghost: { bg: "#705898" }, Steel: { color: "#333", bg: "#b8b8d0" }, Fire: { bg: "#f08030" }, Water: { bg: "#6890f0" }, Grass: { bg: "#4b9228" }, Electric: { color: "#333", bg: "#f8d030" }, Psychic: { bg: "#f85888" }, Ice: { color: "#333", bg: "#98d8d8" }, Dragon: { bg: "#7038f8" }, Dark: { bg: "#705848" }, Fairy: { bg: "#ee99ac" }, Stellar: { bg: "#7cc7b2" } }; if (type && colors[type] && ("color" in colors[type])) { text = colors[type].color; } var bg = "#a8a878"; if (type && colors[type] && ("bg" in colors[type])) { bg = colors[type].bg; } return " " + (string ? string : type) + " "; } function generation(pokeNum, wordy, excludeAlts) { var num = pokeInfo.species(pokeNum); var ret = 1; var useAlt = false; if (isAlolanForm(pokeNum)) { ret = 7; } else if (isGalarianForm(pokeNum)) { ret = 8; } else if (isHisuianForm(pokeNum) || [66019, 66020].contains(pokeNum)) { // Dialga & Palkia Origin ret = 4; useAlt = !excludeAlts; } else if (isPaldeanForm(pokeNum)) { ret = 9; } else if (pokeNum == 66437) { // Ursaluna-Bloodmoon is from Kitakami ret = 9; useAlt = !excludeAlts; } else if (inclusive(num, 152, 251)) { ret = 2; } else if (inclusive(num, 252, 386)) { ret = 3; } else if (inclusive(num, 387, 493)) { ret = 4; } else if (inclusive(num, 494, 649) || num == 1018) { // Archaludon ret = 5; } else if (inclusive(num, 650, 721)) { ret = 6; } else if (inclusive(num, 722, 807)) { // Meltan (808) and Melmetal (809) will be considered to be from Kanto ret = 7; } else if (inclusive(num, 810, 898)) { ret = 8; } else if (inclusive(num, 899, 905)) { // New Hisui region Pokémon from Legends: Arceus ret = 4; useAlt = !excludeAlts; } else if (inclusive(num, 906, 1010) || inclusive(num, 1020, 1024)) { ret = 9; } else if (inclusive(num, 1011, 1017) || [1019, 1025].contains(num)) { // Kitakami ret = 9; useAlt = !excludeAlts } if (wordy) { var altRegions = ["None", "", "", "", "Hisui", "", "", "", "", "Kitakami"]; return useAlt && altRegions[ret] ? altRegions[ret] : generations[ret]; } else { return ret; } } function isAlolanForm(pokeNum) { return [65555, 65556, 65562, 65563, 65564, 65573, 65574, 65586, 65587, 65588, 65589, 65610, 65611, 65612, 65624, 65625, 65639, 65641].contains(parseInt(pokeNum, 10)); } function isGalarianForm(pokeNum) { return [131124, 65613, 65614, 65619, 65646, 65658, 65758, 65799, 65800, 66090, 131627, 197163, 66098, 66154, 65680, 65681, 65682, 65615, 131152, 65735].contains(parseInt(pokeNum, 10)); } function isHisuianForm(pokeNum) { return [65594, 65595, 65636, 65637, 65693, 65747, 65751, 66019, 66020, 66039, 66085, 131622, 66106, 66107, 66164, 66241, 66242, 66249, 66260].contains(parseInt(pokeNum, 10)); } function isPaldeanForm(pokeNum) { return [65664, 131200, 196736, 65730].contains(parseInt(pokeNum, 10)); } function getPokeColor(pokeNum) { var c, id = parseInt(pokeNum, 10); for (c in pokeColors) { if (pokeColors[c].contains(id)) { return c; } } id = pokeInfo.species(id); for (c in pokeColors) { if (pokeColors[c].contains(id)) { return c; } } return "Undefined"; } function getEggGroups(poke1) { var hold = eggdata[pokeInfo.species(poke1)]; if (!Array.isArray(hold)) { return ["None"]; } if (hold.length > 0) { return hold; } return ["None"]; } function hasCommonEggGroup(poke1, poke2) { var hold = eggdata[pokeInfo.species(poke1)]; for (var t in hold) { if (eggdata[pokeInfo.species(poke2)] && eggdata[pokeInfo.species(poke2)].contains(hold[t])) { return true; } } return false; } function hasPureNormal(party) { for (var p = party.length; p--; ) { if (hasType(party[p], "Normal") && hasType(party[p], "???")) { return true; } } return false; } function getPossibleEvo(id, data) { var num = parseInt(id, 10); var species = pokeInfo.species(num); if (evolutions.hasOwnProperty(id)) { species = parseInt(id); } var evoData = evolutions[species]; var possibleEvo = evoData.evo, evolveTo; if (Array.isArray(possibleEvo)) { if (data && possibleEvo.contains(getInputPokemon(data).num)) { evolveTo = getInputPokemon(data).num; } else { evolveTo = evoData.evo[sys.rand(0, possibleEvo.length)]; } } else { evolveTo = possibleEvo; } var forme = pokeInfo.forme(num); if (forme !== 0) { var tempForme = pokeInfo.calcForme(evolveTo, forme); if (pokePlain(tempForme).toLowerCase() !== "missingno") { evolveTo = tempForme; } /* Custom logic for Galarian Darumaka and Galarian Darmanitan as their forme numbers don't match Intended result: 554-1 --> 555-2 i.e 131627 (Galarian Darumaka --> Galarian Darmanitan) Actual result: 554-1 --> 555-3 i.e 131627-1 (Galarian Darumaka --> Galarian Darmanitan-Zen) Hence, we override this behaviour by forcing 131627 */ if (num === 66090) { evolveTo = 131627; } } return evolveTo; } /* Item & Costume Functions */ function itemAlias(name, returnGarbage, full) { name = name.toLowerCase(); for (var e in itemData) { if (itemData[e].aliases.indexOf(name) !== -1) { if (full) { return itemData[e].fullName; } else { return itemData[e].name; } } } if (returnGarbage) { if (name === "wild") { return "Wild Pokémon"; } else if (name === "nothing") { return "No item"; } else if (name === "recharge") { return "Recharge"; } else if (name === "valuables") { return "Valuables"; } else if (name === "milk") { return "Moomoo Milk"; } return name; } else { return "safari"; } } function finishName(name) { return itemAlias(name, true, true); } function costumeAlias(name, returnGarbage, full) { for (var e in costumeData) { if (costumeData[e].aliases.indexOf(name.toLowerCase()) !== -1) { if (full) { return costumeData[e].fullName; } else { return costumeData[e].name; } } } if (returnGarbage || name === "none") { return name; } else { return "error"; } } function costumeSprite(player, os) { var id = player.costume; var n = costumeAlias(id); if (id === "none") { return ""; } if (isNaN(id)) { if (costumeData.hasOwnProperty(n)) { id = costumeData[n].icon; } else { id = 1; } } if (n == "ace") { var info = player.aviData, data = []; data = base64acetrainer[info.style][info.tone][info.hair]; return ""; } for (var e in base64costumes) { if (e === costumeData[n].name) { return ""; } } if (os === "android") { return ""; } else if (os === "webclient") { return ""; } else { return ""; } } function decorationAlias(name, returnGarbage, full) { if (decorations.hasOwnProperty(name)) { return full ? decorations[name].name : name; } var n = name.toLowerCase(), dec; for (var e in decorations) { dec = decorations[e]; if (n === dec.name.toLowerCase() || (dec.aliases && dec.aliases.contains(n))) { if (full) { return dec.name; } else { return e; } } } if (returnGarbage || name === "none") { return name; } else { return "error"; } } function isBall(item) { for (var i in itemData) { if (itemData[i].aliases.indexOf(item) !== -1 && itemData[i].type === "ball") { return true; } } return false; } function getBall(item) { for (var i in itemData) { if (itemData[i].aliases.indexOf(item) !== -1 && itemData[i].type === "ball") { return i; } } return ""; } function bagRow (player, arr, isAndroid, textOnly, first, title) { var ret = "", item, item2; if (textOnly) { if (first) { if (title) { ret += "" + title + "
"; } ret += "Money: $" + addComma(player.money) + " | "; } else { ret += "
"; if (title) { ret += "
" + title + "
"; } } for (var i = 0; i < arr.length; i++) { item = itemData[arr[i]]; if (item.name === "itemfinder") { item2 = player.balls.itemfinder + player.balls.permfinder; } else { item2 = player.balls[arr[i]]; } ret += item.fullName + ": " + item2; if (i + 1 < arr.length) { ret += " | "; } } } else { if (isAndroid) { var linebreak = 6; if (first) { ret += "
Inventory
"; ret += ": $" + addComma(player.money); linebreak--; } for (var i = 0; i < arr.length; i++) { item = itemData[arr[i]]; if (item.name === "itemfinder") { item2 = player.balls.itemfinder + player.balls.permfinder; } else { item2 = player.balls[arr[i]]; } ret += " | : " + item2; linebreak--; if (linebreak === 0 || (i + 1 == arr.length)) { ret += "
"; } } } else { if (first) { ret += ""; ret += ""; } else { ret += ""; } for (var i = 0; i < arr.length; i++) { item = itemData[arr[i]]; ret += ""; } else { ret += item.fullName + "'>"; } } ret += ""; if (first) { ret += ""; } for (var i = 0; i < arr.length; i++) { if (arr[i] === "itemfinder") { item = player.balls.itemfinder + player.balls.permfinder; } else { item = player.balls[arr[i]]; } ret += ""; } ret += ""; } } return ret; } function toUsableBall(player, ball) { var picked, ballOrder = ["safari", "great", "ultra", "quick", "spy", "luxury", "heavy", "premier", "clone", "myth", "lightning", "level", "photo", "mirror", "uturn", "love", "inver", "spirit", "cherish", "master"]; var startFrom = 0; if (ball == ballOrder[0]) { startFrom = 1; } if (player.balls[ball] <= 0) { for (var e = startFrom; e < ballOrder.length; e++) { picked = ballOrder[e]; if (player.balls[picked] > 0) { return picked; } } } else { return ball; } return null; } function ballMacro(src, override) { var player = getAvatar(src); var name = sys.name(src); if (!player) { return; } if (!isRare(currentDisplay) && !wildEvent && !wildTera) { if ((currentEvent && currentEvent.isInEvent(name)) || (currentGame && currentGame.playerInGame(name)) || (player.tutorial.inTutorial && !override) || safari.isBattling(name)) { return; } for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(name)) { return; } } for (var p in currentBakings) { if (currentBakings[p].isInKitchen(name)) { return; } } if (player.story.inStory && (!(override))) { return; } } var ret = "", hasBalls = false, isAndroid = sys.os(src) === "android"; for (var i = 0; i < allBalls.length; i++) { var e = allBalls[i]; if (isBallAvailable(player, e)) { if (e === "master" && (currentPokemon && !isRare(currentDisplay) && !player.options.alwaysShowMasterBall)) { // use currentDisplay instead of currentPokemon, otherwise ballMacro can be used to see through disguises/illusions ret += "«" + itemData[e].fullName.split(" ")[0] + "» "; } else if (e === "cherish" && !player.options.alwaysShowCherishBall) { ret += "«" + itemData[e].fullName.split(" ")[0] + "» "; } else if (e !== "master" || !isAndroid) { ret += "«" + link("/" + ccatch + " " + itemData[e].name, itemData[e].fullName.split(" ")[0]) + "» "; hasBalls = true; } } } var ph = player.story.inStory ? "" : "«" + link("/photo", "Take Photo") + "» "; var pkblk = player.story.inStory ? "" : (wildEvent || wildTera ? "" : "«" + link("/pokeblock", "Feed") + "» "); if (isAndroid && isBallAvailable(player, "master")) { // use currentDisplay instead of currentPokemon, otherwise ballMacro can be used to see through disguises/illusions if (currentPokemon && !isRare(currentDisplay) && !player.options.alwaysShowMasterBall) { sys.sendHtmlMessage(src, " ±Throw:«" + cap(itemData.master.name) + "»", safchan); } else { sys.sendHtmlMessage(src, " ±Throw: «" + link("/" + ccatch + " " + itemData.master.name, cap(itemData.master.name)) + "»", safchan); } } var dashboard = player.story.inStory ? "" : "«" + link("/dashboard", "Dashboard") + "» "; var rock = player.balls.rock > 0 && !wildEvent && !isRare(currentDisplay) && !player.story.inStory ? "«" + link("/rockscare", "Scare") + "» " : ""; if (hasBalls) { safaribot.sendHtmlMessage(src, "Throw: " + ret + "[" + link("/" + ccatch + " cancel", "Cancel") + "] " + (player.balls.lens > 0 ? ph : "") + (player.balls.pokeblock > 0 ? pkblk : "") + rock + dashboard, safchan); } else if (player.balls.lens > 0) { safaribot.sendHtmlMessage(src, "Throw: " + ph + "[" + link("/" + ccatch + " cancel", "Cancel") + "] ", safchan); } } function isBallAvailable(player, ball) { if (player.story.inStory && player.story.state == "Catching") { return player.balls[ball] > 0 && (["safari", "great", "ultra"].contains(ball)); } return player.balls[ball] > 0 && (!currentRules || !currentRules.excludeBalls || (!currentRules.excludeBalls.contains(ball) || ball === "spirit")) && !(wildEvent && ball === "master") && ((wildSpirit && ball === "spirit") || (!wildSpirit) && (!(!safari.events.spiritDuelsEnabled && ball === "spirit"))); } function getCap(item) { return item in itemData ? (itemData[item].cap || itemCap) : itemCap; } function getPerkBonus(player, perk) { var info = itemData[perk], cap = info.maxRate; if (cap) { if ((player.costume === "rich" && ["amulet", "crown", "soothe"].contains(perk)) || (player.costume === "backpacker" && ["honey", "scarf", "battery"].contains(perk))) { cap *= costumeData[player.costume].rate; if (safari.hasCostumeSkill(player, "megaPacker")) { cap *= 1.1; } } if (player.costume === "preschooler" && perk === "eviolite") { cap *= costumeData[player.costume].rate2; } return Math.min(player.balls[perk] * info.bonusRate, cap); } else { return player.balls[perk] * info.bonusRate; } } function rewardCapCheck(player, reward, amount, withName, suppress) { reward = itemAlias(reward); var cap = getCap(reward); var master = reward === "master"; if (player.balls[reward] + amount > cap) { var check = cap - player.balls[reward]; var src = sys.id(player.id); if (!suppress) { if (src) { if (master) { safaribot.sendMessage(src, "As you try to put it away, the " + finishName(reward) + " starts to glow very bright and then shatters in your hands. Sadly, all you could do was carefully grab a salvageable piece and stow it safely in your bag.", safchan); } else if (check < 1) { safaribot.sendMessage(src, "However, you didn't have any space left and were forced to discard " + (withName ? "the " + plural(amount, reward) : (amount === 1 ? "it" : "them")) + "!", safchan); } else { safaribot.sendMessage(src, "However, you only had space for " + check + " and were forced to discard the rest" + (withName ? " of the " + finishName(reward) : "") + "!", safchan); } } //player.records.itemsDiscarded += (amount - check); } if (master) { check = amount + player.balls[reward] - cap; reward = "fragment"; } amount = check; if (amount < 0) { amount = 0; } } player.balls[reward] += amount; } function toMoney(str) { if (str[0] === "$") { str = str.substr(1); } return parseInt(str.replace(",", ""), 10); } function translateAsset(asset) { if (asset[0] == "$") { var amount = toMoney(asset); return { id: "$" + amount, input: asset, amount: amount, name: "$" + addComma(amount), type: "money" }; } else if (asset.indexOf("@") !== -1 || allItems.contains(itemAlias(asset, true))) { var item = itemAlias(asset.substr(asset.indexOf("@") + 1), true); if (!allItems.contains(item)) { return getInputPokemon(asset); } var amount = parseInt(asset.substr(0, asset.indexOf("@")), 10) || 1; return { id: item, input: "@" + item, amount: amount, name: itemAlias(item, true, true), type: "item" }; } else { return getInputPokemon(asset); } } function toStuffObj(raw) { var out = {}, info = typeNull(raw).replace(/,/g, ":").split(":"), data, n; for (var e = 0; e < info.length; e++) { n = info[e].trim(); if (!n) { continue; } data = translateAsset(n); if (data.type == "money") { if (!out.$) { out.$ = 0; } out.$ += data.amount; } else if (data.type == "item" || (data.type == "poke" && data.num)) { if (!(data.input in out)) { out[data.input] = 0; } out[data.input] += data.amount || 1; } } return out; } function toStuffInput(stuff) { var out = [], s, p, amt, asset; for (s in stuff) { amt = stuff[s]; if (amt === 0) { continue; } if (s[0] === "$") { out.push("$" + amt); } else if (s[0] === "@" || (allItems.contains(itemAlias(s, true)))) { asset = s; if (asset[0] === "@") { asset = asset.substr(1); } asset = itemAlias(asset, true); out.push((amt === 1 ? "" : amt) + "@" + asset); } else { asset = getInputPokemon(s); for (p = 0; p < amt; p++) { out.push(asset.name); } } } return out.join(","); } function validateStuff(str) { var info = typeNull(str).replace(/\,/g, ":").split(":"), out = [], data, n; for (var e = 0; e < info.length; e++) { n = info[e].trim(); if (!n) { continue; } data = translateAsset(n); if ((data.type === "poke" && !data.num) || (data.type === "item" && !allItems.contains(data.id)) || (data.type === "money" && isNaN(data.amount))) { out.push(info[e]); } } return out; } function translateStuff(stuff, forceNumber) { if (typeof stuff === "string") { stuff = toStuffObj(stuff.replace(/,/g, ":")); } var out = [], s, amt, asset; for (s in stuff) { amt = stuff[s]; if (s[0] === "$") { if (amt > 0) { out.push("$" + addComma(amt)); } else if (amt < 0) { out.push("-$" + addComma(-amt)); } } else if (s == "@expup") { out.push("EXP Up"); } else if (s[0] === "@" || (allItems.contains(itemAlias(s, true)))) { asset = s; if (asset[0] === "@") { asset = asset.substr(1); } asset = itemAlias(asset, true); if (amt !== 0) { out.push(plural(amt, asset)); } } else { asset = getInputPokemon(s); out.push(plural(amt, asset.input, forceNumber)); } } return readable(out); } function giveStuff(player, stuff, rawResult, isTrade) { var out = { gained: [], lost: [], discarded: [] }, asset, amt, total, max; if (!player) { return "nothing"; } if (stuff === "@expup") { out.gained.push((350 + (safari.getCostumeLevel(player) * 50) + " Costume Exp")); safari.costumeEXP(player, "alchemy", 350 + (safari.getCostumeLevel(player) * 50)); return out; } if (typeof stuff === "string") { asset = stuff; stuff = {}; if (asset[0] === "$") { stuff.$ = parseInt(asset.replace(",", "").substr(1), 10); } else if (asset.indexOf("@") >= 0) { amt = parseInt(asset.substr(0, asset.indexOf("@")), 10) || 1; asset = asset.substr(asset.indexOf("@")); stuff[asset] = amt; } else { asset = getInputPokemon(asset); stuff[asset.input] = 1; } } for (var s in stuff) { total = amt = stuff[s]; if (s[0] === "$") { player.money += amt; if (isTrade) safari.updateEconomyData(Math.abs(amt), "playerTrade"); if (amt > 0) { if (player.money > moneyCap) { out.discarded.push("$" + (player.money - moneyCap)); total = amt - (player.money - moneyCap); player.money = moneyCap; } out.gained.push("$" + addComma(total)); } else if (amt < 0) { if (player.money < 0) { total = amt - player.money; player.money = 0; } out.lost.push("$" + addComma(-total)); } } else if (s[0] === "@" || (allItems.contains(itemAlias(s, true)))) { asset = s; if (asset[0] === "@") { asset = asset.substr(1); } asset = itemAlias(asset, true); if (asset === "burn" && player.balls[asset] === 0 && amt > 0) { player.burnLastUsed = now(); } player.balls[asset] += amt; max = getCap(asset); if (player.balls[asset] > max) { out.discarded.push(plural(player.balls[asset] - max, asset)); total = amt - (player.balls[asset] - max); player.balls[asset] = max; } else if (player.balls[asset] < 0) { total = amt - player.balls[asset]; player.balls[asset] = 0; } if (asset === "entry") { rafflePlayers.add(player.id, player.balls.entry); safari.sanitizeRaffle(); } safari.updateShop(player, asset); if (total > 0) { out.gained.push(plural(total, asset)); } else if (total < 0) { out.lost.push(plural(-total, asset)); } } else { asset = getInputPokemon(s); total = 0; if (amt > 0) { for (var p = 0; p < amt; p++) { player.pokemon.push(asset.id); total++; } out.gained.push(plural(total, asset.input, rawResult)); } else if (amt < 0) { for (var p = 0; p < -amt; p++) { if (!player.pokemon.contains(asset.id)) { break; } safari.removePokemon2(player, asset.id); total++; } out.lost.push(plural(total, asset.input, rawResult)); } } } safari.sanitize(player); if (rawResult) { return out; } return stuffReceivedDesc(out); } function stuffReceivedDesc(out) { var res = []; if (out.gained.length > 0) { res.push("received " + readable(out.gained)); } if (out.lost.length > 0) { res.push("lost " + readable(out.lost)); } if (out.discarded.length > 0) { if (res.length > 0) { res.push("discarded " + readable(out.discarded) + " due to excess"); } else { res.push("could not receive " + readable(out.discarded) + " due to excess"); } } return res.length > 0 ? readable(res) : "received nothing"; } function hasStuff(player, stuff) { var out = { missing: [], needs: [] }, asset, amt, total; if (!player) { return out; } if (typeof stuff === "string") { stuff = toStuffObj(stuff.replace(/,/g, ":")); } for (var s in stuff) { amt = stuff[s]; if (s[0] === "$") { if (amt > 0 && player.money < amt) { out.missing.push("$" + amt); out.needs.push("$" + (amt - player.money)); } } else if (s[0] === "@" || (allItems.contains(itemAlias(s, true)))) { asset = s; if (asset[0] === "@") { asset = asset.substr(1); } asset = itemAlias(asset, true); if (amt > 0 && player.balls[asset] < amt) { out.missing.push(plural(amt, asset)); out.needs.push(plural(amt-player.balls[asset], asset)); } } else { asset = getInputPokemon(s); total = countRepeated(player.pokemon, asset.id); if (amt > 0 && total < amt) { out.missing.push(plural(amt, asset.input)); out.needs.push(plural(amt-total, asset.input)); } } } out.result = out.missing.length === 0; return out; } function canLoseStuff(player, stuff) { //TODO: Add an isTrade clause if (typeof stuff === "string") { stuff = toStuffObj(stuff); } var out = { missing: hasStuff(player, stuff).missing, shop: [], mega: [], tradeReq: [], partyEmpty: false, starter: false, result: true }, info, amt, e, party = {}, inBox, inShop; for (e = player.party.length; e--; ) { info = player.party[e]; info = (typeof info === "string" ? "*" : "") + pokePlain(info); if (!party.hasOwnProperty(info)) { party[info] = 0; } party[info] += 1; } for (e in stuff) { info = translateAsset(e); info.amount = stuff[e]; if (info.type === "money") { } else if (info.type === "item") { inShop = player.shop.hasOwnProperty(info.input) ? player.shop[info.input].limit : 0; if (player.shop.hasOwnProperty(info.input)) { if (player.balls[info.id] - player.shop[info.input].limit < info.amount) { out.shop.push(info.name); } else if ("tradeReq" in itemData[info.id] && player.balls[info.id] - info.amount - inShop < itemData[info.id].tradeReq) { out.shop.push(info.name); } } if ("tradeReq" in itemData[info.id] && player.balls[info.id] - info.amount < itemData[info.id].tradeReq) { out.tradeReq.push(info.name + " (" + itemData[info.id].tradeReq + ")"); } } else if (info.type === "poke") { amt = countRepeated(player.pokemon, info.id); inShop = player.shop.hasOwnProperty(info.input) ? player.shop[info.input].limit : 0; if (player.shop.hasOwnProperty(info.input) && amt - player.shop[info.input].limit < info.amount) { out.shop.push(info.name); } if (party.hasOwnProperty(info.input)) { inBox = amt - party[info.input] - inShop; if (info.amount > inBox) { party[info.input] -= info.amount - inBox; out.partyEmpty = true; } } if (player.starter === info.id && info.amount >= amt) { out.starter = true; } if (isMega(info.num)) { out.mega.push(info.name); } } } for (e in party) { if (party[e] > 0) { out.partyEmpty = false; break; } } out.result = (out.missing.length === 0 && out.shop.length === 0 && out.mega.length === 0 && out.tradeReq.length === 0 && !out.partyEmpty && !out.starter); return out; } function canReceiveStuff(player, stuff) { if (typeof stuff === "string") { stuff = toStuffObj(stuff); } var out = { money: false, poke: false, item: [], limit: [], result: true }, info, amt, e, pCount = 0; for (e in stuff) { info = translateAsset(e); amt = info.amount = stuff[e]; if (info.type === "money") { if (player.money + amt > moneyCap) { out.money = true; out.limit.push("$" + addComma(moneyCap - player.money)); } } else if (info.type === "item") { if (player.balls[info.id] + amt > getCap(info.id)) { out.item.push(plural(amt, info.id)); out.limit.push(plural(getCap(info.id) - player.balls[info.id], info.id)); } } else if (info.type === "poke") { pCount += amt; } } if (player.pokemon.length + pCount > getPerkBonus(player, "box")) { out.poke = true; out.limit.push(getPerkBonus(player, "box") - player.pokemon.length + " Pokémon"); } out.result = out.limit.length === 0; return out; } function removeInvisibleStuff(stuff) { if (typeof stuff === "string") { stuff = toStuffObj(stuff); } var e, info; for (e in stuff) { info = translateAsset(e); if (info.type === "item" && itemData[info.id].invisible) { delete stuff[e]; } } return stuff; } function mapAssetName(x) { var asset = translateAsset(x); if (asset.type === "money") { return "Money"; } return asset.name; } /* Wild Pokemon & Contests */ this.createWild = function(dexNum, makeShiny, amt, bstLimit, leader, player, appearAs, baitType, themeOverride, spawnTest, spiritMon, teraMon) { if (currentPokemon) { this.pokemonFlee(); } var num, pokeId, goldenBonus = baitType === "golden" && player.records.goldenBaitUsed >= player.records.goldenBaitWeak, shinyRand = sys.rand(0, shinyChance - (goldenBonus ? itemData.golden.shinyBonus : 0)), shiny = shinyRand < 1, statCap, amount = amt || 1, ignoreForms = false, canLegend = true, crystalEffect; if (amount > 1) { shiny = false; } if (makeShiny) { shiny = true; } var cType = {effect:"none"}; if (player && player.zcrystalUser && !isNaN(player.zcrystalUser)) { cType = getCrystalEffect(player.zcrystalUser); crystalEffect = player.zcrystalDeadline >= now() && player.zcrystalUser === player.party[0] ? zCrystalData[cType] : { effect: "none" }; } if (dexNum) { //Forced spawn of a specific mon num = parseInt(dexNum, 10); pokeId = poke(num); if (makeShiny) { pokeId = poke(dexNum); } } else { var cTheme = themeOverride || currentTheme; var isPortaled = false; if (cTheme) { if (currentThemeSecondary && currentThemeEffect == "portal" && chance(0.5)) { cTheme = currentThemeSecondary; isPortaled = true; } var theme = contestThemes[cTheme], list = []; if (spiritMon) { statCap = [810, 740, 695, 630, 605, 590][safari.events.spiritDuelsTeams.length - 2]; statMin = [480, 450, 420, 380, 320, 0][safari.events.spiritDuelsTeams.length - 2]; statCap -= (25 * sys.rand(0, 1)); for (i = 1; i < highestDexNum; i++) { bst = getBST(i); if (this.validForTheme(i, cTheme) && bst <= statCap && bst >= statMin) { list.push(i); } if (this.validForTheme(i, cTheme) && (i in megaEvolutions)) { var possibleEvo = megaEvolutions[i]; var evolveTo = possibleEvo[sys.rand(0, possibleEvo.length)]; if ((getBST(evolveTo)) <= statCap) { list.push(evolveTo); } } } if ((!(currentThemeAlter)) && (list.length > 0)) { for (h in theme.include) { id = theme.include[h]; bst = getBST(id); if (bst <= statCap && bst >= statMin) { list.push(id); } } } } else { var extraCap = (goldenBonus ? (itemData.golden.bstBonus + crystalEffect == "golden" ? 2 : 0) : 0); statCap = sys.rand(300, 601 + extraCap); var list = [], bst, extrabst = 0, truebst, extrabstChance = 1, h, i, id, extrabstChanceModifier = 0.22; var hitEvent = false; var includeContestVariations = themeOverride && theme.variations; var include = theme.include; if (theme.hasOwnProperty("day"+currentDay)) { include = include.concat(theme["day"+currentDay]); } if (includeContestVariations) { // mushroom can draw from any special variation pool for (var variation in theme.variations) { include = include.concat(theme.variations[variation]); } } else if (currentThemeEffect && !isPortaled) { include = include.concat(theme.variations[currentThemeEffect]); } if (currentThemeAlter && theme.alter) { // alter pool overrides everything include = theme.alter; } for (h in include) { id = include[h]; truebst = bst = "editBST" in theme && id in theme.editBST ? theme.editBST[id] : getBST(id); if (isRare(id) && theme.hasOwnProperty("editBST") && !theme.editBST.hasOwnProperty(""+id)) { // if rare mon and no editBST defined truebst = bst = Math.max(bst, defaultRareBST); // take the higher of its natural BST and defaultRareBST } extrabstChance = 1; if (bst > 600) { extrabst = (bst - 600); bst = 599; extrabstChance = ((((125 - extrabst) * (180 - extrabst)) / (240 - extrabst)) * 0.01 * extrabstChanceModifier); } if ((this.validForTheme(id, cTheme) || includeContestVariations) && bst <= statCap && chance(extrabstChance)) { list.push(id); if (isLegendary(id) && chance(0.9)) { var extras = 0; if (truebst >= 670) { extras = 5; } else if (truebst >= 660) { extras = 7; } else if (truebst >= 600) { extras = 6; } for (i = extras; i--;) { list.push(id); } } } if (theme && theme.eventFinal && theme.eventFlags && theme.eventFinal == id) { if (chance(0.02)) { var players = sys.playersOfChannel(safchan); for (var pid in players) { var player = getAvatar(players[pid]); if (player) { if (safari.allFlagsMet(player, theme.eventFlags)) { list = [id]; hitEvent = true; sendAll(poke(id) + " appeared because it sensed someone worthy!"); break; } } } } } if (hitEvent) { break; } } for (i = 1; i < highestDexNum; i++) { if (isRare(i)) { // rares should only be added to the list in the theme.include check above continue; } truebst = bst = "editBST" in theme && i in theme.editBST ? theme.editBST[i] : getBST(i); extrabstChance = 1; if (bst > 600) { extrabst = (bst - 600); bst = 599; extrabstChance = ((((125 - extrabst) * (180 - extrabst)) / (240 - extrabst)) * 0.01 * extrabstChanceModifier); } if (this.validForTheme(i, cTheme) && bst <= statCap && chance(extrabstChance) && !list.contains(id)) { list.push(i); } } } if (list.length === 0) { return; } num = list[sys.rand(0, list.length)]; pokeId = poke(num + (shiny ? "" : 0)); ignoreForms = true; } else { var defTheme = contestThemes.hasOwnProperty("none") ? contestThemes.none : {"name":"Default","types":[],"excludeTypes":[],"include":[],"exclude":[],"editBST":{},"floorBST":300,"ceilBST":600,"icon":0}; var maxRoll = bstLimit || 601 + (goldenBonus ? (itemData.golden.bstBonus + (crystalEffect == "golden" ? 2 : 0)) : 0); var bst; statCap = sys.rand(300 + (goldenBonus ? (itemData.golden.minBstBonus + (crystalEffect == "golden" ? 15 : 0)) : 0), maxRoll); if (leader) { var list = [], loops = 0, found = false, atk1 = type1(leader), atk2 = type2(leader), maxList = player.costume === "chef" ? costumeData.chef.rate : 7; if (safari.hasCostumeSkill(player, "superChef")) { maxList = costumeData.chef.rate + 5; } do { num = sys.rand(1, highestDexNum); bst = defTheme.hasOwnProperty("editBST") && defTheme.editBST.hasOwnProperty(""+num) ? defTheme.editBST[""+num] : getBST(num); if (isRare(num) && defTheme.hasOwnProperty("editBST") && !defTheme.editBST.hasOwnProperty(""+num)) { // if rare mon and no editBST defined bst = Math.max(bst, defaultRareBST); // take the higher of its natural BST and defaultRareBST } if (bst <= statCap) { var typeBonus = this.checkEffective([atk1, atk2], [type1(num), type2(num)], player.costume === "inver"); if ((typeBonus > 1)) { if (bst < 600 || (!(isLegendary(num))) || goldenBonus) { found = true; break; } } else { list.push(num); } } loops++; } while (list.length < maxList && loops < 50); if (!found) { if (list.length === 0) { do { num = sys.rand(1, highestDexNum); bst = defTheme.hasOwnProperty("editBST") && defTheme.editBST.hasOwnProperty(""+num) ? defTheme.editBST[""+num] : getBST(num); if (isRare(num) && defTheme.hasOwnProperty("editBST") && !defTheme.editBST.hasOwnProperty(""+num)) { // if rare mon and no editBST defined bst = Math.max(bst, defaultRareBST); // take the higher of its natural BST and defaultRareBST } pokeId = poke(num + (shiny ? "" : 0)); } while (!pokeId || bst > statCap || (isLegendary(pokeId) && bst >= 600)); } else { num = list[sys.rand(0, list.length)]; } } pokeId = poke(num + (shiny ? "" : 0)); } else { do { num = sys.rand(1, highestDexNum); bst = defTheme.hasOwnProperty("editBST") && defTheme.editBST.hasOwnProperty(""+num) ? defTheme.editBST[""+num] : getBST(num); if (isRare(num) && defTheme.hasOwnProperty("editBST") && !defTheme.editBST.hasOwnProperty(""+num)) { // if rare mon and no editBST defined bst = Math.max(bst, defaultRareBST); // take the higher of its natural BST and defaultRareBST } pokeId = poke(num + (shiny ? "" : 0)); } while (!pokeId || bst > statCap || (isLegendary(pokeId) && bst >= 600)); } } } if (spawnTest) { return num; } else { if ((isRare(num) || shiny) && !dexNum) { amount = 1; if ((isLegendary(num) || shiny) && contestCount > 0) { // if legendary or shiny and contest is active wildEvent = chance(0.5); } else if (getBST(num) > 600 && themeOverride && isLegendary(num)) { // if bst > 600 and is shroom bait if (getBST(num) > 670) { wildEvent = true; // > 670s are always event } else { wildEvent = chance(0.5); // the rest are 50/50 like standard contests } if (wildEvent) { restrictedThrowers.push(player.id); restrictedThrowTime = restrictedThrowTimeLimit; restrictedThrows = restrictedThrowLimit; } } } if (!ignoreForms && !dexNum && num in wildForms) { var pickedForm = sys.rand(0, wildForms[num] + 1); num = pokeInfo.calcForme(num, pickedForm); } if (dailyBoost && dailyBoost.pokemon === pokeInfo.species(num) && shinyRand < 2 && !noDailyBonusForms.contains(num)) { shiny = true; // wild botd mon has 2x chance to be shiny amount = 1; } if (noShinySprite.indexOf(num) !== -1 && !makeShiny) { shiny = false; } if (spiritMon) { amount = 1; shiny = false; wildSpirit = true; } if (teraMon) { amount = 1; if (currentExtraBST < 550) { currentExtraBST += 550 - getBST(num); } wildTera = true; if (themeOverride || currentTheme) { if (chance(0.95)) { var theme = contestThemes[themeOverride || currentTheme]; var favouredTypes = theme.teraTypes ? theme.teraTypes : Object.keys(effectiveness); if (chance(0.5)) { currentTypeOverride = favouredTypes.random(); } } else { currentTypeOverride = "Stellar"; } } if (pokeInfo.species(currentPokemon) === 1024) { // Terapagos currentTypeOverride = "Stellar"; } if (currentTypeOverride === null) { currentTypeOverride = Object.keys(effectiveness).random(); } } pokeId = poke(num + (shiny ? "" : 0)); currentPokemon = shiny ? "" + num : num; currentPokemonCount = lastPokemonCount = amount; currentThrows = getMaxThrows(num, amount, shiny, 0); throwAttempts = 0; pokeblockThrows = 0; var disguise, appearance, multiplier = 1; if (appearAs) { disguise = true; appearance = appearAs.num; //At this point changing shiny is purely aesthetic and won't affect the caught pokemon shiny = appearAs.shiny; } else { disguise = [132, 151, 185, 570, 571, 778, 66106, 66107].contains(num); // Ditto, Mew, Sudowoodo, Zorua, Zoroark, Mimikyu, Hisuian Zorua, Hisuian Zoroark if (disguise && chance(0.6)) { switch (num) { case 778: // Mimikyu appearance = 25; // Pikachu break; case 185: // Sudowoodo appearance = [103, 709].random(); // Exeggutor or Trevenant break; default: appearance = sys.rand(1, highestDexNum); break; } if (shiny) { multiplier = 5; } } else { disguise = false; } } currentDisplay = (disguise ? appearance : num) + (shiny ? "" : 0); if (currentDisplay === currentPokemon) { disguise = false; } var t1 = type1(currentPokemon); var t2 = type2(currentPokemon); currentPokemonAction = photoActions.Any.concat(photoActions[t1], (t2 !== "???" ? photoActions[t2] : [])).random(); currentPokemonMoodRate = sys.rand(1, 31); var mood = ["Negative", "Neutral", "Positive"][Math.ceil(currentPokemonMoodRate/10)-1]; currentPokemonMood = photoMood[mood].random(); if (themeOverride || leader || dexNum) { // bait, shroom bait or dbait currentPokemonAction = "eating"; } currentDisplayBST = getBST(currentDisplay) + (disguise && !isLegendary(num) ? [-5, -4, -3, 3, 4, 5].random() * multiplier : 0); var mandatoryDisplay = isRare(currentDisplay) || wildEvent || wildTera; safari.spawnDisplay(false, mandatoryDisplay, false, isPortaled ? "A wild {2}{0} came through the portal! (BST: {1})" : null); safari.afterSpawnDisplay(false, mandatoryDisplay); preparationPhase = sys.rand(5, 8); preparationThrows = {}; preparationFirst = null; if (contestCount > 0) { this.compileThrowers(); } lastWild = now(); lastWildAction = now(); if (restrictedThrowers.length) { safaribot.sendHtmlAll("Only {0} can throw the first {1}, or until {2} is up!".format( readable(restrictedThrowers.map(function(e) { return e.toCorrectCase(); })), plural(restrictedThrowLimit, "Ball"), utilities.getTimeString(restrictedThrowTimeLimit)), safchan); } } }; this.spawnDisplay = function(src, mandatoryDisplay, channelJoin, customMessage) { var currentId = poke(currentDisplay, true); var displayId = currentId.split(""); var shiny = typeof currentDisplay === "string"; displayId.splice(sys.rand(1, displayId.length - 1), 0, permObj.get("widthJoiner") || ""); displayId = displayId.join(""); var displayBST = currentDisplayBST; displayBST = (displayBST + "").split(""); displayBST.splice(sys.rand(1, displayBST.length - 1), 0, permObj.get("widthJoiner") || ""); displayBST = displayBST.join(""); var term = currentPokemonCount >= 4 ? "horde of " : ["", "pair of ", "group of "][currentPokemonCount-1]; if (wildSpirit) { displayId = "Spirit Realm " + displayId; } if (wildTera) { // don't overwrite the shiny text colour because it looks pretty awful displayId = shiny ? "Terastallized " + displayId : "Terastallized " + displayId + "" } var appmsg; if (customMessage) { appmsg = customMessage.format(displayId, displayBST, term); } else { appmsg = wildPokemonMessage.format(displayId, displayBST, term, poke(legendaries.random()), poke(legendaries.random()), poke(legendaries.random()), poke(legendaries.random()), sys.rand(300, 700), sys.rand(300, 700), poke(legendaries.random()), sys.rand(300, 700)); } var sprite = cageMode ? cage : pokeInfo.sprite(currentDisplay); if (wildTera) { sprite = "
" + sprite; } /*var cTheme = currentTheme; var isGhost = false; if (cTheme) { var theme = contestThemes[cTheme]; if (theme.disguises && theme.notDisguised && (!(theme.notDisguised.contains(currentPokemon)))) { isGhost = true; wildEvent = false; var wildPokemonMessage2 = "A {1}wild Trick-or-treater appeared!: (BST: {0}) (Types: {2}{3}) ({4}: {5})"; var type_1 = (chance(0.5) ? type1(currentPokemon) : Object.keys(effectiveness).random()); var type_2 = (chance(0.5) ? type2(currentPokemon) : (chance(0.7) ? (Object.keys(effectiveness).random()) : "???")); if (chance(0.4) && (type_2 !== "???")) { var pl = type_1; type_1 = type_2; type_2 = pl; } if (type_2 == "???") { type_2 = ""; } else { type_2 = "/" + type_2; } var statName = ["HP", "Attack", "Defense", "Special Attack", "Special Defense", "Speed"].random(); var statVal = getStatsNamed(currentPokemon)[statName]; if (chance(0.33)) { statVal = Math.round(50 + (100 * Math.random()) - (25 * Math.random()) - (25 * Math.random())); } else if (chance(0.5)) { statVal = Math.round(statVal + (3 * Math.random()) - (3 * Math.random())); } if (chance(0.15)) { currentDisplayBST = Math.round(300 + (175 * Math.random()) + (175 * Math.random()) - (50 * Math.random()) - (50 * Math.random())); } else if (chance(0.25)) { currentDisplayBST = Math.round(200 + (50 * Math.random()) + (150 * Math.random())); } else if (chance(0.15)) { currentDisplayBST = Math.round(200 - (30 * Math.random()) + (150 * Math.random())); } else if (chance(0.6)) { currentDisplayBST = Math.round(currentDisplayBST + (5 * Math.random()) - (5 * Math.random())); } appmsg = wildPokemonMessage2.format(currentDisplayBST, term, type_1, type_2, statName, statVal); sprite = ""; } }*/ if (currentPokemonCount > 1) { var ret = []; ret += "
" + (shiny ? "" + toColor(appmsg, "DarkOrchid") + "" : appmsg) + "
" + (wildEvent ? "This is an Event Pokémon! No " + es(finishName("master")) + " allowed!
" : ""); for (var i = 0; i < currentPokemonCount; i++) { ret += sprite; } ret += "

"; if (src) { sys.sendHtmlMessage(src, ret, safchan); } else { sendAll(ret, true, true, false, mandatoryDisplay); } } else { var ret = "
" + (shiny ? "" + toColor(appmsg, "DarkOrchid") + "" : appmsg) + "
" + (wildEvent ? "This is an Event Pokémon! No " + es(finishName("master")) + " allowed!
" : "") + sprite + ((wildTera || wildEvent || isRare(currentDisplay) && contestCount > 0) ? "
All ball cooldowns were reset!" : "") + "

"; if (src) { sys.sendHtmlMessage(src, ret, safchan); } else { sendAll(ret, true, true, false, mandatoryDisplay); } } for (t = allTrackers.length; t--; ) { id = sys.id(allTrackers[t]); if (id) { var trackMessage = appmsg; var widthJoiner = permObj.get("widthJoiner"); if (widthJoiner) { var regex = new RegExp(widthJoiner, "g"); trackMessage = trackMessage.replace(regex, "") } safaribot.sendHtmlMessage(id, "[Track] " + trackMessage + (wildEvent ? " [Event]" : ""), safchan); } } }; this.afterSpawnDisplay = function(src, mandatoryDisplay) { var toSend = src ? [src] : sys.playersOfChannel(safchan); var abilityMessageList = {}; var miscMessageList = {}; var wildAbilityMessageList = []; var is_are = currentPokemonCount > 1 ? "are" : "is" if (safari.isDailyBoost(currentPokemon)) { wildAbilityMessageList.push("The wild {0} {1} surrounded by a {2}!".format(poke(currentDisplay, true), is_are, typeIcon("Fire", "Burning Aura"))); } if (canHaveAbility(currentPokemon, abilitynum("Contrary"))) { wildAbilityMessageList.push("The wild {0} {1} inverting type matchups with Contrary!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Levitate"))) { wildAbilityMessageList.push("The wild {0} {1} airborne due to Levitate!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Pressure"))) { wildAbilityMessageList.push("The wild {0} {1} exerting {2} Pressure!".format(poke(currentDisplay, true), is_are, currentPokemonCount > 1 ? "their" : "its")); } if (canHaveAbility(currentPokemon, abilitynum("Wonder Guard"))) { wildAbilityMessageList.push("The wild {0} {1} protected by Wonder Guard!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Mirror Armor"))) { wildAbilityMessageList.push("The wild {0} {1} protected from {2} due to Mirror Armor!".format(poke(currentDisplay, true), is_are, es(finishName("mirror")))); } if (canHaveAbility(currentPokemon, abilitynum("Shadow Tag"))) { wildAbilityMessageList.push("The wild {0} {1} preventing your Pokémon from switching due to Shadow Tag!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Arena Trap"))) { wildAbilityMessageList.push("The wild {0} {1} preventing grounded Pokémon from switching due to Arena Trap!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Magnet Pull"))) { wildAbilityMessageList.push("The wild {0} {1} preventing Steel-type Pokémon from switching due to Magnet Pull!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Unnerve")) && contestCount > 0 && currentRules) { wildAbilityMessageList.push("The wild {0} {1} negating Contest buffs due to Unnerve!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Purifying Salt"))) { wildAbilityMessageList.push("The wild {0} {1} scattering Purifying Salt!".format(poke(currentDisplay, true), is_are)); } if (canHaveAbility(currentPokemon, abilitynum("Tera Shell"))) { wildAbilityMessageList.push("The wild {0} {1} distorting type matchups!".format(poke(currentDisplay, true), is_are)); } var ruinAbilities = [283, 284, 285, 286]; // Vessel of Ruin, Sword of Ruin, Tablets of Ruin, Beads of Ruin for (var i = 0; i < ruinAbilities.length; i++) { if (canHaveAbility(currentPokemon, ruinAbilities[i])) { wildAbilityMessageList.push("The wild {0} {1} reducing the power of surrounding Pokémon due to {2}!".format(poke(currentDisplay, true), is_are, abilityOff(ruinAbilities[i]))); break; } } var fastCounterAbilities = [199, 214, 295]; // Dazzling, Queenly Majesty, Armor Tail for (var i = 0; i < fastCounterAbilities.length; i++) { if (canHaveAbility(currentPokemon, fastCounterAbilities[i])) { wildAbilityMessageList.push("The wild {0} {1} protected from {2} due to {3}!".format(poke(currentDisplay, true), is_are, readable(["quick", "lightning"].map(finishName).map(es)), abilityOff(fastCounterAbilities[i]))); break; } } for (var e in toSend) { var pid = toSend[e]; var player = getAvatar(pid); if (!player) { continue; } if (wildTera || wildEvent || (isRare(currentDisplay) && contestCount > 0)) { // if a rare display spawns, reset ball cooldown so everyone can attempt a throw player.cooldowns.ball = 0; } if ((player.options.flashRares && (isRare(currentDisplay) || wildTera)) || player.pokeFlashList.contains(parseInt(currentDisplay))) { sys.sendHtmlMessage(pid, toColor("±Ping: ", "#3daa68") + toFlashing(addFlashTag(sys.name(pid)) + ", " + an((wildTera ? "Terastallized " : "") + poke(currentDisplay, true)) + " has spawned!", sys.name(pid)), safchan); } ballMacro(pid); player.spawnlessThrows = 0; safari.saveGame(player); if (!abilityMessageList.hasOwnProperty(pid)) { abilityMessageList[pid] = []; } if (!miscMessageList.hasOwnProperty(pid)) { miscMessageList[pid] = []; } var leader = this.getEffectiveLead(player, true); var ignore = ignoresWildAbilities(player); if (this.hasCostumeSkill(player, "typeMatchupHelper")) { var matchupLeader = this.getEffectiveLead(player); var typeEffectiveness = 0; var wild = currentDisplay; var pType1 = type1(matchupLeader), pType2 = type2(matchupLeader), wType1 = type1(wild), wType2 = type2(wild); var pType3 = canHaveAbility(leader, abilitynum("Steelworker")) && !hasType(leader, "Steel") ? "Steel" : null; if (currentTypeOverride) { wType1 = currentTypeOverride; wType2 = "???"; } var inverse = currentThemeEffect == "distortion" || (player.costume === "inver" || (currentRules && currentRules.inver)) || (this.getFortune(player, "inver", 0) !== 0) || (canHaveAbility(currentPokemon, abilitynum("Contrary")) && !ignore); // use currentPokemon instead of currentDisplay for ability related stuff, since they broadcast an ability message anyway var select = { levitate: canHaveAbility(currentPokemon, abilitynum("Levitate")) && !ignore, scrappy: canHaveAbility(leader, abilitynum("Scrappy")) }; if ((currentRules && currentRules.defensive) || (this.getFortune(player, "resistance", 0) !== 0) || currentThemeEffect == "distortion") { typeEffectiveness = this.checkEffective([wType1, wType2], [pType1, pType2, pType3], !inverse); } else { typeEffectiveness = this.checkEffective([pType1, pType2, pType3], [wType1, wType2], inverse, select); } if (canHaveAbility(currentPokemon, abilitynum("Wonder Guard")) && !ignore && typeEffectiveness < 2) { typeEffectiveness = Math.min(typeEffectiveness, immuneMultiplier); } miscMessageList[pid].push("Your {0}'s type effectiveness against the wild {1} is {2}x!".format(poke(leader, true), poke(currentDisplay, true), typeEffectiveness)); } if (this.hasCostumeSkill(player, "ballHint")) { var bestBalls = []; var bestRate = 0; for (var i = 0; i < allBalls.length; i++) { var ball = allBalls[i]; if (isBallAvailable(player, ball) && !["master", "spirit", "uturn", "cherish"].contains(ball)) { var ballRate = safari.computeCatchRate(pid, ball, currentDisplay, true); //sys.sendMessage(sys.id("ripper roo"), ball + ": " + ballRate, safchan); if (ballRate >= bestRate) { if (ballRate > bestRate) { bestRate = ballRate; bestBalls = []; } bestBalls.push(ball); } } } if (bestBalls.length > 0) { bestBalls = readable(bestBalls.map( function(e) { return "«" + link("/" + ccatch + " " + e, finishName(e).split(" ")[0]) + "»" })); miscMessageList[pid].push("The best Balls for you to use against the wild {0} are: {1}".format(poke(currentDisplay, true), bestBalls)); } } if (safari.hasCostumeSkill(player, "revealAction")) { miscMessageList[pid].push("The wild {0} {1} currently {2}!".format(poke(currentDisplay, true), is_are, currentPokemonAction)); } if (safari.hasCostumeSkill(player, "revealMood")) { miscMessageList[pid].push("The wild {0} {1} feeling {2}!".format(poke(currentDisplay, true), is_are, currentPokemonMood)); } if (player.options.leadAbilityMessages) { var getIgnorableAbilities = function(id) { var ignorable = ["Color Change", "Contrary", "Defeatist", "Emergency Exit", "Levitate", "Moxie", "Pressure", "Run Away", "Wimp Out", "Wonder Guard", "Armor Tail", "Queenly Majesty", "Dazzling", "Mirror Armor", "Tablets of Ruin", "Sword of Ruin", "Beads of Ruin", "Vessel of Ruin", "Unnerve"].map(abilitynum); var ret = []; for (var i = 0; i < ignorable.length; i++) { if (canHaveAbility(id, ignorable[i])) { ret.push(ignorable[i]); } } return ret; }; if (canHaveAbility(leader, abilitynum("Commander")) && player.party[1] == 977 && contestCount > 0 && !contestForfeited.contains(player.idnum)) { // Dondozo abilityMessageList[pid].push("Your {0} acts as a Commander to your {1}, allowing them to battle together!".format(poke(leader, true), poke(player.party[1], true))); leader = player.party[1]; // swap out the leader variable here to automatically trigger Dondozo's ability messages. does not affect actual effective party lead } var ignorable = getIgnorableAbilities(currentDisplay); if (ignore && ignorable.length > 0) { abilityMessageList[pid].push("Your {0}'s {1} bypasses the wild {2}'s {3}!".format(poke(leader, true), abilityOff(ignore), poke(currentDisplay, true), readable(ignorable.map(abilityOff)))); } if (canHaveAbility(leader, abilitynum("Intimidate"))) { abilityMessageList[pid].push("Your {0} weakens the wild {1} with Intimidate!".format(poke(leader, true), poke(currentDisplay, true))); } if (canHaveAbility(leader, abilitynum("Scrappy")) && (hasType(leader, "Normal") || hasType(leader, "Fighting")) && hasType(currentDisplay, "Ghost")) { abilityMessageList[pid].push("Your {0} can strike Ghost-types with Scrappy!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Technician"))) { abilityMessageList[pid].push("Your {0}'s Technician strengthens weaker Poké Balls!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Tinted Lens"))) { abilityMessageList[pid].push("Your {0}'s Tinted Lens mitigates type resistances!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Keen Eye")) && player.balls.lens > 0) { abilityMessageList[pid].push("Your {0}'s Keen Eye gives you a better chance of taking higher quality photos!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Simple"))) { abilityMessageList[pid].push("Your {0}'s Simple intensifies type matchups!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Speed Boost"))) { abilityMessageList[pid].push("Your {0}'s Speed Boost gives you a better chance of throwing before others!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Prankster"))) { abilityMessageList[pid].push("Your {0}'s Prankster gives you a better chance of taking photos before others!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Infiltrator")) && currentPokemon != currentDisplay) { abilityMessageList[pid].push("Your {0}'s Infiltrator reveals that the wild {1} {2} actually {3} in disguise!".format(poke(leader, true), poke(currentDisplay, true), is_are, poke(currentPokemon, true))); } if (canHaveAbility(leader, abilitynum("Steelworker")) && !hasType(leader, "Steel")) { abilityMessageList[pid].push("Your {0}'s Steelworker grants it the Steel-type!".format(poke(leader, true), poke(currentDisplay, true), is_are, poke(currentPokemon, true))); } if (canHaveAbility(leader, abilitynum("Imposter")) && !canHaveAbility(currentDisplay, abilitynum("Imposter"))) { abilityMessageList[pid].push("Your {0}'s Imposter transformed it into {1}!".format(poke(leader, true), an(poke(currentDisplay, true)))); } if (canHaveAbility(leader, abilitynum("Ball Fetch"))) { abilityMessageList[pid].push("Your {0}'s Ball Fetch can recover your Poké Balls for you!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Skill Link"))) { abilityMessageList[pid].push("Your {0}'s Skill Link prevents your Consecutive Catch Combo from resetting!".format(poke(leader, true))); } if (canHaveAbility(leader, abilitynum("Supreme Overlord")) && player.party.length < 6 && contestCount > 0 && !contestForfeited.contains(player.idnum)) { abilityMessageList[pid].push("Your {0}'s Supreme Overlord grants it strength from its {1}!".format(poke(leader, true), player.party.length === 5 ? "missing comrade" : plural(6 - player.party.length, "missing comrade"))); } if (canHaveAbility(leader, abilitynum("Wind Power")) && hasType(currentDisplay, "Flying")) { abilityMessageList[pid].push("Your {0}'s Wind Power prepares it to catch a breeze!".format(poke(leader, true))); } var superPrioAbilities = [182, 265]; // Gale Wings, Quick Draw for (var i = 0; i < superPrioAbilities.length; i++) { if (canHaveAbility(leader, superPrioAbilities[i])) { abilityMessageList[pid].push("Your {0}'s {1} gives you a much better chance of throwing before others!".format(poke(leader, true), abilityOff(superPrioAbilities[i]))); } } var snipeAbilities = [97, 289, 197]; // Sniper, Opportunist, Stakeout if (currentBaiter !== null && currentBaiter !== player.id) { for (var i = 0; i < snipeAbilities.length; i++) { if (canHaveAbility(leader, snipeAbilities[i])) { abilityMessageList[pid].push("Your {0}'s {1} boosts your catch rate against Pokémon baited by others!".format(poke(leader, true), abilityOff(snipeAbilities[i]))); break; } } } var ignoreRules = [12, 109, 20, 282, 305]; // Oblivious, Unaware, Own Tempo, Good as Gold, Teraform Zero var defyRules = [128, 181]; // Defiant, Competitive if (contestCount > 0 && currentRules) { for (var i = 0; i < ignoreRules.length; i++) { if (canHaveAbility(leader, ignoreRules[i])) { abilityMessageList[pid].push("Your {0}'s {1} ignores Contest buffs and nerfs!".format(poke(leader, true), abilityOff(ignoreRules[i]))); break; } } for (i = 0; i < defyRules.length; i++) { if (canHaveAbility(leader, defyRules[i])) { abilityMessageList[pid].push("Your {0}'s {1} turns Contest nerfs into buffs!".format(poke(leader, true), abilityOff(defyRules[i]))); break; } } } var heldChanceAbilities = [14, 105, 119, 124, 187, 60, 53]; // Compound Eyes, Super Luck, Frisk, Pickpocket, Magician, Pickup, Sticky Hold for (var i = 0; i < heldChanceAbilities.length; i++) { if (canHaveAbility(leader, heldChanceAbilities[i])) { abilityMessageList[pid].push("Your {0}'s {1} helps you find held items more often!".format(poke(leader, true), abilityOff(heldChanceAbilities[i]))); break; } } var abilBoosted = []; if (currentThemeEffect == "rain") { abilBoosted = [abilitynum("Swift Swim"), abilitynum("Hydration"), abilitynum("Rain Dish"), abilitynum("Dry Skin"), abilitynum("Quark Drive"), abilitynum("Hadron Engine")]; } else if (currentThemeEffect == "sunny") { abilBoosted = [abilitynum("Chlorophyll"), abilitynum("Solar Power"), abilitynum("Flower Gift"), abilitynum("Leaf Guard"), abilitynum("Protosynthesis"), abilitynum("Orichalcum Pulse")]; } else if (currentThemeEffect == "sandstorm") { abilBoosted = [abilitynum("Sand Rush"), abilitynum("Sand Veil"), abilitynum("Sand Force")]; } else if (currentThemeEffect == "hail") { abilBoosted = [abilitynum("Slush Rush"), abilitynum("Snow Cloak"), abilitynum("Ice Body"), abilitynum("Ice Face")]; } for (var i = 0; i < abilBoosted.length; i++) { if (canHaveAbility(leader, abilBoosted[i])) { abilityMessageList[pid].push("Your {0}'s {1} boosts your catch rate during the current weather!".format(poke(leader, true), abilityOff(abilBoosted[i]))); break; } } } } for (var msg in wildAbilityMessageList) { if (src) { safaribot.sendMessage(src, wildAbilityMessageList[msg], safchan); } else { sendAll(wildAbilityMessageList[msg], true, false, false, mandatoryDisplay); } } for (var user in abilityMessageList) { player = getAvatar(user); if (!player) { continue; } if (abilityMessageList[user].length === 0 || !player.options.leadAbilityMessages) { if (player.options.showLeadMessage && (mandatoryDisplay || !cantBecause(user, "", ["auction", "battle", "event", "pyramid", "baking"], "", true))) { safaribot.sendMessage(user, "Your {0} prepares to fight!".format(poke(safari.getEffectiveLead(player, true), true)), safchan); } continue; } if (mandatoryDisplay || !cantBecause(user, "", ["auction", "battle", "event", "pyramid", "baking"], "", true)) { for (var message in abilityMessageList[user]) { safaribot.sendMessage(user, abilityMessageList[user][message], safchan); } } } for (var user in miscMessageList) { if (miscMessageList[user].length === 0) { continue; } if (mandatoryDisplay || !cantBecause(user, "", ["auction", "battle", "event", "pyramid", "baking"], "", true)) { for (var message in miscMessageList[user]) { safaribot.sendHtmlMessage(user, miscMessageList[user][message], safchan); } } } }; this.allFlagsMet = function(player, flags) { for (var i = 0; i < flags.length; i++) { if (player.eventFlags[flags[i]+""] < now()) { return false; } } return true; }; this.startContest = function(commandData) { if (currentPokemon && isRare(currentPokemon)) { sys.appendToFile(mythLog, now() + "|||" + poke(currentPokemon) + "::disappeared when a Contest started::\n"); } currentPokemon = null; safari.runPendingActive(); currentTypeOverride = null; currentExtraBST = 0; wildBallThrows = {}; currentThrowers = []; contestCooldown = contestCooldownLength; contestCount = contestDuration; contestMidPoint = false; spiritSpawn = true; canSpawnTera = true; wildTera = false; wildSpirit = false; var themesListed = [].concat(nextTheme); var votesResult; if (commandData.toLowerCase() === "none") { currentTheme = null; currentThemeAlter = false; currentThemeEffect = null; currentThemeSecondary = null; currentThemeFlavor = null; currentRules = this.pickRules(currentTheme); } else if (commandData.toLowerCase() in contestThemes) { currentTheme = commandData.toLowerCase(); currentRules = this.pickRules(currentTheme); } else if (nextTheme) { if (contestVotes) { var vCount = {}, max = 0, winners = nextTheme.concat(); for (var t in contestVotes) { if (nextTheme.contains(contestVotes[t])) { if (!vCount.hasOwnProperty(contestVotes[t])) { vCount[contestVotes[t]] = 0; } vCount[contestVotes[t]]++; } } for (t in vCount) { if (vCount[t] >= max) { if (vCount[t] > max) { winners = []; max = vCount[t]; } winners.push(t); } } votesResult = "Votes: " + nextTheme.map(function(x) { return themeName(x) + " (" + (vCount[x] || 0) + ")"; }).join(", "); nextTheme = winners.random(); contestVotes = null; } else { if (Array.isArray(nextTheme)) { nextTheme = nextTheme[sys.rand(0, nextTheme.length)]; } } currentTheme = nextTheme == "none" ? null : nextTheme; if (nextRules && nextTheme in nextRules) { currentRules = nextRules[nextTheme]; } nextTheme = null; } else { var themeList = Object.keys(contestThemes); currentTheme = chance(0.4) ? null : themeList[sys.rand(0, themeList.length)]; currentRules = this.pickRules(currentTheme); } var alterMsg = ""; if (currentTheme) { if (contestThemes[currentTheme].alterName && (chance(0.25))) { alterMsg = " (" + (contestThemes[currentTheme].alterName) + ")"; if (contestThemes[currentTheme].alterColor) { alterMsg = toColor(alterMsg, contestThemes[currentTheme].alterColor); } currentThemeAlter = true; } } var icon = null; if (currentThemeAlter) { if (contestThemes[currentTheme].alterIcon) { icon = pokeInfo.icon(contestThemes[currentTheme].alterIcon) + " "; if (contestThemes[currentTheme].alterIcon2) { icon = icon + pokeInfo.icon(contestThemes[currentTheme].alterIcon2) + " "; if (contestThemes[currentTheme].alterIcon3) { icon = icon + pokeInfo.icon(contestThemes[currentTheme].alterIcon3) + " "; } } } } if (!(icon)) { if (currentTheme && contestThemes[currentTheme].iconAlts) { icon = pokeInfo.icon(contestThemes[currentTheme].iconAlts.random()); } else { icon = currentTheme && contestThemes[currentTheme].icon ? pokeInfo.icon(contestThemes[currentTheme].icon) + " " : ""; if (icon && currentTheme && contestThemes[currentTheme].icon2) { icon += (pokeInfo.icon(contestThemes[currentTheme].icon2) + " "); } } } if (icon) { sys.sendHtmlAll(toColor(" *** *********************************************************** ", "magenta") + icon, safchan); } else { sys.sendAll(separator, safchan); } if (votesResult) { safaribot.sendAll(votesResult, safchan); } safaribot.sendHtmlAll("A new " + (currentTheme ? "" + link("/themerares " + currentTheme, themeName(currentTheme)) + alterMsg + "-themed" : "") + " Safari Contest is starting now!", safchan); if (currentRules && Object.keys(currentRules).length > 0) { safaribot.sendHtmlAll("Rules: " + this.translateRules(currentRules, true), safchan); } sys.sendAll(separator, safchan); var messOut = {}; var onChannel = sys.playersOfChannel(safchan); if (currentTheme && contestThemes[currentTheme].flavor) { currentThemeFlavor = contestThemes[currentTheme].flavor; } else { currentThemeFlavor = null; } if (currentTheme && contestThemes[currentTheme].themeEffect && contestThemes[currentTheme].hasOwnProperty("variations") && Object.keys(contestThemes[currentTheme].variations).length > 0) { currentThemeEffect = Object.keys(contestThemes[currentTheme].variations).random(); if (currentThemeEffect == "portal") { var ph = []; for (var i = 0; i < themesListed.length; i++) { if (!themesListed[i] || themesListed[i].toLowerCase() == currentTheme.toLowerCase()) { continue; } ph.push(themesListed[i]); } if (ph.length > 0) { currentThemeSecondary = ph.random(); } else { currentThemeSecondary = Object.keys(contestThemes).filter(function(e) { return e !== currentTheme }).random(); } safaribot.sendHtmlAll(pokeInfo.icon(484) + " Palkia appeared and opened a portal to " + contestThemes[currentThemeSecondary].name + "!", safchan); } else if (currentThemeEffect == "past") { var player, party, mon, validAlts, buffAmt, isShiny, n = now(); messOut = {}; safaribot.sendHtmlAll(pokeInfo.icon(483) + " Dialga appeared and opened a rift to the past!", safchan); for (var i in onChannel) { player = getAvatar(onChannel[i]); if (!(player)) { continue; } player.altTimeline.lead = 0; player.altTimeline.buff = 1; party = player.party || []; mon = party[0]; if (mon && !isMega(mon)) { validAlts = getAlternateEvolutions(mon).concat(getAllForms(mon, true)).filter(function(e) { return !isMega(e) }); if (validAlts.length > 0) { isShiny = typeof mon == "string"; buffAmt = (1.25 + 0.05 * Math.min(validAlts.length, 10)); validAlts = removeDuplicates(validAlts, true).random(); messOut[player.id] = [pokeInfo.icon(mon) + " -> " + pokeInfo.icon(parseInt(validAlts, 10)), "Time traveling to the past created a parallel timeline where your " + poke(mon, true) + " was actually a " + (isShiny ? "Shiny " : "") + poke(validAlts, true) + "! You have a " + buffAmt + "x catch rate during this effect!"]; player.altTimeline.lead = validAlts + (isShiny ? "" : 0); player.altTimeline.buff = buffAmt; player.altTimeline.cooldown = n + 60 * 5 * 1000; } } } } else if (currentThemeEffect == "distortion") { safaribot.sendHtmlAll(pokeInfo.icon(66023) + " Giratina appeared and twisted the dimensions! Inverted BST, Inverted Type Effectiveness, and Resistance Mode are now in effect!", safchan); } else if (["rain","sandstorm","sunny","hail"].contains(currentThemeEffect)) { weatherMessage(); } } for (var x in messOut) { for (var i = 0; i < messOut[x].length; i++) { safaribot.sendHtmlMessage(sys.id(x), messOut[x][i], safchan); } } if (contestBroadcast) { sys.sendAll("", 0); sys.sendAll(separator, 0); safaribot.sendAll("A new " + (currentTheme ? themeName(currentTheme) + "-themed" : "") + " Safari Contest is starting now at #" + defaultChannel + "!", 0); sys.sendAll(separator, 0); sys.sendAll("", 0); } else { contestBroadcast = true; } this.flashPlayers(); contestantsCount = {}; contestantsWild = []; wildEvent = false; currentBaiter = null; nextRules = null; safari.createWild(); }; this.pickRules = function(theme) { var out = {}, e, list, exclude, obj; var rules = theme in contestThemes ? contestThemes[theme].rules : null; var defRules = contestThemes.none && contestThemes.none.rules ? contestThemes.none.rules : defaultRules; if (!rules) { if (sys.rand(0, 100) < 20) { return {}; } rules = defRules; } var getRule = function(name) { return rules[name] === "default" ? defRules[name] : rules[name]; }; var buffNerfCount = 0; if ("onlyTypes" in rules && chance(getRule("onlyTypes").chance)) { obj = getRule("onlyTypes"); list = obj.sets.random(); exclude = []; for (e in effectiveness) { if (!list.contains(e)) { exclude.push(e); } } out.excludeTypes = exclude; buffNerfCount += exclude.length; } else if ("excludeTypes" in rules) { exclude = []; obj = getRule("excludeTypes"); for (e in obj) { if (chance(obj[e])) { exclude.push(e); if (exclude.length >= 3) { break; } } } out.excludeTypes = exclude; buffNerfCount += exclude.length; } if ("bonusTypes" in rules) { obj = getRule("bonusTypes"); list = []; for (e in obj) { if ((!out.excludeTypes || !out.excludeTypes.contains(e) ) && chance(obj[e])) { list.push(e); if (list.length >= 3) { break; } } } out.bonusTypes = list; buffNerfCount += list.length; } if ("singleType" in rules) { var obj = getRule("singleType"); if ("nerf" in obj && chance(obj.nerf)) { out.nerfSingle = true; buffNerfCount++; } else if ("buff" in obj && chance(obj.buff)) { out.buffSingle = true; buffNerfCount++; } } if ("dualType" in rules) { var obj = getRule("dualType"); if ("nerf" in obj && chance(obj.nerf) && !out.nerfSingle) { out.nerfDual = true; buffNerfCount++; } else if ("buff" in obj && chance(obj.buff) && !out.buffSingle) { out.buffDual = true; buffNerfCount++; } } if ("shiny" in rules) { var obj = getRule("shiny"); if ("nerf" in obj && chance(obj.nerf)) { out.nerfShiny = true; buffNerfCount++; } else if ("buff" in obj && chance(obj.buff)) { out.buffShiny = true; buffNerfCount++; } } if ("generation" in rules) { var obj = getRule("generation"); for (var i = 1; i < generations.length; i++) { var g = generations[i].toLowerCase(); if (g in obj) { if ("nerf" in obj[g] && chance(obj[g].nerf)) { out["nerfGen" + cap(g)] = true; buffNerfCount++; } else if ("buff" in obj[g] && chance(obj[g].buff)) { out["buffGen" + cap(g)]= true; buffNerfCount++; } } } } if ("color" in rules) { var obj = getRule("color"); for (var g in pokeColors) { if (g in obj) { if ("nerf" in obj[g] && chance(obj[g].nerf)) { out["nerfColor" + cap(g)] = true; buffNerfCount++; } else if ("buff" in obj[g] && chance(obj[g].buff)) { out["buffColor" + cap(g)]= true; buffNerfCount++; } } } } if ("buffmoves" in rules) { var obj = getRule("buffmoves"); out.buffMoves = []; for (e in obj) { if (chance(obj[e])) { out.buffMoves.push(e); } } } if ("exactBuff" in rules) { var obj = getRule("exactBuff"); out.buffMons = []; for (e in obj) { out.buffMons.push(obj[e]); } out.buffMonsDesc = rules.exactBuffDesc; } if ("noLegendaries" in rules && chance(getRule("noLegendaries").chance)) { out.noLegendaries = true; buffNerfCount++; } var removables = []; if ("onlyBalls" in rules && chance(getRule("onlyBalls").chance)) { obj = getRule("onlyBalls"); list = obj.sets.random(); exclude = []; for (e = 0; e < allBalls.length; e++) { if (!list.contains(allBalls[e])) { exclude.push(allBalls[e]); } } out.excludeBalls = exclude; if (obj.chance < 1) { removables.push("excludeBalls"); } } else if ("excludeBalls" in rules) { exclude = []; obj = getRule("excludeBalls"); var canRemove = true; for (e in obj) { if (chance(obj[e])) { if (obj[e] === 1) { canRemove = false; } exclude.push(e); if (exclude.length >= 3) { break; } } } out.excludeBalls = exclude; if (canRemove) { removables.push("excludeBalls"); } } if ("buffBalls" in rules) { bonusBalls = []; obj = getRule("buffBalls"); for (e in obj) { if (chance(obj[e])) { bonusBalls.push(e); if (bonusBalls.length >= 3) { break; } } } out.buffBalls = bonusBalls; } if ("bst" in rules) { var bst = getRule("bst"); if ("min" in bst && "minChance" in bst && chance(bst.minChance)) { if (typeof bst.min === "number") { out.minBST = bst.min; } else { out.minBST = sys.rand(bst.min[0], bst.min[1]); } } if ("max" in bst && "maxChance" in bst && chance(bst.maxChance)) { if (typeof bst.max === "number") { out.maxBST = bst.max; } else { out.maxBST = sys.rand(bst.max[0], bst.max[1]); } } if (bst.maxChance < 1 && bst.minChance < 1) { removables.push("bst"); } } if ("invertedBST" in rules && chance(getRule("invertedBST").chance)) { out.invertedBST = true; if (getRule("invertedBST").chance < 1) { removables.push("invertedBST"); } } if ("similarityMode" in rules && chance(getRule("similarityMode").chance)) { out.similarityMode = true; } else { if ("inver" in rules && chance(getRule("inver").chance)) { out.inver = true; if (getRule("inver").chance < 1) { removables.push("inver"); } } if ("defensive" in rules && chance(getRule("defensive").chance)) { out.defensive = true; if (getRule("defensive").chance < 1) { removables.push("defensive"); } } } var extraRules = 2; if (buffNerfCount >= 6) { extraRules = 0; } else if (buffNerfCount >= 4) { extraRules = 1; } else if (buffNerfCount <= 1) { extraRules = 3; } while (removables.length > extraRules) { var toRemove = removables.random(); if (toRemove == "bst") { delete out.minBST; delete out.maxBST; } else { delete out[toRemove]; } removables.splice(removables.indexOf(toRemove), 1); } if ("rewards" in rules) { var rew = getRule("rewards"); var set = randomSample(rew.chance); if (set in rew.sets) { out.rewards = JSON.parse(JSON.stringify(rew.sets[set])); } if (theme == "asia") { var festivalItem = [ "moonshard", "cookie", "hdew", "mushroom", "nugget", "starpiece", "sunshard" ][currentDay - 1]; var festivalAmt = [ 1, 1, 10, 1, 1, 1, 1 ][currentDay - 1]; out.rewards[festivalItem] = festivalAmt; } } out.berries = [].concat(["oran", "pecha", "bluk", "razz", "leppa"].random()); if (chance(0.25)) { out.berries = out.berries.concat(["tamato", "pinap", "nanab", "petaya", "watmel"].random()); } if ("doubleBerry" in rules) { out.doubleBerry = true; } else { out.doubleBerry = false; } return out; }; this.translateRules = function(rules, colored) { var out = [], list, e; if (!rules) { return "No special rules"; } var buffed = []; var nerfed = []; if ("bonusTypes" in rules && rules.bonusTypes.length > 0) { buffed = buffed.concat(rules.bonusTypes.map(function(e) { return link("/find type " + e, e) })); } if ("excludeTypes" in rules && rules.excludeTypes.length > 0) { if (rules.excludeTypes.length > Object.keys(effectiveness).length / 2) { list = []; for (e in effectiveness) { if (!rules.excludeTypes.contains(e)) { list.push(link("/find type " + e, e)); } } out.push("Enforced Types: " + readable(list, "and")); } else { nerfed = nerfed.concat(rules.excludeTypes); } } if (rules.buffMoves) { for (var i = 0; i < rules.buffMoves.length; i++) { var moveName = moveOff(parseInt(rules.buffMoves[i], 10)); buffed.push(link("/find move " + moveName, moveName + "-compatible")); } } if (rules.buffMons && rules.buffMonsDesc) { buffed.push(rules.buffMonsDesc); } if (rules.nerfSingle) { nerfed.push("Single-type"); } else if (rules.buffSingle) { buffed.push("Single-type"); } if (rules.nerfDual) { nerfed.push("Dual-type"); } else if (rules.buffDual) { buffed.push("Dual-type"); } if (rules.noLegendaries) { nerfed.push("Legendary"); } if (rules.nerfShiny) { nerfed.push("Shiny"); } else if (rules.buffShiny) { buffed.push(link("/find shiny", "Shiny")); } for (var i = 1; i < generations.length; i++) { var g = generations[i]; if (rules["nerfGen" + g]) { nerfed.push(g); } else if (rules["buffGen" + g]) { buffed.push(link("/find region " + g, g)); } } for (i in pokeColors) { if (rules["nerfColor" + cap(i)]) { nerfed.push(cap(i)); } else if (rules["buffColor" + cap(i)]) { buffed.push(link("/find color " + i, cap(i))); } } var optionalColor = function(msg, isColored, color) { if (isColored) { return toColor(msg, color); } else { return msg; } }; if (buffed.length > 0) { out.push(optionalColor("Buffed: " + readable(buffed, "and") + " Pokémon", colored, "blue")); } if (nerfed.length > 0) { out.push(optionalColor("Nerfed: " + readable(nerfed, "and") + " Pokémon", colored, "red")); } var inver = rules.inver; var invertedBST = rules.invertedBST; var defensive = rules.defensive; var similar = rules.similarityMode; if (similar) { out.push(optionalColor("Similarity Mode", colored, "purple")); if (invertedBST) { out.push(optionalColor("Inverted BST", colored, "purple")); } } else if (inver && defensive) { out.push(optionalColor("Weakness Mode", colored, "purple")); if (invertedBST) { out.push(optionalColor("Inverted BST", colored, "purple")); } } else if (inver && invertedBST) { out.push(optionalColor("Inverted BST & Type Effectiveness", colored, "purple")); } else { if (inver) { out.push(optionalColor("Inverted Type Effectiveness", colored, "purple")); } if (invertedBST) { out.push(optionalColor("Inverted BST", colored, "purple")); } if (defensive) { out.push(optionalColor("Resistance Mode", colored, "purple")); } } if ("minBST" in rules && "maxBST" in rules) { out.push("Recommended BST: " + link("/find bst " + rules.minBST + " + && bst " + rules.maxBST + " -", rules.minBST + "~" + rules.maxBST)); } else { if ("minBST" in rules) { out.push(optionalColor("Recommended BST: " + link("/find bst " + rules.minBST + " +", rules.minBST + " or more"), colored, "red")); } if ("maxBST" in rules) { out.push(optionalColor("Recommended BST: " + link("/find bst " + rules.maxBST + " -", rules.maxBST + " or less"), colored, "red")); } } if ("excludeBalls" in rules && rules.excludeBalls.length > 0) { if (rules.excludeBalls.length > allBalls.length / 2) { list = []; for (e = 0; e < allBalls.length; e++) { if (!rules.excludeBalls.contains(allBalls[e])) { list.push(allBalls[e]); } } out.push(optionalColor("Allowed Balls: " + readable(list.map(cap), "and"), colored, "red")); } else { out.push(optionalColor("Forbidden Balls: " + readable(rules.excludeBalls.map(cap), "and"), colored, "red")); } } if ("buffBalls" in rules && rules.buffBalls.length > 0) { out.push(optionalColor("Buffed Balls: " + readable(rules.buffBalls.map(cap), "and"), colored, "blue")); } if ("rewards" in rules) { list = []; for (var e in rules.rewards) { if (e == "cashbonus") { list.push("Cash Money"); } else { list.push(rules.rewards[e] + " " + itemAlias(e, false, true) + (rules.rewards[e] === 1 ? "" : "s")); } } if (list.length > 1 || list[0] !== "10 Gachapon Tickets") { out.push(optionalColor("Reward: " + readable(list, "and"), colored, "darkgreen")); } } if ("berries" in rules) { list = []; for (var e in rules.berries) { list.push(itemAlias(rules.berries[e], false, true)); } if (list.length > 0) { out.push(optionalColor("Berries: " + readable(list, "and"), colored, "darkgreen")); } } if ("doubleBerry" in rules && rules.doubleBerry === true) { out.push(optionalColor("[Double Berries]", colored, "darkgreen")); } if (out.length === 0) { return "No special rules"; } return out.join(" | "); }; this.validForTheme = function(pokeId, name) { // this checks poke validity in a theme based on current factors like current contest theme/alter, current time, current day, etc. var theme = contestThemes[name]; var pokeNum = parseInt(pokeId, 10); if (pokeInfo.species(pokeNum) >= highestDexNum) { return false; } if (theme.alter && theme.alter.length > 0 && currentThemeAlter) { if (theme.alter.indexOf(pokeNum) !== -1) { return true; } else { return false; } } var variations = theme.variations || {}; for (var ef in variations) { var variation = variations[ef]; if (currentThemeEffect === ef && variation.contains(pokeId)) { return true; } } var today = "day" + currentDay; if (theme.hasOwnProperty(today)) { if (theme[today].contains(pokeId)) { return true; } if (theme.include.contains(pokeId)) { return true; } return false; } var period = new Date().getUTCHours(); period = ["night", "morning", "afternoon", "evening"][Math.floor(period/6)]; if (theme[period] && theme[period].contains(pokeId)) { return true; } if (theme.exclude.indexOf(pokeNum) !== -1) { return false; } if (theme.include.indexOf(pokeNum) !== -1) { return true; } var bst = theme.editBST[pokeNum] || getBST(pokeNum); if (isRare(pokeNum) && theme.hasOwnProperty("editBST") && !theme.editBST.hasOwnProperty(""+pokeNum)) { // if rare mon and no editBST defined bst = Math.max(bst, defaultRareBST); // take the higher of its natural BST and defaultRareBST } if (bst > theme.maxBST) { return false; } for (var e in theme.excludeTypes) { if (hasType(pokeNum, theme.excludeTypes[e])) { return false; } } for (e in theme.types) { //Legendary can only be manually added. if (hasType(pokeNum, theme.types[e]) && !isRare(pokeNum)) { return true; } } return false; }; this.isInTheme = function(pokeId, name, baseOnly) { // this checks poke validity in a theme excluding alters and across all periods of the day, and if a theme has daily variations, across all daily variations of that theme pokeId = parseInt(pokeId); var theme = contestThemes[name]; if (!theme) { return false; } if (!baseOnly) { var variations = theme.variations || {}; for (var ef in variations) { var variation = variations[ef]; if (variation.contains(pokeId)) { return true; } } var periods = ["night", "morning", "afternoon", "evening"]; for (var p = 0 ; p < periods.length; p++) { if (theme[periods[p]] && theme[periods[p]].contains(pokeId)) { return true; } } for (var day = 1; day <= 7; day++) { if (theme.hasOwnProperty("day"+day) && theme["day"+day].contains(pokeId)) { return true; } } } if (theme.exclude.contains(pokeId)) { return false; } if (theme.include.contains(pokeId)) { return true; } var bst = theme.editBST[pokeId] || getBST(pokeId); if (isRare(pokeId) && theme.hasOwnProperty("editBST") && !theme.editBST.hasOwnProperty(""+pokeId)) { // if rare mon and no editBST defined bst = Math.max(bst, defaultRareBST); // take the higher of its natural BST and defaultRareBST } if (bst > theme.maxBST) { return false; } for (var e in theme.excludeTypes) { if (hasType(pokeId, theme.excludeTypes[e])) { return false; } } for (e in theme.types) { //Legendary can only be manually added. if (hasType(pokeId, theme.types[e]) && !isRare(pokeId)) { return true; } } return false; }; this.isInAlter = function(pokeId, name) { return contestThemes[name].alter && contestThemes[name].alter.contains(pokeId); }; this.getAllThemesForPoke = function(pokeId, retFull) { var themeList = []; for (var theme in contestThemes) { if (this.isInTheme(pokeId, theme)) { if (retFull) { var hasPermanentVariation = false; for (var day = 1; day <= 7; day++) { // for retFull we want the full names of all possible daily variations of the theme if (contestThemes[theme].hasOwnProperty("day" + day)) { hasPermanentVariation = true; if (contestThemes[theme]["day" + day].contains(pokeId)) { themeList.push(contestThemes[theme]["day" + day + "name"]); } } } var variations = contestThemes[theme].variations || {}; for (var ef in variations) { var variation = variations[ef]; hasPermanentVariation = true; if (variation.contains(pokeId)) { themeList.push(contestThemes[theme].name + " [" + cap(ef) + "]"); } } if (hasPermanentVariation && contestThemes[theme].include.contains(pokeId)) { // theme has variations but they're in the base include i.e. will be in every variation themeList.push(contestThemes[theme].name + " [All Variations]"); } else if (!hasPermanentVariation) { themeList.push(contestThemes[theme].name); } } else { themeList.push(theme); } } if (retFull && this.isInAlter(pokeId, theme)) { themeList.push(contestThemes[theme].alterName); } } return themeList; }; this.getAllRaresInTheme = function(theme) { if (!contestThemes.hasOwnProperty(theme)) { return null; } var ascendingSpecies = function(a, b) { return pokeInfo.species(a) !== pokeInfo.species(b) ? pokeInfo.species(a) - pokeInfo.species(b) : pokeInfo.forme(a) - pokeInfo.forme(b); }; var notExcluded = function(e) { return !contestThemes[theme].exclude.contains(e); }; var finalFormat = function(pokeId) { return pokeInfo.icon(pokeId) + " " + link("/bst " + pokeId, poke(pokeId)); }; var ret = {}, include = contestThemes[theme].include.slice(0).sort(ascendingSpecies).filter(notExcluded), hasPermanentVariation = false; for (var day = 1; day <= 7; day++) { var dayIncludes = contestThemes[theme]["day"+day]; if (!dayIncludes) { continue; } hasPermanentVariation = true; dayIncludes = dayIncludes.slice(0).sort(ascendingSpecies); for (var pokeId in dayIncludes) { if (!ret.hasOwnProperty(contestThemes[theme]["day" + day + "name"])) { ret[contestThemes[theme]["day" + day + "name"]] = []; } if (isRare(dayIncludes[pokeId])) { ret[contestThemes[theme]["day" + day + "name"]].push(dayIncludes[pokeId]); } // e.g. {"Festival": ["Jirachi, Furfrou-Kabuki"...], "Moon Festival": ["Lunala"], "Fire Festival": [""]} } } var variations = contestThemes[theme].variations || {}; for (var ef in variations) { var variationIncludes = variations[ef]; var key = contestThemes[theme].name + " [" + cap(ef) + "]"; if (!variationIncludes) { continue; } hasPermanentVariation = true; variationIncludes = variationIncludes.slice(0).sort(ascendingSpecies); for (var pokeId in variationIncludes) { if (!ret.hasOwnProperty(key)) { ret[key] = []; } if (isRare(variationIncludes[pokeId])) { ret[key].push(variationIncludes[pokeId]); } } } for (var pokeId in include) { if (isRare(include[pokeId])) { if (hasPermanentVariation) { // theme has variations but they're in the base include i.e. will be in every variation if (!ret.hasOwnProperty(contestThemes[theme].name + " [All Variations]")) { ret[contestThemes[theme].name + " [All Variations]"] = []; } ret[contestThemes[theme].name + " [All Variations]"].push(include[pokeId]); } else { if (!ret.hasOwnProperty(contestThemes[theme].name)) { ret[contestThemes[theme].name] = []; } ret[contestThemes[theme].name].push(include[pokeId]); } } // e.g. {"Desert": ["Regirock, Registeel"...]} } if (contestThemes[theme].alter) { var alterIncludes = contestThemes[theme].alter.slice(0).sort(ascendingSpecies); for (var pokeId in alterIncludes) { if (!ret.hasOwnProperty(contestThemes[theme].alterName)) { ret[contestThemes[theme].alterName] = []; } if (isRare(alterIncludes[pokeId])) { ret[contestThemes[theme].alterName].push(alterIncludes[pokeId]); } // e.g. {"Desert": ["Regirock, Registeel"...], "Cursed Pharaoh's Treasury":["Runerigus, Groudon"...]} } } for (var arr in ret) { ret[arr] = removeDuplicates(ret[arr], true).sort(ascendingSpecies).map(finalFormat); } return ret; }; this.getAllSpawnsInTheme = function(theme, raw) { if (!contestThemes.hasOwnProperty(theme)) { return null; } var ascendingSpecies = function(a, b) { return pokeInfo.species(a) !== pokeInfo.species(b) ? pokeInfo.species(a) - pokeInfo.species(b) : pokeInfo.forme(a) - pokeInfo.forme(b); }; var notExcluded = function(e) { return !contestThemes[theme].exclude.contains(e); }; var finalFormat = function(pokeId) { return pokeInfo.icon(pokeId) + " " + link("/bst " + pokeId, poke(pokeId)); }; var ret = {}, include = contestThemes[theme].include.slice(0).sort(ascendingSpecies).filter(notExcluded), hasPermanentVariation = false; for (var day = 1; day <= 7; day++) { var dayIncludes = contestThemes[theme]["day"+day]; if (!dayIncludes) { continue; } hasPermanentVariation = true; dayIncludes = dayIncludes.slice(0).sort(ascendingSpecies); for (var pokeId in dayIncludes) { if (!ret.hasOwnProperty(contestThemes[theme]["day" + day + "name"])) { ret[contestThemes[theme]["day" + day + "name"]] = []; } ret[contestThemes[theme]["day" + day + "name"]].push(dayIncludes[pokeId]); // e.g. {"Festival": ["Jirachi, Furfrou-Kabuki"...], "Moon Festival": ["Lunala"], "Fire Festival": [""]} } } var variations = contestThemes[theme].variations || {}; for (var ef in variations) { var variationIncludes = variations[ef]; var key = contestThemes[theme].name + " [" + cap(ef) + "]"; if (!variationIncludes) { continue; } hasPermanentVariation = true; variationIncludes = variationIncludes.slice(0).sort(ascendingSpecies); for (var pokeId in variationIncludes) { if (!ret.hasOwnProperty(key)) { ret[key] = []; } ret[key].push(variationIncludes[pokeId]); } } for (var pokeId in include) { if (hasPermanentVariation) { // theme has variations but they're in the base include i.e. will be in every variation if (!ret.hasOwnProperty(contestThemes[theme].name + " [All Variations]")) { ret[contestThemes[theme].name + " [All Variations]"] = []; } ret[contestThemes[theme].name + " [All Variations]"].push(include[pokeId]); } else { if (!ret.hasOwnProperty(contestThemes[theme].name)) { ret[contestThemes[theme].name] = []; } ret[contestThemes[theme].name].push(include[pokeId]); } // e.g. {"Desert": ["Regirock, Registeel"...]} } for (var i = 1; i < highestDexNum; i++) { var forms = i in wildForms ? wildForms[i] : 0; for (var j = 0; j < forms+1; j++) { var pokeId = i + 65536 * j; if (!this.isInTheme(pokeId, theme, true)) { continue; } if (hasPermanentVariation) { // theme has variations but they're in the base include i.e. will be in every variation if (!ret.hasOwnProperty(contestThemes[theme].name + " [All Variations]")) { ret[contestThemes[theme].name + " [All Variations]"] = []; } ret[contestThemes[theme].name + " [All Variations]"].push(pokeId); } else { if (!ret.hasOwnProperty(contestThemes[theme].name)) { ret[contestThemes[theme].name] = []; } ret[contestThemes[theme].name].push(pokeId); } // e.g. {"Desert": ["Regirock, Registeel"...]} } } if (contestThemes[theme].alter) { var alterIncludes = contestThemes[theme].alter.slice(0).sort(ascendingSpecies); for (var pokeId in alterIncludes) { if (!ret.hasOwnProperty(contestThemes[theme].alterName)) { ret[contestThemes[theme].alterName] = []; } ret[contestThemes[theme].alterName].push(alterIncludes[pokeId]); // e.g. {"Desert": ["Regirock, Registeel"...], "Cursed Pharaoh's Treasury":["Runerigus, Groudon"...]} } } for (var arr in ret) { ret[arr] = removeDuplicates(ret[arr], true).sort(ascendingSpecies); if (!raw) { ret[arr] = ret[arr].map(finalFormat); } } return ret; }; this.getThemeKeyByName = function(name) { name = name.toLowerCase(); for (var theme in contestThemes) { if (theme === name || contestThemes[theme].name.toLowerCase() === name || (contestThemes[theme].alterName && contestThemes[theme].alterName.toLowerCase() === name)) { return theme; } for (var day = 1; day <= 7; day++) { if (contestThemes[theme]["day"+day] && contestThemes[theme]["day"+day+"name"].toLowerCase() === name) { return theme; } } } return null; }; this.showThemeRares = function(src, theme) { var themeKey = safari.getThemeKeyByName(theme); if (!themeKey || themeKey === "none") { var valid = getAllThemeNames("themerares"); safaribot.sendHtmlMessage(src, "Valid theme inputs are: " + readable(valid) + ".", safchan); return; } var themeRares = safari.getAllRaresInTheme(themeKey); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Rare Pokémon that appear in the following themes:", safchan); for (var key in themeRares) { safaribot.sendHtmlMessage(src, "{0}: {1}".format(key, readable(themeRares[key])), safchan); } sys.sendMessage(src, "", safchan); }; this.showThemeSpawns = function(src, theme) { var themeKey = safari.getThemeKeyByName(theme); if (!themeKey || themeKey === "none") { var valid = getAllThemeNames("themespawns"); safaribot.sendHtmlMessage(src, "Valid theme inputs are: " + readable(valid) + ".", safchan); return; } var themeSpawns = safari.getAllSpawnsInTheme(themeKey); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Pokémon that appear in the following themes:", safchan); for (var key in themeSpawns) { safaribot.sendHtmlMessage(src, "{0}: {1}".format(key, readable(themeSpawns[key])), safchan); } sys.sendMessage(src, "", safchan); }; this.showAbilityReference = function(src, ability, pageNum) { if (ability && isNaN(ability)) { ability = abilitynum(ability); } var colorTags = function(str) { return str .replace("[Lead]", toColor("[Lead]", "green")) .replace("[Wild]", toColor("[Wild]", "red")) .replace("[Misc]", toColor("[Misc]", "brown")); } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Safari Ability Effect Reference", safchan); if (abilityEffects.hasOwnProperty(ability)) { var effect = abilityEffects[ability]; if (!isNaN(effect)) { effect = abilityEffects[effect]; } safaribot.sendHtmlMessage(src, "{0}: {1}".format(link("/find ability:" + abilityOff(ability), abilityOff(ability)), colorTags(effect)), safchan); } else if (ability != 0) { safaribot.sendHtmlMessage(src, "No effect found for this Ability!", safchan); } else { var keys = Object.keys(abilityEffects).sort(function(a, b) { return abilityOff(a) < abilityOff(b) ? -1 : 1 }); var displayLimit = 10, pageNum = Math.abs(parseInt(pageNum)) || 0; var page = keys.slice(pageNum * displayLimit, pageNum * displayLimit + displayLimit); for (var i = 0; i < page.length; i++) { ability = page[i]; var effect = abilityEffects[ability]; if (!isNaN(effect)) { effect = abilityEffects[effect]; } var pageContent = "{0}: {1}".format( link("/find ability:" + abilityOff(ability), abilityOff(ability)), colorTags(effect) ); safaribot.sendHtmlMessage(src, "-" + pageContent, safchan); if (i === page.length-1) { var pageControls = (page.contains(keys[0]) ? "" : link("/abref 0:" + (pageNum-1), "«Previous Page»")) + (page.contains(keys[keys.length-1]) ? "" : " " + link("/abref 0:" + (pageNum+1), "«Next Page»")); if (pageControls) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, pageControls, safchan); } } } } }; this.getAuraEffect = function(player, type, def) { if (!player) { return def; } if (!player.burningAura && !player.brilliantAura) { return def; } var bonuses = { "questcd": [0.2, 0.5], "costumecd": [0.25, 0.5], "catchrate": [1.05, 1.15], "extramega" : [1, 1.2], "extracrystal": [1, 2] }; return bonuses[type][player.brilliantAura + 0] || def; }; this.describeAuraEffects = function(player) { if (!player) { return "None"; } if (!player.burningAura && !player.brilliantAura) { return "None"; } if (player.brilliantAura) { return "-{0}% Costume Cooldown, -{1}% Quest Cooldown, +{2}% Catch Rate, +{3}% Mega Evolution Time, +{4}% Z-Crystal Time".format(safari.getAuraEffect(player, "costumecd", 0) * 100, safari.getAuraEffect(player, "questcd", 0) * 100, Math.round(safari.getAuraEffect(player, "catchrate", 1) * 100 - 100), Math.round(safari.getAuraEffect(player, "extramega", 1) * 100 - 100), Math.round(safari.getAuraEffect(player, "extracrystal", 1) * 100 - 100), plural(auraHours, "hour")); } return "-{0}% Costume Cooldown, -{1}% Quest Cooldown, +{2}% Catch Rate".format(safari.getAuraEffect(player, "costumecd", 0) * 100, safari.getAuraEffect(player, "questcd", 0) * 100, Math.floor(safari.getAuraEffect(player, "catchrate", 1) * 100 - 100), plural(auraHours, "hour")); }; this.getTier = function(pokeId) { if (ultraPokes.hasOwnProperty(pokeId+"")) { return ultraPokes[pokeId+""].tier; } for (var x = 0; x < tiers.length; x++) { if (tiers[x] === "SM Ubers" && sys.isPokeBannedFromTier(pokeId, tiers[x])) { return tiers[x]; // default AG to Ubers } if (sys.isPokeBannedFromTier && !sys.isPokeBannedFromTier(pokeId, tiers[x])) { return tiers[x]; } } return "SM LC"; }; this.getTierChance = function(pokeId) { return catchTierChance[tiers.indexOf(safari.getTier(pokeId))] || 0.02; }; this.getRulesMod = function(player, pokeId, rules, pColor) { var type_1 = type1(pokeId), type_2 = type2(pokeId), id = parseInt(pokeId, 10), bst = getBST(pokeId), val = 1, gen = generation(id), color = pColor || getPokeColor(id); var anyNerf = false; if (("excludeTypes" in rules && (rules.excludeTypes.contains(type_1) || rules.excludeTypes.contains(type_2))) || ("minBST" in rules && bst < rules.minBST) || ("maxBST" in rules && bst > rules.maxBST) || (rules.noLegendaries && isLegendary(id)) || ("minBST" in rules && bst < rules.minBST) || (rules["nerfGen" + generations[gen]]) || (rules["nerfColor" + cap(color)]) || (rules.nerfShiny && typeof pokeId === "string") || (rules.nerfSingle && type_2 === "???") || (rules.nerfDual && type_2 !== "???")) { val = RULES_NERF; anyNerf = true; } if ("bonusTypes" in rules) { if (rules.bonusTypes.contains(type_1)) { val += RULES_BUFF; } if (rules.bonusTypes.contains(type_2)) { val += RULES_BUFF; } } if (rules["buffColor" + cap(color)]) { val += RULES_BUFF; } if (rules["buffGen" + generations[gen]]) { val += RULES_BUFF; } if (rules.buffShiny && typeof pokeId === "string") { val += RULES_BUFF; } if (rules.buffSingle && type2 === "???") { val += RULES_BUFF; } if (rules.buffDual && type2 !== "???") { val += RULES_BUFF; } if (rules.buffMoves) { for (var i = 0; i < rules.buffMoves.length; i++) { if (canLearnMove(id, parseInt(rules.buffMoves, 10))) { val += RULES_BUFF; } } } if (rules.buffMons && rules.buffMons.contains(pokeInfo.species(id))) { val += RULES_BUFF; } return [val, anyNerf]; }; this.computeCatchRate = function(src, ball, target, theory) { var player = getAvatar(src), wild, isShiny, wildStats, story = false, storyMultiplier = 1; var leader = this.getEffectiveLead(player); var trueLeader = this.getEffectiveLead(player, true); var ballBonus = itemData[ball].ballBonus; var usingPokemon = target ? target : currentPokemon if (player.story.inStory && player.story.state == "Catching") { isShiny = false; wild = parseInt(player.story.currentPokemon.id, 10); wildStats = parseInt(player.story.currentPokemon.bst, 10); storyMultiplier = player.story.currentPokemon.multiplier; story = true; } else { isShiny = typeof usingPokemon == "string"; wild = isShiny ? parseInt(usingPokemon, 10) : usingPokemon; wildStats = getBST(wild) + currentExtraBST; if (canHaveAbility(leader, abilitynum("Intimidate"))) { if (getBST(wild) < getBST(leader)) { wildStats *= 0.7; } else { wildStats *= 0.9; } } } var shinyMultiplier = isShiny ? 0.30 : 1; var eventChance = wildEvent ? 0.4 : 1; var isLegend = isLegendary(wild); var legendaryChance = isLegend ? 0.50 : 1; var spiritMonBonus = wildSpirit ? 0.50 : 1; var teraChance = wildTera && !wildEvent ? (contestComboPlayers.contains(player.idnum) ? 1.5 : 0.8) : 1; var flowerGirlBonus = 1; var cherishBonus = Math.min(countDuplicates(pokeInfo.species(getInputPokemon(poke(leader)).num)), 10); var scaleColor = player.scaleDeadline >= now() ? player.scaleColor : null; var costumeBonus = 1; var costumeBoost = function(player, half) { return (1.01 + Math.max((((half ? 0.5 : 1) * safari.getCostumeLevel(player) - 5)/100), 0)); } if (hasType(usingPokemon, "Normal") && this.hasCostumeSkill(player, "catchNormal")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Fighting") && this.hasCostumeSkill(player, "catchFighting")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Electric") && this.hasCostumeSkill(player, "catchElectric")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Poison") && this.hasCostumeSkill(player, "catchPoison")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Dark") && this.hasCostumeSkill(player, "catchDark")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Grass") && this.hasCostumeSkill(player, "catchGrass")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Fire") && this.hasCostumeSkill(player, "catchFire")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Water") && this.hasCostumeSkill(player, "catchWater")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Ice") && this.hasCostumeSkill(player, "catchIce")) { costumeBonus = costumeBoost(player); } if ((hasType(usingPokemon, "Rock") || hasType(usingPokemon, "Ground")) && this.hasCostumeSkill(player, "catchRockGround")) { costumeBonus = costumeBoost(player); } if (hasType(usingPokemon, "Ghost") && this.hasCostumeSkill(player, "catchGhost")) { costumeBonus = costumeBoost(player); } if (this.hasCostumeSkill(player, "catchThief") && canLearnMove(usingPokemon, 168)) { costumeBonus *= costumeBoost(player); } if (this.hasCostumeSkill(player, "catchSing") && canLearnMove(usingPokemon, 47)) { costumeBonus *= costumeBoost(player); } if (this.hasCostumeSkill(player, "catchSplash") && canLearnMove(usingPokemon, 150)) { costumeBonus *= costumeBoost(player); } if (this.hasCostumeSkill(player, "catchRockClimb") && canLearnMove(usingPokemon, 431)) { costumeBonus *= costumeBoost(player); } if (wildStats <= 480 && this.hasCostumeSkill(player, "catchLowBST")) { costumeBonus = costumeBoost(player); } if (ball == "safari" && player.costume == "rich") { ballBonus *= 0.5; } if (isLegendary(leader) && player.costume == "ninja") { ballBonus *= 0.85; } var userStats = (getBST(leader)); if (canHaveAbility(leader, abilitynum("Commander")) && player.party[1] == 977 && contestCount > 0 && !contestForfeited.contains(player.idnum)) { // Dondozo check var currentStats = getStats(leader), supportStats = getStats(player.party[1]); for (var i = 0; i < currentStats.length; i++) { currentStats[i] = Math.max(currentStats[i], supportStats[i]); } userStats = add(currentStats); } var statsBonus = 0.01; if (!(currentRules && currentRules.invertedBST)) { if (userStats <= 600 && this.hasCostumeSkill(player, "useLowBST")) { userStats = Math.min(userStats + Math.floor(this.getCostumeLevel(player) * 3.5), 600); } } var evioBonus = 0; if (userStats <= itemData.eviolite.threshold) { evioBonus = getPerkBonus(player, "eviolite"); userStats += evioBonus; } var ruinAbilities = [283, 284, 285, 286]; // Vessel of Ruin, Sword of Ruin, Tablets of Ruin, Beads of Ruin for (var i = 0; i < ruinAbilities.length; i++) { if (canHaveAbility(usingPokemon, ruinAbilities[i]) && !ignoresWildAbilities(player)) { userStats = Math.round(userStats * 0.75); break; } } if ((currentRules && currentRules.invertedBST) || this.getFortune(player, "invertbst", 0) || currentThemeEffect == "distortion") { userStats -= evioBonus; userStats -= (cherishBonus * 6); if (canHaveAbility(trueLeader, abilitynum("Supreme Overlord")) && player.party.length < 6 && contestCount > 0 && !contestForfeited.contains(player.idnum)) { userStats = Math.round(userStats * (1 - (0.0364 * (6 - player.party.length)))); // maximum of 18.2% decrease for solo, takes Kingambit to 450 BST } statsBonus = (userStats - wildStats) / -8000; } else { userStats += (ball === "level" ? 80 : 0) + (player.costume === "flower" && type2(leader) === "???" ? 50 : 0); userStats += (cherishBonus * 6); if (canHaveAbility(trueLeader, abilitynum("Supreme Overlord")) && player.party.length < 6 && contestCount > 0 && !contestForfeited.contains(player.idnum)) { userStats = Math.round(userStats * (1 + (0.0364 * (6 - player.party.length)))); // maximum of 18.2% increase for solo, takes Kingambit to 650 BST } statsBonus = (userStats - wildStats) / 8000; } var wildWeight = getWeight(wild); var typeBonus; var pType1 = type1(leader), pType2 = type2(leader), wType1 = type1(wild), wType2 = type2(wild); var pType3 = canHaveAbility(trueLeader, abilitynum("Steelworker")) && !hasType(leader, "Steel") ? "Steel" : null; if (currentTypeOverride) { wType1 = currentTypeOverride; wType2 = "???"; } var inverse = currentThemeEffect == "distortion" || (player.costume === "inver" || ball === "inver" || (currentRules && currentRules.inver)) || (this.getFortune(player, "inver", 0) !== 0) || (canHaveAbility(usingPokemon, abilitynum("Contrary")) && !ignoresWildAbilities(player)); var select = { levitate: canHaveAbility(usingPokemon, abilitynum("Levitate")) && !ignoresWildAbilities(player), scrappy: canHaveAbility(trueLeader, abilitynum("Scrappy")), tintedLens: canHaveAbility(trueLeader, abilitynum("Tinted Lens")), simple: canHaveAbility(trueLeader, abilitynum("Simple")), tera: wildTera, teraShell: canHaveAbility(usingPokemon, abilitynum("Tera Shell")) && !ignoresWildAbilities(player) }; if ((currentRules && currentRules.defensive) || (this.getFortune(player, "resistance", 0) !== 0) || currentThemeEffect == "distortion") { if (ball === "mono") { typeBonus = this.checkEffective([wType1, wType2], (pType2 === "???" || !player.options.monoSecondary ? [pType1] : [pType2]), !inverse, select); } else { typeBonus = this.checkEffective([wType1, wType2], [pType1, pType2, pType3], !inverse, select); } } else { if (ball === "mono") { typeBonus = this.checkEffective((pType2 === "???" || !player.options.monoSecondary ? [pType1] : [pType2]), [wType1, wType2], inverse, select); } else { typeBonus = this.checkEffective([pType1, pType2, pType3], [wType1, wType2], inverse, select); } if (this.hasCostumeSkill(player, "monoBallBoost")) { costumeBonus *= costumeBoost(player); } } if (ball === "level" && wildStats >= 450) { ballBonus = 1 + itemData[ball].bonusRate * (Math.floor((wildStats - 450) / 30) + 1); if (ballBonus > itemData[ball].maxBonus) { ballBonus = itemData[ball].maxBonus; } } if (ball === "heavy") { if (this.hasCostumeSkill(player, "heavyBallBoost")) { costumeBonus *= costumeBoost(player); } if (wildWeight >= 360) { ballBonus = 4; } else if (wildWeight >= 280) { ballBonus = 3.5; } else if (wildWeight >= 200) { ballBonus = 3; } else if (wildWeight >= 150) { ballBonus = 2.75; } else if (wildWeight >= 120) { ballBonus = 2.5; } else if (wildWeight >= 100) { ballBonus = 2; } else if (wildWeight >= 90) { ballBonus = 1.5; } else if (wildWeight >= 80) { ballBonus = 1.25; } else if (wildWeight >= 65) { ballBonus = 1; } else if (wildWeight >= 50) { ballBonus = 0.75; } else if (wildWeight >= 35) { ballBonus = 0.5; } else { ballBonus = 0.25; } //typeBonus = ((1 + typeBonus) / 2); // i dont know why it did this but it seems unnecessary these days? legendaryChance = Math.max(0.75, legendaryChance); eventChance = Math.max(0.75, eventChance); } if (ball === "myth") { if (this.hasCostumeSkill(player, "mythBallBoost")) { costumeBonus *= costumeBoost(player); } shinyMultiplier = 1; legendaryChance = 1; eventChance = Math.max(0.75, eventChance); if (isRare(wild)) { ballBonus = itemData[ball].bonusRate; } else { typeBonus = 1; } } if (ball === "cherish") { legendaryChance = 1; eventChance = 1; } if (ball === "lightning") { legendaryChance = 1; eventChance = Math.max(0.75, eventChance); if (this.hasCostumeSkill(player, "lightningBallBoost")) { costumeBonus *= costumeBoost(player); } } if (ball === "inver") { eventChance = Math.max(0.75, legendaryChance); legendaryChance = Math.max(0.75, legendaryChance); } if (ball === "spirit") { legendaryChance = 1; eventChance = 1; ballBonus = 1.5; if (safari.spiritDuelsEnabled) { for (var s in player.spiritDuels.skills) { if (player.spiritDuels.skills[s].type === "catch") { ballBonus *= player.spiritDuels.skills[s].val; } } } } if (ball === "uturn") { var switchingMoves = [369, 521, 754, 226, 100, 600, 822, 823]; // U-turn, Volt Switch, Flip Turn, Baton Pass, Teleport, Parting Shot, Shed Tail, Chilly Reception if (switchingMoves.filter(function(e) { return canLearnMove(leader, e) }).length > 0) { ballBonus = itemData[ball].bonusRate; } else { ballBonus = 1; } } if (ball === "love") { if (hasCommonEggGroup(leader, wild)) { ballBonus = itemData[ball].bonusRate; } else { ballBonus = 1; } if (this.hasCostumeSkill(player, "loveBallBoost")) { costumeBonus *= costumeBoost(player); } } if (ball === "level") { if (this.hasCostumeSkill(player, "levelBallBoost")) { costumeBonus *= costumeBoost(player); } } if (ball === "quick") { if (this.hasCostumeSkill(player, "quickBallBoost")) { costumeBonus *= costumeBoost(player); } } if (ball === "clone") { if (this.hasCostumeSkill(player, "cloneBallBoost")) { costumeBonus *= costumeBoost(player, true); } if (this.hasCostumeSkill(player, "cloneBallBoost2")) { costumeBonus *= costumeBoost(player, true); } } if (ball === "spy") { if (this.hasCostumeSkill(player, "spyBallBoost")) { costumeBonus *= costumeBoost(player); } } if (ball === "photo") { ballBonus = 1; eventChance = Math.max(0.75, legendaryChance); legendaryChance = Math.max(0.75, legendaryChance); for (i = player.photos.length; i--; ) { if (safari.photoMatchesRequest(player.photos[i], {species: wild})) { ballBonus = itemData[ball].bonusRate; break; } } if (this.hasCostumeSkill(player, "photoBallBoost")) { costumeBonus *= costumeBoost(player, true); } } if (ball === "luxury") { if (this.hasCostumeSkill(player, "luxuryBallBoost")) { costumeBonus *= costumeBoost(player, true); } } if (currentTheme && currentRules && currentRules.ballBuff && currentRules.ballBuff.contains(ball)) { ballBonus *= 1.15; } if (ball === "mirror" || (currentRules && currentRules.similarityMode)) { typeBonus = 1; ballBonus = Math.min(itemData[ball].maxBonus, this.checkSimilarity(leader, wild, scaleColor)); if (ball == "mirror") { if (this.hasCostumeSkill(player, "mirrorBallBoost")) { costumeBonus *= costumeBoost(player); } if (wildTera) { ballBonus = itemData[ball].ballBonus; if (!theory) { safaribot.sendHtmlMessage(sys.id(player.id), "The light of the Tera Jewels distorts the wild {0}'s power! You can't get an accurate {1} reading!".format(poke(currentDisplay, true), finishName("mirror")), safchan); } } else { legendaryChance = 1; eventChance = Math.max(0.75, eventChance); if (currentRules && currentRules.similarityMode) { ballBonus *= 2; } } } } var tierChance = wildTera ? Math.min(catchTierChance[catchTierChance.length-2], safari.getTierChance(wild)) : safari.getTierChance(wild); var species = pokeInfo.species(leader); var dailyBonus = safari.validDailyBoost(player) ? (dailyBoost.bonus * this.hasCostumeSkill(player, "botdboost") ? 1.1 : 1) : 1; var rulesMod = currentRules ? this.getRulesMod(player, leader, currentRules, scaleColor) : [1, false]; var costumeMod = 1; if (story) { rulesMod = storyMultiplier; } if (currentRules) { var defyRules = [128, 181]; // Defiant, Competitive for (var i = 0; i < defyRules.length; i++) { if (canHaveAbility(trueLeader, defyRules[i]) && rulesMod[1] === true) { rulesMod = [1 + RULES_BUFF * 2, false]; break; } } var ignoreRules = [12, 109, 20, 282]; // Oblivious, Unaware, Own Tempo, Good as Gold for (var i = 0; i < ignoreRules.length; i++) { if (canHaveAbility(trueLeader, ignoreRules[i]) || (contestCount > 0 && canHaveAbility(trueLeader, abilitynum("Commander")) && player.party[1] == 977 && !contestForfeited.contains(player.idnum))) { // Dondozo check rulesMod = [1, false]; break; } } if (canHaveAbility(currentPokemon, abilitynum("Unnerve")) && !ignoresWildAbilities(player)) { if (rulesMod[0] > 1) { rulesMod[0] = 1; } } } if (trueLeader === player.starter || player.starter2.contains(trueLeader)) { if (player.costume === "preschooler") { var c = costumeData.preschooler; costumeMod = c.rate; var rec = player.records.pokesCaught; if (rec > c.thresh3) { costumeMod = 1; } else if (rec > c.thresh2) { costumeMod -= c.changeRate*2; } else if (rec > c.thresh1) { costumeMod -= c.changeRate; } } costumeMod += this.getFortune(player, "preschooler", 0); } if (ball === "premier") { if (hasType(leader, "Normal") && player.costume !== "inver") { ballBonus = hasType(leader, "???") ? itemData.premier.maxBonus : itemData.premier.bonusRate; } else if (hasType(leader, "???")) { ballBonus = itemData.premier.bonusRate; } if (this.hasCostumeSkill(player, "premierBallBoost")) { costumeBonus *= costumeBoost(player); } } else if ((player.costume === "flower") && (hasType(leader, "???"))) { flowerGirlBonus = 1.125; } var ballbuff = 1 + this.getFortune(player, "ballbuff", 0, ball); var anyballbuff = this.getFortune(player, "anyballbuff", 0); var typebuff = 1 + (this.getFortune(player, "typebuff", 0, pType1) || this.getFortune(player, "typebuff", 0, pType2)); var wildtypebuff = 1 + (this.getFortune(player, "typebuffwild", 0, wType1) || this.getFortune(player, "typebuffwild", 0, wType2)); var n = now(); var timelinemod = (currentThemeEffect == "past" && player.altTimeline.lead !== 0 && player.altTimeline.cooldown > n ? (player.altTimeline.buff && player.altTimeline.buff > 1 ? player.altTimeline.buff : 1.3) : 1); var wonderGuardBreak = false; if (canHaveAbility(usingPokemon, abilitynum("Wonder Guard")) && !ignoresWildAbilities(player)) { if (typeBonus >= 2) { if (ball === "spy" && !theory) { safaribot.sendMessage(sys.id(player.id), "The wild {0}'s Wonder Guard shattered instantly!".format(poke(usingPokemon, true)), safchan); } else if (!theory) { safaribot.sendAll("The wild {0}'s Wonder Guard shattered instantly!".format(poke(usingPokemon, true)), safchan); } wonderGuardBreak = true; } else { typeBonus = Math.min(typeBonus, immuneMultiplier); //safaribot.sendAll("The wild {0}'s Wonder Guard is protecting it!".format(poke(usingPokemon)), safchan); } } if (canHaveAbility(trueLeader, abilitynum("Technician")) && ballBonus < itemData.great.ballBonus) { ballBonus = itemData.great.ballBonus; } var abilityBoost = 1; if (currentThemeEffect) { var abilBoosted = []; if (currentThemeEffect == "rain") { abilBoosted = [abilitynum("Swift Swim"), abilitynum("Hydration"), abilitynum("Rain Dish"), abilitynum("Dry Skin"), abilitynum("Quark Drive"), abilitynum("Hadron Engine")]; } else if (currentThemeEffect == "sunny") { abilBoosted = [abilitynum("Chlorophyll"), abilitynum("Solar Power"), abilitynum("Flower Gift"), abilitynum("Leaf Guard"), abilitynum("Protosynthesis"), abilitynum("Orichalcum Pulse")]; } else if (currentThemeEffect == "sandstorm") { abilBoosted = [abilitynum("Sand Rush"), abilitynum("Sand Veil"), abilitynum("Sand Force")]; } else if (currentThemeEffect == "hail") { abilBoosted = [abilitynum("Slush Rush"), abilitynum("Snow Cloak"), abilitynum("Ice Body"), abilitynum("Ice Face")]; } for (var i = 0; i < abilBoosted.length; i++) { if (canHaveAbility(trueLeader, abilBoosted[i])) { abilityBoost *= 1.3; break; } } } if (currentBaiter !== null && currentBaiter !== player.id) { var snipeAbilities = [97, 289, 197]; // Sniper, Opportunist, Stakeout for (var i = 0; i < snipeAbilities.length; i++) { if (canHaveAbility(trueLeader, snipeAbilities[i])) { abilityBoost *= 1.3; break; } } if (safari.hasCostumeSkill(player, "ninjaSniper")) { abilityBoost *= 1.3; } } var auraBoost = safari.getAuraEffect(player, "catchrate", 1); // safeguard against potential negative tierChance+statsBonus var finalChance = Math.max((tierChance + statsBonus > 0 ? tierChance + statsBonus : 0.01) * timelinemod * typeBonus * shinyMultiplier * legendaryChance * teraChance * spiritMonBonus * dailyBonus * rulesMod[0] * costumeMod * ballBonus * ballbuff * flowerGirlBonus * costumeBonus * typebuff * wildtypebuff * abilityBoost * auraBoost + anyballbuff, 0.01) * eventChance; if (rulesMod[1] == true && !theory) { if (player.helds.length > 0 && player.helds[0] == 2 && !needsPechaCleared.contains(player.id.toLowerCase())) { player.berries.pecha = true; needsPechaCleared.push(player.id.toLowerCase()); player.helds[0] = -1; safaribot.sendMessage(sys.id(player.id), "Your " + poke(trueLeader, true) + " ate its Pecha Berry and weakened the nerf!", safchan); } finalChance = Math.min(RULES_NERF_CAP * (player.berries.pecha ? itemData.pecha.rate : 1), finalChance); } if (ball == "clone") { var maxCloneRate = itemData.clone.bonusRate + (player.costume === "scientist" ? costumeData.scientist.rate : 0) + this.getFortune(player, "scientist", 0); var finalChanceRollover = 0; if (finalChance > 1) { finalChanceRollover = (finalChance - 1) } maxCloneRate += (finalChanceRollover * 0.07) if (this.hasCostumeSkill(player, "cloneBallBoost")) { maxCloneRate += 0.1 + ((this.getCostumeLevel(player)/150) * finalChanceRollover); } if (this.hasCostumeSkill(player, "cloneBallBoost2")) { maxCloneRate += 0.15 + (finalChanceRollover * 0.25); } finalChance = Math.min(finalChance, maxCloneRate); } if (restrictedThrowers.contains(player.id)) { finalChance *= restrictedThrowBoost; } if (!(story)) { if (player.truesalt >= n && chance(player.srate)) { finalChance = 0; } if (currentTheme && contestThemes && contestThemes[currentTheme]) { var theme = contestThemes[currentTheme] ? contestThemes[currentTheme] : {}; if (theme.eventFinal && theme.eventFinal == usingPokemon) { if (!(this.allFlagsMet(player, theme.eventFlags))) { finalChance = 0; } } } } return wonderGuardBreak ? 1 : finalChance; }; this.throwBall = function(src, data, bypass, suppress, command, baitThrow, freeThrow, bufferThrow) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "catch Pokémon"; var leader = this.getEffectiveLead(player); var trueLeader = this.getEffectiveLead(player, true); if (player.tutorial.inTutorial) { if (player.tutorial.privateWildPokemon) { safari.tutorialCatch(src, data); return; } else if (player.story.inStory && player.story.state == "Catching") { safari.storyThrow(src, data); return; } else { if (cantBecause(src, reason, ["tutorial", "story"])) { return; } } } var name = sys.name(src); if (!suppress && !bypass) { var mess = "[Track] " + name + " is using /" + (command || "catch") + " " + data + " (Time since last wild/trick: " + ((now() - lastWild)/1000) + " seconds)" + (bufferThrow ? " [Buffered Throw]" : ""); this.trackMessage(mess, player); } if (contestCount > 0 && !contestActivity.hasOwnProperty(player.id)) { contestActivity[player.id] = []; } data = data.toLowerCase(); if (data === "cancel") { if (preparationPhase > 0 && preparationThrows.hasOwnProperty(name.toLowerCase())) { safaribot.sendMessage(src, "You cancelled your " + (preparationThrows[name.toLowerCase()] === "takephoto" ? "photo" : "throw") + "!", safchan); delete preparationThrows[name.toLowerCase()]; } else if (bufferThrows.hasOwnProperty(player.id)) { safaribot.sendMessage(src, "You cancelled your " + (bufferThrows[player.id].ball === "takephoto" ? "photo" : "throw") + "!", safchan); delete bufferThrows[player.id]; } else { safaribot.sendMessage(src, "You weren't preparing to throw a ball...", safchan); } return; } if (!currentPokemon) { if (suppress) { safaribot.sendMessage(src, "Someone caught the wild Pokémon while you were preparing your throw!", safchan); } else { player.spawnlessThrows++; var spamThreshold = 5; if (player.spawnlessThrows === 1) { safaribot.sendHtmlMessage(src, "No wild Pokémon around!", safchan); } else { safaribot.sendHtmlMessage(src, "No wild Pokémon around! " + toColor("(Please do not spam throw commands when there are no wild Pokémon!)", "red"), safchan); if (player.spawnlessThrows > spamThreshold) { player.cooldowns.ball = Math.max(now(), player.cooldowns.ball) + (1000 * player.spawnlessThrows - spamThreshold); safari.saveGame(player); } } } return; } ball = getBall(data); if (!isBall(ball) || player.balls[ball] === 0) { ball = (player.balls[player.options.favoriteBall] > 0 ? player.options.favoriteBall : "safari"); } var ballName = itemAlias(ball, false, true); if (player.pokemon.length >= getPerkBonus(player, "box") && ball !== "spirit") { safaribot.sendMessage(src, "Your boxes are full! You cannot catch any more Pokémon unless you buy another " + finishName("box") + " or decrease the number of Pokémon in your possession.", safchan); return; } else if (isBallAvailable(player, "spirit") && player.spiritDuels.box.length >= safari.events.spiritBoxLimit) { safaribot.sendMessage(src, "Your Spirit Box is full!", safchan); return; } if (contestCount > 0 && contestForfeited.contains(player.idnum)) { safaribot.sendMessage(src, "You forfeited this Contest, so you can't catch any Pokémon from the Contest!", safchan); return; } if (wildEvent && ball == "master") { safaribot.sendMessage(src, "This is an Event Pokémon, you cannot use " + es(finishName("master")) + "!", safchan); return; } if (ball === "mirror" && canHaveAbility(currentPokemon, abilitynum("Mirror Armor")) && !ignoresWildAbilities(player)) { safaribot.sendMessage(src, "The wild Pokémon's Ability prevents the use of this Ball!", safchan); return; } if (["quick", "lightning"].contains(ball) && !ignoresWildAbilities(player)) { var fastCounterAbilities = [199, 214, 295]; // Dazzling, Queenly Majesty, Armor Tail for (var i = 0; i < fastCounterAbilities.length; i++) { if (canHaveAbility(currentPokemon, fastCounterAbilities[i])) { safaribot.sendMessage(src, "The wild Pokémon's Ability prevents the use of this Ball!", safchan); return; } } } if (ball === "uturn") { if (contestCount > 0 && !contestForfeited.contains(player.idnum)) { if (canHaveAbility(trueLeader, abilitynum("Commander")) && player.party[1] == 977) { safaribot.sendMessage(src, "You can't use {0} while Commander is active!".format(es(finishName("uturn"))), safchan); return; } if (canHaveAbility(trueLeader, abilitynum("Supreme Overlord"))) { safaribot.sendMessage(src, "You can't use {0} while Supreme Overlord is active!".format(es(finishName("uturn"))), safchan); return; } } if (!hasType(leader, "Ghost")) { if (canHaveAbility(currentPokemon, abilitynum("Shadow Tag"))) { safaribot.sendMessage(src, "You can't use {0} due to the wild {1}'s Shadow Tag!".format(es(finishName("uturn")), poke(currentDisplay, true)), safchan); return; } if (canHaveAbility(currentPokemon, abilitynum("Arena Trap")) && !hasType(leader, "Flying")) { safaribot.sendMessage(src, "You can't use {0} due to the wild {1}'s Arena Trap!".format(es(finishName("uturn")), poke(currentDisplay, true)), safchan); return; } if (canHaveAbility(currentPokemon, abilitynum("Magnet Pull")) && hasType(leader, "Steel")) { safaribot.sendMessage(src, "You can't use {0} due to the wild {1}'s Magnet Pull!".format(es(finishName("uturn")), poke(currentDisplay, true)), safchan); return; } } } if (ball === "spirit" && (safari.spiritDuelsCanSignup(src, player, true))) { // || !safari.inSpiritTeam(src, player))) { safaribot.sendHtmlMessage(src, "You can't throw " + es(finishName("spirit")) + " without signing up for " + link("/spiritduels", "Spirit Duels") + " first!", safchan); return; } if (wildSpirit && ball !== "spirit") { safaribot.sendMessage(src, "This is a Spirit Pokémon, you can only use " + es(finishName("spirit")) + "!", safchan); return; } if (currentRules && currentRules.excludeBalls && currentRules.excludeBalls.contains(ball) && ball !== "spirit") { safaribot.sendMessage(src, "The use of " + ballName + " is forbidden during this Contest!", safchan); return; } if (restrictedThrowers.length && !restrictedThrowers.contains(player.id)) { safaribot.sendHtmMessage(src, "Only {0} can throw the next {1}, or until {2} is up!".format( readable(restrictedThrowers.map(function(e) { return e.toCorrectCase(); })), plural(restrictedThrows, "Ball"), utilities.getTimeString(restrictedThrowTimeLimit)), safchan); return; } if (cantBecause(src, reason, ["item", "auction", "battle", "event", "pyramid", "baking"], ball, suppress)) { if (cantBecause(src, reason, ["battle"], ball, true) && (isRare(currentDisplay) || wildEvent || wildTera) && player.options.autoForfeitThrow) { // if there's a rare spawn and you're in a battle // you can throw, but at the cost of instantly forfeiting the match once the preparation phase is over if (!suppress) { safaribot.sendHtmlMessage(src, "You are forfeiting this battle to catch the wild " + poke(currentDisplay) + "! " + (preparationPhase > 0 ? "[" + link("/" + ccatch + " cancel", "Cancel") + "]" : ""), safchan); } } else { // otherwise, block the action return; } } var currentTime = now(); // we check not only if cooldown is over, but also if there is already a queued throw, and if there is, block the input + update bufferThrows with the new input, therefore only allowing the queued throw to be executed on next stepEvent. prevents weird situations where player cd is over + player manually inputs a throw between seconds, making it seem like the queued command disappears or is even re-queued after the manual input induces cooldown again // e.g. stepEvent, player has queued throw and original cd is not over --> player cd wears off between seconds + player inputs throw manually --> ball is thrown, new cd is induced --> next stepEvent, original cd is over therefore queued throw is executed --> queued throw hits new cd and is requeued if (!bypass && (!preparationFirst || name.toLowerCase() !== preparationFirst) && (player.cooldowns.ball > currentTime || ((player.id in bufferThrows) && !bufferThrow)) && (preparationPhase <= 0 || player.cooldowns.ball + preparationPhase > currentTime)) { safaribot.sendHtmlMessage(src, "You are preparing to throw your " + ballName + " at the next opportunity! (Remaining cooldown: " + (timeLeftString(player.cooldowns.ball) || "1 second") + ") [" + link("/cancel", "Cancel") + "]", safchan); bufferThrows[player.id] = { "ball": ball, "throwAt": player.cooldowns.ball + 1 }; if (contestCount > 0 && !freeThrow) { if (!contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } } return; } if (contestCount > 0 && contestantsWild.indexOf(name.toLowerCase()) === -1) { contestantsWild.push(name.toLowerCase()); } if (baitThrow) { safaribot.sendMessage(src, "You quickly scramble to put your " + finishName(command === "gbait" ? "golden" : "bait") + " away in order to try to catch the wild Pokémon lured by someone else!", safchan); } if (preparationPhase > 0) { if (ball === "master" || ball === "cherish") { safaribot.sendHtmlMessage(src, toColor("You are preparing to throw your " + ballName + "!", "red") + " [" + link("/catch cancel", "Cancel") + "]", safchan); } else { safaribot.sendMessage(src, "You are preparing to throw your " + ballName + "!", safchan); } preparationThrows[name.toLowerCase()] = ball; if (contestCount > 0 && !freeThrow) { if (!contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } } return; } if (safari.isBattling(sys.name(src))) { for (var b = 0; b < currentBattles.length; b++) { var battle = currentBattles[b]; if (battle.isInBattle(name)) { safari.forfeitBattle(src); } } } var aType = type1(trueLeader); var crystalEffect = ball !== "master" && player.zcrystalDeadline >= now() && player.zcrystalUser === trueLeader && chance(zCrystalData[aType].chance) ? zCrystalData[aType] : { effect: "none" }; if (!freeThrow) { player.balls[ball] -= 1; this.updateShop(player, ball); } var finalChance = safari.computeCatchRate(src, ball); var ballBonus = itemData[ball].ballBonus; var cooldown = itemData[ball].cooldown; if (player.costume == "breeder" || player.costume == "explorer") { if (ball !== "safari" && ball !== "love" && (player.costume == "breeder")) { cooldown += 750; } if (ball !== "safari" && ball !== "heavy" && (player.costume == "explorer")) { cooldown += 750; } } var pokeName = poke(currentPokemon, true); var isShiny = typeof currentPokemon == "string"; var wild = isShiny ? parseInt(currentPokemon, 10) : currentPokemon; var isLegend = isLegendary(wild); var wildStats = getBST(wild); var rng = Math.random(); var flee; var caughtAny = false; if (crystalEffect.effect === "photo" && player.photos.length < 20) { this.takePhoto(src, "*", true, true, false, false, true); } if (rng < finalChance || ballBonus == 255) { caughtAny = true; currentPokemonCount--; var amt = currentPokemonCount; var remaining = " There " + (amt > 1 ? "are" : "is") + " still " + currentPokemonCount + " " + pokeName + " left to catch!"; if (amt < 1) { sendAll("", true, true); } var revealName = poke(currentDisplay) != poke(currentPokemon) ? "" + pokeName + " (who was disguised as "+ poke(currentDisplay, true) + ")" : "" + pokeName + ""; var msg = ""; if (now() > player.cooldowns.unown) { for (var u = 0; u < player.party.length; u++) { if (pokeInfo.species(player.party[u]) === 201) { msg += "abcdefghijklmnopqrstuvwxyz!?".charAt(pokeInfo.forme(player.party[u])); } } msg = msg.length > 4 ? msg : ""; msg = /asshole|dick|pussy|bitch|porn|nigga|cock|gay|slut|whore|cunt|penis|vagina|nigger|fuck|dildo|anus|boner|tits|condom|rape/gi.test(msg) ? "" : msg; if (msg) { player.cooldowns.unown = now() + hours(0.33); } } var ch = ""; var catchingMon = leader; var finalCatch = currentPokemon; if (player.cherished.indexOf(pokeInfo.species(getInputPokemon(poke(catchingMon)).num)) !== -1) { if (!player.options.cherishOff) { ch = "Cherished "; } } var catchVerb = "caught the "; if (currentThemeFlavor && currentThemeFlavor == "rocket") { catchVerb = player.costume == "rocket" ? "stole the " : "liberated the "; } var stype = ball === "mono" && type2(catchingMon) !== "???" ? "pure " + (!player.options.monoSecondary ? type1(catchingMon) : type2(catchingMon)) + " " : ""; var scolor = player.scaleDeadline >= now() ? cap(player.scaleColor) + " " : ""; var playerDisplayName = name; var playerDisplayMon = ch + stype + scolor + poke(trueLeader, true); var isStealth = false; if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { isStealth = true; playerDisplayName = "Some stealthy ninja"; playerDisplayMon = "well-trained ninja Pokémon"; } if (ball == "spy") { isStealth = true; playerDisplayName = "Some stealthy person"; playerDisplayMon = "well-trained spy Pokémon"; player.records.catchSpy += 1; } if (ball == "spirit") { safari.catchSpiritMon(player, currentPokemon, wildSpirit); player.records.catchSpirit += 1; var team = player.spiritDuels.team; if (team === "None") { team = "Unemployed" } var title = player.spiritDuels.rankName; safaribot.sendHtmlAll(team + " " + title + " " + name + " " + catchVerb + revealName + " with " + an(ballName)+ " and the help of their " + poke(trueLeader, true) + "!", safchan); } else { safaribot.sendHtmlAll(playerDisplayName + " " + catchVerb + revealName + " with " + an(ballName)+ " and the help of their " + playerDisplayMon + "!" + (msg ? " Some shadows shaped like the letters " + msg.toUpperCase() + " could be seen around the " + ballName + "!" : "") + (amt > 0 ? remaining : ""), safchan); } player.consecutiveCombo++; player.consecutiveCombo = Math.min(maxConsecutiveCombo, player.consecutiveCombo); safaribot.sendHtmlMessage(src, "Gotcha! " + pokeName + " was caught with " + an(ballName) + "! " + itemsLeft(player, ball) + (player.options.showConsecutiveCombo ? " (Consecutive Catch Combo: " + addComma(player.consecutiveCombo) + ")" : "") + (amt > 0 ? (player.balls[ball] > 0 ? " [" + link("/" + ccatch + " " + ball, "Throw Again") + "]" : "") + " [" + link("/ballmacro", "Choose Another Action") + "]" : ""), safchan); if (canHaveAbility(currentPokemon, abilitynum("Pressure")) && !ignoresWildAbilities(player) && !allBalls.filter(function(e) { return itemData[e].special }).contains(ball) && player.balls[ball] > 1) { safaribot.sendAll("The wild {0}'s Pressure used up an extra {1}...".format(poke(currentPokemon, true), finishName(ball)), safchan); player.balls[ball] -= 1; this.updateShop(player, ball); } if (baitCooldown <= 5) { baitCooldown = sys.rand(5, 8); } if (goldenBaitCooldown <= 3) { goldenBaitCooldown = sys.rand(3, 5); } if (deluxeBaitCooldown < 3) { deluxeBaitCooldown = sys.rand(3, 5); } if (currentTheme && contestThemes[currentTheme] && contestThemes[currentTheme].eventFlags && contestThemes[currentTheme].eventFlags.contains(parseInt(currentPokemon, 10))) { player.eventFlags[currentPokemon+""] = now() + (72 * 60 * 60 * 1000); //flag set for 72 hours sendAll("The captured " + pokeName + " was a mirage! " + playerDisplayName + " is one step closer to unlocking the Legendary Pokémon!"); this.missionProgress(player, "catchMirage", 0, 1); } else if (currentTheme && contestThemes[currentTheme] && contestThemes[currentTheme].catchRewards && Object.keys(contestThemes[currentTheme].catchRewards).contains(currentPokemon+"")) { var g = giveStuff(player, contestThemes[currentTheme].catchRewards[currentPokemon+""], true); g = readable(g.gained); player.pokemon.push(currentPokemon); sendAll("The " + pokeName + " was holding " + g + "!"); rafflePlayers.add(player.id, player.balls.entry); rafflePlayers.save(); } else if (ball !== "spirit") { if (crystalEffect.effect === "evolution" && evolutions.hasOwnProperty(currentPokemon+"") && evolutions[currentPokemon].evo !== -1) { var evolved = getPossibleEvo(currentPokemon) + (typeof currentPokemon === "string" ? "" : 0); player.pokemon.push(evolved); sendAll(pokeInfo.icon(currentPokemon) + " -> " + pokeInfo.icon(parseInt(evolved, 10)), true); sendAll("The " + pokeName + " instantly evolved into " + poke(evolved, true) + "!"); finalCatch = evolved; } else { player.pokemon.push(currentPokemon); } } else if (ball !== "spirit") { player.pokemon.push(currentPokemon); } if (canHaveAbility(currentPokemon, abilitynum("Purifying Salt")) && player.burningAura && !player.brilliantAura) { if (player.auraExpiry - now() <= ((auraHours - 1) * 60 * 60 * 1000) && player.balls.salt >= 1) { var saltUsed = Math.min(player.balls.salt, 5); var auraReduction = 0; switch (saltUsed) { case 1: case 2: auraReduction = 0.75; break; case 3: case 4: auraReduction = 0.5; break; default: auraReduction = 0.25; break; } player.brilliantAura = true; player.balls.salt -= saltUsed; player.auraExpiry = now() + Math.round((player.auraExpiry - now()) * (1 - auraReduction)); if (isStealth) { safaribot.sendHtmlMessage(src, "The " + poke(currentPokemon, true) + " purified your " + typeIcon("Fire", "Burning Aura") + " into a " + typeIcon("Fairy", "Brilliant Aura") + " using " + saltUsed + " of your " + es(finishName("salt")) + "!", safchan); } else { safaribot.sendHtmlAll("The " + poke(currentPokemon, true) + " purified " + sys.name(src) + "'s " + typeIcon("Fire", "Burning Aura") + " into a " + typeIcon("Fairy", "Brilliant Aura") + " using " + saltUsed + " of their " + es(finishName("salt")) + "!", safchan); } safaribot.sendMessage(src, "However, your remaining aura duration was reduced by {0}%! {1}".format(auraReduction * 100, itemsLeft(player, "salt")), safchan); sys.appendToFile(miscLog, now() + "|||Wild " + poke(currentPokemon) + "|||purified " + sys.name(src) + " with Purifying Salt, granting a Brilliant Aura (Aura reduction: " + (auraReduction * 100) + "%).\n"); } } var heldChanceBoost = false; var heldChanceAbilities = [14, 105, 119, 124, 187, 60, 53]; // Compound Eyes, Super Luck, Frisk, Pickpocket, Magician, Pickup, Sticky Hold for (var i = 0; i < heldChanceAbilities.length; i++) { if (canHaveAbility(trueLeader, heldChanceAbilities[i])) { heldChanceBoost = true; break; } } if (globalWildItems && globalWildItems.hasOwnProperty(currentPokemon+"") && !wildSpirit) { var wildItemHeldList = globalWildItems[currentPokemon+""]; var drop = [], gained = [], discarded = [], lost = []; for (var i = 0; i < wildItemHeldList.length; i++) { // sample data - {"1": [ {"item":"bait","perc":0.5}, {"item":"golden","perc":0.1} ]}; var itemObj = wildItemHeldList[i]; var item = itemObj.item, perc = itemObj.perc; var custom = safari.getCustomWildItems(); if (item in custom) { item = custom[item].random(); } if (heldChanceBoost) { if (perc >= 0.5) { perc += 0.1; } else if (perc >= 0.2) { perc += 0.2; } else { perc *= 2; } } if (chance(perc)) { var stuff = giveStuff(player, item, true); drop.push(translateStuff(item)); gained = gained.concat(stuff.gained); lost = lost.concat(stuff.lost); discarded = discarded.concat(stuff.discarded); } } if (drop.length > 0 && (gained.length > 0 || discarded.length > 0)) { // check that there's actually an item gained/discarded so it doesn't display "x was holding -1 Item!" if the held item is negative sendAll("The {0} was holding {1}!".format(pokeName, readable(drop))); } if (lost.length > 0) { sendAll("The {0} took away {1}!".format(pokeName, readable(lost))); } if (discarded.length > 0) { //if (isStealth) safaribot.sendMessage(src, "Unfortunately, you had to discard {0} as your bag was full!".format(readable(discarded)), safchan); //else // sendAll("Unfortunately, {0} had to discard {1} as their bag was full!".format(name, readable(discarded))); } } if (safari.validDailyBoost(player)) { var drop = [], gained = [], discarded = []; var bonusDrops = [ {"item":"70@dust", "perc":0.1}, {"item":"6@gacha","perc":0.07}, {"item":"3@silver","perc":0.04}, {"item":"@pack","perc":0.01} ]; for (var i = 0; i < bonusDrops.length; i++) { // sample data - {"1": [ {"item":"bait","perc":0.5}, {"item":"golden","perc":0.1} ]}; var itemObj = bonusDrops[i]; var item = itemObj.item, perc = itemObj.perc; if (chance(perc)) { var stuff = giveStuff(player, item, true); drop.push(translateStuff(item)); gained = gained.concat(stuff.gained); discarded = discarded.concat(stuff.discarded); } } if (drop.length > 0 && (gained.length > 0 || discarded.length > 0)) { if (isStealth) safaribot.sendMessage(src, "The power of your {0} made {1} stealthily appear out of thin air!".format(poke(trueLeader, true), readable(drop)), safchan); else sendAll("The power of {0}'s {1} made {2} appear out of thin air!".format(playerDisplayName, poke(trueLeader, true), readable(drop))); } if (discarded.length > 0) { //if (isStealth) safaribot.sendMessage(src, "Unfortunately, you had to discard {0} as your bag was full!".format(readable(discarded)), safchan); //else //sendAll("Unfortunately, {0} had to discard {1} as their bag was full!".format(ball === "spy" ? "the stealthy person" : name, readable(discarded))); } } if (currentRules && currentRules.berries) { var berry = currentRules && currentRules.berries ? currentRules.berries : []; if (berry.length > 0) { var itemBST = getBST(currentPokemon); var itemPower = itemBST; if (itemBST > 400) { itemPower += (itemBST - 300); } else if (itemBST > 500) { itemPower += (itemBST - 450); } itemPower += (100 * Math.random()) + (100 * Math.random()) - (200 * Math.random()); var berryChance = 0.05 + (safari.hasCostumeSkill(player, "berryCatcher") ? 0.2 : 0); if (currentRules.doubleBerry) { berryChance *= 2; } if (heldChanceBoost) { if (berryChance >= 0.5) { berryChance += 0.1; } else { berryChance += 0.2; } } if (chance(berryChance)) { if (itemPower > 600 && berry.length > 1) { berry = berry[1]; } else { berry = berry[0]; } var stuff = giveStuff(player, "@" + berry, true); var drop = [], gained = [], discarded = [], lost = []; drop.push(translateStuff("@" + berry)); gained = gained.concat(stuff.gained); lost = lost.concat(stuff.lost); discarded = discarded.concat(stuff.discarded); if (drop.length > 0 && (gained.length > 0 || discarded.length > 0)) { // check that there's actually an item gained/discarded so it doesn't display "x was holding -1 Item!" if the held item is negative sendAll("The {0} was holding {1} from the Contest!".format(pokeName, readable(drop))); } if (lost.length > 0) { sendAll("The {0} took away {1}!".format(pokeName, readable(lost))); } if (discarded.length > 0) { safaribot.sendMessage(src, "Unfortunately, you had to discard {0} as your bag was full!".format(readable(discarded)), safchan); } } } } if (wildTera) { player.records.teraMonsCaught += 1; var drop = [], gained = [], discarded = []; var item = currentTypeOverride === "Stellar" ? "5@terashard" : "1@terashard"; var stuff = giveStuff(player, item, true); drop.push(translateStuff(item)); gained = gained.concat(stuff.gained); discarded = discarded.concat(stuff.discarded); if (drop.length > 0 && (gained.length > 0 || discarded.length > 0)) { sendAll("The lingering Terastal energy caused {0} to form!".format(readable(drop))); } if (discarded.length > 0) { safaribot.sendMessage(src, "Unfortunately, you had to discard {0} as your bag was full!".format(readable(discarded)), safchan); } } if (canHaveAbility(trueLeader, abilitynum("Wind Power")) && hasType(currentPokemon, "Flying")) { var drop = [], gained = [], discarded = []; var item = randomSample({ "5": 7, "10": 3 }) + "@permfinder"; var stuff = giveStuff(player, item, true); drop.push(translateStuff(item)); gained = gained.concat(stuff.gained); discarded = discarded.concat(stuff.discarded); if (drop.length > 0 && (gained.length > 0 || discarded.length > 0)) { safaribot.sendMessage(src, "Your {0}'s Wind Power generated {1}!".format(poke(trueLeader, true), readable(drop)), safchan); } if (discarded.length > 0) { safaribot.sendMessage(src, "Unfortunately, you had to discard {0} as your bag was full!".format(readable(discarded)), safchan); } } player.records.pokesCaught += 1; if (currentBaiter !== null && currentBaiter !== player.id) { player.records.notBaitedCaught += 1; } this.addToMonthlyLeaderboards(player.id, "pokesCaught", 1); if (parseInt(currentPokemon, 10) === 686) { player.records.catchInkay += 1; } safari.detectiveClue(player.idnum, "catch:" + (parseInt(currentPokemon, 10) % 65536), src); if (ball === "cherish") { player.records.catchCherish += 1; player.cherished.push(pokeInfo.species(getInputPokemon(poke(currentPokemon)).num)); } if (ball === "myth") { player.records.catchMyth += 1; } if (ball === "level") { player.records.catchLevel += 1; } if (ball === "heavy") { player.records.catchHeavy += 1; } if (ball === "quick") { player.records.catchQuick += 1; } if (ball === "lightning") { player.records.catchLightning += 1; } if (ball === "photo") { player.records.catchPhoto += 1; } if (ball === "mono") { player.records.catchMono += 1; } if (ball === "inver") { player.records.catchInvert += 1; } if (ball === "mirror") { player.records.catchMirror += 1; } if (ball === "uturn") { player.records.catchSwitch += 1; } if (ball === "love") { player.records.catchLove += 1; } var clonedAmount = 0, cloneDiscarded = 0; if (ball === "clone" || crystalEffect.effect === "clone") { if (ball === "clone") { player.records.catchClone += 1; } var costumed = player.costume === "scientist"; var triple = costumed && chance(costumeData.scientist.bonusChance); if (!triple && this.hasCostumeSkill(player, "tripleChance") && chance(costumeData.scientist.bonusChance + costumeData.scientist.bonusChance * 0.4) && chance((this.getCostumeLevel(player)-10)/10)) { // if failed triple RNG first time, tripleChance gives you a reroll triple = true; } if (triple && (isLegend || isShiny) && chance(0.75)) { triple = false; } if (triple) { //TWO CLONES safaribot.sendAll("But wait! The " + pokeName + " was cloned by the " + ballName + "! " + playerDisplayName + " received another " + pokeName + "!", safchan); safaribot.sendHtmlAll("Hold on! The " + pokeName + " was actually cloned TWICE by the " + ballName + "! " + html_escape(playerDisplayName) + " received yet another " + pokeName + "!", safchan); safari.costumeEXP(player, "clonepoke", 4); clonedAmount = 2; } else if (!costumed && (isLegend || isShiny)) { //NO CLONE safaribot.sendAll("Unfortunately, due to the complex genetic sequence of " + pokeName + ", the cloning process failed!", safchan); } else { // ONE CLONE safaribot.sendAll("But wait! The " + pokeName + " was cloned by the " + ballName + "! " + playerDisplayName + " received another " + pokeName + "!", safchan); clonedAmount = 1; safari.costumeEXP(player, "clonepoke", 1); } if (ball === "clone" && crystalEffect.effect === "clone") { clonedAmount *= 2; safaribot.sendAll("What is this?! The " + pokeName + " was further cloned by some mysterious power! " + playerDisplayName + " received a total of " + plural(clonedAmount+1, pokeName) + "!", safchan); } if (clonedAmount > 0) { var boxLimit = getPerkBonus(player, "box"), cloneDiscarded = 0; for (var i = 0; i < clonedAmount; i++) { if (player.pokemon.length >= boxLimit) { cloneDiscarded++; } else { player.pokemon.push(currentPokemon); } } player.records.pokesCloned += clonedAmount; if (cloneDiscarded > 0) { safaribot.sendMessage(src, "Oh heck! You didn't have enough space in your boxes for the cloned Pokémon, so you had to let " + plural(cloneDiscarded, currentPokemon + (typeof currentPokemon === "string" ? "*" : "")) + " go!", safchan); } } } var luxuryAmount = 0; if (ball == "luxury") { var perkBonus = 1 + getPerkBonus(player, "scarf") + this.getFortune(player, "scarf", 0); var luxuryAmount = Math.floor(wildStats/2 * perkBonus); safaribot.sendAll((player.balls.scarf > 0 && !safari.hasCostumeSkill(player, "permanentStealthThrow") ? "The Fashionable " : "") + playerDisplayName + " found $" + addComma(luxuryAmount) + " on the ground after catching " + pokeName + "!" , safchan); player.money += luxuryAmount; if (player.money > moneyCap) { player.money = moneyCap; } player.records.luxuryEarnings += luxuryAmount; player.records.catchLuxury += 1; } if (ball == "uturn" && player.party.length > 1) { safaribot.sendAll(playerDisplayName + "'s " + playerDisplayMon + " switched out after catching the " + pokeName + "!" , safchan); if (canHaveAbility(trueLeader, abilitynum("Regenerator")) && player.balls.uturn < getCap("uturn")) { player.balls.uturn += 1; safaribot.sendMessage(src, "Your {0}'s Regenerator restored your {1}!".format(poke(trueLeader, true), finishName("uturn")), safchan); this.updateShop(player, "uturn"); } var oldLead = trueLeader; player.party = player.party.slice(1).concat([oldLead]); if (currentThemeEffect == "past") { player.altTimeline.lead = 0; } leader = this.getEffectiveLead(player); trueLeader = this.getEffectiveLead(player, true); safaribot.sendMessage(src, "Your lead Pokémon is now {0}!".format(poke(trueLeader, true)), safchan); if (player.berries.petayaCombo > 0 && oldLead !== trueLeader) { safaribot.sendMessage(src, "Your Petaya Combo was reset from {0} to 0 since your lead Pokémon was switched out!".format(player.berries.petayaCombo), safchan); player.berries.petayaCombo = 0; } } if (ball == "love") { for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].ownernum === player.idnum) { var addedHearts = 0; addedHearts += 1; if (hasCommonEggGroup(this.daycarePokemon[t].id, currentPokemon)) { addedHearts += 3; if (this.daycarePokemon[t].hearts < 50) { addedHearts += 2 if (this.hasCostumeSkill(player, "extraLoveBall")) { addedHearts++; } } if (this.daycarePokemon[t].hearts < 80) { addedHearts += 3 } if (this.daycarePokemon[t].hearts < 130) { addedHearts += 3 } if (this.hasCostumeSkill(player, "extraLoveBall")) { addedHearts++; } } safaribot.sendMessage(src, "Your {0} in the Daycare gained {1}!".format(poke(this.daycarePokemon[t].id), plural(addedHearts, "affection point")), safchan); this.daycarePokemon[t].hearts += addedHearts; safari.saveDaycare(); } } } if (crystalEffect.effect === "silver") { var gained = sys.rand(crystalEffect.rate[0], crystalEffect.rate[1]); player.balls.silver += gained; player.records.luxurySilver += gained; if (player.balls.silver > getCap("silver")) { player.balls.silver = getCap("silver"); } safaribot.sendAll(playerDisplayName + " found " + plural(gained, "silver") + " on the ground after catching " + pokeName + "!" , safchan); } this.fullBoxWarning(src); if (ball === "spirit" && safari.events.spiritBoxLimit - player.spiritDuels.box.length <= 5) { safaribot.sendMessage(src, "Your Spirit Box is almost full! You can only add " + (safari.events.spiritBoxLimit - player.spiritDuels.box.length) + " more Pokémon to your Spirit Box!", safchan); } var penalty = 2 * (1 - getPerkBonus(player, "soothe") - this.getFortune(player, "soothe", 0)); if (ball === "spy") { penalty *= 0.25; } if (player.costume == "chef" || player.costume == "battle") { cooldown += 500; } cooldown *= penalty; if (isRare(currentPokemon) || ball === "master" || ball === "cherish" || wildEvent || wildTera) { sys.appendToFile(mythLog, now() + "|||" + (clonedAmount - cloneDiscarded > 0 ? (clonedAmount - cloneDiscarded + 1) + "x " : "") + (wildEvent ? "Event " : "") + (wildSpirit ? "Spirit Realm " : "") + (wildTera ? "[" + currentTypeOverride + "] Terastallized " : "") + poke(currentPokemon) + (poke(currentDisplay) != poke(currentPokemon) ? " (disguised as "+ poke(currentDisplay) +")" : "") + "::caught::" + name + "'s " + finishName(ball) + (contestCount > 0 ? " during " + an(themeName(currentTheme)) + " Contest" : "") + "\n"); } var active = leader; var activeNum = parseInt(active, 10); var activeSpecies = evolutions.hasOwnProperty(activeNum+"") ? activeNum : pokeInfo.species(activeNum); if (evolutions.hasOwnProperty(activeSpecies) && evolutions[activeSpecies].evo !== -1 && leader === trueLeader) { // check against trueLeader to determine if transformed if (player.helds[0] == 9) { player.berries.petayaCombo++; var activeShiny = pokeInfo.shiny(active); var evoData = evolutions[activeSpecies]; var candiesRequired = Math.floor((evoData.candies || 300) * (activeShiny ? 1.25 : 1)); var discountRate = player.costume === "breeder" ? costumeData.breeder.rate : 1; candiesRequired = Math.floor(candiesRequired * discountRate); candiesRequired = this.candyCostConversion(player, candiesRequired); if (player.berries.petayaCombo >= candiesRequired) { player.berries.petayaCombo = 0; player.helds[0] = -1; var evolveTo = getPossibleEvo(active); var evolvedId = activeShiny ? "" + evolveTo : evolveTo; this.missionProgress(player, "evolve", active, 1, {}); var activeName = poke(active, true); this.evolvePokemon(src, { num: activeNum, id: active, shiny: activeShiny, name: poke(active), input: (activeShiny ? "*" : "") + pokePlain(activeNum), type: "poke" }, evolvedId, "evolved into", false, false); this.logLostCommand(sys.name(src), "evolve " + activeName, "evolved into " + poke(evolvedId, true)); safaribot.sendMessage(src, "Your " + activeName + " ate its Petaya Berry and evolved!", safchan); } else { safaribot.sendMessage(src, "Your {0}'s Petaya Combo: {1}/{2}".format(poke(leader, true), addComma(player.berries.petayaCombo), addComma(candiesRequired)), safchan); } } } var usingStarter = false; if (leader === player.starter) { usingStarter = true; } this.missionProgress(player, "catch", currentPokemon, 1, { starter: usingStarter, ball: ball, active: leader, luxury: luxuryAmount, clone: clonedAmount, color: (player.scaleDeadline >= now() ? player.scaleColor : null) }); this.missionProgress(player, "catchAny", currentPokemon, 1); this.costumeEXP(player, "catch"); if (currentThemeFlavor) { if (currentThemeFlavor.toLowerCase() == player.costume.toLowerCase() && player.costumeInfo[player.costume].level < 20) { this.costumeEXP(player, "catch"); //double exp safaribot.sendMessage(src, "You gained extra EXP for your {0} costume from the {1} theme!".format(costumeData[player.costume].fullName, themeName(currentTheme)), safchan); } } if (hasType(currentPokemon, "Water")) { this.costumeEXP(player, "catchwater"); } if (getBST(currentPokemon) <= 270) { this.costumeEXP(player, "catchlowbst"); } if (getBST(currentPokemon) >= 540) { this.costumeEXP(player, "catchhighbst", getBST(currentPokemon)); } if (parseInt(currentPokemon, 10) === 582) { this.missionProgress(player, "catchScoop", 0, 1); } if (parseInt(currentPokemon, 10) === 583) { this.missionProgress(player, "catchScoop", 0, 2); } if (parseInt(currentPokemon, 10) === 584) { this.missionProgress(player, "catchScoop", 0, 4); } if (currentTheme && contestThemes && contestThemes[currentTheme].eventFinal && contestThemes[currentTheme].eventFinal == parseInt(currentPokemon, 10)) { for (var i in player.eventFlags) { player.eventFlags[i] = 0; } } if (player.options.sellPrompt && !isRare(finalCatch)) { safaribot.sendHtmlMessage(src, "«{0}» ({1})".format(link("/sell " + poke(finalCatch) + ":confirm", "Click here to sell the " + poke(finalCatch, true) + " you just caught"), "You now have " + countRepeated(player.pokemon, finalCatch) + " " + poke(finalCatch, true)), safchan); } if (contestCount > 0) { var nameLower = name.toLowerCase(); if (!(nameLower in contestCatchers)) { contestCatchers[nameLower] = []; } contestCatchers[nameLower].push(currentPokemon); if (ball == "clone") { for (var i = 0; i < clonedAmount; i++) { contestCatchers[nameLower].push(currentPokemon); } } if (player.options.showContestCaptures) { sys.sendHtmlMessage(src, toColor("±Contest: ", "#3daa68") + "(Caught {0}, BST {1})".format(contestCatchers[nameLower].length, add(contestCatchers[nameLower].map(getBST))), safchan); } contestCombo += 1; if (!contestComboPlayers.contains(player.idnum)) { contestComboPlayers.push(player.idnum); } } if (player.costume === "fisherman" && safari.hasCostumeSkill(player, "reelOtherPokeballs") && chance(0.5)) { var validReels = Object.keys(wildBallThrows).filter(function(e) { return !itemData[e].special }); var finalSample = {}; for (var b in wildBallThrows) { if (!validReels.contains(b)) { continue; } var otherThrows = wildBallThrows[b].filter(function(e) { return e !== player.id }).length; if (otherThrows > 0) { finalSample[b] = otherThrows; } } if (Object.keys(finalSample).length > 0) { var finalReel = randomSample(finalSample); if (player.balls[finalReel] >= getCap(finalReel)) { safaribot.sendMessage(src, "You reeled in someone's unsuccessful " + finishName(finalReel) + "! Unfortunately, you can't carry any more...", safchan); } else { player.balls[finalReel] += 1; safaribot.sendMessage(src, "You reeled in someone's unsuccessful " + finishName(finalReel) + "! You now have " + plural(player.balls[finalReel], finishName(finalReel)) + ".", safchan); safari.saveGame(player); } } } if (safari.isDailyBoost(currentPokemon)) { if (!player.burningAura && !player.brilliantAura) { if (isStealth) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "After catching the Pokémon-of-the-Day, the " + typeIcon("Fire", "Burning Aura") + " was transferred to you!", safchan); if (amt > 1) { sys.sendMessage(src, "", safchan); } } else { sys.sendAll("", safchan); safaribot.sendHtmlAll("After catching the Pokémon-of-the-Day, the " + typeIcon("Fire", "Burning Aura") + "" + " was transferred to " + playerDisplayName + "!", safchan); if (amt > 1) { sys.sendAll("", safchan); } } player.burningAura = true; player.auraExpiry = now() + hours(auraHours); safaribot.sendHtmlMessage(src, "The Burning Aura granted you the following bonuses: " + safari.describeAuraEffects(player) + "!", safchan); sys.appendToFile(miscLog, now() + "|||" + sys.name(src) + "|||caught the Pokémon-of-the-Day (" + poke(currentPokemon) + ") and received a Burning Aura.\n"); } else { if (isStealth) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "The wild " + poke(currentPokemon, true) + "'s Burning Aura dissipated...", safchan); if (amt > 1) { sys.sendMessage(src, "", safchan); } } else { sys.sendAll("", safchan); safaribot.sendHtmlAll("The wild " + poke(currentPokemon, true) + "'s Burning Aura dissipated...", safchan); if (amt > 1) { sys.sendAll("", safchan); } } } } if (amt < 1) { sendAll("", true, true); currentPokemon = null; currentTypeOverride = null; currentExtraBST = 0; currentDisplay = null; currentDisplayBST = 0; wildEvent = false; wildBallThrows = {}; bufferThrows = {}; currentThrowers = []; wildSpirit = false; wildTera = false; currentBaiter = null; restrictedThrowers = []; restrictedThrows = 0; restrictedThrowTime = 0; if (contestCount <= 150 && (!(contestMidPoint))) { contestMidPoint = true; if (currentThemeEffect && ["rain","sunny","sandstorm","hail"].contains(currentThemeEffect)) { var opt = ["rain","sunny","sandstorm","hail"]; opt.splice(opt.indexOf(currentThemeEffect), 1); currentThemeEffect = opt.random(); weatherMessage(); } } checkUpdate(); } else { currentThrows -= (lastPokemonCount > 4 ? 2 : (contestCount > 0 ? 2 : 4)) * (lastPokemonCount - amt); if (currentThrows <= 0 && !wildEvent && !wildTera && !resolvingThrows) { flee = true; } else if (currentThrows <= 0 && !resolvingThrows && (currentTheme && contestThemes && contestThemes[currentTheme].eventFinal && contestThemes[currentTheme].eventFinal == currentPokemon)) { flee = true; } } } else { if (!wildBallThrows.hasOwnProperty(ball)) { wildBallThrows[ball] = []; } var keep = false; var keepMsg = ""; wildBallThrows[ball].push(player.id); cooldown = cooldown * (1 - this.getFortune(player, "soothe", 0)); if (!canHaveAbility(trueLeader, abilitynum("Skill Link")) && !wildEvent && !wildTera) { player.consecutiveCombo = 0; } if (ball == "ultra" && this.hasCostumeSkill(player, "lowUltraCD")) { cooldown *= 0.5; } if ((ball === "spirit") || (ball === "cherish")) { player.balls[ball] += 1; } else if (!freeThrow) { var chanceKeep = chance((player.costume === "fisherman" ? costumeData.fisherman.rate : 0) + this.getFortune(player, "fisherman", 0)); if (crystalEffect.effect === "fisherman" || chanceKeep || canHaveAbility(trueLeader, abilitynum("Ball Fetch"))) { keep = true; player.balls[ball] += 1; if (crystalEffect.effect === "fisherman") { keepMsg = "The power of your {0} instantly replaced your lost {1}!".format(finishName("crystal"), finishName(ball)); } else if (chanceKeep) { keepMsg = "A quick jerk of your fishing rod snags the {0} you just threw, allowing you to recover it!".format(finishName(ball)); } else if (canHaveAbility(trueLeader, abilitynum("Ball Fetch"))) { keepMsg = "Your {0}'s Ball Fetch retrieved the {1} for you!".format(poke(trueLeader, true), finishName(ball)); } } } pokeName = poke(currentDisplay, true); if (currentTheme && contestThemes[currentTheme].disguises && contestThemes[currentTheme].notDisguised && (!(contestThemes[currentTheme].notDisguised.contains(currentDisplay)))) { pokeName = "Trick-or-treater"; } safaribot.sendHtmlMessage(src, "You threw " + an(ballName) + " at " + pokeName +"! " + itemsLeft(player, ball), safchan); var failMsg = ""; if (rng < finalChance + 0.1) { failMsg = "Gah! It was so close, too!"; } else if (rng < finalChance + 0.2) { failMsg = "Aargh! Almost had it!"; } else if (rng < finalChance + 0.3) { failMsg = "Aww! It appeared to be caught!"; } else { failMsg = "Oh no! The " + pokeName + " broke free!"; } safaribot.sendHtmlMessage(src, failMsg + (player.balls[ball] > 0 ? " [" + link("/" + ccatch + " " + ball, "Try Again") + "]" : "") + " [" + link("/ballmacro", "Choose Another Action") + "]", safchan); var playerDisplayName = name; var isStealth = false; if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { isStealth = true; playerDisplayName = "an anonymous ninja"; } if (ball == "spy") { isStealth = true; playerDisplayName = "an anonymous person"; } sendAll(pokeName + " broke out of " + playerDisplayName + "'s " + ballName + "!"); if (keep) { safaribot.sendMessage(src, keepMsg, safchan); } if (canHaveAbility(currentPokemon, abilitynum("Color Change")) && !ignoresWildAbilities(player) && !wildTera) { if (type2(leader) !== "???") { // If has 2 types currentTypeOverride = [type1(leader), type2(leader)].random(); // Pick a random one } else { currentTypeOverride = type1(leader); // Else pick their only type } sendAll("The wild " + pokeName + "'s Color Change changed " + (currentPokemonCount > 1 ? "their" : "its") + " type to " + (isStealth ? "something unknown" : currentTypeOverride) + "!"); } if (canHaveAbility(currentPokemon, abilitynum("Moxie")) && currentExtraBST < wildAbilityBoostLimit && !ignoresWildAbilities(player)) { currentExtraBST += wildAbilityBoost; sendAll("The wild " + pokeName + " " + (currentPokemonCount > 1 ? "are" : "is") + " getting stronger due to Moxie!"); } if (canHaveAbility(currentPokemon, abilitynum("Defeatist")) && -currentExtraBST < wildAbilityBoostLimit && !ignoresWildAbilities(player)) { currentExtraBST -= wildAbilityBoost; sendAll("The wild " + pokeName + " " + (currentPokemonCount > 1 ? "are" : "is") + " losing confidence due to Defeatist!"); } player.records.pokesNotCaught += 1; currentThrows -= 1; throwAttempts += 1; if (canHaveAbility(currentPokemon, abilitynum("Run Away")) && !ignoresWildAbilities(player)) { currentThrows -= 1; } if (canHaveAbility(currentPokemon, abilitynum("Wimp Out")) && !ignoresWildAbilities(player)) { currentThrows = 0; } if (canHaveAbility(currentPokemon, abilitynum("Emergency Exit")) && !ignoresWildAbilities(player) && chance(0.5)) { currentThrows = 0; } if (currentThrows <= 0 && !wildEvent && !wildTera && !resolvingThrows) { flee = true; } } if ((ball === "heavy")) { var wildWeight = getWeight(wild); var mult = Math.min((1.33 - ((wildWeight-60)/300)), 1); cooldown *= mult; } if (this.hasCostumeSkill(player, "reducedCatchFailCD")) { cooldown *= (1 - ((this.getCostumeLevel(player)-5)/75)) } if (flee) { this.pokemonFlee(); if (contestCount <= 150 && (!(contestMidPoint))) { contestMidPoint = true; if (currentThemeEffect && ["rain","sunny","sandstorm","hail"].contains(currentThemeEffect)) { var opt = ["rain","sunny","sandstorm","hail"]; opt.splice(opt.indexOf(currentThemeEffect), 1); currentThemeEffect = opt.random(); weatherMessage(); } } } else { this.changeWildMood(leader); this.changeWildAction("catch") if (!freeThrow && !allBalls.filter(function(e) { return itemData[e].special }).contains(ball)) { if (crystalEffect.effect === "double" || (safari.hasCostumeSkill(player, "ninjaDoubleThrow") && chance(0.5) && currentPokemonCount === 1 && !caughtAny)) { this.throwBall(src, ball, true, true, command, false, true); } } } if (crystalEffect.effect === "gacha") { this.gachapon(src, "*", true); } if (crystalEffect.effect === "cooldown") { cooldown = Math.round(cooldown * crystalEffect.rate); } player.cooldowns.ball = currentTime + cooldown; player.cooldowns.bait = player.cooldowns.ball; if (ball === player.options.favoriteBall && ball !== "safari" && player.balls[ball] <= 5) { if (player.balls[ball] === 0) { safaribot.sendMessage(src, "Note: You are completely out of " + es(ballName) + ". Consider obtaining more or setting a new favorite ball with /options favorite.", safchan); } else { safaribot.sendMessage(src, "Note: You have " + plural(player.balls[ball], ballName) + " left. Consider obtaining more or setting a new favorite ball with /options favorite.", safchan); } } lastWildAction = now(); safari.pendingNotifications(player.id); if (currentPokemonCount > 0 && !currentThrowers.contains(player.id)) { currentThrowers.push(player.id); } if (contestCount > 0 && !contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } if (restrictedThrowers.length && restrictedThrows > 0) { restrictedThrows--; if (restrictedThrows <= 0) { restrictedThrowers = []; restrictedThrows = 0; restrictedThrowTime = 0; safaribot.sendHtmlAll("Anyone can throw Balls now!", safchan); } } this.saveGame(player); }; this.pokemonFlee = function(customFlee, skipLog) { var pokeName = poke(currentPokemon, true); var runmsgs = [ "The wild {0} got spooked and fled!", "The wild {0} got hungry and went somewhere else to find food!", "The wild {0} went back home to take their medicine!", "The wild {0} hid in a hole and disappeared!", "The wild {0} pointed to the sky. While everyone was looking at the clouds, the {0} fled!", "The wild {0} vanished into thin air!", "The wild {0} spontaneously combusted and turned to ash.", "The wild {0} was really just a figment of everyone's imagination!", "The wild {0} got eaten by a much larger Pokémon!", "The wild {0} was actually just a well made PokéDoll!", "The wild {0} was actually a Poké Fan cosplaying as a {0}!", "The wild {0} turned into MissingNo and glitched out of existence!", "The wild {0} was not really wild! Their owner called them back to their Poké Ball!", "The wild {0} was snatched up by a Team Rocket mecha!", "The wild {0} ate a Warp Seed and escaped via teleport!", "The wild {0} escaped through a hidden entrance to the Distortion World!", "The wild {0} was stolen and turned into candies!", "The wild {0} used Self-Destruct. BOOM!", "The wild {0} blasted off at the speed of light!", "\"I must go. My people need me.\" The wild {0} rocketed off!", "The wild {0} was banished to the Shadow Realm!", "The wild {0} accidentally triggered a trap card and was sent to the graveyard!", "The wild {0} achieved nirvana and transcended to a higher plane!", "The wild {0} got bored and went to #" + ["Mafia", "Trivia", "Hangman", "Evolution Game", "Tournaments"].random() + "!", "The wild {0} paid its retreat cost and returned to the bench!", "The wild {0} was caught, but the Poké Ball containing it mysteriously vanished!", "The wild {0} was divided by zero!" ]; if ((isRare(currentPokemon) || wildTera) && !skipLog) { sys.appendToFile(mythLog, now() + "|||" + (wildSpirit ? "Spirit Realm " : "") + (wildTera ? "[" + currentTypeOverride + "] Terastallized " : "") + poke(currentPokemon) + "::fled" + (contestCount > 0 ? " during " + an(themeName(currentTheme)) + (currentThemeAlter ? " (" + contestThemes[currentTheme].alterName + ")" : "") + (currentThemeEffect ? " [" + cap(currentThemeEffect) + "]": "") + " Contest" : "") + "::\n"); runmsgs = ["The wild {0} was obliterated by a grumpy old safari coder!"]; } if (canHaveAbility(currentPokemon, abilitynum("Run Away"))) { runmsgs = ["The wild {0} escaped using Run Away!"]; } if (canHaveAbility(currentPokemon, abilitynum("Wimp Out"))) { runmsgs = ["The wild {0} fled instantly due to Wimp Out!"]; } if (canHaveAbility(currentPokemon, abilitynum("Emergency Exit"))) { runmsgs = ["The wild {0} made a tactical retreat using Emergency Exit!"]; } if (customFlee) { runmsgs = [customFlee]; } if (contestCount > 0) { contestCombo = 0; contestComboPlayers = []; } if (lastEscapedMons) { var bst = getBST(currentPokemon); if ((bst >= 450 || isRare(currentPokemon)) && bst <= 600) { lastEscapedMons.push({ poke: currentPokemon, price: Math.round(getPrice(currentPokemon, typeof currentPokemon === "string") * (isRare(currentPokemon) ? 4 : 1.5 * (1 + itemData.amulet.maxRate))) }); } while (lastEscapedMons.length > 100) { lastEscapedMons.shift(); } permObj.add("lastEscapedMons", JSON.stringify(lastEscapedMons)); } sys.sendAll("", safchan); safaribot.sendAll(runmsgs.random().format(pokeName), safchan); sys.sendAll("", safchan); currentPokemon = null; currentTypeOverride = null; currentDisplay = null; currentDisplayBST = 0; currentExtraBST = 0; currentPokemonCount = lastPokemonCount = 1; currentBaiter = null; wildSpirit = false; wildTera = false; wildBallThrows = {}; bufferThrows = {}; currentThrowers = []; spiritSpawn = false; }; this.checkSimilarity = function(poke1, poke2, colorOverride) { var out = 1; var userColor = colorOverride ? colorOverride : getPokeColor(poke1); if (hasType(poke2, type1(poke1))) { out = Math.max(2, out + 0.5); if (hasType(poke2, type2(poke1)) && type2(poke1) === "???") { out = Math.max(2, out + 2); } else if (hasType(poke2, type2(poke1))) { out = Math.max(out + 2, 4); } } else if (hasType(poke2, type2(poke1)) && type2(poke1) !== "???") { out = Math.max(2, out + 0.5); } var ab = [getPokeAbility(poke2, 0), getPokeAbility(poke2, 1), getPokeAbility(poke2, 2)].filter(function (a) { return a !== 0; }); for (var i = 0; i < ab.length; i++) { if (canHaveAbility(poke1, ab[i])) { out = Math.max(6, out + (3 / ab.length)); } } if (userColor === getPokeColor(poke2)) { out += 2; } if (hasCommonEggGroup(poke1, poke2)) { out *= 1.667; } var stats = ["HP", "Attack", "Defense", "Special Attack", "Special Defense", "Speed"], st; for (var s in stats) { st = stats[s]; if (Math.abs(getStatsNamed(poke1)[st] - getStatsNamed(poke2))[st] <= 10) { out *= 1.2; } if (Math.abs(getStatsNamed(poke1)[st] - getStatsNamed(poke2))[st] <= 1) { out *= 1.2; } } return out; }; this.checkEffective = function(atk, def, inverted, select, select2) { var result = 1; var immuneCount = 0; var typeCount = 1; if (!atk[1]) { atk[1] = "???"; } if (!def[1]) { def[1] = "???"; } if (!def[2]) { def[2] = "???"; } if (atk.contains("Stellar")) { def = [def[0], "???", "???"]; } if (select) { if (select.classicTypes) { def = def.map(function(e) { return ["Dark", "Steel", "Fairy"].contains(e) ? "???" : e }); } if (select.corrosion) { if (atk[0] == "Poison" && def.contains("Steel")) { return 2 * this.checkEffective([atk[0]], def.filter(function(e) { return e !== "Steel" }), inverted); } } if (select.normalcy) { var third = (def.contains("Normal") ? "???": "Normal"); return this.checkEffective(atk, def.concat([third]), inverted); } if (select.draconian) { var third = (def.contains("Dragon") ? "???": "Dragon"); return this.checkEffective(atk, def.concat([third]), inverted); } if (select.mechanical) { var third = (def.contains("Steel") ? "???": "Steel"); return this.checkEffective(atk, def.concat([third]), inverted); } if ((select.resistMode) || (select2 && select2.resistMode)) { return this.checkEffective(def, atk, !inverted); } } var countImmune = function(value) { if (value === 0) { immuneCount++; return immuneMultiplier; } return value; }; var inverse = function(value) { if (inverted) { return Math.max(value, 0.5); } return value; }; for (var a in atk) { var attackType = atk[a]; if (!effectiveness.hasOwnProperty(attackType)) { continue; } var attacker = effectiveness[attackType]; for (var d in def) { var defenseType = def[d]; if (!attacker.hasOwnProperty(defenseType)) { continue; } if (select && select.scrappy && ["Normal", "Fighting"].contains(attackType) && defenseType === "Ghost" && !inverted) { continue; } result *= countImmune(inverse(attacker[defenseType])); } if (select && select.levitate && attackType === "Ground") { result *= countImmune(0); } } if (select) { if (select.tintedLens && result <= 0.5) { result *= 2; } if (select.simple) { if (result > 1) { result *= 2; } else if (result < 1) { result /= 2; } } if (atk.contains("Stellar") && select.tera) { result = 2; } if (select.teraShell) { result = Math.min(0.5, result); } } if (atk[1] !== "???") { typeCount++; } if (immuneCount >= typeCount) { return 0; } return inverted ? 1 / result : result; }; this.releasePokemon = function(src, data) { var verb = "release"; if (!canLosePokemon(src, data, verb)) { return; } var player = getAvatar(src); if (player.tradeban > now()) { safaribot.sendMessage(src, "You are banned from releasing for " + timeLeftString(player.tradeban) + "!", safchan); return; } if (data === "*") { safaribot.sendMessage(src, "To release a Pokémon, use /release [name]:confirm!", safchan); return; } if (this.isBattling(sys.name(src))) { safaribot.sendMessage(src, "You can't release a Pokémon during a battle!", safchan); return; } if (this.isInAuction(sys.name(src))) { safaribot.sendMessage(src, "You can't release a Pokémon while participating in an auction!", safchan); return; } if (currentPokemon) { safaribot.sendMessage(src, "There's already a Pokemon out there!", safchan); return true; } if (releaseCooldown > 0) { safaribot.sendMessage(src, "Please spend the next " + timeLeftString(releaseCooldown) + " saying good bye to your Pokémon before releasing it!", safchan); return; } var input = data.split(":"); var info = getInputPokemon(input[0]); var shiny = info.shiny; var num = info.num; var id = info.id; if (input.length < 2 || input[1].toLowerCase() !== "confirm") { safaribot.sendMessage(src, "You can release your " + info.name + " by typing /release " + info.input + ":confirm.", safchan); return; } safaribot.sendAll(sys.name(src) + " released their " + info.name + "!", safchan); this.logLostCommand(sys.name(src), "release " + data); this.removePokemon(src, id); player.records.pokesReleased += 1; player.cooldowns.ball = now() + 20 * 1000; this.saveGame(player); releaseCooldown = releaseCooldownLength; safari.createWild(num, shiny); }; this.compileThrowers = function() { var name, e; for (e in contestantsWild) { if (contestantsWild.hasOwnProperty(e)) { name = contestantsWild[e]; if (!(name in contestantsCount)) { contestantsCount[name] = 0; } contestantsCount[name]++; } } contestantsWild = []; }; this.flashPlayers = function() { var players = sys.playersOfChannel(safchan); for (var pid in players) { var player = getAvatar(players[pid]); if (player && player.options.flashme) { sys.sendHtmlMessage(players[pid], toColor("±Ping: ", "#3daa68") + toFlashing(addFlashTag(sys.name(players[pid])) + ", a Contest/event is starting!", sys.name(players[pid])), safchan); } } }; this.fullBoxWarning = function(src) { var player = getAvatar(src); if (player.pokemon.length >= getPerkBonus(player, "box") - 5) { var remaining = getPerkBonus(player, "box") - player.pokemon.length; if (remaining > 0) { safaribot.sendMessage(src, "Your boxes are almost full! You can still catch " + remaining + " more Pokémon!", safchan); } else { safaribot.sendMessage(src, "Your boxes are full! You cannot catch any more Pokémon unless you buy another box or decrease the number of Pokémon in your possession.", safchan); } } }; this.rockScare = function(src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "scare a wild Pokémon"; if (player.tutorial.inTutorial) { if (cantBecause(src, reason, ["tutorial"])) { return; } } var name = sys.name(src); var timeSinceLastAction = now() - lastWildAction; if (cantBecause(src, reason, ["item", "auction", "battle", "event", "pyramid"])) { return; } if (!currentPokemon) { safaribot.sendMessage(src, "No wild Pokémon around!", safchan); return; } if (contestCount > 0 && contestForfeited.contains(player.idnum)) { safaribot.sendMessage(src, "You forfeited this Contest, so you can't scare any Pokémon from the Contest!", safchan); return; } if (wildEvent) { safaribot.sendMessage(src, "You can't scare an Event Pokémon!", safchan); return; } if (isRare(currentDisplay)) { safaribot.sendMessage(src, "You can't scare a rare Pokémon!", safchan); return; } if (timeSinceLastAction < rockScareThreshold) { safaribot.sendHtmlMessage(src, "You can only scare the Pokémon away if " + (currentPokemonCount > 1 ? "they have" : "it has") + " not been interacted with for " + plural(Math.ceil((rockScareThreshold - timeSinceLastAction) / 1000), "more second") + "! [" + link("/rockscare", "Try Again") + "] [" + link("/ballmacro", "Choose Another Action") + "]", safchan); return; } if (player.balls.rock < 1) { safaribot.sendMessage(src, "You do not have any " + es(finishName("rock")) + " to throw!", safchan); return; } if (isRare(currentPokemon) || wildTera) { sys.appendToFile(mythLog, now() + "|||" + (wildTera ? "[" + currentTypeOverride + "] Terastallized " : "") + poke(currentPokemon) + (currentDisplay !== currentPokemon ? " (disguised as " + poke(currentDisplay) + ")" : "") + "::was rock scared by " + sys.name(src) + "::\n"); } player.balls.rock -= 1; player.records.wildsScared += 1; var playerDisplayName = sys.name(src); if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { playerDisplayName = "Some stealthy ninja"; } safari.pokemonFlee("{0} threw {1} at the wild {2}, causing {3} to flee!".format(playerDisplayName, an(finishName("rock")), poke(currentPokemon, true), currentPokemonCount > 1 ? "them" : "it")); safaribot.sendMessage(src, itemsLeft(player, "rock"), safchan); this.saveGame(player); if (contestCount > 0) { if (!contestActivity.hasOwnProperty(player.id)) { contestActivity[player.id] = []; } if (!contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } } }; this.throwPokeblock = function(src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "throw a pokeblock"; if (player.tutorial.inTutorial) { if (cantBecause(src, reason, ["tutorial"])) { return; } } var name = sys.name(src); if (cantBecause(src, reason, ["item", "auction", "battle", "event", "pyramid"])) { return; } if (!currentPokemon) { safaribot.sendMessage(src, "No wild Pokémon around!", safchan); return; } if (contestCount > 0 && contestForfeited.contains(player.idnum)) { safaribot.sendMessage(src, "You forfeited this Contest, so you can't feed any Pokémon from the Contest!", safchan); return; } if (wildEvent || wildTera) { safaribot.sendMessage(src, "Event and Terastallized Pokémon won't flee, there's no point throwing a Pokéblock!", safchan); return; } if (player.balls.pokeblock < 1) { safaribot.sendMessage(src, "You do not have any Pokéblocks to throw!", safchan); return; } if (pokeblockThrows >= 3) { safaribot.sendMessage(src, "The wild Pokémon is not interested in any more Pokéblocks!", safchan); return; } var currentTime = now(); pokeblockThrows += 1; player.balls.pokeblock -= 1; safaribot.sendMessage(src, "You threw a Pokéblock! You now have " + plural(player.balls.pokeblock, "Pokéblock") + "!", safchan); lastWildAction = now(); this.saveGame(player); if (contestCount > 0) { if (!contestActivity.hasOwnProperty(player.id)) { contestActivity[player.id] = []; } if (!contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } } var playerDisplayName = sys.name(src); if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { playerDisplayName = "Some stealthy ninja"; } var boosted = safari.hasCostumeSkill(player, "pokeblockBoost"); safaribot.sendHtmlAll(toColor(playerDisplayName + " is feeding the " + poke(currentDisplay, true) + " a " + (boosted ? "strong " : "") + "Pokéblock!", "#438ed9"), safchan); this.changeWildMood(safari.getEffectiveLead(player)); currentThrows = getMaxThrows(currentPokemon, currentPokemonCount, (typeof currentPokemon == "string" ? true : false), throwAttempts, boosted); }; /* Photos */ this.takePhoto = function(src, data, bypass, suppress, command, bufferThrow, ignoreThrow) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "take a photo"; if (player.tutorial.inTutorial) { if (cantBecause(src, reason, ["tutorial"])) { return; } } if (currentTheme && contestThemes[currentTheme].disguises) { safaribot.sendMessage(src, "You can't take photos in this theme!", safchan); return; } var name = sys.name(src); if (!suppress && !bypass) { var mess = "[Track] " + name + " is using /" + command + " " + data + " (Time since last wild/trick: " + ((now() - lastWild)/1000) + " seconds)" + (bufferThrow ? " [Buffered Throw]" : ""); this.trackMessage(mess, player); } if (contestCount > 0 && !contestActivity.hasOwnProperty(player.id)) { contestActivity[player.id] = []; } if (player.balls.lens === 0) { safaribot.sendMessage(src, "You need at least one " + finishName("lens") + " to take a photo!", safchan); return; } if (data === "cancel") { if (preparationPhase > 0 && preparationThrows.hasOwnProperty(name.toLowerCase())) { safaribot.sendMessage(src, "You cancelled your photo!", safchan); delete preparationThrows[name.toLowerCase()]; } else if (bufferThrows.hasOwnProperty(player.id)) { safaribot.sendMessage(src, "You cancelled your photo!", safchan); delete bufferThrows[player.id]; } else { safaribot.sendMessage(src, "You weren't preparing to take a photo...", safchan); } return; } if (!currentPokemon) { if (suppress) { safaribot.sendMessage(src, "Someone caught the wild Pokémon while you were preparing your camera!", safchan); } else { safaribot.sendMessage(src, "No wild Pokémon around!", safchan); } return; } if (player.photos.length >= 20) { safaribot.sendMessage(src, "Your camera's memory is full! You need to delete some photos to take more!", safchan); return; } if (cantBecause(src, reason, ["item", "auction", "battle", "event", "pyramid"])) { return; } if (contestCount > 0 && contestForfeited.contains(player.idnum)) { safaribot.sendMessage(src, "You forfeited this Contest, so you can't takes photos of any Pokémon from the Contest!", safchan); return; } var currentTime = now(); if (!bypass && (!preparationFirst || name.toLowerCase() !== preparationFirst) && player.cooldowns.ball > currentTime) { safaribot.sendHtmlMessage(src, "You are preparing to take a photo at the next opportunity! (Remaining cooldown: " + timeLeftString(player.cooldowns.ball) + ") [" + link("/cancel", "Cancel") + "]", safchan); bufferThrows[player.id] = { "ball": "takephoto", "throwAt": player.cooldowns.ball + 1 }; if (contestCount > 0 && !contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } return; } if (contestCount > 0 && contestantsWild.indexOf(name.toLowerCase()) === -1) { contestantsWild.push(name.toLowerCase()); } if (preparationPhase > 0) { safaribot.sendMessage(src, "You are preparing your camera to take a photo!", safchan); preparationThrows[name.toLowerCase()] = "takephoto"; if (contestCount > 0 && !contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } return; } var cooldown = Math.round(itemData.lens.cooldown * (1 - this.getFortune(player, "photocd", 0))); if (this.hasCostumeSkill(player, "lowPhotoCD")) { cooldown *= (1 - (this.getCostumeLevel(player)/40)); } cooldown = Math.max(6000, cooldown); var p = player.balls.lens * itemData.lens.bonusRate; p = Math.min(p, itemData.lens.maxRate); var qualityOdds = { 0: 11, 1: Math.min(10 + p, 11), 2: Math.min(9 + p, 11), 3: Math.min(8 + p, 11), 4: Math.min(7 + p, 11), 5: Math.min(6 + p, 11), 6: Math.min(5 + p, 11), 7: Math.min(4 + p, 11), 8: Math.min(3 + p, 11), 9: Math.min(2 + p, 11), 10: Math.min(1 + p, 11) }; var leader = safari.getEffectiveLead(player, true); if (canHaveAbility(leader, abilitynum("Keen Eye"))) { for (var i = -5, q = 0; i <= 5; i++, q++) { qualityOdds[q] += i; if (i >= 0 && player.costume === "journalist") { qualityOdds[q] += costumeData.journalist.rate; } } } var quality = randomSample(qualityOdds); var target = currentDisplay; if (target === 0 || wildEvent) { target = currentPokemon; } if (safari.hasCostumeSkill(player, "scientistPhotoGuarantee") && target == scientistQuest.pokemon) { quality = Math.max(7, quality); } var pokeName = poke(target, true); var period = new Date().getUTCHours(); period = ["night", "morning", "afternoon", "evening"][Math.floor(period/6)]; var where = (contestCount > 0 && currentTheme ? currentTheme : (player.mushroomTheme ? player.mushroomTheme : "default")); if (player.mushroomTheme && player.mushroomDeadline === 0) { player.mushroomTheme = null; } var photo = { id: target, amt: currentPokemonCount, when: period, where: where, what: currentPokemonAction, mood: currentPokemonMood, score: parseInt(quality, 10) }; var playerDisplayName = sys.name(src); if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { playerDisplayName = "Some stealthy ninja"; } sendAll(playerDisplayName + " is taking a photo of the " + pokeName + "!"); player.records.photosTaken += 1; player.photos.push(photo); safaribot.sendHtmlMessage(src, toColor("You took a photo of " + this.describePhoto(photo) + "!" + (player.photos.length < 20 ? " [" + link("/photo", "Take Another") + "]" : "") + " [" + link("/ballmacro", "Choose Another Action") + "] [", "#DD4411") + link("/album delete:" + (player.photos.length), "Delete", true) + toColor("]", "#DD4411"), safchan); safari.costumeEXP(player, "takephoto", currentPokemonCount); if (player.photos.length >= 20) { safaribot.sendMessage(src, "Your camera's memory is now full! You need to free up some space to take more photos!", safchan); } else { safaribot.sendMessage(src, "You can still take " + plural(20-player.photos.length, "photo", false, true) + "!", safchan); } player.cooldowns.ball = currentTime + cooldown; this.missionProgress(player, "photo", currentDisplay, 1, { photo: photo }); lastWildAction = now(); this.saveGame(player); if (contestCount > 0 && !contestActivity[player.id].contains(lastWild)) { contestActivity[player.id].push(lastWild); } if (!ignoreThrow) { currentThrows -= 2; } if (currentThrows <= 0 && !wildEvent && !wildTera && !resolvingThrows) { this.pokemonFlee(); } else { this.changeWildMood(safari.getEffectiveLead(player)); this.changeWildAction("photo"); } if (safari.hasCostumeSkill(player, "smartPhoto") && photographQuest) { var e, i, t, desc, playerTracks = player.quests.journal.trackedRequests; for (e in photographQuest) { obj = photographQuest[e]; if (safari.photoMatchesRequest(photo, obj)) { t = timeLeftString(obj.deadline); if (obj.deadline - now() <= journalDoneDeadline) { t = toColor(t, "red"); } desc = "{0}. A photo of {1} (Score: {2} | Deadline: {3})".format(e, safari.translatePhotoRequest(obj), obj.fscore, t); safaribot.sendHtmlMessage(src, toColor(desc, player.quests.journal.cooldown > now() ? "crimson" : "magenta") + " [" + (playerTracks.hasOwnProperty(e) ? link("/quest journal:untrack:" + e, "Stop Tracking") : (obj.deadline - now() <= journalDoneDeadline ? "You cannot track expiring requests" : link("/quest journal:track:" + e, "Track"))) + "] [" + link("/quest journal:" + e, "You can fulfill this request" + (player.quests.journal.cooldown > now() ? " in " + timeLeftString(player.quests.journal.cooldown) : "")) + "]", safchan); } } } }; this.changeWildMood = function(attacker) { var target = currentPokemon; var typeBonus = this.checkEffective([type1(attacker), type2(attacker)], [type1(target), type2(target)]); var range = [0, 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; var valueList = [10, 8, 4, 2, 1, 0, -1, -2, -4, -8,]; var mod = 0; for (var e = range.length; e--; ) { if (typeBonus >= range[e]) { mod = valueList[e]; break; } } var previousMood = ["Negative", "Neutral", "Positive"][Math.ceil(currentPokemonMoodRate/10)-1]; currentPokemonMoodRate += mod; currentPokemonMoodRate = Math.max(1, Math.min(30, currentPokemonMoodRate)); var newMood = ["Negative", "Neutral", "Positive"][Math.ceil(currentPokemonMoodRate/10)-1]; if (newMood !== previousMood && currentPokemon) { currentPokemonMood = photoMood[newMood].random(); var players = sys.playersOfChannel(safchan); for (var id in players) { var player = getAvatar(players[id]); if (!player) { continue; } if (safari.hasCostumeSkill(player, "revealMood") && !cantBecause(players[id], "", ["auction", "battle", "event", "pyramid", "baking"], "", true)) { safaribot.sendMessage(players[id], "The wild {0} began feeling {1}!".format(poke(currentDisplay, true), currentPokemonMood), safchan); } } } }; this.changeWildAction = function(action) { if (chance(0.3 + (currentThrows/55))) { return; } var id = currentPokemon; var t = type1(id); var list = photoActions[t]; var newAction; t = type2(id); if (t !== "???") { list = list.concat(photoActions[t]); } var bst = getBST(id), type = "Neutral", max = bst / 1000, rng = Math.random(); if (rng < max) { type = action === "photo" ? "Negative" : "Positive"; } else if (rng > max + 0.24) { type = action === "photo" ? "Positive" : "Negative"; } list = removeDuplicates(list.concat(photoActions[type])); newAction = list.random(); if (currentPokemonAction !== newAction && currentPokemon) { currentPokemonAction = newAction; var players = sys.playersOfChannel(safchan); for (var id in players) { var player = getAvatar(players[id]); if (!player) { continue; } if (safari.hasCostumeSkill(player, "revealAction") && !cantBecause(players[id], "", ["auction", "battle", "event", "pyramid", "baking"], "", true)) { safaribot.sendMessage(players[id], "The wild {0} started {1}!".format(poke(currentDisplay, true), currentPokemonAction), safchan); } } } }; this.describePhoto = function(photo) { var qualityName = photoQuality[photo.score]; return (photo.amt === 1 ? an(photo.mood) : toWord(photo.amt) + " " + photo.mood) + " " + poke(photo.id) + " " + photo.what + " in " + an(themeName(photo.where)) + " environment during the " + photo.when + " (Quality: " + qualityName + ")"; }; this.viewPhotos = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var photos = player.photos; var l = photos.length; if (l === 0) { safaribot.sendMessage(src, "You have no photos!", safchan); return; } var cmd = toCommandData(data, ["action", "index"]); var action = cmd.action ? cmd.action.toLowerCase() : "*"; if (action === "*") { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "You have " + l + " photos stored (you can hold up to 20 photos):", safchan); for (var e = 0; e < l; e++) { safaribot.sendHtmlMessage(src, "[" + (e+1) + "] " + cap(this.describePhoto(photos[e])), safchan); } safaribot.sendHtmlMessage(src, "To delete a photo, use " + link("/album delete:Number", null, true) + ". To delete multiple photos at once, use " + link("/album delete:Number,Number", null, true) +"!", safchan); sys.sendMessage(src, "", safchan); } else if (action === "delete") { if (!cmd.index) { safaribot.sendHtmlMessage(src, "To delete a photo, use " + link("/album delete:Number", null, true) + ". To delete multiple photos at once, use " + link("/album delete:Number,Number", null, true) +"!", safchan); return; } var list = cmd.index.split(","); var out = [], index, r; for (var e = list.length; e--; ) { r = list[e].trim(); index = parseInt(r, 10); if (isNaN(index) || index < 1 || index > l) { out.push(r); } else { list[e] = index; } } if (out.length > 0) { safaribot.sendMessage(src, "The following values are not valid photos: " + readable(out), safchan); return; } list = list.sort(function(a, b) { return a - b; }); list = removeDuplicates(list, true); if (list.length === 0) { safaribot.sendMessage(src, "You didn't delete any photo!", safchan); return; } out = []; for (e = list.length; e--; ) { index = list[e]-1; out.push(list[e] + " (" + cap(this.describePhoto(photos[index])) + ")"); photos.splice(index, 1); } out.reverse(); safaribot.sendMessage(src, "You deleted the following " + (out.length === 1 ? "photo" : (out.length + " photos")) + ": " + readable(out) + "!", safchan); this.saveGame(player); } }; /* Player Info */ this.viewOwnInfo = function(src, textOnly) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var isAndroid = (sys.os(src) === "android"); var out = ""; //Current Party table out += this.showParty(src, true); //All owned Pokémon out += this.showBox(player, 1, isAndroid, textOnly); //Money/Balls table if (textOnly) { out += "
"; } out += this.showBag(player, isAndroid, textOnly); if (textOnly) { out += "
"; } //Costumes out += this.showCostumes(src); sys.sendHtmlMessage(src, out, safchan); }; this.cherishVisible = function(src, data) { var player = getAvatar(src); if (player) { switch (data.toLowerCase()) { case "off": player.options.cherishOff = true; safaribot.sendMessage(src, "Now hiding Cherished message on successful catch.", safchan); break; case "on": player.options.cherishOff = false; safaribot.sendMessage(src, "Now allowing Cherished message on successful catch.", safchan); break; default: safaribot.sendHtmlMessage(src, "Type " + link("/options cherishmsg:on") + " or " + link("/options cherishmsg:off") + " to toggle the Cherished message on successful catch.", safchan); } } this.saveGame(player); } this.viewPlayer = function(src, data, textOnly, bypass) { var player = getAvatar(src); if (!player) { return; } var id = sys.id(data); if (!id) { safaribot.sendMessage(src, "No such player!", safchan); return; } var target = getAvatar(id); if (!target) { safaribot.sendMessage(src, "This person didn't enter the Safari!", safchan); return; } if (target != player && !target.options.visible) { if (!bypass && !SESSION.channels(safchan).isChannelOwner(src)) { safaribot.sendMessage(src, "You cannot view this person's party!", safchan); return; } else if (bypass) { safaribot.sendHtmlMessage(id, "" + sys.name(src) + " used an auth bypass to view your party. If this was not communicated/consented to beforehand, contact a channel owner to report abuse", safchan); } } sys.sendHtmlMessage(src, this.showParty(id, false, src, textOnly), safchan); }; this.viewItems = function(src, command, search) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var isAndroid = (sys.os(src) === "android"); var textOnly = false; if (command !== "bagf") { textOnly = player.options.textViewOnly || command === "bagt"; } sys.sendHtmlMessage(src, this.showBag(player, isAndroid, textOnly, search), safchan); }; this.viewBox = function(src, data, textOnly, shopLink) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var isAndroid = (sys.os(src) === "android"); sys.sendHtmlMessage(src, this.showBox(player, (data === "*" ? 1 : data), isAndroid, textOnly, shopLink), safchan); }; this.viewCherished = function(src, textOnly) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var isAndroid = (sys.os(src) === "android"); sys.sendHtmlMessage(src, this.showCherished(player, isAndroid, textOnly), safchan); }; this.giveItem = function(src, item, slot) { var player = getAvatar(src); if (cantBecause(src, "give an item", ["tutorial", "auction", "pyramid", "battle", "baking"])) { return false; } var hit = null; for (var a in heldCodes) { if (heldCodes[a+""] == item.toLowerCase().replace(" berry", "")) { hit = a; } } if (hit == null) { safaribot.sendMessage(src, "No such item!", safchan); return; } this.heldItem(player, hit, false, slot); }; this.takeItem = function(src, slot, removeSlot) { var player = typeof src === "number" ? getAvatar(src) : src; if (typeof player !== "object") { return; } if (isPlaying(player.id)) { if (cantBecause(sys.id(player.id), "take an item", ["tutorial", "auction", "pyramid", "battle", "baking"])) { return false; } } if (!slot || isNaN(slot)) { //safaribot.sendMessage(src, "Please enter a valid party slot number (1 to 6)!", safchan); //return; slot = 0; } else slot = parseInt(slot) - 1; // assumption that most players will logically input 1~6 instead of proper array index of 0~5 if (slot < 0 || slot > 5) { safaribot.sendMessage(src, "Please enter a valid party position (1 to 6)!", safchan); return; } if (slot >= player.party.length) { safaribot.sendMessage(src, "You don't have a " + getOrdinal(slot+1) + " Pokémon in your party!", safchan); return; } if (player.helds[slot] == -1) { safaribot.sendMessage(src, "Your " + poke(player.party[slot], true) + " at position " + (slot+1) + " is not holding an item!", safchan); return; } this.heldItem(player, player.helds[slot], true, slot); if (player.party[slot] && !removeSlot) player.helds[slot] = -1; else player.helds.splice(slot, 1); safari.saveGame(player); }; this.heldItem = function(player, item, taking, slot) { var src = sys.id(player.id); if (!(heldCodes.hasOwnProperty(item+""))) { safaribot.sendHtmlMessage(src, "No item under " + item + " found!", safchan); return; } var getItem = heldCodes[item+""]; if (taking) { player.balls[getItem] += 1; safaribot.sendHtmlMessage(src, "Took back the " + itemAlias(getItem, false, true) + " from " + poke(player.party[slot], true) + " at position " + (slot+1) + "!", safchan); } else { if (player.balls[getItem] <= 0) { safaribot.sendHtmlMessage(src, "You don't have any " + itemAlias(getItem, false, true) + " to give!", safchan); return; } if (!slot || isNaN(slot)) slot = 0; else slot = parseInt(slot) - 1; // assumption that most players will logically input 1~6 instead of proper array index of 0~5 if (slot < 0 || slot > 5) { safaribot.sendMessage(src, "Please enter a valid party position (1 to 6)!", safchan); return; } if (slot >= player.party.length) { safaribot.sendMessage(src, "You don't have a " + getOrdinal(slot+1) + " Pokémon in your party!", safchan); return; } if (player.helds[slot] != -1) { this.heldItem(player, player.helds[slot], true, slot); } player.helds[slot] = item; player.balls[getItem] -= 1; safaribot.sendHtmlMessage(src, "Gave " + poke(player.party[slot], true) + " the " + itemAlias(getItem, false, true) + "!", safchan); } safari.saveGame(player); }; this.manageParty = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (data === "*") { sys.sendHtmlMessage(src, this.showParty(src, true), safchan); var n = now(); safaribot.sendMessage(src, "To modify your party, type /add [pokémon] or /remove [pokémon]. Use /active [pokémon] to set your party leader. Use /remove active to remove your active Pokémon. You can also manage saved parties with /party save:[slot], /party delete:[slot] or /party load:[slot], or quickly change your party with /qload Pokémon1,Pokémon2,Pokémon3,etc.", safchan); safaribot.sendHtmlMessage(src, "Your party and battles are currently {0} other players! Use /options view:[on|off] to change it.".format(player.options.visible ? "visible to" : "hidden from"), safchan); if (safari.validDailyBoost(player)) { safaribot.sendHtmlMessage(src, "Your lead Pokémon is the current Pokémon-of-the-Day!", safchan); } if (player.teraActive) { safaribot.sendHtmlMessage(src, "Your Tera Orb is currently active!", safchan); } if (player.brilliantAura) { safaribot.sendHtmlMessage(src, "Your Brilliant Aura grants you the following bonuses: " + safari.describeAuraEffects(player) + ". (Expires in " + timeLeftString(player.auraExpiry) + ")", safchan); } else if (player.burningAura) { safaribot.sendHtmlMessage(src, "Your Burning Aura grants you the following bonuses: " + safari.describeAuraEffects(player) + ". (Expires in " + timeLeftString(player.auraExpiry) + ")", safchan); } if (player.cooldowns.costume > now()) { safaribot.sendHtmlMessage(src, "Costume Cooldown: " + timeLeftString(player.cooldowns.costume) + ".", safchan); } if (player.helds[0] == "9") { safaribot.sendHtmlMessage(src, "Current Petaya Combo: " + addComma(player.berries.petayaCombo), safchan); } if (player.consecutiveCombo > 0) { safaribot.sendHtmlMessage(src, "Current Consecutive Catch Combo: " + addComma(player.consecutiveCombo), safchan); } for (var i = 0; i < player.party.length; i++) { var activeSkills = getActiveSkills(player.party[i], player); activeSkills = activeSkills.map(function(e) { return link("/quest idol:charge:" + player.party[i] + (typeof player.party[i] === "string" ? "*" : "") + ":" + skillData[e].name, skillData[e].name) + " [" + plural(player.pokeskills[player.party[i]][e].uses, "use") + "]"; }); if (activeSkills.length > 0) { safaribot.sendHtmlMessage(src, "{0}'s Active Skills: {1} {2}".format(poke(player.party[i], true), readable(activeSkills), (player.options.pokeskillsDisabled ? "(Idol skills are currently disabled)" : "")), safchan); } } if (player.fortune.deadline > n || player.fortune.limit > 0) { safaribot.sendHtmlMessage(src, "Current " + finishName("cookie") + "'s Effect: \"" + this.fortuneDescription(player.fortune) + "\"!", safchan); } if (player.scaleDeadline >= n) { safaribot.sendHtmlMessage(src, "Current " + finishName("scale") + "'s Color: " + cap(player.scaleColor) + " for the next " + timeLeftString(player.scaleDeadline) + "!", safchan); } if (player.mushroomDeadline > 0) { safaribot.sendHtmlMessage(src, "Current " + finishName("mushroom") + "'s Theme: " + link("/themerares " + player.mushroomTheme, themeName(player.mushroomTheme)) + " for the next " + player.mushroomDeadline + " Pokémon that you bait! (Effect can be cancelled with " + link("/shroomcancel", false, true) + ")", safchan); } if (player.zcrystalDeadline >= n && player.zcrystalUser) { var type = getCrystalEffect(player.zcrystalUser); safaribot.sendHtmlMessage(src, "Current " + finishName("crystal") + "'s Effect: You will " + zCrystalData[type].description.format(zCrystalData[type].chance * 100) + " for the next " + timeLeftString(player.zcrystalDeadline) + " (only if " + poke(player.zcrystalUser, true) + " is your active Pokémon)! " + poke(player.zcrystalUser, true) + " will deal " + (100 * (zCrystalData[type].npcBuff || defaultCrystalBuff)) + "% more damage in the next NPC Rotation Battle quest you enter.", safchan); } sys.sendMessage(src, "", safchan); return; } var input = data.split(":"), action, targetId; if (input.length < 2) { input = data.split(" "); if (input.length < 2) { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "To modify your party, type /party add:[pokémon] or /party remove:[pokémon]. Use /party active:[pokémon] to set your party leader. Use /remove active to remove your active Pokémon. To quickly change your party, use /qload Pokémon1,Pokémon2,Pokémon3,etc.", safchan); safaribot.sendMessage(src, "You can also manage saved parties with /party save:[slot] and /party load:[slot]. " + (player.savedParties.length > 0 ? "You have the following parties saved: " : ""), safchan); this.showSavedParties(src); sys.sendMessage(src, "", safchan); return; } action = input[0]; targetId = input.slice(1).join(" "); } else { action = input[0].toLowerCase(); targetId = input[1].toLowerCase(); } var info, id, slots = 10; if (!["save", "delete", "load"].contains(action)) { if (targetId === "active") { info = getInputPokemon(player.party[0] + (typeof player.party[0] === "string" ? "*" : "")); } else { targetId = targetId.replace(/\,[\s]/gi, ",").split(","); // if player inputs multiple e.g. /add Bulbasaur,Ivysaur, split it up if (targetId.length > 1) { for (var i in targetId) { // and call manageParty on each of them safari.manageParty(src, action + ":" + targetId[i]); } return; } else { info = getInputPokemon(targetId.join(",")); } } if (!info.num) { safaribot.sendMessage(src, "Invalid Pokémon!", safchan); return; } id = info.id; if (player.pokemon.indexOf(id) === -1) { safaribot.sendMessage(src, "You don't have that Pokémon!", safchan); return; } } else { targetId = parseInt(targetId, 10); targetId = isNaN(targetId) ? 0 : targetId; if (targetId === 0) { if (player.savedParties.length > 0) { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Your saved parties are:", safchan); this.showSavedParties(src); safaribot.sendMessage(src, "Use /party save:[slot] to save your current party to that slot, or /party load:[slot] to load that party.", safchan); sys.sendMessage(src, "", safchan); } else { safaribot.sendMessage(src, "You have no party saved! Use /party save:1 to save your current party to a slot.", safchan); } return; } } if (contestCount > 0 && !contestForfeited.contains(player.idnum)) { if (canHaveAbility(this.getEffectiveLead(player, true), abilitynum("Commander")) && player.party[1] == 977) { safaribot.sendMessage(src, "You can't modify your party while Commander is active!", safchan); return; } if (canHaveAbility(this.getEffectiveLead(player, true), abilitynum("Supreme Overlord"))) { safaribot.sendMessage(src, "You can't modify your party while Supreme Overlord is active!", safchan); return; } } if (action === "add") { if (player.tutorial.inTutorial && info.name !== "Pikachu") { if (cantBecause(src, reason, ["tutorial"])) { return; } } if (cantBecause(src, "modify your party", ["auction", "battle", "event", "pyramid"])) { safari.addPendingActive(player.id, "manageParty", "add:" + info.name, ["auction", "battle", "event", "pyramid", "wild"]); safaribot.sendMessage(src, "Your " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " will be added to your party at the next opportunity!", safchan); return; } if (player.party.length >= 6) { safaribot.sendMessage(src, "Please remove a Pokémon from your party before adding another one!", safchan); return; } if (player.party.indexOf(id) !== -1 && countRepeated(player.party, id) >= countRepeated(player.pokemon, id)) { safaribot.sendMessage(src, "You don't have more of this pokémon to add to your party!", safchan); return; } player.party.push(id); player.helds.push(-1); safaribot.sendMessage(src, "You added " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " to your party!", safchan); if (player.tutorial.inTutorial && player.party.contains(sys.pokeNum("Pikachu"))) { advanceTutorial(src, 4); } this.saveGame(player); } else if (action === "remove") { var restrictions = ["auction", "battle", "event", "tutorial", "pyramid", "baking"]; var reason = "modify your party"; var index; if (player.party.indexOf(id) === -1) { safaribot.sendMessage(src, "This Pokémon is not in your party!", safchan); return; } if (player.party.length == 1) { safaribot.sendMessage(src, "You must have at least 1 Pokémon in your party!", safchan); return; } //Allow selling of pokemon that are not the lead if the rest of the party doesn't matter at that point if (targetId === "active") { index = 0; } else { index = player.party.length - player.party.slice(0).reverse().indexOf(id) - 1; // take from the back of party instead of front, unless player specified "/remove active" } if (index === 0) { restrictions = restrictions.concat(["wild", "contest"]); reason = "remove your active Pokémon"; } if (cantBecause(src, reason, restrictions)) { safari.addPendingActive(player.id, "manageParty", "remove:" + info.name, restrictions); safaribot.sendMessage(src, "Your " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " will be removed from your party at the next opportunity!", safchan); return; } var before = id, isLead = index === 0; var getItem = player.helds[player.party.length - 1]; // entire second half of the party shifts forward, so take the item back from the last slot if there is one if (getItem > -1) { safari.takeItem(src, player.party.length, true); } else { player.helds.splice(player.party.length - 1, 1); } player.party.splice(index, 1); if (isLead && player.party[0] !== before && player.berries.petayaCombo > 0) { // if the Pokemon you removed was your lead, and the new Pokemon taking its place is a different species, reset petaya safaribot.sendMessage(src, "Your Petaya Combo was reset from {0} to 0 since your lead Pokémon was switched out!".format(player.berries.petayaCombo), safchan); player.berries.petayaCombo = 0; } safaribot.sendMessage(src, "You removed " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " from your party!", safchan); this.saveGame(player); } else if (action === "active") { var bypassed = false; if (player.party[0] === id) { safaribot.sendMessage(src, "This is already your active Pokémon!", safchan); return; } if (contestCount > 0 || currentPokemon) { if (currentTheme && contestThemes[currentTheme] && contestThemes[currentTheme].disguises) { if (player.party[1] == id || player.party[2] == id) { bypassed = true; } } if (contestCount > 0 && contestForfeited.contains(player.idnum)) { bypassed = true; } if (!bypassed) { safari.addPendingActive(player.id, "manageParty", "active:"+info.input, ["auction", "battle", "event", "tutorial", "pyramid", "wild", "contest", "baking"]); safaribot.sendMessage(src, "Your active Pokémon will automatically be changed to " + poke(getInputPokemon(info.input).num + (info.shiny ? "" : 0), true) + " at the next opportunity!", safchan); return; } } if (!bypassed) { if (cantBecause(src, "change your active Pokémon", ["wild", "contest", "auction", "battle", "event", "pyramid", "tutorial"])) { return; } } var j = 0; while (player.party.length > player.helds.length) { player.helds.push(-1); j++; if (j > 6) { break; } } if (player.party.indexOf(id) === -1) { if (player.party.length >= 6) { var removedId = player.party.splice(5, 1)[0]; safaribot.sendMessage(src, poke(removedId, true) + " was removed from your party!", safchan); } } else { var removeInd = player.party.indexOf(id) player.party.splice(removeInd, 1); var getItem = player.helds.splice(removeInd, 1); player.helds.unshift(getItem[0]); } player.party.splice(0, 0, id); safaribot.sendMessage(src, "You are now using " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " as your active Pokémon!", safchan); while (player.party.length > player.helds.length) { player.helds.push(-1); } if (player.berries.petayaCombo > 0) { safaribot.sendMessage(src, "Your Petaya Combo was reset from {0} to 0 since your lead Pokémon was switched out!".format(player.berries.petayaCombo), safchan); player.berries.petayaCombo = 0; } this.saveGame(player); } else if (action === "save") { if (cantBecause(src, "modify your party", ["tutorial"])) { return; } var num = targetId - 1; if (num > slots-1) { num = slots-1; } else if (num < 0) { num = 0; } var toSave = player.party.concat(); if (num >= player.savedParties.length) { player.savedParties.push(toSave); num = player.savedParties.length - 1; } else { player.savedParties[num] = toSave; } safaribot.sendMessage(src, "Saved your current party to slot " + (num + 1) + "!", safchan); this.saveGame(player); } else if (action === "delete") { if (cantBecause(src, "modify your party", ["tutorial"])) { return; } var num = targetId - 1; if (num > slots-1 || num < 0 || targetId > player.savedParties.length) { safaribot.sendMessage(src, targetId + " is not a valid slot!", safchan); return; } player.savedParties.splice(num, 1); safaribot.sendMessage(src, "Deleted saved party at slot " + targetId + "!", safchan); this.saveGame(player); } else if (action === "load") { var num = targetId - 1; if (num < 0) { num = 0; } if (player.savedParties.length > 0 && num >= player.savedParties.length) { num = player.savedParties.length - 1; } if (num >= player.savedParties.length) { safaribot.sendMessage(src, "You have no party saved on that slot!", safchan); return; } if (cantBecause(src, "modify your party", ["auction", "battle", "event", "pyramid", "tutorial"])) { safari.addPendingActive(player.id, "manageParty", "load:" + (num+1), ["auction", "battle", "event", "pyramid", "tutorial", "wild"]); safaribot.sendMessage(src, "Your " + getOrdinal(num+1) + " saved party will be loaded at the next opportunity!", safchan); return; } var toLoad = player.savedParties[num].slice(0); for (var p = toLoad.length - 1; p >= 0; p--) { id = toLoad[p]; if (player.pokemon.indexOf(id) === -1) { safaribot.sendMessage(src, "You no longer have " + an(poke(id, true)) + "!", safchan); toLoad.splice(p, 1); continue; } var c = countRepeated(toLoad, id); if (c > countRepeated(player.pokemon, id)) { safaribot.sendMessage(src, "You no longer have " + c + " " + poke(id, true) + "!", safchan); toLoad.splice(p, 1); } } if (toLoad.length === 0) { safaribot.sendMessage(src, "Couldn't load from slot " + targetId + " because you no longer have any of those Pokémon!", safchan); return; } if (contestCount > 0 || currentPokemon) { if (!(contestCount > 0 && contestForfeited.contains(player.idnum))) { safari.addPendingActive(player.id, "manageParty", "load:"+targetId, ["auction", "battle", "event", "tutorial", "pyramid", "wild", "contest", "baking"]); safaribot.sendMessage(src, "Your party saved in the slot " + targetId + " (" + readable(toLoad.map(poke), "and") + ") will be loaded at the next opportunity!", safchan); return; } } // synchronise held array and party array size var newSize = toLoad.concat().length; if (player.helds.length > newSize) { // if old party is larger than new party for (var i = player.helds.length; i > newSize; i--) { if (player.helds[i-1] != -1) safari.takeItem(src, i, true); // prevent item loss, make sure to put the excess items back into the bag } } player.helds = player.helds.slice(0, newSize); while (newSize > player.helds.length) { // if new party is larger than old party player.helds.push(-1); // fill in the missing -1s to match new party size } if (player.party[0] != toLoad.concat()[0] && player.berries.petayaCombo > 0) { // if your new active isn't the same safaribot.sendMessage(src, "Your Petaya Combo was reset from {0} to 0 since your lead Pokémon was switched out!".format(player.berries.petayaCombo), safchan); player.berries.petayaCombo = 0; // you should lose your petaya combo } player.party = toLoad.concat(); safaribot.sendMessage(src, "Loaded your party from slot " + (num + 1) + " (" + readable(player.party.map(poke), "and") + ")!", safchan); this.saveGame(player); } else { safaribot.sendMessage(src, "To modify your party, type /party add:[pokémon] or /party remove:[pokémon]. Use /party active:[pokémon] to set your party leader.", safchan); } }; this.quickLoadParty = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); data = data.replace(/\,[\s]/gi, ",").split(","); var firstInParty = player.party[0]; var toLoad = [], invalid = [], info, p, id, c; for (var p = 0; p < 6 && p < data.length; p++) { info = getInputPokemon(data[p]); if (info.num) { toLoad.push(info.id); } else { invalid.push(data[p]); } } if (invalid.length === data.length) { safaribot.sendMessage(src, "No valid Pokémon specified!", safchan); return; } else if (invalid.length > 0) { safaribot.sendMessage(src, "Cannot load invalid Pokémon " + readable(invalid) + "!", safchan); } for (p = toLoad.length - 1; p >= 0; p--) { id = toLoad[p]; if (player.pokemon.indexOf(id) === -1) { safaribot.sendMessage(src, "You don't have " + an(poke(id, true)) + "!", safchan); toLoad.splice(p, 1); continue; } c = countRepeated(toLoad, id); if (c > countRepeated(player.pokemon, id)) { safaribot.sendMessage(src, "You don't have " + c + " " + poke(id, true) + "!", safchan); toLoad.splice(p, 1); } } if (cantBecause(src, "modify your party", ["auction", "battle", "event", "pyramid", "tutorial"])) { // contest and wild has its own separate check below due to the need to check if player has forfeited contest safari.addPendingActive(player.id, "quickLoadParty", data.join(","), ["auction", "battle", "event", "pyramid", "tutorial", "wild"]); safaribot.sendMessage(src, "Your party will be changed to " + readable(toLoad.map(poke), "and") + " at the next opportunity!", safchan); return; } if (contestCount > 0 || currentPokemon) { if (!(contestCount > 0 && contestForfeited.contains(player.idnum))) { safari.addPendingActive(player.id, "quickLoadParty", data.join(","), ["auction", "battle", "event", "tutorial", "pyramid", "wild", "contest", "baking"]); safaribot.sendMessage(src, "Your party will be changed to " + readable(toLoad.map(poke), "and") + " at the next opportunity!", safchan); return; } } if (toLoad.length === 0) { safaribot.sendMessage(src, "Couldn't load party because you don't have any of those Pokémon!", safchan); return; } // synchronise held array and party array size var newSize = toLoad.concat().length; if (player.helds.length > newSize) { // if old party is larger than new party for (var i = player.helds.length; i > newSize; i--) { if (player.helds[i-1] != -1) { safari.takeItem(src, i, true); // prevent item loss, make sure to put the excess items back into the bag } } } player.helds = player.helds.slice(0, newSize); while (newSize > player.helds.length) { // if new party is larger than old party player.helds.push(-1); // fill in the missing -1s to match new party size } player.party = toLoad.concat(); if (player.party[0] !== firstInParty && player.berries.petayaCombo > 0) { safaribot.sendMessage(src, "Your Petaya Combo was reset from {0} to 0 since your lead Pokémon was switched out!".format(player.berries.petayaCombo), safchan); player.berries.petayaCombo = 0; } safaribot.sendMessage(src, "Quick loaded party " + readable(player.party.map(poke), "and") + "!", safchan); this.saveGame(player); }; this.showSavedParties = function(src) { var player = getAvatar(src); for (var e = 0; e < player.savedParties.length; e++) { safaribot.sendHtmlMessage(src, link("/party load: " + (e + 1), "Slot " + (e + 1)) + ": " + readable(player.savedParties[e].map(poke), "and"), safchan); } }; this.showParty = function(id, ownParty, srcId, textOnly) { var player = getAvatar(id), costumed = (player.costume !== "none"); var partyShown = [].concat(player.party); /*if (currentThemeEffect == "distortion" && !contestForfeited.contains(player.idnum)) { partyShown.reverse(); } else */if (currentThemeEffect == "past" && !contestForfeited.contains(player.idnum)) { if (player.altTimeline.lead !== 0) { partyShown[0] = player.altTimeline.lead; } } var party = []; for (var e in partyShown) { party.push(pokeInfo.sprite(partyShown[e])); } if (textOnly) { var ret = [""]; var partyText = []; for (var i = 0; i < partyShown.length; i++) { var pNum = partyShown[i]; partyText.push("#" + pokeInfo.readableNum(pNum) + " " + pokePlain(pNum, true) + (typeof pNum === "string" ? "*" : "") + (player.helds[i] && player.helds[i] > -1 ? " (" + finishName(heldCodes[player.helds[i]]) + ")" : "")); } ret.push("" + sys.name(id) + "'s Party: " + partyText.join(", ")); if (costumed) ret.push("Costume: " + costumeAlias(player.costume, false, true) + " (Level: " + safari.getCostumeLevel(player) + ")"); if (player.medals.length > 0) ret.push("Medals: " + readable(player.medals.slice(0, 6).map(function(m) { return m.desc }))); ret.push(""); return ret.join("
"); } var out = "
Inventory
$" + addComma(player.money) + "" + item + "
"; if (!ownParty) { id = srcId || id; } var os = sys.os(id); if (os === "android") { out += "
"; } var medals = player.medals; if (costumed || (medals && medals.length > 0)) { out += "
"; } for (var e in party) { if (e == 0 && currentPokemon && canHaveAbility(partyShown[e], abilitynum("Imposter")) && !canHaveAbility(currentDisplay, abilitynum("Imposter"))) { party[e] = pokeInfo.sprite(currentDisplay); } out += ""; } out += ""; if (costumed) { out += ""; } var showLinks = ownParty && sys.os(id) !== "android"; for (var e in partyShown) { var member = getPokemonInfo(partyShown[e]); var name = pokePlain(member[0]) + (member[1] ? "*" : ""); var displayName = pokePlain(member[0], true) + (member[1] ? "*" : ""); out += ""; } out += "
" + (ownParty ? "Current" : sys.name(id) + "'s" ) + " Party
"; if (costumed) { out += costumeSprite(player, os); } if (medals && medals !== null && medals.length > 0) { out += "

"; for (var i = 0; i < 3; i++) { if (i >= medals.length) { break; } out += safari.getMedalSprite(medals[i].icon, medals[i].desc); } if (medals.length > 3) { out += "

"; for (var i = 3; i < 6; i++) { if (i >= medals.length) { break; } out += safari.getMedalSprite(medals[i].icon, medals[i].desc); } out += "

" } } out += "
" + party[e] + "
" + costumeAlias(player.costume, false, true) + "
(Level: " + this.getCostumeLevel(player) + ")" + "
"; out += ""; if (player.helds.length > e && player.helds[e] != -1) { var item = heldCodes[player.helds[e]]; var see = ""; if (base64icons.hasOwnProperty(item)) { see = base64icons[item + ""]; } else { see = "item:" + itemData[item].icon; } out += ""; } out += ""; if (showLinks) { out += "" } out += "
#" + pokeInfo.readableNum(member[0]) + " " + displayName + "
"; out += "[" + link("/party active:" + name, "Active") + " / " + link("/party remove:" + (e == 0 ? "active" : name), "Remove") + "]"; out += "
"; if (isAndroid) { out += "
"; } return out; }; this.showBox = function(player, num, isAndroid, textOnly, shopLink) { var out = ""; var maxPages, list = player.pokemon, page = parseInt(num, 10); var perBox = itemData.box.bonusRate; if (isNaN(page) && typeof num === "string" && num.toLowerCase() !== "all") { page = 1; } if (!isNaN(page) && num != "all") { maxPages = Math.floor(list.length / (perBox)) + (list.length % perBox === 0 ? 0 : 1); if (page <= 0) { page = 1; } else if (page > maxPages) { page = maxPages; } list = list.slice(perBox * (page - 1), perBox * (page - 1) + perBox); } var label = "Box Capacity (" + player.pokemon.length + "/" + (player.balls.box * perBox) + ")"; if (textOnly) { out += this.listPokemonText(list, label, shopLink); } else { out += this.listPokemon(list, label, player.options.smallBox); if (isAndroid) { out += "
"; } } if (!isNaN(page)) { if (page > 1) { out += "[" + utilities.html_escape("< Box " + (page - 1)) + "]"; } if (page < maxPages) { if (page > 1) { out += " "; } out += "[" + utilities.html_escape("Box " + (page + 1) + " >") + "]"; } } return out; }; this.showCherished = function(player, isAndroid, textOnly) { var out = ""; var list = player.cherished; var label = "Cherished (" + player.cherished.length + "/120)"; if (textOnly) { out += this.listPokemonText(list, label); } else { out += this.listPokemon(list, label, player.options.smallBox); if (isAndroid) { out += "
"; } } return out; }; this.listPokemon = function(list, title, small) { var out = "", normal = [], count = 0, rowSize = small ? 6 : 12, e; for (e in list) { normal.push(pokeInfo.icon(list[e], true)); } out = ["
" + title + "
"]; for (e in normal) { if (count == 0) { out.push(""); } count++; out.push(""); if (count == rowSize) { out.push(""); count = 0; } } out.push("
" + normal[e] + "
"); return out.join(""); }; this.listPokemonText = function(list, title, shopLink) { var out = "", normal = [], e, p; for (e in list) { p = poke(list[e]); if (shopLink) { normal.push(link("/sell " + p, p)); } else { normal.push(p); } } out += "" + title + "
"; out += normal.join(", "); out += "
"; return out; }; this.listSpiritPokemon = function(list, title, small, enlist) { var out = "", normal = [], count = 0, count2 = 0, rowSize = small ? 6 : 12, e; for (e in list) { normal.push(pokeInfo.icon(list[e], true)); } out = ["
" + title + "
"]; for (e in normal) { if (count == 0) { out.push(""); } count++; count2++; out.push(""); if (count == rowSize) { out.push(""); count = 0; } } out.push("
" + normal[e] + "
"); return out.join(""); }; this.listSpiritPokemonText = function(list, title, enlist) { var out = "", normal = [], e, p, count = 0; for (e in list) { count++; p = poke(list[e]); normal.push(count <= enlist ? "" + p + "" : p); } out += "" + title + "
"; out += normal.join(", "); out += "
"; return out; }; this.showBag = function(player, isAndroid, textOnly, search) { //Manual arrays because easier to put in desired order. Max of 13 in each array or you need to change the colspan. Line1 only gets 11 due to money taking up 2 colspans var line1 = [/*money*/ "silver", "box", "shady", "battlepoint", "entry", "gacha", "pokeblock", "itemfinder", "rare", "dust"]; var line2 = ["safari", "great", "ultra", "master", "myth", "luxury", "quick", "level", "love", "spy", "clone", "premier", "mono"]; var line3 = ["lightning", "heavy", "photo", "mirror", "uturn", "inver", "spirit", "cherish", "bait", "golden", "deluxe", "rock"]; var line4 = ["whtapricorn", "blkapricorn", "redapricorn", "bluapricorn", "pnkapricorn", "grnapricorn", "ylwapricorn", "dew", "hdew", "ldew"]; var line5 = ["oran", "pecha", "razz", "bluk", "leppa", "tamato", "pinap", "nanab", "watmel", "petaya", "miracle", "platinum"]; var line6 = ["pack", "water", "soda", "cookie", "cherry", "gem", "mega", "crystal", "teraorb", "spray", "mail", "burn"] var line7 = ["celebrityTicket", "scale", "mushroom", "brush", "egg", "bright", "form"]; var line8 = ["amulet", "soothe", "scarf", "eviolite", "crown", "honey", "battery", "lens"]; var line9 = ["pearl", "stardust", "bigpearl", "starpiece", "nugget", "bignugget", "cometshard"]; var line10 = ["stick", "salt", "fossil", "coupon", "materia", "fragment", "sunshard", "moonshard", "terashard", "terajewel", "philosopher", "philosopherpebble", "ash"]; if (["wallet", "utility", "utilities", "balls", "ball", "apricorn", "apricorns", "usable", "usables", "consumable", "consumables", "perk", "perks", "pawn", "pawns", "pawnable", "pawnables", "rare", "rares", "rarities", "berries", "berry"].indexOf(search) === -1) { search = "*"; } var out = ""; if ((search === "*") || ["wallet", "utility", "utilities"].contains(search)) { out += bagRow(player, line1, isAndroid, textOnly, true, "Wallet"); } if ((search === "*") || ["ball", "balls"].contains(search)) { out += bagRow(player, line2, isAndroid, textOnly, false, "Poké Balls"); out += bagRow(player, line3, isAndroid, textOnly, false); } if ((search === "*") || ["apricorn", "apricorns"].contains(search)) { out += bagRow(player, line4, isAndroid, textOnly, false, "Apricorns & Dew"); } if ((search === "*") || ["berry", "berries"].contains(search)) { out += bagRow(player, line5, isAndroid, textOnly, false, "Berries"); } if ((search === "*") || ["usable", "usables", "consumable", "consumables"].contains(search)) { out += bagRow(player, line6, isAndroid, textOnly, false, "Consumables"); out += bagRow(player, line7, isAndroid, textOnly, false); } if ((search === "*") || ["perk", "perks"].contains(search)) { out += bagRow(player, line8, isAndroid, textOnly, false, "Perks"); } if ((search === "*") || ["pawn", "pawns", "pawnable", "pawnables"].contains(search)) { out += bagRow(player, line9, isAndroid, textOnly, false, "Pawnables"); } if ((search === "*") || ["rare", "rares", "rarities"].contains(search)) { out += bagRow(player, line10, isAndroid, textOnly, false, "Rarities"); } out += (textOnly ? "" : ""); return out; }; this.showCostumes = function(src) { var player = getAvatar(src); if (!player) { return; } var out = [], n, i; var costumes = player.costumes.slice(0).sort(function(a, b) { return costumeAlias(a, false, true) < costumeAlias(b, false, true) ? -1 : 1 }); for (i = 0; i < costumes.length; i++) { n = costumeAlias(costumes[i], false, true); out.push(link("/dressup " + n, n)); } return "Owned Costumes: " + (out.length > 0 ? out.join(", ") : "None"); }; this.changeCostume = function (src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); data = data.toLowerCase(); if (data === "*") { if (player.cooldowns.costume > now()) { safaribot.sendMessage(src, "You can change your costume in " + timeLeftString(player.cooldowns.costume) + ".", safchan); } else { safaribot.sendHtmlMessage(src, "You are able to change your costume right now! " + this.showCostumes(src), safchan); } return; } var cos = costumeAlias(data, true); var currentTime = now(); var costumeName = costumeAlias(data, false, true); if (cos !== "none") { if (player.costume === cos) { safaribot.sendMessage(src, "You are already wearing the " + costumeName + " costume!", safchan); return; } if (allCostumes.indexOf(cos) === -1){ safaribot.sendMessage(src, cos + " is not a valid costume!", safchan); return; } if (!player.costumes.contains(cos)) { safaribot.sendMessage(src, "You do not have the " + costumeName + " costume!", safchan); return; } if (player.cooldowns.costume > currentTime) { safaribot.sendMessage(src, "You changed your costume recently. Please try again in " + timeLeftString(player.cooldowns.costume) + "!", safchan); return; } } else if (player.costume === "none") { safaribot.sendMessage(src, "You are not currently wearing a costume!", safchan); return; } if (!player.tutorial.inTutorial && player.tutorial.step !== 4) { if (cantBecause(src, "change your costume", ["battle", "auction", "wild", "contest", "event", "tutorial", "baking"])) { safari.addPendingActive(player.id, "changeCostume", data, ["battle", "auction", "wild", "contest", "event", "tutorial", "baking"]); safaribot.sendMessage(src, "You will change into your " + costumeName + " costume at the next opportunity!", safchan); return; } } player.costume = cos; if (cos === "none") { safaribot.sendMessage(src, "You removed your costume! You can put on a new costume in " + timeLeftString(player.cooldowns.costume) + ".", safchan); } else { player.cooldowns.costume = currentTime + Math.floor(hours(3) * (1 - safari.getAuraEffect(player, "costumecd", 0))); player.notificationData.costumeWaiting = true; safaribot.sendMessage(src, "You changed into your " + costumeName + " costume! [Effect: " + costumeData[cos].effect + "]", safchan); if (player.tutorial.inTutorial && player.tutorial.step === 4 && costumeName === costumeData.preschooler.fullName) { advanceTutorial(src, 5); } } this.saveGame(player); }; this.getCostumes = function (src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var rec = player.records; var c = player.costumes; var received = [], notReceived = []; for (var e in costumeData) { if (costumeData.hasOwnProperty(e)) { e = costumeData[e]; var giveCostume = false; if (!c.contains(e.name)) { if (e.acqReq > 0 && e.record in rec && !e.specialAcq) { if (rec[e.record] >= e.acqReq) { if (e.acqReq2 > 0 && e.record2 in rec) { if (rec[e.record2] >= e.acqReq2) { giveCostume = true; } } else { giveCostume = true; } } if (giveCostume) { c.push(e.name); received.push(e.fullName); } else { notReceived.push(((e.noAcq || "It is not currently known how") + " to obtain the " + e.fullName + " costume!").format(addComma(Math.max(e.acqReq - rec[e.record], 0)), addComma(Math.max(e.acqReq2 - rec[e.record2], 0)))); } } else if (e.specialAcq) { notReceived.push(e.noAcq + " to obtain the " + e.fullName + " costume!"); } } } } if (received.length + notReceived.length === 0) { safaribot.sendMessage(src, "You currently own all of the available costumes.", safchan); } else { for (var x in notReceived) { safaribot.sendHtmlMessage(src, notReceived[x], safchan); } if (received.length > 0) { safaribot.sendHtmlMessage(src, "Received the following " + plural(received.length, "costume") + ": " + readable(received, "and") + ".", safchan); } } if (player.tutorial.step === 4) { tutorMsg(src, "Great! Now you can use " + link("/dressup Preschooler") + " to change costumes."); } this.saveGame(player); }; this.removePokemon = function(src, pokeNum) { var player = getAvatar(src); if (player.pokemon.contains(pokeNum)) { player.pokemon.splice(player.pokemon.lastIndexOf(pokeNum), 1); } if (countRepeated(player.party, pokeNum) > countRepeated(player.pokemon, pokeNum)) { do { if (player.helds[player.party.lastIndexOf(pokeNum)] > -1) safari.takeItem(src, player.party.lastIndexOf(pokeNum) + 1, true); player.party.splice(player.party.lastIndexOf(pokeNum), 1); } while (countRepeated(player.party, pokeNum) > countRepeated(player.pokemon, pokeNum)); } var input = getInput(poke(pokeNum)); var total = countRepeated(player.party, pokeNum) + countRepeated(player.pokemon, pokeNum); if (input.input in player.shop && player.shop[input.input].limit > total) { player.shop[input.input].limit = total; } }; this.removePokemon2 = function(player, pokeNum) { if (player.pokemon.contains(pokeNum)) { player.pokemon.splice(player.pokemon.lastIndexOf(pokeNum), 1); } if (countRepeated(player.party, pokeNum) > countRepeated(player.pokemon, pokeNum)) { do { if (player.helds[player.party.lastIndexOf(pokeNum)] > -1) safari.takeItem(player, player.party.lastIndexOf(pokeNum) + 1, true); player.party.splice(player.party.lastIndexOf(pokeNum), 1); } while (countRepeated(player.party, pokeNum) > countRepeated(player.pokemon, pokeNum)); } var input = getInput(poke(pokeNum)); var total = countRepeated(player.party, pokeNum) + countRepeated(player.pokemon, pokeNum); if (input.input in player.shop && player.shop[input.input].limit > total) { player.shop[input.input].limit = total; } }; this.findPokemon = function(src, commandData, textOnly, shopLink, showDupes) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (commandData === "*") { sys.sendMessage(src, "", safchan); sys.sendMessage(src, "How to use /find:", safchan); safaribot.sendMessage(src, "Define a parameter (Name, Number, BST, Type, Color, Move, Ability, Shiny, NotEvolved, CanEvolve, HasEvolved, FinalForm, CanMega, IsMyth, IsRare, Duplicate, Duplicate+, Region, Theme or Tier) and a value to find Pokémon in your box. Examples: ", safchan); safaribot.sendMessage(src, "For Name: Type any part of the Pokémon's name. e.g.: /find name LUG (both Lugia and Slugma will be displayed, among others with LUG on the name). To search for EXACT name, put a \"=\" in front of the name, e.g. /find name =Lugia", safchan); safaribot.sendMessage(src, "For Type: Type any one or two types. If you type 2, only pokémon with both types will appear. e.g.: /find type water grass", safchan); safaribot.sendMessage(src, "For Move: Type any move. e.g.: /find move tackle (will show all Pokémon that can learn Tackle)", safchan); safaribot.sendMessage(src, "For Ability: Type any ability. e.g.: /find ability overgrow (will show all Pokémon that can have Overgrow)", safchan); safaribot.sendMessage(src, "For Duplicate: Type a number. e.g.: /find duplicate 3 (will display all Pokémon that you have exactly 3 copies of)", safchan); safaribot.sendMessage(src, "For Duplicate+: Type a number greater than 1. e.g.: /find duplicate+ 3 (will display all Pokémon that you have at least 3 copies of)", safchan); safaribot.sendMessage(src, "For Region: Select any valid region (" + readable(generations.slice(1, generations.length), "or") + ") to display all currently owned Pokémon from that region", safchan); safaribot.sendMessage(src, "For Theme: Select any valid theme (" + readable(getAllThemeNames(), "or") + ") to display all currently owned Pokémon that spawn in that theme.", safchan); safaribot.sendMessage(src, "For Tier: Select any valid tier (" + readable(tiers, "or") + ") to display all currently owned Pokémon in that tier", safchan); safaribot.sendMessage(src, "For Color: Select any valid color (" + readable(Object.keys(pokeColors), "or") + ") to display all currently owned Pokémon with that color", safchan); safaribot.sendMessage(src, "For Start: Type one or more letters to find Pokémon with name starting with that letter sequence.", safchan); safaribot.sendMessage(src, "For Number and BST: There are 4 ways to search with those parameters:", safchan); safaribot.sendMessage(src, "-Exact value. e.g.: /find bst 500 (displays all Pokémon with BST of exactly 500)", safchan); safaribot.sendMessage(src, "-Greater than. e.g.: /find bst 400 > (displays all Pokémon with BST of 400 or more)", safchan); safaribot.sendMessage(src, "-Less than. e.g.: /find bst 350 < (displays all Pokémon with BST of 350 or less)", safchan); safaribot.sendMessage(src, "-Range. e.g.: /find number 1 150 (displays all Pokémon with pokédex number between 1 and 150)", safchan); safaribot.sendMessage(src, "For Shiny, NotEvolved, CanEvolve, HasEvolved, FinalForm, IsMyth, IsRare and CanMega: No additional parameter required.", safchan); safaribot.sendMessage(src, "To look for more than one parameter, use && (e.g.: '/find region johto && duplicate 3' to look for Pokémon from Johto that you have 3 copies of)", safchan); safaribot.sendMessage(src, "To look for any of more than one parameter, use or (e.g.: '/find type steel or color green' to look for Pokémon that's Steel type or green.)", safchan); sys.sendMessage(src, "", safchan); return; } if (currentEvent && ["Quiz", "Hidden Quiz"].contains(currentEvent.eventName) && currentEvent.isInEvent(player.id)) { safaribot.sendMessage(src, "You cannot use this command during a Quiz event!", safchan); return; } var sets = commandData.split(" or "); var multi; var str, info, crit, val, m, def, title = [], finalTitle = [], list, current = player.pokemon.concat(), finalList = []; var spacedVal = ["move","learn","canlearn", "tier", "ability", "hasability", "theme"]; for (var i = 0; i < sets.length; i++) { multi = sets[i].split("&&"); for (m = 0; m < multi.length; m++) { list = []; str = multi[m].trim(); info = str.split(":"); crit = "abc", val = "1"; if (info.length < 2) { info = str.split(" "); } crit = info[0].toLowerCase(); val = info.length > 1 ? (spacedVal.contains(crit) ? info.slice(1).join(" ") : info[1]).toLowerCase() : "asc"; def = applyFilterCriteria(src, info, crit, val, list, current, str, player.pokemon); if (!def) { return; } title.push(def); current = list.concat(); } finalList = finalList.concat(current); finalTitle.push(title); title = []; current = player.pokemon.concat(); } finalTitleMsg = ""; for (var i = 0; i < finalTitle.length; i++) { finalTitleMsg += readable(finalTitle[i]); if (i !== finalTitle.length - 1) { finalTitleMsg += " or "; } } if (!(showDupes)) { finalList = removeDuplicates(finalList, true); } if (textOnly) { sys.sendHtmlMessage(src, this.listPokemonText(finalList, "Pokémon " + finalTitleMsg + " (" + finalList.length + ")", shopLink), safchan); } else { sys.sendHtmlMessage(src, this.listPokemon(finalList, "Pokémon " + finalTitleMsg + " (" + finalList.length + ")", player.options.smallBox), safchan); } if (shopLink) { safaribot.sendHtmlMessage(src, "Sell all of these Pokémon at once? (This command will not sell Shinies, Legendaries, or Rare Formes", safchan); var toTurbo = [], p; for (var i = 0; i < finalList.length; i++) { p = finalList[i]; if (typeof p == "string") { continue; } if (isRare(p)) { continue; } toTurbo.push(p+""); } safaribot.sendHtmlMessage(src, link("/multisell " + toTurbo.join(",") + ":confirm", "«Sell All»"), safchan); } }; function applyFilterCriteria(src, info, crit, val, list, current, commandData, box) { var noparam = ["shiny", "canevolve", "finalform", "canmega", "hasevolved", "notevolved", "legendary", "islegendary", "islegend", "mythical", "ismythical", "ismyth", "israre"], def; if (info.length >= 2) { switch (crit) { case "theme": case "themes": crit = "theme"; break; case "number": case "num": case "index": case "no": case "dex": crit = "number"; break; case "bst": case "status": case "stats": case "stat": crit = "bst"; break; case "type": crit = "type"; break; case "shiny": crit = "shiny"; break; case "duplicate": case "duplicates": case "repeated": case "count": crit = "duplicate"; break; case "duplicate+": case "duplicates+": case "repeated+": case "count+": crit = "duplicate+"; break; case "canevolve": crit = "canevolve"; break; case "canmega": crit = "canmega"; break; case "evolved": case "hasevolved": case "isevolved": crit = "hasevolved"; break; case "unevolved": case "notevolved": crit = "notevolved"; break; case "finalform": crit = "finalform"; break; case "region": crit = "region"; break; case "color": case "colour": crit = "color"; break; case "move": case "learn": case "canlearn": crit = "move"; break; case "ability": case "hasability": crit = "ability"; break; case "start": case "starting": case "initial": crit = "start"; break; case "tier": crit = "tier"; break; default: crit = "abc"; } } else if (!noparam.contains(crit)) { crit = "abc"; val = info[0].toLowerCase(); } var mode = "equal"; if (crit == "abc") { val = commandData ? commandData.toLowerCase() : val.toLowerCase(); if (val === TYPE_NULL_NAME.toLowerCase()) { val = sys.pokemon(772).toLowerCase(); } val = val.replace(/é/g, "e"); var exact = false; if (val[0] === "=") { exact = true; val = val.slice(1); } current.forEach(function(x){ var compName = pokePlain(x).toLowerCase().replace(/é/g, "e"); if (exact ? compName === val : compName.indexOf(val) !== -1) { list.push(x); } }); return exact ? "with exact name = " + val : "with " + val + " in their name"; } if (crit == "start") { val = val.toLowerCase(); current.forEach(function(x){ if (pokePlain(x).toLowerCase().indexOf(val) === 0) { list.push(x); } }); return "with name starting with " + cap(val) + ""; } else if (crit == "number") { def = rangeFilter(src, current, list, val, mode, "Pokédex Number", info, crit); if (!def) { return false; } return def; } else if (crit == "bst") { def = rangeFilter(src, current, list, val, mode, "BST", info, crit); if (!def) { return false; } return def; } else if (crit == "type") { var type_1 = cap(val.toLowerCase()), type_2 = null; if (info.length > 2) { type_2 = cap(info[2].toLowerCase()); } if (!(type_1 in effectiveness)) { safaribot.sendMessage(src, type_1 + " is not a valid type!", safchan); return false; } if (type_2 && !(type_2 in effectiveness)) { safaribot.sendMessage(src, type_2 + " is not a valid type!", safchan); return false; } current.forEach(function(x){ if (hasType(x, type_1) && (!type_2 || hasType(x, type_2))) { list.push(x); } }); return "with " + type_1 + (type_2 ? "/" + type_2 : "") + " type"; } else if (crit == "ability") { var old = val; val = abilitynum(val); if (!val) { safaribot.sendMessage(src, old + " is not a valid ability!", safchan); return false; } current.forEach(function(x) { if (canHaveAbility(x, val)) { list.push(x); } }); return "that have the ability " + abilityOff(val); } else if (crit == "move") { var old = val; val = movenum(val); if (!val) { safaribot.sendMessage(src, old + " is not a valid move!", safchan); return false; } var m = val + ""; current.forEach(function(x){ if (canLearnMove(x, m)) { list.push(x); } }); return "that can learn " + moveOff(val); } else if (crit == "shiny") { current.forEach(function(x){ if (typeof x === "string") { list.push(x); } }); return "that are Shiny"; } else if (["legendary", "islegendary", "islegend", "mythical", "ismythical", "ismyth"].contains(crit)) { current.forEach(function(x) { if (isLegendary(x)) { list.push(x); } }); return "that are Legendary"; } else if (crit == "israre") { current.forEach(function(x) { if (isRare(x)) { list.push(x); } }); return "that are rare"; } else if (crit == "duplicate") { var pokeList = current.concat().sort(); val = parseInt(val, 10); if (isNaN(val) || val < 1) { safaribot.sendMessage(src, "Please specify a valid number higher than 0!", safchan); return false; } var normalCount = {}, shinyCount = {}, p, obj, e; for (var i = 0, l = pokeList.length; i < l; i++) { p = pokeList[i]; obj = typeof p === "string" ? shinyCount : normalCount; if (!obj.hasOwnProperty(p)) { obj[p] = 0; } obj[p]++; } for (i in normalCount) { if (normalCount[i] === val) { p = parseInt(i, 10); for (e = 0, l = normalCount[i]; e < l; e++) { list.push(p); } } } for (i in shinyCount) { if (shinyCount[i] === val) { for (e = 0, l = shinyCount[i]; e < l; e++) { list.push(i); } } } return "with exactly " + val + " duplicates"; } else if (crit == "duplicate+") { var pokeList = current.concat().sort(); val = parseInt(val, 10); if (isNaN(val) || val < 2) { safaribot.sendMessage(src, "Please specify a valid number higher than 1!", safchan); return false; } var normalCount = {}, shinyCount = {}, p, obj, e; for (var i = 0, l = pokeList.length; i < l; i++) { p = pokeList[i]; obj = typeof p === "string" ? shinyCount : normalCount; if (!obj.hasOwnProperty(p)) { obj[p] = 0; } obj[p]++; } for (i in normalCount) { if (normalCount[i] >= val) { p = parseInt(i, 10); for (e = 0, l = normalCount[i]; e < l; e++) { list.push(p); } } } for (i in shinyCount) { if (shinyCount[i] >= val) { for (e = 0, l = shinyCount[i]; e < l; e++) { list.push(i); } } } return "with at least " + val + " duplicates"; } else if (crit == "canevolve") { current.forEach(function(x){ if (pokeInfo.species(x) in evolutions) { list.push(x); } }); return "that can evolve"; } else if (crit == "finalform") { current.forEach(function(x){ if (!(pokeInfo.species(x) in evolutions) && !isMega(x)) { list.push(x); } }); return "fully evolved"; } else if (crit == "hasevolved") { current.forEach(function(x){ if (pokeInfo.species(x) in devolutions) { list.push(x); } }); return "has evolved"; } else if (crit == "notevolved") { current.forEach(function(x) { if (!(pokeInfo.species(x) in devolutions)) { list.push(x); } }); return "not evolved"; } else if (crit == "canmega") { current.forEach(function(x){ if (!isMega(x) && x in megaEvolutions) { list.push(x); } }); return "that can Mega Evolve"; } else if (crit == "region") { val = cap(val.toLowerCase()); var pos = generations.indexOf(val); if (pos < 1) { //"None" is 0 index, we don't want to include that. Both here and in the readable safaribot.sendMessage(src, val + " is not a valid region! Valid regions are " + readable(generations.slice(1, generations.length)) + ".", safchan); return false; } current.forEach(function(x){ if (generation(x) === pos) { list.push(x); } }); return "from the " + val + " Region"; } else if (crit == "color") { val = val.toLowerCase(); if (val === "grey") { val = "gray"; } if (!pokeColors.hasOwnProperty(val)) { safaribot.sendMessage(src, val + " is not a valid color! Valid colors are " + readable(Object.keys(pokeColors)) + ".", safchan); return false; } current.forEach(function(x){ if (getPokeColor(x) === val) { list.push(x); } }); return cap(val) + "-colored"; } else if (crit == "tier") { val = val.toLowerCase(); var tierLowerCase = tiers.map(function(e) { return e.toLowerCase() }); if (!tierLowerCase.contains(val)) { safaribot.sendMessage(src, val + " is not a valid tier! Valid tiers are " + readable(tiers) + ".", safchan); return false; } current.forEach(function(x) { if (safari.getTier(x).toLowerCase() === val) { list.push(x); } }); return "in the " + tiers[tierLowerCase.indexOf(val)] + " tier"; } else if (crit == "theme") { val = val.toLowerCase(); var themeKey = safari.getThemeKeyByName(val); if (!themeKey || themeKey === "none") { var valid = getAllThemeNames(); safaribot.sendMessage(src, val + " is not a valid theme! Valid theme inputs are: " + readable(valid) + ".", safchan); return false; } var themeSpawns = safari.getAllSpawnsInTheme(themeKey, true); var values = Object.keys(themeSpawns).map(function(e) { return themeSpawns[e] }); // polyfill Object.values var combined = values.reduce(function(a, b) { return a.concat(b) }); current.forEach(function(x) { if (combined.contains(x)) { list.push(x); } }); return "that appear in the " + themeName(safari.getThemeKeyByName(val)) + " theme (and alters/variations)"; } } function rangeFilter(src, current, list, val, mode, paramName, info, type) { val = parseInt(val, 10); var val2; if (isNaN(val)) { safaribot.sendMessage(src, "Please specify a valid number!", safchan); return false; } var title = "with " + paramName + " equal to " + val; if (info.length > 2) { val2 = parseInt(info[2], 10); if (isNaN(val2)) { switch (info[2].toLowerCase()) { case ">": case "higher": case "greater": case "more": case "+": case "above": mode = "greater"; title = "with " + paramName + " greater than or equal to " + val; break; case "<": case "lower": case "less": case "-": case "below": mode = "less"; title = "with " + paramName + " less than or equal to " + val; break; } if (mode !== "greater" && mode !== "less") { safaribot.sendMessage(src, "Invalid parameter! Use either >, < or another number.", safchan); return false; } } else { mode = "range"; title = "with " + paramName + " between " + val + " and " + val2; } } var param; current.forEach(function(x){ switch (type) { case "bst": param = getBST(x); break; case "number": param = parseInt(pokeInfo.species(x), 10); break; } if (mode == "equal" && param === val) { list.push(x); } else if (mode == "greater" && param >= val) { list.push(x); } else if (mode == "less" && param <= val) { list.push(x); } else if (mode == "range" && param >= val && param <= val2) { list.push(x); } }); return title; } this.sortBox = function(src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var info = commandData.split(":"); var crit = "number", order = "asc"; if (info.length < 2) { info = commandData.split(" "); } crit = info[0]; order = info.length > 1 ? info[1].toLowerCase() : "asc"; switch (crit.toLowerCase()) { case "abc": case "alpha": case "alphabet": case "alphabetical": case "name": crit = "abc"; break; case "bst": case "status": case "stats": case "stat": crit = "bst"; break; case "type": crit = "type"; break; case "shiny": crit = "shiny"; break; case "duplicate": case "duplicates": case "repeated": crit = "duplicate"; break; default: crit = "number"; } switch (order.toLowerCase()) { case "descending": case "desc": case "cba": case "321": order = "desc"; break; case "ascending": case "asc": case "abc": case "123": order = "asc"; } if (crit === "number") { player.pokemon.sort(function(a, b){ if (pokeInfo.species(a) != pokeInfo.species(b)) { return pokeInfo.species(a)-pokeInfo.species(b); } else { return pokeInfo.forme(a)-pokeInfo.forme(b); } }); if (order === "desc") { player.pokemon.reverse(); } safaribot.sendMessage(src, "Your box was sorted by Pokédex Number (" + (order === "desc" ? "descending" : "ascending") + ").", safchan); } else if (crit === "abc") { player.pokemon.sort(function(a, b){ if (pokePlain(a) < pokePlain(b)) { return -1;} if (pokePlain(a) > pokePlain(b)) { return 1;} return 0; } ); if (order === "desc") { player.pokemon.reverse(); } safaribot.sendMessage(src, "Your box was sorted " + (order === "desc" ? "inversely " : "") + "alphabetically.", safchan); } else if (crit === "bst") { player.pokemon.sort(function(a, b){return getBST(a)-getBST(b);}); if (order === "desc") { player.pokemon.reverse(); } safaribot.sendMessage(src, "Your box was sorted by BST (" + (order === "desc" ? "descending" : "ascending") + ").", safchan); } else if (crit === "shiny") { player.pokemon.sort(function(a, b){ if (typeof a === "string" && typeof b !== "string") { return -1; } else if (typeof a !== "string" && typeof b === "string") { return 1; } else { return 0; } }); if (order === "desc") { player.pokemon.reverse(); } safaribot.sendMessage(src, "Your box was sorted by Shiny Pokémon (" + (order === "desc" ? "descending" : "ascending") + ").", safchan); } else if (crit === "type") { var type_1 = cap(order.toLowerCase()); if (!(type_1 in effectiveness)) { safaribot.sendMessage(src, "Please specify a valid type!", safchan); return; } var type_2 = info.length > 2 ? cap(info[2].toLowerCase()) : null; if (type_2 && !(type_2 in effectiveness)) { safaribot.sendMessage(src, "Please specify a valid secondary type!", safchan); return; } player.pokemon.sort(function(a, b) { if (hasType(a, type_1) && !hasType(b, type_1)) { return -1; } if (!hasType(a, type_1) && hasType(b, type_1)) { return 1; } if (type2) { if (hasType(a, type_2) && !hasType(b, type_2)) { return -1; } if (!hasType(a, type_2) && hasType(b, type_2)) { return 1; } } return 0; }); safaribot.sendMessage(src, "Your box was sorted by types (" +(type_2 ? type_1 + "/" + type_2 + " > " : "")+ type_1 + (type_2 ? " > " + type_2 : "" ) + ").", safchan); } else if (crit === "duplicate") { player.pokemon.sort(); player.pokemon.sort(function(a, b){return countArray(player.pokemon, a)-countArray(player.pokemon, b);}); if (order === "desc") { player.pokemon.reverse(); } safaribot.sendMessage(src, "Your box was sorted by duplicates (" + (order === "desc" ? "descending" : "ascending") + ").", safchan); } this.saveGame(player); }; this.tallyAllPlayersMoney = function() { if (economyData.total[0] === undefined) return; economyData.total[0] = 0; for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { data = JSON.parse(rawPlayers.hash[e]); if (!data.excludeFromEconomy) economyData.total[0] += data.money; } } permObj.add("economicSummary", JSON.stringify(economyData)); }; this.flipEconomyDay = function() { var threshold = 14; // 2 weeks this.tallyAllPlayersMoney(); for (var key in economyData) { if (economyData[key].length === threshold) { economyData[key].unshift(0); economyData[key] = economyData[key].slice(0, threshold); } else if (economyData[key].length > threshold) { economyData[key] = economyData[key].slice(economyData[key].length - threshold, economyData[key].length); } else { economyData[key].unshift(0); } } permObj.add("economicSummary", JSON.stringify(economyData)); }; this.updateEconomyData = function(amount, key) { if (!economyData.hasOwnProperty(key)) { return; } economyData[key][0] += amount; permObj.add("economicSummary", JSON.stringify(economyData)); }; this.excludeFromEconomy = function(src, name) { var player = getAvatarOff(name); if (!player) { safaribot.sendMessage(src, "Unable to find player \"" + name + "\".", safchan); return; } player.excludeFromEconomy = !player.excludeFromEconomy; safari.saveGame(player); safaribot.sendMessage(src, name.toCorrectCase() + " will now be " + (player.excludeFromEconomy ? "excluded from " : "included in ") + " economy statistics.", safchan); }; this.showEconomyData = function(src, commandData) { var dayIndex; switch (commandData) { case "today": dayIndex = 0; break; case "yesterday": dayIndex = 1; break; default: dayIndex = parseInt(commandData) || 0; break; } if (dayIndex < 0) dayIndex = 0; else if (dayIndex > 13) dayIndex = 13; this.tallyAllPlayersMoney(); sys.sendMessage(src, "Showing Economy Data for {0}:".format(dayIndex === 0 ? "Today" : plural(dayIndex, "Day") + " Ago"), safchan); var npcShop = economyData.npcShop[dayIndex], playerShop = economyData.playerShop[dayIndex], playerTrade = economyData.playerTrade[dayIndex], playerAuction = economyData.playerAuction[dayIndex], playerSell = economyData.playerSell[dayIndex], playerPawn = economyData.playerPawn[dayIndex], collector = economyData.collector[dayIndex], questFee = economyData.questFee[dayIndex], total = economyData.total[dayIndex]; var npcShopPrevious = economyData.npcShop[dayIndex+1] || 0, playerShopPrevious = economyData.playerShop[dayIndex+1] || 0, playerTradePrevious = economyData.playerTrade[dayIndex+1] || 0, playerAuctionPrevious = economyData.playerAuction[dayIndex+1] || 0, playerSellPrevious = economyData.playerSell[dayIndex+1] || 0, playerPawnPrevious = economyData.playerPawn[dayIndex+1] || 0, collectorPrevious = economyData.collector[dayIndex+1] || 0, questFeePrevious = economyData.questFee[dayIndex+1] || 0, totalPrevious = economyData.total[dayIndex+1] || 0; var introduced = playerSell + playerPawn + collector, introducedPrevious = playerSellPrevious + playerPawnPrevious + collectorPrevious, lost = npcShop + questFee, // these are both negative and so should still be added together lostPrevious = npcShopPrevious + questFeePrevious, exchanged = playerShop + playerTrade + playerAuction, exchangedPrevious = playerShopPrevious + playerTradePrevious + playerAuctionPrevious; // unaccounted = misc stuff like luxury balls, bet races, money lost from exceeding cap, etc var unaccounted = (total - totalPrevious) - (introduced + lost), // true difference - accounted difference unaccountedPrevious = (totalPrevious - (economyData.total[dayIndex+2] || 0)) - (introducedPrevious + lostPrevious); var moneyColor = function(amount, showPlus) { return "" + toColor((showPlus && amount >= 0 ? "+" : (amount < 0 ? "-" : "")) + "$" + addComma(Math.abs(amount)), amount < 0 ? "red" : "green") + ""; }; if (dayIndex === 13) sys.sendHtmlMessage(src, toColor("Note: ", "red") + "Economy data for days beyond this point have been deleted, so the values for the previous day are considered 0.", safchan); sys.sendHtmlMessage(src, toColor("Money Earned from Selling Pokémon to the NPC:", "black") + " {0} ({1} from previous day)".format(moneyColor(playerSell), moneyColor(playerSell - playerSellPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Money Earned from Pawning/Selling Items to the NPC:", "black") + " {0} ({1} from previous day)".format(moneyColor(playerPawn), moneyColor(playerPawn - playerPawnPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Money Earned from the Collector:", "black") + " {0} ({1} from previous day)".format(moneyColor(collector), moneyColor(collector - collectorPrevious, true)), safchan); sys.sendHtmlMessage(src, "", safchan); sys.sendHtmlMessage(src, toColor("Money Lost by Buying from the NPC Shop:", "black") + " {0} ({1} from previous day)".format(moneyColor(npcShop), moneyColor(npcShop - npcShopPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Money Lost from Quest Fees:", "black") + " {0} ({1} from previous day)".format(moneyColor(questFee), moneyColor(questFee - questFeePrevious, true)), safchan); sys.sendHtmlMessage(src, "", safchan); sys.sendHtmlMessage(src, toColor("Money Exchanged Between Players via Shop Sales:", "black") + " {0} ({1} from previous day)".format(moneyColor(playerShop), moneyColor(playerShop - playerShopPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Money Exchanged Between Players via Trades: ", "black") + " {0} ({1} from previous day)".format(moneyColor(playerTrade), moneyColor(playerTrade - playerTradePrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Money Exchanged Between Players via Auctions: ", "black") + " {0} ({1} from previous day)".format(moneyColor(playerAuction), moneyColor(playerAuction - playerAuctionPrevious, true)), safchan); sys.sendHtmlMessage(src, "", safchan); sys.sendHtmlMessage(src, toColor("Total Amount Introduced into the Economy:", "black") + " {0} ({1} from previous day)".format(moneyColor(introduced), moneyColor(introduced - introducedPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Total Amount Removed from the Economy:", "black") + " {0} ({1} from previous day)".format(moneyColor(lost), moneyColor(lost - lostPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Total Amount Exchanged Between Players:", "black") + " {0} ({1} from previous day)".format(moneyColor(exchanged), moneyColor(exchanged - exchangedPrevious, true)), safchan); sys.sendHtmlMessage(src, toColor("Total Amount from Unaccounted Sources:", "black") + " {0} ({1} from previous day)".format(moneyColor(unaccounted), moneyColor(unaccounted - unaccountedPrevious, true)), safchan); sys.sendHtmlMessage(src, "", safchan); sys.sendHtmlMessage(src, toColor("Total Amount of Money Between All Safari Players:", "black") + " {0} ({1} from previous day)".format(moneyColor(total), moneyColor(total - totalPrevious, true)), safchan); sys.sendHtmlMessage(src, "", safchan); // earlier index is later date safaribot.sendHtmlMessage(src, (dayIndex < 13 ? link("/stonks " + (dayIndex+1), "«Previous Day» ") : "") + (dayIndex > 0 ? link("/stonks " + (dayIndex-1), "«Next Day»") : ""), safchan); }; this.showRecords = function (src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var rec = player.records; sys.sendMessage(src, "", safchan); if (commandData === "earnings") { sys.sendMessage(src, "*** Earnings Breakdown ***", safchan); safaribot.sendMessage(src, "Sold Pokémon: $" + addComma(rec.pokeSoldEarnings), safchan); safaribot.sendMessage(src, es(finishName("luxury")) + ": $" + addComma(rec.luxuryEarnings), safchan); safaribot.sendMessage(src, "Pawned Items: $" + addComma(rec.pawnEarnings), safchan); safaribot.sendMessage(src, "Collector Reward: $" + addComma(rec.collectorEarnings), safchan); safaribot.sendMessage(src, "Tower Reward: $" + addComma(rec.towerEarnings), safchan); safaribot.sendMessage(src, "Pokémon Race: $" + addComma(rec.pokeRaceEarnings), safchan); safaribot.sendMessage(src, "Hitting Wallets: $" + addComma(rec.rocksWalletEarned), safchan); safaribot.sendMessage(src, "Window Restitution: $" + addComma(rec.rocksWindowEarned), safchan); safaribot.sendMessage(src, "Pyramid Loot: $" + addComma(rec.pyramidMoney), safchan); safaribot.sendMessage(src, "Dropped from Wallet: -$" + addComma(rec.rocksWalletLost), safchan); safaribot.sendMessage(src, "Repairing Windows: -$" + addComma(rec.rocksWindowLost), safchan); sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Scientist Reward: " + plural(rec.scientistEarnings, "silver"), safchan); safaribot.sendMessage(src, "Arena Reward: " + plural(rec.arenaPoints, "silver"), safchan); safaribot.sendMessage(src, "Tower Reward: " + plural(rec.towerSilver, "silver"), safchan); safaribot.sendMessage(src, "Pokémon Race: " + plural(rec.pokeRaceSilver, "silver"), safchan); safaribot.sendMessage(src, "Pyramid Loot: " + plural(rec.pyramidSilver, "silver"), safchan); safaribot.sendMessage(src, es(finishName("luxury")) + ": " + plural(rec.luxurySilver, "silver"), safchan); sys.sendMessage(src, "", safchan); } else if (commandData === "*" || commandData === "1") { sys.sendMessage(src, "*** Player Records | Page 1 ***", safchan); sys.sendMessage(src, "±Pokémon: {0} Pokémon caught in {1} attempts ({2}). Performed {3}, {4}, and {5}. Used {7} and regained {8}. Stole {6} Pokémon from NPCs. Caught {9} Terastallized Pokémon.".format(addComma(rec.pokesCaught), addComma(rec.pokesCaught+rec.pokesNotCaught), percentage(rec.pokesCaught, rec.pokesCaught+rec.pokesNotCaught), plural(rec.pokesEvolved, "Evolution"), plural(rec.megaEvolutions, "Mega Evolution"), plural(rec.pokesCloned, "Cloning"), addComma(rec.pokesStolen), plural(rec.devolutions, "spray"), plural(rec.devolutionDust, "dust"), addComma(rec.teraMonsCaught)), safchan); sys.sendMessage(src, "±Bait: Used {0} and {6} with {1} ({2}) and {3} ({4}). Used {7}. Snagged {5} Pokémon away from other Players.".format(plural(rec.baitUsed, "bait"), plural(rec.baitAttracted, "success"), percentage(rec.baitAttracted, rec.baitUsed + rec.goldenBaitUsed), plural(rec.baitNothing, "failure"), percentage(rec.baitNothing, rec.baitUsed + rec.goldenBaitUsed), rec.notBaitedCaught, plural(rec.goldenBaitUsed, "golden"), plural(rec.deluxeBaitUsed, "deluxe")), safchan); var earnings = rec.pokeSoldEarnings + rec.luxuryEarnings + rec.pawnEarnings + rec.collectorEarnings + rec.rocksWalletEarned + rec.rocksWindowEarned - rec.rocksWindowLost - rec.rocksWalletLost + rec.pokeRaceEarnings + rec.pyramidMoney + rec.towerEarnings; var silverEarnings = rec.scientistEarnings + rec.arenaPoints + rec.pyramidSilver + rec.towerSilver + rec.pokeRaceSilver + rec.luxurySilver; sys.sendHtmlMessage(src, toColor("±Money:", "#3daa68") + " Earned ${0} and {1} [{2}].".format(addComma(earnings), plural(silverEarnings, "silver"), "Use " + link("/records earnings", "/records earnings") + " to show a breakdown by source"), safchan); sys.sendMessage(src, "±Gachapon: Used {0} ({1}, {2}).".format(plural(rec.gachasUsed, "gacha"), plural(rec.masterballsWon, "master"), plural(rec.jackpotsWon, "Jackpot")), safchan); var onOthers = rec.rocksHit + rec.rocksWalletHit + rec.rocksMissedWindow + rec.rocksItemfinderHit; sys.sendMessage(src, "±{0}: Threw {1} ({2} accuracy, {3}). Embarassed {4}.".format(finishName("rock"), plural(rec.rocksThrown, "rock"), percentage(onOthers, rec.rocksThrown), plural(onOthers, "hit"), plural(rec.rocksBounced, "time")), safchan); var onMe = rec.rocksHitBy + rec.rocksWalletHit + rec.rocksDodgedWindow + rec.rocksChargesLost; sys.sendMessage(src, "±{0}: Hit by {1} ({2} evasion, {3}). Caught {4}. Scared away {5} wild Pokémon.".format(finishName("rock"), plural(onMe, "rock"), percentage(rec.rocksDodged, rec.rocksDodged + onMe), plural(rec.rocksDodged, "dodge"), plural(rec.rocksCaught, "throw"), addComma(rec.wildsScared)), safchan); sys.sendMessage(src, "±Game: {0} Consecutive Logins{1}. Won {2} Contests and {4}. Found {3} items with the Itemfinder.".format(addComma(rec.consecutiveLogins), (player.consecutiveLogins !== rec.consecutiveLogins ? " (currently " + player.consecutiveLogins + ")" : ""), addComma(rec.contestsWon), addComma(rec.itemsFound), plural(rec.medalsWon, "medal")), safchan); sys.sendMessage(src, "±Game: Opened {0} and used {1}. Hatched {2} and {3} with {4} being a Rare Pokémon! Gave {5} and received {6}.".format(plural(rec.packsOpened, "pack"), plural(rec.gemsUsed, "gem"), plural(rec.eggsHatched, "egg"), plural(rec.brightEggsHatched, "bright"), addComma(rec.rareHatched), plural(rec.burnGiven, "burn"), plural(rec.burnReceived, "burn")), safchan); sys.sendMessage(src, "±Game: Used {0} and won {1}. Ate {2} and {3}, and used {4} and {5}. Sent {6} and retouched {7}. Deployed {8} with {9} being successful.".format(plural(rec.shadyUsed, "shady"), plural(rec.mongerAuctionsWon, "Monger Auction"), plural(rec.cookiesEaten, "cookie"), plural(rec.mushroomsEaten, "mushroom"), plural(rec.scalesUsed, "scale"), plural(rec.crystalsUsed, "crystal"), plural(rec.mailsSent, "mail"), plural(rec.photosRetouched, "Photograph"), plural(rec.teraOrbsDeployed, "teraorb"), addComma(rec.teraOrbsSucceeded)), safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, link("/records 2","«Next Page»"),safchan); } else if (commandData === "2") { sys.sendMessage(src, "*** Player Records | Page 2 ***", safchan); var captures = "Caught {0} Pokémon in {1}, {2} in {3}, {4} in {5}, {6} in {7}, {8} in {9}, {10} in {11}, {12} in {13}, {14} in {15}, {16} in {17}, {18} in {19}, {20} in {21}, {22} in {23}, {24} in {25}, {26} in {27}, {28} in {29}, and {30} in {31}. Took {32}.".format( addComma(rec.catchQuick), es(finishName("quick")), addComma(rec.catchClone), es(finishName("clone")), addComma(rec.catchSpy), es(finishName("spy")), addComma(rec.catchHeavy), es(finishName("heavy")), addComma(rec.catchLuxury), es(finishName("luxury")), addComma(rec.catchMono), es(finishName("mono")), addComma(rec.catchMyth), es(finishName("myth")), addComma(rec.catchSpirit), es(finishName("spirit")), addComma(rec.catchLightning), es(finishName("lightning")), addComma(rec.catchMirror), es(finishName("mirror")), addComma(rec.catchLove), es(finishName("love")), addComma(rec.catchSwitch), es(finishName("uturn")), addComma(rec.catchInvert), es(finishName("inver")), addComma(rec.catchLevel), es(finishName("level")), addComma(rec.catchPhoto), es(finishName("photo")), addComma(rec.catchCherish), es(finishName("cherish")), plural(rec.photosTaken, "photograph") ); sys.sendMessage(src, "±Balls: " + captures, safchan); var given = rec.collectorGiven + rec.scientistGiven; sys.sendMessage(src, "±Quests: Turned in {0} Pokémon (Collector: {1}, Scientist: {2}).".format(addComma(given), addComma(rec.collectorGiven), addComma(rec.scientistGiven)), safchan); sys.sendMessage(src, "±Quests: Arena Record: {0}-{1} ({2}, {3}). Performed {4}, {5} and {6}.".format(addComma(rec.arenaWon), addComma(rec.arenaLost), percentage(rec.arenaWon, rec.arenaWon + rec.arenaLost), plural(rec.arenaPoints, "point"), plural(rec.wonderTrades, "Wonder Trade"), plural(rec.transmutations + rec.transmutationsMade, "Transmutation"), plural(rec.philosopherTransmutations, "Philosopher Transmutations")), safchan); sys.sendMessage(src, "±Quests: Lead a {0} point Pyramid Run. Participated in a {1} point Pyramid Run. Cleared the Pyramid {2} as Leader and {3} as Helper.".format(addComma(rec.pyramidLeaderScore), addComma(rec.pyramidHelperScore), plural(rec.pyramidLeaderClears, "time"), plural(rec.pyramidHelperClears, "time")), safchan); sys.sendMessage(src, "±Quests: Reached the {0} floor of the Battle Tower. Cleared a cumulative total of {1}. Defeated a Battle Tower boss {2} and defeated the final Battle Tower boss {3}. Earned a total of {4}.".format(getOrdinal(rec.towerHighestNew), plural(rec.towerTotal, "Battle Tower floor"), plural(rec.towerBosses, "time"), plural(rec.towerFinalBosses, "time"), plural(rec.towerBP, "battlepoint")) + (rec.towerSecretBosses > 0 ? " Defeated the secret Battle Tower boss {0}.".format(plural(rec.towerSecretBosses, "time")) : ""), safchan); sys.sendMessage(src, "±Quests: Turned in {0} to Journal for a total of {1}. ".format(plural(rec.journalSubmitted, "phоto"), plural(rec.journalPoints, "Photo Point")), safchan); sys.sendMessage(src, "±Quests: Cleared {0} and lost {1}. Cleared all of the Gyms {2}, all of the Elite Four {3}, and lost to the Elite Four {4}.".format(plural(rec.gymsCleared, "Gym Battle"), plural(rec.gymsLost, "Gym Battle"), plural(rec.allGymsCleared, "time"), plural(rec.eliteCleared, "time"), plural(rec.eliteLost, "time")), safchan); sys.sendMessage(src, "±Quests: Obtained a Celebrity score of {0} on Easy, {1} on Normal, {2} on Hard, {3} on Expert, {4} on Super Expert, and {5} on Abyssal.".format(rec.celebrityScoreEasy, rec.celebrityScore, rec.celebrityScoreHard, rec.celebrityScoreExpert, rec.celebrityScoreSuperExpert, rec.celebrityScoreAbyssal), safchan); sys.sendMessage(src, "±Quests: Unlocked {0} and charged skills {1} from the Idol.".format(plural(rec.idolUnlocked, "skill"), plural(rec.idolActivated, "time")), safchan); var detectiveStats = "±Quests: Solved {0}" + (rec.fastestCaseSolved > 0 ? " and solved a case with a record time of {1}." : "."); sys.sendMessage(src, detectiveStats.format(plural(rec.casesSolved, "Detective case"), timeString(rec.fastestCaseSolved/1000, true)), safchan); sys.sendMessage(src, "±Missions: Cleared {0} for a total of {1}.".format(plural(rec.missionCleared, "mission"), plural(rec.missionPoints, "mission point")), safchan); sys.sendMessage(src, "±Events: Won {0} with {1}. Won {2} ({3} as Favorite, {4} as Underdog). Won Battle Factory {5} and was Runner-up {6}. Won a Quiz {7} and was Runner-up {8}. Obtained a high score of {9} during a Quiz. Won Bingo {10}.".format(plural(rec.factionWins, "Faction War"), plural(rec.factionMVPs, "MVP"), plural(rec.pokeRaceWins, "Pokémon Race"), addComma(rec.favoriteRaceWins), addComma(rec.underdogRaceWins), plural(rec.factoryFirst, "time"), plural(rec.factorySecond, "time"), plural(rec.quizFirst, "time"), plural(rec.quizSecond, "time"), plural(rec.topQuizScore, "point"), plural(rec.bingoWon, "time")), safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, link("/records 1","«Previous Page»"),safchan); } }; this.assignIdNumber = function(player, force) { if (!lastIdAssigned) { lastIdAssigned = loadLastId(); } if (player) { if (!("idnum" in player) || player.idnum === undefined || player.idnum === null || isNaN(player.idnum) || player.idnum < 0 || typeof player.idnum !== "number") { player.idnum = 0; } if (player.idnum === 0 || force) { lastIdAssigned++; permObj.add("lastIdAssigned", lastIdAssigned); permObj.save(); player.idnum = lastIdAssigned; } } }; this.revertMega = function(src, forced, pokeNum) { // pass forced when intending to revert after battles/pyr, because those battles aren't spliced from currentBattles/currentPyramids immediately var player = getAvatar(src); if (!player) { return; } for (var b in currentBattles) { if (currentBattles[b].isInBattle(sys.name(src)) && !forced) // ensure megas can't revert in the middle of a league/celeb run return; } for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(sys.name(src)) && !forced) // or during pyramid return; } var currentTime = now(), info; for (var e = player.megaTimers.length - 1; e >= 0; e--) { info = player.megaTimers[e]; if (info.expires <= currentTime) { if (pokeNum && info.id !== pokeNum) { continue; } if (player.pokemon.indexOf(info.id) !== -1) { this.evolvePokemon(src, getInputPokemon(info.id + (typeof info.id == "string" ? "*" : "")), info.to, "reverted to", true); } player.megaTimers.splice(e, 1); if (pokeNum) { // if passing a pokenum, we're probably only wanting to revert 1 so break out right here break; } } } }; this.showMegaTimers = function(src) { var player = getAvatar(src); if (!player) { return; } if (player.megaTimers.length === 0) { safaribot.sendMessage(src, "You don't have any current Mega Pokémon!", safchan); return; } var displayCap = 10; for (var i = 0; i < Math.min(player.megaTimers.length, displayCap); i++) { var timerObj = player.megaTimers[i]; var diff = timerObj.expires - now(); var diffString = (diff < 0 ? (currentTheme ? "After this Contest" : "After the next Contest") : timeString(Math.round(diff / 1000), true)); var out = "{0} {1}: {2}".format(pokeInfo.icon(timerObj.id, typeof timerObj.id === "string"), getInputPokemon(timerObj.id + (typeof timerObj.id === "string" ? "*" : "")).name, diffString); safaribot.sendHtmlMessage(src, out, safchan); } }; this.setFavoriteBall = function(src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (cantBecause(src, "set a favorite ball", ["tutorial"])) { return; } if (commandData === "*") { safaribot.sendHtmlMessage(src, "Your favorite ball is " + finishName(player.options.favoriteBall) + "! This ball will be thrown automatically if you do not specify a ball when throwing. Use " + link("/options favorite:[Ball Name]") + " to set a different favorite Ball!", safchan); return; } var ball = itemAlias(commandData); if (!isBall(ball)) { ball = "safari"; } player.options.favoriteBall = ball; safaribot.sendMessage(src, "You changed your favorite ball to " + finishName(ball) + "! This ball will be thrown automatically if you do not specify a ball when throwing.", safchan); this.saveGame(player); }; this.viewNotifs = function(src, onlyUnread) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), box = player.notifications; if (box.length === 0) { safaribot.sendMessage(src, "No notifications!", safchan); return; } //TODO: Manage messages (clear/delete/keep as unread) sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Your Notifications" + (onlyUnread ? " (Unread Only)" : "") + ": ", safchan); var hitAny = false; for (var e = 0; e < box.length; e++) { if (onlyUnread && box[e].seen) { continue; } safaribot.sendHtmlMessage(src, ((!(box[e].seen)) ? toColor(box[e].msg, "#fc8403") : box[e].msg), safchan); hitAny = true; box[e].seen = true; } if (!hitAny) { safaribot.sendHtmlMessage(src, "No unread notifications!", safchan); } player.notificationData.daycareWaiting = true; sys.sendMessage(src, "", safchan); player.notificationSources = []; this.saveGame(player); }; this.countUnseenNotifications = function(player) { var out = 0, box = player.notifications; for (var e = 0; e < box.length; e++) { if (!(box[e].seen)) { out++; } } return out; }; this.clearQuestNotifications = function(player, source, removeSource) { var out = 0, box = player.notifications; for (var e = 0; e < box.length; e++) { if (!(box[e].seen)) { if (box[e].source == source) { box[e].seen = true; } } } if (removeSource && player.notificationSources.contains(source)) { player.notificationSources.splice(player.notificationSources.indexOf(source), 1); } safari.saveGame(player); }; this.notification = function(player, message, fromWhere, mute) { if (!player.hasOwnProperty("notifications")) { player.notifications = []; } if (!player.hasOwnProperty("notificationSources")) { player.notificationSources = []; } if (!player.options.anyNotifications) { return; } var questSources = ["tower", "arena", "league", "collector", "scientist", "journal", "wonder", "pyramid", "alchemist", "detective"]; if (!player.options.questNotifications && questSources.contains(fromWhere.toLowerCase())) { return; } var d = new Date().toUTCString(); try { player.notifications.push( { msg: message + " --- [" + d + "]", seen: false, source: fromWhere } ); while (player.notifications.length > 16) { player.notifications.shift(); } if (!(player.notificationSources.contains(fromWhere))) { player.notificationSources.push(fromWhere); } this.saveGame(player); if (mute) { return; } this.notificationPing(player); } catch (e) { safaribot.sendAll(e + " | Notification: " + message + " | fromWhere: " + fromWhere + " | player: " + player.id + " | player.notifications: " + JSON.stringify(player.notifications), staffchannel); } }; this.notificationPing = function(player) { var src = sys.id(player.id); if (!validPlayers("self", src)) { return; } var amt = this.countUnseenNotifications(player); if (amt > 0) { safaribot.sendHtmlMessage(src, "You have " + (amt > 1 ? amt : "a") + " new notification" + (amt > 1 ? "s" : "") + " from " + readable(player.notificationSources, "and") + "! " + link("/notifications unread", "«Notifications»") + "", safchan); } }; this.pendingNotifications = function(name) { var p, data, currentTime = now(), out = "", hold, hold2, currentDay = getDay(now()), m, hold, hitAny = false; var fireNotifications = function(name) { p = getAvatarOff(name); if ((!p) || (!(p.notificationData))) { return; } hitAny = false; data = p.notificationData; if (data.towerWaiting) { if (p.quests.tower.cooldown < currentTime && !safari.isBattling(name)) { data.towerWaiting = false; out = "You are able to fight the " + link("/quest tower", "Battle Tower") + " again!"; if (data.lastTowerParty.length > 0) { hold = []; for (var i = 0; i < data.lastTowerParty.length; i++) { hold.push(poke(data.lastTowerParty[i])); } out += " Load your last used Battle Tower party? " + link("/qload " + hold.join(","), readable(hold, "and")); } safari.notification(p, out, "Tower", true); hitAny = true; } } if (data.arenaWaiting) { if (p.quests.arena.cooldown < currentTime && !safari.isBattling(name)) { data.arenaWaiting = false; out = "Trainer " + link("/quest arena:" + data.lastArenaTrainer, capitalizeFirst(data.lastArenaTrainer)) + " is ready for a rematch in the " + link("/quest arena", "Arena") + "!"; if (data.lastArenaParty.length > 0) { hold = []; for (var i = 0; i < data.lastArenaParty.length; i++) { hold.push(poke(data.lastArenaParty[i])); } out += " Load your last used Arena party? " + link("/qload " + hold.join(","), readable(hold, "and")); } safari.notification(p, out, "Arena", true); hitAny = true; } } if (data.leagueWaiting) { if (p.quests.league.cooldown < currentTime && !safari.isBattling(name)) { data.leagueWaiting = false; out = "You are able to fight the " + link("/quest league", "Pokémon League") + " again!"; if (data.lastLeagueParty.length > 0) { hold = []; for (var i = 0; i < data.lastLeagueParty.length; i++) { hold.push(poke(data.lastLeagueParty[i])); } out += " Load your last used League party? " + link("/qload " + hold.join(","), readable(hold, "and")); } safari.notification(p, out, "League", true); hitAny = true; } } if (data.collectorWaiting) { if (p.quests.collector.cooldown < currentTime) { data.collectorWaiting = false; out = "The " + link("/quest collector", "Collector") + " has finished organizing his collection! He's ready to request new Pokémon!"; safari.notification(p, out, "Collector", true); hitAny = true; } } if (data.wonderWaiting) { if (p.quests.wonder.cooldown < currentTime) { data.wonderWaiting = false; out = "Ready to try your luck once more? " + link("/quest wonder", "Wonder Trade") + " is ready!"; safari.notification(p, out, "Wonder", true); hitAny = true; } } if (data.pyramidWaiting) { if (p.quests.pyramid.cooldown < currentTime) { data.pyramidWaiting = false; out = "You can now lead your next " + link("/quest pyramid", "Pyramid") + " expedition!"; safari.notification(p, out, "Pyramid", true); hitAny = true; } } if (data.alchemistWaiting) { if (p.quests.alchemist.cooldown < currentTime) { data.alchemistWaiting = false; out = "The " + link("/quest alchemist", "Alchemist") + " is ready to make more items!"; safari.notification(p, out, "Alchemist", true); hitAny = true; } } if (data.arboristWaiting) { if (p.quests.arborist.cooldown < currentTime) { data.arboristWaiting = false; out = "The " + link("/quest arborist", "Arborist") + " is ready to trade more Apricorns!"; safari.notification(p, out, "Arborist", true); hitAny = true; } } if (data.journalWaiting) { if (p.quests.journal.cooldown < currentTime) { data.journalWaiting = false; out = "The " + link("/quest journal", "Editor-in-chief") + " is ready for your next photo submission!"; safari.notification(p, out, "Journal", true); hitAny = true; } } if (data.costumeWaiting) { if (p.cooldowns.costume < currentTime) { data.costumeWaiting = false; out = "You can now " + link("/costume", "change costumes") + " again!"; safari.notification(p, out, "Costume", true); hitAny = true; } } for (var t in safari.daycarePokemon) { if (safari.daycarePokemon[t].ownernum === p.idnum) { var mon = safari.daycarePokemon[t]; if (mon.berry && mon.berry.time <= now() && !mon.berry.notified) { var monName = (mon.shiny ? "Shiny " : "") + poke(mon.id); mon.berry.notified = true; safari.notification(p, "Your {0} grew some berries for you! Go visit the {1} to harvest them!".format(monName, link("/daycare berry", "Daycare")), "Daycare", true); hitAny = true; } } } if (data.daycarePlay && chance(0.25)) { data.daycarePlay = false; /*if (data.daycareHungry) { out = "Your " + poke(data.daycarePoke) + " is hungry! Go visit the " + link("/daycare", "Daycare") + " to feed it!"; data.daycareHungry = false; } else { out = "Your " + poke(data.daycarePoke) + " wants to play! Go visit the " + link("/daycare", "Daycare") + " to play with it!"; } safari.notification(p, out, "Daycare", true); hitAny = true;*/ } if (chance(0.22) && data.missionWaiting) { hit = 0; for (var e = 0; e < p.missions.length; e++) { m = p.missions[e]; if (m.count >= m.goal && canReceiveStuff(p, m.reward).result && (!(m.finished))) { hit++; } } if (hit > 0) { out = "You can receive rewards from " + hit + " cleared " + link("/missions", hit > 1 ? "Missions" : "Mission") + "!"; safari.notification(p, out, "Missions", true); data.missionWaiting = false; hitAny = true; } } if (hitAny) { safari.notificationPing(p); } safari.saveGame(p); }; if (!name) { var players = sys.playersOfChannel(safchan); for (var pid in players) { var player = getAvatar(players[pid]); if (!(player)) { continue; } id = player.idnum; recentPlayers[id+""] = currentDay; } for (var s in recentPlayers) { name = idnumList.get(parseInt(s, 10)); if (!(name) || name == undefined) { continue; } if (typeof name !== "string") { continue; } fireNotifications(name); } } else { if (typeof name === "string") { fireNotifications(name); } } }; this.viewInbox = function(src, onlyUnread) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), box = player.inbox, unread = player.unreadInbox; if (box.length === 0) { safaribot.sendMessage(src, "Your inbox is empty!", safchan); return; } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Your Inbox" + (onlyUnread ? " (Unread Only)" : "") + ":", safchan); var hitAny = false; for (var e = 0; e < box.length; e++) { if (onlyUnread && !unread[e]) { continue; } safaribot.sendHtmlMessage(src, (unread[e] ? toColor(box[e], "red") : box[e]) + " " + link("/deleteinbox " + box[e], "[Delete]"), safchan); player.unreadInbox[e] = false; hitAny = true; } if (!hitAny) { safaribot.sendHtmlMessage(src, "No unread inbox messages!", safchan); } sys.sendMessage(src, "", safchan); this.saveGame(player); }; this.inboxMessage = function(player, msg, asRead) { var d = new Date().toUTCString(); if (!player.hasOwnProperty("inbox")) { player.inbox = []; } if (!player.hasOwnProperty("unreadInbox")) { player.unreadInbox = player.inbox.map(function() { return false; }); } player.inbox.push(msg + " --- [" + d + "]"); player.unreadInbox.push(asRead ? false : true); while (player.inbox.length > 30) { player.inbox.shift(); player.unreadInbox.shift(); } while (player.unreadInbox.length > player.inbox.length) { player.unreadInbox.shift(); } this.saveGame(player); }; this.deleteInbox = function(src, msg) { var player = getAvatar(src); for (var a = 0; a < player.inbox.length; a++) { if (player.inbox[a] == msg) { break; } if (a == player.inbox.length - 1) { safaribot.sendHtmlMessage(src, "Error: no mail containing '" + msg + "' found!", safchan); return; } } player.inbox.splice(a, 1); player.unreadInbox.splice(a, 1); safaribot.sendHtmlMessage(src, "Deleted mail: '" + msg + "'!", safchan); return; }; this.runPendingActive = function(name) { var runPending = function(id, name, pendingActive) { for (var propName in pendingActive) { var restrictions = pendingActive[propName].restrictions; var data = pendingActive[propName].data; if (safari.hasOwnProperty(propName) && !cantBecause(id, false, restrictions, false, true)) { safari[propName](id, data); safari.deletePendingActive(name, propName); } } } if (name) { id = sys.id(name); if (id && getAvatar(id)) { if (!pendingActiveChanges.hasOwnProperty(name)) { return; } runPending(id, name, pendingActiveChanges[name]); } } else { for (e in pendingActiveChanges) { id = sys.id(e); if (id && getAvatar(id)) { runPending(id, e, pendingActiveChanges[e]); } } } }; this.addPendingActive = function(name, type, data, restrictions) { if (!pendingActiveChanges.hasOwnProperty(name)) { pendingActiveChanges[name] = {}; } if (!pendingActiveChanges[name].hasOwnProperty(type)) { pendingActiveChanges[name][type] = {}; } pendingActiveChanges[name][type]["data"] = data; pendingActiveChanges[name][type]["restrictions"] = restrictions || []; }; this.deletePendingActive = function(name, type) { if (!pendingActiveChanges.hasOwnProperty(name)) { return; } if (type === "all") { delete pendingActiveChanges[name]; return; } if (!pendingActiveChanges[name].hasOwnProperty(type)) { return; } delete pendingActiveChanges[name][type]; if (Object.keys(pendingActiveChanges[name]).length === 0) { delete pendingActiveChanges[name]; } }; /* Tutorial */ this.skipTutorial = function (src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (!player.tutorial.inTutorial) { safaribot.sendMessage(src, "You have either completed or skipped the tutorial!", safchan); return; } if (commandData !== "confirm") { tutorMsg(src, "Are you sure you want to skip the tutorial? You may have a difficult time playing and will miss out on a special gift to help you in your journey! If you are really sure you want to skip the tutorial, type " + link("/skiptutorial confirm")); return; } if (player.tutorial.step > 0) { if (player.tutorial.step > 9) { tutorMsg(src, "You're nearly finished! Just hold on a little longer and you'll be ready to play! Use " + link("/tutorial") + " to repeat the last step."); return; } tutorMsg(src, "It's sad to see you skip the tutorial while you have already made progress on it. Take care on your journey and if you have any questions, other players may be able to assist you!"); safari.resetToStart(src); } else { tutorMsg(src, "Here's a small gift pack to help you out. Take care on your journey and if you have any questions, other players may be able to assist you!"); } player.tutorialFinished = now(); welcomePack(src); sys.appendToFile(miscLog, now() + "|||" + sys.name(src) + "|||used /skiptutorial\n"); }; this.progressTutorial = function (src, automatedStep, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); this.saveGame(player); //Save so if anything breaks, progress isn't lost var step = automatedStep || player.tutorial.step; if (step === 0) { step = 1; advanceTutorial(src, step); return; } else if (step === 12 && commandData && commandData.toLowerCase() === "finish") { step = 13; } switch(step) { //Buy Safari Ball + Bait case 1: { player.money = 100; player.balls.safari = 6; tutorMsg(src, "Thank you for letting me teach you the basics of Safari! I have given you $100 and a handful of Safari Balls. You can use " + link("/bag") + " to see what you are currently carrying. Now, your " + poke(player.starter) + " looks a little lonely. Why don't you buy a bait from the shop to attract a friend? Use " + link("/buy bait") + " to directly buy that item, or you can use " + link("/buy") + " to check what else the shop has to offer!"); tutorMsg(src, "If at any point you forget what you need to do, simply type " + link("/tutorial") + " to repeat the directions for that step!"); } break; //Use Bait+ Catch Wild case 2: { tutorMsg(src, "Great job! After you finish the tutorial, the shop will have plenty of items to help you during your stay. Now let's use this bait to attempt to attract a Wild Pokémon with " + link("/bait") + ". Then when one appears, use " + link("/catch") + " to throw a ball to catch it!"); } break; //Party modification case 3: { tutorMsg(src, "Congratulations on your catch! One thing to note about Bait is that the person that attracts the Wild Pokémon gets to throw their ball first while everyone else prepares. After they throw, everyone else's ball is thrown in a random order, so make sure you prepare your throw quickly after a Wild Pokémon appears!"); tutorMsg(src, "Your " + poke(player.starter) + " is excited they have a new friend to play with! Why don't you add that Pikachu you just caught to your party with " + link("/add Pikachu") + "? As you catch more Pokémon, it may be difficult to keep track. Luckily, you can view your currently owned Pokémon with " + link("/box") + " and even view your current party with " + link("/party")); } break; //Get Costume + Equip Costume case 4: { tutorMsg(src, "Oh right! I almost forgot, with all the excitement of catching your first Pokémon, you can even wear costumes in Safari to express your individuality. Use " + link("/getcostumes") + " to obtain the Preschooler costume and then you can use " + link("/dressup Preschooler") + " to change into your new costume. Costumes are fun to collect and wear but some are difficult to obtain; you should try to collect them all one day! You can view what costumes you currently own with " + link("/costumes")); } break; //Buy Gacha Ticket+ Use gacha case 5: { if (player.balls.gacha !== 1) { player.money = 200; } tutorMsg(src, "Now, let's learn about some recreational activities around the Safari Zone. First up is Gachapon. The premise of Gachapon is simple: You put in a ticket and a capsule pops out with a prize! The easiest way of obtaining these tickets is buying them from the shop. If you forgot how to view the shop, use " + link("/buy") + " or buy them directly with " + link("/buy gacha") + ". Once you have the ticket in hand, use " + link("/gacha") + " to use the machine."); } break; //Use Gem + Finder case 6: { tutorMsg(src, "The next recreational activity is using the Itemfinder to look for rare items! I've got a spare one you can have, but unfortunately it doesn't have any charge left... Wait a second, you got an Ampere Gem from Gachapon?! Wow, what luck! This works out perfectly! You can use the gem to recharge the Itemfinder with " + link("/use gem") + ". Then you can use " + link("/finder") + " to start looking for rare items!"); } break; //Check bst/info case 7: { tutorMsg(src, "Nice! You found a Rare Candy. You seem to be very lucky during this tutorial. It almost seems rigged... Anyway, you should try to evolve your Pikachu! To see how many Rare Candies it takes to evolve a Pikachu, as well as other information, type " + link("/bst pikachu") + "."); } break; //Use Rare + Evolve Pokemon case 8: { var cr = safari.candyCostConversion(null, evolutions[pokeInfo.species(sys.pokeNum("Pikachu"))].candies); tutorMsg(src, "Hmm... You don't seem to have enough to evolve your Pikachu. According to " + link("/bst Pikachu") + ", you need " + cr + " Rare Candies. I'll help you out this time and give you the 3 you need, then you will be able to evolve your Pikachu with " + link("/evolve Pikachu")); if (player.balls.rare < cr) { player.balls.rare = cr; safaribot.sendMessage(src, "You received a pouch of 2 Rare Candies from Kangaskhan.", safchan); } } break; //Turn in evolved Pokemon to Collector (Quests) case 9: { tutorMsg(src, "Congratulations on your first evolution! While every evolved Pokémon is catchable in the Safari Zone, some are much rarer than others, so you'll likely need to perform some evolutions. The next major recreational activity in Safari is questing! Quests are fun, varied, and give you rewards unobtainable elsewhere. Quests range from the Battle Tower to Arena Battles to helping out inhabitants of the Safari Zone. You can check them all out with " + link("/quests") + " but for now we're going to help the Collector with his urgent request. You can access this quest with " + link("/quest Collector:start")); } break; //Contests case 10: { tutorMsg(src, "Well that was nice of you to help the Collector out. Now you're able to participate in Contests that happen every " + Math.floor(contestCooldownLength/60) + " minutes. Rumor has it that certain Pokémon only spawn during Contests and Contests are an easy way to amass a large collection of Pokémon, so you should participate often! Shortly before each Contest begins, there will be an announcement stating possible themes and rules for the next Contest. Themes determine what Pokémon will spawn. Rules affect catch rate, both positively and negatively, so pay attention and adjust your party accordingly! Why don't you flip through the Contest Booklet with " + link("/contestrules") + "? Once you have completed this, you can progress to the next step with " + link("/tutorial")); } break; //Rules case 11: { tutorMsg(src, "Now, in order to make the experience fun for everyone, there are a few rules you need to follow while playing in Safari. All you need to do to finish this step of the tutorial is read the information in " + link("/safarirules") + " then use " + link("/tutorial") + " to progress the tutorial."); } break; //Shop, Auction, Battles case 12: { tutorMsg(src, "Nicely done. You have completed the tutorial. There are lots of different things to still do in Safari, but you're pretty well set with the basics. You can interact with other players by challenging them to a battle with " + link("/challenge") + ", host auctions to sell your Pokémon or items with " + link("/auction") + ", and even set up a shop for others to buy from you with " + link("/shop") + "! You can use " + link("/commands safari") + " to view all the commands available, both ones we went over and ones we didn't. If you have any additional questions, feel free to ask other Trainers, they will help you out! When you are ready to start your adventure, use " + link("/tutorial finish")); } break; //End of tutorial case 13: { tutorMsg(src, "Congratulations! You have completed the tutorial."); welcomePack(src, true); player.tutorialFinished = now(); var tutDuration = (player.tutorialFinished - player.created) / 1000; if (tutDuration < 300) { sys.appendToFile(miscLog, now() + "|||" + sys.name(src) + "|||used finished the tutorial in "+utilities.getTimeString(tutDuration)+"\n"); } } break; } this.saveGame(player); }; this.buyForTutorial = function(src, data) { var info = data.split(":"), product = info.length > 1 && info[1] !== "" ? info[1].toLowerCase() : "*", player = getAvatar(src); if (player.tutorial.privateWildPokemon) { tutorMsg(src, "This is no time to be shopping! Use " + link("/catch") + " to throw a ball at the Pikachu before it runs away!"); return; } if (product === "*") { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "You can buy the following Items:", safchan); if (player.tutorial.step >= 1) { safaribot.sendHtmlMessage(src, link("/buy bait", "Bait") + ": $100", safchan);} if (player.tutorial.step >= 5) { safaribot.sendHtmlMessage(src, link("/buy gacha", "Gachapon Ticket") + ": $200", safchan);} sys.sendMessage(src, "", safchan); return; } if (player.tutorial.step === 1 && player.money === 100) { if (product !== "bait") { tutorMsg(src, "I gave you that money to buy a Bait! Please do so with " + link("/buy bait")); return; } else { if (player.balls.bait === 1) { tutorMsg(src, "You already bought the Bait. Please use it with " + link("/bait")); return; } safaribot.sendMessage(src, "You bought a Bait for $100!", safchan); player.balls.bait = 1; player.money -= 100; advanceTutorial(src, 2); return; } } else if (player.tutorial.step === 5 && player.money === 200) { if (product !== "gacha") { tutorMsg(src, "I gave you that money to buy a Gachapon Ticket! Please do so with " + link("/buy gacha")); return; } else { if (player.balls.gacha === 1) { tutorMsg(src, "You already bought the Gachapon Ticket. Please use it with " + link("/gacha")); return; } safaribot.sendMessage(src, "You bought a Gachapon Ticket for $200!", safchan); player.balls.gacha = 1; player.money -= 200; tutorMsg(src, "You're pretty good at this, so I probably don't need to remind you that you can now use that ticket with " + link("/gacha")); return; } } tutorMsg(src, "You don't need to buy any item right now. If you forgot what to do, use " + link("/tutorial")); }; this.tutorialBait = function (src) { var player = getAvatar(src); if (player.tutorial.privateWildPokemon) { tutorMsg(src, "There's already a Pokémon in front of you! Use " + link("/catch") + " to throw a ball at the Pikachu before it runs away!"); return; } if (player.balls.bait !== 1) { player.balls.bait = 1; tutorMsg(src, "You seem to have misplaced your bait! Here's another one. Try throwing again with " + link("/bait")); return; } player.balls.bait = 0; this.saveGame(player); safaribot.sendMessage(src, "You left some bait out. The bait attracted a wild Pokémon!", safchan); safari.createTutorialWild(src, sys.pokeNum("Pikachu")); }; this.createTutorialWild = function(src, dexNum) { var player = getAvatar(src); var num = parseInt(dexNum, 10); var pokeName = poke(num); player.tutorial.privateWildPokemon = num; var bst = getBST(num); sys.sendHtmlMessage(src, "
A wild " + pokeName + " appeared! (BST: " + bst + ")
" + pokeInfo.sprite(num) + "
(Note: This Pokémon is only visible to you and will flee if you leave the channel! Any ball you throw will be directed at this Pokémon.)

", safchan); ballMacro(src, true); tutorMsg(src, "Wow, a Wild Pikachu! Quick, throw a ball at it with " + link("/catch") + " before it flees!"); }; this.tutorialCatch = function(src, data) { var player = getAvatar(src); var pokeNum = player.tutorial.privateWildPokemon; var pokeName = poke(pokeNum); var ball = itemAlias(data); if (!isBall(ball)) { ball = "safari"; } if (cantBecause(src, null, ["item"], ball)) { tutorMsg(src, "Try throwing a Safari Ball with " + link("/catch")); return; } var fullBall = finishName(ball); player.balls[ball] -= 1; sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Gotcha! " + pokeName + " was caught with " + an(fullBall) + "! You have " + plural(player.balls[ball], fullBall) + " left!", safchan); sys.sendMessage(src, "", safchan); player.pokemon.push(pokeNum); player.records.pokesCaught = 1; //We need this for preschooler costume player.tutorial.privateWildPokemon = null; advanceTutorial(src, 3); }; this.tutorialGacha = function(src) { var player = getAvatar(src); if (player.balls.gacha !== 1) { if (player.money === 200) { tutorMsg(src, "You need to buy a Gachapon Ticket to use the Machine. Try " + link("/buy gacha") + " to buy one."); return; } player.balls.gacha = 1; tutorMsg(src, "You seem to have misplaced your Gachapon Ticket! Here's another one. Try using it again with " + link("/gacha")); return; } player.balls.gacha = 0; safaribot.sendMessage(src, "Gacha-PON! The Gachapon Machine has dispensed an item capsule. [Remaining Tickets: " + player.balls.gacha + "]", safchan); safaribot.sendMessage(src, "The Gachapon machine emits a bright flash of light as you reach for your prize. Despite being temporarily blinded, you know you just won an Ampere Gem due to a very faint baaing sound!", safchan); safaribot.sendMessage(src, "You received an Ampere Gem.", safchan); player.balls.gem = 1; advanceTutorial(src, 6); }; this.tutorialUseItem = function(src, item) { var player = getAvatar(src); if (item === "gem") { if (player.balls.permfinder > 0) { player.balls.gem = 0; tutorMsg(src, "You've already recharged your Itemfinder. Now you can use it with " + link("/finder")); return; } if (player.balls.gem !== 1) { player.balls.gem = 1; tutorMsg(src, "You seem to have misplaced your Ampere Gem! Here's another one. Try using it with " + link("/use gem")); return; } safaribot.sendHtmlMessage(src, "The Ampere Gem begins to emit a soft baaing sound. Your Itemfinder then lights up and responds with a loud BAA~!", safchan); safaribot.sendMessage(src, "Your Itemfinder now has " + itemData.gem.charges + " charges!", safchan); player.balls.gem = 0; player.balls.permfinder = itemData.gem.charges; tutorMsg(src, "Great. Now the Itemfinder is working again. As a reminder, the manual states to use " + link("/finder") + " to use it!"); } }; this.tutorialFinder = function(src) { var player = getAvatar(src); if (player.balls.permfinder === itemData.gem.charges) { player.balls.permfinder -= 1; safaribot.sendMessage(src, "You pull out your Itemfinder ... ... ... But it did not detect anything. [Remaining charges: " + player.balls.permfinder + "].", safchan); tutorMsg(src, "I guess you weren't as lucky with Itemfinder as you were with Gachapon. Don't worry, it's not often you find items. Why don't you try using your Itemfinder again? (" + link("/finder") + ")"); } else if (player.balls.permfinder === (itemData.gem.charges - 1)) { player.balls.permfinder -= 1; safaribot.sendHtmlMessage(src, "Beep. Beep. BEEP! You found a Rare Candy behind a bush!", safchan); player.balls.rare = 1; advanceTutorial(src, 7); } }; this.tutorialBST = function(src, commandData) { var player = getAvatar(src); if (commandData.toLowerCase() == "pikachu") { tutorMsg(src, "I guess you weren't as lucky with Itemfinder as you were with Gachapon. Don't worry, it's not often you find items. Why don't you try using your Itemfinder again? (" + link("/finder") + ")"); } else { tutorMsg(src, "Use " + link("/bst Pikachu") + " to find out how many Rare Candies it needs to evolve!", safchan); } }; this.tutorialQuest = function(src, start) { var player = getAvatar(src); var quest = player.quests.collector; if (start) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Collector: Thank you so much for hearing my request! My Raichu has been very lonely lately. I'm looking to find another Raichu to keep it company. I don't have much to give you right now, but I can give you my old Contest Booklet and Entry Pass, allowing you to participate in Contests to catch wild Pokémon!", safchan); tutorMsg(src, "Oh! You have a Raichu! You could give it to him and get that Contest Booklet. It's a very helpful item that will allow you to catch tons of Pokémon! Your Raichu seems estatic to bring joy to another person, why don't you help the Collector out? Use " + link("/quest collector:finish") + " to fulfill his request!"); sys.sendMessage(src, "", safchan); quest.requests = [sys.pokeNum("Raichu")]; quest.reward = 1; quest.deadline = null; quest.cooldown = 0; this.saveGame(player); } else { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Collector: Thank you very much! My Raichu will be so happy playing with your Raichu! As promised, here's your Contest Booklet and Entry Pass. Feel free to flip through the booklet at any point, there's a lot of important information in it.", safchan); sys.sendMessage(src, "", safchan); this.removePokemon(src, quest.requests[0]); quest.reward = 0; quest.requests = []; quest.cooldown = now(); quest.deadline = 0; advanceTutorial(src, 10); } }; this.resetToStart = function(src) { var player = getAvatar(src); var tutItem = ["safari", "bait", "gacha", "gem", "rare", "permfinder", "dust"]; for (var i = 0; i < tutItem.length; i++) { player.balls[tutItem[i]] = 0; } player.records.pokesCaught = 0; player.costume = "none"; player.costumes = []; var start = player.starter; player.party = []; player.pokemon = []; player.pokemon.push(start); player.party.push(start); this.saveGame(player); }; /* Story */ this.openStory = function(src, data) { var player = getAvatar(src); var chapters = []; var text = ""; var cleared = player.story.clearedChapters; var progress = 0; if (player.story.inStory) { player.story.step--; this.storyStep(src, player.story.step); } if (data == "*") { for (var a in storyData) { var acc = storyData[a]; if ((acc.req1 == 0 || cleared.contains(acc.req1)) && (acc.req2 == 0 || cleared.contains(acc.req2))) { text += acc.name; if (cleared.contains(parseInt(a, 10))) { text += " [Completed]"; } else { progress = 0; if (player.story.savePoints.hasOwnProperty(a)) { if (player.story.savePoints[a] > 0) { progress = (100 * player.story.savePoints[a])/acc.text.length; } } if (progress > 0) { text += toColor(" Current Progress: " + progress + "%. ", "orange") + link("/story " + a, "»» Continue"); } else { text += toColor(" New ", "cyan") + link("/story a", "»» Start"); } } text += "\n"; } } } else { var acc; if (storyData.hasOwnProperty(data)) { acc = storyData[data]; if ((acc.req1 == 0 || cleared.contains(acc.req1)) && (acc.req2 == 0 || cleared.contains(acc.req2))) { player.story.step = 0; if (player.story.savePoints.hasOwnProperty(data)) { if (player.story.savePoints[data] > 0) { player.story.step = player.story.savePoints[data]; } } player.story.chapter = data; player.story.step -= 1; player.story.inStory = true; player.story.state = "Reading"; this.storyStep(src, player.story.step + 1); } } else { safaribot.sendHtmlMessage(src, "That's not a valid chapter!", safchan); return; } } }; this.closeStory = function(src) { var player = getAvatar(src); var todo = player.story.onQuit; for (var p in todo.demega) { this.evolvePokemon(src, p, todo.demega[p], "reverted to", true, false, true); } player.story.onQuit = { demega: {} }; player.story.inStory = false; player.story.step = 0; player.story.chapter = 0; safaribot.sendHtmlMessage(src, "You are no longer in Story Mode! Return again with " + link("/story") + ".", safchan); this.saveGame(player); }; this.storyQuickSave = function(src, step) { var player = getAvatar(src); player.story.savePoints[player.story.chapter] = step; this.saveGame(player); return; }; this.storyStep = function(src, val) { var player = getAvatar(src); var canProgress = false; if (val == player.story.step + 1) { canProgress = true; } if (player.story.validJumps.contains(val)) { canProgress = true; } if ((!(canProgress)) || (val < 0)) { safaribot.sendHtmlMessage(src, "Nope... the fabric of reality will not allow you to do that!", safchan); return; } if (!(player.story.inStory)) { safaribot.sendHtmlMessage(src, "You must be in the story to do that! Type " + link("/story") + " to begin.", safchan); return; } if (player.story.state !== "Reading") { safaribot.sendHtmlMessage(src, "You must not be in a battle, wild encounter, etc before continuing.", safchan); return; } player.story.step = val; var currentStep = player.story.step; var currentChapter = player.story.chapter; var currentText = storyData[currentChapter].text[currentStep]; if (currentText.indexOf("jump::") == 0) { var jumpTo = parseInt(currentText.slice(6), 10); currentStep = jumpTo; currentText = storyData[currentChapter].text[currentStep]; } if (currentText == "::silentsave::") { player.story.savePoints[player.story.currentChapter] = step; currentStep += 1; currentText = storyData[currentChapter].text[currentStep]; } currentText = currentText.split("--"); var out = []; var textOut = []; for (var i = 0; i < currentText.length; i++) { out = this.storyTextParse(src, player, currentText[i], i + 1 == currentText.length, currentStep); if (!(out)) { return; } if (out == "*") { continue; } textOut.push(out); } textOut = textOut.join("\n"); safaribot.sendHtmlMessage(src, currentText, safchan); safari.saveGame(player); }; this.storyTextParse = function(src, player, text, final, step) { var out; text = this.storyReadVariables(player, text); if (text == "::save::") { text = "Saving progress... Your progress has been saved! Would you like to " + link("/storygo " + (step + 1), "«Continue»", true) + " or " + link("/closestory ", "Quit") + " and return later?"; player.story.savePoints[player.story.currentChapter] = step; player.story.onQuit.demega = []; //Once saved there is no reason to keep this here since you will have cleared the last check required by it return text; } if (text.indexOf("spk::") == 0) { text = text.split("::"); var speaker = text[1]; text = text[2]; if (storyBase64.hasOwnProperty(speaker)) { text = " " + text; } } if (text.indexOf("var::") == 0) { text = text.split("::"); var entry = text[1]; player.story.variables[entry] = text[2]; text = "*"; } if (text.indexOf("storyboxadd::") == 0) { text = text.split("::"); text.shift(); out = []; for (var i = 0; i < text.length; i++) { player.story.box.push(parseInt(text[i], 10)); out.push(poke(parseInt(text[i]))); player.story.box.push(parseInt(text[i], 10)); } this.storyQuickSave(src, step); text = "Added " + out.join(",") + " to your Story Mode team."; } if (text.indexOf("storyboxremove::") == 0) { text = text.split("::"); text.shift(); out = []; var kept = []; for (var i = 0; i < player.story.box.length; i++) { if (text.contains(player.story.box[i]+"")) { this.removePokemon(sys.id(player.id), player.story.box[i]); out.push(parseInt(player.story.box[i], 10)); continue; } kept.push(parseInt(player.story.box[i], 10)); } player.story.box = kept; this.storyQuickSave(src, step); text = "Removed " + out.join(",") + " from your Story Mode team. Bye-bye, " + out.join(",") + "!"; } if (text.indexOf("party::") == 0) { text = text.split("::"); text.shift(); var newparty = []; for (var i = 0; i < text.length; i++) { newparty.push(parseInt(text[i], 10)); } text = this.storyLoadParty(src, newparty); if (!(text)) { return false; } this.storyRenewParty(src); } if (text.indexOf("freeparty::") == 0) { text = text.split("::"); text.shift(); var newparty = []; for (var i = 0; i < text.length; i++) { newparty.push(parseInt(text[i], 10)); } text = this.storyLoadParty(src, newparty); if (!(text)) { return false; } this.storyRenewParty(src); } if (text.indexOf("renewparty::") == 0) { this.storyRenewParty(src); text = "Party HP has been restored."; } if (text.indexOf("silentrenewparty::") == 0) { this.storyRenewParty(src); text = "*"; } if (text.indexOf("evolve::") == 0) { player.story.state = "Evolve"; text = text.split("::"); text.shift(); var canEvolve = []; for (var i = 0; i < text.length; i++) { canEvolve.push(parseInt(i, 10)); } player.story.canEvolve = canEvolve; text = "*"; } if (text.indexOf("mega::") == 0) { player.story.state = "Mega"; text = text.split("::"); text.shift(); var canMega = []; for (var i = 0; i < text.length; i++) { canMega.push(parseInt(i, 10)); } player.story.canEvolve = canMega; text = "*"; } if (text.indexOf("battle::") == 0) { text = text.split("::"); var npcname = text[1]; var npcparty = text[2].split(","); var npcparty2 = []; for (var i = 0; i < npcparty.length; i++) { npcparty2.push(parseInt(npcparty[i], 10)); } var npcpow = 1.2; var npcselect2 = {}; var npcbias = []; var isMarathon = false; if (text.length > 3) { npcpow = text[3]; } if (text.length > 4) { npcselect = text[4].split(","); for (var i = 0; i < npcselect.length; i++) { npcselect2[npcselect[i]] = true; } } if (text.length > 5) { npcbias = text[5]; } if (text.length > 5) { npcbias = text[5]; } if (text.length > 6) { isMarathon = text[6] == "true" ? true : false; } var postBattle = function(name, isWinner, hpLeft, args, viewers, extraArgs) { var player = getAvatarOff(name), e; var id = sys.id(name); player.story.state = "Reading"; if (!(isWinner)) { player.story.step = -5; } safari.saveGame(player); }; var npc = { party: npcparty2, name: npcname, powerBoost: npcpow, bias: npcbias, select: npcselect2, postBattle: postBattle }; var battle = new Battle2(src, npc, { cantWatch: true, winMsg: winMsg + link("/closestory", " «Quit»") , loseMsg: loseMsg + link("/storygo " + (step + 1), " »» Next"), marathon: isMarathon, isStory: true }, null, null, npcselect); player.story.state = "Battle"; currentBattles.push(battle); return "Battle! VS: " + npcname + ""; } if (final) { if (text.indexOf("opt::") == 0) { var textOptions = text.split("::"); var t, o, textOut = "", validJumps = []; for (var i = 1; i < textOptions.length; i++) { t = textOptions[i].split("||"); o = parseInt(t[1], 10); t = t[0]; textOut += link("/storygo " + (o), "«" + t + "»", true) + " "; validJumps.push(o); } player.story.validJumps = validJumps; return textOut; } else { if (text == "*") { text = ""; } text += link("/storygo " + (step + 1), "»» Next", true); player.story.validJumps = []; } } return text; }; this.storyReadVariables = function(player, text) { text = text.replace("~Poke1~", player.story.variables.poke1); text = text.replace("~Poke2~", player.story.variables.poke2); text = text.replace("~Poke3~", player.story.variables.poke3); text = text.replace("~Champ1~", player.story.variables.champ1); text = text.replace("~Champ2~", player.story.variables.champ2); return text; }; this.storyRenewParty = function(player) { player.story.partyHP = {}; for (var a in player.story.party) { player.story.partyHP[player.party[a]] = 1; } }; this.storyLoadParty = function(player, party) { var out = [], text; for (var i = 0; i < party.length; i++) { if (!(player.story.box.contains(party[i]))) { out.push(poke(party[i])); } } if (out.length > 0) { text = "You need to have at least one " + out.join(" and ") + " to continue in the story. Use " + link("/closestory ", "«Quit»") + " to leave and return once you have the required Pokémon."; safaribot.sendHtmlMessage(sys.id(player.id), text, safchan); return false; } player.story.party = party; player.party = party; this.saveGame(player); text = "Loaded party " + readable(player.story.party.map(poke), "and") + "!"; return text; }; this.storyCreateWild = function(src, dexNum, amt, verb, fixedbst, multiplier) { var player = getAvatar(src); var num = parseInt(dexNum, 10); var pokeName = poke(num); if (!(amt)) { amt = 1; } player.story.wildPokemon.id = num; player.story.wildPokemon.amt = amt; if (!(verb)) { verb = "appeared"; } var term = amt >= 4 ? "horde of " : ["", "pair of ", "group of "][amt-1]; var bst = getBST(num); if (fixedbst) { player.story.wildPokemon.bst = fixedbst; } else { player.story.wildPokemon.bst = bst; } if (multiplier) { //Catch rate multiplier player.story.wildPokemon.multiplier = multiplier; } else { player.story.wildPokemon.multiplier = 1; } sys.sendHtmlMessage(src, "
A " + term + "wild " + pokeName + " " + verb + "! (BST: " + bst + ")
" + pokeInfo.sprite(num) + "
(Note: This Pokémon is only visible to you and will flee if you leave the channel! Any ball you throw will be directed at this Pokémon.)

", safchan); ballMacro(src, true); }; this.storyThrow = function(src, data) { var player = getAvatar(src); var pokeNum = player.story.wildPokemon; var pokeName = poke(pokeNum); var ballAmt = player.balls.safari + player.balls.great + player.balls.ultra; var currentTime = now(); var ball = itemAlias(data); if (!isBall(ball)) { ball = "safari"; } if (player.cooldowns.ball > currentTime) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.ball) + " before throwing a ball!", safchan); } if (cantBecause(src, null, ["item"], ball)) { safaribot.sendHtmlMessage(src, "Try throwing a Safari Ball with " + link("/catch"), safchan); return; } var fullBall = finishName(ball); player.balls[ball] -= 1; var finalChance = safari.computeCatchRate(src, ball); var cooldown = itemData[ball].cooldown; var rng = Math.random(); if (rng < finalChance || ballBonus == 255) { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Gotcha! " + pokeName + " was caught with " + an(fullBall) + "! You have " + plural(player.balls[ball], fullBall) + " left!", safchan); player.story.wildPokemon.amt -= 1; player.records.pokesCaught += 1; cooldown *= 2; if (player.story.wildPokemon.amt == 0) { player.story.state = "Reading"; safari.storyStep(src, player.story.step + 1); sys.sendMessage(src, "", safchan); } else { safaribot.sendMessage(src, "There are " + player.story.wildPokemon.amt + " " + pokeName + " left!", safchan); sys.sendMessage(src, "", safchan); } } else { if (rng < finalChance + 0.1) { safaribot.sendHtmlMessage(src, "Gah! It was so close, too!", safchan); } else if (rng < finalChance + 0.2) { safaribot.sendHtmlMessage(src, "Aargh! Almost had it!", safchan); } else if (rng < finalChance + 0.3) { safaribot.sendHtmlMessage(src, "Aww! It appeared to be caught!", safchan); } else { safaribot.sendHtmlMessage(src, "Oh no! The " + pokeName + " broke free!", safchan); } if (ballAmt <= 5) { safaribot.sendHtmlMessage(src, "You only have " + ballAmt + " usable Poké Balls left! If you run out, you can " + link("/closestory", "«Quit»") + ".", safchan); } } player.cooldowns.ball = currentTime + cooldown; safari.saveGame(player); }; this.storyEvolve = function(src, type, info, data) { var player = getAvatar(src); if (player.story.state.toLowerCase !== type.toLowerCase()) { if (player.story.state == "Mega") { safaribot.sendHtmlMessage(src, "You must perform a Mega Evolution!", safchan); } else { safaribot.sendHtmlMessage(src, "You must perform a regular evolution!", safchan); } return; } if (!(player.story.canEvolve.contains(info.num))) { safaribot.sendHtmlMessage(src, "You cannot evolve that Pokémon now!", safchan); return; } var evolveTo = getPossibleEvo(info, data); if (player.story.state == "Evolve") { this.evolvePokemon(src, info, evolvedId, "evolved into", false, false, true); player.pokemon.box.splice(player.story.box.indexOf(id), 1, evolvedId); safaribot.sendMessage(src, "You used " + an(finishName("rare")) + " on " + info.name + " to evolve them into " + poke(evolvedId) + "!", safchan); } else { this.evolvePokemon(src, info, evolvedId, "Mega Evolved into", false, false, true); player.pokemon.box.splice(player.story.box.indexOf(id), 1, evolvedId); player.megaTimers.push({ id: evolvedId, expires: now() + hours(duration), to: id }); safaribot.sendMessage(src, "You used " + an(finishName("mega")) + " on " + info.name + " to evolve them into " + poke(evolvedId) + "! They will revert after " + duration + " hours!", safchan); player.story.onQuit.demega.push(evolvedId); } player.state = "Reading"; this.saveGame(player); this.storyStep(src, player.story.step + 1); }; /* Items */ this.throwBait = function (src, commandData, command, golden, hax, iterations, deluxe) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (player.tutorial.inTutorial && player.tutorial.step === 2) { safari.tutorialBait(src); return; } var isPreparing = preparationPhase > 0 && (!preparationFirst || sys.name(src).toLowerCase() !== preparationFirst); var persist = player.options.persistentBait; var messBait = command; var mess = "[Track] " + sys.name(src) + " is using /" + messBait + " " + commandData + (persist ? " [Persist]" : ""); if (!isPreparing) { this.trackMessage(mess, player); } var item; if (deluxe) { item = "deluxe"; } else { item = golden ? "golden" : "bait"; } var baitName = finishName(item); if (cantBecause(src, "throw " + baitName, ["contest", "auction", "battle", "item", "event", "pyramid", "baking", "tutorial"], item)) { return; } if (contestCount > 0) { // special case for people who opt out of the contest safaribot.sendMessage(src, "You can't throw " + baitName + " during a Contest!", safchan); return; } if (isPreparing && player.options.baitScramble) { safari.throwBall(src, commandData, false, false, (golden ? "g" : "")+"bait", true); return; } if (cantBecause(src, "throw " + baitName, ["wild"])) { return; } if (contestCooldown <= 13) { safaribot.sendMessage(src, "A Contest is about to start, the Pokémon will run away if you throw " + an(baitName) + " now!", safchan); return; } if (player.pokemon.length >= getPerkBonus(player, "box")) { safaribot.sendMessage(src, "You can't catch any new Pokémon because all your boxes are full! Please buy a new box with /buy.", safchan); return; } var ballUsed = itemAlias(commandData, true); if (!isBall(ballUsed) || player.balls[ballUsed] === 0) { ballUsed = (player.balls[player.options.favoriteBall] > 0 ? player.options.favoriteBall : "safari"); } else { ballUsed = isBall(commandData.toLowerCase()) ? commandData.toLowerCase() : "safari"; ballUsed = toUsableBall(player, ballUsed); } if (!ballUsed) { safaribot.sendMessage(src, "If you throw " + an(baitName) + " now, you will have no way to catch a Pokémon because you are out of balls!", safchan); return; } if (!(golden || deluxe) && lastBaiters.indexOf(sys.name(src).toLowerCase()) !== -1) { safaribot.sendHtmlMessage(src, "You just threw some " + baitName + " not too long ago. Let others have a turn! " + (baitCooldown > 0 ? "[Global cooldown: " + plural(baitCooldown, "second") + "] " : " ") + "[" + link("/" + command, "Try Again") + "]", safchan); return; } player.cooldowns.lastBaits.push(now()); if (player.cooldowns.lastBaits.length > 5) { player.cooldowns.lastBaits.shift(); } var list = player.cooldowns.lastBaits; var l = list.length - 1; var slip = 0; if (l >= 4 && list[l] - list[l-4] < 5000) { slip = 4; } else if (l >= 3 && list[l] - list[l-3] < 3900) { slip = 3; } else if (l >= 2 && list[l] - list[l-2] < 2800) { slip = 2; } else if (l >= 1 && list[l] - list[l-1] < 1800) { slip = 1; } if (!(hax)) { /*if (slip) { var n = now(), amt = sys.rand(2, 3 + slip) * 1000; player.cooldowns.bait = (player.cooldowns.bait > n ? player.cooldowns.bait : n) + amt; safaribot.sendMessage(src, "The " + baitName + " you were preparing to throw slipped from your hand! You went to catch it and now need to wait " + timeLeftString(player.cooldowns.bait) + " to throw again!", safchan); return; }*/ if (golden) { if (goldenBaitCooldown > 0) { safaribot.sendHtmlMessage(src, "Please wait " + plural(goldenBaitCooldown, "second") + " before trying to attract another Pokémon with " + an(baitName) + "! [" + link("/" + command + " " + ballUsed, "Try Again") + "]", safchan); return; } } else if (deluxe) { if (deluxeBaitCooldown > 0) { safaribot.sendHtmlMessage(src, "Please wait " + plural(deluxeBaitCooldown, "second") + " before trying to attract another Pokémon with " + an(baitName) + "! [" + link("/" + command + " " + ballUsed, "Try Again") + "]", safchan); return; } } else { if (baitCooldown > 0) { if (player.freebaits <= 0) { safaribot.sendHtmlMessage(src, "Please wait " + plural(baitCooldown, "second") + " before trying to attract another Pokémon with " + an(baitName) + "! [" + link("/" + command + " " + ballUsed, "Try Again") + "]", safchan); return; } else { player.freebaits--; } } } if (player.cooldowns.bait > now()) { safaribot.sendHtmlMessage(src, "You can't use " + baitName + " now! Please wait " + timeLeftString(player.cooldowns.bait) + " before throwing again! [" + link("/" + command + " " + ballUsed, "Try Again") + "]", safchan); return; } } var perkBonus = getPerkBonus(player, "honey"); var finalChance = itemData[item].successRate + perkBonus + this.getFortune(player, "honey", 0); var baitBoostAbilities = [35, 267, 298]; // Illuminate, Lingering Aroma, Supersweet Syrup for (var i = 0; i < baitBoostAbilities.length; i++) { if (canHaveAbility(player.party[0], baitBoostAbilities[i])) { safaribot.sendMessage(src, "Your {0}'s {1} helps attract wild Pokémon!".format(poke(player.party[0], true), abilityOff(baitBoostAbilities[i])), safchan); finalChance *= 1.3; break; } } if (canHaveAbility(player.party[0], abilitynum("Stench"))) { safaribot.sendMessage(src, "Your {0} is emitting a repugnant odor due to its Stench that seems very unappealing to wild Pokémon...".format(poke(player.party[0], true)), safchan); finalChance *= 0.8; } var rollCap = persist ? 100 : 1; var used = 0; while (!currentPokemon && rollCap > 0 && player.balls[item] > 0) { rollCap -= 1; used += 1; player.balls[item] -= 1; if (deluxe) { player.records.deluxeBaitUsed += 1; } else { player.records[golden ? "goldenBaitUsed" : "baitUsed"] += 1; } var playerDisplayName = sys.name(src); if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { playerDisplayName = "Some stealthy ninja"; } if (ballUsed == "spy") { playerDisplayName = "Some stealthy person" } if (chance(finalChance)) { var teraItems = false; if (!(deluxe)) { var showTheme = player.mushroomDeadline > 0 && ballUsed !== "spy" && !safari.hasCostumeSkill(player, "permanentStealthThrow") ? " from the " + themeName(player.mushroomTheme) + " theme" : ""; safaribot.sendAll(playerDisplayName + " left {0} out. The {1} attracted a wild Pokémon".format(plural(used, baitName), used > 1 ? es(baitName) : baitName) + showTheme + "!", safchan); } player.records.baitAttracted += 1; if (!golden && !deluxe) { if (lastBaiters.length >= lastBaitersAmount) { lastBaiters.shift(); } lastBaiters.push(sys.name(src).toLowerCase()); } if (iterations) { var out = "Spawns (" + iterations + " iterations): "; var totalBST = 0; var rareCount = 0; for (x = 0; x < iterations; x++) { var where = player.mushroomDeadline > 0 ? player.mushroomTheme : null; var spawn = safari.createWild(null, null, 1, null, player.party[0], player, null, (player.truesalt >= now() ? false : golden), where, true); if (isRare(spawn)) { out += "" + pokePlain(spawn) + " "; rareCount++; } else { out += pokePlain(spawn) + " "; } totalBST += getBST(spawn); } safaribot.sendHtmlMessage(src, "Theme: " + where, safchan); safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Rare Count: " + rareCount, safchan); safaribot.sendHtmlMessage(src, "Average BST: " + (totalBST/iterations), safchan); safaribot.sendHtmlMessage(src, out, safchan); return; } else { currentBaiter = player.id; var p = player.nextSpawn; if (p.pokemon.num && !deluxe) { var where = player.mushroomDeadline > 0 ? player.mushroomTheme : null; safari.createWild(p.pokemon.num, p.pokemon.shiny, p.amt, null, null, player, p.disguise, golden ? "golden" : "bait", where); p.pokemon = p.disguise = {}; p.amt = 1; } else { if (deluxe) { var mon = 0, val = (100 * Math.random()); if (player.deluxeBait.inedible > 0) { if (chance(player.deluxeBait.inedible)) { if (rollCap === 0 || player.balls[item] === 0) safaribot.sendAll(playerDisplayName + " left {0} out... but nothing showed up.".format(plural(used, baitName), used > 1 ? es(baitName) : baitName), safchan); continue; } } safaribot.sendAll(playerDisplayName + " left {0} out. The {1} attracted a wild Pokémon!".format(plural(used, baitName), used > 1 ? es(baitName) : baitName), safchan); if (val < (player.deluxeBait.rares.rate)) { mon = player.deluxeBait.rares.list.random(); } else if (val < (player.deluxeBait.uncommons.rate + player.deluxeBait.rares.rate)) { mon = player.deluxeBait.uncommons.list.random(); } else { mon = player.deluxeBait.commons.list.random(); } safari.createWild(mon, null, 1, null, null, player, null, false, null); } else { var teraSuccess = false; if (player.teraActive) { player.balls.teraorb -= 1; var icon = ""; player.records.teraOrbsDeployed += 1; if (chance(itemData.teraorb.bonusRate)) { teraSuccess = true; player.records.teraOrbsSucceeded += 1; sys.sendHtmlAll("
" + icon + "
{0} charged up and threw their {1}! The wild Pokémon begins to crystallize... and takes on a resplendent shine!
".format(playerDisplayName, finishName("teraorb")), safchan); } else { sys.sendHtmlAll("
" + icon + "
{0} charged up and threw their {1}! The wild Pokémon begins to crystallize... but the crystals shatter!
".format(playerDisplayName, finishName("teraorb")), safchan); teraItems = true; } player.teraActive = false; sys.appendToFile(mythLog, now() + "|||" + sys.name(src) + "::used their Tera Orb and " + (teraSuccess ? "succeeded" : "failed") + "::\n"); } var where = player.mushroomDeadline > 0 ? player.mushroomTheme : null; safari.createWild(null, teraSuccess, 1, null, safari.getEffectiveLead(player, true), player, null, golden ? "golden" : "bait", where); } if (golden) { goldenBaitCooldown = itemData[item].successCD + sys.rand(0,9); } else if (deluxe) { deluxeBaitCooldown = itemData[item].successCD + sys.rand(0,3); } else { baitCooldown = successfulBaitCount = itemData[item].successCD + sys.rand(0,9); //player.cooldowns.bait = now() + (itemData[item].failCD + sys.rand(0,4)) * 1000; } } } if (teraItems) { var toGive = []; for (var i = 0; i < teraPity.length; i++) { if (chance(teraPity[i][1])) { toGive.push(teraPity[i][0]); } } safaribot.sendMessage(src, "As the crystals shatter, you scramble to pick up some of the remnants!", safchan); var out = giveStuff(player, toStuffObj(toGive.join()), true); safaribot.sendMessage(src, "You received " + readable(out.gained) + (out.discarded.length > 0 ? " (couldn't receive " + readable(out.discarded) + " due to excess)" : "") + "!", safchan); } safari.throwBall(src, commandData, true); preparationFirst = sys.name(src).toLowerCase(); if (!golden && !deluxe) { lastBaitersDecay = lastBaitersDecayTime; } if (hasType(currentPokemon, "Water") && hasType(currentPokemon, "???")) { player.records.baitWater += 1; } var botd = safari.validDailyBoost(player); this.costumeEXP(player, "bait"); this.missionProgress(player, "bait", currentPokemon, 1, { botd: botd, bait: (golden ? "golden" : "bait") }); if (nextGachaSpawn <= now() + 9 * 1000) { nextGachaSpawn = now() + sys.rand(8, 11) * 1000; } if (player.mushroomDeadline > 0) { player.mushroomDeadline -= 1; if (player.mushroomDeadline === 0) { safaribot.sendHtmlMessage(src, "Your {0} effect expired!".format(finishName("mushroom")), safchan); } } } else { //player.cooldowns.bait = now() + (itemData[item].failCD + sys.rand(0,4)) * (1000 + player.costume == "backpacker" ? 500 : 0); if (rollCap === 0 || player.balls[item] === 0) sendAll(playerDisplayName + " left {0} out... but nothing showed up.".format(plural(used, baitName), used > 1 ? es(baitName) : baitName)); player.records.baitNothing += 1; } } safaribot.sendMessage(src, "You have " + plural(player.balls[item], baitName) + " remaining." + (player.mushroomDeadline > 0 ? " ({0} remaining)".format(plural(player.mushroomDeadline, finishName("mushroom") + " spawn")) : ""), safchan); this.saveGame(player); if (hax) { if (currentPokemon) { safaribot.sendMessage(src, "You glared at the Wild Pokémon until they ran away!", safchan); safaribot.sendAll(sys.name(src) + " scared " + (currentPokemonCount > 1 ? "all " : "") + "the " + poke(currentDisplay, true) + " away!", safchan); if (isRare(currentPokemon)) { sys.appendToFile(mythLog, now() + "|||" + poke(currentPokemon) + "::was " + command + "d by " + sys.name(src) + (contestCount > 0 ? " during " + an(themeName(currentTheme)) + " Contest" : "") + "::\n"); } resetVars(true); } checkUpdate(); return true; } }; this.throwRock = function (src, commandData) { if (!validPlayers("both", src, commandData, "You cannot throw " + an(finishName("rock")) + " at yourself!")) { return; } var player = getAvatar(src); var item = "rock"; if (!(item in player.balls) || player.balls[item] <= 0) { safaribot.sendMessage(src, "You have no " + es(finishName(item)) + "!", safchan); return; } var currentTime = now(); if (player.cooldowns.rock > currentTime) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.rock) + " before trying to a throw another " + finishName(item) + "!", safchan); return; } var rng = Math.random(); var rng2 = Math.random(); var targetName = utilities.non_flashing(commandData.toCorrectCase()); var id = commandData.toLowerCase(); var targetId = sys.id(commandData); var target = getAvatar(targetId); if (target.tutorial.inTutorial) { safaribot.sendMessage(src, "Hey! That's not nice. You shouldn't throw " + an(finishName(item)) + " at someone completing the tutorial!", safchan); return; } /*if (countRepeated(player.rockTargets, id) >= 4) { safaribot.sendMessage(src, "You have been throwing " + es(finishName(item)) + " at this person too often!", safchan); return; }*/ player.balls[item] -= 1; player.records.rocksThrown += 1; player.rockTargets.push(id); if (player.rockTargets.length > 10) { player.rockTargets.shift(); } var success = itemData.rock.successRate + this.getFortune(player, "rockaccuracy", 0); if (player.truesalt >= now() && chance(player.srate)) { success = 0; } var verb = "stunned"; //change to stunned, seasonal change if (rng < success) { if (rng2 < 0.4) { if (this.getFortune(target, "rockshield", 0)) { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "... but it missed!", safchan); player.records.rocksMissed += 1; target.records.rocksDodged += 1; } else { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "! *THUD* A direct hit! " + targetName + " was " + verb + "!", safchan); //target.cooldowns.ball = target.cooldowns.ball > currentTime ? target.cooldowns.ball + itemData.rock.targetCD : currentTime + itemData.rock.targetCD; player.records.rocksHit += 1; target.records.rocksHitBy += 1; safari.missionProgress(player,"rockHit",0,1,{}) } } else if (rng2 < 0.5) { if (this.getFortune(target, "rockshield", 0)) { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "... but it missed!", safchan); player.records.rocksMissed += 1; target.records.rocksDodged += 1; } else { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "! " + targetName + " evaded, but their " + poke(safari.getEffectiveLead(target, true), true) + " got hit and " + verb + "!", safchan); //target.cooldowns.ball = target.cooldowns.ball > currentTime ? target.cooldowns.ball + Math.floor(itemData.rock.targetCD/2) : currentTime + Math.floor(itemData.rock.targetCD/2); player.records.rocksHit += 1; target.records.rocksHitBy += 1; } } else if (rng2 < 0.55) { var dropped = sys.rand(4, 10); if (target.money < dropped) { dropped = target.money || 1; } safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but only hit their wallet! " + sys.name(src) + " then picked up the $" + dropped + " dropped by " + targetName + "!", safchan); if (player.money + dropped> moneyCap) { safaribot.sendMessage(src, "But you could only keep $" + (moneyCap - player.money) + "!", safchan); player.money = moneyCap; } else { safaribot.sendMessage(src, "You received $" + dropped + "!", safchan); player.money += dropped; } player.records.rocksWalletHit += 1; player.records.rocksWalletEarned += dropped; target.records.rocksWalletHitBy += 1; target.records.rocksWalletLost += dropped; safaribot.sendMessage(targetId, "You lost $" + dropped + "!", safchan); target.money = target.money - dropped < 0 ? 0 : target.money - dropped; } else if (rng2 < 0.56) { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but only hit their Itemfinder, which caused the Itemfinder to get slightly discharged!", safchan); if (target.balls.permfinder > 1) { safaribot.sendMessage(targetId, "You lost " + plural(1, finishName("recharge")) + "!", safchan); target.balls.permfinder -= 1; } player.records.rocksItemfinderHit += 1; target.records.rocksChargesLost += 1; } else { var parts = ["right leg", "left leg", "right arm", "left arm", "back"]; safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "! The " + finishName(item) + " hit " + targetName + "'s " + parts[sys.rand(0, parts.length)] + "!", safchan); player.records.rocksHit += 1; target.records.rocksHitBy += 1; } } else { if (rng2 < itemData.rock.bounceRate + (preparationPhase > 0 ? 0.4 : 0)) { //Seasonal change safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but it hit a wall and bounced back at " + sys.name(src) + "! *THUD* That will leave a mark on " + sys.name(src) + "'s face and pride!", safchan); // safaribot.sendAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but it broke apart before it was thrown and covered " + sys.name(src) + " in snow! That will leave a mark on " + sys.name(src) + "'s face and pride!", safchan); //player.cooldowns.ball = currentTime + itemData.rock.bounceCD; player.records.rocksBounced += 1; } else if (rng2 < 0.25) { //seasonal change // safaribot.sendAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but " + targetName + " saw it coming and caught the " + finishName(item) + " with their mitten-covered hands!", safchan); safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but " + targetName + " saw it coming and caught the " + finishName(item) + " with their bare hands!", safchan); if (target.balls.rock < getCap("rock")) { target.balls.rock += 1; safaribot.sendMessage(targetId, "You received " + plural(1, item) + "!", safchan); } else { safaribot.sendMessage(targetId, "But you couldn't keep the " + finishName(item) + " because you already have " + getCap("rock") + "!", safchan); } player.records.rocksMissed += 1; target.records.rocksCaught += 1; } else if (rng2 < 0.35) { var dropped = sys.rand(10, 17); if (player.money < dropped) { dropped = player.money || 1; } safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but missed and broke their house's window! " + sys.name(src) + " had to pay $" + dropped + " to " + targetName + "!", safchan); if (target.money + dropped> moneyCap) { safaribot.sendMessage(targetId, "But you could only keep $" + (moneyCap - target.money) + "!", safchan); target.money = moneyCap; } else { safaribot.sendMessage(targetId, "You received $" + dropped + "!", safchan); target.money += dropped; } safaribot.sendMessage(src, "You lost $" + dropped + "!", safchan); player.money = player.money - dropped < 0 ? 0 : player.money - dropped; player.records.rocksMissedWindow += 1; player.records.rocksWindowLost += dropped; target.records.rocksDodgedWindow += 1; target.records.rocksWindowEarned += dropped; safari.missionProgress(player,"rockWindow",0,1,{}) } else if (rng2 < 0.45) { var onChannel = sys.playersOfChannel(safchan); var randomTarget = onChannel[sys.rand(0, onChannel.length)]; if (randomTarget != src && randomTarget != targetId && getAvatar(randomTarget)) { safaribot.sendHtmlAll(sys.name(src) + " tried to throw " + an(finishName(item)) + " at " + targetName + ", but failed miserably and almost hit " + utilities.non_flashing(sys.name(randomTarget)) + "!", safchan); } else { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "... but it missed!", safchan); } player.records.rocksMissed += 1; target.records.rocksDodged += 1; } else if (rng2 < 0.5) { var extraThrown = "safari"; if (player.balls[extraThrown] > 0) { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + ", but it missed... To make things worse, " + sys.name(src) + " also dropped " + an(finishName(extraThrown)) + " that was stuck together with the " + finishName(item) + "!", safchan); safaribot.sendMessage(src, "You lost 1 " + finishName(extraThrown) + "!", safchan); player.balls[extraThrown] -= 1; } else { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "... but it missed!", safchan); } player.records.rocksMissed += 1; target.records.rocksDodged += 1; } else if (rng2 < 0.53) { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "! " + targetName + " evaded, but their Itemfinder got hit! The Itemfinder reacted to the impact and gained 1 extra charge!!", safchan); safaribot.sendMessage(targetId, "You received " + plural(1, finishName("recharge")) + "!", safchan); target.balls.permfinder += 1; player.records.rocksMissed += 1; target.records.rocksChargesGained += 1; } else { safaribot.sendHtmlAll(sys.name(src) + " threw " + an(finishName(item)) + " at " + targetName + "... but it missed!", safchan); player.records.rocksMissed += 1; target.records.rocksDodged += 1; } } player.cooldowns.rock = currentTime + itemData.rock.throwCD; this.saveGame(player); this.saveGame(target); }; this.useStick = function (src, commandData) { var sName = finishName("stick"); if (!validPlayers("both", src, commandData, "You cannot poke yourself with " + an(sName) + "!")) { return; } var player = getAvatar(src); var item = "stick"; if (!(item in player.balls) || player.balls[item] <= 0) { safaribot.sendMessage(src, "You have no " + es(cap(item)) + "!", safchan); return; } var currentTime = now(); if (player.cooldowns.stick > currentTime) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.stick) + " before using your " + sName + "!", safchan); return; } var targetName = utilities.non_flashing(commandData.toCorrectCase()); safaribot.sendHtmlAll(sys.name(src) + " poked " + targetName + " with their " + sName + ".", safchan); player.cooldowns.stick = currentTime + itemData.stick.cooldown; this.saveGame(player); }; this.giveBurnHeal = function (src, commandData) { var item = "burn"; var sName = finishName(item); var player = getAvatar(src); var split = commandData.split(":"); var targetInput = split[0]; var targetId = sys.id(targetInput); var confirm = split[1] === "confirm"; var target = getAvatar(targetId); if (!validPlayers("both", src, targetInput, "You cannot give " + an(sName) + " to yourself!")) { return; } if (!(item in player.balls) || player.balls[item] <= 0) { safaribot.sendMessage(src, "You have no " + sName + "!", safchan); return; } var currentTime = now(); if (player.cooldowns.burn > currentTime) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.burn) + " before giving someone " + an(sName) + "!", safchan); return; } var targetName = utilities.non_flashing(targetInput.toCorrectCase()); if (target.brilliantAura) { safaribot.sendMessage(src, "Doesn't seem like " + targetName + " will need " + an(sName) + " for the rest of today!", safchan); return; } else if (target.burningAura) { if (player.brilliantAura) { safaribot.sendMessage(src, "You already have a Brilliant Aura, give someone else a chance!", safchan); return; } sendAll("", true, true); safaribot.sendHtmlAll(sys.name(src) + " purified " + targetName + "'s " + typeIcon("Fire", "Burning Aura") + " into a " + typeIcon("Fairy", "Brilliant Aura") + " with " + an(sName) + "!", safchan); target.brilliantAura = true; safaribot.sendHtmlMessage(targetId, "The Brilliant Aura granted you the following bonuses: " + safari.describeAuraEffects(target) + "!", safchan); safaribot.sendHtmlMessage(targetId, "As thanks, you shared the power of your Brilliant Aura with " + sys.name(src) + "!", safchan); player.brilliantAura = true; safaribot.sendHtmlMessage(src, "As thanks, " + targetName + " shared the power of their Brilliant Aura with you!", safchan); safaribot.sendHtmlMessage(src, "The Brilliant Aura granted you the following bonuses: " + safari.describeAuraEffects(player) + "!", safchan); sendAll("", true, true); player.records.burnAuraGaveHeal += 1; target.records.burnAuraReceivedHeal += 1; sys.appendToFile(miscLog, now() + "|||" + sys.name(src) + "|||purified " + targetName + " with Burn Heal, both received a Brilliant Aura.\n"); if (player.auraExpiry <= now()) { player.auraExpiry = now() + hours(auraHours); } if (target.auraExpiry <= now()) { target.auraExpiry = now() + hours(auraHours); } } else { if (target.balls[item] >= getCap(item)) { safaribot.sendMessage(src, targetName + " cannot carry any more " + es(sName) + "!", safchan); return; } if (!confirm) { safaribot.sendHtmlMessage(src, targetName + " is not currently under the effects of a Burning Aura, type " + link("/burn " + split[0] + ":confirm") + " if you're sure you want to give them " + an(sName) + ".", safchan); return; } sendAll("", true, true); safaribot.sendAll(sys.name(src) + " gave " + an(sName) + " to " + targetName + "!", safchan); safaribot.sendMessage(targetId, "You received " + plural(1, item) + "!", safchan); sendAll("", true, true); target.balls[item] += 1; } player.cooldowns.burn = currentTime + itemData.burn.cooldown; player.balls[item] -= 1; player.records.burnGiven += 1; target.records.burnReceived += 1; player.burnLastUsed = now(); this.saveGame(player); this.saveGame(target); }; this.gachapon = function (src, commandData, freeUse) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (player.tutorial.inTutorial && player.tutorial.step === 5) { safari.tutorialGacha(src); return; } if (cantBecause(src, "use the Gachapon Machine", ["item", "auction", "battle", "pyramid", "event", "tutorial", "baking"], (freeUse ? null : "gacha"))) { return; } var currentTime = now(); if (!freeUse && player.cooldowns.gacha > currentTime) { safaribot.sendHtmlMessage(src, "Strict gambling regulations require you to wait " + timeLeftString(player.cooldowns.gacha) + " before you can use the Gachapon Machine again! " + link("/gacha", "[Try Again]"), safchan); return; } var pulls = Math.min(this.getFortune(player, "gachaburn", 1), player.balls.gacha); while (pulls > 0) { pulls -= 1; player.records.gachasUsed += 1; gachaJackpot += 1; var reward = randomSample(gachaItems); if (chance(this.getFortune(player, "freegacha", 0)) || freeUse) { safaribot.sendHtmlMessage(src, "Gacha-PON! The Gachapon Machine has dispensed an item capsule... And it didn't even consume your ticket! [Remaining Tickets: " + player.balls.gacha + "] " + link("/gacha", "[Use Again]"), safchan); } else { player.balls.gacha -= 1; safaribot.sendHtmlMessage(src, "Gacha-PON! The Gachapon Machine has dispensed an item capsule. [Remaining Tickets: " + player.balls.gacha + "] " + link("/gacha", "[Use Again]"), safchan); } //Variable for higher quantity rewards later. Make better later maybe? var amount = 1; var rng = Math.random(); if (this.hasCostumeSkill(player, "betterGacha") && (chance(0.1) && (chance(this.getCostumeLevel(player)-5)/15))) { rng = Math.min(rng, Math.random()); } if (this.hasCostumeSkill(player, "betterGacha")) { while (reward == "rock") { reward = randomSample(gachaItems); } } if (rng < 0.01) { amount = 4; } else if (rng < 0.05) { amount = 3; } else if (rng < 0.15) { amount = 2; } if (chance(this.getFortune(player, "extragacha", 0))) { amount += 1; } var giveReward = true; if (player.truesalt > now() && chance(player.srate)) { reward = ["rock", "wild", "rock", "rock", "rock", "rock", "wild", "safari"].random(); giveReward = false; } var masterCap; switch (reward) { case "master": { if (player.balls[reward] >= getCap("master")) { /*safaribot.sendHtmlAll("JACKP-- Wait a second... " + html_escape(sys.name(src)) + "'s " + finishName("master") + " turned out to be a simple " + finishName("safari") + " painted to look like " + an(finishName("master")) + "! What a shame!", safchan); safaribot.sendMessage(src, "You wiped the paint off of the ball and pocketed " + plural(1, "safari") + " for your troubles.", safchan); reward = "safari"; amount = 1; player.records.masterballsWon += 1;*/ giveReward = false; masterCap = true; } safaribot.sendHtmlAll("JACKPOT! " + html_escape(sys.name(src)) + " just won " + an(finishName("master")) + " from the Gachapon Machine!", safchan); if (!masterCap) { safaribot.sendMessage(src, "You received " + an(finishName(reward)) + ".", safchan); } amount = 1; player.records.masterballsWon += 1; } break; case "bait": { safaribot.sendMessage(src, "A delicious smell wafts through the air as you open your capsule. You received " + plural(amount, reward) + ".", safchan); } break; case "rock": { var snowball = finishName("rock") === "Snowball"; safaribot.sendMessage(src, "A " + (snowball ? "wet splashing sound" : "loud clunk" ) + " comes from the machine. Some prankster put " + (snowball ? "snow" : es(finishName("rock"))) + " in the Gachapon Machine! You received " + plural(amount, finishName(reward)) + ".", safchan); } break; case "wild": { giveReward = false; if (currentPokemon || contestCount > 0 || contestCooldown <= 13) { giveReward = true; reward = "safari"; amount = 1; safaribot.sendMessage(src, "Bummer, only " + an(finishName(reward)) + ". You received " + plural(amount, reward) + ".", safchan); } else { var spawn = true; var spawnHorde = amount > 1; var ballUsed = itemAlias(commandData, true); if (!isBall(ballUsed) || player.balls[ballUsed] === 0) { ballUsed = (player.balls[player.options.favoriteBall] > 0 ? player.options.favoriteBall : "safari"); } this.trackMessage("[Track] " + sys.name(src) + " is using /gacha " + commandData, player); var playerDisplayName = sys.name(src); if (safari.hasCostumeSkill(player, "permanentStealthThrow")) { playerDisplayName = "Some stealthy ninja"; } if (ballUsed == "spy") { playerDisplayName = "Some stealthy person"; } safaribot.sendAll(playerDisplayName + " goes to grab their item from the Gachapon Machine but the noise lured a wild Pokémon!", safchan); if (chance(0.15) || nextGachaSpawn > currentTime || player.cooldowns.bait > currentTime) { safaribot.sendAll("Unfortunately " + (spawnHorde ? "they" : "it") + " fled before anyone could try to catch "+ (spawnHorde ? "them" : "it") + "!", safchan); spawn = false; } if (spawn) { var p = player.nextSpawn; if (p.pokemon.num) { safari.createWild(p.pokemon.num, p.pokemon.shiny, p.amt, null, null, player, p.disguise); p.pokemon = p.disguise = {}; p.amt = 1; } else { if (spawnHorde) { safari.createWild(0, false, amount, 580); } else { safari.createWild(); } } safari.throwBall(src, ballUsed, true); preparationFirst = sys.name(src).toLowerCase(); nextGachaSpawn = currentTime + 23 * 1000; if (baitCooldown <= 9) { baitCooldown = sys.rand(9, 13); } if (goldenBaitCooldown <= 6) { goldenBaitCooldown = sys.rand(6, 11); } if (deluxeBaitCooldown < 6) { deluxeBaitCooldown = 6; } } } } break; case "safari": { amount = 1; safaribot.sendMessage(src, "Bummer, only " + an(finishName(reward)) + ". You received 1 " + finishName(reward) + ".", safchan); } break; case "dust": { amount *= 5; safaribot.sendMessage(src, "You open the capsule to find " + an(finishName("rare")) + "! Unfortunately, some rude player pushes you out of the way causing you to drop it. The " + finishName("rare") + " impacts the ground, shatters, and sends dust flying everywhere. You only manage to scoop up " + plural(amount, finishName(reward)) + ".", safchan); } break; case "gacha": { var jackpot = Math.floor(gachaJackpot/10); safaribot.sendHtmlAll("JACKPOT! " + html_escape(sys.name(src)) + " just won the Gachapon Ticket Jackpot valued at " + jackpot + " tickets!", safchan); amount = jackpot; player.records.jackpotsWon += 1; safaribot.sendMessage(src, "You received " + plural(jackpot, "gacha") + ". ", safchan); gachaJackpot = gachaJackpotAmount; //Reset jackpot for next player } break; case "amulet": case "soothe": case "scarf": case "battery": { amount = 1; //sendAll("Sweet! " + sys.name(src) + " just won " + an(finishName(reward)) + " from Gachapon!", true); safaribot.sendMessage(src, "You received " + an(finishName(reward)) + ".", safchan); } break; case "gem": { amount = 1; safaribot.sendMessage(src, "The Gachapon machine emits a bright flash of light as you reach for your prize. Despite being temporarily blinded, you know you just won " + an(finishName(reward)) + " due to a very faint baaing sound!", safchan); safaribot.sendMessage(src, "You received " + an(finishName(reward)) + ".", safchan); } break; case "cometshard": { amount = 1; safaribot.sendMessage(src, "The Gachapon machine spits an old-looking capsule. After some trouble opening it, you find a mysterious " + finishName(reward) + ".", safchan); } break; case "whtapricorn": case "pearl": case "stardust": case "starpiece": case "bigpearl": case "nugget": case "bignugget": { amount = 1; } /* falls through */ default: safaribot.sendMessage(src, "You received " + plural(amount, reward) + ".", safchan); break; } if (giveReward) { rewardCapCheck(player, reward, amount); } if (masterCap) { safaribot.sendMessage(src, "You received " + an(finishName("fragment")) + ".", safchan); rewardCapCheck(player, "fragment", 1); } } player.cooldowns.gacha = currentTime + itemData.gacha.cooldown; this.saveGame(player); SESSION.global().safariGachaJackpot = gachaJackpot; }; this.getCustomWildItems = function() { return customWildItems = { "@moneyset": ["$" + (Math.max(10, sys.rand(1, 11) * sys.rand(1, 11)))], "@moneyset2": ["$" + (sys.rand(10, 21) * sys.rand(10, 26))], "@apricornset": ["@blkapricorn", "@whtapricorn", "@pnkapricorn", "@ylwapricorn", "@bluapricorn", "@redapricorn", "@grnapricorn"], "@dewset": ["@dew", "@hdew"] }; }; this.candyCostConversion = function(player, val) { //ivysaur is 650, bulbasaur 200, dragonair 699 var out = 0; out += 3 * (val > 600 ? (val - 600) : 0); out += 2 * (val > 400 ? (val - 400) : 0); out += val; if (player) { if (player.costume == "breeder") { out *= 0.88; } if (safari.hasCostumeSkill(player, "evolveCheap")) { out *= (0.99 - (this.getCostumeLevel(player)/100)); } if (safari.hasCostumeSkill(player, "evolveCheap2")) { out *= 0.75; } } out = Math.max(Math.round(out/151), 1); return out; }; this.useCandy = function(src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "evolve a Pokémon"; var isTut = player.tutorial.inTutorial && player.tutorial.step === 8; if (!isTut) { if (cantBecause(src, reason, ["tutorial"])) { return; } } var input = commandData.split(":"); var info = getInputPokemon(input[0]); var starter = input.length > 1 ? input[1].toLowerCase() : "*"; var chooseEvo = input.length > 2 ? input[2].toLowerCase() : "*"; var shiny = info.shiny; var num = info.num; if (!num) { safaribot.sendMessage(src, "Invalid Pokémon!", safchan); return; } if (player.story.inStory) { if (player.story.state == "Evolve") { this.storyEvolve(src, "evolve", info, chooseEvo); return; } else if (cantBecause(src, "use " + es(finishName("rare")), ["story"], "evolve")) { return; } } var id = info.id; if (player.pokemon.indexOf(id) == -1) { safaribot.sendMessage(src, "You do not have that Pokémon!", safchan); return; } if (isTut && poke(id) !== "Pikachu") { tutorMsg(src, "You can only evolve Pikachu during the tutorial. Please do so with " + link("/evolve Pikachu")); return; } var species = evolutions.hasOwnProperty(info.num+"") ? info.num : pokeInfo.species(info.num); if (!(species in evolutions) || evolutions[species].evo === -1) { safaribot.sendMessage(src, "This Pokémon cannot evolve!", safchan); return; } var evoData = evolutions[species]; var candiesRequired = Math.floor((evoData.candies || 300) * (info.shiny ? 1.25 : 1)); var costumed = player.costume === "breeder"; var prev = this.candyCostConversion(false, candiesRequired); var discountRate = (costumed ? costumeData.breeder.rate : 1); candiesRequired = Math.floor(candiesRequired * discountRate); candiesRequired = this.candyCostConversion(player, candiesRequired); if (!["confirm", "starter", "normal"].contains(starter)) { var evo = evoData.evo; safaribot.sendHtmlMessage(src, info.name + " requires " + plural(candiesRequired, "rare") + " to evolve into " + (Array.isArray(evo) ? readable(evo.map(poke), "or") : poke(evo)) + ". " + (costumed ? "[Note: Without " + costumeAlias("breeder", true, true) + " " + plural(prev, "rare") + " are required.]" : ""), safchan); safaribot.sendHtmlMessage(src, "If you really wish to evolve " + info.name + ", type " + link("/evolve " + info.input + ":confirm") + ".", safchan); return; } if (info.input in player.shop) { safaribot.sendMessage(src, "You need to remove this Pokémon from your shop before evolving them!", safchan); return; } if (player.balls.rare < candiesRequired) { safaribot.sendMessage(src, info.name + " requires " + plural(candiesRequired, "rare") + " to evolve!", safchan); return; } var evolveStarter = true; if (player.starter == id) { var count = countRepeated(player.pokemon, id); if (count > 1) { if (starter == "starter") { evolveStarter = true; } else if (starter == "normal") { evolveStarter = false; } else { safaribot.sendMessage(src, "This Pokémon is your starter, but you have more than one! To pick which one you want to evolve, type /evolve " + info.input +":starter or /evolve " + info.input +":normal.", safchan); return; } } } var restrictions = ["auction", "battle", "item", "event", "pyramid"]; //Allow selling of pokemon that are not the lead if the rest of the party doesn't matter at that point if (player.party[0] === id) { restrictions = restrictions.concat(["wild", "contest"]); reason = "evolve your active Pokémon"; } if (cantBecause(src, reason, restrictions, "rare")) { return; } var evolveTo = getPossibleEvo(id, chooseEvo); var evolvedId = shiny ? "" + evolveTo : evolveTo; player.balls.rare -= candiesRequired; if (!isTut) { player.records.pokesEvolved += 1; } this.missionProgress(player, "evolve", id, 1, {}); this.evolvePokemon(src, info, evolvedId, "evolved into", evolveStarter, isTut); var bstval = getBST(evolveTo); var amt = ((bstval + (Math.max((bstval-300) * 2, 0)) + (Math.max((bstval-480) * 4, 0))) * (0.075 + (Math.random() * 0.05))) * 2 * (shiny ? 1.5 : 1); if (this.hasCostumeSkill(player, "extraDust")) { amt *= (1 + (this.getCostumeLevel(player)/30)); } if (this.getFortune(player, "breeder", 0, null, true)) { amt *= (1 + (0.01 * this.getFortune(player, "breeder", 0, null, false))); } amt = Math.round(amt); var g = giveStuff(player, toStuffObj(amt + "@dust")); safaribot.sendMessage(src, "You " + g + " for evolving your Pokémon!", safchan); this.logLostCommand(sys.name(src), "evolve " + commandData, "evolved into " + poke(evolvedId)); if (costumed) { safaribot.sendMessage(src, "Your " + info.name + " only needed to eat " + plural(candiesRequired, "rare") + " before evolving into " + poke(evolvedId) + "!", safchan); } else { safaribot.sendMessage(src, "Your " + info.name + " ate " + plural(candiesRequired, "rare") + " and evolved into " + poke(evolvedId) + "!", safchan); } if (isTut) { advanceTutorial(src, 9); } }; this.useSpray = function(src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "devolve a Pokémon"; var input = commandData.split(":"); var info = getInputPokemon(input[0]); var starter = input.length > 1 ? input[1].toLowerCase() : "*"; var shiny = info.shiny; var num = info.num; if (!num) { safaribot.sendMessage(src, "Invalid Pokémon!", safchan); return; } var id = info.id; if (player.pokemon.indexOf(id) == -1) { safaribot.sendMessage(src, "You do not have that Pokémon!", safchan); return; } if (isMega(info.num)) { safaribot.sendMessage(src, "You cannot devolve a Mega Pokémon!", safchan); return; } var species = evolutions.hasOwnProperty(info.num+"") || devolutions.hasOwnProperty(info.num+"") ? info.num : pokeInfo.species(info.num); if (!(species in devolutions) || devolutions[species] === -1) { safaribot.sendMessage(src, "This Pokémon cannot devolve!", safchan); return; } var evoData = devolutions[species]; var dustRegained = Math.min(Math.floor((evolutions[evoData] ? evolutions[evoData].candies : 300) * (info.shiny ? 1.15 : 1) * 0.5), itemData.dust.cap); var forme = pokeInfo.forme(num); if (forme !== 0) { var tempForme = pokeInfo.calcForme(evoData, forme); if (pokePlain(tempForme).toLowerCase() !== "missingno" && !devolutions.hasOwnProperty(info.num+"")) { // only default to a certain forme if there's no strict devolution match evoData = tempForme; } } if (!["confirm", "starter", "normal"].contains(starter)) { safaribot.sendHtmlMessage(src, info.name + " can devolve into " + poke(evoData) + " with " + an(finishName("spray")) + " to regain " + plural(dustRegained, "dust") + ".", safchan); safaribot.sendHtmlMessage(src, "If you really wish to devolve " + info.name + ", type " + link("/spray " + info.input + ":confirm") + ".", safchan); return; } if (info.input in player.shop) { safaribot.sendMessage(src, "You need to remove this Pokémon from your shop before devolving them!", safchan); return; } var evolveStarter = true; if (player.starter == id) { var count = countRepeated(player.pokemon, id); if (count > 1) { if (starter == "starter") { evolveStarter = true; } else if (starter == "normal") { evolveStarter = false; } else { safaribot.sendMessage(src, "This Pokémon is your starter, but you have more than one! To pick which one you want to devolve, type /spray " + info.input +":starter or /spray " + info.input +":normal.", safchan); return; } } } var restrictions = ["auction", "battle", "item", "event", "pyramid"]; if (player.party[0] === id) { restrictions = restrictions.concat(["wild", "contest"]); reason = "devolve your active Pokémon"; } if (cantBecause(src, reason, restrictions, "spray")) { return; } var evolvedId = shiny ? "" + evoData : evoData; player.balls.spray -= 1; player.records.devolutions += 1; player.records.devolutionDust += dustRegained; this.updateShop(player, "spray"); this.evolvePokemon(src, info, evolvedId, "devolved into", evolveStarter); this.logLostCommand(sys.name(src), "spray " + commandData, "devolved into " + poke(evolvedId)); safaribot.sendMessage(src, "You sprayed " + an(finishName("spray")) + " onto your " + info.name + ", making them devolve into " + poke(evolvedId) + "! You received " + plural(dustRegained, "dust") + " from that!", safchan); rewardCapCheck(player, "dust", dustRegained); this.saveGame(player); }; this.cancelMega = function(src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (cantBecause(src, "cancel Mega Evolution", ["wild", "contest", "auction", "battle", "event", "tutorial", "pyramid"])) { return; } commandData = commandData.split(":"); var pokeInput = typeNull(commandData[0]); var confirm = commandData[1] && commandData[1] === "confirm"; var info = getInputPokemon(pokeInput); if (!info.num) { safaribot.sendMessage(src, "That's not a valid Pokémon!", safchan); return; } if (!isMega(info.num)) { safaribot.sendMessage(src, "This Pokémon is not a Mega Evolution!", safchan); return; } var megaIndex; var num = info.num + (info.shiny ? "" : 0); for (var i = 0; i < player.megaTimers.length; i++) { if (player.megaTimers[i].id === num) { megaIndex = i; break; } } if (megaIndex === undefined) { safaribot.sendMessage(src, "You don't have a " + poke(num, true) + " to revert!", safchan); return; } if (!confirm) { safaribot.sendHtmlMessage(src, "You are about to revert your {0}. Type {1} to confirm.".format(poke(num, true), link("/cancelmega " + poke(num, true) + ":confirm", false, true)), safchan); return; } player.megaTimers[megaIndex].expires = 0; safari.revertMega(src, false, num); safari.saveGame(player); }; this.useMegaStone = function(src, commandData) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (player.story.inStory) { if (player.story.state == "Mega") { this.storyEvolve(src, "mega", commandData); return; } else if (cantBecause(src, "use " + es(finishName("mega")), ["story"], "mega")) { return; } } if (cantBecause(src, "use " + es(finishName("mega")), ["wild", "contest", "auction", "battle", "item", "event", "tutorial", "pyramid"], "mega")) { return; } commandData = typeNull(commandData).split(":"); var pokeInput = commandData[0]; var evoChoice = commandData[1] || "*"; var info = getInputPokemon(pokeInput); var shiny = info.shiny; var num = info.num; if (!num) { safaribot.sendMessage(src, "Invalid Pokémon!", safchan); return; } var id = info.id; if (player.pokemon.indexOf(id) == -1) { safaribot.sendMessage(src, "You do not have that Pokémon!", safchan); return; } if (!(num in megaEvolutions) || isMega(num)) { safaribot.sendMessage(src, "This Pokémon cannot Mega Evolve!", safchan); return; } if (info.input in player.shop) { safaribot.sendMessage(src, "You need to remove this Pokémon from your shop before Mega Evolving them!", safchan); return; } var possibleEvo = megaEvolutions[num]; var evolveTo; if (possibleEvo.length > 1) { if (["1", "x", poke(possibleEvo[0]).toLowerCase()].contains(evoChoice)) { evolveTo = possibleEvo[0]; } else if (["2", "y", poke(possibleEvo[1]).toLowerCase()].contains(evoChoice)) { evolveTo = possibleEvo[1]; } else { safaribot.sendHtmlMessage(src, "This Pokémon has multiple Mega Evolutions! Use {0} to choose the one you want.".format(link("/mega " + pokeInput + ":[X or Y]", false, true)), safchan); return; } } else { evolveTo = possibleEvo[0]; } var evolvedId = shiny ? "" + evolveTo : evolveTo; player.balls.mega -= 1; player.records.megaEvolutions += 1; this.updateShop(player, "mega"); var duration = (itemData.mega.duration * 24 + this.getFortune(player, "extramega", 0)) * this.getAuraEffect(player, "extramega", 1); duration = Math.round(duration); this.evolvePokemon(src, info, evolvedId, "Mega Evolved into", true); this.logLostCommand(sys.name(src), "mega " + commandData, "Mega Evolved into " + poke(evolvedId)); player.megaTimers.push({ id: evolvedId, expires: now() + hours(duration), to: id }); this.saveGame(player); safaribot.sendMessage(src, "You used " + an(finishName("mega")) + " on " + info.name + " to evolve them into " + poke(evolvedId) + "! They will revert after " + duration + " hours! " + itemsLeft(player, "mega"), safchan); }; this.evolvePokemon = function(src, info, evolution, verb, evolveStarter, isTut, isStory) { var player = getAvatar(src); var id = info.id; var count = countRepeated(player.pokemon, id); if (id === player.starter && (count <= 1 || evolveStarter)) { player.starter = evolution; } var wasOnParty = player.party.indexOf(id) !== -1; player.pokemon.splice(player.pokemon.indexOf(id), 1, evolution); if (wasOnParty) { player.party.splice(player.party.indexOf(id), 1, evolution); } var evoInput = getInputPokemon(evolution + (typeof evolution == "string" ? "*" : "")); if (evolution === player.starter && evoInput.input in player.shop) { delete player.shop[evoInput.input]; safaribot.sendMessage(src, evoInput.name + " was removed from your shop!", safchan); } if (isStory) { player.story.party = player.party; } if (isTut || isStory) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, pokeInfo.icon(info.num) + " -> " + pokeInfo.icon(parseInt(evolution, 10)), safchan); safaribot.sendMessage(src, "Your " + info.name + " " + verb + " " + poke(evolution) + "!", safchan); sys.sendMessage(src, "", safchan); } else { var rareEvo = (isRare(info.num, true) || isRare(evolution, true)) && !isMega(evolution) && !isMega(info.num); var canView = sys.playersOfChannel(safchan).filter(function(e) { var p = getAvatar(e); if (!p || rareEvo || p.options.showEvoMessages || e === src) { return true; } return false; }); canView.forEach(function(e) { if (cantBecause(e, false, ["auction", "battle", "event", "pyramid", "baking"], false, true) && e !== src) { return; } if (rareEvo || player.options.broadcastEvoMessages) { // if broadcast enabled or rare evo sys.sendMessage(e, "", safchan); safaribot.sendHtmlMessage(e, pokeInfo.icon(info.num) + " -> " + pokeInfo.icon(parseInt(evolution, 10)), safchan); safaribot.sendHtmlMessage(e, sys.name(src) + "'s " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " " + verb + " " + poke(evolution, true) + "!", safchan); sys.sendMessage(e, "", safchan); } else if (e === src) { // else only print to yourself sys.sendMessage(e, "", safchan); safaribot.sendHtmlMessage(e, pokeInfo.icon(info.num) + " -> " + pokeInfo.icon(parseInt(evolution, 10)), safchan); safaribot.sendHtmlMessage(e, "Your " + poke(getInputPokemon(info.name).num + (info.shiny ? "" : 0), true) + " " + verb + " " + poke(evolution, true) + "!", safchan); sys.sendMessage(e, "", safchan); } }); } this.saveGame(player); }; this.findItem = function(src, combo) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "use the Itemfinder"; var isTut = false; if (player.tutorial.inTutorial && player.tutorial.step === 6) { isTut = true; } else { if (cantBecause(src, reason, ["tutorial"])) { return; } } var dailyCharges = player.balls.itemfinder, permCharges = player.balls.permfinder, totalCharges = dailyCharges + permCharges; if (totalCharges < 1) { if (isTut) { tutorMsg(src, "You need to recharge your Itemfinder before you can use it! You can recharge it with " + link("/use gem")); } else { safaribot.sendMessage(src, "You have no charges left for your Itemfinder!", safchan); } return; } if (isTut) { safari.tutorialFinder(src, totalCharges); return; } var currentTime = now(); if (player.cooldowns.itemfinder > currentTime) { safaribot.sendHtmlMessage(src, "Your Itemfinder needs to cool down otherwise it will overheat! Try again in " + timeLeftString(player.cooldowns.itemfinder) + ". " + link("/finder", "[Try Again]"), safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid", "baking"])) { return; } var pulls = this.getFortune(player, "finderburn", 1) var minCombo = 3; var comboPulls = 0; var maxComboPulls = 99; var usedCombo = 1; if (combo) { if (player.consecutiveCombo < minCombo) { safaribot.sendHtmlMessage(src, "Your Consecutive Catch Combo needs to be at least " + minCombo + " in order to power up your Itemfinder! " + link("/finder", "[Use Without Combo]"), safchan); return; } var maxPullsPerCombo = 7; var usable = player.consecutiveCombo - minCombo + 1; for (var i = 0; i <= usable; i++) { comboPulls += Math.min(maxPullsPerCombo, usable - i); usedCombo += 1; if (comboPulls >= maxComboPulls) { break; } } comboPulls = Math.min(comboPulls, maxComboPulls); pulls += comboPulls; } pulls = Math.min(pulls, totalCharges); if (comboPulls) { player.consecutiveCombo -= usedCombo; safaribot.sendHtmlMessage(src, "You powered up the Itemfinder with your Consecutive Catch Combo, allowing you to use it " + plural(pulls, "time") + " in a row! [Remaining Combo: " + player.consecutiveCombo + "]", safchan); } var emptyPulls = freePulls = 0; var foundAny = false; var totalRewards = {}; while (pulls > 0) { pulls -= 1; var freefinder = chance(this.getFortune(player, "freefinder", 0)); if (!freefinder) { if (dailyCharges > 0 ) { player.balls.itemfinder -= 1; dailyCharges -= 1; } else { player.balls.permfinder -= 1; permCharges -= 1; } totalCharges -= 1; } else { freePulls++; } var reward = chance(finderMissRate) ? "nothing" : randomSample(finderItems); if (reward == "nothing" && safari.hasCostumeSkill(player, "finderBasedOnLead")) { var type = type1(parseInt(player.party[0], 10)); var typeChance = Math.max(0.01, (this.getCostumeLevel(player) / 10) / 60); if (chance(typeChance)) { switch (type) { case "Normal": reward = "pack"; break; case "Grass": reward = "golden"; break; case "Fire": reward = "cookie"; break; case "Water": reward = "hdew"; break; case "Ice": reward = "crystal"; break; case "Electric": reward = "recharge2"; break; case "Dragon": reward = "cometshard"; break; case "Fairy": reward = "mushroom"; break; case "Psychic": reward = "sunshard"; break; case "Dark": reward = "moonshard"; break; case "Bug": reward = "spray"; break; case "Poison": reward = "bignugget"; break; case "Flying": reward = "egg"; break; case "Rock": reward = "fossil"; break; case "Ground": reward = "starpiece"; break; case "Steel": reward = "silver"; break; case "Fighting": reward = "celebrityTicket"; break; case "Ghost": reward = "brush"; break; } } } var amount = 1; if (reward === "nothing" && chance((player.costume === "explorer" ? costumeData.explorer.rate : 0) + this.getFortune(player, "explorer", 0))) { reward = chance(finderMissRate) ? "nothing" : randomSample(finderItems); } if (reward === "nothing" && this.hasCostumeSkill(player, "betterFinder") && (chance(0.06)) && (chance((this.getCostumeLevel(player)-10)/10))) { reward = chance(finderMissRate) ? "nothing" : randomSample(finderItems); } if (player.costume === "explorer") { if (reward === "pearl" || reward === "stardust") { amount = chance(costumeData.explorer.rate2) ? 2 : 1; } } var giveReward = true; var cd = itemData.itemfinder.cooldown; if (player.truesalt >= now() && chance(player.srate)) { reward = reward !== "nothing" ? (Math.random() < 0.4 ? "rock" : "nothing") : reward; } switch (reward) { case "pack": { safaribot.sendHtmlMessage(src, "Beep. Boop. How careless! Someone left " + an(finishName(reward)) + " full of goodies lying on the ground!", safchan); this.costumeEXP(player, "findrare"); } break; case "golden": { amount = 3; safaribot.sendHtmlMessage(src, "Bling! Bling! You notice some " + finishName(reward) + " growing on a mysterious, shimmering tree!", safchan); this.costumeEXP(player, "findrare"); } break; case "hdew": { amount = 20; safaribot.sendHtmlMessage(src, "SPLASH! You carelessly tripped and fell into a shining puddle of " + finishName(reward) + "! You decide to make the best of it and collect some for later use.", safchan); this.costumeEXP(player, "findrare"); } break; case "sunshard": { safaribot.sendHtmlMessage(src, "BEEEEEEP!? Your Itemfinder leads you through some brightly-lit ruins. At its heart, you find an altar with a single " + finishName(reward) + " mounted on it.", safchan); this.costumeEXP(player, "findrare"); } break; case "moonshard": { safaribot.sendHtmlMessage(src, "BEEEEEEP!? Your Itemfinder leads you through some dimly-lit ruins. At its heart, you find a pedestal with a single " + finishName(reward) + " mounted on it.", safchan); this.costumeEXP(player, "findrare"); } break; case "spray": { safaribot.sendHtmlMessage(src, "BZZZ! BZZZ! This place is infested with Bug-type Pokémon! You rummage through your bag looking for Repel to spray, but find " + an(finishName(reward)) + " instead. How did that get there?", safchan); this.costumeEXP(player, "findrare"); } break; case "bignugget": { safaribot.sendHtmlMessage(src, "Bzweeeeep! Your Itemfinder reacts violently near a nondescript dumpster. You brave the stench, dive in, and find a beautiful " + finishName(reward) + "!", safchan); this.costumeEXP(player, "findrare"); } break; case "egg": case "bright": { safaribot.sendHtmlMessage(src, "KACAW! An enormous bird Pokémon flies overhead and drops " + an(finishName(reward)) + " right into your hands!", safchan); this.costumeEXP(player, "findrare"); } break; case "fossil": { safaribot.sendHtmlMessage(src, "Beep. Beep. Boop? Your Itemfinder points you towards some strange rocks on the ground. As you turn them over, you realise one of them is " + an(finishName(reward)) + "!", safchan); this.costumeEXP(player, "findrare"); } break; case "starpiece": { amount = 3; safaribot.sendHtmlMessage(src, "Whew! While looking up to admire the starry sky, you almost step into a crater in the ground! On closer inspection, there seem to be some glistening " + es(finishName(reward)) + " at the bottom of it!", safchan); this.costumeEXP(player, "findrare"); } break; case "silver": { amount = 8; safaribot.sendHtmlMessage(src, "Beep. Boop. Bop! Your Itemfinder leads you to a giant X painted on the ground! You grab a shovel and start digging, and uncover a tiny treasure chest with some " + es(finishName(reward)) + " inside!", safchan); this.costumeEXP(player, "findrare"); } break; case "brush": { safaribot.sendHtmlMessage(src, "Beeeeeeeeeeeeeep... A strange fog rolls in as your Itemfinder gives out. Wandering, you come across a mysterious exhumed grave. All that's left inside are some burned and defaced photos, and a conspicuous " + finishName(reward) + "...", safchan); this.costumeEXP(player, "findrare"); } break; case "rare2": { reward = "rare"; amount = 2; safaribot.sendHtmlMessage(src, "Beep. Beep. BEEP! You found " + plural(amount, reward) + " behind a bigger than average bush!", safchan); this.costumeEXP(player, "findrare"); } break; case "rare": { safaribot.sendHtmlMessage(src, "Beep. Beep. BEEP! You found " + an(finishName(reward)) + " behind a bush!", safchan); this.costumeEXP(player, "findrare"); } break; case "crystal": { safaribot.sendHtmlMessage(src, "Beeeeeeep! Oh my, a Crystal! You found " + an(finishName(reward)) + " with your Itemfinder!", safchan); this.costumeEXP(player, "findrare"); } break; case "recharge2": { reward = "permfinder"; amount = 15 + this.getFortune(player, "findershock", 0); showMsg = false; safaribot.sendHtmlMessage(src, "Rai-ai-ai-... CHHUUUUUUUUU! You were shocked by a wild Raichu while looking for items! On the bright side, your Itemfinder recharged a lot due to the shock.", safchan); safari.missionProgress(player,"getShocked",0,1,{}); this.costumeEXP(player, "findrare"); } break; case "recharge": { reward = "permfinder"; amount = 3 + this.getFortune(player, "findershock", 0); showMsg = false; safaribot.sendHtmlMessage(src, "Pi-ka-CHUUU! You were shocked by a wild Pikachu while looking for items! On the bright side, your Itemfinder slightly recharged due to the shock.", safchan); safari.missionProgress(player,"getShocked",0,1,{}); } break; case "crown": { safaribot.sendHtmlMessage(src, "BEEP! BEEPBEEP! Boop!? Your Itemfinder locates an old treasure chest full of ancient relics. Upon picking them up, they crumble into dust except for a single " + finishName("crown") + ".", safchan); this.costumeEXP(player, "findrare"); } break; case "scarf": { safaribot.sendHtmlMessage(src, "BEEP! BEEPBEEP! Beeeeeeeeeep! Your Itemfinder led you to a thrift store! New sale: Silk Scarf (only $0) it said! Nice find!", safchan); this.costumeEXP(player, "findrare"); } break; case "cookie": { safaribot.sendHtmlMessage(src, "!BEEPBEEPBEEPBEEP! Oh no, you're on fire! Luckily, as a result of the fire, you were able to cook a nice " + finishName("cookie") + " from the surroundings!", safchan); this.costumeEXP(player, "findrare"); safari.missionProgress(player,"findCookie",0,1,{}); } break; case "eviolite": { safaribot.sendHtmlMessage(src, "!PEEB !PEEB Another trainer approaches you while you are looking for items and snickers: \"You have it on backwards.\" You correct the position, turn around, and find a sizeable chunk of " + finishName("eviolite") + " on the ground.", safchan); this.costumeEXP(player, "findrare"); } break; case "honey": { safaribot.sendHtmlMessage(src, "BEE! BEE! BEE! You stumbled upon a beehive while using your Itemfinder. Before running off to avoid the swarm, you managed to steal a glob of " + finishName("honey") + "!", safchan); safari.missionProgress(player,"findHoney",0,1,{}); this.costumeEXP(player, "findrare"); } break; case "spy": { safaribot.sendMessage(src, "Bep. Your Itemfinder is pointing towards a shadowy area. Within the darkness, you find a suspicious " + finishName(reward) + "!", safchan); } break; case "gacha": { safaribot.sendMessage(src, "Beeeep. You're led to a nearby garbage can by your Itemfinder. You decide to dig around anyway and find an unused " + finishName(reward) + "!", safchan); } break; case "gacha2": { reward = "gacha"; amount = Math.round(4 + (4 * Math.random())); safaribot.sendMessage(src, "Beeeep. You're led to a nearby garbage can by your Itemfinder. You decide to dig around anyway and find a pile of unused " + es(finishName(reward)) + "!", safchan); } break; case "rock": { safaribot.sendMessage(src, "Beep. Your Itemfinder pointed you towards a very conspicuous " + finishName(reward) + ".", safchan); } break; case "bluapricorn": case "pnkapricorn": case "redapricorn": case "ylwapricorn": case "grnapricorn": case "blkapricorn": { safaribot.sendMessage(src, "Beep-Beep. Your Itemfinder pointed you towards an Apricorn Tree! You decided to pick one and put it in your bag!", safchan); safari.missionProgress(player,"findApricorn",0,1,{}) } break; case "bait": { safaribot.sendMessage(src, "Beep-Beep. Your Itemfinder pointed you towards a berry bush! You decided to pick one and put it in your bag.", safchan); } break; case "bait2": { amount = 5; reward = "bait"; safaribot.sendMessage(src, "Beep-Beep. Your Itemfinder pointed you towards a plentiful berry bush! You collected " + plural(amount, reward) + " from it and put them in your bag.", safchan); } break; case "mushroom": { safaribot.sendMessage(src, "Bep-Bep-Bep-Bep-Bep. Your Itemfinder leads you to a badger. And another badger. And another badger. Oh, " + an(finishName(reward)) + "! And a snake!", safchan); this.costumeEXP(player, "findrare"); } break; case "celebrityTicket": { amount = 1; safaribot.sendHtmlMessage(src, "Beeeeeep! What's this? A voucher for an extra run at the Celebrities!", safchan); this.costumeEXP(player, "findrare"); } break; case "cometshard": { safaribot.sendHtmlMessage(src, "Beeeeeeeeeeeeeep!! Your Itemfinder lets out a high pitched noise as you pass by a strange shard. After analyzing it for a while, you realize the object is not from this world and decide to pick it up.", safchan); this.costumeEXP(player, "findrare"); } break; case "nugget": { reward = "nugget"; this.costumeEXP(player, "findrare"); } case "bigpearl2": { amount = 2; reward = "bigpearl"; } case "bigpearl": { if (player.costume !== "explorer") { reward = "pearl"; } } /*falls through*/ case "pearl": case "stardust": { safaribot.sendMessage(src, "Beep Beep Beep. You dig around a sandy area and unbury " + an(finishName(reward)) + "!", safchan); if (amount > 1) { safaribot.sendMessage(src, "You decided to keep digging and found another " + finishName(reward) + "!", safchan); } } break; case "fragment": { if (player.costume === "explorer") { safaribot.sendHtmlAll("Be-Be-Be-Beeep! " + sys.name(src) + " found " + an(finishName(reward)) + " in a patch of grass!", safchan); this.costumeEXP(player, "findrare"); break; } else { reward = "luxury"; } } /* falls through*/ case "luxury": { safaribot.sendMessage(src, "Be-Beep. You comb a patch of grass that your Itemfinder pointed you towards and found " + an(finishName(reward)) + "!", safchan); } break; default: if (chance(0.08)) { var dynamicHints = safariHints.slice(0); if (chance(0.33)) { var otherPlayer, shopItem, shopItemFinish, otherPlayerName, playersWithValidShops; playersWithValidShops = sys.playersOfChannel(safchan).filter(function(id) { var p = getAvatar(id); if (!p || Object.keys(p.shop).length === 0 || p.tradeban >= now()) // no save data, or shop is empty, or is tradebanned return false; for (var item in p.shop) { if (p.shop[item].limit > 0) // shop has at least one item still in stock return true; } return false; }).map(function(id) { return getAvatar(id); }); if (playersWithValidShops.length > 0) { otherPlayer = playersWithValidShops.random(); do { var playerShop = otherPlayer.shop, randItem = Object.keys(playerShop).random(); shopItem = playerShop[randItem].limit > 0 ? randItem : false; } while (!shopItem); shopItemFinish = getInputPokemon(shopItem).name ? getInputPokemon(shopItem).name : finishName(shopItem.replace("@", "")); otherPlayerName = otherPlayer.id.toCorrectCase(); if (!otherPlayer.shop || !otherPlayer.shop[shopItem] || !otherPlayer.shop[shopItem].price) { safaribot.sendAll("Debug on Itemfinder Ads -- Shop Owner Name: " + otherPlayerName + " -- Shop JSON: " + JSON.stringify(otherPlayer.shop) + " -- shopItem: " + shopItem, staffchannel); } else { dynamicHints = ["...Oh, it's just an ad. \"Come on over to {0}, selling {1} for only ${2}!\" it says.".format( link("/shop " + otherPlayerName, otherPlayerName + "'s shop"), an(shopItemFinish), addComma(otherPlayer.shop[shopItem].price) )]; } } } safaribot.sendHtmlMessage(src, "You pull out your Itemfinder ... ... ... KER-BONK! You walked right into a sign! ...Huh? It has a Trainer Tip written on it!", safchan); sys.sendHtmlMessage(src, "±Hint: " + dynamicHints.random(), safchan); foundAny = true; } else { var hit = false; if (player.costume == "explorer" && chance(0.2) && safari.detectiveData.hasOwnProperty(player.idnum+"") && safari.detectiveData[player.idnum].date === getDay(now()) && !safari.detectiveData[player.idnum].solved) { for (var i = 0; i < safari.detectiveData[player.idnum+""].clues.length; i++) { if (safari.detectiveData[player.idnum+""].clues[i].unlock == "explorerfinder") { safaribot.sendHtmlMessage(src, "You pull out your Itemfinder ... ... ... What's this? It's a clue!", safchan); safari.detectiveClue(player.idnum, "explorerfinder", src); hit = true; foundAny = true; break; } } } if (!(hit)) { emptyPulls++; } } giveReward = false; break; } if (giveReward) { player.records.itemsFound += 1; foundAny = true; if (!totalRewards.hasOwnProperty(reward)) { totalRewards[reward] = 0; } totalRewards[reward] += amount; } if (player.costume == "flower" || player.costume == "fisherman") { cd *= 1.4; } else if (player.costume == "preschooler" && safari.hasCostumeSkill(player, "fasterFinder")) { cd *= 0.5; } } if (emptyPulls > 0) { if (foundAny) { safaribot.sendHtmlMessage(src, "Your Itemfinder did not detect anything the other " + plural(emptyPulls, "time") + "... " + (Object.keys(totalRewards).length ? "" : "[Remaining charges: " + totalCharges + (permCharges > 0 ? " (Daily " + dailyCharges + " plus " + permCharges + " bonus)" : "") + "]. " + link("/finder", "[Use Again]")), safchan); // if your only pulls were hints/ads etc, display charges here since the reward lines don't print } else { safaribot.sendHtmlMessage(src, "You pulled out your Itemfinder " + plural(emptyPulls, "time") + ", but it did not detect anything... [Remaining charges: " + totalCharges + (permCharges > 0 ? " (Daily " + dailyCharges + " plus " + permCharges + " bonus)" : "") + "]. " + link("/finder", "[Use Again]"), safchan); } } if (foundAny && Object.keys(totalRewards).length > 0) { var rewardVerbose = []; for (var i in totalRewards) { rewardVerbose.push(plural(totalRewards[i], i)); } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "You found " + readable(rewardVerbose) + " with your Itemfinder! [Remaining charges: " + totalCharges + (permCharges > 0 ? " (Daily " + dailyCharges + " plus " + permCharges + " bonus)" : "") + "]. " + link("/finder", "[Use Again]"), safchan); for (var i in totalRewards) { // this is kind of awkward but I want the full reward string displayed first, and any messages from rewardCapCheck indicating discarded excess to be printed after, hence looping twice rewardCapCheck(player, i, totalRewards[i], true); } } if (freePulls > 0) { safaribot.sendHtmlMessage(src, "You managed to save " + plural(freePulls, "charge") + " from being consumed!", safchan); } player.cooldowns.itemfinder = currentTime + cd; this.saveGame(player); }; this.useItem = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var info = toCommandData(data, ["item", "target", "reward", "extra1", "extra2"]); var item = itemAlias(info.item, true); if (!allItems.contains(item) || itemData[item].invisible) { safaribot.sendMessage(src, "This is not a valid item.", safchan); return; } if (itemData[item].type !== "consumable") { safaribot.sendMessage(src, item + " is not a usable item!", safchan); return; } var input = "@" + item; if (input in player.shop && player.shop[input].limit >= player.balls[item]) { safaribot.sendMessage(src, "You need to remove that item from your shop before using it!", safchan); return; } if (player.tutorial.inTutorial && (player.tutorial.step === 6 || player.tutorial.step === 7)) { safari.tutorialUseItem(src, item); return; } if (cantBecause(src, "use an item", ["item", "contest", "auction", "battle", "event", "tutorial", "pyramid", "baking"], item)) { return; } if (item === "teraorb") { player.teraActive = !player.teraActive; safaribot.sendMessage(src, "Your {0} was {1}!".format(finishName("teraorb"), player.teraActive ? "activated" : "deactivated"), safchan); this.saveGame(player); return; } if (item === "gem") { var chars = player.balls.itemfinder, limit = getCap("permfinder"); if (player.balls.permfinder >= limit) { safaribot.sendMessage(src, "Your Itemfinder is already fully charged!", safchan); return; } var pulled; if (!info.target) { pulled = 1; } else if (!isNaN(info.target)) { pulled = Math.min(100, Math.max(1, parseInt(info.target))); } else { safaribot.sendMessage(src, "Type /use gem or /use gem:[amount] (e.g. /use gem:10), to use your " + es(finishName("gem")) + "!", safchan); return; } var gained = 0; var totalPulled = 0; while (pulled > 0 && player.balls.gem > 0 && player.balls.permfinder < limit) { var gemdata = itemData.gem.charges + this.getFortune(player, "extragem", 0), pchars = player.balls.permfinder + gemdata; if (pchars > limit) { gemdata = limit - player.balls.permfinder; pchars = limit; } var tchars = chars + pchars; rewardCapCheck(player, "permfinder", gemdata); gained += gemdata; player.balls.gem -= 1; player.records.gemsUsed += 1; pulled -= 1; totalPulled += 1; if (player.balls.permfinder >= limit) { break; } } if (totalPulled === 1) { safaribot.sendHtmlMessage(src, "The " + finishName("gem") + " begins to emit a soft baaing sound. Your Itemfinder then lights up and responds with a loud BAA~!", safchan); } else { safaribot.sendHtmlMessage(src, "The " + plural(totalPulled, "gem") + " begin to emit a soft baaing sound. Your Itemfinder then lights up and responds with a loud BAA~!", safchan); } safaribot.sendMessage(src, "Your Itemfinder gained " + gained + " charges. [Remaining Charges: " + tchars + " (Daily " + chars + " plus " + pchars + " bonus)].", safchan); this.updateShop(player, "gem"); safaribot.sendMessage(src, itemsLeft(player, "gem"), safchan); this.saveGame(player); return; } if (item === "pack") { var pulled; if (!info.target) { pulled = 1; } else if (!isNaN(info.target)) { pulled = Math.min(100, Math.max(1, parseInt(info.target))); } else { safaribot.sendMessage(src, "Type /use pack or /use pack:[amount] (e.g. /use pack:10), to open your " + es(finishName("pack")) + "!", safchan); return; } while (pulled > 0 && player.balls.pack > 0) { var item = randomSample(packItems); var reward = item; var amount = 1; switch (item) { case "gem": amount = 3; break; case "rock": amount = 50; break; case "bait": amount = 10; break; case "gacha": amount = 10; break; case "silver": amount = 8; break; case "silver2": amount = 84; reward = "silver"; break; case "bluapricorn": case "grnapricorn": case "pnkapricorn": amount = 20; break; } safaribot.sendMessage(src, "You excitedly open your " + finishName("pack") + " to reveal " + plural(amount, reward) + "!", safchan); /*if (item === "mega") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("mega")) + " in their " + finishName("pack") + "!", safchan); } if (item === "bignugget") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("bignugget")) + " in their " + finishName("pack") + "!", safchan); } if (item === "water") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("water")) + " in their " + finishName("pack") + "!", safchan); } if (item === "celebrityTicket") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("celebrityTicket")) + " in their " + finishName("pack") + "!", safchan); } if (item === "spray") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("spray")) + " in their " + finishName("pack") + "!", safchan); }*/ if (item === "silver2") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + plural(amount, reward) + " in their " + finishName("pack") + "!", safchan); } rewardCapCheck(player, reward, amount); player.balls.pack -= 1; pulled -= 1; player.records.packsOpened += 1; } this.updateShop(player, "pack"); safaribot.sendMessage(src, itemsLeft(player, "pack"), safchan); this.saveGame(player); return; } /*if (item === "candybag") { var pulled; if (!info.target) { pulled = 1; } else if (parseInt(info.target, 10) === 1) { pulled = 1; } else if (parseInt(info.target, 10) === 10) { pulled = 10; } else if (parseInt(info.target, 10) === 100) { pulled = 100; } else if (parseInt(info.target, 10) === 1000) { pulled = 1000; } else { safaribot.sendMessage(src, "Type /use candybag:1, /use candybag:10, /use candybag:100, or /use candybag:1000, to open your Candy Bags!", safchan); return; } if (pulled > player.balls.candybag) { safaribot.sendMessage(src, "You don't have " + pulled + " Candy Bags!", safchan); return; } var candybagprizes = { "1": { rock: 40, money: 30, pnkapricorn: 10, grnapricorn: 10, bluapricorn: 10, ylwapricorn: 5, safari: 10, great: 15, ultra: 10, gacha: 6, bait: 2, silver: 2, mail: 5, pearl: 2, rare: 3 }, "10": { money: 50, bait: 12, mail: 10, pnkapricorn: 20, grnapricorn: 20, bluapricorn: 20, ylwapricorn: 5, great: 50, ultra: 20, amulet: 2, crown: 2, scarf: 1, soothe: 3, battery: 2, honey: 2, eviolite: 3, rare: 30, silver: 16, gem: 3, gacha: 19, pearl: 30, bigpearl: 15, hdew: 3, water: 2, scale: 1, brush: 1, celebrityTicket: 5, prize: 3, mega: 2, mega2: 1 }, "100": { money: 25, mail: 7, ultra: 12, eviolite: 10, fossil: 4, scale: 8, brush: 8, rare: 45, spray: 5, nugget: 8, bignugget: 1, rare2: 10, gacha: 15, celebrityTicket: 10, prize: 5, golden: 1, silver: 12, mega: 15, mega2: 5, water: 5, hdew: 5 }, "1000": { shiny: 2, hdew: 5, bignugget: 10, mega: 6, mega2: 3, golden: 10, silver: 8, money: 17 }, } var reward = randomSample(candybagprizes[""+pulled]); var amount = 1; var giveReward = true; switch (reward) { case "gem": amount = (pulled === 10 ? 3 : 1); break; case "rare": amount = (pulled === 100 ? 13 : (pulled === 10 ? 3 : 1)); break; case "rare2": amount = 10; reward = "rare"; break; case "celebrityTicket": amount = (pulled === 100 ? 3 : 1); break; case "bait": amount = (pulled === 10 ? 8 : 2); break; case "gacha": amount = (pulled === 100 ? 30 : (pulled === 10 ? 5 : 1)); break; case "silver": amount = (pulled === 1000 ? 200 : (pulled === 100 ? 40 : (pulled === 10 ? 5 : 1))); break; case "golden": amount = (pulled === 1000 ? 15 : 5); break; case "mega": amount = (pulled === 1000 ? 5 : (pulled === 100 ? 3 : 1)); break; case "mega2": amount = (pulled === 1000 ? 10 : (pulled === 100 ? 5 : 2)); reward = "mega"; break; case "dew": amount = (pulled === 1000 ? 50 : (pulled === 100 ? 20 : 5)); break; case "bignugget": amount = (pulled === 1000 ? 2 : 1); break; case "scale": case "brush": amount = (pulled === 100 ? 3 : 1); break; case "bluapricorn": case "grnapricorn": case "pnkapricorn": case "ylwapricorn": amount = (pulled === 1 ? 1 : 5); break; case "water": amount = (pulled === 100 ? 2 : 1); break; case "mail": amount = (pulled === 100 ? 15 : (pulled === 10 ? 5 : 3)); break; case "ultra": case "great": amount = (pulled === 100 ? 25 : (pulled === 10 ? 5 : 1)); break; } if (reward == "shiny") { giveReward = false; if (currentPokemon || contestCount > 0 || contestCooldown <= 13) { reward = "money"; } else { var spawn = true; var ballUsed; if (!isBall(ballUsed) || player.balls[ballUsed] === 0) { ballUsed = (player.balls[player.favoriteBall] > 0 ? player.favoriteBall : "safari"); } safaribot.sendAll(sys.name(src) + " goes to open their Candy Bags but they startled a nearby Pokémon!", safchan); var monlist = [151, 27, 52, 677, 570, 92, 66, 231, 238, 239, 240, 172, 355, 406, 427, 191, 179, 177, 170, 298, 246, 147, 371, 328, 374, 443, 633]; var mon = monlist.random(); if (mon == 151) { monlist = monlist.slice(1) safari.createWild(mon, false, 1, null, null, null, {"num": monlist.random(), "shiny": true}); } else { safari.createWild(mon, true, 1); } safari.throwBall(src, ballUsed, true); preparationFirst = sys.name(src).toLowerCase(); if (baitCooldown <= 9) { baitCooldown = sys.rand(9, 13); } if (goldenBaitCooldown <= 6) { goldenBaitCooldown = sys.rand(6, 11); } } } if (reward == "money") { giveReward = false; amount = Math.round(((13 * Math.random()) + 2.7) * (13 + (2.7 * Math.random())) * (pulled >= 100 ? pulled : (pulled/3))); player.money += amount; this.sanitize(player); safaribot.sendMessage(src, "You " + finishName("candybag") + " to reveal $" + amount + "!", safchan); } if (giveReward) { safaribot.sendMessage(src, "You " + finishName("candybag") + " to reveal " + plural(amount, reward) + "!", safchan); if (reward === "mega" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("mega")) + " in their " + finishName("candybag") + "!", safchan); } else if (reward === "mega" && amount >= 2) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("mega") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "bignugget" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("bignugget")) + " in their " + finishName("candybag") + "!", safchan); } else if (reward === "bignugget" && amount >= 2) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("bignugget") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "dew") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("dew") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "golden") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("golden") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "silver" && amount >= 200) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("silver") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "water" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("water")) + " in their " + finishName("candybag") + "!", safchan); } else if (reward === "water" && amount >= 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("water") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "rare" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("rare")) + " in their " + finishName("candybag") + "!", safchan); } else if (reward === "rare" && amount >= 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("rare") + "s in their " + finishName("candybag") + "!", safchan); } else if (reward === "fossil") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("fossil")) + " in their " + finishName("candybag") + "!", safchan); } else if (reward === "celebrityTicket" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("celebrityTicket")) + " in their " + finishName("candybag") + "!", safchan); } else if (reward === "celebrityTicket") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + finishName("celebrityTicket") + " in their " + finishName("candybag") + "!", safchan); } rewardCapCheck(player, reward, amount); } player.balls.candybag -= pulled; safaribot.sendMessage(src, itemsLeft(player, "candybag"), safchan); this.saveGame(player); return; }*/ /*if (item === "easteregg") { var pulled; if (!info.target) { pulled = 1; } else if (parseInt(info.target, 10) === 1) { pulled = 1; } else if (parseInt(info.target, 10) === 10) { pulled = 10; } else if (parseInt(info.target, 10) === 100) { pulled = 100; } else if (parseInt(info.target, 10) === 1000) { pulled = 1000; } else { safaribot.sendMessage(src, "Type /use easteregg:1, /use easteregg:10, /use easteregg:100, or /use easteregg:1000, to open your Easter Eggs!", safchan); return; } if (pulled > player.balls.easteregg) { safaribot.sendMessage(src, "You don't have " + pulled + " Easter Eggs!", safchan); return; } var eastereggprizes = { "1": { rock: 35, money: 20, pnkapricorn: 10, grnapricorn: 10, bluapricorn: 10, ylwapricorn: 5, safari: 10, great: 3, gacha: 5, bait: 5, silver: 3, mail: 10, pearl: 2, gem: 1, eviolite: 1 }, "10": { money: 20, bait: 10, mail: 10, pnkapricorn: 10, grnapricorn: 10, bluapricorn: 10, ylwapricorn: 5, great: 10, ultra: 4, amulet: 2, crown: 2, scarf: 1, soothe: 2, battery: 2, honey: 2, eviolite: 2, rare: 3, silver: 10, gem: 6, gacha: 12, pearl: 10, bigpearl: 5, dew: 3, water: 2, scale: 1, brush: 1, celebrityTicket: 1, mega: 1 }, "100": { money: 20, mail: 10, eviolite: 10, fossil: 4, scale: 10, brush: 10, rare: 8, spray: 5, nugget: 8, bignugget: 1, rare2: 3, gacha: 15, celebrityTicket: 5, golden: 5, silver: 10, mega: 10, water: 5, dew: 5 }, "1000": { shiny: 2, dew: 5, bignugget: 7, mega: 8, golden: 10, silver: 13, money: 15 }, } var reward = randomSample(eastereggprizes[""+pulled]); var amount = 1; var giveReward = true; switch (reward) { case "gem": amount = (pulled === 10 ? 3 : 1); break; case "rare": amount = (pulled === 100 ? 2 : 1); break; case "rare2": amount = 5; reward = "rare"; break; case "celebrityTicket": amount = (pulled === 100 ? 3 : 1); break; case "bait": amount = (pulled === 10 ? 8 : 2); break; case "gacha": amount = (pulled === 100 ? 30 : (pulled === 10 ? 5 : 1)); break; case "silver": amount = (pulled === 1000 ? 200 : (pulled === 100 ? 40 : (pulled === 10 ? 5 : 1))); break; case "golden": amount = (pulled === 1000 ? 15 : 5); break; case "mega": amount = (pulled === 1000 ? 5 : (pulled === 100 ? 3 : 1)); break; case "dew": amount = (pulled === 1000 ? 50 : (pulled === 100 ? 20 : 5)); break; case "bignugget": amount = (pulled === 1000 ? 2 : 1); break; case "scale": case "brush": amount = (pulled === 100 ? 3 : 1); break; case "bluapricorn": case "grnapricorn": case "pnkapricorn": case "ylwapricorn": amount = (pulled === 1 ? 1 : 5); break; case "water": amount = (pulled === 100 ? 2 : 1); break; case "mail": amount = (pulled === 100 ? 15 : (pulled === 10 ? 5 : 3)); break; } if (reward == "shiny") { giveReward = false; if (currentPokemon || contestCount > 0 || contestCooldown <= 13) { reward = "money"; } else { var spawn = true; var ballUsed; if (!isBall(ballUsed) || player.balls[ballUsed] === 0) { ballUsed = (player.balls[player.favoriteBall] > 0 ? player.favoriteBall : "safari"); } safaribot.sendAll(sys.name(src) + " goes to open their Easter Eggs but they startled a nearby Pokémon!", safchan); var mon = [27, 52, 98, 63, 60, 114, 116, 231, 238, 239, 240, 172, 406, 427, 191, 179, 177, 170, 298, 246, 147, 371, 328, 374, 443, 633].random(); safari.createWild(mon, true, 1); safari.throwBall(src, ballUsed, true); preparationFirst = sys.name(src).toLowerCase(); if (baitCooldown <= 9) { baitCooldown = sys.rand(9, 13); } if (goldenBaitCooldown <= 6) { goldenBaitCooldown = sys.rand(6, 11); } } } if (reward == "money") { giveReward = false; amount = Math.round(((12 * Math.random()) + 3) * (12 + (3 * Math.random())) * (pulled >= 100 ? pulled : (pulled/3))); player.money += amount; this.sanitize(player); safaribot.sendMessage(src, "You " + finishName("easteregg") + " to reveal $" + amount + "!", safchan); } if (giveReward) { safaribot.sendMessage(src, "You " + finishName("easteregg") + " to reveal " + plural(amount, reward) + "!", safchan); if (reward === "mega" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("mega")) + " in their " + finishName("easteregg") + "!", safchan); } else if (reward === "mega" && amount >= 2) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("mega") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "bignugget" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("bignugget")) + " in their " + finishName("easteregg") + "!", safchan); } else if (reward === "bignugget" && amount >= 2) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("bignugget") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "dew") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("dew") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "golden") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("golden") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "silver" && amount >= 200) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("silver") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "water" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("water")) + " in their " + finishName("easteregg") + "!", safchan); } else if (reward === "water" && amount >= 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("water") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "rare" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("rare")) + " in their " + finishName("easteregg") + "!", safchan); } else if (reward === "rare" && amount >= 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + " " + finishName("rare") + "s in their " + finishName("easteregg") + "!", safchan); } else if (reward === "fossil") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("fossil")) + " in their " + finishName("easteregg") + "!", safchan); } else if (reward === "celebrityTicket" && amount === 1) { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + an(finishName("celebrityTicket")) + " in their " + finishName("easteregg") + "!", safchan); } else if (reward === "celebrityTicket") { safaribot.sendHtmlAll("Wow! " + sys.name(src) + " found " + amount + finishName("celebrityTicket") + " in their " + finishName("easteregg") + "!", safchan); } rewardCapCheck(player, reward, amount); } player.balls.easteregg -= pulled; safaribot.sendMessage(src, itemsLeft(player, "easteregg"), safchan); this.saveGame(player); return; }*/ if (item.toLowerCase() === "celebrityticket") { if (player.balls.celebrityTicket < 1) { safaribot.sendMessage(src, "You don't have any Celebrity Tickets to use!", safchan); return; } if (player.ticketCelebrityRun) { safaribot.sendMessage(src, "You already have a valid " + finishName("celebrityTicket") + " reward run waiting to be used!", safchan); return; } var cd = data.split(":"), cd2 = null; if (cd.length > 1) { cd2 = cd[1]; } if (cd2) { if (["kanto", "johto", "hoenn", "sinnoh", "unova", "galar", "random"].indexOf(cd2.toLowerCase()) == -1) { safaribot.sendMessage(src, "You must select a valid region to challenge next! (Kanto, Johto, Hoenn, Sinnoh, Unova, and Galar are valid.)", safchan); return; } if (cd2.toLowerCase() == "random") { cd2 = ["kanto", "johto", "hoenn", "sinnoh", "unova", "galar"].random(); } player.celebrityRegion = cd2.toLowerCase(); } else { safaribot.sendMessage(src, "You must select a valid region to challenge next! (Kanto, Johto, Hoenn, Sinnoh, Unova, Galar, or Random are valid.)", safchan); return; } player.ticketCelebrityRun = true; player.balls.celebrityTicket -= 1; this.updateShop(player, "celebrityTicket"); safaribot.sendMessage(src, "You now have 1 " + finishName("celebrityTicket") + " reward run! You can now challenge the celebrities of " + cap(player.celebrityRegion) + " for rewards!", safchan); safaribot.sendMessage(src, itemsLeft(player, "celebrityTicket"), safchan); this.saveGame(player); return; } if (item === "egg") { var pulled; if (!info.target) { pulled = 1; } else if (!isNaN(info.target)) { pulled = Math.min(100, Math.max(1, parseInt(info.target))); } else { safaribot.sendMessage(src, "Type /use egg or /use egg:[amount] (e.g. /use egg:10), to hatch your " + es(finishName("gem")) + "!", safchan); return; } while (pulled > 0 && player.balls.egg > 0) { if (player.pokemon.length >= getPerkBonus(player, "box")) { safaribot.sendMessage(src, "You can't hatch " + an(finishName("egg")) + " because all your boxes are full! Please buy a new box with /buy.", safchan); break; } var shinyChanceFinal = shinyChance; var id; do { id = sys.rand(1, highestDexNum); } while (isRare(id)); for (var e = 0; e < player.party.length; e++) { if (e >= player.helds.length) { break; } if (player.helds[e] == 10) { if (hasCommonEggGroup(id, parseInt(player.party[e], 10))) { shinyChanceFinal *= itemData.watmel.rate; player.helds[e] = -1; safaribot.sendMessage(src, poke(player.party[e], true) + "'s Watmel Berry was consumed!", safchan); break; } } } var shiny = sys.rand(0, shinyChanceFinal) < 1; if (shiny) { id = id + ""; } sys.sendMessage(src, "", safchan); player.pokemon.push(id); var twins = false; if (this.getFortune(player, "eggtwins", 0)) { twins = true; safaribot.sendHtmlMessage(src, "Oh? The {0} is hatching... {2}{2} Two {1} hatched from the {0}! Nice twins!".format(finishName("egg"), (shiny ? toColor(poke(id, true), "DarkOrchid") : poke(id, true)), pokeInfo.icon(id, shiny)), safchan); player.pokemon.push(id); } else { safaribot.sendHtmlMessage(src, "Oh? The {0} is hatching... {2} {1} hatched from the {0}!".format(finishName("egg"), (shiny ? toColor(poke(id, true), "DarkOrchid") : poke(id, true)), pokeInfo.icon(id, shiny)), safchan); } player.balls.egg -= 1; pulled -= 1; this.updateShop(player, "egg"); sys.sendMessage(src, "", safchan); player.records.eggsHatched += 1; this.missionProgress(player, "hatch", id, 1 + (twins ? 1 : 0), {}); if (isRare(id)) { sys.appendToFile(mythLog, now() + "|||" + poke(id) + "::hatched from Egg"+(twins ? " (Twins)" : "")+"::" + sys.name(src) + "\n"); player.records.rareHatched +=1; sys.sendAll("", safchan); safaribot.sendHtmlAll("Wow! {0} hatched {1} from {2}!".format(sys.name(src), pokeInfo.icon(id, shiny) + " " + poke(id, true) + (twins ? " TWINS" : ""), an(finishName("egg"))), safchan); sys.sendAll("", safchan); } this.saveGame(player); } safaribot.sendMessage(src, itemsLeft(player, "egg"), safchan); return; } if (item === "bright") { var pulled; if (!info.target) { pulled = 1; } else if (!isNaN(info.target)) { pulled = Math.min(100, Math.max(1, parseInt(info.target))); } else { safaribot.sendMessage(src, "Type /use bright or /use bright:[amount] (e.g. /use bright:10), to hatch your " + es(finishName("bright")) + "!", safchan); return; } while (pulled > 0 && player.balls.bright > 0) { if (player.pokemon.length >= getPerkBonus(player, "box")) { safaribot.sendMessage(src, "You can't hatch " + an(finishName("bright")) + " because all your boxes are full! Please buy a new box with /buy.", safchan); break; } var id, shinyChanceFinal = shinyChance; if (sys.rand(0, itemData.bright.legendaryChance) < 1) { do { id = legendaries.random(); } while (getBST(id) > 600 || regionalEvos.concat(ultraBeasts,paradoxPokemon,[892, 898]).contains(id)); } else { do { id = sys.rand(1, highestDexNum); } while (isRare(id)); } for (var e = 0; e < player.party.length; e++) { if (e >= player.helds.length) { break; } if (player.helds[e] == 10) { if (hasCommonEggGroup(id, parseInt(player.party[e], 10))) { shinyChanceFinal *= itemData.watmel.rate; player.helds[e] = -1; safaribot.sendMessage(src, poke(player.party[e], true) + "'s Watmel Berry was consumed!", safchan); break; } } } shiny = sys.rand(0, shinyChanceFinal) < itemData.bright.shinyChance; if (legendaries.contains(id)) { shiny = false; } if (shiny) { id = id + ""; } sys.sendMessage(src, "", safchan); if (isLegendary(id)) { safaribot.sendHtmlMessage(src, "Oh? The {0} is hatching... {2} OH MY GOD! {1} HATCHED FROM THE {3}!!".format(finishName("bright"), an(poke(id, true)).toUpperCase(), pokeInfo.icon(id, shiny), finishName("bright").toUpperCase()), safchan); } else { safaribot.sendHtmlMessage(src, "Oh? The {0} is hatching... {2} {1} hatched from the {0}!".format(finishName("bright"), (shiny ? toColor(poke(id, true), "DarkOrchid") : poke(id, true)), pokeInfo.icon(id, shiny)), safchan); } player.pokemon.push(id); player.balls.bright -= 1; pulled -= 1; this.updateShop(player, "bright"); sys.sendMessage(src, "", safchan); player.records.brightEggsHatched += 1; this.missionProgress(player, "hatch", id, 1, {}); if (isRare(id)) { sys.appendToFile(mythLog, now() + "|||" + poke(id) + "::hatched from Bright Egg::" + sys.name(src) + "\n"); player.records.rareHatched +=1; sys.sendAll("", safchan); safaribot.sendHtmlAll("Wow! {0} hatched {1} from {2}!".format(sys.name(src), pokeInfo.icon(id, shiny) + " " + poke(id, true), an(finishName("bright"))), safchan); sys.sendAll("", safchan); } this.saveGame(player); } safaribot.sendMessage(src, itemsLeft(player, "bright"), safchan); return; } if (item === "water") { if (player.quests.pyramid.bonusStamina > 0) { safaribot.sendMessage(src, "You already packed " + an(finishName("water")) + " for your next Pyramid tour!", safchan); return; } player.balls.water -= 1; player.quests.pyramid.bonusStamina = itemData.water.bonusRate; this.updateShop(player, "water"); sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "You packed some " + finishName("water") + "! You will start your next Pyramid tour with " + (itemData.water.bonusRate * 100) + "% more Stamina!", safchan); safaribot.sendMessage(src, itemsLeft(player, "water"), safchan); sys.sendMessage(src, "", safchan); this.saveGame(player); return; } if (item === "soda") { var questList = Object.keys(player.quests); var otherList = ["costume", "auction", "burn"]; questList.splice(questList.indexOf("scientist"), 1); if (!info.target) { safaribot.sendMessage(src, "Choose a quest to reduce the current cooldown with '/use soda:[Quest]! Valid quests are " + readable(questList.map(cap)) + ". You can reduce the cooldown for " + readable(otherList.map(cap)) + ".", safchan); return; } var id = info.target.toLowerCase(); if (!questList.contains(id) && !otherList.contains(id)) { safaribot.sendMessage(src, "Choose a quest to reduce the current cooldown with '/use soda:[Quest]! Valid quests are " + readable(questList.map(cap)) + ". You can reduce the cooldown for " + readable(otherList.map(cap)) + ".", safchan); return; } var n = now(), remaining, cd, result, name; if (otherList.contains(id)) { if (n >= player.cooldowns[id]) { safaribot.sendMessage(src, "You have no cooldown for " + id + "!", safchan); return; } remaining = player.cooldowns[id] - n; cd = Math.round(remaining * itemData.soda.bonusRate); player.cooldowns[id] = result = n + cd; name = id; } else { var quest = player.quests[id]; if (n >= quest.cooldown) { safaribot.sendMessage(src, "You have no cooldown on this quest!", safchan); return; } remaining = quest.cooldown - n; cd = Math.round(remaining * itemData.soda.bonusRate); quest.cooldown = result = n + cd; name = cap(id) + " quest"; } player.balls.soda -= 1; this.updateShop(player, "soda"); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "You drank some " + finishName("soda") + "! Your cooldown for the " + name + " changed from " + toColor(timeLeftString(n + remaining), "red") + " to " + toColor(timeLeftString(result), "blue") + "!", safchan); safaribot.sendMessage(src, itemsLeft(player, "soda"), safchan); sys.sendMessage(src, "", safchan); this.saveGame(player); return; } if (item === "form") { var evType = getEventId(info.target); var unavailableEv = []; if (!evType || unavailableEv.contains(evType)) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "To start an event, use " + link("/use form:EventType:RewardSet", null, true) + ". You can learn about each event by typing " + link("/eventhelp") + ".", safchan); safaribot.sendMessage(src, "Event Type can be Faction War, Inverted War, Race, Bet Race, Battle Factory, LC Battle Factory, Quiz, Bingo, or Volleyball.", safchan); safaribot.sendMessage(src, "Reward Set is the number for the reward you wish to set for the event. Each event type has a set of pre-defined rewards. You can check the available sets by setting the Reward Set to 0. Rewards are given by the game, you don't need to have them.", safchan); safaribot.sendHtmlMessage(src, "When starting an event that has teams (Faction Wars or Volleyball), you can type " + link("/use form:EventType:RewardSet:Team1:Team2", null, true) + " to choose the factions' names.", safchan); safaribot.sendHtmlMessage(src, "When starting a Bingo, you can type " + link("/use form:EventType:RewardSet:Goal", null, true) + " to set a number from 1 to 3 for the goal.", safchan); sys.sendMessage(src, "", safchan); return; } var sets = { factionwar: [ //If 2 entries, each team gets a different reward. If only 1 entry, then same reward for both teams ["5@gacha"], ["5@silver"], ["1@gem"], ["10@rock"], ["3@pearl"], ["50@dust"], ["4@blkapricorn", "4@whtapricorn"], ["3@pnkapricorn", "3@bluapricorn"], ["3@grnapricorn", "3@ylwapricorn"], ["5@luxury", "5@spy"], ["5@gacha", "1@gem"] ], volleyball: [ //If 2 entries, each team gets a different reward. If only 1 entry, then same reward for both teams ["15@silver"], ["3@gem"], ["2@golden"], ["2@rare"], ["5@dew"], ["5@hdew"], ["@eviolite"], ["6@bigpearl"] ], race: [ //'reward' is required, rewardUnderdog and rewardFavorite are optional { reward: "5@gacha", rewardUnderdog: "8@gacha", rewardFavorite: "3@gacha" }, { reward: "10@rock", rewardUnderdog: "13@rock", rewardFavorite: "7@rock" }, { reward: "3@pearl", rewardUnderdog: "4@pearl", rewardFavorite: "2@pearl" }, { reward: "50@dust", rewardUnderdog: "70@dust", rewardFavorite: "40@dust" }, { reward: "4@silver", rewardUnderdog: "6@silver", rewardFavorite: "3@silver" }, { reward: "4@bluapricorn", rewardUnderdog: "6@bluapricorn", rewardFavorite: "3@bluapricorn" }, { reward: "3@grnapricorn", rewardUnderdog: "5@grnapricorn", rewardFavorite: "2@grnapricorn" }, { reward: "3@ylwapricorn", rewardUnderdog: "5@ylwapricorn", rewardFavorite: "2@ylwapricorn" }, { reward: "3@myth", rewardUnderdog: "5@myth", rewardFavorite: "2@myth" }, { reward: "6@gacha"} ], betrace: [ //Only underdog and favorite are optional, everything else is required { minBet: 100, maxBet: 1000, favorite: 2, underdog: 5, normal: 3, bet: "$", reward: "$" }, { minBet: 2, maxBet: 10, favorite: 1.5, underdog: 3, normal: 2, bet: "silver", reward: "silver" }, { minBet: 5, maxBet: 10, favorite: 200, underdog: 600, normal: 400, bet: "silver", reward: "$" }, { minBet: 5, maxBet: 200, favorite: 20, underdog: 60, normal: 40, bet: "safari", reward: "$" }, { minBet: 10, maxBet: 50, favorite: 0.1, underdog: 0.3, normal: 0.2, bet: "safari", reward: "silver" }, { minBet: 10, maxBet: 50, favorite: 0.1, underdog: 0.3, normal: 0.2, bet: "safari", reward: "gacha" }, { minBet: 1, maxBet: 50, favorite: 2, underdog: 6, normal: 4, bet: "rock", reward: "dust" }, { minBet: 1, maxBet: 100, favorite: 35, underdog: 80, normal: 55, bet: "rock", reward: "$" }, { minBet: 3, maxBet: 50, favorite: 0.33, underdog: 0.75, normal: 0.5, bet: "bait", reward: "gacha" } ], bfactory: [ //Must have 2 or 3 entries. ["10@gacha", "6@gacha"], ["2@gem", "1@gem"], ["8@gacha", "5@gacha", "2@gacha"], ["7@pearl", "5@pearl", "3@pearl"], ["7@silver", "5@silver", "3@silver"], ["100@dust", "60@dust", "30@dust"], ["7@luxury", "4@luxury", "2@luxury"], ["7@spy", "4@spy", "2@spy"], ["7@clone", "4@clone", "2@clone"], ["5@quick", "3@quick", "1@quick"], ["5@myth", "3@myth", "1@myth"], ["7@luxury", "4@luxury", "2@luxury"], ["1@burn,$800", "$500", "$250"] ], bingo: [ //1~3 entries. ["8@gacha", "5@gacha", "2@gacha"], ["6@gacha", "5@gacha", "5@gacha"], ["10@gacha", "6@gacha"], ["1@gem", "1@gem", "1@gem"], ["2@gem", "1@gem"], ["3@gem"], ["7@pearl", "5@pearl", "3@pearl"], ["7@silver", "5@silver", "3@silver"], ["9@silver"], ["100@dust", "60@dust", "30@dust"], ["7@luxury", "4@luxury", "2@luxury"], ["9@luxury", "6@luxury"], ["7@spy", "4@spy", "2@spy"], ["9@spy", "6@spy"], ["7@clone", "4@clone", "2@clone"], ["9@clone", "6@clone"], ["5@quick", "3@quick", "1@quick"], ["5@myth", "3@myth", "1@myth"], ["7@luxury", "4@luxury", "2@luxury"], ["1@burn,$800", "$500", "$250"], ["1@burn,$1100", "$750"], ["$1500"] ] }; var rewardSet = parseInt(info.reward || "0", 10); var sub, p = evType; if (evType === "invertedwar") { p = "factionwar"; } else if (["lcbfactory", "quiz", "hquiz"].contains(evType)) { p = "bfactory"; } var picked = sets[p], rew; if (isNaN(rewardSet) || rewardSet < 1 || rewardSet > picked.length) { safaribot.sendHtmlMessage(src, "Reward sets for " + getEventName(evType) + ": ", safchan); var betRange = function(min, max, asset) { if (asset === "$") { return "$" + addComma(min)+"~$" + addComma(max); } else { return min+"~"+plural(max, translateAsset(asset).id); } }; var getPayout = function(value, reward, bet) { if (value >= 1) { return (reward !== "$" ? plural(value, reward) : "$" + addComma(value)) + " for each " + mapAssetName(bet); } else { var val = Math.round(1/value); return (reward !== "$" ? plural(1, reward) : "$" + addComma(1)) + " for each " + (bet === "$" ? "$" + addComma(val) : plural(val, mapAssetName(bet))); } }; for (var r = 0; r < picked.length; r++) { rew = picked[r]; if (p === "factionwar") { if (rew.length === 1) { safaribot.sendMessage(src, "Set " + (r+1) + ": " + translateStuff(rew[0]), safchan); } else { safaribot.sendMessage(src, "Set " + (r+1) + ": " + translateStuff(rew[0]) + " for Team 1, " + translateStuff(rew[1]) + " for Team 2", safchan); } } if (p === "volleyball") { if (rew.length === 1) { safaribot.sendMessage(src, "Set " + (r+1) + ": " + translateStuff(rew[0]), safchan); } else { safaribot.sendMessage(src, "Set " + (r+1) + ": " + translateStuff(rew[0]) + " for Team 1, " + translateStuff(rew[1]) + " for Team 2", safchan); } } else if (p === "race") { if (rew.rewardUnderdog || rew.rewardFavorite) { safaribot.sendMessage(src, "Set " + (r+1) + ": " + (rew.rewardUnderdog ? translateStuff(rew.rewardUnderdog) + " for Underdog, " : "") + (rew.rewardFavorite ? translateStuff(rew.rewardFavorite) + " for Favorite, " : "") + translateStuff(rew.reward) + " for others", safchan); } else { safaribot.sendMessage(src, "Set " + (r+1) + ": " + translateStuff(rew.reward), safchan); } } else if (p === "betrace") { sub = []; if (rew.reward === rew.bet) { if (rew.underdog) { sub.push(rew.underdog + "x if Underdog"); } if (rew.favorite) { sub.push(rew.favorite + "x if Favorite"); } safaribot.sendMessage(src, "Set " + (r+1) + ": Bet: " + betRange(rew.minBet, rew.maxBet, rew.bet) + " | Payout: " + rew.normal+"x the bet" + (sub.length ? " (" + sub.join(", ") + ")": ""), safchan); } else { sub.push(getPayout(rew.normal, rew.reward, rew.bet)); if (rew.underdog) { sub.push(getPayout(rew.underdog, rew.reward, rew.bet) + " if Underdog"); } if (rew.favorite) { sub.push(getPayout(rew.favorite, rew.reward, rew.bet) + " if Favorite"); } safaribot.sendMessage(src, "Set " + (r+1) + ": Bet: " + betRange(rew.minBet, rew.maxBet, rew.bet) + " | Payout: " + sub.join(", "), safchan); } } else if (p === "bfactory") { safaribot.sendMessage(src, "Set " + (r+1) + ": 1st place: " + translateStuff(rew[0]) + " | 2nd place: " + translateStuff(rew[1]) + (rew.length > 2 ? " | 3rd place: " + translateStuff(rew[2]): ""), safchan); } else if (p === "bingo") { safaribot.sendMessage(src, "Set " + (r+1) + ": 1st place: " + translateStuff(rew[0]) + (rew.length > 1 ? " | 2nd place: " + translateStuff(rew[1]) : "") + (rew.length > 2 ? " | 3rd place: " + translateStuff(rew[2]): ""), safchan); } } return; } var reward = picked[rewardSet-1]; if (currentEvent) { safaribot.sendMessage(src, "There's already an event going on!", safchan); return; } if (contestCooldown <= 181) { safaribot.sendMessage(src, "You can't start an event with less than 3 minutes before the next Contest starts!", safchan); return; } if (["factionwar", "invertedwar", "volleyball"].contains(evType) && info.extra1 !== null && info.extra2 !== null && info.extra1.toLowerCase() === info.extra2.toLowerCase()) { safaribot.sendMessage(src, "Please choose different names for each faction!", safchan); return; } if (["bingo"].contains(evType) && info.extra1 !== null) { var n = parseInt(info.extra1, 10); if (isNaN(n) || n < 1 || n > 3) { safaribot.sendMessage(src, "Goal must be between 1, 2 or 3!", safchan); return; } } var ev; sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "You filled all details required and submitted the " + finishName("form") + "! Your event was approved and will start now!", safchan); switch (evType) { case "factionwar": case "invertedwar": var extra1 = info.extra1 ? info.extra1 : poke(sys.rand(1, highestDexNum)); var extra2 = info.extra2 ? info.extra2 : poke(sys.rand(1, highestDexNum)); while (extra1.toLowerCase() === extra2.toLowerCase()) { extra2 = poke(sys.rand(1, highestDexNum)); } ev = new FactionWar(src, reward[0], extra1, extra2, evType == "invertedwar", reward[1], true); break; case "volleyball": var extra1 = info.extra1 ? info.extra1 : poke(sys.rand(1, highestDexNum)); var extra2 = info.extra2 ? info.extra2 : poke(sys.rand(1, highestDexNum)); while (extra1.toLowerCase() === extra2.toLowerCase()) { extra2 = poke(sys.rand(1, highestDexNum)); } if (extra1 == "official") { if (chance(0.5)) { extra1 = officialVolleyballTeam1; extra2 = officialVolleyballTeam2; } else { extra2 = officialVolleyballTeam1; extra1 = officialVolleyballTeam2; } } ev = new Volleyball(src, extra1, extra2, reward[0], reward[1], false); currentGame = ev; break; case "race": ev = new PokeRace(src, "normal", reward, true); break; case "betrace": ev = new PokeRace(src, "bet", reward, true); break; case "bfactory": case "lcbfactory": ev = new BFactory(src, reward[0], reward[1], reward[2], evType == "lcbfactory", true); break; case "quiz": case "hquiz": ev = new Quiz(src, reward[0], reward[1], reward[2], evType === "hquiz", false, true); break; case "bingo": var g = info.extra1 ? parseInt(info.extra1, 10) : 1; ev = new Bingo(src, reward[0], reward[1], reward[2], g, true); break; } player.balls.form -= 1; this.updateShop(player, "form"); safaribot.sendMessage(src, itemsLeft(player, "form"), safchan); safari.saveGame(player); if (evType !== "volleyball") { currentEvent = ev; } safari.flashPlayers(); return; } if (item === "cherry") { if (player.quests.tower.bonusPower > 0) { safaribot.sendMessage(src, "You already ate " + an(finishName("cherry")) + ". There's no reason to eat another before attempting your next Battle Tower challenge!", safchan); return; } player.balls.cherry -= 1; this.updateShop(player, "cherry"); player.quests.tower.bonusPower = itemData.cherry.bonusRate; sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "You and your Pokémon ate " + an(finishName("cherry")) + "! You will start your next Battle Tower challenge more energized and able to deal up to " + (itemData.cherry.bonusRate) + " more damage with your attacks!", safchan); safaribot.sendMessage(src, itemsLeft(player, "cherry"), safchan); sys.sendMessage(src, "", safchan); this.saveGame(player); return; } if (item === "cookie") { player.balls.cookie -= 1; this.updateShop(player, "cookie"); var res = this.randomFortune(); var old = player.fortune.deadline > now() || player.fortune.limit > 0; player.fortune = { name: res.name, val: res.val, prop: res.prop || null, proptype: res.proptype, deadline: (res.deadline ? now() + res.deadline * 60 * 1000 : 0), limit: res.limit || 0, desc: res.desc }; sys.sendMessage(src, "", safchan); if (old) { safaribot.sendMessage(src, "The effect of your previous " + finishName("cookie") + " expired!", safchan); } safaribot.sendMessage(src, "You ate " + an(finishName("cookie")) + "! There was a note inside the cookie saying \"" + this.fortuneDescription(player.fortune) + "\"!", safchan); safaribot.sendMessage(src, itemsLeft(player, "cookie"), safchan); sys.sendMessage(src, "", safchan); player.records.cookiesEaten += 1; this.saveGame(player); return; } if (item === "scale") { var colors = Object.keys(pokeColors); if (!info.target || !colors.contains(info.target.toLowerCase())) { safaribot.sendMessage(src, "To change a Pokémon's color, use /use scale:Color. Colors can be " + readable(colors.map(cap), "or") + ".", safchan); safaribot.sendMessage(src, "Important: This won't turn the Pokémon into shiny or change its image. It will only count as a different color for Contest buffs/nerfs.", safchan); return; } var c = info.target.toLowerCase(); player.balls.scale -= 1; player.scaleColor = c; player.scaleDeadline = now() + itemData.scale.duration * 60 * 1000; player.records.scalesUsed += 1; this.saveGame(player); safaribot.sendMessage(src, "Your active Pokémon will be considered " + cap(c) + " for the next " + plural(itemData.scale.duration, "minute") + "!", safchan); this.updateShop(player, "scale"); safaribot.sendMessage(src, itemsLeft(player, "scale"), safchan); return; } if (item === "crystal") { var active = player.party[0]; var type = getCrystalEffect(active); var buffDesc = zCrystalData[type].description.format(zCrystalData[type].chance * 100); var finalDuration = itemData.crystal.duration * safari.getAuraEffect(player, "extracrystal", 1); if (info.target !== "confirm") { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "When using " + an(finishName("crystal")) + ", you will receive a bonus based on your Active Pokémon's primary type (use /bst to check the Pokémon's first type).", safchan); safaribot.sendMessage(src, "The bonus will work for " + plural(itemData.crystal.duration, "minute") + ", but only while the Pokémon that used the Z-Crystal on is your active Pokémon. The effects occur when throwing any ball except Master Ball.", safchan); safaribot.sendHtmlMessage(src, "Your active Pokémon is " + toColor(poke(active, true), "red") + ", and it can activate " + zCrystalData[type].name + ". If you use a Z-Crystal, you will " + toColor(buffDesc +" for " + plural(finalDuration, "minute"), "red") + ". To use the Z-Crystal, type " + link("/use Z-Crystal:confirm") + ". ", safchan); safaribot.sendMessage(src, "Your lead Pokémon will also have increased damage output in Rotation Battle quests like Celebrity and League, although this will clear the Z-Crystal's effect.", safchan); sys.sendMessage(src, "", safchan); return; } player.balls.crystal-= 1; var cName = zCrystalData[type].name; player.zcrystalUser = player.party[0], player.zcrystalDeadline = now() + Math.floor(finalDuration * 60 * 1000); player.records.crystalsUsed += 1; this.saveGame(player); sys.sendAll("", safchan); safaribot.sendAll(sys.name(src) + " used " + an(cName) + "! Their " + poke(active, true) + " surrounded itself with its Z-Power!", safchan); safaribot.sendMessage(src, "You used " + an(finishName("crystal")) + " on your " + poke(active, true) + "! You will " + buffDesc + " for " + plural(Math.floor(finalDuration), "minute") + " (only if " + poke(active, true) + " is your active Pokémon)!", safchan); this.updateShop(player, "crystal"); safaribot.sendMessage(src, itemsLeft(player, "crystal"), safchan); sys.sendAll("", safchan); return; } if (item === "mushroom") { var possibleResults = Object.keys(contestThemes); if (possibleResults.contains("none")) { possibleResults.splice(possibleResults.indexOf("none"), 1); } if (player.mushroomDeadline > 0 && possibleResults.contains(player.mushroomTheme)) { possibleResults.splice(possibleResults.indexOf(player.mushroomTheme), 1); } var toConsume = 1; if (canHaveAbility(player.party[0], abilitynum("Mycelium Might")) && player.balls.mushroom >= 2) { toConsume = 2; safaribot.sendMessage(src, "Your {0}'s Mycelium Might caused you to consume {1} instead of 1!".format(poke(player.party[0], true), plural(toConsume, "mushroom")), safchan); } player.balls.mushroom -= toConsume; player.mushroomTheme = possibleResults.random(); var dur = itemData.mushroom.duration; if (this.hasCostumeSkill(player, "extendedMushroom")) { dur = Math.floor(dur * 1.5); } dur *= toConsume; player.mushroomDeadline = dur; player.records.mushroomsEaten += toConsume; this.saveGame(player); safaribot.sendHtmlMessage(src, "You ate a suspicious " + finishName("mushroom") + "! As you get dizzier and dizzier, you start thinking that you are in " + link("/themerares " + player.mushroomTheme, an(themeName(player.mushroomTheme) + " environment")) + " for the next " + player.mushroomDeadline + " Pokémon that you bait! (Effect can be cancelled with " + link("/shroomcancel", false, true) + ")", safchan); this.updateShop(player, "mushroom"); safaribot.sendMessage(src, itemsLeft(player, "mushroom"), safchan); return; } if (item === "brush") { if (!info.target) { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "To modify a photo, type /use brush:PhotoNumber:WhatToModify. Example: /use brush:3:period would change the period (Morning, Afternoon, Evening or Night) of the 3rd photo in your album to a random different one.", safchan); safaribot.sendMessage(src, "You can modify number of Pokémon in the photo, their mood, action, environment, period or quality.", safchan); safaribot.sendMessage(src, "Note: When editing Actions, you will only receive common Actions as a result, you cannot receive any Actions that are specific to certain types.", safchan); sys.sendMessage(src, "", safchan); return; } var id = parseInt(info.target, 10); if (isNaN(id) || id < 1 || id > player.photos.length) { safaribot.sendMessage(src, "This is not a valid photo!", safchan); return; } if (!info.reward) { safaribot.sendMessage(src, "To modify a photo, use /use brush:PhotoNumber:WhatToModify. You can edit Amount, Period, Mood, Action, Environment or Quality.", safchan); safaribot.sendMessage(src, "Note: When editing Actions, you will only receive common Actions as a result, you cannot receive any Actions that are specific to certain types.", safchan); return; } var photo = player.photos[id-1]; var what = info.reward.toLowerCase(); var scoreLostRange = [1, 3]; var possibleResults; switch (what) { case "amt": case "amount": case "quantity": case "number": if (photo.amt >= 4) { safaribot.sendMessage(src, "You can't further increase the number of Pokémon on this photo!", safchan); return; } what = "amt"; scoreLostRange = [2, 5]; break; case "when": case "period": case "time": what = "when"; possibleResults = ["night", "morning", "afternoon", "evening"]; break; case "mood": case "humor": case "humour": what = "mood"; possibleResults = photoMood.Positive.concat(photoMood.Neutral).concat(photoMood.Negative); break; case "action": case "what": case "act": what = "what"; possibleResults = photoActions.Any.concat(); break; case "where": case "place": case "theme": case "environment": what = "where"; possibleResults = Object.keys(contestThemes); if (possibleResults.contains("none")) { possibleResults.splice(possibleResults.indexOf("none"), 1); } possibleResults.push("default"); break; case "quality": case "score": if (photo.score >= 10) { safaribot.sendMessage(src, "This photo's quality is already Perfect!", safchan); return; } what = "score"; break; } var editables = ["amt", "when", "what", "where", "mood", "score"]; if (!editables.contains(what)) { safaribot.sendMessage(src, info.reward + " is not an editable property for the photo! You can edit Amount, Period, Mood, Action, Environment or Quality.", safchan); return; } var translations = { amt: "number of Pokémon", when: "period", what: "action", where: "environment", mood: "mood", score: "quality" }; var confirmation = info.extra1 || "*"; if (confirmation.toLowerCase() !== "confirm") { safaribot.sendHtmlMessage(src, "You are going use " + an(finishName("brush")) + " to modify the " + translations[what] + " of this photo of " + toColor(this.describePhoto(photo), "red") + ". To confirm, type " + link("/use brush:" + id + ":"+ info.reward + ":confirm") + ".", safchan); return; } var oldDesc = this.describePhoto(photo); var getValueTranslation = function(value, type) { switch (type) { case "score": return photoQuality[value]; case "where": return themeName(value); case "action": case "mood": case "when": case "amt": return value; } }; var oldValue = getValueTranslation(photo[what], what); var oldQuality = getValueTranslation(photo.score, "score"); var qualityLost = false; //var qualityDropChance = { amt: 0.8, when: 0.33, what: 0.6, where: 0.4, mood: 0.5, score: 0.45 }; if (what === "amt") { photo.amt += parseInt(randomSample({ "1": 90, "2": 9, "3": 1}), 10); } else if (what !== "score") { var res; do { res = possibleResults.random(); } while (res === photo[what]); photo[what] = res; } /*if (chance(qualityDropChance[what])) { if (photo.score > 0) { qualityLost = true; } photo.score = Math.max(0, photo.score - sys.rand(scoreLostRange[0], scoreLostRange[1])); } else*/ if (what === "score") { photo.score = Math.min(10, photo.score + [1, 1, 1, 1, 2, 2, 3].random()); } var newValue = getValueTranslation(photo[what], what); var newQuality = getValueTranslation(photo.score, "score"); //var changemsg = what === "score" && qualityLost ? "" : "The " + translations[what] + " changed from " + toColor(oldValue, "red") + " to " + toColor(newValue, "blue") + "! "; //qualityLost = qualityLost ? "The quality dropped from " + toColor(oldQuality, "blue") + " to " + toColor(newQuality, "red") : ""; //safaribot.sendHtmlMessage(src, changemsg + qualityLost, safchan); safaribot.sendHtmlMessage(src, "Your photo of " + toColor(oldDesc, "red") + " was edited and is now a photo of " + toColor(this.describePhoto(photo), "blue") + "!", safchan); this.updateShop(player, "brush"); player.records.photosRetouched += 1; player.balls.brush -= 1; safaribot.sendMessage(src, itemsLeft(player, "brush"), safchan); this.saveGame(player); return; } }; this.useMail = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), info = data.split(":"), target = info[0], targetPlayer = getAvatarOff(target), msg = html_escape(info.splice(1).join(":")); if (player.balls.mail <= 0) { safaribot.sendMessage(src, "You need " + an(finishName("mail")) + " to use this command!", safchan); return; } if (!targetPlayer) { safaribot.sendMessage(src, "No such player!", safchan); return; } if (!msg) { safaribot.sendMessage(src, "Invalid syntax! Use /mail Name:Message to send a message to another player's inbox.", safchan); return; } player.balls.mail -= 1; player.records.mailsSent += 1; safaribot.sendMessage(src, target.toCorrectCase() + " will receive the following message in their inbox: [" + sys.name(src) + "] " + msg, safchan); var tId = sys.id(target); safari.notification(targetPlayer, "You received a mail from " + toColor(sys.name(src), "red") + ". To read it, type " + link("/inbox unread") + ".", "Mail"); /*if (tId && sys.isInChannel(tId, safchan)) { safaribot.sendHtmlMessage(tId, "You received a mail from " + toColor(sys.name(src), "red") + ". To read it, type " + link("/inbox") + ".", safchan); }*/ this.inboxMessage(targetPlayer, "[" + sys.name(src) + "] " + msg, false); this.saveGame(player); this.saveGame(targetPlayer); }; this.showCurrentViewers = function(src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var printViewers = function(arr) { var ret = arr.map(function(e) { return e.toCorrectCase(); }).filter(function(e) { return e.toLowerCase() !== player.id; }); return ret.length > 0 ? ret : ""; }; var hit = false; for (var b in currentBattles) { // could use safari.isBattling but we need to loop through currentBattles anyway since we need the Battle/Battle2 object and not just a boolean if (currentBattles[b].isInBattle(player.id)) { safaribot.sendMessage(src, "Currently watching your battle: " + printViewers(currentBattles[b].viewers), safchan); hit = true; break; } } for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(player.id)) { safaribot.sendMessage(src, "Currently watching your Pyramid run: " + printViewers(currentPyramids[p].viewers), safchan); hit = true; break; } } for (b in currentBakings) { if (currentBakings[b].isInKitchen(player.id)) { safaribot.sendMessage(src, "Currently watching your Baking quest: " + printViewers(currentBakings[b].players.concat(currentBakings[b].viewers)), safchan); hit = true; break; } } if (!hit) { safaribot.sendMessage(src, "You are not in any battle/Pyramid run/Baking quest!", safchan); } }; this.watchInvite = function(src, tar) { if (!validPlayers("self", src)) { return; } var targetId = sys.id(tar); var target = getAvatar(targetId); if (!target) { safaribot.sendMessage(src, "Couldn't find a player by that name!", safchan); return; } if (targetId === src) { safaribot.sendMessage(src, "You can't invite yourself!", safchan); return; } var player = getAvatar(src); var hit = false; for (var b in currentBattles) { if (currentBattles[b].isInBattle(player.id)) { currentBattles[b].whitelist.push(target.id); safaribot.sendHtmlMessage(targetId, sys.name(src) + " is inviting your to watch their current battle! Use " + link("/watch " + sys.name(src)) + " to do so.", safchan); safaribot.sendMessage(src, "You invited " + sys.name(targetId) + " to watch your battle!", safchan); hit = true; break; } } for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(player.id)) { safaribot.sendHtmlMessage(targetId, sys.name(src) + " is inviting your to watch their current Pyramid run! Use " + link("/watchpyr " + sys.name(src)) + " to do so.", safchan); safaribot.sendMessage(src, "You invited " + sys.name(targetId) + " to watch your Pyramid run!", safchan); hit = true; break; } } for (b in currentBakings) { if (currentBakings[b].isInKitchen(player.id)) { safaribot.sendHtmlMessage(targetId, sys.name(src) + " is inviting your to watch their current Baking quest! Use " + link("/watchbak " + sys.name(src)) + " to do so.", safchan); safaribot.sendMessage(src, "You invited " + sys.name(targetId) + " to watch your Baking quest!", safchan); hit = true; break; } } if (!hit) { safaribot.sendMessage(src, "You are not in any battle/Pyramid run/Baking quest!", safchan); } }; this.configurePlayerOptions = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var dataInput = data.split(":")[1] || "*"; data = data.split(":")[0]; dataInput = dataInput.toLowerCase(); var changeOption = function(input, prop, flavour) { var flavourOn = flavour[0], flavourOff = flavour[1], flavourDefault = flavour[2], // array flavourCommand = flavour[3]; if (input === "on") { player.options[prop] = true; safaribot.sendHtmlMessage(src, flavourOn, safchan); } else if (input === "off") { player.options[prop] = false; safaribot.sendHtmlMessage(src, flavourOff, safchan); } else { var index = player.options[prop] | 0; // 0 for false, 1 for true safaribot.sendHtmlMessage(src, "You are currently {0}! Use {1} to change it.".format( flavourDefault[index], link("/options " + flavourCommand + ":" + ["on", "off"][index]) ), safchan); return; } safari.saveGame(player); }; switch (data) { case "view": changeOption(dataInput, "visible", [ "Now allowing other players to view your party and battles!", "Now disallowing other players from viewing your party and battles!", ["hidden from other players", "visible to other players"], data ]); break; case "flashme": changeOption(dataInput, "flashme", [ "You will now be flashed when a Contest or event starts!", "You will no longer be flashed when a Contest or event starts!", ["not being flashed when a Contest or event starts", "being flashed when a Contest or event starts"], data ]); break; case "smallbox": changeOption(dataInput, "smallBox", [ "You will now see a narrower Pokémon box!", "You will now see a regular-sized Pokémon box!", ["using a regular box view", "using a narrower box view"], data ]); break; case "trade": case "trades": case "trading": changeOption(dataInput, "trading", [ "You enabled trade requests!", "You disabled trade requests!", ["rejecting trade requests", "accepting trade requests"], data ]); break; case "autoforfeit": case "autoforfeitthrow": changeOption(dataInput, "autoForfeitThrow", [ "You enabled auto-forfeiting when throwing Balls at a rare Pokémon during a battle!", "You disabled auto-forfeiting when throwing Balls at a rare Pokémon during a battle!", ["not auto-forfeiting if you throw a Ball at a rare Pokémon during a battle", "auto-forfeiting if you throw a Ball at a rare Pokémon during a battle"], data ]); break; case "sellform": case "sellforms": case "sellforme": case "sellformes": case "cansellform": case "cansellforms": case "cansellforme": case "cansellformes": changeOption(dataInput, "canSellFormes", [ "You enabled selling Pokémon formes to the NPC!", "You disabled selling Pokémon formes to the NPC!", ["blocking Pokémon forme sales to the NPC", "allowing Pokémon forme sales to the NPC"], data ]); break; case "showevo": case "showdevo": case "showevos": case "showdevos": changeOption(dataInput, "showEvoMessages", [ "You will now view all evolution/devolution messages!", "You will now only see your own evolution/devolution messages! Note: Rare evolutions/devolutions from other players will always be shown.", ["only viewing your own evolution/devolution messages", "viewing all evolution/devolution messages"], data ]); break; case "broadcastevo": case "printevo": case "broadcastdevo": case "printdevo": case "broadcastevos": case "printevos": case "broadcastdevos": case "printdevos": changeOption(dataInput, "broadcastEvoMessages", [ "You will now broadcast your evolution/devolution messages to the channel!", "You will no longer broadcast your evolution/devolution messages to the channel! Note: Rare evolutions/devolutions will always be broadcast.", ["not broadcasting your evolution/devolution messages", "broadcasting your evolution/devolution messages"], data ]); break; case "notifs": case "notifications": case "anynotifs": case "anynotifications": changeOption(dataInput, "anyNotifications", [ "You will now receive notifications!", "You will no longer receive any notifications!", ["not receiving notifications", "receiving notifications"], data ]); break; case "questnotif": case "questnotifs": case "questnotification": case "questnotifications": if (!player.options.anyNotifications) { safaribot.sendHtmlMessage(src, "You currently have notifications disabled, please {0} before trying to enable quest notifications.".format(link("/options notifs:on","turn notifications on")), safchan); break; } changeOption(dataInput, "questNotifications", [ "You will now receive quest notifications!", "You will no longer receive quest notifications!", ["not receiving quest notifications", "receiving quest notifications"], data ]); break; case "persistentbait": case "persistbait": case "baitpersist": case "baitpersistent": changeOption(dataInput, "persistentBait", [ "When baiting, your bait will now be used continuously until you attract a wild Pokémon!", "When baiting, you will now only use 1 bait at a time!", ["using 1 bait at a time", "continuously using bait"], data ]); break; case "androidlag": case "androidinputlag": changeOption(dataInput, "androidTextFlow", [ "You will now receive continuous server messages to offset chat input lag on Android! Blank server messages will be sent to you in " + link("/cjoin Safari Android Spam", "#Safari Android Spam") + ".", "You will no longer receive continuous server messages to offset chat input lag on Android!", ["not receiving continuous server messages", "receiving continuous server messages"], data ]); break; case "weeklymedals": case "receiveweeklymedals": case "weeklymedal": case "receiveweeklymedal": changeOption(dataInput, "receiveWeeklyMedals", [ "You will now receive medals for being in the top 3 of the weekly leaderboards!", "You will no longer receive medals for being in the top 3 of the weekly leaderboards!", ["not receiving medals for being in the top 3 of the weekly leaderboards", "receiving medals for being in the top 3 of the weekly leaderboards"], data ]); break; case "showcontestcaptures": case "contestcaptures": case "viewcontestcaptures": changeOption(dataInput, "showContestCaptures", [ "You will now see your Contest capture count and BST tally during Contests!", "You will no longer see your Contest capture count and BST tally during Contests!", ["not seeing your Contest capture count and BST tally during Contests", "seeing your Contest capture count and BST tally during Contests"], data ]); break; case "showcombo": case "showconsecutive": case "showconsecutivecombo": changeOption(dataInput, "showConsecutiveCombo", [ "You will now see your Consecutive Catch Combo after each capture!", "You will no longer see your Consecutive Catch Combo after each capture!", ["not seeing your Consecutive Catch Combo after each capture", "seeing your Consecutive Catch Combo after each capture"], data ]); break; case "pingrare": case "pingrares": case "flashrare": case "flashrares": changeOption(dataInput, "flashRares", [ "You will now be flashed when a rare Pokémon spawns!", "You will no longer be flashed when a rare Pokémon spawns!", ["not being flashed when a rare Pokémon spawns", "being flashed when a rare Pokémon spawns"], data ]); break; case "textonly": case "textviewonly": case "bagtonly": case "bagtextonly": changeOption(dataInput, "textViewOnly", [ "You will now view the text version of your bag by default!", "You will now view the full version of your bag by default!", ["viewing the full version of your bag by default", "viewing the text version of your bag by default"], data ]); break; case "baitscramble": changeOption(dataInput, "baitScramble", [ "You will now automatically prepare to throw a Ball if someone baits a Pokémon before you!", "You will no longer automatically prepare to throw a Ball if someone baits a Pokémon before you!", ["set to not throw a Ball if someone baits a Pokémon before you", "set to automatically throw a Ball if someone baits a Pokémon before you"], data ]); break; case "favorite": case "favourite": case "favoriteball": case "favouriteball": safari.setFavoriteBall(src, dataInput); break; case "pokeskills": case "idolskills": safari.idolQuest(src, ["toggle"]); break; case "mblink": case "masterballlink": case "mbmacro": case "masterballmacro": safari.setEnableMasterBall(src, dataInput); break; case "cherishlink": case "cherishballlink": case "cherishmacro": case "cherishballmacro": safari.setEnableCherishBall(src, dataInput); break; case "sellprompt": case "sellprompts": safari.setEnableSellPrompt(src, dataInput); break; case "mono": safari.setMonoType(src, dataInput); break; case "cherishmsg": safari.cherishVisible(src, dataInput); break; case "showdex": case "hidedex": safari.setDexOptional(src, data, dataInput); break; case "showability": case "abilitymessage": case "showabilitymessage": safari.setShowAbilityMessage(src, dataInput); break; case "showlead": case "leadmessage": case "showleadmessage": safari.setShowLeadMessage(src, dataInput); break; default: sys.sendMessage(src, "", safchan); if (data === "2") { sys.sendMessage(src, "*** Safari Settings | Page 2 ***", safchan); safaribot.sendHtmlMessage(src, "Contest/Event Flashes: " + link("/options flashme:", player.options.flashme ? "Enabled" : "Disabled"), safchan); safaribot.sendHtmlMessage(src, "Rare Pokémon Flashes: " + link("/options flashrare:", player.options.flashRares ? "Enabled" : "Disabled"), safchan); safaribot.sendHtmlMessage(src, "Cherish Ball Message: " + link("/options cherishmsg:", player.options.cherishOff ? "Do Not Show" : "Always Show"), safchan); safaribot.sendHtmlMessage(src, "Other Players' Evolution/Devolution Messages: " + link("/options showevo:", player.options.showEvoMessages ? "Show Everyone's Evolutions/Devolutions" : "Only Show My Own Evolutions/Devolutions"), safchan); safaribot.sendHtmlMessage(src, "Your Evolution/Devolution Messages: " + link("/options broadcastevo:", player.options.broadcastEvoMessages ? "Broadcast My Evolutions/Devolutions" : "Do Not Broadcast My Evolutions/Devolutions"), safchan); safaribot.sendHtmlMessage(src, "Notifications: " + link("/options notifs:", player.options.anyNotifications ? "Receiving Notifications" : "Not Receiving Notifications"), safchan); if (player.options.anyNotifications) { safaribot.sendHtmlMessage(src, "Quest Notifications: " + link("/options questnotifs:", player.options.questNotifications ? "Receiving Quest Notifications" : "Not Receiving Quest Notifications"), safchan); } safaribot.sendHtmlMessage(src, "Weekly Leaderboard Medals: " + link("/options weeklymedals:", player.options.receiveWeeklyMedals ? "Receiving Weekly Medals" : "Not Receiving Weekly Medals"), safchan); safaribot.sendHtmlMessage(src, "Persistent Bait: " + link("/options persistbait:", player.options.persistentBait ? "Continuously Bait until Successful" : "Only Use 1 Bait at a Time"), safchan); safaribot.sendHtmlMessage(src, "View Contest Captures: " + link("/options viewcontestcaptures:", player.options.showContestCaptures ? "View My Contest Captures & BST Tally During Contests" : "Do Not View My Contest Captures & BST Tally During Contests"), safchan); safaribot.sendHtmlMessage(src, "View Consecutive Catch Combo: " + link("/options showcombo:", player.options.showConsecutiveCombo ? "View My Consecutive Catch Combo After Each Capture" : "Do Not View My Consecutive Catch Combo After Each Capture"), safchan); safaribot.sendHtmlMessage(src, "Offset Android Input Lag: " + link("/options androidlag:", player.options.androidTextFlow ? "Receive Continuous Server Messages" : "Do Not Receive Continuous Server Messages"), safchan); safaribot.sendHtmlMessage(src, "Text-Only Bag: " + link("/options textonly:", player.options.textViewOnly ? "View Text Version of Bag by Default" : "View Full Version of Bag by Default"), safchan); safaribot.sendHtmlMessage(src, "Bait Scramble: " + link("/options baitscramble:", player.options.baitScramble ? "Automatically Throw a Ball When Someone Baits Before You" : "Do Not Throw a Ball When Someone Baits Before You"), safchan); var dexOptions = ["stats", "effectiveness", "trivia"]; safaribot.sendHtmlMessage(src, "Dex Options: " + dexOptions.map(function(e) { return player.options.dexOptional.contains(e) ? link("/options hidedex:" + e, cap(e)) + " [Enabled]" : link("/options showdex:" + e, cap(e)) + " [Disabled]"; }).join(", "), safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, link("/options 1","«Previous Page»"),safchan); } else { sys.sendMessage(src, "*** Safari Settings | Page 1 ***", safchan); safaribot.sendHtmlMessage(src, "Party/Battle Visibility: " + link("/options view:", player.options.visible ? "Visible" : "Hidden"), safchan); safaribot.sendHtmlMessage(src, "Small Box View: " + link("/options smallbox:", player.options.smallBox ? "Enabled" : "Disabled"), safchan); safaribot.sendHtmlMessage(src, "Trade Requests: " + link("/options trade:", player.options.trading ? "Accepting" : "Rejecting"), safchan); safaribot.sendHtmlMessage(src, "Mono Ball Type: " + link("/options mono:", player.options.monoSecondary ? "Secondary Type" : "Main Type"), safchan); safaribot.sendHtmlMessage(src, "Favorite Ball: " + link("/options favorite:", finishName(player.options.favoriteBall)), safchan); safaribot.sendHtmlMessage(src, "Idol Skills: " + link("/options idolskills:", player.options.pokeskillsDisabled ? "Disabled" : "Enabled"), safchan); safaribot.sendHtmlMessage(src, "Cherish Ball Link: " + link("/options cherishlink:", player.options.alwaysShowCherishBall ? "Always Active" : "Inactive"), safchan); safaribot.sendHtmlMessage(src, "Master Ball Link: " + link("/options mblink:", player.options.alwaysShowMasterBall ? "Always Active" : "Only Active on Rare Pokémon"), safchan); safaribot.sendHtmlMessage(src, "Sell Prompts: " + link("/options sellprompt:", player.options.sellPrompt ? "Always Show" : "Do Not Show"), safchan); safaribot.sendHtmlMessage(src, "Sell Pokémon formes to NPC: " + link("/options cansellformes:", player.options.canSellFormes ? "Allow Pokémon Forme Sales to the NPC" : "Do Not Allow Pokémon Forme Sales to the NPC"), safchan); safaribot.sendHtmlMessage(src, "Lead Ability Messages: " + link("/options abilitymessage:", player.options.leadAbilityMessages ? "Always Show" : "Do Not Show"), safchan); safaribot.sendHtmlMessage(src, "Lead Display Messages: " + link("/options leadmessage:", player.options.showLeadMessage ? "Show if No Relevant Ability" : "Do Not Show"), safchan); safaribot.sendHtmlMessage(src, "Auto-Forfeit Battle: " + link("/options autoforfeit:", player.options.autoForfeitThrow ? "Automatically Forfeit When Throwing on Rare Pokémon" : "Do Not Forfeit When Throwing on Rare Pokémon"), safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, link("/options 2","«Next Page»"),safchan); } break; } }; this.setDexOptional = function(src, data, dataInput) { var player = getAvatar(src); if (!player) { return; } if (!["stats", "effectiveness", "trivia"].contains(dataInput)) { safaribot.sendMessage(src, "Toggleable dex options are stats, effectiveness, and trivia.", safchan); return; } if (data == "showdex") { if (player.options.dexOptional.contains(dataInput)) { safaribot.sendMessage(src, "The dex is already showing " + dataInput + " for you!", safchan); return; } player.options.dexOptional.push(dataInput); safaribot.sendMessage(src, "The dex will now show " + dataInput + ".", safchan); safari.saveGame(player); } else if (data == "hidedex") { if (!player.options.dexOptional.contains(dataInput)) { safaribot.sendMessage(src, "The dex already isn't showing " + dataInput + " for you!", safchan); return; } player.options.dexOptional.splice(player.options.dexOptional.indexOf(dataInput), 1); safaribot.sendMessage(src, "The dex will no longer show " + dataInput + ".", safchan); safari.saveGame(player); } }; this.setEnableSellPrompt = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (!["on", "off"].contains(data)) { safaribot.sendHtmlMessage(src, "You can edit your sell prompt settings here. Sell prompts are links that appear after you catch a wild Pokémon that allow you to instantly sell that Pokémon to the NPC. A sell prompt will never appear after you catch a rare Pokémon such as Legendaries or Shinies.", safchan); safaribot.sendHtmlMessage(src, "Your sell prompts are currently {0}. Use {1} to change it!".format((player.options.sellPrompt ? "enabled" : "disabled"), link("/options sellprompt:" + (player.options.sellPrompt ? "off" : "on"))), safchan); return; } if (data === "off") { player.options.sellPrompt = false; safaribot.sendHtmlMessage(src, "Sell prompts will now be disabled after catching a wild Pokémon!", safchan); } else { player.options.sellPrompt = true; safaribot.sendHtmlMessage(src, "Sell prompts will now be enabled after catching a wild Pokémon!", safchan); } safari.saveGame(player); }; this.setShowAbilityMessage = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (!["on", "off"].contains(data)) { safaribot.sendHtmlMessage(src, "Your lead ability messages are currently {0}. Use {1} to change it!".format((player.options.leadAbilityMessages ? "enabled" : "disabled"), link("/options showability:" + (player.options.leadAbilityMessages ? "off" : "on"))), safchan); return; } if (data === "off") { player.options.leadAbilityMessages = false; safaribot.sendHtmlMessage(src, "Lead ability messages will now be disabled!", safchan); } else { player.options.leadAbilityMessages = true; safaribot.sendHtmlMessage(src, "Lead ability messages will now be enabled!", safchan); } safari.saveGame(player); }; this.setShowLeadMessage = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (!["on", "off"].contains(data)) { safaribot.sendHtmlMessage(src, "Your lead display messages are currently {0}. Use {1} to change it!".format((player.options.showLeadMessage ? "enabled" : "disabled"), link("/options showlead:" + (player.options.showLeadMessage ? "off" : "on"))), safchan); return; } if (data === "off") { player.options.showLeadMessage = false; safaribot.sendHtmlMessage(src, "Lead display messages will now be disabled!", safchan); } else { player.options.showLeadMessage = true; safaribot.sendHtmlMessage(src, "Lead display messages will now be enabled!", safchan); } safari.saveGame(player); }; this.setEnableMasterBall = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (!["on", "off"].contains(data)) { safaribot.sendHtmlMessage(src, "Your {0} throw links are currently being {1} on regular Pokémon. Use {2} to change it!".format(finishName("master"), (player.options.alwaysShowMasterBall ? "enabled" : "disabled"), link("/options mblink:" + (player.options.alwaysShowMasterBall ? "off" : "on"))), safchan); return; } if (data === "off") { player.options.alwaysShowMasterBall = false; safaribot.sendHtmlMessage(src, "Your {0} throw links will now be disabled on regular Pokémon! Remember that you can still use /throw or /catch to manually throw a {0}!".format(finishName("master")), safchan); } else { player.options.alwaysShowMasterBall = true; safaribot.sendHtmlMessage(src, "Your {0} throw links will now be enabled on regular Pokémon!".format(finishName("master")), safchan); } safari.saveGame(player); }; this.setEnableCherishBall = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (!["on", "off"].contains(data)) { safaribot.sendHtmlMessage(src, "Your {0} throw links are currently being {1} on regular Pokémon. Use {2} to change it!".format(finishName("cherish"), (player.options.alwaysShowCherishBall ? "enabled" : "disabled"), link("/options cherishlink:" + (player.options.alwaysShowCherishBall ? "off" : "on"))), safchan); return; } if (data === "off") { player.options.alwaysShowCherishBall = false; safaribot.sendHtmlMessage(src, "Your {0} throw links will now be disabled on regular Pokémon! Remember that you can still use /throw or /catch to manually throw a {0}!".format(finishName("cherish")), safchan); } else { player.options.alwaysShowCherishBall = true; safaribot.sendHtmlMessage(src, "Your {0} throw links will now be enabled on regular Pokémon!".format(finishName("cherish")), safchan); } safari.saveGame(player); }; this.setMonoType = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var main = ["1", "1st", "one", "first", "main"], secondary = ["2", "2nd", "two", "second", "secondary"]; if (main.concat(secondary).contains(data) && cantBecause(src, "set your " + finishName("mono") + " type", ["wild"])) { return; } var active = this.getEffectiveLead(player, true); var t = type1(active); data = data.toLowerCase(); if (main.contains(data)) { player.options.monoSecondary = false; } else if (secondary.contains(data)) { player.options.monoSecondary = true; t = type2(active) !== "???" ? type2(active) : t; } else { t = player.options.monoSecondary ? (type2(active) !== "???" ? type2(active) : t) : t; var toSend = "Your {0} is configured to use your Pokémon's {1} type{2}! Currently using {3}'s {4}, you can swap settings by using {5}!".format(finishName("mono"), toColor((player.options.monoSecondary ? "secondary" : "main"), "blue"), (player.options.monoSecondary ? " (only if the Pokémon has two types)" : ""), poke(active, true), typeIcon(t), (player.options.monoSecondary ? link("/options mono:1") : link("/options mono:2"))); safaribot.sendHtmlMessage(src, toSend, safchan); return; } safaribot.sendHtmlMessage(src, "Your " + finishName("mono") + " will now use your Pokémon's " + toColor((player.options.monoSecondary ? "secondary" : "main"), "blue") + " type" + (player.options.monoSecondary ? " (only if the Pokémon has two types)" : "") + "! Currently using " + poke(active, true) + "'s " + typeIcon(t) + "!", safchan); this.saveGame(player); }; this.randomFortune = function() { var res = JSON.parse(JSON.stringify(fortuneData.random())); if (Array.isArray(res.val)) { res.val = res.val.random(); } else if (typeof res.val === "object") { res.val = parseFloat(randomSample(res.val)); } var randomRangeSteps = function(min, max, inc) { var p = sys.rand(0, (max - min) / inc); return min + (p*inc); }; if (res.hasOwnProperty("deadline")) { if (Array.isArray(res.deadline)) { res.deadline = randomRangeSteps(res.deadline[0], res.deadline[1], (res.interval || 1)); } delete res.limit; } else if (res.hasOwnProperty("limit")) { if (Array.isArray(res.limit)) { res.limit = randomRangeSteps(res.limit[0], res.limit[1], (res.interval || 1)); } } if (res.prop) { if (Array.isArray(res.prop)) { res.prop = res.prop.random(); } else if (typeof res.prop === "object") { res.prop = randomSample(res.prop); } } return res; }; this.fortuneDescription = function(res) { var out = res.desc.replace(/{Percent}/gi, Math.round(res.val*100) + "%") .replace(/{Value}/gi, res.val) // .replace(/{Time}/gi, utilities.getTimeString((res.deadline * 60 - now())/1000)) .replace(/{Time}/gi, utilities.getTimeString((res.deadline - now()+10)/1000, true)) .replace(/{Limit}/gi, res.limit) .replace(/{Item}/gi, finishName(res.prop || "")) .replace(/{Type}/gi, cap(res.prop || "")) .replace(/{Quest}/gi, cap(res.prop || "")); return out; }; this.useFortuneCharge = function(player, type, amt, prop) { var fort = player.fortune; if (fort.limit === 0) { return; } if (fort.prop && fort.prop !== prop) { return; } if (!this.isMatchingFortune(fort, type, prop)) { return; } player.fortune.limit -= (amt || 1); if (player.fortune.limit === 0) { safaribot.sendHtmlMessage(sys.id(player.id), "Your Fortune Cookie effect has now been fully used up!", safchan); } }; this.isMatchingFortune = function(fortune, type, prop) { return fortune.name === type && (!prop || fortune.prop === prop); }; this.getFortune = function(player, type, def, prop, skipCharge) { var fort = player.fortune; if (fort.deadline < now() && fort.limit <= 0) { return def; } if (this.isMatchingFortune(fort, type, prop)) { if (!skipCharge && fort.limit > 0) { this.useFortuneCharge(player, type, 1, prop); } return fort.val; } return def; }; /* Costume EXP stuff */ var costumeExpInfo = { "none": "None", "daycareplay": "Play with Pokémon in the Daycare", "wincontest": "Win Contests", "catch": "Catch Pokémon", "catchwater": "Catch Water-type Pokémon", "clonepoke": "Clone Pokémon", "catchhighbst": "Catch Pokémon with BST 540 or higher", "catchlowbst": "Catch Pokémon with BST 270 or lower", "wintour": "Win Event Tournaments", "wintrivia": "Win Event Trivia games", "winmafia": "Participate in Event Mafia games", "fighttower": "Participate in the Tower quest", "findrare": "Find rare items with Itemfinder", "bait": "Bait Pokémon", "journal": "Participate in the Journal quest", "scientist": "Earn " + es(finishName("silver")) + " from the Scientist quest", "arenasilver": "Earn " + es(finishName("silver")) + " from the Arena quest", "soda": "Win " + es(finishName("soda")) + " from Event Trivia games", "stealpoke": "Snag Pokémon from NPCs", "takephoto": "Take photos of Pokémon" }; var costumeSkillInfo = { botdboost: "Catch rate increased when using the Pokémon-of-the-Day", lowPhotoCD: "Reduced cooldown after taking photos", finderBasedOnLead: "Your lead Pokémon's primary type will influence the Itemfinder, allowing you to find unique items", pokefanPack: "Received " + an(finishName("egg")) + " for completing your training", ninjaPack1: "Received prizes for leveling up", ninjaPack2: "Received prizes for leveling up", flowerPack: "Received prizes for leveling up", fisherPack: "Received prizes for leveling up", rocketPack: "Received prizes for leveling up", preschoolerPack1: "Received prizes for leveling up", preschoolerPack2: "Received prizes for leveling up", preschoolerPack3: "Received prizes for leveling up", preschoolerPack4: "Received prizes for leveling up", preschoolerPack5: "Received prizes for leveling up", preschoolerPack6: "Received prizes for leveling up", preschoolerPack7: "Received prizes for leveling up", preschoolerPack8: "Received prizes for leveling up", preschoolerPack9: "Received prizes for leveling up", preschoolerPack10: "Received prizes for leveling up", typeMatchupHelper: "Displays type effectiveness of your lead Pokémon against wild Pokémon", ballHint: "Displays the most effective Ball for you to use against the current wild Pokémon", fasterFinder: "Reduced cooldown after using the Itemfinder", evolveCheap: "Use fewer " + es(finishName("rare")) + " when evolving Pokémon", mythBallBoost: "Higher catch rate when using " + es(finishName("myth")), quickBallBoost: "Higher catch rate when using " + es(finishName("quick")), lightningBallBoost: "Higher catch rate when using " + es(finishName("lightning")), spyBallBoost: "Higher catch rate when using " + es(finishName("spy")), luxuryBallBoost: "Higher catch rate when using " + es(finishName("luxury")), photoBallBoost: "Higher catch rate when using " + es(finishName("photo")), mirrorBallBoost: "Higher catch rate when using " + es(finishName("mirror")), premierBallBoost: "Higher catch rate when using " + es(finishName("premier")), levelBallBoost: "Higher catch rate when using " + es(finishName("level")), heavyBallBoost: "Higher catch rate when using " + es(finishName("heavy")), switchBallBoost: "Higher catch rate when using " + es(finishName("switch")), loveBallBoost: "Higher catch rate when using " + es(finishName("love")), monoBallBoost: "Higher catch rate when using " + es(finishName("mono")), cloneBallBoost: "Higher catch rate when using " + es(finishName("clone")), cloneBallBoost2: "Even higher catch rate when using " + es(finishName("clone")), fasterPhotos: "Increased priority when taking photos", extraDust: "Acquire more " + finishName("dust") + " when evolving Pokémon", pokeblockBoost: "Pokéblocks are more effective", daycarePlay: "Pokémon gain more affection than usual when you play with them in the Daycare", useLowBST: "Your Pokémon with low BST are boosted when catching wild Pokémon", catchLowBST: "Increased chance to catch Pokémon with a BST of 480 or lower", catchNormal: "Increased chance to catch Normal-type Pokémon", catchRockGround: "Increased chance to catch Rock or Ground-type Pokémon", catchFighting: "Increased chance to catch Fighting-type Pokémon", catchGrass: "Increased chance to catch Grass-type Pokémon", catchFire: "Increased chance to catch Fire-type Pokémon", catchWater: "Increased chance to catch Water-type Pokémon", catchElectric: "Increased chance to catch Electric-type Pokémon", catchIce: "Increased chance to catch Ice-type Pokémon", catchDark: "Increased chance to catch Dark-type Pokémon", catchFairy: "Increased chance to catch Fairy-type Pokémon", catchPoison: "Increased chance to catch Poison-type Pokémon", catchGhost: "Increased chance to catch Ghost-type Pokémon", catchSing: "Increased chance to catch Pokémon that can learn Sing", catchThief: "Increased chance to catch Pokémon that can learn Thief", catchRockClimb: "Increased chance to catch Pokémon that can learn Rock Climb", catchSplash: "Increased chance to catch Pokémon that can learn Splash", extraLoveBall: "The effect of " + es(finishName("love")) + " on your Daycare Pokémon when catching wild Pokémon in the same Egg Group is increased", betterPyrItems: "Gain a chance of receiving more items when finding a treasure in the Pyramid", betterFinder: "Increased chance of your Itemfinder successfully finding an item", betterGacha: "Increased chance of receiving higher rarity items from Gachapon", pyrStaminaBoost: "Gain 10 bonus stamina when starting a Pyramid quest", kiai: "Increased chance of surviving KO moves in Rotation Battles against NPCs", superChef: "Increased chance of baiting Pokémon with a type disadvantage against your lead", higherBakingYield: "Increases the number of Deluxe Baits produced during Baking quests", moreEdibleDeluxe: "Reduces the inedibility of Deluxe Baits produced during Baking quests", extendedMushroom: "Mushroom effects last longer", berryCatcher:"Wild Pokémon hold Berries more often", fasterArena:"Lower cooldown after battling in the Arena", bakingDiscount:"Lower admission fee when participating in Baking quests", battleBoost: "Increased move power in autobattles against NPCs", reducedCatchFailCD: "Decreased cooldown after a failed Pokémon capture", lowUltraCD: "Decreased cooldown after a failed Pokémon capture when using " + es(finishName("ultra")), megaPacker: "This costume's boosted perks have a 10% higher effect cap", haggler: "Increased chance of getting a discounted item stock at the market", extraBuyBonus: "Obtain extra patronage items from the market", tripleChance: "Increased chance of double-cloning a Pokémon", towerLoot: "Earn extra rewards from participating in the Tower quest", extraTourRare: "Earn an extra " + finishName("rare") + " from Event Tournaments", extraTourMega: "Earn an extra " + finishName("mega") + " from winning Event Tournaments", extraScientistSilver: "Earn extra " + es(finishName("silver")) + " from the Scientist quest", extraTriviaSoda: "Earn extra " + es(finishName("soda")) + " from Event Trivia games", extraMafiaShady: "Earn extra " + es(finishName("shady")) + " from Event Mafia games", extraApricornsFromContest: "Earn extra apricorns from winning Contests", moreJournalPoints: "Earn extra Journal points from the Journal quest", smartPhoto: "Notifies you if a photo you just took matches any current Journal request", revealAction: "Instantly reveals a wild Pokémon's current action", revealMood: "Instantly reveals a wild Pokémon's current mood", scientistPhotoGuarantee: "Any photos you take of the Scientist's current research subject are guaranteed to be at least Great quality", ninjaSniper: "Increased catch rate when catching Pokémon baited by other people", permanentStealthThrow: "All your baiting, throwing, and catching actions are stealthy", ninjaDoubleThrow: "Create a copy of yourself to throw 2 Poké Balls at once at a 50% chance (does not work with " + readable(allBalls.filter(function(e) { return itemData[e].special }).map(finishName).map(es), "or") + "). Cannot double throw if there is more than 1 wild Pokémon.", reelOtherPokeballs: "Grants a 50% chance to reel in a failed Poké Ball thrown by other players whenever you catch a Pokémon (cannot reel in " + readable(allBalls.filter(function(e) { return itemData[e].special }).map(finishName).map(es), "or") + ").", extraSellProfit: "Increases the amount of money you receive from selling Pokémon and pawning items to the NPC", blackMarketAccess: "Allows you to access various black market deals from the NPC Shop and Monger quest" }; this.showCostumeInfo = function(src, commandData) { var player = getAvatar(src); if (!player) { return; } var costume = commandData; if (!commandData || commandData === "*") { costume = player.costume; } costume = costumeAlias(costume.toLowerCase()); if (!costume || !costumeData.hasOwnProperty(costume)) { safaribot.sendHtmlMessage(src, "That's not a valid costume! The existing costumes are: " + readable(Object.keys(costumeData).map(function(e) { return link("/showcostume " + e, costumeData[e].fullName) })), safchan); return; } sys.sendMessage(src, "", safchan); if (!player.costumes.contains(costume)) { safaribot.sendHtmlMessage(src, toColor("You do not have this costume yet!", "crimson"), safchan); } if (Object.keys(costumeData[costume].skills).length === 0) { if (!player.costumes.contains(costume)) { sys.sendMessage(src, "", safchan); } safaribot.sendHtmlMessage(src, "This costume has no extra skills and cannot level up!", safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Effects & EXP Types", safchan); safaribot.sendMessage(src, safari.getCostumeHelp(costume), safchan); sys.sendMessage(src, "", safchan); return; } if (player.costumes.contains(costume)) { var lev = this.getCostumeLevel(player, costume); var nextexp = (lev < 20 ? " (" + (lev * 100 - player.costumeInfo[costume].exp) + " EXP until next level)" : ""); if (lev >= 20) { nextexp = " [Max]"; } safaribot.sendHtmlMessage(src, "" + costumeAlias(costume, true, true) + " is Level: " + lev + nextexp + "", safchan); } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Effects & EXP Types", safchan); safaribot.sendMessage(src, safari.getCostumeHelp(costume), safchan); var skills = costumeData[costume].skills; sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Skills", safchan); for (var skill in skills) { var hasSkill = player.costumeInfo[costume].skills.contains(skill), singleLevelUnlock = skills[skill][0] === skills[skill][1]; var verb = hasSkill ? "Unlocked" : "Unlocks"; safaribot.sendHtmlMessage(src, "- {0} [{1}]".format( costumeSkillInfo[skill], verb + (singleLevelUnlock ? " at" : " between") + " Level " + (singleLevelUnlock ? skills[skill][0] : readable(skills[skill])), (hasSkill ? "green" : "crimson") ), safchan); } sys.sendMessage(src, "", safchan); return true; }; this.getCostumeHelp = function(costume) { costume = costume.toLowerCase(); if (!costume in costumeData) { return null; } var ret = [costumeData[costume].effect + " " + (costumeData[costume].effect2 ? " - " + costumeData[costume].effect2 : "")]; if (costumeData[costume].expTypes) { var expTypes = costumeData[costume].expTypes, expInfo = []; for (var type in expTypes) { var infoString = costumeExpInfo[expTypes[type]]; if (expTypes[type] === "catch") { for (var theme in contestThemes) { if (contestThemes[theme].flavor === costume) { infoString += " (double EXP during {0}-themed Contests)".format(contestThemes[theme].name); } } } expInfo.push(infoString); } } ret.push("EXP Types: " + expInfo.join(", ")); return ret.join(" "); }; this.getCostumeLevel = function(player, costume) { var cos = costume || player.costume; if (!player.costumeInfo) { return 1; } if (!player.costumeInfo[cos]) { return 1; } if (!player.costumeInfo[cos].level) { return 1; } return player.costumeInfo[cos].level; }; this.hasCostumeSkill = function(player, skill) { var cos = player.costume; if (!player.costumeInfo) { return false; } if (!player.costumeInfo[cos]) { return false; } if (!player.costumeInfo[cos].skills) { return false; } if (!player.costumeInfo[cos].skills.contains(skill)) { return false; } return true; }; this.costumeEXP = function(player, type, val) { var exp = 0, src = sys.id(player.id); var cos = player.costume; if (cos == "inver") { return false; } if (cos == "" || cos == "none" || !cos) { return false; } var cosData = costumeData[cos]; if (!costumeData) { return false; } if (!(cosData.expTypes.contains(type)) && type !== "alchemy") { return false; } switch (type) { case "alchemy": exp = val; break; case "stealpoke": exp = 35; break; case "catch": exp = 10; break; case "catchwater": exp = 20; break; case "catchlowbst": exp = 20; break; case "catchhighbst": exp = 20 + Math.floor((val - 540)*0.75); break; case "takephoto": exp = 10 * val; break; case "clonepoke": exp = 20 * val; break; case "daycareplay": exp = 5; break; case "fighttower": exp = (2 * (5 + val + (val > 10 ? (val - 10) : 0) + (val > 15 ? (2 * (val - 15)) : 0))); break; case "arenasilver": exp = (2 * (3 + val + (val > 3 ? (val - 3) : 0) + (val > 6 ? (2 * (val - 6)) : 0))); break; case "bait": exp = 3; break; case "findrare": exp = 66; break; case "wincontest": exp = 150; break; case "scientist": exp = val; break; case "trivia": exp = val; break; case "wintour": if (val === 1) { exp = 500; } if (val === 2) { exp = 250; } if (val === 3) { exp = 100; } break; case "winmafia": exp = 60; break; case "wintrivia": exp = 15 * val; break; } if (player.costume == "preschooler" && player.records.pokesCaught > 999) { exp *= 0.5; } else if (player.costume == "preschooler") { exp *= 1.5; } if (!player.costumeInfo[cos]) { player.costumeInfo[cos] = {}; } if (!player.costumeInfo[cos].exp) { player.costumeInfo[cos].exp = 0; } if (!player.costumeInfo[cos].level) { player.costumeInfo[cos].level = 1; } if (!player.costumeInfo[cos].skills) { player.costumeInfo[cos].skills = []; } exp = Math.round(exp); player.costumeInfo[cos].exp += exp; for (var i = 0; i < 20; i++) { if (this.costumeLevelUp(player, cos, cosData)) { break; } } this.saveGame(player); return true; }; this.costumeLevelUp = function(player, cos, cosData) { if ((player.costumeInfo[cos].exp > player.costumeInfo[cos].level * 100 && (player.costumeInfo[cos].level < 20))) { var src = sys.id(player.id); player.costumeInfo[cos].exp -= player.costumeInfo[cos].level * 100; player.costumeInfo[cos].level++; var lev = player.costumeInfo[cos].level; safaribot.sendHtmlMessage(src, "Your " + costumeAlias(cos, true, true) + " costume leveled up! (Level: " + lev + ")", safchan); for (var c in cosData.skills) { if ((lev >= cosData.skills[c][0] && (chance(1 / (cosData.skills[c][1] - (lev + 0.01))))) || lev >= cosData.skills[c][1] ) { if (player.costumeInfo[cos].skills.contains(c)) { continue; } player.costumeInfo[cos].skills.push(c); safaribot.sendHtmlMessage(src, "You unlocked a new " + costumeAlias(cos, true, true) + " skill: " + costumeSkillInfo[c] + "!", safchan); if (c == "rocketPack") { g = giveStuff(player, toStuffObj("@fossil,@bignugget,2@scarf,3@amulet,5@pack,10@gem,20@gacha")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "flowerPack") { g = giveStuff(player, toStuffObj("5@cherry,3@mushroom")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "fisherPack") { g = giveStuff(player, toStuffObj("100@dew,100@hdew")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack1") { g = giveStuff(player, toStuffObj("2@gem,10@gacha,10@bait,30@safari")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack2") { g = giveStuff(player, toStuffObj("15@bluapricorn,15@pnkapricorn,15@pnkapricorn,20@bait,5@pearl")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack3") { g = giveStuff(player, toStuffObj("15@redapricorn,15@ylwapricorn,15@grnapricorn,20@bait,5@pearl")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack4") { g = giveStuff(player, toStuffObj("3@eviolite,2@golden,@scarf,@rare,10@bigpearl,20@gacha,5@pokeblock")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack5") { g = giveStuff(player, toStuffObj("2@eviolite,@amulet,5@egg,50@great,5@pokeblock")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack6") { g = giveStuff(player, toStuffObj("3@rare,@battery,20@bait,75@safari")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack7") { g = giveStuff(player, toStuffObj("@eviolite,2@honey,2@stardust,3@golden,50@ultra,5@pokeblock")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack8") { g = giveStuff(player, toStuffObj("25@gacha,25@bait,25@bluapricorn,25@pnkapricorn,25@pnkapricorn,5@pokeblock")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack9") { g = giveStuff(player, toStuffObj("10@egg,@golden,25@redapricorn,25@ylwapricorn,25@grnapricorn")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "preschoolerPack10") { g = giveStuff(player, toStuffObj("@nugget,10@rare,10@golden,@stardust,5@eviolite,50@bait,100@safari")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "pokefanPack") { if (chance(1/32)) { g = giveStuff(player, toStuffObj("@bright")); } else { g = giveStuff(player, toStuffObj("@egg")); } safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "ninjaPack1") { g = giveStuff(player, toStuffObj("@cherry,@nugget,@water")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } if (c == "ninjaPack2") { g = giveStuff(player, toStuffObj("25@pack")); safaribot.sendHtmlMessage(src, "You " + g + "!", safchan); } } } return false; } return true; }; /* Shops & Raffles */ this.sellItems = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "sell items"; if (cantBecause(src, reason, ["tutorial"])) { return; } var validItems = [], blockedItems = []; for (var e in itemData) { if (itemData[e].type === "valuables" && itemData[e].price > 0) { if (player.tradeBlacklist.contains("@" + e)) { blockedItems.push(itemData[e].name); } else { validItems.push(itemData[e].name); } } } var perkBonus = 1 + getPerkBonus(player, "crown"); var fortuneBonus = 1 + this.getFortune(player, "crown", 0, null, true); var costumeBonus = (safari.hasCostumeSkill(player, "extraSellProfit") ? 1.2 : 1); if (data === "*") { safaribot.sendMessage(src, "You can sell the following items:", safchan); for (var i = 0; i < validItems.length; i++) { safaribot.sendMessage(src, itemData[validItems[i]].fullName + ": $" + addComma(Math.floor(itemData[validItems[i]].price/2 * perkBonus * fortuneBonus * costumeBonus)), safchan); } sys.sendMessage(src, "", safchan); var valuables = []; for (var i = 0; i < validItems.length; i++) { var misc = validItems[i]; var amt = player.balls[misc]; if (amt > 0) { var p = amt > 1 ? "s" : ""; valuables.push(amt + " " + finishName(misc) + p); } } if (valuables.length > 0) { safaribot.sendMessage(src, "You currently have " + readable(valuables, "and") + ". To sell an item, use /pawn item:quantity (e.g.: /pawn pearl:3).", safchan); } else { safaribot.sendMessage(src, "You don't have anything that can be sold at this time!", safchan); } return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid"])) { return; } var pawning = [], item, amount, pawnAll; if (data.toLowerCase() === "all") { pawning = validItems; pawnAll = true; } else { var info = data.split(":"); item = itemAlias(info[0], true); amount = 1; if (info.length > 1) { amount = parseInt(info[1], 10); if (isNaN(amount) || amount < 1) { amount = 1; } } if (blockedItems.contains(item)) { safaribot.sendMessage(src, "We'd like to buy " + finishName(item) + " from you, but it's currently in your Tradeblocked list.", safchan); return; } if (!validItems.contains(item)) { safaribot.sendMessage(src, "We do not buy \"" + info[0] + "\" at this location.", safchan); return; } if (player.balls[item] < amount) { safaribot.sendMessage(src, "You don't have " + plural(amount, item) + ", you only have " + player.balls[item] + "!", safchan); return; } pawning.push(item); } var cost, out = [], remaining = [], skipped = [], totalMoney = 0, itemsPawned = [], amountPawned = 0; for (var i = 0; i < pawning.length; i++) { item = pawning[i]; if (player.balls[item] > 0) { if (pawnAll) { amount = player.balls[item]; } cost = Math.floor(itemData[item].price/2 * perkBonus * fortuneBonus * costumeBonus) * amount; if (player.money + cost > moneyCap) { skipped.push(plural(amount, item)); } else { player.money += cost; player.balls[item] -= amount; player.records.pawnEarnings += cost; if (item === "cometshard") { player.records.pawnComet += cost; } amountPawned += amount; totalMoney += cost; itemsPawned.push(amount+"@"+item); out.push(plural(amount, item)); } remaining.push(plural(player.balls[item], item)); this.updateShop(player, item); } } if (skipped.length > 0) { safaribot.sendMessage(src, "You can't hold more than $" + addComma(moneyCap) + ", so you cannot currently sell your " + readable(skipped, "and") + "!", safchan); } if (out.length > 0) { safaribot.sendMessage(src, "You sold " + readable(out, "and") + " for $" + addComma(totalMoney) + "! You now have " + readable(remaining, "and") + " and $" + addComma(player.money) + "!", safchan); this.missionProgress(player, "pawn", itemsPawned.join(","), amountPawned, { price: totalMoney }); safari.updateEconomyData(totalMoney, "playerPawn"); } else { var remaining = remaining.length > 0 ? readable(remaining, "and") : "no pawnable item"; safaribot.sendMessage(src, "Huh? You didn't actually sell anything... If you change your mind, I'll be happy to buy from you! You currently have " + readable(remaining, "and") + " and $" + addComma(player.money) + "!", safchan); } if (this.getFortune(player, "crown", 0, null, true)) { this.useFortuneCharge(player, "crown", 1); } this.saveGame(player); }; this.turboSellPokemon = function(src, data) { if (data === "*") { safaribot.sendMessage(src, "To turbosell Pokémon, use /turbosell [name]:[amount] to check their price, and /turbosell [name]:[amount]:confirm to sell them.", safchan); return; } if (!validPlayers("self", src)) { return; } if (cantBecause(src, "sell a Pokémon", ["tutorial"])) { return; } var player = getAvatar(src); data = typeNull(data); var split = data.split(":"); var confirmed = true; var info, sellNum, maxCount; if (split.length < 2 || !split[1] || isNaN(split[1])) { safaribot.sendMessage(src, "Please enter a valid number of that Pokémon to sell.", safchan); return; } if (split.length < 3 || split[2].toLowerCase() !== "confirm") { confirmed = false; } info = getInputPokemon(split[0]); sellNum = Math.max(1, parseInt(split[1], 10)); if (!info.id) { safaribot.sendMessage(src, split[0] + " is not a valid Pokémon!", safchan); return; } maxCount = countRepeated(player.pokemon, info.id); if (player.party[0] === info.id) { // if is lead pokemon, that copy can't be sold, so reduce by 1 maxCount -= 1; } if (maxCount <= 0) { safaribot.sendMessage(src, "You do not have any " + info.name + " eligible for sale right now.", safchan); return; } if (sellNum > maxCount) { safaribot.sendMessage(src, "You only have " + maxCount + " " + info.name + " eligible for sale right now.", safchan); return; } var totalPayment = 0, trySell; for (var i = 0; i < sellNum; i++) { if (confirmed) { trySell = safari.sellPokemon(src, info.name + (info.shiny ? "*" : ""), true); if (trySell) { totalPayment += trySell; } } else { // simulate sales and output the total payment totalPayment += getPrice(info.num, info.shiny, 1 + getPerkBonus(player, "amulet"), 1 + safari.getFortune(player, "amulet", 0, null, true), (safari.hasCostumeSkill(player, "extraSellProfit") ? 1.2 : 1)); } } if (confirmed && totalPayment > 0) { safaribot.sendMessage(src, "You sold your {0} {1} for a total of ${2}! You now have ${3}!".format(sellNum, info.name, addComma(totalPayment), addComma(player.money)), safchan); } else { var confirmCommand = "/turbosell " + (info.shiny ? "*":"") + pokePlain(info.id) + ":" + sellNum + ":confirm"; safaribot.sendHtmlMessage(src, "You can sell your {0} {1} for ${2}. To confirm it, type {3}.".format(sellNum, info.name, addComma(totalPayment), link(confirmCommand)), safchan); } }; this.multiSellPokemon = function(src, data) { if (data === "*") { safaribot.sendMessage(src, "To multisell Pokémon, use /multisell [name1],[name2],[name3]... to check their prices, and /multisell [name1],[name2],[name3]:confirm to sell them.", safchan); return; } if (!validPlayers("self", src)) { return; } if (cantBecause(src, "sell a Pokémon", ["tutorial"])) { return; } var player = getAvatar(src); data = typeNull(data); var split = data.split(":"); var confirmed = true; if (split.length < 2 || split[1].toLowerCase() !== "confirm") { confirmed = false; } var list = split[0].split(","), trySell, p, out = [], cashout = 0; for (var i = 0; i < list.length; i++) { p = getInputPokemon(list[i].trim()).id; if (!p) { safaribot.sendMessage(src, list[i] + " is not a valid Pokémon!", safchan); continue; } if (isLegendary(p)) { continue; } trySell = this.sellPokemon(src, poke(p), confirmed); if (trySell) { out.push(poke(p)); cashout += trySell; } } if (out.length > 0) { safaribot.sendHtmlMessage(src, "You sold your " + readable(out, "and") + " for a total of $" + addComma(cashout) + "! You now have $" + addComma(player.money) + "!", safchan); } this.saveGame(player); }; this.sellPokemon = function(src, data, automated) { if (data === "*") { safaribot.sendMessage(src, "To sell a Pokémon, use /sell [name] to check its price, and /sell [name]:confirm to sell it.", safchan); return; } if (!validPlayers("self", src)) { return; } var reason = "sell a Pokémon"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (!canLosePokemon(src, data, "sell", true)) { return; } var player = getAvatar(src); var input = data.split(":"); var info = getInputPokemon(input[0]); var shiny = info.shiny; var id = info.id; var perkBonus = 1 + getPerkBonus(player, "amulet"); var fortuneBonus = 1 + this.getFortune(player, "amulet", 0, null, true); var price = getPrice(info.num, info.shiny, perkBonus, fortuneBonus, (safari.hasCostumeSkill(player, "extraSellProfit") ? 1.2 : 1)); if (player.tradeBlacklist.contains(info.input)) { safaribot.sendHtmlMessage(src, "You cannot sell " + info.name + " because it's in your Tradeblocked list. If you really wish to sell it, use /tradeblock to remove it from your tradeblock list.", safchan); return false; } if (pokeInfo.forme(info.num) > 0 && !player.options.canSellFormes) { safaribot.sendHtmlMessage(src, "You cannot sell " + info.name + " without enabling \"Sell Pokémon formes to NPC\" in your " + link("/options") + "!", safchan); return false; } if (!(automated)) { if (input.length < 2 || (input[1].toLowerCase() !== "confirm" && input[1].toLowerCase() !== "iacknowledgethatiamsellingararepokemon")) { var confirmCommand = "/sell " + (shiny ? "*":"") + pokePlain(id) + ":confirm"; safaribot.sendHtmlMessage(src, "You can sell your " + info.name + " for $" + addComma(price) + ". To confirm it, type " + link(confirmCommand) + ".", safchan); return false; } if (isRare(id) && input[1].toLowerCase() !== "iacknowledgethatiamsellingararepokemon") { var confirmCommand = "/sell " + (shiny ? "*":"") + pokePlain(id) + ":IACKNOWLEDGETHATIAMSELLINGARAREPOKEMON"; safaribot.sendHtmlMessage(src, "Are you sure that you want to sell your " + info.name + " for $" + addComma(price) + "? If really want to do this, type " + link(confirmCommand, false, true) + ".", safchan); return false; } } var restrictions = ["auction", "battle", "event", "pyramid", "tutorial"]; //Allow selling of pokemon that are not the lead if the rest of the party doesn't matter at that point if (player.party[0] === id && countRepeated(player.pokemon, id) === 1) { safaribot.sendHtmlMessage(src, "You can't sell your active Pokémon!", safchan); return false; } if (cantBecause(src, reason, restrictions)) { return false; } player.money += price; safari.updateEconomyData(price, "playerSell"); if (player.money > moneyCap) { player.money = moneyCap; } player.records.pokeSoldEarnings += price; if (!(automated)) { safaribot.sendMessage(src, "You sold your " + info.name + " for $" + addComma(price) + "! You now have $" + addComma(player.money) + ".", safchan); } var theft = ""; if (player.costume === "rocket" && chance(costumeData.rocket.rate2)) { safaribot.sendMessage(src, "You cleverly distract the Shopkeeper and while he is not looking, you grab your " + poke(id) + " back and run off!", safchan); player.records.pokesStolen += 1; theft = "but stole it back"; } else { this.removePokemon(src, id); player.lastSold = { mon: info, price: price }; /*if (isRare(id) && player.tradeban <= now()) { sys.sendAll("", safchan); safaribot.sendHtmlAll("Haha! " + sys.name(src) + " just sold their " + info.name + " to the shop! You should make fun of them with " + link("/rock " + sys.name(src)) + "!", safchan); sys.sendAll("", safchan); player.balls.salt += 1; }*/ } if (this.getFortune(player, "amulet", 0, null, true)) { this.useFortuneCharge(player, "amulet", 1); } this.missionProgress(player, "sell", id, 1, { price: price }); this.logLostCommand(sys.name(src), "sell " + data, theft); if (isRare(id)) { sys.appendToFile(mythLog, now() + "|||" + poke(id) + "::was sold to the NPC by " + sys.name(src) + " " + theft + "::\n"); } if (automated) { return price; } this.saveGame(player); }; this.undoSell = function(src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var currentTime = now(); if (player.cooldowns.unsell > currentTime) { safaribot.sendMessage(src, "You've been trying to unsell too often lately! Please wait " + timeLeftString(player.cooldowns.unsell) + " to unsell again.", safchan); return; } var last = player.lastSold; if (!last.price) { safaribot.sendMessage(src, "You cannot unsell your last sold Pokémon at this time.", safchan); return; } if (last.price > player.money) { safaribot.sendMessage(src, "You cannot get your last sold Pokémon back because you used the money you earned from selling your last Pokémon!", safchan); return; } if (player.pokemon.length >= getPerkBonus(player, "box")) { safaribot.sendMessage(src, "You cannot get your last sold Pokémon back because your boxes are full!", safchan); return; } player.records.pokeSoldEarnings -= player.lastSold.price; player.money -= player.lastSold.price; safari.updateEconomyData(-player.lastSold.price, "playerSell"); var get = (last.mon.shiny ? last.mon.id + "" : last.mon.id); player.pokemon.push(get); safaribot.sendMessage(src, "You quickly turned around and got your " + player.lastSold.mon.name + " back! (You lost $" + last.price + ")", safchan); player.lastSold = {}; player.cooldowns.unsell = currentTime + (60 * 60 * 1000); this.saveGame(player); }; this.showPrices = function(src, shop, command, seller) { var i, info, input, price, player = getAvatar(src), pokemon = [], items = [], silverPokemon = [], silverItems = [], displayprice, discmsg, disc = false, blackMarket = false, blackMarketMsg; var fullCommand = "/" + command + " " + (seller ? seller + ":" : ""); var silverName = finishName("silver"), discount = (seller ? 1 : 1 - this.getFortune(player, "discount", 0, null, true)); for (i in shop) { info = shop[i]; disc = ((shop[i].discount ? shop[i].discount : false) || (shop[i].discount2 && this.hasCostumeSkill(player, "haggler"))); discmsg = disc ? " [Discount]" : ""; blackMarket = info.blackMarket; blackMarketMsg = blackMarket ? " [Black Market]" : ""; displayprice = disc ? info.discountprice : info.price; var lim = info.limit; var playerlim = info.playerLimit ? info.playerLimit : lim; if (info.hasOwnProperty("purchases") && info.hasOwnProperty("playerLimit")) { if (info.purchases.hasOwnProperty(player.idnum+"")) { playerlim = Math.min((info.playerLimit - info.purchases[player.idnum+""]), lim); } } lim = Math.min(lim, playerlim); input = getInputPokemon(i); if (blackMarket && !safari.hasCostumeSkill(player, "blackMarketAccess")) { continue; } if (info.silver) { if (input.num) { silverPokemon.push({string: "" + input.name + ": " + plural(displayprice, silverName) + (lim === -1 ? "" : (lim === 0 ? " (Out of stock)" : " (Only " + lim + " available)")) + discmsg + blackMarketMsg, sort: info.price}); } else if (i[0] == "@") { input = i.substr(1); price = input == "box" ? itemData.box.price[Math.min(player.balls.box, itemData.box.price.length - 1)] : displayprice; silverItems.push({string: "" + finishName(input) + ": " + plural(displayprice, silverName) + (lim === -1 ? "" : (lim === 0 ? " (Out of stock)" : " (Only " + lim + " available)")) + discmsg + blackMarketMsg, sort: info.price}); } } else { if (input.num) { pokemon.push({string: "" + input.name + ": $" + addComma(Math.ceil(displayprice * discount)) + (lim === -1 ? "" : (lim === 0 ? " (Out of stock)" : " (Only " + lim + " available)")) + discmsg + blackMarketMsg, sort: info.price}); } else if (i[0] == "@") { input = i.substr(1); price = input == "box" ? itemData.box.price[Math.min(player.balls.box, itemData.box.price.length - 1)] : Math.ceil(displayprice * discount); items.push({string: "" + finishName(input) + ": $" + addComma(price) + (lim === -1 ? "" : (lim === 0 ? " (Out of stock)" : " (Only " + lim + " available)")) + discmsg + blackMarketMsg, sort: price}); } } } sys.sendMessage(src, "", safchan); var hasNormal = false; var hasSilver = false; if (items.length > 0) { safaribot.sendMessage(src, "You can buy the following items" + (seller ? " from " + seller : "") + ":", safchan); items.sort(compare); for (i in items) { safaribot.sendHtmlMessage(src, items[i].string, safchan); } hasNormal = true; } if (pokemon.length > 0) { if (items.length > 0) { sys.sendMessage(src, "", safchan); } safaribot.sendMessage(src, "You can buy the following Pokémon" + (seller ? " from " + seller : "") + ":", safchan); pokemon.sort(compare); for (i in pokemon) { safaribot.sendHtmlMessage(src, pokemon[i].string, safchan); } hasNormal = true; } if (silverItems.length > 0) { if (pokemon.length > 0 || items.length > 0) { sys.sendMessage(src, "", safchan); } safaribot.sendMessage(src, "You can buy the following items with " + es(silverName) + (seller ? " from " + seller : "") + ":", safchan); silverItems.sort(compare); for (i in silverItems) { safaribot.sendHtmlMessage(src, silverItems[i].string, safchan); } hasSilver = true; } if (silverPokemon.length > 0) { if (pokemon.length > 0 || items.length > 0 || silverItems.length > 0) { sys.sendMessage(src, "", safchan); } safaribot.sendMessage(src, "You can buy the following Pokémon with " + es(silverName) + (seller ? " from " + seller : "") + ":", safchan); silverPokemon.sort(compare); for (i in silverPokemon) { safaribot.sendHtmlMessage(src, silverPokemon[i].string, safchan); } hasSilver = true; } sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "You currently have $" + addComma(player.money) + " and " + plural(player.balls.silver, silverName) + ". To buy something, use " + fullCommand + "[Pokémon/Item] (e.g.: " + fullCommand + "Pikachu or " + fullCommand + "Great Ball)", safchan); }; this.isBelowCap = function(src, product, amount, type) { var player = getAvatar(src); if (type == "poke" && player.pokemon.length >= getPerkBonus(player, "box")) { safaribot.sendMessage(src, "All your boxes are full! Please buy a new box with /buy before buying any Pokémon.", safchan); return false; } if (type == "item") { var cap = getCap(product); if (player.balls[product] + amount > cap) { if (player.balls[product] == cap) { safaribot.sendMessage(src, "You are carrying the maximum amount of " + finishName(product) + " (" + cap + ") and cannot buy any more!", safchan); } else { safaribot.sendMessage(src, "You can only carry " + cap + " " + finishName(product) + "! You currently have " + player.balls[product] + " which leaves space for " + (cap - player.balls[product]) + " more.", safchan); } return false; } } return true; }; this.buyBonus = function(src, product, amount, cap) { var bonus, player = getAvatar(src); if (amount >= 10) { var bonusAmt = Math.floor(amount / 10); if (this.hasCostumeSkill(player, "extraBuyBonus")) { bonusAmt = Math.floor(amount / 5); } if (isBall(product)) { bonus = "premier"; safaribot.sendMessage(src, "Here, you also get " + plural(bonusAmt, bonus) + " for your patronage!", safchan); } else if (product === "gacha") { bonus = "gacha"; //bonusAmt *= 2; safaribot.sendMessage(src, "Here, you also get " + plural(bonusAmt, bonus) + " for your patronage!", safchan); } else if (product === "bait") { bonus = ["redapricorn", "pnkapricorn", "ylwapricorn", "grnapricorn", "bluapricorn", "blkapricorn", "whtapricorn"].random(); //bonusAmt *= 2; safaribot.sendMessage(src, "Here, you also get " + plural(bonusAmt, bonus) + " for your patronage!", safchan); } if (player.balls[bonus] + bonusAmt > cap) { var check = cap - player.balls[bonus]; if (check < 1) { safaribot.sendMessage(src, "However, you didn't have any space left and were forced to discard " + (bonusAmt === 1 ? "it" : "them") + "!", safchan); } else { safaribot.sendMessage(src, "However, you only had space for " + check + " and were forced to discard the rest!", safchan); } bonusAmt = check; if (bonusAmt < 0) { bonusAmt = 0; } } player.balls[bonus] += bonusAmt; } if (product === "entry") { rafflePlayers.add(player.id, player.balls.entry); rafflePlayers.save(); } }; this.updateShop = function(player, item) { var itemInput = "@" + item; if (player && itemInput in player.shop && player.shop[itemInput].limit > player.balls[item]) { player.shop[itemInput].limit = player.balls[item]; } }; this.buyFromShop = function(src, data, command, fromNPC) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (player.tutorial.inTutorial) { safari.buyForTutorial(src, data); return; } var reason = "buy items"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (!fromNPC && player.tradeban > now()) { safaribot.sendMessage(src, "You are banned from buying from other players for " + timeLeftString(player.tradeban) + "!", safchan); return; } if (!fromNPC && player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only enter this shop after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } //Show own shop if nothing is specified if ((!data || data === "*") && !fromNPC) { data = sys.name(src); } var info = data.split(":"), input, sellerName = info[0], sName, seller, sellerId, shop = npcShop, product = info.length > 1 && info[1] !== "" ? info[1].toLowerCase() : "*"; if (!fromNPC) { if (sellerName) { seller = getAvatarOff(sellerName); if (!seller) { safaribot.sendMessage(src, "No such person! Use /" + command + " Name to see their shop!", safchan); return; } } else { safaribot.sendMessage(src, "No such person! Use /" + command + " Name to see their shop!", safchan); return; } } if (seller) { if (seller.locked) { safaribot.sendMessage(src, "This user's save is currently locked, so you can't buy from them!", safchan); return; } shop = seller.shop; sName = "" + seller.casedName + ": "; sellerId = sys.id(sellerName); } if (!shop || Object.keys(shop).length === 0) { if (fromNPC) { safaribot.sendMessage(src, "[Closed] This shop is closed for maintenance. Please come back later.", safchan); } else { safaribot.sendHtmlMessage(src, sName + "I have nothing to sell right now.", safchan); } return; } if (product === "*") { this.showPrices(src, shop, command, (seller ? seller.casedName : null)); return; } var allowShared = allowedSharedIPNames.contains(sys.name(src)) || allowedSharedIPNames.contains(sellerName.toLowerCase()) || sys.ip(src) === "::1%0"; if (!fromNPC && (sellerName.toLowerCase() == sys.name(src).toLowerCase() || (sys.ip(sellerId) === sys.ip(src) && !allowShared))) { safaribot.sendMessage(src, "You cannot buy things from your own shop!", safchan); return; } var restrictions = ["contest", "auction", "event", "pyramid"]; if (!fromNPC) restrictions.push("wild"); //Tutorial is restricted above if (cantBecause(src, reason, restrictions)) { return; } if (isNaN(parseInt(player.money), 10) || player.money < 0) { sys.appendToFile(miscLog, now() + "|||" + player.id.toCorrectCase() + "|||had the invalid value " + JSON.stringify(player.money) + " for their money\n"); this.sanitize(player); safaribot.sendMessage(src, "You found a hole in your pocket! All your money is gone! Contact a Safari Auth for clarification.", safchan); return; } input = getInput(product); if (input.type === "item" && itemData[input.id].invisible) { input = null; } if (!input) { if (fromNPC) { safaribot.sendMessage(src, "What's that \"" + product + "\" you are talking about? A new item? Some unknown Pokémon species?", safchan); } else { safaribot.sendHtmlMessage(src, sName + "I don't have anything called \"" + product + "\" in my humble shop.", safchan); } return; } if (!(input.input in shop)) { if (fromNPC) { safaribot.sendMessage(src, "Sorry, we are not selling any " + input.name + " at the moment.", safchan); } else { safaribot.sendHtmlMessage(src, sName + "I'm not selling any " + input.name + " right now, please don't insist!", safchan); } return; } if (shop[input.input].blackMarket && !safari.hasCostumeSkill(player, "blackMarketAccess")) { safaribot.sendMessage(src, "H-huh? " + input.name + "? I-I don't know what you're talking about! There's nothing like that here!", safchan); return; } var lim = shop[input.input].limit; var playerlim = shop[input.input].playerLimit ? shop[input.input].playerLimit : lim; playerlim = Math.min(lim, playerlim); if (fromNPC) { if (shop[input.input].hasOwnProperty("purchases") && shop[input.input].hasOwnProperty("playerLimit")) { if (shop[input.input].purchases.hasOwnProperty(player.idnum+"")) { playerlim = Math.min((shop[input.input].playerLimit - shop[input.input].purchases[player.idnum+""]), lim); } } } if (playerlim === 0) { if (fromNPC) { safaribot.sendMessage(src, "Sorry, we already sold all the " + (input.type === "poke" ? input.name : es(input.name)) + " we had.", safchan); } else { safaribot.sendHtmlMessage(src, sName + "You are out of luck, I just sold my last " + input.name + " " + plural(sys.rand(1, 10), "second") + " ago!", safchan); } return; } if (input.type == "poke" && !fromNPC && countRepeated(seller.pokemon, input.id) <= 1) { if (seller.party.length === 1 && seller.party[0] == input.id) { safaribot.sendHtmlMessage(src, sName + "You are out of luck, I just sold my last " + input.name + " " + plural(sys.rand(1, 10), "second") + " ago!", safchan); if (isPlaying(sellerName)) { safaribot.sendMessage(sellerId, "You cannot sell the last Pokémon in your party! Removing them from your shop.", safchan); } delete seller.shop[input.input]; safari.saveGame(seller); return; } if (seller.starter === input.id) { safaribot.sendHtmlMessage(src, sName + "You are out of luck, I just sold my last " + input.name + " " + plural(sys.rand(1, 10), "second") + " ago!", safchan); if (isPlaying(sellerName)) { safaribot.sendMessage(sellerId, "You cannot sell your starter Pokémon! Removing them from your shop.", safchan); } delete seller.shop[input.input]; safari.saveGame(seller); return; } if (countRepeated(seller.pokemon, input.id) === 0) { safaribot.sendHtmlMessage(src, sName + "You are out of luck, I just sold my last " + input.name + " " + plural(sys.rand(1, 10), "second") + " ago!", safchan); delete seller.shop[input.input]; safari.saveGame(seller); return; } } var amount = 1; if (info.length > 2) { amount = parseInt(info[2], 10); if (input.type == "poke") { if (amount > 1) { safaribot.sendHtmlMessage(src, "You cannot buy multiple Pokémon at once! Changing amount to 1!", safchan); amount = 1; } } else if (input.id == "box") { amount = 1; } } if (isNaN(amount) || amount < 1 || !amount) { safaribot.sendHtmlMessage(src, "Please choose a valid amount!", safchan); return; } if (input.type === "item" && !fromNPC) { var iData = itemData[input.id]; if (iData.tradeReq && seller.balls[input.id] - amount < iData.tradeReq) { safaribot.sendHtmlMessage(src, sName + "You are out of luck, I just sold my last " + input.name + " " + plural(sys.rand(1, 10), "second") + " ago!", safchan); if (isPlaying(sellerName)) { safaribot.sendMessage(sellerId, "You can't sell " + input.name + " unless you have more than " + iData.tradeReq + " of those! Removing them from your shop!", safchan); } delete seller.shop[input.input]; safari.saveGame(seller); return; } if (amount > seller.balls[input.id]) { this.updateShop(seller, input.id); safari.saveGame(seller); safaribot.sendHtmlMessage(src, sName + "I only have " + plural(playerlim, input.name) + " to sell, how do you expect me to sell you " + amount + "?", safchan); return; } } if (playerlim !== -1 && playerlim - amount < 0) { if (fromNPC) { safaribot.sendMessage(src, "I'm sorry, but we currently only have " + plural(playerlim, input.name) + " to sell, so you can't buy " + amount + "!", safchan); } else { safaribot.sendHtmlMessage(src, sName + "I only have " + plural(playerlim, input.name) + " to sell, how do you expect me to sell you " + amount + "?", safchan); } return; } var cap = getCap(input.id); if (!this.isBelowCap(src, input.id, amount, input.type)) { return; } if (fromNPC) { if (player.balls.entry > 0) { var check = rafflePlayers.get(sys.name(src).toLowerCase()); if (!check) { safaribot.sendMessage(src, "You seem to have some raffle entries from a previous drawing. I'll dispose of them so you don't get confused!", safchan); player.balls.entry = 0; } } if (input.id === "entry" && !rafflePrizeObj) { safaribot.sendMessage(src, "There is no raffle going on right now so we cannot legally sell you an entry! Sorry!", safchan); return; } } else { //Eventually enable this when there's a way to confirm you want to buy it. As it is, no one will be able to buy from this shop until the 15 seconds pass. /* if (now() < seller.cooldowns.shopEdit + 15 * 1000) { safaribot.sendMessage(src, "This shop was modified in the last 15 seconds. Please verify if the price for the product you wish to buy didn't change!", safchan); return; } */ } if (!fromNPC && now() <= player.cooldowns.buyFromPlayer) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.buyFromPlayer) + " before buying another item from a player!", safchan); return; } var isSilver = shop[input.input].silver || false; var silverName = finishName("silver"); var itemPrice = shop[input.input].price; if (fromNPC && (shop[input.input].discount || (shop[input.input].discount2 && this.hasCostumeSkill(player, "haggler")))) { itemPrice = shop[input.input].discountprice; } var boxPrice = itemData.box.price[Math.min(player.balls.box, itemData.box.price.length - 1)]; var cost = input.id == "box" ? boxPrice : itemPrice * amount; var discount = (fromNPC && !isSilver), discountRate = 1 - this.getFortune(player, "discount", 0, null, true); if (discount && input.id !== "box") { cost = Math.ceil(cost * discountRate); } if (isSilver) { if (player.balls.silver < cost) { if (fromNPC) { safaribot.sendMessage(src, "You need " + plural(cost, silverName) + " to buy " + plural(amount, input.name) + ", but you only have " + plural(player.balls.silver, silverName) + "!", safchan); } else { safaribot.sendHtmlMessage(src, sName + "Ok, it will cost you " + plural(cost, silverName) + " to buy " + plural(amount, input.name) + "... Hey! You only have " + plural(player.balls.silver,silverName) + ", so no deal!", safchan); } return; } player.balls.silver -= cost; } else { if (player.money < cost) { if (fromNPC) { safaribot.sendMessage(src, "You need $" + addComma(cost) + " to buy " + plural(amount, input.name) + ", but you only have $" + addComma(player.money) + "!", safchan); } else { safaribot.sendHtmlMessage(src, sName + "Ok, it will cost you $" + addComma(cost) + " to buy " + plural(amount, input.name) + "... Hey! You only have $" + addComma(player.money) + ", so no deal!", safchan); } return; } player.money -= cost; } if (input.type == "poke") { player.pokemon.push(input.id); } else if (input.type == "item") { if (!fromNPC && input.id == "battery") { this.dailyReward(src, getDay(now())); } player.balls[input.id] += amount; } if (isSilver) { if (fromNPC) { safaribot.sendMessage(src, "You bought " + plural(amount, input.name) + " for " + plural(cost, silverName) + "! You now have " + (input.type == "item" ? plural(player.balls[input.id], input.name) + " and " : "") + plural(player.balls.silver, silverName) + "!", safchan); if (input.type == "poke" && isRare(input.id)) { sys.appendToFile(mythLog, now() + "|||" + input.name + "::was bought by " + sys.name(src) + " for " +plural(cost, silverName) + "::\n"); } } else { safaribot.sendHtmlMessage(src, sName + "Thanks for buying " + plural(amount, input.name) + " for " + plural(cost, silverName) + "! You now have " + (input.type == "item" ? plural(player.balls[input.id], input.name) + " and " : "") + plural(player.balls.silver, silverName) + "!", safchan); } } else { if (fromNPC) { safaribot.sendMessage(src, "You bought " + plural(amount, input.name) + " for $" + addComma(cost) + "! You now have " + (input.type == "item" ? plural(player.balls[input.id], input.name) + " and $" : "$") + addComma(player.money) + "!", safchan); if (input.type == "poke" && isRare(input.id)) { sys.appendToFile(mythLog, now() + "|||" + input.name + "::was bought by " + sys.name(src) + " for $" + cost + "::\n"); } if (this.getFortune(player, "discount", 0, null, true)) { this.useFortuneCharge(player, "discount", 1); } } else { safaribot.sendHtmlMessage(src, sName + "Thanks for buying " + plural(amount, input.name) + " for $" + addComma(cost) + "! You now have " + (input.type == "item" ? plural(player.balls[input.id], input.name) + " and $" : "$") + addComma(player.money) + "!", safchan); } } var limitChanged = false; if (shop[input.input].limit > 0) { shop[input.input].limit -= amount; shop[input.input].limit = Math.max(shop[input.input].limit, 0); limitChanged = true; } if (playerlim > 0 && fromNPC) { if (!shop[input.input].purchases) { shop[input.input].purchases = {}; } if (!shop[input.input].purchases[player.idnum+""]) { shop[input.input].purchases[player.idnum+""] = 0; } shop[input.input].purchases[player.idnum+""] += amount; if (!isSilver) { safari.updateEconomyData(-cost, "npcShop"); } sys.appendToFile(shopLog, now() + "|||NPC::" + amount + "::" + input.name + (shop[input.input].blackMarket ? " [Black Market]" : "") + "::" + shop[input.input].price + "::" + cost + "::" + isSilver + "|||" + sys.name(src) + "\n"); } if (seller) { if (isSilver) { seller.balls.silver = Math.min(seller.balls.silver + cost, getCap("silver")); } else { seller.money = Math.min(seller.money + cost, moneyCap); } if (input.type == "poke") { this.removePokemon2(seller, input.id); } else if (input.type == "item") { seller.balls[input.id] -= amount; this.updateShop(seller, input.id); } if (isPlaying(sellerName)) { sys.sendMessage(sellerId, "", safchan); if (isSilver) { safaribot.sendMessage(sellerId, "Someone bought " + amount + " of your " + input.name + "! You received " + plural(cost, silverName) + " and now have " + plural(seller.balls.silver, silverName) + "!", safchan); } else { safaribot.sendMessage(sellerId, "Someone bought " + plural(amount, input.name) + " from your shop! You received $" + addComma(cost) + " and now have $" + addComma(seller.money) + "!", safchan); } sys.sendMessage(sellerId, "", safchan); } else { if (!seller.hasOwnProperty("offlineSales")) { seller.offlineSales = {}; } if (!seller.offlineSales.hasOwnProperty(input.input)) { seller.offlineSales[input.input] = 0; } seller.offlineSales[input.input] += amount; } safari.saveGame(seller); if (!isSilver) { safari.updateEconomyData(cost, "playerShop"); } sys.appendToFile(shopLog, now() + "|||" + seller.casedName + "::" + amount + "::" + input.name + "::" + shop[input.input].price + "::" + cost + "::" + isSilver + "|||" + sys.name(src) + "\n"); if (shop[input.input].price >= 10000 || (input.type == "poke" && isRare(input.id))) { sys.appendToFile(rareTradeLog, now() + "|||" + sys.name(src) + " bought " + plural(amount, input.input) + " from " + seller.casedName + " for $" + addComma(cost) + (amount > 1 ? " ($" + addComma(shop[input.input].price) + " each)" : "") + "\n"); } } else { this.buyBonus(src, input.id, amount, cap); if (limitChanged) { this.saveShop(); } } if (!fromNPC) { player.cooldowns.buyFromPlayer = now() + 10 * 1000; } this.saveGame(player); }; this.checkShops = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if ((!data || data === "*")) { safaribot.sendHtmlMessage(src, "Use " + link("/price ", "/price Pokémon", true) + " or " + link("/price @", "/price @Item", true) + " to find shops selling that Pokémon/item (only checks for online players).", safchan); return; } var currentTime = now(); if (player.cooldowns.price > currentTime) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.price) + " before checking a price again!", safchan); return; } var input = getInput(data); if (!input || (input.type === "item" && itemData[input.id].invisible)) { safaribot.sendMessage(src, data + " is not a valid item or Pokémon!", safchan); return; } var list = [], p, obj; for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { p = JSON.parse(rawPlayers.hash[e]); if (p && !p.locked && player.tradeban <= now()) { obj = p.shop[input.input]; if (obj && obj.limit > 0) { list.push([obj.price, obj.limit, p.casedName]); list.push(); } } } } player.cooldowns.price = currentTime + 20*1000; this.saveGame(player); if (!list.length) { safaribot.sendHtmlMessage(src, "No player with " + input.name + " in their shop found!", safchan); return; } list.sort(function(a, b) { return a[0] - b[0]; }); if (list.length > 10) { list = list.slice(0, 10); } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Players with " + input.name + " in their shop (only cheapest 10 results shown):", safchan); for (e = 0; e < list.length; e++) { var price = list[e][0]; var limit = list[e][1]; var name = list[e][2]; safaribot.sendHtmlMessage(src, link("/shop " + name, name) + " ($" + addComma(price) + ", " + limit + " available)", safchan); } safaribot.sendHtmlMessage(src, "Use " + link("/shop [Name]", false, true) + " to visit their shop.", safchan); sys.sendMessage(src, "", safchan); }; this.editShop = function(src, data, editNPCShop, isSilver) { var player = getAvatar(src); if (!editNPCShop) { if (!validPlayers("self", src)) { return; } var reason = "edit your shop"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (player.tradeban > now()) { safaribot.sendMessage(src, "You are banned from creating your own shop for " + timeLeftString(player.tradeban) + "!", safchan); return; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only set a shop after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid"])) { return; } } var comm = editNPCShop ? "npc" : "shop"; var info = data.split(":"); if (info.length < 2) { safaribot.sendMessage(src, "Invalid format! Use /"+comm+"add [pokémon/item]:[price]:[amount] or /"+comm+"remove [pokémon/item].", safchan); return true; } var action = info[0]; var product = info[1]; var input = getInput(product); if ((action !== "close" && action !== "clean") && !input) { safaribot.sendMessage(src, "Invalid format! Use /"+comm+"add [pokémon/item]:[price]:[limit] or /"+comm+"remove [pokémon/item]. You can clear your shop with /"+comm+"close or /"+comm+"clean.", safchan); return true; } var productType = input.type; if (action == "add") { if (info.length < 3) { safaribot.sendMessage(src, "Invalid format! Use /"+comm+"add [pokémon/item]:[price]:[limit] or /"+comm+"remove [pokémon/item]. You can clear your shop with /"+comm+"close or /"+comm+"clean.", safchan); return true; } var limit = info.length > 3 ? parseInt(info[3], 10) : -1; limit = isNaN(limit) ? -1 : limit; if (limit < 1 && !editNPCShop) { limit = 1; } if (!editNPCShop) { if (productType == "poke") { if (!canLosePokemon(src, input.input, "sell", null, limit, true)) { return true; } if (limit > countRepeated(player.pokemon, input.id)) { safaribot.sendMessage(src, "You do not have " + limit + " " + input.name + " to add to your shop!", safchan); return true; } } else if (productType == "item") { if (!itemData[input.id].tradable) { safaribot.sendMessage(src, "This item cannot be added to your shop!", safchan); return true; } if (player.balls[input.id] < limit) { safaribot.sendMessage(src, "You do not have " + limit + " " + input.name + " to add to your shop!", safchan); return true; } if ("tradeReq" in itemData[input.id] && player.balls[input.id] - limit < itemData[input.id].tradeReq) { safaribot.sendMessage(src, "This item cannot be added to your shop unless you have more than " + itemData[input.id].tradeReq + " of those!", safchan); return true; } } if (Object.keys(player.shop).length >= 22 && !player.shop.hasOwnProperty(input.input)) { safaribot.sendMessage(src, "Your shop is too large! Please remove something before adding a new item!", safchan); return true; } } var price = parseInt(info[2].replace(",", ""), 10); if (isNaN(price) || price < 1 || price > moneyCap) { safaribot.sendMessage(src, "Please type a valid price!", safchan); return true; } if (!editNPCShop && productType == "poke") { var minPrice = getPrice(input.id, input.shiny); if (price < minPrice) { safaribot.sendMessage(src, "You cannot sell " + input.name + " for less than $" + addComma(minPrice) + "!", safchan); return true; } } safaribot.sendMessage(src, input.name + " has been " + (input.input in player.shop ? "re" : "") + "added to " + (editNPCShop ? "the NPC Shop" : "your shop") + " with the price of " + (isSilver ? plural(price, "silver") : "$" + price) + " (Units Available: " + (limit == -1 ? "Unlimited" : limit) + ")!", safchan); if (editNPCShop) { npcShop[input.input] = { price: price, limit: limit, silver: isSilver }; this.saveShop(); } else { player.shop[input.input] = { price: price, limit: limit }; player.cooldowns.shopEdit = now(); this.saveGame(player); } } else if (action == "remove") { if (editNPCShop) { if (input.input in npcShop) { delete npcShop[input.input]; safaribot.sendMessage(src, input.name + " has been removed from the NPC shop!", safchan); this.saveShop(); } else { safaribot.sendMessage(src, "You can't remove a Pokémon/Item from the shop if they are not there!", safchan); } } else { if (input.input in player.shop) { delete player.shop[input.input]; safaribot.sendMessage(src, input.name + " has been removed from your shop!", safchan); this.saveGame(player); } else { safaribot.sendMessage(src, "You can't remove a Pokémon/Item from the shop if they are not there!", safchan); } } } else if (action == "close") { if (editNPCShop) { for (var e in npcShop) { delete npcShop[e]; } safaribot.sendMessage(src, "The NPC Shop has been cleared!", safchan); this.saveShop(); } else { for (var e in player.shop) { delete player.shop[e]; } safaribot.sendMessage(src, "Removed all Pokémon/Items from your shop!", safchan); this.saveGame(player); } } else if (action == "clean") { if (editNPCShop) { for (var e in npcShop) { if (npcShop[e].limit === 0) { delete npcShop[e]; } } safaribot.sendMessage(src, "The NPC Shop has been cleaned!", safchan); this.saveShop(); } else { for (var e in player.shop) { if (player.shop[e].limit === 0) { delete player.shop[e]; } } safaribot.sendMessage(src, "Removed all Out of Stock Pokémon/Items from your shop!", safchan); this.saveGame(player); } } else { safaribot.sendMessage(src, "Invalid format! Use /"+comm+"add [pokémon/item]:[price]:[limit] or /"+comm+"remove [pokémon/item]. You can clear your shop with /"+comm+"close or /"+comm+"clean.", safchan); } return true; }; this.dailyReward = function(src, today) { var player = getAvatar(src); if (!player) { return; } player.casedName = sys.name(src); player.nameColor = script.getColor(src); if (player.tutorial.inTutorial) { return; } if (today > player.lastLogin) { var daysdown = parseInt(permObj.get("loginDaysDown"), 10); if (isNaN(daysdown) || daysdown < 0) { daysdown = 0; } var days = today - player.lastLogin; if (today > player.lastLogin + 1 + daysdown) { player.lastStreak = player.consecutiveLogins; player.consecutiveLogins = 0; days = 1; } player.consecutiveLogins += days; player.lastLogin = today; var logins = player.consecutiveLogins; recentPlayers[player.idnum+""] = today; var hasMaster = player.balls.master >= getCap("master"); var reward = {}, temp, t, e; for (e = days-1; e >= 0; e--) { temp = this.getDailyReward(logins-e, hasMaster); for (t in temp) { if (!reward.hasOwnProperty(t)) { reward[t] = 0; } reward[t] += temp[t]; } } if (logins > player.records.consecutiveLogins) { player.records.consecutiveLogins = logins; } var perkBonus = getPerkBonus(player, "battery"); var recharges = 30 + perkBonus; player.balls.itemfinder = (recharges); var out = giveStuff(player, reward, true); safaribot.sendMessage(src, "You received the following rewards for playing Safari " + (logins > 1 ? logins + " days in a row" : "today" ) + ": " + readable(out.gained) + (out.discarded.length > 0 ? " (couldn't receive " + readable(out.discarded) + " due to excess)" : ""), safchan); safari.notification(player, "You received the following rewards for playing Safari " + (logins > 1 ? logins + " days in a row" : "today" ) + ": " + readable(out.gained) + (out.discarded.length > 0 ? " (couldn't receive " + readable(out.discarded) + " due to excess)" : ""), "Login", true); safaribot.sendMessage(src, "Your Itemfinder has been recharged to " + recharges + " charges!", safchan); if (safari.events.spiritDuelsEnabled) { var out = giveStuff(player, toStuffObj("5@spirit"), true); safaribot.sendMessage(src, "You received " + readable(out.gained) + (out.discarded.length > 0 ? " (couldn't receive " + readable(out.discarded) + " due to excess)" : ""), safchan); this.notification(player, "You received " + readable(out.gained) + (out.discarded.length > 0 ? " (couldn't receive " + readable(out.discarded) + " due to excess)" : ""), "Login"); } safari.clearQuestNotifications(player, "Login", true); if (hasMaster && reward.hasOwnProperty("@fragment") && reward["@fragment"] > 0) { safaribot.sendMessage(src, "As you try to put it away, the " + finishName("master") + " starts to glow very bright and then shatters in your hands. Sadly, all you could do was carefully grab a salvageable piece and stow it safely in your bag.", safchan); } if (logins % 32 === 30) { safaribot.sendHtmlMessage(src, "Tip: Logging in tomorrow will reward you with " + an(finishName("master")) + "!", safchan); safari.notification(player, "Tip: Logging in tomorrow will reward you with " + an(finishName("master")) + "!", "Login", true); } player.firstCelebrityRun = true; safari.trialsLogin(player); safari.bonusLogin(player); player.freebaits = 5; player.celebrityRegion = ["kanto", "johto", "hoenn", "sinnoh", "unova", "galar"].random(); reward = {}; if (false) { for (e = player.secretBase.length; e--; ) { temp = randomSample(decorations[player.secretBase[e].deco].drops); if (temp !== "none") { temp = toStuffObj(temp); for (t in temp) { if (!reward.hasOwnProperty(t)) { reward[t] = 0; } reward[t] += temp[t]; } } } out = giveStuff(player, reward, true); if (out.gained.length > 0) { safaribot.sendMessage(src, "Your Secret Base generated " + readable(out.gained) + (out.discarded.length > 0 ? " (it also generated " + readable(out.discarded) + ", which you discarded due to excess)" : "") + "!", safchan); this.inboxMessage(player, "Your Secret Base generated " + readable(out.gained) + (out.discarded.length > 0 ? " (it also generated " + readable(out.discarded) + ", which you discarded due to excess)" : "") + "!", true); } else if (out.discarded.length > 0) { safaribot.sendMessage(src, "Your Secret Base generated " + readable(out.discarded) + ", but you discarded them due to excess!", safchan); this.inboxMessage(player, "Your Secret Base generated " + readable(out.discarded) + ", but you discarded them due to excess!", true); } } /*if (player.burnLastUsed !== 0 && player.balls.burn > 0) { var n = Math.floor((now() - player.burnLastUsed) / hours(itemData.burn.threshold)); if (n > 0) { n = Math.min(n, player.balls.burn); player.balls.burn -= n; player.burnLastUsed = now(); safaribot.sendMessage(src, "You discarded " + plural(n, "burn") + " after noticing it was past its expiration date!", safchan); this.notification(player, "You discarded " + plural(n, "burn") + " after noticing it was past its expiration date!", "Items"); } }*/ if (safari.riceMode === true) { for (var p in player.party) { var mon = player.party[p]; var shiny = (typeof player.party[p] === "string"); if (shiny || (isRare(mon))) { sendAll("", false, true); sendAll(pokeInfo.icon(player.party[p]) + " -> " + pokeInfo.icon(132), true); sendAll(sys.name(sys.id(player.id)) + "'s " + poke(player.party[p]) + " became " + poke(132) + "!"); sendAll("", false, true); } } } this.renewMissions(player); } this.saveGame(player); }; this.getDailyReward = function(logins, masterLimit) { var out = {}; var moneyGained = 250 + 25 * logins; if (moneyGained > 1000) { moneyGained = 1000; } out.$ = moneyGained; var safariGained = logins; if (safariGained > 30) { safariGained = 30; } out["@safari"] = safariGained; var milestone = logins % 32; var milestoneRewards = { "31": { reward: "master", amount: 1 }, "27": { reward: "gacha", amount: 30 }, "24": { reward: "whtapricorn", amount: 10 }, "21": { reward: "blkapricorn", amount: 10 }, "18": { reward: "gacha", amount: 15 }, "15": { reward: "rare", amount: 1, repeatAmount: 2 }, "12": { reward: "bait", amount: 10 }, "9": { reward: "ultra", amount: 5, repeatAmount: 15}, "6": { reward: "great", amount: 8, repeatAmount: 25}, "3": { reward: "rock", amount: 5, repeatAmount: 25} }; if (milestone in milestoneRewards) { var reward = milestoneRewards[milestone]; var item = reward.reward; var amount = logins > 30 && "repeatAmount" in reward? reward.repeatAmount : reward.amount; if (item === "master" && masterLimit) { item = "fragment"; } out["@" +item] = amount; } return out; }; this.refundLogin = function(src, commandData) { var data = toCommandData(commandData, ["target", "base", "additional"]); var player = getAvatarOff(data.target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return; } var base = parseInt(data.base, 10); var add = parseInt(data.additional, 10); if (isNaN(base) || base < 1 || isNaN(add) || add < 1) { safaribot.sendMessage(src, "Invalid syntax! Use /refundlogin Player:PreviousLoginStreak:DaysToAdd.", safchan); return; } var reward = {}, temp, e, t, hasMaster = player.balls.master >= getCap("master"); for (e = 1; e <= add; e++) { temp = this.getDailyReward(base+e, hasMaster); for (t in temp) { if (!reward.hasOwnProperty(t)) { reward[t] = 0; } reward[t] += temp[t]; } } safari.bonusLogin(player); safari.trialsLogin(player); var out = giveStuff(player, reward, true); var gainedmsg = readable(out.gained) + (out.discarded.length > 0 ? " (couldn't receive " + readable(out.discarded) + " due to excess)" : ""); player.consecutiveLogins = base + add; if (player.consecutiveLogins > player.records.consecutiveLogins) { player.records.consecutiveLogins = player.consecutiveLogins; } this.saveGame(player); if (sys.id(data.target)) { safaribot.sendMessage(sys.id(data.target), "Your logins have been refunded! Your login streak was set to " + player.consecutiveLogins + " and you received " + gainedmsg + "!", safchan); } safaribot.sendMessage(src, "You refunded " + data.target.toCorrectCase() + " logins! Their login streak was set to " + player.consecutiveLogins + " and they received " + gainedmsg + "!", safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + data.target.toCorrectCase() + "|||refundlogin|||was login-refunded|||to " + base + " until " + (base+add) + " days and received " + gainedmsg + "\n"); }; this.refundRaffle = function(player, id, refund) { if (!player) { return; } var totalRefund = player.balls.entry * refund; player.balls.entry = 0; if (totalRefund) { player.money += totalRefund; if (id) { if (player.money >= moneyCap) { var excess = player.money - moneyCap; safaribot.sendMessage(id, "You received $" + addComma(totalRefund) + " for your refunded raffle entries but you could only hold $" + addComma(totalRefund - excess) + ". You now have $" + addComma(Math.min(moneyCap, player.money)) + ".", safchan); } else { safaribot.sendMessage(id, "You received $" + addComma(totalRefund) + " for your refunded raffle entries. You now have $" + addComma(player.money) + ".", safchan); } } } this.sanitize(player); }; this.sanitizeRaffle = function() { rafflePlayers.removeIf(function(hash, index) { var val = parseInt(hash.get(index), 10); return isNaN(val) || val <= 0; }); rafflePlayers.save(); }; this.getMedalSprite = function(icon, desc) { return ""; }; /* Medals */ this.viewMedals = function(src, target) { var out = [], m; var player; if (target) { player = getAvatarOff(target); if (!player) { safaribot.sendHtmlMessage(src, "No such player!", safchan); return; } out.push("" + player.id + "'s Medals: "); } else { player = getAvatarOff(sys.name(src)); out.push("Your Medals: (" + player.medals.length + "/" + medalCap + ")"); out.push("" + toColor("Note:", "red") + " Your top 6 medals are featured in your party. Once you reach " + medalCap + " medals, you cannot receive any more until you discard some."); } if (!player) { safaribot.sendMessage(src, "Player not found!", safchan); return; } if (player.medals.length < 1) { safaribot.sendMessage(src, "You do not have any medals!", safchan); return; } for (var i = 0; i < player.medals.length; i++) { m = player.medals[i]; out.push("#" + (i + 1) + ": " + this.getMedalSprite(m.icon, m.desc) + " " + m.desc + " [" + link("/featuremedal " + (i + 1), "Feature") + "] [" + link("/removemedal " + (i + 1), "Permanently Discard") + "]"); } sys.sendMessage(src, "", safchan); for (var o in out) { safaribot.sendHtmlMessage(src, out[o], safchan); } sys.sendMessage(src, "", safchan); return true; }; this.featureMedal = function(src, name, data) { var player = getAvatarOff(name), ls, m, index; index = parseInt(data, 10); if (!player) { safaribot.sendMessage(src, "Player not found!", safchan); return; } if (!index) { safaribot.sendMessage(src, "That's not a number!", safchan); return; } if (index - 1 > player.medals.length || index < 1) { safaribot.sendMessage(src, "Index out of range!", safchan); return; } m = player.medals[index - 1]; ls = player.medals; ls.splice(index - 1, 1); ls.unshift(m); safaribot.sendHtmlMessage(src, "You featured your medal: " + this.getMedalSprite(m.icon, m.desc) + " " + m.desc + "! It will now appear on your trainer screen!", safchan); this.saveGame(player); return true; }; this.removeMedal = function(src, name, data) { var player = getAvatarOff(name), ls, m, index; data = data.split(":"); index = parseInt(data, 10); if (!player) { safaribot.sendMessage(src, "Player not found!", safchan); return; } if (!index) { safaribot.sendMessage(src, "That's not a valid number!", safchan); return; } if (!player.medals[index - 1]) { safaribot.sendMessage(src, "Index out of range!", safchan); return; } if (data.length < 2) { safaribot.sendHtmlMessage(src, "You are about to PERMANENTLY discard your medal {0} {1}, {2} if you're sure you want to continue.".format(this.getMedalSprite(player.medals[index-1].icon, player.medals[index-1].desc), player.medals[index-1].desc, link("/removemedal " + index + ":confirm", "click here")), safchan); //safaribot.sendMessage(src, "Add 'confirm' after the ':' to confirm that you intend to discard this medal! WARNING: This will permanently delete the medal!", safchan); return; } if (data[1] !== "confirm") { safaribot.sendMessage(src, "Add 'confirm' after the ':' to confirm that you intend to discard this medal! WARNING: This will permanently delete the medal!", safchan); return; } m = player.medals[index - 1]; player.medals.splice(index - 1, 1); safaribot.sendHtmlMessage(src, "You discarded your medal: " + this.getMedalSprite(m.icon, m.desc) + " " + m.desc + ".", safchan); //band-aid fix since im not sure what causes this yet, but it just happened and i can't replicate it player.medals = player.medals.filter(function(e) { return e !== null }); //this.viewMedals(src); this.saveGame(player); return true; }; /* Missions */ this.describeMission = function(m) { var g = Math.min(m.count, m.goal) + "/" + m.goal; return m.desc + " " + (m.count >= m.goal ? toColor("(" + g + ")", "blue") : "("+g+")") + " - Reward: " + translateStuff(m.reward) + " + " + plural(m.points, "mission point") + (m.finished ? toColor(" [Received]", "red") : ""); }; this.viewMissions = function(src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), today = player.lastLogin, m, g, finished = []; sys.sendMessage(src, "", safchan); sys.sendMessage(src, "*** CURRENT DAILY MISSIONS ***", safchan); for (var e = 0; e < player.missions.length; e++) { m = player.missions[e]; sys.sendHtmlMessage(src, "" + (e+1) + ". " + safari.describeMission(m) + (canSwapMission(m) ? " [" + link("/swapmission " + e, "Swap") + "]" : ""), safchan); if (!m.finished && m.count >= m.goal) { finished.push(m); } } var clearedAny = false; for (e = 0; e < finished.length; e++) { m = finished[e]; if (canReceiveStuff(player, m.reward).result) { g = giveStuff(player, toStuffObj(m.reward)); player.records.missionPoints += m.points; player.missionPoints += m.points; player.records.missionCleared += 1; clearedAny = true; safaribot.sendHtmlMessage(src, toColor("You " + g + " + " + plural(m.points, "mission point") + " for clearing the following mission: " + m.desc, "blue"), safchan); safari.detectiveClue(player.idnum, "mission", src); m.finished = true; } else { safaribot.sendHtmlMessage(src, toColor("You cleared a mission (" + m.desc + "), but you can't receive the reward (" + translateStuff(m.reward) + "). Type /mission again when you have space to receive the reward.", "red"), safchan); } } sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, (clearedAny ? "You now have " : "You currently have ") + plural(player.missionPoints, "mission point") + "! (Cumulative: " + addComma(player.records.missionPoints) + ")", safchan); safari.toRecentQuests(player, "missions"); player.notificationData.missionWaiting = true; sys.sendMessage(src, "", safchan); safari.pendingNotifications(player.id); safari.saveGame(player); }; this.swapMission = function(src, commandData) { var player = getAvatar(src); if (!player || !player.missions.length) { return; } if (!commandData) { safaribot.sendHtmlMessage(src, "Please provide a mission index to swap!", safchan); return; } var split = commandData.split(":"); var index = parseInt(split[0]); var confirm = split[1] === "confirm"; if (isNaN(index) || index < 0 || index >= player.missions.length) { safaribot.sendHtmlMessage(src, "Invalid mission index!", safchan); return; } var mission = player.missions[index]; if (!canSwapMission(mission)) { safaribot.sendHtmlMessage(src, "This mission can't be swapped out!", safchan); return; } var swapCost = mission.points * 3; if (!confirm) { safaribot.sendHtmlMessage(src, "This mission [" + safari.describeMission(mission) + "] will cost " + plural(swapCost, "mission point") + " to swap. Type " + link("/swapmission " + index + ":confirm") + " to confirm.", safchan); return; } if (player.missionPoints < swapCost) { safaribot.sendHtmlMessage(src, "You don't have enough mission points to swap this mission! You need " + plural(swapCost, "mission point") + ", but you only have " + addComma(player.missionPoints) + ".", safchan); return; } var newMission; do { newMission = safari.generateMission(mission.level); } while (safari.checkMissionConflicts(newMission, player.missions, player.lastLogin)); if (!newMission) { safaribot.sendHtmlMessage(src, "Error generating new mission!", safchan); return; } newMission.count = 0; newMission.day = player.lastLogin; newMission.finished = false; player.missionPoints -= swapCost; var oldDesc = safari.describeMission(mission); var newDesc = safari.describeMission(newMission); safaribot.sendHtmlMessage(src, "You used " + plural(swapCost, "mission point") + " and swapped out the old mission [" + oldDesc + "] for a new one [" + newDesc + "]!", safchan); player.missions.splice(index, 1, newMission); sys.appendToFile(miscLog, now() + "|||" + player.id.toCorrectCase() + "|||used " + plural(swapCost, "mission point") + " and swapped out an old mission [" + oldDesc + "] for a new one [" + newDesc + "]\n"); safari.saveGame(player); }; this.renewMissions = function(player) { var today = player.lastLogin, missions = player.missions, e, m; for (e = missions.length; e--; ) { m = missions[e]; if (m.finished || m.goal > m.count) { missions.splice(e, 1); } } for (e = 1; e <= 5; e++) { do { m = this.generateMission(e); } while (this.checkMissionConflicts(m, player.missions, today)); if (!m) { continue; } m.count = 0; m.day = today; m.finished = false; player.missions.push(m); } this.saveGame(player); }; this.generateMission = function(level) { try { var mission = missionsData[level].random(); } catch (err) { return false; } var newMission = JSON.parse(JSON.stringify(mission)); var targetDesc; if (newMission.template) { newMission.target = newMission.target.random(); delete newMission.template; } switch (newMission.type) { case "move": targetDesc = moveOff(movenum(newMission.target)); break; case "where": targetDesc = an(themeName(newMission.target)); break; case "mood": targetDesc = an(newMission.target); break; case "score": targetDesc = photoQuality[newMission.target]; break; case "what": case "amt": targetDesc = newMission.target; break; default: if (typeof newMission.target === "string") { targetDesc = cap(newMission.target); } else { targetDesc = newMission.target; } } newMission.desc = newMission.desc.replace(/\$\{0\}/g, "$"+addComma(newMission.goal)); newMission.desc = newMission.desc.format(newMission.goal, targetDesc); newMission.reward = missionsData.rewards[level].random(); newMission.level = level; return newMission; }; this.checkMissionConflicts = function(mission, list, today) { if (!mission) { return false; } var obj, e; var noTargetDuplicates = ["playContest", "catchContest", "winContest", "towerFloor", "arenaSilver", ]; for (e = list.length; e--; ) { obj = list[e]; if (obj.count >= obj.goal || obj.day !== today) { continue; } if (obj.objective === mission.objective && obj.type === mission.type && (noTargetDuplicates.contains(obj.objective) || obj.target === mission.target)) { return true; } if (obj.target === "cross" && mission.target === "cross") { return true; } } return false; }; this.missionProgress = function(player, action, target, value, data) { // action = What the player did (catch a Pokémon, photo, finish a quest, etc) // target = Action's target (Pokémon caught, photographed, quest finished, etc) // value = How many of target this action accomplished (How many Pokémon were caught, how much money was earned, etc) // data = Other info (Pokémon used, Poké Ball used, etc var e, m, p, g, src; if (player.missions) { for (e = 0; e < player.missions.length; e++) { m = player.missions[e]; if (!m.finished && m.count < m.goal) { p = this.countProgress(m, action, target, value, data); if (p) { m.count = Math.min(m.count + p, m.goal); if (isPlaying(player.id)) { src = sys.id(player.id); g = Math.min(m.count, m.goal) + "/" + m.goal; sys.sendHtmlMessage(src, toColor("±Mission: ", "#3daa68") + "" + m.desc + " " + (m.count >= m.goal ? toColor("(" + g + ")", "blue") : "("+g+")") + " - Reward: " + translateStuff(m.reward) + " + " + plural(m.points, "mission point") + (m.finished ? toColor(" [Received]", "red") : "") + "", safchan); } } } } } if ((safari.events) && (safari.events.trialsEnabled) && (player.trials)) { for (e = player.trials.missions.length; e--; ) { m = player.trials.missions[e]; if (m.count < m.goal) { p = this.countProgress(m, action, target, value, data); if (p) { m.count = Math.min(m.count + p, m.goal); } } } } }; this.countProgress = function(mission, action, target, value, data) { var out = 0; switch (mission.objective) { case "catchPokeWithStarter": out = (action === "catch" && data.starter && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "catchPoke": out = (action === "catch" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "catchPokeAny": out = (action === "catchAny" && mission.target.indexOf(parseInt(target, 10)) !== -1 ? value : 0); break; case "contestPoke": out = (action === "catch" && this.pokeMatchesMission(target, mission.type, mission.target) && contestCount > 0 ? value : 0); break; case "catchMirage": out = (action === "catchMirage" ? value : 0); break; case "catchScoop": out = (action === "catchScoop" ? value : 0); break; case "baitWithBotD": out = (action === "bait" && data.botd && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "baitPoke": out = (action === "bait" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "sellPoke": out = (action === "sell" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "photoPoke": out = (action === "photo" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "wonderPoke": out = (action === "wonder" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "evolvePoke": out = (action === "evolve" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "hatchPoke": out = (action === "hatch" && this.pokeMatchesMission(target, mission.type, mission.target) ? value : 0); break; case "clonePoke": out = (action === "catch" && this.pokeMatchesMission(target, mission.type, mission.target) && data.clone ? value : 0); break; case "catchWithPoke": out = (action === "catch" && this.pokeMatchesMission(data.active, mission.type, mission.target, data) ? value : 0); break; case "catchWithBall": out = (action === "catch" && data.ball === mission.target ? value : 0); break; case "rockWindow": out = (action === "rockWindow" ? value : 0); break; case "rockHit": out = (action === "rockHit" ? value : 0); break; case "findApricorn": out = (action === "findApricorn" ? value : 0); break; case "findHoney": out = (action === "findHoney" ? value : 0); break; case "findCookie": out = (action === "findCookie" ? value : 0); break; case "getShocked": out = (action === "getShocked" ? value : 0); break; case "playContest": out = (action === "contest" && data.thrown >= mission.target ? value : 0); break; case "catchContest": out = (action === "contest" && data.caught >= mission.target ? value : 0); break; case "winContest": out = (action === "contest" && data.won ? value : 0); break; case "winContestTheme": out = (action === "contest" && data.won && data.theme === mission.target ? value : 0); break; case "winContestLead": out = (action === "contest" && data.won && mission.target.indexOf( parseInt(data.lead, 10) ) !== -1 ? value : 0); break; case "winContestPrize": out = (action === "contestPrize" && mission.target.indexOf( target ) !== -1 ? value : 0); break; case "collectorLevel": out = (action === "collector" && mission.target === data.amount ? value : 0); break; case "towerFloorMono": out = (action === "tower" && target >= mission.target && data.unique && data.mono.indexOf(mission.type) !== -1 ? value : 0); break; case "towerFloorCanEvolve": out = (action === "tower" && target >= mission.target && data.canEvolve === true ? value : 0); break; case "towerFloorBST": out = (action === "tower" && target >= mission.target && data.lowBST ? value : 0); break; case "towerFloor": out = (action === "tower" && target >= mission.target ? value : 0); break; case "gymLeaderMono": out = (action === "gymLeader" && target >= mission.target && data.mono.contains(mission.type) ? value : 0); break; case "celebrity": out = (action === "celebrity" && target >= mission.target ? value : 0); break; case "celebrityHard": out = (action === "celebrity" && data.level > 0 && target >= mission.target ? value : 0); break; case "celebrityAbyssal": out = (action === "celebrity" && data.level > 3 && target >= mission.target ? value : 0); break; case "celebrityGeneration": out = (action === "celebrity" && target >= mission.target && data.gen[0] === mission.type && data.unique ? 1 : 0); break; case "celebrityGenerationHard": out = (action === "celebrity" && data.level > 0 && target >= mission.target && data.gen[0] === mission.type && data.unique ? 1 : 0); break; case "celebrityGenerationExpert": out = (action === "celebrity" && data.level > 1 && target >= mission.target && data.gen[0] === mission.type && data.unique ? 1 : 0); break; case "celebrityGenerationSuperExpert": out = (action === "celebrity" && data.level > 2 && target >= mission.target && data.gen[0] === mission.type && data.unique ? 1 : 0); break; case "celebrityMono": out = (action === "celebrity" && target >= mission.target && data.mono.contains(mission.type) && data.unique ? 1 : 0); break; case "celebrityMonoHard": out = (action === "celebrity" && data.level > 1 && target >= mission.target && data.mono.contains(mission.type) && data.unique ? 1 : 0); break; case "celebrityMonoAbyssal": out = (action === "celebrity" && data.level > 3 && target >= mission.target && data.mono.contains(mission.type) && data.unique ? 1 : 0); break; case "scientistSilver": out = (action === "scientist" ? data.silver : 0); break; case "arenaSilver": out = (action === "arena" && data.silver > mission.target ? data.silver : 0); break; case "pyramidPoints": out = (action === "pyramid" ? data.points : 0); break; case "pyramidRun": out = (action === "pyramid" && data.points >= mission.target ? 1 : 0); break; case "journal": out = (action === "journal" ? target : 0); break; case "sellMoney": out = (action === "sell" ? data.price : 0); break; case "pawnMoney": out = (action === "pawn" ? data.price : 0); break; case "luxuryMoney": out = (action === "catch" && data.luxury ? data.luxury : 0); break; case "collectorMoney": out = (action === "collector" ? data.price : 0); break; case "pokePhoto": out = (action === "photo" && this.photoMatchesMission(data.photo, mission.type, mission.target) ? value : 0); break; case "sodaFromTrivia": out = (action === "sodaFromTrivia" ? value : 0); break; case "shadyFromMafia": out = (action === "shadyFromMafia" ? value : 0); break; case "winMonger": out = (action === "winMonger" && (target.indexOf( mission.target ) > -1 ) ? value : 0); break; case "winTour": out = (action === "winTour" ? value : 0); break; case "completeDetective": out = (action === "completeDetective" ? value : 0); break; case "toursCross": case "triviaCross": case "mafiaCross": case "hangmanCross": out = (action === "cross" && target === mission.target ? value : 0); break; } return out; }; this.pokeMatchesMission = function(id, type, target, data) { switch (type) { case "type": return hasType(id, sys.type(sys.typeNum(target))); case "color": return (data && data.color ? data.color : getPokeColor(id)) === target; case "region": return generation(id, true, true).toLowerCase() === target.toLowerCase(); case "bst": return isInRange(getBST(id), target); case "height": return isInRange(parseFloat(getHeight(id)), target); case "weight": return isInRange(parseFloat(getWeight(id)), target); case "ability": return canHaveAbility(id, abilitynum(target)); case "move": return canLearnMove(id, movenum(target)); case "name": return pokePlain(id)[0].toLowerCase() === target.toLowerCase(); case "fullname": return pokePlain(id).toLowerCase() === target.toLowerCase(); case "any": return true; } return false; }; this.photoMatchesMission = function(photo, type, target) { switch (type) { case "amt": return photo.amt >= target; case "when": return photo.when === target; case "where": return photo.where === target; case "what": return photo.what === target; case "mood": return photo.mood === target; case "score": return photo.score >= target; } return false; }; function canSwapMission(mission) { return !(mission.finished || (mission.day !== getDay(now())) || !mission.level || mission.special || (mission.count >= mission.goal)); } /* Events */ this.safariEvents = function(src,what,enable) { if (!safari.events) { safari.events = { bonusLoginEnabled: false, bonusLoginName: "", trialsEnabled: false, trialsData: {}, spiritDuelsEnabled: false, spiritDuelsData: {}, hiddenQuizEnabled: false, hiddenQuizData: {}, towerTroubleEnabled: false, towerTroubleData: {} }; } switch (what) { case "trials": if (enable && (!(safari.events.trialsData))) { safaribot.sendMessage(src,"Use /loadtrials [url] to load trials before enabling them!"); } else { safari.events.trialsEnabled = (enable ? true : false); safaribot.sendMessage(src,"Event Trials " + (enable ? "enabled" : "disabled") + "!" ); if (!(enable)) { safari.events.trialsData = {}; } } break; case "duels": safari.events.spiritDuelsEnabled = (enable ? true : false); safaribot.sendMessage(src,"Event Spirit Duels " + (enable ? "enabled" : "disabled") + "!" ); if (!(enable)) { safari.events.spiritDuelsData = {}; safari.events.spiritDuelsBattling = false; } break; case "login": safari.events.bonusLoginEnabled = (enable ? true : false); safaribot.sendMessage(src,"Event Login Bonus " + (enable ? "enabled" : "disabled") + "!" ); break; case "hiddenquiz": case "quiz": safari.events.hiddenQuizEnabled = (enable ? true : false); safaribot.sendMessage(src,"Event Hidden Quiz " + (enable ? "enabled" : "disabled") + "!" ); break; case "tt": case "towertrouble": safari.events.towerTroubleEnabled = (enable ? true : false); safaribot.sendMessage(src,"Event Tower Trouble " + (enable ? "enabled" : "disabled") + "!" ); break; default: return; } permObj.add("events", JSON.stringify(safari.events)); } /* Bonus Login */ this.bonusLogin = function(player) { var bg; if (!player) { return; } if (!safari.events.bonusLoginEnabled) { return; } if (!player.bonusLogin) { player.bonusLogin = { index: 0, name: safari.events.bonusLoginName } } if (player.bonusLogin.name !== safari.events.bonusLoginName) { player.bonusLogin.index = 0; player.bonusLogin.name = safari.events.bonusLoginName } if (player.bonusLogin.index >= safari.events.bonusLoginRewards.length) { return; } if (safari.events.bonusLoginColor) { bg = safari.events.bonusLoginColor; } else { bg = "#ffd800"; } var rew = safari.events.bonusLoginRewards[player.bonusLogin.index]; var g = giveStuff(player, toStuffObj(rew)); safari.notification(player, "As part of the " + player.bonusLogin.name + " Event, you " + g + "! Thanks for playing!", "Bonus Login"); player.bonusLogin.index++; return true; }; this.bonusLoginPrint = function(src) { var player = getAvatar(src); if (!player) { return; } if (!safari.events.bonusLoginEnabled) { safaribot.sendHtmlMessage(src, "No Bonus Login even is currently running!", safchan); return; } if (player.bonusLogin.index >= safari.events.bonusLoginRewards.length) { safaribot.sendHtmlMessage(src, "You've collected all the Bonus Login rewards from " + player.bonusLogin.name + "!", safchan); return; } var amt = safari.events.bonusLoginRewards.length - player.bonusLogin.index; if (safari.events.bonusLoginColor) { bg = safari.events.bonusLoginColor; } else { bg = "#ffd800"; } safaribot.sendHtmlMessage(src, "Bonus Login: " + player.bonusLogin.name + ". Log in every day for a reward " + amt + " more times!", safchan); } /* Trials */ this.assignTrials = function(src,player) { if (!player) { return; } if (!safari.events.trialsEnabled) { return; } if (!player.trials) { return; } var k, m, l; while (player.trials.missions.length < 5) { l = this.findTrials(player,safari.events.trialsData[player.trials.level + ""]); if (l.length === 0) { player.trials.level++; if (player.trials.level <= 7) { l = this.findTrials(player,safari.events.trialsData[player.trials.level + ""]); } else { /* Completed all trials */ l = null; player.trials.level--; } } if (!(l)) { return; //Could not find any new missions to give, so it gives nothing } else { m = JSON.parse(JSON.stringify(l.random())); } player.trials.missions.push(m); player.trials.missions[player.trials.missions.indexOf(m)].count = 0; player.trials.currentIDs.push(player.trials.missions[player.trials.missions.indexOf(m)].id); safaribot.sendHtmlMessage(src, toColor( " New Trial: " + m.desc + " (" + plural(m.points, "trials point") + ")", "#32CD32" ), safchan); } }; this.releaseTrial = function(src,player,id) { var k, m, d; for (var e = player.trials.missions.length; e--; ) { m = player.trials.missions[e]; d = m.id; if (m.id === id) { var k = player.trials.missions.indexOf(m); player.trials.missions.splice(k, 1); this.assignTrials(src,player); //sends the new trial to the auth return; } } safaribot.sendMessage(src, "No ID " + id + " found in target's missions",safchan); player.trials.currentIDs.push(id); return; }; this.resetTrial = function(src,player,id) { var k, m, d; player.trials.currentIDs = player.trials.currentIDs.slice(player.trials.currentIDs.indexOf(id), 1); for (var e = player.trials.missions.length; e--; ) { m = player.trials.missions[e]; d = m.id; if (m.id === id) { var k = player.trials.missions.indexOf(m); player.trials.missions.splice(k, 1); this.assignTrials(src,player); //sends the new trial to the auth return; } } return; }; this.setTrialsPoints = function(src,target,num) { if (!target.trials) { return; } var old = target.trials.points; target.trials.points = num; safaribot.sendMessage(src, "Changed " + target.id + "'s trial points from " + old + " to " + target.trials.points + ".",safchan); }; this.trialsLogin = function(player) { if (player.trials && safari.events.hasOwnProperty("trialsEnabled") ? safari.events.trialsEnabled : false) { safaribot.sendMessage(sys.id(player.id), "You received +1 bonus Trials point for logging in today!",safchan); if (player.trials.name !== safari.events.trialsData.name) { player.trials.points = 0; } player.trials.points += 1; } return; }; this.grantTrialsBonusPoints = function(src,player) { var k, m, d, out = []; if (player.trials.bonusPointsReceived === false) { player.trials.bonusPointsReceived = true; player.trials.points += 10; safaribot.sendMessage(src, "Bonus 10 points granted to " + player.id + "!",safchan); return; } safaribot.sendMessage(src, player.id + " already received their bonus points!",safchan); return; }; this.findTrials = function(player,tier) { var k, out = []; if (!tier) { return null; } for (var t in tier) { k = tier[t]; if ((player.trials.pastIDs.indexOf(k.id) === -1) && (player.trials.currentIDs.indexOf(k.id) === -1) ) { out.push(k); } } return out; }; this.viewTrials = function(src) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), m, g, finished = []; if (!safari.events) { safaribot.sendMessage(src, "Trials is currently not in session!", safchan); return; } if ((!safari.events.trialsEnabled)) { safaribot.sendMessage(src, "Trials is currently not in session!", safchan); return; } var name = safari.events.trialsData.name; if ((player.trials) && (player.trials.name !== name)) { /* Unloads the data from last trials session if it exists */ player.trials = null; } if (!player.trials) { player.trials = {}; player.trials.missions = []; player.trials.pastIDs = []; player.trials.currentIDs = []; player.trials.level = 1; player.trials.points = 0; player.trials.name = name; player.trials.bonusPointsReceived = false; } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, toColor( "*** " + name + " Trials ***", "#BA55D3" ), safchan); for (var e = 0; e < player.trials.missions.length; e++) { m = player.trials.missions[e]; if (m.count >= m.goal) { finished.push(m); } g = Math.min(m.count, m.goal) + "/" + m.goal; safaribot.sendHtmlMessage(src, "" + (e+1) + ". " + m.desc + " " + (m.count >= m.goal ? toColor("(" + g + ")", "blue") : "("+g+")") + " + " + plural(m.points, "trials point"), safchan); } var clearedAny = false, rew; for (e = 0; e < finished.length; e++) { m = finished[e]; clearedAny = true; rew = safari.events.trialsData.rewards[player.trials.level + ""].random(); //Gives rewards based on player's current level, not the mission's level g = giveStuff(player, toStuffObj(rew)); player.trials.points += m.points; safaribot.sendHtmlMessage(src, toColor("You " + g + " + " + plural(m.points, "trials point") + " for clearing the following mission: " + m.desc, "blue"), safchan); var k = player.trials.missions.indexOf(m); player.trials.missions.splice(k, 1); } this.assignTrials(src,player); if (clearedAny) { safaribot.sendMessage(src, "You now have " + plural(player.trials.points, "trials point") + "!", safchan); } else { safaribot.sendMessage(src, "You have " + plural(player.trials.points, "trials point") + "!", safchan); } sys.sendMessage(src, "", safchan); }; this.viewTrialsLb = function(src,num) { var player, points, name, id; var playerPoints = []; var limit = parseInt(num, 10); for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); name = player.casedName; id = player.id; if (!player.trials) { continue; } if (!player.trials.points) { continue; } if (player.trials.points <= 0) { continue; } points = player.trials.points; playerPoints.push({ id: id, points: points }); } } playerPoints.sort(function(a, b) { return a.points - b.points; }); safaribot.sendMessage(src, "Top " + playerPoints.length + " players in trials by points: ", safchan); var j = 1; var received = [], p; for (var i = playerPoints.length; i--;) { //p = getAvatarOff(playerPoints[i].id); safaribot.sendMessage(src, "#" + j + ": " + playerPoints[i].id + " (" + playerPoints[i].points + ")", safchan); received.push(playerPoints[i].id); j++; } return; }; this.endTrials = function() { var t = Object.keys(rawPlayers), player, points; var playerPoints = [], src; var strata = safari.events.trialsData.payout.strata, k, id; var top = safari.events.trialsData.payout.top; var p; for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); name = player.casedName; id = player.id; if (!player.trials) { continue; } if (!player.trials.points) { continue; } if (player.trials.points <= 0) { continue; } points = player.trials.points; playerPoints.push({ id: id, points: points }); } for (var s in strata) { k = strata[s]; rew = k.prizes; if (k.range[0] <= points && k.range[1] >= points) { break; } } src = sys.id(player.id); p = getAvatarOff(player.id); for (var h in rew) { g = giveStuff(p, toStuffObj(rew[h])); if (sys.isInChannel(sys.id(player.id), safchan)) { safaribot.sendHtmlMessage(src, toColor("For earning " + points + " points in the " + safari.events.trialsData.name + " Trials, you " + g + "!", "blue"), safchan); } this.notification(p, "For earning " + points + " points in " + safari.events.trialsData.name + " Trials, you " + g + "!", "Trials"); } } playerPoints.sort(function(a, b) { return b.points - a.points; }) var j = 1; var limit = Math.min(playerPoints.length, 2); for (var i in playerPoints) { p = getAvatarOff(playerPoints[i].id); src = sys.id(p.id); rew = top[j+""]; rew = rew.random(); g = giveStuff(p, toStuffObj(rew)); safaribot.sendHtmlMessage(src, toColor("For placing #" + j + " in " + safari.events.trialsData.name + " Trials, you " + g + "!", "blue"), safchan); this.notificiation(p, "For placing #" + j + " in " + safari.events.trialsData.name + " Trials, you " + g + "!", "Trials"); safaribot.sendHtmlAll(toColor("(#" + j + "): " + p.id.toCorrectCase() + " " + g + "!!", "#BA55D3"), safchan); j++; if (j >= 3) { break; } } return; }; /* Spirit Duels */ this.clearSpiritDuels = function() { for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); } if (player && player.id) { src = sys.id(player.id); p = getAvatarOff(player.id); if (p && p.spiritDuels) { p.balls.spirit = 0; p.spiritDuels.rank = -1; p.spiritDuels.rankName = "Zoomer"; p.spiritDuels.team = "None"; p.spiritDuels.exp = -2000; p.spiritDuels.box = []; p.spiritDuels.skills = []; p.spiritDuels.skillChoices = {}; p.spiritDuels.roundStreak = 0; p.spiritDuels.prizePoints = 0; safari.saveGame(p); } } } return; }; this.loadDuels = function(src,data) { //Just loads the data var m; safari.events.spiritDuelsTeams = []; safari.events.spiritDuelsSignups = []; safari.events.spiritDuelsRefugees = []; for (var k in data.teams) { m = data.teams[k]; safari.events.spiritDuelsTeams.push({ players: [], name: m.name, skills: m.skills, alive: true, won: 0, fought: 0, rate: 0, activityWarned: {} }); } safari.events.spiritDuelsLog = []; safari.events.spiritBoxLimit = 140; safari.events.bonusSpiritEnlistRanks = [3, 7, 9]; safari.events.spiritDuelsRanks = [ {rank: "Grunt", exp: 0}, {rank: "Ensign", exp: 2000}, {rank: "Officer Trainee", exp: 6000}, {rank: "Secretary Officer", exp: 10000}, {rank: "Squadron Leader", exp: 15000}, {rank: "Field Lieutenant", exp: 22000}, {rank: "Commander", exp: 30000}, {rank: "Vice Admiral", exp: 35000}, {rank: "Administrator", exp: 40000}, {rank: "Supreme Master", exp: 50000} ]; safari.events.spiritDuelsSkills = { "Ensign": [ {type: "type", target: "Poison", val: 20, desc: "Poison type Spirits receive a fighting advantage."}, {type: "type", target: "Bug", val: 20, desc: "Bug type Spirits receive a fighting advantage."}, {type: "type", target: "Normal", val: 20, desc: "Normal type Spirits receive a fighting advantage."}, {type: "region", target: "Kanto", val: 10, desc: "Spirits from Kanto receive a slight fighting advantage."}, {type: "region", target: "Alola", val: 10, desc: "Spirits from Alola receive a slight fighting advantage."}, {type: "color", target: "Green", val: 12, desc: "Green Spirits receive a fighting advantage."}, {type: "color", target: "Yellow", val: 12, desc: "Yellow Spirits receive a fighting advantage."}, {type: "any", target: "", val: 3, desc: "All Spirits receive a very minor fighting advantage."}, {type: "catch", target: "rate", val: 1.06, desc: "Catch rate with Spirit Balls raised slightly."}, {type: "exp", target: "rate", val: 1.1, desc: "Generate Spirit Duels EXP at a higher rate."} ], "Officer Trainee": [ {type: "type", target: "Psychic", val: 22, desc: "Psychic type Spirits receive a fighting advantage."}, {type: "type", target: "Grass", val: 22, desc: "Grass type Spirits receive a fighting advantage."}, {type: "type", target: "Ice", val: 22, desc: "Ice type Spirits receive a fighting advantage."}, {type: "region", target: "Johto", val: 12, desc: "Spirits from Johto receive a slight fighting advantage."}, {type: "region", target: "Kalos", val: 12, desc: "Spirits from Kalos receive a slight fighting advantage."}, {type: "color", target: "Red", val: 13, desc: "Red Spirits receive a fighting advantage."}, {type: "color", target: "Purple", val: 13, desc: "Purple Spirits receive a fighting advantage."}, {type: "any", target: "", val: 4, desc: "All Spirits receive a very minor fighting advantage."}, {type: "catch", target: "rate", val: 1.12, desc: "Catch rate with Spirit Balls raised slightly."}, {type: "exp", target: "rate", val: 1.2, desc: "Generate Spirit Duels EXP at a higher rate."} ], "Secretary Officer": [ {type: "type", target: "Flying", val: 24, desc: "Flying type Spirits receive a fighting advantage."}, {type: "type", target: "Fighting", val: 24, desc: "Fighting type Spirits receive a fighting advantage."}, {type: "type", target: "Rock", val: 24, desc: "Rock type Spirits receive a fighting advantage."}, {type: "region", target: "Hoenn", val: 14, desc: "Spirits from Hoenn receive a slight fighting advantage."}, {type: "region", target: "Unova", val: 14, desc: "Spirits from Unova receive a slight fighting advantage."}, {type: "color", target: "Black", val: 14, desc: "Black Spirits receive a fighting advantage."}, {type: "color", target: "White", val: 14, desc: "White Spirits receive a fighting advantage."}, {type: "any", target: "", val: 5, desc: "All Spirits receive a very minor fighting advantage."}, {type: "catch", target: "rate", val: 1.18, desc: "Catch rate with Spirit Balls raised slightly."}, {type: "exp", target: "rate", val: 1.3, desc: "Generate Spirit Duels EXP at a higher rate."}, ], "Squadron Leader": [ {type: "type", target: "Fire", val: 24, desc: "Fire type Spirits receive a fighting advantage."}, {type: "type", target: "Water", val: 24, desc: "Water type Spirits receive a fighting advantage."}, {type: "type", target: "Electric", val: 24, desc: "Electric type Spirits receive a fighting advantage."}, {type: "region", target: "Sinnoh", val: 15, desc: "Spirits from Sinnoh receive a slight fighting advantage."}, {type: "region", target: "Kanto", val: 15, desc: "Spirits from Kanto receive a slight fighting advantage."}, {type: "color", target: "Blue", val: 15, desc: "Blue Spirits receive a fighting advantage."}, {type: "color", target: "Grey", val: 15, desc: "Grey Spirits receive a fighting advantage."}, {type: "any", target: "", val: 6, desc: "All Spirits receive a very minor fighting advantage."}, {type: "catch", target: "rate", val: 1.24, desc: "Catch rate with Spirit Balls raised slightly."}, {type: "exp", target: "rate", val: 1.4, desc: "Generate Spirit Duels EXP at a higher rate."} ], "Field Lieutenant": [ {type: "type", target: "Ground", val: 25, desc: "Ground type Spirits receive a fighting advantage."}, {type: "type", target: "Fairy", val: 25, desc: "Fairy type Spirits receive a fighting advantage."}, {type: "type", target: "Dark", val: 25, desc: "Dark type Spirits receive a fighting advantage."}, {type: "region", target: "Johto", val: 16, desc: "Spirits from Johto receive a slight fighting advantage."}, {type: "region", target: "Unova", val: 16, desc: "Spirits from Unova receive a slight fighting advantage."}, {type: "move", target: "Dual Chop", val: 30, desc: "Spirits that can know Dual Chop receive a fighting advantage."}, {type: "move", target: "Solar Beam", val: 30, desc: "Spirits that can know Solar Beam receive a fighting advantage."}, {type: "move", target: "Double Kick", val: 30, desc: "Spirits that can know Double Kick receive a fighting advantage."}, {type: "any", target: "", val: 7, desc: "All Spirits receive a very minor fighting advantage."}, {type: "catch", target: "rate", val: 1.3, desc: "Catch rate with Spirit Balls raised slightly."}, {type: "catch", target: "cooldown", val: 1.1, desc: "Catch cooldown on Spirit Balls reduced slightly."} ], "Commander": [ {type: "type", target: "Ghost", val: 25, desc: "Ghost type Spirits receive a fighting advantage."}, {type: "type", target: "Steel", val: 25, desc: "Steel type Spirits receive a fighting advantage."}, {type: "type", target: "Dragon", val: 25, desc: "Dragon type Spirits receive a fighting advantage."}, {type: "region", target: "Hoenn", val: 17, desc: "Spirits from Hoenn receive a slight fighting advantage."}, {type: "region", target: "Kalos", val: 17, desc: "Spirits from Kalos receive a slight fighting advantage."}, {type: "move", target: "Earthquake", val: 32, desc: "Spirits that can know Earthquake receive a fighting advantage."}, {type: "move", target: "Calm Mind", val: 32, desc: "Spirits that can know Calm Mind receive a fighting advantage."}, {type: "move", target: "Taunt", val: 32, desc: "Spirits that can know Taunt receive a fighting advantage."}, {type: "attack", target: "", val: 12, scaling: true, desc: "Spirits with at least 100 Attack stat receive a fighting advantage. This skill is 0.2x stronger for every 10 Attack stat past 100, up to a maximum multiplier of 3x."}, {type: "catch", target: "cooldown", val: 1.17, desc: "Catch cooldown on Spirit Balls reduced slightly."} ], "Vice Admiral": [ {type: "singletype", target: "", val: 37, desc: "Single-type Spirits receive a major fighting advantage."}, {type: "move", target: "Overheat", val: 34, desc: "Spirits that can know Overheat receive a fighting advantage."}, {type: "move", target: "Megahorn", val: 34, desc: "Spirits that can know Megahorn receive a fighting advantage."}, {type: "move", target: "Stored Power", val: 34, desc: "Spirits that can know Stored Power receive a fighting advantage."}, {type: "move", target: "Charge Beam", val: 34, desc: "Spirits that can know Charge Beam receive a fighting advantage."}, {type: "catch", target: "rate", val: 1.4, desc: "Catch rate with Spirit Balls raised."}, {type: "spattack", target: "", val: 14, scaling: true, desc: "Spirits with at least 100 Special Attack stat receive a fighting advantage. This skill is 0.2x stronger for every 10 Special Attack stat past 100, up to a maximum multiplier of 3x."}, {type: "catch", target: "cooldown", val: 1.24, desc: "Catch cooldown on Spirit Balls reduced slightly."} ], "Administrator": [ {type: "singletype", target: "", val: 42, desc: "Single-type Spirits receive a major fighting advantage."}, {type: "move", target: "Hydro Pump", val: 35, desc: "Spirits that can know Hydro Pump receive a fighting advantage."}, {type: "move", target: "Giga Drain", val: 35, desc: "Spirits that can know Giga Drain receive a fighting advantage."}, {type: "move", target: "Hurricane", val: 35, desc: "Spirits that can know Hurricane receive a fighting advantage."}, {type: "move", target: "Power Gem", val: 35, desc: "Spirits that can know Power Gem receive a fighting advantage."}, {type: "speed", target: "", val: 16, scaling: true, desc: "Spirits with at least 100 Speed stat receive a fighting advantage. This skill is 0.2x stronger for every 10 Speed stat past 100, up to a maximum multiplier of 3x."}, {type: "catch", target: "cooldown", val: 1.35, desc: "Catch cooldown on Spirit Balls reduced."} ], "Supreme Master": [ {type: "move", target: "Wood Hammer", val: 40, desc: "Spirits that can know Wood Hammer receive a fighting advantage."}, {type: "move", target: "Smart Strike", val: 40, desc: "Spirits that can know Smart Strike receive a fighting advantage."}, {type: "move", target: "Moonblast", val: 40, desc: "Spirits that can know Moonblast receive a fighting advantage."}, {type: "move", target: "Electro Ball", val: 40, desc: "Spirits that can know Electro Ball receive a fighting advantage."}, {type: "move", target: "Psyshock", val: 40, desc: "Spirits that can know Psyshock receive a fighting advantage."} ] } }; this.resetDuelsTeams = function() { safari.events.spiritDuelsSignups = []; safari.events.spiritDuelsRefugees = []; }; this.toRate = function( rate ) { return (Math.floor(rate * 10000)/100); } this.shoveDuelTeam = function( src,name,data ) { var oldBox, id; name = name.toLowerCase(); idnum = getAvatarOff(name).idnum; for (var t in safari.events.spiritDuelsTeams) { if (safari.events.spiritDuelsTeams[t].name !== data) { continue; } if (safari.events.spiritDuelsTeams[t].players.indexOf(idnum) === -1) { continue; } safari.events.spiritDuelsTeams[t].players.splice(safari.events.spiritDuelsTeams[t].players.indexOf(idnum), 1); safaribot.sendMessage( src,"Removed player " + name + " from " + data + ".",safchan ); return; } safaribot.sendMessage( src,"No team exists, or that player is not assigned to that team!",safchan ); }; this.shoveDuelSignups = function( src,name ) { var oldBox, id; name = name.toLowerCase(); idnum = getAvatarOff(name).idnum; if (safari.events.spiritDuelsSignups.indexOf(idnum) === -1) { return; } safari.events.spiritDuelsSignups.splice(safari.events.spiritDuelsSignups.indexOf(idnum), 1); return; }; this.pushDuelTeam = function( src,player,data ) { var oldBox; if (!player) { safaribot.sendMessage( src,"Not a player!",safchan ); return false; } for (var t in safari.events.spiritDuelsTeams) { if (safari.events.spiritDuelsTeams[t].name !== data) { continue; } safari.events.spiritDuelsTeams[t].players.push(player.idnum); if (player.spiritDuels) { if (player.spiritDuels.box.length > 0) { oldBox = player.spiritDuels.box; } else { oldBox = [19]; } } else { oldBox = [19]; } player.spiritDuels = { box: oldBox, skills: [], skillChoices: {}, roundStreak: 0 }; player.spiritDuels.rank = 0; player.spiritDuels.rankName = "Grunt"; player.spiritDuels.team = safari.events.spiritDuelsTeams[t].name; player.spiritDuels.exp = 0; safaribot.sendMessage( src,"Added player " + player.id + " to " + data + ".",safchan ); this.shoveDuelSignups(src,player.id); return; } safaribot.sendMessage( src,"No team exists!",safchan ); }; this.assignDuelsTeams = function( src,data ) { //Takes everyone from signups and puts them in teams safari.events.spiritDuelsSignups = removeDuplicates(safari.events.spiritDuelsSignups).shuffle(); var sgnf = []; var i = 0, player, name; for (var p in safari.events.spiritDuelsSignups) { name = idnumList.get(safari.events.spiritDuelsSignups[p]); player = getAvatarOff(name); if (player.spiritDuels.rank < 0) { safari.notification(player, "You could not be assigned to a team because you are still a Zoomer! If you want to join a team, sign up again and try to advance past Zoomer rank.", "Spirit Duels"); player.balls.spirit = 0; safari.saveGame(player); continue; } sgnf.push({ "name": name, "exp": parseInt(player.spiritDuels.exp, 10), "box": player.spiritDuels.box.length > 0 ? player.spiritDuels.box.slice(0) : [19] }); } sgnf = sgnf.shuffle(); sgnf = sgnf.sort(function(a, b) { return a.exp - b.exp; }); for (var p = 0; p < sgnf.length; p++) { player = getAvatarOff(sgnf[p].name) safari.events.spiritDuelsTeams[i].players.push(player.idnum); player.spiritDuels = { rank: 0, rankName: "Grunt", team: safari.events.spiritDuelsTeams[i].name, exp: 0, box: sgnf[p].box.slice(0), skills: [], skillChoices: {}, roundStreak: 0 }; if (!safari.events.spiritDuelsRefugees.contains(player.idnum)) { player.balls.spirit = 5; } safaribot.sendAll(player.id.toCorrectCase() + " joined " + safari.events.spiritDuelsTeams[i].name + "!", safchan); safari.notification(player, "You've been assigned to team " + safari.events.spiritDuelsTeams[i].name + "!", "Spirit Duels"); safari.saveGame(player); i++; if (i >= safari.events.spiritDuelsTeams.length) { i = 0; } } safari.events.spiritDuelsSignups = []; safari.events.spiritDuelsRefugees = []; safari.events.spiritDuelsBattling = true; }; this.spiritDuelsPrizes = function( teams ) { var g = "", i = teams.length, r, rew, amt, apri, members, s, player, g = ""; var j = 0; var round = (7 - teams.length); for (var t in teams) { r = (Math.floor(teams[t].rate * 10000) / 100); i--; amt = Math.round((((j) * 1.8) + (9 - teams.length))/2); apri = ["redapricorn", "ylwapricorn", "grnapricorn", "pnkapricorn"].random(); if (r >= 50) { rew += ("," + ((5 + round) * (j + round)) + "@dew"); } if (round >= 2) { rew += ("," + amt + "@cookie"); } members = teams[t].players; var n = now(), finalrew = rew + "", g2, pamt, pamt2, pamt3; for (var p in members) { s = idnumList.get(members[p]); finalrew = rew + ""; if (!(s)) { continue; } player = getAvatarOff(s); if (!player) { continue; } pamt = Math.round( ((j * 1.8 + (9 - teams.length)) / 2) * (player.spiritDuels.rank + 4) * 0.15 ) * player.spiritDuels.roundStreak; pamt2 = Math.round( ((j * 1.2 + (10 - teams.length)) / 2) * (player.spiritDuels.rank + 3) * 0.2 ) * Math.round(player.spiritDuels.roundStreak / 2); pamt3 = Math.round( (j * 1.5 + (5 - teams.length)) * (player.spiritDuels.rank + 6) * 0.08 ) * Math.round(player.spiritDuels.roundStreak / 2); rareamt = Math.round( ((j * 1.67 + (10 - teams.length)) / 2) * (player.spiritDuels.rank + 5) * 0.2 ) * player.spiritDuels.roundStreak; if (round >= 1) { finalrew += ("," + pamt + "@pearl"); finalrew += ("," + pamt2 + "@silver"); finalrew += ("," + (5 * pamt) + "@" + apri); } else { finalrew += ("," + pamt2 + "@golden"); finalrew += ("," + pamt + "@bigpearl"); finalrew += ("," + (2 * pamt2) + "@silver"); finalrew += ("," + (10 * pamt) + "@" + apri); } if (round == 4) { finalrew += ("," + pamt3 + "@mega"); finalrew += ("," + (2 * pamt2) + "@pearl"); } if (round >= 5) { finalrew += ("," + (pamt3) + "@mega"); finalrew += ("," + (5 * pamt2) + "@stardust"); finalrew += ("," + pamt2 + "@nugget"); } finalrew = (finalrew + ("," + rareamt + "@rare")); g = giveStuff(player, toStuffObj(finalrew)); sys.appendToFile(giftLog, now() + "|||Spirit Duels|||" + player.casedName + " from " + teams[t].name + "|||nextduels|||received|||" + g + "\n"); safari.notification(player, "Your Spirit Duels team " + teams[t].name + " scored " + r + "% and got #" + (i + 1) + "! (You " + g + ").", "Spirit Duels", true); safari.saveGame(player); } sendAll(teams[t].name + " scored " + r + "% and got #" + (i + 1) + "!", true); j++; } } this.getMinimumDuelsRank = function(player) { var streak = player.spiritDuels.roundStreak; return streak >= 6 ? streak - 2 : streak >= 4 ? streak - 1 : streak; }; this.progressDuels = function( src ) { //Counts everyone's scores //The one with the least points is eliminated //eliminated players are forced to join another team and lose their skills, get set back to grunt var team, player; for (var t in safari.events.spiritDuelsTeams) { team = safari.events.spiritDuelsTeams[t]; team.rate = (team.won / team.fought); } safari.events.spiritDuelsTeams.sort( function(a, b) { return a.rate - b.rate; }); this.spiritDuelsPrizes(safari.events.spiritDuelsTeams); sys.sendAll("", safchan); safaribot.sendHtmlAll("" + safari.events.spiritDuelsTeams[0].name + " has been eliminated!", safchan); for (var t = 0; t < safari.events.spiritDuelsTeams.length; t++) { team = safari.events.spiritDuelsTeams[t]; team.won = 0; team.fought = 0; team.rate = 0; for (var p = 0; p < team.players.length; p++) { var name = idnumList.get(safari.events.spiritDuelsTeams[t].players[p]); var player = getAvatarOff(name); if (player.spiritDuels.rank < safari.getMinimumDuelsRank(player)) { // indicates inactivity, disqualify team.players.splice(p, 1); safaribot.sendAll(player.casedName + " failed to qualify for the next round and was removed!", safchan); p--; } else if (t === 0) { safari.events.spiritDuelsSignups.push(player.idnum); safari.events.spiritDuelsRefugees.push(player.idnum); // used solely to check if player needs a spirit ball reset. if they're in this array, do not reset spirit balls since only signups from r1 should be reset } else { player.spiritDuels.roundStreak++; } } } safari.events.spiritDuelsTeams[0].alive = false; safari.events.spiritDuelsTeams = safari.events.spiritDuelsTeams.slice(1); safari.events.spiritDuelsTeams = safari.events.spiritDuelsTeams.shuffle(); safari.events.spiritDuelsTeams.sort( function(a, b) { return a.players.length - b.players.length; }); this.assignDuelsTeams(); }; this.prepareNextSpiritDuel = function(tiebreak) { //Creates a matchup between two teams //Should use a formula that makes teams with similar records fight if (tiebreak) { safari.events.spiritDuelsTeams = safari.events.spiritDuelsTeams.shuffle().sort( function(a, b) { return a.rate - b.rate; }); } else { safari.events.spiritDuelsTeams = safari.events.spiritDuelsTeams.shuffle().sort( function(a, b) { return a.fought - b.fought; }); } sendAll("Next Spirit Duel: " + safari.events.spiritDuelsTeams[0].name + " vs " + safari.events.spiritDuelsTeams[1].name + "!", true); permObj.add("events", JSON.stringify(safari.events)); }; this.startSpiritDuel = function() { //Creates horde v horde battle safari.events.spiritDuelsViewers = []; var team1 = [], team2 = []; var p1 = safari.events.spiritDuelsTeams[0].name + ": ", p2 = safari.events.spiritDuelsTeams[1].name + ": "; var army1init = safari.events.spiritDuelsTeams[0].players; var army2init = safari.events.spiritDuelsTeams[1].players; safari.events.spiritDuelsViewers = army1init.concat(army2init) .map(function(e) { return idnumList.get(e) }) .filter(function(e) { return isPlaying(e) && !cantBecause(sys.id(e), false, ["auction", "battle", "event", "pyramid", "baking"], false, true) }); if (!safari.events.spiritDuelsViewers.contains(idnumList.get(21))) { safari.events.spiritDuelsViewers.push(idnumList.get(21)); } var army1 = []; var army2 = []; var n = now(); for (var a in army1init) { if (safari.events.spiritDuelsTeams[0].activityWarned.hasOwnProperty(army1init[a]+"")) { if (n > safari.events.spiritDuelsTeams[0].activityWarned[army1init[a]+""]) { continue; } } army1.push(army1init[a]); } for (var a in army2init) { if (safari.events.spiritDuelsTeams[0].activityWarned.hasOwnProperty(army2init[a]+"")) { if (n > safari.events.spiritDuelsTeams[0].activityWarned[army2init[a]+""]) { continue; } } army2.push(army2init[a]); } if (army1.length < 2) { army1 = army1init; } if (army2.length < 2) { army2 = army2init; } var enlistPerPlayer1 = safari.events.spiritDuelsTeams[0].admin ? 100 : safari.spiritEnlistsPerPlayer(army1.length); var enlistPerPlayer2 = safari.events.spiritDuelsTeams[1].admin ? 100 : safari.spiritEnlistsPerPlayer(army2.length); var army1Avatars = [], army2Avatars = []; for (var a in army1) { army1Avatars.push(getAvatarOff(idnumList.get(army1[a]))); } for (var a in army2) { army2Avatars.push(getAvatarOff(idnumList.get(army2[a]))); } var preCount1 = 0, preCount2 = 0, hold, p, j; for (var a in army1Avatars) { p = army1Avatars[a]; hold = parseInt(enlistPerPlayer1, 10); for (var i = 0; i < safari.events.bonusSpiritEnlistRanks.length; i++) { if (p.spiritDuels.rank >= safari.events.bonusSpiritEnlistRanks[i]) { hold++; } } preCount1 += hold; hold = 0; } for (var a in army2Avatars) { p = army2Avatars[a]; hold = parseInt(enlistPerPlayer2, 10); for (var i = 0; i < safari.events.bonusSpiritEnlistRanks.length; i++) { if (p.spiritDuels.rank >= safari.events.bonusSpiritEnlistRanks[i]) { hold++; } } preCount2 += hold; hold = 0; } var maxSize = Math.min(preCount1, preCount2); //disp1 and disp2 represent how much the respective army much be decreased in number var disp1 = preCount1 - maxSize; var disp2 = preCount2 - maxSize; army1Avatars.sort(function(a, b) {return a.spiritDuels.exp - b.spiritDuels.exp}); army2Avatars.sort(function(a, b) {return a.spiritDuels.exp - b.spiritDuels.exp}); var playerAmt1 = army1Avatars.length; var playerAmt2 = army2Avatars.length; hold = 0; for (var a in army1Avatars) { p = army1Avatars[a]; j = 0; hold = parseInt(enlistPerPlayer1, 10); for (var i = 0; i < safari.events.bonusSpiritEnlistRanks.length; i++) { if (p.spiritDuels.rank >= safari.events.bonusSpiritEnlistRanks[i]) { hold++; } } if (disp1 > 0) { if (playerAmt1 < disp1) { //in case of a player needing to drop two mons to balance it out (three or more should be virtually impossible) disp1 -= 1; hold -= 1; } disp1 -= 1; hold -= 1; } playerAmt1 -= 1; for (var i = 0; i < hold; i++) { team1.push({ mon: p.spiritDuels.box[j], owner: p, won: 0, fought: 0, rate: 0, alive: true }); j++; if (j >= p.spiritDuels.box.length) { j = 0; if (enlistPerPlayer1 < 5) { break; } } } } for (var a in army2Avatars) { p = army2Avatars[a]; j = 0; hold = parseInt(enlistPerPlayer2, 10); for (var i = 0; i < safari.events.bonusSpiritEnlistRanks.length; i++) { if (p.spiritDuels.rank >= safari.events.bonusSpiritEnlistRanks[i]) { hold++; } } if (disp2 > 0) { if (playerAmt2 < disp2) { //in case of a player needing to drop two mons to balance it out (three or more should be virtually impossible) disp2 -= 1; hold -= 1; } disp2 -= 1; hold -= 1; } playerAmt2 -= 1; for (var i = 0; i < hold; i++) { team2.push({ mon: p.spiritDuels.box[j], owner: p, won: 0, fought: 0, rate: 0, alive: true }); j++; if (j >= p.spiritDuels.box.length) { j = 0; if (enlistPerPlayer2 < 5) { break; } } } } team1 = team1.shuffle(); team2 = team2.shuffle(); if (team1.length !== team2.length) { safaribot.sendHtmlMessage(sys.id("Miki Sayaka"), "Spiritduels Teams were not balanced correctly. " + team1.length + " - " + team2.length, staffchannel); safaribot.sendHtmlMessage(sys.id("Miki Sayaka"), "Spiritduels Teams were not balanced correctly.", safchan); var smaller = Math.min(team1.length, team2.length); team1 = team1.slice(0, smaller).shuffle(); team2 = team2.slice(0, smaller).shuffle(); } safari.events.sd1 = [].concat(team1); safari.events.sd2 = [].concat(team2); safari.events.sdStep = -1; }; this.spiritDuelTurn = function() { safari.events.sdStep++; var step = safari.events.sdStep; var team1 = safari.events.sd1, team2 = safari.events.sd2; var fighter1 = {}, fighter2 = {}, victory1 = true, victory2 = true, team1name = safari.events.spiritDuelsTeams[0].name + ": ", team2name = safari.events.spiritDuelsTeams[1].name + ": "; var team1fighters = team1name, team2fighters = team2name; var boost1 = 0, boost2 = 0; var range1 = [10, 120], range2 = [10, 120]; var res; if (step === 0) { sendAll("A Spirit Duel between " + safari.events.spiritDuelsTeams[0].name + " and " + safari.events.spiritDuelsTeams[1].name + " is about to begin! [" + link("/spiritduel watch", "Watch") + "]", true); } else if (step === 4) { sendAll("The Spirit Duel between " + safari.events.spiritDuelsTeams[0].name + " and " + safari.events.spiritDuelsTeams[1].name + " has commenced! [" + link("/spiritduel watch", "Watch") + "]", true); this.spiritDuelsMessage("Preparations complete!"); } else if (step >= 5) { this.spiritDuelsMessage( "Turn " + (step - 4) + ": " ); for (var a in team1) { team1[a].won = 0; team1[a].fought = 0; } for (var a in team2) { team2[a].won = 0; team2[a].fought = 0; } victory1 = true; victory2 = true; for (var a in team1) { fighter1 = team1[a]; if (!fighter1.alive) { continue; } boost1 = this.spiritMonBoost(fighter1.owner, fighter1.mon); range1 = [10 + boost1, 100 + (boost1 * 2)]; for (var b in team2) { fighter2 = team2[b]; if (!fighter2.alive) { continue; } boost2 = this.spiritMonBoost(fighter2.owner, fighter2.mon); range2 = [10 + boost2, 100 + (boost2 * 2)]; res = calcDamage(fighter1.mon, fighter2.mon, range1, range2); if (res.power[0] >= res.power[1]) { team1[a].won++; } if (res.power[1] >= res.power[0]) { team2[b].won++; } team1[a].fought++; team2[b].fought++; } } for (var a in team1) { team1[a].rate = (team1[a].won / team1[a].fought); if (team1[a].alive) { team1fighters += pokeInfo.icon(team1[a].mon) + " " + team1[a].owner.casedName + "'s " + poke(team1[a].mon) + " " + toColor(" (" + this.toRate(team1[a].rate) + "%) ", this.getSpiritDuelColor(team1[a].rate) ); } if (team1[a].rate < 0.5) { team1[a].alive = false; } else { if (team1[a].alive) { victory2 = false; } } } this.spiritDuelsMessage(team1fighters); for (var a in team2) { team2[a].rate = (team2[a].won / team2[a].fought); if (team2[a].alive) { team2fighters += pokeInfo.icon(team2[a].mon) + " " + team2[a].owner.casedName + "'s " + poke(team2[a].mon) + " " + toColor(" (" + this.toRate(team2[a].rate) + "%) ", this.getSpiritDuelColor(team2[a].rate) ); } if (team2[a].rate < 0.5) { team2[a].alive = false; } else { if (team2[a].alive) { victory1 = false; } } } this.spiritDuelsMessage(team2fighters); if (!victory1 && !victory2 && step >= 27) { safari.events.spiritDuelsTeams[0].won += 0.5; safari.events.spiritDuelsTeams[1].won += 0.5; safari.events.spiritDuelsTeams[0].fought++; safari.events.spiritDuelsTeams[1].fought++; safari.events.spiritDuelsTeams[0].rate = (safari.events.spiritDuelsTeams[0].won / safari.events.spiritDuelsTeams[0].fought); safari.events.spiritDuelsTeams[1].rate = (safari.events.spiritDuelsTeams[1].won / safari.events.spiritDuelsTeams[1].fought); safari.events.spiritDuelsViewers = []; sendAll("The Spirit Duel ended in a draw!"); safari.events.spiritDuelsLog.unshift(safari.events.spiritDuelsTeams[0].name + " " + safari.events.spiritDuelsTeams[1].name + " tied!"); safari.prepareNextSpiritDuel(); return true; } if (victory1 || victory2) { var w; if (victory1 && victory2) { //This shouldn't happen w = "Glithced (Please contact a Safari Admin)"; } else if (victory1) { safari.events.spiritDuelsTeams[0].won++; w = safari.events.spiritDuelsTeams[0].name; safari.events.spiritDuelsLog.unshift( safari.events.spiritDuelsTeams[0].name + " defeated " + safari.events.spiritDuelsTeams[1].name + "!" ); } else if (victory2) { safari.events.spiritDuelsTeams[1].won++; w = safari.events.spiritDuelsTeams[1].name; safari.events.spiritDuelsLog.unshift( safari.events.spiritDuelsTeams[1].name + " defeated " + safari.events.spiritDuelsTeams[0].name + "!" ); } safari.events.spiritDuelsTeams[0].fought++; safari.events.spiritDuelsTeams[1].fought++; safari.events.spiritDuelsTeams[0].rate = (safari.events.spiritDuelsTeams[0].won / safari.events.spiritDuelsTeams[0].fought); safari.events.spiritDuelsTeams[1].rate = (safari.events.spiritDuelsTeams[1].won / safari.events.spiritDuelsTeams[1].fought); safari.events.spiritDuelsViewers = []; sendAll( "The winner of the Spirit Duel is " + w + "!!"); safari.prepareNextSpiritDuel(); return true; } } return false; }; this.getSpiritDuelColor = function (rate) { if (rate >= 0.75) { return "#9636d6"; } else if (rate >= 0.66) { return "#4242f4"; } else if (rate >= 0.575) { return "#37d684"; } else if (rate >= 0.5) { return "#dbca32"; } return "#db4632"; } this.spiritMonBoost = function( player,mon ) { //Adds buffs according to a mon's owner's skills var out = 0, data, active, mult; for (var s in player.spiritDuels.skills) { data = player.spiritDuels.skills[s]; target = data.target; mult = 1; active = false switch (data.type) { case "type": if (hasType(mon, target)) { active = true; } break; case "color": if (getPokeColor(mon).toLowerCase() === target.toLowerCase()) { active = true; } break; case "region": if (generation(mon, true).toLowerCase() === target.toLowerCase()) { active = true; } break; case "move": if (canLearnMove(mon, movenum(target))) { active = true; } break; case "name": if (pokePlain(mon)[0].toLowerCase() === target.toLowerCase()) { active = true; } break; case "attack": if (getStatsNamed(mon)["Attack"] >= 100) { active = true; mult = Math.min(3, 1 + Math.floor((getStatsNamed(mon)["Attack"] - 100) / 10) * 0.2); } break; case "spattack": if (getStatsNamed(mon)["Special Attack"] >= 100) { active = true; mult = Math.min(3, 1 + Math.floor((getStatsNamed(mon)["Special Attack"] - 100) / 10) * 0.2); } break; case "speed": if (getStatsNamed(mon)["Speed"] >= 100) { active = true; mult = Math.min(3, 1 + Math.floor((getStatsNamed(mon)["Speed"] - 100) / 10) * 0.2); } break; case "singletype": if (type2(mon) === "???") { active = true; } break; case "any": active = true; break; } if (active) { out += Math.round(data.val * mult); } } if (isLegendary(mon)) { out += 25; } return out; }; this.getSpiritExpRequired = function(player) { if (!player.spiritDuels.rankName) { return; } if (safari.spiritDuelsMaxLevel(player)) { return 0; } safari.events.spiritDuelsRanks = [ {rank: "Grunt", exp: 0}, {rank: "Ensign", exp: 2000}, {rank: "Officer Trainee", exp: 6000}, {rank: "Secretary Officer", exp: 10000}, {rank: "Squadron Leader", exp: 15000}, {rank: "Field Lieutenant", exp: 22000}, {rank: "Commander", exp: 30000}, {rank: "Vice Admiral", exp: 35000}, {rank: "Admin", exp: 40000}, {rank: "Supreme Master", exp: 50000} ]; return safari.events.spiritDuelsRanks[player.spiritDuels.rank + 1].exp || 0; }; this.spiritDuelsMaxLevel = function(player) { return player.spiritDuels.rank >= safari.events.spiritDuelsRanks.length - 1; }; this.spiritDuelsLevelUp = function(player) { if (safari.spiritDuelsMaxLevel(player)) { return; } var nextLevel = player.spiritDuels.rank + 1 || -1; var expNeeded = safari.getSpiritExpRequired(player); if (player.spiritDuels.exp >= expNeeded) { player.spiritDuels.rank++; player.spiritDuels.rankName = safari.events.spiritDuelsRanks[player.spiritDuels.rank].rank; safaribot.sendMessage(sys.id(player.id), "You leveled up and became " + an(player.spiritDuels.rankName) + "!", safchan); if (player.spiritDuels.rank > 0) { canLearn = JSON.parse(JSON.stringify(safari.events.spiritDuelsSkills))[player.spiritDuels.rankName].shuffle().slice(0, 3); player.spiritDuels.skillChoices = canLearn; safari.showSpiritSkill(sys.id(player.id),player); } } safari.saveGame(player); }; this.catchSpiritMon = function( player,mon, spiritRealm ) { //Adds the mon to player's spirit box //Also increases their EXP var id = parseInt(mon, 10), exp; player.spiritDuels.box.push(id); exp = Math.round((getBST(id) + Math.max(getBST(id) - 300, 0)) * 0.75); for (var s in player.spiritDuels.skills) { if (player.spiritDuels.skills[s].type === "exp") { exp *= player.spiritDuels.skills[s].val; } } if (isLegendary(id)) { exp *= 4; } else if (spiritRealm) { exp *= 2; } exp = Math.round(exp); player.spiritDuels.exp += exp; player.spiritDuels.exp = Math.round(player.spiritDuels.exp); var expNeeded = safari.getSpiritExpRequired(player); if (!safari.spiritDuelsMaxLevel(player)) { safaribot.sendMessage(sys.id(player.id), "You gained {0} Spirit Duels EXP! (Next Rank: {1}/{2})".format(addComma(exp), addComma(player.spiritDuels.exp), addComma(expNeeded)), safchan); while (player.spiritDuels.exp >= safari.getSpiritExpRequired(player) && !safari.spiritDuelsMaxLevel(player)) { safari.spiritDuelsLevelUp(player); } } this.saveGame(player); }; this.showSpiritSkill = function( src,player ) { //Shows them their spirit monns var skill, msg = "", letters = ["a", "b", "c"], i = 0; msg = "You can learn one of these skills with /spiritskill [letter]!"; if (Object.keys(player.spiritDuels.skillChoices).length === 0) { safaribot.sendMessage(src, "You have no skills to learn!", safchan); return; } safaribot.sendMessage(src, msg, safchan); for (var s in player.spiritDuels.skillChoices) { skill = player.spiritDuels.skillChoices[s]; safaribot.sendHtmlMessage(src, link("/spiritskill " + letters[i], "[" + letters[i].toUpperCase() + "]", true) + " " + skill.desc + " (+" + skill.val + ")", safchan); i++; } }; this.chooseSpiritSkill = function( src,commandData ) { var skill, msg = "", letters = ["a", "b", "c"], i = 0; var player = getAvatar(src); if (Object.keys(player.spiritDuels.skillChoices).length === 0) { return; } if (letters.indexOf(commandData) === -1) { this.showSpiritSkill( src,player ); return; } for (var s in player.spiritDuels.skillChoices) { if (letters[i] === commandData) { safaribot.sendMessage(sys.id(player.id), "You chose the skill '" + player.spiritDuels.skillChoices[s].desc + "'!", safchan); player.spiritDuels.skills.push( JSON.parse(JSON.stringify(player.spiritDuels.skillChoices[s]))); player.spiritDuels.skillChoices = {}; this.saveGame(player); return; } i++; } return; }; this.spiritEnlistsPerPlayer = function(memberAmt) { return memberAmt >= 6 ? 3 : (memberAmt >= 5 ? 4 : (memberAmt >= 4 ? 5 : 6)); }; this.getSpiritTeamMembers = function(player) { if (["None", "Unemployed"].contains(player.spiritDuels.team)) { return []; } var team = safari.events.spiritDuelsTeams.filter(function(e) { return e.players.contains(player.idnum) }); if (team.length > 0) { return team.pop().players; } return []; }; this.spiritDuelsCommand = function( src,command,commandData ) { var player = getAvatar(src); if (!safari.events.spiritDuelsEnabled) { safaribot.sendMessage( src,"Spirit Duels are not enabled!",safchan ); return; } switch (command) { case "box": this.showSpiritBox(src,player,false,false); break; case "boxt": this.showSpiritBox(src,player,false,true); break; case "add": this.activeSpiritMon(src,player,commandData); break; case "release": this.releaseSpiritMon(src,player,commandData); break; case "bench": this.benchSpiritMon(src,player,commandData); break; case "join": this.joinSpiritDuels(src,player); break; case "watch": this.watchSpiritDuels(src, player ? player : sys.name(src)); break; case "history": this.showSpiritDuelsLog(src,player,commandData); break; case "party": this.showSpiritDuelsTeam(src,player); break; case "skill": case "skills": this.ownSpiritSkills(src,player); break; case "teams": case "allteams": this.showEachSpiritDuelTeam(src, player); break; case "standings": case "lb": case "leaderboard": this.showSpiritDuelStandings(src); break; default: sys.sendMessage(src, "", safchan); var m = "You are " + an(player.spiritDuels.team) + " " + player.spiritDuels.rankName + "!"; m += (" [" + link("/spiritduels join", "Join") + ", " + link("/spiritduels box", "Box") + ", " + link("/spiritduels boxt", "Box Text") + ", " + link("/spiritduels add:", "Add", true) + ", " + link("/spiritduels bench:", "Bench", true) + ", " + link("/spiritduels release:", "Release", true) + ", " + link("/spiritduels party", "Team Party") + ", " + link("/spiritduels teams", "Teams") + ", " + link("/spiritduels standings", "Standings") + ", " + link("/spiritduels skill", "Skills" + (Object.keys(player.spiritDuels.skillChoices).length > 0 ? " [!]" : "")) + ", " + link("/spiritduels history", "History") + "]."); safaribot.sendHtmlMessage(src, m, safchan); if (safari.spiritDuelsMaxLevel(player)) { safaribot.sendHtmlMessage(src, "You have achieved the highest rank!", safchan); } else { safaribot.sendHtmlMessage(src, "Next Rank: {0} ({1}/{2} EXP).".format(safari.events.spiritDuelsRanks[player.spiritDuels.rank + 1].rank, addComma(player.spiritDuels.exp), addComma(safari.getSpiritExpRequired(player))), safchan); if (player.spiritDuels.rank < safari.getMinimumDuelsRank(player) && safari.inSpiritTeam(src, player)) { safaribot.sendHtmlMessage(src, "You have been on this team for {0} rounds and must reach the {1} rank to qualify for the next round.".format(player.spiritDuels.roundStreak, safari.events.spiritDuelsRanks[safari.getMinimumDuelsRank(player) - 1].rank), safchan); } } for (var i = 0; i < safari.events.spiritDuelsTeams.length; i++) { var team = safari.events.spiritDuelsTeams[i]; if (team.name.toLowerCase() === player.spiritDuels.team.toLowerCase()) { safaribot.sendHtmlMessage(src, "Team Record: {0} Duels won out of {1} Duels fought (Win Rate: {2}%).".format(team.won, team.fought, (team.rate * 100).toFixed(2)), safchan); } } if (safari.events.spiritDuelsBattling && safari.events.spiritDuelsTeams.length > 1) { safaribot.sendHtmlMessage(src, "Next Spirit Duel: " + safari.events.spiritDuelsTeams[0].name + " vs " + safari.events.spiritDuelsTeams[1].name + "!", safchan); } var bonusRanks = safari.events.bonusSpiritEnlistRanks; var teamMemberAmount = safari.getSpiritTeamMembers(player).length || 0; safaribot.sendHtmlMessage(src, "You can use {0} to move your Spirit Pokémon around your Spirit Box, or {1} to place them at the back. If you are running low on Spirit Box space, you can use {2} to permanently remove unwanted Spirit Pokémon (you will not lose EXP or regain any Spirit Balls).".format(link("/spiritduels add:", false, true), link("/spiritduels bench:", false, true), link("/spiritduels release:", false, true)), safchan); safaribot.sendHtmlMessage(src, "You currently have {0} in your team, so each member can enlist the first {1} Pokémon in their Spirit Box per Duel.".format(plural(teamMemberAmount, "player"), safari.spiritEnlistsPerPlayer(teamMemberAmount)), safchan); safaribot.sendHtmlMessage(src, "Reaching the {0} ranks will allow you to enlist 1 extra Spirit Pokémon per each of those ranks, so reaching {1} will allow you to enlist {2} extra Spirit Pokémon, for example." .format( readable(bonusRanks.map(function(e) { return safari.events.spiritDuelsRanks[e].rank })), safari.events.spiritDuelsRanks[bonusRanks[bonusRanks.length - 1]].rank, bonusRanks.length ), safchan); sys.sendMessage(src, "", safchan); } }; this.markActivity = function( src,player ) { var army, named, index, passed = false; for (var a in safari.events.spiritDuelsTeams) { if (safari.events.spiritDuelsTeams[a].name == player.spiritDuels.team) { army = safari.events.spiritDuelsTeams[a].players passed = true; name = safari.events.spiritDuelsTeams[a].name; index = a; break; } } if (!passed) { safaribot.sendMessage( src,"You are not on a team!",safchan ); return; } if (!(Object.keys(safari.events.spiritDuelsTeams[a].activityWarned).contains(player.idnum+""))) { safaribot.sendMessage( src,"You are not activity warned!",safchan ); return; } delete safari.events.spiritDuelsTeams[a].activityWarned[player.idnum+""]; safaribot.sendMessage( src,"You marked yourself as active!",safchan ); return; }; this.markInactivity = function( src,player,target ) { var army, named, index, passed = false, targetNum, targetPlayer; for (var a in safari.events.spiritDuelsTeams) { if (safari.events.spiritDuelsTeams[a].name == player.spiritDuels.team) { army = safari.events.spiritDuelsTeams[a].players passed = true; name = safari.events.spiritDuelsTeams[a].name; index = a; break; } } if (!passed) { safaribot.sendMessage( src,"You are not on a team!",safchan ); return; } if (!target) { safaribot.sendMessage( src,"You need to mark a target as inactive!",safchan ); return; } targetPlayer = getAvatarOff(target); if (!targetPlayer) { safaribot.sendMessage( src,"No one named " + target + " around here!",safchan ); return; } targetNum = targetPlayer.idnum; if (!(army.contains(targetNum))) { safaribot.sendMessage( src,"No one named " + target + " on this team!",safchan ); return; } if (Object.keys(safari.events.spiritDuelsTeams[a].activityWarned).contains(targetNum+"")) { safaribot.sendMessage( src,target + " is already on activity warning!",safchan ); return; } safari.events.spiritDuelsTeams[a].activityWarned[targetNum+""] = now() + (24 * 1000 * 60 * 60); safaribot.sendMessage( src,target + " has been warned for their low activity, and will not be able to participate in 24 hours if they don't reactivate!",safchan ); safari.inboxMessage(targetPlayer, "You've been marked as inactive in spirit duels by your teammates. Type /spiritduels reactive to undo this!", false); return; }; this.ownSpiritSkills = function( src,player ) { if (Object.keys(player.spiritDuels.skills).length === 0 && Object.keys(player.spiritDuels.skillChoices).length === 0) { safaribot.sendMessage(src, "You have no Spirit Skills!", safchan); return; } sys.sendMessage(src, "", safchan); if (Object.keys(player.spiritDuels.skills).length > 0) { safaribot.sendMessage(src, "You have the following Spirit Skills:", safchan); for (var s in player.spiritDuels.skills) { safaribot.sendMessage(src, "- " + player.spiritDuels.skills[s].desc + " (+" + player.spiritDuels.skills[s].val + ")", safchan); } sys.sendMessage(src, "", safchan); } if (Object.keys(player.spiritDuels.skillChoices).length > 0) { safari.showSpiritSkill(src, player); sys.sendMessage(src, "", safchan); } return; } this.showSpiritDuelsLog = function( src,player,data ) { var log = safari.events.spiritDuelsLog, out = ""; data = parseInt(data, 10); if (!data) { data = 10; } if (data > 20) { data = 20; } if (data > log.length) { data = log.length; } sys.sendMessage(src, "", safchan); for (var i = 0; i < log.length; i++) { safaribot.sendMessage(src, log[i], safchan); if (i > data) { break; } } sys.sendMessage(src, "", safchan); return; } this.spiritDuelsCanSignup = function(src, player, silent) { var id = player.id.toLowerCase(); if (!safari.events.spiritDuelsEnabled) { return false; } if (!safari.events.spiritDuelsTeams) { return false; } var k; for (var t in safari.events.spiritDuelsTeams) { k = safari.events.spiritDuelsTeams[t].players.indexOf(player.idnum); if (k !== -1) { team = safari.events.spiritDuelsTeams[t].name; if (!silent) safaribot.sendMessage( src,"You are already assigned to team " + team + "!",safchan ); return false; } } k = safari.events.spiritDuelsSignups.indexOf(player.idnum); if (k > -1) { if (!silent) safaribot.sendMessage( src,"You are already signed up!",safchan ); return false; } if (safari.inSpiritTeam(src, player)) { if (!silent) safaribot.sendMessage(src, "You are already in a team!", safchan); return false; } if (safari.events.spiritDuelsTeams.length <= 4) { if (!silent) safaribot.sendMessage(src,"You cannot sign up for this season of Spirit Duels anymore!", safchan); return false; } return true; }; this.inSpiritTeam = function(src, player) { for (var a in safari.events.spiritDuelsTeams) { if (safari.events.spiritDuelsTeams[a].players.contains(player.idnum)) { return true; } } return false; }; this.joinSpiritDuels = function( src,player ) { //Joins duels to be assigned a team next time if (!safari.spiritDuelsCanSignup(src, player)) { return; } safari.events.spiritDuelsSignups.push(player.idnum); if (!player.spiritDuels) { player.spiritDuels = { box: [] } } player.spiritDuels.rank = -1; player.spiritDuels.rankName = "Zoomer"; player.spiritDuels.team = "Unemployed"; player.spiritDuels.exp = -2000; player.spiritDuels.skills = []; player.spiritDuels.box = []; player.spiritDuels.skillChoices = {}; player.spiritDuels.roundStreak = 0; player.spiritDuels.prizePoints = 0; player.balls.spirit = 5; safari.sanitize(player); safari.saveGame(player); safaribot.sendMessage(src, "You signed up for Spirit Duels! You will join the next round as soon as it starts!", safchan); return true; }; this.showEachSpiritDuelTeam = function(src, player) { var out = []; for (var a in safari.events.spiritDuelsTeams) { var ownTeam = safari.events.spiritDuelsTeams[a].name == player.spiritDuels.team; var teamNames = safari.events.spiritDuelsTeams[a].players.slice(0).map(function(e) { var avatar = getAvatarOff(idnumList.get(e)); return "" + toColor(avatar.casedName, avatar.nameColor) + ""; }); var tmp = (ownTeam ? "Your team (" + safari.events.spiritDuelsTeams[a].name + "): " : "(" + safari.events.spiritDuelsTeams[a].name + "): ") + readable(teamNames) + "."; if (ownTeam) { out.unshift(tmp); } else { out.push(tmp); } } sys.sendMessage(src, "", safchan); out.forEach(function(e) { safaribot.sendHtmlMessage(src, e, safchan); }); sys.sendMessage(src, "", safchan); return; }; this.showSpiritDuelStandings = function(src) { var standings = []; for (var a in safari.events.spiritDuelsTeams) { if (!standings.contains(safari.events.spiritDuelsTeams[a].rate)) { standings.push(safari.events.spiritDuelsTeams[a].rate); } } standings.sort(function(a, b) { return b - a }); sys.sendMessage(src, "", safchan); safari.events.spiritDuelsTeams.slice(0).sort(function(a, b) { return b.rate - a.rate; }).forEach(function(e) { safaribot.sendHtmlMessage(src, "{0}. {1} with {2}% win rate ({3}/{4} Duels won)".format(getOrdinal(standings.indexOf(e.rate) + 1), e.name, (e.rate * 100).toFixed(2), e.won, e.fought), safchan); }); sys.sendMessage(src, "", safchan); return; }; this.showSpiritDuelsTeam = function(src, player) { var army1 = null, passed = false, out = [], name = ""; for (var a in safari.events.spiritDuelsTeams) { if (safari.events.spiritDuelsTeams[a].name == player.spiritDuels.team) { army1 = safari.events.spiritDuelsTeams[a].players passed = true; name = safari.events.spiritDuelsTeams[a].name; break; } } if (!passed) { safaribot.sendMessage( src,"You are not on a team!",safchan ); return; } var enlistPerPlayer1 = safari.spiritEnlistsPerPlayer(army1.length); var hold = 0, p, j; for (var a in army1) { p = getAvatarOff(idnumList.get(army1[a])); j = 0; hold = parseInt(enlistPerPlayer1, 10); for (var i = 0; i < safari.events.bonusSpiritEnlistRanks.length; i++) { if (p.spiritDuels.rank >= safari.events.bonusSpiritEnlistRanks[i]) { hold++; } } for (var i = 0; i < hold; i++) { var boost = safari.spiritMonBoost(p, p.spiritDuels.box[j]); out.push(pokeInfo.icon(p.spiritDuels.box[j]) + " " + toColor(p.casedName + "'s " + poke(p.spiritDuels.box[j]) + (boost > 0 ? " (+" + boost + ")" : ""), p.nameColor) + ""); j++; if (j >= p.spiritDuels.box.length) { if (enlistPerPlayer1 < 5) { break; } j = 0; } } } safaribot.sendHtmlMessage( src,"Your team (" + name + ")'s Spirit Duel Party: ",safchan ); safaribot.sendHtmlMessage( src,readable(out),safchan ); return; }; this.showSpiritBox = function( src,player,isAndroid,textOnly ) { //Shows them their spirit monns var out = []; var maxPages, list = player.spiritDuels.box.slice(0); var limit = safari.events.spiritBoxLimit || 140; var page = 1; if (!isNaN(page)) { maxPages = Math.floor(list.length / (limit)) + (list.length % limit === 0 ? 0 : 1); if (page > maxPages) { page = maxPages; } list = list.slice(limit * (page - 1), limit * (page - 1) + limit); } var label = "Spirits (" + player.spiritDuels.box.length + "/" + (limit) + ")"; var enlist = safari.spiritEnlistsPerPlayer(safari.getSpiritTeamMembers(player).length), bonusRanks = safari.events.bonusSpiritEnlistRanks; for (var i = 0; i < bonusRanks.length; i++) { if (player.spiritDuels.rank >= bonusRanks[i]) { enlist++; } } if (textOnly) { out.push(this.listSpiritPokemonText(list, label, enlist)); } else { out.push(this.listSpiritPokemon(list, label, player.options.smallBox, enlist)); if (isAndroid) { out.push("
"); } } var buffs = []; for (var i = 0; i < enlist; i++) { var mon = player.spiritDuels.box[i]; var boost = safari.spiritMonBoost(player, mon); if (boost > 0) { buffs.push((textOnly ? "" : pokeInfo.icon(mon) + " ") + poke(mon) + " (+" + boost + ")"); } } if (buffs.length > 0) { if (!textOnly) { out.push(""); } out.push("Current Spirit Party Boosts:"); out.push(buffs.join(", ")); out.push(""); } out.forEach(function(e) { sys.sendHtmlMessage(src, e, safchan); }); }; this.activeSpiritMon = function( src,player,data ) { //Adds the spirit mons to the front of their spirit box if they have it if (!data) { safaribot.sendMessage( src,"The command is /spiritduels add:[pokemon1,pokemon2,...]:[box position]. This command moves the specified Spirit Pokémon to the specified position in your Spirit Box. Spirits at the front of your box will take part in Spirit Duels. If no position is specified, it will move the Spirits to the front of your box.",safchan ); return; } data = typeNull(data).split(":"); var pos = data[1] || 0; if (isNaN(pos)) { pos = 0; } else { pos = parseInt(pos) - 1; } data = data[0].split(",").reverse(); var a, x, toMove = []; for (var i=0; i < data.length; i++) { a = data[i].trim(); a = getInputPokemon(a); x = player.spiritDuels.box.slice(0).reverse().indexOf(a.num); // reversing so it takes first index of that pokemon from the back. this makes it easier to load multiples of the same pokemon if (a.input === "Missingno") { safaribot.sendMessage(src, "Invalid Pokémon: " + data[i] + "!", safchan); continue; } if (x === -1) { safaribot.sendMessage(src, "You don't have any Spirit " + a.name + "!", safchan); continue; } // we box.slice(0) first for a clone since these checks might cause the loop to progress without reversing the box back at the statements below, if we reversed the actual box up above toMove.push(player.spiritDuels.box.reverse().splice(x, 1)[0]); // only reverse the actual array once checks pass player.spiritDuels.box.reverse(); // and make sure to reverse it back } if (toMove.length === 0) { return; } pos = Math.max(Math.min(pos, player.spiritDuels.box.length), 0); // only clamp pos here after specified mons have been removed, so as to use an updated box length toMove.forEach(function(e) { player.spiritDuels.box.splice(pos, 0, e); }); safaribot.sendMessage(src, "You added " + readable(toMove.map(poke)) + " to the " + (pos === 0 ? "lead" : getOrdinal(pos + 1) + " position") + " of your Spirit Box!", safchan); var enlist = safari.spiritEnlistsPerPlayer(safari.getSpiritTeamMembers(player).length), bonusRanks = safari.events.bonusSpiritEnlistRanks; for (var i = 0; i < bonusRanks.length; i++) { if (player.spiritDuels.rank >= bonusRanks[i]) { enlist++; } } safaribot.sendHtmlMessage(src, "Your current active Spirit Party: " + readable(player.spiritDuels.box.slice(0, enlist).map(function(e) { return pokeInfo.icon(e) + " " + poke(e) })), safchan); this.saveGame(player); }; this.releaseSpiritMon = function( src,player,data ) { //Completely removes the spirit mons from their spirit box if (!data) { safaribot.sendMessage( src,"The command is /spiritduels release:[pokemon1,pokemon2,..]. This command will PERMANENTLY delete the specified Spirit Pokémon from your Spirit Box.",safchan ); return; } data = data.split(",").reverse(); var a, x; for (var i=0; i < data.length; i++) { a = data[i].trim(); a = getInputPokemon(a); x = player.spiritDuels.box.indexOf(a.num); if (a.input === "Missingno") { safaribot.sendMessage(src, "Invalid Pokémon: " + data[i] + "!", safchan); continue; } if (x === -1) { safaribot.sendMessage(src, "You don't have any Spirit " + a.name + "!", safchan); continue; } player.spiritDuels.box.splice(x, 1); safaribot.sendMessage(src, "You removed " + a.name + " from your Spirit Box!", safchan); } this.saveGame(player); }; this.benchSpiritMon = function( src,player,data ) { //Moves the spirit mons to the back of their spirit box if (!data) { safaribot.sendMessage( src,"The command is /spiritduels bench:[pokemon1,pokemon2,...]. This command will move the specified Spirit Pokémon to the back of your Spirit Box.",safchan ); return; } data = data.split(",").reverse(); var a, x; for (var i=0; i < data.length; i++) { a = data[i].trim(); a = getInputPokemon(a); x = player.spiritDuels.box.indexOf(a.num); if (a.input === "Missingno") { safaribot.sendMessage(src, "Invalid Pokémon: " + data[i] + "!", safchan); continue; } if (x === -1) { safaribot.sendMessage(src, "You don't have any Spirit " + a.name + "!", safchan); continue; } player.spiritDuels.box.splice(x, 1); player.spiritDuels.box.push(a.num); safaribot.sendMessage(src, "You moved " + a.name + " to the back of your Spirit Box!", safchan); } var enlist = safari.spiritEnlistsPerPlayer(safari.getSpiritTeamMembers(player).length), bonusRanks = safari.events.bonusSpiritEnlistRanks; for (var i = 0; i < bonusRanks.length; i++) { if (player.spiritDuels.rank >= bonusRanks[i]) { enlist++; } } safaribot.sendHtmlMessage(src, "Your current active Spirit Party: " + readable(player.spiritDuels.box.slice(0, enlist).map(function(e) { return pokeInfo.icon(e) + " " + poke(e) })), safchan); this.saveGame(player); }; this.clearSpiritMons = function( src,commandData ) { var player = getAvatarOff(commandData); player.spiritDuels.box = []; this.saveGame(player); } this.bestowSpiritMon = function( src,commandData ) { //Adds the spirit mons to the front of their spirit box if they have it //num = data.getMonNumber(data) || data if data is a number var cmd = commandData.split(":"); var player = getAvatarOff(cmd[0]); var num = 1; if (!player) { safaribot.sendMessage(src, "Player not found!", safchan); return; } else if (cmd.length < 2) { safaribot.sendMessage(src, "Invalid Pokemon input.", safchan); return; } var data = getInputPokemon(cmd[1]); if (data.input === "Missingno") { safaribot.sendMessage(src, "Invalid Pokemon input.", safchan); return; } if (cmd.length > 2) { num = parseInt(cmd[2], 10) || 1; } var i; if (num === -1) { i = player.spiritDuels.box.indexOf(data.num); if (i === -1) { safaribot.sendMessage( src,player.id + " doesn't have that Spirit Pokémon!",safchan ); return; } player.spiritDuels.box.splice(i, 1); safaribot.sendMessage( src,"You took away a " + data.name + " from " + player.id + "'s Spirit Box.",safchan ); } else { player.spiritDuels.box.push(data.num); safaribot.sendMessage( src,"You added " + data.name + " to " + player.id + "'s Spirit Box.",safchan ); return; } this.saveGame(player); }; this.spiritDuelsMessage = function(msg) { var e; var list = safari.events.spiritDuelsViewers; for (e = 0 ; e < list.length; e++) { var id = sys.id(list[e]); if (!id || !sys.isInChannel(id, safchan)) { continue; } safaribot.sendHtmlMessage(id, msg, safchan); } }; this.watchSpiritDuels = function(src,player) { var name = typeof player === "string" ? player : player.id.toCorrectCase(); if (!safari.events.currentSpiritDuel) { safaribot.sendMessage( src,"There is no Spirit Duel to watch!",safchan ); return; } if (safari.events.spiritDuelsViewers.indexOf(name.toLowerCase()) !== -1) { this.spiritDuelsMessage(name + " stopped watching this Spirit Duel!"); safari.events.spiritDuelsViewers.splice(safari.events.spiritDuelsViewers.indexOf(name.toLowerCase()), 1); } else { safari.events.spiritDuelsViewers.push(name.toLowerCase()); this.spiritDuelsMessage(name + " is watching this Spirit Duel!"); } return; }; /* Hidden Quiz Event */ this.quizEventInitialize = function() { safari.events.hiddenQuizData = {}; safari.events.hiddenQuizData.currentID = 0; safari.events.hiddenQuizData.nextQuiz = now(); var player; for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); if (!player.hiddenQuiz) { continue; } if (!player.hiddenQuiz.points) { continue; } player.hiddenQuiz.points = 1000; safari.saveGame(player); } } }; this.viewQuizLb = function(src, public) { var player, points, name, id; var playerPoints = []; for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); name = player.casedName; id = player.id; if (!player.hiddenQuiz) { continue; } if (!player.hiddenQuiz.points) { continue; } if (player.hiddenQuiz.points <= 0) { continue; } points = player.hiddenQuiz.points; playerPoints.push({ id: id, points: points }); } } playerPoints.sort(function(a, b) { return a.points - b.points; }); safaribot.sendMessage(src, "Top " + playerPoints.length + " players in Hidden Quiz Event by points: ", safchan); var j = 1; for (var i = playerPoints.length; i--;) { //p = getAvatarOff(playerPoints[i].id); if (public) { safaribot.sendAll("#" + j + ": " + playerPoints[i].id + " (" + playerPoints[i].points + ")", safchan); } else { safaribot.sendMessage(src, "#" + j + ": " + playerPoints[i].id + " (" + playerPoints[i].points + ")", safchan); } j++; } return; }; this.quizRankDecay = function() { var player, points, name, id; var currentID = this.events.hiddenQuizData.currentID for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); name = player.casedName; id = player.id; if (!player.hiddenQuiz) { continue; } if (!player.hiddenQuiz.points) { continue; } if (player.hiddenQuiz.points <= 0) { continue; } points = player.hiddenQuiz.points; //Every 48 games, we expect/require the player to play 5 times to avoid decay. decay = false; if (player.hiddenQuiz.lastPlayed.length > 5) { player.hiddenQuiz.lastPlayed = player.hiddenQuiz.lastPlayed.slice(0, 5); } for (var i in player.hiddenQuiz.lastPlayed) { if (player.hiddenQuiz.lastPlayed[i] + 48 < currentID) { decay = true; break; } } //Only decay if they're above 1k if (decay && (player.hiddenQuiz.points > 1000)) { player.hiddenQuiz.points -= ((player.hiddenQuiz.points - 1000) * 0.012); player.hiddenQuiz.points = Math.round(player.hiddenQuiz.points); } safari.saveGame(player); } } } this.quizRankUpdate = function(points) { /* We pass this the object "points" from a quiz event */ var players = []; for (var a in points) { players.push( { "id": a, "points": points[a] }); } this.quizRankDecay(); var player, player2, diff, out = {}, src; for (var i = 0; i < players.length; i++) { player = getAvatarOff(players[i].id); if (!player.hiddenQuiz) { player.hiddenQuiz = {}; } if (!player.hiddenQuiz.points) { player.hiddenQuiz.points = 1000; } if (!player.hiddenQuiz.lastPlayed) { player.hiddenQuiz.lastPlayed = []; } } for (var i = 0; i < players.length; i++) { player = getAvatarOff(players[i].id); out[player.id] = 0; for (var j = 0; j < players.length; j++) { if (i == j) { //Same player continue; } diff = 0; player2 = getAvatarOff(players[j].id); diff = player2.hiddenQuiz.points - player.hiddenQuiz.points; if (players[i].points > players[j].points) { //Means the first player won diff = 17 + Math.min(Math.max(-15, diff * 0.04), 15); } else if (players[i].points < players[j].points) { //Means the first player lost diff = 17 + Math.min(Math.max(-15, diff * -0.04), 15); diff *= -1; } else { //Tie diff = 0 + Math.min(Math.max(-15, diff * 0.04), 15); } out[player.id] += Math.round(diff); } } for (var i = 0; i < players.length; i++) { src = sys.id(players[i].id); player = getAvatarOff(players[i].id); player.hiddenQuiz.points += out[player.id]; safaribot.sendHtmlMessage(src, "Your Hidden Quiz score: " + player.hiddenQuiz.points + toColor(" (" + out[player.id] + ")", out[player.id] < 0 ? "red" : "green"), safchan); player.hiddenQuiz.lastPlayed.unshift(safari.events.hiddenQuizData.currentID); safari.saveGame(player); } safari.events.hiddenQuizData.nextQuiz = now() + (2.9 * 60 * 60 * 1000); //Every 3 hours (but 6 minutes back to ensure it starts before the market updates). safari.events.hiddenQuizData.currentID += 1; return; }; this.nextQuiz = function(src) { if (!(safari.events.hiddenQuizEnabled)) { safaribot.sendHtmlMessage(src, "The Hidden Quiz Event is not currently enabled.", safchan); return; } var player = getAvatar(src); if (!player) { return; } if (!player.hiddenQuiz) { player.hiddenQuiz = {}; } if (!player.hiddenQuiz.points) { player.hiddenQuiz.points = 1000; } safaribot.sendHtmlMessage(src, "Your Hidden Quiz score: " + player.hiddenQuiz.points + "!", safchan); if (now() > safari.events.hiddenQuizData.nextQuiz) { safaribot.sendHtmlMessage(src, "The next Hidden Quiz will begin when the market updates.", safchan); } else { safaribot.sendHtmlMessage(src, "The next Hidden Quiz will be in " + timeLeftString(safari.events.hiddenQuizData.nextQuiz) + "!", safchan); } safari.getQuizPrizes(src); return; }; this.getQuizPrizes = function(src) { var rew = []; if ((safari.events.hiddenQuizData.currentID + 1) % 100 === 50) { rew = ["@cometshard,5@cookie,3@rare", "@nugget,@cookie,@rare", "@stardust,@gacha,@dust"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 100 === 0) { rew = ["@scale,@spray,@prima", "@cherry,@ampere,@pack", "@silver,@egg,@safari"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 31 === 0) { rew = ["5@mega", "2@mega", "5@rare"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 55 === 0) { rew = ["5@pearl", "3@pearl", "@pearl"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 24 === 0) { rew = ["2@mushroom", "6@cookie", "@soda"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 43 === 0) { rew = ["2@crystal", "2@ampere", "2@silver"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 20 === 0) { rew = ["3@pearl", "3@rare", "@rare"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 10 === 0) { rew = ["10@rare", "3@rare", "@rare"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 6 === 0) { rew = ["3@golden", "@golden", "2@silver"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 6 === 1) { rew = ["@mega", "@rare", "150@dust"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 6 === 2) { rew = ["25@hdew", "4@egg", "@egg"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 6 === 3) { rew = ["5@stardust", "@bigpearl", "@pearl"] } else if ((safari.events.hiddenQuizData.currentID + 1) % 6 === 4) { rew = ["@mega", "2@stardust", "3@pearl"] } else { rew = ["3@rare", "@rare", "100@dust"] } if (src) { var out = ("1st: " + translateStuff(rew[0]) + ", 2nd: " + translateStuff(rew[1]) + ", 3rd: " + translateStuff(rew[2])); safaribot.sendHtmlMessage(src, "The prizes will be " + out + "!", safchan); return; } return rew; }; this.startQuizEvent = function() { var rew = this.getQuizPrizes(false); var ev = new Quiz(null, rew[0], rew[1], rew[2], true, true); currentEvent = ev; safari.flashPlayers(); }; /* Tower Trouble */ this.setTowerTroubleRequirements = function(src, commandData) { var x = commandData + ""; var sets = commandData.split(" or "); var multi; var str, info, crit, val, m, def, title = [], finalTitle = [], list, current = []; var spacedVal = ["move","learn","canlearn"]; for (var i = 0; i < sets.length; i++) { multi = sets[i].split("&&"); for (m = 0; m < multi.length; m++) { list = []; str = multi[m].trim(); info = str.split(":"); crit = "abc", val = "1"; if (info.length < 2) { info = str.split(" "); } crit = info[0].toLowerCase(); val = info.length > 1 ? (spacedVal.contains(crit) ? info.slice(1).join(" ") : info[1]).toLowerCase() : "asc"; def = applyFilterCriteria(src, info, crit, val, list, current, str, player.pokemon); if (!def) { return; } title.push(def); } finalTitle.push(title); title = []; } finalTitleMsg = ""; for (var i = 0; i < finalTitle.length; i++) { finalTitleMsg += readable(finalTitle[i]); if (i !== finalTitle.length - 1) { finalTitleMsg += " or "; } } if (!(safari.events.hasOwnProperty("towerTroubleData"))) { safari.events.towerTroubleData = { "searchText": "", "searchLink": "", "players": {} } } x = x.replace(">", "greater"); x = x.replace("<", "lower"); safari.events.towerTroubleData.searchText = x; safari.events.towerTroubleData.searchLink = link("/find " + x, finalTitleMsg); safaribot.sendHtmlMessage(src, "Tower Trouble requirements set to " + safari.events.towerTroubleData.searchLink + ".", safchan); stopQuests.tower = false; return; }; this.satisfiesTowerTrouble = function(src, party) { var restrictions = safari.events.towerTroubleData.searchText; var sets = restrictions.split(" or "); var multi; var player = getAvatar(src); if (!(player)) { return false; } var str, info, crit, val, m, def, list, current = player.pokemon.concat(), finalList = []; var spacedVal = ["move","learn","canlearn"]; for (var i = 0; i < sets.length; i++) { multi = sets[i].split("&&"); for (m = 0; m < multi.length; m++) { list = []; str = multi[m].trim(); info = str.split(":"); crit = "abc", val = "1"; if (info.length < 2) { info = str.split(" "); } crit = info[0].toLowerCase(); val = info.length > 1 ? (spacedVal.contains(crit) ? info.slice(1).join(" ") : info[1]).toLowerCase() : "asc"; def = applyFilterCriteria(src, info, crit, val, list, current, str, player.pokemon); if (!def) { return false; } current = list.concat(); } finalList = finalList.concat(current); current = player.pokemon.concat(); } finalList = removeDuplicates(finalList, true); for (var i = 0; i < party.length; i++) { if (!(finalList.contains(party[i]))) { safaribot.sendHtmlMessage(src, poke(party[i]) + " does not meet the requirements.", safchan); return false; } } return true; }; this.towerTroubleCommand = function(src, commandData) { var player = getAvatar(src); if (!player) { return; } if (!safari.events.towerTroubleEnabled) { safaribot.sendMessage(src, "Tower Trouble is not enabled!", safchan); return; } switch (commandData) { case "validate": case "valid": if (this.satisfiesTowerTrouble(src, player.party)) { safaribot.sendHtmlMessage(src, "Your current party is valid for the current Tower Trouble requirements!", safchan); } else { safaribot.sendHtmlMessage(src, "Your current party is " + toColor("NOT valid", "red") + " for the current Tower Trouble requirements!", safchan); } break; case "find": case "requirements": safaribot.sendHtmlMessage(src, "Current requirements: " + safari.events.towerTroubleData.searchLink + ".", safchan); break; case "leaderboard": case "lb": this.showTowerTroubleLeaderboard(src, false); break; default: safaribot.sendHtmlMessage(src, "Your Tower Trouble high score is " + safari.events.towerTroubleData.players[player.idnum+""] + ".", safchan); var m = "Tower Trouble commands are "; m += ("" + link("/towertrouble validate", "Validate") + ", " + link("/towertrouble find", "Requirements") + ", " + link("/towertrouble leaderboard", "Leaderboard") + ", " + link("/quest tower", "Tower")); safaribot.sendHtmlMessage(src, m, safchan); } }; this.showTowerTroubleLeaderboard = function(src, public, final) { var ordered = Object.keys(safari.events.towerTroubleData.players).sort(function(a, b) { return safari.events.towerTroubleData.players[b] - safari.events.towerTroubleData.players[a]; }); var seekAmt = Math.min(public ? 10 : 5, ordered.length); if (final) { seekAmt = Math.min(30, ordered.length); } if (seekAmt == 0) { safaribot.sendMessage(src, "No one ranked in this period yet!", safchan); return; } if (public) { safaribot.sendHtmlAll("Top " + seekAmt + " player(s) in this period:", safchan); } else { safaribot.sendMessage(src, "Top " + seekAmt + " player(s) in this period:", safchan); } var name = "", entry = ""; for (var i = 0; i < seekAmt; i++) { name = idnumList.get(parseInt(ordered[i], 10)); entry = (ordered[i]+""); if (public) { safaribot.sendHtmlAll(name + ": " + safari.events.towerTroubleData.players[entry] + ".", safchan); } else { safaribot.sendMessage(src, name + ": " + safari.events.towerTroubleData.players[entry] + ".", safchan); } } return; }; this.towerTroubleProgress = function(src) { this.showTowerTroubleLeaderboard(src, true, true); safari.events.towerTroubleData.players = {}; safari.events.towerTroubleData.searchText = ""; safari.events.towerTroubleData.searchLink = ""; stopQuests.tower = true; safaribot.sendMessage(src, "Finished the current Tower Trouble round and disabled Battle Tower. Remember to re-enable it once the next round is ready. (Use /settowertrouble [find string])", safchan); }; this.printTowerTroubleLB = function(src) { this.showTowerTroubleLeaderboard(src, false, true); }; /* Max Raid */ this.renewMaxRaid = function() { var holdPlayers = []; if (this.maxRaid && this.maxRaid.recentPlayers) { holdPlayers = [].concat(this.maxRaid.recentPlayers); } this.maxRaid = { weeklyMons: [], teams: { green: [], red: [], blue: [] }, playerScores: {}, recentPlayers: [], raidMons: [], playerMons: {}, playerKOs: {}, teamKOs: { green: 0, red: 0, blue: 0 } }; var maxRaidMons = []; this.maxRaid.weeklyMons = [].concat(maxRaidMons).shuffle().slice(0, 4); }; this.fillMaxRaidMons = function() { function newRaidMon(id) { var out = {}; out.maxhp = Math.round(getStatsNamed(id)["HP"] * 9.5) + 1700; out.hp = out.maxhp; out.atk = Math.round(Math.max(getStatsNamed(id)["Attack"], getStatsNamed(id)["Special Attack"]) * 3.09) + 200; out.def = Math.round(Math.max(getStatsNamed(id)["Defense"], getStatsNamed(id)["Special Defense"]) * 3.25) + 225; out.type1 = type1(id); out.type2 = type2(id); out.proc = 50; out.name = poke(id); out.boosts = { "atk": 0, "def": 0 }; return out; } var j = 0, k = 0; nextMon; while (this.maxRaid.raidMons.length < 3) { nextMon = this.maxRaid.weeklyMons.random(); k = 0; while (this.maxRaid.raidMons.contains(nextMon)) { nextMon = this.maxRaid.weeklyMons.random(); k++; if (k > 1000) { break; } } j++; if (j > 100) { break; } var newMon = newRaidMon(nextMon); this.maxRaid.raidMons.push(newMon); } }; this.joinMaxRaid = function(src, data) { var player = getAvatar(src); if (!(player)) { return; } var idnum = player.idnum; if (this.maxRaid.playerMons.hasOwnProperty(idnum+"")) { //The player already has a Pokémon in max raids, don't let them put in another one. safaribot.sendHtmlMessage(src, "You alread have a Pokémon in Max Raids!", safchan); return; } var input = getInputPokemon(data); if (!input.num) { safaribot.sendHtmlMessage(src, "That's not a valid Pokémon!", safchan); return; } var newMon = this.createPlayerRaider(input.num, player); this.maxRaid.playerMons[idnum+""] = newMon; if (!(this.maxRaid.playerScores.hasOwnProperty(idnum+""))) { this.maxRaid.playerScores[idnum+""] = 0; } if (!(this.maxRaid.playerKOs.hasOwnProperty(idnum+""))) { this.maxRaid.playerKOs[idnum+""] = 0; } if (this.maxRaid.teams.green.contains(idnum+"")) { this.maxRaid.playerMons[idnum+""].team = "green"; return; } if (this.maxRaid.teams.red.contains(idnum+"")) { this.maxRaid.playerMons[idnum+""].team = "red"; return; } if (this.maxRaid.teams.blue.contains(idnum+"")) { this.maxRaid.playerMons[idnum+""].team = "blue"; return; } if (chance(0.33)) { this.maxRaid.playerMons[idnum+""].team = "green"; this.maxRaid.teams.green.push(idnum+""); } else if (chance(0.5)) { this.maxRaid.playerMons[idnum+""].team = "red"; this.maxRaid.teams.red.push(idnum+""); } else { this.maxRaid.playerMons[idnum+""].team = "blue"; this.maxRaid.teams.blue.push(idnum+""); } } this.createPlayerRaider = function(id, owner) { var out = {}; out.maxhp = Math.round(getStatsNamed(id)["HP"] * 1.5) + 300; out.hp = out.maxhp; var atk, def, satk, sdef; out.atk = Math.round(Math.max(getStatsNamed(id)["Attack"], getStatsNamed(id)["Special Attack"]) * 2.35); out.def = Math.round(Math.max(getStatsNamed(id)["Defense"], getStatsNamed(id)["Special Defense"]) * 2.35); out.support = Math.round((Math.min(getStatsNamed(id)["Defense"], getStatsNamed(id)["Special Defense"]) + Math.min(getStatsNamed(id)["Attack"], getStatsNamed(id)["Special Attack"])) * 1.55); out.speed = getStatsNamed(id)["Speed"] * 4; out.boosts = { "atk": 0, "def": 0, "spe": 0, "spt": 0 }; out.type1 = type1(id); out.type2 = type2(id); out.attackMovesAmt = 2; out.supportMovesAmt = 1; out.selected = -1; out.targeted = -1; function getMoveTypes(id) { var num = parseInt(id, 10), m, out = {}, t, moves = fetchMoves(num); if (!moves) { moves = fetchMoves(pokeInfo.species(num)); } for (m = moves.length; m--; ) { t = sys.type(moveType(moves[m])); if (!out.hasOwnProperty(t)) { out[t] = 0; } out[t]++; } return out; }; function getMovePowers(id) { var num = parseInt(id, 10), m, out = {}, t, k, moves = fetchMoves(num); if (!moves) { moves = fetchMoves(pokeInfo.species(num)); } for (var o in effectiveness) { out[o] = 50; } for (m = moves.length; m--; ) { k = getMoveBP(moves[m]); if (k !== "---") { t = sys.type(moveType(moves[m])); out[t] = (Math.max(out[t], parseInt(k, 10))); } } for (var o in effectiveness) { out[o] += 20; } return out; }; out.moveTypes = getMoveTypes(id); out.movePowers = getMovePowers(id); out.ownernum = owner.idnum; }; this.generateRaiderMoves = function(mon) { var out = {}; var moves = [], item = {}; for (var i = 0; i < mon.attackMovesAmt; i++) { item = {}; item.type = randomSample(mon.moveTypes); item.pow = Math.min(sys.rand(20, mon.movePowers[item.type]), sys.rand(20, mon.movePowers[item.type])) + 20; if (chance(mon.supportStat * 0.01 * 0.1)) { item.boostStat = ["atk", "def", "spe", "spt"].random(); item.boostAmt = Math.min(sys.rand(0, mon.supportStat * 0.05), sys.rand(0, mon.supportStat * 0.05), 10); item.boostAmt = Math.max(item.boostAmt, 5); } var extraEffectChance = (chance((mon.supportStat - 225) * 0.01 * 0.25) || chance((mon.supportStat - 150) * 0.01 * 0.01)) if (extraEffectChance) { item.effect = ["stun", "aggravate", "uncover", "debuffAtk", "debuffDef"].random(); } moves.push(item); item = {}; } for (var i = 0; i < mon.supportMovesAmt; i++) { item = {}; item.pow = 0; item.heal = 0; item.protect = 0; if (chance(0.35)) { item.heal = 50 + Math.max(Math.min(sys.rand(0, Math.round(mon.supportStat * 0.5)), sys.rand(0, Math.round(mon.supportStat * 0.5))), 50); } if (chance(0.35)) { item.protect = 0.25 + (Math.max(Math.min(sys.rand(0, mon.supportStat), sys.rand(0, mon.supportStat)), 50) * 0.01 * 0.25); item.protect = Math.min(0.75, item.protect); } item.boostStat = ["atk", "def", "spe", "spt"].random(); item.boostAmt = sys.rand(Math.round(mon.supportStat * 0.5), mon.supportStat) * 0.1; item.boostAmt += 15; if (item.heal > 0) { item.boostAmt *= 0.5; } if (item.protect > 0) { item.boostAmt *= 0.5; } item.boostAmt = Math.round(item.boostAmt); if (mon.supportStat > 300 + mon.supportMovesAmt * 100 && chance(0.15)) { item.extraMove = "Support"; } else if (mon.supportStat > 300 + mon.attackMovesAmt * 75 && chance(0.15)) { item.extraMove = "Attack"; } moves.push(item); item = {}; } return moves; }; this.prepareRaidersForTurn = function() { var raider; for (var i = 0; i < this.maxRaid.playerMons.length; i++) { raider = this.maxRaid.playerMons[i]; raider.supportStat = raider.support + raider.boosts.spt; raider.moves = this.generateRaiderMoves(raider); raider.selected = -1; raider.targeted = -1; } }; this.raiderUseMove = function(mon) { var move = mon.moves[mon.selected]; var target; var atkStat = mon.atk + mon.boosts.atk, defStat; var dmg, eff; if (mon.targeted > -1) { target = this.maxRaid.raidMons[mon.targeted]; } else { target = null; } mon.results.push("Used move " + this.translateRaidMove(move) + target ? (" on " + target.name) : "!"); if (target && target.hp <= 0) { mon.results.push("But there was no target remaining..."); return; } if (move.pow > 0 && target) { //Damage calculation defStat = target.def + target.boosts.def; if (target.uncovered) { defStat -= target.boosts.def; defStat *= 0.75; } dmg = move.pow + atkStat - defStat; eff = safari.checkEffective([move.type], [target.type1, target.type2]); if (move.type == mon.type1 || move.type == mon.type2) { dmg += 50; } if (eff >= 2) { dmg = dmg * 1.2; } else if (eff >= 4) { dmg = dmg * 1.4; } if (eff <= 0.5) { dmg = dmg * 0.8; } else if (eff <= 0.25) { dmg = dmg * 0.6; } dmg = Math.max(Math.round(dmg), 0); dmg = Math.min(target.hp, dmg); target.hp -= dmg; mon.results.push("The " + target.name + " lost " + dmg + "HP!"); var score = dmg; this.maxRaidScore(mon.ownernum, dmg); if (target.hp <= 0) { mon.results.push("The " + target.name + " fainted!"); this.maxRaidScore(mon.ownernum, 50 + (20 * this.maxRaid.playerKOs[mon.ownernum+""])); //Each consecutive KO raises the amount of points you get for a KO. this.maxRaid.playerKOs[mon.ownernum+""] += 1; var team = this.maxRaid.teams[mon.team]; for (var i = 0; i < team.length; i++) { this.maxRaidScore(team[i], 10 + (5 * this.maxRaid.teamKOs[team])); //For each consecutive KO by any team member, the points gained for team KOs increases. this.maxRaid.teamKOs[team] += 1; } return; } switch (move.effect) { case "stun": target.proc -= 6; results.push("The " + target.name + "'s attack has been delayed!"); break; case "aggravate": target.proc += 4; results.push("The " + target.name + "'s attack has been accelerated!"); break; case "uncover": results.push("The " + target.name + "'s defenses have been lowered for the rest of the turn!"); target.uncovered = true; break; case "debuffAtk": results.push("The " + target.name + "'s attack has been lowered!"); target.boosts.atk -= 25; break; case "debuffDef": results.push("The " + target.name + "'s defense has been lowered!"); target.boosts.def -= 25; break; } } }; this.processMaxRaidTurn = function() { //Every 4 hours, once standardized var mon; for (var i = 0; i < this.maxRaid.playerMons.length; i++) { mon = this.maxRaid.playerMons[i]; mon.speedStat = mon.speed + mon.boosts.spe; } var ordered = this.maxRaid.playerMons; ordered = ordered.sort(function(a, b) { return b.speedStat - a.speedStat; }); for (var i = 0; i < ordered.length; i++) { mon = ordered[i]; mon.results = []; this.raiderUseMove(mon); if (mon.results.length > 0) { this.notification(getAvatarOff(idnumList.get(mon.ownerNum)), mon.results.join(" "), "Max Raid"); } } }; this.maxRaidScore = function(idnum, val) { if (!(this.maxRaid.playerScores.hasOwnProperty(idnum+""))) { this.maxRaid.playerScores[idnum+""] = 0; } this.maxRaid.playerScores[idnum+""] += val; }; this.viewMaxRaid = function(src) { var out = [], info, e, p, b; var showBoosts = function(user) { var mp = [], val; for (var e in user.boosts) { val = user.boosts[e]; if (val !== 0) { mp.push(toColor(addSign(val) + " " + e.toUpperCase(), (val > 0 ? "darkgreen" : "red"))); } } return mp.join(", "); }; //TODO: Show current field condition(s) here for (e = 0; e < this.maxRaid.raidMons.length; e++) { p = this.maxRaid.raidMons[e]; if (p.hp === 0) { continue; } info = [getHPColor(p.hp, p.maxhp)]; b = showBoosts(p); if (b) { info.push(b); } info.push(toColor("Proc: " + p.proc, "#fc9003")); out.push(pokeInfo.icon(p.id) + info.join(" / ")); } safaribot.sendHtmlMessage(src, out.join(" || ", safchan)); out = []; for (e = 0; e < this.maxRaid.playerMons.length; e++) { p = this.maxRaid.playerMons[e]; if (p.hp === 0) { continue; } info = [getHPColor(p.hp, p.maxhp)]; b = showBoosts(p); if (b) { info.push(b); } //TODO: Indicate which team (green/red/blue) they are on out.push(pokeInfo.icon(p.id) + info.join(" / ")); } safaribot.sendHtmlMessage(src, out.join(" || ", safchan)); //TODO: Add link for you to be able to click on the move select screen for any Pokémon you control }; /* Celebrity Battles */ this.loadCelebrities = function(src, commandData) { switch (commandData) { case "kanto": safari.celebrityTrainerData = { "Trainer Brock": [65612, 377, 139, 142, 409, 464, "205", 65744, 699], "Trainer Misty": ["121", 350, 73, 195, 565, 581, 395, 65855, 134], "Trainer Lt. Surge": [642, 462, 65562, 466, 738, 474, 135, "405", 66015], "Trainer Erika": [492, 407, 346, "465", 671, 787, 470, 286, 65790], "Trainer Sabrina": ["65601", 376, 655, 196, 199, 282, 124, 488, 203], "Trainer Koga": [65551, 793, "65625", 545, 591, 758, 658, 691], "Trainer Blaine": [146, "467", 65859, 637, 727, 59, 500, 131551], "Trainer Giovanni": [645, 65651, 208, "31", 330, 232, 423, 553] }; safari.strongCelebrityTrainerData = { "Trainer Lorelei": ["131", 473, 461, 65574, 65616, 144, 197087, 478], "Trainer Bruno": [639, 66011, 65984, 784, 647, "68", 500, 208], "Trainer Agatha": [65630, 65890, 720, 65641, 724, 681, 593, "169"], "Trainer Lance": [65666, "149", 65678, 65542, 380, 65639], "Trainer Blue": [65539, "131078", 65545, 59, 103, 130, 150] }; break; case "johto": safari.celebrityTrainerData = { "Trainer Falkner": [65554, 630, 178, 663, 330, 66177, 701, "472", 279], "Trainer Bugsy": [65750, "65748", 213, 768, 743, 637, 330, 542, 469], "Trainer Whitney": ["241", 40, 586, 65964, 143, 780, 773, 66067], "Trainer Morty": [609, 94, 778, "429", 593, 709, 802, 426], "Trainer Chuck": [245, "62", 226, 534, 638, 99, 675, 342, 237], "Trainer Jasmine": ["208", 227, 65717, 65587, 65839, 598, 385, 411], "Trainer Pryce": [699, 713, 740, 646, "473", 65996, 91, 471], "Trainer Clair": ["230", 373, 130, 149, 635, 780, 612, 718] }; safari.strongCelebrityTrainerData = { "Trainer Will": [65818, 251, 786, 65912, 687, "196", 199, 800], "Trainer Bruno": [639, 66011, 65984, 784, 647, "68", 500, 208], "Trainer Koga": [65551, 793, "65625", 545, 65539, 452, 65663, 66194], "Trainer Karen": [491, 430, 461, 65895, 65765, 799, 66256, 625, "197"], "Trainer Lance": [65666, "149", 65678, 65542, 65916, 65784] }; break; case "hoenn": safari.celebrityTrainerData = { "Trainer Roxanne": ["476", 248, 567, 689, 719, 697, 306], "Trainer Brawly": ["65844", 297, 500, 794, 701, 640, 620], "Trainer Wattson": ["65846", 145, 262623, 785, 618, 604], "Trainer Flannery": [65793, 776, 65765, 721, "324", 157, 668], "Trainer Norman": ["289", 760, 463, 628, 695, 474], "Trainer Winona": ["65870", 357, 630, 468, 797, 701, 724], "Trainers Tate & Liza": [380, 381, 344, 576, 437, "65818", "66011"], "Trainer Wallace": ["350", 65796, 490, 365, 65666, 730, 66282] }; safari.strongCelebrityTrainerData = { "Trainer Sidney": [65838, "65895", 635, 65765, 727, 571, 560], "Trainer Phoebo": [66217, 711, 802, 623, 426, "477"], "Trainer Glacia": [740, "478", 65898, 378, 365, 781, 609], "Trainer Drake": [65917, 445, "65909", 706, 784, 799, 691], "Trainer Steven": ["65912", 798, 485, 379, 227, 801] }; break; } return; }; /* Market */ this.updateMarket = function(silent, unset, src) { var mark = marketData, out, md, val, dval, delim, amt; if (!mark || unset) { out = { "@safari": { price: 30, amt: 50, limit: 250, playerLimit: 50, isSilver: false, discount: false, discount2: false }, "@great": { price: 70, amt: 30, limit: 150, playerLimit: 30, isSilver: false, discount: false, discount2: false }, "@ultra": { price: 170, amt: 10, limit: 50, playerLimit: 10, isSilver: false, discount: false, discount2: false }, "@bait": { price: 129, amt: 50, limit: 250, playerLimit: 50, isSilver: false, discount: false, discount2: false }, "@stick": { price: 99999, amt: 1, limit: 1, playerLimit: 1, isSilver: false, discount: false, discount2: false }, "@box": { price: 20000, amt: 1, limit: 5, playerLimit: 1, isSilver: false, discount: false, discount2: false } } } else { out = {}; for (var t in mark) { md = mark[t]; if (chance(1 - md.chance)) { continue; } out[t] = {}; val = 0; dval = 0; delim = 1; if (chance(md.silverChance)) { out[t].silver = true; val = sys.rand(md.silverPrice[0], md.silverPrice[1]); dval = sys.rand(md.silverDiscount[0], md.silverDiscount[1]); delim = md.silverDelim ? md.silverDelim : 1; } else { out[t].silver = false; val = sys.rand(md.price[0], md.price[1]); dval = sys.rand(md.discountprice[0], md.discountprice[1]); delim = md.delim ? md.delim : 10; } val = Math.max((out[t].silver ? md.silverPrice[0] : md.price[0]), Math.round(val / delim) * delim); dval = Math.max((out[t].silver ? md.silverDiscount[0] : md.discountprice[0]), Math.round(dval / delim) * delim); amt = sys.rand(md.amt[0], md.amt[1]); out[t].price = val; out[t].discountprice = dval; out[t].limit = amt * 5; out[t].playerLimit = amt; out[t].purchases = {}; out[t].discount = false; if (md.discountChance && chance(md.discountChance)) { out[t].discount = true; } out[t].discount2 = out[t].discount; if (md.discountChance && chance(md.discountChance)) { out[t].discount2 = true; } } } var carryOver = {}, numCarried = 0; for (var i in npcShop) { if (npcShop[i].blackMarket && isRare(getInputPokemon(i).id) && npcShop[i].limit > 0 && chance(0.95)) { carryOver[i] = JSON.parse(JSON.stringify(npcShop[i])); } } npcShop = {}; for (var t in out) { npcShop[t] = out[t]; } for (var t in carryOver) { if (npcShop.hasOwnProperty(t)) { continue; } npcShop[t] = JSON.parse(JSON.stringify(carryOver[t])); numCarried++; } if (lastEscapedMons) { lastEscapedMons.shuffle(); var max = Math.min(8 - numCarried, lastEscapedMons.length); for (var i = 0; i < max; i++) { var input = getInput(poke(lastEscapedMons[i].poke)); if (npcShop.hasOwnProperty(input.input)) { continue; } npcShop[input.input] = { price: lastEscapedMons[i].price, discountprice : 1, limit: 1, playerLimit: 1, purchases: {}, discount: false, discount2: false, blackMarket: true } } lastEscapedMons.splice(0, max); permObj.add("lastEscapedMons", JSON.stringify(lastEscapedMons)); } this.saveShop(); if (!silent && (mark)) { sendAll("", true, true); safaribot.sendHtmlAll("The market has been updated with new goodies! Type " + link("/buy") + " to see the prices!", safchan ); sendAll("", true, true); } permObj.save(); }; /* Daycare */ /* Secret Base */ this.viewDecorations = function(src) { if (!validPlayers("self", src)) { return; } var isAndroid = (sys.os(src) === "android"); var player = getAvatar(src), obj = player.decorations, info; var list = Object.keys(obj).filter(function(x) { return obj[x] > 0; }).sort(); sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Your decorations:", safchan); var out = []; for (var e = 0; e < list.length; e++) { info = decorations[list[e]]; out.push(obj[list[e]] + "x " + info.name + " (" + info.width + "x" + info.height + ", " + info.points + " points)"); } if (isAndroid) { safaribot.sendMessage(src, out.join(" | "), safchan); } else { if (out.length > 10) { var rowsNumber = 12; var rows = []; for (e = 0; e < rowsNumber; e++) { rows.push([]); } for (e = 0; e < out.length; e++) { rows[e%rowsNumber].push(out[e]); } for (e = 0; e < rowsNumber; e++) { out.push("" + rows[e].join(" | ") + ""); } sys.sendHtmlMessage(src, "" + out.join("") + "
", safchan); } else { for (e = 0; e < out.length; e++) { safaribot.sendMessage(src, out[e], safchan); } } } sys.sendMessage(src, "", safchan); }; this.viewBase = function(src, data) { if (data === "*") { data = sys.name(src); } var id = sys.id(data); if (!id) { safaribot.sendMessage(src, "No such player!", safchan); return; } var target = getAvatar(id); if (!target) { safaribot.sendMessage(src, "This person didn't enter the Safari!", safchan); return; } if (id !== src && target.secretBase.length === 0) { safaribot.sendMessage(src, "This person's Secret Base is empty!", safchan); return; } // Possibly disable this. Adding this because printing Secret Base may end using a fair amount of bandwidth var currentTime = now(); var lastView = SESSION.users(src).secretBaseView || 0; if (lastView > currentTime) { safaribot.sendMessage(src, "Please wait " + timeLeftString(lastView) + " before trying to visit another Secret Base!", safchan); return; } SESSION.users(src).secretBaseView = now() + (src === id ? 30 : 60) * 1000; safaribot.sendMessage(src, sys.name(id) + "'s Secret Base (Value: " + addComma(target.baseValue) + "):", safchan); this.printBase(src, target.secretBaseCache, src === id); }; this.printBase = function(src, base, isSelf) { var isAndroid = (sys.os(src) === "android"); var out = [], e, rowData = []; if (isAndroid) { var side = ""; for (e = 0; e < base.length; e++) { rowData.push(this.getDecorationPic(base[e])); if (e % SECRET_BASE_WIDTH === SECRET_BASE_WIDTH-1) { out.push(rowData.join("") + side); rowData = []; } } if (isSelf) { sys.sendHtmlMessage(src, "1↓ 1→ ______________________", safchan); } for (e = 0; e < out.length; e++) { sys.sendHtmlMessage(src, out[e], safchan); } sys.sendHtmlMessage(src, "¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯", safchan); } else { for (e = 1; e <= SECRET_BASE_WIDTH; e++) { rowData.push(e); } if (isSelf) { out.push("" + rowData.join("") + ""); } rowData = []; for (e = 0; e < base.length; e++) { rowData.push(this.getDecorationPic(base[e])); if (e % SECRET_BASE_WIDTH === SECRET_BASE_WIDTH-1) { out.push("" + (isSelf ? "" + (out.length) + "" : "") + "" + rowData.join("") + ""); rowData = []; } } out = "
" + out.join("") + "
"; sys.sendHtmlMessage(src, out, safchan); } }; this.getDecorationPic = function(id) { if (id === null) { return ""; } var picId = id.substr(0, id.indexOf(":")); var picIndex = parseInt(id.substr(id.indexOf(":")+1), 10); return ""; }; this.editBase = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var info = data.split(":"); var deco = decorationAlias(info[0].toLowerCase(), true); if (info.length < 3 && deco !== "wipe") { safaribot.sendMessage(src, "Invalid format! Use /editbase Decoration:PositionX:PositionY! To remove a decoration, use /editbase remove:PositionX:PositionY, or /editbase wipe to remove all decorations. Bases dimensions are " + SECRET_BASE_WIDTH + "x" + SECRET_BASE_HEIGHT + ".", safchan); return; } var x = parseInt(info[1], 10) - 1 || 0; var y = parseInt(info[2], 10) - 1 || 0; var clearing = ["remove", "clear", "empty", "wipe"].contains(deco); var current = this.getDecoAtPos(player.secretBase, x, y); if (clearing) { if (deco === "wipe") { player.secretBase = []; player.secretBaseCache = this.cacheBase(player.secretBase); this.saveGame(player); safaribot.sendMessage(src, "Removed all decorations from your Secret Base!", safchan); return; } if (!current) { safaribot.sendMessage(src, "There's no decoration to remove from this position!", safchan); return; } player.secretBase.splice(player.secretBase.indexOf(current), 1); player.secretBaseCache = this.cacheBase(player.secretBase); safaribot.sendMessage(src, "You removed the " + decorations[current.deco].name + " from the position " + (x+1) + "," + (y+1) + "!", safchan); return; } if (!decorations.hasOwnProperty(deco)) { safaribot.sendMessage(src, "Invalid decoration!", safchan); return; } if (!player.decorations.hasOwnProperty(deco) || player.decorations[deco] === 0) { safaribot.sendMessage(src, "You do not have this decoration!", safchan); return; } var e, count = 0; for (e = 0; e < player.secretBase.length; e++) { if (player.secretBase[e].deco === deco) { count++; } } if (player.decorations[deco] - count < 1) { safaribot.sendMessage(src, "You do not have any more of this decoration available!", safchan); return; } if (!this.canFitDeco(deco, player.secretBaseCache, x, y)) { safaribot.sendMessage(src, "You cannot put this decoration at this position! Please check if there's enough space at this point.", safchan); return; } player.secretBase.push({ deco: deco, x: x, y: y}); player.secretBaseCache = this.cacheBase(player.secretBase); player.baseValue = 0; for (e = player.secretBase.length; e--; ) { player.baseValue += decorations[player.secretBase[e].deco].points; } this.saveGame(player); safaribot.sendMessage(src, "You added the " + decorations[deco].name + " to the position " + (x+1) + "," + (y+1) + "!", safchan); }; this.canFitDeco = function(deco, base, x, y) { var data = decorations[deco]; if (x < 0 || y < 0 || x + data.width > SECRET_BASE_WIDTH || y + data.height > SECRET_BASE_HEIGHT) { return false; } var px, py, index; for (py = y; py < y + data.height; py++) { for (px = x; px < x + data.width; px++) { index = py * SECRET_BASE_WIDTH + px; if (base[index] !== null) { return false; } } } return true; }; this.getDecoAtPos = function(data, x, y) { var e, obj, info; for (e = 0; e < data.length; e++) { obj = data[e]; info = decorations[obj.deco]; if (x >= obj.x && x < obj.x + info.width && y >= obj.y && y < obj.y + info.height) { return obj; } } return null; }; this.cacheBase = function(data) { var out = [], e, slots = SECRET_BASE_WIDTH * SECRET_BASE_HEIGHT; for (e = 0; e < slots; e++) { out.push(null); } var info, id, x, y, px, py, deco, part; for (e in data) { deco = data[e]; id = deco.deco; info = decorations[id]; x = deco.x; y = deco.y; if (info.width > 1 || info.height > 1) { part = 0; for (py = 0; py < info.height; py++) { for (px = 0; px < info.width; px++) { out[Math.floor((y + py) * SECRET_BASE_WIDTH) + ((x + px) % SECRET_BASE_WIDTH)] = id + ":" + part; part++; } } } else { out[Math.floor(y * SECRET_BASE_WIDTH) + (x % SECRET_BASE_WIDTH)] = id + ":" + 0; } } return out; }; this.sanitizeBase = function(player) { if (player) { var decosCount = {}, e, data, diff, amt, i, d; for (e = 0; e < player.secretBase.length; e++) { data = player.secretBase[e]; if (!decosCount.hasOwnProperty(data.deco)) { decosCount[data.deco] = 0; } decosCount[data.deco]++; } for (e in decosCount) { amt = player.decorations.hasOwnProperty(e) ? player.decorations[e] : 0; if (amt < decosCount[e]) { diff = decosCount[e] - amt; for (i = 0; i < diff; i++) { for (d = 0; d < player.secretBase.length; d++) { if (player.secretBase[d].deco === e) { player.secretBase.splice(d, 1); break; } } } } } player.secretBaseCache = this.cacheBase(player.secretBase); } }; function printDecoration(src, id) { var isAndroid = (sys.os(src) === "android"); var out = [], e, rowData = [], deco = decorations[id], imgset = deco.img, w = deco.width; if (isAndroid) { for (e = 0; e < imgset.length; e++) { rowData.push(""); if (e % w === w-1) { out.push(rowData.join("")); rowData = []; } } for (e = 0; e < out.length; e++) { sys.sendHtmlMessage(src, out[e], safchan); } } else { for (e = 0; e < imgset.length; e++) { rowData.push(""); if (e % w === w-1) { out.push("" + rowData.join("") + ""); rowData = []; } } out = "" + out.join("") + "
"; sys.sendHtmlMessage(src, out, safchan); } } this.buyLuckyCoins = function(src, data) { var reason = "buy stuff"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (!(buyLuckyPossible)) { safaribot.sendMessage(src, "You cannot buy Lucky Coins anymore!", safchan); return; } var player = getAvatar(src); if (player.balls.lucky > 400) { safaribot.sendMessage(src, "You cannot buy any more Lucky Coins!", safchan); return; } if (!data) { safaribot.sendMessage(src, "You can buy Lucky Coins with /buylucky [amt]!", safchan); if (player.balls.lucky == 0) { safaribot.sendMessage(src, "You can get 300 Lucky Coins with $1,000 to begin!", safchan); } return; } var amt = parseInt(data, 10), cost = 1000, extraCount = 0; for (var a in player.npcBets) { if (player.npcBets[a] > 0) { extraCount += player.npcBets[a]; } } if (player.balls.lucky + extraCount - (player.celebBetEarned ? player.celebBetEarned : 0) == 0) { amt = 300; cost = 1000; } if (!data) { safaribot.sendMessage(src, "You can buy Lucky Coins with /buylucky [amt]!", safchan); return; } if (amt % 10 !== 0) { safaribot.sendMessage(src, "You can buy Lucky Coins in multiples of 10 only!", safchan); return; } if (amt + player.balls.lucky + extraCount - (player.celebBetEarned ? player.celebBetEarned : 0) > 400) { safaribot.sendMessage(src, "You can't buy more than 400 Lucky Coins per Tournament!", safchan); return; } cost = (amt * 500); if (cost > player.money) { safaribot.sendMessage(src, "You don't have $" + cost + " to buy the lucky coins!", safchan); return; } player.money -= cost; player.balls.lucky += amt; safaribot.sendMessage(src, "You purchased " + amt + " Lucky Coins with $" + cost + "!", safchan); return; }; this.myCelebrityBets = function(src) { var player = getAvatar(src); var out = []; for (var a in player.npcBets) { if (player.npcBets[a] > 0) { out.push((a + "").toUpperCase() + ": " + (player.npcBets[a] + "")); } } if (out.length <= 0) { safaribot.sendMessage(src, "You don't have any bets yet!", safchan); return; } safaribot.sendMessage(src, "Your current Celebrity Bets are " + out.join(", ") + "!", safchan); return; }; this.betCelebrity = function(src, data) { var reason = "start a battle"; if (cantBecause(src, reason, ["tutorial"])) { return; } var player = getAvatar(src); var cdata; if (!data) { safaribot.sendMessage(src, "You can bet on which celebrity you think will win with /betnpc [name]:[amt]!", safchan); return; } cdata = data.toLowerCase().split(":"); if (cdata.length < 2) { safaribot.sendMessage(src, "You can bet on which celebrity you think will win with /betnpc [name]:[amt]!", safchan); return; } var trainer = cdata[0]; var amt = parseInt(cdata[1], 10); if (amt > player.balls.lucky) { safaribot.sendMessage(src, "You don't have " + amt + " Lucky Coins!", safchan); return; } if (amt < 0) { safaribot.sendMessage(src, "You can't bet " + amt + " Lucky Coins!", safchan); return; } if ((amt % 5) !== 0) { safaribot.sendMessage(src, "You can only bet in multiples of 5!", safchan); return; } if (!(npcMatchAlive.contains(trainer.toLowerCase()))) { safaribot.sendMessage(src, "That's not a valid trainer in this tournament!", safchan); return; } if (!(player.npcBets)) { player.npcBets = {}; } if (!(player.npcBets[trainer])) { player.npcBets[trainer] = 0; } if (amt + player.npcBets[trainer] > npcBetCap) { safaribot.sendMessage(src, "You cannot bet more than " + npcBetCap + " on a single trainer in this round!", safchan); return; } player.npcBets[trainer] += amt; player.balls.lucky -= amt; safaribot.sendMessage(src, "You bet " + amt + " Lucky Coins on Trainer " + trainer.toUpperCase() + "!", safchan); this.saveGame(player); return; }; /* Battles */ this.challengePlayerTag = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "start a battle"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (data !== "abort") { if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid"])) { return; } } if (stopQuests.league) { safaribot.sendMessage(src, "Rotation Battle challenges are temporarily disabled!", safchan); return; } var name = sys.name(src).toLowerCase(); var found = null, ind = 0; for (var i = 0; i < challengeRequests2.length; i++) { var teams = challengeRequests2[i]; if (teams[0].hasOwnProperty(name)) { found = challengeRequests2[i]; ind = i; break; } if (teams[1].hasOwnProperty(name)) { found = challengeRequests2[i]; ind = i; break; } } if (!found) { //player initiates a new challengeRequest2 var cdata = data.split(":"); if (cdata.length < 2) { safaribot.sendMessage(src, "The format for a Tag Battle challenge is /challenge3 teammate:foe,foe2!", safchan); return; } var foes = (cdata[1].split(",")); if (foes.length < 2) { safaribot.sendMessage(src, "The format for a Tag Battle challenge is /challenge3 teammate:foe,foe2!", safchan); return; } var ally = cdata[0].toLowerCase(); var opp1 = foes[0].toLowerCase(); var opp2 = foes[1].toLowerCase(); if (!sys.id(ally)) { safaribot.sendMessage(src, ally + " couldn't be found!", safchan); return; } if (!sys.id(opp1)) { safaribot.sendMessage(src, opp1 + " couldn't be found!", safchan); return; } if (!sys.id(opp2)) { safaribot.sendMessage(src, opp2 + " couldn't be found!", safchan); return; } var input = [], team1 = {}, team2 = {}; team1[name] = true; team1[ally] = false; team2[opp1] = false; team2[opp2] = false; input.push(team1); input.push(team2); challengeRequests2.push(input); safaribot.sendMessage(src, "You and " + ally.toCorrectCase() + " challenged " + opp1.toCorrectCase() + " and " + opp2.toCorrectCase() + " to a Tag Battle!", safchan); safaribot.sendMessage(sys.id(ally), name.toCorrectCase() + " wants to team up with you to battle " + opp1.toCorrectCase() + " and " + opp2.toCorrectCase() + " in a Tag Battle!", safchan); safaribot.sendMessage(sys.id(opp1), name.toCorrectCase() + " and " + ally.toCorrectCase() + " challenged you and " + opp2.toCorrectCase() + " to a Tag Battle!", safchan); safaribot.sendMessage(sys.id(opp2), name.toCorrectCase() + " and " + ally.toCorrectCase() + " challenged you and " + opp1.toCorrectCase() + " to a Tag Battle!", safchan); safaribot.sendHtmlMessage(sys.id(ally), "Type " + link("/challenge3 accept") + " to accept or " + link("/challenge3 abort") + " to reject!", safchan); safaribot.sendHtmlMessage(sys.id(opp1), "Type " + link("/challenge3 accept") + " to accept or " + link("/challenge3 abort") + " to reject!", safchan); safaribot.sendHtmlMessage(sys.id(opp2), "Type " + link("/challenge3 accept") + " to accept or " + link("/challenge3 abort") + " to reject!", safchan); return; } else { if (data === "abort" || data === "cancel") { for (var t in found) { for (var p in found[t]) { safaribot.sendMessage(sys.id(p), name.toCorrectCase() + " aborted the Tag Team battle!", safchan); } } challengeRequests2.splice(ind, 1); return; } if (data !== "accept") { safaribot.sendHtmlMessage(sys.id(p), "Type " + link("/challenge3 accept") + " to accept a pending challenge!", safchan); safaribot.sendHtmlMessage(sys.id(p), "To abort the match, type " + link("/challenge3 abort") + "!", safchan); return; } var team, ready = true, players = []; for (var t in found) { if (Object.keys(found[t]).contains(name)) { found[t][name] = true; } } for (var t in found) { for (var p in found[t]) { safaribot.sendMessage(sys.id(p), name.toCorrectCase() + " accepted the Tag Team battle invitation!", safchan); if (!(sys.id(p))) { ready = false; } else if (found[t][p] === false) { ready = false; } else { players.push(sys.id(p)); } } } if (ready && players.length === 4) { var battle = new Battle2(players[0], players[2], {}, players[1], players[3]); currentBattles.push(battle); challengeRequests2.splice(ind, 1); return; } } }; this.challengePlayer = function(src, data, isRotation, conditions, command) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "start a battle"; if (cantBecause(src, reason, ["tutorial"])) { return; } var name = sys.name(src).toLowerCase(); if (data.toLowerCase() === "cancel") { if (name in challengeRequests) { safaribot.sendMessage(src, "You cancelled your challenge against " + challengeRequests[name].opponent.toCorrectCase() + "!", safchan); delete challengeRequests[name]; } else { safaribot.sendMessage(src, "You have no pending challenges initiated by you!", safchan); } return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid"])) { return; } if (isRotation && stopQuests.league) { safaribot.sendMessage(src, "Rotation Battle challenges are temporarily disabled!", safchan); return; } var autoCancel, isSameUser; var targetId = sys.id(data); if (!targetId) { safaribot.sendMessage(src, "No such person!", safchan); return; } var tName = sys.name(targetId).toLowerCase(); if (name in challengeRequests) { if (challengeRequests[name].opponent === tName) { isSameUser = true; } autoCancel = true; } if (tName === name) { safaribot.sendMessage(src, "You can't battle yourself!", safchan); return; } /* if (!validPlayers("target", src, data, "You can't battle yourself!")) { return; } */ var target = getAvatar(targetId); if (!target) { safaribot.sendMessage(src, "No such player!", safchan); return; } if (target.playerBlacklist.contains(player.idnum)) { safaribot.sendMessage(src, "This person is not accepting battle requests from you right now!", safchan); return; } if (this.isInAuction(tName)) { safaribot.sendMessage(src, "This person is currently in an auction! Wait for them to finish to challenge again!", safchan); return; } if (this.isBattling(tName)) { safaribot.sendMessage(src, "This person is already battling! Wait for them to finish to challenge again!", safchan); return; } if (target.tutorial.inTutorial) { safaribot.sendMessage(src, "This person is currently completing the tutorial! Wait for them to finish to challenge again!", safchan); return; } var type = isRotation ? "rotation" : "normal"; if (challengeRequests.hasOwnProperty(tName) && challengeRequests[tName].opponent == name) { var chall = challengeRequests[tName]; if (chall.type !== type) { safaribot.sendHtmlMessage(src, sys.name(targetId) + " challenged you for a " + cap(chall.type) + " Battle! To accept it, use " + link("/" + (chall.type === "rotation" ? command : "challenge") + " " + tName.toCorrectCase()) + ".", safchan); return; } /* if (target.party.length < 3) { safaribot.sendMessage(src, "Battle not started because " + sys.name(targetId) + " has less than 3 Pokémon in their party!", safchan); safaribot.sendMessage(targetId, "Your party must have at least 3 Pokémon to battle!", safchan); return; } */ if (player.party.length !== target.party.length) { safaribot.sendMessage(src, (isRotation ? "Rotation " : "") + "Battle not started because both players have a different number of Pokémon in their party!", safchan); return; } var battle = isRotation ? new Battle2(targetId, src, {}, false, false, conditions) : new Battle(targetId, src); currentBattles.push(battle); delete challengeRequests[tName]; } else { if (autoCancel) { if (isSameUser) { safaribot.sendHtmlMessage(src, "You cancelled your previous challenge against " + challengeRequests[name].opponent.toCorrectCase() + " to send a new challenge request.", safchan); } else { safaribot.sendHtmlMessage(src, "You cancelled your challenge against " + challengeRequests[name].opponent.toCorrectCase() + " to challenge " + tName.toCorrectCase() + " instead.", safchan); } delete challengeRequests[name]; } challengeRequests[name] = { opponent: tName, type: type }; var commandLink = "/challenge" + (isRotation ? "2" : "") + " cancel"; safaribot.sendHtmlMessage(src, "You are challenging " + sys.name(targetId) + " to a" + (isRotation ? " Rotation" : "") + " Battle! Wait for them to accept, or cancel the challenge with " + link(commandLink) + ".", safchan); commandLink = (isRotation ? "/" + command : "/challenge") + " " + sys.name(src); sys.sendMessage(targetId, "", safchan); safaribot.sendHtmlMessage(targetId, sys.name(src) + " is challenging you for a" + (isRotation ? " Rotation" : "") + " Battle! To accept, type " + link(commandLink) + ".", safchan); sys.sendMessage(targetId, "", safchan); } }; this.forfeitBattle = function(src) { if (!validPlayers("self", src)) { return; } var name = sys.name(src); if (!this.isBattling(name)) { safaribot.sendMessage(src, "You are not battling!", safchan); return; } var battle, b; for (b in currentBattles) { battle = currentBattles[b]; if (battle.isInBattle(name)) { battle.abort(src, name, true); currentBattles.splice(b, 1); return; } } }; this.watchBattle = function(src, data, soft, bypass) { var name = sys.name(src); if (!validPlayers("self", src)) { return; } var player = getAvatar(src); for (b in currentBattles) { // ???? what is this and why is it here battle = currentBattles[b]; if (!(battle.fullNPC)) { continue; } if (data.toLowerCase() == battle.name1.toLowerCase() || data.toLowerCase() == battle.name2.toLowerCase()) { if (battle.cantWatch && ((!SESSION.channels(safchan).isChannelAdmin(src)) || (soft))) { safaribot.sendMessage(src, "You cannot watch/unwatch this battle!", safchan); return; } if (battle.viewers.indexOf(name.toLowerCase()) !== -1) { battle.sendToViewers(name + " stopped watching this battle!"); battle.viewers.splice(battle.viewers.indexOf(name.toLowerCase()), 1); } else { battle.viewers.push(name.toLowerCase()); battle.sendToViewers(name + " is watching this battle!"); if (battle.battle2 && (battle.select || battle.biasNPC)) { battle.showInfo(name.toLowerCase()); } } return; } } if (!validPlayers("target", src, data)) { return; } var tName = sys.name(sys.id(data)).toLowerCase(); var canView = function(src, name) { var target = getAvatarOff(name); if (!target) { return false; } return !(target.id !== sys.name(src).toLowerCase() && !target.options.visible); }; var battle, b; for (b in currentBattles) { battle = currentBattles[b]; if (battle.isInBattle(tName)) { if (battle.cantWatch && !SESSION.channels(safchan).isChannelOwner(src) && !battle.whitelist.contains(name.toLowerCase())) { safaribot.sendMessage(src, "You cannot watch/unwatch this battle!", safchan); return; } if (battle.viewers.indexOf(name.toLowerCase()) !== -1) { battle.sendToViewers(name + " stopped watching this battle!"); battle.viewers.splice(battle.viewers.indexOf(name.toLowerCase()), 1); } else { if (!battle.isInBattle(name) && (!canView(src, battle.name1) || (!battle.npcBattle && !canView(src, battle.name2))) && !battle.whitelist.contains(name.toLowerCase())) { if (!bypass) { safaribot.sendMessage(src, "You cannot view this person's battles!", safchan); return; } else { battle.sendToViewers("" + name + " is using an auth bypass to watch this battle! If this was not communicated/consented to beforehand, contact a channel owner to report abuse."); } } if (player.idnum in battle.watchCounts) { if (battle.watchCounts[player.idnum] >= watchCountMax && !bypass) { safaribot.sendMessage(src, "You can only watch this battle {0}!".format(plural(watchCountMax, "time")), safchan); return; } battle.watchCounts[player.idnum] += 1; } else { battle.watchCounts[player.idnum] = 1; } battle.viewers.push(name.toLowerCase()); battle.sendToViewers(name + " is watching this battle!"); if (battle.battle2 && (battle.select || battle.biasNPC)) { battle.showInfo(name.toLowerCase()); } } return; } } safaribot.sendMessage(src, "This person is not battling!", safchan); }; this.isBattling = function(name) { for (var b in currentBattles) { if (currentBattles[b].isInBattle(name)) { return true; } } return false; }; function Battle(p1, p2, silent) { var player1 = getAvatar(p1); this.cantWatch = false; this.startTime = now(); this.name1 = sys.name(p1); this.viewers = [this.name1.toLowerCase()]; this.watchCounts = {}; this.whitelist = [this.name1.toLowerCase()]; this.team1 = player1.party.concat().shuffle(); var isNPC = this.npcBattle = typeof p2 == "object"; var player2 = isNPC ? p2 : getAvatar(p2); var npcDesc = null; this.pinap = []; if (isNPC) { var costumeBonus = player1.costume === "battle" ? costumeData.battle.rate : (player1.costume === "pokefan" ? costumeData.pokefan.rate2 : 0); if (safari.hasCostumeSkill("battleBoost")) { costumeBonus *= (1.05 + Math.max((this.getCostumeLevel(player1) - 5)/100, 0)); } var useBonus = 0; if (p2.desc.indexOf("Tower Lvl.") > -1) { useBonus = player1.quests.tower.bonusPower; } this.selfPowerMin = 10 + costumeBonus + useBonus; this.selfPowerMax = (100 + costumeBonus + useBonus) * (player1.truesalt >= now() && chance(player1.srate) ? 0.35 : 1); this.name2 = player2.name; this.team2 = player2.party.concat().shuffle(); if (!player2.power || player2.length < 2) { player2.power = [10, 100]; } this.copy = player2.copy; this.powerMin = this.copy ? this.selfPowerMin : player2.power[0]; this.powerMax = this.copy ? this.selfPowerMax : player2.power[1]; this.postBattle = player2.postBattle; this.postArgs = player2.postArgs; npcDesc = player2.desc || null; this.pinap = []; if (p2.postArgs.hasOwnProperty("pinap")) { this.pinap = player2.postArgs.pinap; } } else { this.name2 = sys.name(p2); this.viewers.push(this.name2.toLowerCase()); this.team2 = player2.party.concat().shuffle(); } this.turn = -1; this.duration = Math.min(this.team1.length, this.team2.length); if (this.team1.length > this.duration) { this.team1 = this.team1.slice(0, this.duration); } if (this.team2.length > this.duration) { this.team2 = this.team2.slice(0, this.duration); } this.p1Score = 0; this.p2Score = 0; this.p1TotalScore = 0; this.p2TotalScore = 0; this.scoreOrder = []; this.finished = false; if (!silent) { sendAll("A battle between " + this.name1 + " and " + this.name2 + (npcDesc ? " (" + npcDesc + ")" : "") + " has started! [" + link("/watch " + this.name1, "Watch") + "]", true); } } Battle.prototype.nextTurn = function() { if (this.turn < 0) { this.turn++; this.sendToViewers("Preparations complete, battle will start soon!"); return; } var p1Poke = this.team1[this.turn]; var p2Poke = this.team2[this.turn]; var res = calcDamage(p1Poke, p2Poke, (this.npcBattle ? [this.selfPowerMin, this.selfPowerMax] : null), (this.npcBattle ? [this.powerMin, this.powerMax] : null), false, getCherished(p1Poke, this.name1), getCherished(p2Poke, this.name2)); var name1 = this.name1 + "'s " + poke(p1Poke, true); var name2 = this.name2 + "'s " + poke(p2Poke, true); this.sendToViewers("Turn " + (this.turn + 1) + " | " + res.statName + " | " + name1 + " " + pokeInfo.icon(p1Poke) + " x " + pokeInfo.icon(p2Poke) + " " + name2); this.sendToViewers(name1 + " | " + res.statName + ": " + res.stat[0] + " | Move Power: " + res.move[0] + " | Type Effectiveness: " + res.bonusString[0] + "x"); this.sendToViewers(name2 + " | " + res.statName + ": " + res.stat[1] + " | Move Power: " + res.move[1] + " | Type Effectiveness: " + res.bonusString[1] + "x"); var p1Power = res.power[0]; var p2Power = res.power[1]; var player = getAvatarOff(name1); if (player) { if (player.costume && player.costume == "flower" && type2(p1Poke) == "???") { p1Power += Math.round(15 * (1 + Math.random())); } } if ((this.pinap.contains(p1Poke))) { p1Power += itemData.pinap.rate; if (this.copy) { p2Power += itemData.pinap.rate; } } if (p1Power > p2Power) { this.p1Score++; this.scoreOrder.push(1); this.sendToViewers("Result: " + name1 + " (" + p1Power + ") x (" + p2Power + ") " + name2, true); } else if (p2Power > p1Power) { this.p2Score++; this.scoreOrder.push(2); this.sendToViewers("Result: " + name1 + " (" + p1Power + ") x (" + p2Power + ") " + name2 + "", true); } else { this.scoreOrder.push(0); this.sendToViewers("Result: " + name1 + " (" + p1Power + ") x (" + p2Power + ") " + name2 + " | Draw", true); } this.p1TotalScore += p1Power; this.p2TotalScore += p2Power; this.turn++; if (this.turn >= this.duration) { if (this.turn == this.duration && this.p1Score == this.p2Score) { this.team1.push(this.team1.random()); this.team2.push(this.team2.random()); this.sendToViewers("No winner after the regular rounds! An extra tiebreaker round will be held!", true); } else { this.finishBattle(); } } else { if (Math.abs(this.p1Score - this.p2Score) > this.duration - this.turn) { this.finishBattle(); } } this.sendToViewers(""); }; Battle.prototype.finishBattle = function() { var winner = 0, loser, score, tiebreaker = false, tiebreakerMsg; if (this.p1Score > this.p2Score) { winner = 1; } else if (this.p2Score > this.p1Score) { winner = 2; } else { var e; if (this.p1TotalScore > this.p2TotalScore) { winner = 1; tiebreakerMsg = "More damage dealt - " + this.p1TotalScore + " x " + this.p2TotalScore; tiebreaker = true; } else if (this.p1TotalScore < this.p2TotalScore) { winner = 2; tiebreakerMsg = "More damage dealt - " + this.p2TotalScore + " x " + this.p1TotalScore; tiebreaker = true; } else { for (e = 0; e < this.scoreOrder.length; e++) { if (this.scoreOrder[e] !== 0) { winner = this.scoreOrder[e]; tiebreaker = true; tiebreakerMsg = "First to score"; break; } } } } if (winner === 1) { winner = this.name1; loser = this.name2; score = this.p1Score + " x " + this.p2Score; } else if (winner == 2) { winner = this.name2; loser = this.name1; score = this.p2Score + " x " + this.p1Score; } if (winner) { this.sendToViewers("" + winner + " defeated " + loser + " in a battle with a score of " + score + (tiebreaker ? " (Tiebreaker: " + tiebreakerMsg + ")" : "") + "!", true); } else { this.sendToViewers("The battle between " + this.name1 + " and " + this.name2 + " ended in a draw!", true); } if (this.npcBattle && this.postBattle) { this.postBattle(this.name1, winner === this.name1, this.p1Score, this.p2Score, this.postArgs, this.viewers); } this.finished = true; }; Battle.prototype.sendMessage = function(name, msg) { var id = sys.id(name); if (id && sys.isInChannel(id, safchan)) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } }; Battle.prototype.sendToViewers = function(msg, bypassUnwatch) { var e; for (e in this.viewers) { this.sendMessage(this.viewers[e], msg); } if (bypassUnwatch) { if (!this.viewers.contains(this.name1.toLowerCase())) { this.sendMessage(this.name1, msg); } if (!this.npcBattle && !this.viewers.contains(this.name2.toLowerCase())) { this.sendMessage(this.name2, msg); } } }; Battle.prototype.abort = function(src, loser, isForfeit) { var winner = "Tie"; if (loser.toLowerCase() == this.name1.toLowerCase()) { winner = this.name2; } else if (loser.toLowerCase() == this.name2.toLowerCase()) { winner = this.name1; } if (isForfeit) { this.sendToViewers("" + loser + " forfeited! " + (winner !== "Tie" ? winner + " won!" : "") + "", true); } else { this.sendToViewers("This battle was aborted by " + sys.name(src) + "! " + (winner !== "Tie" ? winner + " was declared the winner!" : "") + "", true); } if (this.npcBattle && this.postBattle) { var extraArgs = {}; extraArgs.turn = this.turn; extraArgs.winMsg = this.winMsg; extraArgs.loseMsg = this.loseMsg; this.postBattle(this.name1, winner === this.name1, this.p1Score, this.p2Score, this.postArgs, this.viewers, extraArgs); } this.finished = true; }; Battle.prototype.isInBattle = function(name) { return this.name1.toLowerCase() == name.toLowerCase() || (!this.npcBattle && this.name2.toLowerCase() == name.toLowerCase()); }; function calcDamage(p1, p2, p1Handicap, p2Handicap, inverted, ch1, ch2) { var p1Type1 = type1(p1), p1Type2 = type2(p1); var p2Type1 = type1(p2), p2Type2 = type2(p2); var p1Bonus = safari.checkEffective([p1Type1, p1Type2], [p2Type1, p2Type2], inverted); var p2Bonus = safari.checkEffective([p2Type1, p2Type2], [p1Type1, p1Type2], inverted); var p1Move = p1Handicap ? sys.rand(p1Handicap[0], p1Handicap[1]) : sys.rand(10, 100); var p2Move = p2Handicap ? sys.rand(p2Handicap[0], p2Handicap[1]) : sys.rand(10, 100); var statName = ["HP", "Attack", "Defense", "Sp. Atk", "Sp. Def", "Speed"]; var stat = sys.rand(0, 6); var sName = statName[stat]; var p1Stat = getStats(p1)[stat] + (ch1 ? ch1 : 0); var p2Stat = getStats(p2)[stat] + (ch2 ? ch2 : 0); var p1Power = Math.round((p1Stat + p1Move) * p1Bonus); var p2Power = Math.round((p2Stat + p2Move) * p2Bonus); return { type: [[p1Type1, p1Type2], [p2Type1, p2Type2]], bonus: [p1Bonus, p2Bonus], bonusString: [toFixed(p1Bonus, 3), toFixed(p2Bonus, 3)], move: [p1Move, p2Move], statName: sName, stat: [p1Stat, p2Stat], power:[p1Power, p2Power] }; } function Battle2(p1, p2, opt, p3, p4, select, viewers, difficulty, select2) { this.battle2 = true; this.paused = false; this.pendingPause = false; this.totalPauseTime = 0; this.pauseLimit = 600 * 3; // in seconds this.startTime = now(); this.tagBattle = false; this.oneOnTwo = false; this.fullNPC = typeof p1 == "object"; var isNPC = this.npcBattle = typeof p2 == "object"; if (viewers) { this.viewers = viewers; } else { this.viewers = []; } this.watchCounts = {}; this.whitelist = []; this.moveAmt = 3; this.cantWatch = opt.cantWatch || false; this.player1lastUsed = null; var player1, player2; if (this.fullNPC) { player1 = p1; this.name1 = player1.name; this.biasNPC0 = player1.bias; this.idnum1 = "" + player1.name; } else { player1 = getAvatar(p1); this.name1 = sys.name(p1); this.idnum1 = player1.idnum; if (this.viewers.length == 0) { this.viewers = [this.name1.toLowerCase()]; } this.whitelist.push(this.name1.toLowerCase()); this.biasNPC0 = null; } if (typeof p2 !== "object") { this.name2 = sys.name(p2); this.whitelist.push(this.name2.toLowerCase()); this.idnum2 = getAvatar(p2).idnum; this.viewers.push(this.name2.toLowerCase()); } this.winMsg = opt.winMsg; this.loseMsg = opt.loseMsg; player2 = isNPC ? p2 : getAvatar(p2); var npcDesc = null; this.canPickMoves = false; this.player1Input = "a"; this.player2Input = "a"; this.p1PickedTeam = []; this.p2PickedTeam = []; this.p1MoveCodes = {}; this.p2MoveCodes = {}; this.protectCount1 = 0; this.protectCount2 = 0; this.usedProtect1 = false; this.usedProtect2 = false; this.crit = false; this.side1Field = { "spikes": 0, "stealthrock": false, "stealthicicles": false, "stealththunder": false, "toxicspikes": false, "dynamicweb": false, "quicksand": false, "reflect": -1, "lightscreen": -1 }; this.side2Field = { "spikes": 0, "stealthrock": false, "stealthicicles": false, "stealththunder": false, "toxicspikes": false, "dynamicweb": false, "quicksand": false, "reflect": -1, "lightscreen": -1 }; this.select = null; this.select2 = null; if (select) { this.select = select; } if (select2) { this.select2 = this.select; this.select = select2; } this.npcItems = { hyper: 0, revive: 0, full: 0 }; this.selectData = null; this.selectData2 = null; if (this.select) { if (this.select.hyperpotion3) { this.npcItems.hyper = 3; } else if (this.select.hyperpotion2) { this.npcItems.hyper = 2; } else if (this.select.hyperpotion) { this.npcItems.hyper = 1; } if (this.select.fullrestore3) { this.npcItems.full = 3; } else if (this.select.fullrestore2) { this.npcItems.full = 2; } else if (this.select.fullrestore) { this.npcItems.full = 1; } if (this.select.spikes3) { this.side1Field.spikes = 3; } else if (this.select.spikes2) { this.side1Field.spikes = 2; } else if (this.select.spikes) { this.side1Field.spikes = 1; } if (this.select.stealthrock) { this.side1Field.stealthrock = true; } if (this.select.stealththunder) { this.side1Field.stealththunder = true; } if (this.select.stealthicicles) { this.side1Field.stealthicicles = true; } if (this.select.toxicspikes) { this.side1Field.toxicspikes = true; } if (this.select.dynamicWeb) { this.side1Field.dynamicweb = true; } if (this.select.quickSand) { this.side1Field.quicksand = true; } if (this.select.initialReflect) { this.side2Field.reflect = 5; if (this.select.lightClay) { this.side2Field.reflect = 8; } } if (this.select.initialReflect2) { this.side1Field.reflect = 5; } if (this.select.initialLightScreen) { this.side2Field.lightscreen = 5; if (this.select.lightClay) { this.side2Field.lightscreen = 8; } } if (this.select.initialLightScreen2) { this.side1Field.lightscreen = 5; } this.selectData = {}; if (select.analytic) { this.selectData.analyticType1 = "???"; this.selectData.analyticType2 = "???"; this.selectData.analyticCount = 0; } if (select.rollout) { this.selectData.rolloutUser = -1; this.selectData.rolloutCount = 0; this.selectData.rolloutUser2 = -1; this.selectData.rolloutCount2 = 0; } if (select.retaliate2) { this.selectData.retaliate1 = false; this.selectData.retaliate2 = false; } if (this.select.lastStand) { this.selectData.lastStandReady = true; } if (this.select.moonblast) { this.selectData.moonblastTimer = (3 + Math.floor(4 * Math.random())); } if (this.select.irontail) { this.selectData.irontailTimer = (3 + Math.floor(4 * Math.random())); } if (select.waterfall || (select2 && select2.waterfall)) { this.selectData.waterfallTimer = (3 + Math.floor(4 * Math.random())); } this.jumbledStats = {}; if ((select && select.statjumble) || (select2 && select2.statjumble)) { var s = ["atk", "def", "satk", "sdef", "spe"]; var s2 = ["atk", "def", "satk", "sdef", "spe"]; var tos = ""; var froms = ""; var j = 0; while (s.length > 0) { j++; if (j > 2500) { break; } tos = s2[0]; froms = s[0]; if (tos == froms) { this.jumbledStats = {}; s = ["atk", "def", "satk", "sdef", "spe"]; s2 = ["atk", "def", "satk", "sdef", "spe"].shuffle(); } else { this.jumbledStats[froms] = tos; s.shift(); s2.shift(); } } } this.selectData.shieldHP = 0; if (this.select.iceshield || this.select.electroshield || this.select.dracoshield || this.select.sludgeshield || this.select.metalshield || this.select.genesisshield) { if (difficulty) { this.selectData.shieldHP = 400 + (difficulty * 100); } else { this.selectData.shieldHP = 400; } if (select.genesisshield) { this.selectData.shieldHP = Math.floor(this.selectData.shieldHP * 1.25); } } } if (this.select2) { this.selectData2 = {}; if (this.select2.spikes3) { this.side2Field.spikes = 3; } else if (this.select2.spikes2) { this.side2Field.spikes = 2; } else if (this.select2.spikes) { this.side2Field.spikes = 1; } if (this.select2.stealthrock) { this.side2Field.stealthrock = true; } if (this.select2.initialReflect) { this.side1Field.reflect = 5; } if (this.select2.initialReflect2) { this.side2Field.reflect = 5; } if (this.select2.initialLightScreen) { this.side1Field.lightscreen = 5; } if (this.select2.initialLightScreen2) { this.side2Field.lightscreen = 5; } if (this.select2.iceshield || this.select2.electroshield || this.select2.dracoshield || this.select2.sludgeshield || this.select2.genesisshield) { if (difficulty) { this.selectData2.shieldHP = 400 + (difficulty * 100); } else { this.selectData2.shieldHP = 400; } if (this.select2.genesisshield) { this.selectData2.shieldHP = Math.floor(this.selectData2.shieldHP * 1.25); } } } if (this.fullNPC) { this.team1 = this.originalTeam1 = this.buildTeam(this.name1, player1.party, this.idnum1); } else { this.team1 = this.originalTeam1 = this.buildTeam(this.name1, player1.party, this.idnum1, player1.cherished, player1.helds, player1.zCrystalUser); } if (isNPC) { this.name2 = player2.name; this.powerBoost = player2.powerBoost || 0; this.idnum2 = "" + this.name2; this.team2 = this.originalTeam2 = this.buildTeam(this.name2, player2.party, this.idnum2); this.postBattle = player2.postBattle; this.postArgs = player2.postArgs; this.npcDesc = player2.desc || null; this.biasNPC = player2.bias; this.npcFavorite = player2.favorite; } else { this.team2 = this.originalTeam2 = this.buildTeam(this.name2, player2.party, this.idnum2, player2.cherished, player2.helds); } if (opt) { var o; if (opt.t1HP) { for (o = 0; o < opt.t1HP.length && o < this.team1.length; o++) { this.team1[o].hp = Math.round(this.team1[o].hp * opt.t1HP[o]); } } } this.skills = { // deprecated "1": {}, "2": {} }; var player3, player4; this.team3 = []; this.team4 = []; this.name3 = ""; this.name4 = ""; if (p4) { this.tagBattle = true; this.oneOnTwo = (p3 ? false : true); player4 = isNPC ? p4 : getAvatar(p4); this.player3Input = "a"; this.player4Input = "a"; this.p3PickedTeam = []; this.p4PickedTeam = []; this.p3MoveCodes = {}; this.p4MoveCodes= {}; this.protectCount3 = 0; this.protectCount4 = 0; this.usedProtect3 = false; this.usedProtect4 = false; this.target1 = 0; this.target2 = 0; this.target3 = 0; this.target4 = 0; this.npcItems2 = {}; for (var a in this.npcItems) { this.npcItems2[a] = this.npcItems[a]; } if (!this.oneOnTwo) { player3 = isNPC ? p3 : getAvatar(p3); this.name3 = sys.name(p3); this.idnum3 = isNPC ? "" + this.name3 : player3.idnum; this.team3 = this.originalTeam3 = this.buildTeam(this.name3, player3.party, this.idnum3, player3.cherished); this.viewers.push(this.name3.toLowerCase()); this.whitelist.push(this.name3.toLowerCase()); } if (isNPC) { this.name4 = player4.name; this.powerBoost = player4.powerBoost || 0; this.idnum4 = "" + this.name4; this.team4 = this.originalTeam4 = this.buildTeam(this.name4, player4.party, this.idnum4); this.biasNPC2 = player4.bias; } else { player4 = isNPC ? p4 : getAvatar(p4); this.name4 = sys.name(p4); this.idnum4 = player4.idnum; this.team4 = this.originalTeam4 = this.buildTeam(this.name4, player4.party, this.idnum4, player4.cherished); this.viewers.push(this.name4.toLowerCase()); this.whitelist.push(this.name4.toLowerCase()); if (!this.oneOnTwo) { this.team1 = this.team1.slice(0, 4); this.team2 = this.team2.slice(0, 4); this.team3 = this.team3.slice(0, 4); this.team4 = this.team4.slice(0, 4); } } } if (this.select || this.biasNPC || this.biasNPC2) { for (var v in this.viewers) { this.showInfo(this.viewers[v]); } } this.weather = ""; this.weatherTimer = 0; if (this.select) { if (this.select.sandstorm) { this.weather = "Sand"; this.weatherTimer = 256; } else if (this.select.sandstorm3) { this.weather = "Sand"; this.weatherTimer = 256; } else if (this.select.rain) { this.weather = "Rain"; this.weatherTimer = 256; } else if (this.select.rain) { this.weather = "Rain"; this.weatherTimer = 256; } else if (this.select.sun) { this.weather = "Sun"; this.weatherTimer = 256; } else if (this.select.sun2) { this.weather = "Sun"; this.weatherTimer = 256; } else if (this.select.hail) { this.weather = "Hail"; this.weatherTimer = 256; } else if (this.select.hail3) { this.weather = "Hail"; this.weatherTimer = 256; } }; this.abilityUsed = { "1": false, "2": false, "3": false, "4": false }; this.abilityOptions = { "1": [], "2": [], "3": [], "4": [] }; this.abilityQueue = { "1": {}, "2": {}, "3": {}, "4": {} }; this.dynamaxLegal = false; this.dynamaxes = { "1": { "mon": -1, "timer": 5 }, "2": { "mon": -1, "timer": 5 }, "3": { "mon": -1, "timer": 5 }, "4": { "mon": -1, "timer": 5 } }; this.monChosen = { "1": 0, "2": 0, "3": 0, "4": 0 }; var pSize = this.partySize = (this.tagBattle ? 2 : 3); this.partySizeP1 = pSize; if (this.tagBattle && this.oneOnTwo) { this.partySizeP1 = 4; } this.phase = "preview"; this.turn = -1; this.subturn = 0; this.finished = false; // sendAll("A battle between " + this.name1 + " and " + this.name2 + (npcDesc ? " (" + npcDesc + ")" : "") + " has started! " + (this.cantWatch ? "" : "[" + link("/watch " + this.name1, "Watch") + "]"), true); if (!this.postArgs || !this.postArgs.delayAnnounce) { this.announceBattle(); } var self = this; var teamPreview = function(name, team, opponent, ally, opponent2, size) { self.sendMessage(name, "Use /bat [Codes] to choose your team of " + size + " Pokémon! Example: " + toColor("/bat ADF", "blue") + " to choose Pokémon with code A, D and F. You can also use " + toColor("/bat pause", "blue") + " to pause and unpause the battle."); var out = [], p, codes = "ABCDEF"; for (var e = 0; e < team.length; e++) { p = team[e]; if (p.hp > 0) { out.push(pokeInfo.icon(p.id) + link("/bat " + codes[e], "[" + codes[e] + "]") + " " + poke(p.id) + (p.hp !== p.maxhp ? " (" + self.getHPColor(p.hp, p.maxhp) + ")" : "")); } } self.sendMessage(name, "Your team: " + out.join(" ")); if (ally) { out = []; for (var e = 0; e < ally.length; e++) { p = ally[e]; out.push(pokeInfo.icon(p.id) + poke(p.id)); } self.sendMessage(name, "Ally's team: " + out.join(" ")); } out = []; for (var e = 0; e < opponent.length; e++) { p = opponent[e]; out.push(pokeInfo.icon(p.id) + poke(p.id)); } self.sendMessage(name, "Opponent's team: " + out.join(" ")); if (opponent2) { out = []; for (var e = 0; e < opponent2.length; e++) { p = opponent2[e]; out.push(pokeInfo.icon(p.id) + poke(p.id)); } self.sendMessage(name, "Opponent's team: " + out.join(" ")); } }; if (this.tagBattle && this.oneOnTwo) { teamPreview(this.name1, this.team1, this.team2, null, this.team4, 4); if (!this.npcBattle) { teamPreview(this.name2, this.team2, this.team1, this.team4, null, 2); teamPreview(this.name4, this.team4, this.team1, this.team2, null, 2); } } else if (this.tagBattle) { teamPreview(this.name1, this.team1, this.team2, this.team3, this.team4, 2); if (!this.npcBattle) { teamPreview(this.name2, this.team2, this.team1, this.team4, this.team3, 2); teamPreview(this.name3, this.team3, this.team2, this.team1, this.team4, 2); teamPreview(this.name4, this.team4, this.team1, this.team2, this.team3, 2); } } else { teamPreview(this.name1, this.team1, this.team2, null, null, 3); if (!this.npcBattle) { teamPreview(this.name2, this.team2, this.team1, null, null, 3); } } } Battle2.prototype.announceBattle = function() { var npcDesc = this.npcDesc; if (this.tagBattle) { if (this.oneOnTwo) { safaribot.sendHtmlAll("A Tag Team Rotation Battle between " + this.name1 + " and " + this.name2 + " & " + this.name4 + (npcDesc ? " (" + npcDesc + ")" : "") + " has started! " + (this.cantWatch ? "" : "[" + link("/watch " + this.name1, "Watch") + "]"), safchan); } else { safaribot.sendHtmlAll("A Tag Team Rotation Battle between " + this.name1 + " & " + this.name3 + " and " + this.name2 + " & " + this.name4 + (npcDesc ? " (" + npcDesc + ")" : "") + " has started! " + (this.cantWatch ? "" : "[" + link("/watch " + this.name1, "Watch") + "]"), safchan); } } else { safaribot.sendHtmlAll("A Rotation Battle between " + this.name1 + " and " + this.name2 + (npcDesc ? " (" + npcDesc + ")" : "") + " has started! " + (this.cantWatch ? "" : "[" + link("/watch " + this.name1, "Watch") + "]"), safchan); } }; Battle2.prototype.loadAbility = function() { this.abilityData = { "Overgrow": { "require": ["Overgrow"], "ranges": [0, 0.2, 0.1, 0, 0], "cost": 3, "description": "Powerful Grass-type moves appear next turn." }, "Overgrow Awakening": { "require": ["Overgrow"], "ranges": [0, 0, 0.25, 0.1, 0], "cost": 5, "description": "Very powerful Grass-type moves appear next turn." }, "Overgrow Ascending": { "require": ["Overgrow"], "ranges": [0, 0, 0, 0.25, 0.18], "cost": 7, "description": "Extremely powerful Grass-type moves appear next turn." }, } }; Battle2.prototype.executeAbility = function(num, value) { this.abilityQueue[num+""][value] = 0; return; }; Battle2.prototype.getAbilitySet = function(num) { return []; var obj, out = []; for (var i in this.abilityData) { obj = this.abilityData[i]; for (var j = 0; j < this.abilityData.require.length; j++) { if (canHaveAbility(num, this.abilityData.require[j])) { out.push({ i: obj }) } } } return out; }; Battle2.prototype.giveAbility = function(team, range) { var obj, opt = {}, out = [], item; for (var i in team) { obj = team[i]; for (var j in obj.ability) { opt[j] = obj.ability[j].ranges[range]; } } if (chance(0.33 + (range * 0.1))) { item = randomSample(opt); out.push(item); } if (chance((range * 0.09) - 0.2)) { item = randomSample(opt); if (!out.hasOwnProperty(item)) { out.push(item); } } return out; }; Battle2.prototype.abilityActive = function(team, value, range) { return false; if (!(this.abilityQueue.hasOwnProperty(team+""))) { return false; } if (!(this.abilityQueue[team+""].hasOwnProperty(value))) { return false; } if ((this.abilityQueue[team+""][value] > range)) { delete this.abilityQueue[team+""][value]; return false; } return true; }; Battle2.prototype.showInfo = function(id) { var m = "", name = ""; if (this.postArgs && this.postArgs.index !== undefined) { this.sendMessage(id, ""); this.sendMessage(id, "Battle #" + (this.postArgs.index+1) + ""); } if (this.biasNPC) { if (this.biasNPC.length > 0) { m = "", name = this.name2; this.sendMessage(id, ""); for (var j in this.biasNPC) { switch (this.biasNPC[j]) { case "aggressive": m = name + " likes to attack."; break; case "stats": m = name + " likes stat-raising moves."; break; case "priority": m = name + " favors priority moves."; break; case "recoil": m = name + " favors recoil moves."; break; case "drain": m = name + " favors drain moves."; break; case "burnout": m = name + " favors high-risk moves."; break; case "effectiveness": m = name + " aims for super-effective moves."; break; case "critical": m = name + " favors critical-hit moves."; break; case "helpingHand": m = name + " likes to support their teammate."; break; case "reflect": m = name + " likes to set up Reflect."; break; case "lightscreen": m = name + " likes to set up Light Screen."; break; case "paralyze": m = name + " favors moves that Paralyze."; break; case "poison": m = name + " favors moves that Poison."; break; case "burn": m = name + " favors moves that Burn."; break; case "freeze": m = name + " favors moves that Freeze."; break; case "sleep": m = name + " favors moves that induce Sleep."; break; } this.sendMessage(id, toColor("+ " + m, "#3CB371")); } } if (this.biasNPC0 && this.biasNPC0.length > 0) { m = "", name = this.name1; this.sendMessage(id, ""); for (var j in this.biasNPC0) { switch (this.biasNPC0[j]) { case "aggressive": m = name + " likes to attack."; break; case "stats": m = name + " likes stat-raising moves."; break; case "priority": m = name + " favors priority moves."; break; case "recoil": m = name + " favors recoil moves."; break; case "drain": m = name + " favors drain moves."; break; case "burnout": m = name + " favors high-risk moves."; break; case "critical": m = name + " favors critical-hit moves."; break; case "effectiveness": m = name + " aims for super-effective moves."; break; case "helpingHand": m = name + " likes to support their teammate."; break; case "reflect": m = name + " likes to set up Reflect."; break; case "lightscreen": m = name + " likes to set up Light Screen."; break; case "paralyze": m = name + " favors moves that Paralyze."; break; case "poison": m = name + " favors moves that Poison."; break; case "burn": m = name + " favors moves that Burn."; break; case "freeze": m = name + " favors moves that Freeze."; break; case "sleep": m = name + " favors moves that induce Sleep."; break; } this.sendMessage(id, toColor("+ " + m, "#3CB371")); } } if (this.biasNPC2) { if (this.biasNPC2.length > 0) { m = "", name = this.name4; for (var j in this.biasNPC2) { switch (this.biasNPC2[j]) { case "aggressive": m = name + " likes to attack."; break; case "stats": m = name + " likes stat-raising moves."; break; case "priority": m = name + " favors priority moves."; break; case "recoil": m = name + " favors recoil moves."; break; case "drain": m = name + " favors drain moves."; break; case "burnout": m = name + " favors high-risk moves."; break; case "critical": m = name + " favors critical-hit moves."; break; case "helpingHand": m = name + " likes to support their teammate."; break; case "effectiveness": m = name + " aims for super-effective moves."; break; case "reflect": m = name + " likes to set up Reflect."; break; case "lightscreen": m = name + " likes to set up Light Screen."; break; case "paralyze": m = name + " favors moves that Paralyze."; break; case "poison": m = name + " favors moves that Poison."; break; case "burn": m = name + " favors moves that Burn."; break; case "freeze": m = name + " favors moves that Freeze."; break; case "sleep": m = name + " favors moves that induce Sleep."; break; } this.sendMessage(id, toColor("+ " + m, "#3CB371")); } } } this.sendMessage(id, ""); } if (this.select) { var sorted = []; for (var j in this.select) { m = safari.getFieldConditionDescription(j, this.select); if (m !== "") { sorted.push(m); } } if (sorted.length > 0) { var self = this; sorted.sort().forEach(function(e) { self.sendMessage(id, toColor("- " + e, "#DC143C")); }); } this.sendMessage(id, ""); } if (this.select2) { var sorted = []; for (var j in this.select2) { m = safari.getFieldConditionDescription(j, this.select2); if (m !== "") { sorted.push(m); } } if (sorted.length > 0) { var self = this; sorted.sort().forEach(function(e) { self.sendMessage(id, toColor("- " + e, "#7d34eb")); }); } this.sendMessage(id, ""); } }; Battle2.prototype.nextTurn = function() { if (this.phase !== "preview" && !this.canPickMoves && this.npcBattle && !this.paused && this.totalPauseTime < this.pauseLimit) { if (!isPlaying(this.name1)) { this.sendToViewers(toColor("The battle was paused since {0} left the channel! (Battle can only remain paused for {1})".format(this.name1, timeString(this.pauseLimit - this.totalPauseTime)), "crimson")); this.paused = true; this.pendingPause = false; return; } else if (this.pendingPause) { this.sendToViewers(toColor("The battle has been paused! Note: The battle will automatically be unpaused after {0}.".format(timeString(this.pauseLimit - this.totalPauseTime)), "crimson")); this.paused = true; this.pendingPause = false; return; } } if (this.phase === "preview") { this.subturn++; if (this.fullNPC && this.subturn >= 2) { this.subturn = 10; } if (this.subturn === 10) { this.phase = "battle"; this.turn++; var fillTeam = function(picked, team, size) { var out = picked.concat(); var rest = [0, 1, 2, 3, 4, 5].filter(function(x) { return ((!out.contains(x) && (!out.contains(x+""))) && team.length > x && team[x].hp > 0); }).shuffle(); while (out.length < size && rest.length) { out.push(rest.shift()); } return out; }; var smartFillTeam = function(team, foeTeam, size, inver, select, select2, npcFavorite) { var out = [], options = {}, moves, powers, a, n, p, q, e, t1, t2, id; for (a = 0; a < team.length; a++) { id = parseInt(team[a].id, 10); q = getBST(id); moves = team[a].types; powers = team[a].movepowers; /*for (var t in foeTeam) { t1 = type1(parseInt(foeTeam[t].id, 10)); t2 = type2(parseInt(foeTeam[t].id, 10)); for (var m in moves) { n = (powers[m] ? powers[m] : 1); p = (moves[m] * n); e = safari.checkEffective(m, "???", t1, t2, "???", inver, select, select2); if (e > 1) { q += ((n * e)/3); } } }*/ q += (300 * Math.random()); if (npcFavorite) { if (npcFavorite.contains(id)) { q += 150; } } options["" + a] = q; } var out2 = Object.keys(options).sort(function(a, b){ return options[b] - options[a]; }).slice(0, 3); for (var k in out2) { out.push(parseInt(out2[k], 10)); } out = out.slice(0, 3); return out; } if (this.fullNPC) { this.p1PickedTeam = smartFillTeam(this.team1, this.team2, this.partySizeP1, (this.select && this.select.inverted), this.select, this.select2); } if (this.npcBattle) { this.p2PickedTeam = smartFillTeam(this.team2, this.team1, this.partySize, (this.select && this.select.inverted), this.select, this.select2, this.npcFavorite); } if (this.tagBattle) { if (this.p1PickedTeam.length < this.partySizeP1) { this.p1PickedTeam = fillTeam(this.p1PickedTeam, this.team1, this.partySizeP1); } if (this.p2PickedTeam.length < this.partySize) { this.p2PickedTeam = fillTeam(this.p2PickedTeam, this.team2, this.partySize); } if (!this.oneOnTwo) { if (this.p3PickedTeam.length < this.partySize) { this.p3PickedTeam = fillTeam(this.p3PickedTeam, this.team3, this.partySize); } } if (this.p4PickedTeam.length < this.partySize) { this.p4PickedTeam = fillTeam(this.p4PickedTeam, this.team4, this.partySize); } } else { if (this.p1PickedTeam.length < this.partySize) { this.p1PickedTeam = fillTeam(this.p1PickedTeam, this.team1, this.partySize); } if (this.p2PickedTeam.length < this.partySize) { this.p2PickedTeam = fillTeam(this.p2PickedTeam, this.team2, this.partySize); } } var self = this; this.team1 = this.p1PickedTeam.map(function(x) { return self.team1[x]; }); this.team2 = this.p2PickedTeam.map(function(x) { return self.team2[x]; }); if (this.tagBattle) { if (!this.oneOnTwo) { this.team3 = this.p3PickedTeam.map(function(x) { return self.team3[x]; }); } this.team4 = this.p4PickedTeam.map(function(x) { return self.team4[x]; }); } this.sendToViewers("Preparations complete, battle will start soon!"); if (this.postArgs && this.postArgs.delayAnnounce) { this.announceBattle(); } this.subturn = 6; } return; } this.subturn++; var self = this; if (this.subturn % 8 === 0) { if ((this.turn === 0) && (this.select)) { if (this.select.hugePower) { var minbst = 1000; for (i = 0; i < this.team2.length; i++) { if (getBST(this.team2[i].id) < minbst) { minbst = getBST(this.team2[i].id); } } for (i = 0; i < this.team2.length; i++) { if (getBST(this.team2[i].id) <= minbst) { this.team2[i].stats["atk"] *= 2; this.sendToViewers(poke(this.team2[i].id) + "'s Huge Power activates!"); break; } } } if (this.select.researcher) { for (i = 0; i < this.team2.length; i++) { for (var j in this.team2[i].stats) { this.team2[i].stats[j] *= 2; } } } if (this.select.balloon) { for (i = 0; i < this.team2.length; i++) { this.team2[i].item.balloon = true; } } if (this.select.hpboost) { for (i = 0; i < this.team2.length; i++) { this.team2[i].stats["hp"] = Math.floor(this.team2[i].stats["hp"] * 1.33); this.team2[i].maxhp = Math.floor(this.team2[i].maxhp * 1.33); this.team2[i].hp = Math.floor(this.team2[i].hp * 1.33); } } if (this.select.intimidate) { for (i = 0; i < this.team1.length; i++) { this.team1[i].boosts["atk"] = -1 } } if (this.select.fortress) { for (i = 0; i < this.team2.length; i++) { this.team2[i].boosts["atk"] = -2 this.team2[i].boosts["satk"] = -2 this.team2[i].boosts["def"] = 3 this.team2[i].boosts["sdef"] = 3 } } if (this.select.autopara) { for (i = 0; i < this.team1.length; i++) { if (!(hasType(this.team1[i].id, "Electric"))) { this.team1[i].condition = "paralyzed"; } } } if (this.select.autopoison) { for (i = 0; i < this.team1.length; i++) { if ((!(hasType(this.team1[i].id, "Steel"))) && (!(hasType(this.team1[i].id, "Poison")))) { this.team1[i].condition = "poison"; } } } if ((this.select.nerfBestStat) || (this.select2 && this.select2.nerfBestStat)) { var highStat; for (i = 0; i < this.team1.length; i++) { highStat = -1; for (var j in this.team1[i].stats) { if (this.team1[i].stats[j] >= highStat) { highStat = this.team1[i].stats[j]; } } for (var j in this.team1[i].stats) { if (this.team1[i].stats[j] >= highStat) { this.team1[i].boosts[j] = -1; } } } for (i = 0; i < this.team2.length; i++) { highStat = -1; for (var j in this.team2[i].stats) { if (this.team2[i].stats[j] >= highStat) { highStat = this.team2[i].stats[j]; } } for (var j in this.team2[i].stats) { if (this.team2[i].stats[j] >= highStat) { this.team2[i].boosts[j] = -1; } } } } } if ((this.turn === 0) && (this.select2)) { if (this.select2.hugePower) { var minbst = 1000; for (i = 0; i < this.team1.length; i++) { if (getBST(this.team1[i].id) < minbst) { minbst = getBST(this.team1[i].id); } } for (i = 0; i < this.team1.length; i++) { if (getBST(this.team1[i].id) <= minbst) { this.team1[i].stats["atk"] *= 2; this.sendToViewers(poke(this.team1[i].id) + "'s Huge Power activates!"); break; } } } if (this.select2.nerfBestStat) { var highStat; for (i = 0; i < this.team1.length; i++) { highStat = -1; for (var j in this.team1[i].stats) { if (this.team1[i].stats[j] >= highStat) { highStat = this.team1[i].stats[j]; } } for (var j in this.team1[i].stats) { if (this.team1[i].stats[j] >= highStat) { this.team1[i].boosts[j] = -1; } } } for (i = 0; i < this.team2.length; i++) { highStat = -1; for (var j in this.team2[i].stats) { if (this.team2[i].stats[j] >= highStat) { highStat = this.team2[i].stats[j]; } } for (var j in this.team2[i].stats) { if (this.team2[i].stats[j] >= highStat) { this.team2[i].boosts[j] = -1; } } } } } if (!this.fullNPC && this.npcBattle && this.turn === 0) { // start of battle var restoredHealth = 0; var partyFullHealth = this.originalTeam1.filter(function(e) { return e.hp < e.maxhp }).length === 0; if (!partyFullHealth) { var finaleSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "finale"); if (finaleSkill) { for (i = 0; i < this.originalTeam1.length; i++) { if (this.originalTeam1[i].hp <= 0) { // if fainted, don't heal continue; } restoredHealth = Math.round(this.originalTeam1[i].maxhp * finaleSkill.rate / 100); this.originalTeam1[i].hp = Math.min(this.originalTeam1[i].maxhp, this.originalTeam1[i].hp + restoredHealth); this.sendToViewers(toColor("[{0}'s {1}] {2}'s {3} restored {4} HP!".format(poke(finaleSkill.id), finaleSkill.name, this.name1, poke(this.originalTeam1[i].id), restoredHealth), "#55E")); } } } if (!((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { var flowerGiftSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "flowerGift"); if (flowerGiftSkill) { for (var i = 0; i < this.team1.length; i++) { this.team1[i].boosts["atk"] += flowerGiftSkill.rate; this.team1[i].boosts["sdef"] += flowerGiftSkill.rate; this.sendToViewers(toColor("[{0}'s {1}] {2}'s {3} had their ATK and SDEF increased by {4}!".format(poke(flowerGiftSkill.id), flowerGiftSkill.name, this.name1, poke(this.team1[i].id), plural(flowerGiftSkill.rate, "stage")), "#55E")); } } } } this.turn++; this.subturn = 0; this.p1MoveCodes = {}; this.p2MoveCodes = {}; this.p3MoveCodes = {}; this.p4MoveCodes = {}; for (var i in this.abilityQueue) { for (var j in this.abilityQueue[i]) { this.abilityQueue[i][j] += 1; } } var showBoosts = function(user, select, select2) { var out = [], val; for (var e in user.boosts) { val = user.boosts[e]; if (select && select.singlespecialstat && e == "satk") { e = "spc"; } if (select2 && select2.singlespecialstat && e == "satk") { e = "spc"; } if (val !== 0) { out.push(toColor(addSign(val) + " " + e.toUpperCase(), (val > 0 ? "darkgreen" : "red"))); } } return out.join(", "); }; var getConditionCode = function(condition) { switch (condition) { case "sleep": return toColor("SLP", "darkgray"); case "paralyzed": return toColor("PAR", "goldenrod"); case "burn": return toColor("BRN", "darkorange"); case "freeze": return toColor("FRZ", "lightblue"); case "poison": return toColor("PSN", "purple"); } }; var opponentInfo = function(team) { var out = [], info, e, p, b; for (e = 0; e < team.length; e++) { p = team[e]; if (p.hp === 0) { continue; } info = [self.getHPColor(p.hp, p.maxhp)]; if (p.condition !== "none") { info.push(getConditionCode(p.condition)); } b = showBoosts(p, this.select, this.select2); if (b) { info.push(b); } out.push(pokeInfo.icon(p.id) + info.join(" / ")); } return out.join(" || "); }; var prepareTeamForTurn = function(name, idnum, team, codesObj, abilityList, isNPC) { var abilityMsg = ""; var range = 1; abilityList = []; //abilityList = self.giveAbility(team, range); var j = 0; for (var i = 0; i < abilityList.length; i++) { j += 1; if (self.abilityData[abilityList[i]].desc !== "" && (!isNPC)) { self.sendMessage(name, link("/bat ability" + j, abilityList[i] + ": " + self.abilityData[abilityList[i]].desc)); } } var dynamaxed, which; which = (this.idnum1 == idnum ? 1 : 2); var e, t = 0, p, m, moves = [], codes = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]; for (e = 0; e < team.length; e++) { p = team[e]; dynamaxed = false if (p.hp > 0) { moves = []; if (this.dynamaxLegal) { var obj = this.dynamaxes[which+""]; var party; if (which == 1) { party = this.team1; } else { party = this.team2; } if (obj.mon !== -1) { if (obj.mon == e) { dynamaxed = true; } } } p.moves = self.generateMoves(e, p, name, idnum, dynamaxed); p.flinch = false; p.protect = false; p.helped = false; if (!p.lastPlayed) { p.lastPlayed2 = false; } else { p.lastPlayed2 = true; } p.lastPlayed = false; if (!isNPC) { self.sendMessage(name, pokeInfo.icon(p.id) + poke(p.id) + " | " + self.getHPColor(p.hp, p.maxhp) + (p.condition !== "none" ? " | " + getConditionCode(p.condition) : "") + " | " + showBoosts(p, this.select)); } for (m = 0; m < p.moves.length; m++) { moves.push(link("/bat " + codes[t], "[" + cap(codes[t]) + "]") + " " + self.translateMove(p.moves[m])); codesObj[codes[t]] = p.moves[m]; t++; } if (!isNPC) { self.sendMessage(name, "Moves: " + moves.join(" --- ")); } } } }; this.sendToViewers(toColor("TURN " + this.turn+"", "red")); var numFainted = 0, aliveIndex = 0; for (var i = 0; i < self.team1.length; i++) { if (self.team1[i].hp <= 0) { numFainted++; continue; } aliveIndex = i; } if (numFainted === 2 && !self.fullNPC && self.npcBattle) { var darkestDaySkill = safari.pokeSkillActivated(self.name1, self.team1[aliveIndex], "darkestDay"); if (darkestDaySkill) { var getStat = function(val) { return 2 * val + 5; }; self.team1[aliveIndex].id = darkestDaySkill.rate var newStats = getStats(darkestDaySkill.rate); self.team1[aliveIndex].maxhp = Math.round(newStats[0] * 2 + 110); self.team1[aliveIndex].stats.atk = getStat(newStats[1]); self.team1[aliveIndex].stats.def = getStat(newStats[2]); self.team1[aliveIndex].stats.satk = getStat(newStats[3]); self.team1[aliveIndex].stats.sdef = getStat(newStats[4]); self.team1[aliveIndex].stats.spe = getStat(newStats[5]); this.sendToViewers("[{0}'s {1}] {0} unleashed its stored energy and transformed into {2}!".format(poke(darkestDaySkill.id), darkestDaySkill.name, poke(self.team1[aliveIndex].id), poke(darkestDaySkill.rate))); } } this.sendToViewers(this.name2 + "'s Team: " + opponentInfo(this.team2), null, (this.npcBattle ? null : [this.name2.toLowerCase()])); if (this.tagBattle) { this.sendToViewers(this.name4 + "'s Team: " + opponentInfo(this.team4), null, (this.npcBattle ? null : [this.name4.toLowerCase()])); } this.sendToViewers(this.name1 + "'s Team: " + opponentInfo(this.team1), null, [this.name1.toLowerCase()]); if (this.tagBattle && (!this.oneOnTwo)) { this.sendToViewers(this.name3 + "'s Team: " + opponentInfo(this.team3), null, [this.name3.toLowerCase()]); } if (this.select || this.side2Field.reflect > 0 || this.side2Field.lightscreen > 0 || this.side1Field.reflect > 0 || this.side1Field.lightscreen > 0) { var m = []; if (this.select) { if (this.npcItems) { if (this.npcItems.hyper && this.npcItems.hyper > 0) { m.push(toColor(this.name2 + "'s Hyper Potions: " + this.npcItems.hyper + " ", "purple")); } if (this.npcItems.full && this.npcItems.full > 0) { m.push(toColor(this.name2 + "'s Full Restores: " + this.npcItems.full + " ", "purple")); } if (this.npcItems.revive && this.npcItems.revive > 0) { m.push(toColor(this.name2 + "'s Revives: " + this.npcItems.revive + " ", "purple")); } } if (this.npcItems2) { if (this.npcItems2.hyper && this.npcItems2.hyper > 0) { m.push(toColor(this.name4 + "'s Hyper Potions: " + this.npcItems2.hyper + " ", "purple")); } if (this.npcItems2.full && this.npcItems2.full > 0) { m.push(toColor(this.name4 + "'s Full Restores: " + this.npcItems2.full + " ", "purple")); } if (this.npcItems2.revive && this.npcItems2.revive > 0) { m.push(toColor(this.name4 + "'s Revives: " + this.npcItems2.revive + " ", "purple")); } } } if (this.side2Field.reflect > 0 || this.side2Field.lightscreen > 0) { if (this.side2Field.reflect > 0) { m.push(toColor("{0}'s Reflect ({1} remaining) ".format(this.name2 + (this.tagBattle ? " & " + this.name4 : ""), plural(this.side2Field.reflect, "turn")), "red")); } if (this.side2Field.lightscreen > 0) { m.push(toColor("{0}'s Light Screen ({1} remaining) ".format(this.name2 + (this.tagBattle ? " & " + this.name4 : ""), plural(this.side2Field.lightscreen, "turn")), "red")); } } if (this.side1Field.reflect > 0 || this.side1Field.lightscreen > 0) { if (this.side1Field.reflect > 0) { m.push(toColor("{0}'s Reflect ({1} remaining) ".format(this.name1 + (this.tagBattle && !this.oneOnTwo ? " & " + this.name3 : ""), plural(this.side1Field.reflect, "turn")), "blue")); } if (this.side1Field.lightscreen > 0) { m.push(toColor("{0}'s Light Screen ({1} remaining) ".format(this.name1 + (this.tagBattle && !this.oneOnTwo ? " & " + this.name3 : ""), plural(this.side1Field.lightscreen, "turn")), "blue")); } } if (m.length > 0) { this.sendToViewers(m.join(" || ")); } } this.player1Input = null; this.player2Input = null; this.player3Input = null; this.player4Input = null; this.canPickMoves = true; this.recharge1 = false; this.recharge2 = false; this.recharge3 = false; this.recharge4 = false; for (var a in this.team1) { if (this.team1[a].mustRecharge && this.team1[a].hp > 0) { this.recharge1 = true; break; } } for (var a in this.team2) { if (this.team2[a].mustRecharge && this.team2[a].hp > 0) { this.recharge2 = true; break; } } if (!(this.recharge1)) { this.sendMessage(this.name1, "Your team " + (this.tagBattle ? "(use /bat [Letter]:[1 or 2] to choose a move and target)" : "(use /bat [Letter] to choose a move)") + ":"); prepareTeamForTurn(this.name1, this.idnum1, this.team1, this.p1MoveCodes, this.abilityOptions["1"]); } else { this.player1Input = "---"; } if (!(this.recharge2)) { if (!this.npcBattle) { this.sendMessage(this.name2, "Your team " + (this.tagBattle ? "(use /bat [Letter]:[1 or 2] to choose a move and target)" : "(use /bat [Letter] to choose a move)") + ":"); } prepareTeamForTurn(this.name2, this.idnum2, this.team2, this.p2MoveCodes, this.abilityOptions["2"], this.npcBattle); } else { this.player2Input = "---"; } if (this.tagBattle) { if (!this.oneOnTwo) { for (var a in this.team3) { if (this.team3[a].mustRecharge && this.team3[a].hp > 0) { this.recharge3 = true; break; } } if (!(this.recharge3)) { this.sendMessage(this.name3, "Your team (use /bat [Letter]:[1 or 2] to choose a move and target):"); prepareTeamForTurn(this.name3, this.idnum3, this.team3, this.p3MoveCodes, this.abilityOptions["3"], this.npcBattle); } else { this.player3Input = "---"; } } for (var a in this.team4) { if (this.team4[a].mustRecharge && this.team4[a].hp > 0) { this.recharge4 = true; break; } } if (!(this.recharge4)) { if (!this.oneOnTwo) { this.sendMessage(this.name4, "Your team (use /bat [Letter]:[1 or 2] to choose a move and target):"); } prepareTeamForTurn(this.name4, this.idnum4, this.team4, this.p4MoveCodes, this.abilityOptions["4"], this.npcBattle); } else { this.player4Input = "---"; } } if (this.dynamaxLegal) { var obj, name, party; for (var i in this.dynamaxes) { obj = this.dynamaxes[i]; if (obj.timer > 0) { obj.timer -= 1; } obj.using = false; if (obj.timer == 0) { if (obj.mon !== -1) { if (parseInt(i, 10) == 1) { obj.timer = -2; name = this.name1; party = this.team1; } else { obj.timer = 5; name = this.name2; party = this.team2; } this.sendToViewers(toColor(name + "'s Dynamax " + poke(party[parseInt(obj.mon, 1)]) + " reverted!", "#cc6699")); obj.mon = 0; } else { obj.timer = -1; if (parseInt(i, 10) == 1) { name = this.name1; } else { name = this.name2; } this.sendToViewers(toColor(name + "'s Dynamax is ready!", "#cc6699")); obj.mon = 0; } } } } if (this.fullNPC) { this.sendToViewers(toColor("Turn is ending early since all players picked their moves!", "crimson")); this.subturn = 5; } else if (this.player1Input == "---" && this.npcBattle) { this.sendToViewers(toColor("Turn is ending early since all players picked their moves!", "crimson")); this.subturn = 5; } else if (this.player1Input == "---" && this.player2Input == "---") { this.sendToViewers(toColor("Turn is ending early since all players picked their moves!", "crimson")); this.subturn = 5; } } else if (this.subturn % 8 === 6) { var checkWin = function() { if (self.tagBattle) { self.player1Fainted = true; self.player2Fainted = true; self.player3Fainted = true; self.player4Fainted = true; for (i = 0; i < self.team1.length; i++) { if (self.team1[i].hp > 0) { self.player1Fainted = false; break; } } for (i = 0; i < self.team2.length; i++) { if (self.team2[i].hp > 0) { self.player2Fainted = false; break; } } if (!self.oneOnTwo) { for (i = 0; i < self.team3.length; i++) { if (self.team3[i].hp > 0) { self.player3Fainted = false; break; } } } else { var k = 0; for (i = 0; i < self.team1.length; i++) { if (self.team1[i].hp > 0) { k++; } } if (k > 1) { self.player3Fainted = false; } } for (i = 0; i < self.team4.length; i++) { if (self.team4[i].hp > 0) { self.player4Fainted = false; break; } } if (self.player1Fainted && self.player3Fainted && self.player2Fainted && self.player4Fainted) { self.finishBattle(sys.rand(1, 3)); return true; } if (self.player1Fainted && self.player3Fainted) { self.finishBattle(2); return true; } if (self.player2Fainted && self.player4Fainted) { self.finishBattle(1); return true; } return false; } else { var defeated = true; for (i = 0; i < self.team1.length; i++) { if (self.team1[i].hp > 0) { defeated = false; break; } } if (defeated) { self.finishBattle(2); return true; } defeated = true; for (i = 0; i < self.team2.length; i++) { if (self.team2[i].hp > 0) { defeated = false; break; } } if (defeated) { self.finishBattle(1); return true; } } }; if (this.player1Input === null && (!(this.fullNPC))) { this.player1Input = Object.keys(this.p1MoveCodes).random(); this.target1 = (chance(0.5) ? 2 : 4); } if (this.player3Input === null && (this.tagBattle && (this.oneOnTwo)) && (!this.player3Fainted)) { this.p3MoveCodes = {}; if (this.tagBattle && this.oneOnTwo) { for (var t in this.p1MoveCodes) { var rem = ["a", "b", "c"]; if (rem.contains(this.player1Input) && rem.contains(t)) { continue; } var rem = ["d", "e", "f"]; if (rem.contains(this.player1Input) && rem.contains(t)) { continue; } var rem = ["g", "h", "i"]; if (rem.contains(this.player1Input) && rem.contains(t)) { continue; } var rem = ["j", "k", "l"]; if (rem.contains(this.player1Input) && rem.contains(t)) { continue; } this.p3MoveCodes[t] = this.p1MoveCodes[t]; } } this.player3Input = Object.keys(this.p3MoveCodes).random(); this.target3 = (chance(0.5) ? 2 : 4); } var emptyMove = { "priority": -99 }; var emptyPoke = { "stats": { "spe": 0 }, "boosts": { "spe": 0 }, "item": {}, "hp": -1, "owner": "---" }; var move1 = emptyMove; var poke1 = emptyPoke; var spd1 = 0; if (this.fullNPC) { if (this.recharge1) { this.player1Input = "---"; } else { this.player1Input = this.chooseNPCMove(this.p1MoveCodes, this.team1, this.team2); move1 = this.p1MoveCodes[this.player1Input]; poke1 = this.team1[move1.ownerId]; } spd1 = this.getStatValue(poke1, "spe", (poke1.condition === "paralyzed" ? 0.5 : 1)); } else { if (!this.player1Fainted) { if (this.recharge1) { for (var a in this.team1) { if (this.team1[a].mustRecharge && this.team1[a].hp > 0) { poke1 = this.team1[a]; move1 = {"isRecharged": true, "priority": 0}; break; } } } else { move1 = this.p1MoveCodes[this.player1Input]; poke1 = this.team1[move1.ownerId]; if (this.npcBattle) { var oppHasFaintedMon = this.team2.filter(function(e) { return e.hp <= 0 }).length > 0 || (this.tagBattle ? this.team4.filter(function(e) { return e.hp <= 0 }).length > 0 : false); if (oppHasFaintedMon && (move1.type === "Water" || move1.type === "Dark") && move1.priority <= 0) { var battleBondSkill = safari.pokeSkillActivated(this.name1, poke1, "battleBond"); if (battleBondSkill) { move1.priority = battleBondSkill.rate; this.sendToViewers(toColor("[{0}'s {1}] {2}'s {3}-type attack had its priority increased to {4}!".format(poke(battleBondSkill.id), battleBondSkill.name, poke(poke1.id), move1.type, battleBondSkill.rate), "#55E")); } } } } spd1 = this.getStatValue(poke1, "spe", (poke1.condition === "paralyzed" ? 0.5 : 1)); } } if (this.npcBattle) { if (this.recharge2) { this.player2Input = "---"; } else { this.player2Input = this.chooseNPCMove(this.p2MoveCodes, this.team2, this.team1); } if (this.tagBattle) { if ((!this.oneOnTwo) && this.player3Input === null) { this.player3Input = Object.keys(this.p3MoveCodes).random(); this.target3 = (chance(0.5) ? 2 : 4); } this.player4Input = this.chooseNPCMove(this.p4MoveCodes, this.team4, this.team1); this.target2 = (chance(0.5) ? 1 : 3); this.target4 = (chance(0.5) ? 1 : 3); if (!(this.player2Fainted)) { if (this.p2MoveCodes[this.player2Input].target === "ALL" || this.p2MoveCodes[this.player2Input].target === "TEAM") { this.target2 = -1; } } if (!(this.player4Fainted)) { if (this.p4MoveCodes[this.player4Input].target === "ALL" || this.p4MoveCodes[this.player4Input].target === "TEAM") { this.target4 = -1; } } } if ((this.player2Input === false && (!(this.tagBattle) || this.player4Input === false))) { if (checkWin()) { return; } else { this.sendToViewers("Error during battle. Please contact a Safari Admin."); } } } else { if (this.player2Input === null) { this.player2Input = Object.keys(this.p2MoveCodes).random(); this.target2 = (chance(0.5) ? 1 : 3); } if (this.player3Input === null && this.tagBattle && (!this.oneOnTwo)) { this.player3Input = Object.keys(this.p3MoveCodes).random(); this.target3 = (chance(0.5) ? 2 : 4); } if (this.player4Input === null && this.tagBattle) { this.player4Input = Object.keys(this.p4MoveCodes).random(); this.target4 = (chance(0.5) ? 1 : 3); } } var move2 = emptyMove; var poke2 = emptyPoke; var spd2 = 0; if (!this.player2Fainted) { if (this.recharge2) { for (var a in this.team2) { if (this.team2[a].mustRecharge) { poke2 = this.team2[a]; move2 = {"isRecharged": true, "priority": 0}; break; } } } else { move2 = this.p2MoveCodes[this.player2Input]; poke2 = this.team2[move2.ownerId]; } spd2 = this.getStatValue(poke2, "spe", (poke2.condition === "paralyzed" ? 0.5 : 1)); } var move3 = emptyMove, move4 = emptyMove, poke3 = emptyPoke, poke4 = emptyPoke, spd3 = 0, spd4 = 0; if (this.tagBattle) { if (!this.player3Fainted) { if (this.oneOnTwo) { if (this.team1.filter(function(e) { return e.hp > 0 }).length > 1) { // ^ check that the player has at least 2 living pokemon to prevent errors in the event the player enters this tag battle with only 1 pokemon // this is only necessary for turn 1, once turn 1 is over checkWin is called below which will set player3Fainted to true for future turns move3 = this.p3MoveCodes[this.player3Input]; poke3 = this.team1[move3.ownerId]; } } else { move3 = this.p3MoveCodes[this.player3Input]; poke3 = this.team3[move3.ownerId]; } spd3 = this.getStatValue(poke3, "spe", (poke3.condition === "paralyzed" ? 0.5 : 1)); } if (!this.player4Fainted) { move4 = this.p4MoveCodes[this.player4Input]; poke4 = this.team4[move4.ownerId]; spd4 = this.getStatValue(poke4, "spe", (poke4.condition === "paralyzed" ? 0.5 : 1)); } } this.poke1 = poke1; this.poke2 = poke2; this.poke3 = poke3; this.poke4 = poke4; this.dynamaxUsed = { "1": false, "2": false } if (this.dynamaxLegal) { var obj, used, party; for (var i in this.dynamaxes) { obj = this.dynamaxes[i]; if (parseInt(i, 10) == 1) { party = this.team1; used = this.team1.indexOf(poke1); } else { party = this.team2; used = this.team2.indexOf(poke2); } if (obj.used) { obj.timer = 3; obj.mon = used; obj.used = false; this.dynamaxUsed[i+""] = true; this.sendToViewers(toColor(name + "'s " + poke(party[obj.mon]) + " Dynamaxed!", "#cc6699")); } } } var order = [], n1, n2, n3, n4; if (this.tagBattle) { var order2 = { "1": { "priority": move1.priority, "speed": spd1 }, "2": { "priority": move2.priority, "speed": spd2 }, "3": { "priority": move3.priority, "speed": spd3 }, "4": { "priority": move4.priority, "speed": spd4 } }; order = ["1", "2", "3", "4"]; order.sort(function(a, b) { if (order2[a].priority > order2[b].priority) { return -1; } else if (order2[a].priority < order2[b].priority) { return 1; } else if (order2[a].speed > order2[b].speed) { return -1; } else if (order2[a].speed < order2[b].speed) { return 1; } return (chance(0.5) ? 1 : -1); }); } else { if (move1.priority === move2.priority) { if (spd1 > spd2) { order = [1, 2]; } else if (spd1 < spd2) { order = [2, 1]; } else { order = [1, 2].shuffle(); } } else { order = move1.priority > move2.priority ? [1, 2] : [2, 1]; } } var o, i, id, user, target, move, out, name; var mColor = "#00A", sColor = "#55E"; this.sendToViewers(""); this.sendToViewers(toColor("TURN " + this.turn+"", "red")); if (this.npcBattle) { if (this.npcItems && (!this.player2Fainted)) { if (this.npcItems.revive > 0) { var foeMons = this.team2.concat(this.team4), mon; for (var i = 0; i < foeMons.length; i++) { mon = foeMons[i]; if (mon.hp <= 0) { this.sendToViewers(toColor(this.name2 + " used a Revive!", "purple")); this.sendToViewers(poke(mon.id) + " was revived!"); mon.hp = Math.round(mon.maxhp / 2); mon.condition = "none"; for (var a in mon.boosts) { mon.boosts[a] = 0; } this.npcItems.revive--; break; } } } else if (this.npcItems.full > 0 && (((this.poke2.maxhp / this.poke2.hp > 1.75) && this.poke2.condition !== "none") || this.poke2.maxhp / this.poke2.hp > 2.5)) { this.sendToViewers(toColor(this.name2 + " used a Full Restore!", "purple")); this.sendToViewers(poke(this.poke2.id) + " restored its HP and condition!"); this.poke2.hp = this.poke2.maxhp; this.poke2.condition = "none"; this.poke2.badlyPoisoned = 0; this.npcItems.full--; } else if (this.npcItems.hyper > 0 && ((this.poke2.maxhp - this.poke2.hp > 200) || this.poke2.hp < (50 + (50 * Math.random())))) { this.sendToViewers(toColor(this.name2 + " used a Hyper Potion!", "purple")); this.sendToViewers(poke(this.poke2.id) + " restored 200 HP!"); this.poke2.hp = (Math.min(this.poke2.hp + 200, this.poke2.maxhp)); this.npcItems.hyper--; } } if (this.npcItems2 && (!this.player4Fainted)) { if (this.npcItems2.hyper > 0 && ((this.poke4.maxhp - this.poke4.hp > 200) || this.poke4.hp < (50 + (50 * Math.random())))) { this.sendToViewers(toColor(this.name4 + " used a Hyper Potion!", "purple")); this.sendToViewers(poke(this.poke4.id) + " restored 200 HP!"); this.poke4.hp = (Math.min(this.poke4.hp + 200, this.poke4.maxhp)); this.npcItems2.hyper--; } if (this.npcItems2.full > 0 && (((this.poke4.maxhp / this.poke4.hp > 1.75) && this.poke4.condition !== "none") || this.poke4.maxhp / this.poke4.hp > 2.5)) { this.sendToViewers(toColor(this.name4 + " used a Full Restore!", "purple")); this.sendToViewers(poke(this.poke4.id) + " restored its HP and condition!"); this.poke4.hp = this.poke4.maxhp; this.poke4.condition = "none"; this.poke4.badlyPoisoned = 0; this.npcItems2.full--; } } } if (this.select) { if (this.select.retaliate) { poke1.retaliate = false; poke2.retaliate = false; poke3.retaliate = false; poke4.retaliate = false; } } for (o = 0; o < order.length; o++) { id = parseInt(order[o], 10); var isP1 = false, isP2 = false, isP3 = false, isP4 = false; user = { "owner": "Null", "id": 0, "hp": 1 } if (id === 1) { user = poke1; move = move1; isP1 = true; } else if (id === 2) { user = poke2; move = move2; isP2 = true; } else if (id === 3) { user = poke3; move = move3; isP3 = true; } else if (id === 4) { user = poke4; move = move4; isP4 = true; } if (this.tagBattle) { if (id === 1) { if (this.target1 === 2) { target = poke2; } else if (this.target1 === 4) { target = poke4; } else { target = move.target; //ALL or TEAM } if (this.target1 !== -1) { if (this.player2Fainted) { target = poke4; } else if (this.player4Fainted) { target = poke2; } } if (this.player4Fainted && this.player2Fainted) { continue; } isP1 = true; } else if (id === 2) { if (this.target2 === 1) { target = poke1; } else if (this.target2 === 3) { target = poke3; } else { target = move.target; //ALL or TEAM } if (this.target2 !== -1) { if (this.player1Fainted) { target = poke3; } else if (this.player3Fainted) { target = poke1; } } if (this.player1Fainted && this.player3Fainted) { continue; } isP2 = true; } if (id === 3) { if (this.target3 === 2) { target = poke2; } else if (this.target3 === 4) { target = poke4; } else { target = move.target; //ALL or TEAM } if (this.target3 !== -1) { if (this.player2Fainted) { target = poke4; } else if (this.player4Fainted) { target = poke2; } } if (this.player4Fainted && this.player2Fainted) { continue; } isP3 = true; } else if (id === 4) { if (this.target4 === 1) { target = poke1; } else if (this.target4 === 3) { target = poke3; } else { target = move.target; //ALL or TEAM } if (this.target4 !== -1) { if (this.player3Fainted) { target = poke1; } else if (this.player1Fainted) { target = poke3; } } if (this.player3Fainted && this.player1Fainted) { continue; } isP4 = true; } if (move.target) { if (move.target === "ALL" || move.target === "TEAM") { target = move.target; } } } else { target = id === 1 ? poke2 : poke1; } name = user.owner + "'s " + poke(user.id), dynamaxed = false; if (this.dynamaxLegal) { var obj = this.dynamaxes[id+""], party; if (id == 1) { party = this.team1; } else { party = this.team2; } if (party[obj.mon] == user.id) { dynamaxed = true; } } if (dynamaxed) { name = "Dynamax " + name; } if (isP1 && (this.player1Fainted)) { continue; } if (isP2 && (this.player2Fainted)) { continue; } if (isP3 && (this.player3Fainted)) { if (!this.oneOnTwo || (this.oneOnTwo && this.player1Fainted)) { continue; } } if (isP4 && (this.player4Fainted)) { continue; } if (!user.lastPlayed2) { if (this.select && this.select.switchHeal && (isP1 || isP3) && this.player1lastUsed !== null) { this.player1lastUsed.hp += this.player1lastUsed.maxhp * 0.07; this.player1lastUsed.hp = Math.min(this.player1lastUsed.maxhp, this.player1lastUsed.hp); this.sendToViewers(toColor(poke(this.player1lastUsed.id) + " regenerated some HP!", sColor)); } } if (user.hp > 0) { if (move.isRecharged === true) { this.sendToViewers(toColor(name + " must recharge!", sColor)); user.mustRecharge = false; user.lastPlayed = true; continue; } var side1ApplicableHazards = (!hasType(user.id, "Flying") && !user.lastPlayed2 && (this.side1Field.spikes || this.side1Field.dynamicweb)) || (!hasType(user.id, "Flying") && !user.lastPlayed2 && !hasType(user.id, "Steel") && this.side1Field.toxicspikes) || (!hasType(user.id, "Flying") && user.lastPlayed2 && this.side1Field.quicksand) || (!user.lastPlayed2 && (this.side1Field.stealthrock || this.side1Field.stealththunder || this.side1Field.stealthicicles)); var deltaStreamSkill; if (!this.fullNPC && this.npcBattle && side1ApplicableHazards && user.ownerID === this.idnum1) { deltaStreamSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "deltaStream"); } if (this.side1Field.spikes > 0 && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the spikes!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if ((!user.lastPlayed2) && (!hasType(user.id, "Flying"))) { this.sendToViewers(toColor(name + " is hurt by the spikes!", sColor)); user.hp = Math.max(Math.floor(user.hp - (this.side1Field.spikes * user.maxhp/24)), 1); } } if (this.side2Field.spikes > 0 && (isP2 || isP4)) { if ((!user.lastPlayed2) && (!hasType(user.id, "Flying"))) { this.sendToViewers(toColor(name + " is hurt by the spikes!", sColor)); user.hp = Math.max(Math.floor(user.hp - (this.side2Field.spikes * user.maxhp/24)), 1); } } if (this.side1Field.stealthrock && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the Stealth Rock!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if (!user.lastPlayed2) { var dmgPerc = 0.08; var mult = safari.checkEffective(["Rock"], [type1(user.id), type2(user.id)], this.select.inverted, this.select, this.select2); dmgPerc *= mult; if (dmgPerc > 0) { user.hp = Math.max(Math.floor(user.hp - (dmgPerc * user.maxhp)), 1); this.sendToViewers(toColor(name + " is hurt by the Stealth Rock!", sColor)); } } } if (this.side2Field.stealthrock && (isP2 || isP4)) { if (!user.lastPlayed2) { var dmgPerc = 0.08; var mult = safari.checkEffective(["Rock"], [type1(user.id), type2(user.id)], this.select.inverted, this.select, this.select2); dmgPerc *= mult; if (dmgPerc > 0) { user.hp = Math.max(Math.floor(user.hp - (dmgPerc * user.maxhp)), 1); this.sendToViewers(toColor(name + " is hurt by the Stealth Rock!", sColor)); } } } if (this.side1Field.stealththunder && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the Stealth Thunder!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if (!user.lastPlayed2) { var dmgPerc = 0.08; var mult = safari.checkEffective(["Electric"], [type1(user.id), type2(user.id)], this.select.inverted, this.select, this.select2); dmgPerc *= mult; if (dmgPerc > 0) { user.hp = Math.max(Math.floor(user.hp - (dmgPerc * user.maxhp)), 1); this.sendToViewers(toColor(name + " is hurt by the Stealth Thunder!", sColor)); } } } if (this.side1Field.stealthicicles && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the Stealth Icicles!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if (!user.lastPlayed2) { var dmgPerc = 0.08; var mult = safari.checkEffective(["Ice"], [type1(user.id), type2(user.id)], this.select.inverted, this.select, this.select2); dmgPerc *= mult; if (dmgPerc > 0) { user.hp = Math.max(Math.floor(user.hp - (dmgPerc * user.maxhp)), 1); this.sendToViewers(toColor(name + " is hurt by the Stealth Icicles!", sColor)); } } } if (this.side1Field.toxicspikes && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the Toxic Spikes!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if (!user.lastPlayed2) { if (hasType(user.id, "Poison") && (!hasType(user.id, "Flying"))) { this.sendToViewers(toColor("The Toxic Spikes were removed from the field!", sColor)); this.side1Field.toxicspikes = false; } else if ((!hasType(user.id, "Flying")) && ((!hasType(user.id, "Steel")) || (this.select && this.select.corrosion)) && user.condition === "none") { this.sendToViewers(toColor(name + " became poisoned!", sColor)); user.condition = "poison"; } } } if (this.side1Field.dynamicweb && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the web!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if ((!user.lastPlayed2) && (!hasType(user.id, "Flying"))) { this.sendToViewers(toColor(name + " was entangled in the web!", sColor)); var stat = ["def", "spe", "sdef"].random(); user.boosts[stat] -= 1; user.boosts[stat] = Math.min(6, Math.max(user.boosts[stat], -6)); this.sendToViewers(poke(user.id) + "'s " + self.statName(stat) + " -1!"); } } if (this.side1Field.quicksand && (isP1 || isP3)) { if (deltaStreamSkill) { this.sendToViewers(toColor("[{0}'s {1}] {2} was protected from the quicksand!".format(poke(deltaStreamSkill.id), deltaStreamSkill.name, poke(user.id)), sColor)); } else if ((user.lastPlayed2) && (!hasType(user.id, "Flying"))) { this.sendToViewers(toColor(name + " sinks further into the quicksand!", sColor)); var stat = ["def", "spe", "sdef"].random(); user.boosts[stat] -= 2; user.boosts[stat] = Math.min(6, Math.max(user.boosts[stat], -6)); this.sendToViewers(poke(user.id) + "'s " + self.statName(stat) + " -2!"); } } if (user.flinch) { this.sendToViewers(toColor(name + " flinched!", sColor)); continue; } if (user.confused) { if (chance(0.25)) { this.sendToViewers(toColor(name + " snapped out of its confusion!", sColor)); user.confused = false; } else { this.sendToViewers(toColor(name + " is confused!", sColor)); } } if (user.condition === "paralyzed" && chance(0.25)) { this.sendToViewers(toColor(pokeInfo.icon(user.id) + name + " is paralyzed! It can't move!", sColor)); continue; } if (user.condition === "sleep") { if (user.conditionDuration <= 0 || ((this.select && this.select.extendedSleep) ? chance(0.04) : chance(0.12))) { this.sendToViewers(toColor(name + " woke up!", sColor)); user.condition = "none"; } else { this.sendToViewers(toColor(pokeInfo.icon(user.id) + name + " is fast asleep!", sColor)); user.conditionDuration -= 1; continue; } } if ((!(move.dynamaxed) && (this.dynamaxLegal))) { var dynamaxUsed; if (isP1) { dynamaxUsed = this.dynamaxUsed["1"]; } else { dynamaxUsed = this.dynamaxUsed["2"]; } if (dynamaxUsed == true) { var newMove = this.dynamaxMove(move.type); move = newMove; } } if (user.condition === "freeze") { if (move.type === "Fire" && chance(0.5)) { this.sendToViewers(toColor(name + " thawed out due to the flames!", sColor)); user.condition = "none"; } else if (move.type === "Water" && (this.select && this.select.scald)) { this.sendToViewers(toColor(name + " thawed out due to the boiling water!", sColor)); user.condition = "none"; } else if (chance(0.25)) { this.sendToViewers(toColor(name + " thawed out!", sColor)); user.condition = "none"; } else { this.sendToViewers(toColor(pokeInfo.icon(user.id) + name + " is frozen solid!", sColor)); continue; } } if (user.confused && chance(0.5)) { this.sendToViewers(toColor(pokeInfo.icon(user.id) + name + " attacked itself in its confusion!", sColor)); var dmg = ((this.getStatValue(user, "atk") * 60) / this.getStatValue(user, "def")); dmg = Math.min(Math.round(dmg * (this.side1Field.reflect > 0 ? 0.5 : 1) * (0.85 + (Math.random() * 0.15))), user.hp); dmg = Math.max(1, dmg); user.hp -= dmg; this.sendToViewers(name + " lost " + dmg + " HP!"); if (user.hp <= 0) { this.sendToViewers("" + name + " fainted!"); } continue; } this.sendToViewers(pokeInfo.icon(user.id)); if (this.tagBattle) { if (target === "ALL" || target === "TEAM" ) { this.sendToViewers(toColor(name + " attacks! [Effect: " + this.translateMove(move) + "]", mColor)); } else { this.sendToViewers(toColor(name + " attacks! [Effect: " + this.translateMove(move) + "]", mColor)); } } else { this.sendToViewers(toColor(name + " attacks! [Effect: " + this.translateMove(move) + "]", mColor)); } out = this.attack(user, target, move, isP1, isP2, isP3, isP4); if (out.length === 0) { this.sendToViewers(toColor("But nothing happened!", sColor)); } while (out.length) { this.sendToViewers(toColor(out.shift(), sColor)); } if (checkWin()) { return; } } } if (!this.usedProtect1) { this.protectCount1 = 0; } this.usedProtect1 = false; if (!this.usedProtect2) { this.protectCount2 = 0; } this.usedProtect2 = false; if (!this.usedProtect3) { this.protectCount3 = 0; } this.usedProtect3 = false; if (!this.usedProtect4) { this.protectCount4 = 0; } this.usedProtect4 = false; if (checkWin()) { return; } //TODO: Make it so Player2 doesn't have the advantage in situations where both players lose by poison/burn at the same time if (!this.player1Fainted) { this.checkCondition(poke1); this.player1lastUsed = poke1; } if (this.tagBattle && (!this.player3Fainted)) { this.checkCondition(poke3); } if (checkWin()) { return; } if (!this.player2Fainted) { this.checkCondition(poke2, true); } if (this.tagBattle && (!this.player4Fainted)) { this.checkCondition(poke4, true); } if (checkWin()) { return; } if (this.select && this.select.moonblast && this.turn === this.selectData.moonblastTimer) { this.sendToViewers("The Moonblast strikes the field!"); this.selectData.moonblastTimer += (3 + Math.floor(4 * Math.random())); var dmg, typeMultiplier; if (poke1 && (poke1.hp > 0) && (!(poke1.protect))) { dmg = ((150 * 95) / this.getStatValue(poke1, "sdef")); typeMultiplier = safari.checkEffective(["Fairy"], [type1(poke1.id), type2(poke1.id)]); dmg = Math.round(dmg * typeMultiplier * (this.side1Field.lightscreen > 0 ? 0.5 : 1) * (0.85 + (Math.random() * 0.15))); dmg = Math.max(1, dmg); if (dmg > poke1.hp) { dmg = poke1.hp; } poke1.hp -= dmg; this.sendToViewers(poke(poke1.id) + " lost " + dmg + " HP!"); if (poke1.hp <= 0) { this.sendToViewers("" + poke(poke1.id) + " fainted!"); } } if (poke2 && (poke2.hp > 0) && (!(poke2.protect))) { dmg = ((150 * 95) / this.getStatValue(poke2, "sdef")); typeMultiplier = safari.checkEffective(["Fairy"], [type1(poke2.id), type2(poke2.id)]); dmg = Math.round(dmg * typeMultiplier * (this.side2Field.lightscreen > 0 ? 0.5 : 1) * (0.85 + (Math.random() * 0.15))); dmg = Math.max(1, dmg); if (dmg > poke2.hp) { dmg = poke2.hp; } poke2.hp -= dmg; this.sendToViewers(poke(poke2.id) + " lost " + dmg + " HP!"); if (poke2.hp <= 0) { this.sendToViewers("" + poke(poke2.id) + " fainted!"); } } } if ((this.select && this.select.irontail && this.turn === this.selectData.irontailTimer) || ((this.select && this.select.waterfall && this.turn === this.selectData.waterfallTimer)) || ((this.select2 && this.select2.waterfall && this.turn === this.selectData.waterfallTimer))) { if (this.select.irontail || (this.select2 && this.select2.irontail)) { this.sendToViewers("The Iron Tail strikes the field!"); this.selectData.irontailTimer += (3 + Math.floor(4 * Math.random())); } else { this.sendToViewers("The Waterfall strikes the field!"); this.selectData.waterfallTimer += (3 + Math.floor(4 * Math.random())); } var dmg, typeMultiplier, type, pow; type = (this.select.irontail ? "Steel": "Water"); pow = (this.select.irontail ? 100 : 80); if (this.select2 && this.select2.irontail) { type = "Steel"; pow = 100; } if (poke1 && (poke1.hp > 0) && (!(poke1.protect))) { dmg = ((150 * pow) / this.getStatValue(poke1, "def")); typeMultiplier = safari.checkEffective([type], [type1(poke1.id), type2(poke1.id)], this.select.inverted, this.select, this.select2); dmg = Math.round(dmg * typeMultiplier * (this.side1Field.reflect > 0 ? 0.5 : 1) * (0.85 + (Math.random() * 0.15))); dmg = Math.max(1, dmg); if (dmg > poke1.hp) { dmg = poke1.hp; } poke1.hp -= dmg; this.sendToViewers(poke(poke1.id) + " lost " + dmg + " HP!"); if (poke1.hp <= 0) { this.sendToViewers("" + poke(poke1.id) + " fainted!"); }else if (this.select2 && this.select2.brawler) { var stat = ["atk", "def", "spe", "satk", "sdef"].random(); poke1.boosts[stat] += 2; poke1.boosts[stat] = Math.min(6, Math.max(poke1.boosts[stat], -6)); this.sendToViewers(poke(poke1.id) + "'s " + self.statName(stat) + " +2!"); } } if (poke2 && (poke2.hp > 0) && (!(poke2.protect))) { dmg = ((150 * pow) / this.getStatValue(poke2, "def")); typeMultiplier = safari.checkEffective([type], [type1(poke2.id), type2(poke2.id)], this.select.inverted, this.select, this.select2); dmg = Math.round(dmg * typeMultiplier * (this.side2Field.reflect > 0 ? 0.5 : 1) * (0.85 + (Math.random() * 0.15))); dmg = Math.max(1, dmg); if (dmg > poke2.hp) { dmg = poke2.hp; } poke2.hp -= dmg; this.sendToViewers(poke(poke2.id) + " lost " + dmg + " HP!"); if (poke2.hp <= 0) { this.sendToViewers("" + poke(poke2.id) + " fainted!"); } else if (this.select.brawler) { var stat = ["atk", "def", "spe", "satk", "sdef"].random(); poke2.boosts[stat] += 2; poke2.boosts[stat] = Math.min(6, Math.max(poke2.boosts[stat], -6)); this.sendToViewers(poke(poke2.id) + "'s " + self.statName(stat) + " +2!"); } } } if (checkWin()) { return; } if ((this.turn % 5 === 0) && (this.select) && (this.turn !== 0)) { if (this.select.hazardsetter) { if (this.side1Field && this.side1Field.spikes < 3) { this.side1Field.spikes++; this.sendToViewers(this.name2 + " set up a layer of spikes!"); } } } if ((this.turn % 5 === 0) && (this.select) && (this.turn !== 0)) { if ((this.select.regenerator)) { var allMons = this.team2.concat(this.team4), mon, k, o; for (var i = 0; i < allMons.length; i++) { mon = allMons[i]; if (mon.hp == 0) { continue; } mon.hp = Math.min(mon.maxhp, Math.floor(mon.hp + (0.25 * mon.maxhp))); } this.sendToViewers("The foe's team regenerated!"); } } if ((this.turn % 7 === 0) && (this.select) && (this.turn !== 0)) { if ((this.select.topsyturvy) || (this.select2 && this.select2.topsyturvy)) { var allMons = this.team1.concat(this.team2).concat(this.team3).concat(this.team4), mon, k, o; for (var i = 0; i < allMons.length; i++) { mon = allMons[i]; for (o in mon.boosts) { k = mon.boosts[o] * -1; mon.boosts[o] = k; } } this.sendToViewers("All stat bonuses were flipped!"); } } if ((this.turn % 3 === 0) && (this.turn !== 0)) { if ((this.select && this.select.statshift) || (this.select2 && this.select2.statshift)) { var allMons = this.team1.concat(this.team2).concat(this.team3).concat(this.team4), mon, k, o; for (var i = 0; i < allMons.length; i++) { mon = allMons[i]; o = mon.boosts.spe; mon.boosts.spe = mon.boosts.sdef; mon.boosts.sdef = mon.boosts.satk; mon.boosts.satk = mon.boosts.def; mon.boosts.def = mon.boosts.atk; mon.boosts.atk = o; } this.sendToViewers("All stat bonuses were shifted!"); } if (!this.fullNPC && this.npcBattle && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { for (var i = 0 ; i < self.team1.length; i++) { // looping through b/c if there are multiple pokemon with this skill, each of them will activate it individually var pokeObj = self.team1[i]; var statList = Object.keys(pokeObj.stats); var pokeStats = statList.map(function(e) { return pokeObj.stats[e] }); var highestStat = Math.max.apply(null, pokeStats); var highestStatName = statList.reduce(function(a, b) { return pokeObj.stats[a] === highestStat ? a : b }); if (pokeObj.boosts[highestStatName] < 6) { var beastBoostSkill = safari.pokeSkillActivated(self.name1, pokeObj, "beastBoost"); if (beastBoostSkill) { pokeObj.boosts[highestStatName] += beastBoostSkill.rate; pokeObj.boosts[highestStatName] = Math.min(6, pokeObj.boosts[highestStatName]); this.sendToViewers(toColor("[{0}'s {1}] {2}'s {3} increased by {4}!".format(poke(beastBoostSkill.id), beastBoostSkill.name, poke(pokeObj.id), highestStatName.toUpperCase(), plural(beastBoostSkill.rate, "stage")), "#55E")); } } } } } if ((this.turn % 5 === 0) && (this.select) && (this.turn !== 0)) { if (this.select.shadowsBlade) { var hitMons = this.team1.concat(this.team3), mon, k, o; for (var i = 0; i < hitMons.length; i++) { mon = hitMons[i]; mon.hp = Math.ceil(mon.hp * 0.5); if (mon.hp < 0) { mon.hp = 1; } } this.sendToViewers("The shadows slice the Pokémons' remaining HP in half."); } } if ((this.turn % 6 === 0) && (this.select) && (this.turn !== 0)) { if (this.select.dancer) { var foeMons = this.team2.concat(this.team4), mon, k, o; for (var i = 0; i < foeMons.length; i++) { mon = foeMons[i]; mon.boosts["satk"] += 1; if (!this.select.singlespecialstat) { mon.boosts["sdef"] += 1; } mon.boosts["spe"] += 1; mon.boosts["satk"] = Math.min(mon.boosts["satk"], 6); mon.boosts["sdef"] = Math.min(mon.boosts["sdef"], 6); mon.boosts["spe"] = Math.min(mon.boosts["spe"], 6); } this.sendToViewers("The foe's Pokémon powered up!"); } if (this.select.dancer2) { var foeMons = this.team2.concat(this.team4), mon, k, o; for (var i = 0; i < foeMons.length; i++) { mon = foeMons[i]; mon.boosts["atk"] += 1; mon.boosts["spe"] += 1; mon.boosts["atk"] = Math.min(mon.boosts["atk"], 6); mon.boosts["spe"] = Math.min(mon.boosts["spe"], 6); } this.sendToViewers("The foe's Pokémon powered up!"); } } if ((this.turn % 6 === 0) && (this.select) && (this.turn !== 0)) { if (this.select.smasher) { var foeMons = this.team2.concat(this.team4), mon, k, o; for (var i = 0; i < foeMons.length; i++) { mon = foeMons[i]; mon.boosts["atk"] += 2; mon.boosts["satk"] += 2; mon.boosts["spe"] += 2; mon.boosts["def"] -= 1; mon.boosts["sdef"] -= 1; mon.boosts["satk"] = Math.min(mon.boosts["satk"], 6); mon.boosts["atk"] = Math.min(mon.boosts["atk"], 6); mon.boosts["spe"] = Math.min(mon.boosts["spe"], 6); mon.boosts["def"] = Math.max(mon.boosts["def"], -6); mon.boosts["sdef"] = Math.max(mon.boosts["sdef"], -6); } this.sendToViewers("The foe's Pokémon smashed their shells!"); } } if (!this.fullNPC && this.npcBattle && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { for (var i = 0 ; i < self.team1.length; i++) { // looping through b/c if there are multiple pokemon with this skill, each of them will activate it individually var pokeObj = self.team1[i]; if (pokeObj.damagingUsed > 0 && pokeObj.damagingUsed % 4 === 0 && pokeObj.boosts["atk"] < 6) { var intrepidSwordSkill = safari.pokeSkillActivated(self.name1, pokeObj, "intrepidSword"); if (intrepidSwordSkill) { pokeObj.boosts["atk"] += intrepidSwordSkill.rate; pokeObj.boosts["atk"] = Math.min(6, pokeObj.boosts["atk"]); pokeObj.damagingUsed = 0; this.sendToViewers(toColor("[{0}'s {1}] {2}'s ATK increased by {3}!".format(poke(intrepidSwordSkill.id), intrepidSwordSkill.name, poke(pokeObj.id), plural(intrepidSwordSkill.rate, "stage")), "#55E")); } } if (pokeObj.nonDamagingUsed > 0 && pokeObj.nonDamagingUsed % 4 === 0 && (pokeObj.boosts["def"] < 6 || pokeObj.boosts["sdef"] < 6)) { var dauntlessShieldSkill = safari.pokeSkillActivated(self.name1, pokeObj, "dauntlessShield"); if (dauntlessShieldSkill) { pokeObj.boosts["def"] += dauntlessShieldSkill.rate; pokeObj.boosts["sdef"] += dauntlessShieldSkill.rate; pokeObj.boosts["def"] = Math.min(6, pokeObj.boosts["def"]); pokeObj.boosts["sdef"] = Math.min(6, pokeObj.boosts["sdef"]); pokeObj.nonDamagingUsed = 0; this.sendToViewers(toColor("[{0}'s {1}] {2}'s DEF and SDEF increased by {3}!".format(poke(dauntlessShieldSkill.id), dauntlessShieldSkill.name, poke(pokeObj.id), plural(dauntlessShieldSkill.rate, "stage")), "#55E")); } } } } if (this.select && this.select.speedboost) { if (poke2 && (!poke2.fainted)) { poke2.boosts["spe"] += 1; poke2.boosts["spe"] = Math.min(poke2.boosts["spe"], 6); } if (this.tagBattle && poke4 && (!poke4.fainted)) { poke4.boosts["spe"] += 1; poke4.boosts["spe"] = Math.min(poke4.boosts["spe"], 6); } } if (this.select2 && this.select2.speedboost) { if (poke1 && (!poke1.fainted)) { poke1.boosts["spe"] += 1; poke1.boosts["spe"] = Math.min(poke1.boosts["spe"], 6); } } if (this.select) { if (this.select.slowStart && this.turn === 5) { this.sendToViewers(this.name2 + "'s Slow Start wore off!"); } } if (this.select2) { if (this.select2.slowStart && this.turn === 5) { this.sendToViewers(this.name1 + "'s Slow Start wore off!"); } } if (this.select) { if (this.select.lastStand && this.selectData.lastStandReady) { var foeMons = this.team2.concat(this.team4), mon, count = 0, remaining = null; for (var i = 0; i < foeMons.length; i++) { mon = foeMons[i]; if (mon.hp > 0) { count++; remaining = mon; } } if (count === 1 && (remaining !== null)) { this.selectData.lastStandReady = false; this.sendToViewers("The foe won't go down without a fight!"); remaining.boosts["atk"] += 2; remaining.boosts["satk"] += 2; remaining.boosts["spe"] += (this.select.trickRoom ? -2 : 2); remaining.boosts["satk"] = Math.min(remaining.boosts["satk"], 6); remaining.boosts["atk"] = Math.min(remaining.boosts["atk"], 6); remaining.boosts["spe"] = Math.min(remaining.boosts["spe"], 6); remaining.boosts["spe"] = Math.max(remaining.boosts["spe"], -6); } } } if (this.side1Field.reflect > 0) { this.side1Field.reflect--; if (this.side1Field.reflect <= 0) { this.side1Field.reflect = -1; this.sendToViewers(toColor("Reflect on " + this.name1 + "'s side wore off!", "purple")); } } if (this.side1Field.lightscreen > 0) { this.side1Field.lightscreen--; if (this.side1Field.lightscreen <= 0) { this.side1Field.lightscreen = -1; this.sendToViewers(toColor("Light Screen on " + this.name1 + "'s side wore off!", "purple")); } } if (this.side2Field.reflect > 0) { this.side2Field.reflect--; if (this.side2Field.reflect <= 0) { this.side2Field.reflect = -1; this.sendToViewers(toColor("Reflect on " + this.name2 + "'s side wore off!", "purple")); } } if (this.side2Field.lightscreen > 0) { this.side2Field.lightscreen--; if (this.side2Field.lightscreen <= 0) { this.side2Field.lightscreen = -1; this.sendToViewers(toColor("Light Screen on " + this.name2 + "'s side wore off!", "purple")); } } this.sendToViewers(""); this.canPickMoves = false; } }; Battle2.prototype.checkCondition = function(user, npc) { if (user.hp === 0) { return; } var name = user.owner + "'s " + poke(user.id); user.helped = false; user.lastPlayed = true; if ((this.select && this.select.shedSkin) && (npc) && chance(0.25)) { user.condition = "none"; this.sendToViewers(name + "'s condition was cleared!"); } if (user.condition === "burn") { user.hp -= Math.round(user.maxhp / 16); this.sendToViewers(name + " was hurt by its burn!"); } else if (user.condition === "poison") { if (user.badlyPoisoned > 0) { user.hp -= Math.round(user.maxhp * user.badlyPoisoned / 16); user.badlyPoisoned++; } else { user.hp -= Math.round(user.maxhp / 8); } this.sendToViewers(name + " was hurt by its poison!"); } if ((this.weather == "Sand") && ((!hasType(user.id, "Rock")) && (!hasType(user.id, "Steel")) && (!hasType(user.id, "Ground")))) { user.hp -= Math.round(user.maxhp / 16); this.sendToViewers(name + " was buffeted by the Sandstorm!"); } if ((this.select && this.weather == "Hail") && ((!hasType(user.id, "Ice")))) { user.hp -= Math.round(user.maxhp / 16); this.sendToViewers(name + " was buffeted by the Hail!"); } if (user.hp <= 0) { user.hp = 0; this.sendToViewers("" + name + " fainted!"); return; } if ((this.select && this.select.leftovers) && (npc)) { user.hp = Math.min(Math.round(user.hp + (user.maxhp / 16)), user.maxhp); this.sendToViewers(name + " restored some HP with its Leftovers!"); } if ((this.select2 && this.select2.leftovers) && ((!npc))) { user.hp = Math.min(Math.round(user.hp + (user.maxhp / 16)), user.maxhp); this.sendToViewers(name + " restored some HP with its Leftovers!"); } if (((this.select && this.select.grassyterrain) || (this.select2 && this.select2.grassyterrain)) && (!(hasType(user.id, "Flying")))) { user.hp = Math.min(Math.round(user.hp + (user.maxhp / 16)), user.maxhp); this.sendToViewers(name + " restored some HP from the Grassy Terrain!"); } if ((this.select && this.select.leechseed) && (canLearnMove(pokeInfo.species(user.id), 73))) { user.hp = Math.min(Math.round(user.hp + (user.maxhp / 8)), user.maxhp); this.sendToViewers(name + " grew from its seeds!"); } if (user.berry == "oran") { if (user.hp <= (user.maxhp * itemData.oran.rate)) { user.berry = ""; user.hp += itemData.oran.rate2; user.hp = Math.min(user.hp, user.maxhp); this.sendToViewers(name + " ate its Oran Berry and recovered 50 HP!"); var id = getAvatarOff(user.owner); if (id) { id.helds[user.index] = -1; if (!this.npcBattle && id.balls.oran < getCap("oran")) { id.balls.oran += 1; } safari.saveGame(id); } } } }; Battle2.prototype.inputMove = function(src, data) { var name = sys.name(src); var idnum = getAvatar(src).idnum; var isP1 = this.idnum1 === idnum; var isP2 = this.idnum2 === idnum; var isP3 = this.idnum3 === idnum; var isP4 = this.idnum4 === idnum; if (data.toLowerCase() == "info" && this.viewers.indexOf(name.toLowerCase()) !== -1) { this.showInfo(name); return; } if (!isP1 && (this.npcBattle || !isP2) && (this.npcBattle || !isP3) && (this.npcBattle || !isP4)) { this.sendMessage(name, "You cannot use this command!"); return; } if (["pause", "unpause"].contains(data.toLowerCase())) { if (!this.npcBattle) { this.sendMessage(name, "You can't pause a player vs player battle!"); return; } if (data.toLowerCase() === "pause") { if (!this.paused && this.totalPauseTime > this.pauseLimit) { this.sendMessage(name, "You can't pause this battle for any longer!"); return; } if (this.canPickMoves || this.phase === "preview") { if (this.pendingPause) { this.sendMessage(name, "The pending pause was cancelled!"); this.pendingPause = false; } else { this.sendMessage(name, "The battle will be paused at the next opportunity!"); this.pendingPause = true; } return; } this.paused = !this.paused; } else if (data.toLowerCase() === "unpause" && this.paused) { this.paused = false; } this.sendToViewers(toColor("The battle has been " + (this.paused ? "paused! Note: The battle will automatically be unpaused after {0}." : "unpaused! (Remaining pause time: {0})").format(timeString(this.pauseLimit - this.totalPauseTime)) + "", "crimson")); return; } if (this.phase === "preview") { var cmdData = data.toLowerCase().replace(/[^abcdef]/,"").split(""); var letters = { a: 0, b: 1, c: 2, d: 3, e:4, f: 5 }; var picked = [], p, i; var team = this.team1; if (isP2) { team = this.team2; } if (isP3) { team = this.team3; } if (isP4) { team = this.team4; } var size = ((isP1 && this.tagBattle && this.oneOnTwo) ? 4 : (this.tagBattle ? 2 : 3)); for (var e = 0; e < size && e < cmdData.length; e++) { p = cmdData[e]; i = letters[p]; if (!picked.contains(i)) { if (team.length > i && team[i].hp > 0) { picked.push(i); } } } if (picked.length > 0) { var pickedTeam; if (isP1) { pickedTeam = this.p1PickedTeam; } else if (isP2) { pickedTeam = this.p2PickedTeam; } else if (isP3) { pickedTeam = this.p3PickedTeam; } else if (isP4) { pickedTeam = this.p4PickedTeam; } if (picked.length !== size) { for (i = 0; i < picked.length; i++) { if (pickedTeam.contains(picked[i])) { pickedTeam.splice(pickedTeam.indexOf(picked[i]), 1); this.sendMessage(name, "You removed " + poke(team[picked[i]].id) + " from your selection!"); } else { pickedTeam.push(picked[i]); this.sendMessage(name, "You have chosen " + poke(team[picked[i]].id) + " for this battle!"); } } } else { for (i = 0; i < picked.length; i++) { pickedTeam.push(picked[i]); } this.sendMessage(name, "You chose a full team of " + readable(picked.map(function(e) { return poke(team[e].id) })) + " for this battle!"); } while (pickedTeam.length > size) { // must mutate array, dont reassign value of pickedTeam to a sliced copy or anything pickedTeam.shift(); } this.sendMessage(name, "Your current team selection: " + (pickedTeam.length > 0 ? pickedTeam.map(function(e) { return pokeInfo.icon(team[e].id) + poke(team[e].id) }).join(", ") : "None")); if (this.tagBattle && this.oneOnTwo) { if (this.p1PickedTeam.length === 4 && (this.npcBattle || (this.p2PickedTeam.length === 2 && this.p4PickedTeam.length === 2)) && this.subturn < 6) { this.sendToViewers(toColor("All players picked their Pokémon!", "crimson")); this.subturn = 8; } } else if (this.tagBattle) { if (this.p1PickedTeam.length === 2 && (this.npcBattle || (this.p2PickedTeam.length === 2 && this.p3PickedTeam.length === 2 && this.p4PickedTeam.length === 2)) && this.subturn < 6) { this.sendToViewers(toColor("All players picked their Pokémon!", "crimson")); this.subturn = 8; } } else { if (this.p1PickedTeam.length === Math.min(3, this.originalTeam1.length) && (this.npcBattle || this.p2PickedTeam.length === Math.min(3, this.originalTeam2.length)) && this.subturn < 6) { this.sendToViewers(toColor("All players picked their Pokémon!", "crimson")); this.subturn = 8; } } } else if (["cancel", "undo", "restart"].contains(data.toLowerCase())) { this.sendMessage(name, "Repick your Pokémon for this battle! Example: " + toColor("/bat ADF", "blue") + " to choose Pokémon with code A, D and F."); if (isP1) { this.p1PickedTeam = [] } else if (isP2) { this.p2PickedTeam = [] } else if (isP3) { this.p3PickedTeam = [] } else if (isP4) { this.p4PickedTeam = [] } } else { this.sendMessage(name, "Use /bat [Codes] to choose your Pokémon! Example: " + toColor("/bat ADF", "blue") + " to choose Pokémon with code A, D and F."); } } else { this.chooseMove(src, data); } }; Battle2.prototype.chooseMove = function(src, data) { var name = sys.name(src); if (!this.canPickMoves) { this.sendMessage(name, "Wait until the next turn to choose your move!"); return; } data = data.toLowerCase(); var idnum = getAvatar(src).idnum; var isP1 = this.idnum1 === idnum; var isP2 = this.idnum2 === idnum; var isP3 = this.idnum3 === idnum; var isP4 = this.idnum4 === idnum; if (this.tagBattle && this.oneOnTwo) { if (this.player1Input !== null) { isP3 = this.idnum1 === idnum; if (isP3) { isP1 = false; } } } var codeList; if (isP1) { if (!(this.recharge1)) { codeList = this.p1MoveCodes; } else { this.sendMessage(name, "You cannot choose moves!"); return; } } else if (isP2) { if (!this.recharge2) { codeList = this.p2MoveCodes; } else { this.sendMessage(name, "You cannot choose moves!"); return; } } else if (isP3) { if (!this.recharge3) { codeList = this.p3MoveCodes; } else { this.sendMessage(name, "You cannot choose moves!"); return; } } else if (isP4) { if (!this.recharge4) { codeList = this.p4MoveCodes; } else { this.sendMessage(name, "You cannot choose moves!"); return; } } var which = (isP1 ? 1 : (isP2 ? 2 : (isP3 ? 3 : 4))); var ab = this.abilityOptions[which+""]; if (ab.length > 0) { if (data == "ability1") { this.executeAbility(ab[0]); } else if (ab.length > 1) { if (data == "ability2") { this.executeAbility(ab[1]); } } } if (data == "dynamax" && this.dynamaxLegal) { var obj; if (isP1) { obj = this.dynamaxes["1"]; } else { obj = this.dynamaxes["2"]; } if (obj.mon !== 0) { this.sendMessage(name, "You cannot Dynamax more than one Pokémon!"); return; } if (obj.timer !== -1) { this.sendMessage(name, "You cannot Dynamax yet!"); return; } obj.using = true; obj.timer = 3; this.sendMessage(name, "You are activating your Dynamax this turn!"); } var aim = 0; if (this.tagBattle) { var cData = data.split(":"); data = cData[0]; aim = parseInt(cData[1], 10); } if (!codeList.hasOwnProperty(data)) { this.sendMessage(name, "This is not a valid move!"); return; } if (this.tagBattle) { var onlyOneTarget = false; if ( codeList[data].target === "ALL" || codeList[data].target === "TEAM" || (isP1 && (this.player2Fainted || this.player4Fainted)) || (isP2 && this.oneOnTwo) || (isP2 && (this.player1Fainted || this.player3Fainted)) || (isP3 && (this.player2Fainted || this.player4Fainted)) || (isP4 && this.oneOnTwo) || (isP4 && (this.player1Fainted || this.player3Fainted)) ) { onlyOneTarget = true; } if (![1, 2].contains(aim)) { if (onlyOneTarget) { aim = 1; } else { this.sendMessage(name, "Invalid target! Input a target with " + link("/bat " + data + ":1") + " or " + link("/bat " + data + ":2") + "."); return; } } } if (isP1) { this.player1Input = data; if (this.tagBattle && this.oneOnTwo) { for (var t in this.p1MoveCodes) { var rem = ["a", "b", "c"]; if (rem.contains(data) && rem.contains(t)) { continue; } var rem = ["d", "e", "f"]; if (rem.contains(data) && rem.contains(t)) { continue; } var rem = ["g", "h", "i"]; if (rem.contains(data) && rem.contains(t)) { continue; } var rem = ["j", "k", "l"]; if (rem.contains(data) && rem.contains(t)) { continue; } this.p3MoveCodes[t] = this.p1MoveCodes[t]; } } } else if (isP2) { this.player2Input = data; } else if (isP3) { this.player3Input = data; } else if (isP4) { this.player4Input = data; } if (["a","b","c"].contains(data)) { this.monChosen[which+""] = 1; } else if (["d","e","f"].contains(data)) { this.monChosen[which+""] = 2; } else if (["g","h","i"].contains(data)) { this.monChosen[which+""] = 3; } var move = codeList[data]; if (this.tagBattle) { var tar = 1; if ((move.target == "ALL") || (move.target == "TEAM")) { aim = -1; } else if ((aim === 1) && (isP1 || isP3)) { tar = 2; if (this.player2Fainted) { tar = 4; } } else if ((aim === 2) && (isP1 || isP3)) { tar = 4; if (this.player4Fainted) { tar = 2; } } else if ((aim === 1) && (isP2 || isP4)) { tar = 1; if (this.player1Fainted) { tar = 3; } } else if ((aim === 2) && (isP2 || isP4)) { tar = 3; if (this.player3Fainted) { tar = 1; } } if ((move.target == "ALL") || (move.target == "TEAM")) { tar = -1; } if (isP1) { this.target1 = tar; } if (isP2) { this.target2 = tar; } if (isP3) { this.target3 = tar; } if (isP4) { this.target4 = tar; } } this.sendMessage(name, toColor("You picked " + poke(move.owner) + "'s move " + data.toUpperCase() + ": " + this.translateMove(move), "crimson")); if (this.tagBattle && this.oneOnTwo) { if (this.player1Input !== null && (this.player3Input !== null || this.player3Fainted) && (this.npcBattle || this.player2Input !== null) && (this.npcBattle || this.player4Input !== null) && this.subturn < 5) { this.sendToViewers(toColor("Turn is ending early since all players picked their moves!", "crimson")); this.subturn = 5; } } else if (this.tagBattle) { if (this.player1Input !== null && (this.npcBattle || this.player2Input !== null) && (this.npcBattle || this.player3Input !== null) && (this.npcBattle || this.player4Input !== null) && this.subturn < 5) { this.sendToViewers(toColor("Turn is ending early since all players picked their moves!", "crimson")); this.subturn = 5; } } else if (this.player1Input !== null && (this.npcBattle || this.player2Input !== null) && this.subturn < 5) { this.sendToViewers(toColor("Turn is ending early since all players picked their moves!", "crimson")); this.subturn = 5; } }; Battle2.prototype.getHPColor = function(current, max) { var val = current/max; if (val < 0.2) { return toColor(current, "red") + "/" + max + " HP"; } else if (val < 0.5) { return toColor(current, "goldenrod") + "/" + max + " HP"; } else { return toColor(current, "darkgreen") + "/" + max + " HP"; } }; Battle2.prototype.getStatValue = function(user, stat, extraMod, crit) { if (this.select && this.select.singlespecialstat) { if (stat == "sdef") { stat = "satk"; } } else if (this.select2 && this.select2.singlespecialstat) { if (stat == "sdef") { stat = "satk"; } } if (this.select && this.select.powertrick) { if (stat == "atk") { stat = "def"; } else if (stat == "def") { stat = "atk"; } else if (stat == "satk") { stat = "sdef"; } else if (stat == "sdef") { stat = "satk"; } } if ((this.select && this.select.statjumble) || (this.select2 && this.select2.statjumble)) { stat = this.jumbledStats[stat]; } var base = user.stats[stat]; var val = user.boosts[stat]; if ((this.select && this.select.simple) || (this.select2 && this.select2.simple)) { val *= 2; } var boost = 1; if (val > 0) { boost = (2 + val) / 2; } else if (val < 0) { boost = 2 / (2 - val); } if (crit && ((crit > 0 && val < 0) || (crit < 0 && val > 0))) { boost = 1; } extraMod = extraMod || 1; if (this.select && this.select.thickPollen && stat == "spe" && (!((hasType(user.id, "Bug") || hasType(user.id, "Grass"))))) { base = 0.25 * base; } if (((this.select && this.select.sandBoostGround) || (this.select2 && this.select2.sandBoostGround)) && (stat == "spe") && hasType(user.id, "Ground")) { base = base * 2; } if (((this.select && this.weather == "Rain" && this.select.SwiftSwim && stat == "spe" && hasType(user.id, "Water")))) { base = base * 2; } if (this.select && this.select.trickRoom && stat == "spe") { return Math.round(10000 / (base * boost * extraMod)); } return Math.round(base * boost * extraMod); }; Battle2.prototype.damageCalc = function(user, move, target, typeMultiplier, targetSide, isP1, isP2, isP3, isP4) { var crit = ((this.select && this.select.shellArmor) ? false : (chance(0.0625 + (move.critical || 0)))); if (this.select && this.select.speedcrit && (!crit) && (!this.select.shellArmor)) { var mod = (this.getStatValue(user, "spe", 1) / this.getStatValue(target, "spe", 1)); if (mod > 1) { mod = Math.max(50, mod * 5); if (chance(mod * 0.01)) { crit = true; } } } var atk = move.category === "physical" ? this.getStatValue(user, "atk", 1, (crit ? 1 : 0)) : this.getStatValue(user, "satk", 1, (crit ? 1 : 0)); var def = move.category === "physical" ? this.getStatValue(target, "def", 1, (crit ? -1 : 0)) : this.getStatValue(target, "sdef", 1, (crit ? -1 : 0)); if (this.select && this.select.bodypress) { if (move.category == "physical") { atk = Math.max(this.getStatValue(user, "atk", 1, (crit ? 1 : 0)), this.getStatValue(user, "def", 1, (crit ? 1 : 0))); } } if (this.select && this.select.electroball) { if (move.category == "physical") { atk = Math.max(this.getStatValue(user, "atk", 1, (crit ? 1 : 0)), this.getStatValue(user, "spe", 1, (crit ? 1 : 0))); } } if (this.select && this.select.psystrike) { if (move.type == "Psychic") { def = Math.min(this.getStatValue(target, "def", 1, (crit ? 1 : 0)), this.getStatValue(target, "sdef", 1, (crit ? 1 : 0))); } } if (this.select && this.select.fairystrike) { if (move.type == "Fairy") { def = Math.min(this.getStatValue(target, "def", 1, (crit ? 1 : 0)), this.getStatValue(target, "atk", 1, (crit ? 1 : 0))); } } if (!this.fullNPC && this.npcBattle && targetSide !== 1) { var lowerDef = Math.min(this.getStatValue(target, "def", 1, (crit ? -1 : 0)), this.getStatValue(target, "sdef", 1, (crit ? -1 : 0))); if (def > lowerDef) { var resoluteSwordSkill = safari.pokeSkillActivated(this.name1, user, "resoluteSword"); if (resoluteSwordSkill) { def = lowerDef; this.sendToViewers(toColor("[{0}'s {1}] {2}'s attack swapped to the {3} category to target the opponent's lower defensive stat!".format(poke(resoluteSwordSkill.id), resoluteSwordSkill.name, poke(user.id), (move.category === "physical" ? "Special" : "Physical")), "#55E")); } } } var burn = user.condition === "burn" && move.category === "physical"; var dmg = atk * move.power / def; var rng = sys.rand(85, 100) / 100; var stab = hasType(user.id, move.type); if (!stab && this.select) { if (this.select.normalcy && move.type == "Normal") { stab = true; } if (this.select.draconian && move.type == "Dragon") { stab = true; } if (this.select.mechanical && move.type == "Steel") { stab = true; } } if (this.select) { if (this.select.weakSTAB) { stab = false; } if (this.select.classicTypes && ["Fairy", "Steel", "Dark"].contains(move.type)) { stab = false; } } var helped = (user.helped ? 1.5 : 1); var screen = ((!crit) && (!move.brickBreak) && ((targetSide === 1 && this.side1Field.reflect > 0 && move.category === "physical") || (targetSide === 2 && this.side2Field.reflect > 0 && move.category === "physical") || (targetSide === 1 && this.side1Field.lightscreen > 0 && move.category === "special") || (targetSide === 2 && this.side2Field.lightscreen > 0 && move.category === "special"))); if (screen && targetSide !== 1 && !this.fullNPC && this.npcBattle) { var distortionSkillList = ["distortionForce", "hyperspaceFury"]; for (var i = 0; i < distortionSkillList.length; i++) { distortionForceSkill = safari.pokeSkillActivated(this.name1, user, distortionSkillList[i]); if (distortionForceSkill) { screen = false; this.sendToViewers(toColor("[{0}'s {1}] {2}'s attack bypassed {3}'s screens!".format(poke(distortionForceSkill.id), distortionForceSkill.name, poke(user.id), poke(target.id)), "#55E")); break; } } } var bonus = 1; if (this.select) { bonus *= ((isP2 || isP4) && (this.select.boostType && this.select.boostType.contains(move.type)) ? 1.3 : 1); bonus *= ((isP1 || isP3) && (this.select.solidRock) && (typeMultiplier > 1) ? 0.75 : 1); bonus *= ((isP2 || isP4) && (this.select.expertBelt) && (typeMultiplier > 1) ? 1.2 : 1); bonus *= ((isP1 || isP3) && (this.select.heatproof) && (move.type === "Fire") ? 0.5 : 1); bonus *= ((isP1 || isP3) && (this.select.thickFat) && (move.type === "Fire" || move.type == "Ice") ? 0.5 : 1); bonus *= ((isP1 || isP3) && (this.select.furcoat) && (move.category === "physical") ? 0.5 : 1); bonus *= (hasType(target.id, "Rock") && (this.weather == "Sand") && (move.category === "special") ? 0.667 : 1); bonus *= ((this.select.sandBoostGround && (hasType(target.id, "Ground") && (this.weather == "Sand") && (move.category === "special"))) ? 0.667 : 1); bonus *= (move.type == "Fire" && (this.weather == "Sun") ? 1.5 : 1); bonus *= (move.type == "Water" && (this.weather == "Sun") ? 0.5 : 1); bonus *= (move.type == "Water" && (this.weather == "Rain") ? 1.5 : 1); bonus *= (move.type == "Fire" && (this.weather == "Rain") ? 0.5 : 1); bonus *= (move.type == "Grass" && ((this.select.grassyterrain) || (this.select2 && this.select2.grassyterrain)) ? 1.5 : 1); bonus *= (move.type == "Psychic" && (this.select.psychicterrain) ? 1.5 : 1); bonus *= (move.type == "Psychic" && (this.select2 && this.select2.psychicterrain) ? 1.5 : 1); bonus *= (move.type == "Electric" && (this.select.electricterrain) ? 1.5 : 1); bonus *= (move.type == "Dragon" && (this.select.mistyterrain) ? 0.5 : 1); bonus *= ((this.select.criticalDouble && crit) ? 1.33 : 1); bonus *= ((this.select.adaptability && stab) ? 1.33 : 1); bonus *= ((this.select.slowStart && (isP2 || isP4) && this.turn <= 5) ? 0.5 : 1); bonus *= ((this.select2 && this.select2.slowStart && (isP1 || isP3) && this.turn <= 5) ? 0.5 : 1); bonus *= ((this.select.retaliate2 && (isP1 || isP3) && (this.selectData.retaliate1)) ? 2 : 1); bonus *= ((this.select.retaliate2 && (isP2 || isP4) && (this.selectData.retaliate2)) ? 2 : 1); bonus *= ((this.select.multiscale && (isP1 || isP3) && (target.hp >= target.maxhp)) ? 0.5 : 1); bonus *= ((this.select.aurashield && (isP1 || isP3) && move.category == "special" && (target.hp >= target.maxhp)) ? 0.5 : 1); bonus *= ((this.select2 && this.select2.multiscale && (isP2 || isP4) && (target.hp >= target.maxhp)) ? 0.5 : 1); bonus *= ((this.select.dualscale && (target.hp >= target.maxhp)) ? 0.5 : 1); bonus *= ((this.select.dualscale && (user.hp >= user.maxhp)) ? 0.5 : 1); bonus *= ((this.select.overgrowblazetorrent && (user.hp <= (user.maxhp/3)) && ["Fire", "Grass", "Water"].contains(move.type) && (hasType(user.id, move.type))) ? 2 : 1); bonus *= ((this.select.reversal && (user.hp <= (user.maxhp/2))) ? (1.75 - (1.75 * user.hp/user.maxhp)) : 1); bonus *= ((this.select.brine && (target.hp <= (target.maxhp/2))) ? (1.75 - (1.75 * target.hp/target.maxhp)) : 1); bonus *= ((this.select.dragonslayer && move.type === "Fighting" && (hasType(target.id, "Dragon")) && (isP2 || isP4)) ? 2 : 1); bonus *= ((this.select.freezedry && move.type === "Ice" && (hasType(target.id, "Water")) && (isP2 || isP4)) ? 4 : 1); bonus *= ((this.select.overheated && move.type === "Fire" && (hasType(target.id, "Fire")) && (isP1 || isP3)) ? 4 : 1); bonus *= ((this.select.overheated && move.type === "Water" && (hasType(target.id, "Fire")) && (isP1 || isP3)) ? 0.25 : 1); bonus *= ((this.select.strongJaw && user.biteTypes.contains(move.type) && (move.category == "physical")) ? 1.5 : 1); bonus *= ((this.select.hex && target.condition !== "none") ? 2 : 1); bonus *= ((this.select.weightattack) && (move.type == "Grass" || move.type == "Fighting") && (getWeight(target) > 200) ? 1.2 : 1); bonus *= ((this.select.weightattack) && (move.type == "Grass" || move.type == "Fighting") && (getWeight(target) > 150) ? 1.2 : 1); bonus *= ((this.select.weightattack) && (move.type == "Grass" || move.type == "Fighting") && (getWeight(target) > 100) ? 1.2 : 1); bonus *= ((this.select.weightattack) && (move.type == "Grass" || move.type == "Fighting") && (getWeight(target) > 50) ? 1.2 : 1); bonus *= ((this.select.weightattack) && (move.type == "Grass" || move.type == "Fighting") && (getWeight(target) < 25) ? 0.8 : 1); bonus *= ((this.select.drainpunch && move.drain && move.drain > 0 && (canLearnMove(pokeInfo.species(user.id), 409))) ? 2 : 1); bonus *= ((this.select.skyBattle && (!((canLearnMove(pokeInfo.species(user.id), 19)) || (canLearnMove(pokeInfo.species(user.id), 340))))) ? 0.75 : 1); bonus *= ((this.select.skyBattle && (!((canLearnMove(pokeInfo.species(target.id), 19)) || (canLearnMove(pokeInfo.species(target.id), 340))))) ? 1.25 : 1); bonus *= ((this.select.arenaBattle && (!((canLearnMove(pokeInfo.species(user.id), 69))))) ? 0.75 : 1); bonus *= ((this.select.arenaBattle && (!((canLearnMove(pokeInfo.species(target.id), 69))))) ? 1.25 : 1); bonus *= ((this.select.blueBoost && (pokeColors.blue.contains(pokeInfo.species(user.id)))) ? 1.2 : 1); bonus *= ((this.select.pinkBoost && (pokeColors.pink.contains(pokeInfo.species(user.id)))) ? 1.2 : 1); if (isP2 || isP4) { bonus *= (this.select.analytic ? (1 + Math.max(this.selectData.analyticCount/6, 0)) : 1); } if (this.select.rollout) { if (this.selectData.rolloutUser == user.id && (isP1 || isP3)) { bonus *= (1 + (Math.min(this.selectData.rolloutCount, 6) / 12)); } else if (this.selectData.rolloutUser2 == user.id && (isP2 || isP4)) { bonus *= (1 + (Math.min(this.selectData.rolloutCount2, 6) / 12)); } } if (this.select.guts) { burn = false; if (user.condition !== "none" && move.category === "physical") { bonus *= 1.5; } } if (this.select.retaliate) { if (user.retaliate) { bonus *= 2; } } } var tname = target.owner + "'s " + poke(target.id); var dmg = Math.round(dmg * typeMultiplier * (stab ? 1.5 : 1) * (crit ? 1.5 : 1) * (burn ? 0.5 : 1) * (screen ? 0.5 : 1) * (user.crystal + 1) * bonus * rng * 0.84 * helped); this.crit = crit; return dmg; }; Battle2.prototype.attack = function(user, target, move, isP1, isP2, isP3, isP4) { var e, o, obj, desc, out = [], fainted = false, name; var wide = (target == "ALL" || target == "TEAM"); var tname; name = user.owner + "'s " + poke(user.id); if (wide) { tname = "All Pokémon on the field"; if (this.poke3.hp <= 0 && this.poke1.hp <= 0 && (user.ownerID === this.idnum2 || user.ownerID === this.idnum4)) { out.push("But there was no target remaining..."); return out; } if (this.poke2.hp <= 0 && this.poke4.hp <= 0 && (user.ownerID === this.idnum1 || user.ownerID === this.idnum3)) { out.push("But there was no target remaining..."); return out; } } else { if ((target.hp <= 0) && target.ownerID === this.idnum2) { target = this.poke4; } if ((target.hp <= 0) && target.ownerID === this.idnum4) { target = this.poke2; } if ((target.hp <= 0) && target.ownerID === this.idnum3 && this.idnum3 !== undefined) { target = this.poke1; } if ((target.hp <= 0) && target.ownerID === this.idnum1) { target = this.poke3; } if (target.hp <= 0 && (move.category !== "other")) { out.push("But there was no target remaining..."); return out; } tname = target.owner + "'s " + poke(target.id); } var party, oppparty; if (user.ownerID === this.idnum1) { party = this.team1; oppparty = this.team2; if (!wide) { if (target.ownerID == this.idnum4) { oppparty = this.team4; } } } else if (user.ownerID === this.idnum2) { party = this.team2; oppparty = this.team1; if (!wide) { if (target.ownerID == this.idnum3) { oppparty = this.team3; } } } else if (user.ownerID === this.idnum3) { party = this.team3; oppparty = this.team2; if (!wide) { if (target.ownerID == this.idnum4) { oppparty = this.team4; } } } else if (user.ownerID === this.idnum4) { party = this.team4; oppparty = this.team1; if (!wide) { if (target.ownerID == this.idnum3) { oppparty = this.team3; } } } var poke1 = this.poke1, poke2 = this.poke2, poke3 = this.poke3, poke4 = this.poke4; var isPlayerVsNPC = ((user.ownerID === this.idnum2 || user.ownerID === this.idnum4) && this.npcBattle); if (move.restore) { if (user.hp < user.maxhp) { var heal = Math.round(user.maxhp * move.restore); heal = Math.min(user.maxhp - user.hp, heal); user.hp += heal; out.push(name + " restored " + heal + " HP!"); if (!this.fullNPC && this.npcBattle && user.ownerID === this.idnum1) { var validMons = this.team1.filter(function(e) { return e.hp > 0 && e.index !== user.index }); if (validMons.length > 0) { var jungleHealingSkill = safari.pokeSkillActivated(this.name1, user, "jungleHealing"); if (jungleHealingSkill) { var spreadHeal = Math.ceil(heal * jungleHealingSkill.rate / 100); for (var i = 0; i < validMons.length; i++) { validMons[i].hp = Math.min(validMons[i].maxhp, validMons[i].hp + spreadHeal); out.push("[{0}'s {1}] {2}'s {3} restored {4} HP!".format(poke(jungleHealingSkill.id), jungleHealingSkill.name, this.name1, poke(validMons[i].id), spreadHeal)); } } } } } else { out.push(name + "'s HP is already full!"); } } // Refresh/Haze/Buff cannot be blocked by protect if it's Other move if (move.category === "other") { if (move.refresh) { switch (move.refresh) { case "self": user.condition = "none"; user.badlyPoisoned = 0; out.push(name + "'s status returned to normal!"); break; case "field": if (isP1 || isP3) { this.poke1.condition = "none"; this.poke3.condition = "none"; this.poke1.badlyPoisoned = 0; this.poke3.badlyPoisoned = 0; } else if (isP2 || isP4) { this.poke2.condition = "none"; this.poke4.condition = "none"; this.poke2.badlyPoisoned = 0; this.poke4.badlyPoisoned = 0; } out.push("Pokémon on " + name + "'s side of the field had their status returned to normal!"); break; case "party": for (e = 0; e < party.length; e++) { party[e].condition = "none"; party[e].badlyPoisoned = 0; } out.push(user.owner + "'s party's status returned to normal!"); break; } } if (move.helpingHand) { if (isP1) { this.poke3.helped = true; out.push(name + " is providing assistance!"); } else if (isP3) { this.poke1.helped = true; out.push(name + " is providing assistance!"); } else if (isP2) { this.poke4.helped = true; out.push(name + " is providing assistance!"); } else if (isP4) { this.poke2.helped = true; out.push(name + " is providing assistance!"); } } if (move.followMe) { if (isP1) { this.target2 = 1; this.target4 = 1; out.push(name + " became the center of attention!"); } else if (isP3) { this.target2 = 3; this.target4 = 3; out.push(name + " became the center of attention!"); } else if (isP2) { this.target1 = 2; this.target3 = 2; out.push(name + " became the center of attention!"); } else if (isP4) { this.target1 = 4; this.target3 = 4; out.push(name + " became the center of attention!"); } } if (move.reflect) { if (isP1 || isP3) { if (this.side1Field.reflect > 0) { out.push("Reflect was already in place!"); } else { this.side1Field.reflect = 5; out.push("Reflect was put up on " + user.owner + "'s side of the field!"); } } if (isP2 || isP4) { if (this.side2Field.reflect > 0) { out.push("Reflect was already in place!"); } else { this.side2Field.reflect = 5; if (this.select && this.select.lightClay) { this.side2Field.reflect = 8; } out.push("Reflect was put up on " + user.owner + "'s side of the field!"); } } } if (move.lightscreen) { if (isP1 || isP3) { if (this.side1Field.lightscreen > 0) { out.push("Light Screen was already in place!"); } else { this.side1Field.lightscreen = 5; out.push("Light Screen was put up on " + user.owner + "'s side of the field!"); } } if (isP2 || isP4) { if (this.side2Field.lightscreen > 0) { out.push("Light Screen was already in place!"); } else { this.side2Field.lightscreen = 5; if (this.select && this.select.lightClay) { this.side2Field.lightscreen = 8; } out.push("Light Screen was put up on " + user.owner + "'s side of the field!"); } } } if (move.haze) { obj = []; switch (move.haze) { case "self": obj.push(user); desc = name; out.push(desc + " stat changes were eliminated!"); break; case "field": obj.push(this.poke1, this.poke2, this.poke3, this.poke4); desc = "All Pokémon on the field's "; out.push(desc + " stat changes were eliminated!"); break; case "both": obj.push(user); desc = name; out.push(desc + " stat changes were eliminated!"); break; case "party": obj = obj.concat(party); desc = user.owner + "'s party"; out.push(desc + " stat changes were eliminated!"); break; case "oppparty": obj = obj.concat(oppparty); desc = target.owner + "'s party"; out.push(desc + " stat changes were eliminated!"); break; case "all": obj = obj.concat(party, oppparty); desc = "All"; out.push(desc + " stat changes were eliminated!"); break; } for (e = 0; e < obj.length; e++) { for (o in obj[e].boosts) { obj[e].boosts[o] = 0; } } } if (move.buff) { for (e = 0; e < move.buff.length; e++) { obj = move.buff[e]; if (chance(obj.buffChance)) { user.boosts[obj.buffStat] += obj.buff; user.boosts[obj.buffStat] = Math.min(6, Math.max(user.boosts[obj.buffStat], -6)); out.push(name + "'s " + this.statName(obj.buffStat) + " " + addSign(obj.buff) + "!"); } } } } var protectUses = 0; if (move.protect) { if (user.ownerID === this.idnum1) { protectUses = this.protectCount1; } else if (user.ownerID === this.idnum2) { protectUses = this.protectCount2; } else if (user.ownerID === this.idnum3) { protectUses = this.protectCount3; } else if (user.ownerID === this.idnum4) { protectUses = this.protectCount4; } protectUses++; if (chance(3/Math.pow(3, protectUses))) { user.protect = true; out.push(name + " protects itself!"); } else { out.push(name + "'s protection failed!"); protectUses = 0; } if (isP1) { this.protectCount1 = protectUses; this.usedProtect1 = true; } else if (isP2) { this.protectCount2 = protectUses; this.usedProtect2 = true; } else if (isP3) { this.protectCount3 = protectUses; this.usedProtect3 = true; } else if (isP4) { this.protectCount4 = protectUses; this.usedProtect4 = true; } } var inver = (this.select && this.select.inverted ? true : false); if (!wide) { var typeMultiplier = move.category !== "other" ? safari.checkEffective([move.type], [type1(target.id), type2(target.id)], inver, this.select, this.select2) : 0; var distortionForceSkill; if (move.type == "Ground" && target.item.balloon) { typeMultiplier = 0; } if (typeMultiplier > 0 && target.protect && !this.fullNPC && this.npcBattle && target.owner.toLowerCase() !== this.name1.toLowerCase()) { var distortionSkillList = ["distortionForce", "hyperspaceFury"]; for (var i = 0; i < distortionSkillList.length; i++) { distortionForceSkill = safari.pokeSkillActivated(this.name1, user, distortionSkillList[i]); if (distortionForceSkill) { target.protect = false; out.push("[{0}'s {1}] {2}'s attack bypassed {3}'s protection!".format(poke(distortionForceSkill.id), distortionForceSkill.name, poke(user.id), poke(target.id))); break; } } } if (target.protect) { if (out.length === 0) { out.push(tname + " protected itself!"); } return out; } } if (move.category !== "other") { var self = this; var dealDamage = function(user, move, target, typeMultiplier, targetSide, out, friendlyFire) { self.crit = false; if (typeMultiplier < 1 && !self.fullNPC && self.npcBattle && targetSide !== 1) { var judgmentSkill = safari.pokeSkillActivated(self.name1, user, "judgment"); if (judgmentSkill) { typeMultiplier = 1; out.push("[{0}'s {1}] {2}'s attack bypassed the type disadvantage!".format(poke(judgmentSkill.id), judgmentSkill.name, poke(user.id))); } } var dmg = self.damageCalc(user, move, target, typeMultiplier, targetSide, isP1, isP2, isP3, isP4); var sdmg = dmg; var dynamaxed = false; var recoilGrace = 0; var userSide = targetSide == 2 ? 1 : 2; if (this.dynamaxLegal) { var dynamaxObj = this.dynamaxes[targetSide+""], party; if (targetSide == 2) { party = this.team2; } else { party = this.team1; } if (dynamaxObj.mon !== -1) { if (party.indexOf(target) == dynamaxObj.mon) { dynamaxed = true; } } } if (dynamaxed) { dmg = Math.ceil(dmg * 0.25); } if (self.selectData && self.selectData.shieldHP > 0 && targetSide === 2) { dmg = Math.ceil(dmg * 0.5); sdmg = dmg; if (self.select.genesisshield) { dmg = Math.ceil(dmg * 0.75); } if (self.select.iceshield) { if (["Water", "Ice"].contains(move.type)) { self.selectData.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Ice Shield absorbed energy! [Shield HP: " + self.selectData.shieldHP + "]"); } else { if (move.type == "Fire") { sdmg *= 1.5; if (!self.fullNPC && self.npcBattle) { var antiShieldSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicFire"); if (antiShieldSkill) { sdmg *= (1 + antiShieldSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Fire-type attack dealt {3}% more damage to the Ice Shield!".format(poke(antiShieldSkill.id), antiShieldSkill.name, poke(user.id), antiShieldSkill.rate)); } } } self.selectData.shieldHP = Math.max(self.selectData.shieldHP - sdmg, 0); if (self.selectData.shieldHP > 0) { out.push("The foe's Ice Shield sustained damage! [Shield HP: " + self.selectData.shieldHP + "]"); } else { out.push("The foe's Ice Shield shattered!"); } } } if (self.select.sludgeshield) { if (["Poison", "Steel"].contains(move.type)) { self.selectData.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Sludge Shield absorbed energy! [Shield HP: " + self.selectData.shieldHP + "]"); } else { if (move.type == "Water") { sdmg *= 1.5; if (!self.fullNPC && self.npcBattle) { var antiShieldSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicWater"); if (antiShieldSkill) { sdmg *= (1 + antiShieldSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Water-type attack dealt {3}% more damage to the Sludge Shield!".format(poke(antiShieldSkill.id), antiShieldSkill.name, poke(user.id), antiShieldSkill.rate)); } } } self.selectData.shieldHP = Math.max(self.selectData.shieldHP - sdmg, 0); if (self.selectData.shieldHP > 0) { out.push("The foe's Sludge Shield sustained damage! [Shield HP: " + self.selectData.shieldHP + "]"); } else { out.push("The foe's Sludge Shield shattered!"); } } } if (self.select.metalshield) { if (["Rock", "Steel"].contains(move.type)) { self.selectData.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Metal Shield absorbed energy! [Shield HP: " + self.selectData.shieldHP + "]"); } else { if (move.type == "Fighting") { sdmg *= 1.5; if (!self.fullNPC && self.npcBattle) { var antiShieldSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicFighting"); if (antiShieldSkill) { sdmg *= (1 + antiShieldSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Fighting-type attack dealt {3}% more damage to the Metal Shield!".format(poke(antiShieldSkill.id), antiShieldSkill.name, poke(user.id), antiShieldSkill.rate)); } } } self.selectData.shieldHP = Math.max(self.selectData.shieldHP - sdmg, 0); if (self.selectData.shieldHP > 0) { out.push("The foe's Metal Shield sustained damage! [Shield HP: " + self.selectData.shieldHP + "]"); } else { out.push("The foe's Metal Shield shattered!"); } } } if (self.select.dracoshield) { if (["Dragon"].contains(move.type)) { self.selectData.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Draco Shield absorbed energy! [Shield HP: " + self.selectData.shieldHP + "]"); } else { if (move.type == "Fairy") { sdmg *= 1.5; if (!self.fullNPC && self.npcBattle) { var antiShieldSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicFairy"); if (antiShieldSkill) { sdmg *= (1 + antiShieldSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Fairy-type attack dealt {3}% more damage to the Draco Shield!".format(poke(antiShieldSkill.id), antiShieldSkill.name, poke(user.id), antiShieldSkill.rate)); } } } self.selectData.shieldHP = Math.max(self.selectData.shieldHP - sdmg, 0); if (self.selectData.shieldHP > 0) { out.push("The foe's Draco Shield sustained damage! [Shield HP: " + self.selectData.shieldHP + "]"); } else { out.push("The foe's Draco Shield shattered!"); } } } if (self.select.electroshield) { if (["Electric", "Ghost"].contains(move.type)) { self.selectData.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Electro Shield absorbed energy! [Shield HP: " + self.selectData.shieldHP + "]"); } else { if (move.type == "Ground") { sdmg *= 1.5; if (!self.fullNPC && self.npcBattle) { var antiShieldSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicGround"); if (antiShieldSkill) { sdmg *= (1 + antiShieldSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Ground-type attack dealt {3}% more damage to the Electro Shield!".format(poke(antiShieldSkill.id), antiShieldSkill.name, poke(user.id), antiShieldSkill.rate)); } } } self.selectData.shieldHP = Math.max(self.selectData.shieldHP - sdmg, 0); if (self.selectData.shieldHP > 0) { out.push("The foe's Electro Shield sustained damage! [Shield HP: " + self.selectData.shieldHP + "]"); } else { out.push("The foe's Electro Shield shattered!"); } } } if (self.select.genesisshield) { if (["Dragon"].contains(move.type)) { self.selectData.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Genesis Shield absorbed energy! [Shield HP: " + self.selectData.shieldHP + "]"); } else { if (move.type == "Psychic") { sdmg *= 1.5; if (!self.fullNPC && self.npcBattle) { var antiShieldSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicPsychic"); if (antiShieldSkill) { sdmg *= (1 + antiShieldSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Psychic-type attack dealt {3}% more damage to the Genesis Shield!".format(poke(antiShieldSkill.id), antiShieldSkill.name, poke(user.id), antiShieldSkill.rate)); } } } self.selectData.shieldHP = Math.max(self.selectData.shieldHP - sdmg, 0); if (self.selectData.shieldHP > 0) { out.push("The foe's Genesis Shield sustained damage! [Shield HP: " + self.selectData.shieldHP + "]"); } else { out.push("The foe's Genesis Shield shattered!"); } } } } if (self.select2 && self.selectData2 && self.selectData2.shieldHP > 0 && targetSide === 1) { dmg = Math.ceil(dmg * 0.5); sdmg = dmg; if (self.select2.genesisshield) { dmg = Math.ceil(dmg * 0.75); } if (self.select2.iceshield) { if (["Water", "Ice"].contains(move.type)) { self.selectData2.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Ice Shield absorbed energy! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { if (move.type == "Fire") { sdmg *= 1.5; } self.selectData2.shieldHP = Math.max(self.selectData2.shieldHP - sdmg, 0); if (self.selectData2.shieldHP > 0) { out.push("The foe's Ice Shield sustained damage! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { out.push("The foe's Ice Shield shattered!"); } } } if (self.select2.sludgeshield) { if (["Poison", "Steel"].contains(move.type)) { self.selectData2.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Sludge Shield absorbed energy! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { if (move.type == "Water") { sdmg *= 1.5; } self.selectData2.shieldHP = Math.max(self.selectData2.shieldHP - sdmg, 0); if (self.selectData2.shieldHP > 0) { out.push("The foe's Sludge Shield sustained damage! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { out.push("The foe's Sludge Shield shattered!"); } } } if (self.select2.dracoshield) { if (["Dragon"].contains(move.type)) { self.selectData2.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Draco Shield absorbed energy! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { if (move.type == "Fairy") { sdmg *= 1.5; } self.selectData2.shieldHP = Math.max(self.selectData2.shieldHP - sdmg, 0); if (self.selectData2.shieldHP > 0) { out.push("The foe's Draco Shield sustained damage! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { out.push("The foe's Draco Shield shattered!"); } } } if (self.select2.electroshield) { if (["Electric", "Ghost"].contains(move.type)) { self.selectData2.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Electro Shield absorbed energy! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { if (move.type == "Ground") { sdmg *= 1.5; } self.selectData2.shieldHP = Math.max(self.selectData2.shieldHP - sdmg, 0); if (self.selectData2.shieldHP > 0) { out.push("The foe's Electro Shield sustained damage! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { out.push("The foe's Electro Shield shattered!"); } } } if (self.select2.genesisshield) { if (["Dragon"].contains(move.type)) { self.selectData2.shieldHP += Math.floor(50 + (50 * Math.random())); out.push("The foe's Genesis Shield absorbed energy! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { if (move.type == "Psychic") { sdmg *= 1.5; } self.selectData2.shieldHP = Math.max(self.selectData2.shieldHP - sdmg, 0); if (self.selectData2.shieldHP > 0) { out.push("The foe's Genesis Shield sustained damage! [Shield HP: " + self.selectData2.shieldHP + "]"); } else { out.push("The foe's Genesis Shield shattered!"); } } } } if (!self.fullNPC && self.npcBattle && targetSide === 1) { if (typeMultiplier < 1) { var damageResistSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicSteel"); if (damageResistSkill) { dmg *= Math.ceil(1 - damageResistSkill.rate / 100); out.push("[{0}'s {1}] The damage of {2}'s attack was reduced by {3}%!".format(poke(damageResistSkill.id), damageResistSkill.name, poke(user.id), damageResistSkill.rate)); } } if (typeMultiplier > 1) { var prismArmorSkill = safari.pokeSkillActivated(self.name1, target, "prismArmor"); if (prismArmorSkill) { dmg *= Math.ceil(1 - prismArmorSkill.rate / 100); out.push("[{0}'s {1}] The damage of {2}'s attack was reduced by {3}%!".format(poke(prismArmorSkill.id), prismArmorSkill.name, poke(user.id), prismArmorSkill.rate)); } } } //safaribot.sendMessage(sys.id("ripper roo"), self.name1 + " && " + self.originalTeam1 + " && " + isPlayerVsNPC + " && " + move.type + " && " + targetSide, safchan); if (!self.fullNPC && self.npcBattle && targetSide !== 1 && !friendlyFire) { if (move.type === "Normal") { var normalSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicNormal"); if (normalSkill) { dmg *= Math.ceil(1 + normalSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Normal-type attack dealt {3}% more damage!".format(poke(normalSkill.id), normalSkill.name, poke(user.id), normalSkill.rate)); } } if (move.type === "Psychic" && (self.side1Field.reflect > 0 || self.side1Field.lightscreen > 0)) { var screenExtendSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicPsychic"); if (screenExtendSkill) { self.side1Field.reflect = (self.side1Field.reflect > 0 ? self.side1Field.reflect + screenExtendSkill.rate2 : 0); self.side1Field.lightscreen = (self.side1Field.lightscreen > 0 ? self.side1Field.lightscreen + screenExtendSkill.rate2 : 0); out.push("[{0}'s {1}] {2}'s Psychic-type attack extended the duration of your screens by {3}!".format(poke(screenExtendSkill.id), screenExtendSkill.name, poke(user.id), plural(screenExtendSkill.rate2, "turn"))); } } if (move.type === "Poison" && target.condition === "poison") { var statusPunishSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicPoison"); if (statusPunishSkill) { dmg *= Math.ceil(1 + statusPunishSkill.rate / 100); out.push("[{0}'s {1}] {2}'s {4}-type attack dealt {3}% more damage!".format(poke(statusPunishSkill.id), statusPunishSkill.name, poke(user.id), statusPunishSkill.rate, move.type)); } } if (move.type === "Electric" && target.condition === "paralyzed") { var statusPunishSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicElectric"); if (statusPunishSkill) { dmg *= Math.ceil(1 + statusPunishSkill.rate / 100); out.push("[{0}'s {1}] {2}'s {4}-type attack dealt {3}% more damage!".format(poke(statusPunishSkill.id), statusPunishSkill.name, poke(user.id), statusPunishSkill.rate, move.type)); } } if (move.type === "Ice" && target.condition === "freeze") { var statusPunishSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicIce"); if (statusPunishSkill) { dmg *= Math.ceil(1 + statusPunishSkill.rate / 100); out.push("[{0}'s {1}] {2}'s {4}-type attack dealt {3}% more damage!".format(poke(statusPunishSkill.id), statusPunishSkill.name, poke(user.id), statusPunishSkill.rate, move.type)); } } if (move.type === "Rock") { var validTargets = self.team2.filter(function(e) { return e.index !== target.index && e.hp > 0; }); if (validTargets.length > 0) { var splashDamageSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicRock"); if (splashDamageSkill) { var splashDamage = Math.ceil(dmg * splashDamageSkill.rate2 / 100); for (var i = 0; i < validTargets.length; i++) { validTargets[i].hp -= splashDamage; validTargets[i].hp = Math.max(1, validTargets[i].hp); // splash damage can't sink an opponent in the back, leave them at 1 hp at minimum out.push("[{0}'s {1}] {2}'s Rock-type attack dealt {3} damage to the opponent's {4}!".format(poke(splashDamageSkill.id), splashDamageSkill.name, poke(user.id), splashDamage, poke(validTargets[i].id))); } } } } if (move.type === "Bug") { var userPartyCount = self.team1.filter(function(e) { return e.hp > 0 }).length; var oppPartyCount = self.team2.filter(function(e) { return e.hp > 0 }).length; if (userPartyCount > oppPartyCount) { var swarmDamageSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicBug"); if (swarmDamageSkill) { var swarmDamage = 1 + 1 * (userPartyCount - oppPartyCount) * swarmDamageSkill.rate2 / 100; dmg = Math.ceil(dmg * swarmDamage); out.push("[{0}'s {1}] {2}'s Bug-type attack dealt {3}% more damage!".format(poke(swarmDamageSkill.id), swarmDamageSkill.name, poke(user.id), Math.round((swarmDamage - 1) * 100))); } } } if (move.type === "Dark") { var stats = ["atk", "def", "spe", "satk", "sdef"]; var hasPositiveStatChange = false, hasNegativeStatChange = false; for (var i = 0; i < stats.length; i++) { if (target.boosts[stats[i]] > 0) hasPositiveStatChange = true; else if (target.boosts[stats[i]] < 0) { hasNegativeStatChange = true; } } if (hasPositiveStatChange && !hasNegativeStatChange) { var statBoostPunishSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicDark"); if (statBoostPunishSkill) { dmg *= Math.ceil(1 + statBoostPunishSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Dark-type attack dealt {3}% more damage!".format(poke(statBoostPunishSkill.id), statBoostPunishSkill.name, poke(user.id), statBoostPunishSkill.rate)); } } } if (typeMultiplier < 1 && move.type === "Ghost") { var resistBypassSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicGhost"); if (resistBypassSkill) { dmg *= Math.ceil(1 + resistBypassSkill.rate / 100); out.push("[{0}'s {1}] {2}'s Ghost-type attack dealt {3}% more damage!".format(poke(resistBypassSkill.id), resistBypassSkill.name, poke(user.id), resistBypassSkill.rate)); } } if (typeMultiplier > 1) { var neuroforceSkill = safari.pokeSkillActivated(self.name1, user, "neuroforce"); if (neuroforceSkill) { dmg *= Math.ceil(1 + neuroforceSkill.rate / 100); out.push("[{0}'s {1}] {2}'s attack dealt {3}% more damage!".format(poke(neuroforceSkill.id), neuroforceSkill.name, poke(user.id), neuroforceSkill.rate)); } } if (move.recoil) { var lightOfRuinSkill = safari.pokeSkillActivated(self.name1, user, "lightOfRuin"); if (lightOfRuinSkill) { var dmgBefore = dmg; dmg *= Math.ceil(1 + lightOfRuinSkill.rate / 100); recoilGrace = dmg - dmgBefore; out.push("[{0}'s {1}] {2}'s attack dealt {3}% more damage!".format(poke(lightOfRuinSkill.id), lightOfRuinSkill.name, poke(user.id), lightOfRuinSkill.rate)); } } } dmg = Math.max(1, dmg); if (dmg > target.hp) { dmg = target.hp; } tname = target.owner + "'s " + poke(target.id); target.hp -= dmg; if (self.select && self.select.retaliate2) { if (isP1 || isP3) { self.selectData.retaliate1 = false; } else if (isP2 || isP4) { self.selectData.retaliate2 = false; } } out.push((typeMultiplier > 1 ? "It's super effective! " : (typeMultiplier < 1 ? "It's not very effective... " : "")) + (self.crit ? "A CRITICAL HIT! " : "") + tname + " loses " + dmg + " HP!"); if (target.hp <= 0) { if (isPlayerVsNPC && (!(self.fullNPC))) { if (chance((0.0125 * Math.random()) + (getCherished(target.id, self.name1.toLowerCase()) > 0 ? 0.01 : 0) + safari.hasCostumeSkill(getAvatarOff(self.name1), "kiai") ? 0.0125 : 0)) { target.hp = 1; out.push("" + tname + " endured the hit!"); } } if (target.berry == "miracle" && target.hp <= 0) { // check HP again, otherwise may consume berry even if hit was endured above target.hp = 1; out.push("" + tname + " ate its Miracle Berry and survived the hit!"); target.berry = ""; var id = getAvatarOff(target.owner); if (id) { id.helds[target.index] = -1; if (!self.npcBattle && id.balls.miracle < getCap("miracle")) { id.balls.miracle += 1; } safari.saveGame(id); } } if (target.hp <= 0) { target.hp = 0; fainted = true; if (target.mustRecharge) { target.mustRecharge = false; } out.push("" + tname + " fainted!"); if (self.select && self.select.retaliate2) { if (isP1 || isP3) { self.selectData.retaliate2 = true; } else { self.selectData.retaliate1 = true; } } if (!self.fullNPC && self.npcBattle && targetSide === 1) { if (!((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { for (var i = 0 ; i < self.team1.length; i++) { // looping through b/c if there are multiple pokemon with this skill, each of them will activate it individually if (self.team1[i].boosts["satk"] === 6) continue; var soulHeartSkill = safari.pokeSkillActivated(self.name1, self.team1[i], "soulHeart"); if (soulHeartSkill) { self.team1[i].boosts["satk"] += soulHeartSkill.rate; self.team1[i].boosts["satk"] = Math.min(6, self.team1[i].boosts["satk"]) out.push("[{0}'s {1}] {2}'s SATK increased by {3}!".format(poke(soulHeartSkill.id), soulHeartSkill.name, poke(self.team1[i].id), plural(soulHeartSkill.rate, "stage"))); } } } } } } else { if (target.item.balloon) { out.push(tname + "'s balloon popped!"); target.item.balloon = false; } if (!self.fullNPC && self.npcBattle && targetSide === 1 && !friendlyFire && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { if (typeMultiplier < 1) { var stats = ["atk", "def", "spe", "satk", "sdef"]; var validStats = []; for (var i = 0; i < stats.length; i++) { if (user.boosts[stats[i]] > -6) { validStats.push(stats[i]); } } if (validStats.length > 0) { var statSpiteSkill = safari.pokeSkillActivated(self.name1, self.originalTeam1, "basicGhost"); if (statSpiteSkill) { var randStat = validStats.shuffle().shift(); user.boosts[randStat] -= statSpiteSkill.rate2; user.boosts[randStat] = Math.max(-6, user.boosts[randStat]); out.push("[{0}'s {1}] {2}'s ineffective attack caused its {3} to drop by {4}!".format(poke(statSpiteSkill.id), statSpiteSkill.name, poke(user.id), randStat.toUpperCase(), plural(statSpiteSkill.rate2, "stage"))); } } } } } if (move.drain) { var placeholder = user.hp; var amt = dmg * move.drain; user.hp += Math.floor(amt); if (user.hp > user.maxhp) { user.hp = user.maxhp; } placeholder = (user.hp - placeholder); if (placeholder > 0) { out.push(name + " restored " + placeholder + " HP!"); } if (!self.fullNPC && self.npcBattle && targetSide !== 1 && !friendlyFire && placeholder > 0) { var validAllies = self.team1.filter(function(e) { return e.hp > 0 && e.hp < e.maxhp && e.index !== user.index }); // allies that have already fainted shouldn't get healing if (validAllies.length > 0) { var drainSplashSkill = safari.pokeSkillActivated(self.name1, user, "drainFlare"); if (drainSplashSkill) { for (var i = 0; i < validAllies.length; i++) { var healAmount = Math.ceil(placeholder * drainSplashSkill.rate / 100); validAllies[i].hp = Math.min(validAllies[i].hp + healAmount, validAllies[i].maxhp); out.push("[{0}'s {1}] {2}'s {3} restored {4} HP!".format(poke(drainSplashSkill.id), drainSplashSkill.name, self.name1, poke(validAllies[i].id), healAmount)); } } } } } if (move.recoil) { var placeholder = user.hp; user.hp -= Math.floor((dmg - recoilGrace) * 0.3333); if (user.hp <= 0) { user.hp = 0; } placeholder = (placeholder - user.hp); if (placeholder > 0) { out.push(name + " lost " + placeholder + " HP in recoil!"); } if (user.hp <= 0) { out.push("" + name + " fainted!"); if (user.mustRecharge) { user.mustRecharge = false; } } } if (move.recharge) { if (user.hp > 0) { user.mustRecharge = true; if (target.hp <= 0 && self.select && self.select.hyperBeamGlitch) { user.mustRecharge = false; } } } if (self.select) { if (self.select.harshWinds && (isP1 || isP3) && (move.type == "Flying")) { placeholder = Math.min(Math.floor(user.maxhp * 0.25), user.hp); user.hp -= placeholder; out.push(name + " lost " + placeholder + " HP from the harsh winds!"); } if (self.select.shellBurn && (isP1 || isP3) && (move.type == "Fire")) { placeholder = Math.min(Math.floor(user.maxhp * 0.25), user.hp); user.hp -= placeholder; out.push(name + " lost " + placeholder + " HP from the shell burn!"); } if (self.select.analytic && (isP2 || isP4)) { if ((self.selectData.analyticType1 !== "???" && hasType(target.id, self.selectData.analyticType1)) || (self.selectData.analyticType2 !== "???" && hasType(target.id, self.selectData.analyticType2))) { self.selectData.analyticCount++; out.push(name + " became better at analyzing " + poke(target.id) + "'s typing!"); } else { self.selectData.analyticCount = 0; out.push(name + " is trying to adapt to " + poke(target.id) + "'s typing!"); } self.selectData.analyticType1 = type1(target.id); self.selectData.analyticType2 = type2(target.id); } } if (self.select && self.select.sabotage) { if (typeMultiplier > 1) { var s = ["atk", "def", "satk", "sdef", "spe"].random(); user.boosts[s] -= 2; user.boosts[s] = Math.min(6, Math.max(user.boosts[s], -6)); out.push(name + "'s " + self.statName(s) + " -2!"); } } if (self.select && self.select.spectralThief) { var typeMultiplier = safari.checkEffective([move.type], [type1(target.id), type2(target.id)], inver, this.select, this.select2); if (typeMultiplier < 1) { var hold = {}; for (var b in user.boosts) { hold[b] = user.boosts[b]; user.boosts[b] = target.boosts[b]; } for (var b in hold) { target.boosts[b] = hold[b]; } out.push(tname + " stat changes were transfered to " + name + "!"); } } if (self.select) { if (self.select.chargebeam && (isP2 || isP4) && move.category == "special") { user.boosts["satk"] += 1; user.boosts["satk"] = Math.min(6, Math.max(user.boosts["satk"], -6)); out.push(name + "'s " + self.statName("satk") + " +1!"); } } if (self.select) { if (self.select.poweruppunch && (isP2 || isP4) && move.category == "physical") { user.boosts["atk"] += 1; user.boosts["atk"] = Math.min(6, Math.max(user.boosts["atk"], -6)); out.push(name + "'s " + self.statName("atk") + " +1!"); } } if (self.select2) { if (self.select2.chargebeam && (isP1 || isP3) && move.category == "special") { user.boosts["satk"] += 1; user.boosts["satk"] = Math.min(6, Math.max(user.boosts["satk"], -6)); out.push(name + "'s " + self.statName("satk") + " +1!"); } } if (self.select) { if (self.select.noncritexhaust) { if (!(self.crit)) { if (move.category == "physical") { user.boosts["atk"] -= 1; user.boosts["atk"] = Math.min(6, Math.max(user.boosts["atk"], -6)); out.push(name + "'s " + self.statName("atk") + " -1!"); } if (move.category == "special") { user.boosts["satk"] -= 1; user.boosts["satk"] = Math.min(6, Math.max(user.boosts["satk"], -6)); out.push(name + "'s " + self.statName("satk") + " -1!"); } } else { if (move.category == "physical" && user.boosts["atk"] < 0) { user.boosts["atk"] = Math.max(user.boosts["atk"], 0); out.push(name + "'s " + self.statName("atk") + " nerfs removed!"); } if (move.category == "special" && user.boosts["satk"] < 0) { user.boosts["satk"] = Math.max(user.boosts["satk"], 0); out.push(name + "'s " + self.statName("satk") + " nerfs removed!"); } } } } if (self.select2 && (!fainted)) { if (self.select2.brawler && (isP2 || isP4) && move.category === "physical") { var stat = ["atk", "def", "spe", "satk", "sdef"].random(); target.boosts[stat] += 2; target.boosts[stat] = Math.min(6, Math.max(target.boosts[stat], -6)); out.push(tname + "'s " + self.statName(stat) + " +2!"); } if (self.select2.inferno && chance(0.25) && target.condition === "none" && move.type === "Fire" && (!hasType(target.id, "Fire"))) { out.push(tname + " got burned!"); target.condition = "burn"; } if (self.select2.zapcannon && chance(0.5) && target.condition === "none" && move.type === "Electric" && (!hasType(target.id, "Electric"))) { out.push(tname + " was paralyzed!"); target.condition = "paralyzed"; } if (self.select2.toxic && chance(0.75) && target.condition === "none" && move.type === "Poison" && (!hasType(target.id, "Poison"))) { if (hasType(user.id, "Poison")) { target.badlyPoisoned = 1; out.push(tname + " was badly poisoned!"); target.condition = "poison"; } else { out.push(tname + " was poisoned!"); target.condition = "poison"; } } } if (self.select && (!fainted)) { if (self.select.brawler && (isP1 || isP3) && move.category === "physical") { var stat = ["atk", "def", "spe", "satk", "sdef"].random(); target.boosts[stat] += 2; target.boosts[stat] = Math.min(6, Math.max(target.boosts[stat], -6)); out.push(tname + "'s " + self.statName(stat) + " +2!"); } if (self.select.inferno && chance(0.25) && target.condition === "none" && move.type === "Fire" && (!hasType(target.id, "Fire"))) { out.push(tname + " got burned!"); target.condition = "burn"; } if (self.select.scald && chance(0.3) && target.condition === "none" && move.type === "Water" && (!hasType(target.id, "Fire"))) { out.push(tname + " got burned!"); target.condition = "burn"; } if (self.select.zapcannon && chance(0.5) && target.condition === "none" && move.type === "Electric" && (!hasType(target.id, "Electric"))) { out.push(tname + " was paralyzed!"); target.condition = "paralyzed"; } if (self.select.toxic && chance(0.75) && target.condition === "none" && move.type === "Poison" && (!hasType(target.id, "Poison"))) { if (hasType(user.id, "Poison")) { target.badlyPoisoned = 1; out.push(tname + " was badly poisoned!"); target.condition = "poison"; } else { out.push(tname + " was poisoned!"); target.condition = "poison"; } } if (self.select.chillSpecial && move.type === "Ice" && (!hasType(target.id, "Ice"))) { target.boosts["satk"] = Math.min(6, Math.max(target.boosts["satk"] - 1, -6)); out.push(tname + "'s Special Attack -1!"); target.boosts["spe"] = Math.min(6, Math.max(target.boosts["spe"] - 1, -6)); out.push(tname + "'s Speed -1!"); } if (self.select.chillPhysical && move.type === "Dragon" && (!hasType(target.id, "Dragon"))) { target.boosts["atk"] = Math.min(6, Math.max(target.boosts["atk"] - 1, -6)); out.push(tname + "'s Attack -1!"); target.boosts["spe"] = Math.min(6, Math.max(target.boosts["spe"] - 1, -6)); out.push(tname + "'s Speed -1!"); } if ((self.select.psyDrop || (self.select2 && self.select2.psyDrop)) && move.type === "Psychic" && chance(0.5)) { if (self.select.singlespecialstat) { target.boosts["satk"] = Math.min(6, Math.max(target.boosts["satk"] - 1, -6)); out.push(tname + "'s Special -1!"); } else { target.boosts["satk"] = Math.min(6, Math.max(target.boosts["satk"] - 1, -6)); out.push(tname + "'s Special Attack -1!"); target.boosts["sdef"] = Math.min(6, Math.max(target.boosts["sdef"] - 1, -6)); out.push(tname + "'s Special Defense -1!"); } } if (self.select.naturalcure && user.condition !== "none" && (move.type === "Water" || move.type === "Grass")) { out.push(name + " cured its status!"); user.condition = "none"; } } out = self.afterDamage(user, move, target, oppparty, true, targetSide, out); if (fainted && self.select && user.hp > 0) { if (self.select.frenzy) { user.hp = Math.min(user.maxhp, Math.floor(user.hp + (0.25 * user.maxhp))); out.push(name + "'s HP regenerated a little!"); } if (self.select.faintTrap) { user.boosts["def"] -= 1; user.boosts["def"] = Math.min(6, Math.max(user.boosts["def"], -6)); user.boosts["sdef"] -= 1; user.boosts["sdef"] = Math.min(6, Math.max(user.boosts["sdef"], -6)); user.boosts["spe"] -= 1; user.boosts["spe"] = Math.min(6, Math.max(user.boosts["spe"], -6)); out.push(name + "'s defenses and speed were cut!"); } if (self.select.grudge) { user.hp = Math.max(0, Math.floor(user.hp - (0.25 * user.maxhp))); out.push(name + "'s suffered from the KO!"); if (user.hp <= 0) { out.push("" + name + " fainted!"); } } } return out; } if (wide) { if (isP1) { var distortionForceSkill; var thousandArrowsSkill; typeMultiplier = safari.checkEffective([move.type], [type1(poke2.id), type2(poke2.id)], inver, this.select, this.select2); if (move.type == "Ground" && (poke2.item.balloon || (hasType(poke2.id, "Flying") && typeMultiplier === 0))) { typeMultiplier = 0; if (!this.fullNPC && this.npcBattle && !poke2.protect) { thousandArrowsSkill = safari.pokeSkillActivated(this.name1, user, "thousandArrows"); if (thousandArrowsSkill) { typeMultiplier = 2; out.push("[{0}'s {1}] {2}'s Ground-type attack struck the airborne {3}!".format(poke(thousandArrowsSkill.id), thousandArrowsSkill.name, poke(user.id), poke(poke2.id))); } } } if (typeMultiplier > 0 && poke2.protect && !this.fullNPC && this.npcBattle) { var distortionSkillList = ["distortionForce", "hyperspaceFury"]; for (var i = 0; i < distortionSkillList.length; i++) { distortionForceSkill = safari.pokeSkillActivated(this.name1, user, distortionSkillList[i]); if (distortionForceSkill) { poke2.protect = false; out.push("[{0}'s {1}] {2}'s attack bypassed {3}'s protection!".format(poke(distortionForceSkill.id), distortionForceSkill.name, poke(user.id), poke(poke2.id))); break; } } } if (typeMultiplier === 0 && poke2.id !== undefined) { out.push("It has no effect on " + poke2.owner + "'s " + poke(poke2.id) + "!"); } else if (poke2.protect) { out.push(poke2.owner + "'s " + poke(poke2.id) + " protected itself!"); } else if (poke2.hp > 0) { out = dealDamage(poke1, move, poke2, typeMultiplier, 2, out); } typeMultiplier = safari.checkEffective([move.type], [type1(poke4.id), type2(poke4.id)], inver, this.select, this.select2); if (move.type == "Ground" && (poke4.item.balloon || (hasType(poke4.id, "Flying") && typeMultiplier === 0))) { typeMultiplier = 0; if (!this.fullNPC && this.npcBattle && !poke4.protect) { thousandArrowsSkill = safari.pokeSkillActivated(this.name1, user, "thousandArrows"); if (thousandArrowsSkill) { typeMultiplier = 2; out.push("[{0}'s {1}] {2}'s Ground-type attack struck the airborne {3}!".format(poke(thousandArrowsSkill.id), thousandArrowsSkill.name, poke(user.id), poke(poke4.id))); } } } if (typeMultiplier > 0 && poke4.protect && !this.fullNPC && this.npcBattle && !distortionForceSkill) { // check distortion skill not already activated for poke2, if it is, don't activate again to prevent consuming 2 uses at once var distortionSkillList = ["distortionForce", "hyperspaceFury"]; for (var i = 0; i < distortionSkillList.length; i++) { distortionForceSkill = safari.pokeSkillActivated(this.name1, user, distortionSkillList[i]); if (distortionForceSkill) { poke4.protect = false; out.push("[{0}'s {1}] {2}'s attack bypassed {3}'s protection!".format(poke(distortionForceSkill.id), distortionForceSkill.name, poke(user.id), poke(poke4.id))); break; } } } if (typeMultiplier === 0 && poke4.id !== undefined) { out.push("It has no effect on " + poke4.owner + "'s " + poke(poke4.id) + "!"); } else if (poke4.protect) { out.push(poke4.owner + "'s " + poke(poke4.id) + " protected itself!"); } else if (poke4.hp > 0) { out = dealDamage(poke1, move, poke4, typeMultiplier, 2, out); } if (target !== "TEAM") { typeMultiplier = safari.checkEffective([move.type], [type1(poke3.id), type2(poke3.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke3.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke3.id !== undefined) { out.push("It has no effect on " + poke3.owner + "'s " + poke(poke3.id) + "!"); } else if (poke3.protect) { out.push(poke3.owner + "'s " + poke(poke3.id) + " protected itself!"); } else if (poke3.hp > 0) { out = dealDamage(poke1, move, poke3, typeMultiplier, 1, out, true); } } } else if (isP3) { typeMultiplier = safari.checkEffective([move.type], [type1(poke2.id), type2(poke2.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke2.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke2.id !== undefined) { out.push("It has no effect on " + poke2.owner + "'s " + poke(poke2.id) + "!"); } else if (poke2.protect) { out.push(poke2.owner + "'s " + poke(poke2.id) + " protected itself!"); } else if (poke2.hp > 0) { out = dealDamage(poke3, move, poke2, typeMultiplier, 2, out); } typeMultiplier = safari.checkEffective([move.type], [type1(poke4.id), type2(poke4.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke4.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke4.id !== undefined) { out.push("It has no effect on " + poke4.owner + "'s " + poke(poke4.id) + "!"); } else if (poke4.protect) { out.push(poke4.owner + "'s " + poke(poke4.id) + " protected itself!"); } else if (poke4.hp > 0) { out = dealDamage(poke3, move, poke4, typeMultiplier, 2, out); } if (target !== "TEAM") { typeMultiplier = safari.checkEffective([move.type], [type1(poke1.id), type2(poke1.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke1.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke1.id !== undefined) { out.push("It has no effect on " + poke1.owner + "'s " + poke(poke1.id) + "!"); } else if (poke1.protect) { out.push(poke1.owner + "'s " + poke(poke1.id) + " protected itself!"); } else if (poke1.hp > 0) { out = dealDamage(poke3, move, poke1, typeMultiplier, 1, out, true); } } } else if (isP2) { typeMultiplier = safari.checkEffective([move.type], [type1(poke1.id), type2(poke1.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke1.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke1.id !== undefined) { out.push("It has no effect on " + poke1.owner + "'s " + poke(poke1.id) + "!"); } else if (poke1.protect) { out.push(poke1.owner + "'s " + poke(poke1.id) + " protected itself!"); } else if (poke1.hp > 0) { out = dealDamage(poke2, move, poke1, typeMultiplier, 1, out); } typeMultiplier = safari.checkEffective([move.type], [type1(poke3.id), type2(poke3.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke3.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke3.id !== undefined) { out.push("It has no effect on " + poke3.owner + "'s " + poke(poke3.id) + "!"); } else if (poke3.protect) { out.push(poke3.owner + "'s " + poke(poke3.id) + " protected itself!"); } else if (poke3.hp > 0) { out = dealDamage(poke2, move, poke3, typeMultiplier, 1, out); } if (target !== "TEAM") { typeMultiplier = safari.checkEffective([move.type], [type1(poke4.id), type2(poke4.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke4.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke4.id !== undefined) { out.push("It has no effect on " + poke4.owner + "'s " + poke(poke4.id) + "!"); } else if (poke4.protect) { out.push(poke4.owner + "'s " + poke(poke4.id) + " protected itself!"); } else if (poke4.hp > 0) { out = dealDamage(poke2, move, poke4, typeMultiplier, 2, out, true); } } } else if (isP4) { typeMultiplier = safari.checkEffective([move.type], [type1(poke1.id), type2(poke1.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke1.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke1.id !== undefined) { out.push("It has no effect on " + poke1.owner + "'s " + poke(poke1.id) + "!"); } else if (poke1.protect) { out.push(poke1.owner + "'s " + poke(poke1.id) + " protected itself!"); } else if (poke1.hp > 0) { out = dealDamage(poke4, move, poke1, typeMultiplier, 1, out); } typeMultiplier = safari.checkEffective([move.type], [type1(poke3.id), type2(poke3.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke3.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke3.id !== undefined) { out.push("It has no effect on " + poke3.owner + "'s " + poke(poke3.id) + "!"); } else if (poke3.protect) { out.push(poke3.owner + "'s " + poke(poke3.id) + " protected itself!"); } else if (poke3.hp > 0) { out = dealDamage(poke4, move, poke3, typeMultiplier, 1, out); } if (target !== "TEAM") { typeMultiplier = safari.checkEffective([move.type], [type1(poke2.id), type2(poke2.id)], inver, this.select, this.select2); if (move.type == "Ground" && poke2.item.balloon) { typeMultiplier = 0; } if (typeMultiplier === 0 && poke2.id !== undefined) { out.push("It has no effect on " + poke2.owner + "'s " + poke(poke2.id) + "!"); } else if (poke2.protect) { out.push(poke2.owner + "'s " + poke(poke2.id) + " protected itself!"); } else if (poke2.hp > 0) { out = dealDamage(poke4, move, poke2, typeMultiplier, 2, out, true); } } } } else { var typeMultiplier = safari.checkEffective([move.type], [type1(target.id), type2(target.id)], inver, this.select, this.select2); if (move.type == "Ground" && (target.item.balloon || (hasType(target.id, "Flying") && typeMultiplier === 0))) { typeMultiplier = 0; if (!this.fullNPC && this.npcBattle && target.ownerID !== this.idnum1 && !target.protect) { var thousandArrowsSkill = safari.pokeSkillActivated(this.name1, user, "thousandArrows"); if (thousandArrowsSkill) { typeMultiplier = 2; out.push("[{0}'s {1}] {2}'s Ground-type attack struck the airborne {3}!".format(poke(thousandArrowsSkill.id), thousandArrowsSkill.name, poke(user.id), poke(target.id))); } } } if (typeMultiplier === 0) { if (this.select && this.select.bypassImmune && (isP2 || isP4)) { typeMultiplier = 1; } else { out.push("But it has no effect!"); return out; } } out = dealDamage(user, move, target, typeMultiplier, ((isP1 || isP3) ? 2 : 1), out); } } if (move.category === "other") { out = this.afterDamage(user, move, target, oppparty, false, ((isP1 || isP3) ? 2 : 1), out); } return out; }; Battle2.prototype.afterDamage = function(user, move, target, oppparty, damaging, targetSide, out) { var fainted = (target.hp <= 0 ? true : false); var tname = target.owner + "'s " + poke(target.id); if (!fainted && target.condition === "none" && move.status && !(this.select && this.select.electricterrain && move.status === "sleep" && !hasType(target.id, "Flying")) && !(this.select && this.select.mistyterrain && !hasType(target.id, "Flying"))) { if (!this.isImmuneTo(target.id, move.status)) { if (["sleep", "freeze"].contains(move.status) === false || this.countCondition(oppparty, move.status) === 0) { // skill effect placed here to ensure all other checks for the status have passed other than statusChance. this avoids activating the skill in vain if the effect would have been prevented for any other reason anyway if (!this.fullNPC && this.npcBattle && targetSide === 1) { var statusList = ["sleep", "paralyzed", "burn", "freeze", "poison"]; var statusDefence = ["basicFairy", "basicGround", "basicWater", "basicFire", "basicSteel"]; var defenceSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, statusDefence[statusList.indexOf(move.status)]); if (defenceSkill) { if (move.statusChance) { move.statusChance -= (defenceSkill.rate2 / 100); } else { // no statusChance means 100% status rate, so assign a reduced statusChance move.statusChance = 1 - (defenceSkill.rate2 / 100); } out.push("[{0}'s {1}] The {2} chance of {3}'s move was reduced by {4}%!".format(poke(defenceSkill.id), defenceSkill.name, cap(move.status), poke(user.id), defenceSkill.rate2)); } } if (!move.statusChance || chance(move.statusChance)) { if (damaging && this.select && this.select.genesisshield && this.selectData.shieldHP > 0 && targetSide === 2) { out.push("The secondary effects were suppressed by the shield."); } else { target.condition = move.status; target.conditionDuration = sys.rand(0, 3); if (move.status == "sleep" && this.select && this.select.extendedSleep) { target.conditionDuration++; } var supressed = false; if (!supressed) { if (move.status == "poison" && (hasType(user.id, "Poison"))) { target.badlyPoisoned = 1; out.push(tname + " got badly poisoned!"); } else { var conditionVerb = { sleep: "fell asleep", paralyzed: "was paralyzed", burn: "got burned", freeze: "was frozen solid", poison: "got poisoned" }; out.push(tname + " " + conditionVerb[move.status] + "!"); } if (!this.fullNPC && this.npcBattle && targetSide === 1 && !(target.boosts["atk"] === 6 && target.boosts["satk"] === 6) && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { // if both stats are maxed, dont use skill var berserkGeneSkill = safari.pokeSkillActivated(this.name1, target, "berserkGene"); if (berserkGeneSkill) { target.boosts["atk"] += berserkGeneSkill.rate; target.boosts["satk"] += berserkGeneSkill.rate; target.boosts["atk"] = Math.min(6, target.boosts["atk"]); target.boosts["satk"] = Math.min (6, target.boosts["satk"]); out.push("[{0}'s {1}] {2}'s ATK and SATK rose by {3}!".format(poke(berserkGeneSkill.id), berserkGeneSkill.name, poke(target.id), plural(berserkGeneSkill.rate, "stage"))); } } } } } } else { out.push(cap(move.status) + " Clause prevented the " + (move.status === "sleep" ? "sleep inducing" : "freezing") + " effect of the move from working!"); } } } var name, party, isP1 = false, isP2 = false, isP3 = false, isP4 = false; name = user.owner + "'s " + poke(user.id); if (user.ownerID === this.idnum1) { party = this.team1; isP1 = true; } if (user.ownerID === this.idnum2) { party = this.team2; isP2 = true; } if (user.ownerID === this.idnum3) { party = this.team3; isP3 = true; } if (user.ownerID === this.idnum4) { party = this.team4; isP4 = true; } if (move.category !== "other") { if (move.haze) { obj = []; switch (move.haze) { case "self": obj.push(user); desc = name; out.push(desc + " stat changes were eliminated!"); break; case "field": obj.push(this.poke1, this.poke2, this.poke3, this.poke4); desc = "All Pokémon on the field's "; out.push(desc + " stat changes were eliminated!"); break; case "both": obj.push(user); desc = name; out.push(desc + " stat changes were eliminated!"); break; case "party": obj = obj.concat(party); desc = user.owner + "'s party"; out.push(desc + " stat changes were eliminated!"); break; case "oppparty": obj = obj.concat(oppparty); desc = target.owner + "'s party"; out.push(desc + " stat changes were eliminated!"); break; case "all": obj = obj.concat(party, oppparty); desc = "All"; out.push(desc + " stat changes were eliminated!"); break; } for (e = 0; e < obj.length; e++) { for (o in obj[e].boosts) { obj[e].boosts[o] = 0; } } } if (move.refresh) { switch (move.refresh) { case "self": user.condition = "none"; out.push(name + "'s status returned to normal!"); break; case "field": if (isP1 || isP3) { this.poke1.condition = "none"; this.poke3.condition = "none"; } else if (isP2 || isP4) { this.poke2.condition = "none"; this.poke4.condition = "none"; } out.push("Pokémon on " + name + "'s side of the field had their status returned to normal!"); break; case "party": for (e = 0; e < party.length; e++) { party[e].condition = "none"; } out.push(user.owner + "'s party's status returned to normal!"); break; } } if (move.buff) { for (e = 0; e < move.buff.length; e++) { obj = move.buff[e]; if (chance(obj.buffChance)) { user.boosts[obj.buffStat] += obj.buff; user.boosts[obj.buffStat] = Math.min(6, Math.max(user.boosts[obj.buffStat], -6)); if (((this.select && this.select.singlespecialstat) || (this.select2 && this.select2.singlespecialstat)) && obj.buffStat == "satk") { out.push(name + "'s Special " + addSign(obj.buff) + "!"); } else { out.push(name + "'s " + this.statName(obj.buffStat) + " " + addSign(obj.buff) + "!"); } } } } } if (move.haze && !fainted) { obj = []; switch (move.haze) { case "target": case "both": obj.push(target); desc = tname; out.push(desc + " stat changes were eliminated!"); break; } for (e = 0; e < obj.length; e++) { for (o in obj[e].boosts) { obj[e].boosts[o] = 0; } } } if (move.nerf && !fainted) { if (damaging && this.select && this.select.genesisshield && targetSide === 2 && this.selectData.shieldHP > 0) { out.push("The secondary effects were suppressed by the shield."); } else { var defianted = false; if (!this.fullNPC && this.npcBattle && targetSide === 1 && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { var nerfDefenceSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "basicDark"); } for (e = 0; e < move.nerf.length; e++) { obj = move.nerf[e]; if (nerfDefenceSkill) { obj.nerfChance -= (nerfDefenceSkill.rate2 / 100); out.push("[{0}'s {1}] The stat decrease chance of {2}'s move was reduced by {3}%!".format(poke(nerfDefenceSkill.id), nerfDefenceSkill.name, poke(user.id), nerfDefenceSkill.rate2)); } if (chance(obj.nerfChance)) { target.boosts[obj.nerfStat] += obj.nerf; target.boosts[obj.nerfStat] = Math.min(6, Math.max(target.boosts[obj.nerfStat], -6)); if (((this.select && this.select.singlespecialstat) || (this.select2 && this.select2.singlespecialstat)) && obj.nerfStat == "satk") { out.push(tname + "'s Special " + addSign(obj.nerf) + "!"); } else { out.push(tname + "'s " + this.statName(obj.nerfStat) + " " + addSign(obj.nerf) + "!"); } if (this.select && this.select.defiant && (isP1 || isP3) && (!defianted) && obj.nerf < 0) { out.push(tname + "'s " + this.statName("atk") + " +2!"); target.boosts["atk"] += 2; target.boosts["atk"] = Math.min(6, Math.max(target.boosts["atk"], -6)); defianted = true; } } } } } if (((this.select && this.select.vicious) || (this.select2 && this.select2.vicious)) && move.category !== "other") { var b = ["atk", "def", "spe", "satk", "sdef"].random(); var n = ["atk", "def", "spe", "satk", "sdef"]; if ((this.select.singlespecialstat || ((this.select2 && this.select2.singlespecialstat))) && b == "sdef") { b = "satk"; n = n.splice(n.indexOf("sdef"), 1); } if (n.contains(b)) { n.splice(n.indexOf(b), 1); } n = n.random(); if (this.select.singlespecialstat && n == "sdef") { n = "satk"; } user.boosts[b] += 2; user.boosts[b] = Math.min(6, Math.max(user.boosts[b], -6)); user.boosts[n] -= 1; user.boosts[n] = Math.min(6, Math.max(user.boosts[n], -6)); if ((this.select.singlespecialstat || (this.select2 && this.select2.singlespecialstat)) && b == "satk") { out.push(name + "'s Special +2!"); } else { out.push(name + "'s " + this.statName(b) + " +2!"); } if ((this.select.singlespecialstat || (this.select2 && this.select2.singlespecialstat)) && n == "satk") { out.push(name + "'s Special -1!"); } else { out.push(name + "'s " + this.statName(n) + " -1!"); } } if (((this.select && this.select.dynamicPunch) || (this.select && this.select.dynamicPunch)) && target.hp > 0) { if (move.type == "Fighting" && (!(target.confused))) { target.confused = true; out.push(tname + " was confused!"); } } if (this.select && this.select.rollout) { if (this.select.rollout) { if (this.selectData.rolloutUser == user.id && (isP1 || isP3)) { this.selectData.rolloutCount++; } else if (this.selectData.rolloutUser2 == user.id && (isP2 || isP4)) { this.selectData.rolloutCount2++; } if (isP1 || isP3) { this.selectData.rolloutUser = user.id; this.selectData.rolloutCount = 0; } else if (isP2 || isP4) { this.selectData.rolloutUser2 = user.id; this.selectData.rolloutCount2 = 0; } } } if (!fainted && (move.type === "Fire") && target.condition === "freeze") { out.push(toColor(tname + " thawed out!", "#55E")); target.condition = "none"; } if (!fainted && move.flinch && chance(move.flinch)) { target.flinch = true; } if (!this.fullNPC && this.npcBattle && (isP1 || isP3)) { var stats = ["atk", "def", "spe", "satk", "sdef"]; if (user.boosts["atk"] < 6 || user.boosts["def"] < 6 || user.boosts["satk"] < 6 || user.boosts["sdef"] < 6 || user.boosts["spe"] < 6 && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { var rotoLotoSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "rotoLoto"); if (rotoLotoSkill) { var targetStat; do { targetStat = stats.random(); } while (user.boosts[targetStat] === 6); user.boosts[targetStat] += rotoLotoSkill.rate2; user.boosts[targetStat] = Math.min(6, user.boosts[targetStat]); out.push("[{0}'s {1}] {2}'s {3} was raised by {4}!".format(poke(rotoLotoSkill.id), rotoLotoSkill.name, poke(user.id), targetStat.toUpperCase(), plural(rotoLotoSkill.rate2, "stage"))); } } if (move.type === "Fighting" && !move.brickBreak && (this.side2Field.reflect > 0 || this.side2Field.lightscreen > 0)) { var screenShatterSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "basicFighting"); if (screenShatterSkill) { if (chance(screenShatterSkill.rate2 / 100)) move.brickBreak = true; out.push("[{0}'s {1}] {2}'s Fighting-type attack gained a {3}% chance to shatter screens!".format(poke(screenShatterSkill.id), screenShatterSkill.name, poke(user.id), screenShatterSkill.rate2)); } } if (move.type === "Flying" && user.boosts["spe"] < 6) { var valid = this.team1.filter(function(e) { return e.boosts["spe"] < 6 && e.hp > 0; }); if (valid.length > 0) { var speedBoostSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "basicFlying"); var recipient = valid.random(); if (speedBoostSkill) { recipient.boosts["spe"] += speedBoostSkill.rate2; recipient.boosts["spe"] = Math.min(6, recipient.boosts["spe"]); out.push("[{0}'s {1}] {2}'s Flying-type attack boosted {3}'s speed by {4}!".format(poke(speedBoostSkill.id), speedBoostSkill.name, poke(user.id), poke(recipient.id), plural(speedBoostSkill.rate2, "stage"))); } } } if (move.type === "Grass") { var hasStatus = this.team1.filter(function(e) { return e.condition !== "none" }); if (hasStatus.length > 0) { var aromatherapySkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "basicGrass"); if (aromatherapySkill) { var toCure = hasStatus.random(); toCure.condition = "none"; toCure.badlyPoisoned = 0; out.push("[{0}'s {1}] {2}'s Grass-type attack cured {3} of its status condition!".format(poke(aromatherapySkill.id), aromatherapySkill.name, poke(user.id), poke(toCure.id))); } } } if (move.type === "Dragon" && user.hp <= (user.maxhp * 0.66)) { var validStats = []; for (var i = 0; i < stats.length; i++) { if (user.boosts[stats[i]] < 6) { validStats.push(stats[i]); } } if (validStats.length >= 2 && !((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost))) { var multiBoostSkill = safari.pokeSkillActivated(this.name1, this.originalTeam1, "basicDragon"); if (multiBoostSkill) { var randStat = validStats.shuffle().shift(); var randStat2 = validStats.shift(); user.boosts[randStat] += multiBoostSkill.rate2; user.boosts[randStat2] += multiBoostSkill.rate2; out.push("[{0}'s {1}] {2}'s Dragon-type attack raised its {3} and {4} by {5}!".format(poke(multiBoostSkill.id), multiBoostSkill.name, poke(user.id), randStat.toUpperCase(), randStat2.toUpperCase(), plural(multiBoostSkill.rate2, "stage"))); } } } if (target.condition === "none" && !fainted) { var absofusionSkill; if (!this.isImmuneTo(target.id, "burn")) { absofusionSkill = safari.pokeSkillActivated(this.name1, user, "absofusionWhite"); if (absofusionSkill) { target.condition = "burn"; out.push("[{0}'s {1}] {2}'s attack burned the opponent!".format(poke(absofusionSkill.id), absofusionSkill.name, poke(user.id))); } } if (!this.isImmuneTo(target.id, "paralyzed")) { absofusionSkill = safari.pokeSkillActivated(this.name1, user, "absofusionBlack"); if (absofusionSkill) { target.condition = "paralyzed"; out.push("[{0}'s {1}] {2}'s attack paralyzed the opponent!".format(poke(absofusionSkill.id), absofusionSkill.name, poke(user.id))); } } if (!this.isImmuneTo(target.id, "sleep") && !(this.select && (this.select.electricterrain || this.select.mistyterrain) && !hasType(target.id, "Flying")) && this.countCondition(oppparty, "sleep") < 1) { var relicSongSkill = safari.pokeSkillActivated(this.name1, user, "relicSong"); if (relicSongSkill) { target.condition = "sleep"; out.push("[{0}'s {1}] {2}'s attack put the opponent to sleep!".format(poke(relicSongSkill.id), relicSongSkill.name, poke(user.id))); } } } } if (move.brickBreak) { if (isP1 || isP3) { this.side2Field.reflect = 0; this.side2Field.lightscreen = 0; } if (isP2 || isP4) { this.side1Field.reflect = 0; this.side1Field.lightscreen = 0; } out.push(toColor("The screens were shattered!", "#c03028")); } if (!fainted && this.select) { if (this.select.retaliate && move.category == "special") { target.retaliate = true; } } if (damaging) { user.damagingUsed++; } else { user.nonDamagingUsed++; } return out; }; Battle2.prototype.buildTeam = function(owner, team, idnum, cherished, helds, zCrystalUser) { var out = [], t, p, h = "", stats, info, held; var boost = (this.npcBattle && idnum === this.idnum2 ? this.powerBoost : 0) + 1; zCrystalUser = (this.npcBattle && idnum === this.idnum1 ? zCrystalUser : false); var ch = 0; var getStat = function(val) { return (2 * val + 5) * boost; }; for (t = 0; t < team.length; t++) { p = team[t]; if (helds) { if (t >= helds.length) { held = -1; } else { held = helds[t]; } if (parseInt(held, 10) == 1) { held = "oran"; } else if (parseInt(held, 10) == 11) { held = "miracle"; } } stats = getStats(p); if (cherished) { ch = Math.min(countRepeated(cherished, pokeInfo.species(p)), 10); } h = Math.round(((stats[0] + ch) * 2 + 110) * boost); info = { id: p, owner: owner, ownerID: idnum, hp: h, maxhp: h, stats: { atk: getStat(stats[1] + ch), def: getStat(stats[2] + ch), satk: getStat(stats[3] + ch), sdef: getStat(stats[4] + ch), spe: getStat(stats[5] + ch), }, boosts: { atk: 0, def: 0, satk: 0, sdef: 0, spe: 0, }, protect: false, flinch: false, helped: false, lastPlayed: false, lastPlayed2: true, condition: "none", confused: false, conditionDuration: 0, badlyPoisoned: 0, types: this.getMoveTypes(p), movepowers: this.getMovePowers(p), drain: this.getMoveSet(p, "drain"), recoil: this.getMoveSet(p, "recoil"), critical: this.getMoveSet(p, "critical"), priority: this.getMoveSet(p, "priority"), restore: this.getMoveSet(p, "restore"), burnout: this.getMoveSet(p, "burnout"), weather: this.getWeatherMoves(p), buffMoves: this.getBuffValues(p), nerfMoves: this.getNerfValues(p), biteTypes: this.getBiteTypes(p), ability: this.getAbilitySet(p), item: {}, berry: held, crystal: 0, index: t, moves: [], damagingUsed: 0, nonDamagingUsed: 0 }; if (info.types.Normal) { info.types.Normal = Math.round(info.types.Normal/4); } if (zCrystalUser && p == zCrystalUser) { var buffValue = getCrystalEffect(p).npcBuff || defaultCrystalBuff; info.crystal = buffValue; zCrystalUser = false; //only applies to first found Pokémon of a species to avoid duplicating } out.push(info); } return out; }; Battle2.prototype.translateMove = function(move) { var out = ""; if (move.category === "other") { out += this.getEffectDesc(move); } else { var effs = this.getEffectDesc(move); var pow = move.power; if (this.select && this.select.powerUnknown && (((this.turn % 3 == 0 ? 0 : 1) + (this.turn % 5 == 0 ? 0 : -1) + (move.power % 20 == 0 ? 1 : 0) - (move.power % 35 == 0 ? -1 : 0) + (move.category == "physical" ? (move.power % 15 == 5 ? 1 : 0) : (move.power % 15 == 0 ? 1 : 0))) == 0 ? true : false)) { pow = "???"; } out += typeIcon(move.type) + " " + (move.category === "physical" ? toColor("PHYSICAL", "#ff6600") : toColor("SPECIAL", "#800080")) + " (" + pow + " BP) " + (effs ? ", " + effs : ""); } return out; }; Battle2.prototype.statName = function(stat) { var names = { atk: "Attack", def: "Defense", satk: "Special Attack", sdef: "Special Defense", spe: "Speed" }; return names[stat]; }; Battle2.prototype.getEffectDesc = function(data) { var out = [], e, eff; if (data.hasOwnProperty("restore")) { out.push("Recovers " + Math.round(data.restore * 100) + "% HP"); } if (data.hasOwnProperty("refresh")) { out.push("Heals " + (data.refresh === "self" ? "own" : (data.refresh === "field" ? "field ally Pokémon's" : "party's" )) + " status condition" ); } if (data.hasOwnProperty("protect")) { out.push("Protect from any attack"); } if (data.hasOwnProperty("reflect")) { out.push("Sets up Reflect for 5 turns"); } if (data.hasOwnProperty("lightscreen")) { out.push("Sets up Light Screen for 5 turns"); } if (data.hasOwnProperty("helpingHand")) { out.push("Boosts Ally's Attack Power"); } if (data.hasOwnProperty("followMe")) { out.push("Becomes the center of attention"); } if (data.hasOwnProperty("drain")) { out.push("Recovers " + (Math.floor(data.drain * 100)) + "% of damage dealt"); } if (data.hasOwnProperty("recoil")) { out.push("Suffers 1/3 of damage dealt in recoil"); } if (data.hasOwnProperty("brickBreak")) { out.push("Removes Reflect/Light Screen"); } if (data.hasOwnProperty("recharge")) { out.push("Requires a turn of recharge"); } if (data.hasOwnProperty("target")) { if (data.target == "TEAM") { out.push("Targets both foes"); } if (data.target == "ALL") { out.push("Targets all Pokémon on the field"); } } if (data.hasOwnProperty("status")) { var conditionVerb = { sleep: "Puts target to sleep", paralyzed: "Paralyzes target", burn: "Burns target", freeze: "Freezes target", poison: "Poison target" }; out.push(conditionVerb[data.status] + (data.statusChance && data.statusChance < 1 ? " (" + Math.round(data.statusChance * 100) + "%)" : "")); } if (data.hasOwnProperty("haze")) { switch (data.haze) { case "self": out.push("Clears own stat changes"); break; case "target": out.push("Clears opponent stat changes"); break; case "both": out.push("Clears own and opponent stat changes"); break; case "field": out.push("Clears stat changes for all Pokémon on the field"); break; case "party": out.push("Clears party's stat changes"); break; case "oppparty": out.push("Clears opponent party's stat changes"); break; case "all": out.push("Clears all stat changes"); break; } } if (data.hasOwnProperty("buff")) { for (e = 0; e < data.buff.length; e++) { eff = data.buff[e]; if (((this.select && this.select.singlespecialstat) || ((this.select2 && this.select2.singlespecialstat))) && eff.buffStat.toLowerCase() == "satk") { out.push("Own Special " + addSign(eff.buff) + (eff.buffChance && eff.buffChance < 1 ? " (" + Math.round(eff.buffChance * 100) + "%)" : "")); } else { out.push("Own " + this.statName(eff.buffStat) + " " + addSign(eff.buff) + (eff.buffChance && eff.buffChance < 1 ? " (" + Math.round(eff.buffChance * 100) + "%)" : "")); } } } if (data.hasOwnProperty("nerf")) { for (e = 0; e < data.nerf.length; e++) { eff = data.nerf[e]; if (((this.select && this.select.singlespecialstat) || ((this.select2 && this.select2.singlespecialstat))) && eff.nerfStat.toLowerCase() == "satk") { out.push("Opponent's Special " + addSign(eff.nerf) + (eff.nerfChance && eff.nerfChance < 1 ? " (" + Math.round(eff.nerfChance * 100) + "%)" : "")); } else { out.push("Opponent's " + this.statName(eff.nerfStat) + " " + addSign(eff.nerf) + (eff.nerfChance && eff.nerfChance < 1 ? " (" + Math.round(eff.nerfChance * 100) + "%)" : "")); } } } if (data.hasOwnProperty("priority") && data.priority !== 0) { out.push("Priority " + data.priority); } if (data.hasOwnProperty("flinch")) { out.push("Flinch (" + Math.round(data.flinch * 100) + "%)"); } if (data.hasOwnProperty("critical")) { out.push("Critical Hit (" + Math.round(data.critical * 100) + "%)"); } return out.join(", "); }; Battle2.prototype.dynamaxMove = function(type) { var power = (100 + (5 * (Math.round(4 * Math.random())))); var out = { "Normal": { refresh: "self" }, "Fighting": { buff: { "atk": 1 } }, "Flying": { buff: { "spd": 1 } }, "Poison": { critical: 1 }, "Ground": { buff: { "sdef": 1 } }, "Bug": { nerf: { "satk": 1 }, buff: { "satk": 1 } }, "Ghost": { nerf: { "def": 1 } }, "Steel": { buff: { "def": 1 } }, "Rock": { weather: "sand" }, "Water": { weather: "rain" }, "Fire": { weather: "sun" }, "Ice": { weather: "hail" }, "Electric": { terrain: "electric" }, "Grass": { terrain: "grassy" }, "Psychic": { terrain: "psychic" }, "Fairy": { terrain: "misty" }, "Dark": { nerf: { "sdef": 1 } }, "Dragon": { nerf: { "atk": 1 } } }[type]; out.power = power; out.type = type; out.category = (chance(0.5) ? "physical" : "special"); out.priority = 0; out.dynamax = true; return out; }; Battle2.prototype.generateMoves = function(id, data, name, idnum, dynamaxed) { var out = [], m, move, amt, p, types = data.types, eff, damaging, used, amt, factor; var boost = (this.npcBattle && (name === this.name2 || name === this.name4) ? this.powerBoost * 0.8 : 0) + 1; var moveBoost = (this.npcBattle && name === this.name2 ? this.powerBoost * 0.6 : 0) + 1; var isP1 = (idnum === this.idnum1 ? true : false); var isP3 = (idnum === this.idnum3 ? true : false); var isP2 = (idnum === this.idnum2 ? true : false); var isP4 = (idnum === this.idnum4 ? true : false); var which = (isP1 ? 1 : (isP2 ? 2 : (isP3 ? 3 : 4))); var needed = this.moveAmt; if (hasType(id, "Grass")) { if (this.abilityActive(which, "Overgrow") || this.abilityActive(which, "Overgrow Awakening") || this.abilityActive(which, "Overgrow Ascending")) { out.push({ owner: data.id, ownerId: id, priority: 0, type: "Grass", category: (chance(0.5) ? "physical" : "special"), power: this.abilityActive(which, "Overgrow Ascending") ? 180 : (this.abilityActive(which, "Overgrow Awakening") ? 140 : 100) }); needed--; } } if (hasType(id, "Fire")) { if (this.abilityActive(which, "Blaze") || this.abilityActive(which, "Blaze Awakening") || this.abilityActive(which, "Blaze Ascending")) { out.push({ owner: data.id, ownerId: id, priority: 0, type: "Fire", category: (chance(0.5) ? "physical" : "special"), power: this.abilityActive(which, "Blaze Ascending") ? 180 : (this.abilityActive(which, "Blaze Awakening") ? 140 : 100) }); needed--; } } if (hasType(id, "Water")) { if (this.abilityActive(which, "Torrent") || this.abilityActive(which, "Torrent Awakening") || this.abilityActive(which, "Torrent Ascending")) { out.push({ owner: data.id, ownerId: id, priority: 0, type: "Water", category: (chance(0.5) ? "physical" : "special"), power: this.abilityActive(which, "Torrent Ascending") ? 180 : (this.abilityActive(which, "Torrent Awakening") ? 140 : 100) }); needed--; } } for (m = needed; m--; ) { used = []; move = { owner: data.id, ownerId: id, priority: 0 }; if (chance(0.32)) { move.category = "other"; factor = 0.5 + sys.rand(0, 50)/100; damaging = false; if (data.restore >= 2.5) { factor += 0.1; } else if (data.restore >= 1.5) { factor += 0.05; } } else { move.type = randomSample(types); move.power = Math.round(sys.rand(2, 23)) * 5; if (move.power < 90) { move.power = Math.round(move.power + (20 * Math.random())); } if (move.type == "Normal" && this.select && this.select.pixelate) { move.type = "Fairy"; move.power = Math.round(move.power * 1.2); } if (move.type == "Water" && this.select && this.select.sweltering) { move.type = "Normal"; } if (move.type == "Ice" && this.select && this.select.sweltering) { move.type = "Normal"; } if (move.type == "Normal" && this.select && this.select.ashes) { move.type = "Ghost"; } if (move.type == "Fire" && this.select && this.select.ashes) { move.type = "Rock"; } move.power = Math.min(data.movepowers[move.type], move.power); move.power = Math.round(move.power * moveBoost / 5) * 5; move.category = chance(0.5) ? "physical" : "special"; if ((this.select && this.select.categorySplit) || (this.select2 && this.select2.categorySplit)) { switch (move.type) { case "Normal": case "Fighting": case "Flying": case "Rock": case "Ground": case "Bug": case "Poison": case "Ghost": case "Steel": move.category = "physical"; break; default: move.category = "special"; break; } } if (( (this.select && this.select.specBan) || (this.select2 && this.select2.specBan)) && move.category === "special") { move.category = "physical"; } if (( (this.select && this.select.physBan) || (this.select2 && this.select2.physBan)) && move.category === "physical") { move.category = "special"; } factor = (60 - move.power) / 100; if (factor > -0.1 && factor < 0.1 && chance(0.5)) { factor = 0; } damaging = true; } if (dynamaxed) { move = this.dynamaxMove(move.type); factor = 0; } else { move.dynamax = false; } while (factor !== 0) { if (factor > -0.1 && factor < 0.1 && chance(0.5)) { amt = factor; } else { amt = Math.random() * factor; } var bias = null; if (isP2) { bias = this.biasNPC; } if (isP4) { bias = this.biasNPC2; } var screenSide = (isP1 || isP3 ? 1 : 2); var pokeTypes = [type1(move.owner), type2(move.owner)]; eff = this.generateMoveEffect(data, amt * boost, damaging, bias, data.drain, data.recoil, data.critical, data.priority, data.restore, data.burnout, data.buffMoves, data.nerfMoves, move.category, move.type, pokeTypes, screenSide, used); if (eff.type !== "none") { for (p in eff) { if (["target"].contains(p)) { move.target = eff[p]; if (eff[p] == "TEAM") { move.power = (5 * Math.round(move.power * 0.75/5)); } else if (eff[p] == "ALL") { move.power = (5 * Math.round(move.power * 0.85/5)); } } if (["recharge"].contains(p)) { move.power = (145 + (5 * (Math.round(3 * Math.random())))); if (this.select && this.select.hyperBeamGlitch) { move.power += 30; } } else if (["burnout"].contains(p)) { if (eff[p] === 2) { move.power = (5 * Math.round((Math.max(move.power, Math.ceil(120 + (20 * Math.random()))))/5)); } if (eff[p] === 1) { move.power = (5 * Math.round((Math.max(move.power, Math.ceil(100 + (20 * Math.random()))))/5)); } } if (["recoil"].contains(p)) { if (data.recoil >= 3) { move.power = (5 * Math.round(move.power * 1.4/5)); } else if (data.recoil >= 2.5) { move.power = (5 * Math.round(move.power * 1.3/5)); } else if (data.recoil >= 2) { move.power = (5 * Math.round(move.power * 1.2/5)); } else if (data.recoil >= 1.5) { move.power = (5 * Math.round(move.power * 1.15/5)); } else { move.power = (5 * Math.round(move.power * 1.1/5)); } } else if (this.weather) { if (this.weather == "Rain" && this.weather.thunder && move.type == "Electric" && move.power < 95) { move.power += (5 * Math.round(Math.random() * 6)); } if (this.weather == "Sun" && this.weather.solar && move.type == "Grass" && move.power < 95) { move.power += (5 * Math.round(Math.random() * 6)); } if (this.weather.weather && move.type == "Normal" && chance(0.08)) { if (this.weather == "Rain") { move.type = "Water"; move.power += (5 * Math.round(Math.random() * 6)); } if (this.weather == "Sun") { move.type = "Fire"; move.power += (5 * Math.round(Math.random() * 6)); } if (this.weather == "Sand") { move.type = "Rock"; move.power += (5 * Math.round(Math.random() * 6)); } if (this.weather == "Hail") { move.type = "Ice"; move.power += (5 * Math.round(Math.random() * 6)); } move.power += (5 * Math.round(Math.random() * 6)); } } if (["helpingHand", "followMe"].contains(p)) { if (move.priority < 6) { move.priority = 6; } } if (["buff", "nerf"].contains(p)) { if (!move.hasOwnProperty(p)) { move[p] = []; } move[p].push(eff[p]); } else if (p === "type") { used.push(eff.type); } else { move[p] = eff[p]; } } factor -= amt; } if (!damaging && used.length === 0) { continue; } if ((factor > -0.1 && factor < 0.1) || used.length >= 2 + (damaging ? 0 : 1) || (used.contains("protect") && used.length >= 2) || chance(0.12)) { break; } } if (((this.select && this.select.galeWings) || (this.select2 && this.select2.galeWings)) && move.type === "Flying") { if (move.priority) { move.priority++; } else { move.priority = 1; } } out.push(move); } return out; }; Battle2.prototype.generateMoveEffect = function(user, factor, damaging, bias, drain, recoil, critical, priority, restore, burnout, buffValues, nerfValues, category, type, pokeTypes, screenSide, used) { var effChance; if (damaging) { effChance = { priority: (1 + (priority*2.25 + factor)), lowpriority: (6 * (0.08 - factor)), flinch: 2.65, drain: (0.75 + factor + drain), recoil: (0.75 * (0.7 + recoil - factor)), critical: (0.75 + critical + factor), burnout: (5 + (burnout * 4)), status: 2, buff: 1.8, nerf: 1.8, haze: 1 * factor, recharge: 0, refresh: 1.2 * factor }; } else { effChance = { restore: (1 + Math.min((0.4 * restore), 1)), protect: 1.8, status: 3.8, haze: 1, refresh: 2, buff: 6, nerf: 5, reflect: 0.45, lightscreen: 0.45 }; } if (effChance.restore == 2 && restore > 0) { effChance.restore = 2.1; } if (damaging && user.movepowers[type] && (user.movepowers[type] >= 140)) { effChance.recharge = 2; if (this.select && this.select.hyperBeamGlitch) { effChance.recharge = 2.4; } } if (damaging && this.tagBattle) { effChance.targetAll = 1.75; effChance.targetTeam = 2.875; effChance.recharge = 0; } else if (this.tagBattle) { effChance.protect = 2.4; effChance.helpingHand = 1.65; effChance.followMe = 0.85; effChance.buff = 4; effChance.nerf = 3; effChance.reflect = 0.85; effChance.lightscreen = 0.85; } var screenUp = (((screenSide == 2) && (this.side1Field.reflect > 0 || this.side2Field.lightscreen) > 0) || ((screenSide == 1) && (this.side2Field.reflect > 0 || this.side2Field.lightscreen > 0))); if (screenUp && type == "Fighting") { effChance.brickBreak = 1; if (pokeTypes.contains("Fighting")) { effChance.brickBreak = 2; } } else if (screenUp && type == "Psychic") { if (canLearnMove(user.id, 665)) { effChance.brickBreak = 1; } } if (!(damaging)) { if (canLearnMove(user.id, 115)) { effChance.reflect += 0.15; } if (canLearnMove(user.id, 113)) { effChance.lightscreen += 0.15; } } if (screenSide == 1 && (this.side1Field.reflect > 0)) { effChance.reflect = 0; } if (screenSide == 2 && (this.side2Field.reflect > 0)) { effChance.reflect = 0; } if (screenSide == 1 && (this.side1Field.lightscreen > 0)) { effChance.lightscreen = 0; } if (screenSide == 2 && (this.side2Field.lightscreen > 0)) { effChance.lightscreen = 0; } if (bias) { if (bias.recoil) { effChance.recoil *= 1.25; } if (bias.drain) { effChance.drain *= 1.25; } if (bias.critical) { effChance.critical *= 1.25; } if (bias.burnout) { effChance.burnout *= 1.25; } if (bias.priority) { effChance.priority *= 1.25; } if (bias.stats && factor > 0) { effChance.buff *= 1.33; } if (bias.stats && factor < 0) { effChance.buff *= 0.75; } if (bias.reflect) { effChance.reflect *= 1.5; } if (bias.lightscreen) { effChance.lightscreen *= 1.5; } if (bias.paralyze || bias.poison || bias.freeze || bias.burn || bias.sleep) { effChance.status *= 1.5; } } if (this.select) { if (this.select.specBan) { effChance.lightscreen = 0; } if (this.select.physBan) { effChance.reflect = 0; } } try { if (this.select) { if (this.select.grassyterrain && effChance.drain) { effChance.drain *= 1.5; } if (this.select.psychicterrain && effChance.priority) { effChance.priority = 0; } if (this.select.electricterrain && effChance.flinch) { effChance.flinch *= 1.5; } } if (this.select2) { if (this.select2.grassyterrain && effChance.drain) { effChance.drain *= 1.5; } if (this.select2.psychicterrain && effChance.priority) { effChance.priority = 0; } if (this.select2.electricterrain && effChance.flinch) { effChance.flinch *= 1.5; } } } catch (err) { // } if ((this.select && this.select.noStatBoost) || (this.select2 && this.select2.noStatBoost)) { effChance.nerf = 0; effChance.buff = 0; effChance.haze = 0; effChance.burnout = 0; } /*if (damaging && this.name1.toLowerCase() === "ripper roo" && user.owner.toLowerCase() === "ripper roo") effChance.recharge += 9999999999999;*/ var eff = randomSample(effChance); var out = { type: "none" }, buff, nerf, val; if (used.contains(eff)) { return out; } switch (eff) { case "restore": out.restore = Math.min(Math.random() * 0.5 * factor + (0.18 * (1 + Math.min(((Math.random() * Math.max(restore, 0.1)) + 0.25), 0.5))), 0.75) ; if (this.select && this.select.mistyterrain && (hasType(user.id, "Ice") || (hasType(user.id, "Fairy")))) { out.restore *= 1.5; } out.type = eff; break; case "refresh": out.refresh = chance(factor + 0.1 + (restore > 1.5 ? 0.1 : 0)) ? "party" : "self"; if (out.refresh == "party" && this.tagBattle) { out.refresh = "field"; } out.type = eff; break; case "haze": if (this.tagBattle) { out.haze = randomSample({ self: 4, target: 4, field: 11 }); } else { out.haze = randomSample({ self: 6, target: 6, both: 4, party: 3, oppparty: 3, all: 1 }); } out.type = eff; break; case "protect": if (factor < 0.35) { return out; } out.protect = true; out.priority = 5; out.type = eff; break; case "drain": out.drain = (Math.floor(100 * (0.4 + ((drain + (0.5 * Math.random() - (0.5 * Math.random()))) * 0.09)))/100); if (this.select) { if (this.select.boostDrain) { out.drain *= 1.5; } } if (this.select2) { if (this.select2.boostDrain) { out.drain *= 1.5; } } if (this.select && this.select.grassyterrain && hasType(user.id, "Grass")) { val *= 1.5; } if (this.select2 && this.select2.grassyterrain && hasType(user.id, "Grass")) { val *= 1.5; } out.type = eff; break; case "recoil": if (used.contains("burnout") || used.contains("recoil")) { return out; } if (factor < -0.25 || factor > 0.15) { return out; } out.recoil = true; out.type = eff; break; case "recharge": if (used.contains("recoil") || used.contains("burnout")) { return out; } if (factor > -0.15) { return out; } out.recharge = true; out.type = eff; break; case "burnout": if (used.contains("recoil") || used.contains("recharge") || factor > -0.3) { return out; } buff = {}; buff.buffStat = (category == "physical" ? "atk" : "satk"); buff.buffChance = 1; if (used.contains("buff" + buff.buffStat)) { return out; } var ints = ((chance(0.3 + (burnout/5))) ? 2 : 1); buff.buff = (ints === 2 ? -3 : -2); out.buff = buff; out.burnout = ints; out.type = "buff" + buff.buffStat; break; case "brickBreak": out.brickBreak = true; out.type = eff; break; case "priority": val = Math.round(Math.random() * (5+(2.25*priority)) * (factor + 0.12)); if (val === 0) { return out; } out.priority = Math.min(Math.max(val, -7), 7); out.type = eff; break; case "lowpriority": if (used.contains("priority")) { break; } val = Math.round(Math.random() * 12 * (factor + 0.08)); if (val >= 0) { return out; } out.priority = Math.max(val, -7); out.type = eff; break; case "flinch": if (factor < -0.25) { return out; } val = factor > 0 ? Math.random() * 0.75 * factor + 0.25 : Math.random() * (1+factor) / 4; if (val <= 0.01) { return out; } if (this.select && this.select.electricterrain && hasType(user.id, "Electric")) { val *= 2; } out.flinch = Math.min(1, val); out.type = eff; break; case "critical": if (this.select) { if (this.select.shellArmor) { return out; } } if (this.select2) { if (this.select2.shellArmor) { return out; } } if (factor < -0.275) { return out; } val = factor > 0 ? ((Math.round(16 * (Math.random() + 1) * (Math.random() + 1) * critical * Math.max(critical * 1.8 * Math.random(), 1) * (factor + 0.85)))/3) : ((Math.random() + 0.28) * critical * (6*(1+factor)) / 4); if (val <= 0.01) { return out; } out.critical = Math.min(Math.max(0.06, val/100), 1); out.type = eff; break; case "targetTeam": if (used.contains("wide")) { return out; } out.target = "TEAM"; out.type = "wide"; break; case "targetAll": if (used.contains("wide")) { return out; } out.target = "ALL"; out.type = "wide"; break; case "helpingHand": out.helpingHand = true; out.type = "helpingHand"; break; case "followMe": out.followMe = true; out.type = "followMe"; break; case "reflect": out.reflect = true; out.type = "reflect"; break; case "lightscreen": out.lightscreen = true; out.type = "lightscreen"; break; case "status": val = factor > 0.5 ? 1 : (factor > 0 ? Math.random() * factor + 0.3 : Math.random() * (1+factor) / 4) ; if (val <= 0.01) { return out; } out.statusChance = val; if (this.select && this.select.sun) { out.status = ["sleep", "paralyzed", "burn", "poison"].random(); } else { out.status = ["sleep", "paralyzed", "burn", "freeze", "poison"].random(); } if (bias) { out.status = (bias.sleep && chance(0.35) ? "sleep": out.status); out.status = (bias.paralyze && chance(0.35) ? "paralyzed": out.status); out.status = (bias.burn && chance(0.35) ? "burn": out.status); out.status = (bias.freeze && chance(0.35) ? "freeze": out.status); out.status = (bias.poison && chance(0.35) ? "poison": out.status); } if (out.status === "freeze") { if (!hasType(user.id, "Ice")) { out.statusChance *= 0.7; if (out.statusChance <= 0.01) { return { type: "none" }; } out.statusChance = Math.min(0.5, out.statusChance); } } out.type = eff; break; case "buff": buff = {}; buff.buffStat = ["atk", "def", "satk", "sdef", "spe"].random(); if (this.select && this.select.physBan) { buff.buffStat = ["satk", "sdef", "spe"].random(); } if (this.select && this.select.specBan) { buff.buffStat = ["atk", "def", "spe"].random(); } if (buff.buffStat == "sdef" && ((this.select && this.select.singlespecialstat) || (this.select2 && this.select2.singlespecialstat))) { buff.buffStat = "satk"; } if (used.contains("buff" + buff.buffStat)) { return out; } if (factor > 0) { val = randomSample({ "1": 10, "2": 4 * (1+factor/3), "3": 1 * (0.85+factor) }); } else { val = randomSample({ "-3": 4 * (-factor), "-2": 8 * (-factor), "-1": 12 * (-factor), "1": 1, "2": 0.5 }); } val = parseInt(val, 10); if (this.select && this.select.psychicterrain && hasType(user.id, "Psychic") && val > 0) { val = Math.max(1, Math.min(Math.floor(val * (1.75 * (0.5 + Math.random()))), 3)); } if (buffValues[buff.buffStat] > 0 && val > 0) { if (chance(buffValues[buff.buffStat] * 0.15)) { val += 1; } } val = Math.min(val, 3); buff.buff = parseInt(val, 10); if (damaging) { if (factor > 0 || (factor < 0 && buff.buff < 0)) { buff.buffChance = Math.random() * 0.65 * Math.abs(factor) + 0.35; } else { buff.buffChance = Math.random() * 0.25 * (1-factor); } } else { buff.buffChance = 1; } if (buff.buffChance <= 0.05) { buff.buffChance = 0.05; } out.buff = buff; out.type = "buff" + buff.buffStat; break; case "nerf": nerf = {}; nerf.nerfStat = ["atk", "def", "satk", "sdef", "spe"].random(); if (this.select && this.select.physBan) { nerf.nerfStat = ["satk", "sdef", "spe"].random(); } if (this.select && this.select.specBan) { nerf.nerfStat = ["atk", "def", "spe"].random(); } if (used.contains("nerf" + nerf.nerfStat)) { return out; } if (nerf.nerfStat == "sdef" && ((this.select && this.select.singlespecialstat) || (this.select2 && this.select2.singlespecialstat))) { nerf.nerfStat = "satk"; } if (factor > 0) { val = randomSample({ "-1": 10, "-2": 4 * (1+factor/3), "-3": 1 * (0.85+factor) }); } else { val = randomSample({ "3": 4 * (-factor), "2": 8 * (-factor), "1": 12 * (-factor), "-1": 2, "-2": 1 }); } val = parseInt(val, 10); val = Math.max(val, -3); if (nerfValues[nerf.nerfStat] > 0 && val < 0) { if (chance(nerfValues[nerf.nerfStat] * 0.15)) { val -= 1; } } nerf.nerf = parseInt(val, 10); if (damaging) { if (factor > 0 || (factor < 0 && nerf.nerf > 0)) { nerf.nerfChance = Math.random() * 0.65 * Math.abs(factor) + 0.35; } else { nerf.nerfChance = Math.random() * 0.25 * (1-factor); } } else { nerf.nerfChance = 1; } if (nerf.nerfChance <= 0.04) { return out; } out.nerf = nerf; out.type = "nerf" + nerf.nerfStat; break; } return out; }; Battle2.prototype.isImmuneTo = function(id, condition) { var isImmune = false; var immunities = { sleep: [], paralyzed: ["Electric"], burn: ["Fire"], freeze: ["Ice"], poison: ["Poison", "Steel"] }; if (this.select && (this.select.corrosion || this.select.classicTypes)) { immunities.poison = ["Poison"]; } for (var i = immunities[condition].length; i--; ) { if (hasType(id, immunities[condition][i])) { isImmune = true; break; } } return isImmune; }; Battle2.prototype.chooseNPCMove = function(moves, team, opponent, ind) { var moveChance = {}, move, user, e, o, opp, val, val2, val3, c, totalc, m, eff, affect = opponent.length; alive = 0, oppAlive = 0; var bias = {}; var bias = (ind === 2 ? this.biasNPC : (ind === 4 ? this.biasNPC2 : {})); var isP1 = false, isP2 = false, isP3 = false, isP4 = false; if (ind === 2) { isP2 = true; } if (ind === 4) { isP4 = true; } var self = this; var getBoostCount = function(user) { var c = 0, e; for (e in user.boosts) { if (e == "spe" && self.select && self.select.trickRoom) { c -= user.boosts[e]; } else { c += user.boosts[e]; } } return c; }; for (e = team.length; e--; ) { if (team[e].hp > 0) { alive++; } } for (e = opponent.length; e--; ) { if (opponent[e].hp > 0) { oppAlive++; } } if (alive === 0 || oppAlive === 0) { return false; } for (e in moves) { totalc = 0; move = moves[e]; user = team[move.ownerId]; for (o in opponent) { c = 0; opp = opponent[o]; if (opp.hp > 0) { val = 0; if (move.power) { var inver = ((this.select && this.select.inverted) ? true : false); eff = safari.checkEffective([move.type], [type1(opp.id), type2(opp.id)], inver); dmg = this.damageCalc(user, move, opp, eff, 1, isP1, isP2, isP3, isP4); if (eff > 1 && bias.effectiveness) { c += 30; } var diff = (opp.hp / dmg); var diff2 = (opp.maxhp / dmg) * 0.25; diff = Math.max(diff, diff2); if (diff < 1) { val = 22; } else if (diff < 1.5) { val = 16; } else if (diff < 2) { val = 7; } else if (diff < 3) { val = 4; } else if (diff < 4) { val = 2; } else if (diff < 6) { val = 1; } else if (diff < 8) { val = 0.5; } else { val = 0.1; } if (eff <= 0) { val = -1; affect--; } var spdadv = ((this.getStatValue(user, "spe", 1, null) > this.getStatValue(opp, "spe", 1, null)) ? true : false); if (val > 0) { if (move.priority > 0 && (!spdadv)) { val *= (1 + (move.priority/8)*1.5); } else if ((move.priority < 0 && (spdadv))) { val *= (0.75); } if (move.flinch && (spdadv || move.priority > 0) && (diff > 0.95)) { val *= (1 + move.flinch*2); } if (move.critical) { val *= (1 + move.critical); } } val *= 10; c += val; } if (move.status && val >= 0) { if (opp.condition === "none" && !this.isImmuneTo(opp.id, move.status)) { if (this.select && this.select.guts && (["sleep", "freeze"].indexOf(move.status) === -1)) { c -= move.statusChance * 20; } else { if (!(this.select && this.select.electricterrain && (!hasType(opp.id, "Flying")) && move.status === "sleep")) { c += move.statusChance * 150; } } if (this.select && this.select.hex) { c += move.statusChance * 50; } } } if (move.nerf && val >= 0) { for (m = move.nerf.length; m--; ) { eff = move.nerf[m]; if (eff.nerf > 0) { if (opp.boosts[eff.nerfStat] === 6) { continue; } c -= eff.nerf * 25 * eff.nerfChance; } else { if (opp.boosts[eff.nerfStat] === -6) { continue; } c += -eff.nerf * 70 * eff.nerfChance; } } } if (move.protect) { if (opp.condition === "poison") { c += 120; } if (opp.condition === "burn") { c += 60; } } } totalc += c; } c = totalc; if (move.buff) { for (m = move.buff.length; m--; ) { eff = move.buff[m]; if (eff.buff > 0) { if (user.boosts[eff.buffStat] === 6) { continue; } if (this.select && this.select.trickRoom && eff.buffStat == "spe") { c -= eff.buff * 80 * eff.buffChance; } else { c += eff.buff * 80 * eff.buffChance; } if (bias.stats) { c += eff.buff * 40 * eff.buffChance; } } else { if (user.boosts[eff.buffStat] === -6) { continue; } if (this.select && this.select.trickRoom && eff.buffStat == "spe") { c += eff.buff * 40 * eff.buffChance; } else { c -= eff.buff * 40 * eff.buffChance; } if (bias.stats) { c -= eff.buff * 20 * eff.buffChance; } else if (bias.burnout) { c += eff.buff * 20 * eff.buffChance; } } } } if (this.select) { if (this.select.chillPhysical && move.type == "Dragon") { c += 50; } if (this.select.chillSpecial && move.type == "Ice") { c += 50; } if (this.select.inferno && move.type == "Fire") { c += 30; if (bias.burn) { c += 30; } } if (this.select.scald && move.type == "Water") { c += 30; if (bias.burn) { c += 30; } } if (this.select.zapcannon && move.type == "Electric") { c += 40; if (bias.paralyze) { c += 30; } } if (this.select.toxic && move.type == "Poison") { c += 60; if (bias.poison) { c += 40; } } } if ((move.type === "Water" || move.type === "Grass") && this.select && this.select.naturalcure) { if (["burn", "poison"].contains(user.condition)) { c += 80; } else if (user.condition === "paralyzed") { c += 45; } } if (move.refresh) { if (this.select && this.select.guts) { for (o in team) { if (team[o].hp > 0) { if (team[o] !== user) { if (team[o].condition !== "none") { c += 80; } } } } c -= 70; } else { if (move.refresh === "self") { if (["burn", "poison"].contains(user.condition)) { c += 80; } else if (user.condition === "paralyzed") { c += 45; } else { c = Math.max(c - 50, 0); } } else { for (o in team) { if (team[o].hp > 0) { if (team[o] === user) { if (["burn", "poison"].contains(user.condition)) { c += 80; } else if (user.condition === "paralyzed") { c += 45; } } else { if (team[o].condition !== "none") { c += 80; } } } } } if (this.select && this.select.hex) { c += 100; } } } if (move.haze) { switch (move.haze) { case "self": val = getBoostCount(user); if (val >= 0) { c -= (val + 1) * 16; } else { c += val * 16; } break; case "target": case "oppparty": for (o in opponent) { if (opponent[o].hp > 0) { val = getBoostCount(opponent[o]); if (val > 0) { c += val * 16; } else { c -= (val + 1) * 16; } } } break; case "both": case "field": val = 0; for (o in opponent) { if (opponent[o].hp > 0) { val += getBoostCount(opponent[o]); } } c += ((val/oppAlive) - getBoostCount(user)) * 16; break; case "party": for (o in team) { if (team[o].hp > 0) { val = getBoostCount(team[o]); if (val >= 0) { c -= (val + 1) * 18; } else { c += val * 16; } } } break; case "all": val = 0; val2 = 0; for (o in team) { if (team[o].hp > 0) { val += getBoostCount(team[o]); } } for (o in opponent) { if (opponent[o].hp > 0) { val2 += getBoostCount(opponent[o]); } } c += ((val2/oppAlive) - (val/alive)) * 16; break; } } if (move.restore) { c += (1 + move.restore) * 280 * (1 - (user.hp / user.maxhp)); } if (move.helpingHand) { if (bias.helpingHand) { c += 60; } else { c *= 0.75; } } if (move.reflect) { if (this.side2Field.reflect > 0) { c -= 1; } else { c += 100; if (this.tagBattle) { c += 70; } if (bias.reflect) { c += 70; } } } if (move.lightscreen) { if (this.side2Field.lightscreen > 0) { c -= 1; } else { c += 100; if (this.tagBattle) { c += 70; } if (bias.lightscreen) { c += 70; } } } if (move.status && val >= 0) { if ((move.status == "paralyze") && (bias.paralyze)) { c += 100; } if ((move.status == "sleep") && (bias.sleep)) { c += 100; } if ((move.status == "freeze") && (bias.freeze)) { c += 100; } if ((move.status == "poison") && (bias.poison)) { c += 100; } if ((move.status == "burn") && (bias.burn)) { c += 100; } } if (this.select && this.select.dynamicPunch && move.type == "Fighting" && val >= 0) { c += 45; } var dif = (user.hp / user.maxhp); if (move.drain && val >= 0) { c += 120 * (1 - dif); if (bias.drain) { c += 80 * (1 - dif); } } if (move.critical && val >= 0) { c += 20; if (bias.critical) { c += 60; } if (this.select && this.select.noncritexhaust) { c += 75 * move.critical; } } if (move.recoil && val >= 0) { if (dif > 0.5) { if (bias.recoil) { c += (180 * (dif)); } else { c += (100 * (dif)); } } else { if (bias.recoil) { c += (60 * (dif)); } else { c = Math.min(c - (120 * (1 - dif)), c); } } } if (move.protect) { c += 42; if (bias.protect) { c += 36; } if (this.protectCount2 > 0) { c = c / (this.protectCount2+1); } } if (move.category === "other") { if (bias.support) { c += 60; } else if (bias.aggressive) { c *= 0.75; } } else { if (bias.support) { c *= 0.85; } else if (bias.aggressive) { c *= 1.2; } } c *= (affect / opponent.length); if (c <= 0.1) { c = 0.1; } moveChance[e] = Math.max(Math.round(c * c / 100), 0); } var exclude = [0, 1, 3][alive-1]; var ordered = Object.keys(moveChance).sort(function(a, b){ return moveChance[a] - moveChance[b]; }); while (exclude) { e = ordered.shift(); delete moveChance[e]; exclude--; } return randomSample(moveChance); }; Battle2.prototype.countCondition = function(team, condition) { var c = 0, e; for (e = 0; e < team.length; e++) { if (team[e].hp > 0 && team[e].condition === condition) { c++; } } return c; }; Battle2.prototype.getMoveTypes = function(id) { var num = parseInt(id, 10), m, out = {}, t, moves = fetchMoves(num); if (!moves) { moves = fetchMoves(pokeInfo.species(num)); } for (m = moves.length; m--; ) { t = sys.type(moveType(moves[m])); if (!out.hasOwnProperty(t)) { out[t] = 0; } out[t]++; } if ((this.select && this.select.nostab) || (this.select2 && this.select2.nostab)) { if (out.hasOwnProperty(type1(num))) { out[type1(num)] = 0; } if (out.hasOwnProperty(type2(num))) { out[type2(num)] = 0; } if (this.select && this.select.nostab) { if (this.select.normalcy) { out.Normal = 0; } if (this.select.draconian) { out.Dragon = 0; } if (this.select.mechanical) { out.Steel = 0; } } if (this.select2 && this.select2.nostab) { if (this.select2.normalcy) { out.Normal = 0; } if (this.select2.draconian) { out.Dragon = 0; } if (this.select2.mechanical) { out.Steel = 0; } } } if (this.select && this.select.physBan && this.select.categorySplit) { out.Normal = 0; out.Fighting = 0; out.Flying = 0; out.Bug = 0; out.Rock = 0; out.Ground = 0; out.Steel = 0; out.Ghost = 0; out.Poison = 0; } else if (this.select && this.select.specBan && this.select.categorySplit) { out.Grass = 0; out.Water = 0; out.Fire = 0; out.Ice = 0; out.Electric = 0; out.Psychic = 0; out.Dragon = 0; out.Dark = 0; out.Fairy = 0; } return out; }; Battle2.prototype.getMovePowers = function(id) { var num = parseInt(id, 10), m, out = {}, t, k, moves = fetchMoves(num); if (!moves) { moves = fetchMoves(pokeInfo.species(num)); } for (var o in effectiveness) { out[o] = 40; } for (m = moves.length; m--; ) { k = getMoveBP(moves[m]); if (k !== "---") { t = sys.type(moveType(moves[m])); out[t] = (Math.max(out[t], parseInt(k, 10))); } } return out; }; Battle2.prototype.getBiteTypes = function(p) { var out = [], mt; var moves = fetchMoves(p); var set = [422, 423, 424, 242, 665, 305, 158, 44]; for (var i in set) { if (!(moves.contains(set[i]))) { continue; } mt = sys.type(moveType(set[i])); if (out.contains(mt)) { continue; } out.push(mt); } return out; }; Battle2.prototype.getWeatherMoves = function(id) { var num = parseInt(id, 10); var out = {thunder: false, solar: false, weather: false}; if (canLearnMove(pokeInfo.species(num), 87)) { out.thunder = true; } if (canLearnMove(pokeInfo.species(num), 76)) { out.solar = true; } if (canLearnMove(pokeInfo.species(num), 311)) { out.weather = true; } }; Battle2.prototype.getMoveSet = function(id, moves) { var num = parseInt(id, 10), m, out = 0.1, val = 0, set = []; switch (moves) { case "drain": set = [71, 72, 73, 141, 202, 577, 409, 532, 576, 564, 138]; break; case "recoil": set = [36, 38, 66, 344, 413, 452, 457, 528, 543, 612, 26, 136]; break; case "critical": set = [163, 75, 444, 238, 152, 348, 400, 421, 177, 314, 529, 440, 454, 427]; break; case "priority": set = [98, 453, 582, 418, 410, 183, 245, 252, 632, 425, 389]; break; case "restore": set = [105, 236, 234, 235, 275, 312, 215, 355, 273, 392, 208, 645, 135, 662, 650]; break; case "burnout": set = [434, 315, 437, 276, 354, 623]; break; } for (m = set.length; m--; ) { if (canLearnMove(pokeInfo.species(num), set[m])) { val++; } } out = 8 * (Math.min(((val + 1)/16), 0.5)); if (val === 0) { return 0.1; } return out; }; Battle2.prototype.getBuffValues = function(id) { var num = parseInt(id, 10), m, out = { "atk": 0, "def": 0, "satk": 0, "sdef": 0, "spe": 0 }; if (canLearnMove(pokeInfo.species(num), 339)) { out["atk"] = 1; out["def"] = 1; } if (canLearnMove(pokeInfo.species(num), 347)) { out["satk"] = 1; out["sdef"] = 1; } if (canLearnMove(pokeInfo.species(num), 322)) { out["def"] = 1; out["sdef"] = 1; } if (canLearnMove(pokeInfo.species(num), 74)) { out["atk"] = 1; out["satk"] = 1; } if (canLearnMove(pokeInfo.species(num), 349)) { out["atk"] = 1; out["spe"] = 1; } if (canLearnMove(pokeInfo.species(num), 483)) { out["satk"] = 1; out["sdef"] = 1; out["spe"] = 1; } if (canLearnMove(pokeInfo.species(num), 336)) { out["atk"] = 1; } if (canLearnMove(pokeInfo.species(num), 14)) { out["atk"] = 2; } if (canLearnMove(pokeInfo.species(num), 106)) { out["def"] = 1; } if (canLearnMove(pokeInfo.species(num), 334)) { out["def"] = 2; } if (canLearnMove(pokeInfo.species(num), 151)) { out["def"] = 2; } if (canLearnMove(pokeInfo.species(num), 508)) { out["satk"] = 1; out["spe"] = 2; } if (canLearnMove(pokeInfo.species(num), 417)) { out["satk"] = 2; } if (canLearnMove(pokeInfo.species(num), 294)) { out["satk"] = 3; } if (canLearnMove(pokeInfo.species(num), 133)) { out["sdef"] = 2; } if (canLearnMove(pokeInfo.species(num), 97)) { out["spe"] = 2; } if (canLearnMove(pokeInfo.species(num), 397)) { out["spe"] = 2; } return out; }; Battle2.prototype.getNerfValues = function(id) { var num = parseInt(id, 10), m, out = { "atk": 0, "def": 0, "satk": 0, "sdef": 0, "spe": 0 }; if (canLearnMove(pokeInfo.species(num), 600)) { out["atk"] = 1; out["satk"] = 1; } if (canLearnMove(pokeInfo.species(num), 45)) { out["atk"] = 1; } if (canLearnMove(pokeInfo.species(num), 560)) { out["atk"] = 1; } if (canLearnMove(pokeInfo.species(num), 204)) { out["atk"] = 2; } if (canLearnMove(pokeInfo.species(num), 297)) { out["atk"] = 2; } if (canLearnMove(pokeInfo.species(num), 43)) { out["def"] = 1; } if (canLearnMove(pokeInfo.species(num), 103)) { out["def"] = 2; } if (canLearnMove(pokeInfo.species(num), 555)) { out["satk"] = 1; } if (canLearnMove(pokeInfo.species(num), 445)) { out["satk"] = 2; } if (canLearnMove(pokeInfo.species(num), 718)) { out["sdef"] = 1; } if (canLearnMove(pokeInfo.species(num), 313)) { out["sdef"] = 2; } if (canLearnMove(pokeInfo.species(num), 178)) { out["spe"] = 2; } if (canLearnMove(pokeInfo.species(num), 81)) { out["spe"] = 2; } return out; }; Battle2.prototype.getHpPercent = function(idnum) { var out = [], t, team = idnum === this.idnum2 ? this.originalTeam2 : this.originalTeam1; for (t = 0; t < team.length; t++) { out.push(team[t].hp/team[t].maxhp); } return out; }; Battle2.prototype.finishBattle = function(winId) { var winnerName = "Tie"; var winnerID = ""; var loser; if (this.tagBattle) { if (winId === 1 || winId === 3) { if (this.oneOnTwo) { winnerName = this.name1; winnerID = this.idnum1; } else { winnerName = (this.name1 + " & " + this.name3); winnerID = this.idnum1 + " & " + this.idnum3; } loser = (this.name2 + " & " + this.name4); } else if (winId === 2 || winId === 4) { winnerName = (this.name2 + " & " + this.name4); winnerID = this.idnum2 + " & " + this.idnum4; loser = (this.name1 + " & " + this.name3); } } else { if (winId === 1) { winnerName = this.name1; winnerID = this.idnum1; loser = this.name2; } else if (winId === 2) { winnerName = this.name2; winnerID = this.idnum2; loser = this.name1; } } this.sendToViewers("" + winnerName + " defeated " + loser + " in a battle!", true); if (this.npcBattle && this.postBattle) { var extraArgs = {}; extraArgs.turn = this.turn; extraArgs.loseMsg = this.loseMsg; extraArgs.winMsg = this.winMsg; if (this.tagBattle) { var npcSurviving = this.team2.filter(function(e) { return e.hp > 0 }).length + this.team4.filter(function(e) { return e.hp > 0 }).length; this.postArgs.defeated = Math.max(0, (5 - npcSurviving) * (3/5)); } else { this.postArgs.defeated = Math.max(0, 3 - this.team2.filter(function(e) { return e.hp > 0 }).length); } this.postBattle(this.name1, winnerID === this.idnum1, this.getHpPercent(this.idnum1), this.postArgs, this.viewers, extraArgs); } this.finished = true; }; Battle2.prototype.sendMessage = function(name, msg) { var id = sys.id(name); if (id && sys.isInChannel(id, safchan)) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } }; Battle2.prototype.sendToViewers = function(msg, bypassUnwatch, exclude) { var e; for (e in this.viewers) { if (exclude && exclude.contains(this.viewers[e].toLowerCase())) { continue; } this.sendMessage(this.viewers[e], msg); } if (bypassUnwatch) { if (!this.viewers.contains(this.name1.toLowerCase()) && (!exclude || !exclude.contains(this.name1.toLowerCase()))) { this.sendMessage(this.name1, msg); } if (!this.npcBattle && !this.viewers.contains(this.name2.toLowerCase()) && (!exclude || !exclude.contains(this.name2.toLowerCase()))) { this.sendMessage(this.name2, msg); } } }; Battle2.prototype.abort = function(src, loser, isForfeit) { var winner = "Tie"; var winnerID = ""; if (loser.toLowerCase() == this.name1.toLowerCase()) { winner = this.name2; winnerID = this.idnum2; } else if (loser.toLowerCase() == this.name2.toLowerCase()) { winner = this.name1; winnerID = this.idnum1 } if (isForfeit) { this.sendToViewers("" + loser + " forfeited! " + (winner !== "Tie" ? winner + " won!" : "") + "", true); } else { this.sendToViewers("This battle was aborted by " + sys.name(src) + "! " + (winner !== "Tie" ? winner + " was declared the winner!" : "") + "", true); } if (this.npcBattle && this.postBattle) { var extraArgs = { turn: this.turn, winMsg: this.winMsg, loseMsg: this.loseMsg }; if (this.tagBattle) { var npcSurviving = this.team2.filter(function(e) { return e.hp > 0 }).length + this.team4.filter(function(e) { return e.hp > 0 }).length; this.postArgs.defeated = Math.max(0, (5 - npcSurviving) * (3/5)); } else { this.postArgs.defeated = Math.max(0, 3 - this.team2.filter(function(e) { return e.hp > 0 }).length); } this.postBattle(this.name1, winnerID === this.idnum1, this.getHpPercent(this.idnum1), this.postArgs, this.viewers, extraArgs); } this.finished = true; }; Battle2.prototype.isInBattle = function(name) { var player = getAvatar(sys.id(name)); if (!player) return false; var idnum = player.idnum; return this.idnum1 == idnum || ((!this.npcBattle) && (this.idnum2 == idnum || this.idnum3 == idnum || this.idnum4 == idnum)); }; /* Auctions */ this.createAuction = function(src, data) { if (!validPlayers("self", src, data)) { return; } var player = getAvatar(src); var reason = "start an auction"; if (cantBecause(src, reason, ["tutorial"])) { return; } var info = data.split(":"); if (info.length < 3) { safaribot.sendMessage(src, "To create an auction, type /auction product:startingOffer:minimumBid.", safchan); safaribot.sendMessage(src, "Example: /auction Bulbasaur:550:10 to auction a Bulbasaur, with offers starting at $550 and bids at least $10 higher than current offer.", safchan); if (player.cooldowns.auction > now()) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.auction) + " before starting a new auction!", safchan); } return; } if (player.tradeban > now()) { safaribot.sendMessage(src, "You are banned from starting auctions for " + timeLeftString(player.tradeban) + "!", safchan); return; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only start an auction after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (player.cooldowns.auction > now()) { safaribot.sendMessage(src, "Please wait " + timeLeftString(player.cooldowns.auction) + " before starting a new auction!", safchan); return; } if (cantBecause(src, reason, ["wild", "contest", "auction", "battle", "precontest", "event", "pyramid"])) { return; } var product = removeInvisibleStuff(toStuffObj(info[0])); var startingOffer = toMoney(info[1]); var minBid = toMoney(info[2]); var input = toStuffInput(product); if (!input) { safaribot.sendMessage(src, "Invalid format! Use /auction product:startingOffer:minimumBid.", safchan); return; } if (isNaN(startingOffer) || startingOffer < 1) { safaribot.sendMessage(src, "Please type a valid number for the starting offer!", safchan); return; } if (isNaN(minBid) || minBid < 1) { safaribot.sendMessage(src, "Please type a valid number for the minimum bid raise!", safchan); return; } var errors = [], minPrice = 0, info, untradable = [], invalidAmt = []; for (var e in product) { info = translateAsset(e); info.amount = product[e]; if (info.type == "money") { errors.push("You cannot auction money!"); } else if (info.type == "poke") { minPrice += getPrice(info.id, info.shiny) * info.amount; } else if (info.type == "item") { if (info.amount <= 0) { invalidAmt.push(info.name); } if (!itemData[info.id].tradable) { untradable.push(info.name); } } } if (untradable.length > 0) { errors.push(readable(untradable) + " cannot be traded!"); } if (invalidAmt.length > 0) { errors.push("Invalid amount found for " + readable(invalidAmt) + "!"); } if (errors.length === 0) { info = canLoseStuff(player, product); if (!info.result) { if (info.missing.length > 0) { errors.push("You don't have " + readable(info.missing) + "!"); } if (info.shop.length > 0) { errors.push("You need to remove " + readable(info.shop) + " from your shop!"); } if (info.mega.length > 0) { errors.push(readable(info.mega) + " cannot be sold while mega-evolved!"); } if (info.tradeReq.length > 0) { errors.push("You must have a minimum amount of the following items to sell them: " + readable(info.tradeReq)); } if (info.partyEmpty) { errors.push("You cannot auction all Pokémon in your party/shop!"); } if (info.starter) { errors.push("You cannot auction your starter Pokémon!"); } } if (startingOffer < minPrice) { errors.push("Starting offer for the Pokémon being sold must be at least $" + addComma(minPrice) + "!"); } } if (errors.length > 0) { safaribot.sendMessage(src, "Auction couldn't be started due to the following reason(s):", safchan); for (e = 0; e < errors.length; e++) { safaribot.sendMessage(src, errors[e], safchan); } return; } if (minBid > startingOffer / 2) { safaribot.sendMessage(src, "Minimum bid raise can't be more than half of the starting offer ($" + Math.floor(startingOffer/2) + ")!", safchan); return; } if (startingOffer + minBid >= moneyCap) { safaribot.sendMessage(src, "Starting offer plus minimum bid raise must be lower than $" + addComma(moneyCap) + "!", safchan); return; } var auction = new Auction(src, input, startingOffer, minBid); currentAuctions.push(auction); player.cooldowns.auction = now() + 15 * 60 * 1000; this.saveGame(player); }; this.joinAuction = function(src, data) { if (!validPlayers("both", src, data)) { return; } var player = getAvatar(src); var reason = "join an auction"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only join an auction after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (player.tradeban > now()) { safaribot.sendMessage(src, "You are banned from joining auctions for " + timeLeftString(player.tradeban) + "!", safchan); return; } var name = sys.name(src); var tName = sys.name(sys.id(data)).toLowerCase(); if (preparationFirst && preparationFirst.toLowerCase() === name.toLowerCase() && preparationThrows.hasOwnProperty(name.toLowerCase())) { safaribot.sendMessage(src, "You will be unable to catch the Pokémon you just attracted if you join an auction now! ", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid"])) { return; } var auction, b; for (b in currentAuctions) { auction = currentAuctions[b]; if (auction.host == tName) { auction.join(src); return; } } safaribot.sendMessage(src, "This person is not hosting any auction!", safchan); }; this.quitAuction = function(src, data) { if (!validPlayers("self", src, data)) { return; } var name = sys.name(src), auction, b; for (b in currentAuctions) { auction = currentAuctions[b]; if (auction.isInAuction(name)) { auction.leave(src); return; } } safaribot.sendMessage(src, "You are not participating in any auction!", safchan); }; this.bidAuction = function(src, data) { if (!validPlayers("self", src, data)) { return; } var name = sys.name(src), auction, b; for (b in currentAuctions) { auction = currentAuctions[b]; if (auction.isInAuction(name)) { auction.makeOffer(src, data); return; } } safaribot.sendMessage(src, "You are not participating in any auction!", safchan); }; this.isInAuction = function(name) { for (var b in currentAuctions) { if (currentAuctions[b].isInAuction(name)) { return true; } } return false; }; this.isHostingAuction = function(name) { for (var b in currentAuctions) { if (currentAuctions[b].host == name.toLowerCase()) { return true; } } return false; }; function Auction(src, product, starting, minBid) { this.host = sys.name(src).toLowerCase(); this.hostName = sys.name(src); this.members = []; this.product = product; this.startingOffer = starting; this.minBid = minBid; this.currentOffer = starting - minBid; this.currentBidder = null; this.unbiddedTurns = 0; this.suddenDeath = false; this.suddenDeathOffers = {}; this.confirmBid = {}; this.turn = 0; this.finished = false; this.changed = false; this.productName = translateStuff(product, true); var joinCommand = "/join " + this.hostName; sys.sendAll("", safchan); safaribot.sendHtmlAll(this.hostName + " is starting an auction! The product is " + this.productName + ", with bids starting at $" + addComma(starting) + " (Minimum bid raise: $" + addComma(minBid) + ")! Type " + link(joinCommand) + " to join the auction!", safchan); safaribot.sendMessage(src, "You started an auction! The auction ends when the current bid is not matched after 3 turns or if no one makes a bid for the first 40 seconds!", safchan); sys.sendAll("", safchan); } Auction.prototype.nextTurn = function() { if (this.turn === 0) { this.turn++; this.sendToViewers("[Auction] Preparations complete, the auction will start soon!"); return; } if (this.finished) { return; } if (this.turn == 5 && !this.currentBidder) { this.sendToViewers(""); this.sendToViewers("[Auction] Auction cancelled because no bids have been made!"); this.sendToViewers(""); this.finished = true; return; } if (this.turn > 20) { if (this.turn === 21) { if (this.changed) { this.unbiddedTurns = 0; } this.unbiddedTurns++; if (this.checkWinner()) { return; } this.sendToViewers(""); this.sendToViewers("[Auction] No winner after 20 turns! Auction will now proceed to sudden death mode!"); this.sendToViewers("[Auction] All participants can make one last secret bid. All offers will be revealed simultaneously. Highest offer wins."); this.sendToViewers(""); this.turn++; this.suddenDeath = true; return; } if (this.turn === 22) { this.turn++; this.sendToViewers(""); this.sendToViewers("[Auction] Please make your final bid if you haven't made it yet!"); this.sendToViewers(""); return; } for (var e in this.suddenDeathOffers) { if (this.suddenDeathOffers[e] > this.currentOffer) { this.currentOffer = this.suddenDeathOffers[e]; this.currentBidder = e; } } this.finishAuction(); } else { if (this.changed) { this.unbiddedTurns = 0; } if (this.currentBidder) { this.unbiddedTurns++; if (this.checkWinner()) { return; } this.sendToViewers(""); this.sendToViewers("[Auction] Current highest bid is $" + addComma(this.currentOffer) + " by " + this.currentBidder.toCorrectCase() + "!"); if (this.unbiddedTurns > 0) { this.sendToViewers("[Auction] If no one bids more, " + this.currentBidder.toCorrectCase() + " will win the auction in " + plural(4 - this.unbiddedTurns, "turn") + "!"); } this.sendToViewers(""); } this.changed = false; } this.turn++; }; Auction.prototype.checkWinner = function() { if (this.unbiddedTurns >= 4) { this.finishAuction(); return true; } return false; }; Auction.prototype.finishAuction = function() { this.sendToViewers(""); this.sendToViewers("[Auction] The auction is finished! " + this.currentBidder.toCorrectCase() + " takes " + this.productName + " for $" + addComma(this.currentOffer) + "!"); if (this.suddenDeath && Object.keys(this.suddenDeathOffers).length > 0) { var allOffers = []; for (var e in this.suddenDeathOffers) { if (this.suddenDeathOffers.hasOwnProperty(e)) { allOffers.push(e.toCorrectCase() + " ($" + addComma(this.suddenDeathOffers[e]) + ")"); } } this.sendToViewers("[Auction] All offers: " + allOffers.join(", ")); } var host = getAvatarOff(this.host); var winner = getAvatarOff(this.currentBidder); winner.money -= this.currentOffer; host.money += this.currentOffer; safari.updateEconomyData(this.currentOffer, "playerAuction"); var hasRare = false, id; var info = toStuffObj(this.product); giveStuff(winner, info); for (var e in info) { if (!hasRare) { id = translateAsset(e); if (id.type === "poke" && isRare(id.id)) { hasRare = true; } } info[e] = -info[e]; } giveStuff(host, info); safari.saveGame(host); safari.saveGame(winner); var id = sys.id(this.host); if (id) { safaribot.sendMessage(id, "You received $" + addComma(this.currentOffer) + " from " + this.currentBidder.toCorrectCase() + " for your " + this.productName + "!", safchan); } id = sys.id(this.currentBidder); if (id) { safaribot.sendMessage(id, "You bought " + this.productName + " from " + this.hostName + " for $" + addComma(this.currentOffer) + "!", safchan); } this.sendToViewers(""); this.finished = true; sys.appendToFile(auctionLog, now() + "|||" + this.hostName + "::" + 1 + "::" + this.productName + "::" + this.currentOffer + "|||" + this.currentBidder.toCorrectCase() + "\n"); if (this.currentOffer >= 10000 || hasRare) { sys.appendToFile(rareTradeLog, now() + "|||" + this.currentBidder.toCorrectCase() + " won " + this.hostName + "'s auction for " + this.productName + " by paying $" + addComma(this.currentOffer) + "\n"); } }; Auction.prototype.join = function(src) { var out = canReceiveStuff(getAvatar(src), this.product); if (!out.result) { safaribot.sendMessage(src, "You cannot join this auction because you can receive at most " + readable(out.limit) + "!", safchan); return; } this.sendToViewers(sys.name(src) + " has joined the auction!"); this.members.push(sys.name(src).toLowerCase()); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "You joined " + this.hostName + "'s auction for " + this.productName + ". Type /bid [value] to make your offer, or " + link("/leave") + " to quit the auction.", safchan); if (this.currentBidder) { safaribot.sendMessage(src, "The current offer is $" + addComma(this.currentOffer) + ". You can only make offers at least $" + addComma(this.minBid) + " higher than the current offer.", safchan); } else { safaribot.sendMessage(src, "No offer has been made yet, so you can bid the starting offer ($" + addComma(this.currentOffer + this.minBid) + "). Further offers must be at least $" + addComma(this.minBid) + " higher than the current one.", safchan); } sys.sendMessage(src, "", safchan); }; Auction.prototype.leave = function(src) { var name = sys.name(src).toLowerCase(); if (this.host == name) { safaribot.sendMessage(src, "You can't leave your own auction!", safchan); return; } if (this.currentBidder == name) { safaribot.sendMessage(src, "You can't leave the auction while your bid is the highest one!", safchan); return; } if (this.suddenDeath) { safaribot.sendMessage(src, "You can't leave the auction while it's in sudden death mode!", safchan); return; } this.members.splice(this.members.indexOf(name), 1); this.sendToViewers(sys.name(src) + " left the auction!"); safaribot.sendMessage(src, "You left " + this.hostName + "'s auction!", safchan); }; Auction.prototype.removePlayer = function(src, reason) { var name = sys.name(src).toLowerCase(); if (this.isInAuction(name)) { if (this.host == name) { this.sendToViewers("This auction was cancelled because the host " + reason + "!"); this.finished = true; } else { if (this.currentBidder == name) { this.sendToViewers("This auction was cancelled because the current highest bidder " + reason + "!"); safaribot.sendMessage(sys.id(this.host), "Your auction was cancelled, but you can start a new one immediately!", safchan); this.finished = true; var player = getAvatarOff(this.host); if (player) { player.cooldowns.auction = 0; } } else { this.sendToViewers(sys.name(src) + " was removed from the auction!"); this.members.splice(this.members.indexOf(name), 1); } } } }; Auction.prototype.makeOffer = function(src, bid) { if (this.finished) { return; } var player = getAvatar(src); var id = sys.name(src).toLowerCase(); if (id == this.host) { safaribot.sendMessage(src, "You can't bid in your own auction!", safchan); return; } if (this.turn === 0) { safaribot.sendMessage(src, "Please wait a moment before making a bid!", safchan); return; } var offer = toMoney(bid); if (isNaN(offer) || offer < 1) { safaribot.sendMessage(src, "Please offer a valid value!", safchan); return; } if (offer < this.currentOffer + this.minBid) { safaribot.sendMessage(src, "You must offer a minimum of $" + addComma(this.currentOffer + this.minBid) + "!", safchan); return; } if (isNaN(parseInt(player.money), 10) || player.money < 0) { sys.appendToFile(miscLog, now() + "|||" + player.id.toCorrectCase() + "|||had the invalid value " + JSON.stringify(player.money) + " for their money\n"); safari.sanitize(player); safaribot.sendMessage(src, "You found a hole in your pocket! All your money is gone! Contact a Safari Auth for clarification.", safchan); return; } if (player.money < offer) { safaribot.sendMessage(src, "You do not have $" + addComma(offer) + "!", safchan); return; } if (offer >= this.currentOffer * 6 && (!(id in this.confirmBid) || this.confirmBid[id] != offer)) { safaribot.sendMessage(src, "Do you really want to offer $" + addComma(offer) + " or was that a typo? If you really want to offer that, make that bid again!", safchan); this.confirmBid[id] = offer; return; } delete this.confirmBid[id]; if (this.suddenDeath) { safaribot.sendMessage(src, "[Auction] You are offering $" + addComma(offer) + " for " + this.productName + "!", safchan); this.suddenDeathOffers[id] = offer; } else { if (this.currentBidder === id) { safaribot.sendMessage(src, "You cannot bid when your offer is the highest one!", safchan); return; } this.sendToViewers(""); this.sendToViewers("[Auction] " + sys.name(src) + " is offering $" + addComma(offer) + " for " + this.productName + "!"); if (id !== this.currentBidder) { this.changed = true; } this.currentBidder = id; this.currentOffer = offer; } }; Auction.prototype.sendMessage = function(name, msg) { var id = sys.id(name); if (id) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } }; Auction.prototype.sendToViewers = function(msg) { var e; for (e in this.members) { this.sendMessage(this.members[e], msg); } this.sendMessage(this.host, msg); }; Auction.prototype.isInAuction = function(name) { return this.host == name.toLowerCase() || this.members.contains(name.toLowerCase()); }; /* Trades */ this.offerTrade = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); var reason = "trade"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (player.tradeban > now()) { safaribot.sendMessage(src, "You are banned from trading for " + timeLeftString(player.tradeban) + "!", safchan); return; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only trade after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } var userName = sys.name(src).toLowerCase(); var lData = data.toLowerCase(); if (lData === "cancel") { if (userName in tradeRequests) { safaribot.sendMessage(src, "You cancelled your trade request with " + tradeRequests[userName].target.toCorrectCase() + "!", safchan); delete tradeRequests[userName]; } else { safaribot.sendMessage(src, "You have no pending trades initiated by you!", safchan); } return; } if (!player.options.trading) { safaribot.sendHtmlMessage(src, "You currently have trades disabled! Use " + link("/options trade:on") + " to enable them!", safchan); return; } var info = data.split(":"); if (info.length < 3) { safaribot.sendMessage(src, "To trade Pokémon with another player, use /trade [Player]:[Your Offer]:[Your Request].", safchan); safaribot.sendHtmlMessage(src, "You can trade a Pokémon (type the name or number), money (type $150) or item (type @master). To trade multiple items, type " + toColor("Pichu,3@honey,$200", "blue") + " in the [Your Offer] or [Your Request] spots.", safchan); return; } var autoCancel, isSameUser; var targetName = info[0].toLowerCase(); if (userName in tradeRequests) { if (tradeRequests[userName].target.toLowerCase() === targetName) { isSameUser = true; } autoCancel = true; } if (!validPlayers("target", src, info[0].toLowerCase())) { return; } var targetId = sys.id(targetName); var target = getAvatar(targetId); if (target.tradeban > now() || target.tutorial.inTutorial) { safaribot.sendMessage(src, "This person cannot receive trade requests right now!", safchan); return; } var allowShared = allowedSharedIPNames.contains(targetName) || allowedSharedIPNames.contains(userName) || sys.ip(src) === "::1%0"; if (info[0].toLowerCase() == userName || (sys.ip(targetId) === sys.ip(src) && !allowShared)) { //safaribot.sendMessage(src, "You can't trade with yourself!", safchan); //return; } if (target.records.pokesCaught < 4) { safaribot.sendMessage(src, "This person cannot trade yet!", safchan); return; } if (!target.options.trading) { safaribot.sendMessage(src, "This person is not accepting any trade right now!", safchan); return; } if (target.playerBlacklist.contains(player.idnum)) { safaribot.sendMessage(src, "This person is not accepting trade requests from you right now!", safchan); return; } var offer = info[1].toLowerCase(); var request = info[2].toLowerCase(); var offerObj = removeInvisibleStuff(toStuffObj(offer)); var requestObj = removeInvisibleStuff(toStuffObj(request)); var valid = validateStuff(offer); if (valid.length > 0) { safaribot.sendMessage(src, "The following are not valid Pokémon, item or money: " + readable(valid) + "!", safchan); return; } valid = validateStuff(request); if (valid.length > 0) { safaribot.sendMessage(src, "The following are not valid Pokémon, item or money: " + readable(valid) + "!", safchan); return; } var offerType = this.isValidTrade(src, offerObj, "offer", requestObj); if (!offerType) { return; } var requestType = this.isValidTrade(src, requestObj, "request", offerObj); if (!requestType) { return; } if (isNaN(parseInt(player.money), 10) || player.money < 0) { sys.appendToFile(miscLog, now() + "|||" + player.id.toCorrectCase() + "|||had the invalid value " + JSON.stringify(player.money) + " for their money\n"); this.sanitize(player); safaribot.sendMessage(src, "You found a hole in your pocket! All your money is gone! Contact a Safari Auth for clarification.", safchan); return; } var offerName = translateStuff(offerObj, true); var requestName = translateStuff(requestObj, true); var offerInput = toStuffInput(offerObj); var reqInput = toStuffInput(requestObj); if (offerType == "money" && requestType == "money") { safaribot.sendMessage(src, "You cannot trade money for money!", safchan); return; } var identical = {}; for (var i in offerObj) { if (requestObj.hasOwnProperty(i)) { identical[i] = offerObj[i]; } } if (Object.keys(identical).length > 0) { var rejected = []; identical = toStuffInput(identical).split(","); for (i = 0; i < identical.length; i++) { rejected.push(translateAsset(identical[i]).name); } safaribot.sendMessage(src, "You cannot request the same Pokémon/Item/Money that you offer! Please remove " + readable(rejected) + " from the offer or from the request!", safchan); return; } var blacklisted = []; for (i in offerObj) { if (i !== "$" && player.tradeBlacklist.contains(i)) { blacklisted.push(mapAssetName(i)); } } if (blacklisted.length > 0) { safaribot.sendHtmlMessage(src, "You need to remove " + readable(blacklisted.map(tradeblockRemove)) + " from your trade blacklist before you can trade them!", safchan); return; } blacklisted = []; for (i in requestObj) { if (i !== "$" && target.tradeBlacklist.contains(i)) { blacklisted.push(mapAssetName(i)); } } if (blacklisted.length > 0) { safaribot.sendMessage(src, sys.name(targetId) + " does not want to trade " + readable(blacklisted) + "!", safchan); return; } if (cantBecause(src, reason, ["wild", "contest", "auction", "battle", "event", "pyramid", "baking"])) { safari.addPendingActive(player.id, "offerTrade", data, ["wild", "contest", "auction", "battle", "event", "pyramid", "baking"]); safaribot.sendMessage(src, "Your trade offer of " + offerName + " to " + sys.name(targetId) + " for their " + requestName + " will be sent at the next opportunity!", safchan); return; } if (!this.canTrade(src, offerObj)) { return; } sys.sendMessage(src, "" , safchan); sys.sendMessage(targetId, "" , safchan); if (autoCancel) { if (isSameUser) { safaribot.sendHtmlMessage(src, "You cancelled your previous request with " + tradeRequests[userName].target.toCorrectCase() + " to send a new trade request.", safchan); } else { safaribot.sendHtmlMessage(src, "You cancelled your trade with " + tradeRequests[userName].target.toCorrectCase() + " to start a new trade with " + targetName.toCorrectCase() + ".", safchan); } delete tradeRequests[userName]; } safaribot.sendMessage(src, "You are offering " + offerName + " to " + sys.name(targetId) + " for their " + requestName+ "!" , safchan); safaribot.sendMessage(targetId, sys.name(src) + " is offering you " + offerName + " for your " + requestName + "!" , safchan); if (tradeRequests.hasOwnProperty(targetName) && tradeRequests[targetName].target === userName) { var req = tradeRequests[targetName]; if (objectEquals(offerObj, toStuffObj(req.request)) && objectEquals(requestObj, toStuffObj(req.offer))) { if (!this.canTrade(targetId, requestObj)) { safaribot.sendMessage(src, "Trade cancelled because " + sys.name(targetId) + " couldn't fulfill their offer." , safchan); safaribot.sendMessage(targetId, "Trade cancelled because you couldn't fulfill your offer." , safchan); delete tradeRequests[targetName]; return; } if (!this.canReceiveTrade(src, targetId, offerObj, requestObj)) { delete tradeRequests[targetName]; return; } if (!this.canReceiveTrade(targetId, src, requestObj, offerObj)) { delete tradeRequests[targetName]; return; } if ("@battery" in offerObj || "@battery" in requestObj) { this.dailyReward(src, getDay(now())); this.dailyReward(targetId, getDay(now())); } var hasRare = false; var inverted1 = {}, inverted2 = {}, asset; for (var e in offerObj) { inverted2[e] = -offerObj[e]; } for (e in requestObj) { inverted1[e] = -requestObj[e]; } for (e in inverted1) { if (!(e in offerObj)) { offerObj[e] = 0; } offerObj[e] += inverted1[e]; } for (e in inverted2) { if (!(e in requestObj)) { requestObj[e] = 0; } requestObj[e] += inverted2[e]; } var out1 = readable(giveStuff(player, requestObj, true, true).gained); var out2 = readable(giveStuff(target, offerObj, true).gained); for (e in offerObj) { asset = translateAsset(e); if (asset.type === "item") { this.updateShop(player, e.substr(1)); } else if (asset.type === "poke") { if (isRare(asset.id)) { hasRare = true; } } else if (asset.type === "money" && offerObj[e] >= 10000) { hasRare = true; } } for (e in requestObj) { asset = translateAsset(e); if (asset.type === "item") { this.updateShop(target, e.substr(1)); } else if (asset.type === "poke") { if (isRare(asset.id)) { hasRare = true; } } else if (asset.type === "money" && requestObj[e] >= 10000) { hasRare = true; } } this.saveGame(player); this.saveGame(target); safaribot.sendMessage(src, "You traded your " + out2 + " for " + sys.name(targetId) + "'s " + out1 + "!", safchan); safaribot.sendMessage(targetId, "You traded your " + out1 + " for " + sys.name(src) + "'s " + out2 + "!", safchan); sys.sendMessage(src, "" , safchan); sys.sendMessage(targetId, "" , safchan); delete tradeRequests[targetName]; var p1 = sys.name(src); var p2 = sys.name(targetId); sys.appendToFile(tradeLog, now() + "|||" + p1 + "::" + out2 + "|||" + p2 + "::" + out1 + "|||" + "/undo {0}:{1}:{2}:{3}".format(p1, p2, offerInput, reqInput) + "\n"); if (hasRare) { sys.appendToFile(rareTradeLog, now() + "|||" + sys.name(src) + "'s " + out2 + " <--> " + sys.name(targetId) + "'s " + out1 + "\n"); } } else { var acceptCommand = "/trade " + sys.name(src) + ":" + reqInput + ":" + offerInput; safaribot.sendMessage(src, "You sent a counter-offer to " + sys.name(targetId) + "!" , safchan); safaribot.sendHtmlMessage(targetId, sys.name(src) + " sent you a counter-offer. To accept it, type " + link(acceptCommand, false, true) + ".", safchan); sys.sendMessage(src, "" , safchan); sys.sendMessage(targetId, "" , safchan); delete tradeRequests[targetName]; tradeRequests[userName] = { target: targetName, offer: offerInput, request: reqInput }; } } else { var acceptCommand = "/trade " + sys.name(src) + ":" + reqInput + ":" + offerInput; safaribot.sendHtmlMessage(targetId, "To accept the trade, type " + link(acceptCommand, false, true) + ".", safchan); sys.sendMessage(src, "" , safchan); sys.sendMessage(targetId, "" , safchan); tradeRequests[userName] = { target: targetName, offer: offerInput, request: reqInput }; } }; this.isValidTrade = function(src, stuff, action, traded) { var types = [], warns = [], asset, amt, pkValue = 0, pkTraded = [], moneyOffered = (traded.hasOwnProperty("$") ? traded.$ : 0); for (var e in stuff) { asset = e; amt = stuff[e]; if (asset[0] == "$") { if (isNaN(amt) || amt <= 0) { warns.push("Please " + action + " a valid amount of money!"); } if (amt > moneyCap) { warns.push("You can't " + action + " more than $" + addComma(moneyCap) + "!"); } types.push("money"); } else if (asset.indexOf("@") !== -1) { var item = itemAlias(asset.substr(asset.indexOf("@") + 1), true); if (!itemData[item].tradable) { warns.push(finishName(item) + " cannot be traded!"); } if (isNaN(amt) || amt <= 0) { warns.push("Please " + action + " a valid amount of " + finishName(item) + "!"); } types.push("item"); } else { var info = getInputPokemon(asset); pkValue += getPrice(info.id, info.shiny) * amt; pkTraded.push(plural(amt, info.input)); types.push("poke"); } } if (pkValue > 0 && moneyOffered > 0 && pkValue > moneyOffered) { var onlyMoney = true; for (e in traded) { if (e[0] !== "$") { onlyMoney = false; break; } } if (onlyMoney) { warns.push(cap(readable(pkTraded)) + " cannot be traded for less than $" + addComma(pkValue) + "!"); } } if (warns.length > 0) { for (e = 0; e < warns.length; e++) { safaribot.sendMessage(src, warns[e], safchan); } return false; } var typeFound = types[0]; for (e = 1; e < types.length; e++) { if (types[e] !== typeFound) { return "mixed"; } } return typeFound; }; this.canTrade = function(src, asset) { var player = getAvatar(src); var out = hasStuff(player, asset); if (cantBecause(src, "trade", ["wild", "contest", "auction", "battle", "event", "tutorial", "pyramid"])) { return false; } if (!out.result) { safaribot.sendMessage(src, "You don't have " + readable(out.missing) + " to trade!", safchan); return false; } var e, p, info, item, amount, pokeList = []; for (e in asset) { amount = asset[e]; if (e[0] === "@") { item = itemAlias(e.substr(e.indexOf("@") + 1), true); info = itemData[item]; if (info.tradeReq && player.balls[item] - amount < info.tradeReq) { safaribot.sendMessage(src, "You can't trade " + finishName(item) + " unless you have more than " + info.tradeReq + " of those!", safchan); return false; } } else if (e[0] !== "$") { info = getInputPokemon(e); if (!canLosePokemon(src, info.input, "trade", false, amount)) { return false; } for (p = 0; p < amount; p++) { pokeList.push(info.id); } } } var canEmptyParty = pokeList.length >= player.party.length, partyAmt, boxAmt; if (canEmptyParty) { for (e = 0; e < pokeList.length; e++) { p = pokeList[e]; amount = countRepeated(pokeList, p); partyAmt = countRepeated(player.party, p); boxAmt = countRepeated(player.pokemon, p); if (partyAmt === 0 || amount < partyAmt || boxAmt > amount) { canEmptyParty = false; break; } } if (canEmptyParty) { safaribot.sendMessage(src, "You cannot trade all the Pokémon from your party!", safchan); return false; } } return true; }; this.canReceiveTrade = function(src, receiverId, stuff, offer) { var receiver = getAvatar(receiverId), asset, amt, e, warns = [], warnsTarget = [], pkReceived = 0, pkGiven = 0; for (e in stuff) { asset = e; amt = stuff[e]; if (asset[0] == "$") { if (receiver.money + amt > moneyCap) { warns.push("Trade cancelled because " + sys.name(receiverId) + " can't hold more than $" + moneyCap + "!"); warnsTarget.push("Trade cancelled because you can't hold more than $" + moneyCap + " (you currently have $" + receiver.money + ", so you can receive at most $" + (moneyCap - receiver.money) + ")!"); } } else if (asset.indexOf("@") !== -1) { var item = itemAlias(asset.substr(asset.indexOf("@") + 1), true); if (receiver.balls[item] + amt > getCap(item)) { warns.push("Trade cancelled because " + sys.name(receiverId) + " can't receive " + plural(amt, item) + "!"); warnsTarget.push("Trade cancelled because you can't hold more than " + plural(getCap(item), item) + " (you currently have " + receiver.balls[item] + ", so you can receive at most " + (getCap(item) - receiver.balls[item]) + ")!"); } } else { pkReceived += amt; } } if (pkReceived > 0) { for (e in offer) { if (e[0] !== "$" && e[0] !== "@") { pkGiven += offer[e]; } } if (receiver.pokemon.length + pkReceived - pkGiven > getPerkBonus(receiver, "box")) { warns.push("Trade cancelled because " + sys.name(receiverId) + "'s boxes cannot hold " + pkReceived + " more Pokémon!"); warnsTarget.push("Trade cancelled because your boxes cannot hold " + (pkReceived - pkGiven) + " more Pokémon!"); } } for (e = 0; e < warns.length; e++) { safaribot.sendMessage(src, warns[e], safchan); safaribot.sendMessage(receiverId, warnsTarget[e], safchan); } return warns.length === 0; }; this.trackSpawn = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), list; var removeTrack = function(id) { return link("/poketrack " + poke(id), poke(id)); }; list = player.pokeFlashList; if (data === "*") { if (list.length > 0) { safaribot.sendHtmlMessage(src, "You are currently being flashed for the following Pokémon (" + list.length + "): " + readable(list.map(removeTrack)), safchan); } safaribot.sendMessage(src, "Use \"/poketrack [Pokémon Name 1], [Pokémon Name 2], etc.\" to receive flashes when the specified Pokémon spawn. Use the command again to remove them. Note: If you want to receive flashes for certain Pokémon formes, you must add those formes individually. To clear your entire list, type \"/poketrack ~clear\".", safchan); return; } if (data === "~clear") { safaribot.sendMessage(src, "You cleared your Tracked Pokémon list!", safchan); player.pokeFlashList = []; safari.saveGame(player); return; } var inputList = data.split(",").map(function(e) { return e.trim() }); var added = [], removed = []; for (var i = 0; i < inputList.length; i++) { var input = getInputPokemon(inputList[i]); if (!input.num) { safaribot.sendMessage(src, "\"" + inputList[i] + "\" is not a valid Pokémon!", safchan); continue; } if (list.contains(input.num)) { list.splice(list.indexOf(input.num), 1); removed.push(input.name); } else { var maxSize = 1001; if (list.length > maxSize) { safaribot.sendMessage(src, "You can only add up to " + maxSize + " users to your Tracked Pokémon list.", safchan); break; } list.push(input.num); added.push(input.name); } } if (added.length > 0) { safaribot.sendMessage(src, "You added " + readable(added) + " to your Tracked Pokémon list!", safchan); } if (removed.length > 0) { safaribot.sendMessage(src, "You removed " + readable(removed) + " from your Tracked Pokémon list!", safchan); } safaribot.sendMessage(src, "Current list: " + (readable(list.map(poke)) || "Empty"), safchan); player.pokeFlashList = list; this.saveGame(player); }; this.blacklistPlayer = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), list; var idToName = function(id) { return idnumList.get(id).toCorrectCase(); }; var removeBlock = function(id) { return link("/blacklist " + idToName(id), idToName(id)); }; list = player.playerBlacklist; if (data === "*") { if (list.length > 0) { safaribot.sendHtmlMessage(src, "You currently have the following users blacklisted (" + list.length + "): " + readable(list.map(removeBlock)), safchan); } safaribot.sendMessage(src, "Use \"/blacklist [Username]\" to automatically reject trades and battles from that user. Use the command again to remove it. To clear your entire list, type \"/blacklist ~clear\".", safchan); return; } if (data === "~clear") { // using the tilde here since players can't use that character in names safaribot.sendMessage(src, "You cleared your Player Blacklist!", safchan); player.playerBlacklist = []; safari.saveGame(player); return; } var target = getAvatarOff(data); if (!target) { safaribot.sendMessage(src, "Invalid username!", safchan); return; } var targetID = target.idnum; if (target.idnum === player.idnum) { safaribot.sendMessage(src, "You can't blacklist yourself!", safchan); return; } if (list.contains(targetID)) { list.splice(list.indexOf(targetID), 1); safaribot.sendMessage(src, "You removed " + data.toCorrectCase() + " from your Player Blacklist! Current list: " + (readable(list.map(idToName)) || "Empty"), safchan); } else { var maxSize = 101; if (list.length > maxSize) { safaribot.sendMessage(src, "You can only add up to " + maxSize + " users to your Player Blacklist.", safchan); return; } list.push(targetID); safaribot.sendMessage(src, "You added " + data.toCorrectCase() + " to your Player Blacklist! Current list: " + (readable(list.map(idToName)) || "Empty"), safchan); } player.playerBlacklist = list; this.saveGame(player); } this.blacklistTrade = function(src, data) { if (!validPlayers("self", src)) { return; } var player = getAvatar(src), list; if (data === "*") { list = player.tradeBlacklist; if (list.length > 0) { safaribot.sendHtmlMessage(src, "You currently have the following items/Pokémon tradeblocked (" + list.length + "): " + readable(list.map(tradeblockRemove)), safchan); } safaribot.sendMessage(src, "Use \"/tradeblock [Item/Pokémon]\" to add an Item/Pokémon from this list and automatically reject trade offers for that. Use the command again to remove it. To clear your entire list, type \"/tradeblock ~clear\".", safchan); return; } if (data === "~clear") { safaribot.sendMessage(src, "You cleared your Tradeblocked list!", safchan); player.tradeBlacklist = []; safari.saveGame(player); return; } var info = removeInvisibleStuff(toStuffObj(data)); list = player.tradeBlacklist.concat(); var added = [], removed = []; for (var e in info) { if (e !== "$") { if (list.contains(e)) { list.splice(list.indexOf(e), 1); removed.push(e); } else { list.push(e); added.push(e); } } } var maxSize = 1001; if (list.length > maxSize) { safaribot.sendMessage(src, "You can only add up to " + maxSize + " Items/Pokémon to your Tradeblocked list.", safchan); return; } player.tradeBlacklist = list; var changed = [], toFrom = "to"; if (added.length > 0) { changed.push("added " + readable(added.map(mapAssetName))); } if (removed.length > 0) { changed.push("removed " + readable(removed.map(mapAssetName))); toFrom = "from"; } if (changed.length === 0) { safaribot.sendMessage(src, "No changes made to your Tradeblocked list!", safchan); return; } safaribot.sendMessage(src, "You " + readable(changed) + " " + toFrom + " your Tradeblocked list! Current list: " + (readable(list.map(mapAssetName)) || "Empty"), safchan); this.saveGame(player); }; function tradeblockRemove(x) { return link("/tradeblock " + x, mapAssetName(x)); } /* Quests */ this.questNPC = function(src, data) { if (!validPlayers("self", src)) { return; } if (data == "*") { var n = now(), quest = getAvatar(src).quests, idnum = getAvatar(src).idnum; var qs = Object.keys(base64trainers); var sprites = []; for (var i = 0; i < qs.length; i++) { sprites.push(''); } safaribot.sendHtmlMessage(src, "Quests available:" + sprites.join(""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest collector", "Collector") + " " + (quest.collector.cooldown > n ? "[Available in " + timeLeftString(quest.collector.cooldown) + "]" : (quest.collector.deadline > n ? "[Ends in " + timeLeftString(quest.collector.deadline) + "]" : "[Available]")) + (stopQuests.collector ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest scientist", "Scientist") + " " + (scientistQuest.expires > n ? (quest.scientist.cooldown >= now() && quest.scientist.pokemon == scientistQuest.pokemon && quest.scientist.photo == scientistQuest.pokemon ? "[Available in " : "[Ends in ") + timeLeftString(scientistQuest.expires) + "]" : "[Standby]") + (stopQuests.scientist ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest arena", "Arena") + " " + (quest.arena.cooldown > n ? "[Available in " + timeLeftString(quest.arena.cooldown) + "]" : "[Available]") + (stopQuests.arena ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest wonder", "Wonder Trade") + " " + (quest.wonder.cooldown > n ? "[Available in " + timeLeftString(quest.wonder.cooldown) + "]" : "[Available]") + (stopQuests.wonder ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest tower", "Battle Tower") + " " + (quest.tower.cooldown > n ? "[Available in " + timeLeftString(quest.tower.cooldown) + "]" : "[Available]") + (stopQuests.tower ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest pyramid", "Pyramid") + " " + (quest.pyramid.cooldown > n ? "[Available in " + timeLeftString(quest.pyramid.cooldown) + "]" : "[Available]") + (stopQuests.pyramid ? " [Disabled]" : ""), safchan); //safaribot.sendHtmlMessage(src, "-Pyramid [Closed for renovation]", safchan); //safaribot.sendHtmlMessage(src, "-" + link("/quest piramyd", "Piramyd") + " [Available]", safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest alchemist", "Alchemist") + " " + (quest.alchemist.cooldown > n ? "[Available in " + timeLeftString(quest.alchemist.cooldown) + "]" : "[Available]") + (stopQuests.alchemist ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest arborist", "Arborist") + " " + "[Available]" + (quest.arborist.cooldown > n ? " [Next Apricorn trade available in " + timeLeftString(quest.arborist.cooldown) + "]" : "") + (stopQuests.arborist ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest decor", "Decor") + " " + (quest.decor.cooldown > n ? "[Available in " + timeLeftString(quest.decor.cooldown) + "]" : "[Available]") + (stopQuests.decor ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest league", "League") + " " + (quest.league.cooldown > n ? "[Available in " + timeLeftString(quest.league.cooldown) + "]" : "[Available]") + (stopQuests.league ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest celebrity", "Celebrity") + " [Available]" + (stopQuests.celebrity ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest journal", "Journal") + " " + (quest.journal.cooldown > n ? "[Available in " + timeLeftString(quest.journal.cooldown) + "]" : "[Available]") + (stopQuests.journal ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest monger", "Monger") + " " + (mAuctionsData.length > 0 ? "[Next Auction " + (mAuctionsData[0].deadline < n ? "after Contest" : "in about " + timeLeftString(mAuctionsData[0].deadline)) + "]" : "[Standby]") + (stopQuests.monger ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest baking", "Baking") + " " + (quest.baking.cooldown > n ? "[Available in " + timeLeftString(quest.baking.cooldown) + "]" : "[Available]") + (stopQuests.baking ? " [Disabled]" : ""), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol", "Idol") + " " + (stopQuests.idol ? " [Disabled]" : " [Available]"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest detective", "Detective") + " " + (stopQuests.detective ? " [Disabled]" : (this.detectiveData[idnum] && this.detectiveData[idnum].solved && getDay(now()) == this.detectiveData[idnum].date ? "[Available in " : "[Ends in ") + timeLeftString(new Date().setUTCHours(24, 0, 0, 0)) + "] ") + (quest.detective.cooldown > n ? "[Next guess available in " + timeLeftString(quest.detective.cooldown) + "]" : ""), safchan); sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "For more information, type /quest [name] (example: /quest collector).", safchan); sys.sendMessage(src, "", safchan); return; } var info = data.split(":"); var quest = info[0].toLowerCase(); var args = info.slice(1); args = args.length > 0 ? args : []; switch (quest) { case "collector": case "lazy rich guy": this.collectorQuest(src, args); break; case "scientist": case "ig nobel winner": this.scientistQuest(src, args); break; case "arena": case "fight some dudes": this.fightArena(src, args); break; case "wonder": case "wondertrade": case "wonder trade": case "subara": this.wonderTrade(src, args); break; case "tower": case "battletower": case "battle tower": case "we have no elevators": this.fightTower(src, args); break; case "pyramid": case "old triangle": case "pyr": this.pyramidQuest(src, args); break; case "piramyd": safaribot.sendHtmlMessage(src, "You need ohter 22 palyers to joyn you at Piramyd! Alll of them must be wearing the Yuongster costume and have a Psichyc-type Pokéman in their party!", safchan); break; case "alchemy": case "alchemist": case "alch": case "alc": case "booooom!": this.alchemyQuest(src, args); break; case "philo": case "philosopher": case "i have no quirky secret name": this.philosopherQuest(src, args); break; case "arbor": case "arborist": case "sphere maniac": this.arboristQuest(src, args); break; case "decor": case "decoration": case "cuuuuuuute!": this.decorationQuest(src, args); break; case "league": case "fight stronger dudes": this.fightLeague(src, args); break; case "celebrity": case "celeb": case "celebrity death match": this.celebrityMatch(src, args); break; case "journal": case "journalist": case "the onion": this.journalQuest(src, args); break; case "monger": case "suspicious dude": this.mafiaAuction(src, args); break; case "baking": case "great galarian bait off": case "great galarian bait-off": case "ggbo": this.bakingQuest(src, args); break; case "idol": case "hatsune miku": this.idolQuest(src, args); break; case "detective": case "l": case "mello": case "save me": if (safari.detectiveUpdates && (!(quest == "save me"))) { this.detectiveQuest(src, args, true); } else { this.detectiveQuest(src, args); } break; default: safaribot.sendMessage(src, "This is not a valid quest!", safchan); } }; this.collectorQuest = function(src, data) { var player = getAvatar(src); if (player.tutorial.inTutorial && player.tutorial.step !== 9) { if (cantBecause(src, "start a quest", ["tutorial"])) { return; } } var quest = player.quests.collector; var ongoing = quest.reward > 0; var trainerSprite = ''; if (ongoing && now() > quest.deadline && !player.tutorial.inTutorial) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: I'm sorry, but since you took too long to fulfill my request I decided to buy from someone else!", safchan); safaribot.sendHtmlMessage(src, "Collector: If you still wish to help me, type " + link("/quest collector:start") + " for a new request!", safchan); sys.sendMessage(src, "", safchan); quest.reward = 0; quest.requests = []; quest.cooldown = 0; quest.deadline = null; this.saveGame(player); return; } if (data.length < 1 || !data[0]) { if (ongoing) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Hello, did you bring the " + readable(quest.requests.map(findLink), "and") + " that I asked for?", safchan); if (player.tutorial.inTutorial) { tutorMsg(src, "You can complete the Collector's request with " + link("/quest collector:finish")); } else { safaribot.sendHtmlMessage(src, "Collector: If you did, type " + link("/quest collector:finish", null, true) + " and I will pay you $" + addComma(Math.floor(quest.reward * (player.costume === "pokefan" ? costumeData.pokefan.rate : 1))) + " for those Pokémon, but please bring them in less than " + timeLeftString(quest.deadline) + ".", safchan); safaribot.sendHtmlMessage(src, "Collector: You can also type " + link("/quest collector:abort", null, true) + " if you no longer wish to help me.", safchan); } sys.sendMessage(src, "", safchan); } else { if (player.tutorial.inTutorial) { tutorMsg(src, "Please start the quest with " + link("/quest collector:start")); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Hello, I'm The Collector! I am always willing to pay well for some interesting Pokémon. If you wish to help me, type " + link("/quest collector:help") + ".", safchan); if (quest.cooldown >= now()) { safaribot.sendMessage(src, "Collector: I'm currently organizing my collection, so I won't be making any new request now. Please come back in " + timeLeftString(quest.cooldown) + "!", safchan); } } } return; } var action = data[0].toLowerCase(); switch (action) { case "information": case "info": case "help": if (player.tutorial.inTutorial) { tutorMsg(src, "Please start the quest with " + link("/quest collector:start")); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Collector: I love to collect Pokémon, but I'm not good at catching them. Therefore, I buy them!", safchan); safaribot.sendHtmlMessage(src, "Collector: If you want to help me, type " + link("/quest collector:start:difficulty") + ", and I will request some Pokémon for you to bring me.", safchan); safaribot.sendHtmlMessage(src, "Collector: Once you have them, type " + link("/quest collector:finish", null, true) + ", and I will pay about from 2.4x to 4.8x their normal value. After that, I will need some time to organize my collection, so I won't make any new request until I finish.", safchan); safaribot.sendHtmlMessage(src, "Collector: If you wish to give up on my request, type " + link("/quest collector:abort", null, true) + ".", safchan); safaribot.sendHtmlMessage(src, "Collector: To learn more about the difficulty levels, type " + link("/quest collector:difficulty") + ".", safchan); sys.sendMessage(src, "", safchan); break; case "difficulty": case "level": case "levels": if (player.tutorial.inTutorial) { tutorMsg(src, "Please start the quest with " + link("/quest collector:start")); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Collector: My requests are organized into different levels:", safchan); safaribot.sendHtmlMessage(src, "Collector: " + link("/quest collector:start:Easy", "Easy") + " - Three Pokémon with BST between 175 and 320. Reward is 2.4x their price.", safchan); safaribot.sendHtmlMessage(src, "Collector: " + link("/quest collector:start:Normal", "Normal") + " - Four Pokémon with BST between 320 and 480. Reward is 3.3x their price.", safchan); safaribot.sendHtmlMessage(src, "Collector: " + link("/quest collector:start:Hard", "Hard") + " - Five Pokémon with BST between 470 and 599. Reward is 4.8x their price.", safchan); safaribot.sendHtmlMessage(src, "Collector: " + link("/quest collector:start:Epic", "Epic") + " - Six Pokémon with BST between 500 and 600, with one of them being a Legendary. Reward is 10x their price.", safchan); safaribot.sendHtmlMessage(src, "Collector: " + link("/quest collector:start:Insane", "Insane") + " - For crazy people.", safchan); sys.sendMessage(src, "", safchan); break; case "start": case "begin": if (player.tutorial.inTutorial) { safari.tutorialQuest(src, true); return; } if (stopQuests.collector) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Sorry, I am buried in Pokémon right now. Please return at a later point in time!", safchan); return; } if (ongoing) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Please fulfill my previous request before getting a new one! If you wish to give up this request, type " + link("/quest collector:abort", null, true) + ".", safchan); return; } if (quest.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: I'm sorry, I'm currently organizing my collection. Please come back in " + timeLeftString(quest.cooldown) + "!", safchan); return; } if (data.length < 2) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Please choose a difficulty (" + link("/quest collector:start:easy", "Easy") + ", " + link("/quest collector:start:normal", "Normal") + ", " + link("/quest collector:start:hard", "Hard") + ", " + link("/quest collector:start:epic", "Epic") + ", or " + link("/quest collector:start:insane", "Insane") + ") using /quest collector:start:[difficulty]! Type " + link("/quest collector:difficulty") + " to learn more about the different difficulty levels.", safchan); return; } var diff = data[1].toLowerCase(); var level = 1; switch (diff) { case "easy": case "[easy]": level = 0; break; case "normal": case "[normal]": case "medium": level = 1; break; case "hard": case "[hard]": level = 2; break; case "epic": case "[epic]": level = 3; break; case "insane": case "[insane]": level = 4; break; default: safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Please choose a difficulty (" + link("/quest collector:start:easy", "Easy") + ", " + link("/quest collector:start:normal", "Normal") + ", " + link("/quest collector:start:hard", "Hard") + ", or " + link("/quest collector:start:epic", "Epic") + ") using /quest collector:start:[difficulty]!", safchan); return; } var request = []; var difficultBonus = [2.4, 3.3, 4.8, 10, 20][level]; var minBST = [175, 320, 470, 500, 520][level]; var maxBST = [320, 480, 599, 600, 750][level]; var amount = [3, 4, 5, 5, 9][level]; var deadlineDays = 2; while (request.length < amount) { var randomNum = sys.rand(1, highestDexNum); if (regionalEvos.concat([772, 773]).contains(randomNum)) { // [type: null, silvally] continue; } var bst = getBST(randomNum); if (randomNum in wildForms) { randomNum = pokeInfo.calcForme(randomNum, sys.rand(1, wildForms[randomNum] + 1)); } if (!request.contains(randomNum) && bst >= minBST && bst <= maxBST && (!isLegendary(randomNum))) { request.push(randomNum); } } if (level >= 3) { var legend = 0; var legendNeeded = (level == 3 ? 1 : 3); var excludedLegends = ["Phione", "Meltan", "Cosmog", "Cosmoem", "Kubfu", "Urshifu", "Urshifu-Rapid Strike", "Calyrex"].map(function(e) { return getPokeNum(e) }); for (var i = 0; i < legendNeeded; i++) { legend = 0; while (((level == 3) && (legend == 0 || getBST(legend) > 600)) || legend === 0 || excludedLegends.contains(legend) || legend >= highestDexNum) { legend = legendaries.random(); } request.push(legend); } } var reward = 0; for (e = 0; e < request.length; e++) { reward += getPrice(request[e]); } var perkBonus = getPerkBonus(player, "amulet"); quest.requests = request; quest.reward = Math.round(reward * (difficultBonus + perkBonus)); quest.deadline = now() + hours(deadlineDays * 24); safari.clearQuestNotifications(player, "Collector"); this.saveGame(player); var costumed; var payout = quest.reward; if (player.costume === "pokefan") { payout = Math.floor(payout * costumeData.pokefan.rate); costumed = true; } safari.toRecentQuests(player, "collector"); safaribot.sendHtmlMessage(src, trainerSprite + "Collector: So you will help me? Great! Then bring me " + readable(request.map(findLink), "and") + ", and I will pay you $" + addComma(payout) + " for them!" + (costumed ? "[Note: Without PokeFan Costume only $" + addComma(quest.reward) + " is paid.]" : ""), safchan); safaribot.sendMessage(src, "Collector: But please don't take too long! If you take more than " + (deadlineDays * 24) + " hours, I may buy them from someone else!", safchan); sys.sendMessage(src, "", safchan); break; case "finish": case "complete": if (player.tutorial.inTutorial) { if (!ongoing) { tutorMsg(src, "Please start the quest with " + link("/quest collector:start")); return; } else { safari.tutorialQuest(src); return; } } if (!ongoing) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: I don't recall requesting anything from you. Type " + link("/quest collector:help") + " if you wish to help me.", safchan); return; } if (stopQuests.collector) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Sorry, I am buried in Pokémon right now. Like, literally buried. Please help.", safchan); return; } //Tutorial blocked earlier if (cantBecause(src, "finish this quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } var e, hasPokemon = true, id, showSprite = true, free = this.getFortune(player, "freecollector", 0, null, true), freeUsed = []; for (e = 0; e < quest.requests.length; e++) { id = quest.requests[e]; if (!player.pokemon.contains(id)) { safaribot.sendHtmlMessage(src, (showSprite ? trainerSprite : "") + "Collector: You don't have " + an(pokePlain(id)) + "!", safchan); hasPokemon = false; showSprite = false; } } if (!hasPokemon) { return; } for (e = 0; e < quest.requests.length; e++) { id = quest.requests[e]; if (!canLosePokemon(src, id + "", "give")) { return; } } if (player.party.length <= quest.requests.length) { var allInParty = true; for (e = 0; e < player.party.length; e++) { if (!quest.requests.contains(player.party[e])) { allInParty = false; break; } } if (allInParty) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: I can't possibly take these Pokémon while they are in your party!", safchan); return; } } var payout = Math.floor(quest.reward * (player.costume === "pokefan" ? costumeData.pokefan.rate : 1)); payout = Math.floor(payout * (1 + this.getFortune(player, "pokefan", 0))); var costumed; if (player.costume === "pokefan") { costumed = true; } var givenList = quest.requests.concat(); while (free > freeUsed.length) { e = quest.requests.random(); if (!freeUsed.contains(e)) { freeUsed.push(e); givenList.splice(givenList.indexOf(e), 1); } } safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Superb! You brought everything! Here's your payment!", safchan); safaribot.sendMessage(src, "You gave your " + readable(givenList.map(poke), "and") + " to the Collector and received $" + addComma(payout) + "!", safchan); if (costumed) { safaribot.sendMessage(src, "Collector: Enjoy the little extra I threw in for you, buddy! Always a pleasure doing business with a fellow PokéFan.", safchan); } player.money = Math.min(player.money + payout, moneyCap); if (player.money > moneyCap) { player.money = moneyCap; } var stole = 1, costumed = player.costume === "rocket", theft = "", kept = "", stoleList = [], keptList = []; quest.requests = quest.requests.shuffle(); //Shuffle to make it more random for (e = 0; e < quest.requests.length; e++) { id = quest.requests[e]; if (freeUsed.contains(id)) { keptList.push(poke(id)); safaribot.sendMessage(src, "Collector: I'm feeling generous today, so you don't need to give me the " + poke(id) + "!", safchan); continue; } if (costumed && chance(costumeData.rocket.rate * stole)) { stoleList.push(poke(id)); safaribot.sendMessage(src, "You cleverly distract the Collector and while he is not looking, you grab your " + poke(id) + " back and run off!", safchan); player.records.pokesStolen += 1; stole /= 3; continue; } this.removePokemon(src, id); } if (keptList.length > 0) { kept = ", kept " + readable(keptList); } if (stoleList.length > 0) { theft = ", stole " + readable(stoleList) + " back"; } if (this.getFortune(player, "freecollector", 0, null, true)) { this.useFortuneCharge(player, "freecollector", 1); } sys.sendMessage(src, "", safchan); this.logLostCommand(sys.name(src), "quest collector:" + data.join(":"), "gave " + readable(quest.requests.map(poke), "and") + kept + theft); safari.updateEconomyData(payout, "collector"); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Collector|||Gave " + readable(quest.requests.map(poke)) + "|||Received $" + addComma(payout) + kept + theft + "\n"); player.records.collectorEarnings += payout; player.records.collectorGiven += quest.requests.length; safari.clearQuestNotifications(player, "Collector"); this.missionProgress(player, "collector", quest.requests.join(","), 1, { price: payout, amount: quest.requests.length }); this.addToMonthlyLeaderboards(player.id, "collectorEarnings", payout); quest.reward = 0; quest.requests = []; quest.cooldown = now() + Math.round(hours(3) * (1 - safari.getFortune(player, "questcd", 0, "collector")) * (1 - safari.getAuraEffect(player, "questcd", 0))); player.notificationData.collectorWaiting = true; safari.pendingNotifications(player.id); quest.deadline = 0; this.saveGame(player); break; case "abort": case "cancel": case "giveup": case "give up": if (player.tutorial.inTutorial) { if (!ongoing) { tutorMsg(src, "Please start the quest with " + link("/quest collector:start")); return; } else { tutorMsg(src, "You cannot abort the tutorial quest. Finish it instead with " + link("/quest collector:finish")); return; } } if (!ongoing) { safaribot.sendHtmlMessage(src, trainerSprite + "Collector: You can't abort a quest even before you start it! Type " + link("/quest collector:help") + " if you wish to help me.", safchan); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Collector: Oh, you don't want to help me anymore? It's a shame, but I understand. Come back later if you change your mind!", safchan); quest.reward = 0; quest.cooldown = now() + Math.round(hours(1) * (1 - safari.getAuraEffect(player, "questcd", 0))); player.notificationData.collectorWaiting = true; quest.requests = []; quest.deadline = null; this.saveGame(player); break; default: safaribot.sendHtmlMessage(src, trainerSprite + "Collector: I don't think I can help you with that. But if you wish to help me, type " + link("/quest collector:help") + "!", safchan); } }; this.scientistQuest = function(src, data) { var player = getAvatar(src), rew; if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var quest = scientistQuest; var id = quest.pokemon; var photoReq = {species: id, quality: 7}; var canFulfillPhoto = false; for (var i = player.photos.length; i--;) { if (this.photoMatchesRequest(player.photos[i], photoReq)) { canFulfillPhoto = true; break; } } var trainerSprite = ''; if (now() > quest.expires || stopQuests.scientist) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: I'm going to present my research results in a convention. Please come back later!", safchan); return; } if (player.quests.scientist.cooldown >= now() && player.quests.scientist.pokemon == id && player.quests.scientist.photo == id) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: That " + poke(id) + " that you brought earlier is really helping me! Come back in " + timeLeftString(quest.expires) + " to check my next research!", safchan); return; } /*var codeResearch = { "sunshard": "We're discovering ways this could be used to unlock potential abilities within certain Pokémon!", "moonshard": "We're discovering ways this could be used to activate the abilities that Pokémon have unlocked!" };*/ if (!data[0]) { //Feel free to change these messages and the ones below if you get a better idea var typeResearch = { "Normal": "{0}'s genealogy", "Fighting": "how many martial arts a {0} can learn", "Flying": "{0}'s flying patterns", "Poison": "a possible cure for a {0}'s venom", "Ground": "how deep a {0} can dig into the earth", "Rock": "how a {0} moves if they are made of rocks", "Bug": "how a group of {0} work together in a colony", "Ghost": "if and how a {0} can move into the ethereal world", "Steel": "if {0}'s body is similar to industrial steel", "Fire": "{0}'s maximum body temperature", "Water": "what percentage of {0}'s body is composed of water", "Grass": "how the photosynthesis process works for {0}", "Electric": "how many houses a {0} could provide energy to", "Psychic": "if a {0} can read an human's mind", "Ice": "{0}'s minimum body temperature", "Dragon": "how tough a {0}'s fang is", "Dark": "if a {0} becomes stronger during the night", "Fairy": "if a {0} has magical powers" }; var type = type1(id); var researching = typeResearch[type].format(link("/findd " + poke(id), poke(id))); type = type2(id); type = type === "???" ? type1(id) : type2(id); typeResearch = { "Normal": "if they can be domesticated", "Fighting": "how strong they are when compared to humans", "Flying": "how fast they can fly", "Poison": "which parts of their body is poisonous", "Ground": "their ability to level the ground", "Rock": "how many different minerals can be found in their body", "Bug": "in which environments they can camouflage themselves better", "Ghost": "if they can possess an human", "Steel": "what they eat since they are made of steel", "Fire": "how able they are to adapt to colder environments", "Water": "how deep they can dive into the sea", "Grass": "in which season they grow faster", "Electric": "if there's any side effect to storing electricity in their body", "Psychic": "how far they can exert their psychic powers", "Ice": "how able they are to adapt to warmer environments", "Dragon": "their expected lifespan", "Dark": "how smart they are", "Fairy": "if their charm spells can affect humans" }; researching += " and " + typeResearch[type]; if (player.quests.scientist.pokemon !== id) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Hello, my friend! I'm currently researching " + researching + ", so I would appreciate if you could bring one to me. If you do, I shall reward you with " + plural(quest.reward, "silver") + "!", safchan); safaribot.sendHtmlMessage(src, "Scientist: I expect to finish this research in about " + timeLeftString(quest.expires) + ". If you wish to help, bring me " + an(poke(id)) + " before then and type " + link("/quest scientist:finish", null, true) + ".", safchan); if (canFulfillPhoto) { if (player.quests.scientist.photo !== id) { safaribot.sendHtmlMessage(src, toColor("Scientist: Or, you could help me by bringing a photo of that Pokémon! Please note that I need a photo of Great or better quality. ", "magenta") + "[" + link("/quest scientist:photo", "You can fulfill this request") + "]", safchan); } } else if (player.quests.scientist.photo !== id) { safaribot.sendHtmlMessage(src, "Scientist: Or, you could help me by bringing a photo of that Pokémon! Please note that I need a photo of Great or better quality. [" + toColor("You do not have a matching photo", "red") + "]", safchan); } } else { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Hello, my friend! I'm currently researching " + researching + "! I expect to finish this research in about " + timeLeftString(quest.expires) + ".", safchan); if (canFulfillPhoto) { safaribot.sendHtmlMessage(src, toColor("Scientist: Thanks for bringing me " + an(pokePlain(id)) + " earlier, but if you have any photos of it they would still be helpful! Please note that I need a photo of Great or better quality. ", "magenta") + "[" + link("/quest scientist:photo", "You can fulfill this request") + "]", safchan); } else { safaribot.sendHtmlMessage(src, "Scientist: Thanks for bringing me " + an(pokePlain(id)) + " earlier, but if you have any photos of it they would still be helpful! Please note that I need a photo of Great or better quality. [" + toColor("You do not have a matching photo", "red") + "]", safchan); } } //safaribot.sendHtmlMessage(src, "Scientist: If you're wondering what else we do at my lab, we're also looking into some new technology! " + link("/quest scientist:moonshard", "Moon Shard") + " or " + link("/quest scientist:sunshard", "Sun Shard") + ".", safchan); sys.sendMessage(src, "", safchan); return; } if (data[0].toLowerCase() === "finish") { if (player.quests.scientist.pokemon === id) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Hey, you already brought me " + an (pokePlain(id)) + "! Don't you have someone else to give Pokémon to?", safchan); return; } if (!player.pokemon.contains(id)) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: You don't have " + an(pokePlain(id)) + "!", safchan); return; } if (!canLosePokemon(src, id + "", "give")) { return; } //Tutorial blocked earlier if (cantBecause(src, "finish this quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } rew = quest.reward + this.getFortune(player, "scientistreward", 0, null, true); if (this.hasCostumeSkill(player, "extraScientistSilver")) { rew = Math.round(rew * (2 + ((this.getCostumeLevel(player)-2)/18))); } if (player.balls.silver === getCap("silver")) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Oh, you brought the " + poke(id) + "! But it looks like you can't carry any more " + finishName("silver") + "! Come back when you've made some room.", safchan); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Oh, you brought the " + poke(id) + "! Here, have your " + plural(rew, "silver") + "!", safchan); safaribot.sendMessage(src, "You gave your " + poke(id) + " to the Scientist and received " + plural(rew, "silver") + "!", safchan); player.records.scientistEarnings += rew; player.records.scientistGiven += 1; safari.costumeEXP(player, "scientist", (rew * 4)); player.quests.scientist.cooldown = quest.expires; player.quests.scientist.pokemon = id; safari.missionProgress(player, "scientist", 1, rew, {silver: rew}); safari.toRecentQuests(player, "scientist"); player.balls.silver += rew; if (player.balls.silver > getCap("silver")) { var diff = player.balls.silver - getCap("silver"); safaribot.sendMessage(src, "Unfortunately, you had to discard " + plural(diff, "silver") + " due to excess!", safchan); player.balls.silver = getCap("silver"); } player.notificationData.scientistWaiting = true; if (this.getFortune(player, "scientistreward", 0, null, true)) { this.useFortuneCharge(player, "scientistreward", 1); } var theft = ""; if (player.costume === "rocket" && chance(costumeData.rocket.rate * 2)) { safaribot.sendMessage(src, "You cleverly distract the Scientist and while he is not looking, you grab your " + poke(id) + " back and run off!", safchan); player.records.pokesStolen += 1; theft = " but stole it back"; safari.costumeEXP(player, "stealpoke"); } else { this.removePokemon(src, id); } sys.sendMessage(src, "", safchan); this.logLostCommand(sys.name(src), "quest scientist:" + data.join(":"), "gave " + poke(id) + theft); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Scientist|||Gave " + poke(id) + "|||Received " + plural(rew, "silver") + (theft ? ", stole " + poke(id) + " back" : "") + "\n"); safari.pendingNotifications(player.id); this.saveGame(player); } else if (data[0].toLowerCase() === "photo") { if (data[1] === undefined) { if (canFulfillPhoto) { var highestScore = 0, index = 0; for (var e = 0; e < player.photos.length; e++) { if (this.photoMatchesRequest(player.photos[e], photoReq) && player.photos[e].score > highestScore) { index = e + 1; highestScore = player.photos[e].score; } } safari.scientistQuest(src, ["photo", index]); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Unfortunately, it doesn't look like you have any photos that can help with my research on " + poke(id) + "!", safchan); } } else { var index = parseInt(data[1]) - 1; if (isNaN(index) || index >= player.photos.length || index < 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: I don't think I can help you with that photo.", safchan); return; } if (player.quests.scientist.photo === id) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Hey, you already showed me a picture of a " + poke(id) + "! Don't you have someone else to give photos to?", safchan); return; } if (this.photoMatchesRequest(player.photos[index], photoReq)) { safaribot.sendHtmlMessage(src, "You showed the Scientist a photo of " + safari.describePhoto(player.photos[index]) + "!", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Wow, that's a great photo of " + an(poke(id)) + "! I think I can use this!", safchan); rew = 3; rew = Math.round(1.75 * (player.photos[index].score) - 5); if (this.hasCostumeSkill(player, "extraScientistSilver")) { rew = Math.round(rew * (1.66 + ((this.getCostumeLevel(player)-2)/15))); } rew += this.getFortune(player, "scientistreward", 0, null, true); safari.costumeEXP(player, "scientist", (rew * 4)); safari.missionProgress(player, "scientist", 1, rew, {silver: rew}); safaribot.sendHtmlMessage(src, "Here are your " + plural(rew, "silver") + "!", safchan); player.balls.silver += rew; if (player.balls.silver > getCap("silver")) { var diff = player.balls.silver - getCap("silver"); safaribot.sendMessage(src, "Unfortunately, you had to discard " + plural(diff, "silver") + " due to excess!", safchan); player.balls.silver = getCap("silver"); } player.records.scientistEarnings += rew; player.records.scientistPhotoSubmission += 1; player.quests.scientist.photo = id; safari.pendingNotifications(player.id); safari.saveGame(player); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Scientist|||Showed a photo of " + safari.describePhoto(player.photos[index]) + "|||Received " + plural(rew, "silver") + "\n"); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: Sorry, that won't do! I need a photo of " + poke(id) + " with quality Great or higher!", safchan); } } }/* else if (codeResearch.hasOwnProperty(data[0].toLowerCase())) { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: " + codeResearch[data[0].toLowerCase()], safchan); }*/ else { safaribot.sendHtmlMessage(src, trainerSprite + "Scientist: I don't think I can help you with that.", safchan); } }; this.changeScientistQuest = function(data) { var randomNum, bst; if (data) { randomNum = getInputPokemon(data).num; bst = getBST(randomNum); } if (!randomNum) { do { randomNum = sys.rand(1, highestDexNum); bst = getBST(randomNum); if (randomNum in wildForms) { randomNum = pokeInfo.calcForme(randomNum, sys.rand(1, wildForms[randomNum] + 1)); } } while (bst > 600 || isRare(randomNum) || [772, 773].contains(randomNum)); } var bstRange = [ 175, 211, 251, 301, 351, 391, 431, 481, 511, 525, 536, 581 ], ind; var rewRange = [ 1, 2, 3, 4, 5, 6, 7, 8, 11, 14, 18, 24 ]; for (ind = bstRange.length - 1; ind >= 0; ind--) { if (bst >= bstRange[ind]) { break; } } var reward = rewRange[ind]; scientistQuest = { pokemon: randomNum, reward: reward, expires: now() + hours(3) - 3 * 60 * 1000 }; permObj.add("scientistQuest", JSON.stringify(scientistQuest)); var player, e, amt; try { for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = getAvatarOff(e); if (!player) { continue; } player.quests.scientist.pokemon = player.quests.scientist.photo = 0; safari.saveGame(player); if (!isPlaying(player.id)) { continue; } if (!(player.notificationData)) { continue; } if (!(player.notificationData.scientistWaiting)) { continue; } amt = countDuplicates(player.pokemon, randomNum); var canFulfillPhoto = false; var photoReq = {species: randomNum, quality: 7}; for (var i = player.photos.length; i--;) { if (safari.photoMatchesRequest(player.photos[i], photoReq)) { canFulfillPhoto = true; break; } } if (amt > 0) { safari.notification(player, "The " + link("/quest scientist", "Scientist") + " is now looking for " + an(poke(randomNum)) + ", of which you have " + amt + "!", "Scientist"); player.notificationData.scientistWaiting = false; safari.saveGame(player); } else if (canFulfillPhoto) { safari.notification(player, "The " + link("/quest scientist", "Scientist") + " is now looking for " + an(poke(randomNum)) + ", of which you have a suitable photo for!", "Scientist"); player.notificationData.scientistWaiting = false; safari.saveGame(player); } } } } catch (err) {}; }; this.fightArena = function(src, data) { var player = getAvatar(src); var reason = "start a battle"; if (cantBecause(src, reason, ["tutorial"])) { return; } var trainerSprite = ''; if (data.length === 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Arena Clerk: You want a battle? Then type /quest arena:name to pick who you want to fight!", safchan); safaribot.sendHtmlMessage(src, "Arena Clerk: You need to pay an entry fee in order to challenge but you will get some rewards like " + es(finishName("silver")) + " if you manage to win! Type " + link("/quest arena:help") + "! for more details!", safchan); if (player.quests.arena.cooldown >= now()) { safaribot.sendMessage(src, "Arena Clerk: There's currently a long queue of people fighting in the Arena, so it may need to wait " + timeLeftString(player.quests.arena.cooldown) + " to try a challenge!", safchan); } sys.sendMessage(src, "", safchan); return; } var postBattle = function(name, isWinner, playerScore, npcScore, args) { var player = getAvatarOff(name); var id = sys.id(name); sys.sendMessage(id, "", safchan); var rewname = ""; if (isWinner) { var amt = args.rewardAmt; if (!args.noRecords) { player.records.arenaWon += 1; player.records.arenaPoints += amt; safari.addToMonthlyLeaderboards(player.id, "arenaPoints", amt); } safaribot.sendHtmlMessage(id, "" + args.name + ": Wow, I didn't expect to lose! Good job, here's your reward!", safchan); var rew = args.reward || "silver"; if (amt !== 0) { rewname = plural(amt, rew); amt = amt + safari.getFortune(player, "arenareward", 0); safaribot.sendMessage(id, "You received " + plural(amt, rew) + "!", safchan); rewardCapCheck(player, rew, amt, true); safari.costumeEXP(player, "arenasilver", amt); if (args.name == "Trainer Lorekeeper" && player.costume == "battle") { safari.detectiveClue(player.idnum, "battlearena", id); } } if (args.moneyReward) { rewname = "$" + addComma(args.moneyReward); safaribot.sendMessage(id, "You received $" + addComma(args.moneyReward) + "!", safchan); player.money += args.moneyReward; safari.updateEconomyData(args.moneyReward, "questFee"); if (player.money > moneyCap) { player.money = moneyCap; } } safari.missionProgress(player, "arena", args.name, 1, { silver: (rew === "silver" ? amt : 0) }); } else { if (!args.noRecords) { player.records.arenaLost += 1; } if (args.taunt && now() > player.cooldowns.nubTaunt) { sys.sendAll("", safchan); safaribot.sendHtmlAll(toColor("LOLOLOL! " + name.toCorrectCase() + " lost a battle against Trainer Nub! You should make fun of them with " + link("/rock " + name.toCorrectCase()) + "!", "tomato"), safchan); sys.sendAll("", safchan); player.cooldowns.nubTaunt = now() + hours(24); } safaribot.sendHtmlMessage(id, "" + args.name + ": Haha, seems like I won this time! Try harder next time!", safchan); } var cdamt = (args.cooldown * (isWinner ? 1 : 0.5)) * (safari.hasCostumeSkill(player, "fasterArena") ? 0.85 : 1); player.quests.arena.cooldown = now() + Math.round(hours(cdamt) * (1 - safari.getFortune(player, "questcd", 0, "arena")) * (1 - safari.getAuraEffect(player, "questcd", 0))); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Arena|||Fought " + args.name + " using " + readable(player.party.map(poke)) + " team|||" + (isWinner ? "Won" : "Lost") + " "+ playerScore + "x" + npcScore + (rewname ? ", received " + rewname : "") + "\n"); safari.saveGame(player); }; var price = { nub: 0, pink: 100, mustard: 200, lorekeeper: 250, cyan: 300, crimson: 500, rainbow: 1000, copycat: 400 }; var copycat = { name: "Trainer " + sys.name(src), party: [], power: [10, 100], postArgs: { reward: "dust", rewardAmt: 40, cooldown: 1.2, noRecords: true }, desc: "Arena NPC" }; var opt = data[0].toLowerCase(); if (opt === "help") { safaribot.sendHtmlMessage(src, trainerSprite + "Arena Clerk: Challenge our trainers for a chance to receive some items like " + es(finishName("silver")) + "! The trainers available are: ", safchan); for (var n in arenaOpponents) { var opp = arenaOpponents[n]; safaribot.sendHtmlMessage(src, "-" + link("/quest arena:" + cap(n), cap(n)) + ": Entry Fee: $" + addComma(price[n]) + ". Reward: " + (opp.postArgs.rewardAmt !== 0 ? plural(opp.postArgs.rewardAmt, (opp.postArgs.reward || "silver")) : "") + (opp.postArgs.moneyReward ? "$" + addComma(opp.postArgs.moneyReward) : "") + ". Cooldown: " + utilities.getTimeString(opp.postArgs.cooldown * 60 * 60) + ". ", safchan); } safaribot.sendHtmlMessage(src, "-" + link("/quest arena:" + sys.name(src), sys.name(src)) + ": Entry Fee: $" + addComma(price.copycat) + ". Reward: " + plural(copycat.postArgs.rewardAmt, copycat.postArgs.reward) + ". Cooldown: " + utilities.getTimeString(1.2 * 60 * 60) + ". ", safchan); sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Arena Clerk: Once you decide on your challenge, type /quest arena:[name] (e.g.: /quest arena:nub).", safchan); sys.sendMessage(src, "", safchan); return; } if (player.quests.arena.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Arena Clerk: There's a long queue of people fighting in the Arena! Please come after " + timeLeftString(player.quests.arena.cooldown) + " to try another challenge!", safchan); return; } if (stopQuests.arena) { safaribot.sendHtmlMessage(src, trainerSprite + "Arena Clerk: Sorry, we need to clean out the stadium before we can host more battles. Please return at a later point in time!", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid", "baking"])) { return; } var npc = arenaOpponents[opt]; if (!npc) { if (opt === sys.name(src).toLowerCase()) { npc = copycat; npc.party = player.party.concat(); opt = "copycat"; } else { safaribot.sendMessage(src, "There's no one with that name around here!", safchan); return; } } var cost = price[opt]; if (player.money < cost) { safaribot.sendMessage(src, "You need to pay $" + addComma(cost) + " for this challenge, but you only have $" + addComma(player.money) + "!", safchan); return; } if (player.party.length < 6) { safaribot.sendMessage(src, "Your party must have 6 Pokémon for this challenge!", safchan); return; } npc = JSON.parse(JSON.stringify(npc)); npc.postBattle = postBattle; var rep = false, count = 0, list = player.party.map(function(x) { var arr = [type1(x), type2(x)].sort(); return arr.join("|"); }); if (npc.name !== "Trainer Nub") { for (var e = 0; e < 6; e++) { count = countRepeated(list, list[e]); if (count > 3) { rep = player.party[e]; break; } } if (rep) { var oppTeam = npc.party.concat().shuffle(), result = [], rest = [], t1 = type1(rep), t2 = type2(rep), p1, p2; for (e = 0; e < oppTeam.length; e++) { p1 = type1(oppTeam[e]); p2 = type2(oppTeam[e]); if (result.length < count && this.checkEffective([p1, p2], [t1, t2]) >= this.checkEffective([t1, t2], [p1, p2])) { result.push(oppTeam[e]); } else { rest.push(oppTeam[e]); } } rest = rest.shuffle(); while (result.length < 6) { result.push(rest.shift()); } npc.party = result; } } npc.postArgs.name = npc.name; player.money -= cost; safari.updateEconomyData(-cost, "questFee"); safaribot.sendHtmlMessage(src, trainerSprite + "Arena Clerk: I see you paid the $" + addComma(cost) + " Entry Fee, so you can now proceed to your challenge against " + npc.name + "!", safchan); var battle = new Battle(src, npc); currentBattles.push(battle); safari.toRecentQuests(player, "arena"); player.notificationData.lastArenaParty = [].concat(player.party); player.notificationData.arenaWaiting = true; safari.pendingNotifications(player.id); safari.clearQuestNotifications(player, "Arena"); player.notificationData.lastArenaTrainer = opt; this.saveGame(player); }; this.fightTower = function(src, data) { var player = getAvatar(src); var reason = "start a battle"; if (cantBecause(src, reason, ["tutorial"])) { return; } var cost = costumeData.ninja.thresh || 499; for (var i = 0; i < player.party.length; i++) { cost = Math.max(cost, getBST(player.party[i])); } var trainerSprite = ''; if (data.length === 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: Welcome to the Battle Tower, a place where you can face successive battles until you lose! Type " + link("/quest tower:help") + " for more information!", safchan); safaribot.sendHtmlMessage(src, "Tower Clerk: To enter the Tower, you must pay a cost based on your strongest Pokémon's BST (currently $" + cost + ") and have a party of 6 Pokémon (currently " + player.party.length + "), then type " + link("/quest tower:start") + "! Be careful though, you may miss a Contest during this challenge!", safchan); safaribot.sendHtmlMessage(src, "Tower Clerk: In the Tower, you may earn Battle Points (BP) as well as other items as rewards! You may spend your BP in our shop at " + link("/quest tower:shop") + ".", safchan); if (player.quests.tower.cooldown >= now()) { safaribot.sendMessage(src, "Tower Clerk: Our trainers are still restoring their Pokémon from the last challenge, so please wait " + timeLeftString(player.quests.tower.cooldown) + " to try again!", safchan); } sys.sendMessage(src, "", safchan); return; } var opt = data[0].toLowerCase(); if (opt === "help") { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: In this challenge, you must battle successive trainers in a row with no time to change your party between each round, until you are defeated!", safchan); safaribot.sendMessage(src, "Tower Clerk: You receive prizes such as Apricorns, Candy Dusts, Silver Coins and others according to your progress! Try to win lots of rounds for better rewards. Additionally, for every 7 floors that you clear, you will receive some BP.", safchan); safaribot.sendMessage(src, "Tower Clerk: When you reach certain floors, you may be challenged by one of our Tower bosses! Beating them will give you great items and large amounts of BP!", safchan); safaribot.sendMessage(src, "Tower Clerk: Be careful though, once you start the challenge, you can't stop until you lose, so you may miss Contests or important spawns!", safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Tower Clerk: If you are ready to challenge the Battle Tower, type " + link("/quest tower:start") + ". To view our BP shop, type " + link("/quest tower:shop") + ".", safchan); sys.sendMessage(src, "", safchan); return; } if (["shop", "buy"].contains(opt)) { var itemChoice = data[1], amount = data[2]; var wares = { "@gem": 1, "@dew": 1, "@pokeblock": 2, "@starpiece": 3, "@amulet": 3, "@soothe": 3, "@scarf": 3, "@eviolite": 3, "@crown": 3, "@honey": 3, "@battery": 3, "@nugget": 7, "@fossil": 7, "@pack": 10, "@mushroom": 15, "@cometshard": 15, "@bignugget": 20, "@platinum": 20, "@sunshard": 30, "@moonshard": 30, "@miracle": 40, }; if (!itemChoice) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: You can purchase some souvenirs here with your hard-earned " + es(finishName("battlepoint")) + " (BP)!", safchan); for (var w in wares) { var itemName = getInput(w).name; safaribot.sendHtmlMessage(src, "{0}: {1} BP".format(link("/quest tower:shop:" + itemName + ":1", itemName, true), wares[w]), safchan); } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "You currently have " + plural(player.balls.battlepoint, "battlepoint") + ". To buy something, use " + link("/quest tower:shop:", "/quest tower:shop:[Item Name]:[Amount to Buy]", true), safchan); return; } if (cantBecause(src, "buy items", ["contest", "auction", "event", "pyramid"])) { return; } var input = getInput(itemChoice); var amount = parseInt(amount); if (!wares.hasOwnProperty(input.input)) { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: \"" + itemChoice + "\"? That's not something we have in stock!", safchan); return; } if (!amount || isNaN(amount) || amount <= 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: That's not a valid number!", safchan); return; } var price = wares[input.input]; var cost = price * amount; if (player.balls.battlepoint < cost) { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: You don't have enough {0} to afford that! You need {1}, but you only have {2}.".format(es(finishName("battlepoint")), plural(cost, "battlepoint"), player.balls.battlepoint), safchan); return; } if (!this.isBelowCap(src, input.id, amount, input.type)) { return; } player.balls.battlepoint -= cost; player.balls[input.id] += amount; safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: Thanks for buying " + plural(amount, input.name) + " for " + plural(cost, "battlepoint") + "! You now have " + (input.type == "item" ? plural(player.balls[input.id], input.name) + " and " : "") + plural(player.balls.battlepoint, "battlepoint") + "!", safchan); safari.saveGame(player); sys.appendToFile(shopLog, now() + "|||Tower Clerk NPC::" + amount + "::" + input.name + "::" + price + "::" + cost + "::bp|||" + sys.name(src) + "\n"); return; } if (opt !== "start") { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: If you are ready to challenge the Battle Tower, type /quest tower:start. If you need more information, type /quest tower:help.", safchan); return; } if (stopQuests.tower) { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: Sorry, our Trainers are taking a short lunch break. Please return at a later point in time!", safchan); return; } if (player.quests.tower.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: You want to challenge the Battle Tower again already? Please take a rest while our trainers are preparing their teams, you will be able to challenge again in " + timeLeftString(player.quests.tower.cooldown) + "!", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid", "baking"])) { return; } if (player.party.length < 6) { safaribot.sendMessage(src, "Your party must have 6 Pokémon for this challenge!", safchan); return; } if (hasPokeInShop(src)) { return; } if (player.money < cost) { safaribot.sendMessage(src, "You need to pay $" + addComma(cost) + " to enter the Tower, but you only have $" + addComma(player.money) + "!", safchan); return; } player.money -= cost; safari.updateEconomyData(-cost, "questFee"); this.saveGame(player); var postBattle = function(name, isWinner, playerScore, npcScore, args, viewers) { var player = getAvatarOff(name); var id = sys.id(name); var bossLevels = [21, 35, 49, 70]; var startingMinBST = 0; var startingMaxBST = 430; var startingMinPower = 10; var startingMaxPower = 40; if (isWinner) { var skip = false; if (player.costume === "ninja" && chance(costumeData.ninja.rate) && ((args.count + 1) % 7 !== 0)) { skip = true; } var ac = args.count + skip; var counterMon = (ac >= 42) && chance(0.25 + (ac * Math.random() * 0.033)) ? parseInt(player.party.random(), 10) : null; var nextMinBST = startingMinBST; var nextMaxBST = startingMaxBST; var minPower = startingMinPower; var maxPower = startingMaxPower; var littlecup = ac <= 6; var onlyEvolved = ac >= 13; var specialIncludes = []; if (ac > 3) { nextMaxBST += 10; // 440 } if (ac >= 7) { maxPower += 10; nextMaxBST += 20; // 460 } if (ac > 10) { nextMaxBST += 20; // 480 } if (ac >= 13) { maxPower += 10; nextMaxBST += 50; // 530 } if (ac > 17) { nextMaxBST += 20; // 550 } if (ac >= 20) { minPower += 5; nextMaxBST += 30; // 580 } if (ac >= 34) { minPower += 5; maxPower += 20; nextMinBST = startingMaxBST; // 430 nextMaxBST += 20; // 600 } if (ac >= 41) { nextMinBST += 70; // 500 } if (ac >= 48) { maxPower += 10; nextMinBST += 30; // 530 nextMaxBST += 70; // 670 } if (ac >= 55) { minPower += 5; } if (ac >= 62) { specialIncludes = [65748, 65630, 65717, 65678]; // Mega Scizor, Mega Gengar, Mega Ampharos, Mega Aerodactyl } if (ac >= 69) { minPower = startingMinPower + Math.floor(args.count / 2); maxPower = startingMaxPower + Math.floor(ac / 2); specialIncludes = [65984, 65842, 65790, 65542, 65545, 65796, 65666, 131730]; // Mega Lucario, Mega Aggron, Mega Sceptile, Mega Charizard X, Mega Blastoise, Mega Swampert, Mega Gyarados, Ash Greninja nextMaxBST += 999; } if (ac >= 76) { // Giratina-Origin, Mega Diancie, Kyurem-Black, Kyurem-White, Mega Garchomp, Mega Latios, Mega Latias, Mega Metagross, Mega Salamence, Zygarde-Complete, Zacian-Crowned Sword, Zamazenta-Crowned Shield, Arceus-Water, Arceus-Steel, Arceus-Fairy, Ultra Necrozma, Primal Groudon, Primal Kyogre, Mega Rayquaza, Eternatus-Eternamax specialIncludes = specialIncludes.concat([66023, 66255, 131718, 66182, 65981, 65917, 65916, 65912, 65909, 131790, 66424, 66425, 655853, 524781, 1114605, 197408, 65919, 65918, 65920, 66426]); } var bossNPCs = [ { name: "Tower Tycoon Palmer", party: [681, 612, 537, 350, 464, 149], power: [30 + player.quests.tower.bonusPower, 120 + player.quests.tower.bonusPower], }, { name: "Salon Maiden Anabel", party: [65, 244, 461, 376, 373, 143], power: [40 + player.quests.tower.bonusPower, 120 + player.quests.tower.bonusPower], }, { name: "Tower Tycoon Palmer", party: [66217, 887, 491, 486, 485, 488], power: [45 + player.quests.tower.bonusPower, 130 + player.quests.tower.bonusPower], }, { name: "Salon Maiden Anabel", party: [243, 65917, 65984, 65909, 65912, 143], power: [50 + player.quests.tower.bonusPower, 130 + player.quests.tower.bonusPower], } ]; var npc, clone = args.usedClone || false, cloneFloor = args.cloneFloor || false; if (bossLevels.contains(ac + 1)) { npc = bossNPCs[bossLevels.indexOf(ac + 1)]; } else { if (ac >= bossLevels[0] && chance(0.2) && !clone && ((ac + 1) % 7 === 0)) { npc = { name: player.id.toCorrectCase() + "?", party: player.party, power: [-1, -1], copy: true }; args.usedClone = true; cloneFloor = ac + 1; } else { npc = { name: "Trainer " + generateName(), party: generateTeam(6, nextMinBST, nextMaxBST, null, null, onlyEvolved, littlecup, counterMon, specialIncludes.shuffle().shift()), power: [minPower, maxPower], }; } } npc.postBattle = postBattle; npc.postArgs = { count: ac + 1, reward: args.reward, pinap: args.pinap, usedClone: args.usedClone, name: npc.name, cloneFloor: cloneFloor, skippedPrevious: skip }; npc.desc = "Tower Lvl. " + (ac + 1); /*var roo = sys.id("ripper roo"); sys.sendMessage(roo, "Tower Lvl. " + (ac + 1), safchan); sys.sendMessage(roo, "Minimum Power: " + minPower, safchan); sys.sendMessage(roo, "Maximum Power: " + maxPower, safchan); sys.sendMessage(roo, "Final Power: " + npc.power.join(", "), safchan); sys.sendMessage(roo, "Minimum BST: " + nextMinBST, safchan); sys.sendMessage(roo, "Maximum BST: " + nextMaxBST, safchan); sys.sendMessage(roo, "Special Includes: " + specialIncludes.map(poke), safchan);*/ sys.sendMessage(id, "", safchan); safaribot.sendMessage(id, "Tower Clerk: Good job! You have defeated " + plural(args.count, "trainer") + " so far! Now for the next battle!", safchan); if (skip) { safaribot.sendMessage(id, "You carefully time your movements as you head up to the next floor. You dash for the stairs to skip battling, but not before mocking the unaware Trainer!", safchan); } var minBP = 2, maxBP = 7; var mod, rew, amt, loop, c, bp; c = args.count; mod = c % 7; loop = Math.floor(c / 7) + 1; bp = 0; var cbonus = safari.hasCostumeSkill(player, "towerLoot") ? (1.2 + (safari.getCostumeLevel(player)/50)) : 1; if (bossLevels.contains(c)) { bp = [10, 15, 20, 50][bossLevels.indexOf(c)]; rew = ["mushroom", "mega", "fragment", "fragment"][bossLevels.indexOf(c)]; amt = [1, 2, 1, 4][bossLevels.indexOf(c)]; } else { if (mod === 0) { // every 7th battle bp = Math.min(minBP + (c / 7 - 1), maxBP); } switch (mod) { case 0: rew = "gem"; amt = 1; break; case 1: rew = "gacha"; amt = Math.floor(loop * 1.2 * cbonus); break; case 2: rew = "bait"; amt = Math.floor(loop * 1.2 * cbonus); break; case 3: rew = "dust"; amt = Math.round(loop * cbonus) * 10; break; case 4: rew = "money"; amt = Math.round(3 * loop * cbonus) * 10; break; case 5: rew = ["bluapricorn", "ylwapricorn", "grnapricorn", "blkapricorn"].random(); amt = Math.floor(loop * 1.3 * cbonus); break; case 6: rew = "silver"; amt = Math.ceil(loop / 2); break; } } if (!npc.postArgs.reward.hasOwnProperty("battlepoint")) { npc.postArgs.reward.battlepoint = 0; } if (!npc.postArgs.reward.hasOwnProperty(rew)) { npc.postArgs.reward[rew] = 0; } npc.postArgs.reward[rew] += amt; npc.postArgs.reward.battlepoint += bp; var was_were; if (amt) { was_were = amt > 1 ? "were" : "was"; safaribot.sendMessage(id, (rew === "money" ? "$" + amt : plural(amt, rew)) + " " + was_were + " added to your final prizes!", safchan); } if (bp) { was_were = bp > 1 ? "were" : "was"; safaribot.sendMessage(id, plural(bp, "battlepoint") + " " + was_were + " added to your final prizes!", safchan); } for (var e = 0; e < viewers.length; e++) { if (viewers[e] !== name.toLowerCase()) { safaribot.sendMessage(sys.id(viewers[e]), "Tower Clerk: " + name + " has defeated " + plural(args.count, "trainer") + " so far!", safchan); if (skip) { safaribot.sendMessage(sys.id(viewers[e]), "You notice " + name + " sneak around on the next floor and dash for the stairs without being noticed. Impressed by their ability, you remain quiet so they don't get caught.", safchan); } } } if (id && getAvatar(id)) { var silent = (ac + 1) % 7 !== 0; var battle = new Battle(id, npc, silent); for (e = 0; e < viewers.length; e++) { if (!battle.viewers.contains(viewers[e])) { battle.viewers.push(viewers[e]); } } if (!viewers.contains(name.toLowerCase())) { battle.viewers.splice(battle.viewers.indexOf(name.toLowerCase()), 1); } if (silent) { battle.sendToViewers("A battle between " + battle.name1 + " and " + battle.name2 + " (" + npc.desc + ") has started!"); } currentBattles.push(battle); } else { player.quests.tower.cooldown = now() + hours(0.25); safari.saveGame(player); for (e = 0; e < viewers.length; e++) { safaribot.sendMessage(sys.id(viewers[e]), "Tower Clerk: The challenge was cancelled because " + name + " is nowhere to be found for their next match!", safchan); } } } else { var count = args.count - 1 - args.skippedPrevious, updatelb = false; if (count > player.records.towerHighestNew) { player.records.towerHighestNew = count; if (leaderboards.towerHighestNew.length === 0 || count > leaderboards.towerHighestNew[0].value) { safaribot.sendHtmlAll("" + name.toCorrectCase() + " has defeated " + plural(count, "trainer") + " at the Battle Tower and set a new record!", safchan); updatelb = true; } } if (args.cloneFloor && count >= args.cloneFloor) { player.records.towerSecretBosses += 1; if (!args.reward.hasOwnProperty("mirror")) { args.reward.mirror = 0; } args.reward.mirror += 2; } player.records.towerTotal += count; for (var i = 0; i < bossLevels.length; i++) { if (count >= bossLevels[i]) { player.records.towerBosses += 1; } } if (count >= Math.max.apply(null, bossLevels)) { player.records.towerFinalBosses += 1; } var traveledCount = count; var h = 0; var maxH = 4; //var penalty = player.quests.tower.bonusPower !== 0; //could edit player value to a negative number mid-tower to act as a kill switch in order to complete a run if (traveledCount === 0) { h = 0.25; } else { h = 1; for (var i = 0; i < Math.floor(traveledCount / 7); i++) { h += 0.5; // 30 mins for each 7 floors cleared } } for (var i = 0; i < bossLevels; i++) { if (traveledCount >= (bossLevels[i] - 1)) { h += 0.5; // extra 30 mins for each boss attempted i.e. each boss is worth 1h total } } h = Math.min(h, maxH); player.quests.tower.cooldown = now() + Math.round(hours(h) * (1 - safari.getFortune(player, "questcd", 0, "tower")) * (1 - safari.getAuraEffect(player, "questcd", 0))); var rewardText = []; for (var r in args.reward) { if (r == "money") { rewardText.push("$" + args.reward[r]); } else { rewardText.push(plural(args.reward[r], finishName(r))); } } if (id) { var team = " Your Team: " + (player.costume !== "none" ? " [" + costumeAlias(player.costume, true, true) + " costume] " : "") + readable(player.party.map(poke)) + "."; if (count === 0) { safaribot.sendHtmlMessage(id, trainerSprite + "Tower Clerk: Too bad, " + name + "! You couldn't defeat any trainer!", safchan); } else if (count < 5) { safaribot.sendHtmlMessage(id, trainerSprite + "Tower Clerk: Game over, " + name + "! You defeated " + count + " trainer" + (count == 1 ? "" : "s") + "!" + team, safchan); } else if (count < 20) { safaribot.sendHtmlMessage(id, trainerSprite + "Tower Clerk: Good job, " + name + "! You defeated " + count + " trainers!" + team, safchan); } else if (count < 50) { safaribot.sendHtmlMessage(id, trainerSprite + "Tower Clerk: Amazing, " + name + "! You defeated " + count + " trainers! Congratulations!" + team, safchan); } else { safaribot.sendHtmlMessage(id, trainerSprite + "Tower Clerk: OH MY GOD, " + name + "! You defeated " + count + " trainers! That was superb!" + team, safchan); } if (args.skippedPrevious) { safaribot.sendHtmlMessage(id, trainerSprite + "Tower Clerk: Hey wait a minute! You didn't defeat the previous Trainer, so I can't count it in your performance assessment.", safchan); } if (rewardText.length > 0) { safaribot.sendMessage(id, "Tower Clerk: For your performance in the Battle Tower challenge, we reward you with " + readable(rewardText, "and") + "!", safchan); } } for (r in args.reward) { if (r == "money") { player.money += args.reward[r]; player.records.towerEarnings += args.reward[r]; safari.updateEconomyData(args.reward[r], "questFee"); if (player.money > moneyCap) { player.money = moneyCap; } } else { if (r == "silver") { player.records.towerSilver += args.reward[r]; } else if (r == "battlepoint") { player.records.towerBP += args.reward[r]; } rewardCapCheck(player, r, args.reward[r], true); } } for (var e = 0; e < viewers.length; e++) { if (viewers[e] !== name.toLowerCase() && sys.id(viewers[e])) { safaribot.sendHtmlMessage(sys.id(viewers[e]), "Tower Clerk: " + name + " was able to clear " + plural(count, "trainer") + "!", safchan); if (rewardText.length > 0) { safaribot.sendHtmlMessage(sys.id(viewers[e]), "Tower Clerk: For their performance in the Battle Tower challenge, " + name + " was rewarded with " + readable(rewardText, "and") + "!", safchan); } } } player.notificationData.lastTowerParty = [].concat(player.party); if (!player.costumes.contains("ninja") && count >= costumeData.ninja.acqReq && player.costume !== "battle") { var noAcq; for (var i = 0; i < player.party.length; i++) { if (getBST(player.party[i]) > costumeData.ninja.thresh) { noAcq = true; break; } } if (!noAcq) { player.costumes.push("ninja"); safaribot.sendHtmlMessage(src, "Received the following costume: " + costumeData.ninja.fullName + ".", safchan); player.ninjaParty = player.party.concat(); } } var passed = true; for (var i = 0; i < player.party.length; i++) { if (getBST(player.party[i]) > 480) { passed = false; break; } } var k = Object.keys(effectiveness), m = [], y, l = [], u = true; for (var t in k) { y = true; for (var p in player.party) { if (!hasType(player.party[p],k[t])) { y = false; } } if (y) { m.push(k[t]); } } for (var p in player.party) { if (l.indexOf(player.party[p]) !== -1) { u = false; break; } l.push(player.party[p]); } safari.missionProgress(player, "tower", count, 1, {mono: m, unique: u, lowBST: passed}); safari.costumeEXP(player, "fighttower", 4 + (count * 3)); var currentLB = monthlyLeaderboards["towerHighestNew"].get(player.id) || 0; if (currentLB < count) { safari.addToMonthlyLeaderboards(player.id, "towerHighestNew", count, true); } player.notificationData.towerWaiting = true; safari.pendingNotifications(player.id); safari.clearQuestNotifications(player, "Tower"); if (safari.events.towerTroubleEnabled && safari.events.towerTroubleData.searchText !== "") { if (!(safari.events.towerTroubleData.players.hasOwnProperty(player.idnum+""))) { safari.events.towerTroubleData.players[player.idnum+""] = 0; } if (count > safari.events.towerTroubleData.players[player.idnum+""]) { if (safari.satisfiesTowerTrouble(src, player.party)) { safaribot.sendHtmlMessage(src, "Your Tower Trouble high score is now " + count + "!", safchan); safari.events.towerTroubleData.players[player.idnum+""] = Math.max(safari.events.towerTroubleData.players[player.idnum+""], count); } } else { safaribot.sendHtmlMessage(src, "Your Tower Trouble high score is " + safari.events.towerTroubleData.players[player.idnum+""] + "!", safchan); } } player.quests.tower.bonusPower = 0; sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Tower|||Challenged using " + readable(player.party.map(poke)) + " and " + costumeData[player.costume].fullName + " Costume|||Cleared " + plural(count, "floor") + ", defeated by " + args.name + (rewardText.length > 0 ? ", received " + readable(rewardText, "and") : "") + "\n"); safari.saveGame(player); if (updatelb) { safari.updateLeaderboards(); } } }; var pinapActive = []; for (var i = 0; i < player.party.length; i++) { var hit = false; if (i >= player.helds.length) { continue; } if (parseInt(player.helds[i], 10) != 7) { continue; } for (var e = 0; e < player.party.length; e++) { if (getBST(player.party[e]) == getBST(player.party[i]) && (!(e == i))) { hit = true; continue; } } if (!(hit)) { player.helds[i] = -1; pinapActive.push(player.party[i]); safaribot.sendHtmlMessage(src, "Your " + poke(player.party[i], true) + " consumed its Pinap Berry!", safchan); } } var npc = { name: "Trainer " + generateName(), party: generateTeam(6, 0, 430, null, null, null, true), power: [10, 30], postBattle: postBattle, postArgs: { count: 1, reward: {}, pinap: pinapActive, usedClone: false, skippedPrevious: false }, desc: "Tower Lvl. 1" }; npc.postArgs.name = npc.name; safaribot.sendHtmlMessage(src, trainerSprite + "Tower Clerk: You have a party with 6 Pokémon and paid the $" + addComma(cost) + " entry fee, therefore you are allowed to enter that door and start your Battle Tower challenge!", safchan); var battle = new Battle(src, npc); currentBattles.push(battle); safari.toRecentQuests(player, "tower"); }; this.wonderTrade = function(src, data) { var player = getAvatar(src); var quest = player.quests.wonder; var trainerSprite = ''; if (!data[0]) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: Welcome to Wonder Trade, a place where you can trade a Pokémon you own for a random one!", safchan); safaribot.sendHtmlMessage(src, "Wonder Trade Operator: Use /quest wonder:[Pokémon] to choose a Pokémon to trade. If you wish to know how our system works, type " + link("/quest wonder:help") + "!", safchan); if (quest.cooldown > now()) { safaribot.sendMessage(src, "Wonder Trade Operator: Due to the rules imposed by the Pokémon Association, we cannot allow another trade in less than " + timeLeftString(quest.cooldown) + "!", safchan); } sys.sendMessage(src, "", safchan); return; } var opt = data[0].toLowerCase(); if (opt == "help") { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: To get a trade here you simply choose one of your Pokémon with " + link("/quest wonder:", "/quest wonder:[Pokémon Name]", true) + ", pay a small fee and then you will receive a random Pokémon immediately!", safchan); safaribot.sendMessage(src, "Wonder Trade Operator: The fee is based on your Pokémon's BST, and you will receive a Pokémon within the same BST range.", safchan); safaribot.sendHtmlMessage(src, "Wonder Trade Operator: The available BST ranges are " + link("/findd bst 175 249", "175~249") + " ($50 fee), " + link("/findd bst 250 319", "250~319") + " ($100), " + link("/findd bst 320 389", "320~389") + " ($150), " + link("/findd bst 390 459", "390~459") + " ($300), " + link("/findd bst 460 529", "460~529") + " ($500) and " + link("/findd bst 530 599", "530~599") + " ($750).", safchan); safaribot.sendMessage(src, "Wonder Trade Operator: Also be aware that you CANNOT receive legendaries from Wonder Trade!", safchan); sys.sendMessage(src, "", safchan); return; } if (quest.cooldown > now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: Due to the rules imposed by the Pokémon Association, we cannot allow another trade in less than " + timeLeftString(quest.cooldown) + "!", safchan); return; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only trade after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (data.length < 1) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: Please use /quest wonder:[Pokémon] to choose the Pokémon you wish to trade. You can also type " + link("/quest wonder:help") + " for more information.", safchan); return; } var input = getInputPokemon(data[0]); if (!input.num) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: That's not a valid Pokémon!", safchan); return; } var id = input.id; var bst = getBST(id); if (bst >= 600) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: I'm terribly sorry, but we don't accept Pokémon with a BST of 600 or more in the Wonder Trade!", safchan); return; } if (isMega(id)) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: I'm terribly sorry, but we don't accept Mega Pokémon in the Wonder Trade!", safchan); return; } if (!player.pokemon.contains(id)) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: You don't have " + an(pokePlain(id)) + "!", safchan); return; } if (stopQuests.wonder) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: Sorry, we are having problems with our communication and trading devices, they will be fixed shortly. Please return at a later point in time!", safchan); return; } if (bst < 180) { bst = 180; } var rangeIndex = Math.floor((bst - 180)/70); var bstRange = [[175,249], [250,319], [320,389], [390,459], [460,529], [530,599]][rangeIndex]; var fee = [50, 100, 150, 300, 500, 750][rangeIndex]; var cooldown = [0.5, 0.75, 1, 1.33, 1.66, 2][rangeIndex]; if (data.length < 2 || data[1].toLowerCase() !== "confirm") { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: " + input.name + "'s BST is in the " + bstRange.join("~") + " range, so your fee will be $" + addComma(fee) + "!", safchan); safaribot.sendHtmlMessage(src, "Wonder Trade Operator: If you are sure you want to proceed, type " + link("/quest wonder:" + input.input + ":confirm") + " and you will receive a Pokémon in the same range!", safchan); sys.sendMessage(src, "", safchan); return; } if (safari.getEffectiveLead(player, true) === input.id && countDuplicates(player.pokemon, input.id) === 1) { if (currentPokemon) { safaribot.sendMessage(src, "You can't trade away your lead Pokémon when there's a wild Pokémon out!", safchan); return; } else if (contestCount > 0 && !contestForfeited.contains(player.idnum)) { safaribot.sendMessage(src, "You can't trade away your lead Pokémon during a Contest!", safchan); return; } } /* if (currentPokemon) { safaribot.sendMessage(src, "You can't finish this quest while there's a wild Pokémon around.", safchan); return; }*/ if (player.money < fee) { safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: You don't have $" + addComma(fee) + "!", safchan); return; } if (!canLosePokemon(src, input.input + "", "trade")) { return; } safaribot.sendHtmlMessage(src, trainerSprite + "Wonder Trade Operator: So you want to try the Wonder Trade? Please give me the $" + addComma(fee) + " and your " + input.name + "!", safchan); var receivedId, receivedBST, pickedForm, isShiny = sys.rand(0, shinyChance) < (input.shiny ? 16 : 1); // Kills spawns that aren't supposed to appear in the wild due to edited BST var defTheme = contestThemes.hasOwnProperty("none") ? contestThemes.none : {"name":"Default","types":[],"excludeTypes":[],"include":[],"exclude":[],"editBST":{},"floorBST":300,"ceilBST":600,"icon":0}; do { receivedId = sys.rand(1, highestDexNum); if (receivedId in wildForms && chance(0.5)) { pickedForm = sys.rand(1, wildForms[receivedId] + 1); receivedId = pokeInfo.calcForme(receivedId, pickedForm); } receivedBST = defTheme.hasOwnProperty("editBST") && defTheme.editBST.hasOwnProperty(""+receivedId) ? defTheme.editBST[""+receivedId] : getBST(receivedId); } while (receivedBST < bstRange[0] || receivedBST > bstRange[1] || isLegendary(receivedId) || receivedId == input.num); if (noShinySprite.indexOf(receivedBST) !== -1) { shiny = false; } if ([1, 4, 7, 152, 155, 158, 252, 255, 258, 387, 390, 393, 495, 498, 501, 650, 653, 656, 722, 725, 728, 810, 813, 816].contains(receivedId)) { player.records.wonderStarter += 1; } receivedId = isShiny ? receivedId + "" : receivedId; safaribot.sendMessage(src, "Wonder Trade Operator: Please wait a moment while we process the trade...", safchan); this.removePokemon(src, id); player.pokemon.push(receivedId); player.money -= fee; safari.updateEconomyData(-fee, "questFee"); player.records.wonderTrades += 1; quest.cooldown = now() + Math.round(hours(cooldown) * (1 - safari.getFortune(player, "questcd", 0, "wonder")) * (1 - safari.getAuraEffect(player, "questcd", 0))); player.notificationData.wonderWaiting = true; var twins = false; if (this.getFortune(player, "wondertwins", 0)) { twins = true; player.pokemon.push(receivedId); safaribot.sendMessage(src, "Wonder Trade Operator: The trade was finished successfully! You traded your " + input.name + " and received a... WAIT! You received TWO " + poke(receivedId) + "!!", safchan); } else { safaribot.sendMessage(src, "Wonder Trade Operator: The trade was finished successfully! You traded your " + input.name + " and received " + an(poke(receivedId)) + "!", safchan); } this.missionProgress(player, "wonder", receivedId, 1, {}); sys.sendMessage(src, "", safchan); safari.pendingNotifications(player.id); this.saveGame(player); this.logLostCommand(sys.name(src), "quest wonder:" + data.join(":"), "received " + (twins ? "two " : "") +poke(receivedId)); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Wonder Trade|||Gave " + input.name + "|||Received " + (twins ? "two " : "") +poke(receivedId) + "\n"); if (isRare(receivedId)) { sys.appendToFile(mythLog, now() + "|||" + (twins ? "Two " : "") + poke(receivedId) + "::wonder traded::" + sys.name(src) + "\n"); sys.sendAll("", safchan); safaribot.sendHtmlAll("Wow! {0} received {1} from a Wonder Trade!".format(sys.name(src), pokeInfo.icon(receivedId, typeof receivedId === "string") + " " + poke(receivedId) + (twins ? " TWINS" : "")), safchan); sys.sendAll("", safchan); } safari.toRecentQuests(player, "wonder trade"); }; this.bakingQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; var quest = player.quests.baking; if (bakingRequests.hasOwnProperty(player.id) && now() > bakingRequests[player.id].deadline) { safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: You prepared for Baking quest before, but couldn't start it before deadline! Please use " + link("/quest baking:start:", null, true) + " to start it again!", safchan); delete bakingRequests[player.id]; } if (data.length < 1 || !data[0]) { action = "*"; } else { action = data[0].toLowerCase(); } switch (action) { case "start": var name = sys.name(src); if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only participate in Baking after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (quest.cooldown > now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: You need to wait " + timeLeftString(quest.cooldown) + " before baking again!", safchan); return; } cost = Math.round(cost * (1 - (safari.hasCostumeSkill(player, "bakingDiscount") ? 0.5 : 0))); if (player.money < cost) { safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: You need $" + addComma(cost) + " to enter the kitchen!", safchan); return; } if (stopQuests.baking) { safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: Sorry, but we're putting out a fire in the kitchen right now!", safchan); return; } if (cantBecause(src, "start a Baking quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } if (player.balls.deluxe > 0) { safaribot.sendMessage(src, "You can't make any more deluxe bait until you use up or sell what you have!", safchan); return; } if (bakingRequests.hasOwnProperty(player.id.toLowerCase())) { var invited = (bakingRequests[player.id].invites).map(function(x) { return x.toCorrectCase(); }); safaribot.sendMessage(src, "You already have invited " + readable(invited, "and") + " to join you in the kitchen!", safchan); return; } if (data.length < 2) { safaribot.sendHtmlMessage(src, "You can invite between one and four friends to join in baking fun! Use " + link("/quest baking:start:Name1,Name2,Name3,Name4", null, true) + ".", safchan); return; } var players = data[1].toLowerCase().replace(/\s?,\s?/g, ",").split(","); var id1, p1, n1, taking = [], takingPretty = []; for (var p in players) { id1 = sys.id(players[p]); p1 = getAvatar(id1); if (!p1) { safaribot.sendMessage(src, "There's no player with the name '" + players[p] + "' around to join you in the kitchen!", safchan); return; } n1 = p1.id.toLowerCase(); if (n1 == player.id.toLowerCase()) { safaribot.sendMessage(src, "You cannot invite yourself to the kitchen!", safchan); return; } if (taking.contains(n1)) { safaribot.sendMessage(src, "You cannot invite the same person twice!", safchan); return; } taking.push(n1.toLowerCase()); takingPretty.push(toColored(n1.toCorrectCase(), n1)); } if (taking.length > 4) { safaribot.sendHtmlMessage(src, "You can only invite up to 4 friends! Use " + link("/quest baking:start:Name1,Name2,Name3,Name4", null, true) + ".", safchan); return; } safaribot.sendHtmlMessage(src, "You invited " + takingPretty.join(", ") + " to join you in the kitchen to bake some baits!", safchan); safaribot.sendMessage(src, "The quest will start if they accept your invitation within 1 minute!", safchan); for (var p in taking) { safaribot.sendHtmlMessage(sys.id(taking[p]), name + " is inviting " + takingPretty.join(", ") + " to join their party in the Baking quest! To accept it, type " + link("/quest baking:join:"+name) + " within the next minute!", safchan); } bakingRequests[player.id.toLowerCase()] = { invites: taking, accepted: [], deadline: now() + 60*1000 }; break; case "join": if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only enter the kitchen after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (cantBecause(src, "join a Baking quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } var invites = []; for (var e in bakingRequests) { if (bakingRequests[e].invites.contains(player.id.toLowerCase()) || bakingRequests[e].invites.contains(player.id)) { invites.push(e); } } if (data.length < 2) { safaribot.sendMessage(src, "Please specify whose Baking crew you are joining! Use /quest baking:join:Name for that!", safchan); if (invites.length > 0) { safaribot.sendHtmlMessage(src, "You have pending invites from the following players: " + invites.map(function(x){ return link("/quest baking:join:" + x, x.toCorrectCase()); }), safchan); } return; } if (bakingRequests.hasOwnProperty(player.id.toLowerCase())) { safaribot.sendMessage(src, "You can't accept an invitation to the kitchen because you are already preparing to start your own Baking quest!", safchan); return; } for (e in bakingRequests) { if (bakingRequests[e].accepted.contains(player.id.toLowerCase()) && now() <= bakingRequests[e].deadline) { safaribot.sendMessage(src, "You already accepted an invitation to a Baking quest!", safchan); return; } } var leader = data[1].toLowerCase(); if (!bakingRequests.hasOwnProperty(leader.toLowerCase())) { safaribot.sendMessage(src, "You didn't receive any invitation from " + leader.toCorrectCase() + "!", safchan); return; } var req = bakingRequests[leader.toLowerCase()]; if (!req.invites.contains(player.id.toLowerCase())) { safaribot.sendMessage(src, "You didn't receive any invitation from " + leader.toCorrectCase() + "!", safchan); return; } if (player.balls.deluxe > 0) { safaribot.sendMessage(src, "You can't make any more deluxe bait until you use up or sell what you have!", safchan); return; } req.accepted.push(player.id.toLowerCase()); if (now() > req.deadline) { safaribot.sendMessage(src, "Baking Quest cancelled because the crews took too long to organize themselves!", safchan); var party = [leader]; for (e in req.invites) { if (req.invites[e] === true) { party.push(e); } } for (e = 0; e < party.length; e++) { safaribot.sendMessage(sys.id(party[e]), "Baking Quest cancelled because the crews took too long to organize themselves!", safchan); } delete bakingRequests[leader]; return; } var isReady = true; for (e in req.invites) { if (!(req.accepted.contains(req.invites[e].toLowerCase()))) { isReady = false; } } safaribot.sendMessage(src, "You joined " + leader.toCorrectCase() + " on their Baking quest!", safchan); safaribot.sendMessage(sys.id(leader), sys.name(src) + " accepted your invitation to the Baking quest!", safchan); if (isReady) { var leaderPlayer = getAvatarOff(leader); var players = [leader].concat(req.invites); var unavailable = [], n, p, m; for (e = 0; e < players.length; e++) { n = players[e]; if (!sys.id(n)) { unavailable.push(n + " couldn't be found"); continue; } p = getAvatar(sys.id(n)); var cost = Math.round(5000 * (1 - (safari.hasCostumeSkill(p, "bakingDiscount") ? 0.5 : 0))); if (!p) { unavailable.push(n + " couldn't be found"); continue; } if (this.isBattling(n) || this.isInAuction(n) || (currentEvent && currentEvent.isInEvent(n))) { unavailable.push(n + " is already participating in another activity"); } for (m in currentPyramids) { if (currentPyramids[m].isInPyramid(n)) { unavailable.push(n + " is already participating in a Pyramid quest"); } } for (m in currentBakings) { if (currentBakings[m].isInKitchen(n)) { unavailable.push(n + " is already participating in a Baking quest"); } } if (p.money < cost) { unavailable.push(n + " doesn't have $" + addComma(cost) + " for the entry fee"); } } if (unavailable.length > 0) { for (e = players.length; e--;) { safaribot.sendMessage(sys.id(players[e]), "Baking Quest couldn't be started due to the following reasons: " + unavailable.join(", "), safchan); } return; } for (var e = 0; e < players.length; e++) { p = getAvatarOff(players[e]); var cost = Math.round(5000 * (1 - (safari.hasCostumeSkill(p, "bakingDiscount") ? 0.5 : 0))); p.money -= cost; safari.updateEconomyData(-cost, "questFee"); p.quests.baking.cooldown = now() + Math.round(hours(0.5) * (1 - safari.getFortune(p, "questcd", 0, "baking")) * (1 - safari.getAuraEffect(p, "questcd", 0))); safaribot.sendHtmlMessage(sys.id(p.id), "Baking Administrator: You paid $" + addComma(cost) + " to enter the kitchen!", safchan); safari.toRecentQuests(p, "baking"); this.saveGame(p); } currentBakings.push(new Baking(players)); delete bakingRequests[leader]; } break; case "berries": case "apricorns": var out = "", item, value = action, obj, str; obj = bakingData[value]; for (var i = 0; i < Object.keys(obj).length; i++) { str = Object.keys(obj)[i]; item = obj[str]; if (str == "milk") { out = "Moomoo Milk: " + item.description; } else { out = itemAlias((str+""), false, true) + ": " + item.description; } sys.sendMessage(src, out, safchan); } break; case "sour": case "salt": case "bitter": case "savory": case "tart": case "umami": case "kokumi": case "spicy": case "citrus": case "none": case "secret": var flavor = action[0].toUpperCase() + action.slice(1); var matches = []; var keys = Object.keys(bakingData.apricorns); for (var i = 0; i < keys.length; i++) { if (bakingData.apricorns[keys[i]].flavor === flavor) { matches.push(finishName(keys[i])); } } keys = Object.keys(bakingData.berries); for (var i = 0; i < keys.length; i++) { if (bakingData.berries[keys[i]].flavor === flavor) { matches.push(finishName(keys[i])); } } if (matches.length > 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: Ingredients with a " + flavor + " flavor are: " + readable(matches, "and") + "!", safchan); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: No ingredients found with that flavor!", safchan); } break; default: safaribot.sendHtmlMessage(src, trainerSprite + "Baking Administrator: You can make customized baits using various ingredients! Get started with " + link("/quest baking:start") + "!", safchan); safaribot.sendHtmlMessage(src, "For information on the ingredients available, type " + link("/quest baking:berries") + " and " + link("/quest baking:apricorns") + " or type /quest baking:[flavor] to see which ingredients have a certain flavor.", safchan); sys.sendMessage(src, "", safchan); break; } }; this.pyramidQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var quest = player.quests.pyramid, cost = 5000, action, trainerSprite = ''; if (pyramidRequests.hasOwnProperty(player.id) && now() > pyramidRequests[player.id].deadline) { safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: You prepared for Pyramid quest before, but couldn't start it before deadline! Please use " + link("/quest pyramid:start:Name1:Name2", null, true) + " to start it again!", safchan); delete pyramidRequests[player.id]; } if (data.length < 1 || !data[0]) { action = "*"; } else { action = data[0].toLowerCase(); } switch (action) { case "hazards": //define these higher up later to avoid duplicated data var hazardMoves = { "plants":[163,13,77], "water":[57,181,593], "boulder":[276,477,529], "toxic":[432,54,318], "pit":[19,438,81], "ice":[498,257,503], "flame":[56,410,16], "electric":[50,324,435], "dark":[572,497,425], "barrier":[100,442,107], "sand":[599,722,239], "target":[512,636,129] }; var hazardAbilities = { "plants": [180, 102], "water": [33], "boulder": [159,37], "toxic": [81,30], "pit": [26,226], "ice": [47,115], "flame": [18, 21, 85], "electric": [140,101], "dark": [35], "barrier": [151], "sand":[8,146], "target":[28,97] }; var hazardNames = { "plants": "Plant", "water": "Water Stream", "boulder": "Boulder", "toxic": "Toxic Gas", "pit": "Pit", "ice": "Ice Pillar", "flame": "Flamethrower", "electric": "Mecha", "dark": "Darkness", "barrier": "Barrier", "sand": "Sandstorm", "target": "Flying Target" }; var hits = [], entry = {}, used = [], out = []; var team = player.party.slice(0, 3); for (var x in hazardMoves) { used = []; entry = {"name": hazardNames[x], "autos": 0, "hits": 0}; for (var j = 0; j < hazardAbilities[x].length; j++) { for (var i = 0; i < team.length; i++) { if (canHaveAbility(team[i], hazardAbilities[x][j])) { entry.autos += 1; used.push(abilityOff(hazardAbilities[x][j])); break; } } } for (var j = 0; j < hazardMoves[x].length; j++) { for (var i = 0; i < team.length; i++) { if (canLearnMove(team[i], hazardMoves[x][j])) { entry.hits += 1; used.push(moveOff(hazardMoves[x][j])); break; } } } if (used.length > 0 && entry.hits + entry.autos > 0) { out.push("You can clear " + (parseInt(entry.hits, 10) + parseInt(entry.autos, 10)) + " " + entry.name + (entry.autos > 0 ? " (" + entry.autos + " automatically)" : "") + " using " + readable(used)); } } if (out.length > 0) { for (var i = 0; i < out.length; i++) { safaribot.sendMessage(src, out[i], safchan); } } else { safaribot.sendMessage(src, "Your team cannot clear any hazards (possibly the worst pyramid team ever)", safchan); } break; case "ban": var opt = []; var hazardNames = { "plants": "Plant", "water": "Water Stream", "boulder": "Boulder", "toxic": "Toxic Gas", "pit": "Pit", "ice": "Ice Pillar", "flame": "Flamethrower", "electric": "Mecha", "dark": "Darkness", "barrier": "Barrier", "sand": "Sandstorm", "target": "Flying Target" }; var altnames = { "water": ['water','streams'], "boulder": ['boulder'], "toxic": ['gas','toxic','poison'], "pit": ['pit','pits'], "ice": ['ice'], "flame": ['flames'], "electric": ['fence'] }; opt = Object.keys(hazardNames); if (data.length < 2) { safaribot.sendHtmlMessage(src, "You can choose a hazard to ban from appearing in your next pyramid run with " + link("/quest pyramid:ban:hazard", false, true) + ".", safchan); return; } var d = data[1].toLowerCase(); for (var x in altnames) { for (var i = 0; i < altnames[x].length; i++) { if (altnames[x][i] == d) { d = x; break; } } } for (var x in hazardNames) { if (hazardNames[x].toLowerCase() == d) { d = x; break; } } if (!(opt.contains(d))) { safaribot.sendHtmlMessage(src, "You can choose a hazard to ban from appearing in your next pyramid run with " + link("/quest pyramid:ban:hazard", false, true) + ". Valid hazards are " + readable(opt.map(function(x) { return x + " (" + hazardNames[x] + ")" })) + ".", safchan); return; } if (player.quests.pyramid.hazards.contains(d)) { safaribot.sendHtmlMessage(src, "You are already banning that hazard!", safchan); return; } player.quests.pyramid.hazards.push(d); player.quests.pyramid.hazards = removeDuplicates(player.quests.pyramid.hazards); if (player.quests.pyramid.hazards.length > 3) { //change this to larger amount later on player.quests.pyramid.hazards.shift(); } safaribot.sendHtmlMessage(src, "You will not encounter any " + player.quests.pyramid.hazards.map(function(x){return hazardNames[x]}).join(" and ") + " during your next Pyramid run.", safchan); break; case "bonus": safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: According to legend, you'll have better fortune if you bring one of these Pokémon to the Pyramid: " + readable(pyrBonusMons.map(poke)) + ".", safchan); safaribot.sendHtmlMessage(src, "It is said that these Pokémon fight as though they had 35 more of each stat, deal 25% more damage in statue and strong rooms, and take 20% less damage in defense rooms!", safchan); break; case "horde": var team = player.party.slice(0, 3); var allTypes = Object.keys(effectiveness); var targets = {}; var eff, eff2; for (var j = 0; j < allTypes.length; j++) { targets[allTypes[j]] = 0; for (var i = 0; i < team.length; i++) { eff = safari.checkEffective([type1(team[i]), type2(team[i])], [allTypes[j]]); eff2 = safari.checkEffective([allTypes[j]], [type1(team[i]), type2(team[i])]); targets[allTypes[j]] = Math.max(targets[allTypes[j]], eff / eff2); } } safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: If the three Pokémon in your party were to enter a horde battle, here's the types you might miss:", safchan); var hitAny = false; for (var x in targets) { if (targets[x] > 2) { continue; } hitAny = true; if (targets[x] > 1) { safaribot.sendHtmlMessage(src, typeIcon(x) + " is somewhat covered.", safchan); } else { safaribot.sendHtmlMessage(src, typeIcon(x) + toColor(" is not covered at all.", "red"), safchan); } } if (!hitAny) { safaribot.sendMessage(src, "... none. That's got to be the best horde combo I've ever seen.", safchan); } break; case "start": case "fossil": var name = sys.name(src); if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only enter the Pyramid after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (quest.cooldown > now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: You need to wait " + timeLeftString(quest.cooldown) + " before starting another Pyramid quest (you can join someone else's meanwhile)!", safchan); return; } var isVoucher = action === "fossil"; if (isVoucher) { if (player.balls.fossil < 1) { safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: You need " + plural(1, "fossil") + " to enter the Pyramid!", safchan); return; } } else { cost = Math.round(cost * (1 - this.getFortune(player, "pyrdiscount", 0, null, true))); if (player.money < cost) { safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: You need $" + addComma(cost) + " to enter the Pyramid!", safchan); return; } } if (stopQuests.pyramid) { safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: Sorry, it seems the Pharaoh's Curse is preventing access to the Pyramid right now. Please return at a later point in time!", safchan); return; } if (cantBecause(src, "start a Pyramid quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } if (pyramidRequests.hasOwnProperty(player.id)) { var invited = Object.keys(pyramidRequests[player.id].invites).map(function(x) { return x.toCorrectCase(); }); safaribot.sendMessage(src, "You already have invited " + readable(invited, "and") + " to join you in the pyramid quest! Please wait for them to accept your invitation!", safchan); return; } if (player.party.length < 3) { safaribot.sendMessage(src, "You need to have a party of 3 Pokémon to enter the Pyramid!", safchan); return; } if (data.length < 3) { safaribot.sendHtmlMessage(src, "You need to invite two other players to join you in the Pyramid! Use " + link("/quest pyramid:start:Name1:Name2", null, true) + ".", safchan); return; } var n1 = data[1].toLowerCase().trim(); var n2 = data[2].toLowerCase().trim(); var id1 = sys.id(n1); var id2 = sys.id(n2); var p1 = getAvatar(id1); var p2 = getAvatar(id2); if (!p1) { safaribot.sendMessage(src, "There's no player with the name '" + data[1] + "' around to join you in the Pyramid!", safchan); return; } if (!p2) { safaribot.sendMessage(src, "There's no player with the name '" + data[2] + "' around to join you in the Pyramid!", safchan); return; } if (n1 == player.id || n2 == player.id) { safaribot.sendMessage(src, "You cannot invite yourself to the Pyramid!", safchan); return; } if (n1 === n2) { safaribot.sendMessage(src, "You cannot invite the same person twice!", safchan); return; } safaribot.sendMessage(src, "You invited " + n1.toCorrectCase() + " and " + n2.toCorrectCase() + " to join you in the Pyramid!", safchan); safaribot.sendMessage(src, "The quest will start if they accept your invitation within 1 minute!", safchan); safaribot.sendHtmlMessage(id1, name + " is inviting you and " + n2.toCorrectCase() + " to join their party in the Pyramid quest! To accept it, type " + link("/quest pyramid:join:"+name) + " within the next minute!", safchan); safaribot.sendHtmlMessage(id2, name + " is inviting you and " + n1.toCorrectCase() + " to join their party in the Pyramid quest! To accept it, type " + link("/quest pyramid:join:"+name) + " within the next minute!", safchan); pyramidRequests[player.id] = { invites: {}, deadline: now() + 60*1000, usingVoucher: isVoucher }; pyramidRequests[player.id].invites[n1] = false; pyramidRequests[player.id].invites[n2] = false; break; case "join": if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only enter the Pyramid after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } if (cantBecause(src, "join a Pyramid quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } var invites = []; for (var e in pyramidRequests) { if(pyramidRequests.hasOwnProperty(e) && pyramidRequests[e].invites.hasOwnProperty(player.id)) { invites.push(e); } } if (data.length < 2) { safaribot.sendMessage(src, "Please specify whose Pyramid's Party you are joining! Use /quest pyramid:join:Name for that!", safchan); if (invites.length > 0) { safaribot.sendHtmlMessage(src, "You have pending invites from the following players: " + invites.map(function(x){ return link("/quest pyramid:join:" + x, x.toCorrectCase()); }), safchan); } return; } if (pyramidRequests.hasOwnProperty(player.id)) { safaribot.sendMessage(src, "You can't accept an invitation to the Pyramid because you are already preparing to start your own Pyramid quest!", safchan); return; } for (e in pyramidRequests) { if (pyramidRequests[e].invites[player.id] && now() <= pyramidRequests[e].deadline) { safaribot.sendMessage(src, "You already accepted an invitation to a Pyramid quest!", safchan); return; } } var leader = data[1].toLowerCase(); if (!pyramidRequests.hasOwnProperty(leader)) { safaribot.sendMessage(src, "You didn't receive any invitation from " + leader.toCorrectCase() + "!", safchan); return; } var req = pyramidRequests[leader]; if (!req.invites.hasOwnProperty(player.id)) { safaribot.sendMessage(src, "You didn't receive any invitation from " + leader.toCorrectCase() + "!", safchan); return; } if (player.party.length < 3) { safaribot.sendMessage(src, "You need to have a party of 3 Pokémon to enter the Pyramid!", safchan); return; } req.invites[player.id] = true; if (now() > req.deadline) { var party = [leader]; for (e in req.invites) { if (req.invites[e] === true) { party.push(e); } } for (e = 0; e < party.length; e++) { safaribot.sendMessage(sys.id(party[e]), "Pyramid Quest cancelled because the party took too long to organize themselves!", safchan); } delete pyramidRequests[leader]; return; } var isReady = true; for (e in req.invites) { if (req.invites[e] === false) { isReady = false; break; } } safaribot.sendMessage(src, "You joined " + leader.toCorrectCase() + " on their Pyramid quest!", safchan); safaribot.sendMessage(sys.id(leader), sys.name(src) + " accepted your invitation to the Pyramid quest!", safchan); var isVoucher = req.usingVoucher; if (isReady) { var leaderPlayer = getAvatarOff(leader); cost = Math.round(cost * (1 - this.getFortune(leaderPlayer, "pyrdiscount", 0, null, true))); var players = [leader].concat(Object.keys(req.invites)); var unavailable = [], n, p, m; for (e = 0; e < players.length; e++) { n = players[e]; if (!sys.id(n)) { unavailable.push(n + " couldn't be found"); continue; } p = getAvatar(sys.id(n)); if (!p) { unavailable.push(n + " couldn't be found"); continue; } if (p.party.length < 3) { unavailable.push(n + "'s party has less than 3 Pokémon"); } if (this.isBattling(n) || this.isInAuction(n) || (currentEvent && currentEvent.isInEvent(n))) { unavailable.push(n + " is already participating in another activity"); } for (m in currentPyramids) { if (currentPyramids[m].isInPyramid(n)) { unavailable.push(n + " is already participating in a Pyramid quest"); } } if (n == leader) { if (!isVoucher && p.money < cost) { unavailable.push(n + " doesn't have $" + addComma(cost) + " for the entry fee"); } else if (isVoucher && p.balls.fossil < 1) { unavailable.push(n + " doesn't have " + plural(1, "fossil") + " for the entry fee"); } } } if (unavailable.length > 0) { for (e = players.length; e--;) { safaribot.sendMessage(sys.id(players[e]), "Pyramid Quest couldn't be started due to the following reasons: " + unavailable.join(", "), safchan); } return; } if (isVoucher) { leaderPlayer.balls.fossil -= 1; this.updateShop(leaderPlayer, "fossil"); } else { leaderPlayer.money -= cost; safari.updateEconomyData(-cost, "questFee"); if (this.getFortune(leaderPlayer, "pyrdiscount", 0, null, true)) { this.useFortuneCharge(leaderPlayer, "pyrdiscount", 1); } } this.saveGame(leaderPlayer); players = players.map(getAvatarOff); if (isVoucher) { safaribot.sendHtmlMessage(sys.id(leader), trainerSprite + "Pyramid Guide: Hey, you need to pay $" + addComma(cost) + " if you want to... Oh, that's a nice " + finishName("fossil") + " you have there! Oh, you are giving it to me? Superb! If anyone asks, I didn't see you entering the Pyramid.", safchan); } else { safaribot.sendHtmlMessage(sys.id(leader), trainerSprite + "Pyramid Guide: You paid $" + addComma(cost) + " to enter the Pyramid!", safchan); } currentPyramids.push(new Pyramid(players[0], players[1], players[2], req.usingVoucher)); safaribot.sendHtmlAll("A Pyramid expedition hosted by {0} has started! [{1}]".format(leader.toCorrectCase(), link("/watchpyr " + leader, "Watch")), safchan); delete pyramidRequests[leader]; } break; case "information": case "info": case "help": safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: In the Pyramid, your goal is to form a party with 2 other players to clear all the seven floors before the leader's stamina drops to 0!", safchan); safaribot.sendMessage(src, "Pyramid Guide: Each player can only bring 3 Pokémon in their party. If you bring more than that, only the first 3 in your party will be used.", safchan); safaribot.sendMessage(src, "Pyramid Guide: Once you enter the pyramid, you can use the '/pyr' command to make your choices in each room.", safchan); safaribot.sendMessage(src, "Pyramid Guide: To advance, you must pass several challenges in each room. After a certain number of rooms, the leader can choose to go up a level or quit the challenge.", safchan); safaribot.sendMessage(src, "Pyramid Guide: Be aware that only the leader pays the entry fee and receives the final reward for the quest, but other players can keep treasures they find during the quest!", safchan); sys.sendMessage(src, "", safchan); break; default: safaribot.sendHtmlMessage(src, trainerSprite + "Pyramid Guide: Welcome to the Pyramid, a place for those looking for a thrilling challenge!", safchan); safaribot.sendHtmlMessage(src, "Pyramid Guide: To learn more about the Pyramid, type " + link("/quest pyramid:help") + "! To enter the Pyramid, pay $" + addComma(cost) + " and invite 2 other players with " + link("/quest pyramid:start:Name1:Name2", null, true) + " to join you!", safchan); safaribot.sendHtmlMessage(src, "Pyramid Guide: If you have " + an(finishName("fossil")) + ", you can type " + link("/quest pyramid:fossil:Name1:Name2", null, true) + " to use it instead of paying $" + addComma(cost) + ". ", safchan); safaribot.sendHtmlMessage(src, "Pyramid Guide: For more information about the pyramid, you can check " + readable([link("/quest pyramid:bonus", "Bonus Pokémon"), link("/quest pyramid:hazards", "Hazards"), link("/quest pyramid:ban", "Banned Hazards"), link("/quest pyramid:horde", "Horde Calculator")]) + ".", safchan); sys.sendMessage(src, "", safchan); } }; this.detectiveClue = function(uid, type, src) { if (safari.detectiveData.hasOwnProperty(uid+"")) { if (safari.detectiveData[uid+""].solved || safari.detectiveData[uid+""].date !== getDay(now())) { return; } var hit = false; for (var i = 0; i < safari.detectiveData[uid+""].clues.length; i++) { if (safari.detectiveData[uid+""].clues[i].unlock == type) { safari.detectiveData[uid+""].clues[i].unlock = "free"; hit = true; safaribot.sendHtmlMessage(src, toColor("You unlocked a clue!", "red") + " You should share your findings with the " + link("/quest detective", "Detective") + "!", safchan); } } if (hit) { permObj.add("detectiveData", JSON.stringify(safari.detectiveData)); } } return; }; this.detectiveQuest = function(src, data, detUp) { function createClue(answer, clues, ind, kind, minstrength, maxstrength, unlock) { var out = {}; var strength = 0; var value, isInteract = false; var pk; if (ind === false) { ind = Math.floor(Math.random() * 4); } var kinds = { "start": 24, "contains": 24, "evolved": 6, "evolves": 4, "evolveNewType": 16, "evolvedChangeType": 12, "canMega": 5, "prime": 2, "fewerAbilities": 1, "stat": 12, "moves": 48 }; var kindsinteract = { "effective": 20, "noletters": 42, "sameregion": 56, "samecolor": 50, "sametype": 7, "weaknesses": 9, "bsthigher": 4 }; var otherind = -1; if (kind == "interact") { isInteract = true; otherind = Math.floor(Math.random() * 3); if (otherind == ind) { otherind++; } } if (generation(answer[ind]) !== generation(answer[otherind])) { kindsinteract["sameregion"] = 0; } if (getPokeColor(answer[ind]) !== getPokeColor(answer[otherind])) { kindsinteract["samecolor"] = 0; } kind = kind == "interact" ? randomSample(kindsinteract) : (kind || randomSample(kinds)); unlock = unlock || "free"; //what is required to access this clue var outText = ""; //what the text of this clue is switch (kind) { case "start": value = poke(parseInt(answer[ind], 10))[0]; if (value == "X") { return false; } for (var i = 0; i < clues.length; i++) { if (value == clues[i].value) { return false; } } strength = ["A", "B", "D", "G", "M", "P", "S", "T"].contains(value) ? 5 : (["C", "H", "L", "R", "W", "F"].contains(value) ? 9 : (["E", "K", "N", "V", "Z"].contains(value) ? 16 : 20)); outText = "{0}'s name starts with " + value + "."; break; case "contains": pk = poke(parseInt(answer[ind], 10)); var sl = Math.floor(Math.random() * pk.length); value = pk.slice(sl, sl + 1); //random letter from that pokemon's name value = value.toUpperCase(); for (var i = 0; i < clues.length; i++) { if (value == clues[i].value) { return false; } } if ([" ", ".", "-", ":", "'"].contains(value)) { return false; } strength = ["A", "E", "I", "N", "O", "R", "S", "T"].contains(value) ? 2 : (["C", "D", "F", "L", "P", "U", "Y"].contains(value) ? 5 : (["K", "B", "W"].contains(value) ? 8 : 13)); outText = "{0} has " + value + " in its name."; break; case "evolves": if (answer[ind] in evolutions) { value = true; outText = "{0} can evolve."; } else { value = false; outText = "{0} cannot evolve."; } for (var i = 0; i < clues.length; i++) { if (ind == clues[i].ind && (clues[i].kind == "evolves" || clues[i].kind == "evolveNewType")) { return false; } } strength = 3; break; case "evolveNewType": if (!(answer[ind] in evolutions)) { return false; } var evolvePoke, hit = false, hold; var evo = evolutions[answer[ind]].evo; if (Array.isArray(evo)) { evolvePoke = evo; } else { evolvePoke = [evo]; } for (var i = 0; i < evolvePoke.length; i++) { hold = evolvePoke[i]; if (type1(hold) == type1(answer[ind]) && type2(hold) == type2(answer[ind])) { continue; } hit = true; } if (!hit) { return false; } for (var i = 0; i < clues.length; i++) { if (ind == clues[i].ind && (clues[i].kind == "evolves" || clues[i].kind == "evolveNewType")) { return false; } } strength = 20; outText = "{0} can evolve into a Pokémon with a different type combination."; break; case "evolvedChangeType": if (!(answer[ind] in devolutions)) { return false; } var devo = devolutions[answer[ind]]; if (type1(answer[ind]) == type1(devo) && type2(answer[ind]) == type2(devo)) { return false; } for (var i = 0; i < clues.length; i++) { if (ind == clues[i].ind && (clues[i].kind == "evolved" || clues[i].kind == "evolvedChangeType")) { return false; } } strength = 19; outText = "{0} is evolved from a Pokémon with a different type combination."; break; case "evolved": if (answer[ind] in devolutions) { value = true; outText = "{0} is evolved."; } else { value = false; outText = "{0} is not evolved."; } for (var i = 0; i < clues.length; i++) { if (ind == clues[i].ind && (clues[i].kind == "evolved" || clues[i].kind == "evolvedChangeType" || (value && clues[i].kind == "canMega"))) { return false; } } strength = 3; break; case "fewerAbilities": for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "fewerAbilities") { return false; } } if (getPokeAbility(answer[ind], 1) > 0 && getPokeAbility(answer[ind], 2) > 0) { return false; } strength = 16; outText = "{0} has fewer than three legal abilities."; break; case "canMega": if (answer[ind] in megaEvolutions) { value = true; } else { return false; //do not supply "cannot mega" clue } if ([382, 383].contains(parseInt(answer[ind]))) { return false; } for (var i = 0; i < clues.length; i++) { if (ind == clues[i].ind && (clues[i].kind == "evolved" || clues[i].kind == "canMega")) { return false; } } strength = 20; outText = "{0} can Mega Evolve."; break; case "stat": for (var i = 0; i < clues.length; i++) { if (ind == clues[i].ind && clues[i].kind == "stat") { return false; } } var st = getStats(answer[ind]); var maxstat = 0; for (var j = 0; j < st.length; j++) { maxstat = Math.max(maxstat, st[j]); } var l = []; for (var j = 0; j < st.length; j++) { if (st[j] == maxstat) { l.push(j); } } if (l.length == 1) { value = ["HP","Attack","Defense","Special Attack","Special Defense","Speed"][l[0]]; strength = 5; if (["Attack", "Speed"].contains(value)) { strength = 4; } else { strength = 8; } } else { value = ["two", "three", "four", "five", "six"][l.length - 2]; value = value + " stats tied"; strength = [10, 25, 25, 35, 30][l.length - 2]; } outText = "{0} has " + value + " for its highest stat."; break; case "moves": var mvs = fetchMoves(parseInt(answer[ind], 10)).shuffle(); var m = mvs[0]; var m2 = mvs.length > 1 ? mvs[1] : 0; for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "moves" && (clues[i].value.contains(m) || clues[i].value.contains(m2))) { return false; } } var l = lookupMoveLearners(m), l2 = lookupMoveLearners(m2); if (l.length > 220 || l.length < 15) { return false; } if (l2.length > 220 || l2.length < 15) { return false; } var l3 = removeNonDuplicates(l.concat(l2)); if (l3.length > 120 || l3.length < 8) { return false; } if (l3.length > l.length - 7 || l3.length > l2.length - 7) { //make sure neither move is very insignificant return false; } strength = l3.length > 100 ? 2 : (l3.length > 70 ? 5 : (l3.length > 50 ? 7 : (l3.length > 30 ? 12 : l3.length > 20 ? 19 : 28))); value = [m, m2]; outText = "{0} can learn " + moveOff(m) + " and " + moveOff(m2) + "."; break; case "prime": function isPrime(num) { for (var i = 2; i < num; i++) if (num % i === 0) return false; return num > 1; } for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "prime") { return false; } } if (!(isPrime(parseInt(answer[ind], 10)))) { return false; } value = "prime"; outText = "{0}'s dex number is prime."; strength = 19; break; case "effective": for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "effective" && ((ind == clues[i].ind && otherind == clues[i].otherind) || (otherind == clues[i].ind && ind == clues[i].otherind))) { return false; } } var amt = safari.checkEffective([type1(answer[ind]), type2(answer[ind])], [type1(answer[otherind]), type2(answer[otherind])]); if (amt == 1) { return false; } if (amt > 1) { value = "is super-effective"; strength = 15; } if (amt < 1 && amt > 0) { value = "is not very effective"; strength = 13; } if (amt <= 0) { value = "faces an immunity"; strength = 30; } outText = "{0} " + value + " while catching {1}."; break; case "noletters": for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "noletters" && ((ind == clues[i].ind && otherind == clues[i].otherind) || (otherind == clues[i].ind && ind == clues[i].otherind))) { return false; } } var pk1 = poke(parseInt(answer[ind], 10)).toUpperCase(); var pk2 = poke(parseInt(answer[otherind], 10)).toUpperCase(); for (var i = 0; i < pk1.length; i++) { if (pk2.indexOf(pk1[i]) > -1) { return false; } } value = "no letters in common"; strength = 19; outText = "{0} has " + value + " with {1}."; break; case "sameregion": for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "sameregion" && ((ind == clues[i].ind && otherind == clues[i].otherind) || (otherind == clues[i].ind && ind == clues[i].otherind))) { return false; } } var pk1 = generation(parseInt(answer[ind], 10)); var pk2 = generation(parseInt(answer[otherind], 10)); if (pk1 !== pk2) { return false; } value = "region"; strength = 16; outText = "{0} is from the same region as {1}."; break; case "samecolor": for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "samecolor" && ((ind == clues[i].ind && otherind == clues[i].otherind) || (otherind == clues[i].ind && ind == clues[i].otherind))) { return false; } } var pk1 = getPokeColor(parseInt(answer[ind], 10)); var pk2 = getPokeColor(parseInt(answer[otherind], 10)); if (pk1 !== pk2) { return false; } value = "color"; strength = 16; outText = "{0} is the same color as {1}."; break; case "sametype": for (var i = 0; i < clues.length; i++) { if ((clues[i].kind == "sametype" || clues[i].kind == "weaknesses") && ((ind == clues[i].ind && otherind == clues[i].otherind) || (otherind == clues[i].ind && ind == clues[i].otherind))) { return false; } } var pk11 = type1(parseInt(answer[ind], 10)); var pk12 = type2(parseInt(answer[ind], 10)); var pk21 = type1(parseInt(answer[otherind], 10)); var pk22 = type2(parseInt(answer[otherind], 10)); if (!(pk11 == pk21 || pk12 == pk21 || pk11 == pk22 || (pk12 == pk22 && (pk12 !== "???")))) { return false; } value = "type"; strength = 26; outText = "{0} has a type in common with {1}."; break; case "weaknesses": for (var i = 0; i < clues.length; i++) { if ((clues[i].kind == "sametype" || clues[i].kind == "weaknesses") && ((ind == clues[i].ind && otherind == clues[i].otherind) || (otherind == clues[i].ind && ind == clues[i].otherind))) { return false; } } var pk11 = type1(parseInt(answer[ind], 10)); var pk12 = type2(parseInt(answer[ind], 10)); var pk21 = type1(parseInt(answer[otherind], 10)); var pk22 = type2(parseInt(answer[otherind], 10)); var res = chance(0.5) ? true : false; function getWeaknesses(t1, t2, resists) { var hit = [], value; for (var x in effectiveness) { if (x == "Stellar") { continue; } value = 1; if (effectiveness[x].hasOwnProperty(t1)) { value *= effectiveness[x][t1]; } if (effectiveness[x].hasOwnProperty(t2)) { value *= effectiveness[x][t2]; } if (resists) { if (value < 1) { hit.push(x); } } else { if (value > 1) { hit.push(x); } } } return hit; } var weak1 = getWeaknesses(pk11, pk12, res); var weak2 = getWeaknesses(pk21, pk22, res); var l = removeNonDuplicates(weak1.concat(weak2)).length; if (l == 0) { return false; } if (l == 1) { outText = "{0} has 1 " + (res ? "resistance/immunity" : "weakness") + " in common with {1} (excluding Stellar type)."; } else { outText = "{0} has " + l + " " + (res ? "resistances/immunities" : "weaknesses") + " in common with {1} (excluding Stellar type)."; } strength = 23; value = "weakness"; break; case "bsthigher": for (var i = 0; i < clues.length; i++) { if (clues[i].kind == "bsthigher" && ((ind == clues[i].ind || otherind == clues[i].otherind) || (otherind == clues[i].ind || ind == clues[i].otherind))) { return false; } } var pk1 = getBST(answer[ind]); var pk2 = getBST(answer[otherind]); if (pk1 <= pk2) { return false; } value = "greater stat total"; if (pk2 < 330) { strength = 4; } else if (pk2 < 420) { strength = 7; } else if (pk2 < 480) { strength = 10; } else if (pk2 < 520) { strength = 12; } else { strength = 15; } outText = "{0} has a " + value + " than {1}."; break; case "dualtypes": var amt = 0; for (var i = 0; i < answer.length; i++) { if (type2(answer[i]) !== "???") { amt++; } } value = "dual-type"; strength = 25; outText = (amt == 0 ? "None of the Pokémon are dual-typed." : (amt == 1 ? "1 of the Pokémon is dual-typed." : (amt + " of the Pokémon are dual-typed."))); ind = -1; break; } if (strength < minstrength || strength > maxstrength) { return false; } var inds = ["Pokémon #1", "Pokémon #2", "Pokémon #3", "Pokémon #4"]; outText = outText.format(inds[ind], inds[otherind]); return { kind: kind, value: value, str: outText, ind: ind, otherind: otherind, unlock: unlock, seen: false, strength: strength, interact: isInteract } } function getClue(answer, clues, ind, kind, minstrength, unlock) { var out = false; var i = 0; //var before = new Date().getTime(); var maxloop = 10000; var maxstrength = minstrength + 10; while (!(out)) { out = createClue(answer, clues, ind, i < 160 ? kind : false, minstrength - (i > 25 ? ((i - 25) * 0.1) : 0), maxstrength + (i > 10 ? ((i - 10) * 0.1) : 0), unlock); i++; if (i > maxloop) { out = {kind:"broke",value:"",str:"This clue was glitched, please contact a Safari Admin",ind:ind,unlock:"free",seen:false}; break; } } //var after = new Date().getTime(); //sys.sendMessage(sys.id("Ripper Roo"), "Loops taken on getClue(): {0}, created the clue '{1}' with intended strength of {3} and time delta of {2}".format(i, out.str, after - before, minstrength.toFixed(2)), staffchannel); return out; }; function assignClues() { var out = {}; out.answer = []; out.clues = []; out.solved = false; var n = now(); out.date = getDay(n); out.started = n; out.wrongGuesses = 0; while (out.answer.length < 4) { var pokeId = Math.ceil(Math.random() * highestDexNum - 1); if (pokeId <= 0 || pokeId >= highestDexNum) continue; out.answer.push(pokeId); out.answer = removeDuplicates(out.answer, true); } var firstFourOrder = [2 + 3 * Math.random(), 5 + 6 * Math.random(), 16 + 4 * Math.random(), 19 + 6 * Math.random()].shuffle(); //strength of first few clues var nextThreeOrder = [4 + 8 * Math.random(), 1 + 2 * Math.random(), 10 + 10 * Math.random()].shuffle(); //strength of next few clues var extraOrder = [0, 1, 2, 3].shuffle(); var extraOrder2 = [0, 1, 2, 3].shuffle(); //var before = new Date().getTime(); //sys.sendMessage(sys.id("Ripper Roo"), "", staffchannel); out.clues.push(getClue(out.answer, out.clues, 0, false, firstFourOrder[0])); out.clues.push(getClue(out.answer, out.clues, 1, false, firstFourOrder[1])); out.clues.push(getClue(out.answer, out.clues, 2, false, firstFourOrder[2])); out.clues.push(getClue(out.answer, out.clues, 3, false, firstFourOrder[3])); out.clues.push(getClue(out.answer, out.clues, 0, "interact", nextThreeOrder[0])); out.clues.push(getClue(out.answer, out.clues, 1, "interact", nextThreeOrder[0])); out.clues.push(getClue(out.answer, out.clues, 2, "interact", nextThreeOrder[0])); var specClueOrder = ["dualtypes", "interact", "interact", "interact", "interact"].shuffle(); out.clues.push(getClue(out.answer, out.clues, false, specClueOrder[0], 5 + Math.random() * 15, "journal")); out.clues.push(getClue(out.answer, out.clues, false, specClueOrder[1], 5 + Math.random() * 15, "battlearena")); out.clues.push(getClue(out.answer, out.clues, false, specClueOrder[2], 5 + Math.random() * 15, "pokefandaycare")); out.clues.push(getClue(out.answer, out.clues, false, specClueOrder[3], 5 + Math.random() * 15, "explorerfinder")); out.clues.push(getClue(out.answer, out.clues, 0, specClueOrder[4], 5 + Math.random() * 15, "mafia")); out.clues.push(getClue(out.answer, out.clues, false, false, 10 + Math.random() * 15, "trivia")); out.clues.push(getClue(out.answer, out.clues, extraOrder[0], false, 2 + 2 * Math.random(), "mission")); out.clues.push(getClue(out.answer, out.clues, extraOrder[1], false, 2 + 6 * Math.random(), "contest")); var clueMons = [835, 885, 374, 562, 201, 307, 810, 624, 175, 656, 722, 742, 163, 37].shuffle(); out.clues.push(getClue(out.answer, out.clues, extraOrder[2], false, 10 + 5 * Math.random(), "catch:" + clueMons.shift())); out.clues.push(getClue(out.answer, out.clues, extraOrder[3], false, 10 + 5 * Math.random(), "catch:" + clueMons.shift())); out.clues.push(getClue(out.answer, out.clues, false, false, 10 + 5 * Math.random(), "catch:" + clueMons.shift())); out.clues.push(getClue(out.answer, out.clues, extraOrder2[0], false, 0 + 5 * Math.random(), "pyramid1")); out.clues.push(getClue(out.answer, out.clues, 0, false, 0 + 2 * Math.random(), "pyramid2")); out.clues.push(getClue(out.answer, out.clues, extraOrder2[2], false, 0 + 7 * Math.random(), "pyramid3")); out.clues.push(getClue(out.answer, out.clues, 3, false, 0 + 3 * Math.random(), "pyramid4")); out.clues.push(getClue(out.answer, out.clues, extraOrder2[1], false, 0 + 12 * Math.random(), "pyramid5")); //var after = new Date().getTime(); //sys.sendMessage(sys.id("Ripper Roo"), "Final delta: " + (after - before), staffchannel); //sys.sendMessage(sys.id("Ripper Roo"), "", staffchannel); return out; } var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; if (stopQuests.detective) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Sorry! There's been a murder. Come back another time!", safchan); return; } var uid = player.idnum; var today = getDay(now()); if (safari.detectiveData.hasOwnProperty(uid+"")) { if (today !== safari.detectiveData[uid+""].date) { var answers = readable(safari.detectiveData[uid+""].answer.map(function(x) {return poke(parseInt(x))})); if (!(safari.detectiveData[uid+""].solved)) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: You weren't able to solve our last mystery, were you? The answer was " + answers + "!\nCome back when you're ready for another try at the grand prize!", safchan); delete safari.detectiveData[uid+""]; return; } else { delete safari.detectiveData[uid+""]; } } } if (!(safari.detectiveData[uid+""])) { safari.detectiveData[uid+""] = assignClues(); player.quests.detective.lastGuesses = []; permObj.add("detectiveData", JSON.stringify(safari.detectiveData)); } if (safari.detectiveData.hasOwnProperty(uid+"")) { var d1 = (data.length > 0 ? data[0] : "*"); var found = safari.detectiveData[uid+""].clues; var item, out = [], out2 = [], out3 = [], req; if (d1 == "*") { var inds = ["Pokémon #1", "Pokémon #2", "Pokémon #3", "Pokémon #4"]; //show player's clues for (var x in found) { item = found[x]; if ((!item.seen) || (item.unlock !== "free")) { continue; } out.push(item); } for (var x in found) { item = found[x]; if ((item.seen) || (item.unlock !== "free")) { continue; } item.seen = true; out2.push(item); } for (var x in found) { item = found[x]; if (item.unlock == "free") { continue; } var reqDesc = { "mafia": "Win rewards from an Event Mafia game.", "mafia2": "Win rewards from an Event Mafia game with 7 or more players.", "trivia": "Win rewards from an Event Trivia game.", "battlearena": "Defeat Trainer Lorekeeper while wearing the Battle Girl costume.", "explorerfinder": "Discover a clue with the Itemfinder while wearing the Explorer costume.", "pokefandaycare": "Interact with your Pokémon at the daycare while wearing the Pokéfan costume.", "mission": "Clear a Mission.", "journal": "Submit a photograph with a score of at least 50 in the Journal quest.", "pyramid1": "Clear the first floor of Pyramid.", "pyramid2": "Clear the second floor of Pyramid.", "pyramid3": "Clear the third floor of Pyramid.", "pyramid4": "Clear the fourth floor of Pyramid.", "pyramid5": "Clear the fifth floor of Pyramid.", "contest": "Win a Contest.", }; if (item.unlock.slice(0, 6) == "catch:") { var num = parseInt(item.unlock.split(":")[1], 10); req = "Catch a " + poke(num); } else { req = reqDesc[item.unlock]; } if (item.ind !== -1) { req += " [For a clue about " + inds[item.ind] + (item.otherind !== -1 ? " and " + inds[item.otherind] : "") + "]"; } else { req += " [For a general clue]"; } out3.push(req); } function textFromClue(gClue) { if (detUp && bonusClues.length > 0) { if (gClue.interact) { return bonusCluesInteract.random().format(inds[gClue.ind], inds[gClue.otherind]); } return bonusClues.random().format(inds[gClue.ind], inds[gClue.otherind]); } else { return gClue.str; } } if (out.length > 0) { safaribot.sendHtmlMessage(src, "You have the following clues:", safchan); sys.sendMessage(src, "", safchan); for (var i = 0; i < out.length; i++) { safaribot.sendHtmlMessage(src, textFromClue(out[i]), safchan); } sys.sendMessage(src, "", safchan); } if (out2.length > 0) { safaribot.sendHtmlMessage(src, "You found the following clues:", safchan); sys.sendMessage(src, "", safchan); for (var i = 0; i < out2.length; i++) { safaribot.sendHtmlMessage(src, toColor(textFromClue(out2[i]), colorTranslations["orangered"]), safchan); } sys.sendMessage(src, "", safchan); } if (out3.length > 0) { safaribot.sendHtmlMessage(src, "You can unlock new clues with the following requirements:", safchan); sys.sendMessage(src, "", safchan); for (var i = 0; i < out3.length; i++) { safaribot.sendHtmlMessage(src, toColor(out3[i], colorTranslations["darkgreen"]), safchan); } sys.sendMessage(src, "", safchan); } if (safari.detectiveData[uid+""].solved) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Thank you for solving this mystery! The answer was " + readable(safari.detectiveData[uid+""].answer.map(function(x) {return poke(parseInt(x, 10))})) + "! Come back tomorrow to see if I have another case for you.", safchan); } else { var timeTaken = now() - safari.detectiveData[uid+""].started; safaribot.sendHtmlMessage(src, trainerSprite + "Detective: When you think you know which four Pokémon are, you can guess the combination with " + link("/quest detective:", "/quest detective:pokemon1,pokemon2,pokemon3,pokemon4", true) + ". (Elapsed Time: " + (timeString(timeTaken / 1000, true) || "0 seconds") + ")" + (player.quests.detective.cooldown > now() ? " (Next guess available in " + timeLeftString(player.quests.detective.cooldown) + ")" : ""), safchan); safaribot.sendHtmlMessage(src, "Detective: You have had {0} wrong guesses so far. Use {1} to view your recent attempts.".format(addComma(safari.detectiveData[uid].wrongGuesses), link("/quest detective:guesses")), safchan); } } else { //if they supply a guess, make sure it's 4 valid guesses and then see if it's correct if (safari.detectiveData[uid+""].solved) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: You already solved this mystery! Are you trying to solve it again? Come back tomorrow to see if I have another case for you.", safchan); return; } if (d1 === "guesses") { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Here are your last {0} for this case:".format(plural(player.quests.detective.lastGuesses.length, "guess")), safchan); for (var i = 0; i < player.quests.detective.lastGuesses.length; i++) { safaribot.sendHtmlMessage(src, player.quests.detective.lastGuesses[i].split("|").map(function(e) { return poke(parseInt(e)) }).join(", "), safchan); } return; } var arr = d1.split(",").map(function(e) { return e.trim() }); if (arr.length !== 4) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: You must supply four Pokémon in a combination as your guess using the format " + link("/quest detective:pokemon1,pokemon2,pokemon3,pokemon4", false, true) + ".", safchan); return; } var guesses = []; for (var i = 0; i < arr.length; i++) { if (!(getInputPokemon(arr[i]).num)) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Sorry, " + arr[i] + " is not a valid Pokémon!", safchan); return; } if ((getInputPokemon(arr[i]).num > 65536)) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: You cannot guess forms!", safchan); return; } guesses.push(getInputPokemon(arr[i]).num); } var hasDupes = removeDuplicates(guesses, true).length < 4; if (hasDupes) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: You cannot guess duplicate Pokémon!", safchan); return; } var passed = true; var alreadyGuessed = false; for (var i = 0; i < player.quests.detective.lastGuesses.length; i++) { var g = player.quests.detective.lastGuesses[i]; if (guesses.join("|") == g) { alreadyGuessed = true; break; } } if (alreadyGuessed) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: You have already guessed this combination recently!", safchan); return; } for (var j = 0; j < guesses.length; j++) { if (guesses[j] != safari.detectiveData[uid+""].answer[j]) { passed = false; break; } } var n = now(); if (player.quests.detective.cooldown > n) { safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Please wait " + timeLeftString(player.quests.detective.cooldown) + " before guessing again!", safchan); return; } if (passed) { safari.detectiveData[uid+""].solved = true; safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Congratulations! The combination was " + readable(guesses.map(function(x) {return poke(parseInt(x, 10))})) + "! Here is your prize!", safchan); var today = getDay(now()); var grandprize = "3@prize,5@shady,25@hdew,5@mail"; var g = giveStuff(player, toStuffObj(grandprize)); safaribot.sendHtmlMessage(src, toColor("You " + g + "!", "orangered"), safchan); player.records.casesSolved += 1; var timeTaken = 0; var timeTaken = now() - safari.detectiveData[uid+""].started; if (player.records.fastestCaseSolved === 0 || player.records.fastestCaseSolved > timeTaken) { player.records.fastestCaseSolved = timeTaken; safaribot.sendHtmlMessage(src, "Detective: Wow, you solved this case in " + timeString(timeTaken / 1000, true) + "! That's a new personal record!", safchan); } else { safaribot.sendHtmlMessage(src, "Detective: You solved this case in " + timeString(timeTaken / 1000, true) + ". Maybe you can do even better next time!", safchan); } var unlocked = 0, strengthtotal = 0; for (var i = 0; i < safari.detectiveData[uid+""].clues.length; i++) { if (safari.detectiveData[uid+""].clues[i].unlock == "free") { unlocked++; strengthtotal += safari.detectiveData[uid+""].clues[i].strength; } } strengthtotal = Math.floor(strengthtotal); sys.appendToFile(questLog, n + "|||" + player.id.toCorrectCase() + "|||Detective|||Guessed the answer with " + unlocked + " clues totaling " + strengthtotal + " strength in " + timeString(timeTaken/1000, true) + " after " + (safari.detectiveData[uid+""].wrongGuesses) + " wrong guess(es)|||" + cap(g) + "\n"); player.notificationData.detectiveWaiting = true; safari.missionProgress(player, "completeDetective", 1, 1); safari.pendingNotifications(player.id); player.quests.detective.lastGuesses = []; safari.saveGame(player); permObj.add("detectiveData", JSON.stringify(safari.detectiveData)); } else { safari.detectiveData[uid+""].wrongGuesses = safari.detectiveData[uid+""].wrongGuesses || 0; if (safari.detectiveData[uid+""].wrongGuesses > 6) { player.quests.detective.cooldown = n + ((30 + ((safari.detectiveData[uid+""].wrongGuesses - 6) * 5)) * 1000); } else { player.quests.detective.cooldown = n + (30 * 1000); } safari.detectiveData[uid+""].wrongGuesses++; player.quests.detective.lastGuesses.push(guesses.join("|")); while (player.quests.detective.lastGuesses.length > 30) { player.quests.detective.lastGuesses.shift(); } safari.saveGame(player); safaribot.sendHtmlMessage(src, trainerSprite + "Detective: Nope! {0} is not the right solution! Try getting more clues, or else getting more clever!".format(readable(guesses.map(function(e) { return poke(parseInt(e)) }))), safchan); permObj.add("detectiveData", JSON.stringify(safari.detectiveData)); } } } }; this.idolQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } if (Array.isArray(player.pokeskills)) { player.pokeskills = {}; this.saveGame(player); } var trainerSprite = ''; var alchemistSprite = ''; var alchemistSprite2 = ''; if (stopQuests.idol) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Sorry! The show's on hold. Come back another time!", safchan); return; } var retSkillData = function(pokeId, key, action, label, setmsg) { var skillDescription = safari.getSkillDescription(key); if (skillDescription) { var skill = skillData[key]; return (label ? "" + (safari.isBasicSkill(key) ? "[Basic]" : toColor("[Special]", "DarkOrchid")) + " " : "") + "" + link("/quest idol:" + action + ":" + (pokeId ? poke(pokeId) : "[Pokémon Name]") + ":" + skill.name, skill.name, setmsg) + " [" + skillDescription + ". Uses per Charge: " + skill.uses + "]"; } else { return ""; } }; var d1 = data.length > 0 ? data[0] : "*", d2 = data.length > 1 ? data[1] : "", d3 = data.length > 2 ? data[2] : "", d4 = data.length > 3 ? data[3] : ""; if (!d1 || d1 === "*") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: I bet you've ever wondered how to make your Pokémon stronger!", safchan); safaribot.sendHtmlMessage(src, "Idol: Well you're in luck, because I'm an expert when it comes to identifying the shining talents of Pokémon!", safchan); safaribot.sendHtmlMessage(src, "Idol: Perhaps you're {0}? Or what say we {1}?".format(link("/quest idol:about1", "wondering what this is about"), link("/quest idol:menu", "get started immediately")), safchan); safaribot.sendHtmlMessage(src, "Idol: Please note that to promote fairness, {0}".format(toColor("you cannot use these skills in battles against other players!", "red")), safchan); } else if (d1 === "about1") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: You see, EVERY Pokémon has powerful skills hidden deep within. If you bring me some {0} and {1}, I can unlock and charge these skills for your Pokémon!".format(es(finishName("sunshard")), es(finishName("moonshard"))), safchan); safaribot.sendHtmlMessage(src, "Idol: Have you ever wondered how the {0} and {1} are so strong, or are able to activate special effects during battle? With a little help from me, of course!".format(link("/quest celeb", "Celebrities"), link("/quest league", "League Trainers")), safchan); safaribot.sendHtmlMessage(src, "Idol: But, I'm not alone. In order to tap into the magical power of the Shards, I have my cousin here to assist me!", safchan); safaribot.sendHtmlMessage(src, "Idol: ...? Wait, where is she? Don't tell me she fell asleep again... {0}".format(link("/quest idol:about2", "«Next»")), safchan); } else if (d1 === "about2") { var c = ["Norman", "Champion Cynthia", "Sabrina", "Jasmine", "Bruno", "Champion Alder"].random(); safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Zzz... hmm? Oh, yes, I am here. I'm totally not asleep.", safchan); safaribot.sendHtmlMessage(src, "Alchemist: I'm just... *yawn*... a bit tired from staying up all night making potions. You will not believe how quickly that " + c + " can go through them...", safchan); safaribot.sendHtmlMessage(src, "Alchemist: So, you wanna learn more about hidden Pokémon skills, eh? {0}".format(link("/quest idol:menu", "«Next»")), safchan); } else if (d1 === "menu") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: What can we do for you today?", safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:aboutunlock", "Tell me about unlocking skills"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:aboutcharge", "Tell me about charging skills"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:aboutbasic", "Tell me about Basic skills"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:aboutspecial", "Tell me about Special skills"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:aboutbattle", "Tell me how exactly this works during a battle"), safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:showunlocks", "Show me which skills I've unlocked/charged"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:unlock", "I want to unlock a skill!"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:charge", "I want to charge a skill!"), safchan); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:toggle", "I want to enable/disable my skills!") + " [Currently " + (player.options.pokeskillsDisabled ? "Disabled" : "Enabled")+ "]", safchan); } else if (d1 === "aboutunlock") { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Ya see, every Pokémon species has their skills locked to begin with. By bringing us some {0} and {1}, we can permanently unlock some of their skills.".format(es(finishName("sunshard")), es(finishName("moonshard"))), safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: That's right, an unlock is permanent. Note than when we unlock a skill, it only applies to that particular skill for that particular Pokémon forme.", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Unlocking a skill won't do anything for you by itself though. After you unlock a skill, you will gain the option of {0} it. THAT's where the real magic happens!".format(link("/quest idol:aboutcharge", "charging")), safchan); safaribot.sendHtmlMessage(src, link("/quest idol:menu", "«Back to Menu»"), safchan); } else if (d1 === "aboutcharge") { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: If you wanna actually make use of a skill in battle, you've gotta charge it. It's not free though, we'll need the power of some {0} and {1} first. Then, we can charge that skill for a limited number of uses.".format(es(finishName("sunshard")), es(finishName("moonshard"))), safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: That's right, charging is temporary. After a skill is charged, it will only work a certain number of times. Those Shards don't have unlimited energy you know! If you want to use a skill again after it has run out, you'll have to bring us more Shards to charge it again.", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: You don't actually have to wait for it to run out though, charging can be done any time as long as you have the materials, and each charge will top up your skill with a set number of uses.", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Of course, you can't just charge any skill you want. Only skills that you've already {0} can be charged.".format(link("/quest idol:aboutunlock", "unlocked")), safchan); safaribot.sendHtmlMessage(src, link("/quest idol:menu", "«Back to Menu»"), safchan); } else if (d1 === "aboutbasic") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Basic skills are skills that every Pokémon of a certain type can learn! That means there are 18 different Basic skills in total, one for each type.", safchan); safaribot.sendHtmlMessage(src, "Idol: So for example, Bulbasaur is a Grass and Poison-type, therefore it can unlock and charge both the Basic Grass-type skill, and the Basic Poison-type skill.", safchan); safaribot.sendHtmlMessage(src, "Idol: You might think that Pokémon with only a single type are disadvantaged since they can learn fewer Basic skills. But worry not! Pokémon with only one type will be able to unlock and charge a stronger version of their type's skill!", safchan); //safaribot.sendHtmlMessage(src, "Idol: Unlocking a Basic skill for a Pokémon will unlock it for all Pokémon of the same species. That means if you unlock the Basic Fairy-type skill for Floette-Yellow, all the other Floette formes including Floette-Eternal will be able to activate it!", safchan); safaribot.sendHtmlMessage(src, link("/quest idol:showallbasic", "«List of Basic Skills»") + " " + link("/quest idol:menu", "«Back to Menu»"), safchan); } else if (d1 === "aboutspecial") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Special skills are skills that only certain Pokémon can learn! These Special skills likely contain battle effects that you won't be able to get from Basic skills.", safchan); safaribot.sendHtmlMessage(src, "Idol: Unlocking a Special skill for a Pokémon will only unlock it for the specific forme that has that skill. For example, if you unlock the Special skill that Floette-Eternal has, only Floette-Eternal can charge it. All other Floette formes cannot access that Special skill and will only have the Basic Fairy-type skill.", safchan); safaribot.sendHtmlMessage(src, link("/quest idol:showallspecial", "«List of Special Skills»") + " " + link("/quest idol:menu", "«Back to Menu»"), safchan); } else if (d1 === "aboutbattle") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Skills are passive benefits that apply when that Pokémon is in your party during an NPC Rotation Battle. Some skills will benefit not just the skill owner, but also the entire party.", safchan); safaribot.sendHtmlMessage(src, "Idol: Party skills like these will work even if they are not among the 3 Pokémon you chose for that particular battle. However, they stop working if that Pokémon has fainted.", safchan); safaribot.sendHtmlMessage(src, "Idol: Note that multiples of the same skill do not stack! If you have multiples of the same Basic skill, and one is boosted due to being a single-typed Pokémon, the boosted version will take precedence, and party order will also determine which of the same skill is used first.", safchan); safaribot.sendHtmlMessage(src, link("/quest idol:menu", "«Back to Menu»"), safchan); } else if (d1 === "showunlocks") { var pid = player.idnum; if (!skillUnlocks.hasOwnProperty(pid)) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: It doesn't seem like you have any skills unlocked yet...", safchan); return; } if (!d2) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: You'll have to give me the {0}. If you'd like, you can also just browse through {1} instead!".format(link("/quest idol:showunlocks:[Pokémon Name]", "name of the Pokémon", true), link("/quest idol:showunlocks:all", "all of them")), safchan); return; } if (d2 === "all") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Check out all the Pokémon you've unlocked skills for already!", safchan); var keys = Object.keys(skillUnlocks[pid]).sort(function(a, b) { return parseInt(pokeInfo.species(a)) - parseInt(pokeInfo.species(b)) }); var displayLimit = 10, pageNum = Math.abs(parseInt(d3)) || 0; var page = keys.slice(pageNum * displayLimit, pageNum * displayLimit + displayLimit); // maybe turn this whole thing into a function for (var i = 0; i < page.length; i++) { var activeSkills = getActiveSkills(page[i], player); activeSkills = activeSkills.map(function(e) { return link("/quest idol:charge:" + page[i] + ":" + skillData[e].name, skillData[e].name); }); safaribot.sendHtmlMessage(src, "-" + link("/quest idol:showunlocks:" + page[i], poke(parseInt(page[i]))) + (activeSkills.length > 0 ? " [Active: " + readable(activeSkills) + "]" : ""), safchan); if (i === page.length-1) { var pageControls = (page.contains(keys[0]) ? "" : link("/quest idol:showunlocks:all:" + (pageNum-1), "«Previous Page»")) + (page.contains(keys[keys.length-1]) ? "" : " " + link("/quest idol:showunlocks:all:" + (pageNum+1), "«Next Page»")); if (pageControls) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, pageControls, safchan); } } } } else { var mon = getInputPokemon(d2).num; if (!mon) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: I've never heard of " + d2 + " before. Are you sure such a thing exists?", safchan); return; } if (!skillUnlocks[pid].hasOwnProperty(mon)) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: You haven't unlocked any skills for " + poke(mon) + " yet...", safchan); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Have a look at the skills you've unlocked for {0}!".format(poke(mon)), safchan); for (var skill in skillUnlocks[pid][mon]) { var isActive = safari.playerHasActiveSkill(player, mon, skill); safaribot.sendHtmlMessage(src, "-" + retSkillData(parseInt(mon), skill, "charge", true) + (isActive ? " " + toColor("[Active with " + player.pokeskills[mon][skill].uses + " remaining uses]", "red") : ""), safchan); } safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: If you just wanna check the active skills of your current party Pokémon, you can do that with {0}, so you don't always have to bother us all the time. I'm tryna sleep here, yeah?".format(link("/party")), safchan); } } else if (d1 === "showallbasic") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Here's the list of Basic skills that any Pokémon of that type can learn!", safchan); for (var i = 0; i < Object.keys(skillData).length; i++) { if (safari.isBasicSkill(Object.keys(skillData)[i])) { safaribot.sendHtmlMessage(src, "-" + Object.keys(skillData)[i].slice(5) + "-type's " + retSkillData(false, Object.keys(skillData)[i], "unlock", false, true), safchan); } } } else if (d1 === "showallspecial") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Here's the list of Special skills that only specific Pokémon can learn!", safchan); var keys = Object.keys(skillData).filter(function(e) { return !safari.isBasicSkill(e); }); var monList = [], specialSkills = []; for (var i = 0; i < keys.length; i++) { if (!skillData[keys[i]].eligible) continue; for (var j = 0; j < skillData[keys[i]].eligible.length; j++) { monList.push(skillData[keys[i]].eligible[j]); specialSkills.push(keys[i]); } } // intended result: skillData = {special1: {eligible: [1, 2]}, special2: {eligible: [3]}} --> monList = [1, 2, 3]; specialSkills = [special1, special1, special2]; var displayLimit = 10, pageNum = Math.abs(parseInt(d2)) || 0; var monListSlice = monList.slice(pageNum * displayLimit, pageNum * displayLimit + displayLimit), specialSkillsSlice = specialSkills.slice(pageNum * displayLimit, pageNum * displayLimit + displayLimit); var firstPage = pageNum * displayLimit === 0, lastPage = pageNum * displayLimit + displayLimit >= monList.length; for (i = 0; i < monListSlice.length; i++) { safaribot.sendHtmlMessage(src, "-" + poke(parseInt(monListSlice[i])) + "'s " + retSkillData(monListSlice[i], specialSkillsSlice[i], "unlock"), safchan); if (i === monListSlice.length-1) { var pageControls = (firstPage ? "" : link("/quest idol:showallspecial:" + (pageNum-1), "«Previous Page»")) + (lastPage ? "" : " " + link("/quest idol:showallspecial:" + (pageNum+1), "«Next Page»")); if (pageControls) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, pageControls, safchan); } } } } else if (d1 === "toggle") { if (d2 !== "confirm") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: If you feel like battling for a bit without using up your precious skill charges, you can choose to temporarily disable your skills here!", safchan); safaribot.sendHtmlMessage(src, "Idol: Of course, if you decide that you want your skills to start activating again, you can also re-enable them any time you feel like. It's all free of charge!", safchan); safaribot.sendHtmlMessage(src, "Idol: Note that this affects all your skills at once, none of them will activate when disabled.", safchan); safaribot.sendHtmlMessage(src, "Idol: Looks like your skills are currently " + (player.options.pokeskillsDisabled ? "disabled" : "enabled") + ", use " + link("/quest idol:toggle:confirm") + " to change it.", safchan); return; } if (cantBecause(src, "finish this quest", ["battle"])) { return; } player.options.pokeskillsDisabled = !player.options.pokeskillsDisabled; safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Voilà! Your skills have been " + (player.options.pokeskillsDisabled ? "disabled" : "enabled") + " for future battles, come back if you'd like to change it again!", safchan); safari.saveGame(player); } else if (d1 === "unlock") { if (!d2) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Maybe you wanna tell me the {0}?".format(link("/quest idol:unlock:[Pokémon Name]", "name of the Pokémon", true)), safchan); return; } var input = getInputPokemon(d2); var mon = input.id, monName = input.name; if (!mon) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Uh... what's a " + d2 + "? How am I supposed to help you with a Pokémon that doesn't exist?", safchan); return; } var canUnlock = getUnlockableSkills(mon, player.idnum); var isUnlocked = getUnlockedSkills(mon, player.idnum); if (canUnlock.length === 0 && isUnlocked.length === 0) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Uh... weird... this Pokémon doesn't seem to have any unlockable skills at all? Maybe you should throw it away.", safchan); return; } if (canUnlock.length === 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: You've already unlocked all the skills {0} can learn, maybe you wanted to {1} them instead?".format(monName, link("/quest idol:charge:" + mon, "charge")), safchan); return; } if (!d3) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Check out a list of skills {0} can unlock!".format(monName), safchan); for (var i = 0; i < canUnlock.length; i++) { safaribot.sendHtmlMessage(src, "-" + retSkillData(mon, canUnlock[i], "unlock", true), safchan); } return; } var skillKey = safari.getSkillKeyByName(d3); if (!skillKey) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: That skill just plain doesn't exist it seems.", safchan); return; } var skillInfo = skillData[skillKey]; var skillName = skillInfo.name; // for proper casing if (isUnlocked.contains(skillKey)) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: You've already unlocked this skill for {0}, silly!".format(monName), safchan); return; } if (!canUnlock.contains(skillKey)) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: That's not a skill {0} can learn.".format(monName), safchan); return; } // lifting this section from alchemyQuest var canMake = true, progress = [], ingUsed = [], asset, val, req; var asset2, val2, req2, progress2 = []; // for displaying charge cost only for (var e in skillInfo.unlock) { asset = translateAsset(e); val = player.balls[asset.id]; req = plural(skillInfo.unlock[e], asset.input); ingUsed[e] = -skillInfo.unlock[e]; if (val < skillInfo.unlock[e]) { canMake = false; } progress.push(val + "/" + req); } for (e in skillInfo.activate) { // for displaying charge cost only asset2 = translateAsset(e); val2 = player.balls[asset2.id]; req2 = plural(skillInfo.activate[e], asset2.input); progress2.push(val2 + "/" + req2); } if (!d4 || d4 !== "confirm") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Here are the details on this skill:", safchan); sys.sendMessage(src, "", safchan); sys.sendHtmlMessage(src, "Name: " + skillInfo.name + "", safchan); sys.sendHtmlMessage(src, "Effect: " + skillInfo.description.format(skillInfo.rate[0], skillInfo.rate[1], skillInfo.rate2[0], skillInfo.rate2[1]) + ".", safchan); sys.sendHtmlMessage(src, "Number of Uses per Charge: " + skillInfo.uses, safchan); sys.sendHtmlMessage(src, "Unlock Cost: " + readable(progress), safchan); sys.sendHtmlMessage(src, "Charge Cost: " + readable(progress2) + "", safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Anyway, {0} if you're sure you want your {1} to unlock {2}.".format(link("/quest idol:unlock:" + monName + ":" + skillName + ":confirm", "click here"), monName, skillName), safchan); return; } if (cantBecause(src, "finish this quest", ["battle"])) { return; } if (!player.pokemon.contains(mon)) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Uh... You don't seem to have that Pokémon? Maybe it ran away from you or somethin'.", safchan); return; } if (!canMake) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Umm... you kinda don't have enough stuff to unlock this skill. Did you leave the materials at home or somethin'?", safchan); return; } if (!skillUnlocks.hasOwnProperty(player.idnum)) { skillUnlocks[player.idnum] = {}; } if (!skillUnlocks[player.idnum].hasOwnProperty(mon)) { skillUnlocks[player.idnum][mon] = {}; } var level = 1; if (safari.isBasicSkill(skillKey) && type2(mon) === "???") { // single-typed pokemon get a level 2 basic skill level = 2; } player.records.idolUnlocked += 1; giveStuff(player, ingUsed, true); skillUnlocks[player.idnum][mon][skillKey] = { "level": level }; // we only need to store level here, rest of the skill data will be pulled directly from skillData when activating, then added into player.pokeskills permObj.add("skillUnlocks", JSON.stringify(skillUnlocks)); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Idol|||Gave " + translateStuff(skillInfo.unlock) + "|||Unlocked " + skillName + " for " + monName + "\n"); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Success! Your {0} has permanently unlocked the {1} skill! Come back when you're ready to {2} it!".format(monName, skillName, link("/quest idol:charge:" + monName + ":" + skillName, "charge")), safchan); safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: I was the one who did all the work... *grumble grumble*", safchan); safari.pendingNotifications(player.id); safari.saveGame(player); } else if (d1 === "charge") { if (!d2) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Maybe you wanna tell me the {0}? You can see a list of Pokémon that you've unlocked skills for {1}.".format(link("/quest idol:charge:[Pokémon Name]", "name of the Pokémon", true), link("/quest idol:showunlocks:all", "here")), safchan); return; } var input = getInputPokemon(d2); var mon = input.id, monName = input.name; if (!mon) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Uh... what's a " + d2 + "? How am I supposed to help you with a Pokémon that doesn't exist?", safchan); return; } var isUnlocked = getUnlockedSkills(mon, player.idnum); if (isUnlocked.length === 0) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Uh... this Pokémon doesn't have any skills {0}. Did you forget or somethin'? Maybe you're getting old...".format(link("/quest idol:unlock:" + mon, "unlocked")), safchan); return; } if (!d3) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Check out a list of skills {0} can charge!".format(monName), safchan); for (var i = 0; i < isUnlocked.length; i++) { safaribot.sendHtmlMessage(src, "-" + retSkillData(mon, isUnlocked[i], "charge", true), safchan); } return; } var skillKey = safari.getSkillKeyByName(d3); if (!skillKey) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: That skill just plain doesn't exist it seems.", safchan); return; } var skillInfo = skillData[skillKey]; var skillName = skillInfo.name; // for proper casing /*if (safari.playerHasActiveSkill(player, mon, skillKey)) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: This skill's already activated for {0}, silly. You've got {1} more uses til it runs out!".format(monName, player.pokeskills[mon][skillKey].uses), safchan); return; }*/ if (!isUnlocked.contains(skillKey)) { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: That's not a skill {0} has unlocked.".format(monName), safchan); return; } // lifting this section from alchemyQuest var canMake = true, progress = [], ingUsed = [], asset, val, req; for (var e in skillInfo.activate) { asset = translateAsset(e); val = player.balls[asset.id]; req = plural(skillInfo.activate[e], asset.input); ingUsed[e] = -skillInfo.activate[e]; if (val < skillInfo.activate[e]) { canMake = false; } progress.push(val + "/" + req); } if (!d4 || d4 !== "confirm") { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Here are the details on this skill:", safchan); sys.sendMessage(src, "", safchan); sys.sendHtmlMessage(src, "Name: " + skillInfo.name + "", safchan); sys.sendHtmlMessage(src, "Effect: " + skillInfo.description.format(skillInfo.rate[0], skillInfo.rate[1], skillInfo.rate2[0], skillInfo.rate2[1]) + ".", safchan); sys.sendHtmlMessage(src, "Number of Uses per Charge: " + skillInfo.uses, safchan); sys.sendHtmlMessage(src, "Charge Cost: " + readable(progress), safchan); sys.sendMessage(src, "", safchan); if (safari.playerHasActiveSkill(player, mon, skillKey)) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Looks like you already have {0} uses remaining for this skill, but you can charge it again for a total of {1}.".format(player.pokeskills[mon][skillKey].uses, player.pokeskills[mon][skillKey].uses + skillInfo.uses), safchan); } safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Anyway, {0} if you're sure you wanna charge {1} for your {2}.".format(link("/quest idol:charge:" + monName + ":" + skillName + ":confirm", "click here"), skillName, monName), safchan); return; } if (cantBecause(src, "finish this quest", ["battle"])) { return; } if (!player.pokemon.contains(mon)) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Uh... You don't seem to have that Pokémon? Maybe it ran away from you or somethin'.", safchan); return; } if (!canMake) { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Umm... you kinda don't have enough stuff to charge this skill. Did you leave the materials at home or somethin'?", safchan); return; } if (!player.pokeskills.hasOwnProperty(mon)) { player.pokeskills[mon] = {}; } if (!player.pokeskills[mon].hasOwnProperty(skillKey)) { player.pokeskills[mon][skillKey] = { level: skillUnlocks[player.idnum][mon][skillKey].level || 1, uses: skillData[skillKey].uses }; } else { // if skill already has existing uses, just top it up player.pokeskills[mon][skillKey].uses += skillData[skillKey].uses; } // only level and uses need to be stored, all other data can be pulled live from skillData player.records.idolActivated += 1; giveStuff(player, ingUsed, true); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Idol|||Gave " + translateStuff(skillInfo.activate) + "|||Charged " + skillName + " for " + monName + ", receiving " + skillData[skillKey].uses + " uses for a total of " + player.pokeskills[mon][skillKey].uses + "\n"); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Success! You now have {0} remaining uses for {1}'s {2}. With this skill by your side, you and your Pokémon will be shining stars! I guarantee it, or my name isn't \"Idol\"!".format(player.pokeskills[mon][skillKey].uses, monName, skillName), safchan); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Idol: W-wait, my name isn't really \"Idol\" is it? What IS my name? I-I... Oh no...", safchan); safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: There she goes again{0}.. She'll be alright, this always happens... Just give her some time.".format(link("/quest idol:youfounditcongratsicantbelievethis", ".")), safchan); safari.pendingNotifications(player.id); safari.saveGame(player); } else if (d1 === "youfounditcongratsicantbelievethis") { safaribot.sendHtmlMessage(src, alchemistSprite + "Alchemist: Think you're clever eh? The thing is, unlike my cousin, I'm very much aware that I'm a nameless fictional character in a chat-based game. Do you think you're safe there, in the real world? " + link("/quest idol:thisisthefinalstep", "«Next...?»"), safchan); } else if (d1 === "thisisthefinalstep") { sys.sendHtmlMessage(src, "" + toColor("YOU CANNOT ESCAPE", "red") + "", safchan); sys.sendHtmlMessage(src, alchemistSprite2, safchan); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Idol: Huh, that doesn't seem to be something I can help you with, sorry!", safchan); } }; this.arboristQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; if (stopQuests.arborist) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Ah, I'm mighty tired. Maybe come back anot'er time, will ya?", safchan); return; } var recipes = apricornToBallData; var validItems = Object.keys(recipes); if (!data[0] || data[0].toLowerCase() === "help") { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: I can use these here Apricorns to make some neat stuff, alright? (Use " + link("/quest arborist:", "/quest arborist:[recipe name]", true) + " to view the required materials)", safchan); safaribot.sendHtmlMessage(src, "Available Recipes:" + validItems.map(function(x) { return " " + link("/quest arborist:" + x, finishName(x)); }), safchan); safaribot.sendHtmlMessage(src, "Arborist: I've always got lotsa Apricorns myself, so lemme know if you're interested in {0}!".format(link("/quest arborist:trade", "trading")), safchan); sys.sendMessage(src, "", safchan); return; } var item = data[0].toLowerCase(); if (item === "trade") { var validTrades = ["whtapricorn", "blkapricorn", "redapricorn", "bluapricorn", "pnkapricorn", "grnapricorn", "ylwapricorn"]; var tradeCooldown = 2; // hours var exchangeCap = 100, tradeRatio = 3; if (player.quests.arborist.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Ooh hold up a moment, I'm still sortin' out all those Apricorns ya gave me! Come back in {0} if ya wanna trade somemore okay? But I can still make some Poké Balls if ya want!".format(timeLeftString(player.quests.arborist.cooldown)), safchan); return; } var offer = itemAlias(data[1] || ""); if (!data[1] || !validTrades.contains(offer)) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Up for trading then? If you've got too many Apricorns to spare, let's swap! (Cooldown: {0})".format(plural(tradeCooldown, "hour")), safchan); safaribot.sendHtmlMessage(src, "Available Trades:" + validTrades.map(function(e) { return " " + link("/quest arborist:trade:" + e, finishName(e)); }), safchan); safaribot.sendHtmlMessage(src, "Arborist: I'll exchange every {0} of the Apricorn yer offerin' for a random 1 of mine! Don't worry, I'm not gonna end up givin' back the exact same type ya gave me though, and I'll try to pick a color that ya have enough space to hold. {1}".format(tradeRatio, toColor("But if ya end up having to discard some of 'em anyway, no take-backsies!", "red")), safchan); return; } var amt = data[2]; if (!amt || isNaN(amt)) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Wanna trade me your {0}? Type {1} to do so! Remember to offer in multiples of {2}! Also, I can only accept a max of {3} Apricorns at once. (You have {4})".format(es(finishName(offer)), link("/quest arborist:trade:" + offer + ":", "/quest arborist:trade:" + offer + ":[Amount to Trade]", true), tradeRatio, tradeRatio * exchangeCap, plural(player.balls[offer], finishName(offer))), safchan); return; } amt = parseInt(amt); if (amt <= 0 || amt % tradeRatio !== 0) { var closest = amt + (tradeRatio - amt % tradeRatio); if (closest > player.balls[offer]) { closest = amt - amt % tradeRatio; } closest = Math.min(Math.max(tradeRatio, closest), tradeRatio * exchangeCap); safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: That's not a valid amount! I need a multiple of {0}! Try again would ya? (Closest amount: {1})".format(tradeRatio, link("/quest arborist:trade:" + offer + ":" + closest, plural(closest, offer))), safchan); return; } if (amt > (tradeRatio * exchangeCap)) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Oops, that's way too many! I can only accept a max of {0} Apricorns at once. Try again would ya?".format(tradeRatio * exchangeCap), safchan); return; } if (player.balls[offer] < amt) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Hey hey, ya don't have that many {0}!".format(es(finishName(offer))), safchan); return; } var newApricorn; var receivedAmt = amt / tradeRatio; var remainingApricorns = validTrades.filter(function(e) { return e !== offer; }); var optimalApricorns = remainingApricorns.filter(function(e) { // prioritise giving apricorn colours where the player won't exceed the cap return player.balls[e] + receivedAmt <= getCap(e); }); if (optimalApricorns.length === 0) { // if player has so many apricorns of each colour that they all exceed cap if receivedAmt is added optimalApricorns = remainingApricorns.filter(function(e) { // prioritise giving any apricorn colours that aren't at cap yet return player.balls[e] < getCap(e); }); } if (optimalApricorns.length === 0) { // if optimalApricorns is somehow still empty i.e. player has max of every single apricorn (except the one offered) optimalApricorns = remainingApricorns.slice(0); // just pick one randomly out of the remaining list tbh, it'd be kind of pointless but it's a way to just outright delete apricorns if a player desires } newApricorn = optimalApricorns.random(); var rew = giveStuff(player, toStuffObj(receivedAmt + "@" + newApricorn)); safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Alright, in exchange for your {0}, I'm gonna give ya {1}! Here ya go!".format(plural(amt, finishName(offer)), plural(receivedAmt, finishName(newApricorn))), safchan); player.balls[offer] -= amt; safaribot.sendMessage(src, "You " + rew + ".", safchan); safari.toRecentQuests(player, "arborist"); player.quests.arborist.cooldown = now() + hours(tradeCooldown); player.notificationData.arboristWaiting = true; sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Arborist|||Gave " + plural(amt, finishName(offer)) + "|||" + cap(rew) + "\n"); safari.pendingNotifications(player.id); this.saveGame(player); } else { if (!validItems.contains(itemAlias(item))) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Aww, shucks. That ain't sumthin' I know how to make, ya see? (To view available recipes use " + link("/quest arborist:help") + ")", safchan); return; } var rec = recipes[itemAlias(item)]; var recipeString = translateStuff(rec.ingredients, true); var colors = ["#FF6347", "#0000FF", "#006400", "#9932CC"]; var cc = 0; var possibleRewards = Array.isArray(rec.reward) ? rec.reward.map(function(x){ return translateStuff(x, true); }) : [translateStuff(rec.reward, true)]; var coloredRewards = possibleRewards.map(function(x) { cc++; return toColor(x, colors[cc % colors.length]);}); possibleRewards = readable(possibleRewards, "or"); var canMake = true, progress = [], asset, val, req, pokeIng = 0, lacking = []; for (var e in rec.ingredients) { asset = translateAsset(e); if (asset.type == "item") { val = player.balls[asset.id]; req = plural(rec.ingredients[e], asset.input); } else if (asset.type == "money") { val = player.money; req = addComma(rec.ingredients[e]); } else { val = countRepeated(player.pokemon, asset.id); pokeIng += val; req = rec.ingredients[e] + " " + asset.name; } if (val < rec.ingredients[e]) { lacking.push(asset.type === "money" ? "$" + addComma(val) + "/$" + addComma(req) : val + "/" + req); canMake = false; } if (asset.type === "money") { progress.push("$" + addComma(val) + "/$" + addComma(req)); } else { progress.push(val + "/" + req); } } if (!data[1] || data[1].toLowerCase() !== "finish") { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: See them apricorns? Bring 'em here so I can make y'all the sweetest catching materials around, 'kay? (If you have the required materials you can use " + link("/quest arborist:" + item + ":finish") + " to create an item)", safchan); safaribot.sendHtmlMessage(src, "" + finishName(item) + " Recipe: " + toColor(recipeString, "red") + " --> " + readable(coloredRewards, "or"), safchan); safaribot.sendHtmlMessage(src, "Progress: " + progress.join(", "), safchan); sys.sendMessage(src, "", safchan); return; } if (cantBecause(src, "finish this quest", ["wild", "contest", "auction", "battle", "event", "pyramid", "baking"])) { return; } if (!canMake) { safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Wait-a-secon'. That ain't enough materials! (You lack the following materials: " + lacking.join(", ") + ")", safchan); return; } for (e in rec.ingredients) { asset = translateAsset(e); if (asset.type == "poke") { if (!canLosePokemon(src, asset.input, "give", false, rec.ingredients[e])) { return; } } } var hasRaffle = false; var cantHold = [], pokeRew = 0, ing, reward = Array.isArray(rec.reward) ? rec.reward.random() : rec.reward; for (e in reward) { asset = translateAsset(e); ing = rec.ingredients[e] || 0; if (asset.type == "item") { if (player.balls[asset.id] - ing + reward[e] > getCap(asset.id)) { cantHold.push(asset.name); } if (asset.id === "entry") { hasRaffle = true; } } else if (asset.type == "money") { if (player.money - ing + reward[e] > moneyCap) { cantHold.push("money"); } } else { pokeRew += reward[e]; } } if (pokeRew > 0 && player.pokemon.length - pokeIng + pokeRew > getPerkBonus(player, "box")) { cantHold.push("Pokémon"); } if (cantHold.length > 0) { safaribot.sendHtmlMessage(src, trainerSprite + readable(cantHold) + " mus' be your favorite thing" + (cantHold.length > 1 ? "s" : "") + " or sumthin' cuz y'already plum full of " + (cantHold.length > 1 ? "those" : "that") + "!", safchan); return; } if (hasRaffle && !rafflePrizeObj) { safaribot.sendMessage(src, "There is no raffle going on right now so any entry that you obtain now would be invalid!", safchan); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Arborist: Alright, y'all. Let's help " + player.id.toCorrectCase() + " by making some darn good Poké Balls!!", safchan); var lostPoke = false, lostRare = []; var ingUsed = {}; for (var e in rec.ingredients) { ingUsed[e] = -rec.ingredients[e]; asset = translateAsset(e); if (asset.type === "poke") { lostPoke = true; if (isRare(asset.id)) { lostRare.push(poke(asset.id)); } } } for (e in reward) { asset = translateAsset(e); if (asset.type === "poke" && reward[e] < 0) { lostPoke = true; if (isRare(asset.id)) { lostRare.push(poke(asset.id)); } } } giveStuff(player, ingUsed, true); var rew = giveStuff(player, toStuffObj(reward), true); safaribot.sendMessage(src, "The arborist works in a flurry, and before you know it, you received " + readable(rew.gained) + "!", safchan); safaribot.sendMessage(src, "You now have " + plural(player.balls[rec.showAmt], rec.showAmt) + ".", safchan); if (rec.records) { for (e in rec.records) { player.records[e] += rec.records[e]; } } if (lostPoke) { this.logLostCommand(sys.name(src), "quest arborist:" + data.join(":"), "gave " + translateStuff(rec.ingredients)); if (lostRare.length > 0) { sys.appendToFile(mythLog, now() + "|||" + readable(lostRare) + "::have been given to the Arborist by " + sys.name(src) + "::\n"); } } safari.toRecentQuests(player, "arborist"); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Arborist|||Gave " + translateStuff(rec.ingredients) + "|||Received " + readable(rew.gained) + "\n"); safari.pendingNotifications(player.id); this.saveGame(player); } }; this.alchemyQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; if (stopQuests.alchemist) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Naptime! Zzz... Zzz... (Perhaps you should come back later...)", safchan); return; } var recipes = recipeData; if (player.costume && costumeData[player.costume].expItem && player.costumeInfo[player.costume].level < 20) { var ing = {"dust": 300}; ing[costumeData[player.costume].expItem] = 1; recipes["exp up"] = {ingredients: ing, reward: "@expup", immediate: true, failChance: 0, cooldown: 6} } else { if (recipes["exp up"]) { delete recipes["exp up"]; } } var validItems = Object.keys(recipes); if (!data[0] || data[0].toLowerCase() === "help") { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Hi... Let's make some stuff before I fall asleep again. (Use /quest alchemist:[recipe name] to view the required materials)", safchan); safaribot.sendHtmlMessage(src, "Alchemist: If ya got some " + finishName("philosopher") + " or " + finishName("philosopherpebble") + ", we can try some more audacious transmutations! (Use " + link("/quest alchemist:philosopher") + " to view the other recipes)", safchan); safaribot.sendHtmlMessage(src, "Available Recipes: " + validItems.map(function(x) { return " " + link("/quest alchemist:" + x, cap(x, true)) + " (CD: " + recipes[x].cooldown + "h)"; }), safchan); sys.sendMessage(src, "", safchan); return; } var item = data[0].toLowerCase(); if (item === "philosopher") { this.philosopherQuest(src, data.slice(1)); return; } if (!validItems.contains(item)) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: You silly badonkadonk... I don't know how to make that! (To view available recipes use " + link("/quest alchemist:help") + ")", safchan); return; } var rec = recipes[item]; var recipeString = translateStuff(rec.ingredients, true); var colors = ["#FF6347", "#0000FF", "#006400", "#9932CC"]; var cc = 0; var possibleRewards = Array.isArray(rec.reward) ? rec.reward.map(function(x){ return translateStuff(x, true); }) : [translateStuff(rec.reward, true)]; var coloredRewards = possibleRewards.map(function(x) { cc++; return toColor(x, colors[cc % colors.length]);}); possibleRewards = readable(possibleRewards, "or"); var canMake = true, progress = [], asset, val, req, pokeIng = 0, lacking = []; for (var e in rec.ingredients) { asset = translateAsset(e); if (asset.type == "item") { val = player.balls[asset.id]; req = plural(rec.ingredients[e], asset.input); } else if (asset.type == "money") { val = player.money; req = addComma(rec.ingredients[e]); } else { val = countRepeated(player.pokemon, asset.id); pokeIng += val; req = rec.ingredients[e] + " " + asset.name; } if (val < rec.ingredients[e]) { lacking.push(asset.type === "money" ? "$" + addComma(val) + "/$" + addComma(req) : val + "/" + req); canMake = false; } if (asset.type === "money") { progress.push("$" + addComma(val) + "/$" + addComma(req)); } else { progress.push(val + "/" + req); } } if (!data[1] || data[1].toLowerCase() !== "finish") { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: See those materials? Bring 'em back here so I can make you some shiny new items! (If you have the required materials you can use " + link("/quest alchemist:" + item + ":finish") + " to create an item)", safchan); safaribot.sendHtmlMessage(src, "" + cap(item, true) + " Recipe: " + toColor(recipeString, "red") + " --> " + readable(coloredRewards, "or"), safchan); safaribot.sendHtmlMessage(src, "Progress: " + progress.join(", ") + (canMake ? " [Available]" : "") + (rec.immediate ? toColor(" [You can make this recipe even during cooldown]", "DarkGreen") : ""), safchan); sys.sendMessage(src, "", safchan); return; } if (cantBecause(src, "finish this quest", ["wild", "contest", "auction", "battle", "event", "pyramid", "baking"])) { return; } if (!canMake) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Um, that's just not enough materials... (You lack the following materials: " + lacking.join(", ") + ")", safchan); return; } if (!rec.immediate && player.quests.alchemist.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Hey now! We gotsta wait " + timeLeftString(player.quests.alchemist.cooldown) + " to use this magic spell thingy again else it goes POP! KAPOW! BOOMY BOOM BOOM!", safchan); return; } for (e in rec.ingredients) { asset = translateAsset(e); if (asset.type == "poke") { if (!canLosePokemon(src, asset.input, "give", false, rec.ingredients[e])) { return; } } } var hasRaffle = false; var cantHold = [], pokeRew = 0, ing, reward = Array.isArray(rec.reward) ? rec.reward.random() : rec.reward; var mythReward = []; // store rare poke rewards to add to mythlog later for (e in reward) { asset = translateAsset(e); ing = rec.ingredients[e] || 0; if (asset.type == "item") { if (player.balls[asset.id] - ing + reward[e] > getCap(asset.id)) { cantHold.push(asset.name); } if (asset.id === "entry") { hasRaffle = true; } } else if (asset.type == "money") { if (player.money - ing + reward[e] > moneyCap) { cantHold.push("money"); } } else { pokeRew += reward[e]; if (isRare(getInputPokemon(e).id)) { mythReward.push(plural(reward[e], e)); } } } if (pokeRew > 0 && player.pokemon.length - pokeIng + pokeRew > getPerkBonus(player, "box")) { cantHold.push("Pokémon"); } if (cantHold.length > 0) { safaribot.sendHtmlMessage(src, trainerSprite + readable(cantHold) + " mus' be your favoritest thing" + (cantHold.length > 1 ? "s" : "") + " or sumthin' cuz you are already full of " + (cantHold.length > 1 ? "those" : "that") + "!", safchan); return; } if (hasRaffle && !rafflePrizeObj) { safaribot.sendMessage(src, "There is no raffle going on right now so any entry that you obtain now would be invalid!", safchan); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Yo yo yo yo yo. Less blow stuff up Princess Fluffybutt!", safchan); var lostPoke = false, lostRare = []; if (chance(rec.failChance)) { var failUses = {}, lostStuff = {}; for (var e in rec.failUses) { failUses[e] = -rec.failUses[e]; asset = translateAsset(e); if (asset.type === "poke") { lostPoke = true; if (isRare(asset.id)) { lostRare.push(plural(rec.failUses[e], poke(asset.id))); } lostStuff[e] = rec.failUses[e]; } } var out = giveStuff(player, failUses, true); safaribot.sendMessage(src, "A bright circle appears in the room. The room starts to smell like burnt marshmallows as you notice your ingredients ignite! You quickly douse the flames to prevent the whole place from burning down.", safchan); if (out.lost.length > 0) { safaribot.sendMessage(src, "As the smoke clears you realize that your " + readable(out.lost) + " were burnt to a crisp!", safchan); } player.quests.alchemist.cooldown = now() + hours(0.25); //about 15 minutes if (lostPoke) { this.logLostCommand(sys.name(src), "quest alchemist:" + data.join(":"), "failed, lost " + translateStuff(lostStuff)); } sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Alchemist|||Lost " + (out.lost.length > 0 ? readable(out.lost) : "nothing") + "|||Received nothing (failed)\n"); } else { var ingUsed = {}; for (var e in rec.ingredients) { ingUsed[e] = -rec.ingredients[e]; asset = translateAsset(e); if (asset.type === "poke") { lostPoke = true; if (isRare(asset.id)) { lostRare.push(plural(rec.ingredients[e], poke(asset.id))); } } } for (e in reward) { asset = translateAsset(e); if (asset.type === "poke" && reward[e] < 0) { lostPoke = true; if (isRare(asset.id)) { lostRare.push(plural(reward[e], poke(asset.id))); } } } giveStuff(player, ingUsed, true); var rew = giveStuff(player, reward, true); safaribot.sendMessage(src, "A bright circle appears in the room. The room starts to fill with a sparkling mist but it quickly dissipates to reveal " + readable(rew.gained) + ".", safchan); safaribot.sendMessage(src, "You received " + readable(rew.gained) + ".", safchan); if (mythReward.length > 0) { safaribot.sendHtmlAll("Wow! {0} received {1} from the Alchemist!".format(sys.name(src), readable(mythReward.map(function(e) { return pokeInfo.icon(getInputPokemon(e.slice(e.indexOf(" ") + 1)).id, true) + " " + e }))), safchan); sys.appendToFile(mythLog, now() + "|||" + readable(mythReward) + "::have been received from the Alchemist by " + sys.name(src) + " via the " + cap(item, true) + " recipe::\n"); } player.records.transmutationsMade += rec.transmutation || 0; if (rec.cooldown > 0) { player.quests.alchemist.cooldown = Math.max( player.quests.alchemist.cooldown, now() + Math.round(hours(rec.cooldown) * (1 - safari.getFortune(player, "questcd", 0, "alchemist")) * (1 - safari.getAuraEffect(player, "questcd", 0))) ); player.notificationData.alchemistWaiting = true; } if (rec.records) { for (e in rec.records) { player.records[e] += rec.records[e]; } } if (lostPoke) { this.logLostCommand(sys.name(src), "quest alchemist:" + data.join(":"), "gave " + translateStuff(rec.ingredients)); if (lostRare.length > 0) { sys.appendToFile(mythLog, now() + "|||" + readable(lostRare) + "::have been given to the Alchemist by " + sys.name(src) + "::\n"); } } sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Alchemist|||Gave " + translateStuff(rec.ingredients) + "|||Received " + readable(rew.gained) + "\n"); } safari.toRecentQuests(player, "alchemist"); safari.pendingNotifications(player.id); this.saveGame(player); }; this.philosopherQuest = function(src, data) { var player = getAvatar(src); var opt = data.length > 0 ? data[0].toLowerCase() : "*"; var trainerSprite = ''; var info = getInputPokemon(opt.replace("%25", "%")); // zygarde-10% gets html_escaped into -10%25 var eligible = { "570": { "cost": 2, "forms": [570, 66106] }, "571": { "cost": 2, "forms": [571, 66107] }, "628": { "cost": 2, "forms": [628, 66164] }, "705": { "cost": 2, "forms": [705, 66241] }, "706": { "cost": 2, "forms": [706, 66242] }, "713": { "cost": 2, "forms": [713, 66249] }, "724": { "cost": 3, "forms": [724, 66260] }, "902": { "cost": 1, "forms": [902, 66438], "pebble": true }, "905": { "cost": 5, "forms": [905, 66441] }, "58": { "cost": 2, "forms": [58, 65594] }, "59": { "cost": 2, "forms": [59, 65595] }, "100": { "cost": 2, "forms": [100, 65636] }, "101": { "cost": 2, "forms": [101, 65637] }, "157": { "cost": 3, "forms": [157, 65693] }, "211": { "cost": 2, "forms": [211, 65747] }, "215": { "cost": 2, "forms": [215, 65751] }, "483": { "cost": 5, "forms": [483, 66019] }, "484": { "cost": 5, "forms": [484, 66020] }, "503": { "cost": 3, "forms": [503, 66039] }, "549": { "cost": 2, "forms": [549, 66085] }, "172": { "cost": 20, "forms": [172, 65708], "pebble": true }, "201": { "cost": 1, "forms": [201, 65737, 131273, 196809, 262345, 327881, 393417, 458953, 524489, 590025, 655561, 721097, 786633, 852169, 917705, 983241, 1048777, 1114313, 1179849, 1245385, 1310921, 1376457, 1441993, 1507529, 1573065, 1638601, 1704137, 1769673], "pebble": true }, "412": { "cost": 1, "forms": [412, 65948, 131484], "pebble": true }, "422": { "cost": 1, "forms": [422, 65958], "pebble": true }, "423": { "cost": 1, "forms": [423, 65959], "pebble": true }, "550": { "cost": 2, "forms": [550, 66086, 131622] }, "585": { "cost": 1, "forms": [585, 66121, 131657, 197193], "pebble": true }, "586": { "cost": 1, "forms": [586, 66122, 131658, 197194], "pebble": true }, "666": { "cost": 1, "forms": [666, 66202, 131738, 197274, 262810, 328346, 393882, 459418, 524954, 590490, 656026, 721562, 787098, 852634, 918170, 983706, 1049242, 1114778], "pebble": true }, "669": { "cost": 1, "forms": [669, 66205, 131741, 197277, 262813], "pebble": true }, "671": { "cost": 1, "forms": [671, 66207, 131743, 197279, 262815], "pebble": true }, "710": { "cost": 1, "forms": [710, 66246, 131782, 197318], "pebble": true }, "711": { "cost": 1, "forms": [711, 66247, 131783, 197319], "pebble": true }, "741": { "cost": 1, "forms": [741, 66277, 131813, 197349], "pebble": true }, "745": { "cost": 20, "forms": [745, 66281, 131817], "pebble": true }, "351": { "cost": 15, "forms": [351, 65887, 131423, 262495], "pebble": true }, "413": { "cost": 1, "forms": [413, 65949, 131485], "pebble": true }, "421": { "cost": 10, "forms": [421, 65957], "pebble": true }, "676": { "cost": 15, "forms": [676, 66212, 131748, 197284, 262820, 328356, 393892, 459428, 524964, 590500], "pebble": true }, "19": { "cost": 2, "forms": [19, 65555] }, "20": { "cost": 2, "forms": [20, 65556] }, "25": { "cost": 20, "forms": [25, 65561, 131097, 196633, 262169, 327705, 458777, 524313, 589849, 655385, 720921, 786457, 851993], "pebble": true }, "26": { "cost": 2, "forms": [26, 65562] }, "27": { "cost": 2, "forms": [27, 65563] }, "28": { "cost": 2, "forms": [28, 65564] }, "37": { "cost": 2, "forms": [37, 65573] }, "38": { "cost": 2, "forms": [38, 65574] }, "50": { "cost": 2, "forms": [50, 65586] }, "51": { "cost": 2, "forms": [51, 65587] }, "52": { "cost": 2, "forms": [52, 65588, getForm(52, 2)] }, "53": { "cost": 2, "forms": [53, 65589] }, "74": { "cost": 2, "forms": [74, 65610] }, "75": { "cost": 2, "forms": [75, 65611] }, "76": { "cost": 2, "forms": [76, 65612] }, "88": { "cost": 2, "forms": [88, 65624] }, "89": { "cost": 2, "forms": [89, 65625] }, "103": { "cost": 2, "forms": [103, 65639] }, "105": { "cost": 2, "forms": [105, 65641] }, "670": { "cost": 6, "forms": [670, 66206, 131742, 197278, 262814, 328350] }, "678": { "cost": 2, "forms": [678, 66214], "pebble": true }, "681": { "cost": 1, "forms": [681, 66217] }, "658": { "cost": 4, "forms": [658, 131730] }, "746": { "cost": 2, "forms": [746, 66282] }, "774": { "cost": 20, "forms": [774, 66310, 131846, 197382, 262918, 328454, 393990, 459526], "pebble": true }, "801": { "cost": 4, "forms": [801, 66337] }, "386": { "cost": 5, "forms": [386, 65922, 131458, 196994] }, "487": { "cost": 5, "forms": [487, 66023] }, "641": { "cost": 5, "forms": [641, 66177] }, "642": { "cost": 5, "forms": [642, 66178] }, "645": { "cost": 5, "forms": [645, 66181] }, "647": { "cost": 5, "forms": [647, 66183] }, "649": { "cost": 5, "forms": [649, 66185, 131721, 197257, 262793] }, "479": { "cost": 3, "forms": [479, 66015, 131551, 197087, 262623, 328159] }, "492": { "cost": 4, "forms": [492, 66028] }, "555": { "cost": 3, "forms": [555, 66091, getForm(555, 2)] }, "648": { "cost": 4, "forms": [648, 66184] }, "773": { "cost": 1, "forms": [773, 66309, 131845, 197381, 262917, 328453, 393989, 459525, 525061, 590597, 656133, 721669, 787205, 852741, 918277, 983813, 1049349, 1114885] }, "493": { "cost": 10, "forms": [493, 66029, 131565, 197101, 262637, 328173, 393709, 459245, 524781, 590317, 655853, 721389, 786925, 852461, 917997, 983533, 1049069, 1114605] }, "646": { "cost": 10, "forms": [646, 66182, 131718] }, "718": { "cost": 15, "forms": [718, 66254, 131790] }, "720": { "cost": 10, "forms": [720, 66256] }, "77": {"cost": 2, "forms": [77, getForm(77, 1)]}, "78": {"cost": 2, "forms": [78, getForm(78, 1)]}, "263": {"cost": 2, "forms": [263, getForm(263, 1)]}, "264": {"cost": 2, "forms": [264, getForm(264, 1)]}, "562": {"cost": 2, "forms": [562, getForm(562, 1)]}, "122": {"cost": 2, "forms": [122, getForm(122, 1)]}, "110": {"cost": 2, "forms": [110, getForm(110, 1)]}, "222": {"cost": 2, "forms": [222, getForm(222, 1)]}, "83": { "cost": 2, "forms": [83, 65619] }, "554": { "cost": 3, "forms": [554, 66090] }, "618": { "cost": 2, "forms": [618, 66154] }, "79": { "cost": 2, "forms": [79, 65615] }, "80": { "cost": 2, "forms": [80, 131152] }, "199": { "cost": 2, "forms": [199, 65735] }, "845": { "cost": 1, "forms": [845, 66381, 131917] }, "849": { "cost": 1, "forms": [849, 66385], "pebble": true }, "875": { "cost": 20, "forms": [875, 66411], "pebble": true }, "877": { "cost": 20, "forms": [877, 66413], "pebble": true }, "893": { "cost": 4, "forms": [893, 66429] }, "144": { "cost": 5, "forms": [144, 65680] }, "145": { "cost": 5, "forms": [145, 65681] }, "146": { "cost": 5, "forms": [146, 65682] }, "876": { "cost": 1, "forms": [876, 66412], "pebble": true }, "194": { "cost": 2, "forms": [194, 65730] }, "128": { "cost": 2, "forms": [128, 65664, 131200, 196736] }, "916": { "cost": 2, "forms": [916, 66452], "pebble": true }, "925": { "cost": 1, "forms": [925, 66461] }, "931": { "cost": 2, "forms": [931, 66467, 132003, 197539], "pebble": true }, "964": { "cost": 5, "forms": [964, 66500] }, "978": { "cost": 2, "forms": [978, 66514, 132050], "pebble": true }, "982": { "cost": 1, "forms": [982, 66518] }, "999": { "cost": 1, "forms": [999, 66535], "pebble": true }, "1007": { "cost": 5, "forms": [1007, 66543, 132079, 197615, 263151] }, "1008": { "cost": 5, "forms": [1008, 66544, 132080, 197616, 263152] }, "901": { "cost": 7, "forms": [901, 66437] }, "1017": { "cost": 5, "forms": [1017, 66553, 132089, 197625] }, "1024": { "cost": 7, "forms": [1024, 66560] }}; eligible["869"] = { "cost": 8, "forms": [], "pebble": true }; for (var i = 0; i < 63; i++) { // Alcremie eligible["869"].forms.push(869 + 65536 * i); } if (!info.num) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Y'see, the " + finishName("philosopher") + " and " + finishName("philosopherpebble") + " are legendary artifacts with heavenly transmutation powers! They say it can even transform life itself!", safchan); safaribot.sendHtmlMessage(src, "Alchemist: So that's the thing, I'm eager to play around with those things. With those I believe I can completely reshape some Pokémon!", safchan); if (data[0] === "page") { var displayLimit = 10, pageNum = Math.abs(parseInt(data[1])) || 0; var keys = Object.keys(eligible).sort(function(a, b) { return parseInt(a) - parseInt(b) }); var page = keys.slice(pageNum * displayLimit, pageNum * displayLimit + displayLimit); if (page.length === 0) { safaribot.sendHtmlMessage(src, "Alchemist: " + link("/quest philosopher:page:0", "Check out a list of the Pokémon I can try to transform!"), safchan); return; } else { safaribot.sendHtmlMessage(src, "Alchemist: Here is a list of the Pokémon I can try to transform:", safchan); } for (var pk = 0; pk < page.length; pk++) { var listForms = eligible[page[pk]].forms.map(function(e) { var pkName = getInputPokemon(e + "").name; return link("/quest philosopher:" + pkName, pkName); }); if (page[pk] === "555") { // darmanitan listForms.push(link("/quest philosopher:Galarian Darmanitan-Zen", "Galarian Darmanitan-Zen")); // g garm-zen is omitted from regular darm's form array, so manually push it in here } safaribot.sendHtmlMessage(src, "-" + readable(listForms), safchan); if (pk === page.length-1) { var pageControls = (page.contains(keys[0]) ? "" : link("/quest philosopher:page:" + (pageNum-1), "«Previous Page»")) + (page.contains(keys[keys.length-1]) ? "" : " " + link("/quest philosopher:page:" + (pageNum+1), "«Next Page»")); if (pageControls) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, pageControls, safchan); } } } } else { safaribot.sendHtmlMessage(src, "Alchemist: " + link("/quest philosopher:page:0", "Check out a list of the Pokémon I can try to transform!"), safchan); } } else { var base = pokeInfo.species(info.num), eligible; if (info.num == getForm(555, 2) || info.num == getForm(555, 3)) { // Galarian Darmanitan/Zen eligible = {"555": {"cost": 3, "forms": [555, getForm(555, 2), getForm(555, 3)]}} } else { } if (!eligible.hasOwnProperty(base)) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Doesn't look like I can transform this Pokémon into anything!", safchan); return; } var morph = eligible[base]; var into = data.length > 1 ? data[1].toLowerCase() : ""; var formatPossibleTransmutations = function() { return readable(morph.forms.filter(function(e) { return e !== info.num; }).map(function(e) { return link("/quest alchemist:philosopher:{0}:{1}".format(info.name, e), (info.shiny ? "Shiny " : "") + getInputPokemon(e + "").name); })); }; if (!into) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: I can transform that " + info.name + " into " + formatPossibleTransmutations() + " I think!", safchan); return; } else { into = getInputPokemon(into.replace("%25", "%").replace(/\*/g, "").replace(/shiny /i, "")); if (!into.num) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: I can transform that " + info.name + " into " + formatPossibleTransmutations() + " I think!", safchan); return; } else if (!morph.forms.contains(into.num)) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: I can't transform " + info.name + " into " + into.name + " ya big ol' badonkadonk!", safchan); return; } else if (into.num === info.num) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Death.", safchan); return; } else if (info.shiny && noShinySprite.contains(into.num)) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: " + into.name + " has no Shiny variation, so I can't turn your " + info.name + " into one, sorry!", safchan); return; } } var confirmation = data.length > 2 && ["confirm", "finish"].contains(data[2].toLowerCase()); var cost = morph.cost + (info.shiny ? 1 : 0); var usePebble = morph.pebble; if (!confirmation) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Hmm... To transform that " + info.name + " into " + (info.shiny ? "Shiny " : "") + into.name + " I guess I will need " + plural(cost, (usePebble ? "philosopherpebble" : "philosopher")) + "! If you got them all, use " + link("/quest alchemist:philosopher:" + info.input + ":" + into.input + ":finish", null, true) + " and I will begin the transmutation right away!", safchan); return; } else { if (cantBecause(src, "finish this quest", ["wild", "contest", "auction", "battle", "event", "pyramid", "baking"])) { return; } if ((usePebble ? player.balls.philosopherpebble : player.balls.philosopher) < cost) { safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Did you listen to what I said? I can't transform " + info.name + " without " + plural(cost, (usePebble ? "philosopherpebble" : "philosopher")) + ", so don't use " + link("/quest alchemist:philosopher:" + info.input + ":" + into.input + ":finish", null, true) + " until you got them all!", safchan); return; } if (!canLosePokemon(src, info.input, "give")) { return; } var result = into.num; if (info.shiny) { result = result + ""; } //safari.toRecentQuests(player, "celebrity"); safaribot.sendHtmlMessage(src, trainerSprite + "Alchemist: Alright, you brought the " + info.name + " and the " + plural(cost, (usePebble ? "philosopherpebble" : "philosopher")) + ", so let's start this!", safchan); if (usePebble) { player.balls.philosopherpebble -= cost; this.updateShop(player, "philosopherpebble"); } else { player.balls.philosopher -= cost; this.updateShop(player, "philosopher"); } player.records.philosopherTransmutations += 1; player.records.philosopherTransmutationsCost += (usePebble ? cost / 25 : cost); this.evolvePokemon(src, info, result, "was transmuted into"); safaribot.sendHtmlMessage(src, "Alchemist: We did it! Your " + info.name + " is now a " + poke(result) + "!", safchan); this.saveGame(player); if (isRare(result) || isRare(info.id)) { sys.appendToFile(mythLog, now() + "|||" + poke(result) + "::was transmuted from " + info.name + " by " + sys.name(src) + " using " + plural(cost, (usePebble ? "philosopherpebble" : "philosopher")) + "::\n"); } sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Philosopher|||Gave " + info.name + " and " + plural(cost, (usePebble ? "philosopherpebble" : "philosopher")) + "|||Received " + poke(result) + "\n"); } } }; this.decorationQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; if (stopQuests.decor) { safaribot.sendHtmlMessage(src, trainerSprite + "Home Designer: I'm sorry but we are closed right now to revamp our stock for the next season!", safchan); return; } var moneyCost = Math.round(50000 * (1 - this.getFortune(player, "decordiscount", 0, null, true))), silverCost = 50 - this.getFortune(player, "decorsilverdiscount", 0, null, true); if (!data[0] || !["buy", "coupon"].contains(data[0].toLowerCase())) { safaribot.sendHtmlMessage(src, trainerSprite + "Home Designer: Your face screams \"I need to decorate my home\", so you came to the right place! By paying $" + addComma(moneyCost) + " and " + plural(silverCost, "silver") + " you will receive some random but beautiful decoration for your Secret base!", safchan); safaribot.sendHtmlMessage(src, "If I piqued your interest, then you can type " + link("/quest decor:buy", null, true) + " to receive your new item right now! If you have a " + finishName("coupon") + ", you can trade it for a free decoration by typing " + link("/quest decor:coupon", null, true) + "!", safchan); sys.sendMessage(src, "", safchan); return; } if (cantBecause(src, "finish this quest", ["auction", "battle", "event", "pyramid", "baking"])) { return; } var payment = data[0].toLowerCase(); if (payment === "buy") { if (player.money < moneyCost || player.balls.silver < silverCost) { safaribot.sendHtmlMessage(src, trainerSprite + "Home Designer: Wait a moment! Our goods are top quality stuff, so we cannot sell you anything for less than $" + addComma(moneyCost) + " and " + plural(silverCost, "silver") + "! Please come again when you have those!", safchan); return; } } else if (payment === "coupon") { if (player.balls.coupon < 1) { safaribot.sendHtmlMessage(src, trainerSprite + "Home Designer: Excuse me, but whatever you have in your hands is not a valid " + finishName("coupon") + "!", safchan); return; } } if (player.quests.decor.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Home Designer: We are currently renewing our stock, please come back in " + timeLeftString(player.quests.decor.cooldown) + " to check our new goods!", safchan); return; } var paymsg = ""; if (payment === "buy") { player.money -= moneyCost; player.balls.silver -= silverCost; safari.updateEconomyData(-moneyCost, "questFee"); paymsg = "You paid $" + addComma(moneyCost) + " and " + plural(silverCost, "silver") + " and received {0}!"; if (this.getFortune(player, "decordiscount", 0, null, true)) { this.useFortuneCharge(player, "decordiscount", 1); } if (this.getFortune(player, "decorsilverdiscount", 0, null, true)) { this.useFortuneCharge(player, "decorsilverdiscount", 1); } } else { player.balls.coupon -= 1; paymsg = "You gave your " + finishName("coupon") + " and received {0}!"; } var out = randomSampleObj(decorations, true); if (!player.decorations.hasOwnProperty(out)) { player.decorations[out] = 0; } player.decorations[out] += 1; var decoName = decorationAlias(out, false, true); safaribot.sendHtmlMessage(src, trainerSprite + "Home Designer: So you made up your mind? Good, you can now proudly declare that you are newest owner of a brand new " + decoName + "!", safchan); safaribot.sendMessage(src, paymsg.format(an(decoName)), safchan); printDecoration(src, out); player.quests.decor.cooldown = now() + hours(payment == "buy" ? 3 : 1); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Decor|||Paid " + (payment === "buy" ? addComma(moneyCost) + " and " + plural(silverCost, "silver") : plural(1, "coupon")) + "|||Received " + decoName + "\n"); this.saveGame(player); }; this.celebrityMatch = function(src, data) { var player = getAvatar(src); var reason = "start a battle"; var trainerSprite = ''; if (cantBecause(src, reason, ["tutorial"])) { return; } if (safari.isBattling(sys.name(src))) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: Please finish your current battle first!", safchan); return; } var opt = data.length > 0 ? data[0].toLowerCase() : "*"; var opt2 = data.length > 1 ? data[1].toLowerCase() : "*"; var opt3 = data.length > 2 ? data[2].toLowerCase() : "*"; var opt4 = data.length > 3 ? data[3].toLowerCase() : "*"; if (opt !== "start") { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: Welcome to Celebrity Battles! I am your host, the Announcer!", safchan); safaribot.sendMessage(src, "Announcer: Fight famous trainers from across the " + cap(player.celebrityRegion) + " region! Win prizes on your first attempt daily!", safchan); safaribot.sendMessage(src, "Announcer: You must fight all of the trainers in succession. No backing out once you're in!", safchan); safaribot.sendHtmlMessage(src, "Type " + link("/quest celebrity:start") + " to begin your challenge!", safchan); if (player.firstCelebrityRun || player.ticketCelebrityRun) { safaribot.sendHtmlMessage(src, "Let me check your registration... Yep! You are eligible to win prizes! You have {0} and {1} remaining.".format(plural(player.firstCelebrityRun ? 1 : 0, "free reward run"), plural(player.ticketCelebrityRun ? 1 : 0, finishName("celebrityTicket") + " reward run")), safchan); } sys.sendMessage(src, "", safchan); return; } var difficulty = 0; switch (opt2) { case "easy": difficulty = -1; break; case "normal": difficulty = 0; break; case "hard": difficulty = 1; break; case "expert": difficulty = 2; break; case "super expert": difficulty = 3; break; case "abyssal": difficulty = 4; break; default: if (player.costume == "preschooler") { difficulty = -1; } else { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: Please choose a difficulty level between " + link("/quest celebrity:start:easy", "Easy") + ", " + link("/quest celebrity:start:normal", "Normal") + ", " + link("/quest celebrity:start:hard", "Hard") + ", " + link("/quest celebrity:start:expert", "Expert") + ", " + link("/quest celebrity:start:super expert", "Super Expert") + " and " + link("/quest celebrity:start:abyssal", "Abyssal") + "!", safchan); return; } } if (stopQuests.celebrity) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: Sorry, all the celebrities are out playing golf right now. Try coming back later!", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid", "baking"])) { return; } if (player.party.length < 6) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: You need at least 6 Pokémon in your party to challenge Celebrities!", safchan); return; } if (opt4 !== "bypass" || !SESSION.channels(safchan).isChannelOwner(src)) { if (difficulty < 0) { for (var i in player.party) { if (getBST(player.party[i]) > 480) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: For Easy level difficulty, you cannot use Pokémon with a Base Stat Total above 480!", safchan); return; } } /*if (player.costume !== "preschooler") { safaribot.sendMessage(src, "Announcer: Sorry! Only Preschoolers can do Easy level difficulty!", safchan); }*/ } if (difficulty < 2) { for (var i in player.party) { if (getBST(player.party[i]) > 640) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: For Normal and Hard level difficulties, you cannot use Pokémon with a Base Stat Total above 640!", safchan); return; } } } } var postBattle = function(name, isWinner, hpLeft, args, viewers, extraArgs) { var player = getAvatarOff(name), e; var id = sys.id(name); sys.sendMessage(id, "", safchan); var level = ""; switch (args.difficulty) { case -1: level = "Easy"; break; case 0: level = "Normal"; break; case 1: level = "Hard"; break; case 2: level = "Expert"; break; case 3: level = "Super Expert"; break; case 4: level = "Abyssal"; break; } if (isWinner) { var next = args.index + 1; var celebs = args.celebs; var reward; switch (args.difficulty) { case -1: reward = [ ["gacha", 4], ["egg", 1], ["gem", 1], ["gacha", 6], ["pack", 1], ["rare", 1], ["eviolite", 1], ["rare", 2], ["egg", 3], ["gem", 2], ["pack", 3], ["stardust", 3], ["pack", 5] ][args.index]; break; case 0: reward = [ ["dew", 2], [["pearl", 3], ["bigpearl", 1]], [["gacha", 5], ["gem", 1]], [["stardust", 1], ["golden", 1]], [["bigpearl", 3], ["silver", 20]], ["pack", 2], ["dew", 8], ["nugget", 1], ["golden", 2], ["mega", 1], ["pack", 20], ["nugget", 5], ["ash", 1] ][args.index]; break; case 1: reward = [ ["dew", 5], [["gacha", 5], ["pearl", 9]], [["silver", 35], ["stardust", 4]], ["dew", 8], [["pack", 3], ["gem", 3]], ["nugget", 1], [["pearl", 40], ["dew", 40]], ["golden", 5], [["nugget", 5], ["mega", 2]], ["pack", 30], ["nugget", 10], ["ldew", 1], ["ash", 3] ][args.index]; break; case 2: reward = [ ["gacha", 5], [["silver", 8], ["bigpearl", 3]], ["golden", 1], ["pack", 5], [["dew", 42], ["bigpearl", 10]], [["nugget", 1], ["pack", 15]], ["miracle", 2], ["bignugget", 1], ["nugget", 10], ["mega", 3], ["ldew", 2], ["ldew", 5], ["ash", 5] ][args.index]; break; case 3: reward = [ ["pearl", 10], ["bigpearl", 10], ["dew", 20], [["pack", 10], ["miracle", 2]], ["dew", 40], ["nugget", 2], [["pack", 20], ["miracle", 4]], ["bignugget", 2], ["mega", 3], ["bright", 2], ["ldew", 4], ["ldew", 12], ["ash", 10] ][args.index]; break; case 4: reward = [ ["nugget", 1], ["pack", 5], ["miracle", 3], ["bignugget", 1], ["dew", 100], ["platinum", 2], ["bignugget", 5], ["pack", 50], ["bright", 5], ["pack", 500], ["ldew", 20], ["ldew", 50], ["ash", 81] ][args.index]; break; } /* Mission/Trials related stuff */ var k = ["Kanto", "Johto", "Hoenn", "Sinnoh", "Unova", "Kalos", "Alola"], m = [], n = [], y, l = [], u = true; for (var t in k) { y = true; for (var p in player.party) { if (generation(parseInt(player.party[p], 10), true) !== k[t]) { y = false; } } if (y) { m.push(k[t]); } } k = Object.keys(effectiveness); for (var t in k) { y = true; for (var p in player.party) { if (!hasType(player.party[p],k[t])) { y = false; } } if (y) { n.push(k[t]); } } for (var p in player.party) { if (l.indexOf(player.party[p]) !== -1) { u = false; break; } l.push(player.party[p]); } safari.missionProgress(player, "celebrity", next, 1, {gen: m, mono: n, unique: u, level: args.difficulty}); for (e = 0; e < viewers.length; e++) { safaribot.sendMessage(sys.id(viewers[e]), "Announcer: " + name + " has defeated " + next + " trainer(s)! (Difficulty: " + level + ").", safchan); } if (args.canReward) { if (Array.isArray(reward[0])) { reward = reward.random(); } safaribot.sendHtmlMessage(id, "Announcer: Congratulations! You earned " + plural(reward[1], reward[0]) + "!", safchan); rewardCapCheck(player, reward[0], reward[1], true); safari.saveGame(player); } if (extraArgs.turn && extraArgs.turn >= 2) { safari.addToCelebrityLeaderboard(args.name, player.celebrityRegion, args.difficulty, true); } if (next >= 13) { args.index = next; safaribot.sendHtmlMessage(id, "" + args.name + ": I have the great honor of saying that you have triumphed over all of the Celebrity Trainers today!", safchan); sys.sendAll("", safchan); safaribot.sendHtmlAll("Announcer: " + name + " has defeated all 13 Celebrity Trainers (Difficulty: " + level + ")! Please congratulate our Champion!", safchan); sys.sendAll("", safchan); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Celebrity Difficulty: " + level + "|||Challenged with " + readable(player.party.map(poke)) + " (Region: " + cap(player.celebrityRegion) + ")|||Defeated all " + next + " Trainers and " + (args.canReward ? " received " + plural(reward[1], reward[0]) : " was not eligible for prizes") + "\n"); var description = "Cleared " + cap(player.celebrityRegion) + " Celebrities on " + level + " (" + new Date(now()).toUTCString() + ")"; var ic = [253, 252, 251, 249, 258, 255][args.difficulty+1]; safari.awardMedal( player, { desc: description, icon: ic } ); if (player.zcrystalUser && (player.party.indexOf(player.zcrystalUser) > -1 || player.party.indexOf(player.zcrystalUser+"")) > -1) { player.zcrystalUser = false; //remove crystal effect after battle } switch (args.difficulty) { case -1: var val = monthlyLeaderboards["celebrityScoreEasy"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreEasy", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreEasy = Math.max(player.records.celebrityScoreEasy, args.index) || 0; break; case 0: var val = monthlyLeaderboards["celebrityScore"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScore", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScore = Math.max(player.records.celebrityScore, args.index) || 0; break; case 1: var val = monthlyLeaderboards["celebrityScoreHard"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreHard", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreHard = Math.max(player.records.celebrityScoreHard, args.index) || 0; break; case 2: var val = monthlyLeaderboards["celebrityScoreExpert"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreExpert", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreExpert = Math.max(player.records.celebrityScoreExpert, args.index) || 0; break; case 3: var val = monthlyLeaderboards["celebrityScoreSuperExpert"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreSuperExpert", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreSuperExpert = Math.max(player.records.celebrityScoreSuperExpert, args.index) || 0; break; case 4: var val = monthlyLeaderboards["celebrityScoreAbyssal"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreAbyssal", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreAbyssal = Math.max(player.records.celebrityScoreAbyssal, args.index) || 0; break; default: break; } if (isPlaying(sys.name(id))) { safari.revertMega(id, true); } safari.pendingNotifications(player.id); safari.saveGame(player); return; } else { if (!id) { safari.saveGame(player); for (e = 0; e < viewers.length; e++) { safaribot.sendMessage(sys.id(viewers[e]), "Announcer: The challenge was cancelled because " + name + " is nowhere to be found for their next match!", safchan); } return; } var pow, trainer = {}, f = args.firstRun, speedBonusHeal = 1; if (next > 7) { trainer = celebs.elite[next-8]; } else { trainer = celebs.gym[next]; } trainer.desc = level + " Celebrity NPC"; if (extraArgs.loseMsg) { var m = extraArgs.loseMsg.replace("{Name}", name).replace("{Next}", trainer.name); safaribot.sendHtmlMessage(id, "" + args.name + ": " + m, safchan); } else { if (level == "Abyssal") { safaribot.sendHtmlMessage(id, "" + args.name + ": Oh, I have been defeated! Good luck on your next challenge with " + trainer.name + "!", safchan); } else { safaribot.sendHtmlMessage(id, "" + args.name + ": Good going, " + name + "! You defeated me! I will heal your party for a slight amount before you challenge " + trainer.name + "!", safchan); } } if (extraArgs.turn) { if (extraArgs.turn <= 10) { speedBonusHeal = 1.5; } else if (extraArgs.turn <= 15) { speedBonusHeal = 1.2; } } var regen = hpLeft.map(function(x) { return Math.min(1, x + (args.heal * speedBonusHeal)); }); var healing = args.heal; var difficultyLevel = args.difficulty; var canReward = args.canReward; trainer.party = trainer.party.shuffle().slice(0, 6); trainer.postBattle = postBattle; trainer.postArgs = { name: trainer.name, heal: healing, index: next, celebs: celebs, difficulty: difficultyLevel, canReward: canReward, defeated: 0, delayAnnounce: true }; if (trainer.sprite) { safaribot.sendHtmlMessage(id, "", safchan); } if (trainer.startMsg) { safaribot.sendHtmlMessage(id, "" + trainer.name + ": " + trainer.startMsg, safchan); } if (trainer.party2) { var npc2 = { party: [] }; for (var a in trainer.party2) { npc2.party.push(trainer.party2[a]); } trainer.party = trainer.party.shuffle().slice(0, 4); //npc2.postBattle = postBattle; npc2.name = trainer.name2; npc2.bias = trainer.bias2; var battle = new Battle2(id, trainer, { cantWatch: false, t1HP: regen, winMsg: trainer.winMsg, loseMsg: trainer.loseMsg }, null, npc2, trainer.select, viewers); } else { var battle = new Battle2(id, trainer, { cantWatch: false, t1HP: regen, winMsg: trainer.winMsg, loseMsg: trainer.loseMsg }, null, null, trainer.select, viewers); } currentBattles.push(battle); /* for (e = 0; e < viewers.length; e++) { if (!battle.viewers.contains(viewers[e])) { battle.viewers.push(viewers[e]); } }*/ } } else { if (extraArgs.winMsg) { var m = extraArgs.winMsg.replace("{Name}", name); safaribot.sendHtmlMessage(id, "" + args.name + ": " + m, safchan); } else { safaribot.sendHtmlMessage(id, "" + args.name + ": Well, guess that's it! Better luck next time!", safchan); } if (extraArgs.turn && extraArgs.turn >= 3) { safari.addToCelebrityLeaderboard(args.name, player.celebrityRegion, args.difficulty, false); if (player.zcrystalUser && (player.party.indexOf(player.zcrystalUser) > -1 || player.party.indexOf(player.zcrystalUser+"")) > -1) { player.zcrystalUser = false; //remove crystal effect after battle } } /*if (args.difficulty < 0 && player.records.pokesCaught > 2000 && extraArgs.turn && extraArgs.turn >= 3) { sys.sendAll("", safchan); safaribot.sendHtmlAll(" Hahahaha! " + player.id.toCorrectCase() + " just lost to " + args.name + " on Easy! Everyone make fun of them with " + link("/rock " + player.id) + "!", safchan); sys.sendAll("", safchan); }*/ switch (args.difficulty) { case -1: var val = monthlyLeaderboards["celebrityScoreEasy"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreEasy", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreEasy = Math.max(player.records.celebrityScoreEasy, args.index) || 0; break; case 0: var val = monthlyLeaderboards["celebrityScore"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScore", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScore = Math.max(player.records.celebrityScore, args.index) || 0; break; case 1: var val = monthlyLeaderboards["celebrityScoreHard"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreHard", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreHard = Math.max(player.records.celebrityScoreHard, args.index) || 0; break; case 2: var val = monthlyLeaderboards["celebrityScoreExpert"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreExpert", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreExpert = Math.max(player.records.celebrityScoreExpert, args.index) || 0; break; case 3: var val = monthlyLeaderboards["celebrityScoreSuperExpert"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreSuperExpert", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreSuperExpert = Math.max(player.records.celebrityScoreSuperExpert, args.index) || 0; break; case 4: var val = monthlyLeaderboards["celebrityScoreAbyssal"].get(player.id) || '{"value":0}'; val = JSON.parse(val); if (val.value < args.index || (val.value === args.index && val.defeated < args.defeated)) safari.addToMonthlyLeaderboards(player.id, "celebrityScoreAbyssal", {"value": args.index, "time": now(), "defeated": args.defeated}); player.records.celebrityScoreAbyssal = Math.max(player.records.celebrityScoreAbyssal, args.index) || 0; break; default: break; } sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Celebrity Difficulty: " + level + "|||Challenged Celebrities with " + readable(player.party.map(poke)) + " (Region: " + cap(player.celebrityRegion) + ")|||Defeated on " + getOrdinal(args.index+1) + " battle by " + args.name + (args.canReward ? " and was eligible for prizes" : " and was not eligible for prizes") + "\n"); if (isPlaying(sys.name(id))) { safari.revertMega(id, true); } safari.pendingNotifications(player.id); safari.saveGame(player); } }; var canReward = player.firstCelebrityRun || player.ticketCelebrityRun; var celebs = safari.getCelebrities(JSON.parse(JSON.stringify(safari.celebrityData[player.celebrityRegion])), difficulty); var npc; if (!canReward) { if (opt3 === "*") { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: For non-reward runs, you may choose which Celebrity to face first!", safchan); safaribot.sendHtmlMessage(src, "Announcer: " + readable(celebs.gym.filter(function(e) { return !e.party2; }).map(function(e) { return link("/quest celebrity:start:" + opt2 + ":" + e.name, e.name, true); })), safchan); return; } else { var celebIndex; for (var i = 0; i < celebs.gym.length; i++) { if (celebs.gym[i].name.toLowerCase() === opt3) { celebIndex = i; break; } } if (celebIndex === undefined) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: We don't have a Celebrity Gym Leader by that name in the " + cap(player.celebrityRegion) + " region!", safchan); return; } if (celebs.gym[celebIndex].party2) { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: Sorry, you can't pick a Tag Battle for your first battle! Try picking someone else.", safchan); return; } var selected = celebs.gym.splice(celebIndex, 1)[0]; // take selected celeb out celebs.gym.shuffle(); // randomise the rest celebs.gym.unshift(selected); // put selected in front } } else { safaribot.sendHtmlMessage(src, trainerSprite + "Announcer: The first Celebrity will be chosen at random for reward runs!", safchan); var j = 0; do { celebs.gym.shuffle(); j++; } while (celebs.gym[0].party2 && j <= 1000); } var champion = celebs.elite.splice(celebs.elite.length - 1, 1)[0]; // take the champion out celebs.elite.shuffle(); // randomise the rest celebs.elite.push(champion); // force champion as last battle var pack1 = false, pack2 = false; for (var t = 0; t < celebs.gym.length; t++) { var trainer = celebs.gym[t]; if (trainer.pack1) { // ensure celebs part of the same pack e.g. Chili/Cilan/Cress will not appear in a set together if (pack1) { celebs.gym.splice(t, 1); t--; continue; } else { pack1 = true; } } if (trainer.pack2) { // ensure celebs part of the same pack e.g. Gordie/Melony will not appear in a set together if (pack2) { celebs.gym.splice(t, 1); t--; continue; } else { pack2 = true; } } } if (celebs.gym.length > 8) { // if you somehow end up with > 8 gym leaders even after pack filtering above celebs.gym = celebs.gym.slice(0, 8); // slice it down } npc = celebs.gym[0]; npc.postBattle = postBattle; //sys.sendAll(celebs.gym.map(function(e) { return e.name }).join(", ")); //sys.sendAll(celebs.elite.map(function(e) { return e.name }).join(", ")); var heal, desc; switch (difficulty) { case -1: heal = 0.25; desc = "Easy"; break; case 0: heal = 0.2; desc = "Normal"; break; case 1: heal = 0.18; desc = "Hard"; break; case 2: heal = 0.16; desc = "Expert"; break; case 3: heal = 0.14; desc = "Super Expert"; break; case 4: heal = 0; desc = "Abyssal"; break; } npc.postArgs = { name: npc.name, heal: heal, index: 0, difficulty: difficulty, celebs: celebs, canReward: canReward, defeated: 0, delayAnnounce: true }; npc.desc = desc + " Celebrity NPC"; safaribot.sendHtmlMessage(src, "Announcer: Looking for fame, are you? Please enjoy your first battle against " + npc.name + "!", safchan); if (npc.postArgs.canReward) { if (player.firstCelebrityRun) { player.firstCelebrityRun = false; safaribot.sendHtmlMessage(src, "You have activated your free Celebrity reward run!", safchan); } else { player.ticketCelebrityRun = false; safaribot.sendHtmlMessage(src, "You have activated your " + finishName("celebrityTicket") + " reward run!", safchan); } } var battle = new Battle2(src, npc, { cantWatch: false }, null, null, npc.select); currentBattles.push(battle); safari.toRecentQuests(player, "celebrity"); safari.saveGame(player); }; this.addToCelebrityLeaderboard = function(leader, region, difficulty, won) { if (!celebrityPKs.hasOwnProperty(region)) { celebrityPKs[region] = {}; } if (!celebrityPKs[region].hasOwnProperty("total")) { celebrityPKs[region].total = {}; } if (!celebrityPKs[region].hasOwnProperty(difficulty)) { celebrityPKs[region][difficulty] = {}; } if (!celebrityPKs[region].total.hasOwnProperty(leader)) { celebrityPKs[region].total[leader] = [0, 0]; } if (!celebrityPKs[region][difficulty].hasOwnProperty(leader)) { celebrityPKs[region][difficulty][leader] = [0, 0]; } if (won) { celebrityPKs[region][difficulty][leader][1]++; celebrityPKs[region].total[leader][1]++; } else { celebrityPKs[region][difficulty][leader][0]++; celebrityPKs[region].total[leader][0]++; } return; }; this.getCelebrities = function(data, difficulty) { var index = 0, eliteindex = 8, trainer, chal, partyStrength, diff, hold, maxLoop, hazard, currentTrainer, b; var pack1 = false, pack2 = false; var out = { "gym": [], "elite": [] }; //data = data.shuffle(); for (var i = 0; i < data.length; i++) { currentTrainer = {}; trainer = data[i]; /*if (trainer.pack1) { if (pack1) { continue; } else { pack1 = true; } } if (trainer.pack2) { if (pack2) { continue; } else { pack2 = true; } }*/ currentTrainer.name = trainer.name; currentTrainer.winMsg = (trainer.winMsg ? trainer.winMsg.random() : null); currentTrainer.loseMsg = (trainer.loseMsg ? trainer.loseMsg.random() : null); currentTrainer.startMsg = (trainer.startMsg ? trainer.startMsg.random() : null); currentTrainer.sprite = (trainer.sprite ? trainer.sprite + trainer.sprite2 : null); currentTrainer.pack1 = trainer.pack1; currentTrainer.pack2 = trainer.pack2; if (trainer.sprite3) { currentTrainer.sprite += trainer.sprite3; } var ind = (trainer.elite ? eliteindex : index); currentTrainer.powerBoost = ((trainer.power - 1.08) + ((difficulty - 3)/18) + ((difficulty > 1 ? 0.005 : 0)) + ((difficulty < 1 ? 0.0002 : 0)) + (ind/56) + (trainer.elite ? 0.037 : 0)) + (difficulty < 0 ? -0.27 : 0); chal = Math.round(1 + (ind/4) + (difficulty * 1) + (difficulty === 4 ? -3 : 0)); if (ind >= 5) { chal++; } if (ind >= 7) { chal++; } if (ind >= 9) { chal++; } if (ind >= 12) { chal += 1.5; } currentTrainer.party = []; partyStrength = 0; maxLoop = 400; diff = 100; var obj; while (Math.abs(diff) > 2) { maxLoop--; if (maxLoop <= 0) { break; } partyStrength = 0; hold = []; obj = Object.keys(trainer.party).shuffle(); for (var j in obj) { if ((trainer.party[obj[j]] >= 6) && (difficulty < 4)) { continue; } if (hold.contains(parseInt(obj[j])) || hold.contains(obj[j] + "")) { continue; } if (parseInt(trainer.shiny, 10) === parseInt(obj[j], 10)) { hold.push(obj[j] + ""); } else { hold.push(parseInt(obj[j], 10)); } partyStrength += (trainer.party[obj[j]] + Math.random() - Math.random()); if (trainer.party2 && hold.length > 3) { break; } if (hold.length > 5) { break; } } diff = (chal + (difficulty === 4 ? 4 : 0) - partyStrength); } chal += diff; currentTrainer.party = hold; currentTrainer.party2 = null; var isTag = (trainer.party2 ? (trainer.hasOwnProperty("tagBattleChance") ? chance(trainer.tagBattleChance) : true) : false); if (isTag) { diff = 100; maxLoop = 200; currentTrainer.name2 = trainer.name2; hold = []; currentTrainer.party2 = []; while (Math.abs(diff) > 2) { maxLoop--; if (maxLoop <= 0) { break; } partyStrength = 0; hold = []; obj = Object.keys(trainer.party2).shuffle(); for (var j in obj) { if ((trainer.party2[obj[j]] >= 6) && (difficulty < 4)) { continue; } hold.push(parseInt(obj[j], 10)); partyStrength += (trainer.party2[obj[j]] + Math.random() - Math.random()); if (hold.length > 3) { break; } } diff = (chal + (difficulty === 4 ? 4 : 0) - partyStrength); } currentTrainer.party2 = hold; } chal += diff; if (difficulty >= 4) { chal -= 5; } diff = 100; maxLoop = 2000; hazardStrength = 0; var maxRange = 6, minRange = 3; if (trainer.effectRange) { minRange = trainer.effectRange[0]; maxRange = trainer.effectRange[1]; } while (Math.abs(diff) >= 1) { maxLoop--; if (maxLoop <= 0) { hold = []; break; } hold = []; hazardStrength = 0; obj = Object.keys(trainer.effectBalance).shuffle(); for (var j in obj) { if (hold.contains(obj[j])) { continue; } if ((hold.contains("spikes") || hold.contains("spikes2")) && (obj[j] == "spikes" || obj[j] == "spikes2")) { continue; } if ((hold.contains("singlespecialstat") || hold.contains("powertrick")) && (obj[j] == "singlespecialstat" || obj[j] == "powertrick")) { continue; } if ((hold.contains("fullrestore") || hold.contains("fullrestore2") || hold.contains("fullrestore3")) && (obj[j] == "fullrestore3" || obj[j] == "fullrestore2" || obj[j] == "fullrestore")) { continue; } if ((hold.contains("hyperpotion") || hold.contains("hyperpotion2") || hold.contains("hyperpotion3")) && (obj[j] == "hyperpotion3" || obj[j] == "hyperpotion2" || obj[j] == "hyperpotion")) { continue; } if (trainer.effectChance) { if (trainer.effectChance[obj[j]]) { if (!(chance(trainer.effectChance[obj[j]]))) { continue; } } } hold.push(obj[j]); hazardStrength += (trainer.effectBalance[obj[j]] + Math.random() - Math.random() + (maxLoop < 50 ? ( 3 * (Math.random() - Math.random())) : 0)); diff = (chal - hazardStrength) - 1; if (((hold.length >= minRange) && (chance (0.4))) || (hold.length >= maxRange) || (hold.length >= 2 && maxLoop < 250)) { if (Math.abs(diff) <= 1) { break; } else if (Math.abs(diff) <= 2 && (maxLoop < 900)) { diff *= 0.5; break; } else if (Math.abs(diff) <= 4 && (maxLoop < 400)) { diff *= 0.25; break; } else if (Math.abs(diff) <= 16 && (maxLoop < 200)) { diff *= (1/16); break; } } if (((hold.length >= minRange) && (chance(0.18))) || (hold.length >= maxRange)) { break; } } } currentTrainer.select = {}; currentTrainer.select.boostType = []; for (var j in hold) { hazard = hold[j]; if (hazard == "boostType1") { currentTrainer.select.boostType.push(trainer.boostType1); } if (hazard == "boostType2") { currentTrainer.select.boostType.push(trainer.boostType2); } else { currentTrainer.select[hazard] = true; } } if (trainer.forcedEffect) { if (Array.isArray(trainer.forcedEffect)) { currentTrainer.select[trainer.forcedEffect.random()] = true; } else { currentTrainer.select[trainer.forcedEffect] = true; } } currentTrainer.bias = trainer.bias; if (trainer.bias2) { currentTrainer.bias2 = trainer.bias2; } var j = 6; while (j > 0) { j--; b = trainer.chanceBias.shuffle()[0]; if (!b) { break; } if (b === "sleep" || b === "freeze" || b === "poison" || b === "burn" || b === "paralyze") { if (currentTrainer.bias.contains("sleep") || currentTrainer.bias.contains("freeze") || currentTrainer.bias.contains("burn") || currentTrainer.bias.contains("poison") || currentTrainer.bias.contains("paralyze")) { continue; } } if (b === "recoil") { if (currentTrainer.bias.contains("burnout")) { continue; } } if (b === "burnout") { if (currentTrainer.bias.contains("recoil")) { continue; } } if (currentTrainer.bias.contains(b)) { continue; } if (currentTrainer.bias.length >= 3) { break; } if (chance(0.5)) { currentTrainer.bias.push(b); } j--; } currentTrainer.favorite = trainer.favorite || []; if (trainer.elite) { out.elite.push(currentTrainer); eliteindex++; } else { out.gym.push(currentTrainer); index++; } } return out; } this.fightLeague = function(src, data) { var player = getAvatar(src); var reason = "start a battle"; if (cantBecause(src, reason, ["tutorial"])) { return; } if (safari.isBattling(sys.name(src))) { safaribot.sendMessage(src, "League Guide: Please finish your current battle first!", safchan); return; } var quest = player.quests.league; var week = parseInt(permObj.get("currentWeek"), 10); if (quest.week != week) { quest.badges = []; quest.registered = false; quest.eliteCurrent = quest.week + 1 === week ? quest.eliteNext : false; quest.eliteNext = false; quest.eliteCurrentUsed = false; quest.week = week; this.saveGame(player); } var trainerSprite = ''; var opt = data.length > 0 ? data[0].toLowerCase() : "*"; var opt2 = data.length > 1 ? data[1].toLowerCase() : "*"; var cost = 5000; if (opt === "help") { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: Welcome to the Pokémon League! The goal here is to defeat the 7 Gyms to obtain their badges, which grants you a chance to challenge the Elite Four!", safchan); safaribot.sendMessage(src, "League Guide: To be eligible to challenge the Gyms, you must first pay a registration fee of $" + addComma(cost) + ", which is valid for the current period only.", safchan); safaribot.sendMessage(src, "League Guide: Each period lasts for one week, starting on Sunday. Once it finishes, all previous badges are void, and the Gyms are renewed.", safchan); safaribot.sendHtmlMessage(src, "League Guide: If you clear all the 7 Gyms within a single period, you get a chance to challenge the Elite Four during the next period by using " + link("/quest league:elite") + ".", safchan); safaribot.sendHtmlMessage(src, "League Guide: You can see the Hall of Fame of all players who defeated the Elite Four by typing " + link("/quest league:hall") + ".", safchan); sys.sendMessage(src, "", safchan); return; } else if (opt === "hall") { var query = data.length > 1 ? data.slice(1).join(":").trim() : ""; var hallQuery = function(list, query, queryMode) { var e, obj, flat, exp; if (queryMode === "&&") { var queryStr = "^"; for (e = 0; e < query.length; e++) { queryStr += "(?=.*" + escapeRegExp(query[e]) + ")"; } queryStr += ".+"; exp = new RegExp(queryStr, "i"); } else { var queryStr = []; for (e = 0; e < query.length; e++) { queryStr.push("(" + escapeRegExp(query[e]) + ")"); } queryStr = queryStr.join("|"); exp = new RegExp(queryStr, "i"); } for (e = list.length - 1; e >= 0; e--) { obj = list[e]; flat = obj.name + "|||" + obj.party.map(poke).join(","); if (!exp.test(flat)) { list.splice(e, 1); } } return list; }; safari.showLogList(src, "quest league:hall:", query, eliteHall, "Hall of Fame", function(x) { var time = new Date(x.time).toUTCString(); var name = x.name; var party = x.party; // var costume = x.costume; return "{0} defeated the Elite Four at {1} with the following party: {2}.".format(name, time, readable(party.map(poke))); }, hallQuery, true); return; } else if (["elite", "elite 4", "elite four", "elitefour", "elite4", "e4", "e 4"].contains(opt)) { if (opt2 !== "start") { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: You can challenge the Elite Four if you cleared all Gyms during the previous week.", safchan); safaribot.sendHtmlMessage(src, "League Guide: You can start the challenge with " + link("/quest league:elite:start") + ". Once you do, you will fight all 4 members in a row. If you win, you will receive a rare Philosopher's Stone.", safchan); safaribot.sendHtmlMessage(src, "League Guide: You can only challenge the Elite Four once per week, and only if you obtained all badges from the Gyms during the previous week.", safchan); return; } if (quest.eliteCurrentUsed) { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: You already challenged the Elite Four during the current period! Clear the 7 Gyms again during this period to get a new chance next week!", safchan); return; } if (!quest.eliteCurrent) { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: You didn't obtain the 7 badges during the last period, so you cannot challenge the Elite Four! Clear the 7 Gyms during this period to get a chance next week!", safchan); return; } if (stopQuests.league) { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: I'm terribly sorry, but the Elite Four is currently attending to an important event at the Pokémon League Headquarters!", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid", "baking"])) { return; } if (player.party.length < 6) { safaribot.sendMessage(src, "League Guide: You need at least 6 Pokémon in your party to challenge the Elite Four!", safchan); return; } this.fightElite(src); return; } if (!quest.registered) { if (opt === "register") { if (player.money >= cost) { player.money -= cost; safari.updateEconomyData(-cost, "questFee"); quest.registered = true; this.saveGame(player); safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: Great, you paid $" + addComma(cost) + ", so you are now registered! You can now use " + link("/quest league") + " to challenge the Gyms until the end of the week, when your registration expires!", safchan); } else { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: You do not have $" + addComma(cost) + " to register!", safchan); } } else { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: Do you wish to battle in the Pokémon League? Then you first need to register for the current week to be able to challenge the seven Gyms!", safchan); safaribot.sendHtmlMessage(src, "League Guide: Registration costs $" + addComma(cost) + " and is valid until Saturday, 23:59 GMT. To register, type " + link("/quest league:register") + ". For more information, type " + link("/quest league:help") + ". ", safchan); if (quest.eliteCurrent && !quest.eliteCurrentUsed) { safaribot.sendHtmlMessage(src, "League Guide: It seems that you cleared all Gyms during the last period, so you can challenge the Elite Four by typing " + link("/quest league:elite") + "!", safchan); } } return; } if (opt === "*") { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: You are currently registered for the League, so you can challenge the Gyms!", safchan); var legitBadges = []; for (var n in gymData) { var opp = gymData[n]; if (quest.badges.contains(opp.badge.toLowerCase())) { legitBadges.push(opp.badge); safaribot.sendHtmlMessage(src, "-" + cap(n) + " Gym" + " for the " + opp.badge + " [Obtained]", safchan); } else { safaribot.sendHtmlMessage(src, "-" + link("/quest league:" + cap(n), cap(n) + " Gym") + " for the " + opp.badge, safchan); } } if (legitBadges.length > 0) { safaribot.sendHtmlMessage(src, "League Guide: You have the following Badges ({0}): {1}".format(legitBadges.length, readable(legitBadges)), safchan); } if (quest.eliteCurrent && !quest.eliteCurrentUsed) { safaribot.sendHtmlMessage(src, "League Guide: To challenge the Elite Four, use " + link("/quest league:elite") + ".", safchan); } if (quest.cooldown >= now()) { safaribot.sendMessage(src, "League Guide: The Pokémon League strongly advises that you take a break between each Gym Challenge. Come back in " + timeLeftString(quest.cooldown) + " to try another challenge!", safchan); } sys.sendMessage(src, "", safchan); return; } if (quest.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "The Pokémon League strongly advises that you take a break between each Gym Challenge. Come back in " + timeLeftString(quest.cooldown) + " to try another challenge!", safchan); return; } if (stopQuests.league) { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: I'm terribly sorry, but all Gyms are currently closed due to an important event at the Pokémon League Headquarters!", safchan); return; } if (cantBecause(src, reason, ["auction", "battle", "event", "pyramid", "baking"])) { return; } if (opt.indexOf(" gym") !== -1) { opt = opt.substr(0, opt.indexOf(" gym")); } var gym = gymData[opt]; if (!gym) { safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: There's no Gym with that name!", safchan); return; } if (quest.badges.contains(gym.badge.toLowerCase())) { safaribot.sendMessage(src, "League Guide: You already cleared this Gym!", safchan); return; } if (player.party.length < 6) { safaribot.sendMessage(src, "League Guide: You need at least 6 Pokémon in your party to challenge a Gym!", safchan); return; } var postBattle = function(name, isWinner, hpLeft, args, viewers) { var player = getAvatarOff(name), e; var id = sys.id(name); sys.sendMessage(id, "", safchan); if (isWinner) { var gym = args.gym; var next = args.index + 1; if (next === gym.trainers.length) { var reward = [ [["pearl", 5], ["gacha", 10]], [["redapricorn", 25], ["grnapricorn", 25]], [["golden", 2], ["silver", 15]], ["celebrityTicket", 1], ["miracle", 1], ["fragment", 1], ["platinum", 1] ][player.quests.league.badges.length]; //mission stuff var k = ["Kanto", "Johto", "Hoenn", "Sinnoh", "Unova", "Kalos", "Alola"], m = [], n = [], y, l = [], u = true; for (var t in k) { y = true; for (var p in player.party) { if (!generation(parseInt(player.party[p], 10), true) === k[t]) { y = false; } } if (y) { m.push(k[t]); } } k = Object.keys(effectiveness); for (var t in k) { y = true; for (var p in player.party) { if (!generation(parseInt(player.party[p], 10), true) === k[t]) { y = false; } } if (y) { n.push(k[t]); } } for (var p in player.party) { if (l.indexOf(player.party[p]) !== -1) { u = false; break; } l.push(player.party[p]); } safari.missionProgress(player, "gymLeader", 1, 1, {mono: n, region: m}); if (Array.isArray(reward[0])) { reward = reward.random(); } safaribot.sendHtmlMessage(id, "" + args.name + ": Congratulations, " + name + "! For clearing the " + gym.name + " Gym, I award you the " + gym.badge + "!", safchan); safaribot.sendHtmlMessage(id, "League Guide: We also reward you with " + plural(reward[1], reward[0]) + "!", safchan); rewardCapCheck(player, reward[0], reward[1], true); if (player.zcrystalUser && (player.party.indexOf(player.zcrystalUser) > -1 || player.party.indexOf(player.zcrystalUser+"")) > -1) { player.zcrystalUser = false; //remove crystal effect after battle } player.quests.league.badges.push(gym.badge.toLowerCase()); player.quests.league.cooldown = now() + Math.round(hours(2) * (1 - safari.getFortune(player, "questcd", 0, "league")) * (1 - safari.getAuraEffect(player, "questcd", 0))); if (player.quests.league.badges.length >= 7) { player.quests.league.eliteNext = true; player.records.allGymsCleared += 1; if (player.quests.league.eliteCurrentUsed || !player.quests.league.eliteCurrent) { player.notificationData.leagueWaiting = false; } } if (safari.getFortune(player, "leagueheal", 0, null, true)) { safari.useFortuneCharge(player, "leagueheal", 1); } player.records.gymsCleared += 1; sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||League|||Challenged " + args.gym.name + " Gym with " + readable(player.party.map(poke)) + "|||Received " + gym.badge + " and " + plural(reward[1], reward[0]) + " by defeating " + args.name + "\n"); if (isPlaying(name)) { safari.revertMega(id, true); } safari.pendingNotifications(player.id); safari.saveGame(player); } else { var npc = JSON.parse(JSON.stringify(gym.trainers[next])); safaribot.sendHtmlMessage(id, "" + args.name + ": Oh, I have been defeated! I will heal your party for a slight amount before you challenge " + npc.name + "!", safchan); if (!id) { player.records.gymsLost += 1; player.quests.league.cooldown = now() + Math.round(hours(2) * (1 - safari.getFortune(player, "questcd", 0, "league")) * (1 - safari.getAuraEffect(player, "questcd", 0))); safari.saveGame(player); for (e = 0; e < viewers.length; e++) { safaribot.sendMessage(sys.id(viewers[e]), "League Guide: The challenge was cancelled because " + name + " is nowhere to be found for their next match!", safchan); } return; } var regen = hpLeft.map(function(x) { return Math.min(1, x + args.heal); }); npc.party = npc.party.shuffle().slice(0, 6); npc.postBattle = postBattle; npc.postArgs = { name: npc.name, gym: args.gym, heal: 0.15 + safari.getFortune(player, "leagueheal", 0, null, true), index: next }; var battle = new Battle2(id, npc, { cantWatch: false, t1HP: regen }); currentBattles.push(battle); for (e = 0; e < viewers.length; e++) { if (!battle.viewers.contains(viewers[e])) { battle.viewers.push(viewers[e]); } } } } else { safaribot.sendHtmlMessage(id, "" + args.name + ": Well, guess that's it! Better luck next time!", safchan); if (safari.getFortune(player, "leagueheal", 0, null, true)) { safari.useFortuneCharge(player, "leagueheal", 1); } if (player.zcrystalUser && (player.party.indexOf(player.zcrystalUser) > -1 || player.party.indexOf(player.zcrystalUser+"")) > -1) { player.zcrystalUser = false; //remove crystal effect after battle } player.records.gymsLost += 1; player.quests.league.cooldown = now() + Math.round(hours(2) * (1 - safari.getFortune(player, "questcd", 0, "league")) * (1 - safari.getAuraEffect(player, "questcd", 0))); safari.pendingNotifications(player.id); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||League|||Challenged " + args.gym.name + " Gym with " + readable(player.party.map(poke)) + "|||Defeated on " + getOrdinal(args.index+1) + " battle by " + args.name + "\n"); if (isPlaying(name)) { safari.revertMega(id, true); } safari.saveGame(player); } }; var npc = JSON.parse(JSON.stringify(gym.trainers[0])); npc.party = npc.party.shuffle().slice(0, 6); npc.postBattle = postBattle; npc.postArgs = { name: npc.name, gym: gym, heal: 0.15 + safari.getFortune(player, "leagueheal", 0, null, true), index: 0 }; safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: So you are aiming for the " + gym.badge + "? Then please proceed to your first battle against " + npc.name + "!", safchan); var battle = new Battle2(src, npc, { cantWatch: false }); safari.toRecentQuests(player, "league"); player.notificationData.leagueWaiting = true; player.notificationData.lastLeagueParty = [].concat(player.party); safari.clearQuestNotifications(player, "League"); safari.saveGame(player); currentBattles.push(battle); }; this.fightElite = function(src) { var player = getAvatar(src); var trainerSprite = ''; var postBattle = function(name, isWinner, hpLeft, args, viewers) { var player = getAvatarOff(name), e; var id = sys.id(name); sys.sendMessage(id, "", safchan); if (isWinner) { var next = args.index + 1; if (next === args.elite.length) { eliteHall.push({ name: name, color: player.nameColor, party: player.party.concat(), costume: player.costume, time: now() }); permObj.add("hall", JSON.stringify(eliteHall)); if (safari.getFortune(player, "eliteheal", 0, null, true)) { safari.useFortuneCharge(player, "eliteheal", 1); } player.quests.league.eliteCurrentUsed = true; player.quests.league.eliteCurrent = false; player.records.eliteCleared += 1; var description = ("Cleared the League Elite Four (" + new Date(now()).toUTCString() + ")"); safari.awardMedal( player, { desc: description, icon: 248 } ); if (player.zcrystalUser && (player.party.indexOf(player.zcrystalUser) > -1 || player.party.indexOf(player.zcrystalUser+"")) > -1) { player.zcrystalUser = false; //remove crystal effect after battle } if (player.quests.league.badges.length >= 7) { player.notificationData.leagueWaiting = false; } safari.pendingNotifications(player.id); safari.saveGame(player); sys.sendAll("", safchan); safaribot.sendHtmlAll("" + name + " has defeated the Elite Four! Congratulations!", safchan); safaribot.sendHtmlMessage(id, "" + args.name + ": Congratulations, " + name + "! You have proved that you are a true Pokémon Master!", safchan); safaribot.sendHtmlMessage(id, "" + args.name + ": For your success, we award you with " + plural(1, "philosopher") + "!", safchan); rewardCapCheck(player, "philosopher", 1, true); sys.sendAll("", safchan); safari.counterPickLeague(player.party); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Elite Four|||Challenged with " + readable(player.party.map(poke)) + "|||Received " + plural(1, "philosopher") + " by defeating " + args.name + "\n"); } else { var npc = JSON.parse(JSON.stringify(args.elite[next])); safaribot.sendHtmlMessage(id, "" + args.name + ": That was a great battle! I will heal your party a bit, then you can head to battle " + npc.name + "!", safchan); if (!id) { player.quests.league.eliteCurrentUsed = true; player.quests.league.eliteCurrent = false; player.records.eliteLost += 1; safari.saveGame(player); for (e = 0; e < viewers.length; e++) { safaribot.sendMessage(sys.id(viewers[e]), "League Guide: The challenge was cancelled because " + name + " is nowhere to be found for their next match!", safchan); } return; } var regen = hpLeft.map(function(x) { return Math.min(1, x + args.heal); }); npc.party = npc.party.shuffle().slice(0, 6); npc.postBattle = postBattle; npc.postArgs = { name: npc.name, elite: args.elite, heal: 0.2 + safari.getFortune(player, "eliteheal", 0, null, true), index: next }; var battle = new Battle2(id, npc, { t1HP: regen, cantWatch: false }); currentBattles.push(battle); for (e = 0; e < viewers.length; e++) { if (!battle.viewers.contains(viewers[e])) { battle.viewers.push(viewers[e]); } } } } else { safaribot.sendHtmlMessage(id, "" + args.name + ": Come back after you hone your skills a bit more!", safchan); if (safari.getFortune(player, "eliteheal", 0, null, true)) { safari.useFortuneCharge(player, "eliteheal", 1); } if (player.zcrystalUser && (player.party.indexOf(player.zcrystalUser) > -1 || player.party.indexOf(player.zcrystalUser+"")) > -1) { player.zcrystalUser = false; //remove crystal effect after battle } player.quests.league.eliteCurrentUsed = true; player.quests.league.eliteCurrent = false; player.records.eliteLost += 1; if (player.quests.league.badges.length >= 7) { player.notificationData.leagueWaiting = false; } safari.pendingNotifications(player.id); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Elite Four|||Challenged with " + readable(player.party.map(poke)) + "|||Defeated on " + getOrdinal(args.index+1) + " battle by " + args.name + "\n"); safari.saveGame(player); } }; var npc = JSON.parse(JSON.stringify(eliteData[0])); npc.party = npc.party.shuffle().slice(0, 6); npc.postBattle = postBattle; npc.postArgs = { name: npc.name, elite: eliteData, heal: 0.2 + safari.getFortune(player, "eliteheal", 0, null, true), index: 0 }; safaribot.sendHtmlMessage(src, trainerSprite + "League Guide: Everything is ready for your Elite Four challenge! Your first battle will be against " + npc.name + "! Good Luck!", safchan); var battle = new Battle2(src, npc, { cantWatch: false }); currentBattles.push(battle); }; this.journalQuest = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; if (stopQuests.journal) { safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: We are too busy printing tomorrow's edition! Come back later!", safchan); return; } var act = data.length > 0 && data[0] ? data[0] : "*"; var offer = data.length > 1 && data[1] ? parseInt(data[1], 10) : "*"; if (player.balls.lens === 0) { var cost = itemData.lens.price; if (act.toLowerCase() === "buy camera") { if (player.money >= cost) { safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: Good, you will now work as a freelancer for me! All you need to do is to type /photo when a Pokémon appears and /album to check your photos!", safchan); safaribot.sendHtmlMessage(src, "Editor-in-chief: Type " + link("/quest journal") + " to see what kind of photos I need, and I will give you some rewards for them!", safchan); safaribot.sendMessage(src, "You paid $" + addComma(cost) + " and bought 1 " + finishName("lens") + "!", safchan); player.money -= cost; safari.updateEconomyData(-cost, "questFee"); player.balls.lens += 1; this.saveGame(player); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: I can't give you a camera for free! You need to pay $" + addComma(cost) +"!", safchan); } } else { safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: Hey you, do you know how to take photos? I need some Pokémon photos for the journal, but our photographer just disappeared on us!", safchan); safaribot.sendHtmlMessage(src, "Editor-in-chief: If you want to help, I can sell you a camera for $" + addComma(cost) + " if you type " + link("/quest journal:buy camera", null, true) + ", and then you can start taking photos to bring to me!", safchan); } sys.sendMessage(src, "", safchan); return; } this.updatePhotographQuest(); var photos = player.photos, req, n = now(), playerTracks = player.quests.journal.trackedRequests; if (act === "*") { var obj, e, i, canFulfill, desc, t; safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: I'm currently looking for the following photos. If you have any of these, use " + link("/quest journal:Number", null, true) + " to give them to me.", safchan); for (e in photographQuest) { obj = photographQuest[e]; canFulfill = false; for (i = photos.length; i--; ) { if (this.photoMatchesRequest(photos[i], obj)) { canFulfill = true; break; } } t = timeLeftString(obj.deadline); if (obj.deadline - n <= journalDoneDeadline) { t = toColor(t, "red"); } desc = "{0}. A photo of {1} (Score: {2} | Deadline: {3})".format(e, this.translatePhotoRequest(obj), obj.fscore, t); if (canFulfill) { safaribot.sendHtmlMessage(src, toColor(desc, "magenta") + " [" + (playerTracks.hasOwnProperty(e) ? link("/quest journal:untrack:" + e, "Stop Tracking") : (obj.deadline - n <= journalDoneDeadline ? "You cannot track expiring requests" : link("/quest journal:track:" + e, "Track"))) + "] [" + link("/quest journal:" + e, "You can fulfill this request") + "]", safchan); } else { safaribot.sendHtmlMessage(src, desc + " [" + (playerTracks.hasOwnProperty(e) ? link("/quest journal:untrack:" + e, "Stop Tracking") : (obj.deadline - n <= journalDoneDeadline ? "You cannot track expiring requests" : link("/quest journal:track:" + e, "Track"))) + "]", safchan); } } if (player.quests.journal.cooldown >= n) { safaribot.sendMessage(src, "Editor-in-chief: I'm still editing the last photo that you brought, so come back in " + timeLeftString(player.quests.journal.cooldown) + " if you wish to submit more photos!", safchan); } if (Object.keys(playerTracks).length > 0) { safaribot.sendHtmlMessage(src, "Editor-in-chief: By the way, you can use " + link("/quest journal:showtracked") + " to view your tracked requests!", safchan); } sys.sendMessage(src, "", safchan); return; } if (act === "track") { // reusing earlier variables here, "offer" in this code block refers to photo request # var maxTracks = 20; var photoRequest = photographQuest[offer]; if (offer === "*") { safaribot.sendHtmlMessage(src, "Editor-in-chief: You can keep track of Journal requests that you're interested in here using {0}! You will receive a notification if a tracked request is completed by someone else or expires. You can only track up to {1} at once, though.".format(link("/quest journal:track:RequestNumber", false, true), plural(maxTracks, "request")), safchan); return; } if (Object.keys(playerTracks).length >= maxTracks) { safaribot.sendHtmlMessage(src, "Editor-in-chief: You can only track up to {0} at a time! Use {1} to view your list of tracked requests.".format(plural(maxTracks, "request"), link("/quest journal:showtracked")), safchan); return; } if (!photoRequest) { safaribot.sendMessage(src, "Editor-in-chief: That's not a valid request!", safchan); return; } if (photoRequest.deadline - n <= journalDoneDeadline) { safaribot.sendMessage(src, "Editor-in-chief: You can't track a request that's about to expire!", safchan); return; } var formattedRequest = "A photo of {0} | Score: {1} | Deadline: {2}".format(this.translatePhotoRequest(photoRequest), photoRequest.fscore, timeLeftString(photoRequest.deadline)); if (playerTracks.hasOwnProperty(offer)) { safaribot.sendHtmlMessage(src, "Editor-in-chief: You are already tracking request #{0} ({1})! Use {2} to view your list of tracked requests.".format(offer, formattedRequest, link("/quest journal:showtracked")), safchan); return; } playerTracks[offer] = "A photo of {0} | Score: {1}".format(this.translatePhotoRequest(photoRequest), photoRequest.fscore); // store some of the request data on the player side in case we have to print details of expired requests later safaribot.sendHtmlMessage(src, "Editor-in-chief: You started tracking request #{0} ({1})! Use {2} to view your list of tracked requests.".format(offer, formattedRequest, link("/quest journal:showtracked")), safchan); safari.saveGame(player); return; } if (["untrack", "removetrack"].contains(act)) { if (offer === "*") { safaribot.sendHtmlMessage(src, "Editor-in-chief: You can remove Journal requests that you're no longer interested in here using {0}!".format(link("/quest journal:untrack:RequestNumber", false, true)), safchan); return; } if (isNaN(offer) || !playerTracks.hasOwnProperty(offer)) { safaribot.sendHtmlMessage(src, "Editor-in-chief: Invalid request! Use {0} to view your list of tracked requests.".format(link("/quest journal:showtracked")), safchan); return; } var photoRequest = photographQuest[offer]; var formattedRequest = photoRequest ? "A photo of {0} | Score: {1} | Deadline: {2}".format(this.translatePhotoRequest(photoRequest), photoRequest.fscore, timeLeftString(photoRequest.deadline)) : playerTracks[offer]; safaribot.sendHtmlMessage(src, "Editor-in-chief: You stopped tracking request #{0} ({1})! Use {2} to view your list of tracked requests.".format(offer, formattedRequest, link("/quest journal:showtracked")), safchan); delete playerTracks[offer]; safari.saveGame(player); return; } if (["showtrack", "showtracked", "showtracks", "viewtrack", "viewtracked", "viewtracks"].contains(act)) { if (Object.keys(playerTracks).length === 0) { safaribot.sendHtmlMessage(src, "Editor-in-chief: You don't seem to be tracking any Journal requests! You can add some here with {0}.".format(link("/quest journal:track:RequestNumber", false, true)), safchan); return; } safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: Let's see, these are the requests you are currently tracking:", safchan); var obj, e, i, canFulfill, desc, t, expired, toDelete = [], requestNum, sortedKeys = Object.keys(playerTracks).sort(function(a, b) { return parseInt(a) - parseInt(b) }); for (e = 0; e < sortedKeys.length; e++) { requestNum = sortedKeys[e]; expired = !photographQuest.hasOwnProperty(requestNum); // this shouldn't happen since expired tracks are cleared on loadGame, but just in case obj = expired ? playerTracks[requestNum] : photographQuest[requestNum]; canFulfill = false; if (!expired) { for (i = photos.length; i--; ) { if (this.photoMatchesRequest(photos[i], obj)) { canFulfill = true; break; } } } t = expired ? "[Expired]" : timeLeftString(obj.deadline); if (expired || obj.deadline - n <= journalDoneDeadline) { t = toColor(t, "red"); } desc = expired ? "{0}. {1} | Deadline: {2}".format(e, obj, t) : "{0}. A photo of {1} (Score: {2} | Deadline: {3})".format(requestNum, this.translatePhotoRequest(obj), obj.fscore, t); if (canFulfill) { safaribot.sendHtmlMessage(src, toColor(desc, "magenta") + " [" + link("/quest journal:untrack:" + requestNum, "Stop Tracking") + "] [" + link("/quest journal:" + requestNum, "You can fulfill this request") + "]", safchan); } else { safaribot.sendHtmlMessage(src, desc + " [" + link("/quest journal:untrack:" + requestNum, "Stop Tracking") + "]", safchan); } if (expired) { toDelete.push(requestNum); } } if (toDelete.length > 0) { for (e in toDelete) { delete playerTracks[toDelete[e]]; } safaribot.sendMessage(src, "Expired requests #{0} will be deleted.".format(readable(toDelete)), safchan); safari.saveGame(player); } sys.sendMessage(src, "", safchan); return; } if (!act || !photographQuest.hasOwnProperty(act)) { safaribot.sendMessage(src, "Editor-in-chief: That's not a valid request!", safchan); return; } var req = photographQuest[act]; var getFinalPhotoScore = function(req, photo) { var ret = Math.round(req.fscore * (0.5 + (photo.score / 10)) * (1 + safari.getFortune(player, "journalbuff", 0, false, true))); if (safari.hasCostumeSkill(player, "moreJournalPoints")) { ret = Math.round(ret * 1.2); } if (isRare(photo.id)) { ret = Math.round(ret * 1.3); } return ret; }; if (offer === "*") { var t = timeLeftString(req.deadline); if (req.deadline - now() <= journalDoneDeadline) { t = toColor(t, "red"); } safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: Let's see, these are the photos that you have that fill my request " + act + " (" + cap(this.translatePhotoRequest(req)) + "): ", safchan); var found = false; for (var e = 0; e < photos.length; e++) { if (this.photoMatchesRequest(photos[e], req)) { found = true; safaribot.sendHtmlMessage(src, "[" + (e+1) + "] " + cap(this.describePhoto(photos[e])) + " (Score: " + getFinalPhotoScore(req, photos[e]) + ") " + link("/quest journal:" + act + ":" + (e+1), "[Give this photo]", true), safchan); } } if (found) { safaribot.sendHtmlMessage(src, "Editor-in-chief: To choose which photo you will give me, type " + link("/quest journal:" + act + ":Number", false, true) + ". (Deadline: " + t + ")", safchan); } else { safaribot.sendMessage(src, "None", safchan); } return; } if (player.quests.journal.cooldown >= now()) { safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: I'm still editing the last photo that you brought, come back in " + timeLeftString(player.quests.journal.cooldown) + "!", safchan); return; } if (!offer || isNaN(offer) || offer < 1 || offer > player.photos.length) { safaribot.sendMessage(src, "Editor-in-chief: That's not a valid photo!", safchan); return; } offer -= 1; var photo = photos[offer]; if (!this.photoMatchesRequest(photo, req)) { safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: This photo doesn't match this request!", safchan); return; } var score = getFinalPhotoScore(req, photo); var rewLevel = Math.floor(score/30); var rew; switch (rewLevel) { case 0: //0~29 rew = ["70@dust", "5@gacha", "3@pearl", "5@pnkapricorn,5@grnapricorn"].random(); break; case 1: //30~59 rew = ["150@dust", "10@gacha", "3@silver", "10@grnapricorn,10@pnkapricorn", "5@pearl"].random(); break; case 2: //60~89 rew = ["2@rare", "20@gacha", "7@silver", "15@grnapricorn,15@pinkapricorn", "8@pearl"].random(); break; case 3: //90~119 rew = ["4@rare", "20@gacha,2@gem", "10@silver", "15@grnapricorn,15@pinkapricorn,5@dew,5@hdew", "3@bigpearl"].random(); break; case 4: //120~149 rew = ["6@rare", "20@gacha,4@gem", "15@silver", "10@dew,10@hdew", "6@bigpearl", "3@golden"].random(); break; case 5: //150~179 rew = ["8@rare", "30@gacha,6@gem", "20@silver", "6@bigpearl,3@nugget", "5@golden"].random(); break; default: //180+ rew = ["8@rare,20@gacha", "40@gacha,8@gem", "25@silver", "6@bigpearl,3@nugget,1@bignugget", "8@golden"].random(); break; } var excessPoints = score - 209; if (excessPoints >= 0) { var bonusRewLevel = Math.ceil(excessPoints/30); var bonusList = ["3@bigmushroom", "@fossil", "@sunshard", "@moonshard", "@spray", "@crystal", "3@egg"]; for (var i = 0; i < bonusRewLevel; i++) { // starting at 210 (inclusive), every 30 points gives you one element from the bonusList randomly rew += "," + bonusList.random(); } } var rewardName = translateStuff(rew); rew = giveStuff(player, toStuffObj(rew)); player.quests.journal.cooldown = n + Math.round(hours(0.5) * (1 - safari.getFortune(player, "questcd", 0, "journal")) * (1 - safari.getAuraEffect(player, "questcd", 0))); player.notificationData.journalWaiting = true; player.records.journalSubmitted += 1; var oldPoints = player.records.journalPoints; player.records.journalPoints += score; var newPoints = player.records.journalPoints; player.photos.splice(offer, 1); safari.toRecentQuests(player, "journal"); this.addToMonthlyLeaderboards(player.id, "journalPoints", score); safari.costumeEXP(player, "journal", Math.floor(score/5)); safari.missionProgress(player, "journal", score, 1, {}); safaribot.sendHtmlMessage(src, trainerSprite + "Editor-in-chief: Oh great, this photo is exactly what I needed! It will look great on " + (chance(0.05) ? "the cover page" : "page " + sys.rand(2, 13)) + " for tomorrow's edition!", safchan); safaribot.sendMessage(src, "You gave your photo of " + this.describePhoto(photo) + " to the Editor-in-chief! You " + rew + "! You also received " + plural(score, "Photo Point") + " and now have " + plural(player.records.journalPoints, "Photo Point") + "!", safchan); var scoreFortune = safari.getFortune(player, "journalbuff", 0); var added = 0; while (player.balls.lens < 10 && Math.floor(player.records.journalPoints/itemData.lens.threshold) >= player.balls.lens) { player.balls.lens += 1; added++; } var rewards = []; if (added > 0) { rewards.push(plural(added, "lens")); } if (Math.floor(oldPoints / 10000) < Math.floor(newPoints / 10000)) { // every 10k points giveStuff(player, "@bright"); rewards.push(plural(1, "bright")); } if (Math.floor(oldPoints / 1000) < Math.floor(newPoints / 1000)) { // every 1k points if (Math.floor(newPoints / 1000) % 2 === 0) { giveStuff(player, "5@pack"); rewards.push(plural(5, "pack")); } else { giveStuff(player, "@celebrityTicket"); rewards.push(plural(1, "celebrityTicket")); } giveStuff(player, "3@brush"); rewards.push(plural(3, "brush")); } if (rewards.length > 0) { safaribot.sendMessage(src, "Editor-in-chief: You have been a great help with these photos! Here are some extra goodies for your hard work!", safchan); safaribot.sendMessage(src, "You received " + readable(rewards) + "!", safchan); } if (score >= 50) { safari.detectiveClue(player.idnum, "journal", src); player.records.goodJournalSubmission += 1; } if (playerTracks.hasOwnProperty(act)) { delete playerTracks[act]; } sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Journal|||Submitted photo of " + this.describePhoto(photo) + "|||Fulfilled request for " + this.translatePhotoRequest(req) + " (Base Score: " + req.fscore + "), received " + rewardName + (rewards.length > 0 ? ", " + readable(rewards) : "") + " and " + plural(score, "Photo Point") + (safari.hasCostumeSkill(player, "moreJournalPoints") ? " (using Journalist costume multiplier)" : "") + (scoreFortune > 0 ? " (using " + (1+scoreFortune) + "x Fortune multiplier)" : "") + "\n"); safari.pendingNotifications(player.id); this.saveGame(player); if (!req.done) { req.done = true; req.deadline = n + journalDoneDeadline; permObj.add("photographQuest", JSON.stringify(photographQuest)); } var onChannel = sys.playersOfChannel(safchan); for (e in onChannel) { player = getAvatar(onChannel[e]); if (!player) { continue; } playerTracks = player.quests.journal.trackedRequests; var toDelete = []; for (var request in playerTracks) { var photoRequest = photographQuest[request]; if (!photoRequest) { // expired, shouldnt happen bc expired requests are cleared on loadGame and completed requests while online are cleared after deadline is checked below safari.notification(player, "Journal request #{0} ({1}) expired and will be deleted from your tracked requests.".format(request, playerTracks[request]), "Journal"); toDelete.push(request); safari.saveGame(player); } else if (photoRequest.deadline - n <= journalDoneDeadline) { safari.notification(player, "Journal request #{0} ({1}){2} is expiring in {3}!".format(request, playerTracks[request], (photoRequest.done ? " was completed by someone and" : ""), timeLeftString(photoRequest.deadline)), "Journal"); toDelete.push(request); safari.saveGame(player); } } for (var d in toDelete) { delete playerTracks[toDelete[d]]; } } }; this.photoMatchesRequest = function(photo, request) { var id = parseInt(photo.id, 10); if (request.species && pokeInfo.species(request.species) !== pokeInfo.species(id)) { return false; } if (request.amt && photo.amt < request.amt) { return false; } if (request.quality && photo.score < request.quality) { return false; } var pWhere = photo.where; var rWhere = request.where; if (pWhere !== "default" && !contestThemes.hasOwnProperty(pWhere)) { pWhere = "default"; } if (rWhere !== "default" && !contestThemes.hasOwnProperty(rWhere)) { rWhere = "default"; } if (request.where && rWhere && pWhere !== rWhere) { return false; } if (request.what && photo.what !== request.what) { return false; } if (request.mood && photo.mood !== request.mood) { return false; } if (request.when && photo.when !== request.when) { return false; } if (request.type && !hasType(id, request.type)) { return false; } if (request.color && getPokeColor(id) !== request.color) { return false; } if (request.region && generation(id, true) !== request.region) { return false; } if (request.bst && getBST(id) < request.bst) { return false; } if (request.move && !canLearnMove(id, request.move)) { return false; } return true; }; this.translatePhotoRequest = function(obj) { var who = "any Pokémon"; var amt = obj.amt || 1; var mood = obj.mood || null; if (obj.species) { if (mood) { who = (amt === 1 ? an(mood) : toWord(amt) + " " + mood) + " " + poke(obj.species); } else { who = plural(amt, obj.species + ""); } } else if (obj.hasOwnProperty("type") || obj.hasOwnProperty("color") || obj.hasOwnProperty("region") || obj.hasOwnProperty("bst") || obj.hasOwnProperty("move")) { who = []; if (obj.color) { who.push(cap(obj.color)); } if (obj.type) { who.push(obj.type + "-type"); } who.push("Pokémon"); if (obj.region) { who.push("from " + obj.region); } if (obj.bst) { who.push("with BST above " + obj.bst); } if (obj.move) { who.push("that can learn " + moveOff(obj.move)); } who = (mood ? mood + " " : "") + who.join(" "); who = amt === 1 ? an(who) : ["two", "three", "four", "five"][amt-2] + " " + who; } else { who = "any" + (amt > 1 ? " " + ["two", "three", "four", "five"][amt-2] : "") + (mood ? " " + mood + " " : "")+ " Pokémon"; } if (obj.what) { who += " " + obj.what; } if (obj.where) { who += " in " + (obj.where === "default" ? "Default" : an(themeName(obj.where))) + " environment"; } if (obj.when) { who += " during the " + obj.when; } if (obj.quality) { who += " with quality at least " + photoQuality[obj.quality]; } return who; }; this.updatePhotographQuest = function() { var changed = false; var req; var goal = { "easy": 4, "normal": 7, "hard": 5, "ultra": 4 }; var current = { "easy": 0, "normal": 0, "hard": 0, "ultra": 0 }; var getRequestDiff = function(req) { if (req.score <= 47) { return "easy"; } else if (req.score <= 84) { return "normal"; } else if (req.score <= 124) { return "hard"; } else { return "ultra"; } }; var getDiffRange = function(diff) { switch (diff) { case "easy": return [16, 47]; case "normal": return [48, 84]; case "hard": return [85, 124]; case "ultra": return [125, 1000]; } }; var getNeededDiff = function() { if (current.easy < goal.easy) { return "easy"; } else if (current.normal < goal.normal) { return "normal"; } else if (current.hard < goal.hard) { return "hard"; } else if (current.ultra < goal.ultra) { return "ultra"; } return null; }; var getNextIndex = function(quest, justRemoved) { var list = Object.keys(quest).map(function(x) { return parseInt(x, 10); }); if (list.length === 0) { return 1; } var i = list[list.length-1]; while (list.contains(i) || justRemoved.contains(i)) { i++; if (i > 9999) { i = 1; } } return i; }; var n = now(), d, index, justRemoved = []; for (var c in photographQuest) { req = photographQuest[c]; if (req.deadline && n >= req.deadline) { delete photographQuest[c]; justRemoved.push(c); } else { current[getRequestDiff(req)]++; } } justRemoved = justRemoved.map(function(x) { return parseInt(x, 10); }); var diffModifiers = { easy: 0.75, normal: 0.9, hard: 1.05, ultra: 1.2 }; while (current.easy < goal.easy || current.normal < goal.normal | current.hard < goal.hard || current.ultra < goal.ultra) { d = getNeededDiff(); if (!d) { break; } req = this.createPhotoRequest(getDiffRange(d)); if (req) { c = getRequestDiff(req); req.fscore = Math.round(req.score * diffModifiers[c]); if (current[c] < goal[c]) { index = getNextIndex(photographQuest, justRemoved); req.deadline = now() + hours(48); photographQuest[index] = req; current[c]++; changed = true; } } } if (changed) { permObj.add("photographQuest", JSON.stringify(photographQuest)); } }; this.photoRequestList = function(obj) { var list = [], p, f, id; if (obj.species) { list.push(obj.species); if (obj.hasOwnProperty("what") && !photoActions.Any.contains(obj.what)) { var found = false; for (p in photoActions) { if (photoActions[p].contains(obj.what) && hasType(obj.species, p)) { found = true; break; } } if (!found) { return null; } } if (obj.hasOwnProperty("where") && obj.where !== "default" && !this.isInTheme(obj.species, obj.where)) { return null; } } else { if (!obj.hasOwnProperty("type") && !obj.hasOwnProperty("color") && !obj.hasOwnProperty("region") && !obj.hasOwnProperty("bst") && !obj.hasOwnProperty("move") && !obj.hasOwnProperty("what") && !obj.hasOwnProperty("where")) { return false; } var actTypes; if (obj.what && !photoActions.Any.contains(obj.what)) { actTypes = []; for (p in photoActions) { if (photoActions[p].contains(obj.what)) { actTypes.push(p); } } } var forms; for (p = 1; p < highestDexNum; p++) { if (isRare(p) || [772, 773].contains(p)) { // type: null & silvally continue; } forms = []; if (p in wildForms) { for (f = 0; f <= wildForms[p]; f++) { forms.push(pokeInfo.calcForme(p, f)); } } else { forms = [p]; } for (f = forms.length; f--; ) { id = forms[f]; if (obj.hasOwnProperty("type") && !hasType(id, obj.type)) { continue; } if (obj.hasOwnProperty("color") && getPokeColor(id) !== obj.color) { continue; } if (obj.hasOwnProperty("region") && generation(id, true) !== obj.region) { continue; } if (obj.hasOwnProperty("bst") && getBST(id) < obj.bst) { continue; } if (obj.hasOwnProperty("move") && !canLearnMove(p, obj.move)) { continue; } if (obj.hasOwnProperty("what") && actTypes && !hasType(id, actTypes[0]) && (actTypes.length <= 1 || !hasType(id, actTypes[1]))) { continue; } if (obj.hasOwnProperty("where") && obj.where !== "default" && !this.isInTheme(id, obj.where)) { continue; } list.push(id); } } } if (list.length === 0) { return null; } return list; }; this.createPhotoRequest = function(scoreRange) { var out = {}, val = 0; var calculateScore = function(obj, list) { var val = 0, mods = 0; if (obj.mood) { val += 38; mods += 2; } if (obj.when) { val += 26; mods += 1; } if (obj.what) { if (obj.what === "eating" && (!obj.where || obj.where === "default")) { val += 12; mods += 1; } else { val += photoActions.Any.contains(obj.what) ? 40 : 62; mods += 2; } } if (obj.where) { val += obj.where === "default" ? 10 : 31; mods += obj.where === "default" ? 1 : 2; } if (obj.quality) { val += Math.round(obj.quality * obj.quality * 0.6); mods += 1; } if (list && list.length > 0) { var bst = 0; list.forEach(function(x) { bst += getBST(x); }); bst = bst / list.length; var b, e, t = (bst-180)/50+1, amtRange = [1, 2, 4, 7, 11, 21, 51, 101, 201], bonusRange = [6.8, 5.7, 4.6, 3.2, 2, 1.7, 1.4, 1.2, 1]; for (e = amtRange.length; e--; ) { if (list.length >= amtRange[e]) { b = bonusRange[e]; break; } } val += Math.round((t*t/2.5) * b); if (list.length === 1) { mods += 2; } else { var speciesMods = ["type", "color", "region", "bst", "move"]; for (var e = speciesMods.length; e--; ) { if (obj.hasOwnProperty(speciesMods[e])) { mods += 1; } } } } else { //For "Any Pokémon" requests val *= 0.5; } var amtBonus = 1; if (obj.amt) { amtBonus = [1, 1, 1.75, 2.5, 3.8][obj.amt]; mods += 2; } var modsBonus = amtBonus + (mods > 1 ? 0.09 + (mods - 1) * 0.034 : 0); val = val * modsBonus; return Math.round(val); }; var addProperty = function(obj) { var prop = randomSample({ "amt": 3, "where": 3, "what": 5, "when": 4, "mood": 5, "quality": 5, "species": 1, "type": 3, "color": 3, "region": 3, "bst": 3, "move": 3 }); if (obj.hasOwnProperty(prop)) { return 0; } var val; switch (prop) { case "amt": val = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4].random(); if (val === 1) { return false; } obj.amt = val; return true; case "where": do { val = chance(0.33) ? "default" : Object.keys(contestThemes).random(); } while (val === "asia"); // block day-specific festival themes from appearing obj.where = val; return true; case "what": val = chance(0.7) ? photoActions.Any.random() : photoActions[Object.keys(photoActions).random()].random(); obj.what = val; return true; case "when": obj.when = ["night", "morning", "afternoon", "evening"].random(); return true; case "mood": val = photoMood.Positive.concat(photoMood.Neutral, photoMood.Negative).random(); obj.mood = val; return true; case "quality": val = sys.rand(1, 11); obj.quality = val; return true; case "species": if (obj.hasOwnProperty("type") || obj.hasOwnProperty("color") || obj.hasOwnProperty("region") || obj.hasOwnProperty("bst") || obj.hasOwnProperty("move")) { return false; } val = sys.rand(1, highestDexNum); if (isRare(val) || [772, 773].contains(val)) { // type: null & silvally return false; } if (val in wildForms && chance(0.25)) { var f = sys.rand(1, wildForms[val] + 1); val = pokeInfo.calcForme(val, f); } obj.species = val; return true; case "type": if (obj.species) { return false; } obj.type = Object.keys(effectiveness).random(); return true; case "color": if (obj.species) { return false; } val = Object.keys(pokeColors).random(); obj.color = val; return true; case "region": if (obj.species) { return false; } val = generations.random(); if (val === "None") { return false; } obj.region = val; return true; case "bst": if (obj.species) { return false; } val = sys.rand(200, 540); obj.bst = val; return true; case "move": if (obj.species) { return 0; } val = sys.rand(1, 622); obj.move = val; return true; } return true; }; var l = 0, paramCount = parseInt(randomSample({ "2": 9, "3": 5, "4": 1 }), 10), list; while (true) { l++; if (l > 40) { return null; } if (!addProperty(out)) { continue; } list = this.photoRequestList(out); if (list === null) { return null; } if (list && list.length === 1 && list[0] === 235 && out.move && chance(0.9)) { return null; } val = calculateScore(out, list); if (Object.keys(out).length >= paramCount) { break; } if (val >= scoreRange[0] && val <= scoreRange[1]) { break; } } if (!val || isNaN(val) || val < scoreRange[0] || val > scoreRange[1]) { return null; } var anyPoke = list === false; if (!anyPoke && list.length === 0) { return null; } if (list && list.length === 1 && !out.hasOwnProperty("species")) { out.species = list[0]; delete out.type; delete out.color; delete out.region; delete out.bst; delete out.move; } out.score = val; return out; }; this.mafiaAuction = function(src, data) { var player = getAvatar(src); if (cantBecause(src, "start a quest", ["tutorial"])) { return; } var trainerSprite = ''; if (stopQuests.monger) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: I will have to leave the town for a while, contact me again later...", safchan); return; } if (player.costume === "journalist") { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Hey hey, no cameras here. What are you, some kind of journalist? Quit snooping around if you know what's good for you...", safchan); return; } var set = data[0]; if (set && set.toLowerCase() === "help") { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: I hold auctions every few hours for some special items. But I only accept " + es(finishName("shady")) + "!", safchan); safaribot.sendMessage(src, "Monger: You can obtain those " + es(finishName("shady")) + " by playing Event Games at #Mafia. They are distributed 5 minutes after the Event Game is finished.", safchan); safaribot.sendHtmlMessage(src, "Monger: To participate in the auction, just place your bid in the item that you want with " + link("/quest monger:Set:YourBid", null, true) + ". Participants cannot see each other's bids, so offer a value that you think it will be enough to beat any other bid. If your bid is the highest valid offer at the deadline, you get the prize.", safchan); safaribot.sendMessage(src, "Monger: Be sure that you have the amount of " + es(finishName("shady")) + " that you offered by the time the auction finishes or your bid will be ignored. Also, if two or more people bid the same value, the first one to bid will be the winner.", safchan); return; } if (set && set.toLowerCase() === "shop") { if (!safari.hasCostumeSkill(player, "blackMarketAccess")) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Hey, what do you think you're doing back here? Scram! You'd better not let me catch you around these parts again...", safchan); return; } if (!mAuctionsMarket || mAuctionsMarket.length === 0) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Hmph, looks like I don't have anything left in here... Come back some other time.", safchan); return; } var itemChoice = data[1]; var choicePrice = parseInt(data[2]) || 0; if (!itemChoice) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Here are some of the goods I skim off my supplier sometimes... Better keep that to yourself, you hear? You know the drill, " + es(finishName("shady")) + " only.", safchan); for (var w in mAuctionsMarket) { var s = mAuctionsMarket[w]; var itemName = s.itemName; safaribot.sendHtmlMessage(src, "{0}: {1}".format(link("/quest monger:shop:" + s.item + ":" + s.cost, s.itemName, true), plural(s.cost, "shady")), safchan); } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "You currently have " + plural(player.balls.shady, "shady") + ". To buy something, use " + link("/quest monger:shop:", "/quest monger:shop:[Item Input]:[Item Cost]", true) + ", e.g. /quest monger:shop:3@nugget:4", safchan); return; } if (cantBecause(src, "buy items", ["contest", "auction", "event", "pyramid"])) { return; } itemChoice = itemChoice.toLowerCase(); var stockInd = -1 for (var w = 0; w < mAuctionsMarket.length; w++) { if (mAuctionsMarket[w].item === itemChoice && mAuctionsMarket[w].cost === choicePrice) { stockInd = w; break; } } if (stockInd === -1) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Huh? I don't have anything like that in here. Go get your eyes checked.", safchan); return; } var stock = mAuctionsMarket[stockInd]; var asset = translateAsset(stock.item); if (player.balls.shady < stock.cost) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: What's the meaning of this, you trying to shortchange me? This'll run you {0}, but you only have {1}.".format(plural(stock.cost, "shady"), player.balls.shady), safchan); return; } if (!this.isBelowCap(src, asset.id, asset.amount, asset.type)) { return; } player.balls.shady -= stock.cost; giveStuff(player, toStuffObj(stock.item)); safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Heh, nice doing business with you. Take your " + stock.itemName + ". (You now have " + plural(player.balls.shady, "shady") + "!)", safchan); safari.saveGame(player); mAuctionsMarket.splice(stockInd, 1); permObj.add("mAuctionsMarket", JSON.stringify(mAuctionsMarket)); permObj.save(); sys.appendToFile(shopLog, now() + "|||Monger NPC::" + asset.amount + "::" + asset.name + "::" + stock.cost + "::" + stock.cost + "::shady|||" + sys.name(src) + "\n"); return; } var options = ["a", "b", "c", "d", "e", "f"]; var id = player.id; if (!set || !options.contains(set.toLowerCase())) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: I have some interesting goodies here, but I only accept " + es(finishName("shady")) + " for them. To learn how this works, use " + link("/quest monger:help") + ".", safchan); if (mAuctionsData.length > 0) { safaribot.sendMessage(src, "Current Auctions: ", safchan); var e, obj, n = now(); for (e = 0; e < mAuctionsData.length; e++) { obj = mAuctionsData[e]; safaribot.sendHtmlMessage(src, link("/quest monger:" + options[e].toUpperCase() + ":", "[" + options[e].toUpperCase() + "]", true) + " " + obj.rewardName + " --- " + (obj.deadline < n ? "Deal ends after Contest" : "Deal ends in about " + timeLeftString(obj.deadline)) + (id in obj.offers ? " (Your bid: " + plural(obj.offers[id].value, "shady") + ")" : "") , safchan); } safaribot.sendMessage(src, "You currently have " + plural(player.balls.shady, "shady") + "!", safchan); if (safari.hasCostumeSkill(player, "blackMarketAccess") && mAuctionsMarket && mAuctionsMarket.length > 0) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Hey, you're in this business too aren't you? I can tell. I've got a {0} nearby with some special goods if you're interested...".format(link("/quest monger:shop", "storeroom")), safchan); } } return; } set = set.toLowerCase(); if (!options.contains(set)) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: This is not a valid set!", safchan); return; } var auct = mAuctionsData[options.indexOf(set)]; var bid = data[1]; if (!bid) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: To place a bid, type " + link("/quest monger:" + set.toUpperCase() + ":YourBid", null, true) + ". To remove your bid, simply offer 0.", safchan); return; } bid = parseInt(bid, 10); if (isNaN(bid) || bid < 0) { safaribot.sendMessage(src, "Please choose a valid amount for your bid!", safchan); return; } if (bid === 0) { if (id in auct.offers) { delete auct.offers[id]; safaribot.sendHtmlMessage(src, trainerSprite + "Monger: Your bid for the " + auct.rewardName + " auction was removed.", safchan); permObj.add("mAuctions", JSON.stringify(mAuctionsData)); } else { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: You didn't even bid in this auction!", safchan); } return; } if (player.balls.shady < bid) { safaribot.sendHtmlMessage(src, trainerSprite + "Monger: You do not have " + plural(bid, "shady") + " to bid!", safchan); return; } auct.offers[id] = { value: bid, time: now() }; safaribot.sendHtmlMessage(src, trainerSprite + "Monger: I registered your bid of " + plural(bid, "shady") + " for " + auct.rewardName + ". If you win this auction, you will receive your prize in " + timeLeftString(auct.deadline) + "!", safchan); permObj.add("mAuctions", JSON.stringify(mAuctionsData)); safari.toRecentQuests(player, "monger"); }; this.updateMAuctions = function() { //Run MAuctions that passed the deadline var n = now(), obj, changed = false; for (var e = mAuctionsData.length; e--; ) { obj = mAuctionsData[e]; if (n >= obj.deadline) { this.runMAuction(obj); mAuctionsData.splice(e, 1); changed = true; } } //Refill MAuctions list var set = mAuctionsData.length > 0 ? mAuctionsData[mAuctionsData.length-1].set : -1; while (mAuctionsData.length < 6) { set++; if (set >= 6) { set = 0; } mAuctionsData.push(this.createMAuction(mAuctionsData.length, set)); changed = true; } if (changed) { permObj.add("mAuctions", JSON.stringify(mAuctionsData)); permObj.save(); } }; this.mAuctionsMarketFill = function(obj) { var marketSize = 7; if (obj) { mAuctionsMarket.push({ item: obj.secret, itemName: translateStuff(obj.secret), cost: Math.ceil((obj.set + 1) * 1.2) }); } var debugging = []; if (mAuctionsMarket.length < marketSize) { var filler = [ ["10@redapricorn", "10@blkapricorn", "10@whtapricorn", "10@pnkapricorn", "10@grnapricorn", "15@bluapricorn", "15@ylwapricorn"], ["10@redapricorn", "10@blkapricorn", "10@whtapricorn", "10@pnkapricorn", "10@grnapricorn", "15@bluapricorn", "15@ylwapricorn"], ["@form", "5@gacha", "3@pearl", "15@redapricorn"], ["3@burn", "10@gacha", "2@pecha", "15@blkapricorn"], ["@rare", "15@gacha", "2@razz", "15@whtapricorn"], ["3@gem", "20@gacha", "2@bluk", "15@pnkapricorn"], ["5@silver", "25@gacha", "2@pokeblock", "2@leppa", "15@grnapricorn"] ]; var toFill = marketSize - mAuctionsMarket.length; var tmp = []; for (var i = 0; i < toFill; i++) { var f = filler[i].random(); tmp.push({ item: f, itemName: translateStuff(f), cost: Math.ceil((i + 2) * 1.3) }); debugging.push("i: {0}, f: {1}, mAuctionsMarket.length: {2}, marketSize: {3}, toFill: {4}".format(i, f, mAuctionsMarket.length, marketSize, toFill)); } mAuctionsMarket = tmp.concat(mAuctionsMarket); // concat so the filler goes in front and are likely to be pushed off first } while (mAuctionsMarket.length > marketSize) { mAuctionsMarket.shift(); } var hasInvalid = false; for (var i = 0; i < mAuctionsMarket.length; i++) { if (!mAuctionsMarket[i].hasOwnProperty("item") || !mAuctionsMarket[i].itemName) { hasInvalid = true; mAuctionsMarket.splice(i, 1); i--; } } if (hasInvalid) { sys.appendToFile(miscLog, now() + "|||Monger Market Debug|||" + debugging.join(" | ") + (obj ? " | " + JSON.stringify(obj) : "") + "\n"); } permObj.add("mAuctionsMarket", JSON.stringify(mAuctionsMarket)); } this.createMAuction = function(index, set) { var rewards = [ ["5@gem", "5@bigpearl", "3@starpiece", "3@nugget", "@bignugget", "4@golden"], ["4@golden", "@soda", "3@rare"], ["3@egg", "6@golden", "3@soda"], ["3@nugget", "15@silver", "3@pack", "3@brush", "5@gem", "@fossil"], ["3@tamato", "3@pinap", "3@nanab", "3@watmel", "3@petaya", "5@oran"], ["3@soda", "3@brush"] ]; var out = { offers: {}, //name: { value: 5, time: 354891351 } reward: rewards[set].random(), set: set, rewardName: "", deadline: now() + hours((index+1)*3) - 3000 }; do { out.secret = rewards[set].random() } while (out.secret === out.reward); out.rewardName = translateStuff(out.reward); return out; }; this.runMAuction = function(obj) { var off = obj.offers; var order = Object.keys(off).sort(function(a, b) { if (off[a].value === off[b].value) { return off[a].time - off[b].time; } return off[b].value - off[a].value; }); var player, o, id, winner; for (o = 0; o < order.length; o++) { id = order[o]; player = getAvatarOff(id); if (player && player.balls.shady >= off[id].value) { winner = player; break; } } if (winner) { var price = off[winner.id].value; winner.balls.shady -= price; var out = giveStuff(winner, toStuffObj(obj.reward)); winner.records.shadyUsed += price; winner.records.mongerAuctionsWon += 1; safari.missionProgress(winner,"winMonger",obj.rewardName,1,{}); this.saveGame(winner); sys.sendAll("", safchan); safaribot.sendAll(winner.id.toCorrectCase() + " paid " + plural(price, "shady") + " and won the Monger Auction for " + obj.rewardName + "!", safchan); if (sys.id(winner.id)) { safaribot.sendMessage(sys.id(winner.id), "You " + out + "!",safchan); } this.inboxMessage(winner, "You " + out + " from a Monger Auction by paying " + plural(price, "shady") + "!", isPlaying(winner.id)); sys.sendAll("", safchan); sys.appendToFile(crossLog, now() + "|||Monger|||" + winner.id.toCorrectCase() + "|||" + obj.rewardName + " by paying " + plural(price, "shady") + "\n"); sys.appendToFile(questLog, now() + "|||" + winner.id.toCorrectCase() + "|||Monger|||Paid " + plural(price, "shady") + "|||Received " + obj.rewardName + "\n"); safari.mAuctionsMarketFill(obj); } else { sys.sendAll("", safchan); safaribot.sendAll("No valid offers have been made for the Monger Auction of " + obj.rewardName + "!", safchan); sys.sendAll("", safchan); } safari.pendingNotifications(); }; /* Dashboard functions */ this.showOwnDashboard = function(src) { //Shows notifications, frequently used quests, finder/gacha if available var player = getAvatar(src); var line1 = ""; if (!(player)) { return; } var os = sys.os(sys.id(player.id)); line1 += costumeSprite(player, os) + " "; var partyShown = [].concat(player.party); /*if (currentThemeEffect == "distortion") { partyShown = [].concat(player.party) partyShown.reverse(); } else */if (currentThemeEffect == "past") { partyShown = [].concat(player.party); if (player.altTimeline.lead !== 0) { partyShown[0] = player.altTimeline.lead; } } for (var i = 0; i < partyShown.length; i++) { line1 += pokeInfo.icon(partyShown[i]) + " "; } var line2 = ""; if (player.costume !== "none") { line2 = link("/showcostume", costumeAlias(player.costume, false, true) + " (Lv. " + safari.getCostumeLevel(player) + ")"); } line2 += " " + link("/party", "«Party»"); line2 += " " + link("/bag", "«Bag»"); var currentTime = now(); if (player.balls.itemfinder + player.balls.permfinder > 0) { line2 += " " + link("/finder", "«Itemfinder»"); } if (player.balls.gacha > 0) { line2 += " " + link("/gacha", "«Gachapon»"); } if (player.balls.bait > 0) { line2 += " " + link("/bait", "«Bait»"); } if (player.balls.golden > 0) { line2 += " " + link("/gbait", "«Golden Bait»"); } if (player.balls.deluxe > 0) { line2 += " " + link("/dbait", "«Deluxe Bait»"); } line2 += " " + link("/buy", "«Buy»"); line2 += " " + link("/quest", "«Quests»"); var line3 = "Recent Quests:"; var qu = this.organizeRecentQuests(player), item; for (var i = 0; i < qu.length; i++) { item = qu[i]; if (["missions", "daycare"].contains(item)) { line3 += " " + link("/" + item, "«" + item.split(" ").map(capitalizeFirst).join(" ") + "»"); } else { line3 += " " + link("/quest " + item, "«" + item.split(" ").map(capitalizeFirst).join(" ") + "»"); } } var line4 = "Current Events:"; if (safari.events.trialsEnabled) { line4 += " " + link("/trials", "«Trials»"); } if (safari.events.spiritDuelsEnabled) { line4 += " " + link("/spiritduels", "«Spirit Duels»"); } if (safari.events.hiddenQuizEnabled) { line4 += " " + link("/nextquiz", "«Hidden Quiz»"); } if (safari.events.bonusLoginEnabled) { line4 += " " + link("/bonuslogin", "«Bonus Login»"); } if (safari.events.towerTroubleEnabled) { line4 += " " + link("/towertrouble", "«Tower Trouble»"); } var unseenNotifs = this.countUnseenNotifications(player); var unreadMail = countRepeated(player.unreadInbox, true); var line5 = link("/info", "«Info»"); line5 += " " + link("/notifications" + (unseenNotifs > 0 ? " unread" : ""), "«Notifications" + (unseenNotifs > 0 ? " (" + unseenNotifs + ")" : "") + "»") + " " + link("/inbox" + (unreadMail > 0 ? " unread" : ""), "«Inbox" + (unreadMail > 0 ? " (" + unreadMail + ")" : "") + "»"); line5 += " " + link("/options", "«Options»"); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, line1, safchan); safaribot.sendHtmlMessage(src, line2, safchan); safaribot.sendHtmlMessage(src, line3, safchan); if (line4 !== "Current Events:") { safaribot.sendHtmlMessage(src, line4, safchan); } safaribot.sendHtmlMessage(src, line5, safchan); sys.sendMessage(src, "", safchan); }; this.organizeRecentQuests = function(player) { var out = [], item, everyQuest = ["arena", "tower", "collector", "pyramid", "celebrity", "daycare", "missions", "journal", "monger", "wonder trade", "league", "baking", "alchemist", "arborist", "scientist", "idol"]; for (var i = 20; i > 1; i--) { for (var j = 0; j < everyQuest.length; j++) { item = everyQuest[j]; if (out.contains(item)) { continue; } if (countDuplicates(player.recentQuests, item) >= i) { out.push(item); } if (out.length > 7) { break; } } } return out; }; this.toRecentQuests = function(player, qu) { var hold = player.recentQuests.slice(0, 3); if (hold.contains(qu)) { return; } player.recentQuests.unshift(qu); player.recentQuests = player.recentQuests.slice(0, 35); safari.saveGame(player); } this.editOwnDashboard = function(src, data) { underConstruction(src); }; function underConstruction(src) { safaribot.sendMessage(src, "Feature under construction.", safchan); return; }; function generateName() { var part1 = sys.rand(1, highestDexNum), part2, name2, out, name1 = pokePlain(part1); do { part2 = sys.rand(1, highestDexNum); name2 = pokePlain(part2); out = name1.substr(0, Math.floor(name1.length/2)) + name2.substr(Math.floor(name2.length/2)); } while (part2 == part1 || (/asshole|dick|pussy|bitch|porn|nigga|cock|gay|slut|whore|cunt|penis|vagina|nigger|fuck|dildo|anus|boner|tits|condom|rape/gi.test(out)) || getInputPokemon(out+"").num !== null); return out; } function generateComplexName() { var name1, name2, out; name1 = getRandomWord().toLowerCase(); do { name2 = getRandomWord().toLowerCase(); out = cap(name1.substr(0, Math.floor(name1.length/2)) + name2.substr(Math.floor(name2.length/2))); } while ((/asshole|dick|pussy|bitch|porn|nigga|cock|gay|slut|whore|cunt|penis|vagina|nigger|fuck|dildo|anus|boner|tits|condom|rape/gi.test(out)) || getInputPokemon(out+"").num !== null || movenum(out) || abilitynum(out)); if (out.length > 10) { out = out.substr(0, 10); } return out; } function getRandomWord() { if (chance(0.33)) { return pokePlain(sys.rand(1, highestDexNum)); } else if (chance(0.5)) { return moveOff(sys.rand(1, 702)).split(/[\-\s]/).random(); } else { return abilityOff(sys.rand(1, 233)).split(" ").random(); } } function generateTeam(size, minBST, maxBST, maxLegends, useType, onlyEvolved, littlecup, counterMon, include) { var out = [], p, legendCount = 0, bst, maxLoop = 2500, i = 0, countersHit = 0, atkm, defm, hitCounter = false; size = size || 6; maxLegends = maxLegends || 1; if (include) { out = out.concat(include).slice(0, size); } while (out.length < size) { p = sys.rand(1, highestDexNum); bst = getBST(p); if (minBST && bst < minBST) { continue; } else if (maxBST && bst > maxBST) { continue; } if (onlyEvolved && p in evolutions) { continue; } else if (littlecup && (!(p in evolutions) || (p in devolutions))) { continue; } if (useType && out.length < 3) { if (!hasType(p, useType)) { continue; } } if (out.contains(p)) { continue; } if (counterMon && countersHit < 5 && i < maxLoop) { i++; atkm = Math.max(safari.checkEffective([type1(counterMon), type2(counterMon)], [type1(p), type2(p)]), 0.01); defm = safari.checkEffective([type1(p), type2(p)], [type1(counterMon), type2(counterMon)]); hitCounter = false; if (((((defm * 100) / atkm) * 0.01) < 4 && (i * 0.5 < maxLoop)) && (chance(0.65))) { continue; } if ((((defm * 100) / atkm) * 0.01) < 2) { continue; } hitCounter = true; } if (isLegendary(p)) { if (legendCount >= maxLegends) { continue; } legendCount++; } if (hitCounter) { countersHit++; } out.push(p); } return out; } this.playerHasActiveSkill = function(player, pokeId, key) { return pokeId in player.pokeskills && key in player.pokeskills[pokeId] && player.pokeskills[pokeId][key].uses > 0; }; this.isBasicSkill = function(key) { return !!skillData[key].basic; }; this.getSkillDescription = function(key) { if (key in skillData) { var skill = skillData[key]; return skill.description.format(skill.rate[0], skill.rate[1], skill.rate2[0], skill.rate2[1], skill.rate3[0], skill.rate3[1]); } return false; }; function getUnlockableSkills(pokeId, playerId) { var ret = []; var playerUnlocks = skillUnlocks[playerId] || {}; for (var skill in skillData) { if (pokeId in playerUnlocks && skill in playerUnlocks[pokeId]) { continue; } if (safari.isBasicSkill(skill)) { var skillType = skill.slice(5); if (skillType === type1(pokeId) || skillType === type2(pokeId)) { ret.push(skill); } } else if (skillData[skill].eligible && skillData[skill].eligible.contains(parseInt(pokeId))) { ret.push(skill); } } return ret; } function getUnlockedSkills(pokeId, playerId) { var ret = []; var playerUnlocks = skillUnlocks[playerId] || {}; for (var skill in skillData) { if (pokeId in playerUnlocks && skill in playerUnlocks[pokeId]) { ret.push(skill); } } return ret; } function getActiveSkills(pokeId, player) { var ret = []; var unlockedSkills = getUnlockedSkills(pokeId, player.idnum); for (var skill in unlockedSkills) { if (safari.playerHasActiveSkill(player, pokeId, unlockedSkills[skill])) { ret.push(unlockedSkills[skill]); } } return ret; } this.getSkillKeyByName = function(name) { for (var key in skillData) { if (skillData[key].name.toLowerCase() === name.toLowerCase()) return key; } }; this.pokeSkillActivated = function(playerName, party, skillKey) { // determine if a player can activate an ability, and returns the exact skill data var player = getAvatarOff(playerName); if (!player) { return false; } if (player.options.pokeskillsDisabled) { return false; } if (!Array.isArray(party)) { // pass single pokeId if not party skill party = [party]; } var activatedMon; var skillLevel = 0; for (var i = 0; i < party.length; i++) { if (party[i].hp === 0) { // fainted mons can't activate skills continue; } if (party[i].id in player.pokeskills) { // check if they have the skill activated if (!safari.playerHasActiveSkill(player, party[i].id, skillKey)) continue; var skill = player.pokeskills[party[i].id][skillKey]; if (skill.uses <= 0) { // if out of uses, delete the skill delete skill; if (Object.keys(player.pokeskills[party[i].id]).length === 0) { // if that was the last skill for that mon, delete that mon's entry from player.pokeskills delete player.pokeskills[party[i].id]; } continue; } if (skill.level > skillLevel) { // skills of higher level take precedence and will be used over lower levels if possible skillLevel = skill.level; activatedMon = party[i].id; } } } if (!activatedMon) { return false; } if (!safari.pokeSkillChance(skillKey, skillLevel)) { return false; } return safari.usePokeSkillCharge(player, activatedMon, skillKey); }; this.pokeSkillChance = function(skillKey, skillLevel) { // for skills that only activate at certain % chance even under the right conditions var data = skillData[skillKey]; var rate, prop = data.procProperty, // this determines which of rate/rate2 in skillData to use index = skillLevel - 1; if (!prop) { // if they don't have procProperty, that means they activate 100% of the time under the right conditions, so set rate to 1 rate = 1; } else { rate = data[prop][index] / 100; } return chance(rate); }; this.usePokeSkillCharge = function(player, pokeId, skillKey) { var skill = player.pokeskills[pokeId][skillKey]; var skillInfo = skillData[skillKey]; var name = skillInfo.name, desc = safari.getSkillDescription(skillKey); rate = skillInfo.rate, rate2 = skillInfo.rate2, rate3 = skillInfo.rate3 || []; skill.uses--; if (isPlaying(player.id)) safaribot.sendHtmlMessage(sys.id(player.id), "{0}'s {1} activated! [{2}] [Remaining Uses: {3}]".format(poke(pokeId), name, desc, skill.uses), safchan); if (skill.uses <= 0) { delete skill; if (Object.keys(player.pokeskills[pokeId]).length === 0) { delete player.pokeskills[pokeId]; } } safari.saveGame(player); rate = rate[skill.level - 1]; rate2 = rate2[skill.level - 1]; rate3 = rate3[skill.level - 1]; return { rate: rate, rate2: rate2, rate3: rate3, description: desc, name: name, id: pokeId }; }; var bakingData = { "apricorns": { "redapricorn": { "texture": 5, "bulk": 2, "taste": 2, "acidity": 0, "dryness": 4, "flavor": "Sour", "color": "#EE0000", "description": "A firm ingredient that provides a nice texture at the cost of being dry. Its Sour taste attracts Grass- and Rock-types." }, "bluapricorn": { "texture": 1, "bulk": 2, "taste": 4, "acidity": 1, "dryness": 1, "flavor": "Salt", "color": "#3A5FCD", "description": "A silky ingredient that has a good taste. Its Salty taste attracts Water- and Poison-types." }, "grnapricorn": { "texture": 5, "bulk": 2, "taste": 3, "acidity": 4, "dryness": 0, "flavor": "Bitter", "color": "#008000", "description": "A firm ingredient that provides a nice texture at the cost of adding acidity. Its Bitter taste attracts Fighting- and Ghost-types." }, "blkapricorn": { "texture": 4, "bulk": 3, "taste": 0, "acidity": 0, "dryness": 2, "flavor": "Savory", "color": "#000000", "description": "A hearty ingredient that has good bulk for its size. Its Savory taste attracts Dark- and Flying-types." }, "whtapricorn": { "texture": 2, "bulk": 3, "taste": 2, "acidity": 1, "dryness": 0, "flavor": "Tart", "color": "#ADADAD", "description": "A fairly average ingredient that has good bulk for its size. Its Tart taste attracts Psychic- and Fire-types." }, "pnkapricorn": { "texture": 2, "bulk": 1, "taste": 5, "acidity": 0, "dryness": 3, "flavor": "Umami", "color": "#FF69B4", "description": "A tasty ingredient that has poor bulk for its size. Its Umami taste attracts Ice- and Electric-types." }, "ylwapricorn": { "texture": 4, "bulk": 1, "taste": 4, "acidity": 2, "dryness": 2, "flavor": "Kokumi", "color": "#CDAD00", "description": "A volatile ingredient that has poor bulk for its size. Its Kokumi taste attracts Steel- and Fairy-types." }, "dew": { "texture": 1, "bulk": 0, "taste": 4, "acidity": 4, "dryness": 0, "flavor": "Spicy", "color": "#4B0082", "description": "A sharp ingredient. Its Spicy taste attracts Dragon- and Ground-types." }, "hdew": { "texture": 5, "bulk": 0, "taste": 2, "acidity": 1, "dryness": 0, "flavor": "Citrus", "color": "#8B668B", "description": "A smooth ingredient. Its Citrus taste attracts Bug- and Normal-types." } }, "berries": { "oran": { "sweet": 2, "acidity": 1, "wet": 2, "viscosity": 2, "rich": 0, "scent": 2, "quality": 2, "flavor": "Citrus", "color": "#33A1C9", "description": "A middling berry that has some of everything. Its Citrus taste attracts Bug- and Normal-types." }, "pecha": { "sweet": 4, "acidity": 0, "wet": 4, "viscosity": 1, "rich": 0, "scent": 0, "quality": 2, "flavor": "Umami", "color": "#FF1493", "description": "A tasty, juicy berry. Its Umami taste attracts Ice- and Electric-types." }, "bluk": { "sweet": 0, "acidity": 1, "wet": 2, "viscosity": 4, "rich": 4, "scent": 0, "quality": 2, "flavor": "Tart", "color": "#551A8B", "description": "A thick, rich berry. Its Tart taste attracts Psychic- and Fire-types." }, "razz": { "sweet": 0, "acidity": 4, "wet": 5, "viscosity": 3, "rich": 2, "scent": 1, "quality": 2, "flavor": "Spicy", "color": "#8B1A1A", "description": "A moist, rich berry. Its Spicy taste attracts Dragon- and Ground-types." }, "leppa": { "sweet": 0, "acidity": 3, "wet": 2, "viscosity": 1, "rich": 1, "scent": 5, "quality": 2, "flavor": "Sour", "color": "#FF9912", "description": "A berry that has an appealing scent. Its Sour taste attracts Grass- and Rock-types." }, "tamato": { "sweet": 2, "acidity": 0, "wet": 3, "viscosity": 1, "rich": 2, "scent": 2, "quality": 3, "flavor": "Savory", "color": "#FF6347", "description": "A good all-around berry. Its Savory taste attracts Dark- and Flying-types." }, "pinap": { "sweet": 7, "acidity": 2, "wet": 1, "viscosity": 2, "rich": 1, "scent": 0, "quality": 3, "flavor": "Bitter", "color": "#CD9B1D", "description": "An incredibly sweet berry. Its Bitter taste attracts Ghost- and Fighting-types." }, "nanab": { "sweet": 2, "acidity": 0, "wet": 2, "viscosity": 5, "rich": 4, "scent": 0, "quality": 3, "flavor": "Kokumi", "color": "#DA70D6", "description": "A thick berry. Its Kokumi taste attracts Steel- and Fairy-types." }, "petaya": { "sweet": 0, "acidity": 3, "wet": 4, "viscosity": 3, "rich": 4, "scent": 4, "quality": 3, "flavor": "Salt", "color": "#E3A869", "description": "An acidic berry that is very effective as an ingredient. Its Salty taste attracts Water and Poison-types." }, "watmel": { "sweet": 4, "acidity": 0, "wet": 5, "viscosity": 2, "rich": 3, "scent": 1, "quality": 4, "flavor": "None", "color": "#6B8E23", "description": "A chubby berry that adds a lot of moisture and sweetness. It lacks a strong flavor identity." }, "miracle": { "sweet": 6, "acidity": 0, "wet": 5, "viscosity": 2, "rich": 4, "scent": 7, "quality": 10, "flavor": "Spicy", "color": "#8E388E", "description": "A magical berry. Its Spicy taste attracts Dragon- and Ground-types." }, "platinum": { "sweet": 7, "acidity": 0, "wet": 3, "viscosity": 7, "rich": 10, "scent": 9, "quality": 10, "flavor": "Secret", "color": "#CDCD00", "description": "A magical berry. Its Secret taste is capable of baiting even rare Pokémon formes." }, "milk": { "sweet": 2, "acidity": 0, "wet": 6, "viscosity": 2, "rich": 0, "scent": 0, "quality": 0, "flavor": "None", "color": "#A1A1A1", "description": "Moomoo Milk helps keep thick recipes under control!" } } } /* Great Galarian Bait Off */ function Baking(players) { this.table = ["flour", "sugar"]; this.tableNext = []; this.qualityDry = { "texture": 0, "taste": 0, "bulk": 0, "acidity": 0, "dry": 0 } this.dryFlavors = []; this.players = players; this.viewers = []; this.watchCounts = {}; this.playersLower = players.map(function(x) { return x.toLowerCase(); }); this.playersActions = {}; for (var p in players) { this.playersActions[p.toLowerCase()] = ""; } this.judges = []; this.blending = false; this.blendStartTime = 0; this.judgeData = { "Totodile": "judge crumby dough harshly", "Pachirisu": "favor sweet bakes", "Galarian Meowth": "give an edge to bakes with strong aromas", "Chespin": "dislike dry bakes", "Fennekin": "demand rich flavor", "Pineco": "appreciate when multiple flavors are present", "Drilbur": "value the quality of ingredients above all else", "Rookidee": "have a knack for identifying a good sweet-acidic balance", "Makuhita": "prefer bakes with lots of Moomoo Milk", }; this.sweetTotal = 0; this.acidityTotal = 0; this.milkUsed = 0; this.quality = 0; this.secretFlavor = 0; this.needsBlending = 0; this.phase = 0; this.turn = 0; this.finished = false; this.bakeTimeNeeded = 0; this.bakeTimes = {}; this.msgAll("You all paid your entrance fees and now you get to enter the tent of the Great Galarian Bait-Off!"); safaribot.sendHtmlAll("A Baking quest between {0} has started! [{1}]".format(readable(this.players.map(function(e) { return e.toCorrectCase() })), link("/watchbak " + this.players[0], "Watch")), safchan); }; Baking.prototype.nextTurn = function() { this.turn++; if (this.phase == 0) { if (this.turn == 1) { this.msgAll(""); this.msgAll("Hey there, and welcome to the Great Galarian Bait-Off, where you can test your baking skills and get the seal of approval from esteemed judges among the Pokémon community."); this.msgAll("The wild Pokémon pay close attention to judge's reviews, so if you want your bait to be successful, you will need to earn their scores!", true); this.msgAll("My name is Paul Politoed and I will be your bait judge for today.", true); var judge = (Object.keys(this.judgeData)).random(); this.msgAll("I am joined by my fellow judge " + judge + " today. " + judge + " is known to " + this.judgeData[judge] + ", so be on the look-out!", true); for (var i = 0; i < 100; i++) { var judge2 = (Object.keys(this.judgeData)).random(); if (judge2 !== judge) { break; } } this.msgAll("We are also joined by " + judge2 + " as our third judge. " + judge2 + " is known to " + this.judgeData[judge2] + ". Keep this in mind if you want your bait to succeed!", true); this.judges = [judge, judge2]; this.msgAll("Remember: if you want your bait to be popular amongst rare Pokémon, you'll need to impress all 3 of us!", true); this.msgAll(""); } else if (this.turn == 2) { this.phase = 1; this.turn = 0; } } if (this.tableNext.length > 0) { for (var a in this.tableNext) { this.table.push(this.tableNext[a]); } this.tableNext = []; } if (this.blending) { this.blending = false; this.needsBlending -= ((now() - (this.blendStartTime)) * 0.001); } if (this.phase == 1 || this.phase == 2) { if (this.turn >= 7) { if (this.phase == 1) { this.compileDry(); } else { this.compileWet(); } } } else if (this.turn >= 4 && this.phase >= 3) { this.finalScore(); return; } var tableItems = {}; for (var i = 0; i < this.table.length; i++) { if (["milk", "flour", "sugar", "blend"].indexOf(this.table[i]) !== -1) { tableItems[this.table[i]] = -2; } else { if (!tableItems.hasOwnProperty(this.table[i])) { tableItems[this.table[i]] = 0; } tableItems[this.table[i]]++; } } tableReadable = []; var tableKeys = Object.keys(tableItems); for (var i = 0; i < tableKeys.length; i++) { var ingredient = tableKeys[i]; if (ingredient == "milk") { tableReadable.push(link("/bak add:" + ingredient, "Moomoo Milk", false, "#A1A1A1")); } else if (ingredient == "flour") { tableReadable.push(link("/bak add:" + ingredient, "Flour", false, "#8B795E")); } else if (ingredient == "sugar") { tableReadable.push(link("/bak add:" + ingredient, "Sugar", false, "#808080")); } else if (ingredient == "blend") { tableReadable.push(link("/bak add:blend", "Blend", false, "#292421")); } else { tableReadable.push(link("/bak add:" + ingredient, itemAlias(ingredient, false, true), false, this.phase == 1 ? bakingData.apricorns[ingredient].color : bakingData.berries[ingredient].color) + " (" + tableItems[ingredient] + ")"); } } for (var p in this.players) { this.playersActions[this.players[p].toLowerCase()] = ""; } for (p in this.players) { var player = this.players[p]; if (this.turn == 0) { continue; } else { var validItems = []; if (this.phase == 3) { if (this.turn == 1) { this.bakeStarted = now(); this.msg(player, "(Baking) Take the cake out of the oven when you think it's ready with " + link("/bak finish", "Finish") + "."); } } if (this.phase == 1) { this.msg(player, ""); this.msg(player, "(Base) Turn " + this.turn + ": "); validItems = ["redapricorn", "grnapricorn", "bluapricorn", "ylwapricorn", "blkapricorn", "whtapricorn", "pnkapricorn", "dew", "hdew"]; } if (this.phase == 2) { this.msg(player, ""); this.msg(player, "(Batter) Turn " + this.turn + ": "); validItems = ["oran", "pecha", "leppa", "bluk", "razz", "tamato", "pinap", "nanab", "petaya", "watmel", "miracle", "platinum"]; } var validItemsReadable = []; for (var i = 0; i < validItems.length; i++) { validItemsReadable.push(link("/bak fetch:" + validItems[i], itemAlias(validItems[i], false, true), false, this.phase == 1 ? bakingData.apricorns[validItems[i]].color : bakingData.berries[validItems[i]].color)); } if (this.phase == 1 || this.phase == 2) { if (this.turn < 6) { this.msg(player, "You can add the following to the table:"); this.msg(player, validItemsReadable.join(", ")); this.msg(player, ""); } this.msg(player, "Items on the table (you can add up to two to the bowl):"); this.msg(player, tableReadable.join(", ")) if (this.turn == 6) { this.msg(player, "This is the last turn of the phase!") } else if (this.phase == 2 && this.needsBlending > 15) { this.msg(player, "Paul Politoed: You need to be blending more. Your ingredients are having less effect!") } } } } }; Baking.prototype.addToTable = function(player, data) { var get = itemAlias(data.toLowerCase()); if (get == "safari") { this.msg(player, "Sorry, " + data + " is not a valid item!"); return false; } if (this.turn < 1) { this.msg(player, "You can't add " + an(itemAlias(get, false, true)) + " to the table yet!"); return false; } var playerName = player.toLowerCase(); player = getAvatarOff(player); var validItems = []; if (this.phase == 1) { validItems = ["redapricorn", "grnapricorn", "bluapricorn", "ylwapricorn", "blkapricorn", "whtapricorn", "pnkapricorn", "dew", "hdew"] } else if (this.phase == 2) { validItems = ["oran", "pecha", "leppa", "bluk", "razz", "tamato", "pinap", "nanab", "petaya", "watmel", "miracle", "platinum"] } if (!(validItems.contains(get))) { this.msg(playerName, "What are you doing? You can't just add " + itemAlias(get, false, true) + " to the recipe now!"); return false; } if (player.balls[get] <= 0) { this.msg(playerName, "Sorry, you don't have any " + itemAlias(get, false, true) + " to add to the table!"); return false; } if (["ingredient", "ingredient2"].contains(this.playersActions[playerName.toLowerCase()])) { this.msg(playerName, "Your hands are too covered in baking ingredients to reach into your bag! Try again next turn!"); return false; } if (["add"].contains(this.playersActions[playerName.toLowerCase()])) { this.msg(playerName, "You don't have enough time to add two items to the table!"); return false; } if (this.turn >= 6) { this.msg(playerName, "This is the last turn of the phase, so don't add anything else to the table!"); return false; } player.balls[get] -= 1; safari.updateShop(player, get); this.tableNext.push(get); safari.saveGame(player); this.msgAll(playerName.toCorrectCase() + " is adding " + an(itemAlias(get, false, true)) + " to the table!"); this.playersActions[playerName.toLowerCase()] = "add"; return true; }; Baking.prototype.addToBowl = function(player, item) { var get; if (["flour", "sugar", "milk", "blend"].contains(item)) { get = item; } else { get = itemAlias(item.toLowerCase()); } if (this.turn < 1) { this.msg(player, "You can't use " + get + " yet!"); return false; } var validItems = []; if (this.phase == 1) { validItems = ["redapricorn", "grnapricorn", "bluapricorn", "ylwapricorn", "blkapricorn", "whtapricorn", "pnkapricorn", "dew", "hdew", "flour", "sugar"] } else if (this.phase == 2) { validItems = ["oran", "pecha", "leppa", "bluk", "razz", "tamato", "pinap", "nanab", "petaya", "watmel", "miracle", "platinum", "milk", "blend"] } if (!(validItems.contains(get))) { this.msg(player, "What are you doing? You can't just add " + get + " to the bowl now!"); return false; } if (["add"].contains(this.playersActions[player.toLowerCase()])) { this.msg(player, "You already did action this turn!"); return false; } if (["ingredient2"].contains(this.playersActions[player.toLowerCase()])) { this.msg(player, "You can only add two ingredients to the bowl per turn!"); return false; } if (get == "blend") { this.blendBowl(player); return; } if (!(this.table.contains(get))) { this.msg(player, "That's not on the table right now!"); return false; } if (this.blending) { this.msg(player, "You can't add stuff to the bowl while it's blending!"); return false; } this.bowlEnter(player, get); //Pass the user's party lead if (!(["flour", "sugar", "milk"].contains(get))) { this.table.splice(this.table.indexOf(get), 1); } }; Baking.prototype.bowlEnter = function(player, item) { if (this.phase == 1) { var data, bulk, texture, taste, acidity, dryness; if (["flour", "sugar"].contains(item)) { data = { "flour": { "texture": 0, "bulk": 6, "taste": 0, "acidity": 0, "dryness": 6, "flavor": "None" }, "sugar": { "texture": 0, "bulk": 0, "taste": 5, "acidity": 0, "dryness": 2, "flavor": "None" } }[item] } else { data = bakingData["apricorns"][item]; } this.qualityDry.bulk += data.bulk; this.qualityDry.texture += data.texture; this.qualityDry.dry += data.dryness; this.qualityDry.acidity += data.acidity; this.qualityDry.taste += data.taste; this.dryFlavors.push(data.flavor); this.sweetTotal += data.taste; this.acidityTotal += data.acidity; if (!(["sugar", "flour"].contains(item))) { this.quality += 1; } } else if (this.phase == 2) { data = bakingData["berries"][item]; mult = 1; if (this.needsBlending > 8 + (4 * Math.random())) { mult *= 0.75; } if (this.needsBlending > 12 + (5 * Math.random())) { mult *= 0.75; } this.needsSweet -= data.sweet * mult; this.needsSweet += data.acidity * mult; this.needsWet -= data.wet * mult; this.needsRich -= data.rich * mult; this.needsScent -= data.scent * mult; this.needsThick -= data.viscosity * mult; this.sweetTotal += data.sweet * mult; this.acidityTotal += data.acidity * mult; this.quality += data.quality * mult; if (this.flavors.hasOwnProperty(data.flavor)) { this.flavors[data.flavor] += Math.round(((0.2 + (0.3 * Math.random())) * 100)) * 0.01 * mult; this.quality += (0.5 + (Math.random() * 0.5)) * mult; } else if (data.flavor == "Secret") { this.secretFlavor += 1; } if (item == "milk") { this.milkUsed += 1; } this.needsBlending += data.viscosity; } var translations = { "milk": "Moomoo Milk", "flour": "Flour", "sugar": "Sugar" } if (translations.hasOwnProperty(item)) { item = translations[item]; } else { item = itemAlias(item, false, true) } this.msgAll(player.toCorrectCase() + " puts " + item + " into the bowl!"); if (this.playersActions[player.toLowerCase()] == "ingredient") { this.playersActions[player.toLowerCase()] = "ingredient2"; } else { this.playersActions[player.toLowerCase()] = "ingredient"; } }; Baking.prototype.blendBowl = function(player) { if (this.blending) { this.msg(player, "The bowl is already blending!"); return; } this.blendStartTime = now(); this.blending = true; this.msgAll(player.toCorrectCase() + " is blending the bowl! The blender will run for the rest of the turn."); this.playersActions[player.toLowerCase()] = "add"; }; Baking.prototype.compileDry = function(item) { //We create ratios from the provided data this.qualityDry.bulk = Math.max(this.qualityDry.bulk, 10); this.blend = ((100 * (this.qualityDry.texture + this.qualityDry.taste + this.qualityDry.acidity)) / (this.qualityDry.dry + this.qualityDry.bulk)); this.needsSweet = (((95 + (this.qualityDry.bulk)) + ((this.qualityDry.acidity * 4) - this.qualityDry.taste)) / (1)); this.needsWet = (((70 + (this.qualityDry.bulk)) + ((this.qualityDry.dry * 1.65) - this.qualityDry.texture)) / (1)); this.needsRich = (((95 + (this.qualityDry.bulk * 2.4)) - (1.2 * (this.qualityDry.texture + this.qualityDry.taste))) / (1)); this.needsScent = (((80 + (this.qualityDry.bulk * 2.2)) - (1 * (this.qualityDry.acidity + this.qualityDry.taste))) / (1)); this.needsThick = (((100 + (this.qualityDry.bulk)) - (1 * (this.qualityDry.dry + this.qualityDry.texture))) / (1)); this.flavors = {}; var get = ""; for (var i = 0; i < 7; i++) { get = this.dryFlavors.random(); if (get == "None") { continue; //flour and sugar add no flavor } if (this.flavors.hasOwnProperty(get)) { this.flavors[get] += 1; } else { this.flavors[get] = 1; } } if (this.needsSweet > 140) { this.msgAll(toColor("Sweetness: The base is far too acidic. You will need to include lots of berries with good flavor in the wet ingredients to fix it.", "red"), true); } else if (this.needsSweet > 100) { this.msgAll(toColor("Sweetness: Be careful, because your base has a lot of acidic ingredients. You would benefit from including flavorful wet ingredients to balance it out.", "red"), true); } else if (this.needsSweet > 80) { this.msgAll(toColor("Sweetness: Watch out for adding too many acidic ingredients in the wet, or it may become too acidic.", "orange"), true); } else { this.msgAll(toColor("Sweetness: The flavor is good.", "green"), true); } if (this.needsWet > 175) { this.msgAll(toColor("Dryness: The base is far too dry. You will need to balance this out with lots of juicy ingredients.", "red"), true); } else if (this.needsWet > 100) { this.msgAll(toColor("Dryness: Your base has a lot of dry ingredients. I recommend adding a lot of juicy berries to balance it out.", "red"), true); } else { this.msgAll(toColor("Dryness: The balance is good.", "green"), true); } if (this.needsRich > 140) { this.msgAll(toColor("Richness: Zzz... the base is boring. You will need to improve it with some extremely strong berry choices.", "red"), true); } else if (this.needsRich > 100) { this.msgAll(toColor("Richness: Your base has a lot of boring ingredients. You're going to need to improve it with your berry choices.", "red"), true); } else if (this.needsRich > 80) { this.msgAll(toColor("Richness: Your base has a weakness in its depth. If you want to make this bait really good, I would stick to richer berries in the wet.", "orange"), true); } else { this.msgAll(toColor("Richness: The quality is good.", "green"), true); } if (this.needsScent > 140) { this.msgAll(toColor("Scent: I can't smell a thing. You need to add an appealing scent identity to make any decent bait.", "red"), true); } else if (this.needsScent > 100) { this.msgAll(toColor("Scent: Your base lacks a powerful scent. You're going to need to improve it with your berry choices.", "red"), true); } else if (this.needsScent > 80) { this.msgAll(toColor("Scent: Your scent is somewhat lacking. You'll need to improve your scent using your berries, as opposed to weakning it.", "orange"), true); } else { this.msgAll(toColor("Scent: The scent is good.", "green"), true); } if (this.needsThick > 140) { this.msgAll(toColor("Thickness: The base is completely crumbled. You may be able to save it if you include a lot of viscous ingredients.", "red"), true); } else if (this.needsThick > 100) { this.msgAll(toColor("Thickness: Your base kind of crumbling. You're going to need to improve it with more viscous berry choices.", "red"), true); } else if (this.needsThick > 80) { this.msgAll(toColor("Thickness: Your base is a bit lofty. Add more viscous berries to make up for it.", "orange"), true); } else { this.msgAll(toColor("Thickness: The thickness is good.", "green"), true); } this.msgAll(""); for (var a in this.flavors) { if (this.flavors[a] >= 3) { this.msgAll(toColor("It has a strong " + a + " flavor!", "blue"), true); this.flavors[a] = 3; } else if (this.flavors[a] >= 2) { this.msgAll(toColor("It has a noticeable " + a + " flavor!", "blue"), true); } else if (this.flavors[a] >= 1) { this.msgAll(toColor("It has a faint " + a + " flavor!", "blue"), true); } } this.msgAll(""); this.msgAll("Now it's time to add the berries to the batter! If you don't have berries, use Moomoo Milk! And remember to blend your ingredients, especially if you use viscous berries!"); this.msgAll(""); this.turn = 0; this.phase = 2; this.table = ["milk", "blend"]; }; Baking.prototype.compileWet = function(item) { this.msgAll(""); for (var a in this.flavors) { if (this.flavors[a] >= 5) { this.msgAll(toColor("What an excellent " + a + " flavor!", "blue"), true); } else if (this.flavors[a] >= 4) { this.msgAll(toColor("The " + a + " flavor is really strong!", "blue"), true); } else if (this.flavors[a] >= 3) { this.msgAll(toColor("It has a strong " + a + " flavor!", "blue"), true); } else if (this.flavors[a] >= 2) { this.msgAll(toColor("It has a noticeable " + a + " flavor.", "blue"), true); } else if (this.flavors[a] >= 1) { this.msgAll(toColor("It has a faint " + a + " flavor.", "blue"), true); } } var val = (this.qualityDry.bulk * 1.2) + (this.blend * 0.005) - (this.needsWet * 0.03); this.bakeTimeNeeded = (8 + (Math.min(((Math.max(val - 30, 0)) / this.qualityDry.bulk), 1) * 22)); this.bakeTimes = {}; for (var p in this.players) { this.bakeTimes[this.players[p].toLowerCase()] = 0; } this.msgAll(""); this.msgAll("Now let's put it in the oven! Good luck! Take it out when you think it's done!"); this.msgAll(""); this.turn = 0; this.phase = 3; }; Baking.prototype.takeOutOfOven = function(player) { if (this.phase !== 3) { this.msg(player, "It's not even the baking phase yet!"); return; } if (this.turn < 1) { this.msg(player, "Wait until it's gone in the oven first!"); return; } var diff = parseInt(Math.round((now() - this.bakeStarted) * 0.001), 10); if (diff < 5) { this.msg(player, "You can't bake your bait for less than 5 seconds!"); return false; } if (this.bakeTimes[player.toLowerCase()] > 0) { this.msg(player, "You already took your bait out of the oven!"); return false; } this.msgAll(player.toCorrectCase() + " took their bait out of the oven!"); this.bakeTimes[player.toLowerCase()] = parseInt(diff, 10) if (bakingDebug) { sys.sendHtmlMessage(sys.id("Miki Sayaka"), "Bait removed from oven at " + diff + ".", safchan); } for (var a in this.bakeTimes) { if (this.bakeTimes[a] == 0) { return; } } this.msgAll("Everyone took their baits out of the oven! Let's see what the final results are!"); this.turn = 4; }; Baking.prototype.finalScore = function() { for (var a in this.bakeTimes) { if (this.bakeTimes[a] == 0) { this.bakeTimes[a] = 30; this.msgAll(a.toCorrectCase() + " takes the bait out of the oven!"); } } var balance = 100, val = 0; if (this.needsWet > 100) { val = parseInt(Math.max((this.needsWet - 100) * 0.75, 0), 10); val = Math.min(val, 50); balance -= val; this.msgAll(toColor("The recipe is too dry. -" + val + ".", "red"), true); } if (this.needsRich > 100) { val = parseInt(Math.max((this.needsRich - 100) * 1, 0), 10); val = Math.min(val, 50); balance -= val; this.msgAll(toColor("The recipe isn't rich enough, so it feels as though it's lacking something. -" + val + ".", "red"), true); } if (this.needsSweet > 100) { val = parseInt(Math.max((this.needsSweet - 100) * 1, 0), 10); val = Math.min(val, 50); balance -= val; this.msgAll(toColor("The recipe lacks sweetness. -" + val + ".", "red"), true); } if (this.needsScent > 100) { val = parseInt(Math.max((this.needsScent - 100) * 1, 0), 10); val = Math.min(val, 50); balance -= val; this.msgAll(toColor("I really can't even sense the aroma this bait is supposed to have. -" + val + ".", "red"), true); } if (this.needsThick > 100) { val = parseInt(Math.max((this.needsThick - 100) * 1, 0), 10); val = Math.min(val, 50); balance -= val; this.msgAll(toColor("The dough is crumbing. You need more viscosity and firm ingredients to hold it together. -" + val + ".", "red"), true); } balance = Math.max(balance, 0); this.msgAll("Balance score: " + balance + ".", true); var flavor = 0; for (var a in this.flavors) { if (this.flavors[a] > 6) { this.flavors[a] = 6; } flavor += Math.max(this.flavors[a] - 2, 0) + (Math.max(this.flavors[a] * 0.75, 1.2) - 1.2) + this.flavors[a]; } flavor = Math.round(flavor * 1.5); var flavorBonus = Math.round((Math.min(this.sweetTotal, this.acidityTotal * 2) / this.qualityDry.bulk) * 33); flavor += flavorBonus; this.msgAll(""); this.msgAll("Flavor score: " + flavor + " (Bonus: " + flavorBonus + ").", true); var aggregateScore = flavor + (0.5 * balance) + (this.quality * 3) - (this.needsBlending * 2); var judgeStars = function(num) { if (num > 4.5) { return "5"; } else if (num > 4) { return "4 and a half"; } else if (num > 3.5) { return "4"; } else if (num > 3) { return "3 and a half"; } else if (num > 2.5) { return "3"; } else if (num > 2) { return "2 and a half"; } else if (num > 1.5) { return "2"; } else if (num > 1) { return "1 and a half"; } else { return "1" } } var judgeScore = 0; if (this.judges.contains("Totodile")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Salt")) { judgeScore += 2.2; this.msgAll("Totodile appreciates the inclusion of Salty taste."); } if (this.needsThick > 100) { judgeScore -= 1; } else if (this.needsThick < 75) { judgeScore += 1; this.msgAll("Totodile is impressed that the dough is sufficiently thick."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Totodile's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Pachirisu")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Umami")) { judgeScore += 2.2; this.msgAll("Pachirisu appreciates the inclusion of Umami taste."); } if (this.needsSweet > 100) { judgeScore -= 1; } else if (this.needsSweet < 25) { judgeScore += 1; this.msgAll("Pachirisu is happy that it's sweet enough."); } else if (this.needsSweet < 75) { judgeScore += 0.5; this.msgAll("Pachirisu is satisfied that it's sweet enough."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Pachirisu's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Galarian Meowth")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Kokumi")) { judgeScore += 2.2; this.msgAll("Galarian Meowth appreciates the inclusion of Kokumi taste."); } if (this.needsScent > 100) { judgeScore -= 1; } else if (this.needsScent < 75) { judgeScore += 1; this.msgAll("Galarian Meowth is impressed it has a strong aroma."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Galarian Meowth's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Chespin")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Sour")) { judgeScore += 2.2; this.msgAll("Chespin appreciates the inclusion of Sour taste."); } if (this.needsScent > 100) { judgeScore -= 1; } else if (this.needsWet < 75) { judgeScore += 1; this.msgAll("Chespin is glad it's moist enough."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Chespin's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 4; } if (this.judges.contains("Fennekin")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Tart")) { judgeScore += 2.2; this.msgAll("Fennekin appreciates the inclusion of Tart taste."); } if (this.needsScent > 100) { judgeScore -= 1; } else if (this.needsRich < 75) { judgeScore += 1; this.msgAll("Fennekin appreciates the richness."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Fennekin's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Pineco")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Citrus")) { judgeScore += 2.2; this.msgAll("Pineco appreciates the inclusion of Citrus taste."); } if (Object.keys(this.flavors).length == 1) { judgeScore -= 1.8; } if (Object.keys(this.flavors).length > 2) { judgeScore += 2; this.msgAll("Pineco is impressed with the variety of flavors."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Pineco's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Drilbur")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Spicy")) { judgeScore += 2; this.msgAll("Drilbur appreciates the inclusion of Spicy taste."); } if (this.quality < 15) { judgeScore -= 1; } if (this.quality < 8) { judgeScore -= 0.85; } if (this.quality > 30) { judgeScore += 2; this.msgAll("Drilbur is impressed with the quality of the ingredients."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Drilbur's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Rookidee")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Savory")) { judgeScore += 2.2; this.msgAll("Rookidee appreciates the inclusion of Savory taste."); } if (this.acidityTotal < 12) { judgeScore -= 1; } if (this.acidityTotal < 8) { judgeScore -= 0.75; } if (this.acidityTotal > 25) { judgeScore += 2; this.msgAll("Rookidee is pleased with the balance of acidity and sweetness."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Rookidee's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } if (this.judges.contains("Makuhita")) { judgeScore = Math.round(balance * 0.025 * 100) * 0.01; if (this.flavors.hasOwnProperty("Bitter")) { judgeScore += 2.2; this.msgAll("Makuhita appreciates the inclusion of Bitter taste."); } if (this.milkUsed < 1) { judgeScore -= 1; } if (this.milkUsed < 3) { judgeScore -= 0.75; } if (this.milkUsed > 6) { judgeScore += 2.5; this.msgAll("Makuhita is very pleased with the amount of Moomoo Milk used."); } else if (this.milkUsed > 4) { judgeScore += 1.2; this.msgAll("Makuhita is pleased with the amount of Moomoo Milk used."); } judgeScore = Math.min(judgeScore, 5); this.msgAll("Makuhita's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; } judgeScore = Math.round(((balance * 0.015) + Math.max((this.blend * 0.01), 0) + (this.quality * 0.03)) * 100) * 0.01; judgeScore = Math.min(judgeScore, 5); this.msgAll("Paul Politoed's review: " + judgeStars(judgeScore) + "/5 stars!"); aggregateScore += judgeScore * 5; this.msgAll("Total Score: " + aggregateScore + "!"); var amtGiven = (Math.round(0.5 * Math.max(this.qualityDry.bulk - 6, 2) * (100 / this.players.length)) * 0.01); bakeScores = {}; var underbaked = false, score = 0; for (var a in this.bakeTimes) { score = Math.abs(this.bakeTimes[a] - this.bakeTimeNeeded); if (isNaN(score)) { score = 10; } score = (Math.round(score * 100) * 0.01); if (bakingDebug) { sys.sendHtmlMessage(sys.id("Miki Sayaka"), "Oven score for " + a + ": " + score + ".", safchan); } underbaked = this.bakeTimes[a] - this.bakeTimeNeeded > 0 ? false : true; if (score < 0.75) { score = 0; this.msgAll("Baking score for " + toColored(a.toCorrectCase(), a) + ": PERFECT.", true); } else if (score < 1.5) { this.msgAll("Baking score for " + toColored(a.toCorrectCase(), a) + ": GOOD. (" + toColor("-" + toFixed(score, 2), "orange") + ") " + (underbaked ? " [Slightly Underbaked])." : " [Slightly Overbaked])."), true); } else if (score < 5) { score *= parseInt(1.5 * score, 10); this.msgAll("Baking score for " + toColored(a.toCorrectCase(), a) + ": OKAY. (" + toColor("-" + toFixed(score, 2), "red") + (underbaked ? " [Underbaked])." : " [Overbaked])."), true); } else if (score < 10) { score *= parseInt(2 * score, 10); this.msgAll("Baking score for " + toColored(a.toCorrectCase(), a) + ": POOR. (" + toColor("-" + toFixed(score, 2), "red") + (underbaked ? " [Underbaked])." : " [Overbaked])."), true); } else { score *= parseInt(2.5 * score, 10); this.msgAll("Baking score for " + toColored(a.toCorrectCase(), a) + ": DISASTROUS. (" + toColor("-" + toFixed(score, 2), "red") + (underbaked ? " [Underbaked])." : " [Overbaked])."), true); } bakeScores[a.toLowerCase()] = score; } var eggTypes = {}, mon, player, data; for (var p in this.players) { player = getAvatarOff(this.players[p]); mon = pokeInfo.species(parseInt(player.party[0], 10)); data = eggdata[mon+""] || ["Undiscovered"]; for (var i = 0; i < data.length; i++) { if (data[i] == "Undiscovered") { continue; } if (eggTypes.hasOwnProperty(data[i])) { eggTypes[data[i]] += 1; } else { eggTypes[data[i]] = 0; } } } var mon, val = 0, eggval = 0, bstval = 0, flavorval = 0, bst, hits; var out = { "commons": { "rate": 0, "list": [] }, "uncommons": { "rate": 0, "list": [] }, "rares": { "rate": 0, "list": [] } }; var searchedMons0 = 0; var searchedMons1 = 0; var searchedMons2 = 0; var searchedMons3 = 0; var bakeDexNum = 1026; // not using highest dex num here since these are probably floodgates we want to open manually per new gen for (var j = 0; j < bakeDexNum; j++) { for (var i = 0; i <= 62; i++) { // 62 for alcremie formes excluding finale. could probably rewrite this section to utilise getAllForms or something as a more elegant solution but i dont have energy for that :v mon = getInputPokemon(poke(j + (65536 * i))); if (!(mon.num)) { break; } searchedMons0 += 1; var rareForm = false; if (i > 0) { if (!(wildForms.hasOwnProperty(j+""))) { if (this.secretFlavor == 0) { break; } if (poke(j + (65536 * i)).slice(0, 5) == "Mega ") { break; } if (!(chance(0.2 * this.secretFlavor))) { continue; } rareForm = true; } else { if (wildForms[j+""] <= i) { break; } } } searchedMons1 += 1; val = 0; eggval = 0; bst = getBST(mon.num); if (ultraBeasts.concat([892, 66428, 898, 1024]).contains(mon.num) || paradoxPokemon.contains(mon.num)) { // [urshifu, urshifu-rapid strike, calyrex, terapagos] bst = 600; } if (regionalEvos.concat([789, 891]).contains(mon.num)) { // [cosmog, kubfu] bst = Math.max(bst, 510); rareForm = true; } if ((legendaries.contains(mon.num)) || ultraBeasts.contains(mon.num) || paradoxPokemon.contains(mon.num)) { if (bst >= 650) { if (chance((aggregateScore - 276) * 0.007)) { eggval = 20; } else { continue; } } else if (bst >= 590) { if (chance((aggregateScore - 118) * 0.003)) { eggval = 25; } else { continue; } } else { if (chance((aggregateScore - 85) * 0.004)) { eggval = 30; } else { continue; } } } for (var a in eggTypes) { if (eggdata.hasOwnProperty(mon.num+"")) { if (eggdata[mon.num+""].contains(a)) { eggval += (eggTypes[a] * Math.random()); } } } val += Math.max(eggval - 3, 0) + Math.max(eggval - 5, 0) + eggval; flavorval = 0; for (var f in this.flavors) { var target = []; switch (f) { case "Sour": target = ["Grass", "Rock"]; break; case "Salt": target = ["Poison", "Water"]; break; case "Bitter": target = ["Fighting", "Ghost"]; break; case "Savory": target = ["Dark", "Flying"]; break; case "Tart": target = ["Psychic", "Fire"]; break; case "Umami": target = ["Ice", "Electric"]; break; case "Kokumi": target = ["Steel", "Fairy"]; break; case "Spicy": target = ["Dragon", "Ground"]; break; case "Citrus": target = ["Bug", "Normal"]; break; } if (target.contains(type1(mon.num)) || target.contains(type2(mon.num))) { flavorval += 0.55 + ((Math.random() + 0.1) * this.flavors[f]); } } if (flavorval > 3) { val += flavorval * (3 + (0.25 * eggval)); } if (flavorval > 5) { val += flavorval * (5 + eggval); } if (chance((aggregateScore * 0.001))) { val *= 6; } else if (chance((aggregateScore * 0.002))) { val *= 5; } else if (chance((aggregateScore * 0.003))) { val *= 4; } else if (chance((aggregateScore * 0.004))) { val *= 3; } else if (chance((aggregateScore * 0.005))) { val *= 2; } //Determine the ability of this Pokémon to spawn based on its calculated value if (val < 15 && (chance(0.75))) { continue; //Doesn't spawn } if (val < 7 && (chance(0.75))) { continue; //Doesn't spawn } searchedMons2 += 1; if (val > 100) { val = 100; } hits = 0; if (rareForm) { bst += 50; } if (bst <= 400) { hits = (chance(0.5) ? val : (chance(0.5) ? val * 0.5 : val * 0.25)); if (hits < 1) { if (chance(0.5)) { continue; } hits = (chance(0.9) ? 12 : 6); } } else { for (var k = 0; k < 16; k++) { if (((Math.random() * ((val) * 2)) + 404) >= bst) { hits++; } } for (var k = 0; k < 6; k++) { if (((Math.random() * (80 + val)) + 500) >= bst) { hits++; } } } if (bst > 590) { hits -= 1; } if (((legendaries.contains(mon.num) || ultraBeasts.contains(mon.num) || paradoxPokemon.contains(mon.num)) && hits > 0)) { hits *= 0.5; if (bst > 580) { hits -= 1; } if (bst >= 660) { hits = (chance((aggregateScore - 200) * 0.007) ? 1 : 0); } } if (rareForm) { hits -= 1; } if (hits < 1) { continue; } searchedMons3 += 1; if (hits >= 12) { out.commons.list.push(mon.num); } else if (hits >= 5) { out.uncommons.list.push(mon.num); } else if (hits >= 1) { out.rares.list.push(mon.num); } else { searchedMons3 -= 1; } } } var loop = 0; while (out.commons.list.length < 10) { loop += 1; if (loop > 500) { break; } var get = sys.rand(1, bakeDexNum); bst = getBST(get); if (bst < 361 && (!(legendaries.contains(get))) && !out.commons.list.contains(get)) { out.commons.list.push(get); } } loop = 0; while (out.uncommons.list.length < 8) { loop += 1; if (loop > 200) { break; } var get = sys.rand(1, bakeDexNum); bst = getBST(get); if (bst < 481 && (!(legendaries.contains(get))) && !out.uncommons.list.contains(get)) { out.uncommons.list.push(get); } } if (bakingDebug) { sys.sendHtmlMessage(sys.id("Miki Sayaka"), "Searched Mons: " + searchedMons0 + " | " + searchedMons1 + " | " + searchedMons2 + " | " + searchedMons3, safchan); } var player; for (var p in this.players) { player = getAvatarOff(this.players[p]); player.deluxeBait = { "commons": { "rate": 0, "list": [] }, "uncommons": { "rate": 0, "list": [] }, "rares": { "rate": 0, "list": [] } }; player.deluxeBait.commons.list = [].concat(out.commons.list); player.deluxeBait.uncommons.list = [].concat(out.uncommons.list); player.deluxeBait.rares.list = [].concat(out.rares.list); player.deluxeBait.rares.rate = Math.max(((aggregateScore - (bakeScores[this.players[p].toLowerCase()] * 1.75)) * 0.0158) - 2, 0.01); if (player.deluxeBait.rares.list.length <= 0) { player.deluxeBait.rares.rate = 0; } player.deluxeBait.uncommons.rate = Math.max(((aggregateScore - (bakeScores[this.players[p].toLowerCase()] * 3.5)) * 0.059) - player.deluxeBait.rares.rate, 0.1); player.deluxeBait.commons.rate = 100 - (player.deluxeBait.uncommons.rate + player.deluxeBait.rares.rate); if (aggregateScore < 96) { player.deluxeBait.inedible = Math.min(((100 - aggregateScore) * 0.01) + 0.01, 0.45); if (safari.hasCostumeSkill(player, "moreEdibleDeluxe")) { player.deluxeBait.inedible *= 0.5; player.deluxeBait.inedible = Math.min(0.3, player.deluxeBait.inedible); } } else { player.deluxeBait.inedible = 0; } var finalAmt = Math.round(amtGiven * (safari.hasCostumeSkill(player, "higherBakingYield") ? 1.3 : 1)); g = giveStuff(player, toStuffObj(finalAmt + "@deluxe")); this.msg(player.id, "You " + g + "!"); this.msg(player.id, "Bait Data:"); this.msg(player.id, "- Commons (" + toFixed(player.deluxeBait.commons.rate, 3) + "%): " + player.deluxeBait.commons.list.map(function(x) { return poke(x) }).join(", ")); this.msg(player.id, "- Uncommons (" + toFixed(player.deluxeBait.uncommons.rate, 3) + "%): " + player.deluxeBait.uncommons.list.map(function(x) { return poke(x) }).join(", ")); this.msg(player.id, "- Rares (" + toFixed(player.deluxeBait.rares.rate, 5) + "%): " + player.deluxeBait.rares.list.map(function(x) { return poke(x) }).join(", ")); if (player.deluxeBait.inedible > 0) { this.msg(player.id, "- Somewhat inedible (" + toFixed(player.deluxeBait.inedible * 100, 5) + "%)"); } safari.pendingNotifications(player.id); safari.saveGame(player); } for (var v in this.viewers) { this.msg(this.viewers[v], "Bait Data:"); this.msg(this.viewers[v], "- Commons: " + out.commons.list.map(function(x) { return poke(x) }).join(", ")); this.msg(this.viewers[v], "- Uncommons: " + out.uncommons.list.map(function(x) { return poke(x) }).join(", ")); this.msg(this.viewers[v], "- Rares: " + out.rares.list.map(function(x) { return poke(x) }).join(", ")); } this.finished = true; this.msgAll(""); this.msgAll("Thank you for participating in the Great Galarian Bait-Off! We hope you made some satisfactory baits today and learned something!", true); this.msgAll(""); }; Baking.prototype.msg = function(name, msg, flashing, colored) { var id = sys.id(name); if (id) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { if (flashing) { safaribot.sendHtmlMessage(id, toFlashing(msg, name), safchan); } else if (colored) { safaribot.sendHtmlMessage(id, toColored(msg, name), safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } } }; Baking.prototype.msgAll = function(msg, host, flashing, colored) { var e; var list = removeDuplicates(this.players.concat(this.viewers)); if (host) { msg = "Paul Politoed: " + msg; } for (e = 0; e < list.length; e++) { this.msg(list[e], msg, flashing, colored); } }; Baking.prototype.handleCommand = function(player, command) { if (!(this.playersLower.contains(player.toLowerCase()))) { return false; } command = command.split(":"); var commandData = ""; if (command.length > 1) { commandData = command[1]; } command = command[0]; player = player.toLowerCase(); if (command == "fetch") { this.addToTable(player, commandData); } else if (command == "add") { this.addToBowl(player, commandData); } else if (command == "finish") { this.takeOutOfOven(player); } return; }; Baking.prototype.isInKitchen = function(player) { if (!(this.playersLower.contains(player.toLowerCase()))) { return false; } return true; }; /* Pyramid */ function Pyramid(p1, p2, p3, usingVoucher) { this.finished = false; this.leader = p1.id; this.players = [p1, p2, p3]; this.names = [p1.id, p2.id, p3.id]; this.fullNames = this.names.map(function(x) { return x.toCorrectCase(); }); this.viewers = [p1.id, p2.id, p3.id]; this.watchCounts = {}; this.usingVoucher = usingVoucher; this.level = 1; this.room = 0; this.currentRoom = null; this.points = 0; this.ticks = 0; this.turn = -1; this.turnToAdvance = 1; this.movingRoom = false; this.movingFloor = false; this.quitWarning = false; this.finishMode = null; this.stamina = {}; this.stamina[p1.id] = 300 + Math.floor(300 * p1.quests.pyramid.bonusStamina + ((safari.hasCostumeSkill(p1, "pyrStaminaBoost") ? 10 : 0))); this.stamina[p2.id] = 300 + Math.floor(300 * p2.quests.pyramid.bonusStamina + ((safari.hasCostumeSkill(p1, "pyrStaminaBoost") ? 10 : 0))); this.stamina[p3.id] = 300 + Math.floor(300 * p3.quests.pyramid.bonusStamina + ((safari.hasCostumeSkill(p1, "pyrStaminaBoost") ? 10 : 0))); this.recentRooms = []; p1.quests.pyramid.bonusStamina = 0; p2.quests.pyramid.bonusStamina = 0; p3.quests.pyramid.bonusStamina = 0; safari.saveGame(p1); safari.saveGame(p2); safari.saveGame(p3); this.maxStamina = {}; this.maxStamina[p1.id] = this.stamina[p1.id]; this.maxStamina[p2.id] = this.stamina[p2.id]; this.maxStamina[p3.id] = this.stamina[p3.id]; this.parties = {}; this.parties[p1.id] = p1.party.slice(0, 3); this.parties[p2.id] = p2.party.slice(0, 3); this.parties[p3.id] = p3.party.slice(0, 3); this.distractedStrong = {}; this.distractedStrong[p1.id] = 0; this.distractedStrong[p2.id] = 0; this.distractedStrong[p3.id] = 0; /*this.bannedHazard = pokeInfo.species(parseInt(this.parties[p1.id][2], 10)); this.bannedHazard = ["plants", "water", "boulder", "toxic", "pit", "ice", "flame", "electric", "dark", "barrier"][this.bannedHazard % 10];*/ this.bannedHazard = ["sand","barrier","target"]; if (p1.quests.pyramid.hazards.length > 0) { this.bannedHazard = p1.quests.pyramid.hazards || []; //for now you can ban three hazards } this.sendToViewers(""); this.sendToViewers(readable(this.fullNames, "and") + " are entering the Pyramid!"); this.sendToViewers(""); } Pyramid.prototype.nextTurn = function() { this.ticks++; if (this.finished) { return; } if (this.ticks % 7 !== 0) { if (this.ticks % 3 === 0) { if (!(this.movingRoom || this.turn < 0) || this.movingFloor) { return; } } else { return; } } this.turn++; if (this.turn === 0) { this.sendToViewers("The Pyramid challenge is starting! Preparing to enter the first room!"); return; } if (this.quitWarning) { this.sendToViewers(""); this.sendToViewers("The party leader (" + this.leader.toCorrectCase() + ") can finish this Pyramid quest by typing " + link("/pyr quit", null, true) + ", or keep going by waiting a few seconds."); this.sendToViewers(""); this.quitWarning = false; } else if (this.movingRoom) { if (this.movingFloor) { this.level++; this.room = 0; } this.sendToViewers(""); this.sendToViewers("You are now moving towards the room {0}-{1}!".format(this.level, (this.room+1))); this.sendToViewers(""); this.movingRoom = false; this.movingFloor = false; } else if (this.currentRoom === null) { for (var x = 0; x <= this.recentRooms.length; x++) { var type = randomSample({ horde: 11, riddle: 11, hazard: 12, blocked: 13, trainer: 13, defense: 13, empty: 9, strong: 13 }); if (!this.recentRooms.slice(0, this.recentRooms.length - x).contains(type)) { break; } } this.room++; var roomDuration = 2; switch (type) { case "horde": this.currentRoom = new HordeRoom(this, this.level, this.room); break; case "strong": this.currentRoom = new StrongRoom(this, this.level, this.room); break; case "riddle": this.currentRoom = new RiddleRoom(this, this.level, this.room); break; case "hazard": this.currentRoom = new HazardRoom(this, this.level, this.room); roomDuration = this.level > 3 ? 5 : 4; break; case "blocked": this.currentRoom = new BlockedRoom(this, this.level, this.room); break; case "trainer": this.currentRoom = new TrainerRoom(this, this.level, this.room); break; case "defense": this.currentRoom = new DefenseRoom(this, this.level, this.room); break; case "empty": this.currentRoom = new EmptyRoom(this, this.level, this.room); roomDuration = 3; break; } this.currentRoom.turnToAdvance = this.turn + roomDuration; this.recentRooms.unshift(type); this.recentRooms = this.recentRooms.slice(0, Math.min(3, this.recentRooms.length)); } else { if (this.turn < this.currentRoom.turnToAdvance) { this.currentRoom.midturn(); } else { if (!this.currentRoom.earlyFinish) { this.currentRoom.advance(); } if (!this.hasStamina()) { this.finishMode = "stamina"; this.finish(); } else if (this.currentRoom.passed) { var early = this.currentRoom.earlyFinish; this.currentRoom = null; this.movingRoom = true; if (this.room >= 7) { if (this.level >= 7) { this.finishMode = "cleared"; this.finish(); } else { var stmBonus = {}; for (var s in this.stamina) { if (this.stamina[s] > 0) { stmBonus[s] = Math.ceil(this.stamina[s] * (0.03 + this.level * 0.0095)); if (this.stamina[s] + stmBonus[s] > this.maxStamina[s]) { stmBonus[s] = this.maxStamina[s] - this.stamina[s]; } } } var staminaStr = []; for (s in stmBonus) { if (this.stamina[s] <= 0) { continue; } staminaStr.push(s.toCorrectCase() + " +" + stmBonus[s]); } this.sendToViewers("You cleared the level " + this.level + "! Stamina restored: " + staminaStr.join(", ")); this.updateStatus(0, stmBonus); this.sendMessage(this.leader, "You can go up one floor or quit the Pyramid now. To leave now, type " + link("/pyr quit", null, true) + ". To keep going, just wait a few seconds!"); this.sendToViewers(""); this.movingFloor = true; this.quitWarning = true; } } else if (early) { this.nextTurn(); } } } } }; Pyramid.prototype.hasStamina = function() { if (this.stamina[this.leader] <= 0) { this.sendToViewers(""); this.sendToViewers("Party leader " + this.leader.toCorrectCase() + "'s stamina dropped to 0! This Pyramid quest is now over!"); return false; } for (var e in this.stamina) { if (this.stamina[e] > 0) { return true; } } this.sendToViewers(""); this.sendToViewers("Everyone's stamina dropped to 0! This Pyramid quest is now over!"); return false; }; Pyramid.prototype.updateStatus = function(points, stamina, showChange) { if (points) { this.points += points; } if (stamina) { for (var e in stamina) { this.stamina[e] += stamina[e]; this.stamina[e] = Math.max(0, Math.min(this.stamina[e], this.maxStamina[e])); } } var stm = this.stamina; if (showChange) { var staminaStr = []; for (var n in stamina) { if (stamina[n] == 0) { continue; } staminaStr.push(n.toCorrectCase() + " " + (stamina[n] >= 0 ? "+" + stamina[n] : stamina[n])); } if (points !== 0 || staminaStr.length > 0) { this.sendToViewers("Points gained: " + points + (staminaStr.length > 0 ? " | Stamina lost: " + staminaStr.join(", ") : "")); } } this.sendToViewers("Points: " + this.points + " | Stamina: " + this.names.map(function(x){ return x.toCorrectCase() + " (" + stm[x] + ")"; }).join(", ")); }; Pyramid.prototype.useCommand = function(src, commandData) { if (this.finished) { return; } if (this.currentRoom) { this.currentRoom.useCommand(src, commandData); } else if (this.movingFloor && sys.name(src).toLowerCase() === this.leader && ["quit", "leave", "exist", "abort", "give up"].contains(commandData)) { //Leader can abort quest between floors this.sendToViewers(this.leader.toCorrectCase() + " decided to finish this Pyramid quest!"); this.finishMode = "quit"; this.finish(); } }; Pyramid.prototype.finish = function() { var stmBonus = 0; for (var m in this.stamina) { stmBonus += this.stamina[m]; } if (this.finishMode === "cleared") { sys.sendAll("", safchan); } else { this.sendToViewers(""); } stmBonus = Math.round(stmBonus * (this.level/7)); if (stmBonus > 0) { this.points += stmBonus; this.sendToViewers("You received a bonus " + plural(stmBonus, "Point") + " from your remaining stamina!"); } var bonusMult = 1; /* remove bonus multiplier for now for (var i in this.parties) { for (var j in this.parties[i]) { if (pyrBonusMons.contains(parseInt(this.parties[i][j], 10))) { bonusMult = (Math.min(bonusMult + 0.1, 1.5)); } } } */ bonusMult = Math.round(bonusMult * 10) * 0.1 if (bonusMult > 1) { this.sendToViewers("You received a bonus x" + toFixed(bonusMult, 1) + " from your Bonus Pokémon!"); } this.points *= bonusMult; if (this.usingVoucher) { var voucherPoints = Math.round(this.points * itemData.fossil.bonusRate); this.points += voucherPoints; this.sendToViewers("You received a bonus " + plural(toFixed(voucherPoints), "Point") + " for using " + an(finishName("fossil")) + "!"); } if (this.finishMode === "cleared") { var finishBonus = Math.round(this.points * 0.10); this.points += finishBonus; this.sendToViewers("You received a bonus " + plural(finishBonus, "Point") + " for clearing all floors!"); safaribot.sendAll(readable(this.fullNames, "and") + " cleared the Pyramid with a total of " + plural(toFixed(this.points, 2), "Point") + "!", safchan); } else { this.sendToViewers(readable(this.fullNames, "and") + " reached the " + getOrdinal(this.room) + " room of the " + getOrdinal(this.level) + " floor with a total of " + plural(toFixed(this.points, 2), "Point") + "!"); } var finishVerb; switch (this.finishMode) { case "quit": this.sendToViewers("This Pyramid run was finished because the leader decided to leave!"); finishVerb = "Quit at room " + this.level + "-" + this.room; break; case "stamina": this.sendToViewers("This Pyramid run was finished because the leader's stamina dropped to 0!"); finishVerb = "Defeated at room " + this.level + "-" + this.room; break; case "cleared": this.sendToViewers("This Pyramid run was finished because the challengers cleared all floors!"); finishVerb = "Cleared the Pyramid"; break; } var teamNames = this.fullNames.map(function(x) { return x + " (" + this.parties[x.toLowerCase()].map(poke).join(", ") + ")"; }, this).join(", "); this.sendToViewers("Team: " + teamNames); var p = this.points, reward = []; if (p >= 15000) { reward.push("2@bright"); } if (p >= 13000) { reward.push("1@bright"); } if (p >= 11000) { reward.push("1@bright"); } if (p >= 9000) { reward.push("1@bright"); } if (p >= 8000) { reward.push("3@mega"); } if (p >= 7000) { reward.push("10@rare"); } if (p >= 6000) { reward.push("5@pack"); } else if (p >= 5500) { reward.push("1@pack"); } if (p >= 5000) { if (p % 2 == 0) { reward.push("1@mega"); } else { reward.push("1@mushroom"); } } if (p >= 4500) { if (p % 3 == 0) { reward.push("@golden"); } else { reward.push("2@rare"); } } if (p >= 4000) { if (p % 2 == 1) { reward.push("1@mega"); } else { reward.push("1@mushroom"); } } if (p >= 3500) { if (p % 3 == 0) { reward.push("2@rare"); } else { reward.push("@golden"); } } if (p >= 3000) { reward.push("1@nugget"); } if (p >= 2500) { reward.push((this.level * 5) + "@" + ["redapricorn", "ylwapricorn", "pnkapricorn"].random()); } if (p >= 1500) { reward.push((this.level * 5) + "@" + ["blkapricorn", "whtapricorn", "grnapricorn", "bluapricorn"].random()); } if (this.finishMode === "cleared") { reward.push("2@crystal"); reward.push("@mega"); } else { reward.push((this.level * 2) + "@gacha"); } reward = reward.join(", "); var e, name, player, out; for (e = 0; e < this.names.length; e++) { name = this.names[e]; player = getAvatarOff(name); if (name === this.leader) { player.quests.pyramid.cooldown = now() + Math.round(((this.usingVoucher ? 15 : 45)*60*1000) * (1 - safari.getFortune(player, "questcd", 0, "pyramid")) * (1 - safari.getAuraEffect(player, "questcd", 0))); player.notificationData.pyramidWaiting = true; if (this.points > player.records.pyramidLeaderScore) { player.records.pyramidLeaderScore = this.points; } if (this.finishMode === "cleared") { player.records.pyramidLeaderClears += 1; } if (reward) { out = giveStuff(player, toStuffObj(reward)); this.sendToViewers("Party leader " + name.toCorrectCase() + " " + out + " for their performance at Pyramid!"); } } else { if (player.quests.pyramid.cooldown < now()) { player.quests.pyramid.cooldown = now() + (this.usingVoucher ? 0 : 15)*60*1000; } if (this.points > player.records.pyramidHelperScore) { player.records.pyramidHelperScore = this.points; } if (this.finishMode === "cleared") { player.records.pyramidHelperClears += 1; } } if (this.level > 1 || (this.movingFloor && this.level === 1)) { safari.detectiveClue(player.idnum, "pyramid1"); } if (this.level > 2 || (this.movingFloor && this.level === 2)) { safari.detectiveClue(player.idnum, "pyramid2"); } if (this.level > 3 || (this.movingFloor && this.level === 3)) { safari.detectiveClue(player.idnum, "pyramid3"); } if (this.level > 4 || (this.movingFloor && this.level === 4)) { safari.detectiveClue(player.idnum, "pyramid4"); } if (this.level > 5 || (this.movingFloor && this.level === 5)) { safari.detectiveClue(player.idnum, "pyramid5"); } safari.missionProgress(player, "pyramid", this.level + "-" + this.room, 1, { points: this.points }); player.records.pyramidTotalScore += this.points; var val = monthlyLeaderboards["pyramidScore"].get(player.id) || 0; if (val < this.points) safari.addToMonthlyLeaderboards(player.id, "pyramidScore", this.points, true); if (isPlaying(name)) { safari.revertMega(sys.id(name), true); } safari.pendingNotifications(player.id); safari.saveGame(player); } if (this.finishMode === "cleared") { sys.sendAll("", safchan); } else { this.sendToViewers(""); } sys.appendToFile(questLog, now() + "|||" + this.leader.toCorrectCase() + "|||Pyramid|||Challenged with " + teamNames + (this.usingVoucher ? ", paid with " + finishName("fossil") : "") +"|||" + finishVerb + " with " + plural(this.points, "Point") + ", " + out + "\n"); this.finished = true; checkUpdate(); }; Pyramid.prototype.sendMessage = function(name, msg, flashing, colored) { var id = sys.id(name); if (id) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { if (flashing) { safaribot.sendHtmlMessage(id, toFlashing(msg, name), safchan); } else if (colored) { safaribot.sendHtmlMessage(id, toColored(msg, name), safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } } }; Pyramid.prototype.sendToViewers = function(msg, flashing, colored) { var e; var list = removeDuplicates(this.viewers); for (e = 0 ; e < list.length; e++) { this.sendMessage(list[e], msg, flashing, colored); } }; Pyramid.prototype.abort = function(src, name) { this.finishMode = "aborted"; this.sendToViewers("This Pyramid run was aborted because " + sys.name(src) + " shoved " + name + " from it!"); this.finished = true; }; Pyramid.prototype.isInPyramid = function(name) { return this.stamina.hasOwnProperty(name.toLowerCase()) && this.stamina[name.toLowerCase()] > 0; }; function pyrLink(obj) { var out = []; for (var e in obj) { out.push("[" + e.toUpperCase() + "] " + link("/pyr " + obj[e])); } return out.join(", "); } function getTreasure(id, reward) { var player = getAvatarOff(id); if (player && reward.item !== "stamina") { var amt = reward.amount; if (safari.hasCostumeSkill(player, "betterPyrItems")) { var ch = ((0.16 + (safari.getCostumeLevel(player) - 5)/30)); if (chance(ch)) { amt = Math.round(amt * (1.25 + (0.5 * Math.random()))); } } if (reward.item === "money") { player.money += amt; if (player.money > moneyCap) { player.money = moneyCap; } player.records.pyramidMoney += amt; } else { rewardCapCheck(player, reward.item, amt); if (reward.item == "silver") { player.records.pyramidSilver += amt; } } safari.saveGame(player); } } function treasureName(reward) { if (reward.item === "money") { return "$" + addComma(reward.amount); } else if (reward.item === "stamina") { return "a potion that restores " + reward.amount + " Stamina"; } else { return plural(reward.amount, reward.item); } } function toShortcut(choices) { var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z", "!", "?"], out = {}, e; for (e = 0; e < choices.length; e++) { out[letters[e]] = choices[e]; } return out; } function PyramidRoom(pyramidRef) { this.pyr = pyramidRef; this.passed = false; this.choices = {}; this.shortcuts = {}; this.midmsg = "Make your choice, you have 8 seconds!"; this.individualmsg = {}; this.defaultToFirstPoke = true; this.turnToAdvance = 0; } PyramidRoom.prototype.midturn = function() { this.sendToAlive(this.midmsg); this.sendIndividuals(); }; PyramidRoom.prototype.sendIndividuals = function() { for (var n in this.individualmsg) { if (this.pyr.stamina[n] > 0) { this.send(n, this.individualmsg[n]); } } }; PyramidRoom.prototype.useCommand = function(src, commandData) { var player = getAvatar(src); if (commandData.length === 1) { if (this.shortcuts.hasOwnProperty(player.id) && this.shortcuts[player.id].hasOwnProperty(commandData.toLowerCase())) { commandData = this.shortcuts[player.id][commandData.toLowerCase()]; } } if (this.validInput && !this.validInput(player.id, commandData)) { return; } if (commandData == "distract") { this.distractingStrong[player.id] = true; this.sendAll(toColor("{0} is going to distract the Pokémon!".format(player.id.toCorrectCase()), "crimson")); return; } this.choices[player.id] = commandData; if (this.postInput && this.postInput(src, commandData)) { return; } commandData = this.toReadableInput(commandData); this.sendAll(toColor("{0} is going to use {1}!".format(player.id.toCorrectCase(), commandData), "crimson")); }; PyramidRoom.prototype.pokeInParty = function(id, commandData) { var p = getInputPokemon(commandData); if (!p.num) { this.send(id, "Please type a valid Pokémon!"); return false; } if (!this.pyr.parties[id].contains(p.id)) { this.send(id, "You didn't bring this Pokémon in your Pyramid party!"); return false; } return true; }; PyramidRoom.prototype.toReadableInput = function(data) { var p = getInputPokemon(data); return p.name; }; PyramidRoom.prototype.getChoices = function() { var out = {}, p, id, members = this.pyr.names; for (p in members) { id = members[p]; if (this.pyr.stamina[id] <= 0) { continue; } if (id in this.choices) { var pkmn = getInputPokemon(this.choices[id]); if (pkmn.num) { out[id] = pkmn.id; } else { out[id] = this.choices[id]; } } else { if (this.defaultLeaderChoice && id === this.pyr.leader) { out[id] = this.defaultLeaderChoice; } else if (this.defaultChoice) { out[id] = this.defaultChoice; } else if (this.defaultToFirstPoke) { out[id] = this.pyr.parties[id][0]; } } } return out; }; PyramidRoom.prototype.send = function(name, msg, flashing, colored) { var id = sys.id(name); if (id) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { if (flashing) { safaribot.sendHtmlMessage(id, toFlashing(msg, name), safchan); } else if (colored) { safaribot.sendHtmlMessage(id, toColored(msg, name), safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } } }; PyramidRoom.prototype.sendAll = function(msg, flashing, colored) { var e; var list = removeDuplicates(this.pyr.viewers); for (e = 0 ; e < list.length; e++) { this.send(list[e], msg, flashing, colored); } }; PyramidRoom.prototype.sendToAlive = function(msg, flashing, colored) { for (var n in this.pyr.stamina) { if (this.pyr.stamina[n] > 0) { this.send(n, msg, flashing, colored); } } }; function HordeRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.midmsg = "Choose a Pokémon to defeat the horde!"; this.horde = []; this.level = level; var size = this.startingSize = 8 + level, p; var minBST = 130 + 60 * level; var maxLegend = (level == 7 ? 1 : 0); while (this.horde.length < size) { p = sys.rand(1, highestDexNum); if ((!isLegendary(p) || maxLegend > 0) && getBST(p) >= minBST) { this.horde.push(p); if (isLegendary(p)) { maxLegend -= 1; } } } var typeChances = { "Normal":7,"Fighting":9,"Flying":13,"Poison":8,"Ground":11,"Rock":9.5,"Bug":7,"Ghost":19,"Steel":25,"Fire":13,"Water":13,"Grass":7.5,"Electric":9.5,"Psychic":6,"Ice":4.5,"Dragon":16,"Dark":12,"Fairy":16 }; var fTypes = this.forbiddenTypes = [randomSample(typeChances)], t; while (fTypes.length < 2) { t = randomSample(typeChances); if (!fTypes.contains(t)) { fTypes.push(t); } } var parties = pyramidRef.parties, pt; this.possibleBattlers = {}; for (var p in parties) { pt = parties[p]; this.possibleBattlers[p] = pt; if (pt.length > 0) { this.shortcuts[p] = toShortcut(pt.map(poke)); this.individualmsg[p] = "Send one of your Pokémon to help: " + pyrLink(this.shortcuts[p]); } else { this.individualmsg[p] = "None of your Pokémon can participate in this battle!"; } } this.hordePower = [10 + level * 10, 100 + level * 25]; this.treasures = { starpiece: { chance: (3 + (2.1 * level)), item: "starpiece", amount: 1 }, bignugget: { chance: (-2 + (1.1 * level)), item: "bignugget", amount: 1 }, crystal: { chance: (-4 + (1.7 * level)), item: "bignugget", amount: 1 }, bait: { chance: 20, item: "bait", amount: 2 * level }, gacha: { chance: 14, item: "gacha", amount: 2 * level }, dust: { chance: 16, item: "dust", amount: 9 * level }, rare: { chance: (2 + (0.5 * level)), item: "rare", amount: 1}, safari: { chance: 15, item: "safari", amount: 3 * level }, great: { chance: 12, item: "great", amount: level }, pnkapricorn: { chance: 12 - level, item: "pnkapricorn", amount: 2 * level }, redapricorn: { chance: 12 - level, item: "redapricorn", amount: level }, rock: { chance: 14 - level, item: "rock", amount: 4 * level }, pearl: { chance: 10, item: "pearl", amount: 1 * level }, stardust: { chance: 9, item: "stardust", amount: 1 * level } }; this.treasureHolder = null; if (chance(0.2 + this.level * 0.06)) { this.treasureHolder = sys.rand(0, this.horde.length); this.treasureHeld = randomSampleObj(this.treasures); } this.sendAll(""); this.sendAll("Room {0}-{1}: This room is infested with lots of wild Pokémon! Defeat them to pass, but there is a nefarious trap to hurt {2}-type and {3}-type Pokémon!".format(level, roomNum, typeIcon(this.forbiddenTypes[0]), typeIcon(this.forbiddenTypes[1]))); this.sendAll("Wild Pokémon: " + this.horde.map(pokeInfo.icon).join("")); this.sendIndividuals(); this.sendAll(""); } HordeRoom.prototype = new PyramidRoom(); HordeRoom.prototype.validInput = function(id, commandData) { if (!this.pokeInParty(id, commandData)) { return false; } return true; }; HordeRoom.prototype.postInput = function(src, commandData) { var p = getInputPokemon(commandData); var nerfed = hasType(p.id, this.forbiddenTypes[0]) ||hasType(p.id, this.forbiddenTypes[1]); this.sendAll(toColor("{0} is going to use {1}{2}!".format(sys.name(src), p.name, (nerfed ? " (Trap)" : "")), "crimson")); return true; }; HordeRoom.prototype.advance = function() { var members = this.pyr.names, id, choice, m, p, dmg, opp, res, defeated = {}, stamina = {}, defeatedCount = 0, points = 0, attackers = {}, attackersNames, lastAttacker = 0, l, n, treasureWinner, treasurePoke, nerfed = {}, avi, playerBonus = null; attackers = this.getChoices(); attackersNames = Object.keys(attackers); for (m = this.horde.length; m--; ) { opp = this.horde[m]; for (p = lastAttacker, l = 0; l < attackersNames.length; p++, l++) { n = p % (attackersNames.length); id = attackersNames[n]; choice = attackers[id]; avi = getAvatarOff(id); var playerBonus = [10, 100]; if ((type2(choice)) === "???" && (avi.costume === "flower")) { playerBonus = [30, 120]; } if (pyrBonusMons.contains(choice % 65536)) { playerBonus[0] += 35; playerBonus[1] += 35; } res = calcDamage(choice, opp, playerBonus, this.hordePower, false, getCherished(choice, id)); lastAttacker = n + 1; if (hasType(choice, this.forbiddenTypes[0]) && hasType(choice, this.forbiddenTypes[1])) { res.power[0] = Math.round(res.power[0] * 1); nerfed[id] = 2; } else if (hasType(choice, this.forbiddenTypes[0]) || hasType(choice, this.forbiddenTypes[1])) { res.power[0] = Math.round(res.power[0] * 1); nerfed[id] = 1; } if (res.power[0] > res.power[1]) { defeatedCount++; if (!defeated.hasOwnProperty(id)) { defeated[id] = []; } defeated[id].push("{0} ({1} x {2})".format(toColor(poke(opp), "blue"), res.power[0], res.power[1])); this.horde.splice(m, 1); points += 3 + (1.75 * this.level); if (this.treasureHolder !== null && m === this.treasureHolder) { treasurePoke = opp; treasureWinner = id; } break; } } } var defeatedStr = []; for (p in defeated) { defeatedStr.push("{0}'s {1} defeated {2}".format("" + p.toCorrectCase() + "", "" + (nerfed[p] ? toColor(poke(attackers[p]), "red") : poke(attackers[p])) + "", readable(defeated[p].reverse(), "and"))); } for (var p in members) { var id = members[p] stamina[id] = 0; } if (Object.keys(nerfed).length > 0) { this.sendAll(""); var trapDamage = (10 * this.level), finalTrapDamage; for (var a in nerfed) { finalTrapDamage = trapDamage * nerfed[a]; this.sendAll(a.toCorrectCase() + " suffered " + finalTrapDamage + " from the trap!"); stamina[a] = -1 * finalTrapDamage; } } this.sendAll(""); if (defeatedStr.length > 0) { this.sendAll(defeatedStr.join(" | ")); if (treasureWinner) { this.sendAll(""); this.sendAll("The {0} defeated by {1} dropped something! {1} found {2}!".format(poke(treasurePoke), addFlashTag(treasureWinner.toCorrectCase()), toColor(treasureName(this.treasureHeld), "blue")), true); getTreasure(treasureWinner, this.treasureHeld); } } else { points += (10 * level); } if (this.horde.length > 0) { var averageDamage = Math.ceil((this.horde.length / this.startingSize) * ((this.level + 5) * 21)), c; if (this.horde.length > 1) { averageDamage += (0.85 * this.level); } if (this.horde.length > 2) { averageDamage += (0.9 * this.level + 2); } for (p in members) { id = members[p]; if (this.pyr.stamina[id] <= 0) { continue; } c = id in defeated ? defeated[id].length : 0; dmg = Math.round(averageDamage * (1 - ((c / Math.max(defeatedCount, 1)) - 0.3))); stamina[id] -= dmg; } this.sendAll("The following Wild Pokémon haven't been defeated and attacked the players: " + readable(this.horde.map(function(x){ return toColored(poke(x), "red"); }), "and") + "!"); } points = Math.round(points); this.pyr.updateStatus(points, stamina, true); this.sendAll(""); this.passed = true; }; function StrongRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.level = level; this.attacks = 0; this.distractingStrong = {}; var parties = pyramidRef.parties; for (var p in parties) { this.shortcuts[p] = toShortcut(parties[p].map(poke)); this.distractingStrong[p] = false; this.individualmsg[p] = "Send one of your Pokémon to help: " + pyrLink(this.shortcuts[p]) + (p == pyramidRef.leader ? " | You can instead run away with " + link("/pyr flee") + " at the cost of " + (8 + 5 * level) + " stamina!" : "") + " | You can " + link("/pyr distract", "Distract") + " to attempt to draw the Pokémon's attack, although it may fail if you've distracted recently and it will cause you to deal less damage."; } var difficulty = 1; if (chance((0.33 * level) - 0.4)) { difficulty += 1; } if (chance((0.5 * level) - 2.75)) { difficulty += 1; } var diffMsg = "Basic"; var opponents = [149, 248, 289, 373, 376, 445, 571, 635, 681, 706, 773, 784].concat(legendaries); var potentialForms = [getForm(38), getForm(103), getForm(53), 863, getForm(110), getForm(76), getForm(89), getForm(26), getForm(105), getForm(20), getForm(51), 862, 863, 864, 865, 866, 867, 66256, 66184, 66028, 66336, 131872, 197408, 393695]; if (difficulty == 2) { if (chance(0.5)) { opponents = opponents.concat(megaPokemon); diffMsg = "Basic or Mega"; } else { opponents = opponents.concat(potentialForms); diffMsg = "Basic or Form"; } } else if (difficulty == 3) { opponents = opponents.concat(potentialForms).concat(megaPokemon); diffMsg = "All-inclusive"; } this.opponentList = opponents; this.opponent = this.opponentList.random(); this.opponentHP = 420 + 180 * level; this.opponentPower = 12 + Math.floor(4.2 * level); this.isRevealed = false; if ([151, 571].contains(this.opponent)) { this.disguise = this.opponentList.random(); if (this.disguise === this.opponent) { this.disguise = null; } } var stats = ["HP", "Attack", "Defense", "Special Attack", "Special Defense", "Speed"]; var usedStats = stats.concat().shuffle().slice(0, 2); var pokeStats = getStats(this.opponent); var s, val, hints = [], lower, upper, steps; for (p = 0; p < usedStats.length; p++) { s = usedStats[p]; val = pokeStats[stats.indexOf(s)]; steps = sys.rand(0, 2); lower = val - steps*5; upper = val + (2 - steps)*5; if (lower % 5 !== 0) { lower -= lower % 5; } if (upper % 5 !== 0) { upper += 5-(upper % 5); } if (upper-lower > 10) { if (upper-val > val-lower) { upper -= 5; } else { lower += 5; } } hints.push(s + " is between " + lower + "~" + upper); } this.treasures = { egg: { chance: (1 + (1 * level)), item: "egg", amount: 1 }, bignugget: { chance: (-0.5 + (1.3 * level)), item: "bignugget", amount: 1 }, sunshard: { chance: (-0.3 + (1.2 * level)), item: "sunshard", amount: 1 }, moonshard: { chance: (-0.3 + (1.2 * level)), item: "moonshard", amount: 1 }, bait: { chance: 16, item: "bait", amount: 5 * level }, dust: { chance: 14, item: "dust", amount: 14 * level }, grnapricorn: { chance: 21 - level, item: "grnapricorn", amount: 2 * level }, redapricorn: { chance: 19 - level, item: "redapricorn", amount: Math.floor((5 + (1.5 * level))) }, pnkapricorn: { chance: 20 - level, item: "pnkapricorn", amount: 2 * level }, bigpearl: { chance: 7, item: "bigpearl", amount: level } }; this.sendAll(""); this.sendAll("Pokémon: " + pokeInfo.sprite(0)); this.sendAll("Room " + level + "-" + roomNum + ": A strong Pokémon (" + diffMsg + ") stands in your way, but it's too dark to identify them (" + readable(hints) + ")! Defeat them, unless the leader decided to flee!"); this.sendIndividuals(); this.sendAll(""); } StrongRoom.prototype = new PyramidRoom(); StrongRoom.prototype.midturn = function() { this.sendToAlive("Choose a Pokémon to defeat the {0}!".format(this.isRevealed ? poke(this.revealAgain ? this.revealAgain : this.opponent) : "hidden Pokémon")); this.sendIndividuals(); }; StrongRoom.prototype.validInput = function(id, commandData) { return (["run", "runaway", "run away", "flee"].contains(commandData) && id === this.pyr.leader) || this.pokeInParty(id, commandData) || (commandData == "distract"); }; StrongRoom.prototype.postInput = function(src, commandData) { if (sys.name(src).toLowerCase() === this.pyr.leader && ["run", "runaway", "run away", "flee"].contains(commandData)) { this.advance(); return true; } }; StrongRoom.prototype.advance = function() { var m, p, res, stamina = {}, opp = this.opponent, defeated = false, choices = this.getChoices(), attackerNames = Object.keys(choices), name = "", avi = null, playerBonus = null; this.sendAll(""); if (["run", "runaway", "run away", "flee"].contains(choices[this.pyr.leader])) { var pointsLost = 8 + 5 * this.level; this.sendAll(toColor("Party leader {0} decided to run away from the {1}! {0} lost {2} Stamina!".format(this.pyr.leader.toCorrectCase(), (this.isRevealed ? poke(opp) : "hidden Pokémon"), pointsLost), "crimson")); stamina[this.pyr.leader] = -pointsLost; this.pyr.updateStatus(0, stamina); this.sendAll(""); this.turnToAdvance = 0; this.earlyFinish = true; this.passed = true; return; } this.choices = {}; for (p = 0; p < attackerNames.length; p++) { m = choices[attackerNames[p]]; name = attackerNames[p]; if (name && typeof name == "string") { avi = getAvatarOff(name); } else { name = null; avi = null; } if ((type2(m) === "???") && avi && (avi.costume === "flower")) { playerBonus = [35 + 15 * this.level, 100 + 20 * this.level]; } else { playerBonus = [15 + 15 * this.level, 80 + 20 * this.level]; } if (pyrBonusMons.contains(m % 65536)) { playerBonus[0] *= 1.25; playerBonus[1] *= 1.25; } if (this.distractingStrong[attackerNames[i]]) { playerBonus[0] *= 0.75; playerBonus[1] *= 0.75; } res = calcDamage(m, opp, playerBonus, false, false, getCherished(m, name)); if (this.opponentHP > 0) { this.sendAll("{0}'s {1} dealt {2} damage to the {3}!".format(attackerNames[p].toCorrectCase(), poke(m), toColor(res.power[0], "blue"), (this.isRevealed ? poke(opp) : "hidden Pokémon"))); if (res.power[0] > 0) { this.opponentHP -= res.power[0]; if (this.opponentHP <= 0) { this.opponentHP = 0; defeated = true; if (this.attacks === 0 && chance(0.3 + 0.5 * this.level)) { var reward = randomSampleObj(this.treasures); this.sendAll("{0} picked something dropped by the {1}! {0} received {2}!".format(addFlashTag(attackerNames[p].toCorrectCase()), (this.isRevealed ? poke(opp) : "hidden Pokémon"), toColor(treasureName(reward), "blue")), true); if (name && typeof name == "string") { getTreasure(name, reward); } } break; } } } } this.attacks++; var display = this.disguise && !defeated ? this.disguise : opp; if (!this.isRevealed || this.revealAgain) { this.sendAll("The " + (this.isRevealed ? poke(this.revealAgain) : "hidden Pokémon") + " is revealed to be {0}! {1}".format(poke(display), pokeInfo.sprite(display))); this.revealAgain = null; if (this.disguise) { this.revealAgain = this.disguise; } this.disguise = null; this.isRevealed = true; } if (defeated) { var pointsRange = [50 + 35 * this.level, 30 + 18 * this.level, 5 + 5 * this.level, 1 + 2 * this.level, -12 - 8 * this.level]; var points = pointsRange[Math.min(this.attacks-1, pointsRange.length-1)]; this.sendAll("{0}'s HP dropped to 0! The {0} has fainted! Points gained: {1}".format(poke(opp), plural(points, "Point"))); this.pyr.updateStatus(points, stamina); this.passed = true; for (var x in this.distractedStrong) { if (this.pyr.distractedStrong[x] > 0) { this.pyr.distractedStrong[x] = Math.max(this.pyr.distractedStrong[x] - 7, 0); } } } else { var targetable = {}; for (var i = 0; i < attackerNames.length; i++) { targetable[attackerNames[i]] = 6; if (this.distractingStrong[attackerNames[i]]) { var successRate = Math.max(6, 42 - this.pyr.distractedStrong[attackerNames[i]]); targetable[attackerNames[i]] = successRate; this.pyr.distractedStrong[attackerNames[i]] += 23; } } var target = randomSample(targetable); m = choices[target]; var dmg = Math.round(this.opponentPower * safari.checkEffective([type1(opp), type2(opp)], [type1(m), type2(m)])); stamina[target] = -(dmg); this.sendAll("{0}'s HP is now at {1}! The {0} attacks {2}'s {3}! {2} loses {4} stamina!".format(poke(display), toColor(this.opponentHP, "blue"), target.toCorrectCase(), poke(m), toColor(dmg, "red"))); this.sendAll(""); this.pyr.updateStatus(0, stamina); this.sendAll("You may try to attack them again or run away!"); this.sendIndividuals(); this.turnToAdvance += 2; } this.sendAll(""); }; function BlockedRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.level = level; this.attacks = 0; var parties = pyramidRef.parties; for (var p in parties) { this.shortcuts[p] = toShortcut(parties[p].map(poke)); this.individualmsg[p] = "Send one of your Pokémon to help: " + pyrLink(this.shortcuts[p]); } this.types = []; var allTypes = Object.keys(effectiveness), t; while (this.types.length < 3) { t = allTypes.random(); if (!this.types.contains(t)) { this.types.push(t); } } this.revealedTypes = [this.types[0]]; this.hp = 1080 + 250 * level; this.maxhp = this.hp; this.dealt = {}; this.treasureGoal = Math.round(this.hp * (1.45 - this.level * 0.07)); this.treasures = { pack: { chance: (0.5 + (1.1 * level)), item: "pack", amount: 1 }, spray: { chance: (0.5 + (1.3 * level)), item: "spray", amount: 1 }, starpiece: { chance: 3 + level, item: "starpiece", amount: 1 }, silver: { chance: 20, item: "silver", amount: (2 + level) }, redapricorn: { chance: 18 - level, item: "redapricorn", amount: Math.floor(3 + level/2) }, blkapricorn: { chance: 17 - level, item: "blkapricorn", amount: 1 * level }, bluapricorn: { chance: 17 - level, item: "bluapricorn", amount: 1 * level }, rock: { chance: 21 - level, item: "rock", amount: 10 * level }, stardust: { chance: 8, item: "stardust", amount: 1 * level } }; this.sendAll(""); this.sendAll("Room " + level + "-" + roomNum + ": A weird statue is blocking the exit. The statue seems to have 3 types, but only one is visible: " + typeIcon(this.types[0]) + ". Destroy the statue to proceed!"); this.sendIndividuals(); this.sendAll(""); } BlockedRoom.prototype = new PyramidRoom(); BlockedRoom.prototype.midturn = function() { var known = this.revealedTypes.concat().map(function(e) { return typeIcon(e) }); while (known.length < this.types.length) { known.push("???"); } this.sendToAlive("Choose a Pokémon to break the {0}-type statue!".format(known.join(" / "))); this.sendIndividuals(); }; BlockedRoom.prototype.validInput = function(id, commandData) { return this.pokeInParty(id, commandData); }; BlockedRoom.prototype.advance = function() { var m, p, dmg, pow, stamina = {}, turnDealt = {}, turnDamage = 0, avi, defeated = false, choices = this.getChoices(), attackerNames = Object.keys(choices); this.sendAll(""); for (p = 0; p < attackerNames.length; p++) { m = choices[attackerNames[p]]; avi = getAvatarOff(attackerNames[p]); if ((sys.type(type2(m)) === "???") && (avi.costume === "flower")) { pow = sys.rand(150 + this.level * 15, 260 + this.level * 37); } else { pow = sys.rand(130 + this.level * 15, 240 + this.level * 37); } if (pyrBonusMons.contains(m % 65536)) { pow *= 1.25; } dmg = Math.round(pow * safari.checkEffective([type1(m), type2(m)], [this.types[0], this.types[1], this.types[2]])); if (!this.dealt.hasOwnProperty(p)) { this.dealt[p] = 0; } turnDealt[p] = dmg; this.sendAll("{0}'s {1} dealt {2} damage to the statue!".format(attackerNames[p].toCorrectCase(), poke(m), toColor(dmg, "blue"))); if (dmg > 0) { this.hp -= dmg; turnDamage += dmg; this.dealt[p] += dmg; if (this.hp <= 0) { this.hp = 0; defeated = true; break; } } } this.choices = {}; this.attacks++; if (defeated) { var totalDealt = 0, bestDmg = 0, bestAttacker; for (p in this.dealt) { dmg = this.dealt[p]; totalDealt += dmg; if (dmg > bestDmg) { bestDmg = dmg; bestAttacker = attackerNames[p]; } } var pointsRange = [52 + 32 * this.level, 32 + 20 * this.level, 15 + 6 * this.level, 3 + 2 * this.level, -3 - 5 * this.level, -7 - 9 * this.level]; var points = pointsRange[Math.min(this.attacks-1, pointsRange.length-1)]; this.sendAll("The {0}-type statue's HP dropped to 0! The statue was destroyed! Points gained: {1}".format(this.types.map(function(e) { return typeIcon(e) }).join(" / "), plural(points, "Point"))); if (totalDealt >= this.treasureGoal && bestAttacker) { var reward = randomSampleObj(this.treasures); this.sendAll("{0} found something stuck to a fragment of the statue! {0} received {1}!".format(addFlashTag(bestAttacker.toCorrectCase()), toColor(treasureName(reward), "blue")), true); getTreasure(bestAttacker, reward); } this.pyr.updateStatus(points, stamina); this.passed = true; } else { var rt = this.revealedTypes.length; if ((rt < this.types.length) && (rt < 2)) { this.revealedTypes.push(this.types[rt]); this.sendAll("The statue's {0} type is revealed to be {1}! You now know the statue is {2}-type!".format(getOrdinal(rt+1), typeIcon(this.types[rt]), toColor(this.revealedTypes.map(function(e) { return typeIcon(e) }).join(" / "), "blue"))); } var staminaStr = [], members = this.pyr.names, id; var averageDamage = Math.floor(11 + (1.5 * this.level)); for (p in members) { id = members[p]; if (this.pyr.stamina[id] <= 0) { continue; } stamina[id] = (-1 * averageDamage); staminaStr.push(id.toCorrectCase() + " -" + averageDamage); } this.sendAll("The statue's HP is now at {0}! Stamina lost: {1}".format(toColor(this.hp, "blue"), staminaStr.join(", "))); this.sendAll(""); this.pyr.updateStatus(0, stamina); this.sendAll("Keep attacking the statue until you destroy it!"); this.sendIndividuals(); this.turnToAdvance += 2; } this.sendAll(""); }; function RiddleRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.level = level; this.defaultChoice = "none"; this.answerType = chance(0.36 + level * 0.04) ? "move" : "name"; this.answerAttempts = 0; this.cluesSearched = {}; this.turns = 9 - Math.floor(level/2) + (this.answerType === "move" ? 1 : 0); var hints; if (this.answerType === "name") { this.answerId = sys.rand(1, highestDexNum); this.answer = poke(this.answerId); hints = this.writeHints(level); } else if (this.answerType === "move") { do { this.answerId = sys.rand(1, 702); } while (getMoveBP(this.answerId) === "---" && pokedex.getMoveAccuracy(this.answerId) === "---"); this.answer = moveOff(this.answerId); hints = this.writeHintsMove(level); } var objects = ["crate", "barrel", "rock", "box", "wall", "corner", "floor", "ceiling", "puddle", "coffin", "chains", "pillar", "vase", "statue", "chest", "table", "sword", "torch", "chair"].shuffle(); this.hintsLocation = {}; for (var h = 0; h < hints.length; h++){ this.hintsLocation[objects.shift()] = hints[h]; } var treasuresAmt = 0; switch (hints.length) { case 8: case 9: case 10: treasuresAmt = 3; break; case 6: case 11: case 12: treasuresAmt = 2; break; case 7: case 13: treasuresAmt = 1; break; default: treasuresAmt = 0; } var rew = { gem: { chance: 2 * level, item: "gem", amount: 1 }, nugget: { chance: 1 * level, item: "nugget", amount: 1 }, money: { chance: 14, item: "money", amount: 180 * level }, safari: { chance: 19, item: "safari", amount: 5 * level }, redapricorn: { chance: 13, item: "redapricorn", amount: 1 * level }, grnapricorn: { chance: 14, item: "grnapricorn", amount: 1 * level }, pearl: { chance: 9, item: "pearl", amount: 1 * level } }; while (treasuresAmt > 0) { this.hintsLocation[objects.shift()] = randomSampleObj(rew); treasuresAmt--; } this.validObjects = Object.keys(this.hintsLocation).shuffle(); this.hintsFound = []; this.leaderChoice = null; this.sendAll(""); this.sendAll("Room {0}-{1}: The door requires a Pokémon {2} as a password to open! Search for clues in the room so the leader can input the password!!".format(level, roomNum, toColor(cap(this.answerType), "blue"))); this.sendIndividuals(); this.sendAll(""); } RiddleRoom.prototype = new PyramidRoom(); RiddleRoom.prototype.writeHints = function(level) { var hints = [], h; hints.push("Starts with '{0}'".format(this.answer[0])); if (level <= 4) { hints.push("Ends with '{0}'".format(cap(this.answer[this.answer.length-1]))); } hints.push("One of the types is '{0}'".format(type1(this.answerId))); h = type2(this.answerId); if (h !== "???" && level <= 6) { hints.push("One of the types is '{0}'".format(h)); } hints.push("Can have the ability '{0}'".format(abilityOff(getPokeAbility(this.answerId, 0)))); h = getPokeAbility(this.answerId, 1); if (h) { hints.push("Can have the ability '{0}'".format(abilityOff(h))); } h = getPokeAbility(this.answerId, 2); if (h) { hints.push("Can have the ability '{0}'".format(abilityOff(h))); } var moveHintAmount = level <= 2 ? 3 : 4; var moves; var movesHit = []; var j, l = [], l2 = [], l3 = []; for (var k = moveHintAmount; k--;) { j = 0; while (j < 40) { j++; moves = fetchMoves(this.answerId).shuffle().slice(0, 2); if (movesHit.contains(moves[0]) || movesHit.contains(moves[1])) { continue; } l = lookupMoveLearners(moves[0]); l2 = lookupMoveLearners(moves[1]); if (l.length > 300 || l.length < 10) { continue; } if (l2.length > 300 || l2.length < 10) { continue; } var l3 = removeNonDuplicates(l.concat(l2)); if (l3.length > 95 || l3.length < 3) { continue; } break; } movesHit.push(moves[0]); movesHit.push(moves[1]); hints.push("Can have the moves '{0} and {1}'".format(moveOff(moves[0]), moveOff(moves[1]))); } h = generation(this.answerId, true); hints.push("Is from the " + h + " region"); h = getBST(this.answerId); hints.push("BST is between {0} and {1}".format(h - sys.rand(0, 45), h + sys.rand(1, 45))); if (this.answerId in evolutions) { var evolvePoke = evolutions[this.answerId].evo; evolvePoke = Array.isArray(evolvePoke) ? evolvePoke : [evolvePoke]; var hit = false, hold; for (var i = 0; i < evolvePoke.length; i++) { hold = evolvePoke[i]; if (type1(hold) == type1(this.answerId) && type2(hold) == type2(this.answerId)) { continue; } hit = true; } if (hit) { hints.push("Can evolve into a Pokémon with a different type combination"); } else { hints.push("Can evolve"); } } else { hints.push("Can't evolve"); } if (this.answerId in devolutions) { hints.push("Is evolved"); } else { hints.push("Is not evolved"); } return hints.shuffle(); }; RiddleRoom.prototype.writeHintsMove = function(level) { var hints = [], h; hints.push("Starts with '{0}'".format(this.answer[0])); if (level <= 4) { hints.push("Ends with '{0}'".format(cap(this.answer[this.answer.length-1]))); } hints.push("Type is '{0}'".format(sys.type(moveType(this.answerId)))); h = getMoveBP(this.answerId); if (h !== "---") { h = parseInt(h, 10); hints.push("Power is between {0} and {1}".format(h - sys.rand(0, 10), h + sys.rand(1, 10))); } h = pokedex.getMovePP(this.answerId); hints.push("Base PP is {0}".format(h)); h = pokedex.getMoveAccuracy(this.answerId); if (h !== "---") { hints.push("Accuracy is {0}".format(h)); } h = pokedex.getMoveCategory(this.answerId); hints.push("Category is {0}".format(h)); h = pokedex.getMoveContact(this.answerId); hints.push(h ? "Makes contact" : "Doesn't make contact"); var cropHint = function(hint) { var c = hint.length, i; i = hint.indexOf(". "); if (i >= 0 && i < c) { c = i; } if (c > 150) { i = h.indexOf(", ", 100); if (i >= 0 && i < c) { c = i; } } if (c >= hint.length) { c = hint.length; } hint = hint.substring(0, c).trim(); c = hint.length-1; if (hint[c] === "." || hint[c] === ",") { hint = hint.substr(0, hint.length-1); } return hint; }; h = pokedex.getMoveEffect(this.answerId); var res = cropHint(h); hints.push("Description contains \"{0}\"".format(res)); res = h.substr(res.length + 2); if (res.length > 24 && level <= 6) { res = cropHint(res); hints.push("Description contains \"{0}\"".format(res)); } var canLearn = []; var cantLearn = []; for (var i = 1; i < highestDexNum; i++) { if (canLearnMove(i, this.answerId)) { if (i !== 235) { canLearn.push(pokePlain(i)); } } else { cantLearn.push(pokePlain(i)); } } if (canLearn.length > 0) { canLearn = canLearn.shuffle(); hints.push("Can be learned by {0}".format(readable(canLearn.slice(0, 4)))); if (canLearn.length > 4 && level > 2) { hints.push("Can be learned by {0}".format(readable(canLearn.slice(4, 8)))); } } if (cantLearn.length > 0) { hints.push("Can NOT be learned by {0}".format(readable(cantLearn.shuffle().slice(0, 3)))); } h = this.answer.replace(/[\s\-']/g, "").length; i = (this.answer.match(/[\s\-]/g) || []).length + 1; hints.push("Name has {0} letters and {1}".format(h, plural(i, "word"))); return hints.shuffle(); }; RiddleRoom.prototype.midturn = function() { this.sendToAlive("Find the password (Pokémon " + this.answerType + ") to open the door! " + (this.hintsFound.length > 0 ? "Hints found: " + this.hintsFound.map(function(x){ return toColor(x, "blue");}).join(", ") : "")); this.sendIndividuals(); }; RiddleRoom.prototype.sendIndividuals = function() { var n, id; if (this.validObjects.length > 0) { var shortcuts = toShortcut(this.validObjects); var shortcutLinks = []; for (n in shortcuts) { shortcutLinks.push("[" + n.toUpperCase() + "] " + link("/pyr " + cap(shortcuts[n]))); } shortcutLinks = shortcutLinks.join(", "); for (n = this.pyr.names.length; n-- ;) { id = this.pyr.names[n]; if (this.pyr.stamina[id] > 0) { this.shortcuts[id] = shortcuts; this.send(id, "Look for clues at: " + shortcutLinks + (id === this.pyr.leader ? " | Or input the password with " + link("/pyr Answer", null, true) : "")); } } } else { this.send(this.pyr.leader, "Input the password with " + link("/pyr [Answer]", null, true)); } }; RiddleRoom.prototype.useCommand = function(src, commandData) { var player = getAvatar(src), info = getInputPokemon(commandData); if (commandData.length === 1) { if (this.shortcuts.hasOwnProperty(player.id) && this.shortcuts[player.id].hasOwnProperty(commandData.toLowerCase())) { commandData = this.shortcuts[player.id][commandData.toLowerCase()]; } } if (player.id === this.pyr.leader) { if (this.passed) { return; } if (this.answerType === "name" && info.num !== null && !info.shiny) { this.checkAnswer(commandData, info.num); return; } if (this.answerType === "move" && movenum(commandData)) { this.checkAnswer(moveOff(movenum(commandData)), movenum(commandData)); return; } } if (this.validInput && !this.validInput(player.id, commandData)) { return; } this.choices[player.id] = commandData; if (this.postInput && this.postInput(src, commandData)) { return; } this.sendAll(toColor("{0} is going to use {1}!".format(player.id.toCorrectCase(), commandData), "crimson")); }; RiddleRoom.prototype.validInput = function(id, commandData) { if (!this.validObjects.contains(commandData.toLowerCase())) { this.send(id, "There's no {0} to look for clues at!".format(commandData)); return false; } for (var e in this.choices) { if (this.choices[e].toLowerCase() === commandData.toLowerCase()) { if (e === id) { this.send(id, "You are already going to look for clues at the {1}!".format(e.toCorrectCase(), commandData)); } else { this.send(id, "{0} is already going to look for clues at the {1}! Look somewhere else!".format(e.toCorrectCase(), commandData)); } return false; } } return true; }; RiddleRoom.prototype.postInput = function(src, commandData) { this.sendAll(toColor("{0} is going to look for a clue at the {1}!".format(sys.name(src), cap(commandData.toLowerCase())), "crimson")); return true; }; RiddleRoom.prototype.checkAnswer = function(commandData, id) { var stamina = {}, name = this.pyr.leader.toCorrectCase(); this.sendAll(""); if (id == this.answerId) { var extraPoints = 12 + 8 * this.level, hintsUsed = this.hintsFound.length; if (hintsUsed > 10) { extraPoints = Math.round(-extraPoints * 0.115 * (hintsUsed - 10)); } else if (hintsUsed > 3) { extraPoints = Math.round(extraPoints - (extraPoints * (hintsUsed - 3) / 4)); } var basePointsRange = [42 + 27 * this.level, 18 + 12 * this.level, 6 + 6 * this.level, 1 + 2 * this.level]; var points = basePointsRange[Math.min(this.answerAttempts, basePointsRange.length-1)] + extraPoints; this.sendAll("{0} answered {1} and the door opened! Points gained: {2}".format(name, this.answer, plural(points, "Point"))); this.pyr.updateStatus(points, {}); this.passed = true; this.earlyFinish = true; this.turnToAdvance = 0; } else { this.answerAttempts++; var stmLost = 5 + 5 * this.level; if (this.answerAttempts > 1) { stamina[this.pyr.leader] = -stmLost; } this.sendAll("{0} answered {1}, but nothing happened! {2}".format(name, commandData, (this.answerAttempts > 1 ? "Stamina lost: " + name + "-" + stmLost : "") )); this.pyr.updateStatus(0, stamina); if (!this.pyr.hasStamina()) { this.pyr.finishMode = "stamina"; this.pyr.finish(); return; } } this.sendAll(""); }; RiddleRoom.prototype.advance = function() { var p, stamina = {}, place, res, staminaStr = [], isTreasure, choices = this.getChoices(), locationsSearched = {}; this.sendAll(""); for (p in choices) { place = choices[p].toLowerCase(); if (place !== "none") { if (!this.cluesSearched.hasOwnProperty(p)) { this.cluesSearched[p] = 0; } res = this.hintsLocation[place]; isTreasure = typeof res === "object"; if (!isTreasure) { this.cluesSearched[p]++; if (this.cluesSearched[p] > 1) { stamina[p] = (Math.round((this.cluesSearched[p] * -1.5) - (this.answerType === "move" ? 1.5 : 2) * (this.level + 2))); } } locationsSearched[place] = true; if (isTreasure) { this.sendAll("{0} investigated the {1} and found {2}!".format(addFlashTag(p.toCorrectCase()), cap(place), toColor(treasureName(res), "blue")), true); getTreasure(p, res); } else { this.sendAll("{0} investigated the {1} and found a hint: \"{2}\"".format(p.toCorrectCase(), cap(place), toColor(res, "blue"))); if (!this.hintsFound.contains(res)) { this.hintsFound.push(res); } } } } this.choices = {}; for (p in locationsSearched) { this.validObjects.splice(this.validObjects.indexOf(p), 1); } this.turns--; if (this.turns > 0) { for (p in stamina) { staminaStr.push(p.toCorrectCase() + " " + stamina[p]); } if (staminaStr.length > 0) { this.sendAll("The search for the password is becoming tiresome! Stamina lost: {0}".format(staminaStr.join(", "))); } this.pyr.updateStatus(0, stamina); this.sendAll(""); this.sendAll("Look for more clues or input the password to open the door! The password is a Pokémon " + this.answerType + "! You only have {0} left!".format(plural(this.turns, "turn"))); this.sendIndividuals(); this.sendAll(""); this.turnToAdvance += 2; } else { for (p in this.pyr.stamina) { if (this.pyr.stamina[p] > 0) { if (!stamina.hasOwnProperty(p)) { stamina[p] = 0; } stamina[p] -= 7 + 5 * this.level; staminaStr.push(p.toCorrectCase() + " " + stamina[p]); } } var points = -10 - this.level * 8; this.sendAll("As the door opened by itself, a voice so loud that it hurts your ears could be heard: \"YOU ARE TERRIBLE AT RIDDLES!!\" The answer was " + this.answer + "! | Points: {0} | Stamina lost: {1}".format(points, staminaStr.join(", "))); this.pyr.updateStatus(points, stamina); this.sendAll(""); this.passed = true; } }; function TrainerRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.trainerName = "Trainer " + generateName(); this.defaultChoice = pyramidRef.leader; this.midmsg = "Choose a player to battle against {0}!".format(this.trainerName); this.horde = []; this.battler = this.defaultChoice; this.pickedByLeader = false; this.level = level; this.inverted = chance(0.35 + 0.05 * this.level); var parties = pyramidRef.parties, c = -1; var options = Object.keys(parties).map(function(x) { c++; return "[" + ["A", "B", "C"][c] + "] " + link("/pyr " + x.toCorrectCase()); }); for (var p in parties) { if (p == pyramidRef.leader) { this.shortcuts[p] = toShortcut(Object.keys(parties)); this.individualmsg[p] = "Choose who will battle by typing " + readable(options, "or") + "!"; } else { this.individualmsg[p] = "Nominate yourself to battle by typing " + link("/pyr me") + "!"; } } this.trainerPower = [12 + level * 8, 90 + level * 16]; this.trainerTeam = []; var num, bst = 285 + 25 * level, maxLegend = Math.floor((level-1)/3) + 1; var main = this.inverted ? Object.keys(effectiveness).slice(2).random() : Object.keys(effectiveness).slice(1).random(); var m = 6, wildcard = 0; while (this.trainerTeam.length < m - 1) { num = sys.rand(1, highestDexNum); if (getBST(num) >= bst && hasType(num, main)) { if (isLegendary(num)) { if (maxLegend <= 0) { continue; } maxLegend--; } this.trainerTeam.push(num); } } while (this.trainerTeam.length < m) { num = sys.rand(1, highestDexNum); if (getBST(num) >= bst) { if (isLegendary(num)) { if (maxLegend <= 0) { continue; } maxLegend--; } wildcard = num; this.trainerTeam.push(num); } } this.trainerTeam = this.trainerTeam.shuffle().slice(0, 3); this.treasures = { rare: { chance: 1 * level, item: "rare", amount: 1 }, spray: { chance: 1 + level, item: "spray", amount: 1 }, mega: { chance: (-0.1 + (0.05 * level)), item: "mega", amount: 1 }, money: { chance: 3, item: "money", amount: 240 * level }, money2: { chance: 12, item: "money", amount: 150 * level }, silver: { chance: 7, item: "silver", amount: (1 + 1 * level) }, gacha: { chance: 12, item: "gacha", amount: 3 * level }, great: { chance: 10, item: "great", amount: 3 * level }, redapricorn: { chance: 9, item: "redapricorn", amount: 2 + level }, whtapricorn: { chance: 11, item: "whtapricorn", amount: 1 * level }, grnapricorn: { chance: 11, item: "grnapricorn", amount: 1 * level }, premier: { chance: 8, item: "premier", amount: 2 * level } }; this.sendAll(""); this.sendAll("Room {0}-{1}: {2}, {4}-type user, challenges you to a 3v3 {3}battle! You can't refuse!".format(level, roomNum, this.trainerName, this.inverted ? "Inverted " : "", typeIcon(main))); this.sendAll("{0} has the following wildcard: {1}!".format(this.trainerName, poke(wildcard))); this.sendIndividuals(); this.sendAll(""); } TrainerRoom.prototype = new PyramidRoom(); TrainerRoom.prototype.validInput = function(id, commandData) { var target = commandData.toLowerCase(); if (this.pyr.leader === id) { if (this.pyr.parties.hasOwnProperty(target) && this.pyr.stamina[target] > 0) { return true; } else { this.send(id, "You cannot choose this person to the battle!"); return false; } } else { if (target === "me") { if (!this.pickedByLeader) { return true; } else { this.send(id, "The leader has already chosen who will battle!"); } } else { this.send(id, "To nominate yourself, use " + link("/pyr me") + "!"); } } return false; }; TrainerRoom.prototype.postInput = function(src, commandData) { if (sys.name(src).toLowerCase() === this.pyr.leader) { this.battler = commandData.toLowerCase(); this.sendAll(toColor("{0} has decided that {1} will battle {2}!".format(sys.name(src), commandData.toCorrectCase(), this.trainerName), "crimson")); this.pickedByLeader = true; } else { this.battler = sys.name(src).toLowerCase(); this.sendAll(toColor("{0} has nominated themselves to battle {1}!".format(sys.name(src), this.trainerName), "blue")); } return true; }; TrainerRoom.prototype.advance = function() { var id = this.battler, name = id.toCorrectCase(), m, p, opp, res, points = 0, stamina = {}; var party = this.pyr.parties[id].concat().shuffle(); var isFlowerGirl = (getAvatarOff(id).costume === "flower" ? true : false); var playerBonus = null; stamina[id] = 0; this.sendAll(""); var score = 0, oppScore = 0, result, status, n1, n2; for (p = 0; p < 3; p++) { m = party[p]; opp = this.trainerTeam[p]; if ((type2(m) === "???") && (isFlowerGirl)) { playerBonus = [30, 120]; } else { playerBonus = [10, 100]; } if (pyrBonusMons.contains(m % 65536)) { playerBonus[0] += 35; playerBonus[1] += 35; } res = calcDamage(m, opp, playerBonus, this.trainerPower, this.inverted, getCherished(m, id)); result = "{0} {2} ({4}) x ({5}) {3} {1}"; if (res.power[0] == res.power[1]) { result += " Draw"; } status = "Details ({0}/Power/Type) | {1} ({3}/{5}/{7}x) x {2} ({4}/{6}/{8}x)"; n1 = name + "'s " + poke(m); n1 = res.power[0] > res.power[1] ? "" + n1 + "" : n1; n2 = this.trainerName + "'s " + poke(opp); n2 = res.power[0] < res.power[1] ? "" + n2 + "" : n2; this.sendAll(result.format(n1, n2, pokeInfo.icon(m), pokeInfo.icon(opp), res.power[0], res.power[1])); this.sendAll(toColor(status.format(res.statName, poke(m), poke(opp), res.stat[0], res.stat[1], res.move[0], res.move[1], res.bonusString[0], res.bonusString[1]), "gray")); if (res.power[0] > res.power[1]) { score++; } else { if (res.power[0] < res.power[1]) { oppScore++; } stamina[id] -= (5 + this.level); } } if (score > oppScore) { this.sendAll("{3} defeated {0}! Final score {1} x {2}".format(this.trainerName, "" + score + "", "" + oppScore + "", name)); if (score - oppScore >= 3) { var reward = randomSampleObj(this.treasures); this.sendAll(""); this.sendAll("{0} was really impressed by {1}'s battling skills! {0} gives {2} to {1}!".format(this.trainerName, addFlashTag(name), toColor(treasureName(reward), "blue")), true); getTreasure(id, reward); } } else { stamina[id] -= Math.floor(3.5 + (this.level * 1.25)); this.sendAll("{3} couldn't beat {0}! Final score {2} x {1}".format(this.trainerName, "" + score + "", "" + oppScore + "", name)); } if (oppScore > 1) { stamina[id] -= 15 + (5 * this.level); } points = [-10 - 6 * this.level, 0, 28 + 18 * this.level, 56 + 37 * this.level][score]; if (stamina[id] === 0) { delete stamina[id]; } this.pyr.updateStatus(points, stamina, true); this.sendAll(""); this.passed = true; }; function DefenseRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.horde = []; this.level = level; var parties = pyramidRef.parties; for (var p in parties) { this.shortcuts[p] = toShortcut(parties[p].map(poke)); this.individualmsg[p] = "Send one of your Pokémon to defend yourself: " + pyrLink(this.shortcuts[p]); } do { this.opponent = sys.rand(1, highestDexNum); } while ([10, 11, 13, 14, 129, 132, 201, 202, 265, 266, 268, 360, 374, 401, 412, 602, 664, 665, 771, 789, 790].contains(this.opponent)); var moves = fetchMoves(this.opponent), damaging = [], m; damaging = moves.filter(function(x) {return getMoveBP(x) !== "---"}); damaging = damaging.shuffle(); var damagingOriginal = [].concat(damaging); //sys.sendMessage(sys.id("Ripper Roo"), "opponent: {0}, moves: [{1}], damaging: [{2}]".format(this.opponent, moves, damaging), staffchannel); var type, count = 0, l1 = [], l2 = [], existingPool = [], newAttack, found = false, atks = [], lastLength = 900; var j = 4; while (j > 0) { while (!(found)) { if (damaging.length < 3) { damaging = [].concat(damagingOriginal); } newAttack = damaging.pop(); l1 = lookupMoveLearners(newAttack); if (existingPool.length < 1) { l2 = [].concat(l1); } else { l2 = removeNonDuplicates(existingPool.concat(l1)); } count++; //sys.sendMessage(sys.id("Ripper Roo"), "count: {0}, newAttack: {1}, l1: [{2}], l2: [{3}], existingPool: [{4}], atks: [{5}]".format(count, newAttack, l1, l2, existingPool, atks), staffchannel); if (count > (300 - (j * 50)) || (l2.length < 6 && j > 1)) { found = true; } if (j == 4 && l2.length < 190) { found = true; } if (j == 3 && l2.length < 75 && l2.length < lastLength - 15) { found = true; } if (j == 2 && l2.length < 20 && l2.length < lastLength - 10) { found = true; } if (j == 1 && l2.length < (2 * (count * 0.04)) && l2.length < lastLength - 3) { found = true; } if (found) { //sys.sendMessage(sys.id("Miki Sayaka"), "Found move " + newAttack + " after " + count + " loops with learning length " + l1.length + " (total " + l2.length + "). [" + readable(l1) + "]", staffchannel); //sys.sendMessage(sys.id("Ripper Roo"), "Found move " + newAttack + " after " + count + " loops with learning length " + l1.length + " (total " + l2.length + "). [" + readable(l1) + "]", staffchannel); break; } } lastLength = l2.length; if (existingPool.length == 0) { existingPool = existingPool.concat(l1); } else { existingPool = [].concat(l2); } atks.push(newAttack); j--; count = 0; if (l2.length < 6) { j = 0; } found = false; } atks = atks.map(function(x) {return toColor(moveOff(x), "blue")}); count = 0; do { this.thirdAtk = damagingOriginal.random(); type = sys.type(moveType(this.thirdAtk)); count++; } while (count < 2 + level * 2 && type === "Normal"); this.treasures = { rare: { chance: 1 * level, item: "rare", amount: 1 }, gem: { chance: (2 + (2 * level)), item: "gem", amount: (level > 4 ? 2 : 1) }, silver: { chance: 7 + (level), item: "silver", amount: (3 + 1 * level) }, bait: { chance: 13 - level, item: "bait", amount: 2 * level }, great: { chance: 13 - level, item: "great", amount: 2 * level }, blkapricorn: { chance: 13 - level, item: "blkapricorn", amount: 1 * level }, quick: { chance: 11, item: "quick", amount: 1 * level }, rock: { chance: 10 - level, item: "rock", amount: 10 * level }, stardust: { chance: 4, item: "stardust", amount: 1 * level } }; var typeChances = {"Normal":20,"Fighting":20,"Flying":11,"Poison":40,"Ground":20,"Rock":36,"Bug":36,"Ghost":3.5,"Steel":0,"Fire":10,"Water":7,"Grass":12,"Electric":9,"Psychic":45,"Ice":34,"Dragon":6,"Dark":7,"Fairy":0}; this.bonusTypes = [randomSample(typeChances)]; var type_2 = randomSample(typeChances); var j = 0; while (type_2 == this.bonusTypes[0]) { j++; if (j > 16) { break; } type_2 = randomSample(typeChances); } this.bonusTypes.push(type_2); var restoreAmt = {"Normal": 10, "Poison": 8, "Rock": 8, "Fighting": 5, "Fire": 3, "Water": 2, "Grass": 4, "Psychic": 10, "Dark": 3, "Ghost": 1.5, "Dragon": 2, "Electric": 3, "Bug": 7, "Ground": 4, "Ice": 10, "Flying": 3}; this.bonusTypesHeal = this.bonusTypes.map(function(x) {return Math.max(1, Math.floor((restoreAmt[x] * 0.2) * (4 + level)))}); this.bonusTypesObj = {}; for (var i = 0; i < this.bonusTypes.length; i++) { this.bonusTypesObj[this.bonusTypes[i]] = this.bonusTypesHeal[i]; } this.midmsg = "Choose a Pokémon to defend from the next attack (Defending with a Pokémon with the type" + (this.bonusTypes.length > 1 ? "s" : "") + " " + readable(this.bonusTypes.map(function(e) { return typeIcon(e) }), "or") + " will heal you and give bonus points!)!"; var self = this; this.sendAll(""); this.sendAll("Room {0}-{1}: As soon as you enter the room, you see a Pokémon in the shadows using {2}. They then look at you and prepare another attack!".format(level, roomNum, readable(atks))); this.sendAll("Defending with a Pokémon with the type" + (this.bonusTypes.length > 1 ? "s" : "") + " " + readable(this.bonusTypes.map(function(x) {return typeIcon(x) + " (" + self.bonusTypesObj[x] + ")"}), "or") + " will heal your Pokémon and give bonus points!"); this.sendIndividuals(); this.sendAll(""); } DefenseRoom.prototype = new PyramidRoom(); DefenseRoom.prototype.validInput = function(id, commandData) { return this.pokeInParty(id, commandData); }; DefenseRoom.prototype.advance = function() { var members, m, p, points = 0, stamina = {}, choices = {}, atk, dmg, totalDef = 1, defended = [], hit = [], eff, treasureTo, bonusPoints = 0, halfBonus = 0, bonusUsed, bonusMsg = []; choices = this.getChoices(); members = Object.keys(choices); this.sendAll(""); this.sendAll(toColor("{0} {1} used {2}!".format(pokeInfo.icon(this.opponent), poke(this.opponent), moveOff(this.thirdAtk)), "blue")); atk = sys.type(moveType(this.thirdAtk)); for (p in choices) { m = choices[p]; dmg = safari.checkEffective([atk], [type1(m), type2(m)]); totalDef *= dmg; if (dmg === 0) { eff = toColor("It has no effect!", "gray"); } else if (dmg < 1) { eff = toColor("It's not very effective... ({0}x)!".format(dmg), "green"); } else if (dmg === 1) { eff = toColor("A normal hit!", "blue"); } else if (dmg > 1) { eff = toColor("It's super-effective ({0}x)!".format(dmg), "red"); } this.sendAll("{0}'s {1} was hit by {2}'s {3}! {4}".format(p.toCorrectCase(), poke(m), poke(this.opponent), moveOff(this.thirdAtk), eff)); if (dmg < 1) { defended.push(p.toCorrectCase()); if (!treasureTo && dmg <= 0.25 && chance(0.2 + 0.5 * this.level)) { treasureTo = p; } } else { hit.push(p.toCorrectCase()); var amt = -(dmg * (3 + 6 * this.level)); if (pyrBonusMons.contains(m % 65536)) { amt = Math.ceil(amt * 0.8); } stamina[p] = amt; } if (dmg <= 1) { bonusUsed = []; var healamt = 0; for (var l = this.bonusTypes.length; l--; ) { if (hasType(m, this.bonusTypes[l])) { if (dmg === 1) { halfBonus++; } else { bonusPoints++; } bonusUsed.push(this.bonusTypes[l]); healamt = Math.max(healamt, this.bonusTypesObj[this.bonusTypes[l]]); } } if (bonusUsed.length > 0) { bonusMsg.push(p.toCorrectCase() + " defended with " + bonusUsed.map(function(e) { return typeIcon(e) }).join("/") + "-type Pokémon and healed +" + healamt + " stamina"); if (!(stamina[p])) { stamina[p] = 0; } stamina[p] += healamt; } } } if (defended.length >= members.length) { this.sendAll(toColor("The entire party managed to defend from the attack!", "blue")); points = Math.round((25 + 25 * this.level) * (members.length/3)); } else if (defended.length === 0) { this.sendAll(toColor("No one managed to defend from the attack!", "red")); points = Math.round((-7 - 5 * this.level) * (members.length/3)); } else { this.sendAll("{0} managed to defend from the attack, but {1} have been hit!".format(readable(defended, "and"), readable(hit, "and"))); if (members.length === 3) { points = defended.length > hit.length ? 10 + 10 * this.level : 3 + 3 * this.level; } else { points = 10 + 10 * this.level; } } if (bonusPoints > 0 || halfBonus > 0) { var pointsGained = bonusPoints * (8 + 5 * this.level) + Math.round(halfBonus * (8 + 5 * this.level) / 2); this.sendAll("Received {0} Bonus Points because {1}!".format(pointsGained, readable(bonusMsg))); points += pointsGained; } if (treasureTo) { var reward = randomSampleObj(this.treasures); this.sendAll("After defending itself, {0} found {1} at the place the {2} was!".format(addFlashTag(treasureTo.toCorrectCase()), toColor(treasureName(reward), "blue"), poke(this.opponent)), true); getTreasure(treasureTo, reward); } this.sendAll(""); this.pyr.updateStatus(points, stamina, true); this.sendAll(""); this.passed = true; }; function HazardRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.midmsg = "Choose which moves to use clear the obstacles!"; this.defaultChoice = "struggle"; this.level = level; this.movesChosen = {}; this.hazardMoves = { "plants":[163,13,77], "water":[57,181,593], "boulder":[276,477,529], "toxic":[432,54,318], "pit":[19,438,81], "ice":[498,257,503], "flame":[56,410,16], "electric":[50,324,435], "dark":[572,497,425], "barrier":[100,442,107], "sand":[599,722,239], "target":[512,636,129] }; this.hazardAbilities = { "plants": [180, 102], "water": [33], "boulder": [159,37], "toxic": [81,30], "pit": [26,226], "ice": [47,115], "flame": [18, 21, 85], "electric": [140,101], "dark": [35], "barrier": [151], "sand":[8,146], "target":[28,97] }; this.validMoves = []; for (var c in this.hazardMoves) { this.validMoves = this.validMoves.concat(this.hazardMoves[c]); } this.hazardNames = { "plants": "Plant", "water": "Water Stream", "boulder": "Boulder", "toxic": "Toxic Gas", "pit": "Pit", "ice": "Ice Pillar", "flame": "Flamethrower", "electric": "Mecha", "dark": "Darkness", "barrier": "Barrier", "sand": "Sandstorm", "target": "Flying Target", }; this.hazardPlural = { "plants": "Plants", "water": "Water Streams", "boulder": "Boulders", "toxic": "Toxic Gas", "pit": "Pits", "ice": "Ice Pillars", "flame": "Flamethrowers", "electric": "Mechas", "dark": "Darkness", "barrier": "Barriers", "sand": "Sandstorms", "target": "Flying Targets" }; var e, val, max = sys.rand(Math.floor(3.2 + (level * 1.35)), Math.floor(5.3 + (level * ((Math.random() * 0.4) + 1.5)))), maxsize = max, order = Object.keys(this.hazardNames).shuffle(), count = 0, total = max, cont = true, x = 0, i = 0; max = Math.min(10 + level, max); var blockedHazard = this.pyr.bannedHazard; while (cont) { i++; maxsize = max - x; x++; if (x > max * 0.5) { cont = false; } if ((maxsize <= 8) && (chance(0.95 - (0.1 * level)))) { cont = false; } if (i > 60) { cont = false; } } do { this.hazards = {}; max = total; count = 0; i++; if (i > 200) { break; } for (e = 0; e < order.length; e++) { if (blockedHazard.contains(order[e])) { continue; } val = sys.rand(1, Math.min(5, maxsize)); if (max - val < 0) { val = max; } if (val > 0) { this.hazards[order[e]] = val; max -= val; count += val; if (max <= 0) { break; } } } } while (count < total); this.usableMoves = {}; this.usableCommands = {}; this.moveUsers = {}; var parties = pyramidRef.parties, p, m, move, set, id, list, hazList, formatted; var toLowerMove = function(x) { return moveOff(x).toLowerCase(); }; var findLetter = function(obj, val) { for (var e in obj) { if (obj[e] === val) { return "[" + e.toUpperCase() + "] "; } } return ""; }; var toMoveCommand = function(list, shortcuts) { var out = []; for (var e = 0; e < list.length; e++) { out.push(findLetter(shortcuts, moveOff(list[e])) + link("/pyr " + moveOff(list[e]))); } return out.join(", "); }; for (p in parties) { this.usableMoves[p] = []; this.moveUsers[p] = {}; for (m = parties[p].length; m--; ) { id = parseInt(parties[p][m], 10); set = fetchMoves(id); if (!set) { set = fetchMoves(pokeInfo.species(id)); } for (c = this.validMoves.length; c--;) { move = this.validMoves[c] + ""; if (set.contains(move) && !this.usableMoves[p].contains(move)) { this.usableMoves[p].push(move); this.moveUsers[p][moveOff(move).toLowerCase()] = poke(parties[p][m]); } } } this.usableMoves[p].push("165"); //Struggle this.moveUsers[p].struggle = poke(parties[p][0]); this.usableCommands[p] = this.usableMoves[p].map(toLowerMove); list = this.usableMoves[p]; formatted = []; this.shortcuts[p] = toShortcut(this.usableMoves[p].map(moveOff)); for (m in this.hazardMoves) { if (this.pyr.bannedHazard.contains(m.toLowerCase())) { continue; } hazList = []; move = this.hazardMoves[m]; for (e = 0; e < move.length; e++) { if (list.contains(move[e] + "")) { hazList.push(move[e]); } } if (hazList.length > 0) { formatted.push(this.hazardNames[m] + ": " + toMoveCommand(hazList, this.shortcuts[p])); } } formatted.push("Any: " + toMoveCommand([165], this.shortcuts[p]) + " (costs extra stamina)"); this.individualmsg[p] = "Pick moves to clear the obstacles: " + formatted.join(" | "); this.movesChosen[p] = []; } this.treasures = { egg: { chance: 1 * level, item: "egg", amount: 1 }, nugget: { chance: 1 + level, item: "nugget", amount: 1 }, silver: { chance: 9, item: "silver", amount: 1 + level }, dust: { chance: 16, item: "dust", amount: 9 * level }, quick: { chance: 11, item: "quick", amount: 1 * level }, rock: { chance: 18, item: "rock", amount: 12 * level }, bigpearl: { chance: 4, item: "bigpearl", amount: 1 * level } }; if (chance(0.44 + 0.06 * this.level)) { this.hiddenTreasure = randomSampleObj(this.treasures); this.treasureLocation = Object.keys(this.hazards).random(); } var known = Math.min((Math.floor(( 0.15 + level * 0.75 ) + sys.rand(3, Math.ceil( level*1.22 )))),count-(2 + Math.floor( level * 0.34))), unknown = sys.rand(Math.ceil(level*0.75), Math.round((level * 1.3) + 0.8 + Math.random())), display = JSON.parse(JSON.stringify(this.hazards)), h, k = known, n = 0; hazList = Object.keys(display); var revealed = {}; total = 0; for (p in display) { total += display[p]; } while (known > 0) { h = hazList.random(); if (display[h] > 0) { if (!revealed.hasOwnProperty(h)) { revealed[h] = 0; } val = sys.rand(1, display[h]+1); val = Math.min(known, val); revealed[h] += val; display[h] -= val; known -= val; } } var hidden = {}; unknown = Math.min(unknown, total - k); while (unknown > 0) { h = hazList.random(); if (display[h] > 0) { if (!hidden.hasOwnProperty(h)) { hidden[h] = 0; } val = sys.rand(1, display[h]+1); val = Math.min(unknown, val); hidden[h] += val; display[h] -= val; unknown -= val; n += val; } } var notrevealed = {}; for (p in display) { val = display[p]; if (val > 0) { if ((hidden.hasOwnProperty(p) && hidden[p] > 0) || (revealed.hasOwnProperty(p) && revealed[p] > 0)) { if (!hidden.hasOwnProperty(p)) { hidden[p] = 0; } hidden[p] += val; } else { notrevealed[p] = val; } n += val; } } this.wasteCap = n > 5 ? (n > 9 ? 3 : 2) : (n === 0 ? 0 : 1); hazList = []; for (p in revealed) { if (revealed[p] > 0) { hazList.push(this.plural(revealed[p], p)); } } for (p in notrevealed) { if (notrevealed[p] > 0) { hazList.push(this.plural("??", p)); } } count = 0; for (p in hidden) { count += hidden[p]; } if (count > 0) { hazList.push(count + " other unknown hazard" + (count === 1 ? "" : "s")); } this.sendAll(""); this.sendAll("Room {0}-{1}: Some hazards obstruct your path to the next door! You find {2}!".format(level, roomNum, readable(hazList))); this.sendIndividuals(); this.sendAll(""); } HazardRoom.prototype = new PyramidRoom(); HazardRoom.prototype.validInput = function(id, commandData) { if (this.usableCommands[id].contains(commandData.toLowerCase())) { return true; } this.send(id, "You cannot use that move!"); return false; }; HazardRoom.prototype.postInput = function(src, commandData) { var id = sys.name(src).toLowerCase(); var list = this.movesChosen[id]; var move = commandData.toLowerCase(); var mName = moveOff(movenum(move)); if (list.contains(move)) { list.splice(list.indexOf(move), 1); this.send(id, "You cancelled the " + mName + "!"); } else { list.push(move); this.send(id, "You are going to use " + mName + "!"); } var listName = list.map(function(x) { return moveOff(movenum(x)); }); if (listName.length > 0) { this.sendAll(toColor("{0} is going to use {1}!".format(sys.name(src), readable(listName)), "crimson")); } return true; }; HazardRoom.prototype.plural = function(num, id) { if (num === 1) { return num + " " + this.hazardNames[id]; } return num + " " + this.hazardPlural[id]; }; HazardRoom.prototype.advance = function() { var moves = this.movesChosen; var hazards = this.hazardMoves; var obstacles = this.hazards; var users = this.moveUsers; var pyr = this.pyr; var wasted = {}; var struggled = []; var struggleUsers = []; var cleared = {}, found; var alive = Object.keys(this.pyr.parties).filter(function(x) { return pyr.stamina[x] > 0; }); alive = alive.concat(alive).concat(alive); var effective = {}; var ineffective = []; var treasureTo; var points = 0, stamina = {}; var list, e, p, id, m, ab, mv, mid, total = 0, par, usedAbilities = []; for (e in hazards) { list = hazards[e]; if (obstacles.hasOwnProperty(e)) { cleared[e] = 0; total += obstacles[e]; } for (p = 0; p < alive.length; p++) { id = alive[p]; par = this.pyr.parties[id]; for (var k in par) { for (var i = 0; i < this.hazardAbilities[e].length; i++) { if ((canHaveAbility(par[k],this.hazardAbilities[e][i])) && (usedAbilities.indexOf(this.hazardAbilities[e][i]) === -1)) { if (obstacles.hasOwnProperty(e) && obstacles[e] > 0) { ab = abilityOff(this.hazardAbilities[e][i]); obstacles[e] -= 1; cleared[e]++; if (obstacles[e] === 0 && e === this.treasureLocation) { treasureTo = id; } if (!effective.hasOwnProperty(e)) { effective[e] = []; } effective[e].push(pokePlain(par[k]) + "'s " + ab); usedAbilities.push(this.hazardAbilities[e][i]); break; } } } } } for (p = 0; p < alive.length; p++) { id = alive[p]; m = moves[id].indexOf(moveOff(list[0]).toLowerCase()); if (m === -1) { m = moves[id].indexOf(moveOff(list[1]).toLowerCase()); if (m === -1) { m = moves[id].indexOf(moveOff(list[2]).toLowerCase()); } } if (m >= 0) { mid = moves[id][m]; mv = moveOff(movenum(mid)); moves[id].splice(m, 1); if (obstacles.hasOwnProperty(e) && obstacles[e] > 0) { obstacles[e] -= 1; cleared[e]++; if (obstacles[e] === 0 && e === this.treasureLocation) { treasureTo = id; } if (!effective.hasOwnProperty(e)) { effective[e] = []; } effective[e].push(users[id][mid] + "'s " + mv); } else { if (!wasted.hasOwnProperty(id)) { wasted[id] = 0; } wasted[id] += 1; ineffective.push(users[id][mid] + "'s " + mv); } } } } alive = Object.keys(this.pyr.parties).filter(function(x) { return pyr.stamina[x] > 0; }); for (p in alive) { id = alive[p]; if (moves[id].contains("struggle")) { struggled.push(id); struggleUsers.push(users[id].struggle + "'s Struggle"); found = false; for (m in obstacles) { if (obstacles[m] > 0) { obstacles[m] -= 1; if (!cleared.hasOwnProperty(m)) { cleared[m] = 0; } cleared[m]++; found = true; if (obstacles[m] === 0 && m === this.treasureLocation) { treasureTo = id; } if (!effective.hasOwnProperty(m)) { effective[m] = []; } effective[m].push(users[id].struggle + "'s Struggle"); break; } } if (!found) { if (!wasted.hasOwnProperty(id)) { wasted[id] = 0; } wasted[id] += 1; ineffective.push(users[id].struggle + "'s Struggle"); } } } var notCleared = [], count = 0; for (e in obstacles) { if (obstacles[e] > 0) { notCleared.push(this.plural(obstacles[e], e)); count += obstacles[e]; } } var blue = function(x) { return toColor(x, "blue"); }; this.sendAll(""); for (p in cleared) { if (cleared[p] > 0) { this.sendAll("" + this.plural(cleared[p], p) + " have been cleared by " + readable(effective[p].map(blue)) + "!"); if (treasureTo && p === this.treasureLocation) { this.sendAll("After clearing the {1}, {0} found {2}!".format(addFlashTag(treasureTo.toCorrectCase()), this.hazardNames[p], toColor(treasureName(this.hiddenTreasure), "blue")), true); getTreasure(treasureTo, this.hiddenTreasure); } } } if (ineffective.length > 0) { this.sendAll("No hazard was cleared by " + readable(ineffective.map(blue)) + "!"); for (p in wasted) { if (!stamina.hasOwnProperty(p)) { stamina[p] = 0; } stamina[p] -= Math.round(Math.max(wasted[p] - this.wasteCap, 0) * ((((this.level + 6) * 0.85) + 5) * (count === 0 ? 0.5 : 1))); } } if (struggled.length > 0) { var struggleStm = 3 + 5 * this.level; this.sendAll(readable(struggled.map(function(x){ return x.toCorrectCase(); })) + " lost " + struggleStm + " stamina because of " + readable(struggleUsers) + "!"); for (p = struggled.length; p--; ) { id = struggled[p]; if (!stamina.hasOwnProperty(id)) { stamina[id] = 0; } stamina[id] -= struggleStm; } } if (notCleared.length > 0) { var mercy = Math.round(total * 0.25); if (count > mercy) { var stmLost = Math.round((count + 4 - mercy) * (this.level + 8) * 0.33); if (mercy < count) { stmLost += (this.level + 2); } if (mercy < count - 1) { stmLost += (this.level + 4); } if (mercy < count - 2) { stmLost += (this.level + 6); } if (stmLost > 0) { this.sendAll("The following hazards haven't been cleared: " + readable(notCleared) + "! The party loses " + stmLost + " Stamina!"); for (var p = alive.length; p--; ) { id = alive[p]; if (!stamina.hasOwnProperty(id)) { stamina[id] = 0; } stamina[id] -= stmLost; } } else { this.sendAll("The following hazards haven't been cleared: " + readable(notCleared) + "!"); } } else { this.sendAll("The following hazards haven't been cleared: " + readable(notCleared) + "!"); } } points = Math.round((16 + 3 * this.level) * (total - count)); if (notCleared.length === 0) { points += (2 + (6 * this.level)); } this.sendAll(""); var perc = Math.round(count / total * 100); if (perc === 0) { this.sendAll("All hazards have been cleared, so nothing could stop you from going forward!"); } else if (perc <= 20) { this.sendAll("You cleared most of the hazards, so reaching the door was a piece of cake!"); } else if (perc <= 50) { this.sendAll("Some of the hazards haven't been cleared, so you struggled to reach the door!"); } else if (perc <= 75) { this.sendAll("Still a fair number hazards remains, so it takes some time to reach the door!"); } else if (perc === 100) { this.sendAll("Literally no hazard cleared at all! Are you even trying?"); } else { this.sendAll("Too many hazards left, so it took a good amount of effort to pass through the room!"); } this.pyr.updateStatus(points, stamina, true); this.sendAll(""); this.passed = true; }; function EmptyRoom(pyramidRef, level, roomNum) { PyramidRoom.call(this, pyramidRef); this.midmsg = "Choose a Pokémon to help you explore the room to find the door."; this.level = level; var parties = pyramidRef.parties; for (var p in parties) { this.shortcuts[p] = toShortcut(parties[p].map(poke)); this.individualmsg[p] = "Send one of your Pokémon to explore the room: " + pyrLink(this.shortcuts[p]); } var types = Object.keys(effectiveness).shuffle(); this.traps = {}; for (p = 0; p < 3; p++) { this.traps[types.shift()] = { stamina: -(15 + this.level * 5) }; } this.treasures = {}; var rew = { pack: { chance: 3 + level, item: "pack", amount: 1 }, gem: { chance: 4 + level, item: "gem", amount: 1 }, nugget: { chance: 3 + level, item: "nugget", amount: 1 }, bignugget: { chance: (0.2 + (1.1 * level)), item: "bignugget", amount: 1 }, money: { chance: 2, item: "money", amount: 900 * level }, money2: { chance: 8, item: "money", amount: 220 * level }, silver: { chance: 7, item: "silver", amount: 2 * level }, gacha: { chance: 12, item: "gacha", amount: 4 * level }, dust: { chance: 16, item: "dust", amount: 10 * level }, redapricorn: { chance: 17 - level, item: "redapricorn", amount: (3 + (2 * level)) }, grnapricorn: { chance: 18 - level, item: "grnapricorn", amount: (4 + (2 * level)) }, whtapricorn: { chance: 20 - level, item: "whtapricorn", amount: (4 + (2 * level)) }, blkapricorn: { chance: 16 - level, item: "blkapricorn", amount: (4 + (2 * level)) }, premier: { chance: 11, item: "premier", amount: 3 * level }, pearl: { chance: 10, item: "pearl", amount: 2 + level }, bigpearl: { chance: 6, item: "bigpearl", amount: 1 * level }, stamina: { chance: 6, item: "stamina", amount: 3 + level } }; for (p = 0; p < 3; p++) { this.treasures[types.shift()] = randomSampleObj(rew); } var target, t1, t2, badTypes = Object.keys(this.traps), goodTypes = Object.keys(this.treasures), badList = [], goodList = []; for (p = 1; p < highestDexNum; p++) { t1 = type1(p); t2 = type2(p); if (badTypes.contains(t1) && (t2 === "???" || badTypes.contains(t2))) { badList.push(p); continue; } if (goodTypes.contains(t1) && (t2 === "???" || goodTypes.contains(t2))) { goodList.push(p); } } goodList = goodList.shuffle(); var list = [], used = []; for (var j = 0; j < 400; j++) { badList = badList.shuffle(); list = [], used = []; for (p = badList.length; p--; ) { target = badList[p]; t1 = type1(target); t2 = type2(target); if (!(badTypes.contains(t1) && (badTypes.contains(t2) || t2 == "???"))) { continue; } if (!used.contains(t1) && (t2 === "???" || !used.contains(t2))) { used.push(t1); if (t2 !== "???") { used.push(t2); } list.push(target); if (list.length >= 2) { if (used.length >= 3) { break; } continue; } } } } badList = list.map(poke); list = []; used = []; for (p = goodList.length; p--; ) { target = goodList[p]; t1 = type1(target); t2 = type2(target); if (!used.contains(t1) && (t2 === "???" || !used.contains(t2))) { used.push(t1); if (t2 !== "???") { used.push(t2); } list.push(target); if (list.length >= 2) { break; } } } goodList = list.map(poke); this.sendAll(""); badList = badList.join("").split("").shuffle().join(""); goodList = goodList.join("").split("").shuffle().join(""); this.sendAll("Room {0}-{1}: Other than a sign saying \"Pokémon like {2} are not recommended in this room, but {3} may be useful\", there's nothing of interest in this room. Or is there?".format(level, roomNum, toColor(badList, "red"), toColor(goodList, "blue"))); this.sendIndividuals(); this.sendAll(""); } EmptyRoom.prototype = new PyramidRoom(); EmptyRoom.prototype.validInput = function(id, commandData) { return this.pokeInParty(id, commandData); }; EmptyRoom.prototype.advance = function() { var choices = this.getChoices(), id, p, points = 0, stamina = {}, t1, t2, hasTrap, hasTreasure, trapType, treasureType, out, reward, foundTreasure = {}, c; this.sendAll(""); for (p in choices) { c = choices[p]; id = c; t1 = type1(id); t2 = type2(id); hasTrap = false; hasTreasure = false; if (t1 in this.traps || t2 in this.traps) { hasTrap = true; if (t1 in this.traps && t2 in this.traps) { trapType = sys.rand(0, 2) === 0 ? t1 : t2; } else { trapType = t1 in this.traps ? t1 : t2; } } if (t1 in this.treasures || t2 in this.treasures) { hasTreasure = true; if (t1 in this.treasures && t2 in this.treasures) { treasureType = sys.rand(0, 2) === 0 ? t1 : t2; } else { treasureType = t1 in this.treasures ? t1 : t2; } } if (hasTrap && hasTreasure) { if (chance(0.5)) { hasTreasure = false; } else { hasTrap = false; } } out = "But they found nothing..."; reward = null; if (hasTrap) { stamina[p] = this.traps[trapType].stamina; foundTreasure[p] = -10 - 10 * this.level; out = "{0}'s {1} {3}! {0} lost {2} Stamina!".format(p.toCorrectCase(), poke(c), -stamina[p], toColor("triggered a trap", "red")); } else if (hasTreasure) { reward = this.treasures[treasureType]; foundTreasure[p] = 16 + 16 * this.level; out = "{0}'s {1} {3}! {0} received {2}!".format(addFlashTag(p.toCorrectCase()), poke(c), treasureName(reward), toColor("found a treasure", "blue")); if (reward.item === "stamina") { stamina[p] = reward.amount; } } this.sendAll("{0} asked for their {1} to explore the room! {2}".format(p.toCorrectCase(), poke(c), out), true); if (reward) { getTreasure(p, reward); } } for (p in foundTreasure) { points += foundTreasure[p]; } this.pyr.updateStatus(points, stamina, true); this.sendAll(""); this.passed = true; }; /* Events */ function getEventName(id) { switch (id) { case "factionwar": return "Faction War"; case "invertedwar": return "Inverted Faction War"; case "race": return "Pokémon Race"; case "betrace": return "Pokémon Bet Race"; case "bfactory": return "Battle Factory"; case "lcbfactory": return "LC Battle Factory"; case "quiz": return "Quiz"; case "hquiz": return "Hidden Quiz"; case "bingo": return "Bingo"; default: return "No Event"; } } function getEventId(name) { if (!name) { return null; } switch (name.toLowerCase()) { case "war": case "factionwar": case "faction war": case "faction": return "factionwar"; case "inverwar": case "invertedwar": case "inverted war": case "inverted faction war": return "invertedwar"; case "race": case "pokerace": return "race"; case "betrace": case "bet race": return "betrace"; case "bf": case "factory": case "bfactory": case "battlefactory": case "battle factory": return "bfactory"; case "lcbf": case "lc bf": case "lcfactory": case "lcbfactory": case "lcbattlefactory": case "lc battlefactory": case "lc battle factory": return "lcbfactory"; case "quiz": return "quiz"; case "hquiz": case "hiddenquiz": case "hidden quiz": return "hquiz"; case "bingo": return "bingo"; case "vb": case "bikini battle": case "volleyball": return "volleyball"; default: return null; } } function SafariEvent(src, form) { this.hostName = src ? sys.name(src) : "~Unknown~"; this.eventForm = form; this.eventName = "Safari Event"; this.signups = []; this.forbiddenPlayers = []; this.viewers = []; this.watchCounts = {}; this.turn = -1; this.turnLength = 6; this.minPlayers = 1; this.signupsDuration = 6; this.finished = false; this.eventCommands = {}; this.rewardName = ""; this.allowRejoin = false; this.joinmsg = "Type " + link("/signup") + " to participate!"; } SafariEvent.prototype.setupEvent = function() {}; SafariEvent.prototype.nextTurn = function() { this.turn++; var duration = this.signupsDuration; if (this.turn < duration) { //Sign-up Phase if (this.turn == Math.round(duration/2)) { sys.sendAll("", safchan); safaribot.sendHtmlAll("A " + this.eventName + " event is starting in " + ((duration - Math.round(duration/2)) * this.turnLength) + " seconds! " + this.joinmsg, safchan); sys.sendAll("", safchan); } } else if (this.turn === duration) { //Setup if (this.signups.length < this.minPlayers) { safaribot.sendHtmlAll("The " + this.eventName + " event was cancelled due to the low number of participants!", safchan); this.finished = true; if (this.eventForm) { if (isPlaying(this.hostName)) { safaribot.sendMessage(sys.id(this.hostName), "Your " + finishName("form") + " was returned to you.", safchan); } giveStuff(getAvatar(sys.id(this.hostName)), "@form"); } return; } this.setupEvent(); } else { this.playTurn(); } }; SafariEvent.prototype.playTurn = function() { }; SafariEvent.prototype.finish = function() { this.finished = true; }; SafariEvent.prototype.log = function(finished, winners, extra) { sys.appendToFile(eventLog, now() + "|||" + this.eventName + "|||" + this.hostName + "|||" + this.signups.join(", ") + "|||" + finished + "|||" + (Array.isArray(winners) ? (winners.length > 0 ? winners.join(", ") : "None") : winners) + "|||" + this.rewardName + "|||" + (extra || "N/A") + "\n"); }; SafariEvent.prototype.handleCommand = function(src, command, commandData) { this.eventCommands[command].call(this, src, commandData); }; SafariEvent.prototype.join = function(src, data) { if (this.turn >= this.signupsDuration) { safaribot.sendMessage(src, "The " + this.eventName + " is already underway, you can't join now!", safchan); return; } var player = getAvatar(src); var name = sys.name(src); if (!player) { safaribot.sendMessage(src, "You need to enter Safari to join this event!", safchan); return; } if (cantBecause(src, "join an event", ["auction", "battle", "tutorial", "pyramid", "baking"])) { return; } if (this.forbiddenPlayers.contains(name.toLowerCase())) { safaribot.sendMessage(src, "You have been shoved from this event and is not allowed to join!", safchan); return; } var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); if (!this.allowRejoin) { if (signupsLower.contains(name.toLowerCase())) { safaribot.sendMessage(src, "You already joined the event!", safchan); return; } } if (this.canJoin && !this.canJoin(src, data)) { return; } var verb = signupsLower.contains(name.toLowerCase()) ? "rejoined" : "joined"; if (verb === "rejoined") { if (this.onLeave) { this.onLeave(this.signups[signupsLower.indexOf(name.toLowerCase())]); } } else { this.signups.push(name); } var onJoinMsg = ""; if (this.onJoin) { onJoinMsg = this.onJoin(name, data); } this.sendToViewers(name + " has " + verb + " the " + this.eventName + onJoinMsg + "!"); safaribot.sendHtmlMessage(src, "You " + verb + " the " + this.eventName + onJoinMsg + "! To leave the event, type " + link("/unjoin") + "!", safchan); }; SafariEvent.prototype.unjoin = function(src) { var player = getAvatar(src); if (!player) { safaribot.sendMessage(src, "You need to enter Safari to use this command!", safchan); return; } var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); var name = sys.name(src); if (!signupsLower.contains(name.toLowerCase())) { safaribot.sendMessage(src, "You didn't even join this event!", safchan); return; } if (this.turn >= this.signupsDuration) { safaribot.sendMessage(src, "The " + this.eventName + " is already underway, you can't unjoin now!", safchan); return; } var index = signupsLower.indexOf(name.toLowerCase()); this.signups.splice(index, 1); if (this.onLeave) { this.onLeave(name); } this.sendToViewers(name + " has unjoined the " + this.eventName + "!"); safaribot.sendMessage(src, "You unjoined the " + this.eventName + " event!", safchan); }; SafariEvent.prototype.shove = function(src, target) { var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); if (!signupsLower.contains(target.toLowerCase())) { if (src) { safaribot.sendMessage(src, target.toCorrectCase() + " didn't join this event!", safchan); } return; } if (this.turn >= this.signupsDuration) { if (src) { safaribot.sendMessage(src, "The " + this.eventName + " is already underway, you can't shove anyone now!", safchan); } return; } var index = signupsLower.indexOf(target.toLowerCase()); this.sendMessage(target, "You have been shoved from the event!"); if (this.onLeave) { this.onLeave(this.signups[index]); } this.signups.splice(index, 1); if (src) { this.sendToViewers(sys.name(src) + " shoved " + target.toCorrectCase() + " from the " + this.eventName + "!"); } else { this.sendToViewers(target.toCorrectCase() + " has been shoved from the " + this.eventName + "!"); } this.forbiddenPlayers.push(target.toLowerCase()); }; SafariEvent.prototype.watchEvent = function(src) { if (this.turn < this.signupsDuration) { safaribot.sendHtmlMessage(src, "The " + this.eventName + " didn't start yet! " + this.joinmsg, safchan); return; } var name = sys.name(src); if (this.canWatch && !this.canWatch(src)) { return; } if (this.viewers.contains(name.toLowerCase())) { this.viewers.splice(this.viewers.indexOf(name.toLowerCase()), 1); this.sendToViewers(name + " stopped watching this " + this.eventName + "!"); safaribot.sendMessage(src, "You are no longer watching the " + this.eventName + "!", safchan); if (this.onUnwatch) { this.onUnwatch(src); } } else { this.sendToViewers(name + " is watching this " + this.eventName + "!"); this.viewers.push(name.toLowerCase()); if (this.onWatch) { this.onWatch(src); } } }; SafariEvent.prototype.canWatch = function(src) { var name = sys.name(src); var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); if (signupsLower.contains(name.toLowerCase())) { safaribot.sendMessage(src, "You are one of the participants, you can't watch/unwatch this event!", safchan); return false; } return true; }; SafariEvent.prototype.sendMessage = function(name, msg, flashing, colored, nobot, androidAlt) { var id = sys.id(name); if (id) { if (androidAlt && sys.os(id) === "android") { msg = androidAlt; } if (msg === "" || nobot) { if (flashing) { sys.sendHtmlMessage(id, toFlashing(msg, name), safchan); } else if (colored) { sys.sendHtmlMessage(id, toColored(msg, name), safchan); } else { sys.sendHtmlMessage(id, msg, safchan); } } else { if (flashing) { safaribot.sendHtmlMessage(id, toFlashing(msg, name), safchan); } else if (colored) { safaribot.sendHtmlMessage(id, toColored(msg, name), safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } } }; SafariEvent.prototype.sendToViewers = function(msg, flashing, colored, nobot) { var e; var list = removeDuplicates(this.signups.concat(this.viewers)); for (e = 0 ; e < list.length; e++) { this.sendMessage(list[e], msg, flashing, colored, nobot); } }; SafariEvent.prototype.isInEvent = function(name) { var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); return signupsLower.contains(name.toLowerCase()); }; function FactionWar(src, reward, team1, team2, inverted, reward2, form) { SafariEvent.call(this, src, form); this.eventName = (inverted ? "Inverted " : "") + "Faction War"; this.team1Name = team1; this.team2Name = team2; this.team1Color = "red"; this.team2Color = "blue"; this.team1 = []; this.team2 = []; this.party1 = []; this.party2 = []; this.inverted = inverted; this.npcs = []; this.preferredTeams = {}; this.playerTeams = {}; this.reward = reward; this.diffRewards = reward2 && reward !== reward2; this.reward2 = this.diffRewards ? reward2 : reward; this.rewardName = translateStuff(reward); this.reward2Name = translateStuff(this.reward2); this.hasReward = true; this.team1Defeated = 0; this.team2Defeated = 0; this.suddenDeath = false; this.allowRejoin = true; this.joinmsg = "Type " + link("/signup " + this.team1Name) + ", " + link("/signup " + this.team2Name) + " or " + link("/signup") + " " + " to participate! " + (this.diffRewards ? "Members of " + this.team1Name + " will receive " + this.rewardName + " if winning, members of " + this.team2Name + " will receive " + this.reward2Name + " if winning" : "Winners will receive " + this.rewardName + "") + "!"; var joinCommand = "/signup"; sys.sendAll("", safchan); if (this.diffRewards) { safaribot.sendHtmlAll(sys.name(src) + " is starting a " + this.eventName + " event! The teams are " + toColor(team1, this.team1Color) + " (Reward: " + this.rewardName + ") and " + toColor(team2, this.team2Color) + " (Reward: " + this.reward2Name + ")!", safchan); } else { safaribot.sendHtmlAll(sys.name(src) + " is starting a " + this.eventName + " event! The teams are " + toColor(team1, this.team1Color) + " and " + toColor(team2, this.team2Color) + ", and each player from the winning team will receive " + this.rewardName + "!", safchan); } safaribot.sendHtmlAll("Type " + link(joinCommand + " " + team1) + " or " + link(joinCommand + " " + team2) + " to join a side, or " + link(joinCommand) + " to join a random side (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); sys.sendAll("", safchan); } FactionWar.prototype = new SafariEvent(); FactionWar.prototype.setupEvent = function() { //SETUP TEAMS var teamSize = Math.ceil(Math.max(this.signups.length, 6)/2); var pickedSides = this.preferredTeams, name, e, overflow = [], changedTeams = {}; for (e in pickedSides) { name = pickedSides[e]; if (this.signups.contains(e)) { if (name == this.team1Name) { if (this.team1.length < teamSize) { this.team1.push(e); changedTeams[e] = "you have been added to the " + toColor(this.team1Name, this.team1Color) + " side!"; } else { overflow.push(e); } } else if (name == this.team2Name) { if (this.team2.length < teamSize) { this.team2.push(e); changedTeams[e] = "you have been added to the " + toColor(this.team2Name, this.team2Color) + " side!"; } else { overflow.push(e); } } else { overflow.push(e); } } } var players = overflow.shuffle(), otherTeam, chosenTeam, chosenColor; while (players.length > 0) { name = players.shift(); chosenTeam = pickedSides[name]; if (this.team1.length >= teamSize) { this.team2.push(name); otherTeam = this.team2Name; chosenColor = this.team2Color; } else if (this.team2.length >= teamSize) { this.team1.push(name); otherTeam = this.team1Name; chosenColor = this.team1Color; } else { var randomTeam = Math.random() < 0.5 ? this.team1 : this.team2; otherTeam = randomTeam == this.team1 ? this.team1Name : this.team2Name; chosenColor = randomTeam == this.team1 ? this.team1Color : this.team2Color; randomTeam.push(name); } if (chosenTeam === "*") { changedTeams[name] = "you have been added to the " + toColor(otherTeam, chosenColor) + " side!"; } else { changedTeams[name] = "you have been moved to the " + toColor(otherTeam, chosenColor) + " side because the " + chosenTeam + " side was full!"; } } var i, len = this.team1.length, player, member; for (e = 0; e < len; e++) { name = this.team1[e]; player = getAvatarOff(name); this.playerTeams[name] = []; for (i = 0; i < 6; i++) { member = { owner: name, id: player.party[i], score: 0, fought: false }; this.party1.push(member); this.playerTeams[name].push(member); } } len = this.team2.length; for (e = 0; e < len; e++) { name = this.team2[e]; player = getAvatarOff(name); this.playerTeams[name] = []; for (i = 0; i < 6; i++) { member = { owner: name, id: player.party[i], score: 0, fought: false }; this.party2.push(member); this.playerTeams[name].push(member); } } var inver = this.inverted; var createFillerPlayer = function() { var name = "[NPC] " + generateName(); var party = generateTeam(); if (inver && hasPureNormal(party)) { return createFillerPlayer(); } return { name: name, party: party }; }; var playerCount = this.signups.length; if (playerCount < 4) { this.hasReward = false; } while (playerCount < 6 || playerCount % 2 === 1) { var useSide1 = this.team1.length < this.team2.length; var team = useSide1 ? this.team1 : this.team2; var party = useSide1 ? this.party1 : this.party2; var npc = createFillerPlayer(); if (!team.contains(npc.name) && !this.npcs.contains(npc.name)) { team.push(npc.name); this.npcs.push(npc.name); this.playerTeams[npc.name] = []; for (i = 0; i < 6; i++) { member = { owner: npc.name, id: npc.party[i], score: 0, fought: false }; party.push(member); this.playerTeams[npc.name].push(member); } playerCount++; } } this.party1.shuffle(); this.party2.shuffle(); this.battleSize = this.party1.length; this.sendToViewers(""); safaribot.sendHtmlAll("The " + this.eventName + " is starting now! If you didn't join, you still can watch by typing " + link("/watch") + "!", safchan); this.sendToViewers("The teams have been defined! "); this.sendToViewers(toColor(this.team1Name, this.team1Color) + ": " + readable(this.team1.map(addFlashTag), "and"), true); this.sendToViewers(toColor(this.team2Name, this.team2Color) + ": " + readable(this.team2.map(addFlashTag), "and"), true); for (e in changedTeams) { this.sendMessage(e, addFlashTag(e) + ", " + changedTeams[e], true); } this.sendToViewers("Battles will start now!"); this.sendToViewers(""); }; FactionWar.prototype.playTurn = function() { var p1, p2, result, e; for (e = 0; e < 3; e++) { p1 = this.party1[this.team1Defeated]; p2 = this.party2[this.team2Defeated]; result = this.runBattle(p1.id, p2.id, p1.owner, p2.owner); p1.fought = true; p2.fought = true; if (result === 1) { this.team2Defeated++; p1.score++; } else if (result === 2) { this.team1Defeated++; p2.score++; } else { this.team1Defeated++; this.team2Defeated++; } if (this.team1Defeated >= this.party1.length || this.team2Defeated >= this.party2.length) { break; } } this.sendToViewers("[" + toColor(this.team1Name, this.team1Color) + ": " + (this.party1.length - this.team1Defeated) + " Pokémon remaining] ["+ toColor(this.team2Name, this.team2Color) + ": " + (this.party2.length - this.team2Defeated) + " Pokémon remaining]"); this.sendToViewers(""); if (this.team1Defeated >= this.battleSize || this.team2Defeated >= this.battleSize) { if (this.team1Defeated !== this.team2Defeated) { this.finish(); } else { if (!this.suddenDeath) { this.sendToViewers("Both teams have been defeated at the same time! Starting sudden death mode!"); this.suddenDeath = true; } else { this.sendToViewers("Winner still undefined, sudden death mode will continue!"); } this.party1.push(this.party1.random()); this.party2.push(this.party2.random()); } } }; FactionWar.prototype.runBattle = function(p1Poke, p2Poke, owner1, owner2) { var res = calcDamage(p1Poke, p2Poke, null, null, this.inverted, getCherished(p1Poke, owner1), getCherished(p2Poke, owner2)); var name1 = owner1 + "'s " + poke(p1Poke); var name2 = owner2 + "'s " + poke(p2Poke); name1 = res.power[0] > res.power[1] ? "" + toColor(name1, this.team1Color) + "" : name1; name2 = res.power[1] > res.power[0] ? "" + toColor(name2, this.team2Color) + "" : name2; var result = name1 + " " + pokeInfo.icon(p1Poke) + " (" + res.power[0] + ") x (" + res.power[1] + ") "+ pokeInfo.icon(p2Poke) + " " + name2; if (res.power[0] == res.power[1]) { result += " Draw"; } this.sendToViewers(result); var status = "Details (" + res.statName + "/Power/Type) | " + poke(p1Poke) + " (" + res.stat[0] + " / " + res.move[0] + " / " + res.bonusString[0] + "x) x " + poke(p2Poke) + " (" + res.stat[1] + " / " + res.move[1] + " / " + res.bonusString[1] + "x)"; this.sendToViewers(toColor(status, "gray")); if (res.power[0] > res.power[1]) { return 1; } else if (res.power[1] > res.power[0]) { return 2; } else { return 0; } }; FactionWar.prototype.finish = function() { var winner, loser; var rewName = this.team1Defeated > this.team2Defeated ? this.reward2Name : this.rewardName; var reward = this.team1Defeated > this.team2Defeated ? this.reward2 : this.reward; var rewardmsg = this.hasReward ? "Each player will receive " + rewName + "!" : ""; if (this.team1Defeated > this.team2Defeated) { winner = this.team2; loser = this.team1; safaribot.sendHtmlAll("The " + toColor(this.team2Name, this.team2Color) + " (" + readable(winner, "and") + ") has won the Faction War! " + rewardmsg, safchan); } else { winner = this.team1; loser = this.team2; safaribot.sendHtmlAll("The " + toColor(this.team1Name, this.team1Color) + " (" + readable(winner, "and") + ") has won the Faction War! " + rewardmsg, safchan); } var mvp = [], mvpPoints = 0, e, i, teams = this.playerTeams, id, player, score, mon, totalPoints; for (e in teams) { id = sys.id(e); player = teams[e]; score = []; totalPoints = 0; for (i = 0; i < player.length; i++) { mon = player[i]; totalPoints += mon.score; score.push(poke(mon.id) + ": " + (mon.fought ? plural(mon.score, "point") : "Didn't fight")); if (mon.score >= mvpPoints) { if (mon.score > mvpPoints) { mvpPoints = mon.score; mvp = []; } mvp.push(mon); } } this.sendMessage(e, "Your score: " + score.join(" | ") + " | Total: " + plural(totalPoints, "Point")); } if (mvp.length > 0) { this.sendToViewers("The MVP for this " + this.eventName + " was " + readable(mvp.map(function(obj) { return addFlashTag(obj.owner) + "'s " + poke(obj.id); }), "and") + " with " + plural(mvpPoints, "Point") + "!", true); } if (!this.hasReward) { this.sendToViewers("No records or rewards will be given due to the low number of participants!"); } else { var name, len = winner.length, stuff, out; for (e = 0; e < len; e++) { name = winner[e]; if (!this.npcs.contains(name)) { player = getAvatarOff(name); if (player) { id = sys.id(name); if (id) { safaribot.sendMessage(id, "You received " + rewName + " for winning the event!", safchan); } stuff = toStuffObj(reward.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.factionWins += 1; for (i = 0; i < mvp.length; i++) { if (mvp[i].owner == name) { player.records.factionMVPs += 1; } } safari.saveGame(player); } } } rafflePlayers.save(); len = loser.length; for (e = 0; e < len; e++) { name = loser[e]; if (!this.npcs.contains(name)) { player = getAvatarOff(name); if (player) { for (i = 0; i < mvp.length; i++) { if (mvp[i].owner == name) { player.records.factionMVPs += 1; } } safari.saveGame(player); } } } } this.rewardName = rewName; this.log(true, winner, "Teams: " + this.team1Name + (winner == this.team1 ? " (Winner)" : "") + " x " + this.team2Name + (winner == this.team2 ? " (Winner)" : "") + " / MVP: " + readable(mvp.map(function(obj) { return obj.owner + "'s " + poke(obj.id); })) + " with " + plural(mvpPoints, "Point")); this.finished = true; }; FactionWar.prototype.canJoin = function(src) { var player = getAvatar(src); if (player.party.length < 6) { safaribot.sendMessage(src, "You must have a party with 6 Pokémon to join this event!", safchan); return false; } if (hasPokeInShop(src, true)) { return false; } if (this.inverted && hasPureNormal(player.party)) { safaribot.sendMessage(src, "You cannot use a pure Normal-type Pokémon on " + an(this.eventName) + "!", safchan); return false; } return true; }; FactionWar.prototype.onJoin = function(name, data) { var pickedTeam = ""; if (data.toLowerCase() == this.team1Name.toLowerCase()) { pickedTeam = " on the " + this.team1Name + " side"; this.preferredTeams[name] = this.team1Name; } else if (data.toLowerCase() == this.team2Name.toLowerCase()) { pickedTeam = " on the " + this.team2Name + " side"; this.preferredTeams[name] = this.team2Name; } else { this.preferredTeams[name] = "*"; } return pickedTeam; }; FactionWar.prototype.onLeave = function(name) { delete this.preferredTeams[name]; }; FactionWar.prototype.onWatch = function(src) { safaribot.sendMessage(src, "You are watching the " + this.eventName + "! The teams are: ", safchan); safaribot.sendHtmlMessage(src, toColor(this.team1Name, this.team1Color) + ": " + readable(this.team1, "and"), safchan); safaribot.sendHtmlMessage(src, toColor(this.team2Name, this.team2Color) + ": " + readable(this.team2, "and"), safchan); if (this.diffRewards) { safaribot.sendMessage(src, "Rewards for " + this.team1Name + ": " + this.rewardName + " | Rewards for " + this.team2Name + ": " + this.reward2Name, safchan); } else { safaribot.sendMessage(src, "Winners will receive " + this.rewardName + "!", safchan); } }; function BFactory(src, reward1, reward2, reward3, lc, form) { SafariEvent.call(this, src, form); this.eventName = (lc ? "LC " : "") + "Battle Factory"; this.minPlayers = 4; this.lc = lc; this.playerTeams = {}; this.survivors = []; this.battles = []; this.lastByes = []; this.eliminationOrder = []; this.round = -1; this.currentMatch = 0; this.choices = {}; this.opponents = {}; this.shortcuts = {}; this.resting = ""; this.totalPoints = {}; this.lastPoints = {}; this.phase = "signup"; this.subturn = 0; this.thirdPrize = false; this.thirdPlace = null; this.isThirdPlaceMatch = false; this.fightingForThird = []; this.reward1 = reward1; this.reward2 = reward2; this.reward3 = reward3; this.rewardName1 = translateStuff(reward1); this.rewardName2 = translateStuff(reward2); this.rewardName3 = translateStuff(reward3); this.rewardName = "1st: " + this.rewardName1 + (reward2 ? ", 2nd: " + this.rewardName2 : "") + (reward3 ? ", 3rd: " + this.rewardName3 : ""); this.rewardNameB = "1st: " + this.rewardName1 + (reward2 ? ", 2nd: " + this.rewardName2 : "") + (reward3 ? ", 3rd: " + this.rewardName3 : ""); this.hasReward = true; this.eventCommands = { choose: this.choosePokemon }; var joinCommand = "/signup"; this.joinmsg = "Type " + link(joinCommand) + " to participate! Rewards: " + this.rewardNameB + "!"; sys.sendAll("", safchan); safaribot.sendHtmlAll(sys.name(src) + " is starting a " + this.eventName + " event with the following rewards: " + this.rewardNameB + "!", safchan); safaribot.sendHtmlAll("Type " + link(joinCommand) + " to participate (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); sys.sendAll("", safchan); } BFactory.prototype = new SafariEvent(); BFactory.prototype.setupEvent = function() { this.thirdPrize = this.signups.length > 3; var n; for (var e = 0; e < this.signups.length; e++) { n = this.signups[e].toLowerCase(); if (this.lc) { this.playerTeams[n] = generateTeam(8, 174, 480, 1, null, false, true); } else { this.playerTeams[n] = generateTeam(8, 450, 600, 1, null, true, false); } this.shortcuts[n] = toShortcut(this.playerTeams[n].map(poke)); this.survivors.push(n); this.totalPoints[n] = 0; this.lastPoints[n] = 0; } this.sendToViewers(""); safaribot.sendHtmlAll("The " + this.eventName + " is starting now! If you didn't join, you still can watch by typing " + link("/watch") + "!", safchan); for (e in this.playerTeams) { this.sendMessage(e, "Parties have been formed! " + addFlashTag(e.toCorrectCase()) + ", your team for this event is: ", true); this.sendMessage(e, this.playerTeams[e].map(pokeInfo.icon).join(" ")); } this.sendToViewers(""); }; function toChooseLink(obj) { var letters = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z", "!", "?"]; var out = []; for (var e = 0; e < obj.length; e++) { out.push("[" + letters[e] + "] " + link("/choose " + poke(obj[e]), poke(obj[e]))); } return out.join(", "); } BFactory.prototype.playTurn = function() { if (this.phase === "signup") { this.phase = "prepare"; this.sendToViewers("Battles will start soon, use this time to get used to your team!"); return; } if (this.phase === "input") { this.subturn--; if (this.subturn === 3) { this.sendToViewers("Round " + (this.round + 1) + " Matches will start in 18 seconds!"); } if (this.subturn > 0) { return; } this.phase = "match"; } if (this.phase === "prepare") { //Create match-ups here this.prepareBattles(); } else if (this.phase === "match") { //Run battles here this.nextMatch(); } if (this.survivors.length === 1) { //Check if event is over here this.finish(); } }; BFactory.prototype.prepareBattles = function() { var players = this.survivors.concat().shuffle(), freePass, e, bat, det; this.resting = ""; //Check if some player automatically advances to the next round due to odd number of players if (players.length % 2 === 1) { var canPass = [], bestTotal = 0, bestLast = 0, id; var totalPoints = this.totalPoints; var lastPoints = this.lastPoints; for (e in totalPoints) { if (totalPoints[e] > bestTotal) { bestTotal = totalPoints[e]; } } for (e = 0; e < players.length; e++) { id = players[e]; if (!this.lastByes.contains(id) && totalPoints[id] === bestTotal) { if (lastPoints[id] > bestLast) { bestLast = lastPoints[id]; canPass = []; } canPass.push(id); } } if (canPass.length > 0) { freePass = canPass.random(); } else { this.lastByes = []; freePass = players.random(); } this.resting = freePass; players.splice(players.indexOf(freePass), 1); this.lastByes.push(freePass); } this.battles = []; var isFinal = this.survivors.length === 2; var prepareForThird = isFinal; if (this.round >= 0 && this.eliminationOrder[this.round].length === 1) { prepareForThird = false; this.thirdPlace = this.eliminationOrder[this.round][0]; } if (prepareForThird) { var eliminated = this.eliminationOrder[this.round]; this.battles.push([eliminated[0], eliminated[1]]); this.fightingForThird = [eliminated[0], eliminated[1]]; this.isThirdPlaceMatch = true; } for (e = 0; e < players.length; e += 2) { this.battles.push([players[e], players[e+1]]); } this.round++; this.sendToViewers(""); this.sendToViewers(toColor("*** *************************************************** ***", "peru"), false, false, true); var roundTable = ""; var androidTable = ["
Round " + (this.round + 1)]; for (e = 0; e < this.battles.length; e++) { bat = this.battles[e]; det = ""; if (prepareForThird && e === 0) { det = "Third Place Match: "; } else if (isFinal) { det = toColor("Final: ", "red"); } roundTable += "" + (det ? "" : "") + ""; androidTable.push(det + addFlashTag(bat[0].toCorrectCase()) + " " + toColor("vs", "gray") + " " + addFlashTag(bat[1].toCorrectCase())); } if (freePass) { roundTable += ""; androidTable.push(addFlashTag(freePass.toCorrectCase()) + " advances to the next round!"); } roundTable += "
Round " + (this.round + 1) + "
" + det + "" + addFlashTag(bat[0].toCorrectCase()) + " " + toColor("vs", "gray") + " " + addFlashTag(bat[1].toCorrectCase()) + "
" + addFlashTag(freePass.toCorrectCase()) + " advances to the next round!
"; this.sendToViewers(roundTable, true, false, true, androidTable.join("
")); this.sendToViewers(""); this.sendToViewers(toColor("*** *************************************************** ***", "peru"), false, false, true); this.choices = {}; this.opponents = {}; for (e = 0; e < this.battles.length; e++) { bat = this.battles[e]; this.sendMessage(bat[0], "Your opponent (" + bat[1].toCorrectCase() + ")'s Team: " + this.playerTeams[bat[1]].map(pokeInfo.icon).join(" ")); this.sendMessage(bat[0], "Use /choose to pick 3 of your Pokémon for this battle (you have 36 seconds): " + toChooseLink(this.playerTeams[bat[0]])); this.sendMessage(bat[1], "Your opponent (" + bat[0].toCorrectCase() + ")'s Team: " + this.playerTeams[bat[0]].map(pokeInfo.icon).join(" ")); this.sendMessage(bat[1], "Use /choose to pick 3 of your Pokémon for this battle (you have 36 seconds): " + toChooseLink(this.playerTeams[bat[1]])); this.choices[bat[0]] = []; this.choices[bat[1]] = []; this.opponents[bat[0]] = bat[1]; this.opponents[bat[1]] = bat[0]; } this.sendToViewers(""); this.currentMatch = 0; this.eliminationOrder.push([]); this.phase = "input"; this.subturn = 6; }; BFactory.prototype.nextMatch = function() { var bat = this.battles[this.currentMatch], res, p1 = bat[0], p2 = bat[1], p1Poke, p2Poke, r, points1 = 0, points2 = 0; var team1 = this.choices[p1]; var team2 = this.choices[p2]; var roundName = "Round " + (this.round + 1) + ", Match " + (this.currentMatch + 1) + ": "; if (this.isThirdPlaceMatch) { roundName = "Third Place Match: "; } else if (this.survivors.length === 2) { roundName = toColor("Final: ", "red"); } this.sendToViewers(""); this.sendToViewers(toColor(separator, "PaleVioletRed")); this.sendToViewers("" + roundName + "" + addFlashTag(p1.toCorrectCase()) + " x " + addFlashTag(p2.toCorrectCase()), true); while (team1.length < 3) { r = this.playerTeams[p1].random(); if (!team1.contains(r)) { team1.push(r); } } while (team2.length < 3) { r = this.playerTeams[p2].random(); if (!team2.contains(r)) { team2.push(r); } } for (var e = 0; e < 3; e++) { p1Poke = team1[e]; p2Poke = team2[e]; res = this.runBattle(p1Poke, p2Poke, p1.toCorrectCase(), p2.toCorrectCase()); if (res === 1) { points1++; } else if (res === 2) { points2++; } } team1 = team1.shuffle(); team2 = team2.shuffle(); if (points1 === points2) { this.sendToViewers(""); this.sendToViewers("Match winner still not defined! Going into tiebreak rounds!"); } for (e = 0; e < 3 && points1 === points2; e++) { p1Poke = team1[e]; p2Poke = team2[e]; res = this.runBattle(p1Poke, p2Poke, p1.toCorrectCase(), p2.toCorrectCase()); if (res === 1) { points1++; } else if (res === 2) { points2++; } } this.totalPoints[p1] += points1; this.totalPoints[p2] += points2; this.lastPoints[p1] = points1; this.lastPoints[p2] = points2; if (points1 === points2) { this.sendToViewers("Players are stalling! Match will be decided with a coin flip!", true); if (Math.random() < 0.5) { points1++; } else { points2++; } } var score = " with a score of " + (points1 > points2 ? points1 + " x " + points2 : points2 + " x " + points1) + ""; var eliminated = this.eliminationOrder[this.round]; if (this.isThirdPlaceMatch) { this.isThirdPlaceMatch = false; if (points1 > points2) { this.thirdPlace = p1; this.sendToViewers(addFlashTag(p1.toCorrectCase()) + " defeated " + addFlashTag(p2.toCorrectCase()) + score + " and got Third Place!", true); } else if (points2 > points1) { this.thirdPlace = p2; this.sendToViewers(addFlashTag(p2.toCorrectCase()) + " defeated " + addFlashTag(p1.toCorrectCase()) + score + " and got Third Place!", true); } } else { if (points1 > points2) { this.sendToViewers(addFlashTag(p1.toCorrectCase()) + " defeated " + addFlashTag(p2.toCorrectCase()) + score + "!", true); this.survivors.splice(this.survivors.indexOf(p2), 1); this.viewers.push(p2); eliminated.push(p2); } else { this.sendToViewers(addFlashTag(p2.toCorrectCase()) + " defeated " + addFlashTag(p1.toCorrectCase()) + score + "!", true); this.survivors.splice(this.survivors.indexOf(p1), 1); this.viewers.push(p1); eliminated.push(p1); } } this.sendToViewers(toColor(separator, "PaleVioletRed")); this.sendToViewers(""); this.currentMatch++; if (this.currentMatch >= this.battles.length) { this.phase = "prepare"; } }; BFactory.prototype.runBattle = function(p1Poke, p2Poke, owner1, owner2) { var res = calcDamage(p1Poke, p2Poke); var name1 = owner1 + "'s " + poke(p1Poke); var name2 = owner2 + "'s " + poke(p2Poke); name1 = res.power[0] > res.power[1] ? "" + name1 + "" : name1; name2 = res.power[1] > res.power[0] ? "" + name2 + "" : name2; var result = name1 + " " + pokeInfo.icon(p1Poke) + " (" + res.power[0] + ") x (" + res.power[1] + ") "+ pokeInfo.icon(p2Poke) + " " + name2; if (res.power[0] == res.power[1]) { result += " Draw"; } this.sendToViewers(result); var status = "Details (" + res.statName + "/Power/Type) | " + poke(p1Poke) + " (" + res.stat[0] + " / " + res.move[0] + " / " + res.bonusString[0] + "x) x " + poke(p2Poke) + " (" + res.stat[1] + " / " + res.move[1] + " / " + res.bonusString[1] + "x)"; this.sendToViewers(toColor(status, "gray")); if (res.power[0] > res.power[1]) { return 1; } else if (res.power[1] > res.power[0]) { return 2; } else { return 0; } }; BFactory.prototype.choosePokemon = function(src, commandData) { var name = sys.name(src).toLowerCase(); if (this.phase === "signup") { this.sendMessage(name, "The event didn't even start yet!"); return; } if (name === this.resting) { this.sendMessage(name, "You are not in any battle during this round!"); return; } var choices = this.choices[name]; if (!choices) { this.sendMessage(name, "Please wait for an opponent to choose your Pokémon!"); return; } if (this.phase !== "input") { this.sendMessage(name, "You can't choose your Pokémon after the round begins!"); return; } var team = this.playerTeams[name]; var list = commandData.split(":"), info, id, e; if (list.length === 1 && !getInputPokemon(list[0]).num) { list = list[0].split(""); } if (list.length > 3) { this.sendMessage(name, "You can only choose up to 3 Pokémon for each match!"); return; } var invalid = [], n; for (e = 0; e < list.length; e++) { n = list[e]; if (n.length === 1 && this.shortcuts[name].hasOwnProperty(n.toLowerCase())) { n = this.shortcuts[name][n.toLowerCase()]; } info = getInputPokemon(n); if (!info.num) { this.sendMessage(name, n + " is not a valid Pokémon!"); return; } id = info.id; if (!team.contains(id)) { invalid.push(info.name); continue; } if (choices.contains(id)) { choices.splice(choices.indexOf(id), 1); } choices.push(id); if (choices.length > 3) { choices.shift(); } } if (invalid.length > 0) { this.sendMessage(name, "You don't have " + readable(invalid) + " available for this event! You can use " + toChooseLink(team) + "!"); } else { this.sendMessage(name, "You picked " + toColor(readable(choices.map(poke)), "blue") + " for your match against " + this.opponents[name].toCorrectCase() + "!"); } }; BFactory.prototype.finish = function() { var winner = this.survivors[0]; var runnerup = this.eliminationOrder[this.round][0]; var thirdplace = this.thirdPlace; var winnerName = winner.toCorrectCase(); var runnerupName = runnerup.toCorrectCase(); var thirdplaceName = thirdplace ? thirdplace.toCorrectCase() : ""; safaribot.sendHtmlAll("" + toColor(winnerName, "blue") + " won the " + this.eventName + " and received " + this.rewardName1 + "!", safchan); var player = getAvatarOff(winner), out, stuff; if (player) { stuff = toStuffObj(this.reward1.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.factoryFirst += 1; safari.saveGame(player); this.sendMessage(winner, "You " + out + "!"); } safaribot.sendHtmlAll(runnerupName + " got the second place and received " + this.rewardName2 + "!", safchan); player = getAvatarOff(runnerup); if (player) { stuff = toStuffObj(this.reward2.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.factorySecond += 1; safari.saveGame(player); this.sendMessage(runnerup, "You " + out + "!"); } if (thirdplace && this.thirdPrize && this.reward3) { safaribot.sendHtmlAll(thirdplaceName + " got the third place and received " + this.rewardName3 + "!", safchan); player = getAvatarOff(thirdplace); if (player) { stuff = toStuffObj(this.reward3.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.factoryThird += 1; safari.saveGame(player); this.sendMessage(thirdplace, "You " + out + "!"); } } else if (this.reward3) { this.sendToViewers("No third place prize will be given due to the low number of participants!"); } this.log(true, "1st: " + winnerName + ", 2nd: " + runnerupName + (thirdplace ? ", 3rd: " + thirdplaceName : "")); this.finished = true; }; BFactory.prototype.canJoin = function(src) { var player = getAvatar(src); if (player.tradeban > now()) { safaribot.sendMessage(src, "You can't join this event while tradebanned!", safchan); return false; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only join this event after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } return true; }; BFactory.prototype.canWatch = function(src) { var name = sys.name(src); var signupsLower = this.survivors.map(function(x) { return x.toLowerCase(); }); if (signupsLower.contains(name.toLowerCase())) { safaribot.sendMessage(src, "You are one of the participants, you can't watch/unwatch this event!", safchan); return false; } return true; }; BFactory.prototype.onWatch = function(src) { safaribot.sendMessage(src, "You are watching the " + this.eventName + "! Current matches:", safchan); if (this.battles.length > 0) { for (var e in this.battles) { safaribot.sendMessage(src, this.battles[e][0].toCorrectCase() + " x " + this.battles[e][1].toCorrectCase(), safchan); } } else { safaribot.sendMessage(src, "Matches will be drawn in a few seconds.", safchan); } }; BFactory.prototype.sendToViewers = function(msg, flashing, colored, nobot, androidAlt) { var e, players = this.phase === "signup" ? this.signups : this.survivors; var list = removeDuplicates(players.concat(this.viewers)); for (e = 0 ; e < list.length; e++) { this.sendMessage(list[e], msg, flashing, colored, nobot, androidAlt); } }; BFactory.prototype.isInEvent = function(name) { var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); var n = name.toLowerCase(); return signupsLower.contains(n) && (this.phase === "signup" || (this.survivors.contains(n) || this.fightingForThird.contains(n))); }; function Quiz(src, reward1, reward2, reward3, silent, official, form) { SafariEvent.call(this, src, form); this.eventName = (silent ? "Hidden " : "") + "Quiz"; this.minPlayers = 2; this.turnLength = 5; this.signupsDuration = 8; this.silentMode = silent; this.official = official ? true : false; if (official) { this.signupsDuration = 12; } this.round = 0; this.phase = "signup"; this.points = {}; this.answered = {}; this.thirdPrize = false; this.currentQuestion = ""; this.validAnswers = []; this.usedAnswers = []; this.playersAnswered = []; this.silentAnswers = {}; this.noSmeargle = false; this.obscure = false; this.obscureAmt = 0; this.answerSpeeds = {}; this.finalAnswerSpeeds = {}; this.finalAnswerSpeedsObscure = {}; this.answeredObscure = {}; this.reward1 = reward1; this.reward2 = reward2; this.reward3 = reward3; this.rewardName1 = translateStuff(reward1); this.rewardName2 = translateStuff(reward2); this.rewardName3 = translateStuff(reward3); this.rewardName = "1st: " + this.rewardName1 + (reward2 ? ", 2nd: " + this.rewardName2 : "") + (reward3 ? ", 3rd: " + this.rewardName3 : ""); this.rewardNameB = "1st: " + this.rewardName1 + (reward2 ? ", 2nd: " + this.rewardName2 : "") + (reward3 ? ", 3rd: " + this.rewardName3 : ""); this.hasReward = true; this.eventCommands = { answer: this.answer, ans: this.answer }; var joinCommand = "/signup"; this.joinmsg = "Type " + link(joinCommand) + " to participate! Rewards: " + this.rewardNameB + "!"; if (official) { sys.sendAll("", safchan); safaribot.sendHtmlAll("The Official " + this.eventName + " event is beginning with the following rewards: " + this.rewardNameB + "!", safchan); safaribot.sendHtmlAll("Type " + link(joinCommand) + " to participate (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); sys.sendAll("", safchan); } else { sys.sendAll("", safchan); safaribot.sendHtmlAll(sys.name(src) + " is starting a " + this.eventName + " event with the following rewards: " + this.rewardNameB + "!", safchan); safaribot.sendHtmlAll("Type " + link(joinCommand) + " to participate (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); sys.sendAll("", safchan); } } Quiz.prototype = new SafariEvent(); Quiz.prototype.setupEvent = function() { this.thirdPrize = this.signups.length > 3; for (var e = 0; e < this.signups.length; e++) { this.points[this.signups[e].toLowerCase()] = 0; this.answered[this.signups[e].toLowerCase()] = 0; this.answerSpeeds[this.signups[e].toLowerCase()] = 0; this.finalAnswerSpeeds[this.signups[e].toLowerCase()] = 0; this.finalAnswerSpeedsObscure[this.signups[e].toLowerCase()] = 0; this.answeredObscure[this.signups[e].toLowerCase()] = 0; } this.sendToViewers(""); safaribot.sendHtmlAll("The " + this.eventName + " is starting now! If you didn't join, you still can watch by typing " + link("/watch") + "!", safchan); this.sendToViewers("Your goal is to answer each round's question with a Pokémon name (forms are not accepted). Use " + toColor("/ans [Pokémon]", "blue") + " for that!"); if (this.silentMode) { this.sendToViewers("Answers are only revealed when each round finishes. Alternative forms are not accepted. Repeated answers give less points. " + toColor("Please don't give hints or answers to the other players!", "red") + "!"); } else { this.sendToViewers("One answer per player, repeated answers are not accepted. Event lasts for 10 rounds, fastest to answer gains more points. " + toColor("Please don't give hints or answers to the other players", "red") + "!"); } this.sendToViewers(""); this.phase = "preparing"; }; Quiz.prototype.playTurn = function() { if (this.phase === "answer2") { this.sendToViewers("Only 5 seconds remaining to answer!"); this.phase = "answer3"; return; } if (this.phase === "answer") { this.phase = "answer2"; return; } if (this.round === 10) { //Check if event is over here if (this.silentMode) { this.countSilentPoints(); } this.finish(); return; } if (this.phase === "preparing" || this.phase === "answer3") { this.sendToViewers((this.round === 0 ? "First" : "Time's Over! Next") + " question coming in 5 seconds!"); if (this.silentMode && this.round > 0) { this.countSilentPoints(); } this.phase = "question"; return; } if (this.phase === "question") { //Prepare next question var list = []; this.validAnswers = []; for (var e = 1; e < highestDexNum; e++) { list.push(e); } var typesUsed = [], result, desc = [], e; do { result = this.filter(list.concat(), typesUsed); } while (result.list.length < 3); desc.push(result.desc); typesUsed.push(result.type); this.validAnswers = result.list; //Second parameter if (this.validAnswers.length > 15) { for (e = 0; e < 30; e++) { result = this.filter(this.validAnswers.concat(), typesUsed); if (result.list.length > 3 && result.list.length !== this.validAnswers.length) { this.validAnswers = result.list.concat(); desc.push(result.desc); typesUsed.push(result.type); break; } } } //Third parameter if (this.validAnswers.length > 12) { for (e = 0; e < 16; e++) { result = this.filter(this.validAnswers.concat(), typesUsed); if (result.list.length > 3 && result.list.length !== this.validAnswers.length) { this.validAnswers = result.list.concat(); desc.push(result.desc); typesUsed.push(result.type); break; } } } this.noSmeargle = false; for (e = 0; e < typesUsed.length; e++) { if (typesUsed[e] === "move") { this.noSmeargle = true; break; } } this.obscure = false; for (e = 0; e < typesUsed.length; e++) { if (typesUsed[e] === "weight" || typesUsed[e] === "height") { this.obscure = true; this.obscureAmt++; break; } } this.currentQuestion = cap(readable(desc)); this.usedAnswers = []; this.playersAnswered = []; this.round++; this.sendToViewers(""); this.sendToViewers("Round " + this.round + ": Use /ans [Pokémon] to say a Pokémon with these features: " + toColor(this.currentQuestion, "blue") + " (You have 15 seconds)!"); this.sendToViewers(""); this.phase = "answer"; this.questionTime = now() } }; Quiz.prototype.countSilentPoints = function() { var answers = {}, ans = this.silentAnswers, e, p, c, list; for (e in ans) { if (!answers.hasOwnProperty(ans[e])) { answers[ans[e]] = []; } answers[ans[e]].push(e); } var toCC = function(x) { return "" + x.toCorrectCase() + ""; }; for (e in answers) { list = answers[e]; ans = pokePlain(e); p = Math.round(100/list.length); this.sendToViewers(readable(list.map(toCC).map(addFlashTag)) + " answered " + toColor(ans, "blue") + " and received " + plural(p, "point") + (list.length > 1 ? " each" : "") + "!", true); for (c = list.length; c--; ) { this.points[list[c]] += p; this.answered[list[c]] += 1; this.finalAnswerSpeeds[list[c]] += this.answerSpeeds[list[c]]; if (this.obscure) { this.finalAnswerSpeedsObscure[list[c]] += this.answerSpeeds[list[c]]; this.answeredObscure[list[c]] += 1; } } } this.silentAnswers = {}; }; Quiz.prototype.filter = function(list, typesUsed) { var type, val2, val, desc; var types = { "start": 9, "end": 9, "move": 28, "type": 16, "bst": 3, "gen": 6, "color": 6, "height": 3, "weight": 3, "evolve": 2, "evolved": 2 }; do { type = ["start", "start", "end", "move", "type", "bst", "gen", "color", "height", "weight", "evolve", "evolved"].random(); type = randomSample(types); } while (type !== "move" && typesUsed.contains(type)); switch (type) { case "start": val = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"].random(); list = list.filter(function(x){ return pokePlain(x).toLowerCase()[0] === val; }); desc = "starts with " + val.toUpperCase(); break; case "end": val = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"].random(); list = list.filter(function(x){ var n = pokePlain(x).toLowerCase(); return n[n.length-1] === val; }); desc = "ends with " + val.toUpperCase(); break; case "move": val = sys.rand(1, 706); list = list.filter(function(x){ return canLearnMove(x, val); }); desc = "can learn " + moveOff(val); break; case "type": val = Object.keys(effectiveness).random(); list = list.filter(function(x){ return hasType(x, val); }); desc = "has " + val + " type"; break; case "gen": val = generations.slice(1).random(); list = list.filter(function(x){ return generation(x, true) === val; }); desc = "is from the " + val + " region"; break; case "color": val = Object.keys(pokeColors).random(); list = list.filter(function(x){ return getPokeColor(x) === val; }); desc = "is " + val + "-colored"; break; case "bst": val = sys.rand(200, 515); val2 = sys.rand(val + 18, val + 98); list = list.filter(function(x){ return getBST(x) >= val && getBST(x) <= val2; }); desc = "BST is between " + val + " and " + val2; break; case "weight": val = sys.rand(4, 125); val2 = sys.rand(val + 8, val + 70); list = list.filter(function(x){ return parseFloat(getWeight(x)) >= val && parseFloat(getWeight(x)) <= val2; }); desc = "weight is between " + val + "~" + val2 + " kg / " + (val * 2.20462).toFixed(1) + "~" + (val2 * 2.20462).toFixed(1) + " lbs"; break; case "height": val = sys.rand(2, 17); val2 = sys.rand(val + 3, val + 10); val = val/10; val2 = val2/10; list = list.filter(function(x){ return parseFloat(getHeight(x)) >= val && parseFloat(getHeight(x)) <= val2; }); desc = "height is between " + val + "m and " + val2 + "m"; break; case "evolve": val = chance(0.5) ? true : false; list = list.filter(function(x){ return (x in evolutions) === val; }); desc = "can" + (val ? "" : "not")+ " evolve"; break; case "evolved": val = chance(0.5) ? true : false; list = list.filter(function(x){ return (x in devolutions) === val; }); desc = "is " + (val ? "" : "not ")+ "evolved"; break; } return { list: list, type: type, val: val, desc: desc }; }; Quiz.prototype.answer = function(src, commandData) { var name = sys.name(src).toLowerCase(); if (this.phase === "signup") { this.sendMessage(name, "The event didn't even start yet!"); return; } if (["answer", "answer2", "answer3"].contains(this.phase) === false) { this.sendMessage(name, "Wait for the next question!"); return; } if (this.playersAnswered.contains(name)) { this.sendMessage(name, "You already answered during this round! Wait for the next question!"); return; } var info = getInputPokemon(commandData); if (!info.num) { this.sendMessage(name, "This is not a valid Pokémon!"); return; } if (this.usedAnswers.contains(info.num)) { this.sendMessage(name, info.name + " was already answered!"); return; } if (pokeInfo.forme(info.num) > 0) { this.sendMessage(name, "Forms are not accepted as answers!"); return; } if (!this.validAnswers.contains(info.num)) { this.sendMessage(name, info.name + " doesn't fit all the criteria for this round (criteria required: " + this.currentQuestion + ")!"); return; } if (this.noSmeargle && info.num === 235) { this.sendMessage(name, info.name + " is not allowed when the hints include moves!"); return; } if (this.silentMode) { if (this.silentAnswers.hasOwnProperty(name)) { this.sendMessage(name, "You changed your answer to " + toColor(info.name, "blue") + "!"); if (this.official) { this.answerSpeeds[name] = (now() - this.questionTime); } } else { this.sendMessage(name, "You answered " + toColor(info.name, "blue") + "!"); } this.silentAnswers[name] = info.num; } else { var pointsRange = [100, 85, 75, 60, 50, 40, 30, 20, 15, 12]; var points = pointsRange[Math.min(this.playersAnswered.length, pointsRange.length-1)]; this.playersAnswered.push(name); this.usedAnswers.push(info.num); this.points[name] += points; this.answered[name] += 1; this.sendToViewers(addFlashTag(name.toCorrectCase()) + " answered " + toColor(info.name, "blue") + " and got " + plural(points, "point") + "!", true); } }; Quiz.prototype.finish = function() { var ordered = Object.keys(this.points); var pts = this.points; var self = this; ordered = ordered.sort(function(a, b) { return self.finalAnswerSpeeds[a] - self.finalAnswerSpeeds[b]; }); ordered = ordered.sort(function(a, b) { return pts[b] - pts[a]; }); var bestScores = []; for (var p in this.points) { if (!bestScores.contains(this.points[p])) { bestScores.push(this.points[p]); } } bestScores = bestScores.sort(function(a, b){return b-a;}); bestScores.push(0, 0); this.sendToViewers(""); this.sendToViewers("Final Score: " + ordered.map(function(x) { return addFlashTag(x.toCorrectCase()) + " (" + plural(pts[x], "point") + ")"; }).join(", "), true); if (bestScores[0] === 0) { safaribot.sendHtmlAll("No one got any point in the " + this.eventName + ", so no rewards will be given!", safchan); this.log(true, "No winners"); this.finished = true; return; } /* var winner = ordered.filter(function(x) { return pts[x] === bestScores[0]; }); var runnerup = ordered.filter(function(x) { return pts[x] === bestScores[1]; }); var thirdplace = ordered.filter(function(x) { return pts[x] === bestScores[2]; }); */ var winner = ordered[0] !== undefined ? [ordered[0]] : []; var runnerup = ordered[1] !== undefined ? [ordered[1]] : []; var thirdplace = ordered[2] !== undefined ? [ordered[2]] : []; var toCC = function(x) { return x.toCorrectCase(); }; var winnerName = readable(winner.map(toCC)); var runnerupName = readable(runnerup.map(toCC)); var thirdplaceName = readable(thirdplace.map(toCC)); safaribot.sendHtmlAll("" + toColor(winnerName, "blue") + " won the " + this.eventName + " and received " + this.rewardName1 + "!", safchan); var player, id, out, stuff; for (p = 0; p < winner.length; p++) { id = winner[p]; player = getAvatarOff(id); if (player) { stuff = toStuffObj(this.reward1.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.quizFirst += 1; if (!this.silentMode) { player.records.topQuizScore = Math.max(player.records.topQuizScore, pts[id]); } safari.saveGame(player); this.sendMessage(id, "You " + out + "!"); } } if (runnerup.length > 0) { safaribot.sendHtmlAll(runnerupName + " got the second place and received " + this.rewardName2 + "!", safchan); for (p = 0; p < runnerup.length; p++) { id = runnerup[p]; player = getAvatarOff(id); if (player) { stuff = toStuffObj(this.reward2.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.quizSecond += 1; if (!this.silentMode) { player.records.topQuizScore = Math.max(player.records.topQuizScore, pts[id]); } safari.saveGame(player); this.sendMessage(id, "You " + out + "!"); } } } if (this.thirdPrize && this.reward3) { if (thirdplace.length > 0) { safaribot.sendHtmlAll(thirdplaceName + " got the third place and received " + this.rewardName3 + "!", safchan); for (p = 0; p < thirdplace.length; p++) { id = thirdplace[p]; player = getAvatarOff(id); if (player) { stuff = toStuffObj(this.reward3.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.quizThird += 1; if (!this.silentMode) { player.records.topQuizScore = Math.max(player.records.topQuizScore, pts[id]); } safari.saveGame(player); this.sendMessage(id, "You " + out + "!"); } } } } else if (this.reward3) { this.sendToViewers("No third place prize will be given due to the low number of participants!"); } this.sendToViewers(""); this.log(true, "1st: " + winnerName + ", 2nd: " + runnerupName + (thirdplaceName ? ", 3rd: " + thirdplaceName : "")); this.finished = true; if (this.official) { safari.quizRankUpdate(this.points); for (var e = 0; e < this.signups.length; e++) { var player = getAvatarOff(this.signups[e]); var name = this.signups[e].toLowerCase(); if (!(player.hiddenQuiz.hasOwnProperty("responseData"))) { player.hiddenQuiz.responseData = { hit: 0, tried: 0, hitRate: 0, obscureHit: 0, obscureTried: 0, obscureHitRate: 0, responseSpeed: 0, obscureResponseSpeed: 0, obscureAnswerTime: 0, answerTime: 0 } } player.hiddenQuiz.responseData.hit += this.answered[name]; player.hiddenQuiz.responseData.tried += 10; player.hiddenQuiz.responseData.hitRate = (Math.round((player.hiddenQuiz.responseData.hit * 100) / player.hiddenQuiz.responseData.tried)); player.hiddenQuiz.responseData.obscureHit += this.answeredObscure[name]; player.hiddenQuiz.responseData.obscureTried += this.obscureAmt; if (player.hiddenQuiz.responseData.obscureTried > 0) { player.hiddenQuiz.responseData.obscureHitRate = (Math.round((player.hiddenQuiz.responseData.obscureHit * 100) / player.hiddenQuiz.responseData.obscureTried)); } if (player.hiddenQuiz.responseData.hit > 0) { player.hiddenQuiz.responseData.answerTime += this.finalAnswerSpeeds[name]; player.hiddenQuiz.responseData.responseSpeed = (Math.round((player.hiddenQuiz.responseData.answerTime * 100) / player.hiddenQuiz.responseData.hit)); } if (player.hiddenQuiz.responseData.obscureHit > 0) { player.hiddenQuiz.responseData.obscureAnswerTime += this.finalAnswerSpeedsObscure[name]; player.hiddenQuiz.responseData.obscureResponseSpeed = (Math.round((player.hiddenQuiz.responseData.obscureAnswerTime * 100) / player.hiddenQuiz.responseData.obscureHit)); } safari.saveGame(player); } } }; Quiz.prototype.canJoin = function(src) { var player = getAvatar(src); if (player.tradeban > now()) { safaribot.sendMessage(src, "You can't join this event while tradebanned!", safchan); return false; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only join this event after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } return true; }; Quiz.prototype.onWatch = function(src) { safaribot.sendHtmlMessage(src, "You are watching the " + this.eventName + "! " + toColor("Please don't give hints or answers to the participants", "red") + "!", safchan); if (["answer", "answer2", "answer3"].contains(this.phase)) { safaribot.sendMessage(src, "Current round's question: " + this.currentQuestion, safchan); } }; function PokeRace(src, type, data, form) { SafariEvent.call(this, src, form); this.type = type; this.eventName = "Pokémon " + (type === "bet" ? "Bet " : "") + "Race"; this.runners = {}; this.bets = {}; this.goal = 50; this.turnLength = 6; this.signupsDuration = 7; this.minPlayers = 1; this.allowRejoin = true; this.betList = []; this.payouts = {}; var r; if (data.racers) { var racers = data.racers.split(",").map(function(x){ return x.trim(); }); var list = [], p; for (r = 0; r < racers.length && list.length < 6; r++) { p = getInputPokemon(racers[r]); if (p.num && !list.contains(p.name)) { list.push(pokePlain(p.num)); } } for (r = 0; r < list.length; r++) { this.runners[list[r]] = 0; } } while (Object.keys(this.runners).length < 6) { r = pokePlain(sys.rand(1, highestDexNum)); if (!(r in this.runners)) { this.runners[r] = 0; } } if (type === "bet") { this.minBet = data.minBet || 10; this.maxBet = data.maxBet || 1000; this.underdogPay = data.underdog || 8; this.favoritePay = data.favorite || 1.35; this.normalPay = data.normal || 4; this.bet = data.bet || "$1"; this.reward = data.reward || "$1"; this.betType = translateAsset(this.bet).type; this.item = this.betType === "item" ? translateAsset(this.bet).id : null; this.betRange = this.item ? "" + (this.minBet) + " and " + plural(this.maxBet, this.item) + "" : "$" + addComma(this.minBet) + " and $" + addComma(this.maxBet); this.favorite = Object.keys(this.runners).random(); do { this.underdog = Object.keys(this.runners).random(); } while (this.underdog == this.favorite); } else { this.reward = data.reward; this.favorite = null; this.underdog = null; if (data.rewardUnderdog) { this.rewardUnderdog = data.rewardUnderdog; this.underdog = Object.keys(this.runners).random(); } if (data.rewardFavorite) { this.rewardFavorite = data.rewardFavorite; do { this.favorite = Object.keys(this.runners).random(); } while (this.favorite == this.underdog); } } this.rewardObj = translateAsset(this.reward); this.rewardType = this.rewardObj.type; this.racersList = Object.keys(this.runners).map(function(x) { if (x == this.favorite) { return toColor(x, "red") + " (Favorite)"; } else if (x == this.underdog) { return toColor(x, "red") + " (Underdog)"; } else { return toColor(x, "red"); } }, this); this.icons = {}; for (var e in this.runners) { this.icons[e] = pokeInfo.icon(getInputPokemon(e).num); } var joinCommand = "/signup"; var betCommands = Object.keys(this.runners).map(function(x) { if (type === "bet") { return link(joinCommand + " " + x + ":" + this.minBet, null, true); } else { return link(joinCommand + " " + x); } }, this); if (type === "bet") { if (this.betType === this.rewardType && (this.betType === "money" || (this.betType === "item" && this.item === this.rewardObj.id))) { this.payoutmsg = "Favorite (" + this.favorite + ") " + this.favoritePay + "x, Underdog (" + this.underdog + ") " + this.underdogPay + "x, Others " + this.normalPay + "x"; this.rewardName = "Favorite: {0}x / Underdog: {1}x / Normal: {2}x".format(this.favoritePay, this.underdogPay, this.normalPay); } else { var favPay, dogPay, norPay, val, itemName = this.betType === "item" ? " " + finishName(this.item) : " $1", betType = this.betType, rew = this.rewardObj; var getPayout = function(value) { if (value >= 1) { return (rew.type !== "money" ? plural(value, rew.input) : "$" + addComma(value)) + " for each" + itemName; } else { val = Math.round(1/value); return (rew.type !== "money" ? plural(1, rew.input) : "$" + addComma(1)) + " for each " + (betType === "money" ? "$" + addComma(val) : val + itemName ); } }; favPay = getPayout(this.favoritePay); dogPay = getPayout(this.underdogPay); norPay = getPayout(this.normalPay); this.payoutmsg = "Favorite (" + this.favorite + ") " + favPay + ", Underdog (" + this.underdog + ") " + dogPay + ", Others " + norPay; this.rewardName = "Favorite: {0} / Underdog: {1} / Normal: {2}".format(favPay, dogPay, norPay); } this.joinmsg = "Bet with " + readable(betCommands, "or") + "! Bets must be between " + this.betRange + " (Payout: " + this.payoutmsg + ")"; } else { this.joinmsg = "Join with " + readable(betCommands, "or"); this.rewardName = translateStuff(this.reward); if (this.rewardFavorite || this.rewardUnderdog) { var extraRewards = []; if (this.rewardUnderdog) { extraRewards.push(translateStuff(this.rewardUnderdog) + " if Underdog"); } if (this.rewardFavorite) { extraRewards.push(translateStuff(this.rewardFavorite) + " if Favorite"); } this.rewardName += " (" + extraRewards.join(", ") + ")"; } } sys.sendAll("", safchan); safaribot.sendHtmlAll(sys.name(src) + " is starting a " + this.eventName + " event! The contestants will be " + readable(this.racersList, "and") + "!", safchan); if (type === "bet") { safaribot.sendHtmlAll("Bets must be between " + this.betRange + "! Payouts are " + this.payoutmsg + "!", safchan); safaribot.sendHtmlAll("To place your bets, type " + readable(betCommands, "or") + " (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); } else { safaribot.sendHtmlAll("Rewards: " + this.rewardName + "! To join, type " + readable(betCommands, "or") + " (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); } sys.sendAll("", safchan); } PokeRace.prototype = new SafariEvent(); PokeRace.prototype.setupEvent = function() { var e, bet; this.sendToViewers(""); safaribot.sendHtmlAll("The " + this.eventName + " is starting now! If you didn't join, you still can watch by typing " + link("/watch") + "!", safchan); for (e in this.bets) { if (!this.signups.contains(e)) { delete this.bets[e]; } else { bet = this.bets[e]; if (this.type === "bet") { this.betList.push(addColorTag(e) + " bets " + (this.item ? plural(bet.bet, this.item) : "$" + addComma(bet.bet)) + " on " + bet.racer + (bet.racer == this.favorite ? " (Favorite)" : (bet.racer == this.underdog ? " (Underdog)" : "") )); } else { this.betList.push(addColorTag(e) + " bets on " + bet.racer + (bet.racer == this.favorite ? " (Favorite)" : (bet.racer == this.underdog ? " (Underdog)" : "") )); } } } this.sendToViewers("Bets: " + this.betList.join(", "), false, true); this.sendToViewers("Preparations are complete, the race will start shortly!"); this.sendToViewers(""); }; PokeRace.prototype.playTurn = function() { var r, w, passed = []; this.sendToViewers(""); for (r in this.runners) { switch (r) { case this.underdog: w = sys.rand(1, 10); break; case this.favorite: w = sys.rand(2, 11); break; default: w = sys.rand(1, 11); } this.runners[r] += w; if (this.runners[r] >= this.goal) { passed.push(r); } this.sendToViewers(r + " advanced " + w + " spaces and is now at space " + this.runners[r] + "m!"); } var line = [" | Goal | "], spaceUsed; for (w = this.goal-1; w >= 0; w--) { spaceUsed = false; for (r in this.runners) { if (this.runners[r] == w) { line.push(this.icons[r]); spaceUsed = true; } } if (spaceUsed) { line.push(w +"m "); } line.push("-"); } line.push(" | Start | "); var runners = this.runners; passed = passed.sort(function(a, b) { return runners[b] - runners[a]; }).map(function(x) { return this.icons[x] + this.runners[x] + "m"; }, this); line = passed.concat(line); this.sendToViewers("Standings: " + line.join("")); this.sendToViewers(""); var winners = [], highest = this.goal; for (r in this.runners) { if (this.runners[r] >= highest) { if (this.runners[r] > highest) { highest = this.runners[r]; winners = []; } winners.push(r); } } if (winners.length > 0) { this.finish(winners); } }; PokeRace.prototype.finish = function(winners) { this.sendToViewers(""); this.sendToViewers(readable(winners.map(function(x){ return "" + x + "" + (x === this.favorite ? " (Favorite)" : "") + (x === this.underdog ? " (Underdog)" : ""); }, this), "and") + " won the " + this.eventName + "!"); var pwinners = [], player, r, bet, betStr = [], payments = []; for (r in this.bets) { bet = this.bets[r]; if (winners.contains(bet.racer)) { pwinners.push(r); } else if (this.type === "bet"){ player = getAvatarOff(r); if (player) { if (this.item) { player.balls[this.item] -= bet.bet; } else { player.money -= bet.bet; } safari.saveGame(player); this.sendMessage(r, "You lost " + (this.item ? plural(bet.bet, this.item) : "$" + addComma(bet.bet)) + " from your losing bet!"); } } if (this.type === "bet") { betStr.push(r + " (" + (this.item ? plural(bet.bet, this.item) : "$" + addComma(bet.bet)) + ")"); } } if (pwinners.length > 0) { var payouts = this.payouts; var betType = this.type === "bet"; this.sendToViewers("The following players placed a bet on the winner{0} and won the event: {1}".format((winners.length == 1 ? "" : "s"), readable(pwinners.map(function(x) { return "" + addFlashTag(x) + "" + " (" + payouts[x.toLowerCase()] + ")"; }), "and")), true); var prize, name, bet, stuff, out, p, rewName, discarded; for (r = 0 ; r < pwinners.length; r++) { name = pwinners[r]; bet = this.bets[name]; if (betType) { switch (bet.racer) { case this.underdog: prize = Math.floor(bet.bet * this.underdogPay); break; case this.favorite: prize = Math.floor(bet.bet * this.favoritePay); break; default: prize = Math.floor(bet.bet * this.normalPay); } } player = getAvatarOff(name); if (player) { if (betType) { if (this.item) { player.balls[this.item] -= bet.bet; if (player.balls[this.item] < 0) { player.balls[this.item] = 0; } } else { player.money -= bet.bet; if (player.money < 0) { player.money = 0; } } discarded = 0; if (this.rewardType == "item") { stuff = this.rewardObj; player.balls[stuff.id] += prize; if (player.balls[stuff.id] > getCap(stuff.id)) { discarded = player.balls[stuff.id] - getCap(stuff.id); player.balls[stuff.id] = getCap(stuff.id); } if (stuff.id === "silver") { player.records.pokeRaceSilver += prize; } } else if (this.rewardType == "poke") { for (p = 0; p < prize; p++) { player.pokemon.push(this.rewardObj.id); } } else { player.money += prize; if (player.money > moneyCap) { player.money = moneyCap; } player.records.pokeRaceEarnings += prize - (this.item ? 0 : bet.bet); } player.records.pokeRaceWins += 1; if (bet.racer == this.favorite) { player.records.favoriteRaceWins += 1; } else if (bet.racer == this.underdog) { player.records.underdogRaceWins += 1; } if (this.item) { this.sendMessage(name, "You paid " + plural(bet.bet, this.item) + " as your bet!"); } else { this.sendMessage(name, "You paid $" + addComma(bet.bet) + " as your bet!"); } rewName = (this.rewardType === "money" ? "$" + addComma(prize) : plural(prize, this.rewardObj.input)); if (discarded > 0) { discarded = "But you didn't have enough space, so you had to discard " + plural(discarded, this.rewardObj.id) + "! "; } else { discarded = ""; } this.sendMessage(name, "You received " + rewName + " for winning this event! " + discarded + (this.rewardType !== "poke" ? "You now have " + (this.rewardType === "money" ? "$" + addComma(player.money) : plural(player.balls[this.rewardObj.id], this.rewardObj.id)) : "") + "!"); payments.push(name.toCorrectCase() + " (" + rewName + ")"); } else { player.records.pokeRaceWins += 1; if (bet.racer == this.favorite) { p = this.rewardFavorite; player.records.favoriteRaceWins += 1; } else if (bet.racer == this.underdog) { p = this.rewardUnderdog; player.records.underdogRaceWins += 1; } else { p = this.reward; } stuff = toStuffObj(p.replace(/,/g, ":")); out = giveStuff(player, stuff); if ("$" in stuff) { player.records.pokeRaceEarnings += stuff.$; } if ("@silver" in stuff) { player.records.pokeRaceSilver += stuff["@silver"]; } this.sendMessage(name, "You " + out + "!"); payments.push(name.toCorrectCase() + (this.rewardUnderdog || this.rewardFavorite ? " (" + translateStuff(stuff) + ")" : "")); } safari.saveGame(player); } } } else { this.sendToViewers("No one placed a winning bet on this " + this.eventName + "! Better luck next time!"); } this.sendToViewers(""); this.finished = true; this.log(true, payments, (this.type === "bet" ? "Bets: " + betStr.join(", ") : "")); }; PokeRace.prototype.canJoin = function(src, data) { var player = getAvatar(src); if (player.tradeban > now()) { safaribot.sendMessage(src, "You can't join this event while tradebanned!", safchan); return false; } var info = data.split(":"); if (this.type === "bet" && info.length < 2) { safaribot.sendMessage(src, "Invalid format! Type /signup [Pokémon]:[Bet] to join!", safchan); return false; } var racer = getInputPokemon(info[0]); if (!racer.num) { safaribot.sendHtmlMessage(src, "This is not a valid Pokémon! Contestants are " + readable(this.racersList, "and") + "!", safchan); return false; } if (!this.runners.hasOwnProperty(racer.name)) { safaribot.sendHtmlMessage(src, racer.name + " is not participating in this race! Contestants are " + readable(this.racersList, "and") + "!", safchan); return false; } if (this.type === "bet") { var bet = parseInt(info[1], 10); if (!bet || isNaN(bet)) { safaribot.sendMessage(src, "Please type a valid bet!", safchan); return false; } if (bet < this.minBet || bet > this.maxBet) { safaribot.sendHtmlMessage(src, "Bets must be between " + this.betRange + "!", safchan); return false; } if (this.item && player.balls[this.item] < bet) { safaribot.sendMessage(src, "You don't have " + plural(bet, this.item) + " to bet!", safchan); return false; } else if (player.money < bet) { safaribot.sendMessage(src, "You don't have $" + addComma(bet) + " to bet!", safchan); return false; } if (this.item) { var input = "@" + this.item; if (input in player.shop && player.shop[input].limit > player.balls[this.item] - bet) { safaribot.sendMessage(src, "You need to remove that item from your shop before betting it!", safchan); return false; } } } return true; }; PokeRace.prototype.onWatch = function(src) { safaribot.sendMessage(src, "You are watching the " + this.eventName + "!", safchan); safaribot.sendHtmlMessage(src, "Contestants: " + readable(this.racersList, "and"), safchan); if (this.type === "bet") { this.sendMessage(sys.name(src), "Bets: " + this.betList.join(", "), false, true); } }; PokeRace.prototype.onJoin = function(name, data) { var info = data.split(":"); var racer = getInputPokemon(info[0]).name; var bet = this.type === "bet" ? parseInt(info[1], 10) : 0; this.bets[name] = { bet: bet, racer: racer }; var payout = 0; switch (racer) { case this.favorite: payout = bet * this.favoritePay; break; case this.underdog: payout = bet * this.underdogPay; break; default: payout = bet * this.normalPay; } payout = this.rewardType === "money" ? "$" + addComma(Math.floor(payout)) : plural(Math.floor(payout), this.rewardObj.input); if (this.type === "bet") { this.payouts[name.toLowerCase()] = payout; return " by betting " + (this.item ? plural(bet, this.item) : "$" + addComma(bet)) + " on " + racer + " (Payout: " + payout + ")"; } else { this.payouts[name.toLowerCase()] = payout = translateStuff(this.getReward(racer)); return " by betting on " + racer + " (Payout: " + payout + ")"; } }; PokeRace.prototype.getReward = function(racer) { if (racer == this.favorite) { return this.rewardFavorite; } else if (racer == this.underdog) { return this.rewardUnderdog; } else { return this.reward; } }; PokeRace.prototype.onLeave = function(name) { delete this.bets[name]; }; function Bingo(src, reward1, reward2, reward3, goal, form) { SafariEvent.call(this, src, form); this.eventName = "Bingo"; this.minPlayers = 3; this.turnLength = 5; this.signupsDuration = 7; this.round = 0; this.subturn = 0; this.phase = "signup"; this.goal = goal || 1; this.winners = []; this.remainingNumbers = []; this.cards = {}; this.shouts = {}; this.cooldowns = {}; this.watchTargets = {}; if (reward3 && !reward2) { reward2 = reward3; reward3 = null; } this.reward1 = reward1; this.reward2 = reward2; this.reward3 = reward3; this.rewardName1 = translateStuff(reward1); this.rewardName2 = translateStuff(reward2); this.rewardName3 = translateStuff(reward3); this.maxWinners = 1 + (reward2 ? 1 : 0) + (reward3 ? 1 : 0); this.rewardName = "1st: " + this.rewardName1 + (reward2 ? ", 2nd: " + this.rewardName2 : "") + (reward3 ? ", 3rd: " + this.rewardName3 : ""); this.rewardNameB = "1st: " + this.rewardName1 + (reward2 ? ", 2nd: " + this.rewardName2 : "") + (reward3 ? ", 3rd: " + this.rewardName3 : ""); this.hasReward = true; this.eventCommands = { choose: this.chooseSlot, bingo: this.sayBingo }; var joinCommand = "/signup"; this.joinmsg = "Type " + link(joinCommand) + " to participate! Rewards: " + this.rewardNameB + "!"; sys.sendAll("", safchan); safaribot.sendHtmlAll(sys.name(src) + " is starting a " + this.eventName + " event (Goal: " + plural(this.goal, "line") + ") with the following reward: " + this.rewardNameB + "!", safchan); safaribot.sendHtmlAll("Type " + link(joinCommand) + " to participate (you have " + (this.signupsDuration * this.turnLength) + " seconds)!", safchan); sys.sendAll("", safchan); } Bingo.prototype = new SafariEvent(); Bingo.prototype.setupEvent = function() { var n, size = this.signups.length; if (this.reward3 && size < 6) { this.maxWinners = 2; this.reward3 = null; this.noThirdPrize = true; } if (this.reward2 && size < 4) { this.maxWinners = 1; this.reward2 = null; this.noSecondPrize = true; } for (var e = 0; e < size; e++) { n = this.signups[e].toLowerCase(); this.cards[n] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; this.shouts[n] = 0; this.cooldowns[n] = 0; } do { n = sys.rand(1, highestDexNum); if (!this.remainingNumbers.contains(n)) { this.remainingNumbers.push(n); } } while (this.remainingNumbers.length < 40); this.sendToViewers(""); safaribot.sendHtmlAll("The " + this.eventName + " is starting now! If you didn't join, you still can watch by typing " + link("/watch") + "!", safchan); this.sendToViewers("Your goal is to complete " + plural(this.goal, "line") + " in your card with the Pokémon drawn (lines can be horizontal, vertical or diagonal). Once you do, type " + toColor("/bingo", "blue") + " to claim your win!"); this.sendToViewers("You can choose the location for up to 3 Pokémon in your card (the rest will be defined randomly). Use " + link("/choose Pokémon:Slot") + " for that."); var appendSlot = function(x) { if (x < 10) { x = "0" + x; } return "[" + x +"]"; }; this.sendToViewers([ 1, 2, 3, 4, 5].map(appendSlot).join(" ")); this.sendToViewers([ 6, 7, 8, 9,10].map(appendSlot).join(" ")); this.sendToViewers([11,12,13,14,15].map(appendSlot).join(" ")); this.sendToViewers([16,17,18,19,20].map(appendSlot).join(" ")); this.sendToViewers([21,22,23,24,25].map(appendSlot).join(" ")); this.sendToViewers("Pokémon available: " + readable(this.remainingNumbers.map(function(x) { return link("/choose " + poke(x) + ":", poke(x), true); }))); this.sendToViewers(""); this.phase = "preparing"; this.subturn = 4; }; Bingo.prototype.playTurn = function() { if (this.finished) { return; } if (this.phase === "preparing") { if (this.subturn >= 0) { this.subturn--; } else { for (var n in this.cards) { this.fillCard(n); } this.sendToViewers("Cards are filled now! Numbers will start to be drawn soon!"); this.phase = "playing"; this.round++; } return; } if (this.phase === "playing") { if (this.remainingNumbers.length === 0) { this.sendToViewers("Game is finishing because all numbers have been drawn!"); this.finish(); return; } if (this.subturn > 0) { this.subturn--; return; } this.remainingNumbers = this.remainingNumbers.shuffle(); var drawn = this.remainingNumbers.shift(); this.sendToViewers(""); this.sendToViewers(toColor("Pokémon Drawn: < " + pokeInfo.icon(drawn) + " " + poke(drawn) + " >", "blue"), null, null, true); for (var e in this.cards) { if (!this.winners.contains(e)) { this.showCard(e); this.sendMessage(e, "If you completed " + plural(this.goal, "line") + ", type " + link("/bingo") + "!"); } } for (e in this.watchTargets) { this.showCard(this.watchTargets[e], e); } this.subturn = 1; this.round++; return; } }; Bingo.prototype.chooseSlot = function(src, commandData) { var name = sys.name(src).toLowerCase(); if (this.phase !== "preparing") { this.sendMessage(name, "The event already started!"); return; } var card = this.cards[name]; var count = card.filter(function(x) { return x !== 0; }).length; if (count >= 3) { this.sendMessage(name, "You already added 3 Pokémon to your card!"); return; } var info = toCommandData(commandData, ["id", "slot"]); if (!info.id || !info.slot) { this.sendMessage(name, "Use " + link("/choose Pokémon:Slot") + " to choose a slot for a Pokémon."); this.sendMessage(name, "Available Pokémon: " + readable(this.remainingNumbers.map(poke)) + "."); return; } var id = getInputPokemon(info.id); if (!id.num || !this.remainingNumbers.contains(id.num)) { this.sendMessage(name, "Invalid Pokémon! Available Pokémon are: " + readable(this.remainingNumbers.map(poke)) + "."); return; } var slot = parseInt(info.slot, 10); if (isNaN(slot) || slot < 1 || slot > 25) { this.sendMessage(name, "Slot must be a number between 1 and 25!"); return; } if (card[slot-1] !== 0) { this.sendMessage(name, "You already added " + poke(card[slot-1]) + " to slot " + slot + "!"); return; } if (card.contains(id.num)) { this.sendMessage(name, "You already added " + id.name + " to slot " + (card.indexOf(id.num)+1) + "!"); return; } card[slot-1] = id.num; this.sendMessage(name, "You added " + id.name + " to slot " + slot + "!"); }; Bingo.prototype.showCard = function(name, viewer) { if (!viewer) { viewer = name; } var card = this.cards[name]; var remain = this.remainingNumbers; var showSlot = function(n) { var c = remain.contains(n) ? '#DDDDDD' : '#FCD116'; var c2 = remain.contains(n) ? '#555555' : '#FCD116'; return ' ' + pokeInfo.icon(n) + ' '; }; var table = ""; for (var i = 0, rowDelim = 0; i < card.length; i++, rowDelim++) { if (rowDelim === 0) table += ""; table += showSlot(card[i]); if (rowDelim === 4) { table += ""; rowDelim = -1; } } table += "
"; this.sendMessage(viewer, table, null, null, true); }; Bingo.prototype.fillCard = function(name) { var card = this.cards[name], remain = this.remainingNumbers.concat().shuffle(), n; for (var e = 0; e < card.length; e++) { if (card[e] === 0) { do { n = remain.shift(); } while (card.contains(n)); card[e] = n; } } this.sendMessage(name, "This is your card: "); this.showCard(name); }; Bingo.prototype.countLines = function(card) { var lines = [ // Horizontal Lines [0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24], // Vertical Lines [0, 5, 10, 15, 20], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24], // Diagonal Lines [0, 6, 12, 18, 24], [4, 8, 12, 16, 20] ], e, i, n, broken, c = 0; for (e = 0; e < lines.length; e++) { broken = false; for (i = 0; i < 5; i++) { n = lines[e][i]; if (this.remainingNumbers.contains(card[n])) { broken = true; break; } } if (!broken) { c++; } } return c; }; Bingo.prototype.sayBingo = function(src) { if (this.finished) { this.sendMessage(name, "The event already finished!"); return; } var name = sys.name(src).toLowerCase(); if (this.phase !== "playing") { this.sendMessage(name, "The event didn't even start yet!"); return; } if (this.winners.contains(name)) { this.sendMessage(name, "You are already one of the winners, you cannot say bingo again! You can type " + link("/watch") + " to stop watching the event (you will receive your rewards normally)."); return; } if (this.round <= 5) { this.sendMessage(name, "There's no possible way you already got a Bingo!"); return; } if (this.cooldowns[name] > this.round) { this.sendMessage(name, "You falsely shouted Bingo, so you will only be able to say it again in " + plural(this.cooldowns[name] - this.round, "turn") + "!"); return; } var c = this.countLines(this.cards[name]); if (c < this.goal) { this.sendToViewers(sys.name(src) + " shouts BINGO! But they still didn't complete " + plural(this.goal, "line") +"!"); this.shouts[name] += 1; this.cooldowns[name] = this.round + this.shouts[name] + 1; this.sendMessage(name, "You still don't have " + plural(this.goal, "line") + " complete! As a punishment, you won't be able to say Bingo for the next " + plural(this.shouts[name], "turn") + "!"); return; } this.sendToViewers(""); var placement = ["We have a winner!", "Second place is here!", "There's the third place!"][this.winners.length]; this.sendToViewers(toColor(sys.name(src) + " shouts BINGO! " + sys.name(src) + " completed " + plural(c, "line") + "! " + placement, "blue")); this.winners.push(name); this.viewers.push(name); if (this.winners.length >= this.maxWinners) { this.finish(); } else { var t; for (c in this.watchTargets) { if (this.watchTargets[c] === name) { t = this.getOngoingPlayer(); this.watchTargets[c] = t; this.sendMessage(c, "As " + name + " has won, you will now follow " + t.toCorrectCase() + "'s card!"); } } } }; Bingo.prototype.getOngoingPlayer = function() { var target, list = Object.keys(this.cards); if (list.length === this.winners.length) { return this.winners[0]; } if (list.length === 0) { return this.signups[0].toLowerCase(); } do { target = list.random(); } while (this.winners.contains(target)); return target; }; Bingo.prototype.finish = function() { if (this.winners.length === 0) { this.sendToViewers("No one said Bingo, so we got no winners!"); this.log(true, "No winners"); this.finished = true; return; } var winner = this.winners[0]; var runnerup = this.winners.length > 1 ? this.winners[1] : null; var thirdplace = this.winners.length > 2 ? this.winners[2] : null; var winnerName = winner.toCorrectCase(); var runnerupName = runnerup ? runnerup.toCorrectCase() : ""; var thirdplaceName = thirdplace ? thirdplace.toCorrectCase() : ""; this.sendToViewers(""); safaribot.sendHtmlAll("" + toColor(winnerName, "blue") + " won the " + this.eventName + " and received " + this.rewardName1 + "!", safchan); var player = getAvatarOff(winner), out, stuff; if (player) { stuff = toStuffObj(this.reward1.replace(/,/g, ":")), out = giveStuff(player, stuff); player.records.bingoWon += 1; safari.saveGame(player); this.sendMessage(winner, "You " + out + "!"); } if (runnerup) { safaribot.sendHtmlAll(runnerupName + " got the second place and received " + this.rewardName2 + "!", safchan); player = getAvatarOff(runnerup); if (player) { stuff = toStuffObj(this.reward2.replace(/,/g, ":")), out = giveStuff(player, stuff); safari.saveGame(player); this.sendMessage(runnerup, "You " + out + "!"); } } if (thirdplace) { safaribot.sendHtmlAll(thirdplaceName + " got the third place and received " + this.rewardName3 + "!", safchan); player = getAvatarOff(thirdplace); if (player) { stuff = toStuffObj(this.reward3.replace(/,/g, ":")), out = giveStuff(player, stuff); safari.saveGame(player); this.sendMessage(thirdplace, "You " + out + "!"); } } if (this.noSecondPrize) { this.sendToViewers("No 2nd place prize will be given due to the low number of participants!"); } if (this.noThirdPrize) { this.sendToViewers("No 3rd place prize will be given due to the low number of participants!"); } this.log(true, "1st: " + winnerName + (runnerupName ? ", 2nd: " + runnerupName : "") + (thirdplace ? ", 3rd: " + thirdplaceName : "")); this.finished = true; }; Bingo.prototype.canJoin = function(src) { var player = getAvatar(src); if (player.tradeban > now()) { safaribot.sendMessage(src, "You can't join this event while tradebanned!", safchan); return false; } if (player.records.pokesCaught < 4) { safaribot.sendMessage(src, "You can only join this event after you catch " + (4 - player.records.pokesCaught) + " more Pokémon!", safchan); return; } return true; }; Bingo.prototype.canWatch = function(src) { var name = sys.name(src).toLowerCase(); var signupsLower = Object.keys(this.cards); if (signupsLower.contains(name) && !this.winners.contains(name)) { safaribot.sendMessage(src, "You are one of the participants, you can't watch/unwatch this event!", safchan); return false; } return true; }; Bingo.prototype.onWatch = function(src) { var target = this.getOngoingPlayer(); this.watchTargets[sys.name(src).toLowerCase()] = target; safaribot.sendHtmlMessage(src, "You are watching the " + this.eventName + "! You will follow " + target.toCorrectCase() + "'s card!", safchan); }; Bingo.prototype.onUnwatch = function(src) { var name = sys.name(src).toLowerCase(); if (this.watchTargets.hasOwnProperty(name)) { delete this.watchTargets[name]; } }; Bingo.prototype.sendToViewers = function(msg, flashing, colored, nobot, androidAlt) { var e, players = this.signups.map(function(x) { return x.toLowerCase(); }), n; var list = removeDuplicates(players.concat(this.viewers)); for (e = 0 ; e < list.length; e++) { n = list[e]; if (this.winners.contains(n) && !this.viewers.contains(n)) { continue; } this.sendMessage(n, msg, flashing, colored, nobot, androidAlt); } }; Bingo.prototype.isInEvent = function(name) { var signupsLower = this.signups.map(function(x) { return x.toLowerCase(); }); var n = name.toLowerCase(); return signupsLower.contains(n) && !this.winners.contains(n); }; this.castleCommand = function(src, command) { var player = getAvatar(src); if (!player) { return false; } if (!command) { return false; } var cdata = command.split(":"); command = cdata.shift(); switch (command) { case "edit": this.editCastle(src, player, cdata); break; default: safaribot.sendHtmlMessage(src, "The commands in Castle are " + link("/dc dropoff:", "Dropoff", true) + ", " + link("/dc retrieve", "Retrieve") + ", " + link("/dc view", "View") + ", and " + link("/dc help", "Help") + "!", safchan); } }; this.editCastle = function(src, player, cdata) { var room = null, aspect = null, element = null; if (cdata,length > 0) { room = cdata[0] } if (cdata,length > 1) { aspect = cdata[1].toLowerCase(); } if (cdata,length > 2) { element = cdata[2] } if (!room) { safaribot.sendHtmlMessage(src, "Challengers to your Castle are required to clear three rooms to win. Pick " + link("/castle edit:1", "1") + ", " + link("/castle edit:2", "2") + ", or " + link("/castle edit:3", "3") + " that you would like to edit.", safchan); safaribot.sendHtmlMessage(src, "You can also change your theme using " + link("/castle edit:theme", "Theme") + ".", safchan); } if (room == "theme") { if (!aspect) { safaribot.sendHtmlMessage(src, "There are 10 theme available for your castle. Be aware that if you have assigned your castle a theme before, you may need to change aspects of your Rooms in order to adapt to the Castle's Theme.", safchan); safaribot.sendHtmlMessage(src, "The themes to choose from are " + link("/castle edit:theme:aquatic", "Aquatic") + ", " + link("/castle edit:theme:space", "Space") + ".", safchan); if ((!player.castle.theme) || player.castle.theme == "") { safaribot.sendHtmlMessage(src, "You do not have a theme assigned now. You can assign one to allow certain traps in your rooms.", safchan); } else { safaribot.sendHtmlMessage(src, "Your current theme is " + player.castle.theme + ".", safchan); } } else { player.castle.theme = aspect; safaribot.sendHtmlMessage(src, "You've changed your castle theme to " + player.castle.theme + "!", safchan); } } room = parseInt(room, 10); if (room && (!aspect)) { safaribot.sendHtmlMessage(src, "Edit Room #" + room + "'s " + link("/castle edit:" + room + ":pokemon", "Pokémon") + " or " + link("/castle edit:" + room + ":traps", "Traps"), safchan); } if (aspect == "pokemon") { if (element && player.castle.rooms[room+""].pokemon.length >= 6) { safaribot.sendHtmlMessage(src, "You already have 6 Pokémon in your #" + room + ":" + m + "!", safchan); } else { element = getInputPokemon(element); if (!element.num) { safaribot.sendHtmlMessage(src, "That's not a valid Pokémon!", safchan); } else { player.castle.rooms[room+""].push(element.id); safaribot.sendHtmlMessage(src, "You added " + element.input + " to your team!", safchan); } } //Send the existing Pokémon in that room. if (player.castle.rooms[room+""].pokemon.length > 0) { var m = ""; for (var a in player.castle.rooms[room+""].pokemon) { m += poke(player.castle.rooms[room+""].pokemon[a]) + " " + link("/castle edit:" + room + ":remove:" + poke(player.castle.rooms[room-1].pokemon[a]), "(Remove)"); } safaribot.sendHtmlMessage(src, "Pokémon already in your #" + room + ":" + m + ".", safchan); } else if (!element) { safaribot.sendHtmlMessage(src, "There are no Pokémon in your #" + room + " room!", safchan); } } if (aspect == "remove") { if (player.castle.rooms[room-1].pokemon.length > 0) { for (var a in player.castle.rooms[room-1].pokemon) { if ((poke(player.castle.rooms[room-1].pokemon[a])) == element) { player.castle.rooms[room-1].pokemon.splice(a); break; } } safaribot.sendHtmlMessage(src, "Removed " + element + " from room #" + room + ".", safchan); } } if (aspect == "traps") { var t = player.castle.rooms[room+""].traps; if (t.length == 0) { safaribot.sendHtmlMessage(src, "No traps yet!", safchan); } var m = ""; for (var a in t) { m += toColor(this.getFieldConditionDescription(t[a], []) + " (" + this.getFieldConditionCost(t[a], player.castle.theme, player.castle[room+""].pokemon) + ")", (this.getFieldConditionCost(t[a], player.castle.theme, player.castle[room+""].pokemon, true) ? "green" : "blue")); } } }; this.getFieldConditionDescription = function(condition, select) { var m = ""; switch (condition) { case "sandstorm": m = "A Sandstorm rages."; break; case "sandstorm3": m = "A Sandstorm rages."; break; case "hail": m = "Hail begins to fall."; break; case "hail3": m = "Hail begins to fall."; break; case "sun": m = "The Sunlight is intense."; break; case "rain": m = "There is a heavy downpour of Rain."; break; case "electricterrain": m = "The field surges with Electric Terrain."; break; case "grassyterrain": m = "The field surges with Grassy Terrain."; break; case "psychicterrain": m = "The field surges with Psychic Terrain."; break; case "mistyterrain": m = "The field surges with Misty Terrain."; break; case "powertrick": m = "All Pokémons' Attack and Defense as well as their Special Attack and Special Defense are swapped."; break; case "statjumble": m = "Stats are shuffled."; break; case "singlespecialstat": m = "All Pokémon use their Special Attack stat as their Special Defense."; break; case "spikes3": m = "The battle begins with three layers of Spikes on challenger's side of the field."; break; case "spikes2": m = "The battle begins with two layers of Spikes on challenger's side of the field."; break; case "spikes": m = "The battle begins with one layer of Spikes on challenger's side of the field."; break; case "hazardsetter": m = "The foe sets up a layer of Spikes every 5 turns."; break; case "stealthrock": m = "The battle begins with Stealth Rock on challenger's side of the field."; break; case "stealththunder": m = "The battle begins with Stealth Thunder on challenger's side of the field."; break; case "stealthicicles": m = "The battle begins with Stealth Icicles on challenger's side of the field."; break; case "toxicspikes": m = "The battle begins with one layer of Toxic Spikes on challenger's side of the field."; break; case "dynamicWeb": m = "The battle begins with a stat reducing Web on challenger's side of the field."; break; case "quickSand": m = "The battle begins with Quicksand on challenger's side of the field."; break; case "harshWinds": m = "The battle begins with Harsh Winds on challenger's side of the field."; break; case "shellBurn": m = "Fire-type moves harm the user."; break; case "SwiftSwim": m = "Water-type Pokémon have their Speed doubled in Rain."; break; case "sandBoostGround": m = "Ground-type Pokémon have their Speed doubled and receive a Special Defense boost from Sandstorm."; break; case "initialReflect": m = "Reflect begins active on foe's side."; break; case "initialReflect2": m = "Reflect begins active on challenger's side."; break; case "initialLightScreen": m = "Light Screen begins active on foe's side."; break; case "initialLightScreen2": m = "Light Screen begins active on challenger's side."; break; case "boostType": m = (select[condition].length > 0 ? "The foe's " + select[condition].join(" and ") + " attacks are more powerful." : ""); break; case "iceshield": m = "The foe's team is surrounded with an Ice Shield."; break; case "electroshield": m = "The foe's team is surrounded with an Electro Shield."; break; case "sludgeshield": m = "The foe's team is surrounded with a Sludge Shield."; break; case "metalshield": m = "The foe's team is surrounded with a Metal Shield."; break; case "dracoshield": m = "The foe's team is surrounded with a Draco Shield."; break; case "genesisshield": m = "The foe's team is surrounded with a Genesis Shield."; break; case "shellArmor": m = "Critical hits cannot occur."; break; case "criticalDouble": m = "Critical hits do increased damage."; break; case "hyperBeamGlitch": m = "Recharge moves are stronger, and do not require a recharge turn if they score a KO."; break; case "noncritexhaust": m = "Attacking stat lowered, while landing a critical hit removes nerfs in the attacking stat."; break; case "boostDrain": m = "Draining moves restore a greater amount of HP."; break; case "sabotage": m = "Landing super-effective attacks causes you to be sabotaged by the foe's fanclub."; break; case "solidRock": m = "The foe's Pokémon are slightly resistant to super-effective attacks."; break; case "expertBelt": m = "The foe's Pokémon are stronger when using super-effective attacks."; break; case "hugePower": m = "One of the foe's Pokémon has their Attack stat doubled."; break; case "researcher": m = "The foe's Pokémon have their stats doubled."; break; case "hpboost": m = "Foe's Pokémon have a greater amount of HP."; break; case "fortress": m = "Foe's Pokémon begin with their Defense and Special Defense boosted, but with Attack and Special Attack reduced."; break; case "brawler": m = "Foe's Pokémon power up when hit with physical moves."; break; case "balloon": m = "Foe's Pokémon begin the game holding a Balloon."; break; case "autopara": m = "Challenger's Pokémon begin Paralyzed."; break; case "autopoison": m = "Challenger's Pokémon begin Poisoned."; break; case "heatproof": m = "Foe's Pokémon are resistant to Fire."; break; case "furcoat": m = "Foe's Pokémon take half damage from Physical moves."; break; case "switchHeal": m = "User's Pokémon heal when switched out."; break; case "regenerator": m = "Foe's Pokémon heal every 5 turns."; break; case "adaptability": m = "All STAB is increased in power."; break; case "multiscale": m = "Foe's Pokémon take reduced damage while at full HP."; break; case "aurashield": m = "Foe's Pokémon take reduced damage from Special Attacks when above 50% HP."; break; case "dualscale": m = "All Pokémon take and deal reduced damage while at full HP."; break; case "shadowsBlade": m = "Shadow's Blade: Challenger's Pokémon lose half their remaining HP every 5 turns."; break; case "blueBoost": m = "Blue Pokémon receive a fighting boost."; break; case "pinkBoost": m = "Pink Pokémon receive a fighting boost."; break; case "skyBattle": m = "Sky Battle - Pokémon that cannot use Fly or Bounce suffer more damage and deal less damage."; break; case "arenaBattle": m = "Arena Battle - Pokémon that cannot use Seismic Toss suffer more damage and deal less damage."; break; case "weightattack": m = "Heavier Pokémon take more damage from Grass and Fighting-type attacks."; break; case "dragonslayer": m = "Foe's Fighting-type moves deal double damage to Dragon-type Pokémon."; break; case "freezedry": m = "Foe's Ice-type moves are quadruply effective against Water."; break; case "thickFat": m = "Foe's Pokémon are more resistant to Fire and Ice."; break; case "corrosion": m = "Poison type moves are super-effective on Steel types and Steel types may be poisoned."; break; case "overheated": m = "Foe's Fire-type Pokémon resist Water, but are weaker to Fire."; break; case "retaliate": m = "Foe's attack power is doubled until the end of the turn if hit with a special attack."; break; case "nerfBestStat": m = "All Pokémon's best stat(s) begin with a debuff."; break; case "intimidate": m = "Challenger's Pokémon begin the game with debuffed Attack."; break; case "speedcrit": m = "Pokémon with a speed advantage score critical hits more easily."; break; case "frenzy": m = "KO-ing a Pokémon restores HP to the Pokémon that gave the final blow."; break; case "grudge": m = "KO-ing a Pokémon damages the Pokémon that gave the final blow."; break; case "faintTrap": m = "KO-ing a Pokémon weakens the Defense, Special Defense and Speed of the Pokémon that gave the final blow."; break; case "naturalcure": m = "Grass and Water-type moves cure the user's status."; break; case "poweruppunch": m = "Foe's Pokémon power up when using Physical Attacks."; break; case "chargebeam": m = "Foe's Pokémon power up when using Special Attacks."; break; case "vicious": m = "Landing an attacking move raises a random stat two stages and lowers another by one."; break; case "moonblast": m = "Every three to six turns, both Pokémon on the field are struck with a Moonblast."; break; case "irontail": m = "Every three to six turns, both Pokémon on the field are struck with an Iron Tail."; break; case "waterfall": m = "Every three to six turns, both Pokémon on the field are struck with a Waterfall."; break; case "topsyturvy": m = "Every seven turns, all stat changes are inverted."; break; case "statshift": m = "Every three turns, all stat changes shifted over."; break; case "dancer": m = "Every six turns, foe's Special Attack, Special Defense and Speed +1."; break; case "dancer2": m = "Every six turns, foe's Attack and Speed +1."; break; case "smasher": m = "Every six turns, foe's Attack, Special Attack and Speed +2, and Defense and Special Defense -1."; break; case "speedboost": m = "Foe's Speed increases at the end of every turn."; break; case "lastStand": m = "Foe's last Pokémon's Attack, Special Attack and Speed +2."; break; case "retaliate2": m = "Next attack after a teammate faints has its power doubled."; break; case "rollout": m = "Attacking with the same Pokémon consecutively boosts its attack power."; break; case "categorySplit": m = "Move category is determined by type."; break; case "specBan": m = "Special moves cannot be used."; break; case "physBan": m = "Physical moves cannot be used."; break; case "normalcy": m = "All Pokémon gain Normal-type."; break; case "draconian": m = "All Pokémon gain Dragon-type."; break; case "mechanical": m = "All Pokémon gain Steel-type."; break; case "classicTypes": m = "Dark, Steel, and Fairy-types are not accounted for."; break; case "lightClay": m = "Foe's Reflect and Light Screen are extended."; break; case "inverted": m = "Inverted Battle."; break; case "resistMode": m = "Move's type effectiveness is determined by its resistance to the foe's typing."; break; case "trickRoom": m = "Trick Room is in effect."; break; case "powerUnknown": m = "Move damage may be hidden."; break; case "simple": m = "All stat changes are doubly influential."; break; case "noStatBoost": m = "Stat changes cannot occur."; break; case "reversal": m = "Pokémon near fainting receive an extreme power boost."; break; case "overgrowblazetorrent": m = "Pokémon's Grass/Fire/Water STAB increases when below 33%."; break; case "brine": m = "Pokémon near fainting suffer more damage."; break; case "analytic": m = "Foe's attacks adapt to their target's type over consecutive hits."; break; case "slowStart": m = "Foe damage output is halved for the first 5 turns."; break; case "guts": m = "All Pokémon have increased Attack stat while inflicted with a status condition. The Attack reduction from burns is ignored."; break; case "defiant": m = "Foe's Pokémon's Attack +2 when any of its stats are decreased."; break; case "hex": m = "All Pokémon suffer double damage while afflicted with a status condition."; break; case "weakSTAB": m = "There is no bonus for STAB moves."; break; case "nostab": m = "Pokémon cannot use any STAB moves."; break; case "thickPollen": m = "All non-Grass and non-Bug Pokémon suffer reduced Speed from pollen."; break; case "galeWings": m = "All Flying-type moves have increased priority."; break; case "bodypress": m = "All physical moves use the higher value between its Attack and Defense to deal damage."; break; case "electroball": m = "All physical moves use the higher value between its Attack and Speed stat to deal damage."; break; case "psystrike": m = "All Psychic-type moves target the lower defensive stat."; break; case "fairystrike": m = "All Fairy-type moves target the lower of Defense and Attack when doing damage."; break; case "inferno": m = "All Fire-type moves have a 25% chance to Burn."; break; case "scald": m = "All Water-type moves have a 30% chance to Burn and thaw the user."; break; case "zapcannon": m = "All Electric-type moves have a 50% chance to Paralyze."; break; case "toxic": m = "All Poison-type moves have a 75% chance to inflict Poison."; break; case "strongJaw": m = "All physical moves where the user has a bite move of its type are 50% stronger."; break; case "dynamicPunch": m = "All Fighting-type moves inflict Confusion."; break; case "chillPhysical": m = "All Dragon-type moves lower the target's Speed and Attack unless the target is a Dragon-type Pokémon."; break; case "chillSpecial": m = "All Ice-type moves lower target's Speed and Special Attack unless the target is an Ice-type Pokémon."; break; case "psyDrop": m = "All Psychic-type moves have a 50% chance to drop the target's Special Attack and Special Defense."; break; case "pixelate": m = "All Normal-type moves become Fairy-type moves and are receive a 20% damage increase."; break; case "sweltering": m = "All Water and Ice-type moves becomes Normal-type moves."; break; case "ashes": m = "All Normal-type moves become Ghost-type moves, while all Fire-type moves become Rock-type moves."; break; case "extendedSleep": m = "All Pokémon are deep sleepers."; break; case "leftovers": m = "The foe's team restores HP gradually."; break; case "shedSkin": m = "The foe's team can cure itself of status (25% chance)."; break; case "bypassImmune": m = "The foe's team ignores type immunities with their attacks."; break; case "spectralThief": m = "'Not Very Effective' attacks transfer stat boosts."; break; case "leechseed": m = "Pokémon that learn Leech Seed recover HP."; break; case "drainpunch": m = "Pokémon that learn Drain Punch deal more damage when using drain attacks."; break; case "revive": m = "Each foe has a Revive at their disposal."; break; case "fullrestore": m = "Each foe has a Full Restore at their disposal."; break; case "fullrestore2": m = "Each foe has 2 Full Restores at their disposal."; break; case "fullrestore3": m = "Each foe has 3 Full Restores at their disposal."; break; case "hyperpotion": m = "Each foe has a Hyper Potion at their disposal."; break; case "hyperpotion2": m = "Each foe has 2 Hyper Potions at their disposal."; break; case "hyperpotion3": m = "Each foe has 3 Hyper Potions at their disposal."; break; case "boostType1": m = ""; break; case "boostType2": m = ""; break; default: m = ("Missing help text: " + condition + ". Please contact a Safari Admin"); } return m; } this.pokesHas = function(party, attribute, number) { if (attribute == "move") { for (var i = 0; i < party.length; i++) { if (canLearnMove(party[i], number)) { return true; } } } else if (attribute == "exact") { for (var i = 0; i < party.length; i++) { if (number.contains(parseInt(party[i], 10))) { return true; } } } }; this.getFieldConditionCost = function(condition, theme, pokes, checkDiscounted) { var out = 0; switch (condition) { case "spikes": out = 25; break; case "2spikes": out = 45; break; case "3spikes": out = 65; break; case "hazardsetter": out = 52; break; case "stealthrock": out = 30; break; case "toxicspikes": out = 30; break; case "dynamicWeb": out = 35; break; case "quickSand": out = 35; break; case "harshWinds": out = 25; break; case "shellBurn": out = 25; break; case "rain": out = 50; break; case "sun": out = 50; break; case "sandstorm": out = 35; break; case "hail": out = 35; break; case "electricterrain": out = 20; break; case "mistyterrain": out = 20; break; case "psychicterrain": out = 20; break; case "grassyterrain": out = 20; break; case "toxic": out = 9; break; case "zapcannon": out = 9; break; case "inferno": out = 9; break; case "psyDrop": out = 8; break; case "chillSpecial": out = 12; break; case "chillPhysical": out = 12; break; case "freezedry": out = 14; break; case "dragonslayer": out = 11; break; case "galeWings": out = 10; break; case "hugePower": out = 18; break; case "psystrike": out = 10; break; case "pixelate": out = 20; break; case "sweltering": out = 30; break; case "overgrowblazetorrent": out = 15; break; case "bypassImmune": out = 25; break; case "initialReflect": out = 9; break; case "initialLightScreen": out = 9; break; case "multiscale": out = 25; break; case "aurashield": out = 25; break; case "solidRock": out = 20; break; case "hpboost": out = 30; break; case "leftovers": out = 20; break; case "heatproof": out = 17; break; case "furcoat": out = 30; break; case "thickFat": out = 27; break; case "balloon": out = 16; break; case "intimidate": out = 13; break; case "shellArmor": out = 10; break; case "fortress": out = 8; break; case "autopara": out = 35; break; case "autopoison": out = 35; break; case "brawler": out = 24; break; case "retaliate": out = 12; break; case "analytic": out = 12; break; case "dancer": out = 40; break; case "dancer2": out = 30; break; case "smasher": out = 35; break; case "chargebeam": out = 21; break; case "skyBattle": out = 22; break; case "arenaBattle": out = 22; break; case "thickPollen": out = 15; break; case "guts": out = 8; break; case "reversal": out = 7; break; case "weightattack": out = 11; break; case "brine": out = 13; break; case "vicious": out = 9; break; case "hex": out = 6; break; case "boostDrain": out = 5; break; case "criticalDouble": out = 6; break; case "adaptability": out = 10; break; case "speedcrit": out = 12; break; case "extendedSleep": out = 7; break; case "inverted": out = 15; break; case "resistMode": out = 15; break; case "trickRoom": out = 15; break; case "powerUnknown": out = 20; break; case "statjumble": out = 20; break; case "spectralThief": out = 25; break; case "leechseed": out = 15; break; case "drainpunch": out = 15; break; case "simple": out = 15; break; case "noStatBoost": out = 15; break; case "topsyturvy": out = 25; break; case "nerfBestStat": out = 5; break; case "classicTypes": out = 15; break; case "categorySplit": out = 10; break; case "physBan": out = 25; break; case "specBan": out = 25; break; case "normalcy": out = 25; break; case "draconian": out = 25; break; case "mechanical": out = 25; break; case "powertrick": out = 10; break; default: out = 10; break; } var xout = out; switch (condition) { case "spikes": case "2spikes": case "3spikes": case "hazardsetter": if (this.pokesHas(pokes, "move", 191)) { out *= 0.67; } break; case "stealthrock": if (this.pokesHas(pokes, "move", 446)) { out *= 0.82; } break; case "toxicspikes": if (this.pokesHas(pokes, "move", 390)) { out *= 0.5; } break; case "multiscale": if (this.pokesHas(pokes, "exact", [149, 250])) { out *= 0.6; } break; } switch (theme) { case "Aquatic": if (condition == "rain") { out *= 0.25; }; break; case "Boxing": if (condition == "brawler") { out *= 0.25; }; if (condition == "reversal") { out *= 0.25; }; if (condition == "arenaBattle") { out *= 0.25; }; if (condition == "normalcy") { out *= 0.5; }; if (condition == "specBan") { out *= 0.5; }; if (condition == "hpboost") { out *= 0.25; }; if (condition == "dragonslayer") { out *= 0.5; }; if (condition == "guts") { out *= 0.5; }; if (condition == "drainpunch") { out *= 0.33; }; break; case "Desert": if (condition == "sun") { out *= 0.25; }; if (condition == "sandstorm") { out *= 0.25; }; if (condition == "harshWinds") { out *= 0.5; }; if (condition == "shellBurn") { out *= 0.5; }; if (condition == "quickSand") { out *= 0.25; }; if (condition == "sweltering") { out *= 0.5; }; if (condition == "adaptability") { out *= 0.33; }; if (condition == "solidRock") { out *= 0.25; }; if (condition == "hpboost") { out *= 0.33; }; if (condition == "leftovers") { out *= 0.5; }; if (condition == "trickRoom") { out *= 0.33; }; break; case "Haunted": if (condition == "bypassImmune") { out *= 0.2; }; if (condition == "autoPara") { out *= 0.5; }; if (condition == "chargebeam") { out *= 0.33; }; if (condition == "vicious") { out *= 0.25; }; if (condition == "simple") { out *= 0.5; }; if (condition == "statjumble") { out *= 0.25; }; if (condition == "spectralThief") { out *= 0.2; }; if (condition == "classicTypes") { out *= 0.5; }; if (condition == "hex") { out *= 0.25; }; if (condition == "extendedSleep") { out *= 0.25; }; if (condition == "powerUnknown") { out *= 0.2; }; if (condition == "topsyturvy") { out *= 0.25; }; break; case "Ironworks": break; case "Jungle": if (condition == "statjumble") { out *= 0.5; }; if (condition == "vicious") { out *= 0.5; }; if (condition == "corrosion") { out *= 0.2; }; if (condition == "dynamicWeb") { out *= 0.33; }; if (condition == "toxic") { out *= 0.25; }; if (condition == "smasher") { out *= 0.33; }; if (condition == "hpboost") { out *= 0.5; }; if (condition == "toxicspikes") { out *= 0.67; }; if (condition == "weightattack") { out *= 0.25; }; if (condition == "thickPollen") { out *= 0.25; }; if (condition == "hex") { out *= 0.25; }; if (condition == "powertrick") { out *= 0.5; }; if (condition == "retaliate") { out *= 0.5; }; if (condition == "overgrowblazetorrent") { out *= 0.33; }; if (condition == "topsyturvy") { out *= 0.5; }; if (condition == "leechseed") { out *= 0.33; }; break; case "Mythical": break; case "Space": break; case "Summit": break; } if (checkDiscounted) { if (xout < out) { return true; } else { return false; } } }; this.fieldConditionMess = function(condition, args) { var out = "", cost = 0; var pokes = args.pokes; var theme = args.theme; var existing = args.existing; var roomNum = args.roomNum; var allConditions = args.allConditions; out += this.getFieldConditionDescription(condition); out += " [" + this.getFieldConditionCost(condition, theme, pokes) + "]"; if ((!(existing.contains(condition))) && allConditions.contains(condition)) { out = toColor(out, "purple"); } else { if (this.getFieldConditionCost(condition, theme, pokes, true)) { out = toColor(out, "green"); } if (existing.contains(condition)) { out = toColor(out, "blue"); out += " " + link("/castle edit:" + roomNum + ":conditions:" + existing.indexOf(condition), "[Remove]"); } else { out += " " + link("/castle edit:" + roomNum + ":conditions:" + condition + "[Add]"); } } return out; } this.showRoomFieldConditionOptions = function(src, player, number, type) { var m = "", theme = player.castle.theme, pokes = player.castle.rooms[number].party, existing = player.castle.rooms[number].conditions, roomNum = number; var allConditions = []; for (var i = 0; i < player.castle.rooms.length; i++) { if (player.castle.rooms[i].conditions) { allConditions = allConditions.concat(player.castle.rooms[i].conditions); } } var args = { "theme": theme, "pokes": pokes, "existing": existing, "roomNum": roomNum, "allConditions": allConditions } switch (type) { case "stage": m += this.fieldConditionMess("rain", args); m += this.fieldConditionMess("sun", args); m += this.fieldConditionMess("sandstorm", args); m += this.fieldConditionMess("hail", args); m += this.fieldConditionMess("electricterrain", args); m += this.fieldConditionMess("mistyterrain", args); m += this.fieldConditionMess("psychicterrain", args); m += this.fieldConditionMess("grassyterrain", args); break; case "hazards": m += this.fieldConditionMess("spikes", args); m += this.fieldConditionMess("2spikes", args); m += this.fieldConditionMess("3spikes", args); m += this.fieldConditionMess("hazardsetters", args); m += this.fieldConditionMess("stealthrock", args); m += this.fieldConditionMess("toxicspikes", args); m += this.fieldConditionMess("dynamicWeb", args); m += this.fieldConditionMess("quickSand", args); m += this.fieldConditionMess("harshWinds", args); m += this.fieldConditionMess("shellBurn", args); break; case "offense": m += this.fieldConditionMess("toxic", args); m += this.fieldConditionMess("zapcannon", args); m += this.fieldConditionMess("inferno", args); m += this.fieldConditionMess("psyDrop", args); m += this.fieldConditionMess("chillSpecial", args); m += this.fieldConditionMess("chillPhysical", args); m += this.fieldConditionMess("overgrowblazetorrent", args); m += this.fieldConditionMess("freezedry", args); m += this.fieldConditionMess("galeWings", args); m += this.fieldConditionMess("hugePower", args); m += this.fieldConditionMess("psystrike", args); m += this.fieldConditionMess("dragonslayer", args); m += this.fieldConditionMess("spectralThief", args); m += this.fieldConditionMess("bypassImmune", args); break; case "defense": m += this.fieldConditionMess("initialReflect", args); m += this.fieldConditionMess("initialLightScreen", args); m += this.fieldConditionMess("multiscale", args); m += this.fieldConditionMess("aurashield", args); m += this.fieldConditionMess("solidRock", args); m += this.fieldConditionMess("hpboost", args); m += this.fieldConditionMess("leftovers", args); m += this.fieldConditionMess("heatproof", args); m += this.fieldConditionMess("thickFat", args); m += this.fieldConditionMess("furcoat", args); m += this.fieldConditionMess("balloon", args); m += this.fieldConditionMess("intimidate", args); m += this.fieldConditionMess("shellArmor", args); m += this.fieldConditionMess("fortress", args); m += this.fieldConditionMess("autopara", args); m += this.fieldConditionMess("autopoison", args); m += this.fieldConditionMess("retaliate", args); break; case "moves": m += this.fieldConditionMess("pixelate", args); m += this.fieldConditionMess("sweltering", args); m += this.fieldConditionMess("classicTypes", args); m += this.fieldConditionMess("categorySplit", args); m += this.fieldConditionMess("physBan", args); m += this.fieldConditionMess("specBan", args); m += this.fieldConditionMess("normalcy", args); m += this.fieldConditionMess("draconian", args); m += this.fieldConditionMess("mechanical", args); m += this.fieldConditionMess("nostab", args); break; case "stats": m += this.fieldConditionMess("dancer", args); m += this.fieldConditionMess("dancer2", args); m += this.fieldConditionMess("smasher", args); m += this.fieldConditionMess("chargebeam", args); m += this.fieldConditionMess("brawler", args); m += this.fieldConditionMess("vicious", args); break; case "world": m += this.fieldConditionMess("skyBattle", args); m += this.fieldConditionMess("arenaBattle", args); m += this.fieldConditionMess("thickPollen", args); m += this.fieldConditionMess("guts", args); m += this.fieldConditionMess("reversal", args); m += this.fieldConditionMess("weightattack", args); m += this.fieldConditionMess("brine", args); m += this.fieldConditionMess("hex", args); m += this.fieldConditionMess("boostDrain", args); m += this.fieldConditionMess("adaptability", args); m += this.fieldConditionMess("criticalDouble", args); m += this.fieldConditionMess("speedcrit", args); m += this.fieldConditionMess("extendedSleep", args); break; case "weird": m += this.fieldConditionMess("inverted", args); m += this.fieldConditionMess("resistMode", args); m += this.fieldConditionMess("trickRoom", args); m += this.fieldConditionMess("simple", args); m += this.fieldConditionMess("noStatBoost", args); m += this.fieldConditionMess("powerUnknown", args); m += this.fieldConditionMess("topsyturvy", args); m += this.fieldConditionMess("nerfBestStat", args); m += this.fieldConditionMess("powertrick", args); m += this.fieldConditionMess("statjumble", args); break; } }; this.handleDayCareCommand = function(src, cdata, auth) { var player = getAvatar(src); if (!player) { return false; } var command = cdata[0], c2 = "", c3 = ""; if (cdata.length > 1) { c2 = cdata[1]; } if (cdata.length > 2) { c3 = cdata[2]; } var myPokes = []; for (var p in this.daycarePokemon) { if (this.daycarePokemon[p].area == "holding" && this.daycarePokemon[p].ownernum === player.idnum) { daycarebot.sendHtmlMessage(src, "Your Pokémon " + poke(this.daycarePokemon[p].id) + " hasn't been attended to for a while, so we took it to holding!", safchan); if (command !== "retrieve") { daycarebot.sendHtmlMessage(src, "To retrieve your Pokémon, use " + link("/daycare retrieve:" + poke(this.daycarePokemon[p].id)) + "!", safchan); return false; } } if (this.daycarePokemon[p].ownernum === player.idnum) { myPokes.push(this.daycarePokemon[p]); } } switch (command) { case "dropoff": this.addToDayCare(src, player, cdata); break; case "retrieve": this.retrieveFromDayCare(src, player, cdata); break; case "view": this.printDayCare(src, c2); break; case "interact": this.dayCareInteract(src, player, c2, c3); break; case "help": this.dayCareHelp(src); break; case "berry": this.dayCarePlantBerry(src, player, cdata.slice(1).join(":")); break; default: daycarebot.sendHtmlMessage(src, "Hey there! The commands for the Daycare are " + link("/dc dropoff:", "Dropoff", true) + ", " + link("/dc retrieve", "Retrieve") + ", " + link("/dc berry:", "Berry") + ", " + link("/dc interact", "Interact") + ", and " + link("/dc help", "Help") + "!", safchan); var isAtRegion = { "beach": [], "grotto": [], "jungle": [], "mountain": [] }; for (var i = 0; i < myPokes.length; i++) { if (isAtRegion.hasOwnProperty(myPokes[i].area)) { isAtRegion[myPokes[i].area].push(poke(myPokes[i].id)); } } var m = "Regions: "; m += link("/dc view:beach", "Beach") + " " + (isAtRegion.beach.length > 0 ? "[" + isAtRegion.beach.join(", ") + "] " : ""); m += link("/dc view:grotto", "Grotto") + " " + (isAtRegion.grotto.length > 0 ? "[" + isAtRegion.grotto.join(", ") + "] " : ""); m += link("/dc view:jungle", "Jungle") + " " + (isAtRegion.jungle.length > 0 ? "[" + isAtRegion.jungle.join(", ") + "] " : ""); m += link("/dc view:mountain", "Mountain") + " " + (isAtRegion.mountain.length > 0 ? "[" + isAtRegion.mountain.join(", ") + "] " : ""); daycarebot.sendHtmlMessage(src, m, safchan); } }; this.dayCareHelp = function(src) { daycarebot.sendHtmlMessage(src, "Welcome to the daycare! We offer a place for your Pokémon to roam free in our domain!", safchan); daycarebot.sendHtmlMessage(src, "We promise to take good care of your Pokémon, but we also count on trainers to provide them with food and to play with them!", safchan); daycarebot.sendHtmlMessage(src, "To see the full list of Daycare related commands, type " + link("/daycare") + "!", safchan); return; }; this.dayCareRegionCount = function() { var out = { "beach": 0, "jungle": 0, "grotto": 0, "mountain": 0 }; for (var s in this.daycarePokemon) { p = this.daycarePokemon[s]; if (p.area == "holding") { continue; } if (out.hasOwnProperty(p.area)) { out[p.area] += 1; } } return out; }; this.drawDayCareHearts = function(hearts) { var heart = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGrSURBVDhPpZK5SwNBFMa/2cQEi8QrRIgGBTFYRItE20QbLT1QUIMopNAihQgiaCFiEStLbbQSxONfSNBOCNh4IAqCB1qoASVoMGt2fPsSguJ6gB/szLz35vvN7rzFfyXyM1IX5/I+HkP6+hqW8go4WoMoa/Jx/S4el8nEHjRVhd3rhbunr+DjxePRoTyNzuH5/AzQAEm5YmclascieLm8wu3WOtT0s74VJnMRKgJBNC4sspeHg6kJ+bAbhzAp5M7BhZRQbDZomQzk6yukIiiXM2g01kXGURMaEoqkjamTYwhBpbxZl6Q4m0pBEoCKbOY8PSL7hqf9BMd0JCWz9N5G0qHfSMuoPDPA6nbn0H+VUGCr9/CSAc3Lq0KxFHPiV2kaLJVOODraOWSArvJgEJKKP4ruSxSZUd0/iJIGL39fAeCdj4oSnx+CIQbfQ2ZQJ1yd3agZHC5cTgGgy7e0Iuz+FtqrAz5A9NhsgqurF57JmU83S+3+etrx7LRM7sQg1TeGmWx2VA2EUBce/dIWQ4Cuq401ebO9CWtpGdwjYTgDbYY9/RbwUfyTGQp4By6OkZNZnEoVAAAAAElFTkSuQmCC"; var out = ""; hearts = Math.min(hearts, 255); var i = hearts/10, loop = 100; while (loop > 0) { loop--; out += " "; i--; if (i < 0) { break; } } return out; }; this.dayCareInteract = function(src, player, target, mode) { var pokemon = null, target = parseInt(target, 10); if (isNaN(target)) { var ownMons = []; var otherMons = []; // unused... for now for (var t in this.daycarePokemon) { var p = this.daycarePokemon[t]; if (p.area === "holding") { continue; } if (p.ownernum === player.idnum) { ownMons.push(p); } else { otherMons.push(p); } } if (ownMons.length === 0) { daycarebot.sendHtmlMessage(src, "You don't seem to have any Pokémon with us right now! Why not have some of your Pokémon stay here with " + link("/dc dropoff:", "/dc dropoff:[Pokémon Name]", true) + "?", safchan); return false; } daycarebot.sendHtmlMessage(src, "Which of your Pokémon would you like to check up on?", safchan); daycarebot.sendHtmlMessage(src, ownMons.map(function(e) { return pokeInfo.icon(e.id) + " " + link("/dc interact:" + e.uid, poke(e.id)); }).join(", "), safchan); return false; } for (var t in this.daycarePokemon) { if (target === this.daycarePokemon[t].uid) { pokemon = this.daycarePokemon[t]; break; } } if (!pokemon) { daycarebot.sendMessage(src, "Uh-oh! We had trouble processing that. Try contacting a Safari Warden and we will work with them to figure out what the issue was!", safchan); return false; } var currentTime = now(); var isOwner = pokemon.ownernum === player.idnum; var canPlay = (pokemon.canPlay && (pokemon.meter > 8 || (isOwner && pokemon.meter > 1)) && (player.cooldowns.daycare < currentTime)); if (isOwner) { pokemon.toHolding = currentTime + 60 * 60 * 1000 * 24 * 14; //2 weeks if (pokemon.canMax && (pokemon.hearts + pokemon.playhearts >= 255)) { pokemon.canMax = false; sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Daycare|||Maxed Hearts for " + (pokemon.shiny ? "Shiny " : "") + poke(pokemon.id) + "\n"); //add reward } pokemon.owner = player.casedName; // In case player changed alts } safari.toRecentQuests(player, "daycare"); if (player.notificationData) { player.notificationData.daycareWaiting = true; safari.clearQuestNotifications(player, "Daycare"); } if (mode == "") { var m = ""; switch (pokemon.activity) { case "arriving": m = "arriving at the daycare"; break; case "splashing": m = "splashing about"; break; case "swimming": m = "swimming around"; break; case "diving": m = "diving underwater"; break; case "perching": m = "perched atop a rock"; break; case "eating": m = "eating some berries"; break; case "bigtree": m = "enjoying the shade"; break; case "tree": m = "climbing a tree"; break; case "grass": m = "playing in the grass"; break; case "rock": m = "climbing a rock"; break; case "lilypad": m = "playing on the lilypads"; break; case "flowers": m = "smelling the flowers"; break; case "flapping": m = "flapping its wings"; break; case "dancing": m = "doing a dance"; break; case "roaring": m = "roaring"; break; case "singing": m = "singing"; break; case "embering": m = "curling up in its ember"; break; case "bubbling": m = "blowing bubbles"; break; case "vinewhipping": m = "stretching its vines"; break; case "levitating": m = "levitating"; break; case "thundershocking": m = "sparking about"; break; case "plotting": m = "scheming mischievously"; break; case "napping": m = "napping"; break; case "jumping": m = "jumping about"; break; case "digging": m = "digging a hole"; break; case "prancing": m = "prancing about"; break; default: m = "doing nothing in particular" } daycarebot.sendHtmlMessage(src, pokeInfo.icon(pokemon.id).toCorrectCase() + " " + this.drawDayCareHearts(pokemon.hearts + pokemon.playhearts), safchan); daycarebot.sendMessage(src, pokemon.owner + "'s " + (pokemon.shiny ? "Shiny " : "") + poke(pokemon.id) + " is " + m + "! Isn't it cute?", safchan); if (pokemon.hunger > 19) { daycarebot.sendMessage(src, "Oh no! The poor thing is begging for food!", safchan); } else if (pokemon.hunger > 14) { daycarebot.sendMessage(src, "Oh, dear. It looks quite famished!", safchan); } else if (pokemon.hunger > 12) { daycarebot.sendMessage(src, "It looks pretty hungry!", safchan); } else if (pokemon.hunger > 7) { daycarebot.sendMessage(src, "Hmm... it looks a little hungry.", safchan); } else if (pokemon.hunger < 3) { daycarebot.sendMessage(src, "It doesn't seem hungry.", safchan); } if (pokemon.meter > 18) { daycarebot.sendMessage(src, "It's eager to play!", safchan); } else if (pokemon.meter > 13) { daycarebot.sendMessage(src, "Looks like it wants to play!", safchan); } else if (pokemon.meter < 2) { daycarebot.sendMessage(src, "It looks too tired to play!", safchan); } if (pokemon.canItem && isOwner) { pokemon.canItem = false; var getItem = this.getDayCareItem(pokemon.id, pokemon.hearts + pokemon.playhearts, pokemon.area); if (getItem) { pokemon.findItem = false; daycarebot.sendMessage(src, "Huh? What's this? " + poke(pokemon.id) + " is holding an item!", safchan); var g = giveStuff(player, toStuffObj(getItem)); daycarebot.sendHtmlMessage(src, toColor("You " + g + "!", "#FF1493"), safchan); } } if (isOwner && canPlay && pokemon.berry !== undefined && pokemon.berry !== null) { if (pokemon.berry.hasOwnProperty("time") && pokemon.berry.time < now()) { daycarebot.sendMessage(src, poke(pokemon.id) + " grew some berries for you!", safchan); var g = giveStuff(player, toStuffObj(pokemon.berry.amount + "@" + pokemon.berry.name)); daycarebot.sendHtmlMessage(src, toColor("You " + g + "!", "#228B22"), safchan); pokemon.berry = null; } } var m = []; if (canPlay) { m.push("«" + link("/daycare interact:" + pokemon.uid + ":play", "Play") + "»"); } if (pokemon.hunger > 2 && player.balls.pokeblock > 0) { m.push("«" + link("/daycare interact:" + pokemon.uid + ":feed", "Feed") + "»"); } if (pokemon.berry === undefined || pokemon.berry === null) { m.push("«" + link("/daycare berry:" + poke(pokemon.id), "Berry") + "»"); } if (m.length > 0) { daycarebot.sendHtmlMessage(src, m.join(" "), safchan); } this.saveGame(player); return true; } else if (mode == "feed") { if (player.balls.pokeblock <= 0) { daycarebot.sendHtmlMessage(src, "You are out of Pokéblocks! Buy some more to feed to the Pokémon or make them with the Alchemist!", safchan); return false; } if (pokemon.hunger <= 0) { daycarebot.sendHtmlMessage(src, "Hmm... " + poke(pokemon.id) + " doesn't seem hungry at the moment!", safchan); return false; } player.balls.pokeblock--; var boosted = safari.hasCostumeSkill(player, "pokeblockBoost"); var val = (boosted ? 8 : 5); pokemon.hunger = Math.floor(Math.max(pokemon.hunger - val, 0)); pokemon.hearts += 2; if (player.costume == "breeder") { pokemon.hearts += 1; if (this.hasCostumeSkill(player, "pokeblockBoost")) { pokemon.hearts += 2; } } daycarebot.sendHtmlMessage(src, "Nom nom nom-nom-nom! Looks like " + poke(pokemon.id) + " enjoyed the " + (boosted ? "strong " : "") + "Pokéblock!", safchan); sys.sendHtmlMessage(src, pokeInfo.icon(pokemon.id) + " " + this.drawDayCareHearts(pokemon.hearts + pokemon.playhearts), safchan); this.saveGame(player); safari.saveDaycare(); return true; } else if (mode == "play") { if (player.cooldowns.daycare >= currentTime) { daycarebot.sendHtmlMessage(src, "Please let the Pokémon here relax for a while! You can try again in " + timeLeftString(player.cooldowns.daycare) + ".", safchan); return false; } if (!canPlay) { daycarebot.sendHtmlMessage(src, "It look like that Pokémon wants to be on its own for a while! Try again in a few minutes!", safchan); return false; } sys.sendHtmlMessage(src, pokeInfo.icon(pokemon.id) + " " + this.drawDayCareHearts(pokemon.hearts + pokemon.playhearts), safchan); var rng = Math.random(); var name = (isOwner ? "your " : pokemon.owner + "'s ") + (pokemon.shiny ? "Shiny " : "") + poke(pokemon.id); var nearbyPokes = this.getNearbyPokemon(pokemon, pokemon.area); if (this.hasCostumeSkill(player, "daycarePlay")) { rng += 0.02; if (rng <= 0.15) { rng += 0.05; } } if (player.costume == "scientist" || player.costume == "rocket") { rng -= (0.06 * Math.random()); if (rng <= 0.5) { rng -= 0.1; } } if (!isOwner) { rng -= (0.03 * Math.random()); } if (pokemon.hunger > 15) { rng -= (0.06 * Math.random()); } if (rng > 0.95) { pokemon.playhearts += 8; daycarebot.sendHtmlMessage(src, "You and " + name + " played a little bit, and before you knew it, you were creating a deeper bond between person and Pokémon!", safchan); } else if (rng > 0.85) { pokemon.playhearts += 6; daycarebot.sendHtmlMessage(src, "You and " + name + " played for while. " + name + " looks absolutely delighted!", safchan); } else if (rng > 0.7) { pokemon.playhearts += 4; daycarebot.sendHtmlMessage(src, "You and " + name + " played for a long time. Time flies when you are having fun!", safchan); } else if (rng > 0.15 && nearbyPokes.length > 0) { var p = nearbyPokes.random(); var name2 = ((p.ownernum === player.idnum) ? "your " : p.owner + "'s ") + (p.shiny ? "Shiny " : "") + poke(p.id); pokemon.playhearts += 6; p.playhearts += 6; daycarebot.sendHtmlMessage(src, "You and " + name + " played for a bit. What's this? Oh, " + name2 + " wanted to play too! You played with both Pokémon! They're both so happy!", safchan); } else if (rng > 0.3) { pokemon.playhearts += 3; daycarebot.sendHtmlMessage(src, "You and " + name + " had a nice time playing in the fields!", safchan); } else if (rng > 0.15) { pokemon.playhearts += 1; daycarebot.sendHtmlMessage(src, "You and " + name + " played for a bit, but " + name + " seemed somewhat bored.", safchan); } else { pokemon.playhearts += 0.5; daycarebot.sendHtmlMessage(src, "You tried to play with " + name + " a bit. It seemed rather uninterested!", safchan); } this.costumeEXP(player, "daycareplay"); pokemon.meter -= 2; pokemon.canPlay = false; player.cooldowns.daycare = currentTime + (60 * 1000 * 0.75); if (isOwner && player.costume == "pokefan") { safari.detectiveClue(player.idnum, "pokefandaycare", src); } safari.saveDaycare(); return true; } }; this.addToDayCare = function(src, player, cdata) { var myAmt = 0, myPokes = []; for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].ownernum === player.idnum) { myAmt += 1; myPokes.push(this.daycarePokemon[t].id); } } if (myAmt > 1) { daycarebot.sendMessage(src, "You already have two Pokémon in the Daycare! We cannot accept any more!", safchan); return false; } if (cdata.length > 1) { pokemon = getInputPokemon(cdata[1]); } else { daycarebot.sendMessage(src, "Please specify which Pokémon you would like to send to the Daycare!", safchan); return false; } if (!pokemon) { daycarebot.sendMessage(src, "Sorry, I don't know what Pokémon that is!", safchan); return false; } if (!player.pokemon.contains(pokemon.id)) { daycarebot.sendMessage(src, "You don't have that Pokémon!", safchan); return false; } if (player.party.length === 1 && player.party.contains(pokemon.id)) { daycarebot.sendMessage(src, "You can't drop off your only Pokémon!", safchan); return false; } if (myPokes.contains(pokemon.num)) { daycarebot.sendMessage(src, "You already have a " + poke(pokemon.num) + " in the Daycare! Try adding another Pokémon!", safchan); return false; } if (isMega(pokemon.num)) { daycarebot.sendMessage(src, "Sorry, but Mega Pokémon aren't allowed in the daycare!", safchan); return false; } var row, column, place, p; var loop = 100; while (loop > 0) { loop--; row = ["j", "k", "l"].random(); column = (1 + Math.floor(12 * Math.random())); place = row + column; if (this.pokemonAtDayCarePos("grotto", place) === false && this.validDayCareLocation(parseInt(pokemon.num, 10), place, "grotto")) { p = { id: parseInt(pokemon.num, 10), shiny: false, owner: player.casedName, ownernum: player.idnum, area: "grotto", pos: place, meter: 18, canPlay: true, canItem: false, findItem: true, activity: "arriving", hearts: 2, playhearts: 0, hunger: 3, canMax: true, uid: -1, berry: null }; p.toHolding = now() + 60 * 60 * 1000 * 24 * 14; //2 weeks p.uid = this.getUniqueDayCareId(); if (typeof pokemon.id == "string") { p.shiny = true; } p.name = poke(p.id + (p.shiny ? "" : 0)); this.moveDayCarePokemon(p, p.pos); safari.saveDaycare(); this.removePokemon(src, pokemon.id); this.daycarePokemon.push(p); this.saveGame(player); daycarebot.sendMessage(src, "Your " + poke(pokemon.id) + " has been added to the Daycare! Enjoy your stay, " + poke(pokemon.id) + "!", safchan); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Daycare|||Dropped off " + (p.shiny ? "Shiny " : "") + poke(pokemon.id) + "|||Into " + place + "\n"); safari.saveDaycare(); return true; } } daycarebot.sendMessage(src, "Sorry, we couldn't find space in the daycare for your Pokémon! You could try again, or if the problem continues, contact a Safari Warden!", safchan); return false; }; this.retrieveFromDayCare = function(src, player, cdata) { var pokemon = null; if (cdata.length > 1) { pokemon = getInputPokemon(cdata[1]); } if (!pokemon && cdata.length > 2) { pokemon = getInputPokemon(cdata[1] + " " + cdata[2]); } if (!pokemon || cdata.length < 1) { var opt = []; for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].ownernum === player.idnum) { opt.push(this.daycarePokemon[t].id); } } if (opt.length === 0) { daycarebot.sendHtmlMessage(src, "You do not have any Pokémon in the Daycare! Type " + link("/daycare dropoff:", false, true) + " to add one!", safchan); return false; } daycarebot.sendMessage(src, "Please list which Pokémon would you like to return from the daycare!", safchan); for (var i = opt.length - 1; i >= 0; i--) { daycarebot.sendHtmlMessage(src, link("/daycare retrieve:" + poke(opt[i]), "«" + poke(opt[i]) + "»", true), safchan); } return false; } if (player.pokemon.length >= getPerkBonus(player, "box")) { daycarebot.sendMessage(src, "Your boxes are full! Clear your boxes or buy a new one to retrieve your Pokémon!", safchan); return false; } for (var t = 0; t < this.daycarePokemon.length; t++) { var dcpoke = this.daycarePokemon[t]; if (dcpoke.ownernum === player.idnum && dcpoke.id === pokemon.id) { var mon = dcpoke.id; if (dcpoke.shiny) { mon = mon + ""; } if (dcpoke.berry !== undefined && dcpoke.berry !== null && cdata[cdata.length - 1].toLowerCase() !== "confirm") { daycarebot.sendHtmlMessage(src, "Your " + poke(mon) + " is busy growing berries! If you take it back now, the berry you gave it will be lost forever! If you are sure you want to do this, type " + link("/daycare retrieve:" + poke(dcpoke.id) + ":confirm", false, true) + "!", safchan); return false; } player.pokemon.push(mon); daycarebot.sendMessage(src, "You retrieved " + poke(mon) + " from the Daycare!", safchan); sys.appendToFile(questLog, now() + "|||" + player.id.toCorrectCase() + "|||Daycare|||Retrieved " + (mon.shiny ? "Shiny " : "") + poke(mon) + "\n"); this.daycarePokemon.splice(t, 1); this.saveGame(player); safari.saveDaycare(); return true; } } daycarebot.sendMessage(src, "You don't have any " + poke(pokemon.id) + " in the Daycare!", safchan); return false; }; this.getDayCareBerryHarvestData = function(poke, berry) { var amt = 1, t = 86400000; switch (berry) { case "oran": case "pecha": case "razz": case "bluk": case "leppa": amt = 5; t = 21600000; // 6 hours break; case "tamato": case "pinap": case "nanab": case "petaya": case "watmel": amt = 3; t = 43200000; // 12 hours break; case "miracle": case "platinum": amt = 2; t = 259200000; // 72 hours break; default: amt = 2; } amt = (0.5 * amt) + (0.5 * amt * Math.random()); if (hasType(poke, "Grass")) { amt = (amt * 1.35); } if (hasType(poke, "Ground")) { amt = (amt * 1.35); } if (hasType(poke, "Water")) { t = Math.floor(t * 0.8); } if (canHaveAbility(poke, abilitynum("Ripen")) || canHaveAbility(poke, abilitynum("Seed Sower"))) { amt = (amt * 1.5); t = Math.floor(t * 0.75); } if (canHaveAbility(poke, abilitynum("Harvest"))) { amt = (amt * 1.8); } if (canHaveAbility(poke, abilitynum("Gluttony"))) { amt = (amt * 0.7); t = Math.floor(t * 0.4); } if (canHaveAbility(poke, abilitynum("Cheek Pouch"))) { amt = (amt * 0.8); t = Math.floor(t * 0.45); } amt = Math.round(amt); return { amount: amt, time: now() + t }; }; this.dayCarePlantBerry = function(src, player, data) { var pokesList = []; data = data.split(":"); var mon = data[0], hitMon = null, pokemon; for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].ownernum === player.idnum) { pokesList.push(this.daycarePokemon[t]); if (parseInt(getInputPokemon(mon).num, 10) == parseInt(this.daycarePokemon[t].id, 10)) { hitMon = this.daycarePokemon[t]; } } } if (pokesList.length === 0) { daycarebot.sendHtmlMessage(src, "You do not have any Pokémon in the Daycare! Type " + link("/daycare dropoff:", false, true) + " to add one!", safchan); return false; } var gardeners = []; for (var i = 0; i < pokesList.length; i++) { pokemon = pokesList[i]; if (pokemon.berry !== undefined && pokemon.berry !== null) { gardeners.push([(pokemon.shiny ? "Shiny " : "") + poke(pokemon.id), pokemon]); } } if (hitMon === null) { if (gardeners.length === 0) { daycarebot.sendHtmlMessage(src, "None of your Pokémon are growing berries! Type " + link("/daycare berry:", "/daycare berry:[pokemon]:[berry]", true) + " to give your Pokémon a berry!", safchan); return false; } else { for (var i = 0; i < gardeners.length; i++) { var gData = gardeners[i]; var pokeName = gData[0]; var pokemon = gData[1]; if (pokemon.berry.time > now()) { daycarebot.sendMessage(src, "Come back for your " + pokeName + "'s" + plural("", itemAlias(pokemon.berry.name, false, true)) + " in about " + timeLeftString(pokemon.berry.time) + "!", safchan); } else { daycarebot.sendMessage(src, pokeName + " grew some berries for you!", safchan); var g = giveStuff(player, toStuffObj(pokemon.berry.amount + "@" + pokemon.berry.name)); daycarebot.sendHtmlMessage(src, toColor("You " + g + "!", "#228B22"), safchan); daycarebot.sendHtmlMessage(src, "Want to grow more{0}? Use {1} to do so!".format(plural("", itemAlias(pokemon.berry.name, false, true)), link("/daycare berry:" + pokeName + ":" + pokemon.berry.name)), safchan); pokemon.berry = null; this.saveGame(player); safari.saveDaycare(); } } } return false; } mon = poke(hitMon.id + (hitMon.shiny ? "" : 0)); if (data.length < 2) { daycarebot.sendHtmlMessage(src, "Which berry would you like your Pokémon to grow? " + link("/daycare berry:" + mon + ":", "/daycare berry:[pokemon]:[berry]", true), safchan); return false; } var berryName = itemAlias(data[1], true, true); if (typeof berryName !== "string" || berryName.indexOf(" ") === -1 || berryName.slice(berryName.lastIndexOf(" ") + 1) !== "Berry") { daycarebot.sendHtmlMessage(src, berryName + " is not a berry! Type " + link("/daycare berry:" + mon + ":", "/daycare berry:[pokemon]:[berry]", true) + " to give your Pokémon a berry!", safchan); return false; } var berry = itemAlias(data[1], false, false); if (player.balls[berry] < 1) { daycarebot.sendMessage(src, "Sorry, but it doesn't appear that you have any" + plural("", berryName) + "!", safchan); return false; } if (berry === "miracle" || berry === "platinum") { daycarebot.sendMessage(src, "Sorry, but that berry is too precious to keep in the Daycare! Our staff cannot guard it from theft!", safchan); return false; } var gardener = null, pokemon = null; pokemon = hitMon; if ((pokemon.berry === undefined || pokemon.berry === null)) { var harvest = this.getDayCareBerryHarvestData(pokemon.id, berry); pokemon.berry = { name: berry, amount: harvest.amount, time: harvest.time }; gardener = (pokemon.shiny ? "Shiny " : "") + poke(pokemon.id); if (pokemon.hunger <= 7) { pokemon.berry.amount = Math.round(pokemon.berry.amount * 1.3); } else if (pokemon.hunger > 19) { pokemon.berry.amount = Math.round(pokemon.berry.amount * 0.75); } } if (gardener !== null) { player.balls[berry] -= 1; daycarebot.sendMessage(src, "You gave " + an(berryName) + " to your " + gardener + "! Your berries should be ready to harvest in about " + timeLeftString(pokemon.berry.time) + ".", safchan); pokemon.toHolding = now() + 60 * 60 * 1000 * 24 * 14; //2 weeks this.saveGame(player); safari.saveDaycare(); } else { daycarebot.sendMessage(src, "Sorry, but that Pokémon is already busy growing berries!", safchan); } return gardener !== null; }; this.getUniqueDayCareId = function() { var i = 0, j = this.daycarePokemon.length; for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].uid >= i) { i = this.daycarePokemon[t].uid + 1; } if (this.daycarePokemon[t].uid <= j) { j = this.daycarePokemon[t].uid - 1; } } if (j >= 0) { return j; } return i; }; this.getDayCareItem = function(id, hearts, area) { var failChance; if (hearts < 100) { failChance = ((300 - hearts) * 0.33) * 0.01; } else { failChance = ((600 - hearts) * 0.12) * 0.01; } if (chance(failChance)) { return null; } var box = {}, apcamount = 1, pearlamount = 1, gachaamount = 1, hdewamount = 5, prize, amt; if (hearts >= 220) { box = { "hdew": 15, "nugget": 2, "redapricorn": 0.3, "bluapricorn": 0.3, "grnapricorn": 0.3, "ylwapricorn": 0.3, "pnkapricorn": 0.3, "whtapricorn": 0.3, "blkapricorn": 0.3, "gacha": 1, "eviolite": 1, "gem": 1, "silver": 20, "bignugget": 2, }; apcamount = (chance(0.5) ? 20 : 15); gachaamount = 10; hdewamount = 10; } else if (hearts >= 180) { box = { "hdew": 5, "nugget": 5, "redapricorn": 1, "bluapricorn": 1, "grnapricorn": 1, "ylwapricorn": 1, "pnkapricorn": 1, "whtapricorn": 1, "blkapricorn": 1, "gacha": 3, "eviolite": 3, "gem": 2, "silver": 15, "bignugget": 1, }; apcamount = (chance(0.5) ? 15 : 10); gachaamount = 7; hdewamount = 5; } else if (hearts >= 120) { box = { "hdew": 2, "stardust": 10, "redapricorn": 3, "bluapricorn": 3, "grnapricorn": 3, "ylwapricorn": 3, "pnkapricorn": 3, "whtapricorn": 3, "blkapricorn": 3, "gacha": 6, "gem": 3, "eviolite": 2, "silver": 10, "nugget": 2 }; apcamount = (chance(0.5) ? 9 : 7); gachaamount = 5; hdewamount = 5; } else if (hearts >= 80) { box = { "hdew": 2, "bigpearl": 20, "redapricorn": 8, "bluapricorn": 8, "grnapricorn": 8, "ylwapricorn": 8, "pnkapricorn": 8, "whtapricorn": 8, "blkapricorn": 8, "gacha": 10, "gem": 9, "stardust": 5, "silver": 5, "nugget": 3 }; apcamount = (chance(0.5) ? 7 : 5); gachaamount = 3; hdewamount = 5; } else if (hearts >= 60) { box = { "hdew": 2, "bigpearl": 20, "pearl": 10, "redapricorn": 15, "bluapricorn": 15, "grnapricorn": 15, "ylwapricorn": 15, "pnkapricorn": 15, "whtapricorn": 15, "blkapricorn": 15, "gacha": 20, "gem": 5, "stardust": 2, "nugget": 3 }; apcamount = (chance(0.5) ? 5 : 3); pearlamount = (chance(0.25) ? 4 : 3); gachaamount = 3; hdewamount = 5; } else if (hearts >= 40) { box = { "hdew": 2, "pearl": 55, "redapricorn": 25, "bluapricorn": 25, "grnapricorn": 25, "ylwapricorn": 25, "pnkapricorn": 25, "whtapricorn": 25, "blkapricorn": 25, "gacha": 50, "gem": 5, "bigpearl": 35, "nugget": 3 }; apcamount = (chance(0.5) ? 3 : 2); gachaamount = (chance(0.5) ? 2 : 1); pearlamount = (chance(0.5) ? 3 : 2); hdewamount = 5; } else if (hearts >= 20) { box = { "hdew": 2, "bigpearl": 25, "pearl": 120, "redapricorn": 50, "bluapricorn": 50, "grnapricorn": 50, "ylwapricorn": 50, "pnkapricorn": 50, "whtapricorn": 50, "blkapricorn": 50, "gem": 5, "gacha": 70, "nugget": 3 } apcamount = (chance(0.5) ? 2 : 1); pearlamount = 2; hdewamount = 5; } else { box = { "pearl": 150, "bigpearl": 20, "redapricorn": 70, "bluapricorn": 70, "grnapricorn": 70, "ylwapricorn": 70, "pnkapricorn": 70, "whtapricorn": 70, "blkapricorn": 70, "gacha": 90, "nugget": 3 } apcamount = (chance(0.5) ? 2 : 1); hdewamount = 5; } if (area == "grotto") { if (box["grnapricorn"]) { box["grnapricorn"] += 2; box["grnapricorn"] *= 2; } } if (area == "jungle") { if (!box["hdew"]) { box["hdew"] = 0; } box["hdew"] += 5; box["hdew"] *= 1.2; } if (area == "beach") { if (!box["pearl"]) { box["pearl"] = 0; } box["pearl"] += 2; box["pearl"] *= 1.5; } if (hasType(id, "Grass")) { apcamount = Math.floor(apcamount * (1 + Math.random())); } if (hasType(id, "Normal")) { gachaamount++; } if (hasType(id, "Bug")) { box["honey"] = 5; } if (hasType(id, "Water")) { box["water"] = 2; pearlamount++; } if (hasType(id, "Fire")) { box["cookie"] = 9; } if (hasType(id, "Dragon")) { box["comet"] = 3; } if (hasType(id, "Ground")) { box["stardust"] = 10; } if (hasType(id, "Rock")) { box["fossil"] = 2; } if (hasType(id, "Psychic")) { box["pack"] = 5; } if (hasType(id, "Flying")) { if (!box["nugget"]) { box["nugget"] = 0; } box["nugget"] += 2; } if (hasType(id, "Electric")) { box["battery"] = 3; } if (canLearnMove(id, 168)) { box["silver"] = 12; } if (canLearnMove(id, 168) && (hearts >= 100)) { if (!box["bignugget"]) { box["bignugget"] = 0; } box["bignugget"] += 3; } if (evolutions.hasOwnProperty(id+"")) { box["rare"] = 15; } if (megaEvolutions.hasOwnProperty(id+"")) { box["mega"] = 1; } prize = randomSample(box); if (prize.indexOf("apricorn") !== -1) { amt = apcamount; } else if (prize == "pearl" || prize == "bigpearl") { amt = pearlamount; } else if (prize == "gacha") { amt = gachaamount; } else if (prize == "hdew") { amt = hdewamount; } else if (prize == "silver") { amt = 20; } return (amt + "@" + prize); }; this.dayCareStep = function(weight) { if (weight === 2) { //Once per day this.updateDayCareTiles(true); } if (weight === 1) { //end of every contest this.updateDayCareTiles(false); this.relocateDayCarePokemon(true); } else { //Once every 10 minutes or so this.relocateDayCarePokemon(false); } this.saveDaycare(); }; this.relocateDayCarePokemon = function(full) { for (var t in this.daycarePokemon) { //move them 2-5 spaces, unless their current action is napping //if their current preferred area is not their current area, move them towards their new preferred area //check to make sure they can move to the target area (e.g., no rock or tree there, no water if they can't swim) var pk = this.daycarePokemon[t], move = Math.floor(6 - (Math.random() * 3.25)); if (pk.activity == "traveling" || pk.activity == "arriving") { move += 2; } if (pk.activity !== "napping") { this.findNewDayCareLocation(this.daycarePokemon[t], this.daycarePokemon[t].area, move); } if (full) { pk.findItem = true; pk.meter = Math.min(pk.meter + 1, 20); if (chance(0.05)) { pk.hunger = Math.min(pk.hunger + 1, 20); } if (pk.toHolding < now()) { pk.area = "holding"; } } if (pk.findItem) { pk.canItem = true; } this.getNewDayCareActivity(this.daycarePokemon[t]); var name = idnumList.get(pk.ownernum); var player = getAvatarOff(name); if (!(player)) { continue; } if (!(player.notificationData)) { continue; } player.notificationData.daycarePoke = pk.id; if (player.notificationData.daycareWaiting) { player.notificationData.daycareWaiting = false; player.notificationData.daycarePlay = (pk.meter > 5 ? true : false); player.notificationData.daycareHungry = (pk.hunger > 12 ? true : false); } safari.saveGame(player); } }; this.getNewDayCareActivity = function(pokemon) { var feat = this.getNearbyFeatures2(pokemon.pos, pokemon.area); var inter = feat.random(); var feat2 = this.getNearbyFeatures(pokemon.pos, pokemon.area); var inter2 = feat2.random(); var onSquare = this.getFeatureAt(pokemon.pos, pokemon.area); var act = ""; if (onSquare == "water" && (hasType(pokemon.id, "Water") || (!hasType(pokemon.id, "Flying")))) { act = "splashing" if (countDuplicates(feat, "water") > 1 && (chance(0.5))) { act = "swimming"; } else if (countDuplicates(feat, "water") > 2) { act = "swimming"; } if (countDuplicates(feat, "water") > 6 && (chance(0.5))) { act = "diving"; } } else if (onSquare == "rock" && (chance(0.5))) { act = "perching"; } else { if (inter2 == "water" && chance(0.7)) { act = "splashing"; } else if ((inter == "flowers" || (inter == "sandflowers")) && chance(0.9)) { act = "flowers"; } else if ((inter == "rock" || (inter == "sandgrass")) && chance(0.8)) { act = "rock"; } else if (inter == "lilypad" && chance(0.75)) { act = "lilypad"; } else if (inter == "bush" && (chance(0.55)) && pokemon.hunger > 10) { act = "eating"; } else if (inter == "bigtree" && chance(0.75)) { act = "bigtree"; } else if (inter == "bigtree2" && chance(0.75)) { act = "bigtree"; } else if ((inter == "tree1" || inter2 == "tree2") && (chance(0.33))) { act = "tree"; } else if ((inter == "grass" || (inter == "sandgrass")) && (chance(0.33))) { act = "grass"; } else if (chance(0.3)) { act = "napping"; } else { if (hasType(pokemon.id, "Flying") && (chance(0.45))) { act = "flapping"; } else if (chance(0.6)) { var spec = {}; if (canLearnMove(pokemon.id, 46)) { spec.roaring = 1; } if (canLearnMove(pokemon.id, 47)) { spec.singing = 1; } if (canLearnMove(pokemon.id, 84) || canLearnMove(pokemon.id, 209)) { spec.thundershocking = 1; } if (canLearnMove(pokemon.id, 22)) { spec.vinewhipping = 1; } if (canLearnMove(pokemon.id, 52)) { spec.embering = 1; } if (canLearnMove(pokemon.id, 145)) { spec.bubbling = 1; } if (canLearnMove(pokemon.id, 417)) { spec.plotting = 1; } if (canLearnMove(pokemon.id, 477)) { spec.levitating = 1; } act = randomSample(spec); if (!act) { act = ["jumping", "prancing", "dancing"].random(); } } else { act = ["jumping", "prancing", "dancing"].random(); } } } pokemon.activity = act; if (act == "eating") { pokemon.hunger = Math.max(Math.floor(pokemon.hunger - (1.75 + (3 * Math.random()))), 0); } var addh = 1; if (hasType(pokemon.id, "Water") && (act == "splashing" || act == "swimming" || act == "diving")) { addh++; } else if (hasType(pokemon.id, "Grass") && (act == "grass")) { addh++; } else if ((hasType(pokemon.id, "Dragon") || hasType(pokemon.id, "Flying")) && (act == "perching")) { addh++; } else if (act == "roaring") { addh++; } else if (act == "singing") { addh++; } else if (act == "embering") { addh++; } else if (act == "bubbling") { addh++; } else if (act == "vinewhipping") { addh++; } else if (act == "plotting") { addh++; } else if (act == "levitating") { addh++; } else if (act == "thundershocking") { addh++; } else if (act == "diving") { addh += 2; } else if (act == "lilypad") { addh += 3; } else if (act == "flowers") { addh += 4; } addh = 2 * (1 + addh) * Math.random(); var mult = 1; mult = (Math.min(((100 + pokemon.playhearts) / 135), 1) * 0.9875); pokemon.hearts = Math.max(Math.floor(4 * (pokemon.hearts + addh - (pokemon.hunger >= 20 ? 6 : pokemon.hunger > 15 ? 3 : 0)) * mult) * 0.25, 0); if (pokemon.playhearts > 0) { pokemon.playhearts = pokemon.playhearts - 0.07; } if (pokemon.playhearts > 15) { pokemon.playhearts = pokemon.playhearts - 0.1; } if (pokemon.playhearts > 35) { pokemon.playhearts = pokemon.playhearts - 0.25; } if (pokemon.playhearts > 50) { pokemon.playhearts = pokemon.playhearts - 0.33; } if (pokemon.playhearts > 60) { pokemon.playhearts = pokemon.playhearts - 0.42; } if (pokemon.playhearts > 70) { pokemon.playhearts = pokemon.playhearts - 0.5; } if (pokemon.hunger > 15) { pokemon.playhearts = pokemon.playhearts - 1; } if (pokemon.hunger > 17) { pokemon.playhearts = pokemon.playhearts - 1; } if (pokemon.hunger > 19) { pokemon.playhearts = pokemon.playhearts - 1; } pokemon.playhearts = Math.max(pokemon.playhearts, 0); pokemon.activity = act; pokemon.canPlay = true; return true; }; this.moveDayCarePokemon = function(pokemon, pos) { pokemon.pos = pos; var row = pos[0]; var column = parseInt(pos[1], 10); var letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]; row = letters.indexOf(row) + 1; pokemon.row = row; pokemon.column = column; }; this.getPosFromXY = function(x, y) { var row = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"][y-1]; var column = (x + ""); return (row + column); }; this.pokemonAtDayCarePos = function(area, pos) { for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].pos === pos) { return this.daycarePokemon[t]; } } return false; }; this.findNewDayCareLocation = function(pokemon, area, distance) { var loop = 10, dx, dy, tox, toy, pos, cData, r1, r2, newregion, val, passingTo; var regionConnections = { "grotto": { "east": "jungle", "north": "beach" }, "beach": { "south": "grotto", "east": "mountain", }, "jungle": { "west": "grotto", "north": "mountain" }, "mountain": { "south": "jungle", "west": "beach" } }; while (loop > 0) { loop--; range = (1 + Math.floor(Math.random() * distance)); dx = (Math.round(Math.random() * range)); dy = range - dx; dx = (chance(0.5) ? dx * -1 : dx); dy = (chance(0.5) ? dy * -1 : dy); tox = Math.max(Math.min(pokemon.column + dx, 12), 1); toy = Math.max(Math.min(pokemon.row + dy, 12), 1); pos = this.getPosFromXY(tox, toy); if (this.validDayCareLocation(pokemon.id, pos, area)) { if (!(area == "grotto" && tox < 4 && toy > 9)) { if (regionConnections.hasOwnProperty(area)) { cData = regionConnections[area]; newregion = null; var counted = this.dayCareRegionCount(); if (tox >= 11 && dx > 0 && cData.hasOwnProperty("east")) { newregion = cData["east"]; passingTo = "east"; } if (tox <= 1 && dx < 0 && cData.hasOwnProperty("west")) { newregion = cData["west"]; passingTo = "west"; } if (toy >= 12 && dy > 0 && cData.hasOwnProperty("south")) { newregion = cData["south"]; passingTo = "south"; } if (toy <= 1 && dy < 0 && cData.hasOwnProperty("north")) { newregion = cData["north"]; passingTo = "north"; } if (newregion !== null) { r1 = counted[newregion]; r2 = counted[area]; val = (((r2 + 6) * 100)/(r1 + 9)) * 0.01 * 0.5; if (chance(val)) { pokemon.area = newregion; switch (passingTo) { case "east": tox = 1; break; case "west": tox = 12; break; case "north": toy = 12; break; case "south": toy = 1; break; } pos = this.getPosFromXY(tox, toy); } } } this.moveDayCarePokemon(pokemon, pos); return true; } } } return false; }; this.validDayCareLocation = function(id, pos, area) { var feat = this.getFeatureAt(pos, area); if (feat == "bigtree") { return false; } if (feat == "bigtree2") { return false; } if (["cliff1","cliff2","cliff3","cliff4","cliff5","cliff6","cliff7","cliff8"].contains(feat)) { return false; } if (feat == "tree1") { return false; } if (feat == "tree2") { return false; } var category; //swimmer, amphibious, land, flier if ([129, 130, 349, 350, 456, 457, 90, 91, 118, 119].contains(id)) { category = "swimmer"; } else if (hasType(id, "Water")) { category = "amphibious"; } else if (canLearnMove(id, 19)) { category = "flier"; } else { category = "land"; } if (feat == "rock" && category !== "flier") { return false; } if (feat == "water" && category == "land") { return false; } if (feat == "lilypad" && category == "land") { return false; } if (this.getPokemonAt(pos, area) !== undefined) { return false; } return true; }; this.updateDayCareTiles = function(full) { //All the areas in the daycare are slightly changed. //This happens at the end of a contest, and at the end of a day cycle, with the latter being more drastic var c, hold; var featureCount = { grass: 0, sprout: 0, tree: 0, bigtree: 0, rock: 0, water: 0, lilypad: 0, flowers: 0 }; var maxFeaturesGrotto = { grass: 30, sprout: 14, tree: 9, bigtree: 6, rock: 4, water: 20, lilypad: 4, flowers: 9 }; if (!this.daycareRegions.grotto) { this.daycareRegions.grotto = {}; } if (!this.daycareRegions.beach) { this.daycareRegions.beach = {}; } if (!this.daycareRegions.jungle) { this.daycareRegions.jungle = {}; } if (!this.daycareRegions.mountain) { this.daycareRegions.mountain = {}; } var val = ""; var letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]; for (var i = 0; i < 12; i++) { for (var j = 0; j < letters.length; j++) { val = (letters[j] + i); if (!this.daycareRegions.grotto.hasOwnProperty(val)) { this.daycareRegions.grotto[val] = ""; } } } for (var t in this.daycareRegions.grotto) { c = this.daycareRegions.grotto[t]; if (c === "grass") { if (chance(0.04) || (full && chance(0.08))) { this.daycareRegions.grotto[t] = ""; } else { featureCount.grass++; } } else if (c === "sprout") { if (chance(0.025) || (full && chance(0.05))) { this.daycareRegions.grotto[t] = ""; } else { featureCount.sprout++; } } else if (c === "tree1" || c === "tree2") { if (chance(0.015) || (full && chance(0.03))) { this.daycareRegions.grotto[t] = ""; } else { featureCount.tree++; } } else if (c === "bigtree") { if ((full && chance(0.09))) { this.daycareRegions.grotto[t] = ""; } else { featureCount.bigtree++; } } else if (c === "water") { if ((chance(0.01) || (full && chance(0.02))) && (countDuplicates(this.getNearbyFeatures(t, "grotto"), "water") + countDuplicates(this.getNearbyFeatures(t, "grotto"), "lilypad") < 4)) { this.daycareRegions.grotto[t] = ""; } else if ((chance(0.02) || (full && chance(0.25))) && (countDuplicates(this.getNearbyFeatures(t, "grotto"), "water") + countDuplicates(this.getNearbyFeatures(t, "grotto"), "lilypad") < 3)) { this.daycareRegions.grotto[t] = ""; } else if ((chance(0.05) || (full && chance(0.33))) && (countDuplicates(this.getNearbyFeatures(t, "grotto"), "water") + countDuplicates(this.getNearbyFeatures(t, "grotto"), "lilypad") < 2)) { this.daycareRegions.grotto[t] = ""; } else { featureCount.water++; } } else if (c === "lilypad") { if (chance(0.05) || (full && chance(0.1))) { this.daycareRegions.grotto[t] = "water"; } else { featureCount.lilypad++; } } else if (c === "flowers") { if (chance(0.01) || (full && chance(0.12))) { this.daycareRegions.grotto[t] = ""; } else { featureCount.flowers++; } } else if (c === "rock") { if (full && chance(0.12)) { this.daycareRegions.grotto[t] = ""; } else { featureCount.rock++; } } } for (var t in this.daycareRegions.grotto) { c = this.daycareRegions.grotto[t]; if (c === "") { if (featureCount.grass < maxFeaturesGrotto.grass) { if (countDuplicates(this.getNearbyFeatures(t, "grotto"), "grass") >= 3 && (chance(0.6))) { this.daycareRegions.grotto[t] = "grass"; featureCount.grass++; } else if (countDuplicates(this.getNearbyFeatures(t, "grotto"), "grass") >= 2 && (chance(0.4))) { this.daycareRegions.grotto[t] = "grass"; featureCount.grass++; } else if (this.getNearbyFeatures(t, "grotto").contains("grass") && (chance(0.12))) { this.daycareRegions.grotto[t] = "grass"; featureCount.grass++; } else if (chance(0.01)) { this.daycareRegions.grotto[t] = "grass"; featureCount.grass++; } } if (featureCount.water < maxFeaturesGrotto.water) { if ((this.getNearbyFeatures(t, "grotto").contains("water") && (chance(0.5) && full)) || (chance(0.01))) { this.daycareRegions.grotto[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "grotto"), "water") >= 2 && (chance(0.04))) { this.daycareRegions.grotto[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "grotto"), "water") >= 3 && (chance(0.15))) { this.daycareRegions.grotto[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "grotto"), "water") >= 4 && (chance(0.25))) { this.daycareRegions.grotto[t] = "water"; featureCount.water++; } } if (featureCount.rock < maxFeaturesGrotto.rock) { if ((chance(0.03) || ((chance(0.08) && full)))) { this.daycareRegions.grotto[t] = "rock"; featureCount.rock++; } } if (featureCount.flowers < maxFeaturesGrotto.flowers) { if (chance(0.05)) { this.daycareRegions.grotto[t] = "flowers"; featureCount.flowers++; } } if (featureCount.sprout < maxFeaturesGrotto.sprout) { if (chance(0.075)) { this.daycareRegions.grotto[t] = "sprout"; featureCount.sprout++; } } } if (c === "water" && featureCount.lilypad < maxFeaturesGrotto.lilypad) { if (chance(0.015) || (full && chance(0.04))) { this.daycareRegions.grotto[t] = "lilypad"; featureCount.water--; featureCount.lilypad++; } } else if (c === "lilypad" && featureCount.water < maxFeaturesGrotto.water) { if (chance(0.05) || (full && chance(0.1))) { this.daycareRegions.grotto[t] = "water"; featureCount.water++; featureCount.lilypad--; } } else if (c === "sprout") { if ((chance(0.018) || (full && chance(0.037))) && featureCount.tree < maxFeaturesGrotto.tree) { if (chance(0.5)) { this.daycareRegions.grotto[t] = "tree1"; } else { this.daycareRegions.grotto[t] = "tree2"; } featureCount.tree++; featureCount.sprout--; } } else if (c === "tree1" || c === "tree2") { if (((chance(0.01) && (chance(0.6))) || (full && chance(0.07))) && featureCount.bigtree < maxFeaturesGrotto.bigtree) { if (chance(0.5)) { this.daycareRegions.grotto[t] = "bigtree"; featureCount.bigtree++; featureCount.tree--; } } } } featureCount = { grass: 0, sprout: 0, tree: 0, bigtree: 0, rock: 0, water: 0, lilypad: 0, flowers: 0, sand: 0 }; var maxFeaturesBeach = { grass: 6, sprout: 0, tree: 0, bigtree: 0, rock: 4, water: 85, lilypad: 0, flowers: 5, sand: 0 }; for (var i = 0; i < 12; i++) { for (var j = 0; j < letters.length; j++) { val = (letters[j] + i); if ((i < 7 && j < 8) || (i < 10 && j < 5)) { this.daycareRegions.beach[val] = "water"; } else if (!this.daycareRegions.beach.hasOwnProperty(val)) { this.daycareRegions.beach[val] = ""; } } } for (var t in this.daycareRegions.beach) { c = this.daycareRegions.beach[t]; if (c === "grass" || c == "sandgrass") { if (chance(0.03) || (full && chance(0.07))) { this.daycareRegions.beach[t] = ""; } else { featureCount.grass++; } } else if (c === "rock" || c == "sandrock") { if ((full && chance(0.09)) || (this.getNearbyFeatures(t, "beach").contains("water"))) { this.daycareRegions.beach[t] = ""; } else { featureCount.rock++; } } else if (c === "flowers" || c == "sandflowers") { if ((full && chance(0.09)) || (this.getNearbyFeatures(t, "beach").contains("water"))) { this.daycareRegions.beach[t] = ""; } else { featureCount.flowers++; } } else if (c === "water") { if ((chance(0.01) || (full && chance(0.02))) && (countDuplicates(this.getNearbyFeatures(t, "beach"), "water") + countDuplicates(this.getNearbyFeatures(t, "beach"), "lilypad") < 4)) { this.daycareRegions.beach[t] = ""; } else if ((chance(0.02) || (full && chance(0.25))) && (countDuplicates(this.getNearbyFeatures(t, "beach"), "water") + countDuplicates(this.getNearbyFeatures(t, "beach"), "lilypad") < 3)) { this.daycareRegions.beach[t] = ""; } else if ((chance(0.05) || (full && chance(0.33))) && (countDuplicates(this.getNearbyFeatures(t, "beach"), "water") + countDuplicates(this.getNearbyFeatures(t, "beach"), "lilypad") < 2)) { this.daycareRegions.beach[t] = ""; } else { featureCount.water++; } } } for (var t in this.daycareRegions.beach) { c = this.daycareRegions.beach[t]; if (c === "") { if (featureCount.grass < maxFeaturesBeach.grass) { if (countDuplicates(this.getNearbyFeatures(t, "beach"), "grass") >= 3 && (chance(0.6))) { this.daycareRegions.beach[t] = "grass"; featureCount.grass++; } else if (countDuplicates(this.getNearbyFeatures(t, "beach"), "grass") >= 2 && (chance(0.4))) { this.daycareRegions.beach[t] = "grass"; featureCount.grass++; } else if (this.getNearbyFeatures(t, "beach").contains("grass") && (chance(0.12))) { this.daycareRegions.beach[t] = "grass"; featureCount.grass++; } else if (chance(0.01)) { this.daycareRegions.beach[t] = "grass"; featureCount.grass++; } } if (featureCount.rock < maxFeaturesBeach.rock) { if ((chance(0.03) || ((chance(0.08) && full)))) { this.daycareRegions.beach[t] = "rock"; featureCount.rock++; } } if (featureCount.flowers < maxFeaturesBeach.flowers) { if (chance(0.05)) { this.daycareRegions.beach[t] = "flowers"; featureCount.flowers++; } } } if (featureCount.water < maxFeaturesBeach.water) { if ((this.getNearbyFeatures(t, "beach").contains("water") && (chance(0.5) && full)) || (chance(0.01))) { this.daycareRegions.beach[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "beach"), "water") >= 2 && (chance(0.04))) { this.daycareRegions.beach[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "beach"), "water") >= 3 && (chance(0.15))) { this.daycareRegions.beach[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "beach"), "water") >= 4 && (chance(0.25))) { this.daycareRegions.beach[t] = "water"; featureCount.water++; } } } for (var t in this.daycareRegions.beach) { c = this.daycareRegions.beach[t]; hold = this.getNearbyFeatures2(t, "beach"); if (countDuplicates(hold, "water") * 6 + countDuplicates(hold, "sand") >= 8) { if (c == "flowers") { this.daycareRegions.beach[t] = "sandflowers"; } else if (c == "rock") { this.daycareRegions.beach[t] = "sandrock"; } else if (c == "grass") { this.daycareRegions.beach[t] = "sandgrass"; } else if (c == "") { this.daycareRegions.beach[t] = "sand"; } } } featureCount = { grass: 0, sprout: 0, tree: 0, bigtree: 0, rock: 0, water: 0, lilypad: 0, flowers: 0, sand: 0 }; var maxFeaturesJungle = { grass: 9, sprout: 28, tree: 24, bigtree: 20, rock: 0, water: 40, lilypad: 0, flowers: 6, sand: 0 }; for (var i = 0; i < 12; i++) { for (var j = 0; j < letters.length; j++) { val = (letters[j] + i); if (!this.daycareRegions.jungle.hasOwnProperty(val)) { this.daycareRegions.jungle[val] = ""; } } } for (var t in this.daycareRegions.jungle) { c = this.daycareRegions.jungle[t]; if (c === "grass") { if (chance(0.04) || (full && chance(0.08))) { this.daycareRegions.jungle[t] = ""; } else { featureCount.grass++; } } else if (c === "sprout") { if (chance(0.025) || (full && chance(0.05))) { this.daycareRegions.jungle[t] = ""; } else { featureCount.sprout++; } } else if (c === "tree1" || c === "tree2") { if (chance(0.015) || (full && chance(0.03))) { this.daycareRegions.jungle[t] = ""; } else { featureCount.tree++; } } else if (c === "bigtree") { if ((full && chance(0.08))) { this.daycareRegions.jungle[t] = ""; } else { featureCount.bigtree++; } } else if (c === "bigtree2") { if ((full && chance(0.08))) { this.daycareRegions.jungle[t] = ""; } else { featureCount.bigtree++; } } else if (c === "water") { if (((chance(0.01) && (chance(0.15))) || (full && chance(0.2) && chance(0.01))) && (countDuplicates(this.getNearbyFeatures(t, "jungle"), "water") + countDuplicates(this.getNearbyFeatures(t, "jungle"), "lilypad") < 4)) { this.daycareRegions.jungle[t] = ""; } else if ((chance(0.02) || (full && chance(0.1))) && (countDuplicates(this.getNearbyFeatures(t, "jungle"), "water") + countDuplicates(this.getNearbyFeatures(t, "jungle"), "lilypad") < 3)) { this.daycareRegions.jungle[t] = ""; } else if ((chance(0.05) || (full && chance(0.08))) && (countDuplicates(this.getNearbyFeatures(t, "jungle"), "water") + countDuplicates(this.getNearbyFeatures(t, "jungle"), "lilypad") < 2)) { this.daycareRegions.jungle[t] = ""; } else { featureCount.water++; } } else if (c === "flowers") { if (chance(0.01) || (full && chance(0.12))) { this.daycareRegions.jungle[t] = ""; } else { featureCount.flowers++; } } } for (var t in this.daycareRegions.jungle) { c = this.daycareRegions.jungle[t]; if (c === "") { if (featureCount.grass < maxFeaturesJungle.grass) { if (countDuplicates(this.getNearbyFeatures(t, "jungle"), "grass") >= 3 && (chance(0.6))) { this.daycareRegions.jungle[t] = "grass"; featureCount.grass++; } else if (countDuplicates(this.getNearbyFeatures(t, "jungle"), "grass") >= 2 && (chance(0.4))) { this.daycareRegions.jungle[t] = "grass"; featureCount.grass++; } else if (this.getNearbyFeatures(t, "jungle").contains("grass") && (chance(0.12))) { this.daycareRegions.jungle[t] = "grass"; featureCount.grass++; } else if (chance(0.01)) { this.daycareRegions.jungle[t] = "grass"; featureCount.grass++; } } if (featureCount.water < maxFeaturesJungle.water) { var amt = countDuplicates(this.getNearbyFeatures2(t, "jungle"), "water"); if (((((amt < 6 || (amt < 7 && chance(0.5)))) && chance(0.5)) || amt < 5) && (((amt >= 1) && (chance(0.25)) || amt >= 2))) { if ((this.getNearbyFeatures(t, "jungle").contains("water") && (chance(0.8) && full)) || (chance(0.02))) { this.daycareRegions.jungle[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "jungle"), "water") >= 2 && (chance(0.06))) { this.daycareRegions.jungle[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "jungle"), "water") >= 3 && (chance(0.23))) { this.daycareRegions.jungle[t] = "water"; featureCount.water++; } else if (countDuplicates(this.getNearbyFeatures(t, "jungle"), "water") >= 4 && (chance(0.45))) { this.daycareRegions.jungle[t] = "water"; featureCount.water++; } } } if (featureCount.flowers < maxFeaturesJungle.flowers) { if (chance(0.05)) { this.daycareRegions.jungle[t] = "flowers"; featureCount.flowers++; } } if (featureCount.sprout < maxFeaturesJungle.sprout) { if (chance(0.075)) { this.daycareRegions.jungle[t] = "sprout"; featureCount.sprout++; } } } else if (c === "sprout") { if ((chance(0.035) || (full && chance(0.037))) && featureCount.tree < maxFeaturesJungle.tree) { if (chance(0.85)) { this.daycareRegions.jungle[t] = "tree1"; } else { this.daycareRegions.jungle[t] = "tree2"; } featureCount.tree++; featureCount.sprout--; } } else if (c === "tree1" || c === "tree2") { if (((chance(0.01) && (chance(0.8))) || (full && chance(0.12))) && featureCount.bigtree < maxFeaturesJungle.bigtree) { if (chance(0.5)) { if (chance(0.5)) { this.daycareRegions.jungle[t] = "bigtree"; } else { this.daycareRegions.jungle[t] = "bigtree2"; } featureCount.bigtree++; featureCount.tree--; } } } } featureCount = { grass: 0, sprout: 0, tree: 0, bigtree: 0, rock: 0, water: 0, lilypad: 0, flowers: 0, sand: 0 }; var maxFeaturesMountain = {}; //Mountain has to be done manually for (var i = 0; i < 12; i++) { for (var j = 0; j < letters.length; j++) { val = (letters[j] + i); if (!this.daycareRegions.mountain.hasOwnProperty(val)) { this.daycareRegions.mountain[val] = ""; } } } }; this.getDistance = function(row, column, row2, column2) { return (Math.abs(row - row2) + Math.abs(column - column2)); }; this.getNearbyPokemon = function(pokemon, area) { var out = []; for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].area === area && this.daycarePokemon[t].uid !== pokemon.uid) { if (this.getDistance(pokemon.row, pokemon.column, this.daycarePokemon[t].row, this.daycarePokemon[t].column) <= 2) { out.push(this.daycarePokemon[t]); } } } return out; }; this.getNearbyFeatures = function(pos, area) { var out = []; var row = pos[0]; var column = parseInt(pos.slice(1), 10); var letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]; var temprow = letters.indexOf(row); out.push(this.getFeatureAt(letters[temprow] + column, area)); if (temprow > 0) { out.push(this.getFeatureAt(letters[temprow-1] + column, area)); } if (temprow < 11) { out.push(this.getFeatureAt(letters[temprow+1] + column, area)); } if (column > 0) { out.push(this.getFeatureAt(letters[temprow] + (column - 1), area)); } if (column < 11) { out.push(this.getFeatureAt(letters[temprow] + (column + 1), area)); } return out; }; this.getNearbyFeatures2 = function(pos, area) { var out = []; var row = pos[0]; var column = parseInt(pos.slice(1), 10); var letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]; var temprow = letters.indexOf(row); out.push(this.getFeatureAt(letters[temprow] + column, area)); if (temprow > 0) { out.push(this.getFeatureAt(letters[temprow-1] + column, area)); if (column > 0) { out.push(this.getFeatureAt(letters[temprow-1] + (column - 1), area)); } if (column < 11) { out.push(this.getFeatureAt(letters[temprow-1] + (column + 1), area)); } } if (temprow > 1) { out.push(this.getFeatureAt(letters[temprow-2] + column, area)); } if (temprow < 11) { out.push(this.getFeatureAt(letters[temprow+1] + column, area)); if (column > 0) { out.push(this.getFeatureAt(letters[temprow+1] + (column - 1), area)); } if (column < 11) { out.push(this.getFeatureAt(letters[temprow+1] + (column + 1), area)); } } if (temprow < 10) { out.push(this.getFeatureAt(letters[temprow+2] + column, area)); } if (column > 0) { out.push(this.getFeatureAt(letters[temprow] + (column - 1), area)); } if (column > 1) { out.push(this.getFeatureAt(letters[temprow] + (column - 2), area)); } if (column < 11) { out.push(this.getFeatureAt(letters[temprow] + (column + 1), area)); } if (column < 10) { out.push(this.getFeatureAt(letters[temprow] + (column + 2), area)); } return out; }; var daycareTiles = { "grass": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAMAAADPNIq/AAABLFBMVEX///8knAAecgsTKA4upQoogg5MwRlCtxQjgSRAsBwihx4ljB8srgpDvBYjlRAwpRY5thIkhB8fgh0zsw4ysQwadCA3pRhEuRVGvRceXh4nkRcwqRQigh4cfxsYbx84qBk8thUqohQnnRMbehopqQgloQQhfCMtUiIhhB0hfSIrrQoppwwhkQ4dgxkqqgoonxIigCMsZB8jPxwljhwqqAsehBopphErYiATIxJWzydQxR1b1CwskCElgx0nhyIWUhoosVcohiE0dCkfcCJTyyQtcyYwdyYmhCApp0oigEIuym0gcyAhbyI5byskeB8jhho+qxtUyicyciczcCkptlgmkVA3fiUqkRNJuSg0eyMtw1YmqCQdhTArtScVMB8ux2cqt10uxFwnl0EhaT37yHrnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAAFDSURBVEjH7dHXUsJAFIDhPQaX1ZzEElFRdzUQsGGvEInYdU0UCxbs+v7vYMBytVGuvHDyz+xMMvPN2ZMJIXFx/ySAtpaclki0tzaQ0iT8ciWEQPuCzTdlrINS6NSRUgOZmaCURcGubprs6UXL6sNU/8CgFQXTQ8OGMcKxkRg1xux01JJpHZF/wgzaUQMJZFMMnIxADE/OzAvQlG4cJiaZMzVd4HymgLNz83lYWFRCc0kP7142OV8xEVd13VxTQsLsxnY2+zhhbF3taLFIMZdrGnCskks3lN8tXLdIWapMG4n8Rsl1vU0VrHjeVnl7pxwO3qVib//A8w6PVFDyY98PTk6rrntWzZ5f+D6/rCkhXgVBcF0IV7gRcBs+Y10JibwDwLqUQtxL+eAAyMeof0O0+pOUlcqzlC+v5Mdq372RuLi/7h32WCxVHGicqwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wNC0yOFQxNTo1MToxNS0wNDowMENJ0Q8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDQtMjhUMTU6NTE6MTUtMDQ6MDAyFGmzAAAAAElFTkSuQmCC", "grasswater": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAGFBMVEX///8XhyU/tUE3nUEVcitJ1EsxjbkfcqGFqo5cAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAACTSURBVDjL5c7BEcMwCARAtUAL1wIt0AItqAW3nzvhjPOw8NsTHpK8DD7G+Jsyq+vnvUMYRWcdHTo/VsufEMvV6HHpQmfP9mia8gJ8d7pH5RjCLa+kDZp5JC2Dy1fSPbpmk65foEHupLlAnmEtkgJAGNLGHudapjDR4TGVQVNjjhZZXArB+2hRpcbUo8PSs0aLb60PA/lXh1NJRXUAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDQtMjhUMTU6NTA6NDktMDQ6MDAjy94hAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA0LTI4VDE1OjUwOjQ5LTA0OjAwUpZmnQAAAABJRU5ErkJggg==", "flowers": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAG1BMVEX////NcLzqkuzs4tmoX7w0gDFMyk48mz/g4OAq/H8CAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAAC8SURBVDjL7Y5BDoIwEEUhgbi1WOPaygkIB6CJ7DGxxK2JhS0b7PWdaSWdSr0Bf/OSn87vS5It/8IiXSr2kbKKlbJZl4cyMlqwy+o+Y3L9Uyb47zlLZMXhJ9qDozzzoglcwRE368A1le1N8JIHruD4hE0VuILj6Wr0g7ri3tEM5k5d0XEYx/eLuqJj2xvtXJcSHXM1WdLNmhCjrKOnzc46eroYcOwI3T04doSYHB21p8vcu7OF37dqCrglzAe8CC4xxGRq+gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wNC0yOFQxNTo1MTo0NS0wNDowMAup32sAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDQtMjhUMTU6NTE6NDUtMDQ6MDB69GfXAAAAAElFTkSuQmCC", "lilypad": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAIVBMVEX///8ujzIZdxw2qjJA0DvNEXX/7v/6eJH7p7aOwfMXOSAVXR0aAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAACBSURBVDjL7dFdDcAwCATgWsDCWcDCWcBCLdQCFrAwlWM/WZZs7Spg9/glNFco5Y8AcyhKAyDyhaJm1YxU+UBYbd6qETftYEtsJuQYc9rd3HOeGCHIc1wn8KikihjhVmT/5rJc1kHNB3J1ERd1MHLF2eUmPUzNu8UMloh42Dv+GWcFJFBTJX9JqnQAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDQtMjhUMTU6NDk6NTQtMDQ6MDCzLUt1AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA0LTI4VDE1OjQ5OjU0LTA0OjAwwnDzyQAAAABJRU5ErkJggg==", "rock": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAGFBMVEX///9NPySOZUaFXT84MRhcRit0TTEkHg7Ebd7FAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAACCSURBVDjL7dDBDQQhCIVhWqAFW6CF1wIt0ALtL+KazEyEzHEP+0cvn1ETiP69j6NXyCN6+gGZZawufEKIbAxu8GZDOzSzTWaokWFimwK5xPzIvmQKlOiJMYw0hjdoKhpxpjUSWHVh7gZT11KFU4MEzKvIJ3pM1nnkFzvjZPiDCvzxPo/jV8uI6/BgAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTA0LTI4VDE1OjUwOjIyLTA0OjAw56ODXAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wNC0yOFQxNTo1MDoyMi0wNDowMJb+O+AAAAAASUVORK5CYII=", "rock2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWEzE4Mcz/sgAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAADUUlEQVRo3u1YPW/aUBQ9zyqKMYQII4UQKQOCSJYyVSkMeMwQ5TdEzS/IyNY5G2N+Qav+BtShIxmgUadIlgLqECnUkTDCLcYREq+Dex82JSofTrL4SAxgeO+8c++59z6ACBEiRIgQIcJrgoW10MXRIX/q2Yev1+xVCV4cHfJyLhX4rG2PUUzFAADNrr0ySWldclW9xNVEHACwv6tgf1cBABRTMbTtMfZ3FZwe7qCql/gq678JQ8FiKoZm10aza0NNxHGibQEAPt6YsIajtbRgYeWeP8TNrg0AMCxveU3lMCyGrMJQa7TYiylY1Uu8nEvOfdZ3JWjqBES+nAO+/Pj9cjlY1Uv8OD+fnGExpGWPHOXnqpBWJZeWJ8IQZBByL5Hsu2t7cHmCs8qRc+vGAM2uDWs4QmVPhqZymA5Hp/coSNPvny0H3799x4/zSbFh3RjAGo5QzqV8bsU0tJYL0+FIyyM0hyOkZSytqrQMOU3loqwQqb4rBRQiAkQ4q3ihNiwGNRFHIbOxlIpruXieGqbDYTousoqErAJhFtNhuLpz4R1Ser4cpO4AAE+5uLIn4zifRCGzIQ5B6hsWW0rFlWxWNwaC5Lm+jU7vUSh6drApenAxFUMhs/EP+dBzsKqXeFaZNgBrOAqQJHLzFCWyfVeCfw3T4eERnFWh70qwhiPc3juB7/gJUwlq22MR5tl1QjFJVS9xUsEL5ShwrsvGg9i4bgzEoEDvAc8ofkNd3bnh18FiKoZiKvZkP6VD1I2BGLX8SMsTdHqPSMsTaOp0kAjdJOf6tsifZtdGWp4Icv45cJb4bN5pKl/Iyf8lWGu0GG3ozzF/r53NPb+L59VONRFfeIhYKMRURtr2IFAuqFt8vv6J08MdABDGIXJ0OO8wXn8uZEIuM7VGi3V6j6DX2cEmTrQtqIm4UJGI+cf+tj0W7ZA6ir8NhpqDtUaL1Rot5i8VJ9oWjvNJGBYLmOf23hHkAsODLz0WJblyL769d6Zq3fyC6XBcNh4CSs3W0azCYDr87wCx2Pi/NEHKx9mcfAqGxVBJUFglfPr+bak7yUqXJn95oNJR2ZNxdeciq0zHfbooUaFe9sIU2sW9qpc4hZEUplBS/kZ/4kSIECFChAgRngV/ALGTj20avZzJAAAAAElFTkSuQmCC", "sprout": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAGFBMVEX///8WYSAujzI2qjIVGRQqKR47OSZbRidbji8bAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAABrSURBVDjLY2AYBSMICAqg8QWBWFkRRYxR2UiQwcnYEFXQ2NhIRFlZBUWQRdlYSUTJCFWQwcnIyEVJyQFVECji6uKC7hwXF1cHBgzAEopNMAWLd1xKsCgMT3XAIpiGxUxXLCoZWFwYRgFJAAAj+QyB1iHUVgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wNC0yOFQxNTo1NDo0OS0wNDowMCogflsAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDQtMjhUMTU6NTQ6NDktMDQ6MDBbfcbnAAAAAElFTkSuQmCC", "tree1": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAHlBMVEX///8cYCU+okEiZDkmQDkODg5XQzpYU0ElIRmcdDp0EbXCAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAACCSURBVDjL7c6xDYNADAVQ06XkODIAZoK7jxgAhoE2VaCjAyYgbAuiM/YCkfjlk79toif/lCQ1LAtac3BxI5f6hlmOOg4ouZSYMfuIVqA7p3IgyHZEfHf9JO94oP7ejHxV1T/1eYtOIbl1mxTSy8TdwHn/WDgYK8dR95dt0XWy7MmVA4A/Ftk/XnN+AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTA0LTI4VDE1OjUyOjI1LTA0OjAwJvFt7wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wNC0yOFQxNTo1MjoyNS0wNDowMFes1VMAAAAASUVORK5CYII=", "tree2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAHlBMVEX///8cYCU+okEiZDkmQDkODg5XQzpYU0ElIRmcdDp0EbXCAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAACCSURBVDjL7c6xDYNADAVQ06XkODIAZoK7jxgAhoE2VaCjAyYgbAuiM/YCkfjlk79toif/lCQ1LAtac3BxI5f6hlmOOg4ouZSYMfuIVqA7p3IgyHZEfHf9JO94oP7ejHxV1T/1eYtOIbl1mxTSy8TdwHn/WDgYK8dR95dt0XWy7MmVA4A/Ftk/XnN+AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTA0LTI4VDE1OjUyOjI1LTA0OjAwJvFt7wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wNC0yOFQxNTo1MjoyNS0wNDowMFes1VMAAAAASUVORK5CYII=", "bigtree": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0BAMAAAAKxGe+AAAAJFBMVEX///8Vcis/tUE3nUEcYCVJ1EsXhyUlIRkzMiVXRzpwVjOBZjo8WCCxAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAAHpSURBVDjLxZI9jtswEIUlQIC3tH6i3sNQvTiyaitiDKQMnTmAJIN1Oq9TbbfYpA3glXOAxNoLJN7LhaIt2HIfhA2BD4/vvSHpOP9/+f7U7q7vX2AAaU/dEGbXEDLHd0q4hq7gqwKjuWLTC/SI+BLXimhxyZFAlGJIBHJI8ht8T5RgTfQJ8zMtMa9IMS04xQ1mZ8dSVwxAA4hYZ4OrV8Q1AAIkVTQwNyCV1wYmDafZuVRAJjYRyGpTgk71C95DYCighyrqDYErUmA9gRNXYGw9KZRSwIxSAOecSZslZbBiGgG1mM+kPMcvKz8WNSTM7Ouhpta+SIyyZr7W56JmoLlKTEzNV2akkxB1qMiMBAnxVKOVuk1u6jcCWG6qN7kdqcDU3M9JqViIkc3G1PQLFfDU9A3R5se4hvnsjZnwY7CCJVa9ZY4ZyDjgpGaVhBIbY+pinkmtrVJrWTZooXS8XFcGxrpZeNLCqM/SayBY6j6lmA5Ph5HpWeDwbBbm2f07ePtYNtcwW2xLyHZeeQXNvX75Dh9+mhu//qGTr53sfn0ef9u79uX12D6M4bZtu7bdjdhkf2jbdn8YnZ8cukO77/Zj+PzSvXbHp3HS/cuf7sfx8SZ9/3vz7XCTPnnaOdvnm57O5sG52zj/bv0FQbCJneyhKQwAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDQtMjhUMTU6NDg6MzctMDQ6MDCraDNRAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA0LTI4VDE1OjQ4OjM3LTA0OjAw2jWL7QAAAABJRU5ErkJggg==", "bigtree2": "data:image/png;base64,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", "cliff1": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWEzYMXzmdwAAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAABzUlEQVRo3u2Wy0rDQBSG/4i92TaL4gWF7ooIIigVF936HPo27nwKfRNBF1pwVRApuBAqXqg0bWybCnGRHu05ZEyKCmY63+7MTIbw8Z+ZsRCTg51dHwBWFiwAwLqdAQDcOkNWE3Y6xer6Sw8AsJjn+1aXCwCA7a0lNu667wCAOfxz5uMulObi4ngjZq7x7AMADjeLbN3TXQcAkLeDX3IdXQxS9oqpdOi8yiiZo4w+vgXmauUsW7dWCPY9a7kAgEovxcaTb5CyF4Wqm8kcUbF5d3+as7k5Qp8uVpmimjJKtURmr+mMQrMo0c/gV6a4OVXmVNlTZY5o9TzNDDY7PFuqczEqe9OSfIPH51fW5I2iIpP2fvXHThpdTbtY0h1544z6odmT3avqVnknJyaD1rQfRGWRkO89ujlURi8fHFbftC1NDUaZ3CgFw3urdvh5OjY56Ad1NsfnL+4Hmmcwyih1b9vtf2uSMlfK50IN0jtU/3NQhTSjMkemqZYv+Nk1KLtWmpKmaV46MwapK2vlXKzMvg6GM3oOEkf7VX/yLpU3jKq7KYP0XWLOQeuvNo569ZBRiTQ/uwanNSnNGYM/fZGfXtetRBg0GAwGg8FgSDYfFPCpE/iVmpIAAAAASUVORK5CYII=", "cliff2": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWEzotv+XCkgAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAACa0lEQVRo3u2Xz0obURTGv5FEjRNTMKAYWiGo0K1Quuii2z6BS9+kT9M+ROm2Cxel0G4KAf8EtUQUEtsko8ZMO13cOdJzvCd3prrwhvk2Se7N3Jn7y/edcxO0324nAHBy9BMAsPK0CgCYKwf4V1E/AQCUKmwYZz+GAIC15iKyaDQ268RXfDys8fsdtwcAgBk8cpWCmd8AgHpof1Yip4muIzKSvBTNx1eJlZzU4ycYnUdWT9XrVf7FyuSF4otLc330h41rnnat5w/B2xQth+ZNSrTbtZN0SUsz/VLLzSfmfo51yNv+EMT1iO8wilk9uiW0OpumO2bkw7A08Ubk9PP2rzS9JaVqxJ56cK9jUoj0dbOxYOpbwne014nZPJH/dnjBxxV1hjcAgP2OYbpRK3ueYtrRx/aQTXw+7QMAXq7W2HijOmv1Co1/aBmPvW6EdnL9MZuncZJ8Dn88eHaZWH992tGbZrZ6SJ76lHqsF5ljy1JYsXqOyBPJ9focAOCgO/KE4LvvA+sEEV1ZCHItSETIaxo5Iia/L+/vUSdRdNebyOVFl8ir/p9mNJEHZZrJO1qnkCKPSbIy5ZRe/wjKtGrekx1G1jGXJ2VaNXL+EMxb51q9IN35eGLPzpty+uxvL85aB189m7d6SCMpPZe1PnpD8MEfcL8/ZtQ2auXc1Kazk2h1kTqBy2NZKcpTlL8naq0+7p5cAwCeLyUsxa7/MlqqSf52Etd5T85r5LLWRc27xEp63t8U/+9JWvbsVm/AyE9dHVSPMjtbL6xbliTIc5RuTRpBIj19BLMSdSkrufdfvwReEixUqFChQoXup79UjRNpVGonFgAAAABJRU5ErkJggg==", "cliff3": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWFAAs6AQ6+AAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAACTklEQVRo3u2YT08TQRiHn61tgW5dA1SMJBwMQkz0pvHgwStXT34Gv4OfwMRPIVcPXj14MYGDSjAhTYhgPJiUQKGEyrbbEq2H4a3MOLstDSSdsr/L7s7M7rbP/N4/rVd+sdQBKJQCALzMbwB8PwvA140qAGt7x5zVfqiOj0pFbbzePtGuv9VbACwGY9r1bqMDwPL6F48EZRhyZf0Z//RUkQv3FJrQWPhwRpF6U/4FwP2bnkYsyOf6eqGQhFZf64eeoFd99bxzduDtSgWAu4FOZLuue2v1ZwTArYJnkOlPO0312u2jVqIXh9+D4YFy21alAcDTWeXJj5XQeoOQXSXSonExIDF64yQ74G4UCzlTQrJy3LZ68MncuOZFISa6nssn5kGZdz6Ks+9/qAqxdKdoXTBbzJ/Li12CN+wkZZ3Mu0/Q/ObnlenFOAnJ3dMKIvnP/SiWk087dQCeLZQu5MFjeRX9rXZytL5e+eyNBsHNmqdVEDMPxqlbs+f0Gl2uKm9P5lSenS4UBvqA7hC8N6VH87utfQAe3w6sN0plEYJmXhQdnlxTxyO90vTqpN0jaGrKn9BIRU01Pj6BtV80d0I8bapfcu4RFGJxavxRCB8Edk9KHu0+pxYNRGx0PCiqhU2NjJAyo7vXDowuQekmpAJId2KSM0n957lLknse/H6gMv78dDIZIWeSTwnGRW/cPOgelkqyvL7mXQ2CvX6T/COFUWPtgF5+uBhy7npQiE4aQRnXnQzapYx+FB9GGes/BpdNzDmCqVKlSpUqVapUbusv7DnqyF57ICsAAAAASUVORK5CYII=", "cliff4": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWFAYfAY78aAAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAAC5klEQVRo3u1ZzW7TQBD+EhKnjZuEqoUExI8iVeXQGz/H3kAceQL6IEi8AC+BxCtwKtwAIQFVLz0BlSqVCjWAqZLGaewkDYftZHe9m63TBvBKnos3O+vE+r75ZsaTzJvHD4YAUL02h3HWP+Jr1wkBAE9fN8aeX6oUlL3tZgAAeLH5KYMJLIuEW27SG4ILDgBgbaUEAFjfaY98y2UVubf73XM9YOIRtJtiURzj7O7inNG/WpOpXrt9b0i+OIKxE8EocrnZ8//Qam3mTKKxOwangVzUqkUedhSPplhM08zUKO4fdPjmfDH2F2z8MleSOMIxUZ18BBdc9oyef8wDeX78DYVBKH1udIbCuqsIIYqqDmXxfCqSqVM8EoTPg72x15YOURgAQN9AD9Et0g4EErVfWoFCN111ddrOSkKIuZdd1dllCKxv/Y71AxxNdl8p74x8hCYhqBOLnQhqkSPfgntqnZVjUEayVOF7hKYYl2mamTrFlFLEVBL0GB2F/PgM/7DOWn3v0Fyfo3Qf9kKFYrqKPvtEohNGHCR1JtZbqs9aRIuhknrsRXB3hwfTjXpJOuT/8Pn6jC0+IVfLckz2O8cSkrr0lKaZv9KwEt3k074CtEMlpehEwteBTKtAt7hnH4KUXjxBJKZhpqnlJ0GYer5o2hGRfLbxIWNvmhFLncm+fmevp+LgMoqm2NdFOxXRZ4q9NM38k+GRzt7vyfWzgcDQ5nNKdfWW7rV6eJQT62yc1FKvlgEATpeli1sVR+kD5WmD2urHmSikIpkaxVSDTdXDb/F5wsERo+yb11aC3yQc+itMpNXUqNqXZsQBpsnuX2UIPPegiKQV9gAAV2ZnlEqim9ucCOdj0hF8FwvB4mJ5tJnJDtiLlMvcvsdT0autJgD9P0xlJy8hqesHRQS3m8HP0x7wfyO4CeBlkh/wEYCBkeLrNy9KtJpaLDHol8syrZO+5QG40+gMQwAZAMOkIfgEwOeT9SXTwT8qwybFzQBBcgAAAABJRU5ErkJggg==", "cliff5": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWFAobqlZ3fQAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAABQ0lEQVRo3u2VsUoDMRjHf5HWQk9dTiiCFDroLtRJF4cOvoCTXfsEvoTQJ3DVd3BwEERBsOCsDoKDULUIV3L07ME5xKCR6yBUidz3W3JJvkD48f9yIAiCIAiCIAiCIAiCIAhCUVEnu1tZ3kbr6FT5cMEZ7w0+7+84BvVAA3D3GHth0nuDJa1TZyEIAwBWPuYH25sZQOf4XInBPIP2IxmbKAYTCq3J2ygBoHtxpcQgULLmKmUjxGbSZrERmm4eDM2B5mIZgL2N9ewvTPpv0Jr7KasLFQDaa80M4PC6pwppUJ21WxlAvTGfXzEyXXt5EzvL0duYr13dj02Wp23y/7yDk7Dd3Ht5ys2gHSH5lUz6bzAMzB0f7oe5BfWlWSdjtapysvfdZD8eFfRfXFueczbSV9O1Okodc9bkJ4kzs3XTyqL3Bt8BPtRljzoKfOgAAAAASUVORK5CYII=", "cliff6": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWFAw6sGXApQAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAABbElEQVRo3u2Uv07DMBDGP5eEQEwHsAQSQpUy9C1YGBgQr8DMI/AwzLwDAwMLb8EQVCEkKkUFFacKCS1DiOvEzh+YInG/JZaTu5y/784AQRAEQRAEQRAEQRAEQRAEQRDE/4Q1vbw+O17Z9gPh/uonYZRa9y9vH1hb7KD3Ct5dnJRUGh/6ap1sbFqDskX+dLYr+7MYAMD3uTUuScuGeF+fxjePL7Fan97cM/Z0db7iYp1wGr7nP9/1G4v7S4EAIKdS5fdc02HOHchIqmJ7b7HDBYeUmanULC6pWKfc6/OHERuFcwhunr3OlZLCMkPh6BiAo/eG5zJlj94v2cK0s1qc4INOBWDLV7HFIZpaov8WS5mpZk3SlVp7LsMknLcmKFTopJ7GwdHO2oGfwSnyJG/5vbkneG6xGnuXqSkDgFEwtF4VReLOtlrwXKbyT7SerU527y0uFahbWqdMcXKbum3oN4HOKBgikktEcmlM9Dd6K3dx6is0qQAAAABJRU5ErkJggg==", "cliff7": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWFAoOx4uTlgAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAAA6UlEQVRo3u3VQQqCQBTG8acYglYQFkH72ge2qTbRqjvULTpNV+kKnaBN0CaCIrBGsARbyFvMMIq7kvl+u5FB5c9zJAIAAAAAAAAAAABTWf/yIvv1ItNdt1GwYrnhwCMiIj/w61XQ+dWDd6u5tpwQKQpqbWeTjIho1HZL9yWf/GN2GxYKSuXCblO6HrRIO3tcjkuaW3AzDivNXBFzZ5DL9b3ynxSff+q5x86np2EF1XLq7B1uL2m9nPZKC5r7J1HLHaNEWl/j/HwTd5HP2uWtvU/g24YUVGevqBjjfSJKpVJOx5P2pY+4HgW/l39DpYQ+UtsAAAAASUVORK5CYII=", "cliff8": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQWFAYLG1QoFQAAACZpVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOV5F9bAAACvUlEQVRo3u1ZPW/TUBQ9Tms7iZuWyiUpSAFVQmxICAIIITY+RlYW+AXMDKz8E/4CE3tZoHQvqhSBECJVK0haF8VJbAb32s9+z8+u1Qbb8p2s549nn/POPfc+K67r4jTx8tYdFwCurejcud3hOPa+t487AADL1vyxxUb8PIMfRwCAGnIei1lvJLQertf9sUsN7/jrKEDy6cYSAGC8oGWaJ/cIloNiEgYbRO2ypnLX99bUVJNP/yaLpXwiYUWRacIGj6AMyWIjSGuv01TOZ/JGGMEqzcyNYqKWBME6QzRG9oQbCzlJayk91b+PS4JgVBTXl/XYB7BoDY7jKyN9Zid68oHl+MemUatEcrYUs34bdQuWRqKbxkS0ivKmNZqSfwgp5V5stVkwBEVfTSi1VI0bkwmCjQ99r3R/9aDtDdQDwRmE7p4Vi2gx04wIuTSpRObZhunhZVlT/lzbO3fQP6y8eD4UR6k9nNipaO2t8b5rtpJfYjxxQ+5Rnr6YkJMJQ+bTaZHTVY8FvW0ESfxEMMVBMIRSM4zcei34jl+Ok5hSRLHXH8baGcX3Ks3MSyRRaolWllqROLb2j7gxKvmjdLIl/kDgwYUpWH0E321/9lf96949N21aETVUorQkKvkJ1c5qODmz/lzMNciuuWhKkbWgIuSoHqS40o1vmlirq9LMWYUi2+WX7ein8WnRvc/vt2OvZXcWii2SNCW8rN0U1YV2vR5KN0m7DNRIVSI5V4qp9GdLfpko6Pqdoc2JqWuqJzsMDuMkWmz5VZpfYZ8A3JUJQlTVsP9OtvY9BB9dVqRpJesa3Mw1grvD8SaAFwAunha5aCv68ZuXNp7cWIndbXCdhaBpsv6kQvA9gO08p5kZgGd5FokCwO40ldsAvmSZgP4CUJm28TMQxk3TCE9Wm/nH3asXUiFIa28HwJv/geA/uNgQbEmWDesAAAAASUVORK5CYII=", "gravel": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VVBg/m8AAABzSURBVGgF7dKxDcAgEARBTIeU41JdjZFoYRKCJV/pNdzzvesfF7958W3ntA7UH0owQRXQvg0mqALat8EEVUD7NpigCmjfBhNUAe3bYIIqoH0bTFAFtG+DCaqA9m0wQRXQvg0mqALat8EEVUD7NpigCmi/Ad3WAwqE3BkaAAAAAElFTkSuQmCC", "treegravel": "*", "rockgravel": "*", "sand": "*" }; var daycareBGs = { "water": "#42cef4" }; this.forceDayCareStep = function(src, level) { level = level ? level : 0; level = parseInt(level, 10); level = Math.min(level, 2); this.dayCareStep(level); daycarebot.sendMessage(src, "You forced a daycare step at level " + level + "!", safchan); return true; }; this.inspectDayCare = function(src) { var p, out = []; for (var s in this.daycarePokemon) { p = this.daycarePokemon[s]; out.push(p.owner + "'s " + poke(p.id) + " -- idnum: " + p.uid + ". Hearts: " + p.hearts + ". Play-hearts: " + p.playhearts + ". Hunger:" + p.hunger + ". Area: " + p.area + ". Pos: " + p.pos + ". CanItem" + p.canItem + ". FindItem" + p.findItem + ". Activity: " + p.activity + ". Ownernum: " + p.ownernum + "."); } for (var t in out) { safaribot.sendMessage(src, out[t], safchan); } }; this.inspectDayCareFeatures = function(src) { var p, out = []; for (var s in this.daycareRegions.grotto) { p = this.daycareRegions.grotto[s]; out.push(s + ": " + p + "."); } for (var t in out) { safaribot.sendMessage(src, out[t], safchan); } }; this.manualChangeFeature = function(src, cdata) { var pos, area, feature; if (cdata.length < 3) { daycarebot.sendMessage(src, "The syntax for this command is /daycarefeature [area]:[pos]:[feature]!", safchan); return false; } area = cdata[0]; pos = cdata[1]; feature = cdata[2]; if (!this.daycareRegions.hasOwnProperty(area)) { daycarebot.sendMessage(src, "No such area as " + area + "!", safchan); return false; } if (!(daycareTiles.hasOwnProperty(feature)) && !(daycareBGs.hasOwnProperty(feature))) { daycarebot.sendMessage(src, "No such feature as " + feature + "!", safchan); return false; } this.daycareRegions[area][pos] = feature; return true; }; this.getFeatureAt = function(pos, area) { if (!(["grotto", "beach", "jungle", "mountain"].contains(area))) { return ""; } return (this.daycareRegions[area][pos] || ""); }; this.getPokemonAt = function(pos, area) { for (var t in this.daycarePokemon) { if (this.daycarePokemon[t].pos === pos && this.daycarePokemon[t].area === area) { return this.daycarePokemon[t]; } } return undefined; }; this.printDayCare = function(src, area) { var p, mon; var player = getAvatar(src); var rows = {}, features = {}, name; area = area || "grotto"; var props = [ ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12"], ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12"], ["c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12"], ["d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12"], ["e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "e10", "e11", "e12"], ["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12"], ["g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "g9", "g10", "g11", "g12"], ["h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "h9", "h10", "h11", "h12"], ["i1", "i2", "i3", "i4", "i5", "i6", "i7", "i8", "i9", "i10", "i11", "i12"], ["j1", "j2", "j3", "j4", "j5", "j6", "j7", "j8", "j9", "j10", "j11", "j12"], ["k1", "k2", "k3", "k4", "k5", "k6", "k7", "k8", "k9", "k10", "k11", "k12"], ["l1", "l2", "l3", "l4", "l5", "l6", "l7", "l8", "l9", "l10", "l11", "l12"] ]; var grassbg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VVBg/m8AAABzSURBVGgF7dKxDcAgEARBTJGU5irdg5FoYRKCJV/pNdyzvvcfF7958W3ntA7UH0owQRXQvg0mqALat8EEVUD7NpigCmjfBhNUAe3bYIIqoH0bTFAFtG+DCaqA9m0wQRXQvg0mqALat8EEVUD7NpigCmi/AaJRAz8cuAfhAAAAAElFTkSuQmCC"; var waterbg1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VVBg/m8AAABySURBVGgF7dKxDcAgEARBTGn0L1fj3Ei0MAnBkq/0Gu5Z7/ePi9+8+LZzWgfqDyWYoApo3wYTVAHt22CCKqB9G0xQBbRvgwmqgPZtMEEV0L4NJqgC2rfBBFVA+zaYoApo3wYTVAHt22CCKqB9G0xQBbTfQNEDa9UsWhQAAAAASUVORK5CYII="; var waterbg2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VVBg/m8AAABzSURBVGgF7dKxDcAgEARBTCv05u5dg5FoYRKCJV/pNdyz3u8fF7958W3ntA7UH0owQRXQvg0mqALat8EEVUD7NpigCmjfBhNUAe3bYIIqoH0bTFAFtG+DCaqA9m0wQRXQvg0mqALat8EEVUD7NpigCmi/ARzvAt0JaswhAAAAAElFTkSuQmCC"; var sandbg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA0CAYAAAD46nqNAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VVBg/m8AAABxSURBVGgF7dKxDcAgEARBTKHU7WpsJFqYhGDJV3oN93zv+sfFb1582zmtA/WHEkxQBbRvgwmqgPZtMEEV0L4NJqgC2rfBBFVA+zaYoApo3wYTVAHt22CCKqB9G0xQBbRvgwmqgPZtMEEV0L4NJqgC2m9j3QObjtDy9gAAAABJRU5ErkJggg=="; var gravelbg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia219thzg25abkRE/BpDc3pqvphHvRFys2weqvp+krbWKIX7nhDbzLOItiM8358pTwdirqpPFnMF2xLc1WvLyOwTAibpbmvHHcvttU57y5+XqNZrLe3lE/Pq8eUj2fXKfOe3pfOjzhJYtB/yll5SDFcSDiH+hRkH25+L+sdxKEAMZahrlSX8ukqMOWy/jXW2m6M9LDBc31B9LFuv6gVKg/0Szi3KAr1kGq1GMjU/aLbnq6/lRxc4XfJ98hTargX++DbMJBSiYMIe9Ck1YAxFkKEAG3xbYaKmDDgYyFK0UGYpfoWYXG+fAPPI6tJnNwb7ClP7IyF+D+bjOtCpkhz6CFrIa/I6sFtNl8auFXGMTP34sNwI/JhkgEtmDz14ySfaRcTIBInmKPE32kxyyE2Tv+thKbEVePDfW/byMM1Kmm0XdObS7oGD/MypMXFPXrCwOtoYjyyn7BV29/MZfsVzpLDdRtuIZnbpXzvlf+ev8MvYr/Gqk4H/kV/G3csdazLuyTMPsbFhzd1UabQbjFvDRmcWJxR3zcfHkVw9GfpbJmeev9F08WW8uDkaslwX6avlWGU6NRKz0g/SHtCy9J30o/ca9zX3Kfc19zn3BXQKRO8ud477hLnAfc1/G9mrzGlrfexZ5GLdn6ZZrrEohI2wVHhZywjbhUWEy8icMCGNCUdiBlq3r+xafL549HQ5jH+an+1y+LlYBifuxAvRN/lVVVOlwlCkdVm9NOL5BE4wkQ2SMlDZU97hX86EilU/lUmkQUztTE6mx1EEPh7OmdqBtAvv8HdWpbrJS6tJj3n0CWdM6busNzRV3S9KTYhqvNiqWmuroiKgYhshMjmhTh9ptWhsF7970j/SbMrsPE1suR5z7DMC+P/Hs+y7ijrQAlhyAgccjbhjPygfeBTjzhNqy28EdkUh8C+DU9+z2v/oyeH791OncxHOs5y2AtTc7nb/f73TWPkD/qwBnjX8BoJ98VVBg/m8AAAANSURBVAgdY7hZGfgfAAZ1AqPM+DYbAAAAAElFTkSuQmCC"; var ret = "", r, place, inp, f, g, h, bg, hold, icon; var ownMons = []; for (var t in props) { f = props[t]; for (var s in f) { h = f[s]; g = this.getFeatureAt(h, area); if (g !== "") { features[h] = g; } } } for (var t in this.daycarePokemon) { p = this.daycarePokemon[t]; mon = parseInt(p.id, 10); if (p.area === area) { if (rows[p.pos] && rows[p.pos].ownernum === player.idnum) { // if there's already a mon on this spot and the mon is your own continue; // skip over it so your own mon doesn't get overwritten } else { rows[p.pos] = {mon: mon, owner: p.owner, ownernum: p.ownernum, id: p.uid}; } } } ret += ""; for (var i = 0; i < props.length; i++) { bg = null; ret += ""; r = props[i]; for (var j = 0; j < r.length; j++) { var place = r[j]; bg = null; if (["grotto", "beach", "jungle", "mountain"].contains(area)) { bg = "#56EC96"; icon = grassbg; } if (features.hasOwnProperty(place)) { hold = this.getNearbyFeatures(place, area); icon = daycareTiles[features[place]]; if (features[place] == "grass" && area == "grotto") { if (countDuplicates(hold, "water") + countDuplicates(hold, "lilypad") > 0) { icon = daycareTiles["grasswater"]; } } if (features[place] == "sandgrass" && area == "beach") { icon = daycareTiles["grasswater"]; } if (features[place] == "sandflowers" && area == "beach") { icon = daycareTiles["flowers"]; } if (features[place] == "sandrock" && area == "beach") { icon = daycareTiles["rock"]; } if (features[place] == "water" || features[place] == "lilypad") { if (countDuplicates(hold, "water") + countDuplicates(hold, "lilypad") > 4) { bg = "#2366ed"; if (features[place] == "water") { icon = waterbg2; } } else { bg = "#42cef4"; if (features[place] == "water") { icon = waterbg1; } } } if (features[place] == "gravel") { icon = gravelbg; bg = "#D97951"; } if (features[place] == "sand") { icon = gravelbg; //fix this later maybe } if (features[place] == "treegravel") { icon = daycareTiles["bigtree2"]; bg = "#D97951"; } if (features[place] == "rockgravel") { icon = daycareTiles["rock2"]; bg = "#D97951"; } if (["sand", "sandflowers", "sandrock", "sandgrass"].contains(features[place])) { bg = "#ffde70"; } if (["gravel", "cliff1", "cliff2", "cliff3", "cliff4", "cliff5", "cliff6", "cliff7", "cliff8"].contains(features[place])) { bg = "#D97951"; } } ret += ""; } ret += ""; } ret += "
"; if (rows.hasOwnProperty(place)) { inp = parseInt(rows[place].mon, 10); if (ultraPokes.hasOwnProperty(inp+"")) { var species = pokeInfo.species(inp), form = pokeInfo.forme(inp); var key = species + (form > 0 ? "-" + form : ""); ret += ""; ret += "" + link("/daycare interact:" + rows[place].id, (isOwnMon ? "Check*" : "Check"), false, bg === "#2366ed" ? "#B0E2FF" : null) + "

"; } else { if (features.hasOwnProperty(place)) { ret += ""; } else { ret += ""; } } ret += "
"; sys.sendHtmlMessage(src, ret, safchan); return; }; this.viewVolleyballLb = function(src, type) { var player, name, id; var playerPoints = []; var info = null; var ca = ""; var score = 0; switch (type) { case "spike": case "spikes": case "serve": case "serves": case "attack": case "spiking": case "spk": info = "spikes"; ca = "Ace"; break; case "set": case "sets": case "toss": info = "sets"; ca = "Setter"; break; case "blk": case "block": case "blocks": info = "blocks"; ca = "Middle Blocker"; break; case "dig": case "digs": case "rec": case "receive": case "libero": info = "digs"; ca = "Libero"; break; case "point": case "points": case "score": case "mvp": info = "mvp"; ca = "MVP"; break; } if (!info) { safaribot.sendMessage(src, "That is not a valid leaderboard!", safchan); return false; } for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = JSON.parse(rawPlayers.hash[e]); name = player.casedName; id = player.id; score = 0; if (!player.volleyballRecords) { continue; } if (info == "mvp") { if (player.volleyballRecords.points && player.volleyballRecords.pointsGiven) { score = (player.volleyballRecords.points - player.volleyballRecords.pointsGiven); } } else { if (player.volleyballRecords[info]) { score = player.volleyballRecords[info]; } } if (score <= 0) { continue; } playerPoints.push({ id: id, points: score }); } } playerPoints.sort(function(a, b) { return b.points - a.points; }); if (playerPoints.length > 6) { playerPoints = playerPoints.slice(0, 5); } safaribot.sendMessage(src, "Top " + (playerPoints.length) + " players in Current Volleyball " + ca + " Leaderboard: ", safchan); var j = 0; for (var j = 0; j < playerPoints.length; j++) { safaribot.sendMessage(src, "#" + (j + 1) + ": " + playerPoints[j].id + " (" + playerPoints[j].points + ")", safchan); } return true; }; safari.volleyballStats = { "1": {"stamina": 30, "serve": 3, "receive": 4, "toss": 5, "spike": 1, "block": 3, "precision": 2,"speed": 2, "skills": ["reach", "overgrow", "simplicity"]}, "4": {"stamina": 30, "serve": 2, "receive": 3, "toss": 2, "spike": 4, "block": 2, "precision": 3,"speed": 3, "skills": ["dig", "simplicity", "guardian"]}, "7": {"stamina": 30, "serve": 3, "receive": 3, "toss": 3, "spike": 2, "block": 0, "precision": 4,"speed": 4, "skills": ["wide", "simplicity", "splash"]}, "25": {"stamina": 25, "serve": 4, "receive": 3, "toss": 2, "spike": 4, "block": 1, "precision": 4,"speed": 5, "skills": ["ace", "quick", "stun"]}, "26": {"stamina": 30, "serve": 5, "receive": 4, "toss": 3, "spike": 2, "block": 4, "precision": 3,"speed": 3, "skills": ["stun", "energize", "dig"]}, "36": {"stamina": 34, "serve": 5, "receive": 5, "toss": 5, "spike": 2, "block": 0, "precision": 3,"speed": 1, "skills": ["banner", "grounded", "defiant"]}, "62": {"stamina": 31, "serve": 4, "receive": 4, "toss": 4, "spike": 2, "block": 2, "precision": 3,"speed": 2, "skills": ["back-attack", "splash", "haze"]}, "65": {"stamina": 26, "serve": 5, "receive": 2, "toss": 3, "spike": 4, "block": 1, "precision": 4,"speed": 4, "skills": ["psyspike", "interference", "autotargeting"]}, "68": {"stamina": 32, "serve": 1, "receive": 1, "toss": 4, "spike": 4, "block": 5, "precision": 1,"speed": 1, "skills": ["ace", "dump", "guts"]}, "76": {"stamina": 35, "serve": 4, "receive": 4, "toss": 3, "spike": 4, "block": 1, "precision": 2,"speed": 1, "skills": ["grounded", "banner", "back-attack"]}, "94": {"stamina": 27, "serve": 4, "receive": 1, "toss": 4, "spike": 3, "block": 0, "precision": 3,"speed": 4, "skills": ["performer", "sneak", "telepathy"]}, "97": {"stamina": 34, "serve": 5, "receive": 4, "toss": 4, "spike": 2, "block": 1, "precision": 3,"speed": 1, "skills": ["telepathy", "interference", "swap"]}, "124": {"stamina": 27, "serve": 3, "receive": 3, "toss": 4, "spike": 3, "block": 3, "precision": 4,"speed": 3, "skills": ["psyspike", "freeze", "haze"]}, "150": {"stamina": 31, "serve": 5, "receive": 1, "toss": 4, "spike": 4, "block": 3, "precision": 4,"speed": 4, "skills": []}, "151": {"stamina": 30, "serve": 4, "receive": 5, "toss": 5, "spike": 0, "block": 0, "precision": 3,"speed": 4, "skills": ["dump", "performer", "energize"]}, "153": {"stamina": 32, "serve": 2, "receive": 2, "toss": 4, "spike": 2, "block": 2, "precision": 5,"speed": 3, "skills": ["reach", "overgrow", "wide"]}, "156": {"stamina": 30, "serve": 3, "receive": 4, "toss": 1, "spike": 3, "block": 0, "precision": 4,"speed": 3, "skills": ["dump", "grounded", "ace"]}, "159": {"stamina": 30, "serve": 5, "receive": 2, "toss": 1, "spike": 4, "block": 0, "precision": 1,"speed": 1, "skills": ["observer", "rollout", "swap"]}, "172": {"stamina": 20, "serve": 5, "receive": 4, "toss": 3, "spike": 5, "block": 0, "precision": 4,"speed": 5, "skills": ["back-attack", "quick", "dagger"]}, "181": {"stamina": 30, "serve": 1, "receive": 3, "toss": 2, "spike": 3, "block": 2, "precision": 5,"speed": 2, "skills": ["energize", "performer", "dig"]}, "190": {"stamina": 30, "serve": 4, "receive": 3, "toss": 4, "spike": 3, "block": 2, "precision": 3,"speed": 5, "skills": ["simplicity", "alignment", "dump"]}, "194": {"stamina": 32, "serve": 4, "receive": 5, "toss": 1, "spike": 3, "block": 0, "precision": 4,"speed": 2, "skills": ["swap", "grounded", "haze"]}, "231": {"stamina": 33, "serve": 3, "receive": 4, "toss": 5, "spike": 1, "block": 1, "precision": 3,"speed": 4, "skills": ["rollout", "grounded", "performer"]}, "237": {"stamina": 34, "serve": 4, "receive": 5, "toss": 3, "spike": 2, "block": 3, "precision": 3,"speed": 3, "skills": ["dagger", "rollout", "back-attack"]}, "248": {"stamina": 32, "serve": 4, "receive": 5, "toss": 2, "spike": 5, "block": 4, "precision": 4,"speed": 1, "skills": ["ace", "slacker", "defiant"]}, "254": {"stamina": 30, "serve": 4, "receive": 2, "toss": 4, "spike": 2, "block": 2, "precision": 4,"speed": 5, "skills": ["quick", "dig", "wide"]}, "257": {"stamina": 30, "serve": 3, "receive": 2, "toss": 3, "spike": 4, "block": 4, "precision": 2,"speed": 4, "skills": ["burn", "back-attack", "dagger"]}, "260": {"stamina": 34, "serve": 2, "receive": 5, "toss": 0, "spike": 3, "block": 4, "precision": 1,"speed": 2, "skills": ["back-attack", "grounded", "dagger"]}, "272": {"stamina": 30, "serve": 4, "receive": 4, "toss": 4, "spike": 3, "block": 2, "precision": 2,"speed": 2, "skills": ["splash", "overgrow", "swap"]}, "282": {"stamina": 29, "serve": 5, "receive": 3, "toss": 5, "spike": 1, "block": 1, "precision": 4,"speed": 2, "skills": ["clairvoyant", "autotargeting", "float"]}, "286": {"stamina": 32, "serve": 2, "receive": 4, "toss": 1, "spike": 4, "block": 2, "precision": 2,"speed": 1, "skills": ["overgrow", "ace", "back-attack"]}, "289": {"stamina": 40, "serve": 5, "receive": 5, "toss": 4, "spike": 4, "block": 4, "precision": 4,"speed": 2, "skills": ["slacker", "banner", "back-attack"]}, "302": {"stamina": 28, "serve": 4, "receive": 5, "toss": 4, "spike": 2, "block": 0, "precision": 4,"speed": 2, "skills": ["lightbane", "sneak", "autotargeting"]}, "303": {"stamina": 30, "serve": 5, "receive": 5, "toss": 3, "spike": 2, "block": 1, "precision": 2,"speed": 2, "skills": ["swap", "dagger", "grounded"]}, "376": {"stamina": 35, "serve": 3, "receive": 5, "toss": 1, "spike": 4, "block": 4, "precision": 1,"speed": 2, "skills": ["back-attack", "interference", "psyspike"]}, "392": {"stamina": 30, "serve": 4, "receive": 4, "toss": 4, "spike": 3, "block": 2, "precision": 2,"speed": 4, "skills": ["burn", "back-attack", "quick"]}, "424": {"stamina": 32, "serve": 5, "receive": 2, "toss": 3, "spike": 4, "block": 4, "precision": 2,"speed": 3, "skills": ["back-attack", "wide", "dump"]}, "428": {"stamina": 28, "serve": 4, "receive": 1, "toss": 1, "spike": 5, "block": 3, "precision": 3,"speed": 4, "skills": ["dig", "banner", "ace"]}, "439": {"stamina": 31, "serve": 3, "receive": 4, "toss": 4, "spike": 2, "block": 1, "precision": 5,"speed": 3, "skills": ["psyspike", "clairvoyant", "dagger"]}, "445": {"stamina": 31, "serve": 1, "receive": 4, "toss": 0, "spike": 4, "block": 5, "precision": 2,"speed": 3, "skills": ["guardian", "back-attack", "guts"]}, "448": {"stamina": 32, "serve": 3, "receive": 3, "toss": 3, "spike": 3, "block": 3, "precision": 3,"speed": 3, "skills": ["simplicity", "clairvoyant", "telepathy"]}, "460": {"stamina": 32, "serve": 3, "receive": 4, "toss": 2, "spike": 4, "block": 4, "precision": 3,"speed": 2, "skills": ["reach", "freeze", "back-attack"]}, "461": {"stamina": 27, "serve": 4, "receive": 1, "toss": 4, "spike": 3, "block": 1, "precision": 5,"speed": 5, "skills": ["dump", "observer", "defiant"]}, "466": {"stamina": 30, "serve": 3, "receive": 4, "toss": 4, "spike": 3, "block": 2, "precision": 3,"speed": 3, "skills": ["back-attack", "stun", "energize"]}, "475": {"stamina": 30, "serve": 1, "receive": 4, "toss": 1, "spike": 4, "block": 1, "precision": 4,"speed": 4, "skills": ["ace", "dagger", "float"]}, "531": {"stamina": 38, "serve": 2, "receive": 3, "toss": 4, "spike": 1, "block": 2, "precision": 3,"speed": 2, "skills": ["banner", "energize", "alignment"]}, "556": {"stamina": 32, "serve": 4, "receive": 3, "toss": 5, "spike": 2, "block": 3, "precision": 3,"speed": 3, "skills": ["overgrow", "guardian", "performer"]}, "579": {"stamina": 34, "serve": 2, "receive": 4, "toss": 4, "spike": 2, "block": 5, "precision": 4,"speed": 1, "skills": ["psyspike", "interference", "energize"]}, "620": {"stamina": 30, "serve": 4, "receive": 3, "toss": 2, "spike": 4, "block": 2, "precision": 3,"speed": 3, "skills": ["back-attack", "observer", "guts"]}, "652": {"stamina": 33, "serve": 2, "receive": 5, "toss": 2, "spike": 3, "block": 4, "precision": 2,"speed": 3, "skills": ["grounded", "overgrow", "guts"]}, "655": {"stamina": 32, "serve": 5, "receive": 2, "toss": 5, "spike": 2, "block": 1, "precision": 3,"speed": 2, "skills": ["performer", "dump", "burn"]}, "658": {"stamina": 30, "serve": 3, "receive": 2, "toss": 4, "spike": 2, "block": 2, "precision": 5,"speed": 5, "skills": ["wide", "sneak", "lightbane"]}, "675": {"stamina": 33, "serve": 4, "receive": 4, "toss": 2, "spike": 3, "block": 3, "precision": 2,"speed": 1, "skills": ["back-attack", "ace", "lightbane"]}, "724": {"stamina": 30, "serve": 3, "receive": 3, "toss": 3, "spike": 2, "block": 5, "precision": 3,"speed": 2, "skills": ["reach", "overgrow", "float"]}, "727": {"stamina": 33, "serve": 4, "receive": 5, "toss": 1, "spike": 4, "block": 2, "precision": 1,"speed": 1, "skills": ["burn", "grounded", "dagger"]}, "730": {"stamina": 31, "serve": 5, "receive": 4, "toss": 5, "spike": 1, "block": 0, "precision": 4,"speed": 2, "skills": ["splash", "performer", "haze"]}, "766": {"stamina": 30, "serve": 2, "receive": 4, "toss": 4, "spike": 3, "block": 1, "precision": 3,"speed": 3, "skills": ["wide", "guts", "dump"]} }; safari.volleyballSkillDescriptions = { "ace": "Ace: If this Pokémon is the only Pokémon on its team with 'Ace' skill, +2 Spike power.", "alignment": "Alignment: Toss +1 for each Normal or Fairy type teammate in the same row or column when setting.", "autotargeting": "Auto Targeting: When setting to a teammate, that teammate's Precision is increased to 4 (does not lower it if their Precision exceeds 4).", "back-attack": "Back Attack: May perform a Spike from the back row if they are in the C row.", "burn": "Burn: Receivers of this Pokémon's Spikes and Serves suffer -5 stamina unless they are Water-type.", "dagger": "Dagger: Spike +2 if the rally has continued for 5 turns or longer.", "clairvoyant": "Clairvoyant: Allies in the same zone as this Pokémon gain +2 Precision while spiking, serving, and tipping. While this Pokémon is in the front row, nullifies foe's 'Sneak' skills.", "dig": "Dig: This Pokémon can has a chance to receive spikes aimed in front of it with good success but high stamina cost.", "dump": "Dump: Pokémon may perform a Tip on a Pass or Set that is GREAT or higher quality.", "energize": "Energize: When this Pokémon sets to a Spiker, that Spiker restores +2 Stamina.", "banner": "Banner: Pokémon in the same column or row as this Pokémon's Receive +1.", "grounded": "Grounded: Pokémon's Receive strength increases by +2 if it does not move after the spike or serve. +1 Receive if spiked by a Pokémon with 'Stun' ability and negates Stun.", "guardian": "Guardian: Pokémon's Receive strength increases by +2 on Free Pass and Tips.", "haze": "Haze: While this Pokémon is in the front row, the opposing team's serve power is reduced by -2. Has no effect on Water or Ghost types.", "overgrow": "Overgrow: While this is in the back row, opposing team's Water type Pokémon's Spike -1. This Pokémon spends less stamina receiving unless the Spiker has 'Burn'.", "slacker": "Slacker: This Pokémon's Spikes, Receives, Serves, and Sets are reduced in effectiveness for the first 5 volleys.", "sneak": "[ACT] Sneak: Costs 3 Stamina. Becomes invisible to opponents until the next time it touches the ball.", "splash": "Splash: When this Pokémon Sets, all non-Water type Pokémon in both front rows suffer -3 Stamina (Fire types suffer -5).", "telepathy": "Telepathy: While this Pokémon is in the back row, Fighting-type teammate's Spike +1.", "defiant": "Defiant: While this Pokémon is in the back row, Psychic-type teammate's Spike +1.", "observer": "Observer: When another teammate successfully receives the ball, this Pokémon has a chance to activate the following skill: This Pokémon's receive is increased to 7 until the next time it receives the ball.", "float": "[ACT] Float: Costs 2 Stamina. Serve loses precision, gains power, and becomes more challenging to receive overhand or by receivers that move.", "swap": "[ACT] Swap: Costs 3 Stamina. Moves the Pokémon up in the rotation, trading places with the Pokémon ahead of it.", "guts": "Guts: While this Pokémon is in the back row, Dark-type teammate's Spike +1.", "wide": "Wide: Pokémon's Spike score increased by +2 while spiking from the edges of the court.", "interference": "Interference: When in the front row, -1 Proficiency to the other team's Set (stacks). Has no effect on Psychic or Dark type setters.", "lightbane": "Lightbane: Spike and Serve power increased by +1 for each Psychic type on the opposing team.", "performer": "Performer: This Pokémon's Set +2 when setting to a Pokémon whose base Spike is 2 or less.", "psyspike": "[ACT] Psyspike: Costs 5 Stamina. This Pokémon's Spike +1 and Precision +2, bypassing Blockers when activated before spiking.", "reach": "Reach: This Pokémon's Block +2 while blocking non-Fire types.", "rollout": "Rollout: When successfully receiving a ball with FORCEFUL or higher velocity, proficiency is increased.", "simplicity": "Simplicity: Receive, Toss, Spike, Precision, and Block +1 while Stamina is 25 or higher.", "quick": "[ACT] Quick: Costs 2 Stamina. If activated, this Pokémon spikes immediately when set to (the usual attack phase is skipped).", "stun": "Stun: Receiver's of this Pokémon's Serves and Spikes have reduced Receive for the rest of the rally. Does not affect Ground types or Pokémon with 'Grounded'.", "freeze": "Freeze: Receiver's of this Pokémon's Serves and Spikes are frozen to the ground and cannot move for the rest of the rally. Does not affect Fire or Ice types." }; function getVolleyballStat(pkmn, stat) { var mon = pokeInfo.species(getInputPokemon(poke(pkmn)).num); if (safari.volleyballStats.hasOwnProperty(mon+"")) { return safari.volleyballStats[mon][stat]; } return 0; }; function volleyballScoreIcon(val) { var text = "#fefefe"; var colors = { fail: { bg: "#DC143C" }, ace: { bg: "#DC143C" }, meteoric: { bg: "#DC143C" }, earthshattering: { bg: "#DC143C" }, fierce: { bg: "#DC143C" }, forceful: { bg: "#DC143C" }, powerful: { bg: "#ff22b5" }, strong: { bg: "#ff22b5" }, good: { bg: "#ff22b5" }, decent: { bg: "#8521ff" }, weak: { bg: "#6816e2" }, free: { bg: "#973dce" }, perfect: { bg: "#0a9975" }, excellent: { bg: "#0a9975" }, great: { bg: "#098f9b" }, alright: { bg: "#098f9b" }, okay: { bg: "#098f9b" }, soft: { bg: "#098f9b" }, fair: { bg: "#075196" }, tight: { bg: "#500bc6" }, off: { bg: "#500bc6" } }; var bg; if (colors[val.toLowerCase()]) { bg = colors[val.toLowerCase()].bg; } else { bg = "black"; } return " [" + val.toUpperCase() + "] "; } function showVolleyballInfo(src, pkmn) { var mon = pokeInfo.species(getInputPokemon(pkmn).num), out = ""; if (!safari.volleyballStats.hasOwnProperty(mon+"")) { safaribot.sendMessage(src, "That Pokémon is not allowed in Volleyball!"); return; } out += ("" + poke(mon) + " " + "Stamina: " + getVolleyballStat(mon, "stamina") + " || Serve: " + getVolleyballStat(mon, "serve") + " || Receive: " + getVolleyballStat(mon, "receive") + " || Toss: " + getVolleyballStat(mon, "toss") + " || Spike: " + getVolleyballStat(mon, "spike") + " || Precision: " + getVolleyballStat(mon, "precision") + " || Block: " + getVolleyballStat(mon, "block") + " || Speed: " + getVolleyballStat(mon, "speed")); sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, out, safchan); sys.sendMessage(src, "", safchan); var k; for (var s in getVolleyballStat(mon, "skills")) { k = getVolleyballStat(mon, "skills")[s]; safaribot.sendMessage(src, "Skill: " + safari.volleyballSkillDescriptions[k], safchan); } return; }; function showVolleyballLegal(src) { var out = "Pokémon allowed in Volleyball: "; for (var p in safari.volleyballStats) { out += link("/vb " + poke(parseInt(p, 10)), poke(parseInt(p, 10))) + " "; } safaribot.sendHtmlMessage(src, out, safchan); return; }; function tutorialCourtIcon(mon, owner) { return ""; } function tutorialCourtView(teams, goodInputs, nextLink, retryLink) { var atkteam = team, defteam = (team === 0 ? 1 : 0), p, mon; var rows = {}, name; var props = [ ["xa1", "xa2", "xa3", "xa4", "xa5", "xa6", "xa7"], ["xb1", "xb2", "xb3", "xb4", "xb5", "xb6", "xb7"], ["xc1", "xc2", "xc3", "xc4", "xc5", "xc6", "xc7"], ["xd1", "xd2", "xd3", "xd4", "xd5", "xd6", "xd7"], ["---", "---", "---", "---", "---", "---", "---"], ["d1", "d2", "d3", "d4", "d5", "d6", "d7"], ["c1", "c2", "c3", "c4", "c5", "c6", "c7"], ["b1", "b2", "b3", "b4", "b5", "b6", "b7"], ["a1", "a2", "a3", "a4", "a5", "a6", "a7"] ]; var ret = "", r, place, inp, identify; for (var t in teams) { p = teams[t]; mon = p.mon; rows[t] = {"mon": mon, "owner": p.id}; } ret += ""; for (var i = 0; i < props.length; i++) { ret += ""; r = props[i]; for (var j = 0; j < r.length; j++) { var place = r[j]; ret += ""; } ret += ""; } ret += "
"; if (rows.hasOwnProperty(place)) { inp = parseInt(rows[place].mon, 10); ret += tutorialCourtIcon(inp, rows[place].owner); } else { if (goodInputs.indexOf(place) !== -1) { ret += " " + link("/vbhints " + nextLink, place) + " "; } else { ret += " " + link("/vbhints " + retryLink, place) + " "; } } ret += "
"; return ret; }; function showVolleyballHints(src, query) { if (!query) { query = ""; } var name = sys.name(src); switch (query.toLowerCase()) { case "tutorial": safaribot.sendHtmlMessage(src, "*** Volleyball Tutorial ", safchan); safaribot.sendHtmlMessage(src, "- This is a comprehensive interactive tutorial that will help you learn how to play the Volleyball minigame.", safchan); safaribot.sendHtmlMessage(src, "- To get started, press " + link("/vbhints tutorial_serving", "Serving") + ".", safchan); break; case "tutorial_serving_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Make sure you serve the ball to somewhere on the other side of the net (besides the 'd' row)!", safchan); case "tutorial_serving": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Serving ", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"b2": {mon: 1, id: name}}, ["xa1", "xa2", "xa3", "xa4", "xa5", "xa6", "xa7", "xb1", "xb2", "xb3", "xb4", "xb5", "xb6", "xb7", "xc1", "xc2", "xc3", "xc4", "xc5", "xc6", "xc7"], "tutorial_serving2", "tutorial_serving_fail"), safchan); safaribot.sendHtmlMessage(src, "- Time to practice your serve! To serve the ball, all you have to do is click on one of the links starting with 'x' on the other side of the net.", safchan); safaribot.sendHtmlMessage(src, "- You cannot serve to the 'd' row, however. You can click anywhere in the back 3 rows (a-c). For example, click xb3 or xe2.", safchan); break; case "tutorial_serving2_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Did you do a Strong serve?", safchan); case "tutorial_serving2": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Serving ", safchan); safaribot.sendHtmlMessage(src, "- Good job! You served the ball over the net!", safchan); safaribot.sendHtmlMessage(src, "- The next thing you need to know about serving is that you can vary how much effort you put into it.", safchan); safaribot.sendHtmlMessage(src, "- You can choose to serve the ball EASY, NORMAL, or STRONG. EASY hits are more likely to land where you aim, while STRONG hits are more volatile, but have greater power.", safchan); safaribot.sendHtmlMessage(src, "- For now, let's practice a STRONG Serve. Click the link below!", safchan); safaribot.sendHtmlMessage(src, "Choose " + link("/vbhints tutorial_serving2_fail", "Easy") + " " + link("/vbhints tutorial_serving2_fail", "Normal") + " " + link("/vbhints tutorial_serving3", "Strong") + " for your serve strength!", safchan); break; case "tutorial_serving3_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- When doing a STRONG serve, it's better to aim for the middle of the court ('xb4' for example).", safchan); case "tutorial_serving3": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Serving ", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"b2": {mon: 1, id: name}}, ["xb2", "xb3", "xb4", "xb5", "xb6"], "tutorial_serving_done", "tutorial_serving3_fail"), safchan); safaribot.sendHtmlMessage(src, "- Your serve will be STRONG, so that means it will be stronger, but you will have less control over it.", safchan); safaribot.sendHtmlMessage(src, "- Hint: Aim for the middle of the court to avoid hitting the ball out of bounds!", safchan); break; case "tutorial_serving_done": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Serving ", safchan); safaribot.sendHtmlMessage(src, "- Good job! Now you've learned how to hit the ball over the net whils Serving.", safchan); safaribot.sendHtmlMessage(src, "- You've also learned how to use the EASY, NORMAL, and STRONG serves to your advantage!", safchan); safaribot.sendHtmlMessage(src, "- Next, let's work on Receiving. " + link("/vbhints tutorial_receiving", "Click here when you're ready.") + ".", safchan); break; case "tutorial_receiving": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Receiving ", safchan); safaribot.sendHtmlMessage(src, "- Receiving is straightforward: when the other team hits the ball, it will go to a location on your side of the net.", safchan); safaribot.sendHtmlMessage(src, "- If you're near where the ball is going, try and move to receive it!", safchan); safaribot.sendHtmlMessage(src, "- When you're ready for a practice hit, " + link("/vbhints tutorial_receiving2", "Click here.") + ".", safchan); break; case "tutorial_receiving2_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Try again! Click on the link of the location where the ball was served to!", safchan); case "tutorial_receiving2": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Receiving ", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"b2": {mon: 1, id: name}, "xb6": {mon: 4, id: "Foe"}}, ["c3"], "tutorial_receiving3", "tutorial_receiving2_fail"), safchan); safaribot.sendHtmlMessage(src, toColor("Foe's Charmander serves the ball! The ball was served to c3!", "blue"), safchan); safaribot.sendHtmlMessage(src, "- Click on the location the ball was hit to in order to receive it!", safchan); break; case "tutorial_receiving3": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Receiving ", safchan); safaribot.sendHtmlMessage(src, "- Good job! You received the ball!", safchan); safaribot.sendHtmlMessage(src, "- Important: You do not always need to be the one to receive the ball. Communicate with your teammates to make sure the best receiver who is closest to the ball can receive it!", safchan); safaribot.sendHtmlMessage(src, "- Next is Setting. " + link("/vbhints tutorial_setting", "Click here when you're ready.") + ".", safchan); break; case "tutorial_setting_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Don't click on the court! Use the setter links below the court to choose who you want to set to!", safchan); case "tutorial_setting": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Setting ", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d4": {mon: 1, id: name}, "d2": {mon: 159, id: "Teammate"}, "d6": {mon: 156, id: "Teammate"}}, [], "", "tutorial_setting_fail"), safchan); safaribot.sendHtmlMessage(src, "Choose a teammate to set to with " + link("/vbhints tutorial_setting2", "/vol set:croconaw") + " " + link("/vbhints tutorial_setting2", "/vol set:quilava") + "!", safchan); safaribot.sendHtmlMessage(src, "- Setting comes after receiving! If you want to set the ball, you may set to a teammate, and they will be able to spike the ball.", safchan); break; case "tutorial_setting2_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Set to Pikachu, not Mew. If you need help figuring out which one has better Spike power, you can check their stats with /vb pikachu and /vb mew.", safchan); case "tutorial_setting2": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Setting ", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d4": {mon: 1, id: name}, "d2": {mon: 25, id: "Teammate"}, "d6": {mon: 151, id: "Teammate"}}, [], "", "tutorial_setting2_fail"), safchan); safaribot.sendHtmlMessage(src, "Choose a teammate to set to with " + link("/vbhints tutorial_setting3", "/vol set:pikachu") + " " + link("/vbhints tutorial_setting2_fail", "/vol set:mew") + "!", safchan); safaribot.sendHtmlMessage(src, "- In some cases, you have to choose which teammate is better to set to based on the circumstances.", safchan); safaribot.sendHtmlMessage(src, "- In this example, Pikachu's Spike power is much higher than Mew's so you should set to Pikachu and not Mew.", safchan); break; case "tutorial_setting3_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Hint: Set to the teammate who does not have a blocker nearby!", safchan); case "tutorial_setting3": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Setting ", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d4": {mon: 1, id: name}, "d2": {mon: 461, id: "Teammate"}, "d6": {mon: 448, id: "Teammate"}, "xd6": {mon: 257, id: "Foe"}}, [], "", "tutorial_setting3_fail"), safchan); safaribot.sendHtmlMessage(src, "Choose a teammate to set to with " + link("/vbhints tutorial_setting4", "/vol set:weavile") + " " + link("/vbhints tutorial_setting3_fail", "/vol set:lucario") + "!", safchan); safaribot.sendHtmlMessage(src, "- In this example, notice that Lucario is facing a blocker on the opposing team, while Weavile has no blockers nearby.", safchan); safaribot.sendHtmlMessage(src, "- Weavile and Lucario's spike power are similar, so set to the one that will have an easier time spiking the ball.", safchan); break; case "tutorial_setting4": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Setting ", safchan); safaribot.sendHtmlMessage(src, "- Now you understand how to set! It's also important to know when NOT to set. Let your teammates who have a higher Toss score than you set the ball!", safchan); safaribot.sendHtmlMessage(src, "- If you're not sure about the Toss scores, you can check during the game by hovering over them, or by using /vb [pokemon].", safchan); safaribot.sendHtmlMessage(src, "- Net we'll work on Spikes. " + link("/vbhints tutorial_spiking", "Click here when you're ready.") + ".", safchan); break; case "tutorial_spiking_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- For example, you can click 'xb3' to spike the ball to the b3 location. Anywhere besides the front row is fair game.", safchan); case "tutorial_spiking": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Spiking ", safchan); safaribot.sendHtmlMessage(src, toColor("Gardevoir sets the ball to " + name + "'s Bulbasaur!", "blue"), safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d6": {mon: 1, id: name}, "d4": {mon: 282, id: "Teammate"}, "d2": {mon: 475, id: "Teammate"}}, ["xa1", "xa2", "xa3", "xa4", "xa5", "xa6", "xa7", "xb1", "xb2", "xb3", "xb4", "xb5", "xb6", "xb7", "xc1", "xc2", "xc3", "xc4", "xc5", "xc6", "xc7"], "tutorial_spiking2", "tutorial_spiking_fail"), safchan); safaribot.sendHtmlMessage(src, "- If you remember how to serve, spiking is basically the same thing. Click the location on the other side of the net you would like to spike to!", safchan); break; case "tutorial_spiking2_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Try again! Aim for a place that their stronger receiver cannot easily reach!", safchan); case "tutorial_spiking2": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Spiking ", safchan); safaribot.sendHtmlMessage(src, toColor("Gardevoir sets the ball to " + name + "'s Bulbasaur!", "blue"), safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d6": {mon: 1, id: name}, "d4": {mon: 282, id: "Teammate"}, "d2": {mon: 475, id: "Teammate"}, "xb2": {mon: 428, id: "Foe"}, "xb6": {mon: 260, id: "Foe"}}, ["xa1", "xa2", "xa3", "xa4", "xb1", "xb2", "xb3", "xc1", "xc2", "xc3", "xc4"], "tutorial_spiking3", "tutorial_spiking2_fail"), safchan); safaribot.sendHtmlMessage(src, "- Gardevoir sets the ball to " + name + "'s Bulbasaur!", safchan); safaribot.sendHtmlMessage(src, "- When Spiking, try to aim your spike away from the strong receivers on the other team. In this example, avoid spiking around the area with a strong receiver.", safchan); break; case "tutorial_spiking3_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Avoiding blockers can be tricky. Pick another spot that does not put the blocker between you and the spot you target.", safchan); case "tutorial_spiking3": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Spiking ", safchan); safaribot.sendHtmlMessage(src, toColor("Gardevoir sets the ball to " + name + "'s Bulbasaur!", "blue"), safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d5": {mon: 1, id: name}, "d4": {mon: 282, id: "Teammate"}, "xd5": {mon: 257, id: "Foe"}}, ["xc7", "xb7", "xc3", "xb3", "xa2", "xa1", "xb2", "xb1", "xc2", "xc1"], "tutorial_spiking4", "tutorial_spiking3_fail"), safchan); safaribot.sendHtmlMessage(src, "- Nice! You now understand how to cleverly aim your spike!", safchan); safaribot.sendHtmlMessage(src, "- In this example, you are faced with a potential blocker. As long as you aim away from the blocker, they cannot block you.", safchan); break; case "tutorial_spiking4_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "- Keep trying! Look for the spot where there are no receivers nearby and no blockers in your way!", safchan); break; case "tutorial_spiking4": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Spiking ", safchan); safaribot.sendHtmlMessage(src, "- Nicely done! Let's put it all together for a more complicated example:", safchan); safaribot.sendHtmlMessage(src, "- Gardevoir sets the ball to " + name + "'s Bulbasaur!", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d5": {mon: 1, id: name}, "d4": {mon: 282, id: "Teammate"}, "xd5": {mon: 460, id: "Foe"}, "xd6": {mon: 257, id: "Foe"}, "xa1": {mon: 727, id: "Foe"}, "xc1": {mon: 260, id: "Foe"}}, ["xb3"], "tutorial_spiking5", "tutorial_spiking4_fail"), safchan); safaribot.sendHtmlMessage(src, "- This time, there are two potential blockers and two possible receivers.", safchan); safaribot.sendHtmlMessage(src, "- Find the spot that avoids the blockers and does not go directly to the strong receiver!", safchan); break; case "tutorial_spiking5": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Spiking ", safchan); safaribot.sendHtmlMessage(src, "- You're amazing! Now you know how to target the weakest points of the opponent's defense!", safchan); safaribot.sendHtmlMessage(src, "- Next, we will learn how to block. " + link("/vbhints tutorial_blocking", "Click here to learn more") + ".", safchan); break; case "tutorial_blocking_fail": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Click on the --- in front of the spiker (Sceptile) in order to score a block!", safchan); case "tutorial_blocking": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Blocking ", safchan); safaribot.sendHtmlMessage(src, "- The foe's Greninja sets to the foe's Sceptile!", safchan); safaribot.sendHtmlMessage(src, tutorialCourtView({"d2": {mon: 1, id: name}, "d4": {mon: 282, id: "Teammate"}, "xd2": {mon: 254, id: "Foe"}, "xd4": {mon: 658, id: "Foe"}}, ["---"], "tutorial_blocking2", "tutorial_blocking_fail"), safchan); safaribot.sendHtmlMessage(src, "- If you are in the front row, you can block. To do so, click on the net in front of the spiker!", safchan); break; case "tutorial_blocking2": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Blocking ", safchan); safaribot.sendHtmlMessage(src, "- Good job! You blocked the ball!", safchan); safaribot.sendHtmlMessage(src, "- When blocking, you can opt to change the angle of your block. If you want to block straight, click on the net directly in front of the spiker. If you go to the side, your block will be angled to block spikes in that direction.", safchan); safaribot.sendHtmlMessage(src, "- All that's left is to explain how stats work! " + link("/vbhints stats", "Click here to see what the stats do") + "!", safchan); break; case "stats": safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "*** Volleyball Stats ", safchan); safaribot.sendHtmlMessage(src, "- Receive: The higher your receive, the better your ability to receive serves and spikes will be.", safchan); safaribot.sendHtmlMessage(src, "- Toss: The higher your toss, the more effective of a setter you will be.", safchan); safaribot.sendHtmlMessage(src, "- Spike: The higher your spike, the stronger your spikes will be.", safchan); safaribot.sendHtmlMessage(src, "- Serve: The higher your serve, the stronger your serves.", safchan); safaribot.sendHtmlMessage(src, "- Precision: The higher your precision, the more accurately you will be able to land spikes or serves where you want to.", safchan); safaribot.sendHtmlMessage(src, "- Block: The higher your block, the more effective your block will be.", safchan); safaribot.sendHtmlMessage(src, "- Speed: The higher your speed, the faster you will be able to move around the court.", safchan); safaribot.sendHtmlMessage(src, "- Stamina: This is expended when moving around the court, and restores somewhat at the end of each rally. Keep your stamina high or your stats will drop!", safchan); safaribot.sendHtmlMessage(src, "- Skills: Various skills that allow Pokémon to do unique actions on the court.", safchan); safaribot.sendMessage(src, "", safchan); break; case "howto": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Volleyball is a mini-game that requires typing no commands after joining!", safchan); safaribot.sendHtmlMessage(src, "- To understand how to play the game, " + link("/vbhints howto2", "click here to learn more") + ".", safchan); break; case "howto2": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Serving: If it is your turn to serve, simple click the location on the other side of the net you would like to serve to!", safchan); safaribot.sendHtmlMessage(src, "- Serving: For example, you could click 'xc4' to serve the ball.", safchan); safaribot.sendHtmlMessage(src, "- Serving: When serving, you also have the ability to serve with Easy, Normal, or Hard strength. The harder your server, the harder it will be for the other side to receive, but the more stamina it will cost.", safchan); safaribot.sendHtmlMessage(src, "- " + link("/vbhints howto3", "Cick here to learn more") + ".", safchan); break; case "howto3": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Receiving: After a serve or a spike, the other team must receive the ball by having one of their players go to the ball.", safchan); safaribot.sendHtmlMessage(src, "- Receiving: If your current Pokémon has healthy stamina and is within range, click the location the ball was spiked to. If you do, your Pokémon will try to receive it.", safchan); safaribot.sendHtmlMessage(src, "- " + link("/vbhints howto4", "Cick here to learn more") + ".", safchan); break; case "howto4": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Setting: After a successful reception, a Pokémon in the front row may set. Typically, you want the Pokémon with the highest Toss stat on your team to set the ball.", safchan); safaribot.sendHtmlMessage(src, "- Setting: Links such as /vol set:Misty will appear. You may click these links to set the ball.", safchan); safaribot.sendHtmlMessage(src, "- " + link("/vbhints howto5", "Cick here to learn more") + ".", safchan); break; case "howto5": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Spiking: The player who has the ball set to them by the setter can then spike the ball. The higher your Spike stat, the better the spike will be.", safchan); safaribot.sendHtmlMessage(src, "- Spiking: Spiking works like serves. To choose your target, simply click the link on the other team's side of the net, such as 'xc4'.", safchan); safaribot.sendHtmlMessage(src, "- " + link("/vbhints howto6", "Cick here to learn more") + ".", safchan); break; case "howto6": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Blocking: If you are on the defending team, you can try to block the opposing team's spike by clicking on the net in front of the spiker.", safchan); safaribot.sendHtmlMessage(src, "- Blocking: If your block stat is high and the other player spikes the ball in your direction, you may bee able to block their spike and score a point!", safchan); safaribot.sendHtmlMessage(src, "- " + link("/vbhints howto7", "Cick here to learn more") + ".", safchan); break; case "howto7": safaribot.sendHtmlMessage(src, "*** How to play Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- That's all you need to know in order to play this game!", safchan); safaribot.sendHtmlMessage(src, "- For more information, use " + link("/vblegal", "/vblegal") + " to see a list of Pokémon that are allowed alongside their stats.", safchan); break; case "receive": case "receiving": case "pass": case "rec": safaribot.sendHtmlMessage(src, "*** Tips for receiving in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Receiving a spike or serve tests the Receive stat of your active Pokémon.", safchan); safaribot.sendHtmlMessage(src, "- The longer you wait in one spot, the better your reception will be.", safchan); safaribot.sendHtmlMessage(src, "- You can move to receive the ball, but if you move the maximum of your speed stat your reception will be weakened.", safchan); safaribot.sendHtmlMessage(src, "- There are 4 kinds of receptions:", safchan); safaribot.sendHtmlMessage(src, "--- Direct receive: Position yourself where the ball is spiked/served to. The most consistent way to receive.", safchan); safaribot.sendHtmlMessage(src, "--- Side receive: Position yourself where to the left or right of where ball is spiked/served to.", safchan); safaribot.sendHtmlMessage(src, "--- Low receive: Position yourself above the ball and within one column of the ball. If you are in the same column as the ball, this is more effective.", safchan); safaribot.sendHtmlMessage(src, "--- Overhand receive: Position yourself under the ball and in the same column as the ball. If your Toss stat is high, this becomes more effective.", safchan); break; case "toss": case "set": safaribot.sendHtmlMessage(src, "*** Tips for setting in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Setting the ball uses the Toss stat of your active Pokémon.", safchan); safaribot.sendHtmlMessage(src, "- The longer you wait in one spot, the better your set will be.", safchan); safaribot.sendHtmlMessage(src, "- Your set will be improved if the ball was received well.", safchan); safaribot.sendHtmlMessage(src, "- The score of the set can be improved by performing a long set (setting to a player at least 3 columns away).", safchan); safaribot.sendHtmlMessage(src, "- Your set score is increased by +1 if you are in the center of the front row (d4).", safchan); safaribot.sendHtmlMessage(src, "- Even if you start in the back row, you can move to the front row in order to set (but you still cannot spike).", safchan); safaribot.sendHtmlMessage(src, "- Performing a good set boosts the spike power and accuracy of the spiker, and, if the spiker has a high precision stat, allows them to evade blocks.", safchan); break; case "spike": case "hit": case "attack": safaribot.sendHtmlMessage(src, "*** Tips for spiking in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Spiking the ball uses the Spike stat of your active Pokémon.", safchan); safaribot.sendHtmlMessage(src, "- You can spike if you started in the front row and are currently in the front row.", safchan); safaribot.sendHtmlMessage(src, "- The higher your spike stat, the further you can spike the ball without suffering a distance penalty.", safchan); safaribot.sendHtmlMessage(src, "- The higher your precision stat, the more likely you are to spike to the target you chose.", safchan); safaribot.sendHtmlMessage(src, "- If you spike towards a blocker in the front row, you may be blocked!", safchan); break; case "dump": case "tip": safaribot.sendHtmlMessage(src, "*** Tips for tipping in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Tipping the ball bypasses blocks and scores a point if there are no non-blocking opponents nearby.", safchan); safaribot.sendHtmlMessage(src, "- Tipping the ball is more effective if you have a high precision stat.", safchan); safaribot.sendHtmlMessage(src, "- Tips can be more easily dug up by opponent Pokémon that have high speed or high stamina remaining.", safchan); break; case "serve": safaribot.sendHtmlMessage(src, "*** Tips for serving in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Serving the ball uses the serve stat of your active Pokémon.", safchan); safaribot.sendHtmlMessage(src, "- Adjust your serve effort with /vol effort:0 through /vol effort:2. More effort costs more stamina but is more powerful.", safchan); safaribot.sendHtmlMessage(src, "- Lower serve effort are also easier to land in bounds if your precision stat is low.", safchan); safaribot.sendHtmlMessage(src, "- If you have a 'Float' skill, you can activate it while serving to throw off the receivers.", safchan); break; case "block": case "blocking": safaribot.sendHtmlMessage(src, "*** Tips for blocking in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Blocking the ball uses the block stat of your active Pokémon.", safchan); safaribot.sendHtmlMessage(src, "- Aim your block by clicking on the net near your Pokémon.", safchan); safaribot.sendHtmlMessage(src, "- If the angle of the spiker's hit meets the angle of your block, you will have a chance to score a point.", safchan); break; case "stamina": safaribot.sendHtmlMessage(src, "*** Tips for stamina in Volleyball:", safchan); safaribot.sendHtmlMessage(src, "- Stamina is expended when taking action.", safchan); safaribot.sendHtmlMessage(src, "- Receiving hard driven balls can cost extreme stamina.", safchan); safaribot.sendHtmlMessage(src, "- Make sure your dedicated receivers can receive the ball frequently to save the stamina of your offense-oriented players.", safchan); safaribot.sendHtmlMessage(src, "- If your stamina drops below 10, your quality of actions will be reduced.", safchan); safaribot.sendHtmlMessage(src, "- If your stamina drops below 4, your quality of actions will be reduced drastically.", safchan); safaribot.sendHtmlMessage(src, "- If your stamina is low, you may elect to use /vol sub to change in your next Pokémon.", safchan); break; default: safaribot.sendHtmlMessage(src, "If you want to learn how to play the volleyball minigame, " + link("/vbhints tutorial_serving", "click this link to begin the tutorial!"), safchan); safaribot.sendHtmlMessage(src, "For more in-depth specific tips, use these queries: " + link("/vbhints howto", "How To") + ", " + link("/vbhints receive", "receive") + ", " + link("/vbhints toss", "toss") + ", " + link("/vbhints tip", "tip") + ", " + link("/vbhints spike", "spike") + ", " + link("/vbhints serve", "serve") + ", " + link("/vbhints block", "block") + ", " + link("/vbhints stamina", "stamina") + "!", safchan); break; } return; } /* New Ball Mode */ function Volleyball(src, team1, team2, reward1, reward2, silent) { this.team1 = {}, this.team2 = {}; this.viewers = []; this.teams = [this.team1, this.team2]; this.excludeActions = []; this.excludePos = [ [], [] ]; this.teamServed = 0; this.teamHasBall = -1; this.reward1 = reward1; this.reward2 = reward2 ? reward2 : reward1; this.teamData = [ {score: 0, name: team1, signups: [], firstBall: false}, {score: 0, name: team2, signups: [], firstBall: false} ]; this.turnLength = 5; this.step = 0; this.finished = false; this.phase = "signups"; this.cyclePhase = ""; //Skill related variables this.ballBurn = false; this.ballStun = false; this.ballFreeze = false; this.ballFloat = false; this.official = false; this.npcMons1 = []; this.npcMons2 = []; if (officialVolleyballTeam1 && officialVolleyballTeam2 && team1 && team2) { if ((officialVolleyballTeam1 === team1) && (officialVolleyballTeam2 === team2)) { this.official = true; } if ((officialVolleyballTeam1 === team2) && (officialVolleyballTeam2 === team1)) { this.official = true; } } safaribot.sendMessage(src, "You started a Volleyball match!", safchan); if (this.official) { giveStuff(getAvatar(src), toStuffObj("@form"), true); safaribot.sendMessage(src, "Since this is a sponsored match, your Event Form is compensated.", safchan); } if (!silent) { sys.sendAll("", safchan); safaribot.sendHtmlAll("A Volleyball Event has started with teams " + team1 + " and " + team2 + " ready to rumble! Type " + link("/vol join:" + team1) + " or " + link("/vol join:" + team2) + " to join!", safchan); safaribot.sendHtmlAll("The winning team gets " + translateStuff(this.reward1) + "!", safchan); sys.sendAll("", safchan); } }; Volleyball.prototype.handleCommand = function(src, data) { var name = sys.name(src); var cdata = data.split(":"); var cdata1 = cdata[0]; var cdata2 = (cdata.length > 1 ? cdata[1] : ""); if (this.phase == "signups") { var player = getAvatar(src); if (!player) { return; } var party = player.party; var mon, i = 0; for (var p in party) { mon = pokeInfo.species(party[p]); if (Object.keys(safari.volleyballStats).indexOf(mon+"") === -1) { this.sendMessage(name, poke(mon) + " is not valid in Volleyball matches! Make sure your first three Pokémon are legal in this format!", "red"); return; } i++; if (i > 2) { break; } } if (i <= 2) { this.sendMessage(name, "You must have at least 3 Pokémon to enter a Volleyball match!", "red"); return; } if (cdata1 == "unjoin") { if (this.teamData[1].signups.contains(player.id)) { this.teamData[1].signups.splice(this.teamData[1].signups.indexOf(player.id), 1); this.sendMessage(name, "You unjoined the game!", "red"); this.sendMessageAll(name + " unjoined!", "blue"); return; } else if (this.teamData[0].signups.contains(player.id)) { this.teamData[0].signups.splice(this.teamData[0].signups.indexOf(player.id), 1); this.sendMessage(name, "You unjoined the game!", "red"); this.sendMessageAll(name + " unjoined!", "blue"); return; } } if (cdata1 == "npc") { var m = getInputPokemon(cdata2).num; if (Object.keys(safari.volleyballStats).indexOf(mon+"") === -1) { this.sendMessage(name, poke(m) + " cannot play volleyball!", "red"); return false; } if (this.teamData[0].signups.contains(player.id)) { this.npcMons1.push(m); for (var t in this.teamData[0].signups) { this.sendMessage(this.teamData[0].signups[t], poke(m) + " was added to the list of NPC teammates.", "green"); } } if (this.teamData[1].signups.contains(player.id)) { this.npcMons2.push(m); for (var t in this.teamData[1].signups) { this.sendMessage(this.teamData[1].signups[t], poke(m) + " was added to the list of NPC teammates.", "green"); } } } if (cdata1 == "join") { if (this.playerInGame(name)) { this.sendMessage(name, "You've already joined this game!", "red"); return false; } if (this.official) { if (player.volleyballTeam) { if (player.volleyballTeam == this.teamData[0].name) { cdata2 = this.teamData[0].name; } else if (player.volleyballTeam == this.teamData[1].name) { cdata2 = this.teamData[1].name; } else { this.sendMessage(name, "This is an official volleyball league match! You must be affiliated with a team to play!", "red"); return false; } } } if (this.teamData[0].name.toLowerCase() === cdata2.toLowerCase()) { if (this.teamData[0].signups.length === 6) { if (this.official) { this.sendMessage(name, "That team is already full!", "red"); return false; } this.sendMessage(name, "Team " + cdata2 + " already has 6 members! You were sent to the other team!", "red"); cdata2 = this.teamData[1].name; } } if (this.teamData[1].name.toLowerCase() === cdata2.toLowerCase()) { if (this.teamData[1].signups.length === 6) { if (this.official) { this.sendMessage(name, "That team is already full!", "red"); return false; } this.sendMessage(name, "Team " + cdata2 + " already has 6 members! You were sent to the other team!", "red"); cdata2 = this.teamData[0].name; } } if (cdata2 === "") { if (chance(0.5) && this.teamData[0].signups.length < 6) { cdata2 = this.teamData[0].name; } else { cdata2 = this.teamData[1].name; } } if (this.teamData[0].name.toLowerCase() === cdata2.toLowerCase()) { cdata2 = this.teamData[0].name; this.teamData[0].signups.push(player.id); this.sendMessage(name, "You signed up for Team " + cdata2 + "!", "red"); sendAll(name + " signed up for Team " + cdata2 + "!"); this.sendMessageAll(name + " signed up for Team " + cdata2 + "!"); } if (this.teamData[1].name.toLowerCase() === cdata2.toLowerCase()) { cdata2 = this.teamData[1].name; this.teamData[1].signups.push(player.id); this.sendMessage(name, "You signed up for Team " + cdata2 + "!", "red"); this.sendMessageAll(name + " signed up for Team " + cdata2 + "!"); } if (this.teamData[0].signups.length >= 6 && this.teamData[1].signups.length >= 6) { this.assemblePhase(); } } if (cdata1 == "forcestart" && SESSION.channels(safchan).isChannelAdmin(src)) { this.assemblePhase(); } return; } else { this.inputMove(name, data); } return; }; Volleyball.prototype.assemblePhase = function() { var team1 = this.teamData[0].signups, team2 = this.teamData[1].signups, isNPC, newp, p; this.sendMessageAll("The teams have been decided! The match will now begin!"); var npcs = ["Steven", "Cynthia", "Lance", "Misty", "Nessa", "Brock", "Lillie", "Phoebe", "Juan", "Clair", "Bruno", "Maylene", "Koga", "Janine", "Jasmine", "Whitney", "Iris", "Flannery", "Candice", "Will", "Skyla", "Cilan", "Dent", "Blue", "Kiawe"] var name = ""; var index = 0; if (team1.length < 3) { this.official = false; } if (team2.length < 3) { this.official = false; } var hold = this.npcMons1; this.npcMons1 = []; var j = 0, i = -1, ds = 0; var npcNum = (6 - team1.length); while (j < 50) { i++; if (i >= hold.length || i >= npcNum) { break; } this.npcMons1.push(hold[i]); if (i + npcNum >= hold.length) { continue; } else { ds += npcNum; i += npcNum; this.npcMons1.push(hold[i]); } if (i + npcNum >= hold.length) { continue; } else { ds += npcNum; i += npcNum; this.npcMons1.push(hold[i]); } i -= ds; ds = 0; j++; } hold = this.npcMons2; this.npcMons2 = []; j = 0, i = -1, ds = 0; npcNum = (6 - team2.length); while (j < 50) { i++; if (i >= hold.length || i >= npcNum) { break; } this.npcMons2.push(hold[i]); if (i + npcNum >= hold.length) { continue; } else { ds += npcNum; i += npcNum; this.npcMons2.push(hold[i]); } if (i + npcNum >= hold.length) { continue; } else { ds += npcNum; i += npcNum; this.npcMons2.push(hold[i]); } i -= ds; ds = 0; j++; } while (team1.length < 6) { index = Math.floor(npcs.length * Math.random()); team1.push({id: npcs[index]}); npcs.splice(index, 1); } while (team2.length < 6) { index = Math.floor(npcs.length * Math.random()); team2.push({id: npcs[index]}); npcs.splice(index, 1); } for (var t in team1) { isNPC = typeof team1[t] === "object"; if (isNPC) { p = this.generatePlayer(team1[t], true, 0); this.team1[team1[t].id] = p; this.loadNextMon(p); } else { newp = getAvatar(sys.id(team1[t])); if (!newp) { newp = getAvatarOff(team1[t]); } p = this.generatePlayer(newp, false, 0); this.team1[newp.id] = p this.loadNextMon(p); //this.viewers.push(team1[t].id.toLowerCase()); } } for (var t in team2) { isNPC = typeof team2[t] === "object"; if (isNPC) { p = this.generatePlayer(team2[t], true, 1); this.team2[team2[t].id] = p; this.loadNextMon(p); } else { newp = getAvatar(sys.id(team2[t])); if (!newp) { newp = getAvatarOff(team2[t]); } p = this.generatePlayer(newp, false, 1); this.team2[newp.id] = p; this.loadNextMon(p); //this.viewers.push(team2[t].id.toLowerCase()); } } this.teamData[0].signups = []; this.teamData[1].signups = []; this.sendMessageTeam(0, "Your team is: " + Object.keys(this.teams[0]).join(", ") + "!"); this.sendMessageTeam(1, "Your team is: " + Object.keys(this.teams[1]).join(", ") + "!"); this.sendMessageAll("Now it is time to decide your service order! Type /vol 0 to serve first, or /vol 1 if you want to serve last!"); this.sendMessageAll("" + link("/vol 0", "Serving (Back-left)") + " " + link("/vol 1", "Back-center") + " " + link("/vol 2", "Back-right") + " " + link("/vol 3", "Front-right") + " " + link("/vol 4", "Front-center") + " " + link("/vol 5", "Front-left")); this.turn = 0; this.phase = "assemble"; }; Volleyball.prototype.hasSkill = function(player, skill) { return (player.skills.indexOf(skill) !== -1); }; Volleyball.prototype.setterLinks = function(ind) { var team = this.teams[ind], p, q, links = "", links2 = "", first = false, first2 = false; for (var t in team) { p = team[t]; if (p.canSet) { for (var s in team) { q = team[s]; if (q.id == p.id) { continue; } if ((q.row === 4 && q.zone == "front") || (q.row === 3 && q.skills.indexOf("back-attack") !== -1)) { links += (link("/vol set:" + q.id) + " (" + poke(q.party[q.currentPoke].id) + ") "); if (first) { first = false; } else { links += " "; } } else { links2 += link("/vol set:" + q.id); if (first2) { first2 = false; } else { links2 += " "; } } } if (links == "") { this.sendMessage(p.id, "To choose a set target, use " + links2 + "."); } else { this.sendMessage(p.id, "To choose a set target, use " + links + "."); } if (p.canTip) { this.sendMessage(p.id, "You can also perform a setter dump with " + link("/vol tip") + "!"); } } } return; }; Volleyball.prototype.statPrintout = function(player, court) { var p = null; for (var t in this.teams[0]) { if (this.teams[0][t].id === player) { p = this.teams[0][t]; break; } } if (!p) { for (var t in this.teams[1]) { if (this.teams[1][t].id === player) { p = this.teams[1][t]; break; } } } if (!p) { return " "; } //stat printout is here var stm = p.stamina; var srv = srv2 = p.serve; var rec = rec2 = p.receive; var set = set2 = p.toss; var spk = spk2 = p.spike; var blk = blk2 = p.block; var prc = prc2 = p.precision; var speed = p.speed; var tm = this.teams[p.team]; var otherteam = p.team == 0 ? 1 : 0; var tm2 = this.teams[otherteam]; var q; for (var t in tm2) { q = tm2[t]; if (this.hasSkill(q, "haze") && (q.zone == "front")) { if (!(hasType(p.party[p.currentPoke].id), "Water") && (!(hasType(p.party[p.currentPoke].id), "Ghost"))) { srv2 -= 2; } } } if (p.observed) { rec2 = 7; } if (p.stunned) { rec2 -= 2; } if (this.hasSkill(p, "grounded") && p.prep >= 1) { rec2 += 2; } if (this.hasSkill(p, "guardian") && this.ballPower <= 0 && this.phase == "receive") { rec2 += 2; } for (var t in tm) { q = tm[t]; if (this.hasSkill(q, "banner") && (q.row == p.row || q.column == p.column)) { rec2 += 1; } } for (var t in tm2) { q = tm2[t]; if (this.hasSkill(q, "interference") && (q.zone == "front")) { if (!(hasType(p.party[p.currentPoke]), "Psychic") && (!(hasType(p.party[p.currentPoke].id), "Dark"))) { set2 -= 1; } } } if (p.row == 4 && p.column == 4) { set2++; } if (this.hasSkill(p, "alignment")) { for (var t in tm) { q = tm[t]; if ((q.row == p.row || p.column == q.column) && ((hasType(q.party[q.currentPoke].id, "Normal") || (hasType(q.party[q.currentPoke].id, "Fairy"))))) { set2++; } } } for (var t in tm) { q = tm[t]; if (this.hasSkill(q, "guts") && (q.zone == "back") & q.id !== p.id) { if (this.hasSkill(q, "telepathy") && (hasType(p.party[p.currentPoke].id, "Fighting"))) { spk2 += 1; } if (this.hasSkill(q, "guts") && (hasType(p.party[p.currentPoke].id, "Dark"))) { spk2 += 1; } if (this.hasSkill(q, "defiant") && (hasType(p.party[p.currentPoke].id, "Psychic"))) { spk2 += 1; } } } if (this.hasSkill(p, "ace")) { var activate = true; for (var t in tm) { q = tm[t]; if (this.hasSkill(q, "ace") && q.id !== p.id) { activate = false; } } if (activate) { spk2 += 2; } } if (this.hasSkill(p, "wide") && (p.column == 1 || p.column == 7)) { spk2 += 2; } if (this.hasSkill(p, "dagger") && (this.turn >= 5)) { spk2 += 2; } for (var t in tm2) { q = tm2[t]; if (q.zone == "back" && this.hasSkill(q, "overgrow") && (hasType(p.party[p.currentPoke].id, "Water"))) { spk2 -= 1; break; } } if (this.hasSkill(p, "lightbane")) { for (var t in tm2) { q = tm2[t]; if (hasType(q.party[q.currentPoke].id, "Psychic")) { spk2 += 1; srv2 += 1; break; } } } if (p.autotarget) { prc2 = (Math.max(prc2), 4); } for (var t in tm) { q = tm[t]; if (this.hasSkill(q, "clairvoyant") && (q.zone == p.zone) && (!(p.id == q.id))) { prc2 += 2; } } if (p.stamina >= 25 && this.hasSkill(p, "simplicity")) { rec2++; set2++; blk2++; prc2++; spk2++; srv++; } var out = ""; if (court) { out += ("REC: " + rec2 + " "); out += ("SET: " + set2 + " "); out += ("SPK: " + spk2 + " "); out += ("PRC: " + prc2 + " "); out += ("BLK: " + blk2 + " "); out += ("SRV: " + srv2 + " "); out += ("SPEED: " + speed + " "); out += ("STAMINA: " + stm + " "); } else { out += ("REC: " + rec2 + " " + (rec2 > rec ? (toColor("(+" + (rec2 - rec) + ")", "green")) : (rec2 < rec ? (toColor(" (-" + (rec - rec2) + ")", "red")) : "")) + " "); out += ("SET: " + set2 + " " + (set2 > set ? (toColor("(+" + (set2 - set) + ")", "green")) : (set2 < set ? (toColor(" (-" + (set - set2) + ")", "red")) : "")) + " "); out += ("SPK: " + spk2 + " " + (spk2 > spk ? (toColor("(+" + (spk2 - spk) + ")", "green")) : (spk2 < spk ? (toColor(" (-" + (spk - spk2) + ")", "red")) : "")) + " "); out += ("PRC: " + prc2 + " " + (prc2 > prc ? (toColor("(+" + (prc2 - prc) + ")", "green")) : (prc2 < prc ? (toColor(" (-" + (prc - prc2) + ")", "red")) : "")) + " "); out += ("BLK: " + blk2 + " " + (blk2 > blk ? (toColor("(+" + (blk2 - blk) + ")", "green")) : (blk2 < blk ? (toColor(" (-" + (blk - blk2) + ")", "red")) : "")) + " "); out += ("SRV: " + srv2 + " " + (srv2 > srv ? (toColor("(+" + (srv2 - srv) + ")", "green")) : (srv2 < srv ? (toColor(" (-" + (srv - srv2) + ")", "red")) : "")) + " "); out += ("SPEED: " + speed + " "); out += ("STAMINA: " + stm + " "); } return out; }; Volleyball.prototype.courtIcon = function(mon, owner) { return ""; }; Volleyball.prototype.courtView = function(team) { var atkteam = team, defteam = (team === 0 ? 1 : 0), p, mon; var rows = {}, name; var props = [ ["xa1", "xa2", "xa3", "xa4", "xa5", "xa6", "xa7"], ["xb1", "xb2", "xb3", "xb4", "xb5", "xb6", "xb7"], ["xc1", "xc2", "xc3", "xc4", "xc5", "xc6", "xc7"], ["xd1", "xd2", "xd3", "xd4", "xd5", "xd6", "xd7"], ["---", "---", "---", "---", "---", "---", "---"], ["d1", "d2", "d3", "d4", "d5", "d6", "d7"], ["c1", "c2", "c3", "c4", "c5", "c6", "c7"], ["b1", "b2", "b3", "b4", "b5", "b6", "b7"], ["a1", "a2", "a3", "a4", "a5", "a6", "a7"] ]; var ret = "", r, place, inp, identify; for (var t in this.teams[atkteam]) { //Check if Sneak can activate if (this.hasSkill(this.teams[atkteam][t], "clairvoyant") && this.teams[atkteam][t].zone === "front") { identify = true; } } for (var t in this.teams[defteam]) { p = this.teams[defteam][t]; if (!p.actSkills.sneak || (identify)) { mon = parseInt(p.party[p.currentPoke].id, 10); rows["x" + p.pos[0] + (8 - p.column)] = {"mon": mon, "owner": p.id}; } } for (var t in this.teams[atkteam]) { p = this.teams[atkteam][t]; mon = parseInt(p.party[p.currentPoke].id, 10); rows[p.pos] = {"mon": mon, "owner": p.id, "p": p}; } ret += ""; for (var i = 0; i < props.length; i++) { ret += ""; r = props[i]; for (var j = 0; j < r.length; j++) { var place = r[j]; ret += ""; } ret += ""; } ret += "
"; if (rows.hasOwnProperty(place)) { inp = parseInt(rows[place].mon, 10); ret += this.courtIcon(inp, rows[place].owner); } else if (place === "---") { ret += link("/vol block:" + (j + 1), place) + " "; } else { ret += " " + link("/vol " + place, place) + " "; } ret += "
"; return ret; }; Volleyball.prototype.sendMessage = function(name, msg, color) { var id = sys.id(name); if (id && sys.isInChannel(id, safchan)) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { if (color) { safaribot.sendHtmlMessage(id, toColor(msg, color), safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } } return; }; Volleyball.prototype.sendMessageTeam = function(team, msg, color, table) { for (var p in this.teams[team]) { var id = sys.id(p); if (id && sys.isInChannel(id, safchan)) { if (msg === "") { sys.sendHtmlMessage(id, msg, safchan); } else { if (table) { sys.sendHtmlMessage(id, msg, safchan); } else if (color) { safaribot.sendHtmlMessage(id, toColor(msg, color), safchan); } else { safaribot.sendHtmlMessage(id, msg, safchan); } } } } return; }; Volleyball.prototype.sendMessageAll = function(msg, color) { this.sendMessageTeam(0, msg, color); this.sendMessageTeam(1, msg, color); var p, id; for (var a in this.teamData[0].signups) { p = this.teamData[0].signups[a]; if (typeof p == "string") { id = sys.id(p); if (id && sys.isInChannel(id, safchan)) { safaribot.sendHtmlMessage(id, msg, safchan); } } } for (var a in this.teamData[1].signups) { p = this.teamData[1].signups[a]; if (typeof p == "string") { id = sys.id(p); if (id && sys.isInChannel(id, safchan)) { safaribot.sendHtmlMessage(id, msg, safchan); } } } }; Volleyball.prototype.generateVolleyballParty = function() { var p = [], k; for (var i = 0; i < 3; i++) { k = parseInt(Object.keys(safari.volleyballStats).random()); while (p.indexOf(k) !== -1) { k = parseInt(Object.keys(safari.volleyballStats).random(), 10); } p.push(k); } return p; } Volleyball.prototype.generatePlayer = function(data, isNPC, team) { var party = []; var out = { team: team, action: "", ai: true, currentPoke: -1, place: -1, score: 0, pos: "", zone: "", row: 0, column: 0, prep: 0, st: 0, actSkills: {}, canServe: false, canSet: false, canHit: false, canTip: false, blocking: false, blockType: 0, passval: 0, setval: 0, volleysIn: 0, freepass: false, observed: false, autotarget: false, stunned: false, frozen: false, moved: 0, receiver: false, receiveType: "", serveEffort: 0, canSwap: false, }; if (isNPC) { party = []; var j = 0; if (team == 0) { while (this.npcMons1.length > 0) { if (party.length > 1) { break; } party.push(this.npcMons1.pop()); j++; if (j > 1000) { break; } } } else { while (this.npcMons2.length > 0) { if (party.length > 1) { break; } party.push(this.npcMons2.pop()); j++; if (j > 1000) { break; } } } while (party.length < 3) { party.push(parseInt(Object.keys(safari.volleyballStats).random())); j++; if (j > 1000) { break; } } out.id = data.id; } else { for (var i = 0; i < 3; i++) { party.push(data.party[i]); } out.id = data.id.toCorrectCase(); out.ai = false; } out.party = []; for (var p in party) { /* skills = skills available for this character (up to 3-4 may exist) place = where in the rotation you are. 0 = server, 0-2 is back row, 3-5 is front row score = score for a serve, recieve, toss, or spike (can be translated to good, great, perfect etc) position = xy position on the court (can be like a1, a2, a3, b1, etc). abc = vertical placement, 123 = horizontal placement. This only applies for back row players zone = front row or back row. prep = how long you have been waiting in position to receive, set, block. The longer you wait in the same position, the more effective your receive will be. st = stamina remaining */ out.party.push({ id: party[p], owner: out.id, stamina: getVolleyballStat(party[p], "stamina"), serve: getVolleyballStat(party[p], "serve"), receive: getVolleyballStat(party[p], "receive"), toss: getVolleyballStat(party[p], "toss"), spike: getVolleyballStat(party[p], "spike"), precision: getVolleyballStat(party[p], "precision"), block: getVolleyballStat(party[p], "block"), speed: getVolleyballStat(party[p], "speed"), skills: getVolleyballStat(party[p], "skills") }); } return out; }; Volleyball.prototype.loadNextMon = function(p) { var player = p.id; this.inputVal(player, "currentPoke", p.currentPoke + 1); if (p.currentPoke > 2) { this.inputVal(player, "currentPoke", 2); } var m = (p.currentPoke > 2 ? 2 : p.currentPoke); this.inputVal(player, "stamina", p.party[m].stamina); this.inputVal(player, "maxStamina", p.party[m].stamina); this.inputVal(player, "serve", p.party[m].serve); this.inputVal(player, "receive", p.party[m].receive); this.inputVal(player, "toss", p.party[m].toss); this.inputVal(player, "spike", p.party[m].spike); this.inputVal(player, "precision", p.party[m].precision); this.inputVal(player, "block", p.party[m].block); this.inputVal(player, "speed", p.party[m].speed); this.inputVal(player, "skills", p.party[m].skills); this.inputVal(player, "volleysIn", 0); this.inputVal(player, "action", ""); return; }; Volleyball.prototype.actName = function(player) { return (player.id + "'s " + poke(player.party[player.currentPoke].id)); }; Volleyball.prototype.playerInGame = function(name) { var p; for (var a in this.teams[0]) { p = this.teams[0][a]; if (p.id.toLowerCase() == name.toLowerCase()) { return true; } } for (var a in this.teams[1]) { p = this.teams[1][a]; if (p.id.toLowerCase() == name.toLowerCase()) { return true; } } for (var a in this.teamData[0].signups) { p = this.teamData[0].signups[a]; if (typeof p == "string" && p.toLowerCase() == name.toLowerCase()) { return true; } } for (var a in this.teamData[1].signups) { p = this.teamData[1].signups[a]; if (typeof p == "string" && p.toLowerCase() == name.toLowerCase()) { return true; } } return false; }; Volleyball.prototype.action = function() { this.step++; //every 8 seconds if (this.phase == "signups") { if (this.step === 12) { sys.sendAll("", safchan); safaribot.sendHtmlAll("You have about a minute left to join the Volleyball match with " + link("/vol join") + "!", safchan); sys.sendAll("", safchan); } if (this.step === 20) { //after 400 seconds this.assemblePhase(); this.step = 0; } } else if (this.phase == "assemble") { if (this.step >= 5) { this.step = 0; this.aiChooseMove(0); this.aiChooseMove(1); this.teamHasBall = (chance(0.5) ? 1 : 0); if (this.teamHasBall === 1) { this.teamData[0].firstBall = true; } else { this.teamData[1].firstBall = true; } this.sendMessageAll(""); this.sendMessageAll("Team " + this.teamData[this.teamHasBall].name + " has the first serve!", "blue"); this.sendMessageAll(""); this.phase = "prep"; this.resetPosition(0); this.resetPosition(1); } } else if (this.step >= 2) { this.aiChooseMove(0); this.aiChooseMove(1); this.step = 0; if (this.turn > 0) { this.sendMessageAll("TURN " + this.turn + ": "); } else { this.sendMessageAll("SERVICE: "); } if (this.phase == "prep") { this.processMoves(); this.prepareServe(this.teamHasBall); } else { this.cyclePhase = this.phase; this.processMoves(); this.phase = this.cyclePhase; } } }; Volleyball.prototype.inputVal = function(name, k, v) { var cteam, torow, column; for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (p.id !== name) { continue; } this.teams[team][t][k] = v; return true; } } this.sendMessageAll("Error inputting value for " + name + "."); return false; } Volleyball.prototype.movePlayer = function(name, goTo) { var cteam, torow, column; for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (p.id !== name) { continue; } if (goTo[0] == "a") { torow = 1; } if (goTo[0] == "b") { torow = 2; } if (goTo[0] == "c") { torow = 3; } if (goTo[0] == "d") { torow = 4; } tocolumn = parseInt(goTo[1], 10); this.teams[team][t].row = torow; this.teams[team][t].column = tocolumn; this.teams[team][t].pos = goTo; } } } Volleyball.prototype.processMoves = function() { /* For Each player If it's a move move, Move players from place A to place B pay stamina costs for each move as necessary For Each player If it's an attack move, move the ball to the other side and perform an attack */ var torow, tocolumn, dist, cost, cteam, passed = false, defteam = this.teamHasBall === 1 ? 0 : 1; for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (p.action == "") { p.prep += 1; p.prep = (Math.min(p.prep, 3)); } else if (p.action[0] !== "x") { if (["set", "attack", "tip", "block"].indexOf(p.action) !== -1) { continue; } if (p.action[0] == "a") { torow = 1; } if (p.action[0] == "b") { torow = 2; } if (p.action[0] == "c") { torow = 3; } if (p.action[0] == "d") { torow = 4; } tocolumn = parseInt(p.action[1], 10); dist = Math.abs(p.row - torow) + Math.abs(p.column - tocolumn); this.movePlayer(p.id, p.action); cost = (dist > 1 ? dist : dist * 2); p.stamina = Math.max(0, p.stamina-cost); p.moved = dist; p.prep = 0; } } } if (this.phase == "prep") { for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (p.action == "sub") { if (p.currentPoke >= 2) { this.sendMessage(p.id, "You can only sub a Pokémon in twice per match! You are out of substitutions!", "red"); } else { this.loadNextMon(p); this.sendMessageTeam(p.team, p.id + " subs in " + poke(p.party[p.currentPoke].id) + "!", "green"); switch (p.place) { case 0: this.inputVal(p.id, "place", 0); this.movePlayer(p.id, "b2"); break; case 1: this.inputVal(p.id, "place", 1); this.movePlayer(p.id, "b4"); break; case 2: this.inputVal(p.id, "place", 2); this.movePlayer(p.id, "b6"); break; case 3: this.inputVal(p.id, "place", 3); this.movePlayer(p.id, "d6"); break; case 4: this.inputVal(p.id, "place", 4); this.movePlayer(p.id, "d4"); break; case 5: this.inputVal(p.id, "place", 5); this.movePlayer(p.id, "d2"); break; } this.inputVal(p.id, "action", ""); } } } } return; } if (this.phase == "receive") { this.processReceive(); return; } for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (p.team === this.teamHasBall) { continue; } if (["serve", "receive"].indexOf(this.phase) !== -1) { continue; } if (p.action == "block" && p.zone == "front" && p.row === 4) { this.inputVal(p.id, "blocking", true); } } } for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (p.action[0] == "x") { if (p.action[1] == "a") { torow = 1; } if (p.action[1] == "b") { torow = 2; } if (p.action[1] == "c") { torow = 3; } if (p.action[1] == "d") { torow = 4; } tocolumn = parseInt(p.action[2], 10); if (this.phase == "serve") { this.processServe(p, torow, tocolumn); passed = true; } else { this.processAttack(p, torow, tocolumn); passed = true; } break; } if (p.action == "tip") { this.processTip(p); passed = true; break; } if (p.action == "set") { this.processSet(p.id, p.team, p.setTarget, p.passval); passed = true; break; } } } this.endBlock(0); this.endBlock(1); if (this.phase == "set" || this.phase == "attack") { if (!passed) { this.sendMessageAll("The team with the ball failed to complete a play!", "blue"); for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; this.inputMove("miki sayaka", "eval:" + p.id); } } this.recordBlockers = null; this.recordSetter = null; this.recordSpiker = null; this.scorePoint(defteam); } } else if (!passed) { this.sendMessageAll("Something broke!"); for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; this.inputMove("miki sayaka", "eval:" + p.id); } } } for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { var p = cteam[t]; if (this.hasSkill(p, "sneak") && (!p.actSkills.sneak)) { this.sendMessage(p.id, "You can use " + link("/vol sneak") + " to become invisible to opponents until you next touch the ball!"); } } } this.clearVals(); return; }; Volleyball.prototype.aiChooseMove = function(ind) { var p, getRow, getColumn, act = "", act2 = null, maxDist = 5, stay = false, maxSet = -1, maxHit = 0, secondMaxHit = 0, setter = "", chooseHitter, maxr, maxc; var team = this.teams[ind]; var otherteam = (ind === 0 ? this.teams[1] : this.teams[0]); if (this.ballRow === 1) { getRow = "a"; } if (this.ballRow === 2) { getRow = "b"; } if (this.ballRow === 3) { getRow = "c"; } for (var i = 1; i < maxDist; i++) { /* This is the code for AI deciding who should receive the ball. It's pretty tangled so be careful about editing it. */ for (var t in team) { act = ""; p = team[t]; if (this.teamHasBall === ind) { if (this.phase == "receive") { if (sys.isInChannel(sys.id(p.id), safchan) || (!p.ai)) { continue; } if (this.pos === (getRow + this.ballColumn)) { act = ""; stay = true; } else if (p.column === this.ballColumn && p.row - 1 === this.ballRow && p.toss > p.receive) { act = ""; stay = true; } else if (this.getDistance2(p.row, p.column, this.ballRow, this.ballColumn) === 1) { act = ""; stay = true; } else if (this.getDistance2(p.row, p.column, this.ballRow, this.ballColumn) <= (p.speed)) { if (this.getDistance2(p.row, p.column, this.ballRow, this.ballColumn) <= i) { if (((i < (maxDist - 2)) && (chance((1 - (((p.receive*2) + i)*0.07)))))) { continue; } act = (getRow + this.ballColumn); } } else { act = ""; } if ((act !== "") && (!stay)) { this.inputMove(p.id, act); break; } } } } } maxSet = -100; maxHit = -100; secondMaxHit = -100; for (var t in team) { p = team[t]; if (this.teamHasBall === ind) { if (this.phase == "set") { if (p.canSet && p.row == 4 && p.ai) { if (p.toss >= maxSet) { maxSet = p.toss; setter = p.id; } } } } } for (var t in team) { act = ""; p = team[t]; if (this.teamHasBall === ind) { if (this.phase == "set") { if (((p.zone == "front" && p.row == 4) || (this.hasSkill(p, "back-attack"))) && (setter !== p.id)) { if (p.spike >= maxHit) { maxHit = p.spike; } else if (p.spike >= secondMaxHit) { secondMaxHit = p.spike; } } } if ((this.phase == "attack" || this.phase == "set") && p.canHit && p.action == "") { if (this.phase == "set" && chance(1 - p.spike * 0.1)) { continue; } if (p.row !== 4 && !(p.row === 3 && p.skills.indexOf("back-attack") !== -1)) { act = ""; continue; } if (p.precision >= 4) { maxr = 2.5; maxc = 3.2; } else if (p.precision >= 2) { maxr = 1.5; maxc = 2.4; } else if (p.precision >= 2) { maxr = 1.2; maxc = 2; } maxr = Math.floor(2.99 - (Math.random() * maxr)); maxc = (4 - (maxc * Math.random()) + (maxc * Math.random())); maxc = (Math.max(maxc, 1)); maxc = Math.round(Math.min(maxc, 7)); if (maxr === 0) { maxr = "a"; } if (maxr === 1) { maxr = "b"; } if (maxr === 2) { maxr = "c"; } act = ("x" + maxr + maxc); this.inputMove(p.id, act); } } } chooseHitter = (chance(0.67) ? 1 : 2); for (var t in team) { p = team[t]; act = ""; act2 = ""; if (this.phase == "assemble" && p.place === -1) { var pass; pass = this.inputMove(p.id, "0"); if (!pass) { pass = this.inputMove(p.id, "1"); } if (!pass) { pass = this.inputMove(p.id, "2"); } if (!pass) { pass = this.inputMove(p.id, "3"); } if (!pass) { pass = this.inputMove(p.id, "4"); } if (!pass) { pass = this.inputMove(p.id, "5"); } continue; } if (p.canServe && p.action == "" && this.phase == "serve") { //Force it to serve because otherwise the game doesn't start if (p.precision >= 4) { maxr = 2.5; maxc = 3.2; } else { maxr = 1.5; maxc = 2.4; } maxr = Math.floor(2.99 - (Math.random() * maxr)); maxc = Math.floor(4 - (maxc * Math.random()) + (maxc * Math.random())); maxc = (Math.max(maxc, 1)); maxc = Math.round(Math.min(maxc, 7)); if (maxr === 0) { maxr = "a"; } if (maxr === 1) { maxr = "b"; } if (maxr === 2) { maxr = "c"; } act = ("x" + maxr + maxc); if (p.skills.indexOf("float") !== -1 && chance(0.45)) { p.actSkills.float = true; } var s = (Math.floor(Math.random() * (2.1 + (p.serve * 0.15)))); this.inputMove(p.id, "effort:" + s); this.inputMove(p.id, act); continue; } if (p.canHit && p.action == "" && this.phase !== "set") { if (p.row !== 4 && !(p.row === 3 && p.skills.indexOf("back-attack") !== -1) && this.phase !== "set" ) { act = "hitfree"; this.inputMove(p.id, act); continue; } if (p.precision >= 4) { maxr = 2.5; maxc = 3.2; } else { maxr = 1.5; maxc = 2.4; } maxr = Math.floor(3 - (Math.random() * maxr)); maxc = (4 - (maxc * Math.random()) + (maxc * Math.random())); maxc = (Math.max(maxc, 1)); maxc = Math.round(Math.min(maxc, 7)); if (maxr === 1) { maxr = "a"; } if (maxr === 2) { maxr = "b"; } if (maxr === 3) { maxr = "c"; } act = ("x" + maxr + maxc); this.inputMove(p.id, act); } if (this.teamHasBall === ind) { if (this.phase == "set") { if (p.id == setter) { act = "set"; for (var s in team) { q = team[s]; act2 = q.id.toLowerCase(); if ((!((((q.zone == "front" && q.row == 4)) || ((this.hasSkill(q, "back-attack")))))) || (q.id == p.id)) { continue; } if (q.spike === maxHit && chooseHitter === 1) { break; } else if (q.spike === secondMaxHit && chooseHitter === 2) { break; } else if (q.spike >= secondMaxHit + 3) { break; } } if (p.id == q.id) { for (var s in team) { q = team[s]; act2 = q.id.toLowerCase(); if ((p.id !== q.id) && (q.zone == "front" || ((this.hasSkill(q, "back-attack"))))) { break; } } } this.inputMove(p.id, act + ":" + act2); } else if (((p.row === 4) || (p.row === 3 && p.skills.indexOf("back-attack") !== -1)) && p.ai) { dist = Math.floor(0.5 + (Math.random() * p.speed)) * (chance(0.5) ? 1 : -1); if (dist > p.speed) { dist = p.speed; } if (chance(0.85)) { toColumn = p.column + dist; toColumn = Math.max(toColumn, 1); toColumn = Math.min(toColumn, 7); if (p.row === 1) { act = "a"; } if (p.row === 2) { act = "b"; } if (p.row === 3) { act = "c"; } if (p.row === 4) { act = "d"; } act += ("" + toColumn); } } else if (p.zone == "front" && p.row <= 3 && p.receive < p.spike) { if (chance(0.85)) { toColumn = Math.min(Math.max(p.column + (chance(0.3) ? 1 : (chance(0.3) ? -1 : 0)), 1), 7); if (4 - p.row < p.speed) { act = "d"; } else if (3 - p.row < p.speed) { act = "c"; } else if (2 - p.row < p.speed) { act = "b"; } act += ("" + toColumn); } } } } else { if (this.phase == "attack" && p.ai) { if (p.zone == "front") { if (p.block >= (0.7 + (2.7 * Math.random()))) { for (var k in otherteam) { if (otherteam[k].canHit) { if (Math.abs((8 - otherteam[k].column) - p.column) <= 1) { act = "block"; } break; } } } } else { dist = Math.floor(0.75 + (Math.random() * p.speed)); dist = Math.min(Math.max(dist, 1), p.speed); if (chance(0.85)) { toRow = p.row; if (chance(0.33)) { toRow++; dist--; } else if (chance(0.5)) { toRow--; dist--; } toRow = Math.max(toRow, 1); toRow = Math.min(toRow, 3); toColumn = p.column + (dist * (chance(0.5) ? 1 : -1)); toColumn = Math.max(toColumn, 1); toColumn = Math.min(toColumn, 7); if (toRow === 1) { act = "a"; } if (toRow === 2) { act = "b"; } if (toRow === 3) { act = "c"; } act += ("" + toColumn); } } } } if (this.phase == "prep") { if (((p.stamina <= 10 && chance(0.33)) || (p.stamina <= 4)) && p.ai && p.currentPoke < 2) { act = "sub"; } } if (p.action == "" && act !== "") { if (act2) { this.inputMove(p.id, act + ":" + act2); } else { this.inputMove(p.id, act); } } } return; } Volleyball.prototype.clearVals = function() { this.excludePos = [ [], [] ]; for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; if (this.cyclePhase == "receive") { p.canHit = false; p.canTip = false; p.actSkills.quick = false; p.actSkills.psyspike = false; p.receiver = false; p.passval = 0; p.setval = 0; p.moved = 0; if (p.zone == "front" && p.team !== this.teamHasBall) { p.canBlock = true; } } if (this.cyclePhase == "set") { p.actSkills.quick = false; p.actSkills.psyspike = false; p.freepass = false; p.receiver = false; p.moved = 0; } else { this.inputVal(p.id, "canSet", false); } if (this.cyclePhase == "serve") { p.actSkills.quick = false; p.actSkills.psyspike = false; p.freepass = false; p.receiver = false; p.passval = 0; p.setval = 0; p.moved = 0; } for (var s in p.actSkills) { if (s !== "sneak") { p.actSkills[s] = false; } } this.inputVal(p.id, "action", ""); if (this.cyclePhase !== "serve") { this.inputVal(p.id, "canServe", false); } if (p.stamina > p.maxStamina) { p = p.maxStamina; } if (p.stamina < 0) { p = 0; } this.excludePos[team].push(p.pos); } } this.excludeActions = []; if (this.cyclePhase !== "receive") { this.ballPower = 0; this.ballRow = -1; this.ballColumn = -1; this.ballBurn = false; this.ballStun = false; this.ballFreeze = false; this.ballFloat = false; } }; Volleyball.prototype.winGame = function(team) { var player; if (team === 0) { this.sendMessageAll("Team " + this.teamData[0].name + " won the match! Congratulations to the winners!", "blue"); this.sendMessageAll(Object.keys(this.teams[0]).join(", ") + " are the victors!" , "blue"); this.sendMessageAll("The winners receive " + translateStuff(this.reward1) + "!" , "blue"); } if (team === 1) { this.sendMessageAll("Team " + this.teamData[1].name + " won the match! Congratulations to the winners!", "blue"); this.sendMessageAll(Object.keys(this.teams[1]).join(", ") + " are the victors!" , "blue"); this.sendMessageAll("The winners receive " + translateStuff(this.reward2) + "!" , "blue"); } for (var i in this.teams[team]) { player = getAvatarOff(this.teams[team][i].id); if (player) { if (team === 0) { giveStuff(player, toStuffObj(this.reward1)); } else if (team === 1) { giveStuff(player, toStuffObj(this.reward2)); } } } if (this.official) { for (var i in this.teams[0]) { player = getAvatarOff(this.teams[0][i].id); if (player && (!this.teams[0][i].ai)) { player.volleyballRecords.points += this.teamData[0].score; player.volleyballRecords.pointsGiven += this.teamData[1].score; safari.saveGame(player); } } for (var i in this.teams[1]) { player = getAvatarOff(this.teams[1][i].id); if (player && (!this.teams[1][i].ai)) { player.volleyballRecords.points += this.teamData[1].score; player.volleyballRecords.pointsGiven += this.teamData[0].score; safari.saveGame(player); } } if (this.teamData[team].name == officialVolleyballTeam1) { officialVolleyballWins1++; } else if (this.teamData[team].name == officialVolleyballTeam2) { officialVolleyballWins2++; } } this.finished = true; return; } Volleyball.prototype.scorePoint = function(team) { var p, team2; if (team === 0) { this.teamData[0].score++; this.sendMessageAll("Team " + this.teamData[0].name + " scored a point!", "blue"); this.teamHasBall = 0; } if (team === 1) { this.teamData[1].score++; this.sendMessageAll("Team " + this.teamData[1].name + " scored a point!", "blue"); this.teamHasBall = 1; } var player; if (this.recordSpiker) { player = getAvatarOff(this.recordSpiker); if (player) { player.volleyballRecords.spikes += 1; safari.saveGame(player); } } if (this.recordSetter) { player = getAvatarOff(this.recordSetter); if (player) { player.volleyballRecords.sets += 1; safari.saveGame(player); } } if (this.recordBlockers) { for (var i = 0; i < this.recordBlockers.length; i++) { player = getAvatarOff(this.recordBlockers[i]); if (player) { player.volleyballRecords.blocks += 1; safari.saveGame(player); } } } if (this.teamData[team].score >= 7 && Math.abs(this.teamData[1].score - this.teamData[0].score) > 1) { this.winGame(team); return; } team2 = (team === 0 ? 1 : 0); if (this.teamServed !== team) { this.sideOut(team); } this.sendMessageAll(""); this.sendMessageAll("SCORE: "); this.sendMessageTeam(0, "" + this.teamData[0].name + ": " + this.teamData[0].score); this.sendMessageTeam(0, this.teamData[1].name + ": " + this.teamData[1].score); this.sendMessageTeam(1, this.teamData[0].name + ": " + this.teamData[0].score); this.sendMessageTeam(1, "" + this.teamData[1].name + ": " + this.teamData[1].score); this.sendMessageAll(""); this.clearVals(); this.resetPosition(team); this.resetPosition(team2); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendMessageAll("Choose a nearby position to begin the rally in!", "blue"); var regen = (3 + (Math.max(Math.min(((this.turn * 3) - 7), 10), 0))); this.turn = 0; for (var p in this.teams[0]) { this.teams[0][p].stamina = Math.min(this.teams[0][p].stamina + regen, this.teams[0][p].maxStamina); if (this.teams[0][p].currentPoke < 2) { this.sendMessage(this.teams[0][p].id, "You have " + this.teams[0][p].stamina + " stamina remaining. Type " + link("/vol sub") + " to switch into your next Pokémon.", "red"); } this.teams[0][p].volleysIn++; if (this.teams[0][p].volleysIn === 5 && this.hasSkill(this.teams[0][p], "slacker")) { this.sendMessageTeam(0, this.actName(this.teams[0][p]) + " has finally reached full strength!", "green"); } this.teams[0][p].observed = false; this.teams[0][p].autotarget = false; this.teams[0][p].stunned = false; this.teams[0][p].frozen = false; this.teams[0][p].canSwap = true; if (this.hasSkill(this.teams[0][p], "swap")) { this.sendMessage(this.teams[0][p].id, "Type " + link("/vol swap") + " to move up in the rotation!"); } this.teams[0][p].prep = 0; } for (var p in this.teams[1]) { this.teams[1][p].stamina = Math.min(this.teams[1][p].stamina + regen, this.teams[1][p].maxStamina); if (this.teams[1][p].currentPoke < 2) { this.sendMessage(this.teams[1][p].id, "You have " + this.teams[1][p].stamina + " stamina remaining. Type " + link("/vol sub") + " to switch into your next Pokémon.", "red"); } this.teams[1][p].volleysIn++; if (this.teams[1][p].volleysIn === 5 && this.hasSkill(this.teams[1][p], "slacker")) { this.sendMessageTeam(1, this.actName(this.teams[1][p]) + " has finally reached full strength!", "green"); } this.teams[1][p].observed = false; this.teams[1][p].autotarget = false; this.teams[1][p].stunned = false; this.teams[1][p].frozen = false; this.teams[1][p].canSwap = true; if (this.hasSkill(this.teams[1][p], "swap")) { this.sendMessage(this.teams[1][p].id, "Type " + link("/vol swap") + " to move up in the rotation!"); } this.teams[1][p].prep = 0; } this.phase = "prep"; this.cyclePhase = "prep"; return; }; Volleyball.prototype.prepareServe = function(team) { var p; this.clearVals(); for (var t in this.teams[team]) { p = this.teams[team][t]; if (p.place === 0) { this.teams[team][t].canServe = true; break; } } this.sendMessageAll(this.actName(p) + " prepares to serve!", "blue"); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); this.sendMessage(p.id, "Choose " + link("/vol effort:0", "Easy") + " " + link("/vol effort:1", "Normal") + " " + link("/vol effort:2", "Strong") + " for your serve strength!"); if (this.hasSkill(p, "float")) { this.sendMessage(p.id, "Type " + link("/vol float") + " to perform a Float Serve!"); } this.phase = "serve"; this.teamHasBall = team; return true; }; Volleyball.prototype.sideOut = function(team) { var p, firstBall = this.teamData[team].firstBall; this.teamData[team].firstBall = false; if (firstBall) { this.resetPosition(team); return; } for (var t in this.teams[team]) { p = this.teams[team][t]; switch (p.place) { case 5: this.inputVal(p.id, "place", 0); this.movePlayer(p.id, "b2"); break; case 0: this.inputVal(p.id, "place", 1); this.movePlayer(p.id, "b4"); break; case 1: this.inputVal(p.id, "place", 2); this.movePlayer(p.id, "b6"); break; case 2: this.inputVal(p.id, "place", 3); this.movePlayer(p.id, "d6"); break; case 3: this.inputVal(p.id, "place", 4); this.movePlayer(p.id, "d4"); break; case 4: this.inputVal(p.id, "place", 5); this.movePlayer(p.id, "d2"); break; } if (p.place <= 2) { p.zone = "back"; } else { p.zone = "front"; } } }; Volleyball.prototype.resetPosition = function(team) { var p; for (var t in this.teams[team]) { p = this.teams[team][t]; switch (p.place) { case 5: this.movePlayer(p.id, "d2"); this.inputVal(p.id, "zone", "front"); break; case 0: this.movePlayer(p.id, "b2"); this.inputVal(p.id, "zone", "back"); break; case 1: this.movePlayer(p.id, "b4"); this.inputVal(p.id, "zone", "back"); break; case 2: this.movePlayer(p.id, "b6"); this.inputVal(p.id, "zone", "back"); break; case 3: this.movePlayer(p.id, "d6"); this.inputVal(p.id, "zone", "front"); break; case 4: this.movePlayer(p.id, "d4"); this.inputVal(p.id, "zone", "front"); break; } } }; Volleyball.prototype.getDistance = function(p1, p2) { var out, c, r; if (p1[0] == "a") { c = 1; } if (p1[0] == "b") { c = 2; } if (p1[0] == "c") { c = 3; } if (p1[0] == "d") { c = 4; } if (p2[0] == "a") { c -= 1; } if (p2[0] == "b") { c -= 2; } if (p2[0] == "c") { c -= 3; } if (p2[0] == "d") { c -= 4; } c = Math.abs(c); r = (parseInt(p1[1], 10) - parseInt(p2[1])); out = c + r; return out; }; Volleyball.prototype.getDistance2 = function(p1row, p1column, p2row, p2column) { return (Math.abs(p2row - p1row) + Math.abs(p1column - p2column)); }; Volleyball.prototype.getPos = function(row, column, view) { var out = ""; if (view === 1) { out = "x"; } if (row === 1) { out += "a"; } if (row === 2) { out += "b"; } if (row === 3) { out += "c"; } if (row === 4) { out += "d"; } if (view === 1) { out += ("" + (8 - column)); } else { out += ("" + column); } return out; }; Volleyball.prototype.processServe = function(player, row, column) { /* Player may perform a serve with different levels: easy (2 stamina) normal (4 stamina) or jump (6 stamina) */ var pow, stcost, defteam = player.team === 0 ? 1 : 0, xvar, yvar, torow, tocolumn; var atkteam = defteam === 0 ? 1 : 0; this.teamServed = player.team; pow = player.serve; if (player.serveEffort === 0) { pow *= 0.5; } else if (player.serveEffort === 2) { pow *= 1.5; } var prec = player.precision; for (var t in this.teams[player.team]) { var p = this.teams[player.team][t]; if (p.zone === player.zone && (this.hasSkill(p, "clairvoyant"))) { prec += 2; } } for (var t in this.teams[defteam]) { q = this.teams[defteam][t]; if (this.hasSkill(q, "haze") && (q.zone == "front")) { if (!(hasType(player.party[player.currentPoke].id), "Water") && (!(hasType(player.party[player.currentPoke].id), "Ghost"))) { pow -= 2; } } } if (player.serveEffort === 0) { prec++; } if (player.serveEffort === 2) { prec -= (chance(0.5) ? 2 : 1); } if (player.actSkills.float) { prec -= 2; } else { pow--; } xvar = Math.max(Math.floor((1.5 * Math.random()) + (Math.max((7 - (prec * Math.random())), 1)) - ((3 + prec) * (1 + Math.random()))), 0); xvar = (chance(0.5) ? xvar * -1 : xvar); yvar = (chance(0.8 - (((prec * 1.2) + 2) * 0.1)) ? 1 : 0); yvar = (chance(0.5 + (player.actSkills.float ? 0.2 : 0)) ? yvar * -1 : yvar); torow = row + yvar; tocolumn = column + xvar; if (torow > 3 && player.actSkills.float) { torow = 3; } if (torow > 3) { //add chance for a net in later this.sendMessageAll(this.actName(player) + " served the ball into the net!", "blue"); this.recordBlockers = null; this.recordSetter = null; this.recordSpiker = null; this.scorePoint(defteam); this.phase = "prep"; this.cyclePhase = "prep"; return; } if (tocolumn < 1 || tocolumn > 7 || torow < 1) { this.sendMessageAll(this.actName(player) + " served the ball out of bounds!", "blue"); this.recordBlockers = null; this.recordSetter = null; this.recordSpiker = null; this.scorePoint(defteam); this.phase = "prep"; this.cyclePhase = "prep"; return; } if (torow > 1 && player.serve < 4) { pow--; } if (torow > 2 && player.serve < 3) { pow--; } pow += (3 * Math.random() - Math.random()); pow = Math.min(8, pow); if (player.stamina <= 10) { pow -= 1; } if (player.stamina <= 7) { pow -= 1; } if (player.stamina <= 4) { pow -= 1; } if (player.actSkills.float) { pow += 1; } pow = Math.ceil(Math.max(pow, 1)); if (this.hasSkill(player, "slacker") && player.volleysIn < 5) { pow = Math.ceil(pow * 0.5); } this.ballPower = pow; this.ballRow = torow; this.ballColumn = (8 - tocolumn); stcost = (2 + (player.serveEffort * 4)); player.stamina = Math.max(player.stamina - stcost, 0); if (player.actSkills.float) { this.ballFloat = true; player.actSkills.float = false; } if (this.hasSkill(player, "burn")) { this.ballBurn = true; } if (this.hasSkill(player, "stun")) { this.ballStun = true; } if (this.hasSkill(player, "freeze")) { this.ballFreeze = true; } var might = "FREE"; switch (pow) { case 9: might = "EARTHSHATTERING"; break; case 8: might = "METEORIC"; break; case 7: might = "FIERCE"; break; case 6: might = "FORCEFUL"; break; case 5: might = "POWERFUL"; break; case 4: might = "STRONG"; break; case 3: might = "GOOD"; break; case 2: might = "DECENT"; break; case 1: might = "WEAK"; break; case 0: might = "FREE"; break; }; this.sendMessageAll(volleyballScoreIcon(might) + this.actName(player) + " serves the ball!" + (this.ballFloat ? " It's a Float Ball!" : ""), "blue"); this.sendMessage(player.id, "You spent " + stcost + " stamina serving! You now have " + player.stamina + " left!" , "red"); player.actSkills.sneak = false; this.cyclePhase = "receive"; this.teamHasBall = defteam; this.sendMessageTeam(atkteam, "The ball was served to " + this.getPos(this.ballRow, this.ballColumn, 1) + "!", "blue"); this.sendMessageTeam(defteam, "The ball was served to " + this.getPos(this.ballRow, this.ballColumn, 0) + "!", "blue"); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); this.clearVals(); return; } Volleyball.prototype.endBlock = function(defteam) { var p; for (var t in this.teams[defteam]) { p = this.teams[defteam][t]; if (p.blocking) { stcost = 2; p.stamina = Math.max(p.stamina - stcost, 0); this.sendMessage(p.id, "You spent " + stcost + " stamina blocking! You now have " + p.stamina + "!" , "red"); p.canBlock = false; p.blocking = false; p.prep = 0; } } } Volleyball.prototype.processSet = function(name, ind, setTo, passscore) { var dist, proficiency, bonus, stcost, score, target = null, player = null; for (var p in this.teams[ind]) { if (this.teams[ind][p].id.toLowerCase() == setTo.toLowerCase()) { target = this.teams[ind][p]; } if (this.teams[ind][p].id.toLowerCase() == name.toLowerCase()) { player = this.teams[ind][p]; } } if (!target || !player) { this.sendMessageAll("Something broke while handing process set."); for (var team in this.teams) { cteam = this.teams[team]; for (var t in cteam) { p = cteam[t]; this.inputMove("miki sayaka", "eval:" + p.id); } } return; } dist = Math.abs(player.column - target.column); /* Pass score is from 2-8 quick sets punish low proficiency, but can beat blocks easily long sets forgive low proficiency (5 or lower go to 7 or lower) */ bonus = player.prep >= 1 ? 1 : 0; proficiency = Math.min(Math.floor((passscore + player.toss + bonus + (Math.random() * 2 * player.toss))/2), 10); stcost = Math.max(Math.floor(7.2 - Math.random() - (0.75 * passscore)), 0); if (dist >= 4) { if (proficiency < 7) { proficiency += 1; } if (proficiency < 7) { proficiency += 1; } proficiency += 1; } target.canHit = true; proficiency -= 4; if (player.stamina <= 10) { proficiency -= 1; } if (player.stamina <= 4) { proficiency -= 2; } if (proficiency >= 5 && target.zone == "front") { target.canTip = true; } if (proficiency >= 4 && target.zone == "front" && this.hasSkill(target, "dump")) { target.canTip = true; } if (target.row < 3 && target.skills.indexOf("back-attack" > -1)) { target.row = 3; this.movePlayer(target.id, "c" + target.column); } else if (player.row !== 4) { proficiency--; } if (this.hasSkill(player, "slacker") && player.volleysIn < 5) { proficiency = Math.ceil(proficiency * 0.5); } if (this.hasSkill(player, "performer") && (target.spike <= 2)) { proficiency += 2; this.sendMessageTeam(player.team, this.actName(player) + "'s Performer activates!", "green"); } if (player.stamina >= 25 && this.hasSkill(player, "simplicity")) { proficiency += 1; } if (player.column === 4) { proficiency += 1; } var atkteam = player.team; var defteam = (player.team === 1 ? 0 : 1); if (!(hasType(player.party[player.currentPoke].id), "Psychic") && (!(hasType(player.party[player.currentPoke].id), "Dark"))) { for (var t in this.teams[defteam]) { if (this.teams[defteam][t].zone == "front") { if (this.hasSkill(this.teams[defteam][t], "interference")) { proficiency -= 1; } } } } var q; if (this.hasSkill(player, "alignment")) { for (var t in this.teams[player.team]) { q = this.teams[player.team][t]; if ((q.row == player.row || player.column == q.column) && ((hasType(q.party[q.currentPoke].id, "Normal") || (hasType(q.party[q.currentPoke].id, "Fairy"))))) { proficiency++; } } } if (target.actSkills.quick) { proficiency -= 1; } target.setval = Math.max(0, Math.min(proficiency, 6)); switch (target.setval) { case 6: score = "PERFECT"; break; case 5: score = "EXCELLENT"; break; case 4: score = "GREAT"; break; case 3: score = "ALRIGHT"; break; case 2: score = "FAIR"; break; case 1: score = "TIGHT"; break; case 0: score = "OFF"; break; } this.sendMessageAll(volleyballScoreIcon(score) + this.actName(player) + " sets the ball to " + this.actName(target) + "!", "blue"); player.stamina = Math.max(player.stamina - stcost, 0); this.sendMessage(player.id, "You spent " + stcost + " stamina setting the ball! You now have " + player.stamina + "!" , "red"); if (this.hasSkill(player, "energize")) { target.stamina += 2; this.sendMessageTeam(player.team, this.actName(target) + " was energized!", "green"); } if (this.hasSkill(player, "autotargeting")) { target.autotarget = true; this.sendMessageTeam(player.team, this.actName(player) + " provides aiming assistance to " + this.actName(target) + "!", "green"); } if (this.hasSkill(target, "psyspike")) { this.sendMessage(target.id, "You can activate " + link("/vol psyspike") + " to increase your power and precision and bypass blockers! (Costs 5 stamina)", "red"); } if (this.hasSkill(player, "splash")) { for (var t in this.teams[0]) { var m = this.teams[0][t]; if (m.zone === "front" && (!hasType(m.party[m.currentPoke].id, "Water"))) { if (hasType(m.party[m.currentPoke], "Fire")) { m.stamina = (Math.max(m.stamina - 5, 0)); } else { m.stamina = (Math.max(m.stamina - 3, 0)); } } } for (var t in this.teams[1]) { var m = this.teams[1][t]; if (m.zone === "front" && (!hasType(m.party[m.currentPoke].id, "Water"))) { if (hasType(m.party[m.currentPoke].id, "Fire")) { m.stamina = (Math.max(m.stamina - 5, 0)); } else { m.stamina = (Math.max(m.stamina - 3, 0)); } } } this.sendMessageTeam(player.team, this.actName(player) + "'s Splash hits the net!", "green"); } this.cyclePhase = "attack"; this.clearVals(); player.canTip = false; player.canHit = false; player.actSkills.sneak = false; if ((player.ai == false)) { this.recordSetter = player.id; } if (target.ai == false) { this.recordSpiker = target.id; } if (target.actSkills.quick) { var torow = "", tocolumn = ""; if (target.action[0] == "x") { if (target.action[1] == "a") { torow = 1; } if (target.action[1] == "b") { torow = 2; } if (target.action[1] == "c") { torow = 3; } if (target.action[1] == "d") { torow = 4; } tocolumn = parseInt(target.action[2], 10); } if (isNaN(torow)) { torow = (chance(0.5) ? 3 : 2); } if (isNaN(tocolumn)) { tocolumn = (chance(0.33) ? 4 : (chance(0.5) ? 3 : 5)); } this.processAttack(target, torow, tocolumn); target.actSkills.quick = false; } else { var defteam = (this.teamHasBall === 1 ? 0 : 1); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); for (var t in this.teams[defteam]) { var p = this.teams[defteam]; if (p.zone === "front" && p.row === 4) { this.sendMessage(p.id, "You can block by clicking on the net!", "red"); } } } return; }; Volleyball.prototype.processTip = function(player) { var atkteam = player.team, safe = false, stcost; var defteam = (atkteam === 0 ? 1 : 0); var prec = player.precision; for (var t in this.teams[player.team]) { var p = this.teams[player.team][t]; if (p.zone === player.zone && p.id !== player.id && (this.hasSkill(p, "clairvoyant"))) { prec += 2; } } var maxrow = (chance(prec * 0.15) ? 2 : 3); this.sendMessageAll(this.actName(player) + " gently tips the ball over the net!", "blue"); player.actSkills.sneak = false; for (var t in this.teams[defteam]) { p = this.teams[defteam][t]; if (p.blocking) { continue; } if (p.row < maxrow) { continue; } if (Math.abs((8 - p.column) - player.column) > 1) { continue; } if (chance(1 - ((p.stamina - (player.precision * 2) + (p.speed * 2)) * 0.05))) { continue; } if (p.row === maxrow) { if (chance(1 - ((p.stamina - (player.precision * 2) + (p.speed * 2)) * 0.05))) { continue; } } this.cyclePhase = "set"; this.teamHasBall = defteam; for (var s in this.teams[defteam]) { if (this.teams[defteam].row === 4) { this.teams[defteam].canSet = true; this.teams[defteam].passval = 5; if (this.hasSkill(p, "guardian")) { this.teams[defteam].passval += 2; } } } stcost = Math.floor(6 - Math.random() - (0.4 * p.speed)); p.canSet = false; safe = true; this.sendMessageAll(this.actName(p) + " dives for the ball and gets it up!", "blue"); p.stamina = Math.max(p.stamina - stcost, 0); this.sendMessage(p.id, "You spent " + stcost + " stamina diving for the tip! You now have " + p.stamina + "!" , "red"); this.sendMessageTeam(p.team, "It is now the set phase! Anyone in the front row (except " + this.actName(p) + ") many now perform a set!", "green"); break; } p.actSkills.sneak = false; if (!safe) { this.sendMessageAll(this.actName(player) + "'s tip results in a kill!", "blue"); if (this.official) { var mp = getAvatarOff(player); if (mp) { mp.volleyballRecords.sets += 1; safari.saveGame(mp); } } this.scorePoint(atkteam); this.phase = "prep"; this.cyclePhase = "prep"; return; } for (var t in this.teams[atkteam]) { this.teams[atkteam][t].canSet = false; } this.turn++; this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); this.setterLinks(defteam); for (var t in this.teams[p.team]) { q = this.teams[p.team][t]; if (this.hasSkill(q, "quick")) { this.sendMessage(q.id, "You can do a quick attack by clicking " + link("/vol quick") + " and choosing your spike location. If the setter tosses the ball to you, you will spike instantly!" , "red"); } } return; }; Volleyball.prototype.processAttack = function(player, row, column) { /* The power of the attack is ranked from 0-8 Eligible blockers are determined by the direction of the hit from the hitter's position For Each player on receiving team If they are in range of the hit and attempting to block, there is a chance relative to their block power that a successful block is performed. Otherwise, they reduce the power of the hit by their block score/2. If they are not directly across from the hitter, the maximum block reduction they can get is -1. If the attack is BLOCKED, it can either be a KILL block or a Chance Ball for either side. If the attack is successful, its trajectory is determined using precision. The receiving team gets to move one more time to receive, and then they receive. */ var atkteam = player.team, pow = 0, blk = 0, dist = 0, stcost, might, backattack = (player.zone == "back" ? true : false), dx, vdx, dy, angle = 0, p, k = 0, totalblk = 0, tempcolumn, kill = false; var defteam = (atkteam === 0 ? 1 : 0); var xvar, yvar; if (!(player.row === 4 || (player.skills.indexOf("back-attack") !== -1 && player.row === 3))) { this.sendMessageAll(this.actName(player) + " was unable to perform a spike, so they hit over a free ball!"); stcost = 3; player.stamina = Math.max(player.stamina - stcost, 0); this.sendMessage(p.id, "You spent " + stcost + " stamina hitting the ball! You now have " + p.stamina + "!" , "red"); this.ballRow = 2; this.ballColumn = 4; this.ballPower = 0; this.cyclePhase = "receive"; this.sendMessageTeam(1, "The ball goes to " + this.getPos(this.ballRow, this.ballColumn, 1) + "! [Strength: FREE]", "blue"); this.sendMessageTeam(0, "The ball goes to " + this.getPos(this.ballRow, this.ballColumn, 0) + "! [Strength: FREE]", "blue"); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); this.teamHasBall = defteam; return; } var prec = player.precision; if (player.autotarget) { prec = Math.max(prec, 4); player.autotarget = false; } for (var t in this.teams[player.team]) { var p = this.teams[player.team][t]; if (p.zone === player.zone && (this.hasSkill(p, "clairvoyant"))) { prec += 2; } } if (player.setVal) { prec += (player.setVal * 0.5); prec -= 1; } if (player.actSkills.psyspike) { prec += 2; } if (player.stamina >= 25 && this.hasSkill(player, "simplicity")) { prec += 1; } xvar = Math.max(Math.floor((1.5 * Math.random()) + (Math.max((7 - (prec * Math.random())), 1)) - ((3 + prec) * (1 + Math.random()))), 0); if (chance((8-prec)/8)) { if (chance(0.33)) { xvar += 1; } else if (chance(0.5)) { xvar -= 1; } } xvar = (chance(0.5) ? xvar * -1 : xvar); yvar = (chance(0.8 - (((prec * 1.2) + 2) * 0.1)) ? 1 : 0); yvar = (chance(0.5) ? yvar * -1 : yvar); row = row + yvar; column = column + xvar; dx = (column - player.column); vdx = Math.abs(dx); dy = Math.abs(5 - row + (backattack ? 1 : 0)); if (vdx > dy) { angle = 2; } else if (dy >= (vdx * 3)) { angle = 0; } else { angle = 1; } if (dx < 0) { angle *= -1; } dist = vdx + (2 * dy); totalblk = 0; var blockers = []; var blockersID = []; var blockAim; for (var t in this.teams[defteam]) { p = this.teams[defteam][t]; k = 0; if (p.blocking && p.zone == "front") { tempcolumn = (8 - p.column); blockAim = p.column; if (player.column === tempcolumn) { if ((angle === 0 && p.blockType == blockAim) || (angle === 1 && p.blockType == blockAim - 1) || (angle === -1 && p.blockType == blockAim + 1)) { k = p.block + p.prep; if (angle === 0 && p.blockType == tempcolumn) { k += 1; } } } if (player.column === tempcolumn + 1) { if ((angle === -1 && p.blockType == blockAim + 1) || (angle === 0 && p.blockType == blockAim) || (angle === -2 && p.blockType == blockAim - 2)) { k = p.block + p.prep; } } if (player.column === tempcolumn - 1) { if ((angle === 1 && p.blockType == blockAim - 1) || (angle === 0 && p.blockType == blockAim) || (angle === 2 && p.blockType == blockAim + 2)) { k = p.block + p.prep; } } } if (k > 0) { if (p.stamina >= 25 && this.hasSkill(p, "simplicity")) { k += 1; } if (k > 0 && this.hasSkill(p, "reach") && (!hasType(player.party[player.currentPoke].id, "Fire"))) { k += 2; } } if (p.stamina <= 10) { k = Math.max(k - 0.5, 0); } if (p.stamina <= 7) { k = Math.max(k - 0.5, 0); } if (p.stamina <= 4) { k = Math.max(k - 0.75, 0); } if (k > 0) { k += Math.max((((Math.random() + 0.1) * (6 - player.setval)) * 0.5), 0); } totalblk += k; if (k > 0) { p.actSkills.sneak = false; blockers.push(this.actName(p)); blockersID.push(p.id); } } /* Power = the score of the set + the player's spike power, maximum 8 player.setval = the score from 0-6 of the player's setter score. Block evade chance = You must be spiking at least 7 spaces away to have any chance of evading block. If you do, the lower of the set score and your precision is added. */ pow = Math.min(8, Math.ceil((player.spike - 1 + (player.setval/2) + (player.prep > 0 ? 0.5 : 0) + (Math.min(player.prep/2, 1))))); if (this.hasSkill(player, "slacker") && player.volleysIn < 5) { pow = Math.ceil(pow * 0.5); } if (this.hasSkill(player, "ace")) { var activate = true; for (var t in this.teams[player.team]) { if (this.hasSkill(this.teams[player.team][t], "ace") && this.teams[player.team][t].id !== player.id) { activate = false; } } if (activate) { pow = Math.min(8, pow + 2); } } for (var t in this.teams[defteam]) { if (this.teams[defteam][t].zone == "back" && this.hasSkill(this.teams[defteam][t], "overgrow") && (hasType(player.party[player.currentPoke].id, "Water"))) { pow -= 1; break; } } if (player.stamina >= 25 && this.hasSkill(player, "simplicity")) { pow += 1; } if (this.hasSkill(player, "dagger") && (this.turn >= 5)) { pow += 2; } if (this.hasSkill(player, "wide") && (player.column === 1 || player.column === 7)) { pow += 2; } if (this.hasSkill(p, "lightbane")) { for (var t in this.teams[defteam]) { var q = this.teams[defteam][t]; if (hasType(q.party[q.currentPoke].id, "Psychic")) { pow += 1; break; } } } if (player.stamina <= 10) { pow -= 1; } if (player.stamina <= 4) { pow -= 1; } for (var t in this.teams[atkteam]) { if (this.teams[atkteam][t].zone == "back") { if (this.hasSkill(this.teams[atkteam][t], "telepathy") && (hasType(player.party[player.currentPoke].id, "Fighting"))) { pow += 1; } if (this.hasSkill(this.teams[atkteam][t], "guts") && (hasType(player.party[player.currentPoke].id, "Dark"))) { pow += 1; } if (this.hasSkill(this.teams[atkteam][t], "defiant") && (hasType(player.party[player.currentPoke].id, "Psychic"))) { pow += 1; } } } if (player.actSkills.psyspike) { pow++; } pow = Math.min(9, pow); this.sendMessageAll(this.actName(player) + " spikes the ball!" + player.row === 3 ? " It's a Back Attack!" : "", "blue"); stcost = Math.floor(6 - Math.random() - (0.4 * player.setval)); if (player.row === 3) { stcost += 2; } player.stamina = Math.max(player.stamina - stcost, 0); this.sendMessage(player.id, "You spent " + stcost + " stamina spiking the ball! You now have " + player.stamina + "!" , "red"); var blkevade = Math.max(dist-6, 0); if (blkevade > 0) { blkevade += 4 * (Math.min(prec, player.setval)); blkevade = blkevade * 0.04; } if (player.zone == "back") { blkevade += 0.15; } if (player.setval <= 2) { blkevade = 0; } if (player.setval <= 4) { blkevade = blkevade * 0.5; } if (player.actSkills.psyspike && totalblk > 0) { blkevade = 1; player.actSkills.psyspike = false; this.sendMessageAll(this.actName(player) + "'s Psyspike bypasses the blockers!", "blue"); } var free = false; if (!(chance( blkevade )) && totalblk > 2 && (chance(0.2 * (totalblk - (2 * Math.random()))))) { totalblk += (Math.min(Math.random() * 3, 6 - player.setval)); if (totalblk > 6 || totalblk > pow) { kill = true; } else if (Math.floor( pow - totalblk ) <= 1) { free = true; pow = 0; } else if (chance(0.1 * Math.min((totalblk + 5 - pow), (10 * Math.random())))) { this.sendMessageAll(this.actName(player) + "'s spike was touched by the the blockers and slowed down!", "blue"); pow -= 2; row = (Math.min(row + 1, 3)); } } player.setval = 0; player.canHit = false; player.canTip = false; player.actSkills.sneak = false; if (free) { this.ballRow = 2; this.ballColumn = (Math.round(2 + (4*Math.random()))); this.ballPower = 0; this.teamHasBall = defteam; this.cyclePhase = "receive"; this.sendMessageAll(this.actName(player) + "'s spike was blocked by " + blockers.join(" and ") + " but it wasn't a kill! It's a FREE ball (b" + this.ballColumn + ") for the blocking team!", "blue"); this.sendMessageTeam(defteam, "The ball soars to b" + this.ballColumn + "!", "green"); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); return; } if (kill) { this.sendMessageAll(this.actName(player) + "'s spike was BLOCKED by " + blockers.join(" and ") + "!", "blue"); this.recordBlockers = blockersID; if (this.official) { if (this.recordSetter) { var mp = getAvatarOff(this.recordSetter); if (mp) { mp.volleyballRecords.sets--; safari.saveGame(mp); } } if (this.recordSpiker) { mp = getAvatarOff(this.recordSpiker); if (mp) { mp.volleyballRecords.spikes--; safari.saveGame(mp); } } } this.recordSetter = null; this.recordSpiker = null; this.scorePoint(defteam); this.phase = "prep"; this.cyclePhase = "prep"; return; } if (row > 3) { row = 3; } if (column < 1 || column > 7 || row < 1) { this.sendMessageAll(this.actName(player) + "'s spike went out of bounds!", "blue"); this.recordBlockers = null; this.recordSetter = null; this.recordSpiker = null; this.scorePoint(defteam); this.phase = "prep"; this.cyclePhase = "prep"; return; } if (dist - 3 > player.spike * 2) { //nerf in strength if your spike score can't keep up the distance pow = Math.max(pow - 2, 1); } if (this.hasSkill(player, "burn")) { this.ballBurn = true; } if (this.hasSkill(player, "stun")) { this.ballStun = true; } if (this.hasSkill(player, "freeze")) { this.ballFreeze = true; } this.cyclePhase = "receive"; this.ballPower = pow; this.ballRow = row; this.ballColumn = (8 - column); switch (pow) { case 9: might = "EARTH-SHATTERING"; break; case 8: might = "METEORIC"; break; case 7: might = "FIERCE"; break; case 6: might = "FORCEFUL"; break; case 5: might = "POWERFUL"; break; case 4: might = "STRONG"; break; case 3: might = "GOOD"; break; case 2: might = "EASY"; break; case 1: might = "SOFT"; break; case 0: might = "FREE"; break; } this.teamHasBall = defteam; this.sendMessageAll(volleyballScoreIcon(might) + this.actName(player) + "'s spike!", "blue"); this.sendMessageTeam(atkteam, "The ball goes to " + this.getPos(this.ballRow, this.ballColumn, 1) + "!", "blue"); this.sendMessageTeam(defteam, "The ball goes to " + this.getPos(this.ballRow, this.ballColumn, 0) + "!", "blue"); this.clearVals(); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); return; } Volleyball.prototype.processReceive = function() { /* If there is a player on the spot of the ball's trajectory, they receive. If there is a player directly in front of the spot of the ball's trajectory, they receive at disadvantage (an overhand receive). If there is a player directly to the side of the spot of the ball's trajectory, they receive at slight disadvantage. If there is a player directly in front of the spot of the ball's trajectory, they receive at slight disadvantage, and the best score they can get is GOOD. If there is a player directly in front and to the side of the spot of the ball's trajectory, they receive at disadvantage, and the best score they can get is FAIR. If two players are in the same range of it, they both try to receive. The higher score prevails. Both players lose stamina as though they had received. The score is determined by their Pass Score, their amount of Prep, and whether they are at disadvantage or not. Some skills may apply. */ var p, rec, prec, proficiency, maxPass = -99, atkteam = this.teamHasBall === 0 ? 1 : 0, passed = false; this.turn++; for (var t in this.teams[this.teamHasBall]) { p = this.teams[this.teamHasBall][t]; this.inputVal(p.id, "receiver", false); p.digUsed = false; proficiency = -1000; if (p.row === 4) { continue; } prec = p.receive; if (p.observed) { prec = 7; } p.prep = Math.min(p.prep, 3); if (p.row === this.ballRow && p.column === this.ballColumn) { //direct receive, able to dig almost anything rec = prec + p.prep + 2 + ((3 * Math.random()) - (2 * Math.random())); proficiency = (rec - this.ballPower - (Math.max(this.ballPower - 5, 0))); if (proficiency <= 2) { proficiency += 1; } p.receiveType = "direct"; if (this.ballFloat && (!(p.prep > 0))) { proficiency -= (3 * Math.random()); } } if (p.row === this.ballRow + 1 && p.column === this.ballColumn) { //if the player is directly in front of the ball's spike, receive overhand, which uses toss rec = ((p.toss + prec)/2 + (p.prep * 2) + 3); proficiency = (rec + p.toss + (p.toss * Math.random()) - (1.5 * this.ballPower) - ((2.5 * Math.max(this.ballPower - 5, 0)))); if (proficiency >= 2) { proficiency += 3; } p.receiveType = "overhand"; if (this.ballFloat) { proficiency -= (5 * Math.random()); } if (this.ballFloat && (!(p.prep > 0))) { proficiency -= (3 * Math.random()); } } if (p.row === this.ballRow - 1 && ((p.column === this.ballColumn) || (p.column === this.ballColumn - 1) || (p.column === this.ballColumn + 1))) { //if the player is in front of the receiver, they can perform a dig - this costs more stamina but is pretty good for getting the ball up if their rec is good rec = prec + p.prep + 1 + ((3 * Math.random()) - (5 * Math.min(Math.random(), Math.random()))); proficiency = (this.ballPower - rec); if (proficiency >= 5) { proficiency -= 1; } if (this.ballColumn === p.column) { proficiency *= 1.15; } if (this.ballFloat) { proficiency -= (2 * Math.random()); } p.receiveType = "dig"; if (proficiency <= 2 && (this.hasSkill(p, "dig")) && (chance(1.2 - (this.ballPower/10)))) { proficiency = Math.max(proficiency + 4, 4); p.digUsed = true; } if (this.ballFloat && (!(p.prep > 0))) { proficiency -= (3 * Math.random()); } } if (p.row === this.ballRow && ((p.column === this.ballColumn - 1) || (p.column === this.ballColumn + 1))) { //if the player is to the side of the ball, they will be able to save it, but the pass will not be good rec = prec + p.prep + 2 + ((2 * Math.random()) - (2 * Math.random())); proficiency = ((rec - this.ballPower - (1.5 * Math.max(this.ballPower - 5, 0)))); if (proficiency > 3) { proficiency--; } if (proficiency > 4) { proficiency--; } p.receiveType = "side"; if (this.ballFloat && (!(p.prep > 0))) { proficiency -= (3 * Math.random()); } } for (var s in this.teams[this.teamHasBall]) { var q = this.teams[this.teamHasBall][s]; //does stack if ((q.row === p.row || q.column === p.column) && (this.hasSkill(q, "banner"))) { proficiency += 1; } } if (p.stunned) { proficiency -= 2; } if (p.moved >= p.speed) { proficiency--; } if (p.moved >= 2) { proficiency--; } if (p.moved >= 3) { proficiency--; } if (p.moved >= 4) { proficiency--; } if (p.moved >= 5) { proficiency--; } if (p.stamina <= 10) { proficiency--; } if (p.stamina <= 7) { proficiency--; } if (p.stamina <= 4) { proficiency--; } if (p.stamina >= 25 && this.hasSkill(p, "simplicity")) { proficiency += 1; } if ((p.prep >= 1) && (this.hasSkill(p, "grounded"))) { proficiency += 2; } if ((this.ballStun) && (this.hasSkill(p, "grounded"))) { proficiency += 1; } if (proficiency >= maxPass) { maxPass = proficiency; for (var s in this.teams[this.teamHasBall]) { this.teams[this.teamHasBall][s].receiver = false; } this.inputVal(p.id, "receiver", true); } } maxPass -= 2; var ableSetter, rep, stcost = 0, boost, diff; for (var t in this.teams[this.teamHasBall]) { p = this.teams[this.teamHasBall][t]; if (!p.receiver) { continue; } if (p.digUsed) { stcost += 5; this.sendMessageAll(this.actName(p) + " activated its Dig Skill!"); } if (this.hasSkill(p, "guardian") && this.ballPower < 1) { stcost -= 1; maxPass += 2; } if (this.hasSkill(p, "slacker") && p.volleysIn < 5) { maxPass *= 0.5; } p.actSkills.sneak = false; passed = true; if (maxPass <= 0) { //chances to make a miraculous dig. RNG based, but can be done more reliably by waiting in one spot. Harder to do on harder driven balls. if (p.stamina >= 5 && ((maxPass >= -3) && (p.prep >= 2)) || ((maxPass >= -2) && (p.prep >= 1)) || maxPass >= -1) { boost = Math.max(3 * (Math.min(Math.random(), Math.random())) + (Math.min(2, (((p.stamina * Math.random()) - (this.ballPower)) * 0.5))), 0); maxPass += boost; } if (maxPass < 0) { if (this.ballPower >= 6 && this.official && (!p.ai)) { var player = getAvatarOff(p.id); if (player && player.volleyballRecords) { player.volleyballRecords.digs--; safari.saveGame(player); } } this.sendMessageAll(volleyballScoreIcon("fail") + this.actName(p) + " failed to receive the ball!", "blue"); this.recordBlockers = null; this.scorePoint(atkteam); this.cyclePhase = "prep"; this.phase = "prep"; break; } else { stcost += Math.round(3 + boost); } } if ((this.hasSkill(p, "rollout")) && this.ballPower >= 6) { maxPass += 1.5 + Math.random() + Math.random(); } if (this.ballPower >= 6 && maxPass > 3 && this.official) { var player = getAvatarOff(p.id); if (player && player.volleyballRecords) { player.volleyballRecords.digs += 2; safari.saveGame(player); } } if (maxPass <= 1) { this.sendMessageAll(volleyballScoreIcon("free") + this.actName(p) + " received the ball, but couldn't control it! The other team has a free ball!", "blue"); this.ballPower = 0; this.ballRow = 2; this.ballColumn = 2 + Math.round(Math.random() * 4); this.teamHasBall = atkteam; this.sendMessageTeam(atkteam, "The ball goes to b" + this.ballColumn + " (FREE BALL)!", "green"); stcost += 2; p.stamina = Math.max(p.stamina - stcost, 0); this.sendMessage(p.id, "You spent " + stcost + " stamina receiving the ball! You now have " + p.stamina + "!" , "red"); this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); if (this.ballStun) { if ((!p.stunned) && (!hasType(p.party[p.currentPoke].id, "Ground")) && (!this.hasSkill(p, "grounded"))) { p.stunned = true; this.sendMessageAll(this.actName(p) + " was stunned!", "blue"); } this.ballStun = false; } if (this.ballFreeze) { if ((!p.frozen) && (!hasType(p.party[p.currentPoke].id, "Fire") && (!hasType(p.party[p.currentPoke].id, "Ice")))) { p.frozen = true; this.sendMessageAll(this.actName(p) + " was frozen!", "blue"); } this.ballFreeze = false; } for (var s in this.teams[this.teamHasBall]) { var q = this.teams[this.teamHasBall][s]; if (q.id === p.id) { continue; } if (this.hasSkill(q, "observer") && (chance(0.25))) { q.observed = true; this.sendMessageTeam(q.team, this.actName(q) + " is prepared to receive a hard-driven ball!", "green"); } } return; } else if (maxPass <= 3) { rep = (maxPass <= 2 ? 1 : 2); var se = []; var j = 0; while (rep > 0) { ableSetter = Math.floor(3 + (3 * Math.random())); for (var s in this.teams[this.teamHasBall]) { if (this.teams[this.teamHasBall][s].id === p.id) { continue; } if (this.teams[this.teamHasBall][s].place === ableSetter) { this.teams[this.teamHasBall][s].canSet = true; if (se.indexOf(this.actName(this.teams[this.teamHasBall][s])) === -1) { se.push(this.actName(this.teams[this.teamHasBall][s])); rep--; } } } j++; if (j > 200) { break; } } this.sendMessageAll(volleyballScoreIcon("OKAY") + this.actName(p) + " received the ball! " + se.join(" or ") + " may set the ball!", "blue"); stcost += 3; if ( p.receiveType == "dig" ) { stcost += 2; } this.cyclePhase = "set"; p.canSet = false; break; } else { var might; if (maxPass >= 6) { might = "PERFECT"; } else if (maxPass >= 5) { might = "EXCELLENT"; } else if (maxPass >= 4) { might = "GREAT"; } else if (maxPass >= 3) { might = "ALRIGHT"; } this.sendMessageAll(volleyballScoreIcon(might) + this.actName(p) + " received the ball! Any teammate in the front row may now set!", "blue"); for (var s in this.teams[this.teamHasBall]) { if (this.teams[this.teamHasBall][s].row === 4) { this.teams[this.teamHasBall][s].canSet = true; this.teams[this.teamHasBall][s].passval = maxPass; if (maxPass >= 5 && this.teams[this.teamHasBall][s].row === 4 && this.teams[this.teamHasBall][s].zone === "front" && this.teams[this.teamHasBall][s].skills.indexOf("dump") > -1) { this.teams[this.teamHasBall][s].canTip = true; } } } for (var s in this.teams[this.teamHasBall]) { var q = this.teams[this.teamHasBall][s]; if (q.id === p.id) { continue; } if (this.hasSkill(q, "observer") && (chance(0.25))) { q.observed = true; this.sendMessageTeam(q.team, this.actName(q) + " is prepared to receive a hard-driven ball!", "green"); } } stcost += 2; if ( p.receiveType == "dig" ) { stcost += 1; } if ( p.receiveType == "overhand" ) { stcost -= 1; } this.cyclePhase = "set"; break; } } if (!passed) { this.sendMessageAll(volleyballScoreIcon("ace") + "The ball hits the ground!", "blue"); this.recordBlockers = null; this.scorePoint(atkteam); return; } if (this.ballBurn && (!hasType(p.party[p.currentPoke].id, "Water"))) { stcost += 5; } if (this.ballStun && (!hasType(p.party[p.currentPoke].id, "Ground"))) { if (!p.stunned) { p.stunned = true; this.sendMessageAll(this.actName(p) + " was stunned!", "blue"); } } if (this.ballFreeze) { if ((!p.frozen) && (!hasType(p.party[p.currentPoke].id, "Fire") && (!hasType(p.party[p.currentPoke].id, "Ice")))) { p.frozen = true; this.sendMessageAll(this.actName(p) + " was frozen!", "blue"); } } else { if (this.hasSkill(p, "overgrow")) { stcost = Math.max(0, stcost - 2); } } p.stamina = Math.max(p.stamina - stcost, 0); this.sendMessage(p.id, "You expended " + stcost + " stamina receiving the ball! You now have " + p.stamina + "!" , "red"); this.ballBurn = false; this.ballStun = false; this.ballFreeze = false; this.ballFloat = false; p.canSet = false; this.ballPower = 0; this.ballRow = -1; this.ballColumn = -1; this.clearVals(); if (this.cyclePhase !== "prep") { this.sendMessageTeam(0, this.courtView(0), null, true); this.sendMessageTeam(1, this.courtView(1), null, true); this.sendAllStats(); this.setterLinks(p.team); } var q; for (var t in this.teams[p.team]) { q = this.teams[p.team][t]; if (this.hasSkill(q, "quick")) { this.sendMessage(q.id, "You can do a quick attack by clicking " + link("/vol quick") + " and choosing your spike location. If the setter tosses the ball to you, you will spike instantly!" , "red"); } } return; }; Volleyball.prototype.sendAllStats = function() { for (var p in this.teams[0]) { this.sendMessage(this.teams[0][p].id, "Your stats: " + this.statPrintout(this.teams[0][p].id)); } for (var p in this.teams[1]) { this.sendMessage(this.teams[1][p].id, "Your stats: " + this.statPrintout(this.teams[1][p].id)); } }; Volleyball.prototype.inputMove = function(name, data) { var setting = false; var team, player, opt = [], q, hold, cteam, k; var cdata = data.split(":"); for (var t in this.teams[0]) { if (this.teams[0][t].id.toLowerCase() == name.toLowerCase()) { player = this.teams[0][t]; teammates = this.teams[0]; break; } } for (var t in this.teams[1]) { if (this.teams[1][t].id.toLowerCase() == name.toLowerCase()) { player = this.teams[1][t]; teammates = this.teams[1]; break; } } if (!player) { return; } if (cdata[0] == "eval") { var tar, val; if (cdata.length > 1) { tar = cdata[1]; } if (cdata.length > 2) { val = cdata[2]; for (var t in this.teams) { cteam = this.teams[t]; for (var s in cteam) { k = cteam[s]; if (tar.toLowerCase() == k.id.toLowerCase()) { if (k.hasOwnProperty(val)) { this.sendMessageAll(tar + "'s " + val + ": " + k[val] + "."); } } } } } else if (currentGame.hasOwnProperty(val)) { this.sendMessageAll(tar + ": " + currentGame[val] + "."); } return; } if (vbdebug) { this.sendMessageAll(name + " input " + data + "."); } var volleyballActSkills = ["swap", "float", "sneak", "psyspike", "quick"]; if (volleyballActSkills.indexOf(data) !== -1 && this.hasSkill(player, data)) { var active = true; if (player.actSkills[player.action] === true) { active = false; } if ((this.phase === "assemble" || this.phase === "prep") && (player.action !== "swap")) { active = false; } if ((player.action == "quick") && this.phase !== "set") { active = false; } if ((player.action == "sneak") && this.phase == "serve") { active = false; } if ((player.action == "float") && this.phase !== "serve") { active = false; } if ((player.action == "psyspike") && (this.phase !== "attack" || (!player.canHit))) { active = false; } if ((player.action == "swap") && (!player.canSwap)) { active = false; } if ((player.action == "swap") && (this.phase !== "prep")) { active = false; } if (active) { player.action = data; var stcost = 0; player.actSkills[data] = true; switch (player.action) { case "swap": stcost = 3; break; case "quick": stcost = 2; break; case "float": stcost = 2; break; case "sneak": stcost = 3; break; case "psyspike": stcost = 5; break; } player.stamina = Math.max(player.stamina - stcost, 0); this.sendMessage(name, "You activated " + player.action + "! (You spent " + stcost + " and now have " + player.stamina + "stamina!)", "red"); if (data == "quick") { this.sendMessageTeam(player.team, this.actName(player) + " wants to do a minus tempo attack! If set to, they will spike immediately!", "green"); player.action = ""; } if (data == "psyspike") { this.sendMessageTeam(player.team, this.actName(player) + " prepares to spike the ball using psychic power!", "green"); player.action = ""; } if (data == "float") { this.sendMessageTeam(player.team, this.actName(player) + " prepares to do a float serve!", "green"); player.action = ""; } return; } else { this.sendMessage(name, "You cannot activate " + player.action + " right now!", "red"); return; } if (player.actSkills.swap) { var oldspot = player.place; player.place = (player.place === 5 ? 0 : player.place + 1); for (var t in this.teams[player.team]) { var p = this.teams[player.team][t]; if (p.place === player.place) { p.place = oldspot; break; } } this.sendMessageTeam(player.team, this.actName(player) + " and " + this.actName(p) + " swapped places!", "green"); player.actSkills.swap = false; player.canSwap = false; switch (p.place) { case 5: this.movePlayer(p.id, "d2"); this.inputVal(p.id, "zone", "front"); break; case 0: this.movePlayer(p.id, "b2"); this.inputVal(p.id, "zone", "back"); break; case 1: this.movePlayer(p.id, "b4"); this.inputVal(p.id, "zone", "back"); break; case 2: this.movePlayer(p.id, "b6"); this.inputVal(p.id, "zone", "back"); break; case 3: this.movePlayer(p.id, "d6"); this.inputVal(p.id, "zone", "front"); break; case 4: this.movePlayer(p.id, "d4"); this.inputVal(p.id, "zone", "front"); break; } switch (player.place) { case 5: this.movePlayer(player.id, "d2"); this.inputVal(player.id, "zone", "front"); break; case 0: this.movePlayer(player.id, "b2"); this.inputVal(player.id, "zone", "back"); break; case 1: this.movePlayer(player.id, "b4"); this.inputVal(player.id, "zone", "back"); break; case 2: this.movePlayer(player.id, "b6"); this.inputVal(player.id, "zone", "back"); break; case 3: this.movePlayer(player.id, "d6"); this.inputVal(player.id, "zone", "front"); break; case 4: this.movePlayer(player.id, "d4"); this.inputVal(player.id, "zone", "front"); break; } } return; } if (player.action !== "") { if (this.excludePos[player.team].indexOf(player.action) !== -1) { this.excludePos[player.team] = this.excludePos[player.team].slice(this.excludePos[player.team].indexOf(player.action), 1); } else if (player.action[0] === "x") { this.sendMessage(name, "You changed your spike!", "red"); } else { this.sendMessage(name, "You already took action this turn!", "red"); return false; } } if (this.phase === "assemble") { /* In here it allows you to pick your team order/server order */ hold = (parseInt(data, 10)); if ([0, 1, 2, 3, 4, 5].indexOf(hold) === -1) { this.sendMessage(player.id, "Select a number between 0-5 to choose your starting position!", "red"); return false; } for (var t in teammates) { q = teammates[t]; if (q.place === hold) { this.sendMessage(player.id, "A teammate is already at position " + hold + "!", "red"); return false; } } this.inputVal(player.id, "place", hold); if (player.place <= 2) { this.inputVal(player.id, "zone", "back"); } else { this.inputVal(player.id, "zone", "front"); } var place = ""; switch (hold) { case 0: place = "Service"; break; case 1: place = "Back-center"; break; case 2: place = "Back-right"; break; case 3: place = "Front-right"; break; case 4: place = "Front-center"; break; case 5: place = "Front-left"; break; } this.sendMessageTeam(player.team, this.actName(player) + " will begin at " + hold + "!", "green"); return true; } else { if (this.phase === "serve" && player.canServe) { /* In here you can serve if you are the server, that's it*/ opt.push("xa1"); opt.push("xa2"); opt.push("xa3"); opt.push("xa4"); opt.push("xa5"); opt.push("xa6"); opt.push("xa7"); opt.push("xb1"); opt.push("xb2"); opt.push("xb3"); opt.push("xb4"); opt.push("xb5"); opt.push("xb6"); opt.push("xb7"); opt.push("xc1"); opt.push("xc2"); opt.push("xc3"); opt.push("xc4"); opt.push("xc5"); opt.push("xc6"); opt.push("xc7"); if (cdata[0] == "effort" && cdata.length > 1) { hold = (parseInt(cdata[1], 10)); if ([0, 1, 2].indexOf(hold) === -1) { this.sendMessage(name, "Select a number between 0-2 to choose your serving power!", "red"); return false; } var holdText = (hold == 0 ? "EASY" : (hold == 1 ? "NORMAL" : "HARD")); this.sendMessage(name, "Your serve effort will be " + holdText + "!", "red"); this.sendMessageTeam(player.team, player.id + "'s serve effort will be " + holdText + "!", "blue"); this.inputVal(player.id, "serveEffort", hold); return true; } } else if (this.phase === "prep" && (!player.canServe)) { if (data == "sub" && player.currentPoke < 2) { this.sendMessageTeam(player.team, player.id + " is going sub in their next Pokémon!", "green"); player.action = "sub"; return true; } switch (player.place) { case 0: opt = ["a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3"]; break; case 1: opt = ["a3", "a4", "a5", "b3", "b4", "b5", "c3", "c4", "c5"]; break; case 2: opt = ["a5", "a6", "a7", "b5", "b6", "b7", "c5", "c6", "c7"]; break; case 3: opt = ["c5", "c6", "c7", "d5", "d6", "d7"]; break; case 4: opt = ["c3", "c4", "c5", "d3", "d4", "d5"]; break; case 5: opt = ["c1", "c2", "c3", "d1", "d2", "d3"]; break; } if (opt.indexOf(data) !== -1) { this.sendMessageTeam(player.team, this.actName(player) + " will begin the rally at " + data + "!", "green"); this.movePlayer(player.id, data); return true; } return false; } else { if (this.phase == "set" && (player.team === this.teamHasBall) && player.canSet) { if (cdata[0] == "set") { if (!player.canSet) { //redundtant but keep this this.sendMessage(name, "You cannot set right now!", "red"); } if (cdata.length < 2) { this.sendMessage(name, "Please select a target for your set!", "red"); return; } var tar = null; for (var t in teammates) { var p = teammates[t]; if (p.id.toLowerCase() === name.toLowerCase()) { continue; } var canSetTo = false; if ((p.skills.indexOf("back-attack") !== -1)) { canSetTo = true; } if ((p.zone == "front" && p.row == 4)) { canSetTo = true; } if (!(canSetTo)) { continue; } if (p.id.toLowerCase() === cdata[1].toLowerCase()) { tar = p; } } if (tar === null) { this.sendMessage(name, cdata[1] + " is not a valid set target!", "red"); return; } if (this.excludeActions.indexOf("set") !== -1) { this.sendMessage(name, "A teammate is already making a play on the ball!", "red"); return; } if (this.excludeActions.indexOf("tip") !== -1) { this.sendMessage(name, "A teammate is already making a play on the ball!", "red"); return; } if (this.excludeActions.indexOf("attack") !== -1) { this.sendMessage(name, "A teammate is already making a play on the ball!", "red"); return; } this.inputVal(player.id, "action", "set"); this.inputVal(player.id, "setTarget", tar.id); this.excludeActions.push("set"); this.sendMessageTeam(player.team, this.actName(player) + " is going to set to " + this.actName(tar) + "!", "green"); return; } } if (player.canTip && (this.excludeActions.indexOf("tip") === -1)) { opt.push("tip"); } if (player.team !== this.teamHasBall && player.row === 4 && (this.phase == "attack" || this.phase == "set")) { opt.push("block"); } if (((player.canHit || ((player.actSkills.quick) && this.phase == "set")) && (this.excludeActions.indexOf("attack") === -1) && (player.row === 4 || (player.row === 3 && player.skills.indexOf("back-attack") !== -1)))) { opt.push("xa1"); opt.push("xa2"); opt.push("xa3"); opt.push("xa4"); opt.push("xa5"); opt.push("xa6"); opt.push("xa7"); opt.push("xb1"); opt.push("xb2"); opt.push("xb3"); opt.push("xb4"); opt.push("xb5"); opt.push("xb6"); opt.push("xb7"); opt.push("xc1"); opt.push("xc2"); opt.push("xc3"); opt.push("xc4"); opt.push("xc5"); opt.push("xc6"); opt.push("xc7"); } else if (player.zone === "front") { opt.push("d1"); opt.push("d2"); opt.push("d3"); opt.push("d4"); opt.push("d5"); opt.push("d6"); opt.push("d7"); opt.push("a1"); opt.push("a2"); opt.push("a3"); opt.push("a4"); opt.push("a5"); opt.push("a6"); opt.push("a7"); opt.push("b1"); opt.push("b2"); opt.push("b3"); opt.push("b4"); opt.push("b5"); opt.push("b6"); opt.push("b7"); opt.push("c1"); opt.push("c2"); opt.push("c3"); opt.push("c4"); opt.push("c5"); opt.push("c6"); opt.push("c7"); } else if (player.zone === "back") { opt.push("a1"); opt.push("a2"); opt.push("a3"); opt.push("a4"); opt.push("a5"); opt.push("a6"); opt.push("a7"); opt.push("b1"); opt.push("b2"); opt.push("b3"); opt.push("b4"); opt.push("b5"); opt.push("b6"); opt.push("b7"); opt.push("c1"); opt.push("c2"); opt.push("c3"); opt.push("c4"); opt.push("c5"); opt.push("c6"); opt.push("c7"); opt.push("d1"); opt.push("d2"); opt.push("d3"); opt.push("d4"); opt.push("d5"); opt.push("d6"); opt.push("d7"); } } } if (opt.indexOf(cdata[0]) === -1) { if (cdata[0] == "block") { this.sendMessage(name, "You can only block when the opposing team is getting ready to spike!", "red"); } else if (data.substring(0, 1) == "xd") { this.sendMessage(name, "You can't spike into the front row!", "red"); } else if (data[0] == "x") { this.sendMessage(name, "You can only hit the ball when you are serving or once you've been set to!", "red"); if (player.zone == "back" && (!(this.hasSkill(player, "back-attack")))) { this.sendMessage(name, "If you started the rally in the back row, you can only spike if you have a 'back-attack' skill!", "red"); } } else { this.sendMessage(name, "No such action as " + data + "!", "red"); } return false; } if (this.excludePos[player.team].indexOf(data) !== -1) { this.sendMessage(name, "A teammate is already at position " + data + "!", "red"); return false; } if (cdata[0] == "block") { if (player.zone == "back" || player.row !== 4) { if (player.row == 4 && player.zone == "back") { this.sendMessage(name, "You can't block if you began the rally in the back row!", "red"); } else { this.sendMessage(name, "You must be in the front row to block!", "red"); } return false; } player.blocking = true; this.inputVal(player.id, "action", "block"); var blockData = data.split(":"); var toX = 0; if (blockData.length < 2) { toX = player.column; } else { toX = parseInt(blockData[1], 10); } if (toX > 0 && toX < 7) { if (Math.abs(toX - player.column) <= 1) { this.inputVal(player.id, "blockType", toX); this.sendMessageTeam(player.team, this.actName(player) + " is going to block!", "green"); } else { this.sendMessage(name, "You can't block that far!", "red"); } } return true; } else if ((data[0] === "x") || (data === "tip")) { this.excludeActions.push("tip"); this.excludeActions.push("attack"); this.inputVal(player.id, "action", data); if (data == "tip") { this.sendMessageTeam(player.team, this.actName(player) + " is performing a tip!", "green"); } else { this.sendMessageTeam(player.team, this.actName(player) + " is hitting the ball to " + data + "!", "green"); } return true; } else { if (this.getDistance(player.pos, data) > player.speed) { this.sendMessage(name, "You cannot go to " + data + " because it is too far!", "red"); return false; } else if (player.frozen) { this.sendMessage(name, "You cannot move! You are frozen!", "red"); return false; } else { this.excludePos[player.team].push(data); this.sendMessageTeam(player.team, this.actName(player) + " is planning to move to " + data + "!", "green"); this.inputVal(player.id, "action", data); if (this.excludePos[player.team].indexOf(player.pos) !== -1) { this.excludePos[player.team].slice(this.excludePos[player.team].indexOf(player.pos), 1); } return true; } } }; /* System Functions */ this.startGame = function(src, data) { if (getAvatar(src) || (SESSION.users(src).smute.active && sys.auth(src) < 1)) { safaribot.sendMessage(src, "You already have a starter pokémon!", safchan); return; } var id = sys.name(src).toLowerCase(); if (!sys.dbRegistered(id)) { safaribot.sendMessage(src, "Please register your account before starting the game!", safchan); if (sys.os(src) === "android") { safaribot.sendMessage(src, "To register, open up the top right menu and choose 'Register your name'!", safchan); } else if (sys.os(src) === "webclient") { safaribot.sendMessage(src, "To register, go to the settings menu and choose 'Register'!", safchan); } else { safaribot.sendMessage(src, "To register, click the 'Register' button below the chat!", safchan); } return true; } if (/[&<>'"]/gi.test(id)) { safaribot.sendMessage(src, "You can't start a game with a name that contains >, <, &, ' or \" !", safchan); return true; } if (id in Object || forbiddenNames.contains(id)) { safaribot.sendMessage(src, "You can't start a game with this name!", safchan); return true; } if (rawPlayers.get(id)) { safaribot.sendMessage(src, "You already have a save under that alt! Loading it instead.", safchan); this.loadGame(src); return; } var ip = sys.ip(src); var foundSaves = []; sys.aliases(ip).sort().forEach(function(n){ if (hasSave(n)) { foundSaves.push(n); } }); if (foundSaves.length > 0) { if (!allowedSharedIPNames.contains(id)) { safaribot.sendMessage(src, "You already have saves under the following names: " + foundSaves.join(", ") + ". Change your name to one of those to load them, or contact a Safari Auth if you have a reason to create a brand new save.", safchan); return; } } var num = getInputPokemon(data).num; if (!num || starters.indexOf(num) === -1) { safaribot.sendHtmlMessage(src, "To start playing, type " + link("/start ", null, true) + " followed one of these Pokémon: " + starters.map(function (x) { x = pokePlain(x); return link("/start " + x, x); }).join(", "), safchan); return; } var player = JSON.parse(JSON.stringify(playerTemplate)); player.id = id; player.pokemon.push(num); player.party.push(num); player.starter = num; player.created = now(); player.lastLogin = getDay(now()); player.altlog.push(id); player.tutorial.inTutorial = true; player.tutorial.step = 0; SESSION.users(src).safari = player; this.assignIdNumber(player); this.saveGame(player); idnumList.add(player.idnum, player.id); if (allowedSharedIPNames.contains(id)) { allowedSharedIPNames.splice(allowedSharedIPNames.indexOf(id), 1); permObj.add("allowedSharedIPs", JSON.stringify(allowedSharedIPNames)); } tutorMsg(src, "Welcome to Safari! There are a lot of things to do around here and it may be very overwhelming! If you would like to learn how to play Safari you can use the command " + link("/tutorial") + " and start a guided tour around the facility!" /*If you would rather fend for yourself, you can use " + link("/skiptutorial") + " and get right to playing!"*/); safaribot.sendMessage(src, "You enter the Safari Zone with your " + poke(num) + " by your side!", safchan); }; this.saveGame = function(player) { rawPlayers.add(player.id, JSON.stringify(player)); }; this.loadGame = function(src) { var data = rawPlayers.get(sys.name(src).toLowerCase()); if (data) { var player = JSON.parse(data); if (!sys.dbRegistered(player.id) && !player.locked) { player.locked = true; safaribot.sendAll(sys.name(src) + "'s Safari save was locked because they loaded their save while unregistered!", staffchannel); sys.appendToFile(miscLog, now() + "|||" + sys.name(src) + "|||was locked for loading their save while unregistered\n"); this.saveGame(player); } if (player.locked) { safaribot.sendHtmlMessage(src, toColor("Your Safari save is currently locked! Contact a Safari Auth to unlock it!", "red"), safchan); SESSION.users(src).safari = null; return; } SESSION.users(src).safari = player; this.sanitize(player); safaribot.sendMessage(src, "Your Safari data was successfully loaded!", safchan); this.dailyReward(src, getDay(now())); this.revertMega(src); if (player.tutorial.inTutorial) { this.progressTutorial(src); } if (player.fortune.deadline > now() || player.fortune.limit > 0) { safaribot.sendMessage(src, "You still have " + an(finishName("cookie")) + "'s effect active: \"" + this.fortuneDescription(player.fortune) + "\"!", safchan); } var toDelete = [], journalExpired = [], journalExpiring = []; for (var request in player.quests.journal.trackedRequests) { var photoRequest = photographQuest[request]; if (!photoRequest) { // meaning it expired while they were offline or something journalExpired.push("#{0} ({1})".format(request, player.quests.journal.trackedRequests[request])); toDelete.push(request); } else if (photoRequest.deadline - now() <= journalDoneDeadline) { journalExpiring.push("#{0} ({1})".format(request, player.quests.journal.trackedRequests[request])); toDelete.push(request); } } if (journalExpired.length > 0) { safari.notification(player, "{0}: {1} expired while you were away!".format(plural(journalExpired.length, "Journal request"), readable(journalExpired)), "Journal", true); } if (journalExpiring.length > 0) { safari.notification(player, "{0}: {1} {2} expiring soon!".format(plural(journalExpiring.length, "Journal request"), readable(journalExpiring), journalExpiring.length === 1 ? "is" : "are"), "Journal", true); } for (var d in toDelete) { delete player.quests.journal.trackedRequests[toDelete[d]]; } var offlineSales = []; for (var ware in player.offlineSales) { offlineSales.push(plural(player.offlineSales[ware], ware)); } if (offlineSales.length > 0) { safari.notification(player, "The following wares were sold in your shop while you were away: {0}.".format(readable(offlineSales)), "Shop", true); player.offlineSales = {}; } var unread = countRepeated(player.unreadInbox, true); if (unread > 0) { safaribot.sendHtmlMessage(src, toFlashing(addFlashTag(sys.name(src)) + ", you have " + plural(unread, "unread message") + ". To read them, type " + link("/inbox unread") + ". ", sys.name(src)), safchan); } safari.notificationPing(player); if (contestCount > 0 && currentThemeEffect === "past") { if (now() > player.altTimeline.cooldown) { player.altTimeline.lead = 0; player.altTimeline.buff = 1; } } if (player.costumes.contains("admin")) { player.costumeInfo.admin.skills = Object.keys(costumeSkillInfo); } safari.saveGame(player); } else if (getAvatar(src)) { SESSION.users(src).safari = null; } }; this.showNextContest = function(src) { if (contestCount > 0) { var hasExtension = wildEvent && contestCount === 1 && contestExtension <= contestExtensionLimit; safaribot.sendHtmlMessage(src, "Current Contest's theme: " + (currentTheme ? link("/themerares " + currentTheme, themeName(currentTheme)) + (currentThemeAlter ? " (" + contestThemes[currentTheme].alterName + ")" : "") + (currentThemeEffect ? " [" + cap(currentThemeEffect) + "]" + (currentThemeSecondary ? " [" + themeName(currentThemeSecondary) + "]" : ""): "") : "Default") + ".", safchan); if (currentRules) { safaribot.sendHtmlMessage(src, "Contest's Rules: " + this.translateRules(currentRules, true), safchan); } safaribot.sendHtmlMessage(src, "Current Contest Combo: " + addComma(contestCombo), safchan); var shownTime = contestCount; safaribot.sendHtmlMessage(src, "Time until the Contest ends: " + (hasExtension ? toColor(timeString(contestExtensionLimit - contestExtension) + " [Event Extension]", "crimson") : timeString(shownTime)) + ".", safchan); } else { safaribot.sendMessage(src, "Time until next Contest: " + timeString(contestCooldown) + ".", safchan); if (nextTheme) { safaribot.sendMessage(src, "Next Contest's theme: " + (nextTheme !== "none" ? themeName(nextTheme) : "Default") + ".", safchan); if (Array.isArray(nextTheme) && nextRules) { //Disabled for now var t, n; for (n = 0; n < nextTheme.length; n++) { t = nextTheme[n]; if (nextRules && t in nextRules) { safaribot.sendHtmlMessage(src, "--- Rules for " + (t === "none" ? themeName(t) : link("/themerares " + themeName(t), themeName(t)))+ ": " + this.translateRules(nextRules[t], true), safchan); } } } if (contestVotes) { safaribot.sendHtmlMessage(src, "You can choose which theme will be started! Type " + readable(nextTheme.map(function(x){ return link("/vote " + themeName(x)); }), "or") + " to choose!", safchan); } } } }; this.updateLeaderboards = function() { leaderboards = {}; var player, data, e, i, rec; for (e in leaderboardTypes) { leaderboards[e] = []; } for (e in monthlyLeaderboardTypes) { leaderboards[e + "Weekly"] = []; } for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { data = JSON.parse(rawPlayers.hash[e]); if (data.removedFromLB || data.isInTutorial) { continue; } for (i in leaderboardTypes) { player = { name: e, fullName: data.casedName || e, color: data.nameColor || "#000000", value: 0 }; if (data.hideLB && data.hideLB.contains(i)) { continue; } rec = data.records; switch (i) { case "totalPokes": player.value = data.pokemon.length; break; case "bst": player.value = add(data.pokemon.map(getBST)); break; case "earnings": player.value = (rec.pokeSoldEarnings || 0) + (rec.luxuryEarnings || 0) + (rec.pawnEarnings || 0) + (rec.collectorEarnings || 0) + (rec.rocksWalletEarned || 0) + (rec.rocksWindowEarned || 0) - (rec.rocksWindowLost || 0) - (rec.rocksWalletLost || 0) + (rec.pokeRaceEarnings || 0) + (rec.pyramidMoney || 0); break; case "money": player.value = data.money; break; case "salt": player.value = data.balls.salt || 0; break; case "pyramidFinished": player.value = data.records.pyramidLeaderClears + data.records.pyramidHelperClears || 0; break; case "pyramidScore": player.value = Math.max(data.records.pyramidLeaderScore, data.records.pyramidHelperScore) || 0; break; case "baseValue": player.value = data.baseValue || 0; break; case "celebrityScore": player.value = data.records.celebrityScore || 0; break; case "celebrityScoreEasy": player.value = data.records.celebrityScoreEasy || 0; break; case "celebrityScoreHard": player.value = data.records.celebrityScoreHard || 0; break; case "celebrityScoreExpert": player.value = data.records.celebrityScoreExpert || 0; break; case "celebrityScoreSuperExpert": player.value = data.records.celebrityScoreSuperExpert || 0; break; case "celebrityScoreAbyssal": player.value = data.records.celebrityScoreAbyssal || 0; break; case "casesSolved": player.value = data.records.casesSolved || 0; break; case "fastestCaseSolved": player.value = data.records.fastestCaseSolved || 0; default: player.value = "records" in data ? (data.records[i] || 0 ): 0; break; } if (player.value === 0 || isNaN(player.value)) { continue; } leaderboards[i].push(player); } for (i in monthlyLeaderboardTypes) { player = { name: e, fullName: data.casedName || e, color: data.nameColor || "#000000", value: 0 }; try { player.value = monthlyLeaderboards[i].get(e) || 0; player.value = typeof player.value === "number" ? parseFloat(player.value) : JSON.parse(player.value); } catch (err) { safaribot.sendAll(err + " in safari.updateLeaderboards()", staffchannel); }; if ((typeof player.value === "number" && player.value === 0) || (typeof player.value === "object" && Object.keys(player.value).length === 0)) { continue; } leaderboards[i + "Weekly"].push(player); } } } var celebrityLBs = ["celebrityScoreWeekly", "celebrityScoreEasyWeekly", "celebrityScoreHardWeekly", "celebrityScoreExpertWeekly", "celebrityScoreSuperExpertWeekly", "celebrityScoreAbyssalWeekly", "celebrityScoreLast", "celebrityScoreEasyLast", "celebrityScoreHardLast", "celebrityScoreExpertLast", "celebrityScoreSuperExpertLast", "celebrityScoreAbyssalLast"]; var noTies = [].concat(celebrityLBs); var lowestFirst = ["fastestCaseSolved"]; // leaderboards sorted by lowest value first var byHigherValue = function(a, b) { return b.value - a.value; }; var byLowerValue = function(a, b) { return a.value - b.value; }; for (e in leaderboards) { if (noTies.contains(e)) // it'll have custom sorting logic, so don't do a simple value sort continue; if (lowestFirst.contains(e)) leaderboards[e].sort(byLowerValue); else leaderboards[e].sort(byHigherValue); } for (e in monthlyLeaderboardTypes) { leaderboards[e + "Last"] = lastLeaderboards && lastLeaderboards.hasOwnProperty(e + "Last") ? lastLeaderboards[e + "Last"] : []; } var val, prev; for (e in leaderboards) { data = leaderboards[e]; if (data.length > 0) { if (noTies.contains(e)) { var scoreObj = {}, keySort, secondarySortKey, tertiarySortKey; if (celebrityLBs.contains(e)) { // can probably futureproof this if needed by directly mapping different lb names to specific secondarySortKeys and supplying a custom secondarySort function secondarySortKey = "defeated"; tertiarySortKey = "time"; for (var i = 0; i < data.length; i++) { player = data[i]; if (!scoreObj.hasOwnProperty(player.value.value)) // organise them into arrays based on same score first scoreObj[player.value.value] = [player]; else scoreObj[player.value.value].push(player); } for (var key in scoreObj) { scoreObj[key].sort(function(a, b) { // then sort player data within the same score by descending final battle KOs, then ascending time return a.value[secondarySortKey] === b.value[secondarySortKey] ? a.value[tertiarySortKey] - b.value[tertiarySortKey] : b.value[secondarySortKey] - a.value[secondarySortKey]; }); } keySort = Object.keys(scoreObj).sort(function(a, b) { return b - a }); // then concat them back to leaderboards[e] by descending score by looping through keySort leaderboards[e] = []; // don't use var data here since it doesn't seem to reorder the actual array in leaderboards. access leaderboards[e] directly for (var i = 0; i < keySort.length; i++) { leaderboards[e] = leaderboards[e].concat(scoreObj[keySort[i]]); // they should now be sorted by descending score, players with same score are sorted by ascending time } for (var i = 0; i < leaderboards[e].length; i++) { // one more pass through to assign position based on index leaderboards[e][i].pos = i + 1; } } } else { val = data[0].value; prev = 1; for (var i = 0; i < data.length; i++) { player = data[i]; if (player.value === val) { player.pos = prev; } else { player.pos = prev = i + 1; val = player.value; } } } } } lastLeaderboardUpdate = new Date().toUTCString(); }; this.addToMonthlyLeaderboards = function(name, record, value, overwrite) { name = name.toLowerCase(); try { var val = monthlyLeaderboards[record].get(name) || 0; if (typeof val === "object" || typeof value === "object") { // if record is an object, simpler to just reconstruct + pass an updated object and overwrite the old value with it rather than try to identify a key in the old object and increment it overwrite = true; } else if (typeof val != "number") { val = parseInt(val, 10); if (isNaN(val)) { val = 0; } } if (overwrite) { monthlyLeaderboards[record].remove(name); monthlyLeaderboards[record].add(name, typeof value === "object" ? JSON.stringify(value) : value); } else { monthlyLeaderboards[record].add(name, val + value); } } catch (err) { return false; } }; this.loadConfigurables = function() { var val; var loadArenaTeams = function(name) { val = configObj.get("arena_" + name); try { val = JSON.parse(val); } catch (err) { val = null; } if (Array.isArray(val)) { if (name in arenaOpponents) { arenaOpponents[name].party = val; } } }; loadArenaTeams("pink"); loadArenaTeams("teal"); loadArenaTeams("mustard"); loadArenaTeams("cyan"); loadArenaTeams("crimson"); loadArenaTeams("rainbow"); val = configObj.get("tier_chances"); try { val = JSON.parse(val); } catch (err) { val = null; } if (Array.isArray(val)) { var valid = true; for (var e = 0; e < val.length; e++) { if (typeof val[e] !== "number") { valid = false; break; } } if (val.length < 6) { valid = false; } if (valid) { catchTierChance = val; } } val = configObj.get("wildmsg"); if (val && typeof val === "string") { wildPokemonMessage = val; } }; this.counterPickLeague = function(party) { //stuff var m; for (var i = 0; i < party.length; i++) { m = parseInt(party[i], 10); switch (m) { case 131790: eliteCounterPicks.push(245); eliteCounterPicks.push(468); eliteCounterPicks.push(797); eliteCounterPicks.push(65870); eliteStrongCounterPicks.push(249); eliteStrongCounterPicks.push(65686); break; case 791: eliteCounterPicks.push(385); eliteCounterPicks.push(490); eliteCounterPicks.push(485); eliteStrongCounterPicks.push(65666); eliteStrongCounterPicks.push(717); eliteStrongCounterPicks.push(383); break; case 245: eliteCounterPicks.push(251); eliteCounterPicks.push(145); eliteStrongCounterPicks.push(382); eliteStrongCounterPicks.push(484); eliteStrongCounterPicks.push(644); break; case 65920: eliteCounterPicks.push(245); eliteCounterPicks.push(65574); eliteCounterPicks.push(376); eliteCounterPicks.push(801); eliteStrongCounterPicks.push(65912); eliteStrongCounterPicks.push(131222); eliteStrongCounterPicks.push(716); eliteStrongCounterPicks.push(483); break; case 486: eliteCounterPicks.push(649); eliteStrongCounterPicks.push(65912); break; case 644: eliteCounterPicks.push(208); eliteCounterPicks.push(445); eliteStrongCounterPicks.push(65981); eliteStrongCounterPicks.push(383); break; case 484: eliteCounterPicks.push(282); eliteCounterPicks.push(251); break; case 131718: eliteCounterPicks.push(801); eliteStrongCounterPicks.push(483); break; case 66194: eliteCounterPicks.push(730); eliteCounterPicks.push(785); eliteStrongCounterPicks.push(383); eliteStrongCounterPicks.push(716); break; case 65912: eliteCounterPicks.push(145); eliteStrongCounterPicks.push(250); break; } } return; }; this.renewLeague = function() { var gyms = {}, elite = [], name, badge, trainers, party, i, l, t, n, trainer, namesUsed = [], badgesUsed = [], trainersUsed = [], baseName = []; var trainerClasses = ["Actor", "Actress", "Aroma Lady", "Artist", "Athlete", "Backpacker", "Baker", "Baron", "Baroness", "Battle Girl", "Beauty", "Big Star", "Biker", "Bird Keeper", "Blackbelt", "Boarder", "Bodybuilder", "Boss", "Bug Catcher", "Bug Maniac", "Burglar", "Butler", "Cameraman", "Camper", "Casual Dude", "Casual Guy", "Celebrity", "Channeler", "Chaser", "Chef", "Child Star", "Clerk", "Clown", "Collector", "Comedian", "Commander", "Cool Beauty", "Cool Trainer", "Countess", "Cowgirl", "Crush Girl", "Cue Ball", "Cute Maniac", "Cyclist", "Dancer", "Delinquent", "Depot Agent", "Doctor", "Dragon Tamer", "Driver", "Duchess", "Duke", "Elder", "Electrifying Guy", "Engineer", "Expert", "Fairy Tale Girl", "Fare Prince", "Firebreather", "Fisherman", "Free Diver", "Fun Old Lady", "Fun Old Man", "Furisode Girl", "Future Girl", "Gambler", "Garçon", "Gardener", "Gentleman", "Glasses Man", "Grand Duchess", "Guitarist", "Hardheaded Girl", "Hex Maniac", "High-Tech Maniac", "Hiker", "Hiking Girl", "Hoopster", "Hunter", "Icy Guy", "Idol", "Infielder", "Interviewer", "Janitor", "Jogger", "Juggler", "Kimono Girl", "Kindler", "Lady", "Lass", "Linebacker", "Lone Wolf", "Lorekeeper", "Maid", "Marchioness", "Marquis", "Medium", "Monsieur", "Motorcyclist", "Movie Star", "Muddy Boy", "Musician", "Navigator", "Newscaster", "Ninja Boy", "Nurse", "Nursery Aide", "Officer", "Ordinary Guy", "Ordinary Lady", "Painter", "Parasol Lady", "Passionate Man", "Passionate Rider", "Picnic Girl", "Picnicker", "Pikachu Fan", "Pilot", "Poké Kid", "Pokéfan", "PokéManiac", "Pokémon Breeder", "Pokémon Ranger", "Preschooler", "Proprietor", "Psychic", "Punk Girl", "Punk Guy", "Rancher", "Reporter", "Rich Boy", "Rider", "Rising Star", "Rocker", "Rogue", "Roller Boy", "Roller Skater", "Rotation Girl", "Ruin Maniac", "Sage", "Sailor", "Schoolboy", "Schoolgirl", "Sci-Fi Maniac", "Scientist", "Scuba Diver", "Shady Guy", "Shocking Girl", "Sightseer", "Sim Trainer", "Skier", "Smasher", "Snagem Head", "Socialite", "Sootopolitan", "Spy", "Star Actor", "Steel Spirit", "Street Thug", "Striker", "Stubborn Boy", "Successor", "Suit Actor", "Super Nerd", "Suspicious Child", "Suspicious Lady", "Suspicious Woman", "Swimmer♀", "Swimmer", "Tamer", "Teacher", "The Riches", "Thug", "Tomboy", "Tourist", "Traveling Guy", "Traveling Lady", "Triathlete", "Tuber", "Unique Star", "Veteran", "Veteran Star", "Viscount", "Viscountess", "Waiter", "Waitress", "Wanderer", "Winstrate", "Worker", "Youngster"]; while (Object.keys(gyms).length < 7) { badge = abilityOff(sys.rand(1, 233)).split(" ").random() + " Badge"; name = moveOff(sys.rand(1, 702)).split(" ").concat(moveOff(sys.rand(1, 702)).split(" ")).shuffle().join("").toLowerCase().replace("-", ""); l = name.length; i = sys.rand(0, l-2); name = cap(name.substr(i, sys.rand(i+2, name.length))); if (name.length > 10) { name = cap(name.substr(0, 10)); } trainers = []; baseName = []; for (t = 0; t < 3; t++) { trainer = {}; n = generateComplexName(); trainer.name = (t === 2 ? "Gym Leader" : trainerClasses.random()) + " " + n; trainer.desc = "Gym NPC"; trainer.party = generateTeam(9, 340 + t*75, 595 + t*10, t+1, null, true); if (t === 2) { i = sys.rand(0, 9); trainer.party[i] = trainer.party[i] + ""; } trainer.powerBoost = 0.095 + t*0.0375; trainers.push(trainer); baseName.push(n); } if (["elite", "elite 4", "elite four", "elitefour", "elite4", "e4", "e 4", "help", "register", "hall"].contains(name.toLowerCase()) || namesUsed.contains(name) || badgesUsed.contains(badge) || trainersUsed.contains(baseName[0]) || trainersUsed.contains(baseName[1]) || trainersUsed.contains(baseName[2])) { continue; } namesUsed.push(name); badgesUsed.push(badge); trainersUsed = trainersUsed.concat(baseName); gyms[name.toLowerCase()] = { name: name, badge: badge, trainers: trainers }; } var validLegendaries = legendaries; var big, small, base, b, strong = [131458, 196994, 66015, 131551, 197087, 262623, 328159, 66023, 66028, 66091, 66177, 66178, 66181, 66182, 131718, 66183, 66184, 328350, 66217, 66256].concat(validLegendaries); var getArceus = function() { return [493, 66029, 131565, 197101, 262637, 328173, 393709, 459245, 524781, 590317, 655853, 721389, 786925, 852461, 917997, 983533, 1049069, 1114605].random(); }; for(t = 0; t < 4; t++) { do { n = generateComplexName(); } while (trainersUsed.contains(n)); name = "Elite Four " + n; party = []; base = []; party = megaPokemon.concat().shuffle().slice(0, 3); if (t === 3) { party.push(getArceus()); } base = party.map(pokeInfo.species); big = 0; small = 0; for (b = 0; b < party.length; b++) { if (isLegendary(party[b])) { if (getBST(party[b]) > 600) { big++; } else { small++; } } } while (big < 2 || small < 2) { i = strong.random(); if (eliteCounterPicks && eliteStrongCounterPicks.length > 0 && chance(0.5) && chance(eliteStrongCounterPicks.length * 0.08)) { i = eliteStrongCounterPicks.random(); } b = pokeInfo.species(i); if (!base.contains(b)) { if (isLegendary(i)) { if (getBST(i) > 600) { if (big >= 2) { continue; } else { big++; if (b === 493) { i = getArceus(); } } } else if (getBST(i) <= 600) { if (small >= 2) { continue; } else { small++; } } } party.push(i); base.push(b); } } while (party.length < 12) { i = sys.rand(1, highestDexNum); if (eliteCounterPicks && eliteCounterPicks.length > 0 && chance(0.5) && chance(eliteCounterPicks.length * 0.05)) { i = eliteCounterPicks.random(); } if (getBST(i) < 490 || i in evolutions || isLegendary(i) || base.contains(i)) { continue; } party.push(i); base.push(pokeInfo.species(i)); } i = sys.rand(0, 12); party[i] = party[i] + ""; i = sys.rand(0, 12); party[i] = party[i] + ""; elite.push({ name: name, desc: "Elite Four NPC", party: party.shuffle(), powerBoost: 0.1775 + 0.0275*(elite.length) }); } if (eliteStrongCounterPicks.length > 0) { eliteStrongCounterPicks = eliteStrongCounterPicks.shuffle().slice(Math.floor(eliteStrongCounterPicks.length/2)); } if (eliteCounterPicks.length > 0) { eliteCounterPicks = eliteCounterPicks.shuffle().slice(Math.floor(eliteCounterPicks.length/2)); } gymData = gyms; eliteData = elite; permObj.add("gyms", JSON.stringify(gyms)); permObj.add("elite", JSON.stringify(elite)); }; this.cancelShroom = function(src) { var player = getAvatar(src); if (!player) { return; } if (player.mushroomDeadline <= 0) { safaribot.sendMessage(src, "You don't have a {0} active!".format(finishName("mushroom")), safchan); return; } player.mushroomDeadline = 0; player.mushroomTheme = null; safaribot.sendMessage(src, "The effect of your {0} was cancelled!".format(finishName("mushroom")), safchan); safari.saveGame(player); }; this.forfeitContest = function(src, commandData) { var player = getAvatar(src); if (!player) { return; } if (contestCount === 0) { safaribot.sendMessage(src, "There is no ongoing Contest!", safchan); return; } if (contestForfeited.contains(player.idnum)) { safaribot.sendMessage(src, "You already withdrew from this Contest!", safchan); return; } if (!commandData || commandData !== "confirm") { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Using this command will allow you to withdraw from the current Contest. By withdrawing, you will no longer be able to throw balls or take photos of Pokémon in this Contest, but any other actions that were otherwise restricted by the Contest can be performed.", safchan); safaribot.sendHtmlMessage(src, "You cannot reverse this decision and rejoin the Contest. If you are sure you want to withdraw, type " + link("/contestforfeit confirm", false, true) + ".", safchan); sys.sendMessage(src, "", safchan); return; } contestForfeited.push(player.idnum); /*if (player.id in contestCatchers) { delete contestCatchers[player.id]; }*/ player.altTimeline.lead = 0; sys.sendMessage(src, "", safchan); safaribot.sendAll(sys.name(src) + " withdrew from the current Contest!", safchan); sys.sendMessage(src, "", safchan); }; this.pyrBonusMons = function() { var possible = [663, 426, 332, 381, 380, 121, 184, 282, 530, 230, 442, 609, 699, 144, 145, 715, 785, 212, 38, 395, 485, 635, 787, 479, 6, 330, 284, 3, 169, 303, 473, 452, 189, 687, 365, 658, 880, 248, 334, 689, 437, 724]; possible = possible.shuffle().slice(0, 7); pyrBonusMons = possible; permObj.add("pyrBonusMons", JSON.stringify(pyrBonusMons)); }; this.pyrBonusMonsManual = function(str) { var arr = str.split(","); pyrBonusMons = []; for (var i in arr) { pyrBonusMons.push(parseInt(arr[i], 10)); } permObj.add("pyrBonusMons", JSON.stringify(pyrBonusMons)); }; this.checkNewWeek = function(forced, lbOnly) { var currentTime = now(); var today = getDay(currentTime) - 3; var week = Math.floor(today/7); currentDay = ((today - 1) % 7) + 1; if (week != permObj.get("currentWeek") || forced) { if (!lbOnly) { this.renewLeague(); } permObj.add("currentWeek", week); var old = {}, e, lbInfo; this.updateLeaderboards(); this.awardMonthlyMedals(leaderboards); for (e in monthlyLeaderboards) { lbInfo = monthlyLeaderboardTypes[e]; if (lbInfo.reward) { this.leaderboardRewards(leaderboards[e+"Weekly"], lbInfo, e); } old[e + "Last"] = JSON.parse(JSON.stringify(leaderboards[e + "Weekly"])); monthlyLeaderboards[e].clear(); } lastLeaderboards = old; permObj.add("lastLeaderboards", JSON.stringify(lastLeaderboards)); permObj.save(); rawPlayers.save(); // clean dupe records out of memoryhash this.updateLeaderboards(); for (var a in recentPlayers) { if (recentPlayers[a] + 10 < today) { delete recentPlayers[a]; } } for (var a in recentPlayers) { if (recentPlayers[a] + 10 < today) { delete recentPlayers[a]; } } } }; this.checkNewMonth = function() { var date = new Date().getUTCMonth(); if (date != permObj.get("currentMonth")) { this.resetCostumes(); permObj.add("currentMonth", date); /*var old = {}; for (var e in monthlyLeaderboards) { old[e + "Last"] = JSON.parse(JSON.stringify(leaderboards[e + "Weekly"])); monthlyLeaderboards[e].clear(); } lastLeaderboards = old; permObj.add("lastLeaderboards", JSON.stringify(lastLeaderboards)); permObj.save(); this.updateLeaderboards();*/ } }; this.resetCostumes = function () { for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = getAvatarOff(e); if (!player) { continue; } if (!player.costumeInfo) { player.costumeInfo = {}; } for (var t in costumeData) { if (t === "admin") { continue; } player.costumeInfo[t] = { level: 1, exp: 0, skills: [] }; } this.saveGame(player); } } }; this.awardMonthlyMedals = function(data, retroactive) { // actually weekly, despite the name var p, lb, e, player, m, n, w = "", r = "", ic = -1; var d = new Date(); var dateWeek = Math.ceil(d.getDate() / 7); var dateMonth = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][d.getUTCMonth()]; var dateYear = d.getFullYear(); var date = "(Week {0} of {1} {2})".format(dateWeek, dateMonth, dateYear); for (var i in data) { lb = data[i]; if (retroactive) { if (!["pokesCaughtLast", "contestsWonLast", "journalPointsLast", "collectorEarningsLast", "arenaPointsLast", "pyramidScoreLast", "towerHighestLast"].contains(i)) { continue; } } else { if (!["pokesCaughtWeekly", "contestsWonWeekly", "journalPointsWeekly", "collectorEarningsWeekly", "arenaPointsWeekly", "pyramidScoreWeekly", "towerHighestWeekly"].contains(i)) { continue; } } switch (i) { case "pokesCaughtLast": case "pokesCaughtWeekly": w = "Best Catcher"; break; case "contestsWonLast": case "contestsWonWeekly": w = "Contest Champion"; break; case "journalPointsLast": case "journalPointsWeekly": w = "Photographer"; break; case "collectorEarningsLast": case "collectorEarningsWeekly": w = "Collector Fanatic"; break; case "arenaPointsLast": case "arenaPointsWeekly": w = "Arena Tycoon"; break; case "pyramidScoreLast": case "pyramidScoreWeekly": w = "Pyramid Adventurer"; break; case "towerHighestLast": case "towerHighestWeekly": w = "Tower Tycoon"; break; default: w = "Nick Cage"; break; } for (e = 0; e < lb.length; e++) { p = lb[e]; m = { desc: "", icon: -1 } player = getAvatarOff(p.name); if (!player) { continue; } if (p.pos === 1) { player.medalRecords[w].first++; player.medalRecords[w].stayfirst = true; } else { player.medalRecords[w].first = 0; player.medalRecords[w].stayfirst = false; } if (p.pos <= 3) { player.medalRecords[w].topthree++; player.medalRecords[w].staytopthree = true; } else { player.medalRecords[w].topthree = 0; player.medalRecords[w].staytopthree = false; continue; } var awarded = []; var toAward = []; var outDesc = ""; if (player.medalRecords) { if (player.medalRecords[w]) { if (p.pos === 1 && player.medalRecords[w].first && player.medalRecords[w].first > 1) { outDesc = "#1 " + w + " for " + player.medalRecords[w].first + " consecutive weeks"; n = { desc: (outDesc + " " + date), icon: 17 } toAward.push(n); awarded.push(outDesc); } else if ([1, 2, 3].contains(p.pos) && player.medalRecords[w].topthree && player.medalRecords[w].topthree > 1) { /* In the event someone has medalRecords.topthree > 1 && p.pos = 1 (2~3 in previous weeks and managed to take 1st in current week), award #1 medal + consecutive top three medal. then if they take first again the next week, they will begin receiving the consecutive medal for 1st place */ outDesc = "Top Three " + w + " for " + player.medalRecords[w].topthree + " consecutive weeks"; n = { "desc": (outDesc + " " + date), icon: 37 } toAward.push(n); awarded.push(outDesc); } } } r = ("#" + p.pos + " "); outDesc = r + w; m.desc = outDesc + " " + date; switch (w) { case "Best Catcher": switch (p.pos) { case 1: ic = 300; break; case 2: ic = 285; break; case 3: ic = 291; break; } break; case "Arena Tycoon": switch (p.pos) { case 1: ic = 1; break; case 2: ic = 2; break; case 3: ic = 3; break; } break; case "Contest Champion": switch (p.pos) { case 1: ic = 299; break; case 2: ic = 281; break; case 3: ic = 287; break; } break; case "Pyramid Adventurer": switch (p.pos) { case 1: ic = 4; break; case 2: ic = 5; break; case 3: ic = 6; break; } break; case "Photographer": switch (p.pos) { case 1: ic = 301; break; case 2: ic = 280; break; case 3: ic = 303; break; } break; case "Collector Fanatic": switch(p.pos) { case 1: ic = 302; break; case 2: ic = 282; break; case 3: ic = 292; break; } case "Tower Tycoon": switch(p.pos) { case 1: ic = 9; break; case 2: ic = 10; break; case 3: ic = 11; break; } break; } m.icon = ic; toAward.push(m); awarded.push(outDesc); safaribot.sendHtmlAll("{0} was awarded the following medals: {1}! Congratulations!".format(p.name.toCorrectCase(), readable(awarded.reverse().map(function(m) { return "" + m + "" }))), safchan); sys.appendToFile(crossLog, now() + "|||" + w + " Weekly Leaderboard Category|||" + p.name.toCorrectCase() + "|||" + readable(awarded) + "\n"); if (player.options.receiveWeeklyMedals) { if (isPlaying(p.name)) { safaribot.sendMessage(sys.id(p.name), "You were awarded the following medals from the " + w + " Weekly Leaderboard Category: " + readable(awarded) + "!", safchan); } safari.notification(player, "You were awarded the following medals from the " + w + " Weekly Leaderboard Category: " + readable(awarded) + "!", "Weekly Leaderboard", isPlaying(p.name)); for (var medal = 0; medal < toAward.length; medal++) { safari.awardMedal(player, toAward[medal]); } } } } for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { var player = getAvatarOff(e); if (!player) { continue; } if (player.medalRecords) { for (var t in player.medalRecords) { if (!player.medalRecords[t].stayfirst) { player.medalRecords[t].first = 0; } if (!player.medalRecords[t].staytopthree) { player.medalRecords[t].topthree = 0; } player.medalRecords[t].stayfirst = false; player.medalRecords[t].staytopthree = false; } } this.saveGame(player); } } }; this.awardMedal = function(player, medal) { if (!player.medals) { player.medals = []; } player.records.medalsWon += 1; if (player.medals.length >= medalCap) { //player.medals = player.medals.slice(0, medalCap); if (isPlaying(player.id)) safaribot.sendMessage(sys.id(player.id), "You were unable to receive the medal [" + medal.desc + "] as your medal collection was full!", safchan); else safari.inboxMessage(player, "You were unable to receive the medal [" + medal.desc + "] as your medal collection was full!", isPlaying(player.id)); return false; } else { player.medals.push(medal); } this.saveGame(player); return true; }; this.giftMedal = function(src, commandData) { var cd = commandData.split(":"); if (commandData.length < 3) { safaribot.sendMessage(src, "The format for this command is /awardmedal player:icon:description.", safchan); return false; } var player = getAvatarOff(cd[0]); if (!player) { safaribot.sendMessage(src, "No player exists!", safchan); return false; } var icon = cd[1]; if (!icon) { safaribot.sendMessage(src, "That is not a valid number!", safchan); return false } icon = parseInt(icon, 10); if (isNaN(icon)) { safaribot.sendMessage(src, "That is not a valid color number!", safchan); return false } var description = cd.slice(2).join(":"); var m = { desc: description, icon: icon } if (this.awardMedal(player, m)) { safaribot.sendMessage(src, "You awarded " + player.id.toCorrectCase() + " the medal: " + description + "!", safchan); if (sys.id(cd[0])) safaribot.sendMessage(sys.id(cd[0]), sys.name(src) + " awarded you with the medal: " + description + "!", safchan); } else { safaribot.sendMessage(src, player.id + "'s medal collection was full, so they could not accept the medal!", safchan); } }; this.changeDailyBoost = function(data) { var randomNum, bst; if (data) { randomNum = getInputPokemon(data).num; bst = getBST(randomNum); } if (!randomNum) { do { randomNum = sys.rand(1, highestDexNum); bst = getBST(randomNum); } while (bst > 498 || isRare(randomNum)); } var bonus = 1.78 - (318 - (498 - bst)) * 0.5 / 318; dailyBoost = { pokemon: randomNum, bonus: bonus, expires: now() + hours(24) }; permObj.add("dailyBoost", JSON.stringify(dailyBoost)); permObj.remove("nextDailyBoost"); permObj.save(); sys.sendAll(separator, safchan); safaribot.sendAll("The Pokémon-of-the-Day is now " + pokePlain(randomNum) + ", who will give a bonus catch rate of " + bonus.toFixed(2) + "x if used as your active Pokémon! Additionally, wild " + pokePlain(randomNum) + " have double the chance to be Shiny!", safchan); sys.sendAll(separator, safchan); }; this.validDailyBoost = function(player) { if (!dailyBoost) return false; var leader = this.getEffectiveLead(player, true); return safari.isDailyBoost(leader); }; this.isDailyBoost = function(mon) { var species = pokeInfo.species(mon); return dailyBoost.pokemon == species && !isMega(mon) && !noDailyBonusForms.contains(parseInt(mon)); }; this.getEffectiveLead = function(player, trueSpecies) { var leader = player.party[0]; // handle any party position manipulation here /*if (currentThemeEffect == "distortion" && !contestForfeited.contains(player.idnum)) { leader = player.party[player.party.length-1]; }*/ if (currentThemeEffect == "past" && player.altTimeline.lead !== 0) { leader = player.altTimeline.lead; } if (!trueSpecies) { // after position manipulation, anything that makes leader count as something else while still internally being the original leader species should go here if (canHaveAbility(leader, abilitynum("Imposter")) && currentDisplay && !canHaveAbility(currentDisplay, abilitynum("Imposter"))) { leader = currentDisplay; } } return leader; }; this.changeAlt = function(src, data) { if (!validPlayers("self", src)) { return; } var targetId = sys.id(data); if (!targetId) { safaribot.sendMessage(src, "No such person!", safchan); return; } if (sys.ip(targetId) !== sys.ip(src) || sys.ip(src) === "::1%0") { safaribot.sendMessage(src, "Both accounts must be on the same IP to switch!", safchan); return true; } if (!sys.dbRegistered(sys.name(src).toLowerCase())) { safaribot.sendMessage(src, "You cannot change alt while your username is unregistered!", safchan); return true; } this.transferAlt(sys.name(src), data, src); }; this.transferAlt = function(name1, name2, user) { var player = getAvatarOff(name1); var target = getAvatarOff(name2); var n1 = name1.toLowerCase(), n2 = name2.toLowerCase(); var src = sys.id(name1); var targetId = sys.id(name2); var byAuth = sys.name(user).toLowerCase() != name1.toLowerCase(); if (!player) { safaribot.sendMessage(user, "There's no Safari data under the " + name1.toCorrectCase() + " name!", safchan); return; } if (name1.toLowerCase() == name2.toLowerCase()) { safaribot.sendMessage(user, "You can't pass it to the same alt!", safchan); return; } if (!sys.dbRegistered(name2.toLowerCase())) { safaribot.sendMessage(user, "That account isn't registered so you can't pass Safari data to them!", safchan); return true; } if (/[&<>'"]/gi.test(name2)) { safaribot.sendMessage(user, "You can't pass save data to a name with >, <, &, ' or \" !", safchan); return true; } if (name2 in Object || forbiddenNames.contains(name2)) { safaribot.sendMessage(user, "You can't pass save data to this name!", safchan); return true; } if (player.tradeban > now()) { if (!byAuth) { safaribot.sendMessage(user, "You can't pass save data while you are tradebanned!", safchan); } else { safaribot.sendMessage(user, "You can't pass save data while " + name1.toCorrectCase() + " is tradebanned!", safchan); } return true; } if (player.locked) { safaribot.sendMessage(user, "This save is locked, so you cannot change alt! Contact a Safari Admin to solve this issue!", safchan); return true; } if ((contestCount > 0 && !contestForfeited.contains(player.idnum))) { safaribot.sendMessage(user, "You can't pass save data during a Contest!", safchan); return true; } if (preparationThrows.hasOwnProperty(n1)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is preparing to throw a ball!", safchan); return true; } if (this.isBattling(name1)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is in a battle!", safchan); return true; } if (this.isInAuction(name1)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is participating in an auction!", safchan); return; } if (currentEvent && currentEvent.isInEvent(name1)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is participating in an event!", safchan); return; } var original = player.altlog[0]; if (target) { if (target.tradeban > now()) { safaribot.sendMessage(user, "You can't pass save data to a tradebanned save!!", safchan); return true; } if (target.locked) { safaribot.sendMessage(user, target.id + "'s save is locked, so you cannot change alt! Contact a Safari Admin to solve this issue!", safchan); return true; } if (preparationThrows.hasOwnProperty(n2)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is preparing to throw a ball!", safchan); return true; } if (this.isBattling(name2)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is in a battle!", safchan); return true; } if (this.isInAuction(name2)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is participating in an auction!", safchan); return; } if (currentEvent && currentEvent.isInEvent(name2)) { safaribot.sendMessage(user, "You can't pass save data while one of the players is participating in an event!", safchan); return; } var save1 = JSON.stringify(player); var save2 = JSON.stringify(target); cookedPlayers.add(player.id, save1); cookedPlayers.add(target.id, save2); try { for (var e in monthlyLeaderboards) { if (monthlyLeaderboards[e].get(player.id)) { var val = monthlyLeaderboards[e].get(player.id) || 0; monthlyLeaderboards[e].add(player.id, monthlyLeaderboards[e].get(target.id) || 0); monthlyLeaderboards[e].add(target.id, val); } else if (monthlyLeaderboards[e].get(target.id)) { var val = monthlyLeaderboards[e].get(target.id) || 0; monthlyLeaderboards[e].add(target.id, monthlyLeaderboards[e].get(player.id) || 0); monthlyLeaderboards[e].add(player.id, val); } } var tId = target.id; target.id = player.id; player.id = tId; if (target.altlog.indexOf(target.id) === -1) { target.altlog.push(target.id); } if (player.altlog.indexOf(player.id) === -1) { player.altlog.push(player.id); } if (src) { SESSION.users(src).safari = target; this.clearPlayer(src); safaribot.sendMessage(src, "You swapped Safari data with " + name2.toCorrectCase() + "!", safchan); } if (targetId) { SESSION.users(targetId).safari = player; this.clearPlayer(targetId); safaribot.sendMessage(targetId, "You swapped Safari data with " + name1.toCorrectCase() + "!", safchan); } if (byAuth) { safaribot.sendMessage(user, "You swapped Safari between " + name1.toCorrectCase() + " and " + name2.toCorrectCase() + "!", safchan); } player.casedName = sys.name(targetId); player.nameColor = script.getColor(targetId); target.casedName = sys.name(src); target.nameColor = script.getColor(src); this.saveGame(player); this.saveGame(target); } catch (err) { if (byAuth) { safaribot.sendMessage(user, "Alt Transfer aborted due to an error during the operation! [" + err + (err.lineNumber ? " at line " + err.lineNumber : "") + "]", safchan); safaribot.sendAll("Alt Transfer aborted due to an error during the operation! [" + err + (err.lineNumber ? " at line " + err.lineNumber : "") + "]", staffchannel); } else { safaribot.sendMessage(src, "Alt Change aborted due to an error during the operation!", safchan); safaribot.sendAll("An Alt Change between " + name1.toCorrectCase() + " and " + name2.toCorrectCase() + " was aborted due to an error during the operation! [" + err + (err.lineNumber ? " at line " + err.lineNumber : "") + "]", staffchannel); } if (src) { SESSION.users(src).safari = JSON.parse(save1); } if (targetId) { SESSION.users(targetId).safari = JSON.parse(save2); } return; } if (lastBaiters.contains(n1) && !lastBaiters.contains(n2)) { lastBaiters.splice(lastBaiters.indexOf(n1), 1, n2); } else if (lastBaiters.contains(n2) && !lastBaiters.contains(n1)) { lastBaiters.splice(lastBaiters.indexOf(n2), 1, n1); } if (contestVotes) { var pVote = contestVotes[player.id], tVote = contestVotes[target.id]; delete contestVotes[player.id]; delete contestVotes[target.id]; contestVotes[player.id] = tVote; contestVotes[target.id] = pVote; } if (player.id in pendingActiveChanges || target.id in pendingActiveChanges) { var pPending = pendingActiveChanges[player.id], tPending = pendingActiveChanges[target.id]; delete pendingActiveChanges[player.id]; delete pendingActiveChanges[target.id]; pendingActiveChanges[player.id] = tPending; pendingActiveChanges[target.id] = pPending; } rafflePlayers.add(player.id, player.balls.entry); rafflePlayers.add(target.id, target.balls.entry); safari.sanitizeRaffle(); var obj, temp, changed = false; for (var e = mAuctionsData.length; e--; ) { obj = mAuctionsData[e].offers; if (obj.hasOwnProperty(player.id) && obj.hasOwnProperty(target.id)) { temp = obj[player.id]; obj[player.id] = obj[target.id]; obj[target.id] = temp; changed = true; } else if (!obj.hasOwnProperty(player.id) && obj.hasOwnProperty(target.id)) { obj[player.id] = obj[target.id]; delete obj[target.id]; changed = true; } else if (obj.hasOwnProperty(player.id) && !obj.hasOwnProperty(target.id)) { obj[target.id] = obj[player.id]; delete obj[player.id]; changed = true; } } if (changed) { permObj.add("mAuctions", JSON.stringify(mAuctionsData)); permObj.save(); } if (saltBans.hash.hasOwnProperty(player.id) || saltBans.hash.hasOwnProperty(target.id)) { saltBans.add(player.id, player.truesalt); saltBans.add(target.id, target.truesalt); saltBans.save(); } idnumList.add(player.idnum, player.id); idnumList.add(target.idnum, target.id); sys.appendToFile(altLog, now() + "|||" + name1 + " <--> " + name2 + "|||" + sys.name(user) + "|||" + original + " & " + target.altlog[0] + "\n"); } else { var save1 = JSON.stringify(player); cookedPlayers.add(player.id, save1); try { if (src) { SESSION.users(src).safari = null; } rawPlayers.remove(player.id); if (targetId) { SESSION.users(targetId).safari = player; } for (var e in monthlyLeaderboards) { if (monthlyLeaderboards[e].get(player.id)) { monthlyLeaderboards[e].add(name2.toLowerCase(), monthlyLeaderboards[e].get(player.id)); monthlyLeaderboards[e].remove(player.id); } } player.id = name2.toLowerCase(); if (player.altlog.indexOf(player.id) === -1) { player.altlog.push(player.id); } if (src) { this.clearPlayer(src); } if (targetId) { this.clearPlayer(targetId); } player.casedName = sys.name(targetId); player.nameColor = script.getColor(targetId); this.saveGame(player); } catch (err) { if (byAuth) { safaribot.sendMessage(user, "Alt Transfer aborted due to an error during the operation! [" + err + (err.lineNumber ? " at line " + err.lineNumber : "") + "]", safchan); safaribot.sendAll("Alt Transfer aborted due to an error during the operation! [" + err + (err.lineNumber ? " at line " + err.lineNumber : "") + "]", staffchannel); } else { safaribot.sendMessage(src, "Alt Change aborted due to an error during the operation!", safchan); safaribot.sendAll("An Alt Change between " + name1.toCorrectCase() + " and " + name2.toCorrectCase() + " was aborted due to an error during the operation! [" + err + (err.lineNumber ? " at line " + err.lineNumber : "") + "]", staffchannel); } if (src) { SESSION.users(src).safari = JSON.parse(save1); } if (targetId) { SESSION.users(targetId).safari = null; } return; } if (lastBaiters.contains(n1)) { lastBaiters.splice(lastBaiters.indexOf(n1), 1, n2); } rafflePlayers.add(player.id, player.balls.entry); rafflePlayers.remove(name1.toLowerCase()); safari.sanitizeRaffle(); if (contestVotes) { var pVote = contestVotes[targetId]; delete contestVotes[targetId]; contestVotes[player.id] = pVote; } if (saltBans.hash.hasOwnProperty(name1.toLowerCase())) { saltBans.add(player.id, player.truesalt); saltBans.remove(name1.toLowerCase()); saltBans.save(); } var obj, changed = false; for (var e = mAuctionsData.length; e--; ) { obj = mAuctionsData[e].offers; if (obj.hasOwnProperty(name1.toLowerCase())) { obj[player.id] = obj[name1.toLowerCase()]; delete obj[name1.toLowerCase()]; changed = true; } } if (changed) { permObj.add("mAuctions", JSON.stringify(mAuctionsData)); permObj.save(); } idnumList.add(player.idnum, player.id); if (src) { safaribot.sendMessage(src, "You passed your Safari data to " + name2.toCorrectCase() + "!", safchan); } if (targetId) { safaribot.sendMessage(targetId, name1.toCorrectCase() + " passed their Safari data to you!", safchan); } if (byAuth) { safaribot.sendMessage(user, "You passed " + name1.toCorrectCase() + "'s Safari data to " + name2.toCorrectCase() + "!", safchan); } sys.appendToFile(altLog, now() + "|||" + name1 + " --> " + name2 + "|||" + sys.name(user) + "|||" + original + "\n"); } }; this.logLostCommand = function(user, command, info) { sys.appendToFile(lostLog, now() + "|||" + user + "::" + command + "::" + (info || ".") + "\n"); }; this.clearPlayer = function(src) { var name = sys.name(src).toLowerCase(); if (name in tradeRequests) { delete tradeRequests[name]; } if (name in challengeRequests) { delete challengeRequests[name]; } var player = getAvatar(src); if (player) { safari.sanitize(player); player.tutorial.privateWildPokemon = null; this.saveGame(player); } }; this.saveShop = function() { permObj.add("npcShop", JSON.stringify(npcShop)); }; this.saveDaycare = function() { if (!(Array.isArray(safari.daycarePokemon))) { safari.daycarePokemon = []; } permObj.add("daycarePokemon", JSON.stringify(safari.daycarePokemon)); permObj.add("daycareRegions", JSON.stringify(safari.daycareRegions)); }; this.showLog = function(src, command, commandData, file, name, parser, querier, html) { var log = sys.getFileContent(file); if (log) { log = log.split("\n"); this.showLogList(src, command, commandData, log, name, parser, querier, html); } else { safaribot.sendMessage(src, cap(name) + " Log not found!", safchan); } }; this.simpleQuery = function(list, query, queryMode) { var e, exp; if (queryMode === "&&") { var queryStr = "^"; for (e = 0; e < query.length; e++) { queryStr += "(?=.*" + escapeRegExp(query[e]) + ")"; } queryStr += ".+"; exp = new RegExp(queryStr, "i"); } else { var queryStr = []; for (e = 0; e < query.length; e++) { queryStr.push("(" + escapeRegExp(query[e]) + ")"); } queryStr = queryStr.join("|"); exp = new RegExp(queryStr, "i"); } for (e = list.length - 1; e >= 0; e--) { if (!exp.test(list[e])) { list.splice(e, 1); } } return list; }; this.showLogList = function(src, command, commandData, log, name, parser, querier, html) { var info = commandData.split(":"), range = getRange(info[0]), term = info.length > 1 ? info[1] : "", limit = info.length > 2 && !isNaN(parseInt(info[2], 10)) ? parseInt(info[2], 10) : 100, e, query, termDesc; if (log.indexOf("") !== -1) { log.splice(log.indexOf(""), 1); } if (!range) { range = { lower: 1, upper: 10 }; } log = getArrayRange(log.slice(0).reverse(), range.lower, range.upper).reverse(); if (term) { var queryMode = term.indexOf("||") < term.indexOf("&&") ? "&&" : "||"; query = term.split(queryMode).map(function(x) { return x.trim(); }).filter(function(x) { return x.length > 0; }); if (querier) { log = querier(log, query, queryMode); } else { log = this.simpleQuery(log, query, queryMode); } termDesc = readable(query, queryMode === "&&" ? "and" : "or"); } if (log.length <= 0) { safaribot.sendMessage(src, "No " + name + " log found for this query!", safchan); } else { var spliced = false; if (log.length > limit) { log = log.slice(log.length-limit, log.length); spliced = true; } sys.sendMessage(src, "", safchan); sys.sendMessage(src, cap(name) + " Log (last " + (range.lower > 1 ? range.lower + "~" : "") + range.upper + " entries" + (term ? ", only displaying entries with the term" + (query.length > 1 ? "s" : "") + " " + termDesc : "") + "):", safchan); for (e in log) { if (!log[e]) { continue; } if (html) { safaribot.sendHtmlMessage(src, parser(log[e]), safchan); } else { safaribot.sendMessage(src, parser(log[e]), safchan); } } if (spliced) { safaribot.sendMessage(src, "Only showing first " + limit + " entries found. Narrow down your search or use /" + command + " [Range]:[Query]:[Max Entries Displayed] for more results.", safchan); } } var more = range.upper - range.lower + (range.lower <= 1 ? 1 : 0); safaribot.sendHtmlMessage(src, link("/" + command + " " + range.upper + "-" + (range.upper + more) + (term ? ":" + term : "")), safchan); if (log.length > 0) { sys.sendMessage(src, "", safchan); } }; this.applyTradeban = function(self, name, player, duration) { var chans = [safchan, staffchannel, sachannel]; if (duration === 0) { player.tradeban = 0; for (var x in chans) { safaribot.sendAll(name + " has been unbanned from trading and shopping by " + self + "!", chans[x]); } safari.saveGame(player); tradeBans.remove(player.id); } else { var length, changing = player.tradeban > now(); if (duration == -1) { length = "permanently"; player.tradeban = 2147483000000; } else { length = "for " + utilities.getTimeString(duration); player.tradeban = now() + duration * 1000; } player.shop = {}; safari.saveGame(player); for (var x in chans) { if (changing) { safaribot.sendAll(name + "'s tradeban was changed by " + self + " and now they are banned from trading and shopping " + length + "!", chans[x]); } else { safaribot.sendAll(name + " has been banned from trading and shopping " + length + " by " + self + "!", chans[x]); } } var id = sys.id(name); if (id) { for (var b in currentAuctions) { currentAuctions[b].removePlayer(id, "was trade banned"); } } tradeBans.add(player.id, player.tradeban); } tradeBans.removeIf(function(obj, e) { return parseInt(obj.get(e), 10) === 0 || parseInt(obj.get(e), 10) < now(); }); tradeBans.save(); }; this.clearBans = function(src) { var b, player, val, n = now(); for (b in tradeBans.hash) { if (tradeBans.hash.hasOwnProperty(b)) { val = parseInt(tradeBans.hash[b], 10); player = getAvatarOff(b); if (!player || n > val) { tradeBans.remove(b); } } } for (b in saltBans.hash) { if (saltBans.hash.hasOwnProperty(b)) { val = parseInt(saltBans.hash[b], 10); player = getAvatarOff(b); if (!player || n > val) { saltBans.remove(b); } } } safaribot.sendMessage(src, "Ban listings cleaned!", safchan); }; this.backupSaves = function() { rawPlayers.save(); sys.write(saveBackupFile5, sys.getFileContent(saveBackupFile4)); sys.write(saveBackupFile4, sys.getFileContent(saveBackupFile3)); sys.write(saveBackupFile3, sys.getFileContent(saveBackupFile2)); sys.write(saveBackupFile2, sys.getFileContent(saveBackupFile1)); sys.write(saveBackupFile1, sys.getFileContent(rawPlayers.fname)); sys.write("scriptdata/safari/safariobjects-backup.txt", sys.getFileContent(permFile)); backupPlayers1 = new MemoryHash(saveBackupFile1); backupPlayers2 = new MemoryHash(saveBackupFile2); backupPlayers3 = new MemoryHash(saveBackupFile3); backupPlayers4 = new MemoryHash(saveBackupFile4); backupPlayers5 = new MemoryHash(saveBackupFile5); }; this.lockInactiveSaves = function() { var today = getDay(now()); for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { data = JSON.parse(rawPlayers.hash[e]); if (today - data.lastLogin > (30 * 5)) { // approx. 5 months var player = getAvatarOff(data.id); if (player.locked) { continue; } player.locked = true; safari.saveGame(player); sys.appendToFile(miscLog, now() + "|||" + data.id.toCorrectCase() + "|||was locked after 5 months of inactivity. Last login: " + data.lastLogin + ", current day: " + today + "\n"); } } } }; this.sanitize = function(player) { if (player) { var clean, i; var toTemplate = function(obj, prop, template) { var p; if (obj[prop] === undefined) { if (Array.isArray(template[prop])) { obj[prop] = []; } else if (typeof template[prop] == "object") { obj[prop] = {}; for (p in template[prop]) { toTemplate(obj[prop], p, template[prop]); } } else { obj[prop] = template[prop]; } } else { if (!Array.isArray(obj[prop]) && typeof obj[prop] == "object" && typeof template[prop] == "object") { for (p in template[prop]) { if (!["shop", "decorations", "secretBase", "nextSpawn", "pokeskills"].contains(p)) { toTemplate(obj[prop], p, template[prop]); } } } } }; var removeInvalid = function(obj, prop, template) { var p; if (!(prop in template)) { if (["favoriteBall", "cherishOff", "dexOptional", "visible", "trading", "flashme", "smallBox", "monoSecondary", "pokeskillsDisabled", "alwaysShowMasterBall", "alwaysShowCherishBall", "sellPrompt"].contains(prop)) { obj.options[prop] = obj[prop]; } delete obj[prop]; } else if (!Array.isArray(obj[prop]) && typeof obj[prop] == "object" && typeof template[prop] == "object") { if (!["shop", "decorations", "secretBase", "nextSpawn", "pokeskills", "trackedRequests", "offlineSales"].contains(prop)) { for (p in obj[prop]) { removeInvalid(obj[prop], p, template[prop]); } } } }; for (i in playerTemplate) { toTemplate(player, i, playerTemplate); } for (i in player) { removeInvalid(player, i, playerTemplate); } if (!Array.isArray(player.costumes)) { player.costumes = []; } for (i = 0; i < allItems.length; i++) { clean = allItems[i]; if (typeof player.balls[clean] !== "number") { player.balls[clean] = parseInt(player.balls[clean], 10); } if (isNaN(player.balls[clean]) || player.balls[clean] === null || player.balls[clean] < 0) { if (clean == "box") { player.balls[clean] = 4; } else { player.balls[clean] = 0; } } var cap = getCap(clean); if (player.balls[clean] > cap) { player.balls[clean] = cap; } if (!safari.events.spiritDuelsEnabled && player.balls.spirit > 0) { player.balls.spirit = 0; } } var redoBase = false; for (i in player.decorations) { if (!decorations.hasOwnProperty(i)) { delete player.decorations[i]; redoBase = true; } } if (redoBase || player.secretBaseCache.length !== SECRET_BASE_WIDTH * SECRET_BASE_HEIGHT) { this.sanitizeBase(player); } if (player.missions.length === 0) { this.renewMissions(player); } while (player.inbox.length > player.unreadInbox.length) { player.unreadInbox.push(false); } if (typeof player.money !== "number") { player.money = parseInt(player.money, 10); } if (isNaN(player.money) || player.money < 0) { player.money = 0; } else if (player.money > moneyCap) { player.money = moneyCap; } if (player.money % 1 !== 0) { player.money = Math.floor(player.money); } if (player.burnLastUsed === 0 && player.balls.burn > 0) { player.burnLastUsed = now(); } if (player.party.length === 0) { player.party = [player.starter]; } if (player.altlog.length === 0) { player.altlog.push(player.id); } var list = photoMood.Positive.concat(photoMood.Neutral, photoMood.Negative); for (i = player.photos.length; i--; ) { if (!player.photos[i].mood) { player.photos[i].mood = list.random(); } } for (i in player.shop) { if (!getInput(i)) { delete player.shop[i]; } else if (!player.shop[i].price) { delete player.shop[i]; } } for (i = player.tradeBlacklist.length; i--; ) { if (!getInput(player.tradeBlacklist[i])) { player.tradeBlacklist.splice(i, 1); } } if (!("idnum" in player) || player.idnum === undefined || player.idnum === null || isNaN(player.idnum) || player.idnum < 0 || typeof player.idnum !== "number") { player.idnum = 0; this.assignIdNumber(player); } if (!player.costumes.contains(player.costume)) { player.costume = "none"; } if (player.casedName.length === 0) { player.casedName = sys.name(sys.id(player.id)); } if (player.nameColor.length === 0) { player.nameColor = script.getColor(sys.id(player.id)); } if (player.helds.length > player.party.length) { player.helds = player.helds.slice(0, player.party.length); } if (player.records.medalsWon === 0 && player.medals.length > 0) { player.records.medalsWon = player.medals.length; } if (player.starter2 === null || !Array.isArray(player.starter2) || player.starter2.length === 0) { player.starter2 = []; switch (player.starter) { case 1: case 2: case 3: player.starter2 = [155, 156, 157]; break; case 4: case 4: case 5: player.starter2 = [158, 159, 160]; break; case 7: case 8: case 9: player.starter2 = [152, 153, 154]; break; } } this.saveGame(player); } }; this.sanitizePokemon = function(player) { if (player) { var e, id, list = player.pokemon; for (e = list.length - 1; e >= 0; e--) { id = player.pokemon[e]; if (!pokeInfo.valid(id)) { list.splice(e, 1); } } } }; this.sanitizeAll = function() { var onChannel = sys.playersOfChannel(safchan); var player, e; try { for (e in onChannel) { player = getAvatar(onChannel[e]); if (!player) { continue; } safari.sanitize(player); safari.sanitizePokemon(player); safari.sanitizeBase(player); safari.saveGame(player); } } catch (err) { safaribot.sendAll("Safari Error during sanitizeAll" + (err.lineNumber ? " at line " + err.lineNumber : "") + ": " + err + " (while sanitizing " + sys.name(onChannel[e]) + ")", staffchannel); } }; this.trackMessage = function(mess, player) { var id; for (var t = 0; t < player.trackers.length; t++) { id = sys.id(player.trackers[t]); if (id !== undefined && !allTrackers.contains(player.trackers[t])) { safaribot.sendMessage(id, mess, safchan); } } for (t = allTrackers.length; t--; ) { id = sys.id(allTrackers[t]); if (id) { safaribot.sendMessage(id, mess, safchan); } } }; this.toursPromo = function (name, placing) { var player = getAvatarOff(name); if (!player) { return; } if (player.locked) { return; } var rew, mushamt = 0, rareamt = 0, packamt = 0, megaamt = 0; switch (placing) { case 1: mushamt = 3; rareamt = 8; packamt = 3; megaamt = 1; this.costumeEXP(player, "wintour", 1); break; case 2: mushamt = 2; rareamt = 5; packamt = 2; this.costumeEXP(player, "wintour", 2); break; case 3: mushamt = 1; rareamt = 2; packamt = 1; this.costumeEXP(player, "wintour", 3); break; default: return; //Only top 3 get. Nothing more than 3 should be passed anyway } if (this.hasCostumeSkill(player, "extraTourRare") && rareamt) { rareamt++; } if (this.hasCostumeSkill(player, "extraTourMega") && megaamt) { megaamt++; } player.balls.rare += rareamt; player.balls.mushroom += mushamt; player.balls.pack += packamt; player.balls.mega += megaamt; rew = [(megaamt > 0 ? plural(megaamt, "mega") : ""), (mushamt > 0 ? plural(mushamt, "mushroom") : ""), (rareamt > 0 ? plural(rareamt, "rare") : ""), (packamt > 0 ? plural(packamt, "pack") : "")] .filter(Boolean) .join(", "); this.missionProgress(player, "cross", "tours", 1, {}); if (placing === 1) { this.missionProgress(player, "winTour", "tours", 1, {}); } this.inboxMessage(player, "You won " + rew + " from an Event Tour!", isPlaying(name)); this.sanitize(player); safaribot.sendHtmlAll("" + getOrdinal(placing) + ": " + html_escape(name.toCorrectCase()) + " (" + rew + ")", safchan); sys.appendToFile(crossLog, now() + "|||Tournaments|||" + name.toCorrectCase() + "|||" + rew + "\n"); }; this.triviaPromo = function (name, placing) { var player = getAvatarOff(name); if (!player) { return; } if (player.locked) { return; } var rew, amt; switch (placing) { case 1: amt = 3; break; case 2: amt = 2; break; case 3: amt = 1; break; default: return; //Only top 3 get. Nothing more than 3 should be passed anyway } if (this.hasCostumeSkill(player, "extraTriviaSoda")) { amt = Math.round(amt * (1.2 + (this.getCostumeLevel(player)/16))); } this.costumeEXP(player, "wintrivia", amt); player.balls.soda += amt; rew = plural(amt, "soda"); this.missionProgress(player, "cross", "trivia", 1, {}); this.missionProgress(player, "sodaFromTrivia", "trivia", (4 - placing), {}); safari.costumeEXP(player, "soda", 10 * amt); this.inboxMessage(player, "You won " + rew + " from an Event Trivia Game!", isPlaying(name)); safari.detectiveClue(player.idnum, "trivia"); this.sanitize(player); safaribot.sendHtmlAll("" + getOrdinal(placing) + ": " + html_escape(name.toCorrectCase()) + " (" + rew + ")", safchan); sys.appendToFile(crossLog, now() + "|||Trivia|||" + name.toCorrectCase() + "|||" + rew + "\n"); }; this.mafiaPromo = function(list) { var amt = list.length; if (list.length === 0) { return; } var player, name, received = [], rew, famt; for (var p = 0; p < list.length; p++) { name = list[p]; player = getAvatarOff(name); if (!player) { continue; } if (player.locked) { continue; } this.costumeEXP(player, "winmafia", amt); famt = (this.hasCostumeSkill(player, "extraMafiaShady") ? 1.5 * amt : 1 * amt); player.balls.shady += Math.floor(famt); this.missionProgress(player, "cross", "mafia", 1, {}); this.missionProgress(player, "shadyFromMafia", "mafia", Math.floor(famt), {}); rew = plural(amt, "shady"); received.push(name.toCorrectCase()); safari.detectiveClue(player.idnum, "mafia"); if (amt >= 7) { safari.detectiveClue(player.idnum, "mafia2"); } this.inboxMessage(player, "You won " + rew + " from a Mafia Event Game!", isPlaying(name)); this.sanitize(player); } if (received.length > 0) { sys.sendAll("", safchan); safaribot.sendHtmlAll(html_escape(readable(received)) + " received " + rew + " for playing in the Mafia Event Game!", safchan); sys.sendAll("", safchan); } sys.appendToFile(crossLog, now() + "|||Mafia|||" + readable(received) + "|||" + rew + "\n"); }; this.hangmanPromo = function(name) { var player = getAvatarOff(name); if (!player) { return; } if (player.locked) { return; } var rew = ["6@silver", "9@gacha", "2@gem", "1@form", "150@dust"].random(); var out = giveStuff(player, toStuffObj(rew), true); var rewardName = translateStuff(rew, true); this.missionProgress(player, "cross", "hangman", 1, {}); this.saveGame(player); if (out.gained.length > 0) { sys.sendAll("", safchan); safaribot.sendAll(player.id.toCorrectCase() + " received " + out.gained + " from an Event Hangman game!", safchan); sys.sendAll("", safchan); } if (sys.id(player.id)) { safaribot.sendMessage(sys.id(player.id), "You " + stuffReceivedDesc(out) + " from an Event Hangman game!",safchan); } this.inboxMessage(player, "You " + stuffReceivedDesc(out) + " from an Event Hangman game!", isPlaying(name)); sys.appendToFile(crossLog, now() + "|||Hangman|||" + player.id.toCorrectCase() + "|||" + rewardName + "\n"); }; this.hangmanPromoLb = function(name, placing) { var player = getAvatarOff(name); if (!player) { return; } placing = Math.min(parseInt(placing, 10), 9); var amt = [12, 9, 9, 7, 7, 4, 4, 4, 1, 1][placing]; var rew = plural(amt, "cookie"); player.balls.cookie += amt; this.inboxMessage(player, "You won " + rew + " from Hangman Leaderboard!", isPlaying(name)); this.sanitize(player); if (placing === 0) { safaribot.sendHtmlAll("" + getOrdinal(placing + 1) + " in Hangman Leaderboard: " + html_escape(name.toCorrectCase()) + " (" + rew + ")", safchan); } else { safaribot.sendHtmlAll("" + getOrdinal(placing + 1) + " in Hangman Leaderboard: " + html_escape(name.toCorrectCase()) + " (" + rew + ")", safchan); } sys.appendToFile(crossLog, now() + "|||Hangman|||" + name.toCorrectCase() + "|||" + rew + "\n"); }; this.leaderboardRewards = function(lb, lbInfo, lbName, reverse) { if (lb.length === 0) { return; } var first = [], second = [], third = [], rest = [], winners = {}, e, p, out, rew; for (e = 0; e < lb.length; e++) { p = lb[e]; if (p.pos > 10) { break; } var prizes; switch (lbName) { case "celebrityScore": prizes = ["2@pebble,8@pack,2@mega,3@crystal,12@golden", "4@pack,1@mega,2@crystal,8@golden", "2@pack,@crystal,4@golden"]; break; case "celebrityScoreHard": prizes = ["3@pebble,8@pack,2@mega,3@crystal,12@golden", "4@pack,1@mega,2@crystal,8@golden", "2@pack,@crystal,4@golden"]; break; case "celebrityScoreExpert": prizes = ["4@pebble,1@ldew,8@pack,2@mega,5@crystal,16@golden", "4@pack,1@mega,3@crystal,10@golden", "2@pack,2@crystal,6@golden"]; break; case "celebrityScoreSuperExpert": prizes = ["5@pebble,1@ldew,8@pack,2@mega,5@crystal,16@golden", "4@pack,1@mega,3@crystal,10@golden", "2@pack,2@crystal,6@golden"]; break; case "celebrityScoreAbyssal": prizes = ["1@pebble,5@pack", "3@pack", "1@pack"]; break; default: prizes = ["3@crystal,3@pack,15@golden,30@silver", "1@crystal,3@pack,10@golden,20@silver", "2@pack,8@golden,10@silver", "1@pack,5@golden,5@silver"]; break; } switch (p.pos) { case 1: first.push(toColor("" + p.fullName + "", p.color)); break; case 2: second.push(toColor("" + p.fullName + "", p.color)); break; case 3: third.push(toColor("" + p.fullName + "", p.color)); break; default: rest.push(p.pos + ". " + p.fullName); } if (lbName.indexOf("celebrityScore") === 0) { if (p.pos <= 3) { winners[p.name] = prizes[p.pos - 1]; } } else { winners[p.name] = prizes[(p.pos <= 3 ? p.pos-1 : 3)]; } if (reverse) winners[p.name] = winners[p.name].split(",").map(function(e) { return "-" + e }).join(","); } if (!reverse) { safaribot.sendHtmlAll(toColor("Top 10 players " + lbInfo.desc + ":", "red"), safchan); if (first.length) { safaribot.sendHtmlAll("1st Place: " + readable(first), safchan); } if (second.length) { safaribot.sendHtmlAll("2nd Place: " + readable(second), safchan); } if (third.length) { safaribot.sendHtmlAll("3rd Place: " + readable(third), safchan); } if (rest.length) { safaribot.sendHtmlAll("4~10th: " + rest.join(", "), safchan); } } for (e in winners) { p = getAvatarOff(e); if (p) { out = giveStuff(p, toStuffObj(winners[e])); if (isPlaying(e)) { safaribot.sendMessage(sys.id(e), "You " + out + " from the " + cap(lbInfo.alias) + " Leaderboard!", safchan); } this.notification(p, "You " + out + " from the " + cap(lbInfo.alias) + " Leaderboard!", "Weekly Leaderboard", isPlaying(e)); rew = translateStuff(winners[e]); sys.appendToFile(crossLog, now() + "|||" + lbInfo.alias.split(" ").map(cap).join(" ") + " Leaderboard|||" + p.id.toCorrectCase() + "|||" + rew + "\n"); } } sys.sendAll("", safchan); }; this.isChannelAdmin = function (src) { return SESSION.channels(safchan).isChannelAdmin(src); }; function runUpdate() { var POglobal = SESSION.global(); var index, source; permObj.add("events", JSON.stringify(safari.events)); permObj.add("marketData", JSON.stringify(marketData)); permObj.add("triviaData", JSON.stringify(triviaData)); permObj.add("celebrityData", JSON.stringify(safari.celebrityData)); permObj.add("celebrityRegion", JSON.stringify(safari.celebrityRegion)); permObj.add("dumps", JSON.stringify(safari.dataDumps)); permObj.add("dumps2", JSON.stringify(safari.dataDumps2)); permObj.add("celebrityPKs", JSON.stringify(celebrityPKs)); permObj.add("recentPlayers", JSON.stringify(recentPlayers)); permObj.add("globalWildItems", JSON.stringify(globalWildItems)); permObj.add("skillData", JSON.stringify(skillData)); permObj.add("detectiveData", JSON.stringify(safari.detectiveData)); permObj.add("moveLearners", JSON.stringify(safari.moveLearners)); safari.saveDaycare(); for (var i = 0; i < POglobal.plugins.length; ++i) { if ("safari.js" == POglobal.plugins[i].source) { source = POglobal.plugins[i].source; index = i; } } if (index !== undefined) { updateModule(source, function (module) { POglobal.plugins[index] = module; module.source = source; module.init(); }); } } function checkUpdate() { if (!needsUpdating) { return; } if (currentPokemon || contestCount > 0 || contestCooldown < 200 || currentAuctions.length > 0 || currentBattles.length > 0 || currentPyramids.length > 0 || currentBakings.length > 0 || currentEvent) { return; } safariUpdating = true; sys.sendHtmlAll(closedMessage, safchan); runUpdate(); } // Stolen from https://stackoverflow.com/questions/6122571/simple-non-secure-hash-function-for-javascript function hashCode(string) { // Used to check if the local Safari script is the same as the one in the GitHub repository var hash = 0; if (typeof string !== "string" || string.length == 0) { return hash; } for (var i = 0; i < string.length; i++) { var c = string.charCodeAt(i); hash = ((hash << 5) - hash) + c; hash = hash & hash; } return hash; } /* Help & Commands */ this["help-string"] = ["safari: To know the safari commands"]; this.showHelp = function (src) { var x, help = [ "", separator, "±Goal: Use your Poké Balls to catch Wild Pokémon that appear during Contest times.", //"±Goal: You can trade those Pokémon with other players or simply brag about your collection.", "±Goal: To start playing, type /start to choose your starter Pokémon and receive 30 Safari Balls.", separator, //"±Contest: A Contest starts every " + contestCooldownLength/60 + " minutes. During that time, wild Pokémon may suddenly appear.", "±Contest: When a wild Pokémon appears, players can use /catch to throw a ball until someone gets it.", "±Contest: Different balls can be used to get a better chance, but they also may have higher cooldown between throws or other effects.", separator, "±Actions: Pokémon you caught can be sold to the NPC with /sell or traded with other players with /trade.", "±Actions: You can use the money gained by selling Pokémon and logging in everyday to /buy more Poké Balls.", //"±Actions: You can set up to 6 Pokémon to be visible to anyone. Form your party with /party, and view others' party with /view.", "±Actions: Use /party to form your party. This can give you a small bonus when trying to catch Pokémon based on type effectiveness and stats.", "±Actions: You can dress up in costumes to gain different bonuses! Use /getcostumes to see how to obtain them and /dressup [costume name] to change into a costume!", separator, "±More: To learn other commands, use /commands safari.", separator, "" ]; for (x in help) { sys.sendMessage(src, help[x], safchan); } }; this.showItemHelp = function (src, data) { if (data === "*") { safaribot.sendMessage(src, "You can use /itemhelp [item] to return information on a particular item, costume, or category. You can display the help for all items using \"/itemhelp all\" or from the following categories: \"balls\", \"items\", \"perks\".", safchan); return; } var help = []; data = data.toLowerCase(); var catStrings = ["all", "balls", "items", "perks", "costumes", "berries"]; if (catStrings.indexOf(data) === -1) { //Try to decode which item the user is looking for var lookup = itemAlias(data, true); if (allItems.indexOf(lookup) !== -1) { //Now grab the help from whichever category it is if (itemHelp.hasOwnProperty(lookup)) { help.push(finishName(lookup) + ": " + itemHelp[lookup]); } else if (itemData[lookup].type == "valuables") { lookup = "valuables"; help.push(finishName(lookup) + ": " + itemHelp[lookup]); } else if (perkHelp.hasOwnProperty(lookup)) { help.push(finishName(lookup) + ": " + perkHelp[lookup]); help.push("Note: This item is a Perk and the effects are passive."); } else if (ballHelp.hasOwnProperty(lookup)) { help.push(finishName(lookup) + ": " + ballHelp[lookup]); help.push("Note: Cooldown value doubles following a successful catch with the exception of " + es(finishName("spy")) + "."); } else if (berryHelp.hasOwnProperty(lookup)) { help.push(finishName(lookup) + ": " + berryHelp[lookup]); } } //If it's not an item, it's either a costume or invalid. lookup = costumeAlias(data, true); if (allCostumes.indexOf(lookup) !== -1) { help.push(costumeAlias(lookup, false, true) + " Costume: " + safari.getCostumeHelp(lookup)); } //Frame out result if (help.length === 0) { safaribot.sendMessage(src, lookup + " is either an invalid item or no help string is defined!", safchan); return; } sys.sendMessage(src, "", safchan); sys.sendMessage(src, "*** Item Help ***", safchan); help.forEach(function(h) { sys.sendMessage(src, h, safchan); }); sys.sendMessage(src, "", safchan); } else { var x, dataArray, out = []; out.push(""); if (data === "all" || data === "items") { out.push("*** Item Help ***"); dataArray = Object.keys(itemHelp); for (var e in dataArray) { e = dataArray[e]; out.push((e === "itemfinder" ? "" : finishName(e) + ": ") + itemHelp[e]); } out.push(""); } if (data === "all" || data === "berries") { out.push("*** Berry Help ***"); dataArray = Object.keys(berryHelp); for (var e in dataArray) { e = dataArray[e]; out.push(finishName(e) + ": " + berryHelp[e]); } out.push(""); } if (data === "all" || data === "balls") { out.push("*** Ball Help ***"); dataArray = Object.keys(ballHelp); for (var e in dataArray) { e = dataArray[e]; out.push(finishName(e) + ": " + ballHelp[e]); } out.push("Note: Cooldown value doubles following a successful catch."); out.push(""); } if (data === "all" || data === "perks") { out.push("*** Perk Help ***"); dataArray = Object.keys(perkHelp); for (var e in dataArray) { e = dataArray[e]; out.push(finishName(e) + ": " + perkHelp[e]); } out.push("Note: These items are Perks and the effects are passive."); out.push(""); } if (data === "all" || data === "costumes") { out.push("*** Costume Help ***"); dataArray = Object.keys(costumeData); for (var e in dataArray) { e = dataArray[e]; out.push(costumeAlias(e, false, true) + " Costume: " + safari.getCostumeHelp(e)); } out.push(""); } for (x in out) { sys.sendMessage(src, out[x], safchan); } } }; this.showEventHelp = function (src) { sys.sendMessage(src, "", safchan); sys.sendMessage(src, "*** EVENTS INFORMATION ***", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Faction War/Inverted Faction War: Players join one of the two teams to battle each other. Pokémon defeated are eliminated from the battle. The team that defeats the all Pokémon from the other side first wins.", safchan); sys.sendMessage(src, "Requirements: A full party (6 Pokémon). Minimum of 1 player for event to start, and 4 players for rewards. If Inverted Faction War, pure Normal-type Pokémon cannot be used.", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Pokémon Race: 6 Pokémon compete in a race to the goal. Players can place bets for the winner to win the reward.", safchan); sys.sendMessage(src, "Requirements: None.", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Pokémon Bet Race: 6 Pokémon compete in a race to the goal. Players can place bets for the winner to win a better payout. Favorite has a better chance of winning, but lower payout, while Underdog has a lower chance of winning but with a higher payout.", safchan); sys.sendMessage(src, "Requirements: Money, Silver Coins or Items to place the bet.", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Battle Factory/LC Battle Factory: Each player is given 8 rental Pokémon. Players then battle each other in a tournament using 3 of those 8 Pokémon on each match (they are sent in the order picked; if not enough Pokémon are selected, the rest will be picked randomly). Rewards are given to the 1st, 2nd and 3rd place.", safchan); sys.sendMessage(src, "Requirements: Have caught at least 4 Pokémon. Minimum of 4 players to start, and 7 for the 3rd place reward.", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Quiz: On each round, a question is made and players must answer with a Pokémon. One answer per player, can't repeat answer within the round, first to answer receives more points. Lasts for 10 rounds, rewards are given to the 1st, 2nd and 3rd place.", safchan); sys.sendMessage(src, "Requirements: Have caught at least 4 Pokémon. Minimum of 3 players to start, and 7 for the 3rd place reward.", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Hidden Quiz: On each round, a question is made and players must answer with a Pokémon. One answer per player, answers are only revealed at the end of each round, repeated answers give less points. Lasts for 10 rounds, rewards are given to the 1st, 2nd and 3rd place.", safchan); sys.sendMessage(src, "Requirements: Have caught at least 4 Pokémon. Minimum of 3 players to start, and 7 for the 3rd place reward.", safchan); sys.sendMessage(src, "", safchan); sys.sendMessage(src, "Bingo: Players receive a 5x5 card with randomly picked Pokémon. Every few seconds, a random Pokémon is drawn. The first player to reach the goal of complete lines in their card with the drawn Pokémon and type /bingo will be the winner.", safchan); sys.sendMessage(src, "Requirements: Have caught at least 4 Pokémon. Minimum of 3 players to start, 5 for 2nd place reward, 7 for 3rd place reward.", safchan); sys.sendMessage(src, "", safchan); }; this.onHelp = function (src, topic, channel) { if (topic === "safari") { safari.showCommands(src, channel); return true; } return false; }; this.showCommands = function (src, channel) { var userHelp = [ "", "*** Safari Commands ***", "*** Informational Commands ***", "/help: For a how-to-play guide.", "/itemhelp [item or category]: Returns information on a particular item, costume, or category. You can display the help for all items using \"/itemhelp all\" or from the following categories: \"balls\", \"items\", \"perks\", \"costumes\".", "/info: View global information like time until next Contest, Contest's theme, current Gachapon jackpot prize.", "/eventhelp: For a explanation about events like Faction War and Pokémon Race.", "/contestrules: For information about Contest rules.", "/lastcontests: For information about the recent Contests played.", "/themes: View available Contest themes.", "/quest: To view available quests.", "/mission: To view your daily missions.", "/records: To view your records. Use \"/records earnings\" to show a break down of earnings by source.", "/leaderboard [type]: View the Safari Leaderboards. Type \"/leaderboard list\" to see all existing leaderboards.", "/trials: Shows you your current trials missions. Only works while trials is in session.", "/showmegas: Shows your currently Mega-Evolved Pokémon and their remaining Mega Evolution time.", "/economy [days ago]: Shows recent Safari economy statistics, up to the last 14 days. Defaults to today.", "*** Action Commands ***", "/start: To pick a starter Pokémon and join the Safari game. Valid starters are Bulbasaur, Charmander, and Squirtle.", "/bait: To throw bait in the attempt to lure a Wild Pokémon. Specify a ball type to throw that first.", "/catch [ball]: To throw a Safari Ball when a wild Pokémon appears. [ball] can be replaced with the name of any other ball you possess.", "/photo: To take photos of wild Pokémon! Use /album to view your photos.", "/use: To use a consumable item.", "/sell: To sell one of your Pokémon.", "/multisell: To sell off multiple Pokémon at once. Alias(es): /msell", "/turbosell: To easily sell off multiple Pokémon of the SAME SPECIES at once. Alias(es): /tsell", "/pawn: To sell specific items. Use /pawnall to sell all your pawnable items at once!", "/burn: To give a Burn Heal to another player. See /itemhelp Burn Heal for more information.", "/trade: To request a Pokémon trade with another player*. Use $200 to trade money and @luxury to trade items (use 3@luxury to trade more than 1 of that item).", "/tradeblock: To edit your tradeblocked list. You will instantly reject trade requests asking you for an Item/Pokémon you tradeblocked. Pokémon in this list cannot be sold with /sell. To reject all trades, use /options trade:off.", "/blacklist: To edit your player blacklist. You will instantly reject trade requests and battle requests from the users you blacklisted.", "/evolve: Use a Rare Candy (or candies) to evolve a Pokémon, which will give you Candy Dusts depending on the amount of Rare Candies used*.", "/spray: Use a Devolution Spray to devolve a Pokémon*.", "/mega [Pokémon*]: Use a Mega Stone to Mega Evolve a Pokémon. Use /mega [Pokémon*]:[X or Y] to choose between Mega Evolutions for species that have multiple.", "/cancelmega [Pokémon*]: Cancel a Mega Evolution instantly.", "/gacha: Use a ticket to win a prize!", "/finder: Use your Itemfinder to look for items. You can use \"/finder combo\" to power up your Itemfinder with your Consecutive Catch Combo, allowing you to use multiple charges at once. Note that this consumes your entire combo.", "/give [Berry Name]:[Party Slot]: Gives a berry to the Pokémon in the specified party slot (1 to 6). Party slot defaults to 1.", "/take [Party Slot]: Takes the held berry from the Pokémon in the specified party slot (1 to 6). Defaults to 1.", "/giveall [Berry Name]: Gives a berry to your entire party at once.", "/takeall: Takes the held berries from your entire party at once.", "/buy: To buy items or Pokémon from an NPC.", "/shop: To buy items or Pokémon from a another player.", "/shopadd: To add items or Pokémon to your personal shop. Use /shopremove to something from your shop, /shopclose to remove all items at once or /shopclean to remove all items out of stock.", "/price [Item/Pokémon]: To find an online shop selling the specified Item or Pokémon.", "/auction: To start an auction.", "/challenge: To challenge another player to a battle. Use /challenge2 for a Rotation Battle.", "/forfeit: To forfeit during a normal or rotation battle.", "/watch: To watch someone else's battle.", "/watchpyr: To watch someone else's Pyramid run", "/watchbak: To watch someone else's Baking quest", "/watchinvite: To invite someone to watch your current battle/Pyramid run/Baking quest. Note that invited users will be able to view your battle even if you have disabled Battle Visibility in your /options.", "*** Utility Commands ***", "/party: To add or remove a Pokémon from your party, set your party's leader*, or load a previously saved party. Type /party for more details.", "/find [criteria] [value]: To find Pokémon that you have that fit that criteria. Type /find for more details. Use /findt for a text-only version or /finds for a text version with links to sell them.", "/sort [criteria] [ascending|descending]: To sort the order in which the Pokémon are listed on /box. Criteria are Alphabetical, Number, BST, Type and Duplicate.", "/bst [pokémon]: To view the BST of a Pokémon and price you can sell a Pokémon.", "/box [number]: To view all your caught Pokémon organized in boxes. Use /boxt for a text-only version or /boxs for a text version with links to sell them. Use /options smallbox to toggle an option to use a narrower box width.", "/bag: To view all money and items. Use /bagt for a text-only version or /bagf for the full version (if the text-only version is your default)", "/costumes: To view your current costumes. Use /getcostume to check your records to see if you earned any new costumes!", "/medals: To view your current medals.", "/daycare: Displays the Daycare menu where you can deposit, retrieve, or interact with your Pokémon. You can deposit up to 2 Pokémon in the Daycare at once. Check the help button inside the command for more information. Alias(es): /dc.", "/changecostume [costume]: To change your costume to a new one. Can also use /dressup [costume].", "/showcostume [costume]: To show detailed information about the specified costume, such as its current skills and level. Shows your current costume info if no costume is specified.", "/inbox: To read the messages you received while offline.", "/base: To view another player's Secret Base.", "/editbase [decoration]։[Coordinate X]։[Coordinate Y]: To edit your Secret Base. Use /decorations to check what you can decorate it with.", "/view: To view another player's party. If no player is specified, all of your data will show up. Use /viewt for a text-only version of your data (excluding party).", "/mail [Name]։[Message]: To send a message to another player's inbox. Requires a Mail.", "/changealt: To pass your Safari data to another alt.", "/themespawns: Show Pokémon that appear in a specified theme.", "/themerares: Show rare Pokémon that appear in a specified theme.", "/abilityref [ability]: Shows Safari-specific effects for the specified Ability. If no Ability is specified, all Abilities that have effects will be listed.", "/options: View and set miscellaneous settings and options for Safari", "/contestforfeit: Allows you to withdraw from any ongoing Contest.", "/rockscare: Allows you to scare a wild Pokémon away. You can only scare Pokémon that haven't been interacted with for " + plural(rockScareThreshold/1000, "second") + ".", "/showdeluxe: View your " + finishName("deluxe") + " pools.", "/finddeluxe [Pokémon]: Search your " + finishName("deluxe") + " pools for a specific Pokémon.", "/selldeluxe: Sell off all your remaining " + finishName("deluxe") + ".", "/shroomcancel: Cancels the effect of any " + finishName("mushroom") + " that you have active.", "/viewers: Shows which users are currently spectating your battle/Pyramid run/Baking quest.", "/clearpending: Cancels any pending commands you have queued.", "/poketrack: To edit your Tracked Pokémon list. You will receive flashes when a Pokémon in the list spawns.", "/showpokemon [Pokémon]: View the sprite and full images of the specified Pokémon.", //seasonal change "*** Fun Commands ***", "/rock: To throw a rock at another player.", "/stick: To poke another player with your stick.", // "/hidelb [type]: To hide yourself from a specific leaderboard.", ]; var help = userHelp; var adminHelp = [ "*** Safari Warden Commands ***", "/startevent [type]։[parameters]: Starts an event. Use /startevent help for more details. To cancel an event, use /abortevent.", "/sanitize [player]: Removes invalid values from the target's inventory, such as NaN and undefined. Use /sanitizeall to sanitize everyone in the channel at once.", "/transferalt [name1]։[name2]: Changes Safari data between two players. Make sure they are the same person before using this.", "/allowname [name]: Allows/disallows a person to start a new Safari save while they share IP with another Safari Player.", "/tradeban [player]։[duration]: Bans a player from trading or using their shop. Use /tradeban [player]:[length]. Use -1 for length to denote permanent, 0 for length to unban. Use /tradebans to view players currently tradebanned.", "/salt [player]։[duration]: Reduces a player's luck to near 0 (unrelated to Salt item/leaderboard). Use /salt [player]:[length]. Use -1 for length to denote permanent, 0 for length to unban. Use /saltbans to view players currently saltbanned.", "/safariban [player]։[duration]: Bans a player from the Safari Channel. Use /safariunban [player] to unban and /safaribans to view players currently banned from Safari.", "/lock [player]: Locks a player's save, making their save not load until it's unlocked. Use /unlock [player] to unlock them.", "/lbban [player]: Removes a player from all leaderboards.", "/analyze [player]։[lookup]: Returns the value of a specified property relating to a person's save. Lookup follows object notation, leave blank to return the entire save's data.", "/track [player]: Adds a tracker to a player that sends a message every time they attempt to bait and throw a ball. Useful to catch botters.", "/trick [player]։[pokemon]։[amount]։[message]: Sends the designated player a fake wild Pokémon. Pokémon and amount are optional, defaults to random and 1. Message is an optional message such as \"Don't throw!\", defaults to nothing.", "/dqphoto [number]: Removes a photo request from Journal quest. Use if a request is too hard or not fulfilled after a long time.", "/offmsg [players]։[message]: Sets a message that will be sent to these players next time they join the channel.", "/watchbypass [player]: Bypasses a player's visibility settings to watch their battle. The player WILL be informed that you bypassed their settings and are watching their battle.", "/viewbypass [player]: Bypasses a player's visibility settings to view their party. The player WILL be informed that you bypassed their settings and are viewing their party.", "/istats [item]: Check item stats for a specific Safari item. Use /istatsr to filter out players who haven't logged in within the past 30 days.", "/safaripay [player]։[amount]: Awards a player with the specified amount of money.", "/safarigift [player/player names]։[item]։[amount]: Gifts a player with any amount of an item or ball. You can send to multiple players at once if you separate each name with a comma or a comma and a space.", "/bestow [player]։[pokemon]: Gifts a player a specific Pokémon. Use /bestow [player]։[pokemon]։Remove to confiscate a Pokémon from a player.", "Log Files: Use /command [amount]։[lookup]։[limit] to return a list of logged data. Defaults to 10. Lookup will only return logs with the specified value in the past amount of logs (can use && or || for multiples terms). Limit will restrict the number of results displayed even if more than that is found (defaults to 100).", "Available logs: ***tradelog (trades), raretrades (trades involving legendaries, shinies or rare forms), shoplog (shop transactions), auctionlog (auctions), lostlog (actions that led to a Pokémon being lost), mythlog (rare spawns and Masterball usage), altlog (save transfers), eventlog (events), giftlog (gifts or values edited), crosslog (cross-promotion rewards), showids (saves created with their idnum), questlog (quests finished), misclog (other stuff)" ]; var ownerHelp = [ "*** Safari Owner Commands ***", "/contest[soft]: Force starts a Safari Contest. Use /contestsoft to skip broadcasting to Tohjo Falls.", "/precontest: Enter the pre-contest phase. Use /skipcontest to cancel the pre-contest phase and skip the Contest.", "/wild[event] [pokemon (optional)]։[amount]։[disguise]: Spawns a random or designated wild Pokémon with no restrictions. Use /wildevent for a spawn that cannot be caught with Master Balls. Amount must be between 1 and 4, else defaults to 1. Disguise is optional and makes the spawned Pokémon appear as something it is not.", // "/horde: Spawns a group of random wild Pokémon with no restrictions. Use a valid dex number to spawn a specific Pokémon.", "/checkrate [item]: Displays the rate of obtaining that item from Gachapon, Itemfinder, and Prize Pack.", "/editdata [type]։[item]։[property]։[value]: Changes a property from an item/costume.", "/forgerecord [player]։[record]։[amount]: Alters a specific record of a player. Use /forgerecordall [record]:[amount] to change it for all players.", "/wipesafari [player]: Wipes the targeted player's safari. Irreversable-ish. Use /bwipesafari or /wipesafarib to broadcast the wipe message.", "/loadsafari [JSON]: Creates a safari save with the specified JSON code.", "/findsaves: Lists all saves the Safari Game currently has data on.", "/checksaves [user1, user2, etc.]: Checks a list of users to see if they have a save file.", "/showids [amount]։[lookup]: Shows all players by their idnum. Use /reloadids to recreate the list if necessary.", "/updatelb: Manually updates the leaderboards.", "/newweek: Manually verifies if the month changed to reset weekly leaderboards.", "/ongoing: To verify ongoing NPC Battles and Auction (use before updating Safari). Use /stopongoing to cancel all ongoing Battles and Auctions, or /stopplayer [player] to only stop activities from a specific player.", "/clearcd [player]։[type]: To clear a player's cooldown on a quest/ball throw/auction.", "/scare: Scares the wild Pokemon away. Use /glare for a silent action.", "/npc[add/remove] [item/pokemon]։[price]։[limit]: Adds or removes an item to the NPC shop with the provided arguments. Use /npcclose to clear the NPC shop or /npcclean to remove items out of stock.", "/addrecipe: Adds/edits a recipe for the Alchemist quest. Use /removerecipe [name] to remove a recipe and /showrecipes to view information about current recipes.", "/addraffle [pokemon]: Changes the current raffle prize to the specified Pokémon.", "/cancelraffle: Clears the current raffle prize. To completely cancel a raffle use /cancelraffle clearfile:[amount], where an optional refund amount can be specified to credit back raffle ticket holders.", "/checkraffle: To see the Raffle Tickets sold so far.", "/drawraffle confirm: Draws the current raffle.", "/setraffledate [string]: Set the estimated raffle draw date.", "/dqraffle [player]։[refund]: Disqualifies a person from the current raffle by removing their name from the raffle players hash and by removing all their current entries. Refund is optional and will refund at the specified rate (Defaults to 0, or no refund).", "/disablequest [quest/all/long]: Disables specified quest, or all of them, or just long ones (Pyramid/Tower). Use /enablequest to re-enable. Updating the script will re-enable all quests.", "/updateafter [abort/cancel/stop]: Updates Safari at next available opportunity. Use any of the command data listed to cancel the pending update.", "/nextspawn [player]։[pokemon]։[amount]։[disguise]: Makes a player's next spawn equal to [amount] of [pokemon] diguised as [disguise]. Amount and Disguse are optional. Affects Bait and Gacha.", "/loginmercy [number]: Sets a number of days that will not break the player's consecutive login if they miss it (use when server is down for too long).", "/refundLogin [player]։[previousStreak]։[daysToAdd]: Changes a player's consecutive logins streak and give them the appropriate rewards.", "/enabletrials: Enables trials session. Make sure trials is loaded first. Use /disabletrials to turn it off (this doesn't wipe data).", "/loadtrials [JSON]: Loads trials data from a json file", "/releasetrial [player]:[id]: Removes target ID from player's trials listing if they have it, and prevents them from getting it again even if they don't.", "/trialspoints [player]:[number]: Set their points.", "/trialsbonus [player]: Gives the target player a bonus 10 points (can only be used once per session.", "/finishtrials: Ends trials and immediately gives out prizes. Don't forget to /disabletrials afterwards.", "/excludeeconomy [player]: Excludes/includes a player from/in economy statistics. Toggles against their current status.", "/showdetective: Prints the current safari.detectiveData object.", "/resetdetective [player's idnum]: Resets the detective data of the user with the provided idnum." //"/tourgift [1st], [2nd], [3rd]: Distributes current prize grid for Tournaments promotion to event winners. Please check save files and spelling before distributing prizes as undoing this takes a bit of effort!", //"/preptour [number, optional]: Checks the saves of the past number of event tours and provides an easy gifting link. If a name is not a valid save, it will be bolded and \"/tourgift\" will print to make substituting easy!" ]; if (SESSION.channels(safchan).isChannelAdmin(src)) { help.push.apply(help, adminHelp); } if (SESSION.channels(safchan).isChannelOwner(src)) { help.push.apply(help, ownerHelp); } for (var x = 0; x < help.length; x++) { sys.sendMessage(src, help[x], channel); } }; this.handleCommand = function (src, message, channel) { var command; var commandData = "*"; var pos = message.indexOf(' '); if (pos !== -1) { command = message.substring(0, pos).toLowerCase(); commandData = message.substr(pos + 1); } else { command = message.substr(0).toLowerCase(); } if (channel !== safchan && ["safariban", "safariunban", "aliases"].indexOf(command) === -1) { return false; } if (SESSION.channels(safchan).muteall && !SESSION.channels(safchan).isChannelOwner(src)) { safaribot.sendMessage(src, "You can't play Safari while the channel is silenced.", safchan); return true; } //Deal with Type: Null input according to command if (["find", "finds", "findt", "findd"].contains(command)) { commandData = typeNull(commandData, TYPE_NULL_NAME); } else { commandData = typeNull(commandData); } //User commands if (!safariUpdating || SESSION.channels(safchan).isChannelOwner(src)) { if (command === "me" || command === "rainbow" || command === "fullrainbow") { if (commandData === "*") { return true; //retain server behavior } if (!SESSION.channels(safchan).isChannelOwner(src)) { sys.sendAll(sys.name(src) + ": " + commandData, safchan); return true; } } if (command === "tutorial") { var player = getAvatar(src); if (!player || !player.tutorial.inTutorial) { return false; } safari.progressTutorial(src, player.tutorial.step, commandData); return true; } if (command === "skiptutorial") { var player = getAvatar(src); if (!player || !player.tutorial.inTutorial) { return false; } safari.skipTutorial(src, commandData); return true; } if (command === "help") { safari.showHelp(src); return true; } if (command === "itemhelp") { safari.showItemHelp(src, commandData); return true; } if (command === "eventhelp") { safari.showEventHelp(src, commandData); return true; } if (command === "start") { safari.startGame(src, commandData); return true; } if (["contestforfeit", "contestwithdraw", "conff", "contestff"].contains(command)) { safari.forfeitContest(src, commandData); return true; } if (["shroomcancel", "cancelshroom", "mushroomcancel", "cancelmushroom"].contains(command)) { safari.cancelShroom(src); return true; } if (command === "catch" || command === "throw" || command === "c" || command === ccatch || command === ccatch2) { safari.throwBall(src, commandData, null, null, command); return true; } if (["cancel", "cc"].contains(command)) { safari.throwBall(src, "cancel", null, null, command); return true; } if (["spiritduel", "spiritduels", "sduel", "sduels"].contains(command)) { var info = commandData.split(":"); safari.spiritDuelsCommand(src, info[0], info.slice(1).join(":")); return true; } if (command === "spiritskill" || command === "spiritskills") { safari.chooseSpiritSkill(src, commandData); return true; } if (command === "bonuslogin" || command === "blogin") { safari.bonusLoginPrint(src); return true; } if (command === "towertrouble" || command === "towertroubles") { safari.towerTroubleCommand(src, commandData); return true; } if (command === "photo" || command === "p") { safari.takePhoto(src, commandData, false, false, command); return true; } if (command === "pokeblock") { safari.throwPokeblock(src); return true; } if (["rockwild", "rockscare"].contains(command)) { safari.rockScare(src); return true; } if (["options", "settings", "o"].contains(command)) { safari.configurePlayerOptions(src, commandData); return true; } if (["viewers", "showviewers", "currentviewers", "watchers", "showwatchers", "currentwatchers"].contains(command)) { safari.showCurrentViewers(src); return true; } if (command === "sell") { safari.sellPokemon(src, commandData); return true; } if (command === "unsell") { safari.undoSell(src); return true; } if (command === "selldeluxe") { var player = getAvatar(src); var amt = player.balls.deluxe; if (amt == 0) { safaribot.sendHtmlMessage(src, "You don't have any Deluxe Baits to sell!", safchan); return true; } var value = player.deluxeBait.rares.rate || 0.01; value = Math.round(25 + ((value + 0.5) * 10) + (Math.max((value + 1.5) * 7))); var cost = amt * value; if (commandData !== "confirm") { safaribot.sendHtmlMessage(src, "You can sell your " + plural(amt, "Deluxe Bait") + " for a total of $" + cost + "!", safchan); safaribot.sendHtmlMessage(src, "Type " + link("/selldeluxe confirm", false, true) + " to confirm it.", safchan); return true; } player.balls.deluxe = 0; player.money += cost; safari.updateEconomyData(cost, "playerPawn"); if (player.money > moneyCap) { player.money = moneyCap; } safaribot.sendHtmlMessage(src, "You sold your " + amt + " Deluxe Baits for a total of $" + cost + "! You now have $" + addComma(player.money) + "!", safchan); safari.saveGame(player); return true; } if (command === "buy") { safari.buyFromShop(src, ":" + commandData, command, true); return true; } if (command === "getcostume" || command === "getcostumes") { safari.getCostumes(src); return true; } if (command === "showcostume" || command === "showcostumeinfo" || command === "skills") { safari.showCostumeInfo(src, commandData); return true; } if (command === "shop") { safari.buyFromShop(src, commandData, command); return true; } var shopCommands = ["shopadd", "addshop", "shopremove", "removeshop", "closeshop", "shopclose", "cleanshop", "shopclean"]; if (shopCommands.contains(command)) { var action = "remove"; switch (command) { case "shopadd": case "addshop": action = "add"; break; case "shopclose": case "closeshop": action = "close"; break; case "shopclean": case "cleanshop": action = "clean"; break; case "shopremove": case "removeshop": action = "remove"; break; } safari.editShop(src, action + ":" + commandData); return true; } if (command === "price") { safari.checkShops(src, commandData, command); return true; } if (command === "pawn" || command === "pawnall") { safari.sellItems(src, command === "pawnall" ? "all" : commandData); return true; } if (command === "trade") { safari.offerTrade(src, commandData); return true; } if (command === "tradeblock") { safari.blacklistTrade(src, commandData); return true; } if (command === "blacklist") { safari.blacklistPlayer(src, commandData); return true; } if (["trackpoke", "poketrack", "pokeflash", "flashpoke"].contains(command)) { safari.trackSpawn(src, commandData); return true; } if (command === "auction") { safari.createAuction(src, commandData); return true; } if (command === "join") { safari.joinAuction(src, commandData); return true; } if (command === "bid") { safari.bidAuction(src, commandData); return true; } if (command === "leave") { safari.quitAuction(src, commandData); return true; } if (command === "signup") { if (currentEvent && !stopEventJoins) { currentEvent.join(src, commandData); } else { safaribot.sendMessage(src, "There's no event going on!", safchan); } return true; } if (command === "unjoin") { if (currentEvent) { currentEvent.unjoin(src); } else { safaribot.sendMessage(src, "There's no event going on!", safchan); } return true; } if (command === "vol") { if (currentGame) { currentGame.handleCommand(src, commandData); } else { safaribot.sendMessage(src, "There's no game going on!", safchan); } return true; } if (command === "party") { safari.manageParty(src, commandData); return true; } if (command === "add") { safari.manageParty(src, "add:" + commandData); return true; } if (command === "remove") { safari.manageParty(src, "remove:" + commandData); return true; } if (command === "pload") { safari.manageParty(src, "load:" + commandData); return true; } if (command === "psave") { safari.manageParty(src, "save:" + commandData); return true; } if (command === "pdelete") { safari.manageParty(src, "delete:" + commandData); return true; } if (command === "active") { safari.manageParty(src, "active:" + commandData); return true; } if (command === "qload") { safari.quickLoadParty(src, commandData); return true; } if (["giveitem", "give", "giveberry"].contains(command)) { var berry, slot; berry = commandData.split(":")[0]; slot = commandData.split(":")[1] || 0; safari.giveItem(src, berry, slot); return true; } if (["giveitemall", "giveall", "giveberryall"].contains(command)) { var player = getAvatar(src); for (var i = 0; i < player.party.length; i++) { safari.giveItem(src, commandData, i+1); } return true; } if (["takeitem", "take", "takeberry"].contains(command)) { safari.takeItem(src, commandData); return true; } if (["takeitemall", "takeall", "takeberryall"].contains(command)) { var player = getAvatar(src); for (var i = 0; i < player.helds.length; i++) { safari.takeItem(src, i+1); } return true; } if (command === "view" || command === "mydata" || command === "viewt" || command === "mydatat") { if (command === "viewt" && commandData !== "*") { safari.viewPlayer(src, commandData, true); } else if (command !== "view" || commandData === "*") { safari.viewOwnInfo(src, (command === "viewt" || command === "mydatat")); } else { safari.viewPlayer(src, commandData); } return true; } if (command === "bag" || command === "bagt" || command === "bagf") { safari.viewItems(src, command, commandData); return true; } if (command === "box" || command === "boxt" || command === "boxs") { safari.viewBox(src, commandData, (command === "boxt" || command === "boxs"), command === "boxs"); return true; } if (command === "cherish" || command === "cherisht" || command === "cherished") { safari.viewCherished(src, command === "cherisht"); return true; } if (command === "album") { safari.viewPhotos(src, commandData); return true; } if (command === "costumes" || command === "costume") { safaribot.sendHtmlMessage(src, safari.showCostumes(src), safchan); return true; } if (command === "dressup" || command === "changecostume") { safari.changeCostume(src, commandData); return true; } if (command === "changealt") { safari.changeAlt(src, commandData); return true; } if (command === "themespawn" || command === "themespawns") { safari.showThemeSpawns(src, commandData); return true; } if (command === "themerare" || command === "themerares") { safari.showThemeRares(src, commandData); return true; } if (["abilityreference", "abilityref", "abref", "abreference"].contains(command)) { commandData = commandData.split(":"); var ability = commandData[0], pageNum = commandData[1] || 0; safari.showAbilityReference(src, ability, pageNum); return true; } if (command === "bait" || command === "b") { safari.throwBait(src, commandData, command); return true; } if (["dbait", "deluxe", "deluxebait", "db"].contains(command)) { safari.throwBait(src, commandData, command, false, false, false, true); return true; } if (command === "gbait" || command === "golden" || command === "gb") { safari.throwBait(src, commandData, command, true, false, false, false); return true; } if (["showdbait", "showdeluxe", "showdeluxebait", "viewdeluxe", "viewdeluxebait", "viewdbait"].contains(command)) { var player = getAvatar(src); if (!player) { return true; } safaribot.sendHtmlMessage(src, "Current " + finishName("deluxe") + " Data:", safchan); safaribot.sendHtmlMessage(src, "- Commons (" + toFixed(player.deluxeBait.commons.rate, 3) + "%): " + player.deluxeBait.commons.list.map(function(x) { return poke(x) }).join(", "), safchan); safaribot.sendHtmlMessage(src, "- Uncommons (" + toFixed(player.deluxeBait.uncommons.rate, 3) + "%): " + player.deluxeBait.uncommons.list.map(function(x) { return poke(x) }).join(", "), safchan); safaribot.sendHtmlMessage(src, "- Rares (" + toFixed(player.deluxeBait.rares.rate, 5) + "%): " + player.deluxeBait.rares.list.map(function(x) { return poke(x) }).join(", "), safchan); if (player.deluxeBait.inedible > 0) { safaribot.sendHtmlMessage(src, "- Somewhat inedible (" + toFixed(player.deluxeBait.inedible * 100, 5) + "%)", safchan); } return true; } if (["finddbait", "finddeluxe", "finddeluxebait", "searchdeluxe", "searchdeluxebait", "searchdbait"].contains(command)) { var player = getAvatar(src); if (!player) { return true; } if (commandData === "*") { safaribot.sendMessage(src, "Enter a Pokémon name to search for!", safchan); return true; } var num = getInputPokemon(typeNull(commandData)).num; if (!num) { safaribot.sendMessage(src, "Enter a valid Pokémon name to search for!", safchan); return true; } if (!player.deluxeBait.commons.list.concat(player.deluxeBait.uncommons.list, player.deluxeBait.rares.list).contains(num)) { safaribot.sendHtmlMessage(src, pokeInfo.icon(num) + poke(num) + " is not in your current " + finishName("deluxe") + " pool.", safchan); return true; } else { var pools = []; if (player.deluxeBait.commons.list.contains(num)) { pools.push("Common (" + toFixed(player.deluxeBait.commons.rate, 3) + "%)"); } if (player.deluxeBait.uncommons.list.contains(num)) { pools.push("Uncommon (" + toFixed(player.deluxeBait.uncommons.rate, 3) + "%)"); } if (player.deluxeBait.rares.list.contains(num)) { pools.push("Rare (" + toFixed(player.deluxeBait.rares.rate, 3) + "%)"); } safaribot.sendHtmlMessage(src, pokeInfo.icon(num) + poke(num) + " is currently in your " + readable(pools) + " " + finishName("deluxe") + " pool.", safchan); } return true; } if (["clearpending", "cancelpending", "deletepending"].contains(command)) { var player = getAvatar(src); if (!player) { return true; } safari.deletePendingActive(player.id, "all"); safaribot.sendMessage(src, "Your pending commands have been cleared.", safchan); return true; } if (command === "ballmacro") { if (!currentPokemon) { return true; } ballMacro(src); return true; } if (command === "rock" || command === "snowball" || command === "snow") { safari.throwRock(src, commandData); return true; } if (command === "stick") { safari.useStick(src, commandData); return true; } if (command === "burn") { safari.giveBurnHeal(src, commandData); return true; } if (command === "gacha" || command === "g") { safari.gachapon(src, commandData); return true; } if (command === "evolve") { safari.useCandy(src, commandData); return true; } if (command === "spray" || command === "devolve" || command === "devolution" || command === "devolutionspray") { safari.useSpray(src, commandData); return true; } if (command === "mega" || command === "megastone") { safari.useMegaStone(src, commandData); return true; } if (["cancelmega", "megacancel", "demega"].contains(command)) { safari.cancelMega(src, commandData); return true; } if (command === "challenge" || command === "challenge2") { safari.challengePlayer(src, commandData, command === "challenge2", null, command); return true; } if (command === "challenge2sun") { safari.challengePlayer(src, commandData, true, {sun: true}, command); return true; } if (command === "challenge2rain") { safari.challengePlayer(src, commandData, true, {rain: true}, command); return true; } if (command === "challenge2sand" || command === "challenge2sandstorm") { safari.challengePlayer(src, commandData, true, {sandstorm: true}, command); return true; } if (command === "challenge2hail") { safari.challengePlayer(src, commandData, true, {hail: true}, command); return true; } if (command === "challenge3" || command === "challengetag") { safari.challengePlayerTag(src, commandData); return true; } if (command === "buylucky" || command === "buyluckycoins" || command === "buynpcbet") { safari.buyLuckyCoins(src, commandData); return true; } if (command === "npcbet" || command === "betnpc" || command === "betceleb") { safari.betCelebrity(src, commandData); return true; } if (command === "mybet" || command === "mybets" || command === "mycelebritybets") { safari.myCelebrityBets(src); return true; } if (command === "movenum") { getInputMove(src, commandData); return true; } if (command === "abilitynum") { getInputAbility(src, commandData); return true; } if (command === "watch") { if (currentEvent && commandData === "*") { currentEvent.watchEvent(src); } else { safari.watchBattle(src, commandData); } return true; } if (command === "watchinvite") { safari.watchInvite(src, commandData); return true; } if (command === "softwatch") { //safari.watchBattle(src, commandData, true); return true; } if (command === "watchpyr") { if (commandData === "*") { safaribot.sendMessage(src, "Type which player's Pyramid run to watch or unwatch with /watchpyr [name]!", safchan); } else { var user = sys.name(src).toLowerCase(); var player = getAvatarOff(commandData); if (!validPlayers("self", src)) { return; } var self = getAvatar(src); if (!(player)) { safaribot.sendMessage(src, "No player named " + commandData + " found!", safchan); return true; } var getPyramid = false; for (var a in currentPyramids) { if (currentPyramids[a].isInPyramid(player.id)) { getPyramid = currentPyramids[a]; } } if (!(getPyramid)) { safaribot.sendMessage(src, player.id + " is not in the Pyramid!", safchan); return true; } if (getPyramid.isInPyramid(user)) { safaribot.sendMessage(src, "You can't watch this Pyramid run because you are in it!", safchan); return true; } if (!getPyramid.viewers.contains(user)) { if (self.idnum in getPyramid.watchCounts) { if (getPyramid.watchCounts[self.idnum] >= watchCountMax) { safaribot.sendMessage(src, "You can only watch this Pyramid run {0}!".format(plural(watchCountMax, "time")), safchan); return true; } getPyramid.watchCounts[self.idnum] += 1; } else { getPyramid.watchCounts[self.idnum] = 1; } //safaribot.sendMessage(src, "You are now watching " + player.id + "'s Pyramid run!", safchan); getPyramid.viewers.push(user); getPyramid.sendToViewers(sys.name(src) + " is watching this Pyramid run!"); return true; } else { //safaribot.sendMessage(src, "You are no longer watching " + player.id + "'s Pyramid run!", safchan); getPyramid.sendToViewers(sys.name(src) + " stopped watching this Pyramid run!"); getPyramid.viewers.splice(getPyramid.viewers.indexOf(user), 1); return true; } } return true; } if (["watchbak", "watchbake", "watchbaking"].contains(command)) { if (commandData === "*") { safaribot.sendMessage(src, "Type which player's Baking quest to watch or unwatch with /watchbak [name]!", safchan); } else { var user = sys.name(src).toLowerCase(); var player = getAvatarOff(commandData); if (!validPlayers("self", src)) { return; } var self = getAvatar(src); if (!(player)) { safaribot.sendMessage(src, "No player named " + commandData + " found!", safchan); return true; } var getBake = false; for (var a in currentBakings) { if (currentBakings[a].isInKitchen(player.id)) { getBake = currentBakings[a]; } } if (!(getBake)) { safaribot.sendMessage(src, player.id + " is not in a Baking quest!", safchan); return true; } if (getBake.isInKitchen(user)) { safaribot.sendMessage(src, "You can't watch this Baking quest because you are in it!", safchan); return true; } if (!getBake.viewers.contains(user)) { if (self.idnum in getBake.watchCounts) { if (getBake.watchCounts[self.idnum] >= watchCountMax) { safaribot.sendMessage(src, "You can only watch this Baking quest {0}!".format(plural(watchCountMax, "time")), safchan); return true; } getBake.watchCounts[self.idnum] += 1; } else { getBake.watchCounts[self.idnum] = 1; } getBake.viewers.push(user); getBake.msgAll(sys.name(src) + " is watching this Baking quest!"); return true; } else { getBake.msgAll(sys.name(src) + " stopped watching this Baking quest!"); getBake.viewers.splice(getBake.viewers.indexOf(user), 1); return true; } } return true; } if (command === "medals" || command === "feathers" || command === "viewmedals" || command === "viewfeathers") { safari.viewMedals(src); return true; } if (command === "featuremedal" || command === "featurefeather") { safari.featureMedal(src, sys.name(src), commandData); return true; } if (command === "removemedal" || command === "removefeather" || command === "discardmedal") { safari.removeMedal(src, sys.name(src), commandData); return true; } if (["decoration", "decorations", "deco", "decos"].contains(command)) { safari.viewDecorations(src, commandData); return true; } if (command === "base") { safari.viewBase(src, commandData); return true; } if (command === "editbase") { safari.editBase(src, commandData); return true; } if (command === "quest" || command === "quests" || command === "q") { safari.questNPC(src, commandData); return true; } if (command === "story" || command === "openstory") { underConstruction(src); return true; safari.openStory(src, commandData); return true; } if (command === "storygo" || command === "storynext") { underConstruction(src); return true; safari.storyStep(src, parseInt(commandData, 10)); return true; } if (command === "closestory" || command === "quitstory") { underConstruction(src); return true; safari.closeStory(src); return true; } if (command === "mission" || command === "missions" || command === "m") { safari.viewMissions(src, commandData); return true; } if (command === "swapmission") { safari.swapMission(src, commandData); return true; } if (command === "trials" || command === "trial") { safari.viewTrials(src, commandData); return true; } if (command === "forfeit" || command === "ff") { safari.forfeitBattle(src, commandData); return true; } if (command === "sort") { safari.sortBox(src, commandData); return true; } if (command === "find") { safari.findPokemon(src, commandData); return true; } if (command === "findt") { safari.findPokemon(src, commandData, true); return true; } if (command === "finds") { safari.findPokemon(src, commandData, true, true); return true; } if (command === "findd") { safari.findPokemon(src, commandData, false, false, true); return true; } if (command === "finddt") { safari.findPokemon(src, commandData, true, false, true); return true; } if (command === "findds") { safari.findPokemon(src, commandData, true, true, true); return true; } if (command === "multisell" || command === "msell") { safari.multiSellPokemon(src, commandData); return true; } if (command === "turbosell" || command === "tsell") { safari.turboSellPokemon(src, commandData); return true; } if (command === "trivia") { var info = commandData.split(":"); if (info.length !== 2) { safaribot.sendMessage(src, "The format for this command is /trivia [pokemon]:[trivia].", safchan); return true; } var mon = getInputPokemon(info[0]).num; if (!mon) { safaribot.sendMessage(src, "There is no such Pokémon!", safchan); return true; } if (!(triviaData.hasOwnProperty(mon+""))) { triviaData[mon+""] = {}; } var fact = info[1].replace(/pokemon/gi, "Pokémon"); if (!/[.!?]$/.test(fact)) { fact += "."; } triviaData[mon+""][fact] = false; safaribot.sendMessage(src, "Submitted Trivia for " + poke(mon) + ": " + fact + "!", safchan); return true; } if (command === "enterdata") { var info = commandData.split(":"); var title = info[0]; if (!safari.dataDumps.hasOwnProperty(title)) { safaribot.sendMessage(src, "The subject " + title + " doesn't currently exist!", safchan); return true; } if (info.length !== 3) { safaribot.sendMessage(src, "The format for this command is /enterdata [subject]:[pokemon]:[categories].", safchan); var e = 0; for (var c = 1; c <= 802; c++) { if (!safari.dataDumps[title][c+""].Completed) { safaribot.sendMessage(src, "Enter data for " + pokePlain(c) + " with " + "/enterdata " + title + ":" + c + ":[Categories].", safchan); e++; } if (e > 4) { break; } } return true; } var mon = pokeInfo.species(getInputPokemon(info[1]).num); if (!mon) { safaribot.sendMessage(src, "There is no such Pokémon!", safchan); return true; } if (safari.dataDumps[title][mon+""].Completed) { safaribot.sendMessage(src, "That Pokémon's data is already completed for this subject!", safchan); return true; } var categories = info[2].split(","); if (!categories) { safaribot.sendMessage(src, "Please enter categories!", safchan); return true; } safari.dataDumps[title][mon+""].Categories = []; for (var c in categories) { safari.dataDumps[title][mon+""].Categories.push(categories[c]); } safari.dataDumps[title][mon+""].Submitter = sys.name(src); safari.dataDumps[title][mon+""].Completed = true; return true; } if (command === "enterdata2") { var info = commandData.split(":"); var title = info[0]; if (!safari.dataDumps2.hasOwnProperty(title)) { safaribot.sendMessage(src, "The subject " + title + " doesn't currently exist!", safchan); return true; } if (info.length !== 3) { safaribot.sendMessage(src, "The format for this command is /enterdata2 [subject]:[move]:[categories].", safchan); var e = 0; for (var c = 1; c <= 621; c++) { if (!safari.dataDumps2[title][c+""].Completed) { safaribot.sendMessage(src, "Enter data for " + moveOff(c) + " with " + "/enterdata2 " + title + ":" + c + ":[Categories].", safchan); e++; } if (e > 4) { break; } } return true; } var m; if (movenum(info[1])) { m = movenum(info[1]); } else { m = info[1]; } var move = moveOff(m); var moveNum = movenum(move); if (!(moveNum >= 1)) { safaribot.sendMessage(src, "There is no such move!", safchan); return true; } if (safari.dataDumps2[title][moveNum+""].Completed) { safaribot.sendMessage(src, "That move's data is already completed for this subject!", safchan); return true; } var categories = info[2].split(","); if (!categories) { safaribot.sendMessage(src, "Please enter categories!", safchan); return true; } safari.dataDumps2[title][moveNum+""].Categories = []; for (var c in categories) { safari.dataDumps2[title][moveNum+""].Categories.push(categories[c]); } safaribot.sendMessage(src, "Added category " + categories + " to " + move + ".", safchan); safari.dataDumps2[title][moveNum+""].Submitter = sys.name(src); safari.dataDumps2[title][moveNum+""].Completed = true; return true; } if (command === "leaderboard" || command == "lb") { var rec = commandData.toLowerCase(), e; var capAll = function(str) { return str.split(" ").map(cap).join(" "); }; if (!rec || rec === "*" || rec === "list" || rec === "ls") { sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Existing leaderboards (type /lb [type] for the list): ", safchan); for (e in leaderboardTypes) { safaribot.sendHtmlMessage(src, link("/lb " + leaderboardTypes[e].alias, capAll(leaderboardTypes[e].alias)) + ": Leaderboard " + leaderboardTypes[e].desc + (monthlyLeaderboardTypes.hasOwnProperty(e) ? " | " + link("/lb " + monthlyLeaderboardTypes[e].alias, capAll(monthlyLeaderboardTypes[e].alias)) + " | " + link("/lb " + monthlyLeaderboardTypes[e].lastAlias, capAll(monthlyLeaderboardTypes[e].lastAlias)) : ""), safchan); } sys.sendMessage(src, "", safchan); return true; } var info = rec.split(":"); rec = info[0]; var lbKeys = Object.keys(leaderboards); var lbData = leaderboardTypes; var recName = rec, desc; var weekly, last; var lowCaseKeys = lbKeys.map(function(x) { return x.toLowerCase(); }); if (lowCaseKeys.indexOf(rec) !== -1) { rec = recName = lbKeys[lowCaseKeys.indexOf(rec)]; desc = lbData[rec].desc; } else { var found = false; for (e in leaderboards) { if (e.indexOf("Weekly") >= 0) { recName = e.substr(0, e.indexOf("Weekly")); if (recName in monthlyLeaderboardTypes && monthlyLeaderboardTypes[recName].alts.indexOf(rec) !== -1) { rec = e; found = true; lbData = monthlyLeaderboardTypes; desc = lbData[recName].desc; weekly = true; break; } } else if (e.indexOf("Last") >= 0) { recName = e.substr(0, e.indexOf("Last")); if (recName in monthlyLeaderboardTypes && monthlyLeaderboardTypes[recName].lastAlias == rec.toLowerCase()) { rec = e; found = true; lbData = monthlyLeaderboardTypes; desc = lbData[recName].lastDesc; last = true; break; } } else if (leaderboardTypes[e].alts.indexOf(rec) !== -1) { rec = recName = e; found = true; desc = lbData[rec].desc; break; } } if (!found) { rec = recName = "pokesCaught"; desc = lbData[rec].desc; } } var range = info.length > 1 ? getRange(info[1]) : { lower: 1, upper: 10 }; var self = sys.name(src).toLowerCase(); if (!range) { range = { lower: 1, upper: 10 }; if (info.length > 1) { self = info[1].toLowerCase(); } } var list = getArrayRange(leaderboards[rec], range.lower, range.upper); var out = ["", "" + (recName == "salt" ? "Safari Lameboards " : "Safari Leaderboards ") + desc + "" + (lastLeaderboardUpdate ? " (last updated: " + lastLeaderboardUpdate + ")" : "")], selfFound = false; var sign = (lbData[recName].isMoney ? "$" : ""); var value; var celebrityLBs = ["celebrityScore", "celebrityScoreEasy", "celebrityScoreHard", "celebrityScoreExpert", "celebrityScoreSuperExpert", "celebrityScoreAbyssal"]; for (e = 0; e < list.length; e++) { value = typeof list[e].value === "number" ? list[e].value : list[e].value.value; if (recName === "fastestCaseSolved") { value = timeString(value / 1000, true); } else if (celebrityLBs.contains(recName) && (weekly || last)) { var defeated = list[e].value.defeated; defeated = defeated % 1 === 0 ? defeated : defeated.toFixed(1); value = "{0} defeated ({1} on final opponent on {2})".format(plural(value, "trainer"), plural(defeated, "KO"), new Date(list[e].value.time).toUTCString()); } out.push("" + (list[e].pos) + ". " + toColor(list[e].fullName + ":", list[e].color) + " " + sign + addComma(value)); if (list[e].name == self) { selfFound = true; } } if (!selfFound) { list = leaderboards[rec]; for (e = 0; e < list.length; e++) { if (list[e].name == self) { value = typeof list[e].value === "number" ? list[e].value : list[e].value.value; if (recName === "fastestCaseSolved") { value = timeString(value / 1000, true); } else if (celebrityLBs.contains(recName) && (weekly || last)) { var defeated = list[e].value.defeated; defeated = defeated % 1 === 0 ? defeated : defeated.toFixed(1); value = "{0} defeated ({1} on final opponent on {2})".format(plural(value, "trainer"), plural(defeated, "KO"), new Date(list[e].value.time).toUTCString()); } var entry = "" + (list[e].pos) + ". " + toColor(list[e].fullName + ":", list[e].color) + " " + sign + addComma(value); if (e < range.lower) { out.splice(2, 0, entry); } else { out.push(entry); } selfFound = true; break; } } if (!selfFound) { out.push((self == sys.name(src).toLowerCase() ? "You are" : self.toCorrectCase() + " is" ) + " not ranked in this leaderboard!"); } } out.push(""); sys.sendHtmlMessage(src, out.join("
"),safchan); return true; } /* if (command === "hidelb") { if (!validPlayers("self", src)) { return; } var player = getAvatar(src); if (commandData === "*") { if (player.hideLB.length > 0) { safaribot.sendMessage(src, "You are currently hidden on the following leaderboards: " + player.hideLB.map(function(x) { return leaderboardTypes[x].alias; }).join(", "), safchan); } else { safaribot.sendMessage(src, "You are currently visible on all leaderboards!", safchan); } safaribot.sendMessage(src, "To hide/show yourself on a leaderboard, use /hidelb [LearboardName].", safchan); return true; } var n = commandData.toLowerCase(); var lbname; for (var l in leaderboardTypes) { if (leaderboardTypes[l].alias === "n" || leaderboardTypes[l].alts.contains(n)) { lbname = l; break; } } if (!lbname) { safaribot.sendMessage(src, "This is not a valid leaderboard!", safchan); return true; } if (lbname === "salt") { safaribot.sendMessage(src, "You cannot hide yourself from this leaderboard!", safchan); return true; } if (player.hideLB.contains(lbname)) { player.hideLB.splice(player.hideLB.indexOf(lbname), 1); safaribot.sendMessage(src, "You are back to the the " + leaderboardTypes[lbname].alias + " leaderboard!", safchan); } else { player.hideLB.push(lbname); safaribot.sendMessage(src, "You are now hidden from the " + leaderboardTypes[lbname].alias + " leaderboard!", safchan); } safari.saveGame(player); return true; } */ if (command === "inbox" || command === "i") { safari.viewInbox(src, ["unseen", "unread", "new", "u"].contains(commandData)); return true; } if (command === "deleteinbox") { safari.deleteInbox(src, commandData); return true; } if (["notifications", "notification", "notifs", "notif", "n"].contains(command)) { safari.viewNotifs(src, ["unseen", "unread", "new", "u"].contains(commandData)); return true; } if (command === "safarirules") { script.beforeChatMessage(src, "/crules", safchan); var player = getAvatar(src); if (player) { if (player.tutorial.inTutorial && player.tutorial.step === 11) { player.tutorial.viewedRules = true; player.tutorial.step = 12; tutorMsg(src, "These rules can be referenced at any time. When you are ready, proceed with " + link("/tutorial")); } player.lastViewedRules = now(); } return true; } if (command === "safariadmins" || command === "safariauth" || command === "sas") { script.beforeChatMessage(src, "/cauth", safchan); return true; } if (command === "info") { var time = new Date(now()).toUTCString(); var period = new Date().getUTCHours(); period = ["Night", "Morning", "Afternoon", "Evening"][Math.floor(period/6)]; sys.sendMessage(src, separator, safchan); if (currentThemeEffect == "past" && (chance(0.95))) { if (chance(0.45)) { time = new Date(now() - 400 * 365 * 24 * 60 * 60 * 1000).toUTCString(); } else { time = new Date(now() - Math.round((Math.random() + 0.2) * 18 * 60 * 60) * 1000).toUTCString(); } } safaribot.sendMessage(src, "Current Time: " + time + " (" + period + ")", safchan); safari.showNextContest(src); safaribot.sendHtmlMessage(src, "Pokémon-of-the-Day: " + link("/active " + pokePlain(dailyBoost.pokemon), pokePlain(dailyBoost.pokemon)) + " (" + dailyBoost.bonus.toFixed(2) + "x catch rate if used as active, and wild " + pokePlain(dailyBoost.pokemon) + " have double the chance to be Shiny).", safchan); safaribot.sendMessage(src, "Current Gachapon Jackpot: " + Math.floor(gachaJackpot/10) + " Tickets.", safchan); if (rafflePrizeObj) { var total = 0; for (var e in rafflePlayers.hash) { if (rafflePlayers.hash.hasOwnProperty(e)) { total += parseInt(rafflePlayers.hash[e], 10); } } safaribot.sendMessage(src, "Current Raffle Prize: " + plural(rafflePrizeObj.amount, rafflePrizeObj.name) + " with " + total + " entries sold!" + (rafflePrizeObj.drawDate ? " Estimated draw date: " + rafflePrizeObj.drawDate : ""), safchan); } sys.sendMessage(src, separator, safchan); return true; } if (command === "vb") { showVolleyballInfo(src, commandData); return true; } if (command === "clod") { if (nextClod >= now()) { safaribot.sendMessage(src, "Please wait " + timeLeftString(nextClod) + "!", safchan); return true; } sys.sendHtmlAll("" + sys.name(src) + ": ", safchan); nextClod = now() + 15000; return true; } if (command === "vblegal" || command === "volleyballlegal") { showVolleyballLegal(src); return true; } if (command === "vbhints" || command === "vbhint" || command === "vbh" || command === "volleyballhints") { showVolleyballHints(src, commandData); return true; } if (["dash", "dashboard", "d"].contains(command)) { safari.showOwnDashboard(src); return true; } if (["editdash", "editdashboard"].contains(command)) { safari.editOwnDashboard(src, commandData); return true; } if (command === "learns") { commandData = commandData.split(":"); if (commandData.length < 2) { safaribot.sendMessage(src, "Supply both a Pokémon and a move to search.", safchan); return true; } var info = getInputPokemon(commandData[0]); if (!info.num) { safaribot.sendMessage(src, "Invalid Pokémon.", safchan); return true; } var info2 = movenum(commandData[1].toLowerCase()); var moveName = moveOff(info2); if (!moveName) { safaribot.sendMessage(src, "Invalid Move.", safchan); return true; } if (canLearnMove(info.num, info2)) { safaribot.sendMessage(src, poke(info.num) + " can learn " + moveName + ".", safchan); return true; } safaribot.sendMessage(src, poke(info.num) + " cannot learn " + moveName + ".", safchan); return true; } if (command === "bst" || command === "dex") { var info = getInputPokemon(commandData); if (!info.num) { safaribot.sendMessage(src, "Invalid Pokémon.", safchan); return true; } var player = getAvatar(src), opt; if (player) { opt = player.options.dexOptional; } else { opt = []; } sys.sendMessage(src, "", safchan); var type_1 = type1(info.num); var type_2 = type2(info.num); var ic = pokeInfo.icon(info.num, false, true); var stats = getStatsNamed(info.num), statsmsg = [], efmsg = [], efmsg2 = []; for (var i in stats) { statsmsg.push(i + ": " + stats[i]); } statsmsg = statsmsg.join(" | "); if (!(opt.contains("stats"))) { statsmsg = "."; } else { statsmsg = ". [" + statsmsg + "]." } if (opt.contains("effectiveness")) { var val, se = [], nve = [], im = []; // incoming var val2, se2 = [], nve2 = [], im2 = []; // outgoing for (var e in effectiveness) { val = safari.checkEffective([e], [type_1, type_2]); val2 = safari.checkEffective([type_1, type_2], [e]); if (val > 1) { se.push(typeIcon(e)); } else if (val == 0) { im.push(typeIcon(e)); } else if (val < 1) { nve.push(typeIcon(e)); } if (val2 > 1) { se2.push(typeIcon(e)); } else if (val2 == 0) { im2.push(typeIcon(e)); } else if (val2 < 1) { nve2.push(typeIcon(e)); } } efmsg.push("Weaknesses: " + (se.join("") || "None")); efmsg.push("Resistances: " + (nve.join("") || "None")); efmsg.push("Immunities: " + (im.join("") || "None")); efmsg2.push("Super-Effective Against: " + (se2.join("") || "None")); efmsg2.push("Not-Very-Effective Against: " + (nve2.join("") || "None")); efmsg2.push("No Effect Against: " + (im2.join("") || "None")); } if (permObj.get("usingLangPack") == "true") { info.name = poke(getInputPokemon(info.name).num, true); } var bst = getBST(info.num); var region = generation(info.num, true); var trueRegion = generation(info.num, true, true); safaribot.sendHtmlMessage(src, "" + ic + " " + pokeInfo.species(info.num) + (pokeInfo.forme(info.num) > 0 ? "-" + pokeInfo.forme(info.num) : "") + (pokeInfo.species(info.num) !== info.num ? " (" + info.num + "). " : ". ") + info.name + "'s BST is " + bst + (bst <= itemData.eviolite.threshold ? " (" + (bst + itemData.eviolite.maxRate * (player && player.costume === "preschooler" ? costumeData["preschooler"].rate2 : 1)) + " with max " + es(finishName("eviolite")) + ")" : "") + statsmsg, safchan); safaribot.sendHtmlMessage(src, "Type: " + (typeIcon(type_1) + (type_2 === "???" ? "" : typeIcon(type_2)))+ ", Region: " + region + (region === trueRegion ? "" : " (" + trueRegion + ")") + ", Tier: " + safari.getTier(info.num) + ", Color: " + cap(getPokeColor(info.num)) + ", Egg Group(s): " + readable(getEggGroups(info.num)) + ", Height: " + getHeight(info.num) + " m, Weight: " + getWeight(info.num) + " kg.", safchan); safaribot.sendHtmlMessage(src, "Abilities: " + readable([0, 1, 2].map(function(e) { return getPokeAbility(info.num, e) }).filter(function(e) { return !!e }).map(function(e) { return e in abilityEffects ? link("/abref " + e, abilityOff(e)) : abilityOff(e) })) + ".", safchan); if (opt.contains("effectiveness")) { safaribot.sendHtmlMessage(src, efmsg.join(", "), safchan); safaribot.sendHtmlMessage(src, efmsg2.join(", "), safchan); } if (player) { if (isMega(info.num)) { safaribot.sendMessage(src, info.name + " cannot be sold.", safchan); } else { var perkBonus = 1 + getPerkBonus(player, "amulet"); var fortuneBonus = 1 + safari.getFortune(player, "amulet", 0, null, true); var costumeBonus = (safari.hasCostumeSkill(player, "extraSellProfit") ? 1.2 : 1); var price = getPrice(info.num, info.shiny, perkBonus, fortuneBonus, costumeBonus); safaribot.sendMessage(src, "You can sell " + an(info.name) + " for $" + addComma(price) + ". " + (!info.shiny ? "If it's Shiny, you can sell it for $" + addComma(getPrice(info.num, true, perkBonus, fortuneBonus, costumeBonus)) + ". " : ""), safchan); } } var species = evolutions.hasOwnProperty(info.num+"") ? info.num : pokeInfo.species(info.num); if (evolutions.hasOwnProperty(species+"")) { var evoData = evolutions[species]; var candiesRequired = Math.floor((evoData.candies || 300) * (info.shiny ? 1.25 : 1)); candiesRequired = safari.candyCostConversion(false, candiesRequired); var evo = evoData.evo; if (evo !== -1) { var conditionals = []; if (!info.shiny) { var shinyRequired = Math.floor((evoData.candies || 300) * 1.25); shinyRequired = safari.candyCostConversion(false, shinyRequired); conditionals.push(addComma(shinyRequired) + " if shiny"); } safaribot.sendHtmlMessage(src, info.name + " requires " + plural(candiesRequired, "rare") + " to evolve into " + (Array.isArray(evo) ? readable(evo.map(function(e) { return link("/bst " + e, poke(e)) }), "or") : link("/bst " + evo, poke(evo))) + (conditionals.length > 0 ? " (" + conditionals.join(", ") + ")" : "") + ". ", safchan); } } if (!isMega(info.num) && info.num in megaEvolutions) { safaribot.sendMessage(src, info.name + " can Mega Evolve into " + readable(megaEvolutions[info.num].map(poke), "or") + ".", safchan); } if (getAllForms(info.num, true).length > 0) { safaribot.sendMessage(src, info.name + " has " + plural(getAllForms(info.num, true).length, "alternate forme") + ".", safchan); } if (isRare(info.num)) { safaribot.sendMessage(src, info.name + " is a rare Pokémon!", safchan); } var themes = safari.getAllThemesForPoke(info.num, true); if (themes.length > 0) { safaribot.sendHtmlMessage(src, info.name + " can be found in the following " + plural(themes.length, "theme") + ": " + readable(themes.map(function(e) { return link("/themespawns " + e.replace(/(\[.+\])/, "").trim(), e) }), "and") + ".", safchan); } else { safaribot.sendMessage(src, info.name + " cannot currently be found in any theme.", safchan); } if (info.num in globalWildItems) { var itemDrops = []; for (var item in globalWildItems[info.num]) { var asset = translateAsset(globalWildItems[info.num][item].item); if (asset.type !== "item") { if (globalWildItems[info.num][item].item in safari.getCustomWildItems()) { itemDrops.push("{0} [Custom Item] ({1}%)".format(globalWildItems[info.num][item].item, globalWildItems[info.num][item].perc * 100)); } continue; } itemDrops.push("{0} ({1}%)".format(plural(asset.amount, asset.name), globalWildItems[info.num][item].perc * 100)); } if (itemDrops.length > 0) { safaribot.sendMessage(src, info.name + " drops the following items: " + readable(itemDrops), safchan); } } if (SESSION.channels(safchan).isChannelOwner(src)) { var editBST = []; for (var theme in contestThemes) { if (contestThemes[theme].editBST && info.num in contestThemes[theme].editBST) { editBST.push(contestThemes[theme].name + " (" + contestThemes[theme].editBST[info.num] + ")"); } } if (editBST.length > 0) { safaribot.sendMessage(src, info.name + " has a custom BST value in the following themes: " + readable(editBST), safchan); } } if (opt.contains("trivia")) { if (triviaData.hasOwnProperty(info.num+"")) { var l = [], td = triviaData[info.num+""]; for (var i in td) { if (td[i] === true) { l.push(i); } } if (l.length > 0) { safaribot.sendHtmlMessage(src, "Did you know? " + html_escape(l.random()), safchan); } } } safaribot.sendHtmlMessage(src, "Use {0} or {1} to show or hide additional information!".format(link("/options showdex:", "/options showdex:[stats|effectiveness|trivia]", true), link("/options hidedex:", "/options hidedex:[stats|effectiveness|trivia]", true)), safchan); sys.sendMessage(src, "", safchan); if (player) { if (player.tutorial.inTutorial && player.tutorial.step === 7 && (commandData.toLowerCase() == "pikachu")) { advanceTutorial(src, 8); return; } } return true; } if (command === "showpokemon") { var info = getInputPokemon(commandData); if (!info.num) { safaribot.sendMessage(src, "Invalid Pokémon.", safchan); return true; } var out = [" " + info.name + ":"]; if (sys.pokemon(info.num) !== "Missingno") { out.push(pokeInfo.icon(info.num)); out.push(pokeInfo.sprite(info.num) + " " + pokeInfo.sprite(info.num+"")); } else { var species = pokeInfo.species(info.num), form = pokeInfo.forme(info.num); var key = species + (form > 0 ? "-" + form : ""); out.push(""); out.push(" "); } for (var i = 0; i < out.length; i++) { sys.sendHtmlMessage(src, out[i], safchan); } return true; } if (command === "lastcontest" || command === "lastcontests" || command === "lc") { sys.sendMessage(src, "", safchan); sys.sendMessage(src, "*** LAST CONTESTS ***", safchan); for (var e = 0, x; e < lastContests.length; e++) { x = lastContests[e]; safaribot.sendHtmlMessage(src, "Theme: {0} --- Won by: {1} --- Score: {2} --- Rules: {3} --- Finished {4} ago".format(x.theme, (x.winners ? x.winners + (!x.saved ? " (No Rewards)" : "") : "No one"), (x.winners ? "Caught " + x.caught + ", BST " + x.bst + "" : "N/A"), x.rules, utilities.getTimeString(Math.floor((now() - x.finished)/1000) + 1)), safchan); } sys.sendMessage(src, "", safchan); return true; } if (command === "records" || command === "record") { safari.showRecords(src, commandData); return true; } if (command === "itemfinder" || command === "finder" || command === "f") { safari.findItem(src, commandData === "combo"); return true; } if (command === "use") { safari.useItem(src, commandData); return true; } if (command === "mail") { safari.useMail(src, commandData); return true; } if (command == "vblb") { this.viewVolleyballLb(src, commandData.toLowerCase()); return true; } if (["nextquiz", "nexthiddenquiz", "hiddenquiz", "hq"].indexOf(command) !== -1) { safari.nextQuiz(src); return true; } if ((command === "daycare" || command === "dc") && (dayCareEnabled)) { safari.handleDayCareCommand(src, commandData.split(":")); return true; } if (command === "themes") { var ret = getAllThemeNames(); safaribot.sendMessage(src, "Available Contest Themes: " + readable(ret, "and") + ".", safchan); if (SESSION.channels(safchan).isChannelOwner(src)) { var url = permObj.get("themesurl"); if (url) { safaribot.sendMessage(src, "Current themes file: " + url, safchan); } } return true; } if (command === "showmega" || command === "showmegas") { safari.showMegaTimers(src); return true; } if (["economy", "showeconomy", "economydata", "stonks"].contains(command)) { safari.showEconomyData(src, commandData); return true; } if (command === "vote") { if (!contestVotes || !nextTheme) { safaribot.sendMessage(src, "There's no voting for a theme ongoing!", safchan); return true; } var player = getAvatar(src); if (!player) { safaribot.sendMessage(src, "You need to start the game to vote!", safchan); return true; } var voted = commandData.toLowerCase(); if (!nextTheme.contains(voted)) { var tNames = nextTheme.map(themeName).map(function(x) { return x.toLowerCase();}); if (tNames.contains(voted)) { voted = nextTheme[tNames.indexOf(voted)]; } else { if (voted in contestThemes || voted === "none") { safaribot.sendMessage(src, "This theme is not available to vote at this time! Type /info to find out which themes can be voted for.", safchan); } else { safaribot.sendMessage(src, voted + " is not a valid theme!", safchan); } return true; } } if (contestVotes.hasOwnProperty(player.id)) { safaribot.sendMessage(src, "You changed your vote to " + themeName(voted) + "!", safchan); } else { safaribot.sendMessage(src, "You voted for " + themeName(voted) + "!", safchan); } contestVotes[player.id] = voted; return true; } if (command === "contestrules" || command === "contestrule") { var out = [ "", "*** CONTEST RULES EXPLANATION *** ", "NERF: Any number of nerfs reduces the catch rate to " + (RULES_NERF * 100) + "% of its initial value. However, if the final catch rate is higher than " + (RULES_NERF_CAP * 100) + "%, it will be capped at " + (RULES_NERF_CAP * 100) + "%.", "BUFF: Each buff increases the catch rate by " + (RULES_BUFF * 100) + "%.", "Nerf is only applied once, but Buffs can stack.", "", "Buffed/Nerfed Types: Pokémon with any of those types gets Buffed/Nerfed.", "Buffed/Nerfed Regions: Pokémon from that region are Buffed/Nerfed. Forms and Megas use the base form for this check.", "Enforced Types: Pokémon with any type not in this list gets Nerfed.", "Shiny Pokémon Buffed/Nerfed: Shiny Pokémon gets Buffed/Nerfed.", "Single-type Pokémon Buffed/Nerfed: Pokémon with only one type gets Buffed/Nerfed.", "Dual-type Pokémon Buffed/Nerfed: Pokémon with only two types gets Buffed/Nerfed.", "Legendaries Nerfed: Legendary Pokémon get Nerfed.", "Recommended BST: Pokémon outside of this BST range gets Nerfed.", "", "Inverted BST: Lower BST = Better.", "Inverted Type Effectiveness*: Wild Pokémon resisting your Pokémon = Good (example: Using Normal-type against a Steel-type Wild Pokémon).", "Resistance Mode*: Your Pokémon resisting the Wild Pokémon = Good (example: Using Steel-type against a Normal-type Wild Pokémon).", "Weakness Mode*: Wild Pokémon super-effective on your Pokémon = Good (example: Using Ground-type against an Ice-type Wild Pokémon).", "*These rules replace normal Type Effectiveness.", "", "Allowed/Forbidden Balls: Balls not allowed cannot be thrown.", "Reward: Different items given to the Contest winner.", "" ]; for (var e = 0; e < out.length; e++) { sys.sendMessage(src, out[e], safchan); } var player = getAvatar(src); if (player) { if (player.tutorial.inTutorial && player.tutorial.step === 10) { player.tutorial.viewedContestRules = true; player.tutorial.step = 11; tutorMsg(src, "These rules can be referenced at any time. When you are ready, proceed with " + link("/tutorial")); } } return true; } if (command === "pyr") { var found = false; for (var p in currentPyramids) { if (currentPyramids[p].isInPyramid(sys.name(src))) { currentPyramids[p].useCommand(src, commandData); found = true; break; } } if (!found) { return false; } return true; } if (command === "bak") { var found = false; for (var p in currentBakings) { if (currentBakings[p].isInKitchen(sys.name(src))) { currentBakings[p].handleCommand(sys.name(src), commandData); found = true; break; } } if (!found) { return false; } return true; } if (command === "bat") { var found = false, bat; for (var p in currentBattles) { bat = currentBattles[p]; if (bat.isInBattle(sys.name(src)) && bat.battle2) { found = true; break; } } if (!found) { return false; } bat.inputMove(src, commandData); return true; } if (currentEvent && currentEvent.isInEvent(sys.name(src)) && currentEvent.eventCommands.hasOwnProperty(command)) { currentEvent.handleCommand(src, command, commandData); return true; } } else { safaribot.sendMessage(src, "You can't play Safari while it is updating.", safchan); return true; } //Staff Commands if (SESSION.channels(safchan).isChannelAdmin(src)) { if (command === "watchbypass") { if (currentEvent && commandData === "*") { currentEvent.watchEvent(src); } else { safari.watchBattle(src, commandData, false, true); } return true; } if (command === "viewbypass") { safari.viewPlayer(src, commandData, false, true); return true; } if (command === "safaribans") { script.banList(src, "safaribans", commandData); return true; } if (command === "modspeak" || command === "ms") { var m = commandData, n = "", l = ""; out = m; if (m.indexOf("link(") !== 1) { n = m.slice(m.indexOf("link(") + 5, m.length); m = m.slice(0, m.indexOf("link("), m.length); l = n.slice(0, n.indexOf(")")); n = n.slice(n.indexOf(")") + 1, n.length); out = m + (link(l) + n); } var color = script.getColor(src); sys.sendHtmlAll("" + sys.name(src) + ": " + out, safchan); return true; } if (command === "checktrivia" || command === "showtrivia") { var out, data, approved, mon, hit = false; if (commandData && commandData.length > 0 && commandData !== "unapproved" && commandData !== "*") { mon = getInputPokemon(commandData).num; if (triviaData.hasOwnProperty(mon+"")) { data = triviaData[mon+""]; for (var i in data) { approved = data[i]; hit = true; safaribot.sendHtmlMessage(src, i + " " + (approved ? "" : link("/approvetrivia " + mon + ":" + i, "Approve")) + " " + link("/removetrivia " + mon + ":" + i, "Remove"), safchan); } if (!(hit)) { safaribot.sendHtmlMessage(src, "No trivia for " + poke(mon) + " found!", safchan); } } safaribot.sendHtmlMessage(src, "No submitted questions for " + poke(mon) + " found!", safchan); return true; } for (var a in triviaData) { data = triviaData[a]; for (var i in data) { approved = data[i]; if (approved) { continue; } hit = true; safaribot.sendHtmlMessage(src, poke(parseInt(a, 10)) + ": " + i + (approved ? "" : link("/approvetrivia " + a + ":" + i, "Approve")) + " " + link("/removetrivia " + a + ":" + i, "Remove"), safchan); } } if (!(hit)) { safaribot.sendHtmlMessage(src, "No unapproved trivia found!", safchan); } return true; } if (command === "approvetrivia") { data = commandData.split(":"); if (data.length !== 2) { return true; } triviaData[data[0]][data[1]] = true; safaribot.sendHtmlMessage(src, "Approved trivia: " + poke(parseInt(data[0], 10)) + " " + data[1] + "!", safchan); return true; } if (command === "removetrivia") { data = commandData.split(":"); if (data.length !== 2) { return true; } delete triviaData[data[0]][data[1]]; safaribot.sendHtmlMessage(src, "Removed trivia: " + poke(parseInt(data[0], 10)) + " " + data[1] + "!", safchan); return true; } if (command === "startevent") { if (currentEvent) { safaribot.sendMessage(src, "There's already an event going on!", safchan); return true; } var info = commandData.split(":"); var type = getEventId(info[0]); if (type === null) { safaribot.sendHtmlMessage(src, "To start an event, use one of the following commands (parameters in red are optional):", safchan); safaribot.sendHtmlMessage(src, "Faction War: /startevent [war/invertedwar]:[Reward]:" + toColor("[Team1]:[Team2]:[Team2Reward]", "orangered"), safchan); safaribot.sendHtmlMessage(src, "Pokémon Race: /startevent race:[Reward]:" + toColor("[UnderdogReward]:[FavoriteReward]:[Racers]", "orangered"), safchan); safaribot.sendHtmlMessage(src, "Pokémon Bet Race: " + link("/startevent betrace:BetItem:Reward:MinimumBet:MaximumBet:FavoritePayout:UnderdogPayout:NormalPayout:Racers", "/startevent betrace:[BetItem]:[Reward]:[MinimumBet]:[MaximumBet]:[FavoritePayout]:[UnderdogPayout]:[NormalPayout]:[Racer1,Racer2,etc]", true) + toColor(" (everything is optional)", "orangered"), safchan); safaribot.sendHtmlMessage(src, "Battle Factory: /startevent [factory/lcfactory]:[1st Place Rewards]:[2nd Place Rewards]:" + toColor("[3rd Place Rewards]", "orangered"), safchan); safaribot.sendHtmlMessage(src, "Quiz: /startevent [quiz/hquiz]:[1st Place Rewards]:[2nd Place Rewards]:" + toColor("[3rd Place Rewards]", "orangered"), safchan); safaribot.sendHtmlMessage(src, "Bingo: /startevent [bingo]:[1st Place Rewards]:" + toColor("[2nd Place Rewards]:[3rd Place Rewards]:[Goal]", "orangered"), safchan); return true; } var param = info.slice(1); if (type == "factionwar" || type == "invertedwar") { if (param.length < 1) { safaribot.sendMessage(src, "Use /startevent [FactionWar/InvertedWar]:Reward:Team1:Team2:Reward2 to start a Faction War!", safchan); return true; } var reward = param[0]; var valid = validateStuff(reward); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward found: " + readable(valid) + "!", safchan); return true; } var name1 = param.length > 1 && param[1] ? param[1] : pokePlain(sys.rand(1, highestDexNum)); var name2; if (param.length > 2 && param[2]) { name2 = param[2]; } else { do { name2 = pokePlain(sys.rand(1, highestDexNum)); } while (name1 === name2); } var reward2 = null; if (param.length > 3 && param[3]) { reward2 = param[3]; var valid = validateStuff(reward2); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward2 found: " + readable(valid) + "!", safchan); return true; } } var ev = new FactionWar(src, reward, name1, name2, type == "invertedwar", reward2); currentEvent = ev; safari.flashPlayers(); } else if (type == "race") { var r1 = param[0], r2 = "", r3 = "", racers = null, l = param.length; if (!r1) { safaribot.sendMessage(src, "Please specify a valid reward for the first place!", safchan); return true; } var valid = validateStuff(r1); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward found: " + readable(valid) + "!", safchan); return true; } if (l > 1) { r2 = param[1]; valid = validateStuff(r2); if (r2 && valid.length > 0) { safaribot.sendMessage(src, "Invalid Underdog reward found: " + readable(valid) + "!", safchan); return true; } } if (l > 2) { r3 = param[2]; valid = validateStuff(r3); if (r3 && valid.length > 0) { safaribot.sendMessage(src, "Invalid Favorite reward found: " + readable(valid) + "!", safchan); return true; } } if (l > 3) { racers = param[3]; } var data = { reward: r1, rewardUnderdog: r2, rewardFavorite: r3, racers: racers }; var ev = new PokeRace(src, "normal", data); currentEvent = ev; safari.flashPlayers(); } else if (type == "betrace") { var data = { minBet: null, maxBet: null, favorite: null, underdog: null, normal: null, bet: null, reward: null, racers: null }; var l = param.length; if (l > 0) { var valid = validateStuff(param[0]); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid bet found: " + readable(valid) + "!", safchan); return true; } valid = toStuffObj(param[0]); if (Object.keys(valid).length > 1) { safaribot.sendMessage(src, "You cannot set more than one item as the bet asset!", safchan); return true; } valid = translateAsset(param[0]); if (valid.type === "poke") { safaribot.sendMessage(src, "You cannot set a Pokémon as the bet asset!", safchan); return true; } data.bet = param[0]; } if (l > 1) { var valid = validateStuff(param[1]); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward found: " + readable(valid) + "!", safchan); return true; } valid = toStuffObj(param[1]); if (Object.keys(valid).length > 1) { safaribot.sendMessage(src, "You cannot set more than one item as the reward payout!", safchan); return true; } data.reward = param[1]; } if (l > 2) { val = parseInt(param[2], 10); if (val && !isNaN(val) && val > 0) { data.minBet = val; } } if (l > 3) { val = parseInt(param[3], 10); if (val && !isNaN(val)) { data.maxBet = val; } } if (l > 4) { val = parseFloat(param[4]); if (val && !isNaN(val) && val > 0) { data.favorite = val; } } if (l > 5) { val = parseFloat(param[5]); if (val && !isNaN(val) && val > 0) { data.underdog = val; } } if (l > 6) { val = parseFloat(param[6]); if (val && !isNaN(val) && val > 0) { data.normal = val; } } if (l > 7) { data.racers = param[7]; } var ev = new PokeRace(src, "bet", data); currentEvent = ev; safari.flashPlayers(); } else if (type == "bfactory" || type == "lcbfactory") { var r1 = "", r2 = "", r3 = "", pLen = param.length; if (pLen > 0) { r1 = param[0]; } if (pLen > 1) { r2 = param[1]; } if (pLen > 2) { r3 = param[2]; } if (!r1) { safaribot.sendMessage(src, "Please specify a valid reward for the first place!", safchan); return true; } if (!r2) { safaribot.sendMessage(src, "Please specify a valid reward for the second place!", safchan); return true; } var valid = validateStuff(r1); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for first place found: " + readable(valid) + "!", safchan); return true; } valid = validateStuff(r2); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for second place found: " + readable(valid) + "!", safchan); return true; } if (r3) { valid = validateStuff(r3); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for third place found: " + readable(valid) + "!", safchan); return true; } } var ev = new BFactory(src, r1, r2, r3, type == "lcbfactory"); currentEvent = ev; safari.flashPlayers(); } else if (type == "quiz" || type == "hquiz") { var r1 = "", r2 = "", r3 = "", silent = (type === "hquiz"), pLen = param.length; if (pLen > 0) { r1 = param[0]; } if (pLen > 1) { r2 = param[1]; } if (pLen > 2) { r3 = param[2]; } if (!r1) { safaribot.sendMessage(src, "Please specify a valid reward for the first place!", safchan); return true; } if (!r2) { safaribot.sendMessage(src, "Please specify a valid reward for the second place!", safchan); return true; } var valid = validateStuff(r1); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for first place found: " + readable(valid) + "!", safchan); return true; } valid = validateStuff(r2); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for second place found: " + readable(valid) + "!", safchan); return true; } if (r3) { valid = validateStuff(r3); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for third place found: " + readable(valid) + "!", safchan); return true; } } var ev = new Quiz(src, r1, r2, r3, silent); currentEvent = ev; safari.flashPlayers(); } else if (type == "bingo") { var r1 = "", r2 = "", r3 = "", goal = 1, pLen = param.length; if (pLen > 0) { r1 = param[0]; } if (pLen > 1) { r2 = param[1]; } if (pLen > 2) { r3 = param[2]; } if (pLen > 3) { goal = parseInt(param[3], 10); } if (!r1) { safaribot.sendMessage(src, "Please specify a valid reward for the first place!", safchan); return true; } var valid = validateStuff(r1); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for first place found: " + readable(valid) + "!", safchan); return true; } if (r2) { valid = validateStuff(r2); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for second place found: " + readable(valid) + "!", safchan); return true; } } if (r3) { valid = validateStuff(r3); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward for third place found: " + readable(valid) + "!", safchan); return true; } } if (!goal || isNaN(goal) || goal < 1 || goal > 12) { safaribot.sendMessage(src, "Goal must be between 1 and 12!", safchan); return true; } var ev = new Bingo(src, r1, r2, r3, goal); currentEvent = ev; safari.flashPlayers(); } else { safaribot.sendMessage(src, info[0] + " is not a valid event! Type /startevent help for more information!", safchan); } return true; } if (command === "volleyball") { var names = commandData.split(":"); if (names.length < 2) { names = ["One", "Two"]; } var vb = new Volleyball(src, names[0], names[1], "@rock", "@rock", true); currentGame = vb; return true; } if (command === "volleyballrecords") { safaribot.sendMessage(src, officialVolleyballTeam1 + ": " + officialVolleyballWins1 + " | " + officialVolleyballTeam2 + " | " + officialVolleyballWins2, safchan); return true; } if (command === "shove") { if (!currentEvent) { safaribot.sendMessage(src, "There's no event going on!", safchan); return true; } currentEvent.shove(src, commandData); return true; } if (command === "abortevent") { if (!currentEvent) { safaribot.sendMessage(src, "There's no event going on!", safchan); return true; } safaribot.sendAll(sys.name(src) + " cancelled the " + currentEvent.eventName + " event!", safchan); currentEvent.log(false, [], "Signups: " + currentEvent.signups.join(", ")); currentEvent = null; checkUpdate(); return true; } if (command === "sanitize") { var playerId = sys.id(commandData); if (!playerId) { safaribot.sendMessage(src, "No such person!", safchan); return true; } var player = getAvatar(playerId); if (player) { safari.sanitize(player); safari.sanitizePokemon(player); safaribot.sendMessage(src, commandData + "'s safari has been sanitized of invalid values!", safchan); safaribot.sendMessage(playerId, "Your Safari has been sanitized of invalid values!", safchan); } else { safaribot.sendMessage(src, "No such person!", safchan); } return true; } if (command === "sanitizeall") { safari.sanitizeAll(); safaribot.sendMessage(src, "All safaris have been sanitized of invalid values!", safchan); return true; } if (command === "track") { var target = getAvatarOff(commandData); if (!target) { safaribot.sendMessage(src, "No such player!", safchan); return true; } var name = sys.name(src).toLowerCase(); if (target.trackers.contains(name)) { target.trackers.splice(target.trackers.indexOf(name), 1); safari.saveGame(target); safaribot.sendMessage(src, "No longer tracking " + commandData.toCorrectCase() + "!", safchan); } else { target.trackers.push(name); safari.saveGame(target); safaribot.sendMessage(src, "Now tracking " + commandData.toCorrectCase() + "!", safchan); } return true; } if (command === "trackall") { var name = sys.name(src).toLowerCase(); var someoneElse = false; if (commandData !== "*" && sys.id(commandData) && sys.ip(sys.id(commandData)) === sys.ip(src)) { name = commandData.toLowerCase(); someoneElse = true; } if (allTrackers.contains(name)) { allTrackers.splice(allTrackers.indexOf(name), 1); safaribot.sendMessage(src, "You are no longer tracking everyone" + (someoneElse ? " as " + name.toCorrectCase() : "") + "!", safchan); } else { allTrackers.push(name); safaribot.sendMessage(src, "You are now tracking everyone" + (someoneElse ? " as " + name.toCorrectCase() : "") + "!", safchan); } SESSION.global().allTrackers = allTrackers; return true; } if (command === "eventhiddenquiz") { safari.startQuizEvent(); return true; } if (command === "supdatemarket") { safari.updateMarket(true); return true; } if (command === "updatemarket") { safari.updateMarket(false, false, src); return true; } if (command === "updatemarketdefault") { safari.updateMarket(false, true, src); return true; } if (command === "forcedaycarestep") { safari.forceDayCareStep(src, parseInt(commandData, 10)); return true; } if (command === "daycarefeature") { safari.manualChangeFeature(src, commandData.split(":")); return true; } if (command === "inspectdaycare") { safari.inspectDayCare(src); return true; } if (command === "inspectdaycare2") { safari.inspectDayCareFeatures(src); return true; } if (command === "daycare" || command === "dc") { safari.handleDayCareCommand(src, commandData.split(":")); return true; } if (command === "enabledc" || command === "enabledaycare") { dayCareEnabled = (dayCareEnabled ? false : true); safaribot.sendMessage(src, "Daycare enabled: " + dayCareEnabled + ".", safchan); return true; } if (command === "monstonums") { var l = commandData.split(","), out = [], item; for (var i = 0; i < l.length; i++) { item = getInputPokemon(l[i]).num; if (!(item)) { continue; } out.push(item); } safaribot.sendMessage(src, "Inputed Pokémon: " + l.join(","), safchan); safaribot.sendMessage(src, "Corresponding Dex Numbers: " + out.join(","), safchan); return true; } if (command === "numstomons") { var l = commandData.split(","), out = [], item; for (var i = 0; i < l.length; i++) { item = l[i]; if (item.indexOf("-") > -1) { var spl = item.split("-"); item = parseInt(spl[0], 10) + (65536 * parseInt(spl[1], 10)); } item = poke(parseInt(item, 10)); out.push(item); } safaribot.sendMessage(src, "Inputed Dex Numbers: " + l.join(","), safchan); safaribot.sendMessage(src, "Corresponding Pokémon: " + out.join(","), safchan); return true; } if (command === "clueresearch" || command === "clueresearch2") { var foundTypes = {}, reg, typing; for (var j = 1; j < highestDexNum; j++) { reg = generation(highestDexNum, true) + ""; if (!foundTypes.hasOwnProperty(reg)) { foundTypes[reg] = {}; } typing = type1(j) + "," + (type2(j) == "???" ? "" : type2(j)); if (!foundTypes[reg].hasOwnProperty(typing)) { foundTypes[reg][typing] = []; } foundTypes[reg][typing].push(j); } var out = []; for (var x in foundTypes) { for (var y in foundTypes[x]) { if (command == "clueresearch2") { if (foundTypes[x][y].length <= 2) { out.push(poke(foundTypes[x][y][0])); if (foundTypes[x][y].length == 2) { out.push(poke(foundTypes[x][y][0])); } } continue; } if (foundTypes[x][y].length == 1) { out.push(poke(foundTypes[x][y][0])); } } } safaribot.sendMessage(src, "Pokémon with unique typing within their region: " + out.join(", ") + " (" + out.length + ")", safchan); return true; } if (command === "clearcpksconfirm") { celebrityPKs = {}; return; }; if (command === "celebritypks" || command == "cpks") { var rec = commandData.toLowerCase(), e, g, out, i, cdata = rec.split(":"); if (cdata.length < 1) { cdata.unshift("kanto"); } if (cdata.length < 2) { cdata.push("total"); } if (cdata.length < 3) { cdata.push("percentage"); } if (!celebrityPKs.hasOwnProperty(cdata[0])) { safaribot.sendMessage(src, "There is no celebrity data for region " + cdata[0] + "!", safchan); return true; } e = celebrityPKs[cdata[0]]; if (!e.hasOwnProperty(cdata[1])) { safaribot.sendMessage(src, "There is no celebrity data for " + cdata[1] + " in that region!", safchan); return true; } e = e[cdata[1]]; list = Object.keys(e); out = list.sort(function(a, b) { if (cdata[2] == "percentage") { return ((e[b][0]/(Math.max(e[b][1] + e[b][0], 1))) - ((e[a][0]/(Math.max(e[a][1] + e[a][0], 1))))); } return e[b][0] - e[a][0]; }); safaribot.sendMessage(src, "Top run killers in " + rec + " for " + cdata[1] + " difficulty: ", safchan); if (cdata[2] !== "percentage") { for (var i = 0; i < list.length; i++) { safaribot.sendMessage(src, (i + 1) + ": " + list[i] + " [" + e[list[i]][0] + "].", safchan); } } else { for (var i = 0; i < list.length; i++) { safaribot.sendMessage(src, (i + 1) + ": " + list[i] + " [" + (100 * e[list[i]][0] / (Math.max(e[list[i]][1] + e[list[i]][0], 1))) + "%" + "].", safchan); } } return true; } if (command === "trick" || command === "trick2") { var info = commandData.split(":"); var targetId = sys.id(info[0]); if (!targetId || !sys.isInChannel(targetId, safchan)) { safaribot.sendMessage(src, "No such person in the channel!", safchan); return true; } var input; if (info.length > 1) { input = getInputPokemon(info[1]); } if (!input || !input.num) { input = getInputPokemon(sys.rand(1, highestDexNum) + ""); } var amt = 1; if (info.length > 2) { amt = parseInt(info[2], 10); if (isNaN(amt) || amt < 1) { amt = 1; } } var player = getAvatar(targetId); if (player) { safaribot.sendMessage(src, "Tricking " + sys.name(targetId) + " into seeing a wild " + input.name + "!", safchan); for (var x in player.trackers) { var trackerName = player.trackers[x]; var trackerId = sys.id(trackerName); if (trackerId && trackerId !== src) { safaribot.sendMessage(trackerId, sys.name(src).toCorrectCase() + " is tricking " + sys.name(targetId) + " into seeing a wild " + input.name + "!", safchan); } } } else { safaribot.sendMessage(src, "Tricking " + sys.name(targetId) + " into seeing a wild " + input.name + "!", safchan); } if (contestCount <= 0) { var bName = finishName("bait").toLowerCase(); safaribot.sendMessage(targetId, "Some stealthy person left some " + bName + " out. The " + bName + " attracted a wild Pokémon!", safchan); } var ret = ""; if (amt > 1) { var term = amt === 2 ? "pair" : amt === 3 ? "group" : "horde"; ret += "
A " + term + " of wild " + input.name + " appeared! (BST: " + getBST(input.num) + ")
"; for (var i = 0; i < amt; i++) { ret += pokeInfo.sprite(input.id); } ret += "

"; } else { var appmsg = "A wild " + input.name + " appeared! (BST: " + getBST(input.num) + ")"; ret = "
" + (input.shiny ? toColor(appmsg, "DarkOrchid") : appmsg) + "
" + pokeInfo.sprite(input.id) + "

"; } sys.sendHtmlMessage(targetId, ret, safchan); ballMacro(targetId); if (info.length > 3) { sys.sendMessage(targetId, info.slice(3).join(":"), safchan); } if (command === "trick2") { sys.setTimer(function() { sys.sendMessage(targetId, "", safchan); safaribot.sendHtmlMessage(targetId, "Some stealthy person caught the " + input.name + " with " + an(finishName("spy")) + " and the help of their well-trained spy Pokémon!", safchan); sys.sendMessage(targetId, "", safchan); }, 3200, false); } lastWild = now(); return true; } if (command === "tradeban") { var info = commandData.split(":"); var name = info[0]; if (info.length < 2) { safaribot.sendMessage(src, "Please set a duration!", safchan); return true; } var duration = info[1]; if (duration != -1) { duration = utilities.getSeconds(info[1]); } var player = getAvatarOff(name); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } var self = utilities.non_flashing(sys.name(src).toCorrectCase()); safari.applyTradeban(self, name, player, duration); return true; } if (command === "tradebans") { if (tradeBans) { var out = [], val, currentTime = now(); for (var b in tradeBans.hash) { if (tradeBans.hash.hasOwnProperty(b)) { val = parseInt(tradeBans.hash[b], 10); if (val > currentTime) { out.push(b.toCorrectCase() + " is tradebanned until " + (new Date(val).toUTCString()) + "."); } else if (val == -1) { out.push(b.toCorrectCase() + " is permanently tradebanned."); } } } if (out.length > 0) { sys.sendMessage(src, "", safchan); sys.sendMessage(src, "*** SAFARI TRADE BANS ***", safchan); for (b = 0; b < out.length; b++) { safaribot.sendMessage(src, out[b], safchan); } sys.sendMessage(src, "", safchan); } else { safaribot.sendMessage(src, "No one is currently tradebanned!", safchan); } } else { safaribot.sendMessage(src, "Trade Bans file not found!", safchan); } return true; } if (command === "inspectmedals" || command === "inspectfeathers") { safari.viewMedals(src, commandData); return true; } if (command === "revokemedal" || command === "revokefeather") { commandData = commandData.split(":"); var name = commandData.shift(); safari.removeMedal(src, name, commandData.join(":")); return true; } if (command === "awardmedal" || command === "awardfeather") { safari.giftMedal(src, commandData); return true; } if (command === "salt") { var info = commandData.split(":"); var name = info[0]; if (info.length < 2) { safaribot.sendMessage(src, "Please set a duration!", safchan); return true; } var duration = info[1]; if (duration != -1) { duration = utilities.getSeconds(info[1]); } var player = getAvatarOff(name); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); if (saltBans.get(name.toLowerCase()) && duration === 0) { safaribot.sendMessage(src, "Removing entry " + name + " from the salt list!", safchan); saltBans.remove(name.toLowerCase()); } return true; } if (duration === 0) { player.truesalt = 0; safaribot.sendMessage(src, name + " has been de-salted!", safchan); safari.saveGame(player); saltBans.remove(player.id); } else { var length; if (duration == -1) { length = "permanently"; player.truesalt = 2147483000000; } else { length = "for " + utilities.getTimeString(duration); player.truesalt = now() + duration * 1000; } safari.saveGame(player); safaribot.sendMessage(src, name + " has been salted " + length + "!", safchan); saltBans.add(player.id, player.truesalt); } saltBans.removeIf(function(obj, e) { return parseInt(obj.get(e), 10) === 0 || parseInt(obj.get(e), 10) < now(); }); saltBans.save(); return true; } if (command === "saltbans") { if (saltBans) { var out = [], val, currentTime = now(); for (var b in saltBans.hash) { if (saltBans.hash.hasOwnProperty(b)) { val = parseInt(saltBans.hash[b], 10); if (val > currentTime) { out.push(b.toCorrectCase() + " is salted until " + (new Date(val).toUTCString()) + "."); } else if (val == -1) { out.push(b.toCorrectCase() + " is permanently salted."); } } } if (out.length > 0) { sys.sendMessage(src, "", safchan); sys.sendMessage(src, "*** SAFARI SALT BANS ***", safchan); for (b = 0; b < out.length; b++) { safaribot.sendMessage(src, out[b], safchan); } sys.sendMessage(src, "", safchan); } else { safaribot.sendMessage(src, "No one is currently salted!", safchan); } } else { safaribot.sendMessage(src, "Salt Bans file not found!", safchan); } return true; } if (command === "safariban") { if (commandData === undefined) { safaribot.sendMessage(src, "Please specify a valid user to safari ban!", channel); return true; } var tar = sys.id(commandData); //No upper limit on time because you have to be admin to use this command anyway! script.issueBan("safban", src, tar, commandData); return true; } if (command === "safariunban") { var tar = sys.id(commandData); script.unban("safban", src, tar, commandData); return true; } if (command === "dqphoto") { if (!photographQuest.hasOwnProperty(commandData)) { safaribot.sendMessage(src, "This is not a valid Journal photo request!", safchan); return true; } var req = this.translatePhotoRequest(photographQuest[commandData]); delete photographQuest[commandData]; safaribot.sendMessage(src, "Removed Journal photo request for " + req + "!", safchan); safari.updatePhotographQuest(); return true; } if (command === "lbban") { var info = commandData.split(":"); var name = info[0]; var player = getAvatarOff(name); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } if (player.removedFromLB) { player.removedFromLB = false; safaribot.sendMessage(src, name + " has been readded to all Leaderboards!", safchan); } else { player.removedFromLB = true; safaribot.sendMessage(src, name + " has been removed from all Leaderboards!", safchan); } safari.saveGame(player); return true; } if (command === "lock" || command === "unlock") { var info = commandData.split(":"); var player = getAvatarOff(info[0]); if (!player) { safaribot.sendMessage(src, "This person doesn't have a Safari save!", safchan); return true; } var locking = command === "lock"; if (!locking && !sys.dbRegistered(player.id)) { safaribot.sendMessage(src, "You cannot unlock this person's save as this alt is still unregistered!", safchan); return true; } player.locked = locking; safari.saveGame(player); safaribot.sendMessage(src, "You " + (!locking ? "un" : "") + "locked " + player.id.toCorrectCase() + "'s save! " + (!locking ? "Their save will be loaded normally by joining the channel or using /start." : ""), safchan); var id = sys.id(player.id); if (locking && id && getAvatar(id)) { safaribot.sendHtmlMessage(id, toColor("Your save was locked by a Safari auth!", "red"), safchan); safari.clearPlayer(id); SESSION.users(id).safari = null; } if (!locking && id) { safaribot.sendHtmlMessage(id, toColor("Your Safari save was unlocked! Use /start to load it!", "red"), safchan); } sys.appendToFile(miscLog, now() + "|||" + player.id.toCorrectCase() + "|||'s save was " + (locking ? "locked" : "unlocked") + " by " + sys.name(src) + "\n"); return true; } if (command === "offmsg") { var data = toCommandData(commandData, ["names", "message"]); if (!data.names || !data.message) { safaribot.sendMessage(src, "Format is '/offmsg Player1,Player2,etc:::Message'.", safchan); return true; } var players = toUserNames(data.names), player, p, id, invalid = []; for (p = 0; p < players.length; p++) { id = players[p]; if (!getAvatarOff(id)) { invalid.push(id); } } if (invalid.length > 0) { safaribot.sendMessage(src, "The following names do not have a Safari save: " + readable(invalid), safchan); return true; } var out; for (p = 0; p < players.length; p++) { id = players[p]; player = getAvatarOff(id); safari.inboxMessage(player, data.message); } safaribot.sendMessage(src, readable(players) + " will receive the following message in their inbox: " + data.message, safchan); return true; } if (["itemstats", "istats", "itemstatsrecent", "itemstatsr", "istatsr", "istatsrecent"].contains(command)) { var recent = false; if (["itemstatsrecent", "itemstatsr", "istatsr", "istatsrecent"].contains(command)) { recent = true; } var item = itemAlias(commandData); var itemTotal = 0, usersChecked = 0, userTallies = [], itemAverage = 0; for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { var player = getAvatarOff(e); if (recent && getDay(now()) - player.lastLogin > 30) { continue; } if (player.excludeFromEconomy) { continue; } usersChecked += 1; if (!player.balls.hasOwnProperty(item)) { continue; } itemTotal += player.balls[item]; userTallies.push([player.casedName, player.balls[item]]); } } itemAverage = itemTotal / usersChecked; userTallies.sort(function(a, b) { return b[1] - a[1]; }); sys.sendMessage(src, "", safchan); sys.sendMessage(src, (recent ? "Recent " : "") + "Item Stats for " + es(finishName(item)) + ":", safchan); sys.sendHtmlMessage(src, toColor("Total Amount of " + es(finishName(item)) + ": ", "black") + addComma(itemTotal) + " (" + addComma(usersChecked) + " users checked)", safchan); sys.sendHtmlMessage(src, toColor("Average Amount of " + es(finishName(item)) + ": ", "black") + addComma(itemAverage), safchan); sys.sendHtmlMessage(src, toColor("Top 10 Players with Most Amount of " + finishName(item) + ": ", "black") + userTallies.slice(0, 10).map(function(e) { return e[0] + " (" + addComma(e[1]) + ")" }).join(", "), safchan); sys.sendMessage(src, "", safchan); return true; } if (command === "analyze" || command === "analyzer" || command === "analyzerare") { var info = commandData.split(":"); var target = sys.id(info[0]); var player = getAvatarOff(info[0]); if (!player) { safaribot.sendMessage(src, "This person doesn't have a Safari save!", safchan); return true; } var prop = (info.length < 2) ? [] : info[1].split("."); var attr = player[prop[0]]; var propName = ["safari"]; if (prop.length == 1 && prop[0] === "") { attr = player; } else { if (attr === undefined) { attr = player; } propName.push(prop[0]); for (var e = 1; e < prop.length; e++) { propName.push(prop[e]); if (prop[e] in attr) { attr = attr[prop[e]]; } else { safaribot.sendMessage(src, "This player does not have a '" + propName.join(".") + "' property!", safchan); return true; } } } var spc = 0; if (command === "analyzer" || command === "analyzerare") { //Readable format if (["pokemon", "party", "ninjaParty"].contains(propName[1])) { if (command === "analyzerare") { attr = attr.filter(isRare); } attr = attr.map(poke); spc = 1; } } safaribot.sendMessage(src, (target ? sys.name(target) : player.id) + "." + propName.join(".") + ": " + JSON.stringify(attr, null, spc), safchan); return true; } if (command === "undo") { var info = commandData.split(":"); if (info.length < 4) { safaribot.sendMessage(src, "Invalid format! Use /undo Player1:Player2:Items,To,Player1:Items,To,Player2!", safchan); return true; } var n1 = info[0], n2 = info[1], p1 = getAvatarOff(n1), p2 = getAvatarOff(n2); if (!p1) { safaribot.sendMessage(src, "No player found with the name " + n1.toCorrectCase(), safchan); return true; } if (!p2) { safaribot.sendMessage(src, "No player found with the name " + n2.toCorrectCase(), safchan); return true; } var stuff1 = toStuffObj(info[2].replace(/,/g, ":")), stuff2 = toStuffObj(info[3].replace(/,/g, ":")); var valid = validateStuff(info[2]); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid assets found: " + readable(valid) + " !", safchan); return true; } valid = validateStuff(info[3]); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid assets found: " + readable(valid) + " !", safchan); return true; } var out = hasStuff(p1, stuff2); if (!out.result) { safaribot.sendMessage(src, n1.toCorrectCase() + " doesn't have " + readable(out.missing) + "!", safchan); return true; } out = hasStuff(p2, stuff1); if (!out.result) { safaribot.sendMessage(src, n2.toCorrectCase() + " doesn't have " + readable(out.missing) + "!", safchan); return true; } var inverted1 = {}, inverted2 = {}, e; for (e in stuff1) { inverted2[e] = -stuff1[e]; } for (e in stuff2) { inverted1[e] = -stuff2[e]; } for (e in inverted1) { if (!(e in stuff1)) { stuff1[e] = 0; } stuff1[e] += inverted1[e]; } for (e in inverted2) { if (!(e in stuff2)) { stuff2[e] = 0; } stuff2[e] += inverted2[e]; } var out1 = giveStuff(p1, stuff1); var out2 = giveStuff(p2, stuff2); safari.sanitize(p1); //This also saves safari.sanitize(p2); safaribot.sendMessage(src, n1.toCorrectCase() + " " + out1 + "!", safchan); safaribot.sendMessage(src, n2.toCorrectCase() + " " + out2 + "!", safchan); if (sys.id(n1) && out1 !== "received nothing") { safaribot.sendMessage(sys.id(n1), "You " + out1 + "!", safchan); } if (sys.id(n2) && out2 !== "received nothing") { safaribot.sendMessage(sys.id(n2), "You " + out2 + "!", safchan); } sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + n1.toCorrectCase() + " and " + n2.toCorrectCase() + "|||undo|||exchanged|||" + translateStuff(info[2]) + " <--> " + translateStuff(info[3]) + "\n"); return true; } if (command === "checkbackup") { var info = commandData.split(":"); var target = info[0].toLowerCase(); if (info.length < 2) { safaribot.sendMessage(src, "Searching Safari Save for name '" + target + "' on backup files:", safchan); safaribot.sendHtmlMessage(src, "Backup File 1: " + (backupPlayers1.get(target) ? link("/checkbackup " + target + ":backup1") : "Not found"), safchan); safaribot.sendHtmlMessage(src, "Backup File 2: " + (backupPlayers2.get(target) ? link("/checkbackup " + target + ":backup2") : "Not found"), safchan); safaribot.sendHtmlMessage(src, "Backup File 3: " + (backupPlayers3.get(target) ? link("/checkbackup " + target + ":backup3") : "Not found"), safchan); safaribot.sendHtmlMessage(src, "Backup File 4: " + (backupPlayers4.get(target) ? link("/checkbackup " + target + ":backup4") : "Not found"), safchan); safaribot.sendHtmlMessage(src, "Backup File 5: " + (backupPlayers5.get(target) ? link("/checkbackup " + target + ":backup5") : "Not found"), safchan); safaribot.sendHtmlMessage(src, "Deleted Saves: " + (cookedPlayers.get(target) ? link("/checkbackup " + target + ":deleted") : "Not found"), safchan); return true; } var filename = info[1].toLowerCase(); var hash; switch (filename) { case "backup1": hash = backupPlayers1; break; case "backup2": hash = backupPlayers2; break; case "backup3": hash = backupPlayers3; break; case "backup4": hash = backupPlayers4; break; case "backup5": hash = backupPlayers5; break; case "deleted": hash = cookedPlayers; break; default: safaribot.sendMessage(src, filename + " is not a valid backup file! Try 'backup1', 'backup2', 'backup3', 'backup4', 'backup5' or 'deleted'!", safchan); return true; } var data = hash.get(target); if (!data) { safaribot.sendMessage(src, "There's no Safari save with the name '{0}' in the {1} file!".format(target, filename), safchan); return true; } if (typeof data === "object") { data = JSON.stringify(data, null, 0); } safaribot.sendMessage(src, target + "'s backup save from " + filename + " file: " + data, safchan); return true; } if (command === "allowname" || command === "sharedname") { if (commandData === "*") { safaribot.sendMessage(src, "The following names are currently allowed to create a new save sharing IP with another player: " + readable(allowedSharedIPNames), safchan); return true; } var id = commandData.toLowerCase(); if (allowedSharedIPNames.contains(id)) { allowedSharedIPNames.splice(allowedSharedIPNames.indexOf(id), 1); safaribot.sendMessage(src, id + " is no longer allowed to create a new save while sharing IP with another player!", safchan); } else { allowedSharedIPNames.push(id); safaribot.sendMessage(src, id + " is now allowed to create a new save despite sharing IP with another player!", safchan); if (sys.id(id)) { safaribot.sendMessage(sys.id(id), "You can now create a Safari save with /start!", safchan); } } sys.appendToFile(miscLog, now() + "|||" + id + "||| was " + (!allowedSharedIPNames.contains(id) ? "dis" : "") + "allowed by " + sys.name(src) + " to create a save despite sharing IP with another player\n"); permObj.add("allowedSharedIPs", JSON.stringify(allowedSharedIPNames)); return true; } if (command === "transferalt") { var data = commandData.split(":"); if (data.length < 2) { safaribot.sendMessage(src, "You need to define 2 names! Use /transferalt Name1:Name2 for that!", safchan); return true; } safari.transferAlt(data[0], data[1], src); return true; } if (command === "tradelog") { safari.showLog(src, command, commandData, tradeLog, "trade", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var p1 = info[1].split("::")[0]; var p1offer = info[1].split("::")[1]; var p2 = info[2].split("::")[0]; var p2offer = info[2].split("::")[1]; var undoLink = link(info[3], "[undo]", true); if (undoLink.indexOf("undefined") > -1) { undoLink = ""; } return html_escape(p1 + "'s " + p1offer + " <--> " + p2 + "'s " + p2offer + " - (" + time + ") ") + undoLink; }, null, true); return true; } if (command === "giftlog") { safari.showLog(src, command, commandData, giftLog, "gift", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var name = info[1]; var target = info[2]; var commandName = info[3]; var verb = info[4]; var gift = info[5]; return name + " used /" + commandName + " --- " + target + " " + verb + " " + gift + " --- (" + time + ")"; }); return true; } if (command === "raretrades") { safari.showLog(src, command, commandData, rareTradeLog, "rare trade", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var data = info[1]; return data + " - (" + time + ")"; }); return true; } if (command === "shoplog") { safari.showLog(src, command, commandData, shopLog, "shop", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var p1Info = info[1].split("::"); var p1 = p1Info[0]; var amount = parseInt(p1Info[1], 10); var item = p1Info[2]; //var price = parseInt(p1Info[3], 10); var cost = parseInt(p1Info[4], 10); var price = cost/amount; var p2 = info[2].split("::")[0]; var type = p1Info[5]; var translatedCost = "", translatedPrice = ""; if (type) { type = type.replace(/:/g, ""); switch (type) { case "silver": case "true": translatedCost = plural(cost, "silver"); translatedPrice = plural(price, "silver"); break; case "bp": translatedCost = plural(cost, "battlepoint"); translatedPrice = plural(price, "battlepoint"); break; case "shady": price = cost; translatedCost = plural(cost, "shady"); translatedPrice = plural(price, "shady"); break; default: translatedCost = "$" + addComma(cost); translatedPrice = "$" + addComma(price); break; } } else { translatedCost = "$" + addComma(cost); translatedPrice = "$" + addComma(price); } return p2 + " bought " + amount + "x " + item + " from " + p1 + " for " + translatedCost + (amount > 1 ? " (" + translatedPrice + " each)" : "") + " --- (" + time + ")"; }); return true; } if (command === "auctionlog") { safari.showLog(src, command, commandData, auctionLog, "auction", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var p1Info = info[1].split("::"); var p1 = p1Info[0]; // var amount = parseInt(p1Info[1], 10); var item = p1Info[2]; var price = parseInt(p1Info[3], 10); var p2 = info[2].split("::")[0]; return p2 + " won " + p1 + "'s auction for " + item + " by paying $" + addComma(price) + " --- (" + time + ")"; }); return true; } if (command === "altlog") { safari.showLog(src, command, commandData, altLog, "alt Change", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var transfer = info[1].split("::"); var user = info[2]; var original = info.length > 2 ? info[3] : false; return transfer + " --- " + (original ? "[ID: " + original + "] --- " : "") + " (by " + user + " at " + time + ")"; }); return true; } if (command === "lostlog") { safari.showLog(src, command, commandData, lostLog, "lost Pokémon", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var p1Info = info[1].split("::"); var user = p1Info[0]; var comm = p1Info[1]; var extra = p1Info[2]; return user + " used /" + comm + (extra == "." ? "" : " [" + extra + "]") + " --- (" + time + ")"; }); return true; } if (command === "mythlog") { safari.showLog(src, command, commandData, mythLog, "Legendary/Shiny Pokémon spawn", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var p1Info = info[1].split("::"); var pk = p1Info[0]; var act = p1Info[1]; var who = p1Info[2]; if (act == "caught") { return pk + " was caught by " + who + " --- (" + time + ")"; } else if (["hatched from Egg", "hatched from Bright Egg", "won from Raffle"].contains(act)) { return pk + " " + act + " by " + who + " --- (" + time + ")"; } else if (["wonder traded"].contains(act)) { return pk + " " + act + " to " + who + " --- (" + time + ")"; } else { return pk + " " + act + " --- (" + time + ")"; } }); return true; } if (command === "eventlog") { safari.showLog(src, command, commandData, eventLog, "event", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var name = info[1]; var host = info[2]; var signups = info[3]; var finished = info[4]; var winners = info[5]; var rewards = info[6]; var extra = info[7]; return name + " (" + plural(signups.split(",").length, "Player") + ", by " + host + ", " + (finished == "true" ? "finished" : "aborted") + ") " + " --- Winners: " + winners + " --- Rewards: " + rewards + (extra ? " --- " + extra : "") + " --- (" + time + ")"; }); return true; } if (command === "misclog") { safari.showLog(src, command, commandData, miscLog, "misc", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var name = info[1]; var action = info[2]; return name + " " + action + " --- (" + time + ")"; }); return true; } if (command === "crosslog") { safari.showLog(src, command, commandData, crossLog, "cross-promotion rewards", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var name = info[1]; var target = info[2]; var gift = info[3]; return name + " awarded " + target + " with " + gift + " --- (" + time + ")"; }); return true; } if (command === "questlog") { safari.showLog(src, command, commandData, questLog, "quest", function(x) { var info = x.split("|||"); var time = new Date(parseInt(info[0], 10)).toUTCString(); var name = info[1]; var quest = info[2]; var input = info[3]; var output = info[4]; return name + " --- " + quest + " quest --- " + input + " --- " + output + " --- (" + time + ")"; }); return true; } if (command === "showids") { var list = []; for (var e in idnumList.hash) { if (idnumList.hash.hasOwnProperty(e)) { list.push(e + ": " + idnumList.hash[e]); } } list.sort(function(b, a) { var inA = parseInt(a.substr(0, a.indexOf(":")), 10); var inB = parseInt(b.substr(0, b.indexOf(":")), 10); return inB - inA; }); safari.showLogList(src, command, commandData, list, "ID Numbers", function(x) { var n = x.substr(x.indexOf(": ") + 2); return x + " [" + link("/analyze " + n + ":", "Analyze", true) + "]" + " [" + link("/identify " + n, "Identify", false) + "]"; }, null, true); return true; } if (command === "cleanbans") { safari.clearBans(src); return true; } if (command === "safarigift" || command === "gift") { var cmd = commandData.split(":"); if (cmd.length < 2) { safaribot.sendMessage(src, "Invalid format! Use /safarigift Player:Item:Amount.", safchan); return true; } var target = cmd[0]; var res, playerArray = []; if (target.indexOf(",") !== -1) { res = target.split(","); for (var i = 0; i < res.length; i++) { playerArray.push(res[i].trim()); } } else { playerArray.push(target); } var item = cmd[1].toLowerCase(); var itemQty = cmd.length > 2 ? parseInt(cmd[2], 10) : 1; if (isNaN(itemQty)) { itemQty = 1; } if (allItems.indexOf(item) === -1) { safaribot.sendMessage(src, "No such item!", safchan); return true; } var player, index, invalidPlayers = []; for (var j = 0; j < playerArray.length; j++) { player = getAvatarOff(playerArray[j]); if (!player) { invalidPlayers.push(playerArray[j]); index = playerArray.indexOf(playerArray[j]); playerArray.splice(index, 1); continue; } if (item === "burn" && player.balls[item] === 0 && itemQty > 0) { player.burnLastUsed = now(); } player.balls[item] += itemQty; if (item === "entry") { rafflePlayers.add(player.id, player.balls.entry); rafflePlayers.save(); } this.updateShop(player, item); this.sanitize(player); } var customValues; try { customValues = JSON.parse(permObj.get("customItemData"))[item].fullName; } catch (err) { customValues = item; } if (playerArray.length > 0) { var targets = readable(playerArray.map(function (x) { return x.toCorrectCase(); }), "and"); safaribot.sendAll(targets + " has been awarded with " + plural(itemQty, item) + " by " + sys.name(src) + "!", safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + targets + "|||gift|||" + (itemQty < 0 ? "lost" : "received") + "|||" + plural(itemQty, item) + (customValues === finishName(item) ? " [actually " + defaultItemData[item].fullName + "]" : "") + "\n"); } if (invalidPlayers.length > 0) { safaribot.sendMessage(src, readable(invalidPlayers, "and") + (invalidPlayers.length > 1 ? " were" : " was") + " not given anything because their name did not match any current save file.", safchan); } return true; } if (command === "bestow") { var cmd = commandData.split(":"); if (cmd.length < 2) { safaribot.sendMessage(src, "Invalid format! Use /bestow Player:Pokémon.", safchan); return true; } var target = cmd[0]; var playerId = sys.id(target); var player = getAvatarOff(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } var info = getInputPokemon(cmd[1]); var remove = cmd.length > 2 && ["remove", "confiscate", "-1"].contains(cmd[2].toLowerCase()); if (!info.num) { safaribot.sendMessage(src, "Invalid Pokémon!", safchan); return true; } if (remove) { if (!player.pokemon.contains(info.id)) { safaribot.sendMessage(src, target.toCorrectCase() + " doesn't have " + an(info.name) + "!", safchan); return true; } if (info.id == player.starter && countRepeated(player.pokemon, info.id) <= 1) { safaribot.sendMessage(src, "You can't remove " + target.toCorrectCase() + "'s starter Pokémon!", safchan); return true; } if (safari.isHostingAuction(player.id)) { safaribot.sendMessage(src, "You can't remove a Pokémon from " + target.toCorrectCase() + " while they are hosting an auction!", safchan); return true; } safari.removePokemon2(player, info.id); if (player.party.length === 0) { player.party = [player.starter]; } if (player.shop && player.shop.hasOwnProperty(info.input) && player.shop[info.input].limit > countRepeated(player.pokemon, info.id)) { delete player.shop[info.input]; } safari.logLostCommand(sys.name(src), "bestow " + target.toCorrectCase() + ":" + info.input + ":" + cmd[2]); safaribot.sendMessage(src, "You took away " + an(info.name) + " from " + target.toCorrectCase() + "!", safchan); if (playerId) { safaribot.sendMessage(playerId, "A Safari Warden confiscated " + an(info.name) + " from you!", safchan); } sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + target.toCorrectCase() + "|||bestow|||lost|||" + info.name + "\n"); } else { safaribot.sendMessage(src, "You gave " + an(info.name) + " to " + target.toCorrectCase() + "!", safchan); if (playerId) { safaribot.sendMessage(playerId, "You received " + an(info.name) + "!", safchan); } player.pokemon.push(info.id); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + target.toCorrectCase() + "|||bestow|||received|||" + info.name + "\n"); } this.saveGame(player); return true; } } if (SESSION.channels(safchan).isChannelOwner(src)) { if (command === "aliases") { require("modcommands.js").handleCommand(src, "aliases", commandData, -1, channel); return true; } if (command === "identify") { var player = getAvatarOff(commandData); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "{0}'s Information | IDnum: {1} | Original Name: {2} {3} {4}".format(player.id.toCorrectCase(), player.idnum, player.altlog[0], (player.locked ? " [Locked]" : ""), player.tradeban > now() ? " [Tradebanned]" : ""), safchan); safaribot.sendMessage(src, "Recent Alts (Safari): {0}".format(player.altlog.slice(-10).join(", ")), safchan); safaribot.sendHtmlMessage(src, "Recent Alts (Server): " + link("/aliases " + player.id) + " or " + link("/aliases ~" + player.id) + (sys.id(commandData) ? " (on " + sys.os(sys.id(commandData)) + ")" : ""), safchan); safaribot.sendMessage(src, "Created: {0} | Tutorial Finished: {1} | Tutorial Duration {2}".format(new Date(player.created).toUTCString(), new Date(player.tutorialFinished).toUTCString(), utilities.getTimeString((player.tutorialFinished - player.created) / 1000)), safchan); sys.sendMessage(src, "", safchan); return true; } var shopCommands = ["npcadd", "addnpc", "npcremove", "removenpc", "closenpc", "npcclose", "npcclean", "cleannpc", "silveradd", "addsilver"]; if (shopCommands.contains(command)) { var action = "remove"; var isSilver = false; switch (command) { case "npcadd": case "addnpc": action = "add"; break; case "silveradd": case "addsilver": action = "add"; isSilver = true; break; case "closenpc": case "npcclose": action = "close"; break; case "cleannpc": case "npcclean": action = "clean"; break; case "npcremove": case "removenpc": action = "remove"; break; } safari.editShop(src, action + ":" + commandData, true, isSilver); return true; } if (command === "addrecipe") { if (commandData == "*") { safaribot.sendHtmlMessage(src, "To add or edit an Alchemist recipe, use " + link("/addrecipe RecipeName:Reward:Ingredients:Cooldown:FailChance:FailUses:Transmutation:Immediate:Records", null, true), safchan); return true; } var data = toCommandData(commandData, ["name", "reward", "ingredients", "cooldown", "failChance", "failUses", "transmutation", "immediate", "records"]); data.name = data.name.toLowerCase(); if (!data.reward || !data.ingredients) { safaribot.sendHtmlMessage(src, "Invalid format! To add or edit an Alchemist recipe, use " + link("/addrecipe RecipeName:Reward:Ingredients:Cooldown:FailChance:FailUses:Transmutation:Immediate", null, true), safchan); return true; } var valid, reward = data.reward.split("|||"); for (var e = 0; e < reward.length; e++) { valid = validateStuff(reward[e]); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid reward found: " + readable(valid) + "!", safchan); return true; } } data.reward = reward.length === 1 ? toStuffObj(reward[0]) : reward.map(toStuffObj); valid = validateStuff(data.ingredients); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid ingredient found: " + readable(valid) + "!", safchan); return true; } data.ingredients = toStuffObj(data.ingredients); if (data.failChance) { data.failChance = parseFloat(data.failChance); if (isNaN(data.failChance)) { safaribot.sendHtmlMessage(src, "Please choose a valid fail chance!", safchan); return true; } if (data.failUses) { if (!["FailUses", "0", "false", ""].contains(data.failUses)) { valid = validateStuff(data.failUses); if (valid.length > 0) { safaribot.sendMessage(src, "Invalid failUses found: " + readable(valid) + "!", safchan); return true; } data.failUses = toStuffObj(data.failUses); } } } else { data.failChance = 0; } if (data.cooldown) { data.cooldown = parseFloat(data.cooldown); if (isNaN(data.cooldown)) { safaribot.sendHtmlMessage(src, "Please choose a valid duration for the cooldown!", safchan); return true; } } else { data.cooldown = 2; } if (data.records) { if (["Records", "false", "0", ""].contains(data.records)) { data.records = null; } else { var info = data.records.split(","), out = {}, valid = [], e, i, n; var trim = function(x) { return x.trim(); }; for (e = 0; e < info.length; e++) { i = info[e].split("=").map(trim); if (i.length < 2) { valid.push("Format is recordName=1"); break; } if (!playerTemplate.records.hasOwnProperty(i[0])) { valid.push(i[0] + " is not a valid record"); continue; } n = parseInt(i[1], 10); if (isNaN(n) || n === 0) { valid.push(i[1] + " is not a valid value for " + i[0]); continue; } out[i[0]] = n; } if (valid.length > 0) { safaribot.sendMessage(src, "Invalid records changes found: " + readable(valid) + "!", safchan); return true; } data.records = out; } } data.transmutation = data.transmutation && ["true", "yes", "y", "1"].contains(data.transmutation.toLowerCase()) ? true : false; data.immediate = data.immediate && ["true", "yes", "y", "1"].contains(data.immediate.toLowerCase()) ? true : false; var rec = { cooldown: data.cooldown, reward: data.reward, ingredients: data.ingredients, failChance: data.failChance, transmutation: data.transmutation, immediate: data.immediate }; if (data.failUses) { rec.failUses = data.failUses; } if (data.records) { rec.records = data.records; } recipeData[data.name] = rec; permObj.add("alchemistRecipes", JSON.stringify(recipeData)); sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Recipe " + cap(data.name, true) + " added to Alchemist quest!", safchan); safaribot.sendMessage(src, "Ingredients: " + translateStuff(rec.ingredients), safchan); safaribot.sendMessage(src, "Reward: " + (Array.isArray(rec.reward) ? readable(rec.reward.map(translateStuff), "or") : translateStuff(rec.reward)), safchan); safaribot.sendMessage(src, "Cooldown: " + rec.cooldown + "h | Immediate: " + rec.immediate + " | Transmutation: " + rec.transmutation + " | Fail Chance: " + percentage(rec.failChance, 1, 1) + (rec.failChance > 0 && rec.failUses ? " (uses " + (translateStuff(rec.failUses) || "nothing") + " at failure)" : ""), safchan); if (rec.records) { valid = []; for (var e in rec.records) { valid.push(e + " " + addSign(data.records[e])); } safaribot.sendMessage(src, "Record changes: " + valid.join(", "), safchan); } sys.sendMessage(src, "", safchan); return true; } if (command === "showrecipes" || command === "viewrecipes") { var recipes = recipeData, rec, l, cc = 0, colors = ["#FF6347", "#0000FF", "#006400", "#9932CC"]; sys.sendMessage(src, "", safchan); var trans = function(x) { return translateStuff(x, true); }; var nextColor = function(x) { cc++; return toColor(x, colors[cc % colors.length]); }; for (var e in recipes) { rec = recipes[e]; l = "/addrecipe " + e + ":" + (Array.isArray(rec.reward) ? rec.reward.map(toStuffInput).join("|||") : toStuffInput(rec.reward)) + ":" + toStuffInput(rec.ingredients) + ":" + rec.cooldown + ":" + rec.failChance + ":" + toStuffInput(rec.failUses) + ":" + rec.transmutation + ":" + rec.immediate; cc = 0; var possibleRewards = Array.isArray(rec.reward) ? rec.reward.map(trans) : [translateStuff(rec.reward, true)]; var coloredRewards = possibleRewards.map(nextColor); possibleRewards = readable(coloredRewards, "or"); safaribot.sendHtmlMessage(src, "" + link(l, cap(e, true), true) + ": " + toColor(translateStuff(rec.ingredients, true), "red") + " --> "+ possibleRewards + " --- Cooldown: " + rec.cooldown + "h | Transmutation: " + rec.transmutation + " | Immediate: " + rec.immediate + " | Fail Chance: " + percentage(rec.failChance, 1, 1) + (rec.failChance > 0 && rec.failUses ? ", uses " + (translateStuff(rec.failUses) || "nothing") + " at failure" : "") + " --- [" + link("/removerecipe " + e, "Remove", true) + "]", safchan); } sys.sendMessage(src, "", safchan); return true; } if (command === "removerecipe") { var n = commandData.toLowerCase(); if (recipeData.hasOwnProperty(n)) { delete recipeData[n]; safaribot.sendMessage(src, "Removed recipe " + n + " from Alchemist!", safchan); permObj.add("alchemistRecipes", JSON.stringify(recipeData)); } else { safaribot.sendMessage(src, cap(n) + " is not a valid Alchemist recipe!", safchan); } return true; } if (command === "checkrate") { commandData = commandData.toLowerCase(); if (allItems.indexOf(commandData) !== -1 || commandData === "wild" || commandData === "nothing" || commandData === "recharge") { var itemSets = [gachaItems, finderItems, packItems]; var method = ["Gachapon", "Item Finder", "Prize Pack"]; for (var i = 0; i < itemSets.length; i++) { var nothing = method[i] === "Item Finder" && commandData === "nothing"; var total = 0; var instance = itemSets[i][commandData] || 0; if (instance < 1 && !nothing) { safaribot.sendMessage(src, method[i] + ": This item is not available from " + method[i] + ".", safchan); } else { for (var e in itemSets[i]) { total += itemSets[i][e]; } if (method[i] === "Item Finder") { total = Math.floor(total / (1 - finderMissRate)); } if (nothing) { instance = total * finderMissRate; } safaribot.sendMessage(src, method[i] + ": The rate of " + finishName(commandData) + " is " + instance + "/" + total + ", or " + percentage(instance, total) + ".", safchan); } } } else { safaribot.sendMessage(src, "No such item!", safchan); } return true; } //Enable if necessary. Kind of dangerous to leave this floating around. /* if (command === "newleague") { safari.renewLeague(); safaribot.sendHtmlMessage(src, "League was renewed! Check with " + link("/viewgyms") + " and " + link("/viewelite") + ".", safchan); return true; } */ if (command === "viewgyms") { var e, gym, t, trainer; sys.sendMessage(src, "", safchan); for (e in gymData) { gym = gymData[e]; safaribot.sendMessage(src, gym.name + " Gym (gives " + gym.badge + ")", safchan); for (t = 0; t < gym.trainers.length; t++) { trainer = gym.trainers[t]; safaribot.sendMessage(src, "-" + trainer.name + " (Power: " + (1+trainer.powerBoost) + "x): " + readable(trainer.party.map(poke)) , safchan); } sys.sendMessage(src, "", safchan); } return true; } if (command === "viewelite") { var e, trainer; sys.sendMessage(src, "", safchan); safaribot.sendMessage(src, "Elite Four Members: ", safchan); for (e = 0; e < eliteData.length; e++) { trainer = eliteData[e]; safaribot.sendMessage(src, "-" + trainer.name + " (Power: " + (1+trainer.powerBoost) + "x): " + readable(trainer.party.map(poke)) , safchan); } sys.sendMessage(src, "", safchan); return true; } if (["wild", "wildevent", "horde", "wild2"].contains(command)) { if (currentPokemon) { safaribot.sendMessage(src, "There's already a Wild Pokemon out there silly!", safchan); return true; } var data = commandData.split(":"); var info = getInputPokemon(data[0]), num = info.num, makeShiny = info.shiny; var amount = command === "horde" ? 3 : 1; if (data.length > 1) { amount = parseInt(data[1], 10); } var appearAs = null; if (data.length > 2) { appearAs = getInputPokemon(data[2]); } if (command === "wildevent") { wildEvent = true; } else if (command === "wild2" && contestCount === 0) { var bName = finishName("bait").toLowerCase(); if (amount > 1 || baitCooldown > 28 || chance(0.3)) { safaribot.sendAll("Some stealthy person goes to grab their item from the Gachapon Machine but the noise lured a wild Pokémon!", safchan); } else { safaribot.sendAll("Some stealthy person left some " + bName + " out. The " + bName + " attracted a wild Pokémon!", safchan); } } safari.createWild(num, makeShiny, amount, null, null, null, appearAs); return true; } if (command === "wildtest") { var iterations = commandData; var out = "Spawns (" + iterations + " iterations): "; var totalBST = 0; var rareCount = 0; for (x = 0; x < iterations; x++) { var spawn = safari.createWild(null, null, 1, null, null, null, null, null, null, true); if (isRare(spawn)) { out += "" + pokePlain(spawn) + " "; rareCount++; } else { out += pokePlain(spawn) + " "; } totalBST += getBST(spawn); } safaribot.sendHtmlMessage(src, "Theme: " + (currentTheme ? link("/themerares " + currentTheme, themeName(currentTheme)) + (currentThemeAlter ? " (" + contestThemes[currentTheme].alterName + ")" : "") + (currentThemeEffect ? " [" + cap(currentThemeEffect) + "]" + (currentThemeSecondary ? " [" + themeName(currentThemeSecondary) + "]" : ""): "") : "Default"), safchan); safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Rare Count: " + rareCount, safchan); safaribot.sendHtmlMessage(src, "Average BST: " + (totalBST/iterations), safchan); safaribot.sendHtmlMessage(src, out, safchan); return true; } if (command === "raretest") { var bstRequirement = parseInt(commandData); var totalSpawns = 0; var totalBST = 0; var rareCount = 0; var target = ""; while (true) { var spawn = safari.createWild(null, null, 1, null, null, null, null, null, null, true); totalSpawns++; if (isRare(spawn)) { rareCount++; } totalBST += getBST(spawn); if (getBST(spawn) >= bstRequirement) { target = pokePlain(spawn); break; } } safaribot.sendHtmlMessage(src, "Theme: " + (currentTheme ? link("/themerares " + currentTheme, themeName(currentTheme)) + (currentThemeAlter ? " (" + contestThemes[currentTheme].alterName + ")" : "") + (currentThemeEffect ? " [" + cap(currentThemeEffect) + "]" + (currentThemeSecondary ? " [" + themeName(currentThemeSecondary) + "]" : ""): "") : "Default"), safchan); safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "Rare Count: " + rareCount, safchan); safaribot.sendHtmlMessage(src, "Average BST: " + (totalBST/totalSpawns), safchan); safaribot.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, "BST Requirement: " + bstRequirement + ", found a " + target + " after " + totalSpawns + " total spawns.", safchan); return true; } if (command === "contest" || command === "contestsoft") { if (command == "contestsoft") { contestBroadcast = false; } if (contestCount > 0) { contestCount = 1; } else { safari.startContest(commandData); } return true; } if (command === "precontest") { if (contestCount > 0) { safaribot.sendMessage(src, "You can't use this command during a Contest!", safchan); } else { contestCooldown = 181; safaribot.sendMessage(src, "Entering the pre-contest preparation phase!", safchan); var data = commandData.split(":"); if (data.length >= 3) { chosenThemes = []; for (var i = 0; i < data.length; i++) { if (contestThemes.hasOwnProperty(data[i].toLowerCase())) { chosenThemes.push(data[i].toLowerCase()); safaribot.sendMessage(src, "Adding theme " + data[i].toLowerCase() + " to the precontest.", safchan); } } if (chosenThemes.length < 3) { chosenThemes = null; safaribot.sendMessage(src, "Submit 3 valid themes to start or random ones will be chosen!", safchan); } } } return true; } if (command === "skipcontest") { if (contestCount > 0 || contestCooldown <= 180) { if (contestCount > 0) { if (currentPokemon && isRare(currentPokemon)) { sys.appendToFile(mythLog, now() + "|||" + (wildSpirit ? "Spirit Realm " : "") + poke(currentPokemon) + "::disappeared with the " + themeName(currentTheme) + " Contest" + (wildEvent ? " (Event)" : "") + (Object.keys(wildBallThrows).length === 0 ? " (No Throws)" : "") + "::\n"); } resetVars(); currentRules = null; sys.sendAll(separator, safchan); safaribot.sendAll("The current Contest has been aborted!", safchan); sys.sendAll(separator, safchan); } else { sys.sendAll(separator, safchan); safaribot.sendAll("The next Contest will be skipped!", safchan); sys.sendAll(separator, safchan); } contestCooldown = contestCooldownLength; contestCount = 0; currentTheme = null; currentThemeAlter = false; currentThemeEffect = null; currentThemeFlavor = null; currentThemeSecondary = null; nextRules = null; nextTheme = null; contestCatchers = {}; contestActivity = {}; contestCombo = 0; contestComboPlayers = []; checkUpdate(); } else { safaribot.sendMessage(src, "You can't skip a Contest if there's none running or about to start!", safchan); } return true; } if (command === "wipesafari" || command === "bwipesafari" || command === "wipesafarib") { var name = commandData.toLowerCase(); var playerId = sys.id(name); var player = getAvatarOff(name); if (player) { cookedPlayers.add(player.id, JSON.stringify(player)); //Create a backup in case a save is wiped accidentally if (playerId) { SESSION.users(playerId).safari = null; } rafflePlayers.remove(name); rawPlayers.remove(name); idnumList.remove(player.idnum); if (command === "wipesafari") { safaribot.sendMessage(src, commandData + "'s safari has been reset!", safchan); } else { safaribot.sendAll(commandData + "'s safari has been reset!", safchan); } sys.appendToFile(miscLog, now() + "|||" + name + "||| had their save wiped by " + sys.name(src) + "\n"); } else { safaribot.sendMessage(src, "No such person!", safchan); } return true; } if (command === "loadsafari") { var info; try { info = JSON.parse(commandData); } catch (err) { safaribot.sendMessage(src, "Invalid JSON!", safchan); return; } var name = info.id; var id = sys.id(name); if (id) { SESSION.users(id).safari = info; } safari.saveGame(info); safaribot.sendMessage(src, "Created save with the name " + name + "!", safchan); return true; } if (command === "forgerecord") { var cmd = commandData.split(":"); var target = cmd[0]; var player = getAvatarOff(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } var record = cmd[1]; if (!(record in playerTemplate.records)) { safaribot.sendMessage(src, "Invalid record!", safchan); return true; } var recValue = parseInt(cmd[2], 10); if (isNaN(recValue)) { safaribot.sendMessage(src, "Invalid amount!", safchan); return true; } player.records[record] = recValue; this.sanitize(player); safaribot.sendAll(target.toCorrectCase() + "'s \"" + record + "\" record has been changed to " + recValue + " by " + sys.name(src) + "!", safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + target.toCorrectCase() + "|||forgerecord|||had their " + record + " record forged to|||" + recValue + "\n"); return true; } if (command === "forgerecordall") { var cmd = toCommandData(commandData, ["prop", "val"]); if (cmd.prop === "*" || !cmd.prop) { safaribot.sendMessage(src, "Use /forgerecordall [RecordName]:[NewValue] to change a record for all saves.", safchan); return true; } if (!playerTemplate.records.hasOwnProperty(cmd.prop)) { safaribot.sendMessage(src, "No such record!", safchan); return true; } var rec = cmd.prop; var val = parseInt(cmd.val, 10); if (isNaN(val)) { safaribot.sendMessage(src, "Please type a valid value for the record!", safchan); return true; } var e, data, obj = {}, player, failed = []; for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = getAvatarOff(e); if (player) { player.records[rec] = val; safari.saveGame(player); } else { failed.push(e); } } } safaribot.sendMessage(src, "Everyone's " + rec + " record has been changed to " + val + "!", safchan); if (failed.length > 0) { safaribot.sendMessage(src, "Exceptions: " + failed.join(", "), safchan); } sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||Everyone|||forgerecordall|||had their " + cmd.prop + " record forged to|||" + val + "\n"); return true; } if (command === "reassignid") { var target = commandData; var player = getAvatarOff(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } this.assignIdNumber(player, true); this.saveGame(player); safaribot.sendMessage(src, target.toCorrectCase() + "'s ID has been reset and is now " + player.idnum + ".", safchan); return true; } if (command === "safaripay") { var cmd = commandData.split(":"); var target = cmd[0]; var player = getAvatarOff(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } var moneyGained = parseInt(cmd[1], 10); if (isNaN(moneyGained)) { safaribot.sendMessage(src, "Invalid amount!", safchan); return true; } player.money += moneyGained; this.sanitize(player); safaribot.sendAll(target.toCorrectCase() + " has been awarded with $" + moneyGained + " by " + sys.name(src) + "!", safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + target.toCorrectCase() + "|||safaripay|||" + (moneyGained < 0 ? "lost" : "received") + "|||$" + addComma(moneyGained) + "\n"); return true; } if (command === "reward" || command === "sreward") { var data = toCommandData(commandData, ["players", "assets"]); if (!data.players || !data.assets) { safaribot.sendMessage(src, "Format is '/reward Player1,Player2,etc:Asset1,Asset2,etc'.", safchan); return true; } var players = toUserNames(data.players), player, p, id, invalid = []; for (p = 0; p < players.length; p++) { id = players[p]; if (!getAvatarOff(id)) { invalid.push(id); } } if (invalid.length > 0) { safaribot.sendMessage(src, "The following names do not have a Safari save: " + readable(invalid), safchan); return true; } var stuff = toStuffObj(data.assets.replace(/,/g, ":")); invalid = validateStuff(data.assets); if (invalid.length > 0) { safaribot.sendMessage(src, "Invalid assets found: " + readable(invalid) + " !", safchan); return true; } var out; for (p = 0; p < players.length; p++) { id = players[p]; player = getAvatarOff(id); out = giveStuff(player, stuff); if (sys.id(id)) { safaribot.sendMessage(sys.id(id), "You " + out + "!", safchan); } safari.sanitize(player); } if (command === "reward") { safaribot.sendAll(sys.name(src) + " rewarded " + readable(players) + " with " + translateStuff(stuff) + "!", safchan); } safaribot.sendMessage(src, "You rewarded " + readable(players) + " with " + translateStuff(stuff) + "!", safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + readable(players) + "|||" + command +"|||received|||" + translateStuff(stuff) + "\n"); return true; } if (command === "givedeco") { var cmd = commandData.split(":"); if (cmd.length < 2) { safaribot.sendMessage(src, "Invalid format! Use /givedeco Player:Decoration:Amount.", safchan); return true; } var target = cmd[0]; var res, playerArray = []; if (target.indexOf(",") !== -1) { res = target.split(","); for (var i = 0; i < res.length; i++) { playerArray.push(res[i].trim()); } } else { playerArray.push(target); } var item = decorationAlias(cmd[1].toLowerCase(), true); var itemQty = cmd.length > 2 ? parseInt(cmd[2], 10) : 1; if (isNaN(itemQty)) { itemQty = 1; } if (!decorations.hasOwnProperty(item)) { safaribot.sendMessage(src, "No such decoration!", safchan); return true; } var player, index, invalidPlayers = []; for (var j = 0; j < playerArray.length; j++) { player = getAvatarOff(playerArray[j]); if (!player) { invalidPlayers.push(playerArray[j]); index = playerArray.indexOf(playerArray[j]); playerArray.splice(index, 1); continue; } if (!player.decorations.hasOwnProperty(item)) { player.decorations[item] = 0; } player.decorations[item] += itemQty; if (itemQty < 0) { this.sanitizeBase(player); } this.sanitize(player); } var decoName = decorationAlias(item, false, true); if (playerArray.length > 0) { var targets = readable(playerArray.map(function (x) { return x.toCorrectCase(); }), "and"); safaribot.sendAll(targets + " has been awarded with " + plural(itemQty, decoName) + " by " + sys.name(src) + "!", safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + targets + "|||givedeco|||" + (itemQty < 0 ? "lost" : "received") + "|||" + plural(itemQty, decoName) + "\n"); } if (invalidPlayers.length > 0) { safaribot.sendMessage(src, readable(invalidPlayers, "and") + (invalidPlayers.length > 1 ? " were" : " was") + " not given anything because their name did not match any current save file.", safchan); } return true; } if (command === "clearcd") { var cmd = commandData.split(":"); if (cmd.length < 2) { safaribot.sendMessage(src, "Wrong format! Use /clearcd Player:Type!", safchan); safaribot.sendMessage(src, "Types can be ball, bait, auction, stick, costume, rock, gacha, itemfinder, baseView, unown, burn, price, collector, scientist, arena, tower, pyramid, wonder, alchemist, league, decor or arborist!", safchan); return true; } var target = cmd[0]; var player = getAvatarOff(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } var type = cmd[1].toLowerCase(); switch (type) { case "collector": case "scientist": case "arena": case "tower": case "wonder": case "pyramid": case "baking": case "alchemist": case "decor": case "league": case "journal": case "arborist": player.quests[type].cooldown = 0; break; case "ball": case "bait": case "golden": case "stick": case "costume": case "rock": case "auction": case "gacha": case "itemfinder": case "burn": case "unown": case "price": case "unsell": player.cooldowns[type] = 0; break; case "baseview": SESSION.users(src).secretBaseView = 0; break; default: safaribot.sendMessage(src, type + " is not a valid cooldown!", safchan); return true; } this.saveGame(player); safaribot.sendMessage(src, target.toCorrectCase() + "'s cooldown for " + type + " was reset!", safchan); return true; } if (command === "disablequest" || command === "stopquest" || command === "enablequest") { if (commandData === "*") { var status = []; for (var e in stopQuests) { status.push(cap(e) + ": " + stopQuests[e]); } safaribot.sendMessage(src, "Disabled? " + status.join(", "), safchan); return true; } var off = (command === "disablequest" || command === "stopquest"); if (commandData.toLowerCase() === "all") { for (var e in stopQuests) { stopQuests[e] = off; } safaribot.sendMessage(src, "All quests were " + (off ? "disabled" : "enabled") + ".", safchan); return true; } else if (commandData.toLowerCase() === "long") { stopQuests.pyramid = off; stopQuests.tower = off; stopQuests.league = off; stopQuests.celebrity = off; safaribot.sendMessage(src, "Pyramid, Tower, Celebrity and League were " + (off ? "disabled" : "enabled") + ".", safchan); return true; } var data = commandData.split(":"); var quest = data[0].toLowerCase(); var allQuests = Object.keys(stopQuests); if (allQuests.contains(quest)) { stopQuests[quest] = off; safaribot.sendMessage(src, cap(quest) + " was " + (off ? "disabled" : "enabled") + ".", safchan); } else { safaribot.sendMessage(src, "This is not a valid quest. Valid quests are: " + readable(allQuests, "and") + ".", safchan); } return true; } if (command === "gbtest") { safari.throwBait(src, commandData, true, true); return true; } if (command === "gb300") { safari.throwBait(src, commandData, command, true, false, 300); return true; } if (command === "contest300") { safari.throwBait(src, commandData, false, false, false, true); return true; } if (command === "tourgift") { var tour = commandData.split("*"); var targets = tour[1].split(", "); var placing = 1, player, invalidPlayers = [], out = []; for (var i in targets) { i = targets[i]; player = getAvatarOff(i); if (!player) { invalidPlayers.push(i.toCorrectCase()); placing++; continue; } switch (placing) { case 1: player.balls.rare += 1; player.balls.gem += 1; player.balls.mega += 1; break; case 2: player.balls.rare += 1; player.balls.gem += 1; break; case 3: player.balls.gem += 1; break; } this.sanitize(player); out.push("" + getOrdinal(placing) + ": " + html_escape(i.toCorrectCase())); placing++; } if (out.length === 0) { safaribot.sendMessage(src, "No names supplied match existing Safari Accounts.", safchan); return true; } sendAll("" + cap(tour[0]) + " Event Prizes: " + out.join(" | "), true); if (invalidPlayers.length > 0) { safaribot.sendMessage(src, "The following players did not match any save: " + invalidPlayers.join(", "), safchan); } return true; } if (command === "preptour") { var file = "scriptdata/tourdata/tourhistory.json"; var eventData = JSON.parse(sys.getFileContent(file)).eventtours; var amount = parseInt(commandData, 10); if (isNaN(amount)) { amount = 10; } var firstPos, lastPos, ranks = [], player, playerState = [], tourName, noSave = 0; for (var x = 0; x < amount; x++) { var string = eventData[x]; tourName = string.substring(0, string.indexOf("Event:")).trim(); for (var i = 1; i < 4; i++) { firstPos = string.indexOf("#" + i + ":") + 3; lastPos = string.indexOf(";"); player = string.substring(firstPos, lastPos).trim(); ranks.push(player); if (!hasSave(player)) { playerState.push("~~" + player + "~~"); noSave++; } else { playerState.push(player); } string = string.substring(lastPos + 1); } var rankString = ranks.join(", "); var printString = playerState.join(", "); sys.sendHtmlMessage(src, "[" + link("/tourgift " + tourName + "*" + rankString, "Gift") + "] " + tourName + ": " + (noSave > 0 ? "/tourgift " + tourName + "*" : "") + printString, safchan); ranks = []; playerState = []; noSave = 0; } return true; } if (command === "checksave" || command === "checksaves") { var checks = [], temp = commandData.split(","); for (var i = 0; i < temp.length; i++) { checks.push(temp[i].trim()); } var has = [], hasNot = [], player; for (var j = 0; j < checks.length; j++) { player = hasSave(checks[j]); if (!player) { hasNot.push(checks[j]); } else { has.push(link("/analyze " + html_escape(checks[j]), checks[j])); } } if (has.length > 0) { safaribot.sendHtmlMessage(src, "The following have a save: " + has.join(", "), safchan); } if (hasNot.length > 0) { safaribot.sendMessage(src, "The following do not have a save: " + hasNot.join(", "), safchan); } return true; } if (command === "changeboost") { safari.changeDailyBoost(commandData); return true; } if (command === "nextboost") { var info = getInputPokemon(commandData); if (!info.num) { safaribot.sendMessage(src, commandData + " is not a valid Pokémon!", safchan); return true; } safaribot.sendMessage(src, info.name + " will be the next Boost of the Day!", safchan); permObj.add("nextDailyBoost", info.input); return true; } if (command === "changescientist") { safari.changeScientistQuest(commandData); safaribot.sendMessage(src, "You changed the scientist quest to request for " + poke(scientistQuest.pokemon) + ".", safchan); return true; } if (command === "findsaves") { safaribot.sendMessage(src, "List of all saves by name: " + Object.keys(rawPlayers.hash).sort().join(", "), safchan); return true; } if (command === "scare" || command === "glare") { if (currentPokemon) { safaribot.sendMessage(src, "You glared at the Wild Pokémon until they ran away!", safchan); if (command === "scare") { safaribot.sendAll(sys.name(src) + " scared " + (currentPokemonCount > 1 ? "all " : "") + "the " + poke(currentDisplay) + " away!", safchan); } if (isRare(currentPokemon)) { sys.appendToFile(mythLog, now() + "|||" + poke(currentPokemon) + "::was " + command + "d by " + sys.name(src) + (contestCount > 0 ? " during " + an(themeName(currentTheme)) + " Contest" : "") + "::\n"); } resetVars(true); } checkUpdate(); return true; } if (command === "forceduel") { safari.events.spiritDuelsBattling = true; safari.events.currentSpiritDuel = true; safari.startSpiritDuel(); return true; } if (command === "tiebreakduel") { safari.prepareNextSpiritDuel(true); safari.events.spiritDuelsBattling = true; safari.events.currentSpiritDuel = true; safari.startSpiritDuel(); return true; } if (command === "clearspirits") { safari.clearSpiritMons(src,commandData); return true; } if (command === "bestowspirit") { safari.bestowSpiritMon(src,commandData); return true; } if (command === "bestowcherish") { var cmd = commandData.split(":"); var player = getAvatarOff(cmd[0]); var num = 1; if (!player) { safaribot.sendMessage(src, "Player not found!", safchan); return true; } else if (cmd.length < 2) { safaribot.sendMessage(src, "Invalid Pokemon input.", safchan); return true; } var data = getInputPokemon(cmd[1]); if (data.input === "Missingno") { safaribot.sendMessage(src, "Invalid Pokemon input.", safchan); return true; } if (cmd.length > 2) { num = parseInt(cmd[2], 10) || 1; } var i; if (num === -1) { i = player.cherished.indexOf(data.num); if (i === -1) { safaribot.sendMessage( src,player.id + " doesn't have that Cherished Pokémon!",safchan ); return true; } player.cherished.splice(i, 1); safari.saveGame(player); safaribot.sendMessage( src,"You took away a " + data.name + " from " + player.id + "'s Cherished List.",safchan ); } else { player.cherished.push(data.num); safari.saveGame(player); safaribot.sendMessage( src,"You added " + data.name + " to " + player.id + "'s Cherished List.",safchan ); }; return true; } if (command === "vbdebug") { vbdebug = vbdebug ? false : true; return true; } if (command === "clearccherished") { var player = getAvatarOff(commandData); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } player.cherished = []; this.saveGame(player); return true; } if (command === "clearjackpot") { gachaJackpot = 200; safaribot.sendAll("Gachapon Jackpot was reset!", safchan); return true; } if (command === "changelastid") { var value = parseInt(commandData, 10); if (isNaN(value)) { safaribot.sendMessage(src, "Invalid number!", safchan); return true; } permObj.add("lastIdAssigned", value); permObj.save(); safaribot.sendMessage(src, "Last ID number reset to " + value + ".", safchan); return true; } /*if (command === "wipesafariall") { var info = commandData.toLowerCase().split(":"); if (info[0] !== "confirm") { safaribot.sendMessage(src, "This will wipe all Safari's save data. If you wish to proceed, type /wipesafariall confirm.", safchan); return true; } if (info.length < 2 || info[1] !== "really") { safaribot.sendMessage(src, "Are you absolutely sure you want to delete all saves? This cannot be undone! To confirm, type /wipesafariall confirm:really.", safchan); return true; } var onChannel = sys.playerIds(safchan); for (var e in onChannel) { if (sys.isInChannel(onChannel[e], safchan) && getAvatar(onChannel[e])) { SESSION.users(onChannel[e]).safari = null; } } rawPlayers.clear(); idnumList.clear(); safaribot.sendAll("Safari has been completely reset!", safchan); return true; }*/ if (command === "update") { if (commandData !== "force") { if (contestCount > 0 || contestCooldown < 181) { safaribot.sendMessage(src, "You shouldn't update during or right before a Contest!", safchan); return true; } if (currentPokemon) { safaribot.sendMessage(src, "You shouldn't update while a Wild Pokemon is out!", safchan); return true; } } sys.sendHtmlAll(closedMessage, safchan); safariUpdating = true; safaribot.sendHtmlMessage(src, "There are currently " + currentBattles.length + " ongoing battles, " + currentAuctions.length + " ongoing auctions, " + currentPyramids.length + " ongoing Pyramid quests and " + (currentEvent ? "1" : "0") + " ongoing events.", safchan); return true; } if (command === "cancelupdate") { if (safariUpdating) { safariUpdating = false; sys.sendHtmlAll(openedMessage, safchan); } else { safaribot.sendMessage(src, "Safari was not in an updating state.", safchan); } return true; } if (command === "updategame") { if (!currentPokemon && contestCooldown > 180 && contestCount <= 0 && currentBattles.length === 0 && currentPyramids.length === 0 && currentAuctions.length === 0 && !currentEvent && !safariUpdating) { safariUpdating = true; sys.sendHtmlAll(closedMessage, safchan); } if (!safariUpdating) { safaribot.sendMessage(src, "You shouldn't update without putting Safari into an update ready state with /update.", safchan); return true; } safariUpdating = true; runUpdate(); return true; } if (command === "updateafter") { if (["abort", "cancel", "stop"].contains(commandData.toLowerCase())) { safaribot.sendMessage(src, needsUpdating ? "Pending update has been cancelled." : "No update was pending.", safchan); needsUpdating = false; } else { safaribot.sendMessage(src, "Safari will be updated at the next available opportunity.", safchan); needsUpdating = true; checkUpdate(); //in case it can go right away } return true; } if (command === "isupdateready") { if (now() - lastCheckedRepo < 60000) { safaribot.sendMessage(src, "Please wait " + timeLeftString(lastCheckedRepo + 60000) + " before checking if the update is ready again!", safchan); return true; } lastCheckedRepo = now(); safaribot.sendMessage(src, "Getting Safari script from the web repository...", safchan); if (scriptHashCode === null) { scriptHashCode = hashCode(sys.getFileContent("scripts/safari.js")); } sys.webCall(Config.base_url + "scripts/safari.js", function(resp) { if (hashCode(resp) === scriptHashCode) { safaribot.sendMessage(src, "The web repository for Safari is the same as the local version! Nothing will be changed by updating.", safchan); } else { safaribot.sendHtmlMessage(src, "The web repository for Safari has refreshed! Safari is ready to be updated!", safchan); } lastCheckedRepo = now(); }); return true; } if (command === "updatefile") { var r = commandData.toLowerCase(); if (commandData === "*") { safaribot.sendMessage(src, "Resource files that can be updated are: " + Object.keys(resources.$).join(", "), safchan); } else if (!resources.$.hasOwnProperty(r)) { safaribot.sendMessage(src, "No resource called '" + commandData + "' was found!", safchan); } else { var resource = resources.$[r]; var fname = resource.file.split("/").pop(); safaribot.sendMessage(src, "Downloading '" + fname + "' from " + resource.url, safchan); var result = downloadResource(r); if (result !== null) { safaribot.sendMessage(src, result, safchan); } else { safaribot.sendMessage(src, "Successfully downloaded '" + fname + "'!", safchan); } } return true; } if (command === "updatelb") { safari.updateLeaderboards(); safaribot.sendMessage(src, "Leaderboards updated!", safchan); return true; } if (command === "newmonth") { safaribot.sendMessage(src, "Checking if current month changed!", safchan); safari.checkNewWeek(); safari.checkNewMonth(); return true; } if (command === "forcenewweek") { safaribot.sendMessage(src, "Checking if current month changed!", safchan); safari.checkNewWeek(true); return true; } if (command === "addraffle") { var info = commandData.split(":"); var input = getInput(info[0]); if (!input) { safaribot.sendMessage(src, "Invalid Pokémon/Item!", safchan); return true; } sys.sendAll("", safchan); var amt = 1; if (input.type === "item") { if (info.length > 1) { amt = parseInt(info[1], 10); if (!amt || isNaN(amt)) { amt = 1; } } } var when; if (info.length > 2) { when = info.slice(2).join(":"); input.drawDate = when; } input.amount = amt; if (!rafflePrizeObj) { safaribot.sendAll("A new raffle has started! Buy your tickets for a chance to win " + plural(amt, input.name) + (input.drawDate ? " (Estimated draw date: " + input.drawDate + ")" : "") + "! ", safchan); } else { safaribot.sendAll("The current raffle prize has been changed to " + plural(amt, input.name) + (input.drawDate ? " (Estimated draw date: " + input.drawDate + ")" : "") + ". ", safchan); } sys.sendAll("", safchan); permObj.add("rafflePrize", JSON.stringify(input)); rafflePrizeObj = input; return true; } if (command === "cancelraffle") { var info = commandData.split(":"); if (info[0] === "clearfile") { var refund = 0; if (info.length === 2) { refund = parseInt(info[1], 10); if (isNaN(refund) || refund < 0) { safaribot.sendMessage(src, "Invalid refund amount!", safchan); return true; } } sys.sendAll("", safchan); safaribot.sendAll("The current raffle has been cancelled." + (refund > 0 ? " Tickets have been refunded at $" + refund + " each." : ""), safchan); sys.sendAll("", safchan); rafflePlayers.save(); for (var e in rafflePlayers.hash) { if (rafflePlayers.hash.hasOwnProperty(e)) { this.refundRaffle(getAvatarOff(e), sys.id(e), refund); } } rafflePlayers.clear(); } else if (rafflePrizeObj) { sys.sendAll("", safchan); safaribot.sendAll("The current raffle prize has been cleared.", safchan); sys.sendAll("", safchan); } rafflePrizeObj = null; permObj.add("rafflePrize", JSON.stringify(rafflePrizeObj)); return true; } if (command === "setraffledate") { if (!rafflePrizeObj) { safaribot.sendMessage(src, "There is no ongoing raffle!", safchan); return true; } rafflePrizeObj.drawDate = commandData; permObj.add("rafflePrize", JSON.stringify(rafflePrizeObj)); safaribot.sendMessage(src, "Set the estimated raffle draw date to " + commandData, safchan); return true; } if (command === "checkraffle") { var obj = {}; for (var e in rafflePlayers.hash) { if (rafflePlayers.hash.hasOwnProperty(e)) { obj[e] = parseInt(rafflePlayers.get(e), 10); } } if (Object.keys(obj).length === 0) { safaribot.sendMessage(src, "No one has entered this raffle yet!", safchan); return true; } safaribot.sendMessage(src, "Raffle Entries bought: " + Object.keys(obj).filter(function(x) { return obj[x] > 0; }).map(function(x) { return x + ": " + obj[x]; }).join(" | "), safchan); return true; } if (command === "drawraffle") { if (commandData !== "confirm") { safaribot.sendMessage(src, "This will conclude the current raffle and immediately pick a winner. If you wish to proceed type /drawraffle confirm.", safchan); return true; } if (!rafflePrizeObj) { safaribot.sendMessage(src, "No prize is defined! Use /addraffle to create a prize before trying to draw!", safchan); return true; } //Clean out duplicates rafflePlayers.save(); var obj = {}; for (var e in rafflePlayers.hash) { if (rafflePlayers.hash.hasOwnProperty(e)) { obj[e] = parseInt(rafflePlayers.get(e), 10); } } if (Object.keys(obj).length === 0) { safaribot.sendMessage(src, "No one has entered this raffle meaning you can't draw a winner!", safchan); return true; } var winner = randomSample(obj); var player = getAvatarOff(winner); do { winner = randomSample(obj); player = getAvatarOff(winner); } while (!player); sys.sendAll("", safchan); sys.sendHtmlAll("The winner of the raffle for " + plural(rafflePrizeObj.amount, rafflePrizeObj.name) + " is: " + html_escape(winner.toCorrectCase()) + "!", safchan); safaribot.sendMessage(src, "Raffle Entries bought: " + Object.keys(obj).map(function(x) { return x + ": " + obj[x]; }).join(" | "), safchan); var playerId = sys.id(winner); if (playerId) { safaribot.sendMessage(playerId, "You received " + plural(rafflePrizeObj.amount, rafflePrizeObj.name) + "!", safchan); } safari.notification(player, "You won a raffle for " + plural(rafflePrizeObj.amount, rafflePrizeObj.name) + "!", "Raffle", true); var loser; for (var a in obj) { if (a == winner) { continue; } loser = getAvatarOff(a); safari.notification(player, "The winner of the raffle for " + plural(rafflePrizeObj.amount, rafflePrizeObj.name) + " was " + winner + "!", "Raffle", true); } if (rafflePrizeObj.type == "poke") { player.pokemon.push(rafflePrizeObj.id); if (isRare(rafflePrizeObj.id)) { sys.appendToFile(mythLog, now() + "|||" + poke(rafflePrizeObj.id) + "::won from Raffle::" + winner.toCorrectCase() + "\n"); } } else { rewardCapCheck(player, rafflePrizeObj.id, rafflePrizeObj.amount); } this.saveGame(player); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + player.casedName + "|||drawraffle|||" + "won|||" + plural(rafflePrizeObj.amount, rafflePrizeObj.name) + "\n"); sys.sendAll("", safchan); var currentPlayer; for (var e in obj) { if (obj.hasOwnProperty(e)) { currentPlayer = getAvatarOff(e); if (currentPlayer) { currentPlayer.balls.entry = 0; this.saveGame(currentPlayer); } } } rafflePrizeObj = null; permObj.add("rafflePrize", JSON.stringify(rafflePrizeObj)); rafflePlayers.clear(); return true; } if (command === "ongoing") { var out = [], e, nothingFound = true, obj, list, i; for (e = 0; e < currentBattles.length; e++) { out.push(currentBattles[e].name1 + " x " + currentBattles[e].name2 + (currentBattles[e].battle2 ? " (Rotation)" : "")); } if (out.length > 0) { nothingFound = false; safaribot.sendMessage(src, "Ongoing Battles: " + out.join(" | "), safchan); } out = []; for (e = 0; e < currentAuctions.length; e++) { obj = currentAuctions[e]; out.push(obj.hostName + "'s " + obj.productName + " (Current bid: " + (obj.currentBidder ? "$" + addComma(obj.currentOffer) + " by " + obj.currentBidder.toCorrectCase() : "N/A") + ")"); } if (out.length > 0) { nothingFound = false; safaribot.sendMessage(src, "Ongoing Auctions: " + out.join(" | "), safchan); } out = []; for (e = 0; e < currentPyramids.length; e++) { obj = currentPyramids[e]; list = []; for (i in obj.stamina) { list.push(i.toCorrectCase() + " (" + obj.stamina[i] + ")"); } out.push(readable(list, "and") + " at Room " + obj.level + "-" + obj.room + " (" + plural(obj.points, "Point") + ")"); } if (out.length > 0) { nothingFound = false; safaribot.sendMessage(src, "Ongoing Pyramid quests: " + out.join(" | "), safchan); } out = []; for (e = 0; e < currentBakings.length; e++) { obj = currentBakings[e]; out.push(obj.players.join(", ") + " (" + obj.phase + ")"); } if (out.length > 0) { nothingFound = false; safaribot.sendMessage(src, "Ongoing Great Galarian Bait-Off quests: " + out.join(" | "), safchan); } if (currentEvent) { nothingFound = false; safaribot.sendMessage(src, "Ongoing Event: " + currentEvent.eventName + " (" + plural(currentEvent.signups.length, "player") + ")", safchan); } if (nothingFound) { safaribot.sendMessage(src, "No ongoing Battles, Auctions or Events!", safchan); } return true; } if (command === "stopongoing") { var cancelled = [], obj, e, i, list; for (e = 0; e < currentAuctions.length; e++) { obj = currentAuctions[e]; var player = getAvatar(sys.id(currentAuctions[e].hostName)); cancelled.push(obj.hostName + "'s " + obj.productName + " Auction (Current bid: " + (obj.currentBidder ? "$" + addComma(obj.currentOffer) + " by " + obj.currentBidder.toCorrectCase() : "N/A") + ")"); player.cooldowns.auction = 0; } currentAuctions = []; for (e = 0; e < currentBattles.length; e++) { cancelled.push(currentBattles[e].name1 + " x " + currentBattles[e].name2 + (currentBattles[e].battle2 ? " (Rotation)" : "")); } currentBattles = []; for (e = 0; e < currentPyramids.length; e++) { obj = currentPyramids[e]; list = []; for (i in obj.stamina) { list.push(i.toCorrectCase() + " (" + obj.stamina[i] + ")"); } cancelled.push(readable(list, "and") + " at Room " + obj.level + "-" + obj.room + " (" + plural(obj.points, "Point") + ")"); } currentPyramids = []; if (currentEvent) { currentEvent.log(false, [], "Signups: " + currentEvent.signups.join(", ")); cancelled.push(currentEvent.eventName + " (" + plural(currentEvent.signups.length, "player") + ")"); } currentEvent = null; currentBakings = []; safaribot.sendAll("All ongoing battles, auctions and events have been stopped.", safchan); safaribot.sendMessage(src, "Cancelled activities: " + cancelled.join(" | "), safchan); checkUpdate(); return true; } if (command === "stopplayer") { var target = sys.id(commandData); if (!target) { safaribot.sendMessage(src, "No such person!", safchan); return true; } var player = getAvatar(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } for (var b in currentAuctions) { currentAuctions[b].removePlayer(target, "was shoved from it"); } var battle; for (b = currentBattles.length; b--;) { battle = currentBattles[b]; if (battle.isInBattle(commandData)) { battle.abort(src, commandData); currentBattles.splice(b, 1); } } if (currentEvent && currentEvent.isInEvent(commandData)) { currentEvent.shove(src, commandData); } for (var p = currentPyramids.length; p--;) { if (currentPyramids[p].isInPyramid(commandData)) { currentPyramids[p].abort(src, commandData.toCorrectCase()); currentPyramids.splice(p, 1); } } safaribot.sendMessage(target, "All of your Safari activities have been aborted!", safchan); safaribot.sendMessage(src, "You aborted all of " + sys.name(target) + "'s Safari activities!", safchan); return true; } if (command === "dqraffle" || command === "sdqraffle") { var cmd = commandData.split(":"); var target = cmd[0]; var playerId = command === "sdqraffle" ? null : sys.id(target); var player = getAvatarOff(target); if (!player) { safaribot.sendMessage(src, "No such player!", safchan); return true; } if (playerId) { safaribot.sendMessage(playerId, "A Safari Warden disqualified you from the current raffle.", safchan); } var refund = parseInt(cmd[1], 10) || 0; this.refundRaffle(player, playerId, refund); rafflePlayers.remove(target.toLowerCase()); safari.sanitizeRaffle(); this.saveGame(player); safaribot.sendMessage(src, target.toCorrectCase() + " was disqualified from the current raffle.", safchan); return true; } if (["gachaload", "finderload", "packload"].contains(command)) { var setName = ["Gachapon", "Itemfinder", "Prize Pack"][["gachaload", "finderload", "packload"].indexOf(command)]; if (commandData === "*") { switch (command) { case "gachaload": safaribot.sendMessage(src, setName + " set: " + JSON.stringify(gachaItems), safchan); break; case "finderload": safaribot.sendMessage(src, setName + " set: " + JSON.stringify(finderItems), safchan); break; case "packload": safaribot.sendMessage(src, setName + " set: " + JSON.stringify(packItems), safchan); break; } return true; } var data; try { data = JSON.parse(commandData); } catch (err) { safaribot.sendMessage(src, "Invalid JSON format!", safchan); return true; } for (var e in data) { if (allItems.indexOf(e) !== -1 || e === "wild" || e === "nothing" || e === "recharge" || e == "silver2") { continue; } safaribot.sendMessage(src, e + " is not a valid item!", safchan); return true; } switch (command) { case "gachaload": gachaItems = data; permObj.add("gachaRates", JSON.stringify(data)); break; case "finderload": finderItems = data; permObj.add("finderRates", JSON.stringify(data)); break; case "packload": packItems = data; permObj.add("packRates", JSON.stringify(data)); break; } safaribot.sendMessage(src, "Successfully loaded " + setName + " chances!", safchan); return true; } if (command === "showdetective") { var out = JSON.stringify(safari.detectiveData); safaribot.sendMessage(sys.id("Blinky"), sys.name(src) + " is using /showdetective", safchan); sys.appendToFile(questLog, now() + "|||" + sys.name(src) + "|||Detective|||Used /showdetective|||N/A\n"); safaribot.sendMessage(src, out, safchan); return true; } if (command === "resetdetective") { var out, data; if (safari.detectiveData.hasOwnProperty(commandData)) { safari.detectiveData[commandData] = null; delete safari.detectiveData[commandData]; safaribot.sendMessage(src, "Reset detective data for player with UID " + commandData + ".", safchan); return true; } safaribot.sendMessage(src, "No detective data for player with UID " + commandData + " found.", safchan); return true; } if (command === "loaddatadump") { var title = commandData; safari.dataDumps[title] = {}; for (var i = 1; i <= 802; i++) { safari.dataDumps[title][i+""] = { "Categories": [], "Submitter": "", "Completed": false }; } safaribot.sendMessage(src, "Data dump " + title + " added.", safchan); return true; } if (command === "loaddatadump2") { var title = commandData; safari.dataDumps2[title] = {}; for (var i = 1; i <= 621; i++) { safari.dataDumps2[title][i+""] = { "Categories": [], "Submitter": "", "Completed": false }; } safaribot.sendMessage(src, "Data dump " + title + " added.", safchan); return true; } if (command === "cleardatadump") { var info = commandData.split(":"); title = info[0]; mon = info[1]; safari.dataDumps[title][mon+""] = { "Categories": [], "Submitter": "", "Completed": false }; safaribot.sendMessage(src, "Data dump " + title + " cleared for entry " + mon + ".", safchan); return true; } if (command === "cleardatadump2") { var info = commandData.split(":"); title = info[0]; mon = info[1]; safari.dataDumps2[title][mon+""] = { "Categories": [], "Submitter": "", "Completed": false }; safaribot.sendMessage(src, "Data dump " + title + " cleared for entry " + mon + ".", safchan); return true; } if (command === "showdatadump" || command === "showdatadumpclean") { var out, data; if (safari.dataDumps.hasOwnProperty(commandData)) { out = JSON.stringify(safari.dataDumps[commandData]); safaribot.sendMessage(src, out, safchan); return true; } safaribot.sendMessage(src, "No data dump " + commandData + " found.", safchan); return true; } if (command === "showdatadump2" || command === "showdatadump2clean") { var out, data; if (safari.dataDumps2.hasOwnProperty(commandData)) { out = JSON.stringify(safari.dataDumps2[commandData]); safaribot.sendMessage(src, out, safchan); return true; } safaribot.sendMessage(src, "No data dump " + commandData + " found.", safchan); return true; } if (command === "loadthemes" || command === "loadtheme") { var cThemes = contestThemes; var url = commandData === "*" ? (permObj.get("themesurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading themes from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { contestThemes = JSON.parse(resp); sys.write(themesFile, resp); permObj.add("themesurl", url); safaribot.sendMessage(src, "Contest Themes successfully loaded!", safchan); } catch (error) { contestThemes = cThemes; safaribot.sendMessage(src, "Couldn't load Contest Themes from " + url + "! Error: " + error, safchan); } }); } catch (err) { contestThemes = cThemes; safaribot.sendMessage(src, "Couldn't load Contest Themes from " + url + "! Error: " + err, safchan); } return true; } if (command === "loaddecorations" || command === "loaddecoration") { var cThemes = decorations; var url = commandData === "*" ? (permObj.get("decosurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading decorations from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { decorations = JSON.parse(resp); sys.write(decorationsFile, resp); permObj.add("decosurl", url); safaribot.sendMessage(src, "Decorations successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { decorations = cThemes; safaribot.sendMessage(src, "Couldn't load Decorations from " + url + "! Error: " + error, safchan); } }); } catch (err) { decorations = cThemes; safaribot.sendMessage(src, "Couldn't load Decorations from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadmissions" || command === "loadmissions") { var cThemes = missionsData; var url = commandData === "*" ? (permObj.get("missionsurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading missions from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { missionsData = JSON.parse(resp); sys.write(missionsFile, resp); permObj.add("missionsurl", url); safaribot.sendMessage(src, "Missions successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { missionsData = cThemes; safaribot.sendMessage(src, "Couldn't load Missions from " + url + "! Error: " + error, safchan); } }); } catch (err) { missionsData = cThemes; safaribot.sendMessage(src, "Couldn't load Missions from " + url + "! Error: " + err, safchan); } return true; } if (command === "itemicon") { safaribot.sendHtmlMessage(src, "" + commandData + "", safchan); return true; } if (command === "costumeicon") { safaribot.sendHtmlMessage(src, "", safchan); return true; } if (command === "ricemode") { safari.riceMode = (safari.riceMode ? false : true); safaribot.sendMessage(src, "Toggled RiceMode " + (safari.riceMode ? "on" : "off") + "!", safchan); return true; } if (command === "detectiveupdates") { safari.detectiveUpdates = (safari.detectiveUpdates ? false : true); safaribot.sendMessage(src, "Toggled Detective Updates " + (safari.detectiveUpdates ? "on" : "off") + "!", safchan); return true; } if (command === "massitem") { var info = commandData.split(":"); this.massConvertItem(info[0], info[1]); return true; } if (command === "killallbattles") { currentBattles = []; return; } if (command === "testtagbattle") { var npc = { party: [3, 6, 9, 154], name: "Alex", bias: ["recoil", "drain"], select: { sun: true, hugePower: true, simple: true, frenzy: true, topsyturvy: true, boostDrain: true, shellArmor: true, boostType: ["Grass", "Fire"] } } var npc2 = { party: [9, 154, 157, 160], name: "er", bias: ["reflect", "paralyze"] } var battle = new Battle2(src, npc, { cantWatch: true }, null, npc2, npc.select); currentBattles.push(battle); return; } if (command === "testbattle") { var npc = { party: [3, 6, 9, 154], name: "Alex", bias: ["burnout", "aggressive", "stats"], select: { criticalDouble: true, hail: true, simple: true, boostType: ["Dark", "Normal"] } } var battle = new Battle2(src, npc, { cantWatch: true }, null, null, npc.select); currentBattles.push(battle); return; } if (command == "enablebuylucky") { buyLuckyPossible = (buyLuckyPossible ? false : true); return true; } if (command == "npcbetcap") { npcBetCap = parseInt(commandData, 10); return true; } if (command == "addcelebritytrainer") { npcMatchAlive.push(commandData.toLowerCase()); return true; } if (command == "killcelebritytrainer") { npcMatchAlive.splice(npcMatchAlive.indexOf(commandData.toLowerCase()), 1); return true; } if (command == "celebritybetsranked") { var out = {}, out2; for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = getAvatarOff(e); if (!player) { continue; } if (!player.celebBetEarned) { continue; } if (player.celebBetEarned > 0) { out[player.id] = player.celebBetEarned; } } } out2 = Object.keys(out); out2.sort(function(a, b) { return out[a] - out[b]; }) if (Object.keys(out).length > 0) { safaribot.sendMessage(src, "No results yet!", safchan); } for (var a in out) { safaribot.sendMessage(src, a + ": " + out[a], safchan); } return true; } if (command == "payoutcelebritytrainer") { var trainer = commandData.toLowerCase(); if (!(npcMatchAlive.contains(trainer))) { safaribot.sendMessage(src, "That trainer isn't in this round!", safchan); } for (var e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { player = getAvatarOff(e); if (!player) { continue; } if (!player.npcBets) { continue; } if (!player.npcBets[trainer]) { continue; } player.balls.lucky += (Math.round(player.npcBets[trainer]) * 0.4); player.celebBetEarned += (Math.round(player.npcBets[trainer]) * 0.4); this.saveGame(player); safari.inboxMessage(player, "You earned " + (Math.round(player.npcBets[trainer]) * 0.4) + " Lucky Coins from your bet on Trainer " + trainer.toUpperCase() + "!", false); } } return true; } if ((command === "celebritydeathmatch") || (command == "cdm") || (command == "cdms")) { var info = commandData.split(":"); if (info.length < 2) { safaribot.sendMessage(src, "You must supply two trainers for a celebrity match", safchan); return true; } safaribot.sendMessage(src, "Trainers: " + info[0] + " and " + info[1] + ".", safchan); var reg, p1 = {"party": [], "name": ""}, p2 = {"party": [], "name": ""}, s1 = null, s2 = null, b1, b2, data; data = JSON.parse(JSON.stringify((safari.celebrityData))); for (var r in data) { reg = data[r]; for (var t = 0; t < reg.length; t++) { if (reg[t].name.toLowerCase() == info[0].toLowerCase()) { b1 = reg[t]; p1.party = Object.keys(b1.party).shuffle().slice(0, 6); p1.name = b1.name; s1 = b1.npcDuelEffects; p1.bias = b1.bias; for (var a in b1.chanceBias) { if (chance(0.5)) { p1.bias = p1.bias.concat(b1.chanceBias[a]); } } } if (reg[t].name.toLowerCase() == info[1].toLowerCase()) { b2 = reg[t]; p2.party = Object.keys(b2.party).shuffle().slice(0, 6); p2.name = b2.name; s2 = b2.npcDuelEffects; p2.bias = b2.bias; for (var a in b2.chanceBias) { if (chance(0.5)) { p2.bias = p2.bias.concat(b2.chanceBias[a]); } } } } } if (p1.name == "") { safaribot.sendMessage(src, "No trainer found " + info[0] + ".", safchan); return true; } if (p2.name == "") { safaribot.sendMessage(src, "No trainer found " + info[1] + ".", safchan); return true; } p1.select = {}; p2.select = {}; for (var a in s1) { p1.select[s1[a]] = true; } for (var a in s2) { p2.select[s2[a]] = true; } p1.select.boostType = []; p2.select.boostType = []; var watchable = true; if (command == "cdms") { watchable = false; } var battle = new Battle2(p1, p2, { cantWatch: watchable }, null, null, p1.select, null, null, p2.select); currentBattles.push(battle); return true; } if (command === "choosecelebrities") { safari.celebrityRegion = commandData.toLowerCase(); safaribot.sendMessage(src, "Changed Celebrity Region to " + safari.celebrityRegion + "!", safchan); return true; } if (command === "loadcelebrities") { var cThemes = {}; var url = commandData === "*" ? (permObj.get("celebrityurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading celebrities from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { celebrityData = JSON.parse(resp); permObj.add("celebrityurl", url); safari.celebrityData = celebrityData; safaribot.sendMessage(src, "Celebrities successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { celebrityData = cThemes; safaribot.sendMessage(src, "Couldn't load Celebrities from " + url + "! Error: " + error, safchan); } }); } catch (err) { celebrityData = cThemes; safaribot.sendMessage(src, "Couldn't load Celebrities from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadmarket") { var cThemes = {}; var url = commandData === "*" ? (permObj.get("marketurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading market from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { marketData = JSON.parse(resp); permObj.add("marketurl", url); safaribot.sendMessage(src, "Market successfully loaded!", safchan); safari.sanitizeAll(); permObj.add("marketData", JSON.stringify(marketData)); permObj.save(); } catch (error) { marketData = cThemes; safaribot.sendMessage(src, "Couldn't load Market from " + url + "! Error: " + error, safchan); } }); } catch (err) { marketData = cThemes; safaribot.sendMessage(src, "Couldn't load Market from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadbonuslogin" || command === "loadbonuslogins") { var cThemes = {}; var url = commandData === "*" ? (permObj.get("bonusloginurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading bonus logins from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { bonusLoginData = JSON.parse(resp); permObj.add("bonusloginurl", url); if (!safari.hasOwnProperty("events")) { safari.events = {bonusLoginEnabled: false}; } safari.events.bonusLoginName = bonusLoginData.name; safari.events.bonusLoginRewards = bonusLoginData.rewards; safari.events.bonusLoginColor = bonusLoginData.color; safaribot.sendMessage(src, "Bonus Login event " + safari.events.bonusLoginName + " successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { bonusLoginData = cThemes; safaribot.sendMessage(src, "Couldn't load Bonus Logins from " + url + "! Error: " + error, safchan); } }); } catch (err) { bonusLoginData = cThemes; safaribot.sendMessage(src, "Couldn't load Bonus Logins from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadtrials" || command === "loadtrial") { var cThemes = trialsData ? trialsData : {}; var url = commandData === "*" ? (permObj.get("trialsurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading trials from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { trialsData = JSON.parse(resp); permObj.add("trialsurl", url); if (!safari.hasOwnProperty("events")) { safari.events = {trialsEnabled: false}; } safari.events.trialsData = trialsData; safaribot.sendMessage(src, "Trials successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { trialsData = cThemes; safaribot.sendMessage(src, "Couldn't load Trials from " + url + "! Error: " + error, safchan); } }); } catch (err) { trialsData = cThemes; safaribot.sendMessage(src, "Couldn't load Trials from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadwilditems" || command === "loadwilditem") { var data = globalWildItems ? globalWildItems : {}; var url = commandData === "*" ? (permObj.get("wilditemsurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading Wild Items from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { data = JSON.parse(resp); permObj.add("wilditemsurl", url); globalWildItems = data; permObj.add("globalWildItems", JSON.stringify(globalWildItems)); safaribot.sendMessage(src, "Wild Items successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { safaribot.sendMessage(src, "Couldn't load Wild Items from " + url + "! Error: " + error, safchan); } }); } catch (err) { trialsData = cThemes; safaribot.sendMessage(src, "Couldn't load Trials from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadpokeskills" || command === "loadpokeskill") { var data = skillData ? skillData : {}; var url = commandData === "*" ? (permObj.get("pokeskillsurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading Pokémon Skills from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { data = JSON.parse(resp); permObj.add("pokeskillsurl", url); skillData = data; permObj.add("skillData", JSON.stringify(skillData)); safaribot.sendMessage(src, "Pokéskills successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { safaribot.sendMessage(src, "Couldn't load Pokéskills from " + url + "! Error: " + error, safchan); } }); } catch (err) { skillData = data; safaribot.sendMessage(src, "Couldn't load Pokéskills from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadspiritduels" || command === "loadspiritduel") { var cThemes = {}; var url = commandData === "*" ? (permObj.get("duelsurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading Spirit Duels from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { data = JSON.parse(resp); permObj.add("duelsurl", url); if (!safari.hasOwnProperty("events")) { safari.events = {spiritDuelsEnabled: false}; } safari.loadDuels(src, data); safaribot.sendMessage(src, "Duels successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { safaribot.sendMessage(src, "Couldn't load Duels from " + url + "! Error: " + error, safchan); } }); } catch (err) { trialsData = cThemes; safaribot.sendMessage(src, "Couldn't load Trials from " + url + "! Error: " + err, safchan); } return true; } if (command === "loaddetectiveupdate" || command === "loaddetectiveupdates") { var cThemes = {}; var url = commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading Detective Update from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { data = JSON.parse(resp); bonusCluesInteract = data.interact; bonusClues = data.normal; safaribot.sendMessage(src, "Detective Updates successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { safaribot.sendMessage(src, "Couldn't load Detective Updates from " + url + "! Error: " + error, safchan); } }); } catch (err) { safaribot.sendMessage(src, "Couldn't load Detective Updates from " + url + "! Error: " + err, safchan); } return true; } if (command === "loadfortune" || command === "loadfortunes") { var cThemes = fortuneData; var url = commandData === "*" ? (permObj.get("fortunesurl") || commandData) : commandData; if (url === "*") { safaribot.sendMessage(src, "Please type a valid URL!", safchan); return true; } safaribot.sendMessage(src, "Loading fortunes from " + url + "!", safchan); try { sys.webCall(url, function (resp) { try { fortuneData = JSON.parse(resp); sys.write(fortunesFile, resp); permObj.add("fortunesurl", url); safaribot.sendMessage(src, "Fortune Data successfully loaded!", safchan); safari.sanitizeAll(); } catch (error) { fortuneData = cThemes; safaribot.sendMessage(src, "Couldn't load Fortunes from " + url + "! Error: " + error, safchan); } }); } catch (err) { fortuneData = cThemes; safaribot.sendMessage(src, "Couldn't load Fortunes from " + url + "! Error: " + err, safchan); } return true; } if (command === "reloadids") { var e, data, obj = {}; for (e in rawPlayers.hash) { if (rawPlayers.hash.hasOwnProperty(e)) { data = JSON.parse(rawPlayers.hash[e]); if (data.idnum === undefined) { safari.sanitize(data); } obj[data.idnum] = data.id; } } idnumList.clear(); for (e in obj) { if (obj.hasOwnProperty(e)) { idnumList.add(e, obj[e]); } } safaribot.sendMessage(src, "ID Numbers successfully reloaded!", safchan); return true; } if (command === "setccatch" || command === "setccatch2") { if (commandData === "*") { safaribot.sendMessage(src, "Current ccatch are '" + ccatch + "' and '" + ccatch2 + "'!", safchan); return true; } if (command === "setccatch") { ccatch = commandData.toLowerCase(); permObj.add("ccatch", ccatch); safaribot.sendMessage(src, "Changed ccatch to " + ccatch + "!", safchan); } else { ccatch2 = commandData.toLowerCase(); permObj.add("ccatch2", ccatch2); safaribot.sendMessage(src, "Changed ccatch2 to " + ccatch2 + "!", safchan); } return true; } if (command === "editplayer") { var info = commandData.split(":"); var player = getAvatarOff(info[0]); if (!player) { safaribot.sendMessage(src, "This person doesn't have a Safari save!", safchan); return true; } if (info.length < 3) { safaribot.sendMessage(src, "Invalid format! Use /editplayer Name:Property.child1.child2:NewValue to edit someone's data!", safchan); return true; } try { var prop = info[1].split("."); var val = JSON.parse(info.slice(2).join(":")), oldVal; var attr = player; var propName = []; var success = false; if (prop.length == 1 && prop[0] === "") { safaribot.sendMessage(src, "Please specify a valid property to edit!", safchan); return true; } else { for (var e = 0; e < prop.length; e++) { propName.push(prop[e]); if (prop[e] in attr) { if (propName.join(".") === prop.join(".")) { oldVal = JSON.stringify(attr[prop[e]]); attr[prop[e]] = val; success = true; break; } else { attr = attr[prop[e]]; } } else { safaribot.sendMessage(src, "This player does not have a '" + propName.join(".") + "' property!", safchan); return true; } } } if (success) { safaribot.sendMessage(src, "Successfully changed {0}.{1} from '{2}' to '{3}'!".format(info[0].toCorrectCase(), propName.join("."), oldVal, JSON.stringify(val)), safchan); safari.saveGame(player); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + info[0].toCorrectCase() + "|||editplayer|||had their " + propName.join(".") + " property changed from|||" + oldVal + " to " + JSON.stringify(val) + "\n"); } else { safaribot.sendMessage(src, "Couldn't edit {0}.{1} from '{2}' to '{3}'!".format(info[0].toCorrectCase(), propName.join("."), oldVal, JSON.stringify(val)), safchan); } } catch (err) { safaribot.sendMessage(src, "Error when trying to edit data: " + err + (err.lineNumber ? " on line " + err.lineNumber : ""), safchan); } return true; } if (command === "pyrbonusmons") { safari.pyrBonusMons(); return true; } if (command === "choosepyrbonusmons") { safari.pyrBonusMonsManual(commandData); return true; } if (command === "resetcostumes") { safari.resetCostumes(); return true; } if (command === "resetduels") { safari.resetDuelsTeams(); return true; } if (command === "clearduels") { safari.clearSpiritDuels(); return true; } if (command === "monthlymedals" || command === "weeklymedals") { safari.awardMonthlyMedals(monthlyLeaderboards); return true; } if (command === "enabletrials") { safari.safariEvents( src,"trials",true ); return true; } if (command === "disabletrials") { safari.safariEvents( src,"trials",false ); return true; } if (command === "enablebonuslogins") { safari.safariEvents( src,"login",true ); return true; } if (command === "disablebonuslogins") { safari.safariEvents( src,"login",false ); return true; } if (command === "enableduels") { safari.safariEvents( src,"duels",true ); return true; } if (command === "disableduels") { safari.safariEvents( src,"duels",false ); return true; } if (command === "enablehq") { safari.safariEvents( src,"hiddenquiz",true ); safari.quizEventInitialize(); return true; } if (command === "disablehq") { safari.safariEvents( src,"hiddenquiz",false ); return true; } if (command === "enablett") { safari.safariEvents( src,"towertrouble",true ); safari.quizEventInitialize(); return true; } if (command === "disablett") { safari.safariEvents( src,"towertrouble",false ); return true; } if (command === "releasetrial") { var info = commandData.indexOf("::") > -1 ? commandData.split("::") : commandData.split(":"); safari.releaseTrial( src,getAvatarOff(info[0]),parseInt(info[1]) ); return true; } if (command === "trialspoints") { var info = commandData.indexOf("::") > -1 ? commandData.split("::") : commandData.split(":"); safari.setTrialsPoints( src,getAvatarOff(info[0]),parseInt(info[1]) ); return true; } if (command === "trialsbonus") { safari.grantTrialsBonusPoints( src,getAvatarOff(commandData) ); return true; } if (command === "trialslb") { safari.viewTrialsLb( src,commandData ); return true; } if (command === "finishtrials") { safari.endTrials(); return true; } if (command === "excludeeconomy") { safari.excludeFromEconomy(src, commandData); return true; } if (command === "startduels") { safari.assignDuelsTeams(); return true; } if (command === "settowertrouble" || command === "towertrouble" ) { safari.setTowerTroubleRequirements(src, commandData); return true; } if (command === "nexttowertrouble" || command === "progresstowertrouble" ) { safari.towerTroubleProgress(src); return true; } if (command === "printtowertrouble" || command === "printtt" ) { safari.printTowerTroubleLB(src); return true; } if (command === "nextduels" || command === "nextduel") { safari.progressDuels(); return true; } if (command === "pushduelteam") { var info = commandData.indexOf("::") > -1 ? commandData.split("::") : commandData.split(":"); safari.pushDuelTeam(src, getAvatarOff(info[0]), info[1]); return true; } if (command === "shoveduelteam") { var info = commandData.indexOf("::") > -1 ? commandData.split("::") : commandData.split(":"); safari.shoveDuelTeam(src, info[0], info[1]); return true; } if (command === "nextspawn") { if (commandData === "*") { safaribot.sendMessage(src, "Syntax: /nextspawn [player]:[pokemon]:[amount]:[disguise].", safchan); return true; } var data = commandData.split(":"); var player = getAvatarOff(data[0]); if (!player) { safaribot.sendMessage(src, "This person doesn't have a Safari save!", safchan); return true; } if (data.length < 2) { safaribot.sendMessage(src, "Invalid format! Use /nextspawn [player]:[pokemon]:[amount]:[disguise]!", safchan); return true; } var info = getInputPokemon(data[1]); if (!info.num) { safaribot.sendMessage(src, "Invalid Actual Pokémon!", safchan); return true; } player.nextSpawn.pokemon = info; var amount = parseInt(data[2], 10) || 1; player.nextSpawn.amt = amount; var appearAs = info; if (data[3]) { appearAs = getInputPokemon(data[3]); if (!appearAs.num) { safaribot.sendMessage(src, "Invalid Disguise Pokémon!", safchan); return true; } } player.nextSpawn.disguise = appearAs; safari.saveGame(player); var translated = plural(amount, info.name) + (appearAs.name !== info.name ? " disguised as " + appearAs.name : "") + "."; safaribot.sendMessage(src, "The next spawn by " + data[0].toCorrectCase() + " will be " + translated, safchan); sys.appendToFile(giftLog, now() + "|||" + sys.name(src) + "|||" + data[0].toCorrectCase() + "|||nextspawn|||had their next spawn changed to " + translated + "|||\n"); return true; } if (command === "editdata" || command === "rename") { if (commandData === "*") { safaribot.sendMessage(src, "Syntax: /editdata [costume/item]:[Name]:[Property]:[New Value].", safchan); safaribot.sendMessage(src, "When editing a string, remember to include the quotation marks.", safchan); return true; } var info = commandData.indexOf("::") > -1 ? commandData.split("::") : commandData.split(":"); if (command === "rename") { var r = ""; if (info.length > 1 && info[1]) { r = info[1].replace(/\"/g, ""); if (r !== "clear") { r = "\"" + r + "\""; } } info = ["item", info[0], "fullName", r]; } var data, e, type, list, defaultData, customValues; switch (info[0]) { case "costume": case "costumes": type = "costume"; data = costumeData; defaultData = defaultCostumeData; list = editableCostumeProps; break; default: type = "item"; data = itemData; defaultData = defaultItemData; list = editableItemProps; } var objName = "custom" + cap(type) + "Data"; try { customValues = JSON.parse(permObj.get(objName)); } catch (err) { customValues = {}; } var showEditable = function(obj, list, id) { var out = [], i; for (i in obj) { if (i in list) { if (customValues.hasOwnProperty(id) && customValues[id].hasOwnProperty(i) && customValues[id][i] !== defaultData[id][i]) { out.push("" + i + ": " + obj[i] + ""); } else { out.push(i + ": " + obj[i]); } } } return out.join(", "); }; if (info.length < 2 || info[1] === "") { sys.sendMessage(src, "", safchan); for (e in data) { safaribot.sendHtmlMessage(src, e + ": " + showEditable(data[e], list, e), safchan); } sys.sendMessage(src, "", safchan); return true; } var item = info[1]; if (!data.hasOwnProperty(item)) { safaribot.sendMessage(src, "Invalid " + cap(type) + "!", safchan); return true; } if (info.length < 3) { sys.sendMessage(src, "", safchan); safaribot.sendHtmlMessage(src, item + ": " + showEditable(data[item], list, item), safchan); sys.sendMessage(src, "", safchan); return true; } var prop = info[2]; if (!(prop in list)) { safaribot.sendMessage(src, prop + " is not a valid editable property!", safchan); return true; } var propTypes = Array.isArray(list[prop]) ? list[prop] : [list[prop]]; var currentVal = data[item][prop]; if (info.length < 4 || info[3] === "") { safaribot.sendMessage(src, "The property " + prop + " can be edited into " + an(readable(propTypes, "or")) + "! " + (prop in data[item] ? "Current value: " + currentVal : ""), safchan); return true; } var newVal; if (info[3].toLowerCase() === "clear") { if (!customValues.hasOwnProperty(item)) { safaribot.sendMessage(src, "The " + type + " " + item + " has no customized property to clear!", safchan); return true; } if (!customValues[item].hasOwnProperty(prop)) { safaribot.sendMessage(src, "The property " + prop + " for " + type + " " + item + " is not customized!", safchan); return true; } delete customValues[item][prop]; data[item][prop] = newVal = defaultData[item][prop]; if (newVal === undefined) { delete data[item][prop]; } safaribot.sendMessage(src, "Reset custom property " + prop + " for " + type + "Data." + item + "." + prop + " to its default value '" + newVal + "'!", safchan); } else { try { newVal = JSON.parse(info[3]); } catch (err) { safaribot.sendMessage(src, info[3] + " is not a valid value!", safchan); return true; } var newType = Array.isArray(newVal) ? "array" : typeof newVal; var currentType = Array.isArray(currentVal) ? "array" : typeof currentVal; if (newType !== currentType && !propTypes.contains(newType)) { safaribot.sendMessage(src, prop + " cannot be " + an(newType) + "!", safchan); return true; } if (!customValues.hasOwnProperty(item)) { customValues[item] = {}; } data[item][prop] = newVal; customValues[item][prop] = newVal; safaribot.sendMessage(src, "Changed " + type + "Data." + item + "." + prop + " from '" + currentVal + "' to '" + newVal + "'!", safchan); } permObj.add(objName, JSON.stringify(customValues)); updateItemHelp(); return true; } if (command === "refundlogin") { safari.refundLogin(src, commandData); return true; } if (command === "loginmercy") { var info = parseInt(commandData, 10); if (isNaN(info) || info < 0) { safaribot.sendMessage(src, "Please choose a valid number of days!", safchan); return true; } permObj.add("loginDaysDown", info); if (info === 0) { safaribot.sendMessage(src, "Resetting login mercy value!", safchan); } else { safaribot.sendMessage(src, "Players who didn't log in in the last " + plural(info, "day") + " won't lose their consecutive login streak!", safchan); } return true; } if (command === "config") { var info = commandData.split(":"), editable = ["arena_pink", "arena_teal", "arena_mustard", "arena_cyan", "arena_crimson", "arena_rainbow", "tier_chances", "wildmsg"], prop, val; if (commandData.toLowerCase() === "current") { safaribot.sendMessage(src, "Current configurable values:", safchan); for (var e = 0; e < editable.length; e++) { prop = editable[e]; val = configObj.get(prop); if (val) { safaribot.sendMessage(src, prop + ": " + val, safchan); } } return true; } if (commandData === "*" || info.length < 2) { safaribot.sendMessage(src, "Syntax: /config [Property]:[New Value].", safchan); safaribot.sendMessage(src, "When editing a string, remember to include the quotation marks.", safchan); safaribot.sendMessage(src, "Valid Properties: " + editable.join(", ") + ". Type '/config current' to see the current values.", safchan); return true; } prop = info[0].toLowerCase(); val = info.slice(1).join(":"); if (editable.contains(prop) === false) { safaribot.sendMessage(src, "Invalid property! Type /config for more details!", safchan); return true; } var old = configObj.get(prop); configObj.add(prop, val); safari.loadConfigurables(); safaribot.sendMessage(src, "Edited property '{0}' from '{1}' to '{2}'!".format(prop, old, val), safchan); return true; } if (command === "fakespawn" || command === "fakespawn2") { var data = toCommandData(commandData, ["name", "img", "shiny", "isEvent"]); if (commandData == "*" || !data.name || data.name == "*" || !data.img) { var msg = wildPokemonMessage.replace(//g, "]").replace(/:/g, "։").format("Name", "300", ""); safaribot.sendHtmlMessage(src, "Syntax is " + link("/" + command + " " + msg + ":::ImageSource", null, true), safchan); return true; } var appmsg = data.name.replace(/\[/g, "<").replace(/\]/g, ">").replace(/։/g, ":"); var img = ""; sendAll("
" + (data.shiny ? toColor(appmsg, "DarkOrchid") : appmsg) + "
" + (data.isEvent ? "This is an Event Pokémon! No " + es(finishName("master")) + " allowed!
" : "") + img + "

", true, true); if (command !== "fakespawn2") { var onChannel = sys.playersOfChannel(safchan); for (var e in onChannel) { ballMacro(onChannel[e]); } } return true; } if (command === "rephoto") { photographQuest = {}; safari.updatePhotographQuest(); safaribot.sendMessage(src, "Renewed photo requests for Journal quest!", safchan); return true; } if (sys.auth(src) >= 3) { if (command === "safarieval") { if (commandData === undefined) { safaribot.sendMessage(src, "Define code to execute. Proceed with caution as you can break stuff.", safchan); return true; } try { eval(commandData); } catch (error) { safaribot.sendMessage(src, error, safchan); } return true; } if (command === "safarievalp") { if (commandData === undefined) { safaribot.sendMessage(src, "Define code to execute. Proceed with caution as you can break stuff.", safchan); return true; } try { var result = eval(commandData); safaribot.sendMessage(src, "Type: '" + (typeof result) + "'", safchan); safaribot.sendMessage(src, "Got from eval: '" + result + "'", safchan); } catch (error) { safaribot.sendMessage(src, "Error in eval: " + error, safchan); } return true; } } } /*if (sys.auth(src) > 2) { if (command === "updateplugin" && commandData === "safari.js") { if (!currentPokemon && contestCooldown > 180 && contestCount <= 0 && currentBattles.length === 0 && currentPyramids.length === 0 && currentAuctions.length === 0 && !currentEvent && !safariUpdating) { safariUpdating = true; sys.sendHtmlAll(closedMessage, safchan); } if (!safariUpdating) { safaribot.sendMessage(src, "You shouldn't update without putting Safari into an update ready state with /update.", safchan); return true; } safariUpdating = true; //Then fall through to the actual command to update plugin } }*/ return false; }; /* Events */ this.init = function () { var name = defaultChannel; if (sys.existChannel(name)) { safchan = sys.channelId(name); } else { safchan = sys.createChannel(name); } SESSION.global().channelManager.restoreSettings(safchan); SESSION.channels(safchan).perm = true; rawPlayers = new MemoryHash(saveFiles); cookedPlayers = new MemoryHash(deletedSaveFiles); backupPlayers1 = new MemoryHash(saveBackupFile1); backupPlayers2 = new MemoryHash(saveBackupFile2); backupPlayers3 = new MemoryHash(saveBackupFile3); backupPlayers4 = new MemoryHash(saveBackupFile4); backupPlayers5 = new MemoryHash(saveBackupFile5); rafflePlayers = new MemoryHash(rafflePlayersFile); tradeBans = new MemoryHash(tradebansFile); saltBans = new MemoryHash(saltbansFile); idnumList = new MemoryHash(idnumFile); permObj = new MemoryHash(permFile); configObj = new MemoryHash(configFile); safari.loadConfigurables(); var parseFromPerm = function(name, defaultVal) { var out; try { out = JSON.parse(permObj.get(name)); } catch (err) { out = defaultVal; } return out; }; npcShop = parseFromPerm("npcShop", {}); safari.daycareRegions = parseFromPerm("daycareRegions", {}); safari.daycarePokemon = parseFromPerm("daycarePokemon", []); recipeData = parseFromPerm("alchemistRecipes", {}); gymData = parseFromPerm("gyms", {}); eliteData = parseFromPerm("elite", []); eliteHall = parseFromPerm("hall", []); challengeRequests2 = []; pyrBonusMons = parseFromPerm("pyrBonusMons", []); gachaItems = parseFromPerm("gachaRates", gachaItems); finderItems = parseFromPerm("finderRates", finderItems); packItems = parseFromPerm("packRates", packItems); apricornToBallData = loadApricornRecipes(); economyData = parseFromPerm("economicSummary", economyData); safari.riceMode = false; safari.detectiveUpdates = false; dailyBoost = parseFromPerm("dailyBoost", null); if (dailyBoost === null) { this.changeDailyBoost(); } scientistQuest = parseFromPerm("scientistQuest", null); if (scientistQuest === null) { this.changeScientistQuest(); } try { var data = JSON.parse(sys.getFileContent(themesFile)); contestThemes = data || contestThemes; } catch (err) { } photographQuest = parseFromPerm("photographQuest", {}); try { safari.updatePhotographQuest(); }catch (err) { } lastLeaderboards = parseFromPerm("lastLeaderboards", null); lastEscapedMons = parseFromPerm("lastEscapedMons", []); celebrityPKs = parseFromPerm("celebrityPKs", {}); rafflePrizeObj = parseFromPerm("rafflePrize", null); mAuctionsData = parseFromPerm("mAuctions", []); mAuctionsMarket = parseFromPerm("mAuctionsMarket", []); lastContests = parseFromPerm("lastContests", []); allowedSharedIPNames = parseFromPerm("allowedSharedIPs", []); marketData = parseFromPerm("marketData", {}); triviaData = parseFromPerm("triviaData", {}); safari.events = parseFromPerm("events", {}); safari.celebrityData = parseFromPerm("celebrityData", {}); safari.celebrityRegion = parseFromPerm("celebrityRegion", ""); safari.dataDumps = parseFromPerm("dumps", {}); safari.dataDumps2 = parseFromPerm("dumps2", {}); recentPlayers = parseFromPerm("recentPlayers", {}); globalWildItems = parseFromPerm("globalWildItems", {}); skillData = parseFromPerm("skillData", {}); skillUnlocks = parseFromPerm("skillUnlocks", {}); safari.detectiveData = parseFromPerm("detectiveData", {}); safari.moveLearners = parseFromPerm("moveLearners", {}); // Not using parseFromPerm here because those are not stored as a JSON string if (permObj.hash.hasOwnProperty("ccatch")) { ccatch = permObj.get("ccatch"); } if (permObj.hash.hasOwnProperty("ccatch2")) { ccatch2 = permObj.get("ccatch2"); } try { if (!defaultItemData) { defaultItemData = JSON.parse(JSON.stringify(itemData)); } var customValues = JSON.parse(permObj.get("customItemData")), e, i, obj; //safaribot.sendMessage(sys.id("Ripper Roo"), JSON.stringify(customValues), safchan); for (e in customValues) { if (e in itemData) { obj = customValues[e]; //safaribot.sendMessage(sys.id("Ripper Roo"), JSON.stringify(obj), safchan); for (i in obj) { itemData[e][i] = obj[i]; } } } } catch (err) { safaribot.sendAll("Error loading defaultItemData/customItemData: " + err, staffchannel); } try { if (!defaultCostumeData) { defaultCostumeData = JSON.parse(JSON.stringify(costumeData)); } var customValues = JSON.parse(permObj.get("customCostumeData")), e, i, obj; for (e in customValues) { if (e in costumeData) { obj = customValues[e]; for (i in obj) { costumeData[e][i] = obj[i]; } } } } catch (err) { safaribot.sendAll("Error loading defaultCostumeData/customCostumeData: " + err, staffchannel); } updateItemHelp(); spiritSpawn = false; wildSpirit = false; canSpawnTera = false; wildTera = false; var currentGame = null; currentDay = ((getDay(now()) - 4) % 7) + 1; if (!safari.hasOwnProperty("events")) { safari.events = { spiritDuelsEnabled: false, trialsEnabled: false }; } for (var r in resources.$) { var resource = resources.$[r]; var fname = resource.file.split("/").pop(); if (!sys.fileExists(resource.file)) { safaribot.sendAll("Couldn't find Safari resource file '" + fname + "'. Downloading from web repository...", staffchannel); var result = downloadResource(r); if (result !== null) { safaribot.sendAll(result, staffchannel); } else { safaribot.sendAll("Successfully downloaded '" + fname + "'!", staffchannel); } } else { var res = loadResource(r); if (res !== null) { safaribot.sendAll(res, staffchannel); } } } try { var data = JSON.parse(sys.getFileContent(decorationsFile)); decorations = data || {}; } catch (err) { } try { var data = JSON.parse(sys.getFileContent(missionsFile)); missionsData = data || {}; } catch (err) { missionsData = {}; } var oldData = fortuneData; try { var data = JSON.parse(sys.getFileContent(fortunesFile)); fortuneData = data || oldData; } catch (err) { fortuneData = oldData; } monthlyLeaderboards = {}; for (var e in monthlyLeaderboardTypes) { if (monthlyLeaderboardTypes[e].hasOwnProperty("file")) { var file = monthlyLeaderboardTypes[e].file; if (typeof cleanFile === "function") { cleanFile(file); } monthlyLeaderboards[e] = new MemoryHash(file); } } lastIdAssigned = loadLastId(); var template = permObj.get("playerTemplate"); if (template) { template = JSON.parse(template); if (!objectEquals(playerTemplate, template)) { safaribot.sendAll("Updating save files...", safchan); safari.sanitizeAll(); } } permObj.add("playerTemplate", JSON.stringify(playerTemplate)); permObj.save(); this.updateLeaderboards(); sys.sendHtmlAll(openedMessage, safchan); if (baitCooldown < 7) { baitCooldown = sys.rand(5,7); } if (goldenBaitCooldown < 10) { goldenBaitCooldown = sys.rand(10, 13); } this.updateMAuctions(); }; this.afterChannelJoin = function (src, channel) { if (channel == safchan) { this.loadGame(src); sys.sendMessage(src, "*** ******************************************** ***", safchan); this.showNextContest(src); sys.sendMessage(src, "*** ******************************************** ***", safchan); if (currentPokemon && (!(currentTheme && contestThemes[currentTheme].disguises))) { safari.spawnDisplay(src, false, true, "There's a wild {2}{0}! (BST: {1})"); safari.afterSpawnDisplay(src, true); } else { sys.sendHtmlMessage(src, link("/dashboard", "«Dashboard»"), safchan); } for (var b in currentBattles) { var battle = currentBattles[b]; if (battle.name1 === sys.name(src) && battle.battle2 && battle.npcBattle && battle.paused) { battle.sendMessage(battle.name1, toColor("Your battle is currently paused! Type {0} to unpause it! (Battle can only remain paused for {1})".format(link("/bat pause"), timeString(battle.pauseLimit - battle.totalPauseTime)), "crimson")); } } } return false; }; this.beforeChannelLeave = function (src, channel) { if (channel == safchan && getAvatar(src)) { this.clearPlayer(src); var player = getAvatar(src); var id = sys.name(src).toLowerCase(); if (!sys.dbRegistered(id)) { player.locked = true; safaribot.sendAll(sys.name(src) + "'s Safari save was locked because they left the channel while unregistered!", staffchannel); sys.appendToFile(miscLog, now() + "|||" + sys.name(src) + "|||was locked for leaving channel while unregistered\n"); } this.saveGame(player); } return false; }; this.afterChangeTeam = function (src) { if (sys.isInChannel(src, safchan)) { var player = getAvatar(src); if (player) { if (sys.name(src).toLowerCase() !== player.id) { this.clearPlayer(src); if (!sys.dbRegistered(player.id)) { player.locked = true; safaribot.sendAll(player.id.toCorrectCase() + "'s Safari save was locked because they unregistered their alt and changed names!", staffchannel); sys.appendToFile(miscLog, now() + "|||" + player.id + "|||was locked for changing names while unregistered\n"); } this.saveGame(player); SESSION.users(src).safari = null; this.loadGame(src); } } else { this.loadGame(src); } } return false; }; this.afterChatMessage = function (src, message, channel) { if (channel == safchan) { var player = getAvatar(src); var msg = message.toLowerCase(); if (!player && msg.search(/how/gi) !== -1 && msg.search(/play|start|catch|join/gi) != -1) { var color = script.getColor(src); var c = "" + toColor(sys.name(src), color) + ""; safaribot.sendHtmlMessage(src, "Hello, " + c + "! To play Safari, please type " + link("/start") + " and follow the instructions.", safchan); } } return false; }; this.onBan = function (src, dest) { if (this.isInAuction(sys.name(src))) { for (var b in currentAuctions) { currentAuctions[b].removePlayer(sys.id(dest)); } } if (currentEvent) { currentEvent.shove(src, dest); } }; this.onSafban = function (src) { if (this.isInAuction(sys.name(src))) { for (var b in currentAuctions) { currentAuctions[b].removePlayer(src); } } if (currentEvent) { currentEvent.shove(null, sys.name(src)); } //TODO: Make them lose their battle too if (sys.isInChannel(src, safchan)) { sys.kick(src, safchan); } }; this.stepEvent = function () { contestCooldown--; releaseCooldown--; baitCooldown--; goldenBaitCooldown--; successfulBaitCount--; deluxeBaitCooldown--; safari.runPendingActive(); if (currentPokemon) { var keys = Object.keys(bufferThrows); for (var p = 0; p < keys.length; p++) { var pName = keys[p]; if (currentPokemon && bufferThrows[pName] && bufferThrows[pName].throwAt <= now()) { // check some of these values again since i suspect there might be a race condition here if (isPlaying(pName)) { var ball = bufferThrows[pName].ball; if (ball === "takephoto") { safari.takePhoto(sys.id(pName), "*", false, false, "photo", true); } else { safari.throwBall(sys.id(pName), ball, null, null, "catch", false, false, true); } } delete bufferThrows[pName]; p--; } } if (restrictedThrowers && restrictedThrowTime > 0) { restrictedThrowTime--; if (restrictedThrowTime <= 0) { restrictedThrowers = []; restrictedThrows = 0; restrictedThrowTime = 0; safaribot.sendHtmlAll("Anyone can throw Balls now!", safchan); } } } var onChannel = sys.playersOfChannel(safchan); for (var e in onChannel) { // potentially server-intensive, but should be okay. shift to after contest w/ mega reversion & lb update if not. var pid = onChannel[e] var p = getAvatar(pid); if (!p) { continue; } var needsUpdate = false; if (p.fortune) { if (p.fortune.deadline < now() && p.fortune.deadline > 0) { safaribot.sendHtmlMessage(pid, "Your {0} effect expired!".format(finishName("cookie")), safchan); p.fortune.deadline = 0; needsUpdate = true; } } if (p.zcrystalDeadline) { if (p.zcrystalDeadline < now() && p.zcrystalDeadline > 0) { safaribot.sendHtmlMessage(pid, "Your {0} effect expired!".format(finishName("crystal")), safchan); p.zcrystalDeadline = 0; needsUpdate = true; } } if (p.options.androidTextFlow && sys.playersOfChannel(sys.channelId("Safari Android Spam")).contains(pid)) { var spamChan = sys.channelId("Safari Android Spam") || safchan; sys.sendMessage(pid, "", spamChan); } if (p.auraExpiry > 0 && p.auraExpiry <= now()) { if (p.burningAura) { safaribot.sendMessage(pid, "The power of your Burning Aura wore off...", safchan); p.burningAura = false; needsUpdate = true; } if (p.brilliantAura) { safaribot.sendMessage(pid, "The power of your Brilliant Aura wore off...", safchan); p.brilliantAura = false; needsUpdate = true; } } if (needsUpdate) { safari.saveGame(p); } } if (currentEvent && contestCooldown % currentEvent.turnLength === 0) { currentEvent.nextTurn(); if (currentEvent.finished) { currentEvent = null; checkUpdate(); //test } } if (currentGame && contestCooldown % currentGame.turnLength === 0) { currentGame.action(); if (currentGame.finished) { currentGame = null; //checkUpdate(); //test } } if (currentBattles.length > 0 && contestCooldown % 2 === 0) { for (var e = currentBattles.length - 1; e >= 0; e--) { var battle = currentBattles[e]; if (battle.battle2) { continue; } battle.nextTurn(); if (battle.finished) { currentBattles.splice(e, 1); checkUpdate(); } } } if (currentBattles.length > 0 && contestCooldown % 4 === 0) { // Battle2 for (var e = currentBattles.length - 1; e >= 0; e--) { var battle = currentBattles[e]; if (!battle.battle2) { continue; } if (!battle.paused) battle.nextTurn(); else battle.totalPauseTime += 4; if (battle.paused && battle.totalPauseTime > battle.pauseLimit) { battle.paused = false; battle.sendToViewers(toColor("The battle has been unpaused since the time limit is up!", "crimson")); } if (battle.finished) { currentBattles.splice(e, 1); checkUpdate(); } } } if (currentPyramids.length > 0) { for (var e = currentPyramids.length - 1; e >= 0; e--) { var pyr = currentPyramids[e]; pyr.nextTurn(); if (pyr.finished) { currentPyramids.splice(e, 1); checkUpdate(); } } } if (currentBakings.length > 0 && contestCooldown % 10 === 2) { for (var e = currentBakings.length - 1; e >= 0; e--) { var bak = currentBakings[e]; bak.nextTurn(); if (bak.finished) { currentBakings.splice(e, 1); checkUpdate(); } } } if (currentAuctions.length > 0 && contestCooldown % 8 === 0) { for (var e = currentAuctions.length - 1; e >= 0; e--) { var auction = currentAuctions[e]; auction.nextTurn(); if (auction.finished) { currentAuctions.splice(e, 1); checkUpdate(); } } } if (safari.events && contestCooldown % 7 === 0) { if (safari.events.spiritDuelsEnabled && safari.events.currentSpiritDuel) { var finished = safari.spiritDuelTurn(); if (finished) { safari.events.currentSpiritDuel = false; checkUpdate(); } } } if (successfulBaitCount <= 0) { lastBaitersDecay--; } if (preparationPhase > 0) { preparationPhase--; if (preparationPhase <= 0) { var size = Object.keys(preparationThrows).length; if (size) { resolvingThrows = true; var name, i, throwChances = {}, throwers = [], alreadyThrow = [], n = now(), p, aType, crystalEffect; for (i in preparationThrows) { p = getAvatarOff(i); if (p && p.truesalt >= n && chance(p.srate)) { throwChances[i] = 0.1; } else { var lead = safari.getEffectiveLead(p, true); aType = type1(lead); crystalEffect = !["master", "takephoto"].contains(preparationThrows[i]) && p.zcrystalDeadline >= now() && p.zcrystalUser === lead && chance(zCrystalData[aType].chance) ? zCrystalData[aType] : { effect: "none" }; throwChances[i] = size * (["quick", "lightning", "spy"].contains(preparationThrows[i]) ? itemData[preparationThrows[i]].bonusRate : 1) * (crystalEffect.effect === "priority" ? sys.rand(crystalEffect.rate[0], crystalEffect.rate[1]) : 1); if (preparationThrows[i] == "takephoto" && this.hasCostumeSkill(p, "fasterPhotos")) { throwChances[i] *= 3; } if (preparationFirst !== p.id) { if (preparationThrows[i] !== "takephoto") { if (canHaveAbility(lead, abilitynum("Speed Boost"))) { throwChances[i] += size; } if (canHaveAbility(lead, abilitynum("Gale Wings")) || canHaveAbility(lead, abilitynum("Quick Draw"))) { throwChances[i] += size * 3; } if (p.costume === "backpacker") { throwChances[i] *= 0.75; } if (contestCount > 0) { if (contestActivity.hasOwnProperty(p.id) && contestActivity[p.id].length >= 3 && isRare(currentDisplay)) { throwChances[i] += (throwChances[i] * 2); } /*if (contestComboPlayers.contains(p.idnum) && wildTera) { throwChances[i] += (throwChances[i] * 3); }*/ } } else { if (canHaveAbility(lead, abilitynum("Prankster"))) { throwChances[i] *= 3; } } } } } while (Object.keys(throwChances).length) { n = randomSample(throwChances); throwers.push(n); delete throwChances[n]; } if (preparationFirst) { if (throwers.indexOf(preparationFirst) !== -1) { throwers.splice(throwers.indexOf(preparationFirst), 1); throwers.splice(0, 0, preparationFirst); } } for (i = 0; i < throwers.length; i++) { if (i + 1 >= throwers.length) { resolvingThrows = false; } name = throwers[i]; if (sys.isInChannel(sys.id(name), safchan) && alreadyThrow.indexOf(name) === -1) { alreadyThrow.push(name); if (preparationThrows[name] === "takephoto") { safari.takePhoto(sys.id(name), preparationThrows[name], false, true); } else { safari.throwBall(sys.id(name), preparationThrows[name], false, true); } } } } preparationFirst = null; preparationThrows = {}; } } if (contestCooldown === 180) { var possibleThemes; if (chosenThemes) { possibleThemes = [].concat(chosenThemes); } else { possibleThemes = Object.keys(contestThemes).concat(); } var repetitionCooldown = chosenThemes ? 0 : 4; for (var e = lastContests.length - 1, i = 0; e >= 0 && i < repetitionCooldown; e--, i++) { if (possibleThemes.contains(lastContests[e].themeId) && (lastContests[e].themeId !== "seasonal" && lastContests[e].themeId !== "seasonal2")) { possibleThemes.splice(possibleThemes.indexOf(lastContests[e].themeId), 1); } } if (possibleThemes.contains("none")) { possibleThemes.splice(possibleThemes.indexOf("none"), 1); } if (possibleThemes.contains("seasonal") && (chance(0.45))) { nextTheme = ["seasonal"]; possibleThemes.splice(possibleThemes.indexOf("seasonal"), 1); nextTheme = nextTheme.concat(possibleThemes.shuffle().slice(0, 2)); } else if (possibleThemes.contains("seasonal2") && (chance(0.45))) { nextTheme = ["seasonal2"]; possibleThemes.splice(possibleThemes.indexOf("seasonal"), 1); nextTheme = nextTheme.concat(possibleThemes.shuffle().slice(0, 2)); } /*else if (sys.rand(0, 100) < 38) { nextTheme = ["none"]; nextTheme = nextTheme.concat(possibleThemes.shuffle().slice(0, 2)); }*/ else { nextTheme = []; nextTheme = possibleThemes.shuffle().slice(0, 3); } nextRules = {}; var rulesDesc = []; for (var n = 0; n < nextTheme.length; n++) { var t = nextTheme[n]; if (t == "none") { nextRules[t] = safari.pickRules(null); } else { nextRules[t] = safari.pickRules(t); } rulesDesc.push("Rules for " + (t === "none" ? themeName(t) : link("/themerares " + t, themeName(t))) + " --- " + safari.translateRules(nextRules[t], true)); } contestVotingCount--; contestVotes = null; if (chosenThemes) { contestVotingCount = 0; } if (contestVotingCount <= 0) { contestVotes = {}; contestVotingCount = contestVotingCooldown; } chosenThemes = null; sys.sendAll(separator, safchan); safaribot.sendAll("A new " + (nextTheme !== "none" ? themeName(nextTheme) + "-themed" : "") + " Safari Contest will start in 3 minutes! Prepare your active Pokémon and all the Poké Balls you need!", safchan); for (n = 0; n < rulesDesc.length; n++) { safaribot.sendHtmlAll(" --- " + rulesDesc[n], safchan); } if (contestVotes) { safaribot.sendHtmlAll("You can choose which theme will be started! Type " + readable(nextTheme.map(function(x){ return link("/vote " + themeName(x)); }), "or") + " to choose!", safchan); } sys.sendAll(separator, safchan); sys.sendAll("", 0); sys.sendAll(separator, 0); safaribot.sendAll("A new " + (nextTheme !== "none" ? themeName(nextTheme) + "-themed" : "") + " Safari Contest will start in 3 minutes at #" + defaultChannel + "! Prepare your active Pokémon and all the Poké Balls you need!", 0); sys.sendAll(separator, 0); sys.sendAll("", 0); safari.flashPlayers(); var players = sys.playersOfChannel(safchan); for (var pid in players) { var player = getAvatar(players[pid]); if (player) { safari.fullBoxWarning(players[pid]); } } } if (contestCooldown <= 0) { safari.startContest("*"); } if (lastBaitersDecay === 0) { lastBaiters.shift(); lastBaitersDecay = lastBaitersDecayTime; } SESSION.global().safariContestCooldown = contestCooldown; SESSION.global().safariBaitCooldown = baitCooldown; SESSION.global().safariReleaseCooldown = releaseCooldown; if (contestCooldown === 60 * 19) { if (dayCareEnabled) { safari.dayCareStep(0); } safari.pendingNotifications(); } if (contestCooldown === 60 * 13) { safari.updateMarket(); if (safari.events.hiddenQuizEnabled && safari.events.hiddenQuizData.nextQuiz < now()) { safari.startQuizEvent(); } safari.pendingNotifications(); } if (contestCooldown === 60 * 7) { if (dayCareEnabled) { safari.dayCareStep(0); } safari.pendingNotifications(); } if (contestCount > 0) { if (wildEvent && contestCount === 1 && contestExtension <= contestExtensionLimit) { // if 1 second left and event active, start counting contestExtension up until the extension limit contestExtension++; if (contestExtension === 1) { safaribot.sendHtmlAll("The Contest has been extended due to the event! (Max extension time: {0} minutes)".format(contestExtensionLimit / 60), safchan); } if (contestExtensionLimit - contestExtension === 30) { safaribot.sendHtmlAll("The Contest extension ends in 30 seconds!", safchan); } } else { contestCount--; contestExtension = 0; if (contestCount === 0 && preparationPhase > 0) { contestCount += 1; } else if (contestCount === 0) { contestMidPoint = false; var winners = [], pokeWinners = [], maxCaught = 0, maxBST = 0, player, contestInfo = { finished: now() }, fullWinners = [], contestSaved; for (var e in contestCatchers) { if (contestCatchers.hasOwnProperty(e)) { player = getAvatarOff(e); if (contestForfeited.contains(player.idnum)) { continue; } if (contestCatchers[e].length >= maxCaught) { if (player) { if (contestCatchers[e].length > maxCaught) { winners = []; pokeWinners = []; fullWinners = []; maxCaught = contestCatchers[e].length; } winners.push(e); pokeWinners.push(poke(safari.getEffectiveLead(player, true), true)); fullWinners.push(e.toCorrectCase() + " (using " + poke(safari.getEffectiveLead(player, true), true) + ")"); } } } } var tieBreaker = [], bst, name, top = winners.length, catchersBST = {}, allContestants = []; safari.compileThrowers(); var allContestants = Object.keys(contestCatchers), pokemonSpawned = 0; for (e in contestCatchers) { if (contestCatchers.hasOwnProperty(e)) { catchersBST[e] = add(contestCatchers[e].map(getBST)); pokemonSpawned += contestCatchers[e].length; } } if (top > 1) { maxBST = 0; pokeWinners = []; for (e in winners) { name = winners[e]; bst = catchersBST[name]; if (bst >= maxBST) { player = getAvatarOff(name); if (player) { if (bst > maxBST) { tieBreaker = []; pokeWinners = []; fullWinners = []; maxBST = bst; } tieBreaker.push(name); pokeWinners.push(poke(safari.getEffectiveLead(player, true), true)); fullWinners.push(name.toCorrectCase() + " (using " + poke(safari.getEffectiveLead(player, true), true) + ")"); } } } winners = tieBreaker; } allContestants.sort(function(a, b) { if (contestCatchers[a].length > contestCatchers[b].length) { return -1; } else if (contestCatchers[a].length < contestCatchers[b].length) { return 1; } else if (catchersBST[a] > catchersBST[b]) { return -1; } else if (catchersBST[a] < catchersBST[b]) { return 1; } return 0; }); var playerScore = function(name) { return "(Caught " + contestCatchers[name].length + ", BST " + catchersBST[name] + ")"; }; var reward = currentRules && currentRules.rewards ? currentRules.rewards : { gacha: 10 }; sys.sendAll(separator, safchan); safaribot.sendAll("The Safari Contest is now over! Please come back during the next Contest!", safchan); var saveContestVal = 8; //test this at 8 for now, if it needs to be higher or lower it can be adjusted in the future contestInfo.saved = contestSaved = true; if (Object.keys(contestCatchers).length === 1) { // if there's only 1 contestant player = getAvatarOff(Object.keys(contestCatchers)[0]); if (player && !contestForfeited.contains(player.idnum)) { // and that contestant didn't forfeit if (contestCatchers[winners[0]].length < saveContestVal) { safaribot.sendAll("No prizes have been given because there was only one contestant!", safchan); contestInfo.saved = contestSaved = false; } } } if (winners.length > 0) { var list = []; for (var e in reward) { if (e == "cashbonus") { list.push("$$$"); } else { list.push(reward[e] + " " + itemAlias(e, false, true) + (reward[e] === 1 ? "" : "s")); } } if (list.length < 1) { list.push("a prize"); } safaribot.sendAll(readable(winners.map(function (x) { return x.toCorrectCase(); }), "and") + ", with the help of their " + readable(pokeWinners, "and") + ", caught the most Pokémon (" + maxCaught + (top > 1 ? ", total BST: " + maxBST : "") + ") during the Contest" + (!contestSaved ? "!" : " and " + (winners.length > 1 ? "have" : "has") + " won " + readable(list, "and") + "!"), safchan); contestInfo.winners = readable(fullWinners, "and"); contestInfo.caught = maxCaught; contestInfo.bst = maxBST === 0 ? catchersBST[winners[0]] : maxBST; } if (allContestants.length > 0) { safaribot.sendAll(allContestants.map(function (x) { return x.toCorrectCase() + " " + playerScore(x); }).join(", "), safchan); } sys.sendAll(separator, safchan); var winner, playerId, amt; for (e in contestantsCount) { if (contestantsCount.hasOwnProperty(e)) { player = getAvatarOff(e); if (contestantsCount[e] > 0 && player) { if (contestForfeited.contains(player.idnum)) { continue; } playerId = sys.id(e); var basis = 2.5; amt = Math.max(Math.floor(Math.min(contestantsCount[e] / pokemonSpawned, 1) * basis), 1); if (playerId) { if (e in contestCatchers) { safari.missionProgress(player, "contest", "caught", 1, { caught: contestCatchers[e].length }); if (player.options.showContestCaptures) { safaribot.sendMessage(playerId, "You caught the following Pokémon in this Contest: {0}".format(readable(contestCatchers[e].map(poke, true))), safchan); } safaribot.sendMessage(playerId, "You finished in " + getOrdinal(winners.contains(e) ? 1 : allContestants.indexOf(e) + 1) + " place " + playerScore(e), safchan); } safaribot.sendMessage(playerId, "You received " + plural(amt, "bait") + " for participating in the Contest!", safchan); } rewardCapCheck(player, "bait", amt); safari.missionProgress(player, "contest", "thrown", 1, { thrown: contestantsCount[e] }); /*if (amt >= Math.floor(basis)) { player.records.fullyPlayedContests += 1; }*/ safari.saveGame(player); } } } if (winners.length > 0) { var r, rewardName, amt; for (e in winners) { winner = winners[e]; player = getAvatarOff(winner); if (player) { rewardName = []; if (contestSaved) { for (r in reward) { amt = reward[r]; if (["redapricorn", "grnapricorn", "bluapricorn", "whtapricorn", "blkapricorn", "pnkapricorn", "ylwapricorn"].contains(r) && safari.hasCostumeSkill(player, "extraApricornsFromContest")) { amt = Math.floor(amt * (1 + (safari.getCostumeLevel(player) + 10)/30)); } if (r == "cashbonus") { var val = 1000; if (catchersBST.hasOwnProperty(winner)) { val = catchersBST[winner]; } if (val > 0) { player.money += val; player.money = Math.min(player.money, moneyCap); rewardName.push("$" + val); } } else if (amt > 0) { player.balls[r] += amt; if (player.balls[r] > getCap(r)) { player.balls[r] = getCap(r); } if (r == "entry") { rafflePlayers.add(player.id, player.balls.entry); rafflePlayers.save(); } rewardName.push(amt + " " + itemAlias(r, false, true) + (amt === 1 ? "" : "s")); safari.missionProgress(player, "contestPrize", r, amt); } } } player.records.contestsWon += 1; var c = currentTheme ? themeName(currentTheme) : "Default"; safari.missionProgress(player, "contest", "won", 1, { won: true, theme: c, lead: player.party[0] }); safari.costumeEXP(player, "wincontest"); safari.addToMonthlyLeaderboards(player.id, "contestsWon", 1); safari.saveGame(player); playerId = sys.id(winner); safari.detectiveClue(player.idnum, "contest", playerId); if (contestSaved) { if (playerId) { safaribot.sendMessage(playerId, "You received " + readable(rewardName, "and") + " for winning the Contest!", safchan); } safari.notification(player, "You received " + readable(rewardName, "and") + " for winning the Contest!", "Contest", isPlaying(winner)); } } } } if (currentPokemon && (isRare(currentPokemon) || wildEvent || wildTera)) { sys.appendToFile(mythLog, now() + "|||" + (wildTera ? "[" + currentTypeOverride + "] Terastallized " : "") + poke(currentPokemon) + "::disappeared with the " + themeName(currentTheme) + " Contest" + (wildEvent ? " (Event)" : "") + (Object.keys(wildBallThrows).length === 0 ? " (No Throws)" : "") + "::\n"); } contestInfo.themeId = currentTheme ? currentTheme : "none"; contestInfo.theme = currentTheme ? themeName(currentTheme) + (currentThemeAlter ? " (" + contestThemes[currentTheme].alterName + ")" : "") + (currentThemeEffect ? " [" + cap(currentThemeEffect) + "]": "") : "Default"; contestInfo.rules = safari.translateRules(currentRules, true); lastContests.push(contestInfo); if (lastContests.length > 10) { lastContests.shift(); } permObj.add("lastContests", JSON.stringify(lastContests)); if (currentPokemon) { safari.pokemonFlee(false, true); } //Clear throwers if the contest ends with a Wild Pokemon uncaught resetVars(); currentRules = null; contestCatchers = {}; contestActivity = {}; contestCombo = 0; contestComboPlayers = []; contestForfeited = []; wildSpirit = false; wildTera = false; for (var e = 0; e < needsPechaCleared.length; e++) { var p = getAvatarOff(needsPechaCleared[e]); if (p) { p.berries.pecha = false; safari.saveGame(p); } } needsPechaCleared = []; //Check daily rewards after a contest so players won't need to relog to get their reward when date changes var onChannel = sys.playersOfChannel(safchan), today = getDay(now()); for (e in onChannel) { safari.dailyReward(onChannel[e], today); safari.revertMega(onChannel[e]); } safari.updateLeaderboards(); rawPlayers.save(); cookedPlayers.save(); rafflePlayers.save(); safari.updateMAuctions(); if (now() > scientistQuest.expires) { safari.changeScientistQuest(); } if (today >= getDay(dailyBoost.expires)) { var next = permObj.get("nextDailyBoost"); safari.changeDailyBoost(next); safari.checkNewWeek(); safari.dayCareStep(2); safari.checkNewMonth(); safari.lockInactiveSaves(); safari.backupSaves(); safari.flipEconomyDay(); var mercy = parseInt(permObj.get("loginDaysDown"), 10); if (mercy > 0) { permObj.add("loginDaysDown", 0); } this.pyrBonusMons(); } else { safari.dayCareStep(1); } safari.pendingNotifications(); if (safari.events.spiritDuelsEnabled) { if (safari.events.spiritDuelsBattling && safari.events.spiritDuelsTeams.length > 1) { safari.events.currentSpiritDuel = true; safari.startSpiritDuel(); } } checkUpdate(); } else { var timeSinceLastWild = Math.floor((now() - lastWild) / 1000); var wildChance = Math.max(0.1, 0.02 * timeSinceLastWild); if (!currentPokemon && chance(wildChance)) { var amt = chance(0.05919) ? (chance(0.35) ? (chance(0.042069) ? 4 : 3) : 2) : 1; if (currentTheme && (chance(0.02)) && (currentTheme === "seasonal" || (currentTheme === "seasonal2"))) { amt++; } if (safari.events.spiritDuelsEnabled && spiritSpawn && chance(0.3) && contestCount < 150) { spiritSpawn = false; safari.createWild(null, null, amt, null, null, null, null, false, false, false, true); } else if (chance(0.01 * Math.ceil(3 * (contestCombo - 5) * (contestCombo / 100))) && canSpawnTera) { canSpawnTera = false; safari.createWild(null, null, 1, null, null, null, null, false, false, false, false, true); } else { safari.createWild(null, null, amt); } } } } } }; } module.exports = new Safari();