Add stat boosts in tooltip

Created a new line displayed in a pokemonToolTip which mirrors the line showing the pokemon's stats. However, this new line contians stats mulitplied by their respective boosts, following Pokemon's in-game formula.

Update client-battle-tooltips.js
Update battle.js to include Unburden as a volatile. Previously, there was no way for the client to know that a Pokemon had boosted speed from Unburden. The Unburden volatile is added to a pokemon regardless of that Pokemon's ability. This is because the Pokmeon object passed into Battle.runMinor in battle.js does not have an Ability so there is no way to check if the Pokemon has Unburden.

Made all changes suggested by Zarel: Only show modified stats if there is a modified stat; bold any boosted stats; re-structure findBoost into the improved calculateModifiedStat which finds the whole group of stats rather than just one stat; include modifiers from: paralysis, burn, unburden, pure power, slow start, defeatist, swift swim, unburden, choice items, iron ball, light ball, and a rock type's Special Defense boost in the sand.

The code written does not support Gen 1 methods of stat calculation. That is a feature which requires far more research on my end in order to implement.

I am an inexperienced Javascript developer (I had never written anything in Javascript prior to starting to work on this feature a few days ago), so feel free to let me know how I could do things better.
This commit is contained in:
Brandon Geren
2016-03-08 00:01:42 -06:00
parent 385c284c96
commit f92fbeb478
2 changed files with 268 additions and 3 deletions

View File

