mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-04-26 10:14:50 -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
a9d5937a04
commit
5c983df6d3
|
|
@ -12,3 +12,4 @@ node_modules/
|
||||||
/js/battle-animations-moves.js
|
/js/battle-animations-moves.js
|
||||||
/js/battle-scene-stub.js
|
/js/battle-scene-stub.js
|
||||||
/js/battle-animations.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,
|
"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,
|
"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,
|
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false,
|
||||||
|
"ModifiableValue": false,
|
||||||
|
|
||||||
// Generic global variables
|
// Generic global variables
|
||||||
"Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Dex": false,
|
"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-dex-data.js
|
||||||
/js/battle-animations-moves.js
|
/js/battle-animations-moves.js
|
||||||
/js/battle-animations.js
|
/js/battle-animations.js
|
||||||
|
/js/battle-tooltips.js
|
||||||
/js/battle-scene-stub.js
|
/js/battle-scene-stub.js
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
||||||
|
|
@ -124,21 +124,27 @@ exports.BattleText = {
|
||||||
// stats
|
// stats
|
||||||
hp: {
|
hp: {
|
||||||
statName: "HP",
|
statName: "HP",
|
||||||
|
statShortName: "HP",
|
||||||
},
|
},
|
||||||
atk: {
|
atk: {
|
||||||
statName: "Attack",
|
statName: "Attack",
|
||||||
|
statShortName: "Atk",
|
||||||
},
|
},
|
||||||
def: {
|
def: {
|
||||||
statName: "Defense",
|
statName: "Defense",
|
||||||
|
statShortName: "Def",
|
||||||
},
|
},
|
||||||
spa: {
|
spa: {
|
||||||
statName: "Special Attack",
|
statName: "Special Attack",
|
||||||
|
statShortName: "SpA",
|
||||||
},
|
},
|
||||||
spd: {
|
spd: {
|
||||||
statName: "Special Defense",
|
statName: "Special Defense",
|
||||||
|
statShortName: "SpD",
|
||||||
},
|
},
|
||||||
spe: {
|
spe: {
|
||||||
statName: "Speed",
|
statName: "Speed",
|
||||||
|
statShortName: "Spe",
|
||||||
},
|
},
|
||||||
accuracy: {
|
accuracy: {
|
||||||
statName: "accuracy",
|
statName: "accuracy",
|
||||||
|
|
@ -148,6 +154,7 @@ exports.BattleText = {
|
||||||
},
|
},
|
||||||
spc: {
|
spc: {
|
||||||
statName: "Special",
|
statName: "Special",
|
||||||
|
statShortName: "Spc",
|
||||||
},
|
},
|
||||||
stats: {
|
stats: {
|
||||||
statName: "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-ladder.js?"></script>
|
||||||
<script src="//play.pokemonshowdown.com/js/client-chat.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-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-battle.js?"></script>
|
||||||
<script src="//play.pokemonshowdown.com/js/client-rooms.js?"></script>
|
<script src="//play.pokemonshowdown.com/js/client-rooms.js?"></script>
|
||||||
<script src="//play.pokemonshowdown.com/data/graphics.js?"></script>
|
<script src="//play.pokemonshowdown.com/data/graphics.js?"></script>
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@
|
||||||
|
|
||||||
BattleSound.setMute(Dex.prefs('mute'));
|
BattleSound.setMute(Dex.prefs('mute'));
|
||||||
this.battle = new Battle(this.$battle, this.$chatFrame, this.id);
|
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.roomid = this.id;
|
||||||
this.battle.joinButtons = true;
|
this.battle.joinButtons = true;
|
||||||
|
|
@ -333,7 +334,7 @@
|
||||||
|
|
||||||
act = this.request.requestType;
|
act = this.request.requestType;
|
||||||
if (this.request.side) {
|
if (this.request.side) {
|
||||||
switchables = this.myPokemon;
|
switchables = this.battle.myPokemon;
|
||||||
}
|
}
|
||||||
if (!this.finalDecision) this.finalDecision = !!this.request.noCancel;
|
if (!this.finalDecision) this.finalDecision = !!this.request.noCancel;
|
||||||
}
|
}
|
||||||
|
|
@ -407,7 +408,7 @@
|
||||||
// Request full team order if one of our Pokémon has Illusion
|
// Request full team order if one of our Pokémon has Illusion
|
||||||
for (var i = 0; i < switchables.length && i < 6; i++) {
|
for (var i = 0; i < switchables.length && i < 6; i++) {
|
||||||
if (toId(switchables[i].baseAbility) === 'illusion') {
|
if (toId(switchables[i].baseAbility) === 'illusion') {
|
||||||
this.choice.count = this.myPokemon.length;
|
this.choice.count = this.battle.myPokemon.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.battle.teamPreviewCount) {
|
if (this.battle.teamPreviewCount) {
|
||||||
|
|
@ -491,7 +492,7 @@
|
||||||
app.addPopup(TimerPopup, {room: this});
|
app.addPopup(TimerPopup, {room: this});
|
||||||
},
|
},
|
||||||
updateMoveControls: function (type) {
|
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') {
|
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) {
|
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) {
|
} else if (!pokemon || pokemon.fainted) {
|
||||||
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> ';
|
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> ';
|
||||||
} else {
|
} 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++) {
|
for (var i = 0; i < myActive.length; i++) {
|
||||||
|
|
@ -574,7 +575,7 @@
|
||||||
} else if (!pokemon || pokemon.fainted) {
|
} else if (!pokemon || pokemon.fainted) {
|
||||||
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> ';
|
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> ';
|
||||||
} else {
|
} 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 hasMoves = false;
|
||||||
var moveMenu = '';
|
var moveMenu = '';
|
||||||
var movebuttons = '';
|
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++) {
|
for (var i = 0; i < curActive.moves.length; i++) {
|
||||||
var moveData = curActive.moves[i];
|
var moveData = curActive.moves[i];
|
||||||
var move = Dex.getMove(moveData.move);
|
var move = Dex.getMove(moveData.move);
|
||||||
|
|
@ -602,11 +604,11 @@
|
||||||
if (move.id === 'Struggle' || move.id === 'Recharge') pp = '–';
|
if (move.id === 'Struggle' || move.id === 'Recharge') pp = '–';
|
||||||
if (move.id === 'Recharge') move.type = '–';
|
if (move.id === 'Recharge') move.type = '–';
|
||||||
if (name.substr(0, 12) === 'Hidden Power') name = 'Hidden Power';
|
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) {
|
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 {
|
} 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;
|
hasMoves = true;
|
||||||
}
|
}
|
||||||
movebuttons += name + '<br /><small class="type">' + (moveType ? Dex.getType(moveType).name : "Unknown") + '</small> <small class="pp">' + pp + '</small> </button> ';
|
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++) {
|
for (var i = 0; i < curActive.moves.length; i++) {
|
||||||
var moveData = curActive.moves[i];
|
var moveData = curActive.moves[i];
|
||||||
var move = Dex.getMove(moveData.move);
|
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]) {
|
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> ';
|
movebuttons += canZMove[i].move + '<br /><small class="type">' + (moveType ? Dex.getType(moveType).name : "Unknown") + '</small> <small class="pp">1/1</small> </button> ';
|
||||||
} else {
|
} else {
|
||||||
movebuttons += '<button disabled="disabled"> </button>';
|
movebuttons += '<button disabled="disabled"> </button>';
|
||||||
|
|
@ -663,9 +665,9 @@
|
||||||
var pokemon = switchables[i];
|
var pokemon = switchables[i];
|
||||||
pokemon.name = pokemon.ident.substr(4);
|
pokemon.name = pokemon.ident.substr(4);
|
||||||
if (pokemon.fainted || i < this.battle.mySide.active.length || this.choice.switchFlags[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') + '><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 {
|
} 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) {
|
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 myActive = this.battle.mySide.active;
|
||||||
|
|
||||||
var requestTitle = '';
|
var requestTitle = '';
|
||||||
|
|
@ -710,13 +712,13 @@
|
||||||
requestTitle += "Which Pokémon will it switch in for?";
|
requestTitle += "Which Pokémon will it switch in for?";
|
||||||
var controls = '<div class="switchmenu" style="display:block">';
|
var controls = '<div class="switchmenu" style="display:block">';
|
||||||
for (var i = 0; i < myActive.length; i++) {
|
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]) {
|
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) {
|
} else if (!pokemon) {
|
||||||
controls += '<button disabled></button> ';
|
controls += '<button disabled></button> ';
|
||||||
} else {
|
} 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>';
|
controls += '</div>';
|
||||||
|
|
@ -737,9 +739,9 @@
|
||||||
for (var i = 0; i < switchables.length; i++) {
|
for (var i = 0; i < switchables.length; i++) {
|
||||||
var pokemon = switchables[i];
|
var pokemon = switchables[i];
|
||||||
if (pokemon.fainted || i < this.battle.mySide.active.length || this.choice.switchFlags[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 {
|
} 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> ';
|
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) {
|
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 maxIndex = Math.min(switchables.length, 24);
|
||||||
|
|
||||||
var requestTitle = "";
|
var requestTitle = "";
|
||||||
|
|
@ -775,9 +777,9 @@
|
||||||
var oIndex = this.choice.teamPreview[i] - 1;
|
var oIndex = this.choice.teamPreview[i] - 1;
|
||||||
var pokemon = switchables[oIndex];
|
var pokemon = switchables[oIndex];
|
||||||
if (i < this.choice.done) {
|
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 {
|
} 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 />';
|
buf += 'use ' + Dex.getMove(move).name + (target ? ' against ' + target : '') + '.<br />';
|
||||||
break;
|
break;
|
||||||
case 'switch':
|
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]) {
|
if (myActive[i]) {
|
||||||
buf += ', replacing ' + myActive[i].species;
|
buf += ', replacing ' + myActive[i].species;
|
||||||
}
|
}
|
||||||
|
|
@ -946,7 +948,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateSide: function (sideData) {
|
updateSide: function (sideData) {
|
||||||
this.myPokemon = sideData.pokemon;
|
this.battle.myPokemon = sideData.pokemon;
|
||||||
for (var i = 0; i < sideData.pokemon.length; i++) {
|
for (var i = 0; i < sideData.pokemon.length; i++) {
|
||||||
var pokemonData = sideData.pokemon[i];
|
var pokemonData = sideData.pokemon[i];
|
||||||
this.battle.parseDetails(pokemonData.ident.substr(4), pokemonData.ident, pokemonData.details, pokemonData);
|
this.battle.parseDetails(pokemonData.ident.substr(4), pokemonData.ident, pokemonData.details, pokemonData);
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ class BattleScene {
|
||||||
$messagebar: JQuery = null!;
|
$messagebar: JQuery = null!;
|
||||||
$delay: JQuery = null!;
|
$delay: JQuery = null!;
|
||||||
$hiddenMessage: JQuery = null!;
|
$hiddenMessage: JQuery = null!;
|
||||||
|
$tooltips: JQuery = null!;
|
||||||
|
tooltips: BattleTooltips;
|
||||||
|
|
||||||
sideConditions: [{[id: string]: Sprite[]}, {[id: string]: Sprite[]}] = [{}, {}];
|
sideConditions: [{[id: string]: Sprite[]}, {[id: string]: Sprite[]}] = [{}, {}];
|
||||||
|
|
||||||
|
|
@ -109,6 +111,8 @@ class BattleScene {
|
||||||
numericId = Math.floor(Math.random() * 1000000);
|
numericId = Math.floor(Math.random() * 1000000);
|
||||||
}
|
}
|
||||||
this.numericId = numericId;
|
this.numericId = numericId;
|
||||||
|
this.tooltips = new BattleTooltips(battle);
|
||||||
|
this.tooltips.listen($frame[0]);
|
||||||
|
|
||||||
this.preloadEffects();
|
this.preloadEffects();
|
||||||
// reset() is called during battle initialization, so it doesn't need to be called here
|
// 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.$messagebar = $('<div class="messagebar message"></div>');
|
||||||
this.$delay = $('<div></div>');
|
this.$delay = $('<div></div>');
|
||||||
this.$hiddenMessage = $('<div class="message" style="position:absolute;display:block;visibility:hidden"></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.$bg);
|
||||||
this.$battle.append(this.$terrain);
|
this.$battle.append(this.$terrain);
|
||||||
|
|
@ -170,6 +192,7 @@ class BattleScene {
|
||||||
this.$battle.append(this.$messagebar);
|
this.$battle.append(this.$messagebar);
|
||||||
this.$battle.append(this.$delay);
|
this.$battle.append(this.$delay);
|
||||||
this.$battle.append(this.$hiddenMessage);
|
this.$battle.append(this.$hiddenMessage);
|
||||||
|
this.$battle.append(this.$tooltips);
|
||||||
|
|
||||||
if (!this.animating) {
|
if (!this.animating) {
|
||||||
this.$battle.append('<div class="seeking"><strong>seeking...</strong></div>');
|
this.$battle.append('<div class="seeking"><strong>seeking...</strong></div>');
|
||||||
|
|
@ -557,7 +580,7 @@ class BattleScene {
|
||||||
} else {
|
} else {
|
||||||
let statustext = '';
|
let statustext = '';
|
||||||
if (pokemon.hp !== pokemon.maxhp) {
|
if (pokemon.hp !== pokemon.maxhp) {
|
||||||
statustext += pokemon.hpDisplay();
|
statustext += Pokemon.getHPText(pokemon);
|
||||||
}
|
}
|
||||||
if (pokemon.status) {
|
if (pokemon.status) {
|
||||||
if (statustext) statustext += '|';
|
if (statustext) statustext += '|';
|
||||||
|
|
@ -578,23 +601,23 @@ class BattleScene {
|
||||||
for (let i = 0; i < pokemonCount; i++) {
|
for (let i = 0; i < pokemonCount; i++) {
|
||||||
let poke = side.pokemon[i];
|
let poke = side.pokemon[i];
|
||||||
if (i >= side.totalPokemon && i >= side.pokemon.length) {
|
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) {
|
} 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) {
|
} 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) {
|
} 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) {
|
} 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) {
|
} else if (!poke.ident && this.battle.teamPreviewCount && this.battle.teamPreviewCount < side.pokemon.length) {
|
||||||
const details = this.getDetailsText(poke);
|
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 {
|
} else {
|
||||||
const details = this.getDetailsText(poke);
|
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>';
|
pokemonhtml = '<div class="teamicons">' + pokemonhtml + '</div>';
|
||||||
const $sidebar = (side.n ? this.$rightbar : this.$leftbar);
|
const $sidebar = (side.n ? this.$rightbar : this.$leftbar);
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,15 @@ if (!Object.assign) {
|
||||||
return thing;
|
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) {
|
// if (!Object.create) {
|
||||||
// Object.create = function (proto) {
|
// Object.create = function (proto) {
|
||||||
// function F() {}
|
// function F() {}
|
||||||
|
|
@ -149,31 +158,39 @@ interface SpriteData {
|
||||||
shiny?: boolean;
|
shiny?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Dex = {
|
const Dex = new class implements ModdedDex {
|
||||||
gen: 7,
|
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 = '';
|
let prefix = '';
|
||||||
if (!window.document || !document.location || document.location.protocol !== 'http:') prefix = 'https:';
|
if (!window.document || !document.location || document.location.protocol !== 'http:') prefix = 'https:';
|
||||||
return prefix + '//play.pokemonshowdown.com/';
|
return prefix + '//play.pokemonshowdown.com/';
|
||||||
})(),
|
})();
|
||||||
|
|
||||||
fxPrefix: (() => {
|
fxPrefix = (() => {
|
||||||
if (window.document && document.location && document.location.protocol === 'file:') {
|
if (window.document && document.location && document.location.protocol === 'file:') {
|
||||||
if (window.Replays) return 'https://play.pokemonshowdown.com/fx/';
|
if (window.Replays) return 'https://play.pokemonshowdown.com/fx/';
|
||||||
return 'fx/';
|
return 'fx/';
|
||||||
}
|
}
|
||||||
return '//play.pokemonshowdown.com/fx/';
|
return '//play.pokemonshowdown.com/fx/';
|
||||||
})(),
|
})();
|
||||||
|
|
||||||
moddedDexes: {} as any as {[mod: string]: ModdedDex},
|
loadedSpriteData = {xy: 1, bw: 0};
|
||||||
mod(modid: ID) {
|
moddedDexes: {[mod: string]: ModdedDex} = {};
|
||||||
|
|
||||||
|
mod(modid: ID): ModdedDex {
|
||||||
|
if (modid === 'gen7') return this;
|
||||||
if (modid in this.moddedDexes) {
|
if (modid in this.moddedDexes) {
|
||||||
return this.moddedDexes[modid];
|
return this.moddedDexes[modid];
|
||||||
}
|
}
|
||||||
this.moddedDexes[modid] = new ModdedDex(modid);
|
this.moddedDexes[modid] = new ModdedDex(modid);
|
||||||
return this.moddedDexes[modid];
|
return this.moddedDexes[modid];
|
||||||
},
|
}
|
||||||
|
|
||||||
resolveAvatar(avatar: string): string {
|
resolveAvatar(avatar: string): string {
|
||||||
if (avatar in BattleAvatarNumbers) {
|
if (avatar in BattleAvatarNumbers) {
|
||||||
|
|
@ -189,7 +206,7 @@ const Dex = {
|
||||||
'/avatars/' + encodeURIComponent(avatar).replace(/\%3F/g, '?');
|
'/avatars/' + encodeURIComponent(avatar).replace(/\%3F/g, '?');
|
||||||
}
|
}
|
||||||
return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png';
|
return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png';
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to sanitize strings from data files like `moves.js` and
|
* This is used to sanitize strings from data files like `moves.js` and
|
||||||
|
|
@ -205,13 +222,13 @@ const Dex = {
|
||||||
sanitizeName(name: any) {
|
sanitizeName(name: any) {
|
||||||
if (!name) return '';
|
if (!name) return '';
|
||||||
return ('' + name).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').slice(0, 50);
|
return ('' + name).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').slice(0, 50);
|
||||||
},
|
}
|
||||||
|
|
||||||
prefs(prop: string, value?: any, save?: boolean) {
|
prefs(prop: string, value?: any, save?: boolean) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (window.Storage && Storage.prefs) return Storage.prefs(prop, value, save);
|
if (window.Storage && Storage.prefs) return Storage.prefs(prop, value, save);
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
}
|
||||||
|
|
||||||
getShortName(name: string) {
|
getShortName(name: string) {
|
||||||
let shortName = name.replace(/[^A-Za-z0-9]+$/, '');
|
let shortName = name.replace(/[^A-Za-z0-9]+$/, '');
|
||||||
|
|
@ -219,7 +236,7 @@ const Dex = {
|
||||||
shortName += name.slice(shortName.length).replace(/[^\(\)]+/g, '').replace(/\(\)/g, '');
|
shortName += name.slice(shortName.length).replace(/[^\(\)]+/g, '').replace(/\(\)/g, '');
|
||||||
}
|
}
|
||||||
return shortName;
|
return shortName;
|
||||||
},
|
}
|
||||||
|
|
||||||
getEffect(name: string | null | undefined): PureEffect | Item | Ability | Move {
|
getEffect(name: string | null | undefined): PureEffect | Item | Ability | Move {
|
||||||
name = (name || '').trim();
|
name = (name || '').trim();
|
||||||
|
|
@ -232,7 +249,7 @@ const Dex = {
|
||||||
}
|
}
|
||||||
let id = toId(name);
|
let id = toId(name);
|
||||||
return new PureEffect(id, name);
|
return new PureEffect(id, name);
|
||||||
},
|
}
|
||||||
|
|
||||||
getMove(nameOrMove: string | Move | null | undefined): Move {
|
getMove(nameOrMove: string | Move | null | undefined): Move {
|
||||||
if (nameOrMove && typeof nameOrMove !== 'string') {
|
if (nameOrMove && typeof nameOrMove !== 'string') {
|
||||||
|
|
@ -273,16 +290,13 @@ const Dex = {
|
||||||
let move = new Move(id, name, data);
|
let move = new Move(id, name, data);
|
||||||
window.BattleMovedex[id] = move;
|
window.BattleMovedex[id] = move;
|
||||||
return move;
|
return move;
|
||||||
},
|
}
|
||||||
|
|
||||||
getCategory(move: Move, gen: number, type?: string) {
|
getGen3Category(type: string) {
|
||||||
if (gen <= 3 && move.category !== 'Status') {
|
return [
|
||||||
return [
|
'Fire', 'Water', 'Grass', 'Electric', 'Ice', 'Psychic', 'Dark', 'Dragon',
|
||||||
'Fire', 'Water', 'Grass', 'Electric', 'Ice', 'Psychic', 'Dark', 'Dragon',
|
].includes(type) ? 'Special' : 'Physical';
|
||||||
].includes(type || move.type) ? 'Special' : 'Physical';
|
}
|
||||||
}
|
|
||||||
return move.category;
|
|
||||||
},
|
|
||||||
|
|
||||||
getItem(nameOrItem: string | Item | null | undefined): Item {
|
getItem(nameOrItem: string | Item | null | undefined): Item {
|
||||||
if (nameOrItem && typeof nameOrItem !== 'string') {
|
if (nameOrItem && typeof nameOrItem !== 'string') {
|
||||||
|
|
@ -302,7 +316,7 @@ const Dex = {
|
||||||
let item = new Item(id, name, data);
|
let item = new Item(id, name, data);
|
||||||
window.BattleItems[id] = item;
|
window.BattleItems[id] = item;
|
||||||
return item;
|
return item;
|
||||||
},
|
}
|
||||||
|
|
||||||
getAbility(nameOrAbility: string | Ability | null | undefined): Ability {
|
getAbility(nameOrAbility: string | Ability | null | undefined): Ability {
|
||||||
if (nameOrAbility && typeof nameOrAbility !== 'string') {
|
if (nameOrAbility && typeof nameOrAbility !== 'string') {
|
||||||
|
|
@ -322,7 +336,7 @@ const Dex = {
|
||||||
let ability = new Ability(id, name, data);
|
let ability = new Ability(id, name, data);
|
||||||
window.BattleAbilities[id] = ability;
|
window.BattleAbilities[id] = ability;
|
||||||
return ability;
|
return ability;
|
||||||
},
|
}
|
||||||
|
|
||||||
getTemplate(nameOrTemplate: string | Template | null | undefined): Template {
|
getTemplate(nameOrTemplate: string | Template | null | undefined): Template {
|
||||||
if (nameOrTemplate && typeof nameOrTemplate !== 'string') {
|
if (nameOrTemplate && typeof nameOrTemplate !== 'string') {
|
||||||
|
|
@ -363,7 +377,7 @@ const Dex = {
|
||||||
forme,
|
forme,
|
||||||
});
|
});
|
||||||
return template;
|
return template;
|
||||||
},
|
}
|
||||||
|
|
||||||
getTier(pokemon: Pokemon, gen = 7, isDoubles = false): string {
|
getTier(pokemon: Pokemon, gen = 7, isDoubles = false): string {
|
||||||
let table = window.BattleTeambuilderTable;
|
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
|
// 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';
|
if (this.getTemplate(pokemon.species).gen > gen) return 'Illegal';
|
||||||
return table.overrideTier[toId(pokemon.species)];
|
return table.overrideTier[toId(pokemon.species)];
|
||||||
},
|
}
|
||||||
|
|
||||||
getType(type: any): Effect {
|
getType(type: any): Effect {
|
||||||
if (!type || typeof type === 'string') {
|
if (!type || typeof type === 'string') {
|
||||||
|
|
@ -389,41 +403,16 @@ const Dex = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
},
|
}
|
||||||
|
|
||||||
getAbilitiesFor(template: any, gen = 7): {[id: string]: string} {
|
hasAbility(template: Template, ability: string) {
|
||||||
template = this.getTemplate(template);
|
for (const i in template.abilities) {
|
||||||
if (gen < 3 || !template.abilities) return {};
|
// @ts-ignore
|
||||||
const id = template.id;
|
if (ability === template.abilities[i]) return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
|
|
||||||
loadedSpriteData: {xy: 1, bw: 0},
|
|
||||||
loadSpriteData(gen: 'xy' | 'bw') {
|
loadSpriteData(gen: 'xy' | 'bw') {
|
||||||
if (this.loadedSpriteData[gen]) return;
|
if (this.loadedSpriteData[gen]) return;
|
||||||
this.loadedSpriteData[gen] = 1;
|
this.loadedSpriteData[gen] = 1;
|
||||||
|
|
@ -435,7 +424,7 @@ const Dex = {
|
||||||
let el = document.createElement('script');
|
let el = document.createElement('script');
|
||||||
el.src = path + 'data/pokedex-mini-bw.js' + qs;
|
el.src = path + 'data/pokedex-mini-bw.js' + qs;
|
||||||
document.getElementsByTagName('body')[0].appendChild(el);
|
document.getElementsByTagName('body')[0].appendChild(el);
|
||||||
},
|
}
|
||||||
getSpriteData(pokemon: Pokemon | Template | string, siden: number, options: {
|
getSpriteData(pokemon: Pokemon | Template | string, siden: number, options: {
|
||||||
gen?: number, shiny?: boolean, gender?: GenderName, afd?: boolean, noScale?: boolean,
|
gen?: number, shiny?: boolean, gender?: GenderName, afd?: boolean, noScale?: boolean,
|
||||||
} = {gen: 6}) {
|
} = {gen: 6}) {
|
||||||
|
|
@ -576,7 +565,7 @@ const Dex = {
|
||||||
}
|
}
|
||||||
|
|
||||||
return spriteData;
|
return spriteData;
|
||||||
},
|
}
|
||||||
|
|
||||||
getPokemonIcon(pokemon: any, facingLeft?: boolean) {
|
getPokemonIcon(pokemon: any, facingLeft?: boolean) {
|
||||||
let num = 0;
|
let num = 0;
|
||||||
|
|
@ -620,7 +609,7 @@ const Dex = {
|
||||||
let left = (num % 12) * 40;
|
let left = (num % 12) * 40;
|
||||||
let fainted = (pokemon && pokemon.fainted ? ';opacity:.3;filter:grayscale(100%) brightness(.5)' : '');
|
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;
|
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) {
|
getTeambuilderSprite(pokemon: any, gen: number = 0) {
|
||||||
if (!pokemon) return '';
|
if (!pokemon) return '';
|
||||||
|
|
@ -661,7 +650,7 @@ const Dex = {
|
||||||
else if (gen <= 3 && template.gen <= 3) spriteDir = Dex.resourcePrefix + 'sprites/rse';
|
else if (gen <= 3 && template.gen <= 3) spriteDir = Dex.resourcePrefix + 'sprites/rse';
|
||||||
else if (gen <= 4 && template.gen <= 4) spriteDir = Dex.resourcePrefix + 'sprites/dpp';
|
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';
|
return 'background-image:url(' + spriteDir + shiny + '/' + spriteid + '.png);background-position:10px 5px;background-repeat:no-repeat';
|
||||||
},
|
}
|
||||||
|
|
||||||
getItemIcon(item: any) {
|
getItemIcon(item: any) {
|
||||||
let num = 0;
|
let num = 0;
|
||||||
|
|
@ -671,13 +660,13 @@ const Dex = {
|
||||||
let top = Math.floor(num / 16) * 24;
|
let top = Math.floor(num / 16) * 24;
|
||||||
let left = (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';
|
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
|
getTypeIcon(type: string, b?: boolean) { // b is just for utilichart.js
|
||||||
if (!type) return '';
|
if (!type) return '';
|
||||||
let sanitizedType = type.replace(/\?/g, '%3f');
|
let sanitizedType = type.replace(/\?/g, '%3f');
|
||||||
return '<img src="' + Dex.resourcePrefix + 'sprites/types/' + sanitizedType + '.png" alt="' + type + '" height="14" width="32"' + (b ? ' class="b"' : '') + ' />';
|
return '<img src="' + Dex.resourcePrefix + 'sprites/types/' + sanitizedType + '.png" alt="' + type + '" height="14" width="32"' + (b ? ' class="b"' : '') + ' />';
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModdedDex {
|
class ModdedDex {
|
||||||
|
|
@ -688,10 +677,10 @@ class ModdedDex {
|
||||||
Items: {} as any as {[k: string]: Item},
|
Items: {} as any as {[k: string]: Item},
|
||||||
Templates: {} as any as {[k: string]: Template},
|
Templates: {} as any as {[k: string]: Template},
|
||||||
};
|
};
|
||||||
getAbility = Dex.getAbility;
|
getAbility: (nameOrAbility: string | Ability | null | undefined) => Ability = Dex.getAbility;
|
||||||
constructor(modid: ID) {
|
constructor(modid: ID) {
|
||||||
this.modid = modid;
|
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");
|
if (!modid.startsWith('gen') || !gen) throw new Error("Unsupported modid");
|
||||||
this.gen = gen;
|
this.gen = gen;
|
||||||
}
|
}
|
||||||
|
|
@ -716,6 +705,9 @@ class ModdedDex {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.gen <= 3 && data.category !== 'Status') {
|
||||||
|
data.category = Dex.getGen3Category(data.type);
|
||||||
|
}
|
||||||
|
|
||||||
const move = new Move(id, name, data);
|
const move = new Move(id, name, data);
|
||||||
this.cache.Moves[id] = move;
|
this.cache.Moves[id] = move;
|
||||||
|
|
@ -753,18 +745,25 @@ class ModdedDex {
|
||||||
let data = {...Dex.getTemplate(name)};
|
let data = {...Dex.getTemplate(name)};
|
||||||
|
|
||||||
const table = window.BattleTeambuilderTable[this.modid];
|
const table = window.BattleTeambuilderTable[this.modid];
|
||||||
if (id in table.overrideAbility) {
|
if (this.gen < 3) {
|
||||||
data.abilities = {...data.abilities, 0: table.overrideAbility[id]};
|
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) {
|
if (id in table.overrideStats) {
|
||||||
data.baseStats = {...data.baseStats, ...table.overrideStats[id]};
|
data.baseStats = {...data.baseStats, ...table.overrideStats[id]};
|
||||||
}
|
}
|
||||||
if (id in table.overrideType) data.types = table.overrideType[id].split('/');
|
if (id in table.overrideType) data.types = table.overrideType[id].split('/');
|
||||||
if (id in table.removeSecondAbility) {
|
|
||||||
data.abilities = {...data.abilities};
|
if (id in table.overrideTier) data.tier = table.overrideTier[id];
|
||||||
// @ts-ignore
|
|
||||||
delete data.abilities['1'];
|
|
||||||
}
|
|
||||||
|
|
||||||
const template = new Template(id, name, data);
|
const template = new Template(id, name, data);
|
||||||
this.cache.Templates[id] = template;
|
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 EffectTable = {[effectid: string]: EffectState};
|
||||||
type HPColor = 'r' | 'y' | 'g';
|
type HPColor = 'r' | 'y' | 'g';
|
||||||
|
|
||||||
class Pokemon {
|
class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
name = '';
|
name = '';
|
||||||
species = '';
|
species = '';
|
||||||
|
|
||||||
|
|
@ -94,7 +94,6 @@ class Pokemon {
|
||||||
volatiles: EffectTable = {};
|
volatiles: EffectTable = {};
|
||||||
turnstatuses: EffectTable = {};
|
turnstatuses: EffectTable = {};
|
||||||
movestatuses: EffectTable = {};
|
movestatuses: EffectTable = {};
|
||||||
weightkg = 0;
|
|
||||||
lastMove = '';
|
lastMove = '';
|
||||||
|
|
||||||
/** [[moveName, ppUsed]] */
|
/** [[moveName, ppUsed]] */
|
||||||
|
|
@ -132,7 +131,7 @@ class Pokemon {
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
getPixelRange(pixels: number, color: HPColor): [number, number] {
|
static getPixelRange(pixels: number, color: HPColor | ''): [number, number] {
|
||||||
let epsilon = 0.5 / 714;
|
let epsilon = 0.5 / 714;
|
||||||
|
|
||||||
if (pixels === 0) return [0, 0];
|
if (pixels === 0) return [0, 0];
|
||||||
|
|
@ -155,7 +154,7 @@ class Pokemon {
|
||||||
|
|
||||||
return [pixels / 48, (pixels + 1) / 48 - epsilon];
|
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]) {
|
if (range[0] === range[1]) {
|
||||||
let percentage = Math.abs(range[0] * 100);
|
let percentage = Math.abs(range[0] * 100);
|
||||||
if (Math.floor(percentage) === percentage) {
|
if (Math.floor(percentage) === percentage) {
|
||||||
|
|
@ -185,8 +184,8 @@ class Pokemon {
|
||||||
return [damage[2] / 100, damage[2] / 100];
|
return [damage[2] / 100, damage[2] / 100];
|
||||||
}
|
}
|
||||||
// pixel damage
|
// pixel damage
|
||||||
let oldrange = this.getPixelRange(damage[3], damage[4]);
|
let oldrange = Pokemon.getPixelRange(damage[3], damage[4]);
|
||||||
let newrange = this.getPixelRange(damage[3] + damage[0], this.hpcolor);
|
let newrange = Pokemon.getPixelRange(damage[3] + damage[0], this.hpcolor);
|
||||||
if (damage[0] === 0) {
|
if (damage[0] === 0) {
|
||||||
// no change in displayed pixel width
|
// no change in displayed pixel width
|
||||||
return [0, newrange[1] - newrange[0]];
|
return [0, newrange[1] - newrange[0]];
|
||||||
|
|
@ -384,6 +383,10 @@ class Pokemon {
|
||||||
// let badBoostTable = ['Normal', '−1', '−2', '−3', '−4', '−5', '−6'];
|
// let badBoostTable = ['Normal', '−1', '−2', '−3', '−4', '−5', '−6'];
|
||||||
return '' + badBoostTable[-this.boosts[boostStat]] + ' ' + boostStatTable[boostStat];
|
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) {
|
getBoostType(boostStat: BoostStatName) {
|
||||||
if (!this.boosts[boostStat]) return 'neutral';
|
if (!this.boosts[boostStat]) return 'neutral';
|
||||||
if (this.boosts[boostStat] > 0) return 'good';
|
if (this.boosts[boostStat] > 0) return 'good';
|
||||||
|
|
@ -391,9 +394,6 @@ class Pokemon {
|
||||||
}
|
}
|
||||||
clearVolatile() {
|
clearVolatile() {
|
||||||
this.ability = this.baseAbility;
|
this.ability = this.baseAbility;
|
||||||
if (window.BattlePokedex && BattlePokedex[this.species] && BattlePokedex[this.species].weightkg) {
|
|
||||||
this.weightkg = BattlePokedex[this.species].weightkg;
|
|
||||||
}
|
|
||||||
this.boosts = {};
|
this.boosts = {};
|
||||||
this.clearVolatiles();
|
this.clearVolatiles();
|
||||||
for (let i = 0; i < this.moveTrack.length; i++) {
|
for (let i = 0; i < this.moveTrack.length; i++) {
|
||||||
|
|
@ -452,29 +452,59 @@ class Pokemon {
|
||||||
this.removeVolatile('typeadd' as ID);
|
this.removeVolatile('typeadd' as ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getTypes(): [string[], string] {
|
getTypes(serverPokemon?: ServerPokemon): [ReadonlyArray<TypeName>, TypeName | ''] {
|
||||||
let types;
|
let types: ReadonlyArray<TypeName>;
|
||||||
if (this.volatiles.typechange) {
|
if (this.volatiles.typechange) {
|
||||||
types = this.volatiles.typechange[1].split('/');
|
types = this.volatiles.typechange[1].split('/');
|
||||||
} else {
|
} else {
|
||||||
const species = this.getSpecies();
|
types = this.getTemplate(serverPokemon).types;
|
||||||
types = (
|
}
|
||||||
window.BattleTeambuilderTable &&
|
if (this.volatiles.roost && types.includes('Flying')) {
|
||||||
window.BattleTeambuilderTable['gen' + this.side.battle.gen] &&
|
types = types.filter(typeName => typeName !== 'Flying');
|
||||||
window.BattleTeambuilderTable['gen' + this.side.battle.gen].overrideType[toId(species)]
|
if (!types.length) types = ['Normal'];
|
||||||
);
|
|
||||||
if (types) types = types.split('/');
|
|
||||||
if (!types) types = Dex.getTemplate(species).types || [];
|
|
||||||
}
|
}
|
||||||
const addedType = (this.volatiles.typeadd ? this.volatiles.typeadd[1] : '');
|
const addedType = (this.volatiles.typeadd ? this.volatiles.typeadd[1] : '');
|
||||||
return [types, addedType];
|
return [types, addedType];
|
||||||
}
|
}
|
||||||
getTypeList() {
|
isGrounded(serverPokemon?: ServerPokemon) {
|
||||||
const [types, addedType] = this.getTypes();
|
const battle = this.side.battle;
|
||||||
return types.concat(addedType);
|
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 {
|
getTypeList(serverPokemon?: ServerPokemon) {
|
||||||
return this.volatiles.formechange ? this.volatiles.formechange[1] : this.species;
|
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() {
|
reset() {
|
||||||
this.clearVolatile();
|
this.clearVolatile();
|
||||||
|
|
@ -500,7 +530,7 @@ class Pokemon {
|
||||||
// Draw the health bar to the middle of the range.
|
// Draw the health bar to the middle of the range.
|
||||||
// This affects the width of the visual health bar *only*; it
|
// This affects the width of the visual health bar *only*; it
|
||||||
// does not affect the ranges displayed in any way.
|
// 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;
|
let ratio = (range[0] + range[1]) / 2;
|
||||||
return Math.round(maxWidth * ratio) || 1;
|
return Math.round(maxWidth * ratio) || 1;
|
||||||
}
|
}
|
||||||
|
|
@ -510,11 +540,11 @@ class Pokemon {
|
||||||
}
|
}
|
||||||
return percentage * maxWidth / 100;
|
return percentage * maxWidth / 100;
|
||||||
}
|
}
|
||||||
hpDisplay(precision = 1) {
|
static getHPText(pokemon: PokemonHealth, precision = 1) {
|
||||||
if (this.maxhp === 100) return this.hp + '%';
|
if (pokemon.maxhp === 100) return pokemon.hp + '%';
|
||||||
if (this.maxhp !== 48) return (100 * this.hp / this.maxhp).toFixed(precision) + '%';
|
if (pokemon.maxhp !== 48) return (100 * pokemon.hp / pokemon.maxhp).toFixed(precision) + '%';
|
||||||
let range = this.getPixelRange(this.hp, this.hpcolor);
|
let range = Pokemon.getPixelRange(pokemon.hp, pokemon.hpcolor);
|
||||||
return this.getFormattedRange(range, precision, '–');
|
return Pokemon.getFormattedRange(range, precision, '–');
|
||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.sprite) this.sprite.destroy();
|
if (this.sprite) this.sprite.destroy();
|
||||||
|
|
@ -875,6 +905,48 @@ enum Playback {
|
||||||
Seeking = 5,
|
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 {
|
class Battle {
|
||||||
scene: BattleScene | BattleSceneStub;
|
scene: BattleScene | BattleSceneStub;
|
||||||
|
|
||||||
|
|
@ -923,9 +995,12 @@ class Battle {
|
||||||
yourSide: Side = null!;
|
yourSide: Side = null!;
|
||||||
p1: Side = null!;
|
p1: Side = null!;
|
||||||
p2: Side = null!;
|
p2: Side = null!;
|
||||||
|
myPokemon: ServerPokemon[] | null = null;
|
||||||
sides: [Side, Side] = [null!, null!];
|
sides: [Side, Side] = [null!, null!];
|
||||||
lastMove = '';
|
lastMove = '';
|
||||||
|
|
||||||
gen = 7;
|
gen = 7;
|
||||||
|
dex: ModdedDex = Dex;
|
||||||
teamPreviewCount = 0;
|
teamPreviewCount = 0;
|
||||||
speciesClause = false;
|
speciesClause = false;
|
||||||
tier = '';
|
tier = '';
|
||||||
|
|
@ -934,6 +1009,11 @@ class Battle {
|
||||||
endLastTurnPending = false;
|
endLastTurnPending = false;
|
||||||
totalTimeLeft = 0;
|
totalTimeLeft = 0;
|
||||||
graceTimeLeft = 0;
|
graceTimeLeft = 0;
|
||||||
|
/**
|
||||||
|
* true: timer on, state unknown
|
||||||
|
* false: timer off
|
||||||
|
* number: seconds left this turn
|
||||||
|
*/
|
||||||
kickingInactive: number | boolean = false;
|
kickingInactive: number | boolean = false;
|
||||||
|
|
||||||
// options
|
// options
|
||||||
|
|
@ -1394,7 +1474,7 @@ class Battle {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (damage[1] !== 100) {
|
||||||
let hover = '' + ((damage[0] < 0) ? '\u2212' : '') +
|
let hover = '' + ((damage[0] < 0) ? '\u2212' : '') +
|
||||||
Math.abs(damage[0]) + '/' + damage[1];
|
Math.abs(damage[0]) + '/' + damage[1];
|
||||||
|
|
@ -1406,7 +1486,7 @@ class Battle {
|
||||||
}
|
}
|
||||||
args[3] = damageinfo;
|
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);
|
this.log(args, kwArgs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1439,7 +1519,7 @@ class Battle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.scene.runOtherAnim('heal' as ID, [poke]);
|
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);
|
this.log(args, kwArgs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1449,7 +1529,7 @@ class Battle {
|
||||||
if (cpoke) {
|
if (cpoke) {
|
||||||
let damage = cpoke.healthParse(args[2 + 2 * k])!;
|
let damage = cpoke.healthParse(args[2 + 2 * k])!;
|
||||||
let range = cpoke.getDamageRange(damage);
|
let range = cpoke.getDamageRange(damage);
|
||||||
let formattedRange = cpoke.getFormattedRange(range, 0, ' to ');
|
let formattedRange = Pokemon.getFormattedRange(range, 0, ' to ');
|
||||||
let diff = damage[0];
|
let diff = damage[0];
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
this.scene.healAnim(cpoke, formattedRange);
|
this.scene.healAnim(cpoke, formattedRange);
|
||||||
|
|
@ -2058,11 +2138,10 @@ class Battle {
|
||||||
}
|
}
|
||||||
newSpecies = args[2].substr(0, commaIndex);
|
newSpecies = args[2].substr(0, commaIndex);
|
||||||
}
|
}
|
||||||
let template = Dex.getTemplate(newSpecies);
|
let template = this.dex.getTemplate(newSpecies);
|
||||||
|
|
||||||
poke.species = newSpecies;
|
poke.species = newSpecies;
|
||||||
poke.ability = poke.baseAbility = (template.abilities ? template.abilities['0'] : '');
|
poke.ability = poke.baseAbility = (template.abilities ? template.abilities['0'] : '');
|
||||||
poke.weightkg = template.weightkg;
|
|
||||||
|
|
||||||
poke.details = args[2];
|
poke.details = args[2];
|
||||||
poke.searchid = args[1].substr(0, 2) + args[1].substr(3) + '|' + args[2];
|
poke.searchid = args[1].substr(0, 2) + args[1].substr(3) + '|' + args[2];
|
||||||
|
|
@ -2083,7 +2162,6 @@ class Battle {
|
||||||
|
|
||||||
poke.boosts = {...tpoke.boosts};
|
poke.boosts = {...tpoke.boosts};
|
||||||
poke.copyTypesFrom(tpoke);
|
poke.copyTypesFrom(tpoke);
|
||||||
poke.weightkg = tpoke.weightkg;
|
|
||||||
poke.ability = tpoke.ability;
|
poke.ability = tpoke.ability;
|
||||||
const species = (tpoke.volatiles.formechange ? tpoke.volatiles.formechange[1] : tpoke.species);
|
const species = (tpoke.volatiles.formechange ? tpoke.volatiles.formechange[1] : tpoke.species);
|
||||||
const pokemon = tpoke;
|
const pokemon = tpoke;
|
||||||
|
|
@ -2187,6 +2265,7 @@ class Battle {
|
||||||
break;
|
break;
|
||||||
case 'imprison':
|
case 'imprison':
|
||||||
this.scene.resultAnim(poke, 'Imprisoning', 'good');
|
this.scene.resultAnim(poke, 'Imprisoning', 'good');
|
||||||
|
break;
|
||||||
case 'disable':
|
case 'disable':
|
||||||
this.scene.resultAnim(poke, 'Disabled', 'bad');
|
this.scene.resultAnim(poke, 'Disabled', 'bad');
|
||||||
break;
|
break;
|
||||||
|
|
@ -2238,6 +2317,11 @@ class Battle {
|
||||||
break;
|
break;
|
||||||
case 'autotomize':
|
case 'autotomize':
|
||||||
this.scene.resultAnim(poke, 'Lightened', 'good');
|
this.scene.resultAnim(poke, 'Lightened', 'good');
|
||||||
|
if (poke.volatiles.autotomize) {
|
||||||
|
poke.volatiles.autotomize[1]++;
|
||||||
|
} else {
|
||||||
|
poke.addVolatile('autotomize' as ID, 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'focusenergy':
|
case 'focusenergy':
|
||||||
this.scene.resultAnim(poke, '+Crit rate', 'good');
|
this.scene.resultAnim(poke, '+Crit rate', 'good');
|
||||||
|
|
@ -2684,7 +2768,7 @@ class Battle {
|
||||||
return data.spriteData[siden];
|
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.details = details;
|
||||||
output.name = name;
|
output.name = name;
|
||||||
output.species = name;
|
output.species = name;
|
||||||
|
|
@ -2699,7 +2783,7 @@ class Battle {
|
||||||
splitDetails.pop();
|
splitDetails.pop();
|
||||||
}
|
}
|
||||||
if (splitDetails[splitDetails.length - 1] === 'M' || splitDetails[splitDetails.length - 1] === 'F') {
|
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();
|
splitDetails.pop();
|
||||||
}
|
}
|
||||||
if (splitDetails[1]) {
|
if (splitDetails[1]) {
|
||||||
|
|
@ -2710,13 +2794,7 @@ class Battle {
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
parseHealth(hpstring: string, output: any = {}): {
|
parseHealth(hpstring: string, output: PokemonHealth = {} as any) {
|
||||||
hp: number,
|
|
||||||
maxhp: number,
|
|
||||||
hpcolor: HPColor | '',
|
|
||||||
status: StatusName | '',
|
|
||||||
fainted?: boolean,
|
|
||||||
} | null {
|
|
||||||
let [hp, status] = hpstring.split(' ');
|
let [hp, status] = hpstring.split(' ');
|
||||||
|
|
||||||
// hp parse
|
// hp parse
|
||||||
|
|
@ -3176,6 +3254,7 @@ class Battle {
|
||||||
}
|
}
|
||||||
case 'gen': {
|
case 'gen': {
|
||||||
this.gen = parseInt(args[1], 10);
|
this.gen = parseInt(args[1], 10);
|
||||||
|
this.dex = Dex.mod(`gen${this.gen}` as ID);
|
||||||
this.scene.updateGen();
|
this.scene.updateGen();
|
||||||
this.log(args);
|
this.log(args);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -711,6 +711,60 @@ License: GPLv2
|
||||||
-ms-interpolation-mode: nearest-neighbor;
|
-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
|
* Message log styling
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
@ -829,7 +883,7 @@ License: GPLv2
|
||||||
color: #DDD;
|
color: #DDD;
|
||||||
}
|
}
|
||||||
.stat-boosted {
|
.stat-boosted {
|
||||||
color: #119911;
|
color: #117911;
|
||||||
}
|
}
|
||||||
.stat-lowered {
|
.stat-lowered {
|
||||||
color: #991111;
|
color: #991111;
|
||||||
|
|
|
||||||
|
|
@ -1970,12 +1970,12 @@ a.ilink.yours {
|
||||||
.movemenu button small.type {
|
.movemenu button small.type {
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
float: left;
|
float: left;
|
||||||
font-size: 7pt;
|
font-size: 8pt;
|
||||||
}
|
}
|
||||||
.movemenu button small.pp {
|
.movemenu button small.pp {
|
||||||
padding-top: 3px;
|
padding-top: 2px;
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 7pt;
|
font-size: 8pt;
|
||||||
}
|
}
|
||||||
.megaevo {
|
.megaevo {
|
||||||
clear: both;
|
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
|
* Teambuilder
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
<script src="js/client-ladder.js"></script>
|
<script src="js/client-ladder.js"></script>
|
||||||
<script src="js/client-chat.js"></script>
|
<script src="js/client-chat.js"></script>
|
||||||
<script src="js/client-chat-tournament.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-battle.js"></script>
|
||||||
<script src="js/client-rooms.js"></script>
|
<script src="js/client-rooms.js"></script>
|
||||||
<script src="js/storage.js"></script>
|
<script src="js/storage.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"strict": true
|
"strict": true
|
||||||
},
|
},
|
||||||
"types": ["node"],
|
"types": [],
|
||||||
"include": [
|
"include": [
|
||||||
"./js/lib/preact.d.ts",
|
"./js/lib/preact.d.ts",
|
||||||
"./src/*"
|
"./src/*"
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,9 @@
|
||||||
"no-bitwise": false,
|
"no-bitwise": false,
|
||||||
"prefer-conditional-expression": false,
|
"prefer-conditional-expression": false,
|
||||||
"no-shadowed-variable": [true, {"temporalDeadZone": false}],
|
"no-shadowed-variable": [true, {"temporalDeadZone": false}],
|
||||||
|
"no-switch-case-fall-through": true,
|
||||||
"object-literal-sort-keys": false,
|
"object-literal-sort-keys": false,
|
||||||
"object-literal-key-quotes": [true, "as-needed"],
|
"object-literal-key-quotes": false,
|
||||||
"trailing-comma": [
|
"trailing-comma": [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user