Massively improve tooltips

- Tooltips work in replays now
- Calculation is better (correctly handles more corner cases)
- Explanations are better (better messages for Magic Room etc)
- Tooltips for sidebar pokemon
- Support "locking" tooltips with long-click / long-tap
- Can copy/paste from locked tooltips
- Increased font size
This commit is contained in:
Guangcong Luo 2019-02-18 17:25:53 -06:00
parent 4fec9031e5
commit 434afefeaa
15 changed files with 1841 additions and 202 deletions

View File

@ -12,3 +12,4 @@ node_modules/
/js/battle-animations-moves.js
/js/battle-scene-stub.js
/js/battle-animations.js
/js/battle-tooltips.js

View File

@ -23,6 +23,7 @@ module.exports = {
"BattleFormats": false, "BattleFormatsData": false, "BattleLearnsets": false, "BattleItems": false, "BattleMoveAnims": false, "BattleMovedex": false, "BattleNatures": false,
"BattleOtherAnims": false, "BattlePokedex": false,"BattlePokemonSprites": false, "BattlePokemonSpritesBW": false, "BattleSearchCountIndex": false, "BattleSearchIndex": false, "BattleArticleTitles": false,
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false,
"ModifiableValue": false,
// Generic global variables
"Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Dex": false,

1
.gitignore vendored
View File

@ -21,6 +21,7 @@ package-lock.json
/js/battle-dex-data.js
/js/battle-animations-moves.js
/js/battle-animations.js
/js/battle-tooltips.js
/js/battle-scene-stub.js
.vscode

View File

@ -124,21 +124,27 @@ exports.BattleText = {
// stats
hp: {
statName: "HP",
statShortName: "HP",
},
atk: {
statName: "Attack",
statShortName: "Atk",
},
def: {
statName: "Defense",
statShortName: "Def",
},
spa: {
statName: "Special Attack",
statShortName: "SpA",
},
spd: {
statName: "Special Defense",
statShortName: "SpD",
},
spe: {
statName: "Speed",
statShortName: "Spe",
},
accuracy: {
statName: "accuracy",
@ -148,6 +154,7 @@ exports.BattleText = {
},
spc: {
statName: "Special",
statShortName: "Spc",
},
stats: {
statName: "stats",

View File

@ -134,7 +134,7 @@ ga('send', 'pageview');
<script src="//play.pokemonshowdown.com/js/client-ladder.js?"></script>
<script src="//play.pokemonshowdown.com/js/client-chat.js?"></script>
<script src="//play.pokemonshowdown.com/js/client-chat-tournament.js?"></script>
<script src="//play.pokemonshowdown.com/js/client-battle-tooltips.js?"></script>
<script src="//play.pokemonshowdown.com/js/battle-tooltips.js?"></script>
<script src="//play.pokemonshowdown.com/js/client-battle.js?"></script>
<script src="//play.pokemonshowdown.com/js/client-rooms.js?"></script>
<script src="//play.pokemonshowdown.com/data/graphics.js?"></script>

View File

@ -23,7 +23,8 @@
BattleSound.setMute(Dex.prefs('mute'));
this.battle = new Battle(this.$battle, this.$chatFrame, this.id);
this.tooltips = new BattleTooltips(this.battle, this);
this.tooltips = this.battle.scene.tooltips;
this.tooltips.listen(this.$controls);
this.battle.roomid = this.id;
this.battle.joinButtons = true;
@ -333,7 +334,7 @@
act = this.request.requestType;
if (this.request.side) {
switchables = this.myPokemon;
switchables = this.battle.myPokemon;
}
if (!this.finalDecision) this.finalDecision = !!this.request.noCancel;
}
@ -407,7 +408,7 @@
// Request full team order if one of our Pokémon has Illusion
for (var i = 0; i < switchables.length && i < 6; i++) {
if (toId(switchables[i].baseAbility) === 'illusion') {
this.choice.count = this.myPokemon.length;
this.choice.count = this.battle.myPokemon.length;
}
}
if (this.battle.teamPreviewCount) {
@ -491,7 +492,7 @@
app.addPopup(TimerPopup, {room: this});
},
updateMoveControls: function (type) {
var switchables = this.request && this.request.side ? this.myPokemon : [];
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
if (type !== 'movetarget') {
while (switchables[this.choice.choices.length] && switchables[this.choice.choices.length].fainted && this.choice.choices.length + 1 < this.battle.mySide.active.length) {
@ -555,7 +556,7 @@
} else if (!pokemon || pokemon.fainted) {
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> ';
} else {
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"' + this.tooltips.tooltipAttrs("your" + i, 'pokemon', true) + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.species : BattleLog.escapeHTML(pokemon.name)) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '" class="has-tooltip" data-tooltip="activepokemon|1|' + i + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.species : BattleLog.escapeHTML(pokemon.name)) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
for (var i = 0; i < myActive.length; i++) {
@ -574,7 +575,7 @@
} else if (!pokemon || pokemon.fainted) {
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> ';
} else {
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '" class="has-tooltip" data-tooltip="activepokemon|0|' + i + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
@ -593,6 +594,7 @@
var hasMoves = false;
var moveMenu = '';
var movebuttons = '';
var typeValueTracker = new ModifiableValue(this.battle, this.battle.mySide.active[pos], this.battle.myPokemon[pos]);
for (var i = 0; i < curActive.moves.length; i++) {
var moveData = curActive.moves[i];
var move = Dex.getMove(moveData.move);
@ -602,11 +604,11 @@
if (move.id === 'Struggle' || move.id === 'Recharge') pp = '&ndash;';
if (move.id === 'Recharge') move.type = '&ndash;';
if (name.substr(0, 12) === 'Hidden Power') name = 'Hidden Power';
var moveType = this.tooltips.getMoveType(move, this.battle.mySide.active[pos] || this.myPokemon[pos]);
var moveType = this.tooltips.getMoveType(move, typeValueTracker)[0];
if (moveData.disabled) {
movebuttons += '<button disabled="disabled"' + this.tooltips.tooltipAttrs(moveData.move, 'move') + '>';
movebuttons += '<button disabled="disabled" class="has-tooltip" data-tooltip="move|' + move.id + '|' + pos + '">';
} else {
movebuttons += '<button class="type-' + moveType + '" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(moveData.move) + '" data-target="' + BattleLog.escapeHTML(moveData.target) + '"' + this.tooltips.tooltipAttrs(moveData.move, 'move') + '>';
movebuttons += '<button class="type-' + moveType + ' has-tooltip" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(moveData.move) + '" data-target="' + BattleLog.escapeHTML(moveData.target) + '" data-tooltip="move|' + name + '|' + pos + '">';
hasMoves = true;
}
movebuttons += name + '<br /><small class="type">' + (moveType ? Dex.getType(moveType).name : "Unknown") + '</small> <small class="pp">' + pp + '</small>&nbsp;</button> ';
@ -619,9 +621,9 @@
for (var i = 0; i < curActive.moves.length; i++) {
var moveData = curActive.moves[i];
var move = Dex.getMove(moveData.move);
var moveType = this.tooltips.getMoveType(move, this.battle.mySide.active[pos] || this.myPokemon[pos]);
var moveType = this.tooltips.getMoveType(move, typeValueTracker)[0];
if (canZMove[i]) {
movebuttons += '<button class="type-' + moveType + '" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(canZMove[i].move) + '" data-target="' + BattleLog.escapeHTML(canZMove[i].target) + '"' + this.tooltips.tooltipAttrs(moveData.move, 'zmove') + '>';
movebuttons += '<button class="type-' + moveType + ' has-tooltip" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(canZMove[i].move) + '" data-target="' + BattleLog.escapeHTML(canZMove[i].target) + '" data-tooltip="zmove|' + move.id + '|' + pos + '">';
movebuttons += canZMove[i].move + '<br /><small class="type">' + (moveType ? Dex.getType(moveType).name : "Unknown") + '</small> <small class="pp">1/1</small>&nbsp;</button> ';
} else {
movebuttons += '<button disabled="disabled">&nbsp;</button>';
@ -663,9 +665,9 @@
var pokemon = switchables[i];
pokemon.name = pokemon.ident.substr(4);
if (pokemon.fainted || i < this.battle.mySide.active.length || this.choice.switchFlags[i]) {
switchMenu += '<button class="disabled" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (pokemon.hp ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
switchMenu += '<button class="disabled has-tooltip" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '" data-tooltip="switchpokemon|' + i + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (pokemon.hp ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
} else {
switchMenu += '<button name="chooseSwitch" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
switchMenu += '<button name="chooseSwitch" value="' + i + '" class="has-tooltip" data-tooltip="switchpokemon|' + i + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
if (this.finalDecisionSwitch && this.battle.gen > 2) {
@ -696,7 +698,7 @@
}
}
var switchables = this.request && this.request.side ? this.myPokemon : [];
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
var myActive = this.battle.mySide.active;
var requestTitle = '';
@ -710,13 +712,13 @@
requestTitle += "Which Pokémon will it switch in for?";
var controls = '<div class="switchmenu" style="display:block">';
for (var i = 0; i < myActive.length; i++) {
var pokemon = this.myPokemon[i];
var pokemon = this.battle.myPokemon[i];
if (pokemon && !pokemon.fainted || this.choice.switchOutFlags[i]) {
controls += '<button disabled' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
controls += '<button disabled class="has-tooltip" data-tooltip="switchpokemon|' + i + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
} else if (!pokemon) {
controls += '<button disabled></button> ';
} else {
controls += '<button name="chooseSwitchTarget" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
controls += '<button name="chooseSwitchTarget" value="' + i + '" class="has-tooltip" data-tooltip="switchpokemon|' + i + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
controls += '</div>';
@ -737,9 +739,9 @@
for (var i = 0; i < switchables.length; i++) {
var pokemon = switchables[i];
if (pokemon.fainted || i < this.battle.mySide.active.length || this.choice.switchFlags[i]) {
switchMenu += '<button class="disabled" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '>';
switchMenu += '<button class="disabled has-tooltip" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '" data-tooltip="switchpokemon|' + i + '">';
} else {
switchMenu += '<button name="chooseSwitch" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '>';
switchMenu += '<button name="chooseSwitch" value="' + i + '" class="has-tooltip" data-tooltip="switchpokemon|' + i + '">';
}
switchMenu += '<span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
}
@ -760,7 +762,7 @@
}
},
updateTeamControls: function (type) {
var switchables = this.request && this.request.side ? this.myPokemon : [];
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
var maxIndex = Math.min(switchables.length, 24);
var requestTitle = "";
@ -775,9 +777,9 @@
var oIndex = this.choice.teamPreview[i] - 1;
var pokemon = switchables[oIndex];
if (i < this.choice.done) {
switchMenu += '<button disabled="disabled"' + this.tooltips.tooltipAttrs(oIndex, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> ';
switchMenu += '<button disabled="disabled" class="has-tooltip" data-tooltip="switchpokemon|' + oIndex + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> ';
} else {
switchMenu += '<button name="chooseTeamPreview" value="' + i + '"' + this.tooltips.tooltipAttrs(oIndex, 'sidepokemon') + '><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> ';
switchMenu += '<button name="chooseTeamPreview" value="' + i + '" class="has-tooltip" data-tooltip="switchpokemon|' + oIndex + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> ';
}
}
@ -859,7 +861,7 @@
buf += 'use ' + Dex.getMove(move).name + (target ? ' against ' + target : '') + '.<br />';
break;
case 'switch':
buf += '' + this.myPokemon[parts[1] - 1].species + ' will switch in';
buf += '' + this.battle.myPokemon[parts[1] - 1].species + ' will switch in';
if (myActive[i]) {
buf += ', replacing ' + myActive[i].species;
}
@ -946,7 +948,7 @@
}
},
updateSide: function (sideData) {
this.myPokemon = sideData.pokemon;
this.battle.myPokemon = sideData.pokemon;
for (var i = 0; i < sideData.pokemon.length; i++) {
var pokemonData = sideData.pokemon[i];
this.battle.parseDetails(pokemonData.ident.substr(4), pokemonData.ident, pokemonData.details, pokemonData);

View File

@ -60,6 +60,8 @@ class BattleScene {
$messagebar: JQuery = null!;
$delay: JQuery = null!;
$hiddenMessage: JQuery = null!;
$tooltips: JQuery = null!;
tooltips: BattleTooltips;
sideConditions: [{[id: string]: Sprite[]}, {[id: string]: Sprite[]}] = [{}, {}];
@ -109,6 +111,8 @@ class BattleScene {
numericId = Math.floor(Math.random() * 1000000);
}
this.numericId = numericId;
this.tooltips = new BattleTooltips(battle);
this.tooltips.listen($frame[0]);
this.preloadEffects();
// reset() is called during battle initialization, so it doesn't need to be called here
@ -156,6 +160,24 @@ class BattleScene {
this.$messagebar = $('<div class="messagebar message"></div>');
this.$delay = $('<div></div>');
this.$hiddenMessage = $('<div class="message" style="position:absolute;display:block;visibility:hidden"></div>');
this.$tooltips = $('<div class="tooltips"></div>');
let tooltipBuf = '';
const tooltips = {
p2c: {top: 70, left: 250, width: 80, height: 100, tooltip: 'activepokemon|1|2'},
p2b: {top: 85, left: 320, width: 90, height: 100, tooltip: 'activepokemon|1|1'},
p2a: {top: 90, left: 390, width: 100, height: 100, tooltip: 'activepokemon|1|0'},
p1a: {top: 200, left: 130, width: 120, height: 160, tooltip: 'activepokemon|0|0'},
p1b: {top: 200, left: 250, width: 150, height: 160, tooltip: 'activepokemon|0|1'},
p1c: {top: 200, left: 350, width: 150, height: 160, tooltip: 'activepokemon|0|2'},
};
for (const id in tooltips) {
let layout = tooltips[id as 'p1a'];
tooltipBuf += `<div class="has-tooltip" style="position:absolute;`;
tooltipBuf += `top:${layout.top}px;left:${layout.left}px;width:${layout.width}px;height:${layout.height}px;`;
tooltipBuf += `" data-id="${id}" data-tooltip="${layout.tooltip}" data-ownheight="1"></div>`;
}
this.$tooltips.html(tooltipBuf);
this.$battle.append(this.$bg);
this.$battle.append(this.$terrain);
@ -170,6 +192,7 @@ class BattleScene {
this.$battle.append(this.$messagebar);
this.$battle.append(this.$delay);
this.$battle.append(this.$hiddenMessage);
this.$battle.append(this.$tooltips);
if (!this.animating) {
this.$battle.append('<div class="seeking"><strong>seeking...</strong></div>');
@ -557,7 +580,7 @@ class BattleScene {
} else {
let statustext = '';
if (pokemon.hp !== pokemon.maxhp) {
statustext += pokemon.hpDisplay();
statustext += Pokemon.getHPText(pokemon);
}
if (pokemon.status) {
if (statustext) statustext += '|';
@ -578,23 +601,23 @@ class BattleScene {
for (let i = 0; i < pokemonCount; i++) {
let poke = side.pokemon[i];
if (i >= side.totalPokemon && i >= side.pokemon.length) {
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon('pokeball-none') + '"></span>';
pokemonhtml += `<span class="picon" style="` + Dex.getPokemonIcon('pokeball-none') + `"></span>`;
} else if (noShow && poke && poke.fainted) {
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon('pokeball-fainted') + '" title="Fainted" aria-label="Fainted"></span>';
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball-fainted') + `" title="Fainted" aria-label="Fainted"></span>`;
} else if (noShow && poke && poke.status) {
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon('pokeball-statused') + '" title="Status" aria-label="Status"></span>';
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball-statused') + `" title="Status" aria-label="Status"></span>`;
} else if (noShow) {
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon('pokeball') + '" title="Non-statused" aria-label="Non-statused"></span>';
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball') + `" title="Non-statused" aria-label="Non-statused"></span>`;
} else if (!poke) {
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon('pokeball') + '" title="Not revealed" aria-label="Not revealed"></span>';
pokemonhtml += `<span class="picon" style="` + Dex.getPokemonIcon('pokeball') + `" title="Not revealed" aria-label="Not revealed"></span>`;
} else if (!poke.ident && this.battle.teamPreviewCount && this.battle.teamPreviewCount < side.pokemon.length) {
const details = this.getDetailsText(poke);
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon(poke, !side.n) + ';opacity:0.6" title="' + details + '" aria-label="' + details + '"></span>';
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon(poke, !side.n) + `;opacity:0.6" title="` + details + `" aria-label="` + details + `"></span>`;
} else {
const details = this.getDetailsText(poke);
pokemonhtml += '<span class="picon" style="' + Dex.getPokemonIcon(poke, !side.n) + '" title="' + details + '" aria-label="' + details + '"></span>';
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon(poke, !side.n) + `" title="` + details + `" aria-label="` + details + `"></span>`;
}
if (i % 3 === 2) pokemonhtml += '</div><div class="teamicons">';
if (i % 3 === 2) pokemonhtml += `</div><div class="teamicons">`;
}
pokemonhtml = '<div class="teamicons">' + pokemonhtml + '</div>';
const $sidebar = (side.n ? this.$rightbar : this.$leftbar);

View File

@ -68,6 +68,15 @@ if (!Object.assign) {
return thing;
};
}
if (!Object.values) {
Object.values = function values(thing: any) {
let out: any[] = [];
for (let k in thing) {
out.push(thing[k]);
}
return out;
};
}
// if (!Object.create) {
// Object.create = function (proto) {
// function F() {}
@ -149,31 +158,39 @@ interface SpriteData {
shiny?: boolean;
}
const Dex = {
gen: 7,
const Dex = new class implements ModdedDex {
readonly gen = 7;
readonly modid = 'gen7' as ID;
readonly cache = null!;
resourcePrefix: (() => {
readonly statNames: ReadonlyArray<StatName> = ['hp', 'atk', 'def', 'spa', 'spd', 'spe'];
readonly statNamesExceptHP: ReadonlyArray<StatNameExceptHP> = ['atk', 'def', 'spa', 'spd', 'spe'];
resourcePrefix = (() => {
let prefix = '';
if (!window.document || !document.location || document.location.protocol !== 'http:') prefix = 'https:';
return prefix + '//play.pokemonshowdown.com/';
})(),
})();
fxPrefix: (() => {
fxPrefix = (() => {
if (window.document && document.location && document.location.protocol === 'file:') {
if (window.Replays) return 'https://play.pokemonshowdown.com/fx/';
return 'fx/';
}
return '//play.pokemonshowdown.com/fx/';
})(),
})();
moddedDexes: {} as any as {[mod: string]: ModdedDex},
mod(modid: ID) {
loadedSpriteData = {xy: 1, bw: 0};
moddedDexes: {[mod: string]: ModdedDex} = {};
mod(modid: ID): ModdedDex {
if (modid === 'gen7') return this;
if (modid in this.moddedDexes) {
return this.moddedDexes[modid];
}
this.moddedDexes[modid] = new ModdedDex(modid);
return this.moddedDexes[modid];
},
}
resolveAvatar(avatar: string): string {
if (avatar in BattleAvatarNumbers) {
@ -189,7 +206,7 @@ const Dex = {
'/avatars/' + encodeURIComponent(avatar).replace(/\%3F/g, '?');
}
return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png';
},
}
/**
* This is used to sanitize strings from data files like `moves.js` and
@ -205,13 +222,13 @@ const Dex = {
sanitizeName(name: any) {
if (!name) return '';
return ('' + name).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').slice(0, 50);
},
}
prefs(prop: string, value?: any, save?: boolean) {
// @ts-ignore
if (window.Storage && Storage.prefs) return Storage.prefs(prop, value, save);
return undefined;
},
}
getShortName(name: string) {
let shortName = name.replace(/[^A-Za-z0-9]+$/, '');
@ -219,7 +236,7 @@ const Dex = {
shortName += name.slice(shortName.length).replace(/[^\(\)]+/g, '').replace(/\(\)/g, '');
}
return shortName;
},
}
getEffect(name: string | null | undefined): PureEffect | Item | Ability | Move {
name = (name || '').trim();
@ -232,7 +249,7 @@ const Dex = {
}
let id = toId(name);
return new PureEffect(id, name);
},
}
getMove(nameOrMove: string | Move | null | undefined): Move {
if (nameOrMove && typeof nameOrMove !== 'string') {
@ -273,16 +290,13 @@ const Dex = {
let move = new Move(id, name, data);
window.BattleMovedex[id] = move;
return move;
},
}
getCategory(move: Move, gen: number, type?: string) {
if (gen <= 3 && move.category !== 'Status') {
return [
'Fire', 'Water', 'Grass', 'Electric', 'Ice', 'Psychic', 'Dark', 'Dragon',
].includes(type || move.type) ? 'Special' : 'Physical';
}
return move.category;
},
getGen3Category(type: string) {
return [
'Fire', 'Water', 'Grass', 'Electric', 'Ice', 'Psychic', 'Dark', 'Dragon',
].includes(type) ? 'Special' : 'Physical';
}
getItem(nameOrItem: string | Item | null | undefined): Item {
if (nameOrItem && typeof nameOrItem !== 'string') {
@ -302,7 +316,7 @@ const Dex = {
let item = new Item(id, name, data);
window.BattleItems[id] = item;
return item;
},
}
getAbility(nameOrAbility: string | Ability | null | undefined): Ability {
if (nameOrAbility && typeof nameOrAbility !== 'string') {
@ -322,7 +336,7 @@ const Dex = {
let ability = new Ability(id, name, data);
window.BattleAbilities[id] = ability;
return ability;
},
}
getTemplate(nameOrTemplate: string | Template | null | undefined): Template {
if (nameOrTemplate && typeof nameOrTemplate !== 'string') {
@ -363,7 +377,7 @@ const Dex = {
forme,
});
return template;
},
}
getTier(pokemon: Pokemon, gen = 7, isDoubles = false): string {
let table = window.BattleTeambuilderTable;
@ -374,7 +388,7 @@ const Dex = {
// Prevents Pokemon from having their tier displayed as 'undefined' when they're in a previous generation teambuilder
if (this.getTemplate(pokemon.species).gen > gen) return 'Illegal';
return table.overrideTier[toId(pokemon.species)];
},
}
getType(type: any): Effect {
if (!type || typeof type === 'string') {
@ -389,41 +403,16 @@ const Dex = {
}
}
return type;
},
}
getAbilitiesFor(template: any, gen = 7): {[id: string]: string} {
template = this.getTemplate(template);
if (gen < 3 || !template.abilities) return {};
const id = template.id;
const templAbilities = template.abilities;
const table = (gen >= 7 ? null : window.BattleTeambuilderTable['gen' + gen]);
if (!table) return {...templAbilities};
const abilities: {[id: string]: string} = {};
if (table.overrideAbility && id in table.overrideAbility) {
abilities['0'] = table.overrideAbility[id];
} else {
abilities['0'] = templAbilities['0'];
}
const removeSecondAbility = table.removeSecondAbility && id in table.removeSecondAbility;
if (!removeSecondAbility && templAbilities['1']) {
abilities['1'] = templAbilities['1'];
}
if (gen >= 5 && templAbilities['H']) abilities['H'] = templAbilities['H'];
if (gen >= 7 && templAbilities['S']) abilities['S'] = templAbilities['S'];
return abilities;
},
hasAbility(template: any, ability: string, gen = 7) {
const abilities = this.getAbilitiesFor(template, gen);
for (const i in abilities) {
if (ability === abilities[i]) return true;
hasAbility(template: Template, ability: string) {
for (const i in template.abilities) {
// @ts-ignore
if (ability === template.abilities[i]) return true;
}
return false;
},
}
loadedSpriteData: {xy: 1, bw: 0},
loadSpriteData(gen: 'xy' | 'bw') {
if (this.loadedSpriteData[gen]) return;
this.loadedSpriteData[gen] = 1;
@ -435,7 +424,7 @@ const Dex = {
let el = document.createElement('script');
el.src = path + 'data/pokedex-mini-bw.js' + qs;
document.getElementsByTagName('body')[0].appendChild(el);
},
}
getSpriteData(pokemon: Pokemon | Template | string, siden: number, options: {
gen?: number, shiny?: boolean, gender?: GenderName, afd?: boolean, noScale?: boolean,
} = {gen: 6}) {
@ -576,7 +565,7 @@ const Dex = {
}
return spriteData;
},
}
getPokemonIcon(pokemon: any, facingLeft?: boolean) {
let num = 0;
@ -620,7 +609,7 @@ const Dex = {
let left = (num % 12) * 40;
let fainted = (pokemon && pokemon.fainted ? ';opacity:.3;filter:grayscale(100%) brightness(.5)' : '');
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/smicons-sheet.png?a5) no-repeat scroll -' + left + 'px -' + top + 'px' + fainted;
},
}
getTeambuilderSprite(pokemon: any, gen: number = 0) {
if (!pokemon) return '';
@ -661,7 +650,7 @@ const Dex = {
else if (gen <= 3 && template.gen <= 3) spriteDir = Dex.resourcePrefix + 'sprites/rse';
else if (gen <= 4 && template.gen <= 4) spriteDir = Dex.resourcePrefix + 'sprites/dpp';
return 'background-image:url(' + spriteDir + shiny + '/' + spriteid + '.png);background-position:10px 5px;background-repeat:no-repeat';
},
}
getItemIcon(item: any) {
let num = 0;
@ -671,13 +660,13 @@ const Dex = {
let top = Math.floor(num / 16) * 24;
let left = (num % 16) * 24;
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/itemicons-sheet.png) no-repeat scroll -' + left + 'px -' + top + 'px';
},
}
getTypeIcon(type: string, b?: boolean) { // b is just for utilichart.js
if (!type) return '';
let sanitizedType = type.replace(/\?/g, '%3f');
return '<img src="' + Dex.resourcePrefix + 'sprites/types/' + sanitizedType + '.png" alt="' + type + '" height="14" width="32"' + (b ? ' class="b"' : '') + ' />';
},
}
};
class ModdedDex {
@ -688,10 +677,10 @@ class ModdedDex {
Items: {} as any as {[k: string]: Item},
Templates: {} as any as {[k: string]: Template},
};
getAbility = Dex.getAbility;
getAbility: (nameOrAbility: string | Ability | null | undefined) => Ability = Dex.getAbility;
constructor(modid: ID) {
this.modid = modid;
let gen = parseInt(modid.slice(3));
let gen = parseInt(modid.slice(3), 10);
if (!modid.startsWith('gen') || !gen) throw new Error("Unsupported modid");
this.gen = gen;
}
@ -716,6 +705,9 @@ class ModdedDex {
break;
}
}
if (this.gen <= 3 && data.category !== 'Status') {
data.category = Dex.getGen3Category(data.type);
}
const move = new Move(id, name, data);
this.cache.Moves[id] = move;
@ -753,18 +745,25 @@ class ModdedDex {
let data = {...Dex.getTemplate(name)};
const table = window.BattleTeambuilderTable[this.modid];
if (id in table.overrideAbility) {
data.abilities = {...data.abilities, 0: table.overrideAbility[id]};
if (this.gen < 3) {
data.abilities = {0: "None"};
} else {
let abilities = {...data.abilities};
if (id in table.overrideAbility) {
abilities['0'] = table.overrideAbility[id];
}
if (id in table.removeSecondAbility) {
delete abilities['1'];
}
if (this.gen < 5) delete abilities['H'];
if (this.gen < 7) delete abilities['S'];
}
if (id in table.overrideStats) {
data.baseStats = {...data.baseStats, ...table.overrideStats[id]};
}
if (id in table.overrideType) data.types = table.overrideType[id].split('/');
if (id in table.removeSecondAbility) {
data.abilities = {...data.abilities};
// @ts-ignore
delete data.abilities['1'];
}
if (id in table.overrideTier) data.tier = table.overrideTier[id];
const template = new Template(id, name, data);
this.cache.Templates[id] = template;

1513
src/battle-tooltips.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ type WeatherState = [string, number, number];
type EffectTable = {[effectid: string]: EffectState};
type HPColor = 'r' | 'y' | 'g';
class Pokemon {
class Pokemon implements PokemonDetails, PokemonHealth {
name = '';
species = '';
@ -94,7 +94,6 @@ class Pokemon {
volatiles: EffectTable = {};
turnstatuses: EffectTable = {};
movestatuses: EffectTable = {};
weightkg = 0;
lastMove = '';
/** [[moveName, ppUsed]] */
@ -132,7 +131,7 @@ class Pokemon {
}
return '';
}
getPixelRange(pixels: number, color: HPColor): [number, number] {
static getPixelRange(pixels: number, color: HPColor | ''): [number, number] {
let epsilon = 0.5 / 714;
if (pixels === 0) return [0, 0];
@ -155,7 +154,7 @@ class Pokemon {
return [pixels / 48, (pixels + 1) / 48 - epsilon];
}
getFormattedRange(range: [number, number], precision: number, separator: string) {
static getFormattedRange(range: [number, number], precision: number, separator: string) {
if (range[0] === range[1]) {
let percentage = Math.abs(range[0] * 100);
if (Math.floor(percentage) === percentage) {
@ -185,8 +184,8 @@ class Pokemon {
return [damage[2] / 100, damage[2] / 100];
}
// pixel damage
let oldrange = this.getPixelRange(damage[3], damage[4]);
let newrange = this.getPixelRange(damage[3] + damage[0], this.hpcolor);
let oldrange = Pokemon.getPixelRange(damage[3], damage[4]);
let newrange = Pokemon.getPixelRange(damage[3] + damage[0], this.hpcolor);
if (damage[0] === 0) {
// no change in displayed pixel width
return [0, newrange[1] - newrange[0]];
@ -384,6 +383,10 @@ class Pokemon {
// let badBoostTable = ['Normal', '&minus;1', '&minus;2', '&minus;3', '&minus;4', '&minus;5', '&minus;6'];
return '' + badBoostTable[-this.boosts[boostStat]] + '&nbsp;' + boostStatTable[boostStat];
}
getWeightKg(serverPokemon?: ServerPokemon) {
let autotomizeFactor = this.volatiles.autotomize ? this.volatiles.autotomize[1] * 100 : 0;
return Math.max(this.getTemplate(serverPokemon).weightkg - autotomizeFactor, 0.1);
}
getBoostType(boostStat: BoostStatName) {
if (!this.boosts[boostStat]) return 'neutral';
if (this.boosts[boostStat] > 0) return 'good';
@ -391,9 +394,6 @@ class Pokemon {
}
clearVolatile() {
this.ability = this.baseAbility;
if (window.BattlePokedex && BattlePokedex[this.species] && BattlePokedex[this.species].weightkg) {
this.weightkg = BattlePokedex[this.species].weightkg;
}
this.boosts = {};
this.clearVolatiles();
for (let i = 0; i < this.moveTrack.length; i++) {
@ -452,29 +452,59 @@ class Pokemon {
this.removeVolatile('typeadd' as ID);
}
}
getTypes(): [string[], string] {
let types;
getTypes(serverPokemon?: ServerPokemon): [ReadonlyArray<TypeName>, TypeName | ''] {
let types: ReadonlyArray<TypeName>;
if (this.volatiles.typechange) {
types = this.volatiles.typechange[1].split('/');
} else {
const species = this.getSpecies();
types = (
window.BattleTeambuilderTable &&
window.BattleTeambuilderTable['gen' + this.side.battle.gen] &&
window.BattleTeambuilderTable['gen' + this.side.battle.gen].overrideType[toId(species)]
);
if (types) types = types.split('/');
if (!types) types = Dex.getTemplate(species).types || [];
types = this.getTemplate(serverPokemon).types;
}
if (this.volatiles.roost && types.includes('Flying')) {
types = types.filter(typeName => typeName !== 'Flying');
if (!types.length) types = ['Normal'];
}
const addedType = (this.volatiles.typeadd ? this.volatiles.typeadd[1] : '');
return [types, addedType];
}
getTypeList() {
const [types, addedType] = this.getTypes();
return types.concat(addedType);
isGrounded(serverPokemon?: ServerPokemon) {
const battle = this.side.battle;
if (battle.hasPseudoWeather('Gravity')) {
return true;
} else if (this.volatiles['ingrain'] && battle.gen >= 4) {
return true;
} else if (this.volatiles['smackdown']) {
return true;
}
let item = toId(serverPokemon ? serverPokemon.item : this.item);
let ability = toId(this.ability || (serverPokemon && serverPokemon.ability));
if (battle.hasPseudoWeather('Magic Room') || this.volatiles['embargo'] || ability === 'klutz') {
item = '' as ID;
}
if (item === 'ironball') {
return true;
}
if (ability === 'levitate') {
return false;
}
if (this.volatiles['magnetrise'] || this.volatiles['telekinesis']) {
return false;
} else if (item !== 'airballoon') {
return false;
}
return !this.getTypeList(serverPokemon).includes('Flying');
}
getSpecies(): string {
return this.volatiles.formechange ? this.volatiles.formechange[1] : this.species;
getTypeList(serverPokemon?: ServerPokemon) {
const [types, addedType] = this.getTypes(serverPokemon);
return addedType ? types.concat(addedType) : types;
}
getSpecies(serverPokemon?: ServerPokemon): string {
return this.volatiles.formechange ? this.volatiles.formechange[1] :
(serverPokemon ? serverPokemon.species : this.species);
}
getTemplate(serverPokemon?: ServerPokemon) {
return this.side.battle.dex.getTemplate(this.getSpecies(serverPokemon));
}
reset() {
this.clearVolatile();
@ -500,7 +530,7 @@ class Pokemon {
// Draw the health bar to the middle of the range.
// This affects the width of the visual health bar *only*; it
// does not affect the ranges displayed in any way.
let range = this.getPixelRange(this.hp, this.hpcolor);
let range = Pokemon.getPixelRange(this.hp, this.hpcolor);
let ratio = (range[0] + range[1]) / 2;
return Math.round(maxWidth * ratio) || 1;
}
@ -510,11 +540,11 @@ class Pokemon {
}
return percentage * maxWidth / 100;
}
hpDisplay(precision = 1) {
if (this.maxhp === 100) return this.hp + '%';
if (this.maxhp !== 48) return (100 * this.hp / this.maxhp).toFixed(precision) + '%';
let range = this.getPixelRange(this.hp, this.hpcolor);
return this.getFormattedRange(range, precision, '');
static getHPText(pokemon: PokemonHealth, precision = 1) {
if (pokemon.maxhp === 100) return pokemon.hp + '%';
if (pokemon.maxhp !== 48) return (100 * pokemon.hp / pokemon.maxhp).toFixed(precision) + '%';
let range = Pokemon.getPixelRange(pokemon.hp, pokemon.hpcolor);
return Pokemon.getFormattedRange(range, precision, '');
}
destroy() {
if (this.sprite) this.sprite.destroy();
@ -875,6 +905,48 @@ enum Playback {
Seeking = 5,
}
interface PokemonDetails {
details: string;
name: string;
species: string;
level: number;
shiny: boolean;
gender: GenderName | '';
ident: string;
searchid: string;
}
interface PokemonHealth {
hp: number;
maxhp: number;
hpcolor: HPColor | '';
status: StatusName | 'tox' | '' | '???';
fainted?: boolean;
}
interface ServerPokemon extends PokemonDetails, PokemonHealth {
ident: string;
details: string;
condition: string;
active: boolean;
/** unboosted stats */
stats: {
atk: number,
def: number,
spa: number,
spd: number,
spe: number,
};
/** currently an ID, will revise to name */
moves: string[];
/** currently an ID, will revise to name */
baseAbility: string;
/** currently an ID, will revise to name */
ability?: string;
/** currently an ID, will revise to name */
item: string;
/** currently an ID, will revise to name */
pokeball: string;
}
class Battle {
scene: BattleScene | BattleSceneStub;
@ -923,9 +995,12 @@ class Battle {
yourSide: Side = null!;
p1: Side = null!;
p2: Side = null!;
myPokemon: ServerPokemon[] | null = null;
sides: [Side, Side] = [null!, null!];
lastMove = '';
gen = 7;
dex: ModdedDex = Dex;
teamPreviewCount = 0;
speciesClause = false;
tier = '';
@ -934,6 +1009,11 @@ class Battle {
endLastTurnPending = false;
totalTimeLeft = 0;
graceTimeLeft = 0;
/**
* true: timer on, state unknown
* false: timer off
* number: seconds left this turn
*/
kickingInactive: number | boolean = false;
// options
@ -1394,7 +1474,7 @@ class Battle {
break;
}
} else {
let damageinfo = '' + poke.getFormattedRange(range, damage[1] === 100 ? 0 : 1, '\u2013');
let damageinfo = '' + Pokemon.getFormattedRange(range, damage[1] === 100 ? 0 : 1, '\u2013');
if (damage[1] !== 100) {
let hover = '' + ((damage[0] < 0) ? '\u2212' : '') +
Math.abs(damage[0]) + '/' + damage[1];
@ -1406,7 +1486,7 @@ class Battle {
}
args[3] = damageinfo;
}
this.scene.damageAnim(poke, poke.getFormattedRange(range, 0, ' to '));
this.scene.damageAnim(poke, Pokemon.getFormattedRange(range, 0, ' to '));
this.log(args, kwArgs);
break;
}
@ -1439,7 +1519,7 @@ class Battle {
}
}
this.scene.runOtherAnim('heal' as ID, [poke]);
this.scene.healAnim(poke, poke.getFormattedRange(range, 0, ' to '));
this.scene.healAnim(poke, Pokemon.getFormattedRange(range, 0, ' to '));
this.log(args, kwArgs);
break;
}
@ -1449,7 +1529,7 @@ class Battle {
if (cpoke) {
let damage = cpoke.healthParse(args[2 + 2 * k])!;
let range = cpoke.getDamageRange(damage);
let formattedRange = cpoke.getFormattedRange(range, 0, ' to ');
let formattedRange = Pokemon.getFormattedRange(range, 0, ' to ');
let diff = damage[0];
if (diff > 0) {
this.scene.healAnim(cpoke, formattedRange);
@ -2059,11 +2139,10 @@ class Battle {
}
newSpecies = args[2].substr(0, commaIndex);
}
let template = Dex.getTemplate(newSpecies);
let template = this.dex.getTemplate(newSpecies);
poke.species = newSpecies;
poke.ability = poke.baseAbility = (template.abilities ? template.abilities['0'] : '');
poke.weightkg = template.weightkg;
poke.details = args[2];
poke.searchid = args[1].substr(0, 2) + args[1].substr(3) + '|' + args[2];
@ -2084,7 +2163,6 @@ class Battle {
poke.boosts = {...tpoke.boosts};
poke.copyTypesFrom(tpoke);
poke.weightkg = tpoke.weightkg;
poke.ability = tpoke.ability;
const species = (tpoke.volatiles.formechange ? tpoke.volatiles.formechange[1] : tpoke.species);
const pokemon = tpoke;
@ -2188,6 +2266,7 @@ class Battle {
break;
case 'imprison':
this.scene.resultAnim(poke, 'Imprisoning', 'good');
break;
case 'disable':
this.scene.resultAnim(poke, 'Disabled', 'bad');
break;
@ -2239,6 +2318,11 @@ class Battle {
break;
case 'autotomize':
this.scene.resultAnim(poke, 'Lightened', 'good');
if (poke.volatiles.autotomize) {
poke.volatiles.autotomize[1]++;
} else {
poke.addVolatile('autotomize' as ID, 1);
}
break;
case 'focusenergy':
this.scene.resultAnim(poke, '+Crit rate', 'good');
@ -2685,7 +2769,7 @@ class Battle {
return data.spriteData[siden];
}
*/
parseDetails(name: string, pokemonid: string, details = "", output: any = {}) {
parseDetails(name: string, pokemonid: string, details = "", output: PokemonDetails = {} as any) {
output.details = details;
output.name = name;
output.species = name;
@ -2700,7 +2784,7 @@ class Battle {
splitDetails.pop();
}
if (splitDetails[splitDetails.length - 1] === 'M' || splitDetails[splitDetails.length - 1] === 'F') {
output.gender = splitDetails[splitDetails.length - 1];
output.gender = splitDetails[splitDetails.length - 1] as GenderName;
splitDetails.pop();
}
if (splitDetails[1]) {
@ -2711,13 +2795,7 @@ class Battle {
}
return output;
}
parseHealth(hpstring: string, output: any = {}): {
hp: number,
maxhp: number,
hpcolor: HPColor | '',
status: StatusName | '',
fainted?: boolean,
} | null {
parseHealth(hpstring: string, output: PokemonHealth = {} as any) {
let [hp, status] = hpstring.split(' ');
// hp parse
@ -3177,6 +3255,7 @@ class Battle {
}
case 'gen': {
this.gen = parseInt(args[1], 10);
this.dex = Dex.mod(`gen${this.gen}` as ID);
this.scene.updateGen();
this.log(args);
break;

View File

@ -711,6 +711,60 @@ License: GPLv2
-ms-interpolation-mode: nearest-neighbor;
}
/*********************************************************
* Tooltips
*********************************************************/
#tooltipwrapper {
position: absolute;
top: 400px;
left: 100px;
text-align: left;
color: black;
pointer-events: none;
}
#tooltipwrapper .tooltipinner {
position: relative;
}
#tooltipwrapper .tooltip {
position: absolute;
bottom: 0;
left: 0;
width: 300px;
border: 1px solid #888888;
background: #EEEEEE;
background: rgba(240,240,240,.9);
border-radius: 5px;
z-index: 50;
}
#tooltipwrapper .tooltip h2 {
padding: 2px 4px;
margin: 0;
border-bottom: 1px solid #888888;
font-size: 10pt;
}
#tooltipwrapper .tooltip h2 small {
font-weight: normal;
}
#tooltipwrapper .tooltip p {
padding: 2px 4px;
margin: 0;
font-size: 9pt;
}
#tooltipwrapper .tooltip p small {
font-size: 8pt;
}
#tooltipwrapper .tooltip p.section {
border-top: 1px solid #888888;
}
#tooltipwrapper.tooltip-locked {
pointer-events: auto;
}
#tooltipwrapper.tooltip-locked .tooltip {
border: 2px solid #888888;
background: #DEDEDE;
}
/*********************************************************
* Message log styling
*********************************************************/
@ -829,7 +883,7 @@ License: GPLv2
color: #DDD;
}
.stat-boosted {
color: #119911;
color: #117911;
}
.stat-lowered {
color: #991111;

View File

@ -1970,12 +1970,12 @@ a.ilink.yours {
.movemenu button small.type {
padding-top: 3px;
float: left;
font-size: 7pt;
font-size: 8pt;
}
.movemenu button small.pp {
padding-top: 3px;
padding-top: 2px;
float: right;
font-size: 7pt;
font-size: 8pt;
}
.megaevo {
clear: both;
@ -2135,48 +2135,6 @@ a.ilink.yours {
}
}
/****************/
#tooltipwrapper {
position: absolute;
top: 400px;
left: 100px;
text-align: left;
color: black;
pointer-events: none;
}
#tooltipwrapper .tooltipinner {
position: relative;
}
#tooltipwrapper .tooltip {
position: absolute;
bottom: 0;
left: 0;
width: 300px;
border: 1px solid #888888;
background: #EEEEEE;
background: rgba(240,240,240,.9);
border-radius: 5px;
z-index: 50;
}
#tooltipwrapper .tooltip h2 {
padding: 2px 4px;
margin: 0;
border-bottom: 1px solid #888888;
font-size: 10pt;
}
#tooltipwrapper .tooltip h2 small {
font-weight: normal;
}
#tooltipwrapper .tooltip p {
padding: 2px 4px;
margin: 0;
font-size: 8pt;
}
#tooltipwrapper .tooltip p.section {
border-top: 1px solid #888888;
}
/*********************************************************
* Teambuilder
*********************************************************/

View File

@ -104,7 +104,7 @@
<script src="js/client-ladder.js"></script>
<script src="js/client-chat.js"></script>
<script src="js/client-chat-tournament.js"></script>
<script src="js/client-battle-tooltips.js"></script>
<script src="js/battle-tooltips.js"></script>
<script src="js/client-battle.js"></script>
<script src="js/client-rooms.js"></script>
<script src="js/storage.js"></script>

View File

@ -7,7 +7,7 @@
"jsx": "preserve",
"strict": true
},
"types": ["node"],
"types": [],
"include": [
"./js/lib/preact.d.ts",
"./src/*"

View File

@ -32,8 +32,9 @@
"no-bitwise": false,
"prefer-conditional-expression": false,
"no-shadowed-variable": [true, {"temporalDeadZone": false}],
"no-switch-case-fall-through": true,
"object-literal-sort-keys": false,
"object-literal-key-quotes": [true, "as-needed"],
"object-literal-key-quotes": false,
"trailing-comma": [
true,
{