@@ -2162,6 +2162,7 @@ var Side = (function () {
electrify: '<span class="bad">Electrify</span>',
ragepowder: '<span class="good">Rage&nbsp;Powder</span>',
followme: '<span class="good">Follow&nbsp;Me</span>',
itemremoved: '',
// Gen 1
lightscreen: '<span class="good">Light&nbsp;Screen</span>',
reflect: '<span class="good">Reflect</span>'
@@ -4240,7 +4241,7 @@ var Battle = (function () {
poke.prevItem = item.name;
poke.prevItemEffect = '';
poke.removeVolatile('airballoon');
poke.addVolatile('itemremoved');
if (kwargs.silent) {
// do nothing
} else if (kwargs.eat) {
@@ -6013,6 +6014,7 @@ var Battle = (function () {
var poke = this.getPokemon('inactive: ' + args[1], args[2]);
var slot = poke.slot;
poke.healthParse(args[3]);
poke.removeVolatile('itemremoved');
if (args[0] === 'switch') {
if (poke.side.active[slot]) {
poke.side.switchOut(poke.side.active[slot]);

View File

@@ -334,7 +334,8 @@ var BattleTooltips = (function () {
}
text += '</p>';
} else if (myPokemon.item) {
text += '<p>Item: ' + Tools.getItem(myPokemon.item).name + '</p>';
item = Tools.getItem(myPokemon.item).name;
text += '<p>Item: ' + item + '</p>';
}
text += '<p>' + myPokemon.stats['atk'] + '&nbsp;Atk /&nbsp;' + myPokemon.stats['def'] + '&nbsp;Def /&nbsp;' + myPokemon.stats['spa'];
if (this.battle.gen === 1) {
@@ -343,7 +344,15 @@ var BattleTooltips = (function () {
text += '&nbsp;SpA /&nbsp;' + myPokemon.stats['spd'] + '&nbsp;SpD /&nbsp;';
}
text += myPokemon.stats['spe'] + '&nbsp;Spe</p>';
if (isActive) text += '<p class="section">Opponent sees:</p>';
if (isActive) {
var modifiedStats = this.calculateModifiedStats(pokemon, myPokemon);
var statsText = this.makeModifiedStatText(myPokemon, modifiedStats);
if (statsText.match('<b')) {
text += '<p>After Modifiers:</p>';
text += statsText;
}
text += '<p class="section">Opponent sees:</p>';
}
} else {
showOtherSees = true;
}
@@ -409,6 +418,260 @@ var BattleTooltips = (function () {
return text;
};
BattleTooltips.prototype.findModifier = function (pokemon, myPokemon, statName) {
var modifier = 1;
if (pokemon.boosts) {
if (pokemon.boosts[statName]) {
if (pokemon.boosts[statName] > 0) {
var goodBoostTable = [1, 1.5, 2, 2.5, 3, 3.5, 4];
// var goodBoostTable = ['Normal', '+1', '+2', '+3', '+4', '+5', '+6'];
modifier = goodBoostTable[pokemon.boosts[statName]];
} else {
var badBoostTable = [1, 0.67, 0.5, 0.4, 0.33, 0.29, 0.25];
// var badBoostTable = ['Normal', '&minus;1', '&minus;2', '&minus;3', '&minus;4', '&minus;5', '&minus;6'];
modifier = badBoostTable[-pokemon.boosts[statName]];
}
}
}
// check for burn, paralysis, guts, quick feet
if (pokemon.status) {
if (statName === 'atk') {
if (this.battle.gen > 2 && pokemon.ability === 'Guts') {
modifier = modifier * 1.5;
} else if (pokemon.status === 'brn') {
modifier = modifier * 0.5;
}
}
if (statName === 'spe') {
if (this.battle.gen > 2 && pokemon.ability === 'Quick Feet') {
modifier = modifier * 1.5;
} else if (pokemon.status === 'par') {
modifier = modifier * 0.25;
}
}
}
// gen 1 stuff doesn’t work yet
if (this.battle.gen > 1) {
var item = '';
if (myPokemon.item) {
item = Tools.getItem(myPokemon.item).name;
}
// check for light ball, thick club, metal/quick powder
// the only stat modifying items in gen 2 were light ball, thick club, metal powder. gen 1 had none
if (item === 'Light Ball') {
if (pokemon.baseSpecies === 'Pikachu' && (statName === 'atk' || statName === 'spa')) {
modifier = modifier * 2;
}
}
if (item === 'Thick Club') {
if ((pokemon.baseSpecies === 'Marowak' || pokemon.baseSpecies === 'Cubone') && statName === 'atk') {
modifier = modifier * 2;
}
}
if (pokemon.baseSpecies === 'Ditto') {
if (!('transform' in pokemon.volatiles)) {
if (item === 'Quick Powder' && statName === 'spe') {
modifier = modifier * 2;
}
if (item === 'Metal Powder' && (statName === 'spd' || statName === 'def')) {
modifier = modifier * 1.5;
}
}
}
// check abilities other than Guts and Quick Feet
// check items other than light ball, thick club, metal/quick powder
if (this.battle.gen > 2) {
var ability = Tools.getAbility(myPokemon.baseAbility).name;
if (this.battle.weather) {
// Check if you have an anti weather ability
var noWeatherAbility = !!(ability in {'Air Lock': 1, 'Cloud Nine': 1});
// If you don't, check if the opponent has it afterwards.
if (!noWeatherAbility) {
for (var i = 0; i < this.battle.yourSide.active.length; i++) {
if (this.battle.yourSide.active[i] && this.battle.yourSide.active[i].ability in {'Air Lock': 1, 'Cloud Nine': 1}) {
noWeatherAbility = true;
break;
}
}
}
}
if (item === 'Choice Band' && statName === 'atk') {
modifier = modifier * 1.5;
}
if (ability === 'Toxic Boost' && (pokemon.status === 'tox' || pokemon.status === 'psn') && statName === 'atk') {
modifier = modifier * 1.5;
}
if (ability === 'Pure Power' || pokemon.ability === 'Huge Power' && statName === 'atk') {
modifier = modifier * 2;
}
if (ability === 'Hustle' && statName === 'atk') {
modifier = modifier * 1.5;
}
if (this.battle.weather) {
if (!noWeatherAbility) {
if (ability === 'Flower Gift' && (statName === 'atk' || statName === 'spd') && (this.battle.weather === 'sunnyday' || this.battle.weather === 'desolateland')) {
modifier = modifier * 1.5;
}
if (ability === 'Solar Power' && (this.battle.weather === 'sunnyday' || this.battle.weather === 'desolateland') && statName === 'spa') {
modifier = modifier * 1.5;
}
if ((pokemon.types[0] === 'Rock' || pokemon.types[1] === 'Rock') && this.battle.weather === 'sandstorm' && statName === 'spd') {
modifier = modifier * 1.5;
}
if (statName === 'spe') {
if (ability === 'Chlorophyll' && (this.battle.weather === 'sunnyday' || this.battle.weather === 'desolateland')) {
modifier = modifier * 2;
}
if (ability === 'Swift Swim' && (this.battle.weather === 'raindance' || this.battle.weather === 'primordialsea')) {
modifier = modifier * 2;
}
if (ability === 'Sand Rush' && this.battle.weather === 'sandstorm') {
modifier = modifier * 2;
}
}
}
}
if (ability === 'Defeatist' && (statName === 'atk' || statName === 'spa') && myPokemon.hp <= myPokemon.maxhp / 2) {
modifier = modifier * 0.5;
}
if (pokemon.volatiles) {
for (var volatile in pokemon.volatiles) {
if (volatile === 'slowstart' && (statName === 'atk' || statName === 'spe')) {
modifier = modifier * 0.5;
}
if (ability === 'Unburden' && volatile === 'itemremoved' && statName === 'spe') {
modifier = modifier * 2;
}
}
}
if (ability === 'Marvel Scale' && pokemon.status && statName === 'def') {
modifier = modifier * 1.5;
}
if (item === 'Eviolite' && (statName === 'def' || statName === 'spd')) {
if (Tools.getTemplate(pokemon.name).evos) {
modifier = modifier * 1.5;
}
}
if (this.battle.hasPseudoWeather('Grassy Terrain') && ability === 'Grass Pelt' && statName === 'def') {
modifier = modifier * 1.5;
}
if (item === 'Choice Specs' && statName === 'spa') {
modifier = modifier * 1.5;
}
if (ability === 'Flare Boost' && pokemon.status === 'brn' && statName === 'spa') {
modifier = modifier * 1.5;
}
if (item === 'DeepSeaTooth' && pokemon.baseSpecies === 'Clamperl' && statName === 'spa') {
modifier = modifier * 2;
}
if (item === 'Soul Dew' && (pokemon.baseSpecies === 'Latios' || pokemon.baseSpecies === 'Latias') && (statName === 'spa' || statName === 'spd')) {
modifier = modifier * 1.5;
}
if ((ability === 'Plus' || ability === 'Minus') && statName === 'spa') {
var allyActive = pokemon.side.active;
if (allyActive.length > 1) {
if (this.battle.gen > 4) {
for (var i = 0; i < allyActive.length; i++) {
if (allyActive[i] && allyActive[i].searchid !== pokemon.searchid && !allyActive[i].fainted && (allyActive[i].ability === 'Plus' || allyActive[i].ability === 'Minus')) {
modifier = modifier * 1.5;
break;
}
}
} else {
if (ability === 'Plus') {
for (var i = 0; i < allyActive.length; i++) {
if (allyActive[i] && !allyActive[i].fainted && allyActive[i].ability === 'Minus') {
modifier = modifier * 1.5;
}
}
}
if (ability === 'Minus') {
for (var i = 0; i < allyActive.length; i++) {
if (allyActive[i] && !allyActive[i].fainted && allyActive[i].ability === 'Plus') {
modifier = modifier * 1.5;
}
}
}
}
}
}
if (item === 'Assault Vest' && statName === 'spd') {
modifier = modifier * 1.5;
}
if (item === 'DeepSeaScale' && statName === 'spd') {
if (pokemon.baseSpecies === 'Clamperl') {
modifier = modifier * 2;
}
}
if (item === 'Choice Scarf' && statName === 'spe') {
modifier = modifier * 1.5;
}
if ((item === 'Iron Ball' || item === 'Macho Brace') && statName === 'spe') {
modifier = modifier * 0.5;
}
}
}
return modifier;
};
BattleTooltips.prototype.calculateModifiedStats = function (pokemon, myPokemon) {
var modifiedStats = {};
if (this.battle.gen === 1) {
modifiedStats = {atk: Math.max(Math.floor(myPokemon.stats['atk'] * this.findModifier(pokemon, myPokemon, 'atk')), 999),
def: Math.max(Math.floor(myPokemon.stats['def'] * this.findModifier(pokemon, myPokemon, 'def')), 999),
spa: Math.max(Math.floor(myPokemon.stats['spa'] * this.findModifier(pokemon, myPokemon, 'spa')), 999),
spe: Math.max(Math.floor(myPokemon.stats['spe'] * this.findModifier(pokemon, myPokemon, 'spe')), 999)};
} else {
modifiedStats = {atk: Math.floor(myPokemon.stats['atk'] * this.findModifier(pokemon, myPokemon, 'atk')),
def: Math.floor(myPokemon.stats['def'] * this.findModifier(pokemon, myPokemon, 'def')),
spa: Math.floor(myPokemon.stats['spa'] * this.findModifier(pokemon, myPokemon, 'spa')),
spd: Math.floor(myPokemon.stats['spd'] * this.findModifier(pokemon, myPokemon, 'spd')),
spe: Math.floor(myPokemon.stats['spe'] * this.findModifier(pokemon, myPokemon, 'spe'))};
}
return modifiedStats;
};
BattleTooltips.prototype.makeModifiedStatText = function (myPokemon, modifiedStats) {
var statsText = '<p>';
var statTable = {atk: '&nbsp;Atk /&nbsp;', def: '&nbsp;Def /&nbsp;', spa: '&nbsp;SpA /&nbsp;',
spc: '&nbsp;Spc /&nbsp;', spd: '&nbsp;SpD /&nbsp;', spe: '&nbsp;Spe</p>'};
statsText += this.boldModifiedStat(myPokemon, modifiedStats, 'atk') + statTable['atk'];
statsText += this.boldModifiedStat(myPokemon, modifiedStats, 'def') + statTable['def'];
statsText += this.boldModifiedStat(myPokemon, modifiedStats, 'spa');
if (this.battle.gen === 1) {
statsText += statTable['spc'];
} else {
statsText += statTable['spa'];
statsText += this.boldModifiedStat(myPokemon, modifiedStats, 'spd') + statTable['spd'];
}
statsText += this.boldModifiedStat(myPokemon, modifiedStats, 'spe') + statTable['spe'];
return statsText;
};
BattleTooltips.prototype.boldModifiedStat = function (myPokemon, modifiedStats, statName) {
var statText = '';
if (myPokemon.stats[statName] === modifiedStats[statName]) {
statText += '' + modifiedStats[statName];
} else {
statText += '<b>' + modifiedStats[statName] + '</b>';
}
return statText;
};
BattleTooltips.prototype.getPPUseText = function (moveTrackRow, showKnown) {
var moveName = moveTrackRow[0];
var ppUsed = moveTrackRow[1];