mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-05-06 05:26:21 -05:00
877 lines
34 KiB
JavaScript
877 lines
34 KiB
JavaScript
(function($) {
|
|
|
|
var BattleRoom = this.BattleRoom = this.Room.extend({
|
|
minWidth: 955,
|
|
maxWidth: 1180,
|
|
initialize: function(data) {
|
|
this.me = {};
|
|
|
|
this.$el.addClass('ps-room-opaque').html('<div class="battle">Battle is here</div><div class="foehint"></div><div class="battle-log"></div><div class="battle-log-add">Connecting...</div><div class="battle-controls"></div>');
|
|
|
|
this.$battle = this.$el.find('.battle');
|
|
this.$controls = this.$el.find('.battle-controls');
|
|
this.$chatFrame = this.$el.find('.battle-log');
|
|
this.$chatAdd = this.$el.find('.battle-log-add');
|
|
this.$join = null;
|
|
this.$foeHint = this.$el.find('.foehint');
|
|
|
|
this.battle = new Battle(this.$battle, this.$chatFrame);
|
|
|
|
this.$chat = this.$chatFrame.find('.inner');
|
|
|
|
// this.battle.setMute(me.isMuted());
|
|
this.battle.customCallback = _.bind(this.updateControls, this);
|
|
this.battle.endCallback = _.bind(this.updateControls, this);
|
|
this.battle.startCallback = _.bind(this.updateControls, this);
|
|
this.battle.stagnateCallback = _.bind(this.updateControls, this);
|
|
|
|
this.battle.play();
|
|
|
|
app.user.on('change', this.updateUser, this);
|
|
this.updateUser();
|
|
},
|
|
events: {
|
|
'keydown textarea': 'keyPress',
|
|
'submit form': 'submit',
|
|
'click .username': 'clickUsername'
|
|
},
|
|
battleEnded: false,
|
|
focus: function() {
|
|
if (this.$chatbox) this.$chatbox.focus();
|
|
},
|
|
join: function() {
|
|
app.send('/join '+this.id);
|
|
},
|
|
leave: function() {
|
|
app.send('/leave '+this.id);
|
|
if (this.battle) this.battle.dealloc();
|
|
},
|
|
updateLayout: function() {
|
|
if (this.$el.width() < 950) {
|
|
this.battle.messageDelay = 800;
|
|
} else {
|
|
this.battle.messageDelay = 8;
|
|
}
|
|
if (this.$chat) this.$chatFrame.scrollTop(this.$chat.height());
|
|
},
|
|
show: function() {
|
|
Room.prototype.show.apply(this, arguments);
|
|
this.updateLayout();
|
|
},
|
|
receive: function(data) {
|
|
this.add(data);
|
|
},
|
|
init: function(data) {
|
|
var log = data.split('\n');
|
|
if (data.substr(0,6) === '|init|') log.shift();
|
|
if (this.battle.activityQueue.length) return;
|
|
this.battle.activityQueue = log;
|
|
this.battle.fastForwardTo(-1);
|
|
this.updateLayout();
|
|
this.updateControls();
|
|
},
|
|
add: function(data) {
|
|
if (data.substr(0,6) === '|init|') {
|
|
return this.init(data);
|
|
}
|
|
|
|
var log = data.split('\n');
|
|
for (var i = 0; i < log.length; i++) {
|
|
var logLine = log[i];
|
|
|
|
if (logLine === '') {
|
|
this.callbackWaiting = false;
|
|
this.$controls.html('');
|
|
}
|
|
|
|
if (logLine.substr(0, 6) === '|chat|' || logLine.substr(0, 3) === '|c|' || logLine.substr(0, 9) === '|chatmsg|' || logLine.substr(0, 10) === '|inactive|') {
|
|
this.battle.instantAdd(logLine);
|
|
} else {
|
|
this.battle.activityQueue.push(logLine);
|
|
}
|
|
}
|
|
this.battle.add('', Tools.prefs('noanim'));
|
|
this.updateControls();
|
|
},
|
|
updateUser: function() {
|
|
var name = app.user.get('name');
|
|
var userid = app.user.get('userid');
|
|
if (!name) {
|
|
this.$chatAdd.html('Connecting...');
|
|
this.$chatbox = null;
|
|
} else if (!app.user.get('named')) {
|
|
this.$chatAdd.html('<form><button>Join chat</button></form>');
|
|
this.$chatbox = null;
|
|
} else {
|
|
this.$chatAdd.html('<form class="chatbox"><label style="' + hashColor(userid) + '">' + Tools.escapeHTML(name) + ':</label> <textarea class="textbox" type="text" size="70" autocomplete="off"></textarea></form>');
|
|
this.$chatbox = this.$chatAdd.find('textarea');
|
|
this.$chatbox.autoResize({
|
|
animate: false,
|
|
extraSpace: 0
|
|
});
|
|
if (this === app.curSideRoom || this === app.curRoom) {
|
|
this.$chatbox.focus();
|
|
}
|
|
}
|
|
},
|
|
submit: function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var text;
|
|
if ((text = this.$chatbox.val())) {
|
|
// this.tabComplete.reset();
|
|
// this.chatHistory.push(text);
|
|
// text = this.parseCommand(text);
|
|
if (text) {
|
|
this.send(text);
|
|
}
|
|
this.$chatbox.val('');
|
|
}
|
|
},
|
|
keyPress: function(e) {
|
|
if (e.keyCode === 13 && !e.shiftKey) { // Enter
|
|
this.submit(e);
|
|
} else if (e.keyCode === 9 && !e.shiftKey && !e.ctrlKey) { // Tab
|
|
// if (this.handleTabComplete(this.$chatbox)) {
|
|
// e.preventDefault();
|
|
// e.stopPropagation();
|
|
// }
|
|
}
|
|
},
|
|
clickUsername: function(e) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
var name = $(e.currentTarget).data('name');
|
|
app.addPopup('user', UserPopup, {name: name, sourceEl: e.currentTarget});
|
|
},
|
|
|
|
/*********************************************************
|
|
* Battle stuff
|
|
*********************************************************/
|
|
|
|
updateControls: function() {
|
|
if (this.$join) {
|
|
this.$join.remove();
|
|
this.$join = null;
|
|
}
|
|
|
|
if (this.battle.playbackState === 5) {
|
|
|
|
// battle is seeking
|
|
this.$controls.html('');
|
|
return;
|
|
|
|
} else if (this.battle.playbackState === 2) {
|
|
|
|
// battle is playing
|
|
this.$controls.html('<button name="skipTurn">Skip turn <i class="icon-step-forward"></i></button>');
|
|
return;
|
|
|
|
}
|
|
|
|
// tooltips
|
|
var myActive = this.battle.mySide.active;
|
|
var yourActive = this.battle.yourSide.active;
|
|
var buf = '';
|
|
if (yourActive[1]) {
|
|
buf += '<div style="position:absolute;top:85px;left:320px;width:90px;height:100px;"' + this.tooltipAttrs(yourActive[1].getIdent(), 'pokemon', true, 'foe') + '></div>';
|
|
}
|
|
if (yourActive[0]) {
|
|
buf += '<div style="position:absolute;top:90px;left:390px;width:100px;height:100px;"' + this.tooltipAttrs(yourActive[0].getIdent(), 'pokemon', true, 'foe') + '></div>';
|
|
}
|
|
if (myActive[0]) {
|
|
buf += '<div style="position:absolute;top:210px;left:130px;width:180px;height:160px;"' + this.tooltipAttrs(myActive[0].getIdent(), 'pokemon', true, true) + '></div>';
|
|
}
|
|
if (myActive[1]) {
|
|
buf += '<div style="position:absolute;top:210px;left:270px;width:160px;height:160px;"' + this.tooltipAttrs(myActive[1].getIdent(), 'pokemon', true, true) + '></div>';
|
|
}
|
|
this.$foeHint.html(buf);
|
|
|
|
if (this.battle.done) {
|
|
|
|
// battle has ended
|
|
this.$controls.html('<div class="controls"><em><button name="instantReplay"><i class="icon-undo"></i> Instant Replay</button> <button name="saveReplay"><i class="icon-upload"></i> Share replay</button></div>');
|
|
|
|
} else if (this.side) {
|
|
|
|
// player
|
|
this.updateControlsForPlayer()
|
|
|
|
} else if (this.battle.mySide.initialized && this.battle.yourSide.initialized) {
|
|
|
|
// full battle
|
|
this.$controls.html('<em>Waiting for players...</em>');
|
|
|
|
} else {
|
|
|
|
// empty battle
|
|
this.$join = $('<div class="playbutton"><button name="joinBattle">Join Battle</button></div>');
|
|
this.$battle.append(this.$join);
|
|
|
|
}
|
|
|
|
// This intentionally doesn't happen if the battle is still playing,
|
|
// since those early-return.
|
|
app.topbar.updateTabbar();
|
|
},
|
|
updateControlsForPlayer: function() {
|
|
if (!this.request) {
|
|
if (this.battle.kickingInactive) {
|
|
this.$controls.html('<div class="controls"><p><button name="setTimer" value="off"><small>Stop timer</small></button> <small>← Your opponent has disconnected. Click this to delay your victory.</small></p></div>');
|
|
} else {
|
|
this.$controls.html('<div class="controls"><p><button name="setTimer" value="on"><small>Claim victory</small></button> <small>← Your opponent has disconnected. Click this if they don\'t reconnect.</small></p></div>');
|
|
}
|
|
}
|
|
|
|
var battle = this.battle;
|
|
// this.notifying = false;
|
|
|
|
if (!this.choice) {
|
|
this.updateSide(this.request.side);
|
|
}
|
|
|
|
// updated trappedness
|
|
if (false && 'trapped') {
|
|
var idx = parseInt(moveTarget[1], 10); // moveTarget is a poor name now...
|
|
if (this.request && this.request.active &&
|
|
this.request.active[idx]) {
|
|
// This pokemon is now known to be trapped.
|
|
this.request.active[idx].trapped = true;
|
|
// TODO: Maybe a more sophisticated UI for this.
|
|
// In singles, this isn't really necessary because the switch UI will be
|
|
// immediately disabled. However, in doubles it might not be obvious why
|
|
// the player is being asked to make a new decision without this message.
|
|
this.battle.add(this.battle.mySide.active[idx].getName() + ' is trapped!');
|
|
}
|
|
}
|
|
|
|
this.callbackWaiting = true;
|
|
var active = this.battle.mySide.active[0];
|
|
if (!active) active = {};
|
|
|
|
var act = '';
|
|
var switchables = [];
|
|
if (this.request) {
|
|
act = this.request.requestType;
|
|
if (this.request.side) {
|
|
switchables = this.battle.mySide.pokemon;
|
|
}
|
|
}
|
|
|
|
var type = '';
|
|
var moveTarget = '';
|
|
if (this.choice) {
|
|
type = this.choice.type;
|
|
moveTarget = this.choice.moveTarget;
|
|
if (this.choice.waiting) act = '';
|
|
}
|
|
// The choice object:
|
|
// !this.choice = nothing has been chosen
|
|
// this.choice.choices = array of choice strings
|
|
// this.choice.switchFlags = dict of pokemon indexes that have a switch pending
|
|
|
|
switch (act) {
|
|
case 'move':
|
|
{
|
|
if (!this.choice) {
|
|
this.choice = {
|
|
choices: [],
|
|
switchFlags: {}
|
|
}
|
|
while (switchables[this.choice.choices.length] && switchables[this.choice.choices.length].fainted) {
|
|
this.choice.choices.push('pass');
|
|
}
|
|
}
|
|
var pos = this.choice.choices.length - (type === 'movetarget'?1:0);
|
|
|
|
// hp bar
|
|
var hpbar = '';
|
|
if (switchables[pos].hp * 5 / switchables[pos].maxhp < 1) {
|
|
hpbar = '<small class="critical">';
|
|
} else if (switchables[pos].hp * 2 / switchables[pos].maxhp < 1) {
|
|
hpbar = '<small class="weak">';
|
|
} else {
|
|
hpbar = '<small class="healthy">';
|
|
}
|
|
hpbar += ''+switchables[pos].hp+'/'+switchables[pos].maxhp+'</small>';
|
|
|
|
var active = this.request;
|
|
if (active.active) active = active.active[pos];
|
|
var moves = active.moves;
|
|
var trapped = active.trapped;
|
|
this.finalDecision = active.maybeTrapped || false;
|
|
if (this.finalDecision) {
|
|
for (var i = pos + 1; i < this.battle.mySide.active.length; ++i) {
|
|
var p = this.battle.mySide.active[i];
|
|
if (p && !p.fainted) {
|
|
this.finalDecision = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
var controls = '<div class="controls"><div class="whatdo">';
|
|
if (type === 'move2' || type === 'movetarget') {
|
|
controls += '<button name="clearChoice">Back</button> ';
|
|
}
|
|
|
|
// Target selector
|
|
|
|
if (type === 'movetarget') {
|
|
controls += 'At who? '+hpbar+'</div>';
|
|
controls += '<div class="switchmenu" style="display:block">';
|
|
|
|
var myActive = this.battle.mySide.active;
|
|
var yourActive = this.battle.yourSide.active;
|
|
var yourSlot = yourActive.length-1-pos;
|
|
for (var i = yourActive.length-1; i >= 0; i--) {
|
|
var pokemon = yourActive[i];
|
|
|
|
var disabled = false;
|
|
if (moveTarget === 'adjacentAlly' || moveTarget === 'adjacentAllyOrSelf') {
|
|
disabled = true;
|
|
} else if (moveTarget === 'normal' || moveTarget === 'adjacentFoe') {
|
|
if (Math.abs(yourSlot-i) > 1) disabled = true;
|
|
}
|
|
|
|
if (!pokemon) {
|
|
controls += '<button disabled></button> ';
|
|
} else if (disabled || pokemon.zerohp) {
|
|
controls += '<button disabled' + this.tooltipAttrs(pokemon.getIdent(), 'pokemon', true, 'foe') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + (!pokemon.zerohp?'<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 {
|
|
var posString = '';
|
|
controls += '<button name="chooseMoveTarget" value="'+(i+1)+'"' + this.tooltipAttrs(pokemon.getIdent(), 'pokemon', true, 'foe') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.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 style="clear:both"></div> </div><div class="switchmenu" style="display:block">';
|
|
for (var i = 0; i < myActive.length; i++) {
|
|
var pokemon = myActive[i];
|
|
|
|
var disabled = false;
|
|
if (moveTarget === 'adjacentFoe') {
|
|
disabled = true;
|
|
} else if (moveTarget === 'normal' || moveTarget === 'adjacentAlly' || moveTarget === 'adjacentAllyOrSelf') {
|
|
if (Math.abs(pos-i) > 1) disabled = true;
|
|
}
|
|
if (moveTarget !== 'adjacentAllyOrSelf' && pos == i) disabled = true;
|
|
|
|
if (!pokemon) {
|
|
controls += '<button disabled="disabled"></button> ';
|
|
} else if (disabled || pokemon.zerohp) {
|
|
controls += '<button disabled="disabled"' + this.tooltipAttrs(i, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + (!pokemon.zerohp?'<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 {
|
|
controls += '<button name="chooseMoveTarget" value="' + (-(i+1)) + '"' + this.tooltipAttrs(i, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.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>';
|
|
this.$controls.html(controls);
|
|
break;
|
|
}
|
|
|
|
// Move chooser
|
|
|
|
controls += 'What will <strong>' + Tools.escapeHTML(switchables[pos].name) + '</strong> do? '+hpbar+'</div>';
|
|
var hasMoves = false;
|
|
var hasDisabled = false;
|
|
controls += '<div class="movecontrols"><div class="moveselect"><button name="selectMove">Attack</button></div><div class="movemenu">';
|
|
var movebuttons = '';
|
|
for (var i = 0; i < moves.length; i++) {
|
|
var moveData = moves[i];
|
|
var move = Tools.getMove(moves[i].move);
|
|
if (!move) {
|
|
move = {
|
|
name: moves[i].move,
|
|
id: moves[i].move,
|
|
type: ''
|
|
};
|
|
}
|
|
var name = move.name;
|
|
var pp = moveData.pp + '/' + moveData.maxpp;
|
|
if (!moveData.maxpp) pp = '–';
|
|
if (move.id === 'Struggle' || move.id === 'Recharge') pp = '–';
|
|
if (move.id === 'Recharge') move.type = '–';
|
|
if (name.substr(0, 12) === 'Hidden Power') name = 'Hidden Power';
|
|
if (moveData.disabled) {
|
|
movebuttons += '<button disabled="disabled"' + this.tooltipAttrs(moveData.move, 'move') + '>';
|
|
hasDisabled = true;
|
|
} else {
|
|
movebuttons += '<button class="type-' + move.type + '" name="chooseMove" value="' + Tools.escapeHTML(moveData.move) + '"' + this.tooltipAttrs(moveData.move, 'move') + '>';
|
|
hasMoves = true;
|
|
}
|
|
movebuttons += name + '<br /><small class="type">' + move.type + '</small> <small class="pp">' + pp + '</small> </button> ';
|
|
}
|
|
if (!hasMoves) {
|
|
controls += '<button class="movebutton" name="chooseMove" value="Struggle">Struggle<br /><small class="type">Normal</small> <small class="pp">–</small> </button> ';
|
|
} else {
|
|
controls += movebuttons;
|
|
}
|
|
controls += '<div style="clear:left"></div>';
|
|
if (hasDisabled) {
|
|
// controls += '<small>(grayed out moves have been disabled by Disable, Encore, or something like that)</small>';
|
|
}
|
|
controls += '</div></div><div class="switchcontrols"><div class="switchselect"><button name="selectSwitch">Switch</button></div><div class="switchmenu">';
|
|
if (trapped) {
|
|
controls += '<em>You are trapped and cannot switch!</em>';
|
|
} else {
|
|
controls += '';
|
|
if (this.finalDecision) {
|
|
controls += '<em>You <strong>might</strong> be trapped, so you won\'t be able to cancel a switch!</em><br/>';
|
|
}
|
|
for (var i = 0; i < switchables.length; i++) {
|
|
var pokemon = switchables[i];
|
|
pokemon.name = pokemon.ident.substr(4);
|
|
if (pokemon.zerohp || i < this.battle.mySide.active.length || this.choice.switchFlags[i]) {
|
|
controls += '<button disabled' + this.tooltipAttrs(i, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + (!pokemon.zerohp?'<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 {
|
|
controls += '<button name="chooseSwitch" value="' + i + '"' + this.tooltipAttrs(i, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.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></div></div>';
|
|
this.$controls.html(controls);
|
|
}
|
|
this.notifying = true;
|
|
break;
|
|
|
|
case 'switch':
|
|
this.finalDecision = false;
|
|
if (!this.choice) {
|
|
this.choice = {
|
|
choices: [],
|
|
switchFlags: {}
|
|
};
|
|
if (this.request.forceSwitch !== true) {
|
|
while (!this.request.forceSwitch[this.choice.choices.length] && this.choice.choices.length < 6) this.choice.choices.push('pass');
|
|
}
|
|
}
|
|
var pos = this.choice.choices.length;
|
|
var controls = '<div class="controls"><div class="whatdo">';
|
|
if (type === 'switch2') {
|
|
controls += '<button name="clearChoice">Back</button> ';
|
|
}
|
|
controls += 'Switch <strong>'+Tools.escapeHTML(switchables[pos].name)+'</strong> to:</div>';
|
|
controls += '<div class="switchcontrols"><div class="switchselect"><button name="selectSwitch">Switch</button></div><div class="switchmenu">';
|
|
for (var i = 0; i < switchables.length; i++) {
|
|
var pokemon = switchables[i];
|
|
if (pokemon.zerohp || i < this.battle.mySide.active.length || this.choice.switchFlags[i]) {
|
|
controls += '<button disabled' + this.tooltipAttrs(i, 'sidepokemon') + '>';
|
|
} else {
|
|
controls += '<button name="chooseSwitch" value="' + i + '"' + this.tooltipAttrs(i, 'sidepokemon') + '>';
|
|
}
|
|
controls += '<span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + (!pokemon.zerohp?'<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></div></div>';
|
|
this.$controls.html(controls);
|
|
this.selectSwitch();
|
|
this.notifying = true;
|
|
break;
|
|
|
|
case 'team':
|
|
var controls = '<div class="controls"><div class="whatdo">';
|
|
if (!this.choice || !this.choice.done) {
|
|
this.choice = {
|
|
teamPreview: [1,2,3,4,5,6].slice(0,switchables.length),
|
|
done: 0,
|
|
count: 0
|
|
}
|
|
if (this.battle.gameType === 'doubles') {
|
|
this.choice.count = 2;
|
|
}
|
|
controls += 'How will you start the battle?</div>';
|
|
controls += '<div class="switchcontrols"><div class="switchselect"><button name="selectSwitch">Choose Lead</button></div><div class="switchmenu">';
|
|
for (var i = 0; i < switchables.length; i++) {
|
|
var pokemon = switchables[i];
|
|
if (i >= 6) {
|
|
break;
|
|
}
|
|
if (toId(pokemon.baseAbility) === 'illusion') {
|
|
this.choice.count = 6;
|
|
}
|
|
controls += '<button name="chooseTeamPreview" value="'+i+'"' + this.tooltipAttrs(i, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + '</button> ';
|
|
}
|
|
if (this.battle.teamPreviewCount) this.choice.count = parseInt(this.battle.teamPreviewCount,10);
|
|
controls += '</div>';
|
|
} else {
|
|
controls += '<button name="clearChoice">Back</button> What about the rest of your team?</div>';
|
|
controls += '<div class="switchcontrols"><div class="switchselect"><button name="selectSwitch">Choose a pokemon for slot '+(this.choice.done+1)+'</button></div><div class="switchmenu">';
|
|
for (var i = 0; i < switchables.length; i++) {
|
|
var pokemon = switchables[this.choice.teamPreview[i]-1];
|
|
if (i >= 6) {
|
|
break;
|
|
}
|
|
if (i < this.choice.done) {
|
|
controls += '<button disabled="disabled"' + this.tooltipAttrs(i, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + '</button> ';
|
|
} else {
|
|
controls += '<button name="chooseTeamPreview" value="'+i+'"' + this.tooltipAttrs(this.choice.teamPreview[i]-1, 'sidepokemon') + '><span class="pokemonicon" style="display:inline-block;vertical-align:middle;'+Tools.getIcon(pokemon)+'"></span>' + Tools.escapeHTML(pokemon.name) + '</button> ';
|
|
}
|
|
}
|
|
controls += '</div>';
|
|
}
|
|
controls += '</div></div>';
|
|
this.$controls.html(controls);
|
|
this.selectSwitch();
|
|
this.notifying = true;
|
|
break;
|
|
|
|
default:
|
|
var buf = '<div class="controls"><em>Waiting for opponent...</em> ';
|
|
if (this.choice && this.choice.waiting && !this.finalDecision) {
|
|
buf += '<button name="undoChoice">Cancel</button>';
|
|
}
|
|
buf += '</div>';
|
|
if (this.battle.kickingInactive) {
|
|
buf += ' <br /><button name="setTimer" value="off"><small>Stop timer</small></button>';
|
|
} else {
|
|
buf += ' <br /><button name="setTimer" value="on"><small>Kick inactive player</small></button>';
|
|
}
|
|
this.$controls.html(buf);
|
|
break;
|
|
}
|
|
},
|
|
// Same as send, but appends the rqid to the message so that the server
|
|
// can verify that the decision is sent in response to the correct request.
|
|
sendDecision: function(message) {
|
|
this.send(message + '|' + this.request.rqid);
|
|
},
|
|
request: null,
|
|
receiveRequest: function(request) {
|
|
request.requestType = 'move';
|
|
var notifyObject = null;
|
|
if (request.forceSwitch) {
|
|
request.requestType = 'switch';
|
|
notifyObject = {
|
|
type: 'yourSwitch',
|
|
room: this.id
|
|
};
|
|
} else if (request.teamPreview) {
|
|
request.requestType = 'team';
|
|
notifyObject = {
|
|
type: 'yourSwitch',
|
|
room: this.id
|
|
};
|
|
} else if (request.wait) {
|
|
request.requestType = 'wait';
|
|
} else {
|
|
notifyObject = {
|
|
type: 'yourMove',
|
|
room: this.id
|
|
};
|
|
}
|
|
|
|
this.choice = null;
|
|
this.request = request;
|
|
if (request.side) {
|
|
this.updateSideLocation(request.side, true);
|
|
}
|
|
|
|
// if (notifyObject) {
|
|
// var doNotify = function() {
|
|
// notify(notifyObject);
|
|
// this.notifying = true;
|
|
// updateRoomList();
|
|
// };
|
|
// if (this.battle.yourSide.initialized) {
|
|
// // The opponent's name is already known.
|
|
// notifyObject.user = this.battle.yourSide.name;
|
|
// doNotify();
|
|
// } else {
|
|
// // The opponent's name isn't known yet, so wait until it is
|
|
// // known before sending the notification, so that it can include
|
|
// // the opponent's name.
|
|
// var callback = this.battle.stagnateCallback;
|
|
// this.battle.stagnateCallback = function(battle) {
|
|
// notifyObject.user = battle.yourSide.name;
|
|
// doNotify();
|
|
// battle.stagnateCallback = callback;
|
|
// if (callback) callback(battle);
|
|
// };
|
|
// }
|
|
// }
|
|
},
|
|
updateSideLocation: function(sideData, midBattle) {
|
|
if (!sideData.id) return;
|
|
this.side = sideData.id;
|
|
if (this.battle.sidesSwitched !== !!(this.side === 'p2')) {
|
|
sidesSwitched = true;
|
|
this.battle.reset();
|
|
this.battle.switchSides();
|
|
if (midBattle) {
|
|
this.battle.fastForwardTo(-1);
|
|
} else {
|
|
this.battle.play();
|
|
}
|
|
}
|
|
},
|
|
updateSide: function(sideData) {
|
|
for (var i = 0; i < sideData.pokemon.length; i++) {
|
|
var pokemonData = sideData.pokemon[i];
|
|
var pokemon;
|
|
if (i == 0) {
|
|
pokemon = this.battle.getPokemon(''+pokemonData.ident, pokemonData.details);
|
|
pokemon.slot = 0;
|
|
pokemon.side.pokemon = [pokemon];
|
|
// if (pokemon.side.active[0] && pokemon.side.active[0].ident == pokemon.ident) pokemon.side.active[0] = pokemon;
|
|
} else if (i < this.battle.mySide.active.length) {
|
|
pokemon = this.battle.getPokemon('new: '+pokemonData.ident, pokemonData.details);
|
|
pokemon.slot = i;
|
|
// if (pokemon.side.active[i] && pokemon.side.active[i].ident == pokemon.ident) pokemon.side.active[i] = pokemon;
|
|
if (pokemon.side.active[i] && pokemon.side.active[i].ident == pokemon.ident) {
|
|
pokemon.side.active[i].item = pokemon.item;
|
|
pokemon.side.active[i].ability = pokemon.ability;
|
|
pokemon.side.active[i].baseAbility = pokemon.baseAbility;
|
|
}
|
|
} else {
|
|
pokemon = this.battle.getPokemon('new: '+pokemonData.ident, pokemonData.details);
|
|
}
|
|
pokemon.healthParse(pokemonData.condition);
|
|
if (pokemonData.baseAbility) {
|
|
pokemon.baseAbility = pokemonData.baseAbility;
|
|
if (!pokemon.ability) pokemon.ability = pokemon.baseAbility;
|
|
}
|
|
pokemon.item = pokemonData.item;
|
|
pokemon.moves = pokemonData.moves;
|
|
}
|
|
this.battle.mySide.updateSidebar();
|
|
},
|
|
|
|
// buttons
|
|
joinBattle: function() {
|
|
this.send('/joinbattle');
|
|
},
|
|
setTimer: function(setting) {
|
|
this.send('/timer '+setting);
|
|
},
|
|
forfeit: function() {
|
|
this.send('/forfeit');
|
|
},
|
|
saveReplay: function() {
|
|
this.send('/savereplay');
|
|
},
|
|
instantReplay: function() {
|
|
this.hideTooltip();
|
|
this.request = null;
|
|
this.battle.reset();
|
|
this.battle.play();
|
|
},
|
|
skipTurn: function() {
|
|
this.battle.skipTurn();
|
|
},
|
|
|
|
// choice buttons
|
|
chooseMove: function(move) {
|
|
var myActive = this.battle.mySide.active;
|
|
var target = Tools.getMove(move).target;
|
|
this.hideTooltip();
|
|
if (move !== undefined) {
|
|
var choosableTargets = {normal:1, any:1, adjacentAlly:1, adjacentAllyOrSelf:1, adjacentFoe:1};
|
|
this.choice.choices.push('move '+move);
|
|
if (myActive.length > 1 && target in choosableTargets) {
|
|
this.choice.type = 'movetarget';
|
|
this.choice.moveTarget = target;
|
|
this.updateControlsForPlayer();
|
|
return false;
|
|
}
|
|
}
|
|
while (myActive.length > this.choice.choices.length && !myActive[this.choice.choices.length]) {
|
|
this.choice.choices.push('pass');
|
|
}
|
|
if (myActive.length > this.choice.choices.length) {
|
|
this.choice.type = 'move2';
|
|
this.updateControlsForPlayer();
|
|
return false;
|
|
}
|
|
|
|
this.sendDecision('/choose '+this.choice.choices.join(','));
|
|
this.notifying = false;
|
|
|
|
this.finalDecision = false;
|
|
this.choice = {waiting: true};
|
|
this.updateControlsForPlayer();
|
|
},
|
|
chooseMoveTarget: function(posString) {
|
|
this.choice.choices[this.choice.choices.length-1] += ' '+posString;
|
|
this.chooseMove();
|
|
},
|
|
chooseSwitch: function(pos) {
|
|
this.hideTooltip();
|
|
this.choice.choices.push('switch '+(parseInt(pos,10)+1));
|
|
this.choice.switchFlags[pos] = true;
|
|
if (this.request && this.request.requestType === 'move' && this.battle.mySide.active.length > this.choice.choices.length) {
|
|
this.choice.type = 'move2';
|
|
this.updateControlsForPlayer();
|
|
return false;
|
|
}
|
|
if (this.request && this.request.requestType === 'switch') {
|
|
if (this.request.forceSwitch !== true) {
|
|
while (this.battle.mySide.active.length > this.choice.choices.length && !this.request.forceSwitch[this.choice.choices.length]) this.choice.choices.push('pass');
|
|
}
|
|
if (this.battle.mySide.active.length > this.choice.choices.length) {
|
|
this.choice.type = 'switch2';
|
|
this.updateControlsForPlayer();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
this.sendDecision('/choose '+this.choice.choices.join(','));
|
|
this.notifying = false;
|
|
|
|
this.choice = {waiting: true};
|
|
this.updateControlsForPlayer();
|
|
},
|
|
chooseTeamPreview: function(pos) {
|
|
pos = parseInt(pos,10);
|
|
this.hideTooltip();
|
|
if (this.choice.count) {
|
|
var temp = this.choice.teamPreview[pos];
|
|
this.choice.teamPreview[pos] = this.choice.teamPreview[this.choice.done];
|
|
this.choice.teamPreview[this.choice.done] = temp;
|
|
|
|
this.choice.done++;
|
|
|
|
if (this.choice.done < Math.min(this.choice.teamPreview.length, this.choice.count)) {
|
|
this.choice.type = 'team2';
|
|
this.updateControlsForPlayer();
|
|
return false;
|
|
}
|
|
pos = this.choice.teamPreview.join('');
|
|
} else {
|
|
pos = pos+1;
|
|
}
|
|
|
|
this.sendDecision('/team '+(pos));
|
|
this.notifying = false;
|
|
|
|
this.choice = {waiting: true};
|
|
this.updateControlsForPlayer();
|
|
},
|
|
undoChoice: function(pos) {
|
|
this.send('/undo');
|
|
this.notifying = true;
|
|
|
|
this.choice = null;
|
|
this.updateControlsForPlayer();
|
|
},
|
|
leaveBattle: function() {
|
|
this.hideTooltip();
|
|
this.send('/leavebattle');
|
|
this.notifying = false;
|
|
},
|
|
selectSwitch: function() {
|
|
this.hideTooltip();
|
|
this.$controls.find('.controls').attr('class', 'controls switch-controls');
|
|
},
|
|
selectMove: function() {
|
|
this.hideTooltip();
|
|
this.$controls.find('.controls').attr('class', 'controls move-controls');
|
|
},
|
|
|
|
// tooltips
|
|
tooltipAttrs: function(thing, type, ownHeight, isActive) {
|
|
return ' onmouseover="room.showTooltip(\'' + Tools.escapeHTML(''+thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ', ' + (isActive ? 'true' : 'false') + ')" onmouseout="room.hideTooltip()" onmouseup="room.hideTooltip()"';
|
|
},
|
|
showTooltip: function(thing, type, elem, ownHeight, isActive) {
|
|
var offset = {
|
|
left: 150,
|
|
top: 500
|
|
};
|
|
if (elem) offset = $(elem).offset();
|
|
var x = offset.left - 2;
|
|
if (elem) {
|
|
if (ownHeight) offset = $(elem).offset();
|
|
else offset = $(elem).parent().offset();
|
|
}
|
|
var y = offset.top - 5;
|
|
|
|
if (x > 335) x = 335;
|
|
if (y < 140) y = 140;
|
|
$('#tooltipwrapper').css({
|
|
left: x,
|
|
top: y
|
|
});
|
|
|
|
var text = '';
|
|
switch (type) {
|
|
case 'move':
|
|
var move = Tools.getMove(thing);
|
|
if (!move) return;
|
|
var basePower = move.basePower;
|
|
if (!basePower) basePower = '—';
|
|
var accuracy = move.accuracy;
|
|
if (!accuracy || accuracy === true) accuracy = '—';
|
|
else accuracy = '' + accuracy + '%';
|
|
text = '<div class="tooltipinner"><div class="tooltip">';
|
|
text += '<h2>' + move.name + '<br />'+Tools.getTypeIcon(move.type)+' <img src="' + Tools.resourcePrefix + 'sprites/categories/' + move.category + '.png" alt="' + move.category + '" /></h2>';
|
|
text += '<p>Base power: ' + basePower + '</p>';
|
|
text += '<p>Accuracy: ' + accuracy + '</p>';
|
|
if (move.desc) {
|
|
text += '<p class="section">' + move.desc + '</p>';
|
|
}
|
|
text += '</div></div>';
|
|
break;
|
|
case 'pokemon':
|
|
var pokemon = this.battle.getPokemon(thing);
|
|
if (!pokemon) return;
|
|
//fallthrough
|
|
case 'sidepokemon':
|
|
if (!pokemon) pokemon = this.battle.mySide.pokemon[parseInt(thing)];
|
|
text = '<div class="tooltipinner"><div class="tooltip">';
|
|
text += '<h2>' + pokemon.getFullName() + (pokemon.level !== 100 ? ' <small>L' + pokemon.level + '</small>' : '') + '<br />';
|
|
|
|
var types = pokemon.types;
|
|
var template = pokemon;
|
|
if (pokemon.volatiles.transform && pokemon.volatiles.formechange) {
|
|
template = Tools.getTemplate(pokemon.volatiles.formechange[2]);
|
|
types = template.types;
|
|
text += '<small>(Transformed into '+pokemon.volatiles.formechange[2]+')</small><br />';
|
|
} else if (pokemon.volatiles.formechange) {
|
|
template = Tools.getTemplate(pokemon.volatiles.formechange[2]);
|
|
types = template.types;
|
|
text += '<small>(Forme: '+pokemon.volatiles.formechange[2]+')</small><br />';
|
|
}
|
|
if (pokemon.volatiles.typechange) {
|
|
text += '<small>(Type changed)</small><br />';
|
|
types = [pokemon.volatiles.typechange[2]];
|
|
}
|
|
text += Tools.getTypeIcon(types[0]);
|
|
if (types[1]) {
|
|
text += ' '+Tools.getTypeIcon(types[1]);
|
|
}
|
|
text += '</h2>';
|
|
var exacthp = '';
|
|
if (pokemon.maxhp != 100 && pokemon.maxhp != 1000 && pokemon.maxhp != 48) exacthp = ' ('+pokemon.hp+'/'+pokemon.maxhp+')';
|
|
if (pokemon.maxhp == 48 && isActive) exacthp = ' <small>('+pokemon.hp+'/'+pokemon.maxhp+' pixels)</small>';
|
|
text += '<p>HP: ' + pokemon.hpDisplay() +exacthp+(pokemon.status?' <span class="status '+pokemon.status+'">'+pokemon.status.toUpperCase()+'</span>':'')+'</p>';
|
|
if (!pokemon.baseAbility && !pokemon.ability) {
|
|
text += '<p>Possible abilities: ' + Tools.getAbility(template.abilities['0']).name;
|
|
if (template.abilities['1']) text += ', ' + Tools.getAbility(template.abilities['1']).name;
|
|
if (template.abilities['DW']) text += ', ' + Tools.getAbility(template.abilities['DW']).name;
|
|
text += '</p>';
|
|
} else if (pokemon.ability) {
|
|
text += '<p>Ability: ' + Tools.getAbility(pokemon.ability).name + '</p>';
|
|
} else if (pokemon.baseAbility) {
|
|
text += '<p>Ability: ' + Tools.getAbility(pokemon.baseAbility).name + '</p>';
|
|
}
|
|
if (pokemon.item) {
|
|
text += '<p>Item: ' + Tools.getItem(pokemon.item).name + '</p>';
|
|
}
|
|
if (pokemon.moves && pokemon.moves.length && (!isActive || isActive === 'foe')) {
|
|
text += '<p class="section">';
|
|
for (var i = 0; i < pokemon.moves.length; i++) {
|
|
var name = Tools.getMove(pokemon.moves[i]).name;
|
|
text += '⋅ ' + name + '<br />';
|
|
}
|
|
text += '</p>';
|
|
}
|
|
text += '</div></div>';
|
|
break;
|
|
}
|
|
if (!$('#tooltipwrapper').length) $(document.body).append('<div id="tooltipwrapper"></div>');
|
|
$('#tooltipwrapper').html(text).appendTo(document.body);
|
|
},
|
|
hideTooltip: function() {
|
|
$('#tooltipwrapper').html('');
|
|
}
|
|
});
|
|
|
|
}).call(this, jQuery);
|