exports.BattleScripts = { gen: 6, runMove: function(move, pokemon, target, sourceEffect) { if (!sourceEffect && toId(move) !== 'struggle') { var changedMove = this.runEvent('OverrideDecision', pokemon, target, move); if (changedMove && changedMove !== true) { move = changedMove; target = null; } } move = this.getMove(move); if (!target && target !== false) target = this.resolveTarget(pokemon, move); this.setActiveMove(move, pokemon, target); if (pokemon.moveThisTurn) { // THIS IS PURELY A SANITY CHECK // DO NOT TAKE ADVANTAGE OF THIS TO PREVENT A POKEMON FROM MOVING; // USE this.cancelMove INSTEAD this.debug(''+pokemon.id+' INCONSISTENT STATE, ALREADY MOVED: '+pokemon.moveThisTurn); this.clearActiveMove(true); return; } if (!this.runEvent('BeforeMove', pokemon, target, move)) { this.clearActiveMove(true); return; } if (move.beforeMoveCallback) { if (move.beforeMoveCallback.call(this, pokemon, target, move)) { this.clearActiveMove(true); return; } } pokemon.lastDamage = 0; var lockedMove = this.runEvent('LockMove', pokemon); if (lockedMove === true) lockedMove = false; if (!lockedMove) { if (!pokemon.deductPP(move, null, target) && (move.id !== 'struggle')) { this.add('cant', pokemon, 'nopp', move); this.clearActiveMove(true); return; } } pokemon.moveUsed(move); this.useMove(move, pokemon, target, sourceEffect); this.singleEvent('AfterMove', move, null, pokemon, target, move); this.runEvent('AfterMove', target, pokemon, move); this.runEvent('AfterMoveSelf', pokemon, target, move); }, useMove: function(move, pokemon, target, sourceEffect) { if (!sourceEffect && this.effect.id) sourceEffect = this.effect; move = this.getMoveCopy(move); var baseTarget = move.target; if (!target && target !== false) target = this.resolveTarget(pokemon, move); if (move.target === 'self' || move.target === 'allies') { target = pokemon; } if (sourceEffect) move.sourceEffect = sourceEffect.id; this.setActiveMove(move, pokemon, target); this.singleEvent('ModifyMove', move, null, pokemon, target, move, move); if (baseTarget !== move.target) { // Target changed in ModifyMove, so we must adjust it here // Adjust before the next event so the correct target is passed to the // event target = this.resolveTarget(pokemon, move); } move = this.runEvent('ModifyMove', pokemon, target, move, move); if (baseTarget !== move.target) { // Adjust again target = this.resolveTarget(pokemon, move); } if (!move) return false; var attrs = ''; var missed = false; if (pokemon.fainted) { return false; } if (move.isTwoTurnMove && !pokemon.volatiles[move.id]) { attrs = '|[still]'; // suppress the default move animation } var movename = move.name; if (move.id === 'hiddenpower') movename = 'Hidden Power'; if (sourceEffect) attrs += '|[from]'+this.getEffect(sourceEffect); this.addMove('move', pokemon, movename, target+attrs); if (target === false) { this.attrLastMove('[notarget]'); this.add('-notarget'); return true; } if (!this.singleEvent('Try', move, null, pokemon, target, move)) { return true; } if (!this.runEvent('TryMove', pokemon, target, move)) { return true; } if (typeof move.affectedByImmunities === 'undefined') { move.affectedByImmunities = (move.category !== 'Status'); } var damage = false; if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') { if (move.target === 'all') { damage = this.runEvent('TryHitField', target, pokemon, move); } else { damage = this.runEvent('TryHitSide', target, pokemon, move); } if (!damage) { if (damage === false) this.add('-fail', target); return true; } damage = this.moveHit(target, pokemon, move); } else if (move.target === 'allAdjacent' || move.target === 'allAdjacentFoes') { var targets = []; if (move.target === 'allAdjacent') { var allyActive = pokemon.side.active; for (var i=0; i 1) move.spreadHit = true; damage = 0; for (var i=0; i 1) { target = this.runEvent('RedirectTarget', pokemon, pokemon, move, target); } damage = this.tryMoveHit(target, pokemon, move); } if (!pokemon.hp) { this.faint(pokemon, pokemon, move); } if (!damage && damage !== 0 && damage !== undefined) { this.singleEvent('MoveFail', move, null, target, pokemon, move); return true; } if (move.selfdestruct) { this.faint(pokemon, pokemon, move); } if (!move.negateSecondary) { this.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move); this.runEvent('AfterMoveSecondarySelf', pokemon, target, move); } return true; }, tryMoveHit: function(target, pokemon, move, spreadHit) { if (move.selfdestruct && spreadHit) { pokemon.hp = 0; } if ((move.affectedByImmunities && !target.runImmunity(move.type, true)) || (move.isSoundBased && (pokemon !== target || this.gen <= 4) && !target.runImmunity('sound', true))) { return false; } this.setActiveMove(move, pokemon, target); var hitResult = true; if (typeof move.affectedByImmunities === 'undefined') { move.affectedByImmunities = (move.category !== 'Status'); } hitResult = this.runEvent('TryHit', target, pokemon, move); if (!hitResult) { if (hitResult === false) this.add('-fail', target); return false; } var boostTable = [1, 4/3, 5/3, 2, 7/3, 8/3, 3]; // calculate true accuracy var accuracy = move.accuracy; if (accuracy !== true) { if (!move.ignoreAccuracy) { if (pokemon.boosts.accuracy > 0) { accuracy *= boostTable[pokemon.boosts.accuracy]; } else { accuracy /= boostTable[-pokemon.boosts.accuracy]; } } if (!move.ignoreEvasion) { if (target.boosts.evasion > 0 && !move.ignorePositiveEvasion) { accuracy /= boostTable[target.boosts.evasion]; } else if (target.boosts.evasion < 0) { accuracy *= boostTable[-target.boosts.evasion]; } } } if (move.ohko) { // bypasses accuracy modifiers if (!target.volatiles['bounce'] && !target.volatiles['dig'] && !target.volatiles['dive'] && !target.volatiles['fly'] && !target.volatiles['shadowforce'] && !target.volatiles['skydrop']) { accuracy = 30; if (pokemon.level > target.level) accuracy += (pokemon.level - target.level); } } if (move.alwaysHit) { accuracy = true; // bypasses ohko accuracy modifiers } else { accuracy = this.runEvent('Accuracy', target, pokemon, move, accuracy); } if (accuracy !== true && this.random(100) >= accuracy) { if (!spreadHit) this.attrLastMove('[miss]'); this.add('-miss', pokemon, target); return false; } var damage = 0; pokemon.lastDamage = 0; if (move.multihit) { var hits = move.multihit; if (hits.length) { // yes, it's hardcoded... meh if (hits[0] === 2 && hits[1] === 5) { var roll = this.random(6); hits = [2,2,3,3,4,5][roll]; } else { hits = this.random(hits[0],hits[1]+1); } } hits = Math.floor(hits); for (var i=0; i= target.hp) { damage = target.hp - 1; } damage = this.damage(damage, target, pokemon, move); if (!(damage || damage === 0)) { this.debug('damage interrupted'); return false; } didSomething = true; } if (damage === false || damage === null) { if (damage === false) { this.add('-fail', target); } this.debug('damage calculation interrupted'); return false; } if (moveData.boosts && !target.fainted) { hitResult = this.boost(moveData.boosts, target, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.heal && !target.fainted) { var d = target.heal(Math.round(target.maxhp * moveData.heal[0] / moveData.heal[1])); if (!d && d !== 0) { this.add('-fail', target); this.debug('heal interrupted'); return false; } this.add('-heal', target, target.getHealth); didSomething = true; } if (moveData.status) { if (!target.status) { hitResult = target.setStatus(moveData.status, pokemon, move); didSomething = didSomething || hitResult; } else if (!isSecondary) { if (target.status === moveData.status) { this.add('-fail', target, target.status); } else { this.add('-fail', target); } return false; } } if (moveData.forceStatus) { hitResult = target.setStatus(moveData.forceStatus, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.volatileStatus) { hitResult = target.addVolatile(moveData.volatileStatus, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.sideCondition) { hitResult = target.side.addSideCondition(moveData.sideCondition, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.weather) { hitResult = this.setWeather(moveData.weather, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.terrain) { hitResult = this.setTerrain(moveData.terrain, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.pseudoWeather) { hitResult = this.addPseudoWeather(moveData.pseudoWeather, pokemon, move); didSomething = didSomething || hitResult; } if (moveData.forceSwitch || moveData.selfSwitch) { didSomething = true; // at least defer the fail message to later } // Hit events // These are like the TryHit events, except we don't need a FieldHit event. // Scroll up for the TryHit event documentation, and just ignore the "Try" part. ;) hitResult = null; if (move.target === 'all' && !isSelf) { if (moveData.onHitField) hitResult = this.singleEvent('HitField', moveData, {}, target, pokemon, move); } else if ((move.target === 'foeSide' || move.target === 'allySide') && !isSelf) { if (moveData.onHitSide) hitResult = this.singleEvent('HitSide', moveData, {}, target.side, pokemon, move); } else { if (moveData.onHit) hitResult = this.singleEvent('Hit', moveData, {}, target, pokemon, move); var ability = pokemon.battle.getAbility(pokemon.ability); if (ability.onHit) hitResult = this.singleEvent('Hit', ability, {}, target, pokemon, move); if (!isSelf && !isSecondary) { this.runEvent('Hit', target, pokemon, move); } if (moveData.onAfterHit) hitResult = this.singleEvent('AfterHit', moveData, {}, target, pokemon, move); } if (!hitResult && !didSomething && !moveData.self) { if (!isSelf && !isSecondary) { if (hitResult === false || didSomething === false) this.add('-fail', target); } this.debug('move failed because it did nothing'); return false; } } if (moveData.self) { this.moveHit(pokemon, pokemon, move, moveData.self, isSecondary, true); } if (moveData.secondaries) { var secondaryRoll; for (var i = 0; i < moveData.secondaries.length; i++) { secondaryRoll = this.random(100); if (typeof moveData.secondaries[i].chance === 'undefined' || secondaryRoll < moveData.secondaries[i].chance) { this.moveHit(target, pokemon, move, moveData.secondaries[i], true, isSelf); } } } if (target && target.hp > 0 && pokemon.hp > 0 && moveData.forceSwitch) { hitResult = this.runEvent('DragOut', target, pokemon, move); if (hitResult) { target.forceSwitchFlag = true; } else if (hitResult === false) { this.add('-fail', target); } } if (move.selfSwitch && pokemon.hp) { pokemon.switchFlag = move.selfSwitch; } return damage; }, runMegaEvo: function(pokemon) { var side = pokemon.side; var item = this.getItem(pokemon.item); if (!item.megaStone) return false; if (side.megaEvo) return false; var template = this.getTemplate(item.megaStone); if (!template.isMega) return false; if (pokemon.baseTemplate.species !== template.baseSpecies) return false; if (pokemon.volatiles.mustrecharge) return false; // okay, mega evolution is possible this.add('-formechange', pokemon, template.species); this.add('message', template.baseSpecies+" has Mega Evolved into Mega "+template.baseSpecies+"!"); pokemon.formeChange(template); pokemon.baseTemplate = template; // mega evolution is permanent :o pokemon.setAbility(template.abilities['0']); pokemon.baseAbility = pokemon.ability; side.megaEvo = 1; for (var i = 0; i < side.pokemon.length; i++) side.pokemon[i].canMegaEvo = false; return true; }, isAdjacent: function(pokemon1, pokemon2) { if (!pokemon1.fainted && !pokemon2.fainted && pokemon2.position !== pokemon1.position && Math.abs(pokemon2.position-pokemon1.position) <= 1) { return true; } }, checkAbilities: function(selectedAbilities, defaultAbilities) { if (!selectedAbilities.length) return true; var selectedAbility = selectedAbilities.pop(); var isValid = false; for (var i=0; i= mbstmin) break; level++; } //random gender--already handled by PS? //random ability (unreleased hidden are par for the course) var abilities = [template.abilities['0']]; if (template.abilities['1']) { abilities.push(template.abilities['1']); } if (template.abilities['H']) { abilities.push(template.abilities['H']); } var ability = abilities.sample(); //random nature var nature = ["Adamant", "Bashful", "Bold", "Brave", "Calm", "Careful", "Docile", "Gentle", "Hardy", "Hasty", "Impish", "Jolly", "Lax", "Lonely", "Mild", "Modest", "Naive", "Naughty", "Quiet", "Quirky", "Rash", "Relaxed", "Sassy", "Serious", "Timid"].sample(); //random item--I guess if it's in items.js, it's okay var item = Object.keys(this.data.Items).sample(); //since we're selecting forme at random, we gotta make sure forme/item combo is correct if (template.requiredItem) { item = template.requiredItem; } if (this.getItem(item).megaStone) { // we'll exclude mega stones for now item = Object.keys(this.data.Items).sample(); } while ((poke === 'Arceus' && item.indexOf("plate") > -1) || (poke === 'Giratina' && item === 'griseousorb')) { item = Object.keys(this.data.Items).sample(); } //random IVs var ivs = { hp: Math.floor(Math.random()*32), atk: Math.floor(Math.random()*32), def: Math.floor(Math.random()*32), spa: Math.floor(Math.random()*32), spd: Math.floor(Math.random()*32), spe: Math.floor(Math.random()*32) }; //random EVs var evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 }; var s = ["hp","atk","def","spa","spd","spe"]; var evpool = 510; do { var x = s.sample(); var y = Math.floor(Math.random()*Math.min(256-evs[x],evpool+1)); evs[x]+=y; evpool-=y; } while (evpool > 0); //random happiness--useless, since return/frustration is currently a "cheat" var happiness = Math.floor(Math.random()*256); //random shininess? var shiny = (Math.random()*1024<=1); //four random unique moves from movepool. don't worry about "attacking" or "viable" var moves; var pool = ['struggle']; if (poke === 'Smeargle') { pool = Object.keys(this.data.Movedex).exclude('struggle', 'chatter'); } else if (template.learnset) { pool = Object.keys(template.learnset); } if (pool.length <= 4) { moves = pool; } else { moves=pool.sample(4); } team.push({ name: poke, moves: moves, ability: ability, evs: evs, ivs: ivs, nature: nature, item: item, level: level, happiness: happiness, shiny: shiny }); } //console.log(team); return team; }, randomSet: function(template, i) { if (i === undefined) i = 1; template = this.getTemplate(template); var name = template.name; if (!template.exists || (!template.viableMoves && !template.learnset)) { // GET IT? UNOWN? BECAUSE WE CAN'T TELL WHAT THE POKEMON IS template = this.getTemplate('unown'); var stack = 'Template incompatible with random battles: '+name; var fakeErr = {stack: stack}; require('../crashlogger.js')(fakeErr, 'The randbat set generator'); } var moveKeys = Object.keys(template.viableMoves || template.learnset).randomize(); var moves = []; var ability = ''; var item = ''; var evs = { hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85 }; var ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 }; var hasStab = {}; hasStab[template.types[0]] = true; var hasType = {}; hasType[template.types[0]] = true; if (template.types[1]) { hasStab[template.types[1]] = true; hasType[template.types[1]] = true; } var damagingMoves = []; var damagingMoveIndex = {}; var hasMove = {}; var counter = {}; var setupType = ''; var j=0; do { // Choose next 4 moves from learnset/viable moves and add them to moves list: while (moves.length<4 && j 20 || move.multihit || move.basePowerCallback) { damagingMoves.push(move); damagingMoveIndex[moveid] = k; } } // Moves with secondary effects: if (move.secondary) { if (move.secondary.chance < 50) { counter['sheerforce'] -= 5; } else { counter['sheerforce']++; } } // Moves with low accuracy: if (move.accuracy && move.accuracy !== true && move.accuracy < 90) { counter['inaccurate']++; } // Moves which drop stats: var ContraryMove = { leafstorm: 1, overheat: 1, closecombat: 1, superpower: 1, vcreate: 1 }; if (ContraryMove[moveid]) { counter['contrary']++; } // Moves that boost Attack: var PhysicalSetup = { swordsdance:1, dragondance:1, coil:1, bulkup:1, curse:1, bellydrum:1, shiftgear:1, honeclaws:1, howl:1 }; // Moves which boost Special Attack: var SpecialSetup = { nastyplot:1, tailglow:1, quiverdance:1, calmmind:1, chargebeam:1 }; // Moves which boost Attack AND Special Attack: var MixedSetup = { growth:1, workup:1, shellsmash:1 }; if (PhysicalSetup[moveid]) { counter['physicalsetup']++; } if (SpecialSetup[moveid]) { counter['specialsetup']++; } if (MixedSetup[moveid]) { counter['mixedsetup']++; } } // Choose a setup type: if (counter['mixedsetup']) { setupType = 'Mixed'; } else if (counter['specialsetup']) { setupType = 'Special'; } else if (counter['physicalsetup']) { setupType = 'Physical'; } // Iterate through the moves again, this time to cull them: for (var k=0; k 1) rejected = true; isSetup = true; break; case 'nastyplot': case 'tailglow': case 'quiverdance': case 'calmmind': if (counter.Special < 2 && !hasMove['batonpass']) rejected = true; if (setupType !== 'Special' || counter['specialsetup'] > 1) rejected = true; isSetup = true; break; case 'shellsmash': case 'growth': case 'workup': if (counter.Physical+counter.Special < 2 && !hasMove['batonpass']) rejected = true; if (setupType !== 'Mixed' || counter['mixedsetup'] > 1) rejected = true; isSetup = true; break; // bad after setup case 'seismictoss': case 'nightshade': case 'superfang': if (setupType) rejected = true; break; case 'knockoff': case 'perishsong': case 'magiccoat': case 'spikes': if (setupType) rejected = true; break; case 'uturn': case 'voltswitch': if (setupType || hasMove['agility'] || hasMove['rockpolish'] || hasMove['magnetrise']) rejected = true; break; case 'relicsong': if (setupType) rejected = true; break; case 'pursuit': case 'protect': case 'haze': case 'stealthrock': if (setupType || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true; break; case 'trick': case 'switcheroo': if (setupType || (hasMove['rest'] && hasMove['sleeptalk']) || hasMove['trickroom'] || hasMove['reflect'] || hasMove['lightscreen'] || hasMove['batonpass']) rejected = true; break; case 'dragontail': case 'circlethrow': if (hasMove['agility'] || hasMove['rockpolish']) rejected = true; if (hasMove['whirlwind'] || hasMove['roar'] || hasMove['encore']) rejected = true; break; // bit redundant to have both // Attacks: case 'flamethrower': case 'fierydance': if (hasMove['lavaplume'] || hasMove['overheat'] || hasMove['fireblast'] || hasMove['blueflare']) rejected = true; break; case 'overheat': if (setupType === 'Special' || hasMove['fireblast']) rejected = true; break; case 'icebeam': if (hasMove['blizzard']) rejected = true; break; case 'surf': if (hasMove['scald'] || hasMove['hydropump']) rejected = true; break; case 'hydropump': if (hasMove['razorshell'] || hasMove['scald']) rejected = true; break; case 'waterfall': if (hasMove['aquatail']) rejected = true; break; case 'airslash': if (hasMove['hurricane']) rejected = true; break; case 'acrobatics': case 'pluck': case 'drillpeck': if (hasMove['bravebird']) rejected = true; break; case 'solarbeam': if ((!hasMove['sunnyday'] && template.species !== 'Ninetales') || hasMove['gigadrain'] || hasMove['leafstorm']) rejected = true; break; case 'gigadrain': if ((!setupType && hasMove['leafstorm']) || hasMove['petaldance']) rejected = true; break; case 'leafstorm': if (setupType && hasMove['gigadrain']) rejected = true; break; case 'weatherball': if (!hasMove['sunnyday']) rejected = true; break; case 'firepunch': if (hasMove['flareblitz']) rejected = true; break; case 'bugbite': if (hasMove['uturn']) rejected = true; break; case 'crosschop': case 'highjumpkick': if (hasMove['closecombat']) rejected = true; break; case 'drainpunch': if (hasMove['closecombat'] || hasMove['highjumpkick'] || hasMove['crosschop']) rejected = true; break; case 'thunderbolt': if (hasMove['discharge'] || hasMove['voltswitch'] || hasMove['thunder']) rejected = true; break; case 'discharge': case 'thunder': if (hasMove['voltswitch']) rejected = true; break; case 'rockslide': case 'rockblast': if (hasMove['stoneedge'] || hasMove['headsmash']) rejected = true; break; case 'stoneedge': if (hasMove['headsmash']) rejected = true; break; case 'bonemerang': case 'earthpower': if (hasMove['earthquake']) rejected = true; break; case 'dragonclaw': if (hasMove['outrage'] || hasMove['dragontail']) rejected = true; break; case 'ancientpower': if (hasMove['paleowave']) rejected = true; break; case 'dragonpulse': if (hasMove['dracometeor']) rejected = true; break; case 'return': if (hasMove['bodyslam'] || hasMove['facade'] || hasMove['doubleedge'] || hasMove['tailslap']) rejected = true; break; case 'poisonjab': if (hasMove['gunkshot']) rejected = true; break; case 'psychic': if (hasMove['psyshock']) rejected = true; break; case 'fusionbolt': if (setupType && hasMove['boltstrike']) rejected = true; break; case 'boltstrike': if (!setupType && hasMove['fusionbolt']) rejected = true; break; case 'hiddenpowerice': if (hasMove['icywind']) rejected = true; break; // Status: case 'rest': if (hasMove['painsplit'] || hasMove['wish'] || hasMove['recover'] || hasMove['moonlight'] || hasMove['synthesis']) rejected = true; break; case 'softboiled': case 'roost': if (hasMove['wish'] || hasMove['recover']) rejected = true; break; case 'perishsong': if (hasMove['roar'] || hasMove['whirlwind'] || hasMove['haze']) rejected = true; break; case 'roar': // Whirlwind outclasses Roar because Soundproof if (hasMove['whirlwind'] || hasMove['dragontail'] || hasMove['haze'] || hasMove['circlethrow']) rejected = true; break; case 'substitute': if (hasMove['uturn'] || hasMove['voltswitch'] || hasMove['pursuit']) rejected = true; break; case 'fakeout': if (hasMove['trick'] || hasMove['switcheroo']) rejected = true; break; case 'encore': if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true; if (hasMove['whirlwind'] || hasMove['dragontail'] || hasMove['roar'] || hasMove['circlethrow']) rejected = true; break; case 'suckerpunch': if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true; break; case 'cottonguard': if (hasMove['reflect']) rejected = true; break; case 'lightscreen': if (hasMove['calmmind']) rejected = true; break; case 'rockpolish': case 'agility': case 'autotomize': if (!setupType && !hasMove['batonpass'] && hasMove['thunderwave']) rejected = true; if ((hasMove['stealthrock'] || hasMove['spikes'] || hasMove['toxicspikes']) && !hasMove['batonpass']) rejected = true; break; case 'thunderwave': if (setupType && (hasMove['rockpolish'] || hasMove['agility'])) rejected = true; if (hasMove['discharge'] || hasMove['trickroom']) rejected = true; if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true; if (hasMove['yawn'] || hasMove['spore'] || hasMove['sleeppowder']) rejected = true; break; case 'lavaplume': if (hasMove['willowisp']) rejected = true; break; case 'trickroom': if (hasMove['rockpolish'] || hasMove['agility']) rejected = true; break; case 'willowisp': if (hasMove['scald'] || hasMove['yawn'] || hasMove['spore'] || hasMove['sleeppowder']) rejected = true; break; case 'toxic': if (hasMove['thunderwave'] || hasMove['willowisp'] || hasMove['scald'] || hasMove['yawn'] || hasMove['spore'] || hasMove['sleeppowder']) rejected = true; break; } // These moves can be used even if we aren't setting up to use them: var SetupException = { overheat:1, dracometeor:1, leafstorm:1, voltswitch:1, uturn:1, suckerpunch:1, extremespeed:1 }; if (move.category === 'Special' && setupType === 'Physical' && !SetupException[move.id]) { rejected = true; } if (move.category === 'Physical' && setupType === 'Special' && !SetupException[move.id]) { rejected = true; } // This move doesn't satisfy our setup requirements: if (setupType === 'Physical' && move.category !== 'Physical' && counter['Physical'] < 2) { rejected = true; } if (setupType === 'Special' && move.category !== 'Special' && counter['Special'] < 2) { rejected = true; } // Remove rejected moves from the move list. if (rejected && j1) replace = true; } else if (damagingid === 'focuspunch') { // Focus Punch is a bad idea without a sub: if (!hasMove['substitute']) replace = true; } else if (damagingid.substr(0,11) === 'hiddenpower' && damagingType === 'Ice') { // Mono-HP-Ice is never acceptable. replace = true; } else { // If you have one attack, and it's not STAB, Ice, Fire, or Ground, reject it. // Mono-Ice/Ground/Fire is only acceptable if the Pokémon's STABs are one of: Poison, Psychic, Steel, Normal, Grass. if (!hasStab[damagingType]) { if (damagingType === 'Ice' || damagingType === 'Fire' || damagingType === 'Ground') { if (!hasStab['Poison'] && !hasStab['Psychic'] && !hasStab['Steel'] && !hasStab['Normal'] && !hasStab['Grass']) { replace = true; } } else { replace = true; } } } if (replace) moves.splice(damagingMoveIndex[damagingid],1); } } else if (damagingMoves.length===2) { // If you have two attacks, neither is STAB, and the combo isn't Ice/Electric, Ghost/Fighting, or Dark/Fighting, reject one of them at random. var type1 = damagingMoves[0].type, type2 = damagingMoves[1].type; var typeCombo = [type1, type2].sort().join('/'); var rejectCombo = true; if (!type1 in hasStab && !type2 in hasStab) { if (typeCombo === 'Electric/Ice' || typeCombo === 'Fighting/Ghost' || typeCombo === 'Dark/Fighting') rejectCombo = false; } else { rejectCombo = false; } if (rejectCombo) moves.splice(Math.floor(Math.random()*moves.length),1); } else { // If you have three or more attacks, and none of them are STAB, reject one of them at random. var isStab = false; for (var l=0; l= 0) { rejectAbility = true; } if (rejectAbility) { if (ability === ability1.name) { // or not ability = ability0.name; } else if (ability1.rating > 0) { // only switch if the alternative doesn't suck ability = ability1.name; } } if ((abilities[0] === 'Guts' || abilities[1] === 'Guts' || abilities[2] === 'Guts') && ability !== 'Quick Feet' && hasMove['facade']) { ability = 'Guts'; } if ((abilities[0] === 'Swift Swim' || abilities[1] === 'Swift Swim' || abilities[2] === 'Swift Swim') && hasMove['raindance']) { ability = 'Swift Swim'; } if ((abilities[0] === 'Chlorophyll' || abilities[1] === 'Chlorophyll' || abilities[2] === 'Chlorophyll') && ability !== 'Solar Power' && hasMove['sunnyday']) { ability = 'Chlorophyll'; } if (template.id === 'combee') { // it always gets Hustle but its only physical move is Endeavor, which loses accuracy ability = 'Honey Gather'; } } if (hasMove['gyroball']) { ivs.spe = 0; evs.atk += evs.spe; evs.spe = 0; } else if (hasMove['trickroom']) { ivs.spe = 0; evs.hp += evs.spe; evs.spe = 0; } var shouldMegaEvo = this.canMegaEvo(template); if (template.species === 'Alakazam' || template.species === 'Scizor' || template.species === 'Garchomp') { shouldMegaEvo = 'maybe'; } item = 'Leftovers'; if (template.requiredItem) { item = template.requiredItem; } else if (template.species === 'Rotom-Fan') { // this is just to amuse myself // do we really have to keep this item = 'Air Balloon'; } else if (template.species === 'Delibird') { // to go along with the Christmas Delibird set item = 'Leftovers'; // First, the extra high-priority items } else if (ability === 'Imposter') { item = 'Choice Scarf'; } else if (hasMove["magikarpsrevenge"]) { item = 'Choice Band'; } else if (ability === 'Wonder Guard') { item = 'Focus Sash'; } else if (template.species === 'Unown') { item = 'Choice Specs'; } else if (hasMove['trick'] && hasMove['gyroball'] && (ability === 'Levitate' || hasType['Flying'])) { item = 'Macho Brace'; } else if (hasMove['trick'] && hasMove['gyroball']) { item = 'Iron Ball'; } else if (hasMove['trick'] || hasMove['switcheroo']) { var randomNum = Math.random()*2; if (counter.Physical >= 3 && (template.baseStats.spe >= 95 || randomNum>1)) { item = 'Choice Band'; } else if (counter.Special >= 3 && (template.baseStats.spe >= 95 || randomNum>1)) { item = 'Choice Specs'; } else { item = 'Choice Scarf'; } } else if (hasMove['rest'] && !hasMove['sleeptalk'] && ability !== 'Natural Cure' && ability !== 'Shed Skin') { item = 'Chesto Berry'; } else if (hasMove['naturalgift']) { item = 'Liechi Berry'; } else if (hasMove['geomancy']) { item = 'Power Herb'; } else if (ability === 'Harvest') { item = 'Sitrus Berry'; } else if (template.species === 'Cubone' || template.species === 'Marowak') { item = 'Thick Club'; } else if (template.species === 'Pikachu') { item = 'Light Ball'; } else if (template.species === 'Clamperl') { item = 'DeepSeaTooth'; } else if (template.species === 'Spiritomb') { item = 'Leftovers'; } else if (template.species === 'Dusclops') { item = 'Eviolite'; } else if (shouldMegaEvo === true) { item = this.getTemplate(template.otherFormes[0]).requiredItem; // Mega Mawile should never start with Sheer Force if (template.species === 'Mawile') ability = 'Intimidate'; } else if (hasMove['reflect'] && hasMove['lightscreen']) { item = 'Light Clay'; } else if (hasMove['shellsmash']) { item = 'White Herb'; } else if (hasMove['facade'] || ability === 'Poison Heal' || ability === 'Toxic Boost') { item = 'Toxic Orb'; } else if (hasMove['raindance']) { item = 'Damp Rock'; } else if (hasMove['sunnyday']) { item = 'Heat Rock'; } else if (hasMove['sandstorm']) { // lol item = 'Smooth Rock'; } else if (hasMove['hail']) { // lol item = 'Icy Rock'; } else if (ability === 'Magic Guard' && hasMove['psychoshift']) { item = 'Flame Orb'; } else if (ability === 'Sheer Force' || ability === 'Magic Guard') { item = 'Life Orb'; } else if (ability === 'Unburden') { item = 'Red Card'; // Give Unburden mons a Normal Gem if they have a Normal-type attacking move for (var m in moves) { var move = this.getMove(moves[m]); if (move.type === 'Normal' && (move.basePower || move.basePowerCallback)) { item = 'Normal Gem'; break; } } // medium priority } else if (shouldMegaEvo) { item = this.getTemplate(template.otherFormes[0]).requiredItem; } else if (ability === 'Guts') { if (hasMove['drainpunch']) { item = 'Flame Orb'; } else { item = 'Toxic Orb'; } if ((hasMove['return'] || hasMove['hyperfang']) && !hasMove['facade']) { // lol no for (var j=0; j= 4 && !hasMove['fakeout'] && !hasMove['suckerpunch'] && !hasMove['flamecharge'] && !hasMove['rapidspin']) { if (Math.random()*3 > 1) { item = 'Choice Band'; } else { item = 'Expert Belt'; } } else if (counter.Special >= 4) { if (Math.random()*3 > 1) { item = 'Choice Specs'; } else { item = 'Expert Belt'; } } else if (this.getEffectiveness('Ground', template) >= 2 && ability !== 'Levitate' && !hasMove['magnetrise']) { item = 'Air Balloon'; } else if ((hasMove['eruption'] || hasMove['waterspout']) && !counter['Status']) { item = 'Choice Scarf'; } else if (hasMove['substitute'] || hasMove['detect'] || hasMove['protect'] || ability === 'Moody') { item = 'Leftovers'; } else if ((hasMove['flail'] || hasMove['reversal']) && !hasMove['endure'] && ability !== 'Sturdy') { item = 'Focus Sash'; } else if (ability === 'Iron Barbs' || ability === 'Rough Skin') { item = 'Rocky Helmet'; } else if ((template.baseStats.hp+75)*(template.baseStats.def+template.baseStats.spd+175) > 60000 || template.species === 'Skarmory' || template.species === 'Forretress') { // skarmory and forretress get exceptions for their typing item = 'Leftovers'; } else if (counter.Physical + counter.Special >= 3 && setupType) { item = 'Life Orb'; } else if (counter.Special >= 3 && setupType) { item = 'Life Orb'; } else if (counter.Physical + counter.Special >= 4 && template.baseStats.def + template.baseStats.spd > 179) { item = 'Assault Vest'; } else if (counter.Physical + counter.Special >= 4) { item = 'Expert Belt'; } else if (i===0 && ability !== 'Sturdy' && !counter['recoil']) { item = 'Focus Sash'; } else if (hasMove['outrage']) { item = 'Lum Berry'; // this is the "REALLY can't think of a good item" cutoff // why not always Leftovers? Because it's boring. :P } else if (counter.Physical + counter.Special >= 2 && template.baseStats.hp + template.baseStats.def + template.baseStats.spd > 315) { item = 'Weakness Policy'; } else if (hasType['Flying'] || ability === 'Levitate') { item = 'Leftovers'; } else if (this.getEffectiveness('Ground', template) >= 1 && ability !== 'Levitate' && !hasMove['magnetrise']) { item = 'Air Balloon'; } else if (hasType['Poison']) { item = 'Black Sludge'; } else if (counter.Status <= 1) { item = 'Life Orb'; } else { item = 'Leftovers'; } // For Trick / Switcheroo if (item === 'Leftovers' && hasType['Poison']) { item = 'Black Sludge'; } } // 95-86-82-78-74-70 var levelScale = { LC: 95, NFE: 90, 'LC Uber': 86, NU: 86, BL3: 84, RU: 82, BL2: 80, UU: 78, BL: 76, 'Limbo': 86, 'Limbo C': 83, 'Limbo B': 80, 'Limbo A': 77, OU: 74, CAP: 74, Unreleased: 74, Uber: 70 }; var customScale = { // Really bad Pokemon and jokemons Azurill: 99, Burmy: 99, Cascoon: 99, Caterpie: 99, Cleffa: 99, Combee: 99, Feebas: 99, Igglybuff: 99, Happiny: 99, Hoppip: 99, Kakuna: 99, Kricketot: 99, Ledyba: 99, Magikarp: 99, Metapod: 99, Pichu: 99, Ralts: 99, Sentret: 99, Shedinja: 99, Silcoon: 99, Slakoth: 99, Sunkern: 99, Tynamo: 99, Tyrogue: 99, Unown: 99, Weedle: 99, Wurmple: 99, Zigzagoon: 99, Clefairy: 95, Delibird: 95, "Farfetch'd": 95, Jigglypuff: 95, Kirlia: 95, Ledian: 95, Luvdisc: 95, Marill: 95, Skiploom: 95, Pachirisu: 90, // Eviolite Ferroseed: 95, Misdreavus: 95, Munchlax: 95, Murkrow: 95, Natu: 95, Gligar: 90, Metang: 90, Monferno: 90, Roselia: 90, Seadra: 90, Togetic: 90, Wartortle: 90, Whirlipede: 90, Dusclops: 84, Porygon2: 82, Chansey: 78, // Weather or teammate dependent Snover: 95, Vulpix: 95, Ninetales: 78, Tentacruel: 78, Toxicroak: 78, // Banned mega Kangaskhan: 72, Gengar: 72, Blaziken: 72, // Holistic judgment Carvanha: 90, Lucario: 72, Genesect: 72, Kyurem: 78 }; var level = levelScale[template.tier] || 90; if (customScale[template.name]) level = customScale[template.name]; if (template.name === 'Chandelure' && ability === 'Shadow Tag') level = 70; if (template.name === 'Serperior' && ability === 'Contrary') level = 74; if (template.name === 'Magikarp' && hasMove['magikarpsrevenge']) level = 85; if (template.name === 'Spinda' && ability !== 'Contrary') level = 95; return { name: name, moves: moves, ability: ability, evs: evs, ivs: ivs, item: item, level: level, shiny: (Math.random()*1024<=1) }; }, randomTeam: function(side) { var keys = []; var pokemonLeft = 0; var pokemon = []; for (var i in this.data.FormatsData) { if (this.data.FormatsData[i].viableMoves && !this.data.FormatsData[i].isNonstandard && !this.getTemplate(i).evos.length) { keys.push(i); } } keys = keys.randomize(); // PotD stuff var potd = {}; if ('Rule:potd' in this.getFormat().banlistTable) { potd = this.getTemplate(config.potd); } var typeCount = {}; var typeComboCount = {}; var baseFormes = {}; var uberCount = 0; var nuCount = 0; var megaCount = 0; for (var i=0; i 1) continue; if ((tier === 'NFE' || tier === 'NU') && nuCount > 1 && Math.random()*5>1) continue; if (tier === 'Uber' && uberCount > 1 && Math.random()*5>1) continue; // CAPs have 20% the normal rate if (tier === 'CAP' && Math.random()*5>1) continue; // Arceus formes have 1/18 the normal rate each (so Arceus as a whole has a normal rate) if (keys[i].substr(0,6) === 'arceus' && Math.random()*18>1) continue; // Basculin formes have 1/2 the normal rate each (so Basculin as a whole has a normal rate) if (keys[i].substr(0,8) === 'basculin' && Math.random()*2>1) continue; // Genesect formes have 1/5 the normal rate each (so Genesect as a whole has a normal rate) if (keys[i].substr(0,8) === 'genesect' && Math.random()*5>1) continue; // Not available on XY if (template.species === 'Pichu-Spiky-eared') continue; // Limit 2 of any type var types = template.types; var skip = false; for (var t=0; t 1 && Math.random()*5>1) { skip = true; break; } } if (skip) continue; if (potd && potd.name && potd.types) { // The Pokemon of the Day belongs in slot 2 if (i===1) { template = potd; if (template.species === 'Magikarp') { template.viableMoves = {magikarpsrevenge:1, splash:1, bounce:1}; } else if (template.species === 'Delibird') { template.viableMoves = {present:1, bestow:1}; } } else if (template.species === potd.species) { continue; // No, thanks, I've already got one } } var set = this.randomSet(template, i); // Limit 1 of any type combination var typeCombo = types.join(); if (set.ability === 'Drought' || set.ability === 'Drizzle') { // Drought and Drizzle don't count towards the type combo limit typeCombo = set.ability; } if (typeCombo in typeComboCount) continue; // Limit the number of Megas to one, just like in-game if (this.getItem(set.item).megaStone && megaCount > 0) continue; // Limit to one of each species (Species Clause) if (baseFormes[template.baseSpecies]) continue; baseFormes[template.baseSpecies] = 1; // Okay, the set passes, add it to our team pokemon.push(set); pokemonLeft++; // Now that our Pokemon has passed all checks, we can increment the type counter for (var t=0; t1) continue; // Basculin formes have 1/2 the normal rate each (so Basculin as a whole has a normal rate) if (keys[i].substr(0,8) === 'basculin' && Math.random()*2>1) continue; // Genesect formes have 1/5 the normal rate each (so Genesect as a whole has a normal rate) if (keys[i].substr(0,8) === 'genesect' && Math.random()*5>1) continue; // Not available on XY if (template.species === 'Pichu-Spiky-eared') continue; // Limit 2 of any type var types = template.types; var skip = false; for (var t=0; t 1 && Math.random()*5>1) { skip = true; break; } } if (skip) continue; // More potd stuff if (potd && potd.name && potd.types) { // The Pokemon of the Day belongs in slot 2 if (i===1) { template = potd; if (template.species === 'Magikarp') { template.viableMoves = {magikarpsrevenge:1, splash:1, bounce:1}; } else if (template.species === 'Delibird') { template.viableMoves = {present:1, bestow:1}; } } else if (template.species === potd.species) { continue; // No, thanks, I've already got one } } var set = this.randomDoublesSet(template); // Limit 1 of any type combination var typeCombo = types.join(); if (set.ability === 'Drought' || set.ability === 'Drizzle') { // Drought and Drizzle don't count towards the type combo limit typeCombo = set.ability; } if (typeCombo in typeComboCount) continue; // Limit the number of Megas to one, just like in-game if (this.getItem(set.item).megaStone && megaCount > 0) continue; // Limit to one of each species (Species Clause) if (baseFormes[template.baseSpecies]) continue; baseFormes[template.baseSpecies] = 1; // Okay, the set passes, add it to our team pokemon.push(set); pokemonLeft++; // Now that our Pokemon has passed all checks, we can increment the type counter for (var t=0; t 20 || move.multihit || move.basePowerCallback) { damagingMoves.push(move); damagingMoveIndex[moveid] = k; } } // Moves with secondary effects: if (move.secondary) { if (move.secondary.chance < 50) { counter['sheerforce'] -= 5; } else { counter['sheerforce']++; } } // Moves with low accuracy: if (move.accuracy && move.accuracy !== true && move.accuracy < 90) { counter['inaccurate']++; } // Moves which drop stats: var ContraryMove = { leafstorm: 1, overheat: 1, closecombat: 1, superpower: 1, vcreate: 1 }; if (ContraryMove[moveid]) { counter['contrary']++; } // Moves that boost Attack: var PhysicalSetup = { swordsdance:1, dragondance:1, coil:1, bulkup:1, curse:1, bellydrum:1, shiftgear:1, honeclaws:1, howl:1 }; // Moves which boost Special Attack: var SpecialSetup = { nastyplot:1, tailglow:1, quiverdance:1, calmmind:1, chargebeam:1 }; // Moves which boost Attack AND Special Attack: var MixedSetup = { growth:1, workup:1, shellsmash:1 }; if (PhysicalSetup[moveid]) { counter['physicalsetup']++; } if (SpecialSetup[moveid]) { counter['specialsetup']++; } if (MixedSetup[moveid]) { counter['mixedsetup']++; } } // Choose a setup type: if (counter['mixedsetup']) { setupType = 'Mixed'; } else if (counter['specialsetup']) { setupType = 'Special'; } else if (counter['physicalsetup']) { setupType = 'Physical'; } // Iterate through the moves again, this time to cull them: for (var k=0; k 1) rejected = true; isSetup = true; break; case 'nastyplot': case 'tailglow': case 'quiverdance': case 'calmmind': if (counter.Special < 2 && !hasMove['batonpass']) rejected = true; if (setupType !== 'Special' || counter['specialsetup'] > 1) rejected = true; isSetup = true; break; case 'shellsmash': case 'growth': case 'workup': if (counter.Physical+counter.Special < 2 && !hasMove['batonpass']) rejected = true; if (setupType !== 'Mixed' || counter['mixedsetup'] > 1) rejected = true; isSetup = true; break; // bad after setup case 'seismictoss': case 'nightshade': case 'superfang': if (setupType) rejected = true; break; case 'knockoff': case 'perishsong': case 'magiccoat': case 'spikes': if (setupType) rejected = true; break; case 'uturn': case 'voltswitch': if (setupType || hasMove['agility'] || hasMove['rockpolish'] || hasMove['magnetrise']) rejected = true; break; case 'relicsong': if (setupType) rejected = true; break; case 'pursuit': case 'protect': case 'haze': case 'stealthrock': if (setupType || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true; break; case 'trick': case 'switcheroo': if (setupType || (hasMove['rest'] && hasMove['sleeptalk']) || hasMove['trickroom'] || hasMove['reflect'] || hasMove['lightscreen'] || hasMove['batonpass']) rejected = true; break; case 'dragontail': case 'circlethrow': if (hasMove['agility'] || hasMove['rockpolish']) rejected = true; if (hasMove['whirlwind'] || hasMove['roar'] || hasMove['encore']) rejected = true; break; // bit redundant to have both // Attacks: case 'flamethrower': case 'fierydance': if (hasMove['heatwave'] || hasMove['overheat'] || hasMove['fireblast'] || hasMove['blueflare']) rejected = true; break; case 'overheat': if (setupType === 'Special' || hasMove['fireblast']) rejected = true; break; case 'icebeam': if (hasMove['blizzard']) rejected = true; break; case 'surf': if (hasMove['scald'] || hasMove['hydropump'] || hasMove['muddywater']) rejected = true; break; case 'hydropump': if (hasMove['razorshell'] || hasMove['scald'] || hasMove['muddywater']) rejected = true; break; case 'waterfall': if (hasMove['aquatail']) rejected = true; break; case 'airslash': if (hasMove['hurricane']) rejected = true; break; case 'acrobatics': case 'pluck': case 'drillpeck': if (hasMove['bravebird']) rejected = true; break; case 'solarbeam': if ((!hasMove['sunnyday'] && template.species !== 'Ninetales') || hasMove['gigadrain'] || hasMove['leafstorm']) rejected = true; break; case 'gigadrain': if ((!setupType && hasMove['leafstorm']) || hasMove['petaldance']) rejected = true; break; case 'leafstorm': if (setupType && hasMove['gigadrain']) rejected = true; break; case 'weatherball': if (!hasMove['sunnyday']) rejected = true; break; case 'firepunch': if (hasMove['flareblitz']) rejected = true; break; case 'crosschop': case 'highjumpkick': if (hasMove['closecombat']) rejected = true; break; case 'drainpunch': if (hasMove['closecombat'] || hasMove['crosschop']) rejected = true; break; case 'thunder': if (hasMove['thunderbolt']) rejected = true; break; case 'stoneedge': if (hasMove['rockslide']) rejected = true; break; case 'stoneedge': if (hasMove['headsmash']) rejected = true; break; case 'bonemerang': case 'earthpower': if (hasMove['earthquake']) rejected = true; break; case 'outrage': if (hasMove['dragonclaw'] || hasMove['dragontail']) rejected = true; break; case 'ancientpower': if (hasMove['paleowave']) rejected = true; break; case 'dragonpulse': if (hasMove['dracometeor']) rejected = true; break; case 'return': if (hasMove['bodyslam'] || hasMove['facade'] || hasMove['doubleedge'] || hasMove['tailslap']) rejected = true; break; case 'poisonjab': if (hasMove['gunkshot']) rejected = true; break; case 'psychic': if (hasMove['psyshock']) rejected = true; break; case 'fusionbolt': if (setupType && hasMove['boltstrike']) rejected = true; break; case 'boltstrike': if (!setupType && hasMove['fusionbolt']) rejected = true; break; case 'hiddenpowerice': if (hasMove['icywind']) rejected = true; break; // Status: case 'rest': if (hasMove['painsplit'] || hasMove['wish'] || hasMove['recover'] || hasMove['moonlight'] || hasMove['synthesis']) rejected = true; break; case 'softboiled': case 'roost': if (hasMove['wish'] || hasMove['recover']) rejected = true; break; case 'perishsong': if (hasMove['roar'] || hasMove['whirlwind'] || hasMove['haze']) rejected = true; break; case 'roar': // Whirlwind outclasses Roar because Soundproof if (hasMove['whirlwind'] || hasMove['dragontail'] || hasMove['haze'] || hasMove['circlethrow']) rejected = true; break; case 'substitute': if (hasMove['uturn'] || hasMove['voltswitch'] || hasMove['pursuit']) rejected = true; break; case 'fakeout': if (hasMove['trick'] || hasMove['switcheroo']) rejected = true; break; case 'encore': if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true; if (hasMove['whirlwind'] || hasMove['dragontail'] || hasMove['roar'] || hasMove['circlethrow']) rejected = true; break; case 'suckerpunch': if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true; break; case 'cottonguard': if (hasMove['reflect']) rejected = true; break; case 'lightscreen': if (hasMove['calmmind']) rejected = true; break; case 'rockpolish': case 'agility': case 'autotomize': if (!setupType && !hasMove['batonpass'] && hasMove['thunderwave']) rejected = true; if ((hasMove['stealthrock'] || hasMove['spikes'] || hasMove['toxicspikes']) && !hasMove['batonpass']) rejected = true; break; case 'thunderwave': if (setupType && (hasMove['rockpolish'] || hasMove['agility'])) rejected = true; if (hasMove['discharge'] || hasMove['trickroom']) rejected = true; if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true; if (hasMove['yawn'] || hasMove['spore'] || hasMove['sleeppowder']) rejected = true; break; case 'lavaplume': if (hasMove['willowisp']) rejected = true; break; case 'trickroom': if (hasMove['rockpolish'] || hasMove['agility']) rejected = true; break; case 'willowisp': if (hasMove['scald'] || hasMove['yawn'] || hasMove['spore'] || hasMove['sleeppowder']) rejected = true; break; case 'toxic': if (hasMove['thunderwave'] || hasMove['willowisp'] || hasMove['scald'] || hasMove['yawn'] || hasMove['spore'] || hasMove['sleeppowder']) rejected = true; break; } // These moves can be used even if we aren't setting up to use them: var SetupException = { overheat:1, dracometeor:1, leafstorm:1, voltswitch:1, uturn:1, suckerpunch:1, extremespeed:1 }; if (move.category === 'Special' && setupType === 'Physical' && !SetupException[move.id]) { rejected = true; } if (move.category === 'Physical' && setupType === 'Special' && !SetupException[move.id]) { rejected = true; } // This move doesn't satisfy our setup requirements: if (setupType === 'Physical' && move.category !== 'Physical' && counter['Physical'] < 2) { rejected = true; } if (setupType === 'Special' && move.category !== 'Special' && counter['Special'] < 2) { rejected = true; } // Remove rejected moves from the move list. if (rejected && j1) replace = true; } else if (damagingid === 'focuspunch') { // Focus Punch is a bad idea without a sub: if (!hasMove['substitute']) replace = true; } else if (damagingid.substr(0,11) === 'hiddenpower' && damagingType === 'Ice') { // Mono-HP-Ice is never acceptable. replace = true; } else { // If you have one attack, and it's not STAB, Ice, Fire, or Ground, reject it. // Mono-Ice/Ground/Fire is only acceptable if the Pokémon's STABs are one of: Poison, Normal, Grass. if (!hasStab[damagingType]) { if (damagingType === 'Ice' || damagingType === 'Fire' || damagingType === 'Ground') { if (!hasStab['Poison'] && !hasStab['Normal'] && !hasStab['Grass']) { replace = true; } } else { replace = true; } } } if (replace) moves.splice(damagingMoveIndex[damagingid],1); } } else if (damagingMoves.length===2) { // If you have two attacks, neither is STAB, and the combo isn't Ice/Electric, Ghost/Fighting, or Dark/Fighting, reject one of them at random. var type1 = damagingMoves[0].type, type2 = damagingMoves[1].type; var typeCombo = [type1, type2].sort().join('/'); var rejectCombo = true; if (!type1 in hasStab && !type2 in hasStab) { if (typeCombo === 'Electric/Ice' || typeCombo === 'Fighting/Ghost' || typeCombo === 'Dark/Fighting') rejectCombo = false; } else { rejectCombo = false; } if (rejectCombo) moves.splice(Math.floor(Math.random()*moves.length),1); } else { // If you have three or more attacks, and none of them are STAB, reject one of them at random. var isStab = false; for (var l=0; l= 0) { rejectAbility = true; } if (rejectAbility) { if (ability === ability1.name) { // or not ability = ability0.name; } else if (ability1.rating > 0) { // only switch if the alternative doesn't suck ability = ability1.name; } } if ((abilities[0] === 'Guts' || abilities[1] === 'Guts' || abilities[2] === 'Guts') && ability !== 'Quick Feet' && hasMove['facade']) { ability = 'Guts'; } if ((abilities[0] === 'Swift Swim' || abilities[1] === 'Swift Swim' || abilities[2] === 'Swift Swim') && hasMove['raindance']) { ability = 'Swift Swim'; } if ((abilities[0] === 'Chlorophyll' || abilities[1] === 'Chlorophyll' || abilities[2] === 'Chlorophyll') && ability !== 'Solar Power') { ability = 'Chlorophyll'; } if (abilities[0] === 'Intimidate' || abilities[1] === 'Intimidate' || abilities[2] === 'Intimidate') { ability = 'Intimidate'; } } // Make EVs comply with the sets. // Quite simple right now, 252 attack, 252 hp if slow 252 speed if fast, 4 evs for the strong defense. // TO-DO: Make this more complex if (counter.Special >= 2) { evs.atk = 0; evs.spa = 252; } else if (counter.Physical >= 2) { evs.atk = 252; evs.spa = 0; } else { // Fallback in case a Pokémon lacks attacks... go by stats if (template.baseStats.spa >= template.baseStats.atk) { evs.atk = 0; evs.spa = 252; } else { evs.atk = 252; evs.spa = 0; } } if (template.baseStats.spe > 80) { evs.spe = 252; evs.hp = 4; } else { evs.hp = 252; if (template.baseStats.def > template.baseStats.spd) { evs.def = 4; } else { evs.spd = 4; } } // Naturally slow mons already have the proper EVs, check IVs for Gyro Ball and TR if (hasMove['gyroball'] || hasMove['trickroom']) { ivs.spe = 0; } var shouldMegaEvo = this.canMegaEvo(template); if (template.species === 'Alakazam' || template.species === 'Scizor' || template.species === 'Garchomp') { shouldMegaEvo = 'maybe'; } item = 'Sitrus Berry'; if (template.requiredItem) { item = template.requiredItem; // First, the extra high-priority items } else if (ability === 'Imposter') { item = 'Choice Scarf'; } else if (hasMove["magikarpsrevenge"]) { item = 'Mystic Water'; } else if (ability === 'Wonder Guard') { item = 'Focus Sash'; } else if (template.species === 'Unown') { item = 'Choice Specs'; } else if (hasMove['trick'] && hasMove['gyroball'] && (ability === 'Levitate' || hasType['Flying'])) { item = 'Macho Brace'; } else if (hasMove['trick'] && hasMove['gyroball']) { item = 'Iron Ball'; } else if (hasMove['trick'] || hasMove['switcheroo']) { var randomNum = Math.random()*2; if (counter.Physical >= 3 && (template.baseStats.spe >= 95 || randomNum>1)) { item = 'Choice Band'; } else if (counter.Special >= 3 && (template.baseStats.spe >= 95 || randomNum>1)) { item = 'Choice Specs'; } else { item = 'Choice Scarf'; } } else if (hasMove['rest'] && !hasMove['sleeptalk'] && ability !== 'Natural Cure' && ability !== 'Shed Skin') { item = 'Chesto Berry'; } else if (hasMove['naturalgift']) { item = 'Liechi Berry'; } else if (hasMove['geomancy']) { item = 'Power Herb'; } else if (ability === 'Harvest') { item = 'Sitrus Berry'; } else if (template.species === 'Cubone' || template.species === 'Marowak') { item = 'Thick Club'; } else if (template.species === 'Pikachu') { item = 'Light Ball'; } else if (template.species === 'Clamperl') { item = 'DeepSeaTooth'; } else if (template.species === 'Spiritomb') { item = 'Leftovers'; } else if (template.species === 'Dusclops') { item = 'Eviolite'; } else if (template.species === 'Scrafty') { item = 'Assault Vest'; } else if (template.species === 'Amoonguss') { item = 'Black Sludge'; } else if (shouldMegaEvo === true) { item = this.getTemplate(template.otherFormes[0]).requiredItem; } else if (hasMove['reflect'] && hasMove['lightscreen']) { item = 'Light Clay'; } else if (hasMove['shellsmash']) { item = 'White Herb'; } else if (hasMove['facade'] || ability === 'Poison Heal' || ability === 'Toxic Boost') { item = 'Toxic Orb'; } else if (hasMove['raindance']) { item = 'Damp Rock'; } else if (hasMove['sunnyday']) { item = 'Heat Rock'; } else if (hasMove['sandstorm']) { item = 'Smooth Rock'; } else if (hasMove['hail']) { item = 'Icy Rock'; } else if (ability === 'Magic Guard' && hasMove['psychoshift']) { item = 'Flame Orb'; } else if (ability === 'Sheer Force' || ability === 'Magic Guard') { item = 'Life Orb'; } else if (ability === 'Unburden') { item = 'Red Card'; // Give Unburden mons a Normal Gem if they have a Normal-type attacking move for (var m in moves) { var move = this.getMove(moves[m]); if (move.type === 'Normal' && (move.basePower || move.basePowerCallback)) { item = 'Normal Gem'; break; } // Give power herb to hawlucha if it has sky attack and unburden if (template.species === 'Hawlucha' && hasMove['skyattack']) { item = 'Power Herb'; break; } } // medium priority } else if (shouldMegaEvo) { item = this.getTemplate(template.otherFormes[0]).requiredItem; } else if (ability === 'Guts') { if (hasMove['drainpunch']) { item = 'Flame Orb'; } else { item = 'Toxic Orb'; } if ((hasMove['return'] || hasMove['hyperfang']) && !hasMove['facade']) { // lol no for (var j=0; j= 4 && !hasMove['fakeout'] && !hasMove['suckerpunch'] && !hasMove['flamecharge'] && !hasMove['rapidspin']) { item = 'Expert Belt'; } else if (counter.Special >= 4) { item = 'Expert Belt'; } else if (this.getEffectiveness('Ground', template) >= 2 && ability !== 'Levitate' && !hasMove['magnetrise']) { item = 'Shuca Berry'; } else if (this.getEffectiveness('Ice', template) >= 1) { item = 'Yache Berry'; } else if (this.getEffectiveness('Fighting', template) >= 1) { item = 'Chople Berry'; } else if (hasMove['substitute'] || hasMove['detect'] || hasMove['protect'] || ability === 'Moody') { item = 'Leftovers'; } else if ((hasMove['flail'] || hasMove['reversal']) && !hasMove['endure'] && ability !== 'Sturdy') { item = 'Focus Sash'; } else if (ability === 'Iron Barbs' || ability === 'Rough Skin') { item = 'Rocky Helmet'; } else if ((template.baseStats.hp+75)*(template.baseStats.def+template.baseStats.spd+175) > 60000 || template.species === 'Skarmory' || template.species === 'Forretress') { // skarmory and forretress get exceptions for their typing item = 'Sitrus Berry'; } else if (counter.Physical + counter.Special >= 3 && setupType) { item = 'Life Orb'; } else if (counter.Special >= 3 && setupType) { item = 'Life Orb'; } else if (counter.Physical + counter.Special >= 4 && template.baseStats.def + template.baseStats.spd > 179) { item = 'Assault Vest'; } else if (counter.Physical + counter.Special >= 4) { item = 'Expert Belt'; } else if (hasMove['outrage']) { item = 'Lum Berry'; // this is the "REALLY can't think of a good item" cutoff } else if (counter.Physical + counter.Special >= 2 && template.baseStats.hp + template.baseStats.def + template.baseStats.spd > 315) { item = 'Weakness Policy'; } else if (this.getEffectiveness('Ground', template) >= 1 && ability !== 'Levitate' && !hasMove['magnetrise']) { item = 'Shuca Berry'; } else if (hasType['Poison']) { item = 'Black Sludge'; } else if (counter.Status <= 1) { item = 'Life Orb'; } else { item = 'Sitrus Berry'; } // For Trick / Switcheroo if (item === 'Leftovers' && hasType['Poison']) { item = 'Black Sludge'; } } // We choose level based on BST. Min level is 70, max level is 99. 600+ BST is 70, less than 300 is 99. Calculate with those values. // Every 10.35 BST adds a level from 70 up to 99. Results are floored. var bst = template.baseStats.hp + template.baseStats.atk + template.baseStats.def + template.baseStats.spa + template.baseStats.spd + template.baseStats.spe; var level = 70 + Math.floor(((600 - clampIntRange(bst, 300, 600)) / 10.35)); return { name: name, moves: moves, ability: ability, evs: evs, ivs: ivs, item: item, level: level, shiny: (Math.random()*1024<=1) }; }, randomSeasonalFFTeam: function(side) { var seasonalPokemonList = [ 'charizard', 'ninetales', 'houndoom', 'arceusfire', 'arcanine', 'moltres', 'rapidash', 'magmar', 'quilava', 'typhlosion', 'entei', 'hooh', 'blaziken', 'rotomheat', 'chandelure', 'magcargo', 'reshiram', 'zekrom', 'heatran', 'arceusdragon', 'arceusfighting', 'seadra', 'kingdra', 'gyarados', 'dunsparce', 'milotic', 'drapion', 'growlithe', 'paras', 'parasect', 'magikarp', 'suicune', 'raikou', 'absol', 'spiritomb', 'horsea', 'ponyta', 'blitzle', 'zebstrika' ]; seasonalPokemonList = seasonalPokemonList.randomize(); var team = []; for (var i=0; i<6; i++) { var set = this.randomSet(seasonalPokemonList[i], i); if (seasonalPokemonList[i] === 'gyarados') set.shiny = true; set.moves[3] = 'Explosion'; team.push(set); } return team; } };