mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
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:
parent
4fec9031e5
commit
434afefeaa
|
|
@ -12,3 +12,4 @@ node_modules/
|
|||
/js/battle-animations-moves.js
|
||||
/js/battle-scene-stub.js
|
||||
/js/battle-animations.js
|
||||
/js/battle-tooltips.js
|
||||
|
|
|
|||
|
|
@ -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
1
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 = '–';
|
||||
if (move.id === 'Recharge') move.type = '–';
|
||||
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> </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> </button> ';
|
||||
} else {
|
||||
movebuttons += '<button disabled="disabled"> </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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').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
1513
src/battle-tooltips.ts
Normal file
File diff suppressed because it is too large
Load Diff
171
src/battle.ts
171
src/battle.ts
|
|
@ -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', '−1', '−2', '−3', '−4', '−5', '−6'];
|
||||
return '' + badBoostTable[-this.boosts[boostStat]] + ' ' + 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*********************************************************/
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"jsx": "preserve",
|
||||
"strict": true
|
||||
},
|
||||
"types": ["node"],
|
||||
"types": [],
|
||||
"include": [
|
||||
"./js/lib/preact.d.ts",
|
||||
"./src/*"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user