diff --git a/config/formats.js b/config/formats.js index bbefa7799c..f9d2af655e 100644 --- a/config/formats.js +++ b/config/formats.js @@ -266,148 +266,48 @@ exports.Formats = [ column: 2, }, { - name: "[Gen 7] Inheritance", + name: "[Gen 7] Full Potential", desc: [ - "Pokémon may use the ability and moves of another, as long as they forfeit their own learnset.", - "• Inheritance", + "A Pokémon's highest stat is used when calculating the damage their attacks inflict.", + "• Full Potential", ], - mod: 'gen7', - ruleset: ['Pokemon', 'Standard', 'Team Preview'], - banlist: ['Uber', 'Kyurem-Black', 'Pheromosa', 'Regigigas', 'Shedinja', 'Slaking', 'Gengarite', 'Kangaskhanite', 'Lucarionite', 'Salamencite', 'Power Construct', 'Shadow Tag', 'Baton Pass'], - bannedDonors: ['Araquanid', 'Azumarill', 'Azurill', 'Blaziken', 'Bunnelby', 'Carvanha', 'Chatot', 'Combusken', 'Dewpider', 'Diggersby', 'Diglett', 'Ditto', 'Dugtrio', 'Golett', 'Golurk', 'Liepard', 'Machamp', 'Machoke', 'Machop', 'Marill', 'Medicham', 'Meditite', 'Meowstic', 'Purrloin', 'Scolipede', 'Sharpedo', 'Smeargle', 'Torchic', 'Trapinch', 'Venipede', 'Whirlipede'], - noChangeForme: true, - noChangeAbility: true, - getEvoFamily: function (species) { - let template = Tools.getTemplate(species); - while (template.prevo) { - template = Tools.getTemplate(template.prevo); - } - return template.speciesid; - }, - validateSet: function (set, teamHas) { - if (!this.format.abilityMap) { - let abilityMap = Object.create(null); - for (let speciesid in this.tools.data.Pokedex) { - let pokemon = this.tools.data.Pokedex[speciesid]; - if (pokemon.num < 1 || pokemon.species in this.format.banlistTable || this.format.bannedDonors.includes(pokemon.species)) continue; - if (this.tools.data.FormatsData[speciesid].requiredItem || this.tools.data.FormatsData[speciesid].requiredMove) continue; - for (let key in pokemon.abilities) { - let abilityId = toId(pokemon.abilities[key]); - if (abilityMap[abilityId]) { - abilityMap[abilityId][pokemon.evos ? 'push' : 'unshift'](speciesid); - } else { - abilityMap[abilityId] = [speciesid]; - } - } - } - this.format.abilityMap = abilityMap; - } - - this.format.noChangeForme = false; - let problems = this.tools.getFormat('Pokemon').onChangeSet.call(this.tools, set, this.format) || []; - this.format.noChangeForme = true; - - if (problems.length) return problems; - - let species = toId(set.species); - let template = this.tools.getTemplate(species); - if (!template.exists) return [`The Pokemon "${set.species}" does not exist.`]; - if (template.isUnreleased) return [`${template.species} is unreleased.`]; - if (template.tier === 'Uber' || template.species in this.format.banlistTable) return [`${template.species} is banned.`]; - - let name = set.name; - - let abilityId = toId(set.ability); - if (!abilityId || !(abilityId in this.tools.data.Abilities)) return [`${name} needs to have a valid ability.`]; - let pokemonWithAbility = this.format.abilityMap[abilityId]; - if (!pokemonWithAbility) return [`"${set.ability}" is not available on a legal Pokemon.`]; - - let canonicalSource = ''; // Specific for the basic implementation of Donor Clause (see onValidateTeam). - let validSources = set.abilitySources = []; // evolutionary families - for (let i = 0; i < pokemonWithAbility.length; i++) { - let donorTemplate = this.tools.getTemplate(pokemonWithAbility[i]); - let evoFamily = this.format.getEvoFamily(donorTemplate); - - if (validSources.indexOf(evoFamily) >= 0) continue; - - if (set.name === set.species) delete set.name; - set.species = donorTemplate.species; - problems = this.validateSet(set, teamHas) || []; - if (!problems.length) { - canonicalSource = donorTemplate.species; - validSources.push(evoFamily); - } - if (validSources.length > 1) { - // Specific for the basic implementation of Donor Clause (see onValidateTeam). - break; - } - } - - set.species = template.species; - if (!validSources.length && pokemonWithAbility.length > 1) { - return [`${template.species}'s set is illegal.`]; - } - if (!validSources.length) { - problems.unshift(`${template.species} has an illegal set with an ability from ${this.tools.getTemplate(pokemonWithAbility[0]).name}.`); - return problems; - } - - // Protocol: Include the data of the donor species in the `ability` data slot. - // Afterwards, we are going to reset the name to what the user intended. :] - set.ability = `${set.ability}0${canonicalSource}`; - }, - onValidateTeam: function (team, format) { - // Donor Clause - let evoFamilyLists = []; - for (let i = 0; i < team.length; i++) { - let set = team[i]; - if (!set.abilitySources) continue; - evoFamilyLists.push(set.abilitySources.map(format.getEvoFamily)); - } - - // Checking actual full incompatibility would require expensive algebra. - // Instead, we only check the trivial case of multiple Pokémon only legal for exactly one family. FIXME? - // This clause has only gotten more complex over time, so this is probably a won't fix. - let requiredFamilies = Object.create(null); - for (let i = 0; i < evoFamilyLists.length; i++) { - let evoFamilies = evoFamilyLists[i]; - if (evoFamilies.length !== 1) continue; - let [familyId] = evoFamilies; - if (!(familyId in requiredFamilies)) requiredFamilies[familyId] = 1; - requiredFamilies[familyId]++; - if (requiredFamilies[familyId] > 2) return [`You are limited to up to two inheritances from each evolution family by the Donor Clause.`, `(You inherit more than twice from ${this.getTemplate(familyId).species}).`]; - } - }, - onBegin: function () { - for (let pokemon of this.p1.pokemon.concat(this.p2.pokemon)) { - if (pokemon.baseAbility.includes('0')) { - let donor = pokemon.baseAbility.split('0')[1]; - pokemon.donor = toId(donor); - pokemon.baseAbility = pokemon.baseAbility.split('0')[0]; - pokemon.ability = pokemon.baseAbility; - } - } - }, - onSwitchIn: function (pokemon) { - if (!pokemon.donor) return; - let donorTemplate = this.getTemplate(pokemon.donor); - if (!donorTemplate.exists) return; - // Place volatiles on the Pokémon to show the donor details. - this.add('-start', pokemon, donorTemplate.species, '[silent]'); - }, + mod: 'fullpotential', + ruleset: ['[Gen 7] OU'], + banlist: ['Pheromosa', 'Shuckle', 'Speed Boost'], }, { - name: "[Gen 7] Mergemons", + name: "[Gen 7] Automagic", desc: [ - "Pokémon gain the movepool of the previous and the next fully evolved Pokémon, according to the Pokédex.", - "• Mergemons", + "Whenever an attack's secondary effect is triggered, any setup moves in that Pokémon's movepool are run.", + "• Automagic", ], - mod: 'mergemons', + mod: 'automagic', searchShow: false, ruleset: ['[Gen 7] OU'], banlist: [], + onAfterSecondaryEffect: function (target, source, move) { + let moreSetup = ['bellydrum']; + if (!source.types.includes("Ghost")) moreSetup.push("curse"); + source.baseMoveset.forEach(curmove => { + let move = this.getMove(curmove.id); + if (moreSetup.includes(move.id) || (move.category === "Status" && move.boosts && move.target === "self")) { + this.useMove(move, source); + curmove.pp = target.hasAbility("pressure") ? (curmove.pp - 2) : (curmove.pp - 1); + } + }); + }, + onAfterMove: function (source, target, move) { + if (move.id !== "genesissupernova") return; + source.baseMoveset.forEach(curmove => { + let move = this.getMove(curmove.id); + if ((move.id === 'bellydrum' || (move.category === "Status" && move.boosts && move.target === "self")) && this.terrain === "psychicterrain") {// This is so that its confirmed that it successfully set PTerrain + this.useMove(move, source); + curmove.pp = target.hasAbility("pressure") ? (curmove.pp - 2) : (curmove.pp - 1); + } + }); + }, }, { section: "Other Metagames", diff --git a/data/aliases.js b/data/aliases.js index ef3d6e6f9c..eeef4ad7e6 100644 --- a/data/aliases.js +++ b/data/aliases.js @@ -15,7 +15,6 @@ exports.BattleAliases = { "bss": "[Gen 7] Battle Spot Singles", "bsdoubles": "[Gen 7] Battle Spot Doubles", "bstriples": "Battle Spot Triples", - "pokebilities": "[Gen 7] Pokébilities", "balancedhackmons": "[Gen 7] Balanced Hackmons", "bh": "[Gen 7] Balanced Hackmons", "1v1": "[Gen 7] 1v1", @@ -37,6 +36,8 @@ exports.BattleAliases = { "monorandom": "[Gen 7] Monotype Random Battle", "bf": "Battle Factory", "inverse": "Inverse Battle", + "fullpotential": "[Gen 7] Full Potential", + "automagic": "[Gen 7] Automagic", // mega evos "maero": "Aerodactyl-Mega", diff --git a/mods/automagic/scripts.js b/mods/automagic/scripts.js new file mode 100644 index 0000000000..180d51eccf --- /dev/null +++ b/mods/automagic/scripts.js @@ -0,0 +1,257 @@ +'use strict'; + +exports.BattleScripts = { + moveHit: function (target, pokemon, move, moveData, isSecondary, isSelf) { + let damage; + move = this.getMoveCopy(move); + + if (!moveData) moveData = move; + if (!moveData.flags) moveData.flags = {}; + let hitResult = true; + + // TryHit events: + // STEP 1: we see if the move will succeed at all: + // - TryHit, TryHitSide, or TryHitField are run on the move, + // depending on move target (these events happen in useMove + // or tryMoveHit, not below) + // == primary hit line == + // Everything after this only happens on the primary hit (not on + // secondary or self-hits) + // STEP 2: we see if anything blocks the move from hitting: + // - TryFieldHit is run on the target + // STEP 3: we see if anything blocks the move from hitting the target: + // - If the move's target is a pokemon, TryHit is run on that pokemon + + // Note: + // If the move target is `foeSide`: + // event target = pokemon 0 on the target side + // If the move target is `allySide` or `all`: + // event target = the move user + // + // This is because events can't accept actual sides or fields as + // targets. Choosing these event targets ensures that the correct + // side or field is hit. + // + // It is the `TryHitField` event handler's responsibility to never + // use `target`. + // It is the `TryFieldHit` event handler's responsibility to read + // move.target and react accordingly. + // An exception is `TryHitSide` as a single event (but not as a normal + // event), which is passed the target side. + + if (move.target === 'all' && !isSelf) { + hitResult = this.singleEvent('TryHitField', moveData, {}, target, pokemon, move); + } else if ((move.target === 'foeSide' || move.target === 'allySide') && !isSelf) { + hitResult = this.singleEvent('TryHitSide', moveData, {}, target.side, pokemon, move); + } else if (target) { + hitResult = this.singleEvent('TryHit', moveData, {}, target, pokemon, move); + } + if (!hitResult) { + if (hitResult === false) this.add('-fail', target); + return false; + } + + if (target && !isSecondary && !isSelf) { + if (move.target !== 'all' && move.target !== 'allySide' && move.target !== 'foeSide') { + hitResult = this.runEvent('TryPrimaryHit', target, pokemon, moveData); + if (hitResult === 0) { + // special Substitute flag + hitResult = true; + target = null; + } + } + } + if (target && isSecondary && !moveData.self) { + hitResult = true; + } + if (!hitResult) { + return false; + } + + if (target) { + let didSomething = false; + + damage = this.getDamage(pokemon, target, moveData); + + // getDamage has several possible return values: + // + // a number: + // means that much damage is dealt (0 damage still counts as dealing + // damage for the purposes of things like Static) + // false: + // gives error message: "But it failed!" and move ends + // null: + // the move ends, with no message (usually, a custom fail message + // was already output by an event handler) + // undefined: + // means no damage is dealt and the move continues + // + // basically, these values have the same meanings as they do for event + // handlers. + + if ((damage || damage === 0) && !target.fainted) { + if (move.noFaint && damage >= 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 && !isSecondary && !isSelf) { + this.add('-fail', target); + } + this.debug('damage calculation interrupted'); + return false; + } + + if (moveData.boosts && !target.fainted) { + hitResult = this.boost(moveData.boosts, target, pokemon, move, isSecondary, isSelf); + didSomething = didSomething || hitResult; + } + if (moveData.heal && !target.fainted) { + let d = target.heal((this.gen < 5 ? Math.floor : 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) { + hitResult = target.trySetStatus(moveData.status, pokemon, moveData.ability ? moveData.ability : move); + if (!hitResult && move.status) return hitResult; + didSomething = didSomething || hitResult; + } + 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) { + if (this.canSwitch(target.side)) didSomething = true; // at least defer the fail message to later + } + if (moveData.selfSwitch) { + if (this.canSwitch(pokemon.side)) 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); + 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 && !moveData.selfdestruct) { + 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) { + let selfRoll; + if (!isSecondary && moveData.self.boosts) selfRoll = this.random(100); + // This is done solely to mimic in-game RNG behaviour. All self drops have a 100% chance of happening but still grab a random number. + if (typeof moveData.self.chance === 'undefined' || selfRoll < moveData.self.chance) { + this.moveHit(pokemon, pokemon, move, moveData.self, isSecondary, true); + } + } + if (moveData.secondaries) { + let secondaryRoll; + let secondaries = this.runEvent('ModifySecondaries', target, pokemon, moveData, moveData.secondaries.slice()); + for (let i = 0; i < secondaries.length; i++) { + secondaryRoll = this.random(100); + if (typeof secondaries[i].chance === 'undefined' || secondaryRoll < secondaries[i].chance) { + // mod for automagic start + let flag = true; + if (moveData.secondary.status) flag = moveData.secondary.status !== target.status; + if (moveData.secondary.volatileStatus) flag = !(moveData.secondary.volatileStatus in target.volatiles); + if (moveData.secondary.volatileStatus === 'flinch') flag = flag && target.activeTurns && !target.moveThisTurn; + this.moveHit(target, pokemon, move, secondaries[i], true, isSelf); + if (moveData.secondary.self && moveData.secondary.self.boosts) { + Object.keys(moveData.secondary.self.boosts).forEach(boost => { + if (pokemon.boosts[boost] === 6) flag = false; + }); + } else { + flag = flag && !(target.hp === undefined || target.hp <= 0); + } + if (moveData.target === 'Normal' && moveData.secondary.boosts) { + let cantLower = { + 'atk': ['clearbody', 'fullmetalbody', 'hypercutter', 'whitesmoke'], + 'def': ['bigpecks', 'clearbody', 'fullmetalbody', 'whitesmoke'], + 'spa': ['clearbody', 'fullmetalbody', 'whitesmoke'], + 'spd': ['clearbody', 'fullmetalbody', 'whitesmoke'], + 'spe': ['clearbody', 'fullmetalbody', 'whitesmoke'], + 'accuracy': ['clearbody', 'fullmetalbody', 'keeneye', 'whitesmoke'], + }; + for (let k in moveData.secondary.boosts) { + if (target.boosts[k] === -6) { + flag = false; + continue; + } + if (moveData.secondary.boosts[k] < 0) { + for (let j = 0; j < cantLower[k].length; j++) { + if (target.hasAbility(cantLower[k][j])) { + flag = false; + break; + } + } + } + } + } + if (pokemon.hasAbility('sheerforce')) flag = false; + if (target.hasAbility('shielddust') && !move.ignoreAbility) { + flag = false; + } + if (flag) this.runEvent('AfterSecondaryEffect', target, pokemon, moveData); + // mod for automagic end + } + } + } + if (target && target.hp > 0 && pokemon.hp > 0 && moveData.forceSwitch && this.canSwitch(target.side)) { + hitResult = this.runEvent('DragOut', target, pokemon, move); + if (hitResult) { + target.forceSwitchFlag = true; + } else if (hitResult === false && move.category === 'Status') { + this.add('-fail', target); + } + } + if (move.selfSwitch && pokemon.hp) { + pokemon.switchFlag = move.selfSwitch; + } + return damage; + }, +}; diff --git a/mods/fullpotential/scripts.js b/mods/fullpotential/scripts.js new file mode 100644 index 0000000000..9663f18898 --- /dev/null +++ b/mods/fullpotential/scripts.js @@ -0,0 +1,146 @@ +'use strict'; + +exports.BattleScripts = { + getDamage : function (pokemon, target, move, suppressMessages) { + if (typeof move === 'string') move = this.getMove(move); + + if (typeof move === 'number') { + move = { + basePower: move, + type: '???', + category: 'Physical', + willCrit: false, + flags: {}, + }; + } + + if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) { + if (!target.runImmunity(move.type, !suppressMessages)) { + return false; + } + } + + if (move.ohko) return target.maxhp; + + if (move.damageCallback) return move.damageCallback.call(this, pokemon, target); + if (move.damage === 'level') return pokemon.level; + if (move.damage) return move.damage; + + if (!move) move = {}; + if (!move.type) move.type = '???'; + let type = move.type; + let category = this.getCategory(move); + let defensiveCategory = move.defensiveCategory || category; + + let basePower = move.basePower; + if (move.basePowerCallback) basePower = move.basePowerCallback.call(this, pokemon, target, move); + if (!basePower) { + if (basePower === 0) return; + return basePower; + } + basePower = this.clampIntRange(basePower, 1); + + let critMult; + let critRatio = this.runEvent('ModifyCritRatio', pokemon, target, move, move.critRatio || 0); + critRatio = this.clampIntRange(critRatio, 0, 4); + critMult = [0, 16, 8, 2, 1]; + + move.crit = move.willCrit || false; + if (move.willCrit === undefined && critRatio) move.crit = (this.random(critMult[critRatio]) === 0); + + if (move.crit) move.crit = this.runEvent('CriticalHit', target, null, move); + + basePower = this.runEvent('BasePower', pokemon, target, move, basePower, true); + + if (!basePower) return 0; + basePower = this.clampIntRange(basePower, 1); + + let level = pokemon.level; + + let attacker = pokemon; + let defender = target; + let statTable = {atk:'Atk', def:'Def', spa:'SpA', spd:'SpD', spe:'Spe'}; + let attackStat, highestStat = 0; + let defenseStat = defensiveCategory === 'Physical' ? 'def' : 'spd'; + for (let i in statTable) { + let stat = attacker.calculateStat(i, attacker.boosts[i]); + stat = this.runEvent('Modify' + statTable[i], attacker, defender, move, stat); + if (stat > highestStat) { + attackStat = i; + highestStat = stat; + } + } + let attack; + let defense; + + let atkBoosts = move.useTargetOffensive ? defender.boosts[attackStat] : attacker.boosts[attackStat]; + let defBoosts = move.useSourceDefensive ? attacker.boosts[defenseStat] : defender.boosts[defenseStat]; + + let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive; + let ignorePositiveDefensive = !!move.ignorePositiveDefensive; + + if (move.crit) { + ignoreNegativeOffensive = true; + ignorePositiveDefensive = true; + } + let ignoreOffensive = !!(move.ignoreOffensive || (ignoreNegativeOffensive && atkBoosts < 0)); + let ignoreDefensive = !!(move.ignoreDefensive || (ignorePositiveDefensive && defBoosts > 0)); + + if (ignoreOffensive) atkBoosts = 0; + if (ignoreDefensive) defBoosts = 0; + + if (move.useTargetOffensive) { + attack = defender.calculateStat(attackStat, atkBoosts); + } else { + attack = attacker.calculateStat(attackStat, atkBoosts); + } + + if (move.useSourceDefensive) { + defense = attacker.calculateStat(defenseStat, defBoosts); + } else { + defense = defender.calculateStat(defenseStat, defBoosts); + } + + attack = this.runEvent('Modify' + statTable[attackStat], attacker, defender, move, attack); + defense = this.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense); + + let baseDamage = Math.floor(Math.floor(Math.floor(2 * level / 5 + 2) * basePower * attack / defense) / 50) + 2; + + baseDamage = this.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); + + if (move.crit) baseDamage = this.modify(baseDamage, move.critModifier || (this.gen >= 6 ? 1.5 : 2)); + + baseDamage = this.randomizer(baseDamage); + + if (move.hasSTAB || pokemon.hasType(type)) baseDamage = this.modify(baseDamage, move.stab || 1.5); + move.typeMod = target.runEffectiveness(move); + + move.typeMod = this.clampIntRange(move.typeMod, -6, 6); + if (move.typeMod > 0) { + if (!suppressMessages) this.add('-supereffective', target); + + for (let i = 0; i < move.typeMod; i++) { + baseDamage *= 2; + } + } + if (move.typeMod < 0) { + if (!suppressMessages) this.add('-resisted', target); + + for (let i = 0; i > move.typeMod; i--) { + baseDamage = Math.floor(baseDamage / 2); + } + } + + if (move.crit && !suppressMessages) this.add('-crit', target); + + if (pokemon.status === 'brn' && basePower && move.category === 'Physical' && move.id !== 'facade' && !pokemon.hasAbility('guts')) { + baseDamage = this.modify(baseDamage, 0.5); + } + + baseDamage = this.runEvent('ModifyDamage', pokemon, target, move, baseDamage); + + if (basePower && !Math.floor(baseDamage)) return 1; + + return Math.floor(baseDamage); + }, +}; diff --git a/mods/mergemons/scripts.js b/mods/mergemons/scripts.js deleted file mode 100644 index c5de1d6097..0000000000 --- a/mods/mergemons/scripts.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -exports.BattleScripts = { - init: function () { - let learnsets = Object.assign({}, this.data.Learnsets); - let dex = []; - for (let i in this.data.Pokedex) { - if (this.data.Pokedex[i].num <= 0) continue; - if (this.data.Pokedex[i].evos) continue; - if (!learnsets[i]) continue; - if (this.data.FormatsData[i].isUnreleased) continue; - if (this.data.FormatsData[i].tier && this.data.FormatsData[i].tier === 'Illegal') continue; - dex.push(i); - } - for (let i = 0; i < dex.length; i++) { - let pokemon = dex[i]; - while (this.data.Pokedex[pokemon].prevo) { - pokemon = this.data.Pokedex[pokemon].prevo; - } - let target = dex[(i === 0 ? dex.length - 1 : i - 1)]; - while (this.data.Pokedex[pokemon].num === this.data.Pokedex[target].num) { - target = dex[dex.indexOf(target) - 1]; - } - let merge = false; - do { - let learnset = learnsets[target].learnset; - for (let move in learnset) { - let source = learnset[move][0].charAt(0) + 'L0'; - if (!(move in learnsets[pokemon].learnset)) this.modData('Learnsets', pokemon).learnset[move] = [source]; - } - if (this.data.Pokedex[target].prevo) { - target = this.data.Pokedex[target].prevo; - } else if (!merge) { - merge = true; - target = dex[(i === dex.length - 1 ? 0 : i + 1)]; - while (this.data.Pokedex[pokemon].num === this.data.Pokedex[target].num) { - target = dex[dex.indexOf(target) + 1]; - } - } else { - target = null; - } - } while (target && learnsets[target]); - } - }, -};