pokemon-showdown/scripts.js

1141 lines
28 KiB
JavaScript

function toId(text)
{
text = text || '';
if (typeof text !== 'string') return ''; //???
return text.replace(/ /g, '');
}
function clone(object) {
var newObj = (object instanceof Array) ? [] : {};
for (var i in object) {
if (object[i] && typeof object[i] == "object") {
newObj[i] = clone(object[i]);
} else newObj[i] = object[i]
} return newObj;
};
function shuffle(array) {
var tmp, current, top = array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = array[current];
array[current] = array[top];
array[top] = tmp;
}
return array;
}
function objectKeys(object) {
var keys = [];
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
keys.push(prop);
}
}
return keys;
}
exports.BattleScripts = {
runMove: function(move, pokemon, target) {
move = this.getMove(move);
this.setActiveMove(move, pokemon, target);
if (pokemon.movedThisTurn || pokemon.runBeforeMove(target, move))
{
this.debug(''+pokemon.id+' move interrupted; movedThisTurn: '+pokemon.movedThisTurn);
this.clearActiveMove(true);
return;
}
if (move.beforeMoveCallback)
{
if (move.beforeMoveCallback.call(this, pokemon, target, move))
{
this.clearActiveMove(true);
return;
}
}
pokemon.lastDamage = 0;
this.useMove(move, pokemon, target);
pokemon.deductPP(move);
},
useMove: function(move, pokemon, target) {
this.setActiveMove(move, pokemon, target);
var damage = 0;
var canTargetFainted = {
all: 1, foeSide: 1
};
move = this.getMove(move);
move = clone(move);
move = this.runEvent('ModifyMove',pokemon,target,move,move);
if (!move) return false;
var attrs = '';
var moveRoll = Math.random()*100;
var missed = false;
if (pokemon.fainted)
{
return false;
}
var boostTable = [1, 4/3, 5/3, 2, 7/3, 8/3, 3];
var accuracy = move.accuracy;
if (accuracy !== true)
{
if (pokemon.boosts.accuracy > 0)
{
accuracy *= boostTable[pokemon.boosts.accuracy];
}
else
{
accuracy /= boostTable[-pokemon.boosts.accuracy];
}
if (target.boosts.evasion > 0)
{
accuracy /= boostTable[target.boosts.evasion];
}
else
{
accuracy *= boostTable[-target.boosts.evasion];
}
}
if (move.ohko) accuracy = 30; // bypasses accuracy modifiers
if (move.alwaysHit) accuracy = true; // bypasses ohko accuracy modifiers
if (accuracy !== true && moveRoll >= accuracy)
{
missed = true;
attrs = ' miss';
}
if (target.fainted && !canTargetFainted[move.target])
{
attrs = ' no-target';
}
this.add('move '+pokemon.id+' '+move.id+' ??'+attrs);
if (missed)
{
this.add('r-miss '+pokemon.id);
this.singleEvent('MoveFail', move, null, target, pokemon, move);
if (move.selfdestruct)
{
this.faint(pokemon, pokemon, move);
}
return true;
}
if (target.fainted && !canTargetFainted[move.target])
{
this.add('r-no-target');
this.singleEvent('MoveFail', move, null, target, pokemon, move);
if (move.selfdestruct)
{
this.faint(pokemon, pokemon, move);
}
return true;
}
if (move.id === 'Return' || move.id === 'Frustration')
{
move.basePower = 102;
}
if (typeof move.affectedByImmunities === 'undefined')
{
move.affectedByImmunities = (move.category !== 'Status');
}
if (move.affectedByImmunities)
{
var type = move.type;
if (move.typeCallback)
{
if (typeof move.typeCallback === 'string')
{
type = move.typeCallback;
}
else
{
type = move.typeCallback.call(this, pokemon, target);
}
move.type = type;
}
if (!target.runImmunity(type, true))
{
this.singleEvent('MoveFail', move, null, target, pokemon, move);
if (move.selfdestruct)
{
this.faint(pokemon, pokemon, move);
}
return true;
}
}
pokemon.lastDamage = 0;
if (!move.multihit)
{
damage = BattleScripts.moveHit.call(this, target, pokemon, move);
}
else
{
var hits = move.multihit;
if (hits.length)
{
// yes, it's hardcoded... meh
if (hits[0] === 2 && hits[1] === 5)
{
var roll = parseInt(Math.random()*20);
if (roll < 7) hits = 2;
else if (roll < 14) hits = 3;
else if (roll < 17) hits = 4;
else hits = 5;
}
else
{
hits = hits[0] + (Math.random()*(hits[1]-hits[0]+1));
}
}
hits = Math.floor(hits);
for (var i=0; i<hits && target.hp; i++)
{
damage = BattleScripts.moveHit.call(this, target, pokemon, move);
}
this.add('r-hit-count '+target.id+' '+i);
}
target.gotHit(move, damage, pokemon);
if (move.recoil && pokemon.lastDamage)
{
this.damage(pokemon.lastDamage * move.recoil[0] / move.recoil[1], pokemon, target, 'recoil');
}
if (move.drain && pokemon.lastDamage)
{
this.heal(pokemon.lastDamage * move.drain[0] / move.drain[1], pokemon, target, 'drain');
}
if (move.selfdestruct)
{
this.faint(pokemon, pokemon, move);
}
if (move.afterMoveCallback)
{
move.afterMoveCallback.call(this, pokemon, target);
}
if (!move.negateSecondary && damage !== false)
{
this.singleEvent('AfterMoveSecondary', move, null, target, pokemon, move);
this.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move);
this.runEvent('AfterMoveSecondary', target, pokemon, move);
this.runEvent('AfterMoveSecondarySelf', pokemon, target, move);
}
return true;
},
moveHit: function(target, pokemon, move, moveData, isSecondary, isSelf) {
this.setActiveMove(move, pokemon, target);
hitResult = true;
move = this.getMove(move);
if (!moveData) moveData = move;
// don't run the pokemon hit events for side-hits and field-hits
if (move.target !== 'all' && move.target !== 'foeSide' && move.target !== 'allySide')
{
if (isSelf)
{
if (isSecondary)
{
hitResult = this.runEvent('SecondarySelfHit', target, pokemon, move);
}
else
{
hitResult = this.runEvent('SelfHit', target, pokemon, move);
}
}
else if (isSecondary)
{
hitResult = this.runEvent('SecondaryHit', target, pokemon, move);
if (target && target.fainted)
{
}
}
else
{
hitResult = this.runEvent('Hit', target, pokemon, move);
if (!hitResult)
{
if (hitResult === false)
{
this.add('r-failed '+target.id);
}
if (hitResult !== 0)
{
return;
}
}
}
}
if (!isSelf && !isSecondary)
{
if (!this.runEvent('FieldHit', target, pokemon, move))
{
return;
}
}
if (hitResult)
{
if (move.target === 'foeSide' || move.target === 'allySide')
{
hitResult = this.singleEvent('Hit', moveData, {}, target.side, pokemon, move);
}
else
{
hitResult = this.singleEvent('Hit', moveData, {}, target, pokemon, move);
}
}
if (hitResult === 0)
{
target = null;
}
else if (!hitResult)
{
if (hitResult === false)
{
this.add('r-failed '+target.id);
}
return;
}
if (target)
{
var damage = this.getDamage(pokemon, target, moveData);
if (damage === false || damage === null) return false;
if (hitResult === 'noFaint' && damage >= target.hp)
{
damage = target.hp - 1;
}
if (damage && !target.fainted)
{
damage = this.damage(damage, target, pokemon, move);
if (!damage) return false;
}
else if (damage === false && typeof hitResult === 'undefined')
{
this.add('r-failed '+target.id);
}
if (moveData.boosts && !target.fainted)
{
this.boost(moveData.boosts, target, pokemon, move);
}
if (moveData.heal && !target.fainted)
{
var d = target.heal(target.maxhp * moveData.heal[0] / moveData.heal[1]);
this.add('r-heal '+target.id+' '+target.hpPercent(d)+target.getHealth());
}
if (moveData.status)
{
if (!target.status)
{
target.setStatus(moveData.status, pokemon, move);
}
else if (!isSecondary)
{
// already-status
this.add('r-status '+target.id+' '+target.status);
}
}
if (moveData.forceStatus)
{
target.setStatus(moveData.forceStatus, pokemon, move);
}
if (moveData.volatileStatus)
{
target.addVolatile(moveData.volatileStatus, pokemon, move);
}
if (moveData.sideCondition)
{
target.side.addSideCondition(moveData.sideCondition, pokemon, move);
}
if (moveData.weather)
{
this.setWeather(moveData.weather, pokemon, move);
}
if (moveData.pseudoWeather)
{
this.addPseudoWeather(moveData.pseudoWeather, pokemon, move);
}
}
if (moveData.self)
{
BattleScripts.moveHit.call(this, pokemon, pokemon, move, moveData.self, isSecondary, true);
}
if (moveData.secondary && target)
{
var secondaryRoll = Math.random()*100;
if (typeof moveData.secondary.chance === 'undefined' || secondaryRoll < moveData.secondary.chance)
{
BattleScripts.moveHit.call(this, target, pokemon, move, moveData.secondary, true, isSelf);
}
}
if (target)
{
if (moveData.forceSwitch && this.runEvent('DragOut', target, pokemon, move))
{
this.dragIn(target.side);
}
}
if (move.selfSwitch || move.batonPass)
{
pokemon.switchFlag = true;
}
return damage;
},
getTeam: function(side) {
if (side.battle.getFormat().team === 'random')
{
return BattleScripts.randomTeam.call(this, side);
}
else if (side.user && side.user.team && side.user.team !== 'random')
{
return side.user.team;
}
else
{
return BattleScripts.randomTeam.call(this,side);
}
},
randomTeam: function(side) {
var battle = this;
var keys = [];
var pokemonLeft = 0;
var pokemon = [];
for (var prop in BattlePokedex) {
if (BattlePokedex.hasOwnProperty(prop) && BattlePokedex[prop].viable) {
keys.push(prop);
}
}
keys = shuffle(keys);
var PotD = this.getEffect('PotD');
var ruleset = this.getFormat().ruleset;
for (var i=0; i<keys.length && pokemonLeft < 6; i++)
{
var template = this.getTemplate(keys[i]);
if (!template || !template.name) continue;
if (template.tier === 'CAP' && Math.random()*3>1) continue;
if (i===1 && ruleset && ruleset[0]==='PotD' && PotD && PotD.onPotD)
{
template = this.getTemplate(PotD.onPotD);
}
if (template.species === 'Magikarp')
{
template.viablemoves = ["Magikarp'sRevenge", "Splash", "Bounce"];
}
var moveKeys = shuffle(objectKeys(template.viablemoves));
var moves = [];
var ability = '';
var item = '';
var evs = {
hp: 85,
atk: 85,
def: 85,
spa: 85,
spd: 85,
spe: 85
};
var ivs = {
hp: 31,
atk: 31,
def: 31,
spa: 31,
spd: 31,
spe: 31
};
var hasType = {};
hasType[template.types[0]] = true;
if (template.types[1]) hasType[template.types[1]] = true;
var hasMove = {};
var counter = {};
var setupType = '';
var j=0;
do
{
while (moves.length<4 && j<moveKeys.length)
{
var moveid = toId(template.viablemoves[moveKeys[j]]);
//if (j===0) moveid = 'ChargeBeam';
moves.push(moveid);
j++;
}
hasMove = {};
counter = {
Physical: 0, Special: 0, Status: 0, damage: 0,
Technician: 0, SkillLink: 0, Contrary: 0,
recoil: 0, inaccurate: 0,
physicalSetup: 0, specialSetup: 0, mixedSetup: 0
};
for (var k=0; k<moves.length; k++)
{
var moveid = moves[k];
var move = this.getMove(moveid);
hasMove[moveid] = true;
if (move.damage || move.damageCallback)
{
counter['damage']++;
}
else
{
counter[move.category]++;
}
if (move.basePower && move.basePower <= 60)
{
counter['Technician']++;
}
if (move.multihit && move.multihit[1] === 5)
{
counter['SkillLink']++;
}
if (move.recoil)
{
counter['recoil']++;
}
if (move.accuracy && move.accuracy !== true && move.accuracy < 90)
{
counter['inaccurate']++;
}
var ContraryMove = {
LeafStorm: 1, Overheat: 1, CloseCombat: 1, Superpower: 1, 'V-create': 1
};
if (ContraryMove[move.id])
{
counter['Contrary']++;
}
var PhysicalSetup = {
SwordsDance:1, DragonDance:1, Coil:1, BulkUp:1, Curse:1
};
var SpecialSetup = {
NastyPlot:1, TailGlow:1, QuiverDance:1, CalmMind:1
};
var MixedSetup = {
Growth:1, WorkUp:1, ShellSmash:1
};
if (PhysicalSetup[move.id])
{
counter['physicalSetup']++;
}
if (SpecialSetup[move.id])
{
counter['specialSetup']++;
}
if (MixedSetup[move.id])
{
counter['mixedSetup']++;
}
}
if (counter['mixedSetup'])
{
setupType = 'Mixed';
}
else if (counter['specialSetup'])
{
setupType = 'Special';
}
else if (counter['physicalSetup'])
{
setupType = 'Physical';
}
for (var k=0; k<moves.length; k++)
{
var moveid = moves[k];
var move = this.getMove(moveid);
var rejected = false;
var isSetup = false;
switch (moveid)
{
// not very useful without their supporting moves
case 'SleepTalk':
if (!hasMove['Rest']) rejected = true;
if (hasMove['Trick'] || hasMove['Protect'] || hasMove['Substitute'] || hasMove['BellyDrum']) rejected = true;
break;
case 'Endure':
if (!hasMove['Flail'] && !hasMove['Endeavor'] && !hasMove['Reversal']) rejected = true;
break;
// we only need to set up once
case 'SwordsDance': case 'DragonDance': case 'Coil': case 'Curse': case 'BulkUp':
if (!counter['Physical'] && !hasMove['BatonPass']) rejected = true;
if (setupType !== 'Physical' || counter['physicalSetup'] > 1) rejected = true;
isSetup = true;
break;
case 'NastyPlot': case 'TailGlow': case 'QuiverDance': case 'CalmMind':
if (!counter['Special'] && !hasMove['BatonPass']) rejected = true;
if (setupType !== 'Special' || counter['specialSetup'] > 1) rejected = true;
isSetup = true;
break;
case 'ShellSmash': case 'Growth': case 'WorkUp':
if (!counter['Special'] && !counter['Physical'] && !hasMove['BatonPass']) rejected = true;
if (setupType !== 'Mixed' || counter['mixedSetup'] > 1) rejected = true;
isSetup = true;
break;
// bad after setup
case 'SeismicToss': case 'NightShade': case 'SuperFang':
if (setupType) rejected = true;
break;
case 'KnockOff': case 'Protect': case 'PerishSong': case 'MagicCoat':
if (setupType) rejected = true;
break;
// bit redundant to have both
case 'FireBlast':
if (hasMove['Eruption'] || hasMove['Overheat']) rejected = true;
break;
case 'Flamethrower':
if (hasMove['LavaPlume'] || hasMove['FireBlast'] || hasMove['Overheat']) rejected = true;
break;
case 'IceBeam':
if (hasMove['Blizzard']) rejected = true;
break;
case 'Surf':
if (hasMove['Scald'] || hasMove['HydroPump']) rejected = true;
break;
case 'EnergyBall':
case 'GrassKnot':
case 'PetalDance':
if (hasMove['GigaDrain']) rejected = true;
break;
case 'SeedBomb':
if (hasMove['NeedleArm']) rejected = true;
break;
case 'FlareBlitz':
if (hasMove['FirePunch']) rejected = true;
break;
case 'Thunderbolt':
if (hasMove['Discharge'] || hasMove['VoltSwitch'] || hasMove['Thunder']) rejected = true;
break;
case 'Discharge':
if (hasMove['VoltSwitch'] || hasMove['Thunder']) rejected = true;
break;
case 'RockSlide':
if (hasMove['StoneEdge']) rejected = true;
break;
case 'DragonClaw':
if (hasMove['Outrage'] || hasMove['DragonTail']) rejected = true;
break;
case 'AncientPower':
if (hasMove['PaleoWave']) rejected = true;
break;
case 'DragonPulse':
if (hasMove['DracoMeteor']) rejected = true;
break;
case 'Return':
if (hasMove['BodySlam']) rejected = true;
if (hasMove['Flail']) rejected = true;
if (hasMove['Facade']) rejected = true;
break;
case 'Flail':
if (hasMove['Facade']) rejected = true;
break;
case 'PoisonJab':
if (hasMove['GunkShot']) rejected = true;
break;
case 'Psychic':
if (hasMove['Psyshock']) rejected = true;
break;
case 'Yawn':
if (hasMove['GrassWhistle']) rejected = true;
break;
case 'Rest':
if (hasMove['MorningSun']) rejected = true;
break;
case 'Softboiled':
if (hasMove['Wish']) rejected = true;
break;
case 'PerishSong':
if (hasMove['Roar'] || hasMove['Whirlwind'] || hasMove['Haze']) rejected = true;
break;
case 'Whirlwind':
// outclasses Roar because Soundproof
if (hasMove['Roar'] || hasMove['Haze']) rejected = true;
break;
case 'Roost':
if (hasMove['Recover']) rejected = true;
break;
}
if (k===3)
{
if (counter['Status']>=4)
{
// taunt bait, not okay
rejected = true;
}
}
var SetupException = {
Overheat:1, DracoMeteor:1, LeafStorm:1,
VoltSwitch:1, 'U-turn':1,
SuckerPunch:1, ExtremeSpeed:1
};
if (move.category === 'Special' && setupType === 'Physical' && !SetupException[move.id])
{
rejected = true;
}
if (move.category === 'Physical' && setupType === 'Special' && !SetupException[move.id])
{
rejected = true;
}
if (setupType === 'Physical' && move.category !== 'Physical' && counter['Physical'] < 2)
{
rejected = true;
}
if (setupType === 'Special' && move.category !== 'Special' && counter['Special'] < 2)
{
rejected = true;
}
var todoMoves = {
BeatUp:1,
Disable:1
};
if (todoMoves[move.id]) rejected = true;
if (rejected && j<moveKeys.length)
{
moves.splice(k,1);
break;
}
}
} while (moves.length<4 && j<moveKeys.length);
// any moveset modification goes here
//moves[0] = 'Safeguard';
{
var abilities = [template.abilities['0']];
if (template.abilities['1'])
{
abilities.push(template.abilities['1']);
}
if (template.abilities['DW'])
{
abilities.push(template.abilities['DW']);
}
abilities.sort(function(a,b){
return battle.getAbility(b).rating - battle.getAbility(a).rating;
});
ability = toId(abilities[0]);
if (abilities[1])
{
var ability0 = this.getAbility(abilities[0]);
var ability1 = this.getAbility(abilities[1]);
if (ability0.rating <= ability1.rating)
{
if (Math.random()*2<1)
{
ability = toId(abilities[1]);
}
}
else if (ability0.rating - 0.6 <= ability1.rating)
{
if (Math.random()*3<1)
{
ability = toId(abilities[1]);
}
}
var rejectAbility = false;
if (ability === 'Contrary' && !counter['Contrary'])
{
rejectAbility = true;
}
if (ability === 'Technician' && !counter['Technician'])
{
rejectAbility = true;
}
if (ability === 'SkillLink' && !counter['SkillLink'])
{
rejectAbility = true;
}
if ((ability === 'RockHead' || ability === 'Reckless') && !counter['recoil'])
{
rejectAbility = true;
}
if ((ability === 'NoGuard' || ability === 'CompoundEyes') && !counter['inaccurate'])
{
rejectAbility = true;
}
if (ability === 'Moody')
{
rejectAbility = true;
}
if (rejectAbility)
{
if (ability === toId(abilities[1])) // or not
{
ability = toId(abilities[0]);
}
else
{
ability = toId(abilities[1]);
}
}
if ((abilities[1] === 'Guts' || abilities[0] === 'Guts' || abilities[3] === 'Guts') && ability !== 'QuickFeet' && hasMove['Facade'])
{
ability = 'Guts';
}
}
if (hasMove['GyroBall'])
{
ivs.spe = 0;
//evs.atk += evs.spe;
evs.spe = 0;
}
else if (hasMove['TrickRoom'])
{
ivs.spe = 0;
//evs.hp += evs.spe;
evs.spe = 0;
}
item = 'FocusSash';
if (template.species === 'Giratina-O')
{
item = 'GriseousOrb';
}
else if (template.species === 'Rotom-S')
{
// this is just to amuse myself
item = 'AirBalloon';
}
else if (ability === 'Imposter')
{
item = 'ChoiceScarf';
}
else if (hasMove["Magikarp'sRevenge"])
{
item = 'ChoiceBand';
}
else if (ability === 'WonderGuard')
{
item = 'FocusSash';
}
else if (template.species === 'Unown')
{
item = 'ChoiceSpecs';
}
else if (hasMove['Trick'] && hasMove['GyroBall'] && (ability === 'Levitate' || hasType['Flying']))
{
item = 'MachoBrace';
}
else if (hasMove['Trick'] && hasMove['GyroBall'])
{
item = 'IronBall';
}
else if (counter.Physical >= 3 && (hasMove['Trick'] || hasMove['Switcheroo']))
{
item = 'ChoiceBand';
}
else if (counter.Special >= 3 && (hasMove['Trick'] || hasMove['Switcheroo']))
{
item = 'ChoiceSpecs';
}
else if (counter.Status <= 1 && (hasMove['Trick'] || hasMove['Switcheroo']))
{
item = 'ChoiceScarf';
}
else if (hasMove['Rest'] && !hasMove['SleepTalk'])
{
item = 'ChestoBerry';
}
else if (hasMove['NaturalGift'])
{
item = 'LiechiBerry';
}
else if (template.species === 'Cubone' || template.species === 'Marowak')
{
item = 'ThickClub';
}
else if (template.species === 'Pikachu')
{
item = 'LightBall';
}
else if (template.species === 'Clamperl')
{
item = 'DeepSeaTooth';
}
else if (hasMove['Reflect'] && hasMove['LightScreen'])
{
item = 'LightClay';
}
else if (hasMove['Acrobatics'])
{
item = 'FlyingGem';
}
else if (hasMove['ShellSmash'])
{
item = 'WhiteHerb';
}
else if (ability === 'PoisonHeal')
{
item = 'ToxicOrb';
}
else if (hasMove['RainDance'])
{
item = 'DampRock';
}
else if (hasMove['SunnyDay'])
{
item = 'HeatRock';
}
else if (hasMove['Sandstorm']) // lol
{
item = 'SmoothRock';
}
else if (hasMove['Hail']) // lol
{
item = 'IcyRock';
}
else if (ability === 'MagicGuard' && hasMove['PsychoShift'])
{
item = 'FlameOrb';
}
else if (ability === 'SheerForce' || ability === 'MagicGuard')
{
item = 'LifeOrb';
}
else if (hasMove['Trick'] || hasMove['Switcheroo'])
{
item = 'ChoiceScarf';
}
else if (ability === 'Guts')
{
if (hasMove['DrainPunch'])
{
item = 'FlameOrb';
}
else
{
item = 'ToxicOrb';
}
if ((hasMove['Return'] || hasMove['HyperFang']) && !hasMove['Facade'])
{
// lol no
for (var j=0; j<moves.length; j++)
{
if (moves[j] === 'Return' || moves[j] === 'HyperFang')
{
moves[j] = 'Facade';
break;
}
}
}
}
else if (ability === 'MarvelScale' && hasMove['PsychoShift'])
{
item = 'FlameOrb';
}
else if (hasMove['Reflect'] || hasMove['LightScreen'])
{
// less priority than if you'd had both
item = 'LightClay';
}
else if (counter.Physical >= 4 && !hasMove['FakeOut'] && !hasMove['SuckerPunch'])
{
if (Math.random()*3 > 1)
{
item = 'ChoiceBand';
}
else
{
item = 'ExpertBelt';
}
}
else if (counter.Special >= 4)
{
if (Math.random()*3 > 1)
{
item = 'ChoiceSpecs';
}
else
{
item = 'ExpertBelt';
}
}
else if (this.getEffectiveness('Ground', template) >= 2 && ability !== 'Levitate')
{
item = 'AirBalloon';
}
else if (hasMove['Eruption'] || hasMove['WaterSpout'])
{
item = 'ChoiceScarf';
}
else if (hasMove['Substitute'] || hasMove['Detect'] || hasMove['Protect'])
{
item = 'Leftovers';
}
else if ((hasMove['Flail'] || hasMove['Reversal']) && !hasMove['Endure'])
{
item = 'FocusSash';
}
else if (ability === 'IronBarbs')
{
// only Iron Barbs for now
item = 'RockyHelmet';
}
else if ((template.baseStats.hp+75)*(template.baseStats.def+template.baseStats.spd+175) > 60000 || template.species === 'Skarmory' || template.species === 'Forretress')
{
// skarmory and forretress get exceptions for their typing
item = 'Leftovers';
}
else if (counter.Physical + counter.Special >= 3 && setupType)
{
item = 'LifeOrb';
}
else if (counter.Special >= 3 && setupType)
{
item = 'LifeOrb';
}
else if (counter.Physical + counter.Special >= 4)
{
item = 'ExpertBelt';
}
else if (i===0)
{
item = 'FocusSash';
}
// this is the "REALLY can't think of a good item" cutoff
// why not always Leftovers? Because it's boring. :P
else if (hasType['Flying'] || ability === 'Levitate')
{
item = 'Leftovers';
}
else if (this.getEffectiveness('Ground', template) >= 1 && ability !== 'Levitate')
{
item = 'AirBalloon';
}
else if (hasType['Poison'])
{
item = 'BlackSludge';
}
else if (counter.Status <= 1)
{
item = 'LifeOrb';
}
/* else if ((template.baseStats.hp+75)*(template.baseStats.def+template.baseStats.spd+175) > 50000)
{
item = 'Leftovers';
}
else if (this.getEffectiveness('Ground', template) >= 0)
{
item = 'AirBalloon';
} */
else
{
item = 'Leftovers';
}
if (item === 'Leftovers' && hasType['Poison'])
{
item = 'BlackSludge';
}
}
var levelScale = {
LC: 95,
Limbo: 95,
'LC Uber': 90,
NU: 90,
RU: 85,
BL2: 83,
UU: 80,
BL: 78,
OU: 75,
CAP: 74,
Unreleased: 75,
Uber: 70
};
var customScale = {
Meloetta: 78,
Caterpie: 99, Metapod: 99,
Weedle: 99, Kakuna: 99,
Hoppip: 99,
Wurmple: 99, Silcoon: 99, Cascoon: 99,
Feebas: 99,
Magikarp: 99
};
var level = levelScale[template.tier] || 90;
if (customScale[template.name]) level = customScale[template.name];
if (template.name === 'Chandelure' && ability === 'ShadowTag') level = 70;
if (template.name === 'Serperior' && ability === 'Contrary') level = 75;
if (template.name === 'Rotom-S' && item === 'Balloon') level = 95;
if (template.name === 'Magikarp' && hasMove['Magikarp\'sRevenge']) level = 85;
pokemon.push({
name: template.name,
moves: moves,
ability: ability,
evs: evs,
item: item,
level: level
});
pokemonLeft++;
}
return pokemon;
},
getNature: function(nature) {
if (typeof nature === 'string') nature = BattleNatures[nature];
if (!nature) nature = {};
return nature;
},
natureModify: function(stats, nature) {
if (typeof nature === 'string') nature = BattleNatures[nature];
if (!nature) return stats;
if (nature.plus) stats[nature.plus] *= 1.1;
if (nature.minus) stats[nature.minus] *= 0.9;
return stats;
}
};
var BattleNatures = {
Adamant: {plus:'atk', minus:'spa'},
Bashful: {},
Bold: {plus:'def', minus:'atk'},
Brave: {plus:'atk', minus:'spe'},
Calm: {plus:'spd', minus:'atk'},
Careful: {plus:'spd', minus:'spa'},
Docile: {},
Gentle: {plus:'spd', minus:'def'},
Hardy: {},
Hasty: {plus:'spe', minus:'def'},
Impish: {plus:'def', minus:'spa'},
Jolly: {plus:'spe', minus:'spa'},
Lax: {plus:'def', minus:'spd'},
Lonely: {plus:'atk', minus:'def'},
Mild: {plus:'spa', minus:'def'},
Modest: {plus:'spa', minus:'atk'},
Naive: {plus:'spe', minus:'spd'},
Naughty: {plus:'atk', minus:'spd'},
Quiet: {plus:'spa', minus:'spe'},
Quirky: {},
Rash: {plus:'spa', minus:'spd'},
Relaxed: {plus:'def', minus:'spe'},
Sassy: {plus:'spd', minus:'spe'},
Serious: {},
Timid: {plus:'spe', minus:'atk'},
};
var BattleScripts = exports.BattleScripts;