y\'know strawberries have more potassium than bananas
';
buf += '
that doesn\'t have anything to do with teambuilding!
';
buf += '
fiiiiiiine press Ctrl+F to find teams by pokemon name
';
buf += this.clipboardHTML();
buf += '
';
if (!window.localStorage && !window.nodewebkit) buf += '
== CAN\'T SAVE == Your browser doesn\'t support localStorage and can\'t save teams! Update to a newer browser.
';
if (Storage.cantSave) buf += '
== CAN\'T SAVE == You hit your browser\'s limit for team storage! Please backup them and delete some of them. Your teams won\'t be saved until you\'re under the limit again.
';
if (!teams.length) {
if (this.deletedTeamLoc >= 0) {
buf += '';
}
buf += '
you don\'t have any teams lol
';
} else {
buf += '';
for (var i=0; i';
}
if (i >= teams.length) break;
var team = teams[i];
if (team && !team.team && team.team !== '') {
team = null;
}
if (!team) {
buf += '
Error: A corrupted team was dropped
';
teams.splice(i,1);
i--;
if (this.deletedTeamLoc && this.deletedTeamLoc > i) this.deletedTeamLoc--;
continue;
}
var formatText = '';
if (team.format) {
formatText = '['+team.format+'] ';
}
buf += '
';
return buf;
},
clipboardInnerHTMLCache: '',
clipboardInnerHTML: function() {
if (this.clipboardInnerHTMLCache) {
return this.clipboardInnerHTMLCache;
}
var buf = '';
for (var i = 0; i < this.clipboardCount(); i++) {
var res = this.clipboard[i];
var pokemon = Tools.getTemplate(res.species);
buf += '
';
this.$el.html(buf);
this.$chart = this.$('.teambuilder-results');
},
updateSetTop: function() {
this.$('.teambar').html(this.renderTeambar());
this.$('.teamchart').first().html(this.renderSet(this.curSet, this.curSetLoc));
},
renderTeambar: function() {
var buf = '';
var isAdd = false;
if (this.curSetList.length && !this.curSetList[this.curSetList.length-1].name && this.curSetLoc !== this.curSetList.length-1) {
this.curSetList.splice(this.curSetList.length-1, 1);
}
for (var i=0; i';
if (!set.name) {
buf += ' ';
isAdd = true;
} else if (i == this.curSetLoc) {
buf += ' ';
} else {
buf += ' ';
}
}
if (this.curSetList.length < 6 && !isAdd) {
buf += ' ';
}
return buf;
},
updatePokemonSprite: function() {
var set = this.curSet;
if (!set) return;
var shiny = (set.shiny ? '-shiny' : '');
var sprite = Tools.getTemplate(set.species).spriteid;
if (BattlePokemonSprites && BattlePokemonSprites[sprite] && BattlePokemonSprites[sprite].front && BattlePokemonSprites[sprite].front.anif && set.gender === 'F') {
sprite += '-f';
}
this.$('.setcol-icon').css('background-image', Tools.getTeambuilderSprite(set).substr(17));
this.$('.pokemonicon-'+this.curSetLoc).css('background', Tools.getIcon(set).substr(11));
var item = Tools.getItem(set.item);
if (item.id) {
this.$('.setcol-icon .itemicon').css('background', Tools.getItemIcon(item).substr(11));
} else {
this.$('.setcol-icon .itemicon').css('background', 'none');
}
this.updateStatGraph();
},
updateStatGraph: function() {
var set = this.curSet;
if (!set) return;
var stats = {hp:'',atk:'',def:'',spa:'',spd:'',spe:''};
// stat cell
var buf = 'EV';
for (var stat in stats) {
stats[stat] = this.getStat(stat, set);
var ev = ''+(set.evs[stat] || '')+'';
if (BattleNatures[set.nature] && BattleNatures[set.nature].plus === stat) {
ev += '+';
}
else if (BattleNatures[set.nature] && BattleNatures[set.nature].minus === stat) {
ev += '−';
}
var width = stats[stat]*75/504;
if (stat == 'hp') width = stats[stat]*75/704;
if (width > 75) width = 75;
var color = Math.floor(stats[stat]*180/714);
if (color > 360) color = 360;
buf += ' '+ev+'';
}
this.$('button[name=stats]').html(buf);
if (this.curChartType !== 'stats') return;
buf = '';
for (var stat in stats) {
buf += '
'+stats[stat]+'
';
}
this.$chart.find('.statscol').html(buf);
buf = '';
var totalev = 0;
for (var stat in stats) {
var width = stats[stat]*180/504;
if (stat == 'hp') width = stats[stat]*180/704;
if (width > 179) width = 179;
var color = Math.floor(stats[stat]*180/714);
if (color > 360) color = 360;
buf += '
';
totalev += (set.evs[stat]||0);
}
buf += '
Remaining:
';
this.$chart.find('.graphcol').html(buf);
if (totalev <= 510) {
this.$chart.find('.totalev').html(''+(totalev>508?0:508-totalev)+'');
} else {
this.$chart.find('.totalev').html(''+(510-totalev)+'');
}
this.$chart.find('select[name=nature]').val(set.nature||'Serious');
},
curChartType: '',
curChartName: '',
updateChart: function() {
var type = this.curChartType;
app.clearGlobalListeners();
if (type === 'stats') {
this.updateStatForm();
return;
}
if (type === 'details') {
this.updateDetailsForm();
return;
}
// cache movelist ref
var speciesid = toId(this.curSet.species);
var g6 = (this.curTeam.format && (this.curTeam.format === 'vgc2014' || this.curTeam.format === 'vgc2015'));
this.applyMovelist(g6, speciesid);
this.$chart.html('Loading '+this.curChartType+'...');
var self = this;
if (this.updateChartTimeout) clearTimeout(this.updateChartTimeout);
this.updateChartTimeout = setTimeout(function() {
self.updateChartTimeout = null;
if (self.curChartType === 'stats' || self.curChartType === 'details' || !self.curChartName) return;
self.$chart.html(Chart.chart(self.$('input[name='+self.curChartName+']').val(), self.curChartType, true, _.bind(self.arrangeCallback[self.curChartType], self)));
}, 10);
},
updateChartTimeout: null,
updateChartDelayed: function() {
// cache movelist ref
var speciesid = toId(this.curSet.species);
var g6 = (this.curTeam.format && (this.curTeam.format === 'vgc2014' || this.curTeam.format === 'vgc2015'));
this.applyMovelist(g6, speciesid);
var self = this;
if (this.updateChartTimeout) clearTimeout(this.updateChartTimeout);
this.updateChartTimeout = setTimeout(function() {
self.updateChartTimeout = null;
if (self.curChartType === 'stats' || self.curChartType === 'details') return;
self.$chart.html(Chart.chart(self.$('input[name='+self.curChartName+']').val(), self.curChartType, false, _.bind(self.arrangeCallback[self.curChartType], self)));
}, 200);
},
selectPokemon: function(i) {
i = +i;
var set = this.curSetList[i];
if (set) {
this.curSet = set;
this.curSetLoc = i;
var name = this.curChartName || 'details';
if (name === 'details' || name === 'stats') {
this.update();
this.updateChart();
} else {
this.curChartName = '';
this.update();
this.$('input[name='+name+']').select();
}
}
},
stats: function(i, button) {
if (!this.curSet) this.selectPokemon($(button).closest('li').val());
this.curChartName = 'stats';
this.curChartType = 'stats';
this.updateStatForm();
},
details: function(i, button) {
if (!this.curSet) this.selectPokemon($(button).closest('li').val());
this.curChartName = 'details';
this.curChartType = 'details';
this.updateDetailsForm();
},
/*********************************************************
* Set stat form
*********************************************************/
plus: '',
minus: '',
updateStatForm: function(setGuessed) {
var buf = '';
var set = this.curSet;
var baseStats = Tools.getTemplate(this.curSet.species).baseStats;
buf += '
EVs
';
buf += '
';
var role = this.guessRole();
var guessedEVs = {};
var guessedPlus = '';
var guessedMinus = '';
buf += '
Suggested spread:';
if (role === '?') {
buf += ' (Please choose 4 moves to get a suggested spread)
';
for (var i in stats) {
stats[i] = this.getStat(i);
var width = stats[i]*180/504;
if (i=='hp') width = Math.floor(stats[i]*180/704);
if (width > 179) width = 179;
var color = Math.floor(stats[i]*180/714);
if (color>360) color = 360;
buf += '
';
}
buf += '
Remaining:
';
buf += '
';
buf += '
EVs
';
var totalev = 0;
this.plus = '';
this.minus = '';
for (var i in stats) {
var width = stats[i]*200/504;
if (i=='hp') width = stats[i]*200/704;
if (width > 200) width = 200;
var val = ''+(set.evs[i]||'');
if (nature.plus === i) {
val += '+';
this.plus = i;
}
if (nature.minus === i) {
val += '-';
this.minus = i;
}
buf += '';
totalev += (set.evs[i]||0);
}
if (totalev <= 510) {
buf += '
'+(totalev>508?0:508-totalev)+'
';
} else {
buf += '
'+(510-totalev)+'
';
}
buf += '
';
buf += '
';
for (var i in stats) {
buf += '';
}
buf += '
';
buf += '
IVs
';
var totalev = 0;
if (!set.ivs) set.ivs = {};
for (var i in stats) {
if (typeof set.ivs[i] === 'undefined' || isNaN(set.ivs[i])) set.ivs[i] = 31;
var val = ''+(set.ivs[i]);
buf += '';
}
buf += '
';
buf += '
';
for (var i in stats) {
buf += '
'+stats[i]+'
';
}
buf += '
';
buf += '
Nature:
';
buf += '
Protip: You can also set natures by typing "+" and "-" next to a stat.
';
buf += '
';
this.$chart.html(buf);
var self = this;
this.suppressSliderCallback = true;
app.clearGlobalListeners();
this.$chart.find('.evslider').slider({
from: 0,
to: 252,
step: 4,
skin: 'round_plastic',
onstatechange: function(val) {
if (!self.suppressSliderCallback) self.statSlide(val, this);
},
callback: function() {
self.save();
}
});
this.suppressSliderCallback = false;
},
setStatFormGuesses: function() {
this.updateStatForm(true);
},
setSlider: function(stat, val) {
this.suppressSliderCallback = true;
this.$chart.find('input[name=evslider-'+stat+']').slider('value', val||0);
this.suppressSliderCallback = false;
},
updateNature: function() {
var set = this.curSet;
if (!set) return;
if (this.plus === '' || this.minus === '') {
set.nature = 'Serious';
} else {
for (var i in BattleNatures) {
if (BattleNatures[i].plus === this.plus && BattleNatures[i].minus === this.minus) {
set.nature = i;
break;
}
}
}
},
statChange: function(e) {
var inputName = '';
inputName = e.currentTarget.name;
var val = Math.abs(parseInt(e.currentTarget.value, 10));
var set = this.curSet;
if (!set) return;
if (inputName.substr(0,5) === 'stat-') {
// EV
// Handle + and -
var stat = inputName.substr(5);
if (!set.evs) set.evs = {};
var lastchar = e.currentTarget.value.charAt(e.target.value.length-1);
var firstchar = e.currentTarget.value.charAt(0);
var natureChange = true;
if ((lastchar === '+' || firstchar === '+') && stat !== 'hp') {
if (this.plus && this.plus !== stat) this.$chart.find('input[name=stat-'+this.plus+']').val(set.evs[this.plus]||'');
this.plus = stat;
} else if ((lastchar === '-' || lastchar === "\u2212" || firstchar === '-' || firstchar === "\u2212") && stat !== 'hp') {
if (this.minus && this.minus !== stat) this.$chart.find('input[name=stat-'+this.minus+']').val(set.evs[this.minus]||'');
this.minus = stat;
} else if (this.plus === stat) {
this.plus = '';
} else if (this.minus === stat) {
this.minus = '';
} else {
natureChange = false;
}
if (natureChange) {
this.updateNature();
}
// cap
if (val > 252) val = 252;
if (val < 0) val = 0;
if (set.evs[stat] !== val || natureChange) {
set.evs[stat] = val;
this.setSlider(stat, val);
this.updateStatGraph();
}
} else {
// IV
var stat = inputName.substr(3);
if (val > 31 || isNaN(val)) val = 31;
if (val < 0) val = 0;
if (!set.ivs) set.ivs = {};
if (set.ivs[stat] !== val) {
set.ivs[stat] = val;
this.updateStatGraph();
}
}
this.save();
},
statSlide: function(val, slider) {
var stat = slider.inputNode[0].name.substr(9);
var set = this.curSet;
if (!set) return;
val = +val;
if (!this.ignoreEVLimits && set.evs) {
var total = 0;
for (var i in set.evs) {
total += (i===stat?val:set.evs[i]);
}
if (total > 508 && val - total + 508 >= 0) {
// don't allow dragging beyond 508 EVs
val = val - total + 508;
// make sure val is a legal value
val = +val;
if (!val || val <= 0) val = 0;
if (val > 252) val = 252;
// Don't try this at home.
// I am a trained professional.
slider.o.pointers[0].set(val);
}
}
if (!set.evs) set.evs = {};
set.evs[stat] = val;
val = ''+(val||'')+(this.plus===stat?'+':'')+(this.minus===stat?'-':'');
this.$('input[name=stat-'+stat+']').val(val);
this.updateStatGraph();
},
natureChange: function(e) {
var set = this.curSet;
if (!set) return;
if (e) {
set.nature = e.currentTarget.value;
}
this.plus = '';
this.minus = '';
var nature = BattleNatures[set.nature||'Serious'];
for (var i in BattleStatNames) {
var val = ''+(set.evs[i]||'');
if (nature.plus === i) {
this.plus = i;
val += '+';
}
if (nature.minus === i) {
this.minus = i;
val += '-';
}
this.$chart.find('input[name=stat-'+i+']').val(val);
if (!e) this.setSlider(i, set.evs[i]);
}
this.save();
this.updateStatGraph();
},
/*********************************************************
* Set details form
*********************************************************/
updateDetailsForm: function() {
var buf = '';
var set = this.curSet;
var template = Tools.getTemplate(set.species);
if (!set) return;
buf += '
Details
';
buf += '';
this.$chart.html(buf);
},
detailsChange: function() {
var set = this.curSet;
if (!set) return;
// level
var level = parseInt(this.$chart.find('input[name=level]').val(),10);
if (!level || level > 100 || level < 1) level = 100;
if (level !== 100 || set.level) set.level = level;
// happiness
var happiness = parseInt(this.$chart.find('input[name=happiness]').val(),10);
if (happiness > 255) happiness = 255;
if (happiness < 0) happiness = 255;
set.happiness = happiness;
if (set.happiness === 255) delete set.happiness;
// shiny
var shiny = (this.$chart.find('input[name=shiny]:checked').val() === 'yes');
if (shiny) {
set.shiny = true;
} else {
delete set.shiny;
}
var gender = this.$chart.find('input[name=gender]:checked').val();
if (gender === 'M' || gender === 'F') {
set.gender = gender;
} else {
delete set.gender;
}
// update details cell
var buf = '';
var GenderChart = {
'M': 'Male',
'F': 'Female',
'N': '—'
};
buf += ''+(set.level||100)+'';
buf += ''+GenderChart[set.gender||'N']+'';
buf += ''+(typeof set.happiness==='number'?set.happiness:255)+'';
buf += ''+(set.shiny?'Yes':'No')+'';
this.$('button[name=details]').html(buf);
this.save();
this.updatePokemonSprite();
},
/*********************************************************
* Set charts
*********************************************************/
arrangeCallback: {
pokemon: function(pokemon) {
if (!pokemon) {
if (this.curTeam) {
if (this.curTeam.format === 'ubers' || this.curTeam.format === 'anythinggoes') return ['Uber','OU','BL','UU','BL2','RU','BL3','NU','BL4','PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'ou') return ['OU','BL','UU','BL2','RU','BL3','NU','BL4','PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'cap') return ['CAP','OU','BL','UU','BL2','RU','BL3','NU','BL4','PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'uu') return ['UU','BL2','RU','BL3','NU','BL4','PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'ru') return ['RU','BL3','NU','BL4','PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'nu') return ['NU','BL4','PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'pu') return ['PU','NFE','LC Uber','LC'];
if (this.curTeam.format === 'lc') return ['LC'];
}
return ['OU','Uber','BL','UU','BL2','RU','BL3','NU','BL4','PU','NFE','LC Uber','LC','Unreleased','CAP'];
}
var tierData = exports.BattleFormatsData[toId(pokemon.species)];
if (!tierData) return 'Illegal';
return tierData.tier;
},
item: function(item) {
if (!item) return ['Items'];
return 'Items';
},
ability: function(ability) {
if (!this.curSet) return;
if (!ability) return ['Abilities', 'Hidden Ability'];
var template = Tools.getTemplate(this.curSet.species);
if (!template.abilities) return 'Abilities';
if (ability.name === template.abilities['0']) return 'Abilities';
if (ability.name === template.abilities['1']) return 'Abilities';
if (ability.name === template.abilities['H']) return 'Hidden Ability';
if (!this.curTeam || this.curTeam.format !== 'balancedhackmons') return 'Illegal';
},
move: function(move) {
if (!this.curSet) return;
if (!move) return ['Usable Moves', 'Moves', 'Usable Sketch Moves', 'Sketch Moves'];
var movelist = this.movelist;
if (!movelist) return 'Illegal';
if (!movelist[move.id]) {
if (movelist['sketch'] && move.id !== 'chatter' && move.id !== 'struggle') {
if (move.isViable) return 'Usable Sketch Moves';
return 'Sketch Moves';
}
if (!this.curTeam || this.curTeam.format !== 'balancedhackmons') return 'Illegal';
}
var speciesid = toId(this.curSet.species);
if (move.isViable) return 'Usable Moves';
return 'Moves';
}
},
chartTypes: {
pokemon: 'pokemon',
item: 'item',
ability: 'ability',
move1: 'move',
move2: 'move',
move3: 'move',
move4: 'move',
stats: 'stats',
details: 'details'
},
chartClick: function(e) {
this.chartSet($(e.currentTarget).data('name'), true);
},
chartKeydown: function(e) {
var modifier = (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey || e.cmdKey);
if (e.keyCode === 13 || (e.keyCode === 9 && !modifier)) {
if (!this.arrangeCallback[this.curChartType]) return;
e.stopPropagation();
e.preventDefault();
var name = e.currentTarget.name;
this.$chart.html(Chart.chart(e.currentTarget.value, this.curChartType, false, _.bind(this.arrangeCallback[this.curChartType], this)));
var val = Chart.firstResult;
this.chartSet(val, true);
return;
}
},
chartKeyup: function() {
this.updateChartDelayed();
},
chartFocus: function(e) {
var $target = $(e.currentTarget);
var name = e.currentTarget.name;
var type = this.chartTypes[name];
$target.removeClass('incomplete');
if (this.curChartName === name) return;
if (!this.curSet) {
var i = +$target.closest('li').prop('value');
this.curSet = this.curSetList[i];
this.curSetLoc = i;
this.update();
if (type === 'stats' || type === 'details') {
this.$('button[name='+name+']').click();
} else {
this.$('input[name='+name+']').select();
}
return;
}
this.curChartName = name;
this.curChartType = type;
this.updateChart();
},
chartChange: function(e) {
var name = e.currentTarget.name;
var type = this.chartTypes[name];
var arrange = null;
if (this.arrangeCallback[this.curChartType]) {
arrange = _.bind(this.arrangeCallback[this.curChartType], this);
}
this.$chart.html(Chart.chart(e.currentTarget.value, type, false, arrange));
var val = Chart.firstResult;
var id = toId(e.currentTarget.value);
if (toId(val) !== id) {
$(e.currentTarget).addClass('incomplete');
return;
}
this.chartSet(val);
},
chartSet: function(val, selectNext) {
var inputName = this.curChartName;
var id = toId(val);
this.$('input[name='+inputName+']').val(val).removeClass('incomplete');
switch (inputName) {
case 'pokemon':
this.setPokemon(val, selectNext);
break;
case 'item':
this.curSet.item = val;
this.updatePokemonSprite();
if (selectNext) this.$('input[name=ability]').select();
break;
case 'ability':
this.curSet.ability = val;
if (selectNext) this.$('input[name=move1]').select();
break;
case 'move1':
this.curSet.moves[0] = val;
this.chooseMove(val);
if (selectNext) this.$('input[name=move2]').select();
break;
case 'move2':
if (!this.curSet.moves[0]) this.curSet.moves[0] = '';
this.curSet.moves[1] = val;
this.chooseMove(val);
this.$('input[name=move3]').select();
break;
case 'move3':
if (!this.curSet.moves[0]) this.curSet.moves[0] = '';
if (!this.curSet.moves[1]) this.curSet.moves[1] = '';
this.curSet.moves[2] = val;
this.chooseMove(val);
if (selectNext) this.$('input[name=move4]').select();
break;
case 'move4':
if (!this.curSet.moves[0]) this.curSet.moves[0] = '';
if (!this.curSet.moves[1]) this.curSet.moves[1] = '';
if (!this.curSet.moves[2]) this.curSet.moves[2] = '';
this.curSet.moves[3] = val;
this.chooseMove(val);
if (selectNext) this.stats();
break;
}
this.save();
},
chooseMove: function(move) {
var set = this.curSet;
if (!set) return;
if (move.substr(0,13) === 'Hidden Power ') {
set.ivs = {};
for (var i in exports.BattleTypeChart[move.substr(13)].HPivs) {
set.ivs[i] = exports.BattleTypeChart[move.substr(13)].HPivs[i];
}
var moves = this.curSet.moves;
for (var i = 0; i < moves.length; ++i) {
if (moves[i] === 'Gyro Ball' || moves[i] === 'Trick Room') set.ivs['spe'] = set.ivs['spe'] % 4;
}
} else if (move === 'Gyro Ball' || move === 'Trick Room') {
var hasHiddenPower = false;
var moves = this.curSet.moves;
for (var i = 0; i < moves.length; ++i) {
if (moves[i].substr(0,13) === 'Hidden Power ') hasHiddenPower = true;
}
set.ivs['spe'] = hasHiddenPower ? set.ivs['spe'] % 4 : 0;
} else if (move === 'Return') {
this.curSet.happiness = 255;
} else if (move === 'Frustration') {
this.curSet.happiness = 0;
}
},
setPokemon: function(val, selectNext) {
var set = this.curSet;
var template = Tools.getTemplate(val);
var newPokemon = !set.species;
if (!template.exists || set.species === template.species) return;
set.name = template.species;
set.species = val;
if (set.level) delete set.level;
if (this.curTeam && (this.curTeam.format === 'lc' || this.curTeam.format === 'gen5lc')) set.level = 5;
if (set.gender) delete set.gender;
if (template.gender && template.gender !== 'N') set.gender = template.gender;
if (set.happiness) delete set.happiness;
if (set.shiny) delete set.shiny;
set.item = '';
set.ability = template.abilities['0'];
set.moves = [];
set.evs = {};
set.ivs = {};
set.nature = '';
this.updateSetTop();
if (selectNext) this.$('input[name=item]').select();
},
/*********************************************************
* Utility functions
*********************************************************/
// EV guesser
guessRole: function() {
var set = this.curSet;
if (!set) return '?';
if (!set.moves) return '?';
var moveCount = {
'Physical': 0,
'Special': 0,
'PhysicalOffense': 0,
'SpecialOffense': 0,
'PhysicalSetup': 0,
'SpecialSetup': 0,
'Support': 0,
'Setup': 0,
'Restoration': 0,
'Offense': 0,
'Stall': 0,
'SpecialStall': 0,
'PhysicalStall': 0,
'Ultrafast': 0
};
var hasMove = {};
var template = Tools.getTemplate(set.species || set.name);
var stats = template.baseStats;
var itemid = toId(set.item);
var abilityid = toId(set.ability);
if (set.moves.length < 4 && template.id !== 'unown' && template.id !== 'ditto') return '?';
for (var i=0,len=set.moves.length; i 95);
var physicalBulk = (stats.hp+75)*(stats.def+87);
var specialBulk = (stats.hp+75)*(stats.spd+87);
if (hasMove['willowisp'] || hasMove['acidarmor'] || hasMove['irondefense'] || hasMove['cottonguard']) {
physicalBulk *= 1.6;
moveCount['PhysicalStall']++;
}
else if (hasMove['scald'] || hasMove['bulkup'] || hasMove['coil'] || hasMove['cosmicpower']) {
physicalBulk *= 1.3;
if (hasMove['scald']) { // partial stall goes in reverse
moveCount['SpecialStall']++;
} else {
moveCount['PhysicalStall']++;
}
}
if (abilityid === 'flamebody') physicalBulk *= 1.1;
if (hasMove['calmmind'] || hasMove['quiverdance'] || hasMove['geomancy']) {
specialBulk *= 1.3;
moveCount['SpecialStall']++;
}
if (template.id === 'tyranitar') specialBulk *= 1.5;
if (hasMove['bellydrum']) {
physicalBulk *= 0.6;
specialBulk *= 0.6;
}
if (moveCount['Restoration']) {
physicalBulk *= 1.5;
specialBulk *= 1.5;
} else if (hasMove['painsplit'] && hasMove['substitute']) {
// SubSplit isn't generally a stall set
moveCount['Stall']--;
} else if (hasMove['painsplit'] || hasMove['rest']) {
physicalBulk *= 1.4;
specialBulk *= 1.4;
}
if ((hasMove['bodyslam'] || hasMove['thunder']) && abilityid === 'serenegrace' || hasMove['thunderwave']) {
physicalBulk *= 1.1;
specialBulk *= 1.1;
}
if ((hasMove['ironhead'] || hasMove['airslash']) && abilityid === 'serenegrace') {
physicalBulk *= 1.1;
specialBulk *= 1.1;
}
if (hasMove['gigadrain'] || hasMove['drainpunch'] || hasMove['hornleech']) {
physicalBulk *= 1.15;
specialBulk *= 1.15;
}
if (itemid === 'leftovers' || itemid === 'blacksludge') {
physicalBulk *= 1 + 0.1*(1+moveCount['Stall']/1.5);
specialBulk *= 1 + 0.1*(1+moveCount['Stall']/1.5);
}
if (hasMove['leechseed']) {
physicalBulk *= 1 + 0.1*(1+moveCount['Stall']/1.5);
specialBulk *= 1 + 0.1*(1+moveCount['Stall']/1.5);
}
if ((itemid === 'flameorb' || itemid === 'toxicorb') && abilityid !== 'magicguard') {
if (itemid === 'toxicorb' && abilityid === 'poisonheal') {
physicalBulk *= 1 + 0.1*(2+moveCount['Stall']);
specialBulk *= 1 + 0.1*(2+moveCount['Stall']);
} else {
physicalBulk *= 0.8;
specialBulk *= 0.8;
}
}
if (itemid === 'lifeorb') {
physicalBulk *= 0.7;
specialBulk *= 0.7;
}
if (abilityid === 'multiscale' || abilityid === 'magicguard' || abilityid === 'regenerator') {
physicalBulk *= 1.4;
specialBulk *= 1.4;
}
if (itemid === 'eviolite') {
physicalBulk *= 1.5;
specialBulk *= 1.5;
}
if (itemid === 'assaultvest') specialBulk *= 1.5;
var bulk = physicalBulk + specialBulk;
if (bulk < 46000 && stats.spe >= 70) isFast = true;
moveCount['bulk'] = bulk;
moveCount['physicalBulk'] = physicalBulk;
moveCount['specialBulk'] = specialBulk;
if (hasMove['agility'] || hasMove['dragondance'] || hasMove['quiverdance'] || hasMove['rockpolish'] || hasMove['shellsmash'] || hasMove['flamecharge']) {
isFast = true;
} else if (abilityid === 'unburden' || abilityid === 'speedboost' || abilityid === 'motordrive') {
isFast = true;
moveCount['Ultrafast'] = 1;
} else if (abilityid === 'chlorophyll' || abilityid === 'swiftswim' || abilityid === 'sandrush') {
isFast = true;
moveCount['Ultrafast'] = 2;
}
if (hasMove['agility'] || hasMove['shellsmash'] || hasMove['autotomize'] || hasMove['shiftgear'] || hasMove['rockpolish']) moveCount['Ultrafast'] = 2;
moveCount['Fast'] = isFast?1:0;
this.moveCount = moveCount;
this.hasMove = hasMove;
if (template.id === 'ditto') return abilityid==='imposter'?'Physically Defensive':'Fast Bulky Support';
if (template.id === 'shedinja') return 'Fast Physical Sweeper';
if (itemid === 'choiceband' && moveCount['PhysicalAttack'] >= 2) {
if (!isFast) return 'Bulky Band';
return 'Fast Band';
} else if (itemid === 'choicespecs' && moveCount['SpecialAttack'] >= 2) {
if (!isFast) return 'Bulky Specs';
return 'Fast Specs';
} else if (itemid === 'choicescarf') {
if (moveCount['PhysicalAttack'] === 0) return 'Special Scarf';
if (moveCount['SpecialAttack'] === 0) return 'Physical Scarf';
if (moveCount['PhysicalAttack'] > moveCount['SpecialAttack']) return 'Physical Biased Mixed Scarf';
if (moveCount['PhysicalAttack'] < moveCount['SpecialAttack']) return 'Special Biased Mixed Scarf';
if (stats.atk < stats.spa) return 'Special Biased Mixed Scarf';
return 'Physical Biased Mixed Scarf';
}
if (template.id === 'unown') return 'Fast Special Sweeper';
if (moveCount['PhysicalStall'] && moveCount['Restoration']) {
return 'Specially Defensive';
}
if (moveCount['SpecialStall'] && moveCount['Restoration'] && itemid !== 'lifeorb') {
return 'Physically Defensive';
}
var offenseBias = '';
if (stats.spa > stats.atk && moveCount['Special'] > 1) offenseBias = 'Special';
else if (stats.spa > stats.atk && moveCount['Special'] > 1) offenseBias = 'Special';
else if (moveCount['Special'] > moveCount['Physical']) offenseBias = 'Special';
else offenseBias = 'Physical';
var offenseStat = stats[offenseBias === 'Special'?'spa':'atk'];
if (moveCount['Stall'] + moveCount['Support']/2 <= 2 && bulk < 135000 && moveCount[offenseBias] >= 1.5) {
if (isFast) {
if (bulk > 80000 && !moveCount['Ultrafast']) return 'Bulky '+offenseBias+' Sweeper';
return 'Fast '+offenseBias+' Sweeper';
} else {
if (moveCount[offenseBias] >= 3 || moveCount['Stall'] <= 0) {
return 'Bulky '+offenseBias+' Sweeper';
}
}
}
if (isFast && abilityid !== 'prankster') {
if (stats.spe > 100 || bulk < 55000 || moveCount['Ultrafast']) {
return 'Fast Bulky Support';
}
}
if (moveCount['SpecialStall']) return 'Physically Defensive';
if (moveCount['PhysicalStall']) return 'Specially Defensive';
if (template.id === 'blissey' || template.id === 'chansey') return 'Physically Defensive';
if (specialBulk >= physicalBulk) return 'Specially Defensive';
return 'Physically Defensive';
},
ensureMinEVs: function(evs, stat, min, evTotal) {
if (!evs[stat]) evs[stat] = 0;
var diff = min - evs[stat];
if (diff <= 0) return evTotal;
if (evTotal <= 504) {
var change = Math.min(508 - evTotal, diff);
evTotal += change;
evs[stat] += change;
diff -= change;
}
if (diff <= 0) return evTotal;
var evPriority = {def:1, spd:1, hp:1, atk:1, spa:1, spe:1};
for (var i in evPriority) {
if (i === stat) continue;
if (evs[i] && evs[i] > 128) {
evs[i] -= diff;
evs[stat] += diff;
return evTotal;
}
}
return evTotal; // can't do it :(
},
ensureMaxEVs: function(evs, stat, min, evTotal) {
if (!evs[stat]) evs[stat] = 0;
var diff = evs[stat] - min;
if (diff <= 0) return evTotal;
evs[stat] -= diff;
evTotal -= diff;
return evTotal; // can't do it :(
},
guessEVs: function(role) {
var set = this.curSet;
if (!set) return {};
var template = Tools.getTemplate(set.species || set.name);
var stats = template.baseStats;
var hasMove = this.hasMove;
var moveCount = this.moveCount;
var evs = {};
var plusStat = '';
var minusStat = '';
var statChart = {
'Bulky Band': ['atk', 'hp'],
'Fast Band': ['spe', 'atk'],
'Bulky Specs': ['spa', 'hp'],
'Fast Specs': ['spe', 'spa'],
'Physical Scarf': ['spe', 'atk'],
'Special Scarf': ['spe', 'spa'],
'Physical Biased Mixed Scarf': ['spe', 'atk'],
'Special Biased Mixed Scarf': ['spe', 'spa'],
'Fast Physical Sweeper': ['spe', 'atk'],
'Fast Special Sweeper': ['spe', 'spa'],
'Bulky Physical Sweeper': ['atk', 'hp'],
'Bulky Special Sweeper': ['spa', 'hp'],
'Fast Bulky Support': ['spe', 'hp'],
'Physically Defensive': ['def', 'hp'],
'Specially Defensive': ['spd', 'hp']
};
plusStat = statChart[role][0];
if (role === 'Fast Bulky Support') moveCount['Ultrafast'] = 0;
if (plusStat === 'spe' && (moveCount['Ultrafast'] || evs['spe'] < 252)) {
if (statChart[role][1] === 'atk' || statChart[role][1] == 'spa') {
plusStat = statChart[role][1];
} else if (moveCount['Physical'] >= 3) {
plusStat = 'atk';
} else if (stats.spd > stats.def) {
plusStat = 'spd';
} else {
plusStat = 'def';
}
}
this.ignoreEVLimits = (this.curTeam.format === 'classichackmons');
if (this.curTeam && this.ignoreEVLimits) {
evs = {hp:252, atk:252, def:252, spa:252, spd:252, spe:252};
if (hasMove['gyroball'] || hasMove['trickroom']) delete evs.spe;
} else {
if (!statChart[role]) return {};
var evTotal = 0;
var i = statChart[role][0];
var stat = this.getStat(i, null, 252, plusStat===i?1.1:1.0);
var ev = 252;
while (stat <= this.getStat(i, null, ev-4, plusStat===i?1.1:1.0)) ev -= 4;
evs[i] = ev;
evTotal += ev;
var i = statChart[role][1];
if (i === 'hp' && set.level && set.level < 20) i = 'spd';
var stat = this.getStat(i, null, 252, plusStat===i?1.1:1.0);
var ev = 252;
if (i === 'hp' && (hasMove['substitute'] || hasMove['transform']) && stat == Math.floor(stat/4)*4) stat -= 1;
while (stat <= this.getStat(i, null, ev-4, plusStat===i?1.1:1.0)) ev -= 4;
if (ev < 0) ev = 0;
evs[i] = ev;
evTotal += ev;
if (set.item !== 'Leftovers' && set.item !== 'Black Sludge') {
var hpParity = 1; // 1 = should be odd, 0 = should be even
if ((hasMove['substitute'] || hasMove['bellydrum']) && (set.item||'').slice(-5) === 'Berry') {
hpParity = 0;
}
if (this.getStat('hp', null, evs['hp'] || 0, 1) % 2 != hpParity) {
if (evs['hp']) {
evs['hp'] -= 4;
evTotal -= 4;
} else {
evs['hp'] = 4;
evTotal += 4;
}
}
}
if (template.id === 'tentacruel') evTotal = this.ensureMinEVs(evs, 'spe', 16, evTotal);
if (template.id === 'skarmory') evTotal = this.ensureMinEVs(evs, 'spe', 24, evTotal);
if (template.id === 'jirachi') evTotal = this.ensureMinEVs(evs, 'spe', 32, evTotal);
if (template.id === 'celebi') evTotal = this.ensureMinEVs(evs, 'spe', 36, evTotal);
if (template.id === 'volcarona') evTotal = this.ensureMinEVs(evs, 'spe', 52, evTotal);
if (template.id === 'gliscor') evTotal = this.ensureMinEVs(evs, 'spe', 72, evTotal);
if (stats.spe == 97) evTotal = this.ensureMaxEVs(evs, 'spe', 220, evTotal);
if (template.id === 'dragonite' && evs['hp']) evTotal = this.ensureMaxEVs(evs, 'spe', 220, evTotal);
if (evTotal < 508) {
var remaining = 508 - evTotal;
if (remaining > 252) remaining = 252;
if (!evs['atk'] && moveCount['PhysicalAttack']) {
evs['atk'] = remaining;
} else if (!evs['spa'] && moveCount['SpecialAttack']) {
evs['spa'] = remaining;
} else if (stats.hp == 1 && !evs['def']) {
evs['def'] = remaining;
} else if (stats.def === stats.spd && !evs['spd']) {
evs['spd'] = remaining;
} else if (!evs['spd']) {
evs['spd'] = remaining;
} else if (!evs['def']) {
evs['def'] = remaining;
}
}
}
if (hasMove['gyroball'] || hasMove['trickroom']) {
minusStat = 'spe';
} else if (!moveCount['PhysicalAttack']) {
minusStat = 'atk';
} else if (!moveCount['SpecialAttack']) {
minusStat = 'spa';
} else if (stats.def > stats.spd) {
minusStat = 'spd';
} else {
minusStat = 'def';
}
if (plusStat === minusStat) {
minusStat = (plusStat==='spe' ? 'spd' : 'spe');
}
evs.plusStat = plusStat;
evs.minusStat = minusStat;
return evs;
},
// Stat calculator
getStat: function(stat, set, evOverride, natureOverride) {
if (!set) set = this.curSet;
if (!set) return 0;
if (!set.ivs) set.ivs = {
hp: 31,
atk: 31,
def: 31,
spa: 31,
spd: 31,
spe: 31
};
if (!set.evs) set.evs = {
hp: 0,
atk: 0,
def: 0,
spa: 0,
spd: 0,
spe: 0
};
// do this after setting set.evs because it's assumed to exist
// after getStat is run
var template = Tools.getTemplate(set.species);
if (!template.exists) return 0;
if (!set.level) set.level = 100;
if (typeof set.ivs[stat] === 'undefined') set.ivs[stat] = 31;
if (stat === 'hp') {
if (template.baseStats['hp'] === 1) return 1;
return Math.floor(Math.floor(2*template.baseStats['hp']+(set.ivs['hp']||0)+Math.floor((evOverride||set.evs['hp']||0)/4)+100)*set.level / 100 + 10);
}
var val = Math.floor(Math.floor(2*template.baseStats[stat]+(set.ivs[stat]||0)+Math.floor((evOverride||set.evs[stat]||0)/4))*set.level / 100 + 5);
if (natureOverride) {
val *= natureOverride;
} else if (BattleNatures[set.nature] && BattleNatures[set.nature].plus === stat) {
val *= 1.1;
} else if (BattleNatures[set.nature] && BattleNatures[set.nature].minus === stat) {
val *= 0.9;
}
return Math.floor(val);
},
// initialization
buildMovelists: function() {
if (Tools.movelists) return;
if (!window.BattlePokedex) return;
Tools.movelists = {};
Tools.g6movelists = {};
for (var pokemon in window.BattlePokedex) {
var template = Tools.getTemplate(pokemon);
var moves = {};
var g6moves = {};
var alreadyChecked = {};
do {
alreadyChecked[template.speciesid] = true;
if (template.learnset) {
for (var l in template.learnset) {
moves[l] = true;
if (template.learnset[l].length) g6moves[l] = true;
}
}
if (template.speciesid === 'shaymin') {
template = Tools.getTemplate('shayminsky');
} else if (toId(template.baseSpecies) !== toId(template.species) && toId(template.baseSpecies) !== 'kyurem') {
template = Tools.getTemplate(template.baseSpecies);
} else {
template = Tools.getTemplate(template.prevo);
}
} while (template && template.species && !alreadyChecked[template.speciesid]);
Tools.movelists[pokemon] = moves;
Tools.g6movelists[pokemon] = g6moves;
}
},
applyMovelist: function(g6only, speciesid) {
this.buildMovelists();
if (!Tools.movelists) {
this.movelist = false;
} else if (g6only) {
this.movelist = Tools.g6movelists[speciesid];
} else {
this.movelist = Tools.movelists[speciesid];
}
},
destroy: function() {
app.clearGlobalListeners();
Room.prototype.destroy.call(this);
}
});
var MovePopup = exports.MovePopup = Popup.extend({
initialize: function(data) {
var buf = '