Overhaul old-gen random team builders

Mainly follow-up for 1deedc595, but also fixes a few bugs.
- BW/GSC/RBY - randomSet: fixed and renamed slot argument.
- BW - randomTeam: crashlogger relative path was set incorrectly.
- BW - randomTeam: PotD Magikarp and Delibird used outdated randomBattleMoves data format.
- RBY - randomTeam: builder was too reject-happy regarding weaknesses (counters prematurely updated).

An improvement of 130% more op/s is observed for RBY randomCCTeam,
with no significative performance regression for randomTeam in any gen.
This commit is contained in:
Ivo Julca 2015-07-16 11:53:21 -05:00
parent 207cb41957
commit baadd0a92f
3 changed files with 228 additions and 178 deletions

View File

@ -942,26 +942,33 @@ exports.BattleScripts = {
var teamdexno = [];
var team = [];
var hasDexNumber = {};
var formes = [[], [], [], [], [], []];
// Pick six random Pokémon, no repeats.
var num;
for (var i = 0; i < 6; i++) {
while (true) {
var x = Math.floor(Math.random() * 151) + 1;
if (teamdexno.indexOf(x) < 0) {
teamdexno.push(x);
break;
}
do {
num = this.random(151) + 1;
} while (num in hasDexNumber);
hasDexNumber[num] = i;
}
var formeCounter = 0;
for (var id in this.data.Pokedex) {
if (!(this.data.Pokedex[id].num in hasDexNumber)) continue;
var template = this.getTemplate(id);
if (!template.learnset || template.forme) continue;
formes[hasDexNumber[template.num]].push(template.species);
if (++formeCounter >= 6) {
// Gen 1 had no alternate formes, so we can break out of the loop already.
break;
}
}
for (var i = 0; i < 6; i++) {
// Choose forme.
var formes = [];
for (var j in this.data.Pokedex) {
if (this.data.Pokedex[j].num === teamdexno[i] && this.getTemplate(this.data.Pokedex[j].species).learnset && this.data.Pokedex[j].species.substr(0, 8) !== 'Pikachu-') {
formes.push(this.data.Pokedex[j].species);
}
}
var poke = formes.sample();
var poke = formes[i][this.random(formes[i].length)];
var template = this.getTemplate(poke);
// Level balance: calculate directly from stats rather than using some silly lookup table.
@ -992,23 +999,16 @@ exports.BattleScripts = {
// Random DVs.
var ivs = {
hp: Math.floor(Math.random() * 30),
atk: Math.floor(Math.random() * 30),
def: Math.floor(Math.random() * 30),
spa: Math.floor(Math.random() * 30),
spd: Math.floor(Math.random() * 30),
spe: Math.floor(Math.random() * 30)
hp: this.random(30),
atk: this.random(30),
def: this.random(30),
spa: this.random(30),
spd: this.random(30),
spe: this.random(30)
};
// All EVs.
var evs = {
hp: 255,
atk: 255,
def: 255,
spa: 255,
spd: 255,
spe: 255
};
// Maxed EVs.
var evs = {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255};
// Four random unique moves from movepool. don't worry about "attacking" or "viable".
// Since Gens 1 and 2 learnsets are shared, we need to weed out Gen 2 moves.
@ -1017,12 +1017,10 @@ exports.BattleScripts = {
for (var move in template.learnset) {
if (this.getMove(move).gen === 1) pool.push(move);
}
if (pool.length > 4) {
moves = pool.sample(4);
} else if (pool.length > 0) {
if (pool.length <= 4) {
moves = pool;
} else {
moves = ['struggle'];
moves = [this.sampleNoReplace(pool), this.sampleNoReplace(pool), this.sampleNoReplace(pool), this.sampleNoReplace(pool)];
}
team.push({
@ -1044,64 +1042,78 @@ exports.BattleScripts = {
// Random team generation for Gen 1 Random Battles.
randomTeam: function (side) {
// Get what we need ready.
var keys = [];
var pokemonLeft = 0;
var pokemon = [];
var i = 1;
// We need to check it's one of the first 151 because formats data are installed onto main format data, not replaced.
for (var n in this.data.FormatsData) {
if (this.data.FormatsData[n].randomBattleMoves && i < 152) {
keys.push(n);
}
i++;
var handicapMons = {'magikarp':1, 'weedle':1, 'kakuna':1, 'caterpie':1, 'metapod':1, 'ditto':1};
var nuTiers = {'UU':1, 'BL':1, 'NFE':1, 'LC':1};
var uuTiers = {'NFE':1, 'UU':1, 'BL':1};
var n = 1;
var pokemonPool = [];
for (var id in this.data.FormatsData) {
// FIXME: Not ES-compliant
if (n > 151 || !this.data.FormatsData[id].randomBattleMoves) continue;
pokemonPool.push(id);
n++;
}
keys = keys.randomize();
// Now let's store what we are getting.
var typeCount = {};
var weaknessCount = {'electric':0, 'psychic':0, 'water':0, 'ice':0};
var weaknessCount = {'Electric':0, 'Psychic':0, 'Water':0, 'Ice':0};
var uberCount = 0;
var nuCount = 0;
var hasShitmon = false;
for (var i = 0; i < keys.length && pokemonLeft < 6; i++) {
var template = this.getTemplate(keys[i]);
if (!template || !template.name || !template.types) continue;
while (pokemonPool.length && pokemonLeft < 6) {
var template = this.getTemplate(this.sampleNoReplace(pokemonPool));
if (!template.exists) continue;
// Bias the tiers so you get less shitmons and only one of the two Ubers.
// If you have a shitmon, you're covered in OUs and Ubers if possible
var tier = template.tier;
if (tier === 'LC' && (nuCount > 1 || hasShitmon)) continue;
if ((tier === 'NFE' || tier === 'UU' || tier === 'NU') && (hasShitmon || (nuCount > 2 && this.random(1)))) continue;
// Unless you have one of the worst mons, in that case we allow luck to give you both Mew and Mewtwo.
if (tier === 'Uber' && uberCount >= 1 && !hasShitmon) continue;
switch (tier) {
case 'LC':
if (nuCount > 1 || hasShitmon) continue;
break;
case 'Uber':
// Unless you have one of the worst mons, in that case we allow luck to give you all Ubers.
if (uberCount >= 1 && !hasShitmon) continue;
break;
default:
if (uuTiers[tier] && (hasShitmon || (nuCount > 2 && this.random(2) >= 1))) continue;
}
// We need a weakness count of spammable attacks to avoid being swept by those.
// Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard.
var skip = false;
Object.keys(weaknessCount).forEach(function (type) {
var notImmune = Tools.getImmunity(type, template);
if (notImmune && Tools.getEffectiveness(type, template) > 0) {
weaknessCount[type]++;
}
if (weaknessCount[type] > 2) skip = true;
});
if (skip) continue;
// Limit 2 of any type as well. Diversity and minor weakness count.
// The second of a same type has halved chance of being added.
var types = template.types;
for (var t = 0; t < types.length; t++) {
if (typeCount[types[t]] > 1 || (typeCount[types[t]] === 1 && this.random(1))) {
if (typeCount[types[t]] > 1 || (typeCount[types[t]] === 1 && this.random(2))) {
skip = true;
break;
}
}
if (skip) continue;
// We need a weakness count of spammable attacks to avoid being swept by those.
// Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard.
var pokemonWeaknesses = [];
for (var type in weaknessCount) {
var increaseCount = Tools.getImmunity(type, template) && Tools.getEffectiveness(type, template) > 0;
if (!increaseCount) continue;
if (weaknessCount[type] >= 2) {
skip = true;
break;
}
pokemonWeaknesses.push(type);
}
if (skip) continue;
// The set passes the limitations.
var set = this.randomSet(template, i);
var set = this.randomSet(template, pokemon.length);
pokemon.push(set);
// Now let's increase the counters. First, the Pokémon left.
@ -1109,34 +1121,38 @@ exports.BattleScripts = {
// Type counter.
for (var t = 0; t < types.length; t++) {
if (types[t] in typeCount) {
if (typeCount[types[t]]) {
typeCount[types[t]]++;
} else {
typeCount[types[t]] = 1;
}
}
// Increment type bias counters.
// Weakness counter.
for (var t = 0; t < pokemonWeaknesses.length; t++) {
weaknessCount[pokemonWeaknesses[t]]++;
}
// Increment tier bias counters.
if (tier === 'Uber') {
uberCount++;
} else if (tier === 'UU' || tier === 'NU' || tier === 'NFE' || tier === 'LC') {
} else if (nuTiers[tier]) {
nuCount++;
}
// Is it Magikarp?
if (keys[i] in {'magikarp':1, 'weedle':1, 'kakuna':1, 'caterpie':1, 'metapod':1, 'ditto':1}) hasShitmon = true;
if (template.speciesid in handicapMons) hasShitmon = true;
}
return pokemon;
},
// Random set generation for Gen 1 Random Battles.
randomSet: function (template, i) {
if (i === undefined) i = 1;
randomSet: function (template, slot) {
if (slot === undefined) slot = 1;
template = this.getTemplate(template);
if (!template.exists) template = this.getTemplate('pikachu'); // Because Gen 1.
var moveKeys = template.randomBattleMoves;
moveKeys = moveKeys.randomize();
var movePool = template.randomBattleMoves.slice();
var moves = [];
var hasType = {};
hasType[template.types[0]] = true;
@ -1145,24 +1161,28 @@ exports.BattleScripts = {
var counter = {};
var setupType = '';
var j = 0;
// Moves that boost Attack:
var PhysicalSetup = {
swordsdance:1, sharpen:1
};
// Moves which boost Special Attack:
var SpecialSetup = {
amnesia:1, growth:1
};
// Add the mandatory move
if (template.essentialMove) {
moves.push(template.essentialMove);
}
do {
// Choose next 4 moves from learnset/viable moves and add them to moves list:
var howMany = (template.essentialMove) ? 3 : 4;
while (moves.length < howMany && j < moveKeys.length) {
var moveid = toId(moveKeys[j]);
j++;
while (moves.length < 4 && movePool.length) {
var moveid = this.sampleNoReplace(movePool);
moves.push(moveid);
}
// Add now the mandatory move
if (template.essentialMove) {
moves.unshift(template.essentialMove);
j++;
}
// Only do move choosing if we have more than four on the moveset...
if (moveKeys.length > howMany) {
// Only do move choosing if we have backup moves in the pool...
if (movePool.length) {
hasMove = {};
counter = {Physical: 0, Special: 0, Status: 0, physicalsetup: 0, specialsetup: 0};
for (var k = 0; k < moves.length; k++) {
@ -1172,10 +1192,10 @@ exports.BattleScripts = {
if (!move.damage && !move.damageCallback) {
counter[move.category]++;
}
if ({swordsdance:1, sharpen:1}[moveid]) {
if (PhysicalSetup[moveid]) {
counter['physicalsetup']++;
}
if ({amnesia:1, growth:1}[moveid]) {
if (SpecialSetup[moveid]) {
counter['specialsetup']++;
}
}
@ -1188,6 +1208,7 @@ exports.BattleScripts = {
for (var k = 0; k < moves.length; k++) {
var moveid = moves[k];
if (moveid === template.essentialMove) continue;
var move = this.getMove(moveid);
var rejected = false;
if (hasMove[moveid]) rejected = true;
@ -1300,14 +1321,14 @@ exports.BattleScripts = {
break;
} // End of switch for moveid
}
if (rejected && j < moveKeys.length) {
if (rejected) {
moves.splice(k, 1);
break;
}
counter[move.category]++;
} // End of for
} // End of the check for more than 4 moves on moveset.
} while (moves.length < 4 && j < moveKeys.length);
} while (moves.length < 4 && movePool.length);
var levelScale = {
LC: 96,

View File

@ -520,47 +520,54 @@ exports.BattleScripts = {
return damage;
},
// Random team generation for Gen 2 Random Battles.
randomTeam: function (side) {
// Get what we need ready.
var keys = [];
var pokemonLeft = 0;
var pokemon = [];
var i = 1;
// We need to check it's one of the first 251 because formats data are installed onto main format data, not replaced.
for (var n in this.data.FormatsData) {
if (this.data.FormatsData[n].randomBattleMoves && i < 252) {
keys.push(n);
}
i++;
var handicapMons = {'magikarp':1, 'weedle':1, 'kakuna':1, 'caterpie':1, 'metapod':1, 'ditto':1};
var nuTiers = {'UU':1, 'BL':1, 'NFE':1, 'LC':1};
var uuTiers = {'NFE':1, 'UU':1, 'BL':1};
var n = 1;
var pokemonPool = [];
for (var id in this.data.FormatsData) {
// FIXME: Not ES-compliant
if (n > 251 || !this.data.FormatsData[id].randomBattleMoves) continue;
pokemonPool.push(id);
n++;
}
keys = keys.randomize();
// Now let's store what we are getting.
// Setup storage.
var typeCount = {};
var uberCount = 0;
var nuCount = 0;
var hasShitmon = false;
for (var i = 0; i < keys.length && pokemonLeft < 6; i++) {
var template = this.getTemplate(keys[i]);
if (!template || !template.name || !template.types) continue;
while (pokemonPool.length && pokemonLeft < 6) {
var template = this.getTemplate(this.sampleNoReplace(pokemonPool));
if (!template.exists) continue;
// Bias the tiers so you get less shitmons and only one of the two Ubers.
// If you have a shitmon, you're covered in OUs and Ubers if possible
var tier = template.tier;
if (tier === 'LC' && (nuCount > 1 || hasShitmon)) continue;
if ((tier === 'NFE' || tier === 'UU' || tier === 'BL') && (hasShitmon || (nuCount > 2 && this.random(1)))) continue;
// Unless you have one of the worst mons, in that case we allow luck to give you all Ubers.
if (tier === 'Uber' && uberCount >= 1 && !hasShitmon) continue;
switch (tier) {
case 'LC':
if (nuCount > 1 || hasShitmon) continue;
break;
case 'Uber':
// Unless you have one of the worst mons, in that case we allow luck to give you all Ubers.
if (uberCount >= 1 && !hasShitmon) continue;
break;
default:
if (uuTiers[tier] && (hasShitmon || (nuCount > 2 && this.random(2) >= 1))) continue;
}
// Limit 2 of any type as well. Diversity and minor weakness count.
// Limit 2 of any type. Diversity and minor weakness count.
// The second of a same type has halved chance of being added.
var types = template.types;
var skip = false;
for (var t = 0; t < types.length; t++) {
if (typeCount[types[t]] > 1 || (typeCount[types[t]] === 1 && this.random(1))) {
if (typeCount[types[t]] > 1 || (typeCount[types[t]] === 1 && this.random(2) >= 1)) {
skip = true;
break;
}
@ -568,7 +575,7 @@ exports.BattleScripts = {
if (skip) continue;
// The set passes the limitations.
var set = this.randomSet(template, i);
var set = this.randomSet(template, pokemon.length);
pokemon.push(set);
// Now let's increase the counters. First, the Pokémon left.
@ -576,7 +583,7 @@ exports.BattleScripts = {
// Type counter.
for (var t = 0; t < types.length; t++) {
if (types[t] in typeCount) {
if (typeCount[types[t]]) {
typeCount[types[t]]++;
} else {
typeCount[types[t]] = 1;
@ -586,24 +593,23 @@ exports.BattleScripts = {
// Increment type bias counters.
if (tier === 'Uber') {
uberCount++;
} else if (tier === 'UU' || tier === 'BL' || tier === 'NFE' || tier === 'LC') {
} else if (nuTiers[tier]) {
nuCount++;
}
// Is it a shitmon?
if (keys[i] in {'magikarp':1, 'weedle':1, 'kakuna':1, 'caterpie':1, 'metapod':1, 'ditto':1}) hasShitmon = true;
if (handicapMons[template.speciesid]) hasShitmon = true;
}
return pokemon;
},
// Random set generation for Gen 2 Random Battles.
randomSet: function (template, i) {
if (i === undefined) i = 1;
randomSet: function (template, slot) {
if (slot === undefined) slot = 1;
template = this.getTemplate(template);
if (!template.exists) template = this.getTemplate('unown');
var moveKeys = template.randomBattleMoves;
moveKeys = moveKeys.randomize();
var movePool = template.randomBattleMoves.slice();
var moves = [];
var hasType = {};
hasType[template.types[0]] = true;
@ -614,7 +620,15 @@ exports.BattleScripts = {
var item = 'leftovers';
var ivs = {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30};
var j = 0;
// Moves that boost Attack:
var PhysicalSetup = {
swordsdance:1, sharpen:1
};
// Moves which boost Special Attack:
var SpecialSetup = {
amnesia:1, growth:1
};
do {
// Keep track of all moves we have:
hasMove = {};
@ -627,8 +641,8 @@ exports.BattleScripts = {
}
// Choose next 4 moves from learnset/viable moves and add them to moves list:
while (moves.length < 4 && moveKeys.length) {
var moveid = this.sampleNoReplace(moveKeys);
while (moves.length < 4 && movePool.length) {
var moveid = this.sampleNoReplace(movePool);
if (moveid.substr(0, 11) === 'hiddenpower') {
if (hasMove['hiddenpower']) continue;
hasMove['hiddenpower'] = true;
@ -645,10 +659,10 @@ exports.BattleScripts = {
if (!move.damage && !move.damageCallback) {
counter[move.category]++;
}
if ({swordsdance:1, sharpen:1}[moveid]) {
if (PhysicalSetup[moveid]) {
counter['physicalsetup']++;
}
if ({amnesia:1, growth:1}[moveid]) {
if (SpecialSetup[moveid]) {
counter['specialsetup']++;
}
}
@ -774,13 +788,13 @@ exports.BattleScripts = {
break;
} // End of switch for moveid
}
if (rejected && j < moveKeys.length - 1) {
if (rejected && movePool.length) {
moves.splice(k, 1);
break;
}
counter[move.category]++;
} // End of for
} while (moves.length < 4 && j < moveKeys.length);
} while (moves.length < 4 && movePool.length);
// Add specific items.
switch (template.species) {

View File

@ -1,7 +1,7 @@
exports.BattleScripts = {
gen: 5,
randomSet: function (template, i) {
if (i === undefined) i = 1;
randomSet: function (template, slot) {
if (slot === undefined) slot = 1;
template = this.getTemplate(template);
var name = template.name;
@ -11,10 +11,10 @@ exports.BattleScripts = {
var stack = 'Template incompatible with random battles: ' + name;
var fakeErr = {stack: stack};
require('../crashlogger.js')(fakeErr, 'The randbat set generator');
require('./../../crashlogger.js')(fakeErr, 'The randbat set generator');
}
var moveKeys = (template.randomBattleMoves || Object.keys(template.learnset)).randomize();
var movePool = (template.randomBattleMoves ? template.randomBattleMoves.slice() : Object.keys(template.learnset));
var moves = [];
var ability = '';
var item = '';
@ -49,12 +49,10 @@ exports.BattleScripts = {
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 < moveKeys.length) {
var moveid = toId(moveKeys[j]);
j++;
while (moves.length < 4 && movePool.length) {
var moveid = this.sampleNoReplace(movePool);
if (moveid.substr(0, 11) === 'hiddenpower') {
if (!hasMove['hiddenpower']) {
hasMove['hiddenpower'] = true;
@ -404,7 +402,7 @@ exports.BattleScripts = {
}
// Remove rejected moves from the move list.
if (rejected && j < moveKeys.length) {
if (rejected && movePool.length) {
moves.splice(k, 1);
break;
}
@ -417,7 +415,7 @@ exports.BattleScripts = {
}
}
}
if (j < moveKeys.length && moves.length === 4) {
if (movePool.length && moves.length === 4) {
// Move post-processing:
if (damagingMoves.length === 0) {
// Have a 60% chance of rejecting one move at random:
@ -475,7 +473,7 @@ exports.BattleScripts = {
if (!isStab) moves.splice(Math.floor(Math.random() * moves.length), 1);
}
}
} while (moves.length < 4 && j < moveKeys.length);
} while (moves.length < 4 && movePool.length);
// any moveset modification goes here
//moves[0] = 'Safeguard';
@ -666,14 +664,13 @@ exports.BattleScripts = {
item = 'Life Orb';
} else if (ability === 'Unburden' && (counter['Physical'] || counter['Special'])) {
// Give Unburden mons a random Gem of the type of one of their damaging moves
var shuffledMoves = moves.randomize();
for (var m in shuffledMoves) {
var move = this.getMove(shuffledMoves[m]);
if (move.basePower || move.basePowerCallback) {
item = move.type + ' Gem';
break;
}
var eligibleTypes = [];
for (var i = 0; i < moves.length; i++) {
var move = this.getMove(moves[i]);
if (!move.basePower && !move.basePowerCallback) continue;
eligibleTypes.push(move.type);
}
item = eligibleTypes[this.random(eligibleTypes.length)] + ' Gem';
} else if (ability === 'Guts') {
if (hasMove['drainpunch']) {
item = 'Flame Orb';
@ -711,14 +708,13 @@ exports.BattleScripts = {
} else if ((hasMove['eruption'] || hasMove['waterspout']) && !counter['Status']) {
item = 'Choice Scarf';
} else if (hasMove['substitute'] && hasMove['reversal']) {
var shuffledMoves = moves.randomize();
for (var m in shuffledMoves) {
var move = this.getMove(shuffledMoves[m]);
if (move.basePower || move.basePowerCallback) {
item = move.type + ' Gem';
break;
}
var eligibleTypes = [];
for (var i = 0; i < moves.length; i++) {
var move = this.getMove(moves[i]);
if (!move.basePower && !move.basePowerCallback) continue;
eligibleTypes.push(move.type);
}
item = eligibleTypes[this.random(eligibleTypes.length)] + ' Gem';
} else if (hasMove['substitute'] || hasMove['detect'] || hasMove['protect'] || ability === 'Moody') {
item = 'Leftovers';
} else if ((hasMove['flail'] || hasMove['reversal']) && !hasMove['endure'] && ability !== 'Sturdy') {
@ -735,7 +731,7 @@ exports.BattleScripts = {
item = 'Life Orb';
} else if (counter.Physical + counter.Special >= 4) {
item = 'Expert Belt';
} else if (i === 0 && ability !== 'Sturdy' && !counter['recoil']) {
} else if (slot === 0 && ability !== 'Sturdy' && !counter['recoil']) {
item = 'Focus Sash';
} else if (hasMove['outrage']) {
item = 'Lum Berry';
@ -813,19 +809,19 @@ exports.BattleScripts = {
};
},
randomTeam: function (side) {
var keys = [];
var pokemonLeft = 0;
var pokemon = [];
for (var i in this.data.FormatsData) {
if (this.data.FormatsData[i].randomBattleMoves && this.getTemplate(i).gen < 6) {
keys.push(i);
}
var pokemonPool = [];
for (var id in this.data.FormatsData) {
var template = this.getTemplate(id);
if (template.gen >= this.gen || !template.randomBattleMoves) continue;
pokemonPool.push(id);
}
keys = keys.randomize();
// PotD stuff
var potd = {};
if ('Rule:potd' in this.getBanlistTable(this.getFormat())) {
var potd;
if (Config.potd && 'Rule:potd' in this.getBanlistTable(this.getFormat())) {
potd = this.getTemplate(Config.potd);
}
@ -834,52 +830,68 @@ exports.BattleScripts = {
var uberCount = 0;
var nuCount = 0;
for (var i = 0; i < keys.length && pokemonLeft < 6; i++) {
var template = this.getTemplate(keys[i]);
if (!template || !template.name || !template.types) continue;
while (pokemonPool.length && pokemonLeft < 6) {
var template = this.getTemplate(this.sampleNoReplace(pokemonPool));
if (!template.exists) continue;
// Not available on BW
if (template.species === 'Pichu-Spiky-eared') continue;
var tier = template.tier;
// This tries to limit the amount of Ubers and NUs on one team to promote "fun":
// LC Pokemon have a hard limit in place at 2; NFEs/NUs/Ubers are also limited to 2 but have a 20% chance of being added anyway.
// LC/NFE/NU Pokemon all share a counter (so having one of each would make the counter 3), while Ubers have a counter of their own.
if (tier === 'LC' && nuCount > 1) continue;
if ((tier === 'NFE' || tier === 'NU') && nuCount > 1 && Math.random() * 5 > 1) continue;
if (tier === 'Uber' && uberCount > 1 && Math.random() * 5 > 1) continue;
switch (tier) {
case 'LC':
if (nuCount > 1) continue;
break;
case 'NFE': case 'NU':
if (nuCount > 1 && this.random(5) >= 1) continue;
break;
case 'Uber':
if (uberCount > 1 && this.random(5) >= 1) continue;
break;
case 'CAP':
// CAPs have 20% the normal rate
if (this.random(5) >= 1) continue;
}
// CAPs have 20% the normal rate
if (tier === 'CAP' && Math.random() * 5 > 1) continue;
// Arceus formes have 1/17 the normal rate each (so Arceus as a whole has a normal rate)
if (keys[i].substr(0, 6) === 'arceus' && Math.random() * 17 > 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;
// Not available on BW
if (template.species === 'Pichu-Spiky-eared') continue;
// Adjust rate for species with multiple formes
switch (template.baseSpecies) {
case 'Arceus':
if (this.random(17) >= 1) continue;
break;
case 'Basculin':
if (this.random(2) >= 1) continue;
break;
}
// Limit 2 of any type
var types = template.types;
var skip = false;
for (var t = 0; t < types.length; t++) {
if (typeCount[types[t]] > 1 && Math.random() * 5 > 1) {
if (typeCount[types[t]] > 1 && this.random(5) >= 1) {
skip = true;
break;
}
}
if (skip) continue;
if (potd && potd.name && potd.types) {
if (potd && potd.exists) {
// The Pokemon of the Day belongs in slot 2
if (i === 1) {
if (pokemon.length === 1) {
template = potd;
if (template.species === 'Magikarp') {
template.randomBattleMoves = {magikarpsrevenge:1, splash:1, bounce:1};
template.randomBattleMoves = ['magikarpsrevenge', 'splash', 'bounce'];
} else if (template.species === 'Delibird') {
template.randomBattleMoves = {present:1, bestow:1};
template.randomBattleMoves = ['present', 'bestow'];
}
} else if (template.species === potd.species) {
continue; // No, thanks, I've already got one
}
}
var set = this.randomSet(template, i);
var set = this.randomSet(template, pokemon.length);
// Limit 1 of any type combination
var typeCombo = types.join();
@ -892,8 +904,10 @@ exports.BattleScripts = {
// Okay, the set passes, add it to our team
pokemon.push(set);
// Now that our Pokemon has passed all checks, we can increment our counters
pokemonLeft++;
// Now that our Pokemon has passed all checks, we can increment the type counter
// Increment type counters
for (var t = 0; t < types.length; t++) {
if (types[t] in typeCount) {
typeCount[types[t]]++;
@ -902,7 +916,8 @@ exports.BattleScripts = {
}
}
typeComboCount[typeCombo] = 1;
// Increment Uber/NU counter
// Increment Uber/NU counters
if (tier === 'Uber') {
uberCount++;
} else if (tier === 'NU' || tier === 'NFE' || tier === 'LC') {