Add new OMs of the Month (#3309)

This commit is contained in:
Spandan Punwatkar 2017-03-02 01:07:35 +05:30 committed by The Immortal
parent 68df2a78ae
commit 6633d606b5
5 changed files with 436 additions and 177 deletions

View File

@ -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.",
"&bullet; <a href=\"https://www.smogon.com/forums/threads/3592844/\">Inheritance</a>",
"A Pok&eacute;mon's highest stat is used when calculating the damage their attacks inflict.",
"&bullet; <a href=\"https://www.smogon.com/forums/threads/3596777/\">Full Potential</a>",
],
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&eacute;mon gain the movepool of the previous and the next fully evolved Pok&eacute;mon, according to the Pok&eacute;dex.",
"&bullet; <a href=\"https://www.smogon.com/forums/threads/3591780/\">Mergemons</a>",
"Whenever an attack's secondary effect is triggered, any setup moves in that Pok&eacute;mon's movepool are run.",
"&bullet; <a href=\"https://www.smogon.com/forums/threads/3594333/\">Automagic</a>",
],
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",

View File

@ -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",

257
mods/automagic/scripts.js Normal file
View File

@ -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;
},
};

View File

@ -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);
},
};

View File

@ -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]);
}
},
};