diff --git a/js/client-battle-tooltips.js b/js/client-battle-tooltips.js index 74942fca9..21eb945b7 100644 --- a/js/client-battle-tooltips.js +++ b/js/client-battle-tooltips.js @@ -1,4 +1,605 @@ var BattleTooltips = (function () { + + // In addition to some static methods: + // BattleTooltips.tooltipAttrs() + // BattleTooltips.showTooltip() + // BattleTooltips.hideTooltip() + // Most tooltips tend to involve a battle and/or room object + function BattleTooltips(battle, room) { + this.battle = battle; + this.room = room; + + // tooltips + var buf = ''; + var tooltips = { + your2: {top: 70, left: 250, width: 80, height: 100}, + your1: {top: 85, left: 320, width: 90, height: 100}, + your0: {top: 90, left: 390, width: 100, height: 100}, + my0: {top: 200, left: 130, width: 120, height: 160}, + my1: {top: 200, left: 250, width: 150, height: 160}, + my2: {top: 200, left: 350, width: 150, height: 160} + }; + for (var active in tooltips) { + buf += '
'; + } + room.$foeHint.html(buf); + } + + BattleTooltips.showTooltipFor = function (roomid, thing, type, elem, ownHeight) { + app.rooms[roomid].tooltips.showTooltip(thing, type, elem, ownHeight); + }; + BattleTooltips.hideTooltip = function () { + $('#tooltipwrapper').html(''); + }; + + // tooltips + BattleTooltips.prototype.tooltipAttrs = function (thing, type, ownHeight) { + var roomid = this.room.id; + return ' onmouseover="BattleTooltips.showTooltipFor(\'' + roomid + '\', \'' + Tools.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')" ontouchstart="BattleTooltips.showTooltipFor(\'' + roomid + '\', \'' + Tools.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')" onmouseout="BattleTooltips.hideTooltip()" onmouseup="BattleTooltips.hideTooltip()"'; + }; + + BattleTooltips.prototype.showTooltip = function (thing, type, elem, ownHeight) { + var room = this.room; + + var text = ''; + switch (type) { + case 'move': + var move = Tools.getMove(thing); + if (!move) return; + text = this.showMoveTooltip(move); + break; + + case 'pokemon': + var side = room.battle[thing.slice(0, -1) + "Side"]; + var pokemon = side.active[thing.slice(-1)]; + if (!pokemon) return; + /* falls through */ + case 'sidepokemon': + var myPokemon; + var isActive = (type === 'pokemon'); + if (room.myPokemon) { + if (!pokemon) { + myPokemon = room.myPokemon[parseInt(thing, 10)]; + pokemon = myPokemon; + } else if (room.controlsShown && pokemon.side === room.battle.mySide) { + // battlePokemon = pokemon; + myPokemon = room.myPokemon[pokemon.slot]; + } + } + text = this.showPokemonTooltip(pokemon, myPokemon, isActive); + break; + } + + var offset = { + left: 150, + top: 500 + }; + if (elem) offset = $(elem).offset(); + var x = offset.left - 2; + if (elem) { + if (ownHeight) offset = $(elem).offset(); + else offset = $(elem).parent().offset(); + } + var y = offset.top - 5; + + if (x > room.leftWidth + 335) x = room.leftWidth + 335; + if (y < 140) y = 140; + if (x > $(window).width() - 303) x = Math.max($(window).width() - 303, 0); + + if (!$('#tooltipwrapper').length) $(document.body).append(''); + $('#tooltipwrapper').css({ + left: x, + top: y + }); + $('#tooltipwrapper').html(text).appendTo(document.body); + if (elem) { + var height = $('#tooltipwrapper .tooltip').height(); + if (height > y) { + y += height + 10; + if (ownHeight) y += $(elem).height(); + else y += $(elem).parent().height(); + $('#tooltipwrapper').css('top', y); + } + } + }; + + BattleTooltips.prototype.hideTooltip = function () { + BattleTooltips.hideTooltip(); + }; + + BattleTooltips.prototype.showMoveTooltip = function (move) { + var text = ''; + var basePower = move.basePower; + var basePowerText = ''; + var additionalInfo = ''; + var yourActive = this.battle.yourSide.active; + var pokemon = this.battle.mySide.active[this.room.choice.choices.length]; + + // Check if there are more than one active Pokémon to check for multiple possible BPs. + if (yourActive.length > 1) { + // We check if there is a difference in base powers to note it. + // Otherwise, it is just shown as in singles. + // The trick is that we need to calculate it first for each Pokémon to see if it changes. + var previousBasepower = false; + var difference = false; + var basePowers = []; + for (var i = 0; i < yourActive.length; i++) { + if (!yourActive[i]) continue; + basePower = this.getMoveBasePower(move, pokemon, yourActive[i]); + if (previousBasepower === false) previousBasepower = basePower; + if (previousBasepower !== basePower) difference = true; + if (!basePower) basePower = '—'; + basePowers.push('Base power for ' + yourActive[i].name + ': ' + basePower); + } + if (difference) { + basePowerText = '' + basePowers.join('
') + '
Base power: ' + basePower + '
'; + } + var accuracy = move.accuracy; + if (!accuracy || accuracy === true) accuracy = '—'; + else accuracy = '' + accuracy + '%'; + + // Handle move type for moves that vary their type. + var moveType = this.getMoveType(move, pokemon); + + // Deal with Nature Power special case, indicating which move it calls. + if (move.id === 'naturepower') { + if (this.battle.gen === 6) { + additionalInfo = 'Calls '; + if (this.battle.hasPseudoWeather('Electric Terrain')) { + additionalInfo += Tools.getTypeIcon('Electric') + ' Thunderbolt'; + } else if (this.battle.hasPseudoWeather('Grassy Terrain')) { + additionalInfo += Tools.getTypeIcon('Grass') + ' Energy Ball'; + } else if (this.battle.hasPseudoWeather('Misty Terrain')) { + additionalInfo += Tools.getTypeIcon('Fairy') + ' Moonblast'; + } else { + additionalInfo += Tools.getTypeIcon('Normal') + ' Tri Attack'; + } + } else if (this.battle.gen > 3) { + // In gens 4 and 5 it calls Earthquake. + additionalInfo = 'Calls ' + Tools.getTypeIcon('Ground') + ' Earthquake'; + } else { + // In gen 3 it calls Swift, so it retains its normal typing. + additionalInfo = 'Calls ' + Tools.getTypeIcon('Normal') + ' Swift'; + } + } + + text = '' + additionalInfo + '
'; + text += 'Accuracy: ' + accuracy + '
'; + var flags = { + "authentic": "Ignores a target's substitute.", + "bite": "Power is multiplied by 1.5 when used by a Pokemon with the Ability Strong Jaw.", + "bullet": "Has no effect on Pokemon with the Ability Bulletproof.", + "charge": "The user is unable to make a move between turns.", + "contact": "Makes contact.", + "defrost": "Thaws the user if executed successfully while the user is frozen.", + "distance": "Can target a Pokemon positioned anywhere in a Triple Battle.", + "gravity": "Prevented from being executed or selected during Gravity's effect.", + "heal": "Prevented from being executed or selected during Heal Block's effect.", + "mirror": "Can be copied by Mirror Move.", + "nonsky": "Prevented from being executed or selected in a Sky Battle.", + "powder": "Has no effect on Grass-type Pokemon, Pokemon with the Ability Overcoat, and Pokemon holding Safety Goggles.", + "protect": "Blocked by Detect, Protect, ", + "pulse": "Power is multiplied by 1.5 when used by a Pokemon with the Ability Mega Launcher.", + "punch": "Power is multiplied by 1.2 when used by a Pokemon with the Ability Iron Fist.", + "recharge": "If this move is successful, the user must recharge on the following turn and cannot make a move.", + "reflectable": "Bounced back to the original user by Magic Coat or the Ability Magic Bounce.", + "snatch": "Can be stolen from the original user and instead used by another Pokemon using Snatch.", + "sound": "Has no effect on Pokemon with the Ability Soundproof." + }; + if (move.desc) { + text += '' + move.desc; + for (var i in move.flags) { + if (i === 'distance' && move.target !== 'any') continue; + text += " " + flags[i]; + if (i === 'protect') { + if (move.category !== 'Status') text += "King's Shield, "; + text += "and Spiky Shield."; + } + } + var priority = move.priority; + if (priority) { + text += " Priority "; + if (priority > 0) text += " + "; + text += priority + "."; + } + text += '
'; + } + text += 'HP: (fainted)
'; + } else { + var exacthp = ''; + if (myPokemon) exacthp = ' (' + myPokemon.hp + '/' + myPokemon.maxhp + ')'; + else if (pokemon.maxhp == 48) exacthp = ' (' + pokemon.hp + '/' + pokemon.maxhp + ' pixels)'; + text += 'HP: ' + pokemon.hpDisplay() + exacthp + (pokemon.status ? ' ' + pokemon.status.toUpperCase() + '' : '') + '
'; + } + var showOtherSees = isActive; + if (myPokemon) { + if (this.battle.gen > 2) { + text += 'Ability: ' + Tools.getAbility(myPokemon.baseAbility).name; + if (myPokemon.item) { + text += ' / Item: ' + Tools.getItem(myPokemon.item).name; + } + text += '
'; + } else if (myPokemon.item) { + text += 'Item: ' + Tools.getItem(myPokemon.item).name + '
'; + } + text += '' + myPokemon.stats['atk'] + ' Atk / ' + myPokemon.stats['def'] + ' Def / ' + myPokemon.stats['spa']; + if (this.battle.gen === 1) { + text += ' Spc / '; + } else { + text += ' SpA / ' + myPokemon.stats['spd'] + ' SpD / '; + } + text += myPokemon.stats['spe'] + ' Spe
'; + if (isActive) text += 'Opponent sees:
'; + } else { + showOtherSees = true; + } + if (this.battle.gen > 2 && showOtherSees) { + if (!pokemon.baseAbility && !pokemon.ability) { + if (template.abilities) { + text += 'Possible abilities: ' + Tools.getAbility(template.abilities['0']).name; + if (template.abilities['1']) text += ', ' + Tools.getAbility(template.abilities['1']).name; + if (this.battle.gen > 4 && template.abilities['H']) text += ', ' + Tools.getAbility(template.abilities['H']).name; + text += '
'; + } + } else if (pokemon.ability) { + text += 'Ability: ' + Tools.getAbility(pokemon.ability).name + '
'; + } else if (pokemon.baseAbility) { + text += 'Ability: ' + Tools.getAbility(pokemon.baseAbility).name + '
'; + } + } + + if (showOtherSees) { + var item = ''; + var itemEffect = pokemon.itemEffect || ''; + if (pokemon.prevItem) { + item = 'None'; + if (itemEffect) itemEffect += '; '; + var prevItem = Tools.getItem(pokemon.prevItem).name; + itemEffect += pokemon.prevItemEffect ? prevItem + ' was ' + pokemon.prevItemEffect : 'was ' + prevItem; + } + if (pokemon.item) item = Tools.getItem(pokemon.item).name; + if (itemEffect) itemEffect = ' (' + itemEffect + ')'; + if (item) text += 'Item: ' + item + itemEffect + '
'; + + if (template.baseStats) { + text += '' + this.getTemplateMinSpeed(template, pokemon.level) + ' to ' + this.getTemplateMaxSpeed(template, pokemon.level) + ' Spe (before items/abilities/modifiers)
'; + } + } + + if (myPokemon && !isActive) { + text += '';
+ var battlePokemon = this.battle.getPokemon('old: ' + pokemon.ident, pokemon.details);
+ for (var i = 0; i < myPokemon.moves.length; i++) {
+ var move = Tools.getMove(myPokemon.moves[i]);
+ var name = move.name;
+ var pp = 0, maxpp = 0;
+ if (battlePokemon && battlePokemon.moveTrack) {
+ for (var j = 0; j < battlePokemon.moveTrack.length; j++) {
+ if (name === battlePokemon.moveTrack[j][0]) {
+ maxpp = Math.floor(move.pp * 8 / 5);
+ pp = maxpp - battlePokemon.moveTrack[j][1];
+ break;
+ }
+ }
+ }
+ text += '• ' + name + (maxpp ? ' (' + pp + '/' + maxpp + ')' : '') + '
';
+ }
+ text += '
';
+ for (var i = 0; i < pokemon.moveTrack.length; i++) {
+ var moveName = pokemon.moveTrack[i][0];
+ var move, maxpp;
+ if (moveName.charAt(0) === '*') {
+ moveName = moveName.substr(1);
+ move = Tools.getMove(moveName);
+ maxpp = 5;
+ } else {
+ move = Tools.getMove(moveName);
+ maxpp = Math.floor(move.pp * 8 / 5);
+ }
+ var pp = maxpp - pokemon.moveTrack[i][1];
+ text += '• ' + move.name + ' (' + pp + '/' + maxpp + ')
';
+ }
+ text += '