From f92fbeb4789f9580043830fa79744b2cfeb332c9 Mon Sep 17 00:00:00 2001 From: Brandon Geren Date: Tue, 8 Mar 2016 00:01:42 -0600 Subject: [PATCH] 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. --- js/battle.js | 4 +- js/client-battle-tooltips.js | 267 ++++++++++++++++++++++++++++++++++- 2 files changed, 268 insertions(+), 3 deletions(-) diff --git a/js/battle.js b/js/battle.js index a50db497b..27b81d126 100644 --- a/js/battle.js +++ b/js/battle.js @@ -2162,6 +2162,7 @@ var Side = (function () { electrify: 'Electrify', ragepowder: 'Rage Powder', followme: 'Follow Me', + itemremoved: '', // Gen 1 lightscreen: 'Light Screen', reflect: 'Reflect' @@ -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]); diff --git a/js/client-battle-tooltips.js b/js/client-battle-tooltips.js index 09661cbaa..6c64b7826 100644 --- a/js/client-battle-tooltips.js +++ b/js/client-battle-tooltips.js @@ -334,7 +334,8 @@ var BattleTooltips = (function () { } text += '

'; } else if (myPokemon.item) { - text += '

Item: ' + Tools.getItem(myPokemon.item).name + '

'; + item = Tools.getItem(myPokemon.item).name; + text += '

Item: ' + item + '

'; } text += '

' + myPokemon.stats['atk'] + ' Atk / ' + myPokemon.stats['def'] + ' Def / ' + myPokemon.stats['spa']; if (this.battle.gen === 1) { @@ -343,7 +344,15 @@ var BattleTooltips = (function () { text += ' SpA / ' + myPokemon.stats['spd'] + ' SpD / '; } text += myPokemon.stats['spe'] + ' Spe

'; - if (isActive) text += '

Opponent sees:

'; + if (isActive) { + var modifiedStats = this.calculateModifiedStats(pokemon, myPokemon); + var statsText = this.makeModifiedStatText(myPokemon, modifiedStats); + if (statsText.match(' 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', '−1', '−2', '−3', '−4', '−5', '−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 = '

'; + var statTable = {atk: ' Atk / ', def: ' Def / ', spa: ' SpA / ', + spc: ' Spc / ', spd: ' SpD / ', spe: ' Spe

'}; + 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 += '' + modifiedStats[statName] + ''; + } + return statText; + }; + BattleTooltips.prototype.getPPUseText = function (moveTrackRow, showKnown) { var moveName = moveTrackRow[0]; var ppUsed = moveTrackRow[1];