');
this.battleElem = elem.find('.battle');
this.controlsElem = elem.find('.replay-controls');
this.chatFrameElem = elem.find('.battle-log');
this.chatElem = null;
this.chatAddElem = elem.find('.battle-log-add');
this.chatboxElem = null;
this.joinElem = null;
this.foeHintElem = elem.find('.foehint');
this.battleEnded = false;
this.dealloc = function () {
if (selfR.battle) selfR.battle.dealloc();
};
this.focus = function () {
selfR.updateMe();
if (selfR.chatElem) selfR.chatFrameElem.scrollTop(selfR.chatElem.height());
if (selfR.chatboxElem) selfR.chatboxElem.focus();
};
this.updateJoinButton = function () {
if (selfR.battle.done) selfR.battleEnded = true;
if (selfR.battleEnded) {
selfR.controlsElem.html('
');
if (selfR.me.side) {
selfR.controlsElem.html('
');
}
if (selfR.joinElem) {
selfR.joinElem.remove();
}
//selfR.battleElem.append('');
//selfR.joinElem = selfR.battleElem.children().last();
} else if (selfR.battle.mySide.initialized && selfR.battle.yourSide.initialized) {
if (selfR.joinElem) {
selfR.joinElem.remove();
}
selfR.joinElem = null;
} else if (selfR.me.side) {
if (selfR.joinElem) {
selfR.joinElem.remove();
}
selfR.joinElem = null;
if (selfR.battle.kickingInactive) {
selfR.controlsElem.html('
← Your opponent has disconnected. Click this to delay your victory.
');
} else {
selfR.controlsElem.html('
← Your opponent has disconnected. Click this if they don\'t reconnect.
');
}
} else {
if (selfR.joinElem) {
selfR.joinElem.remove();
}
selfR.battleElem.append('');
selfR.joinElem = selfR.battleElem.children().last();
}
};
this.init = function (data) {
this.version = (data.version !== undefined) ? data.version : 0;
if (selfR.battle.activityQueue) {
// re-initialize
selfR.battleEnded = false;
selfR.battle = new Battle(selfR.battleElem, selfR.chatFrameElem);
if (widthClass !== 'tiny-layout') {
selfR.battle.messageSpeed = 80;
}
selfR.battle.setMute(me.mute);
selfR.battle.customCallback = selfR.callback;
selfR.battle.startCallback = selfR.updateJoinButton;
selfR.battle.stagnateCallback = selfR.updateJoinButton;
selfR.battle.endCallback = selfR.updateJoinButton;
selfR.chatFrameElem.find('.inner').html('');
selfR.controlsElem.html('');
}
selfR.battle.play();
if (data.battlelog) {
for (var i = 0; i < data.battlelog.length; i++) {
selfR.battle.add(data.battlelog[i]);
}
selfR.battle.fastForwardTo(-1);
}
selfR.updateMe();
if (selfR.chatElem) {
selfR.chatFrameElem.scrollTop(selfR.chatElem.height());
}
};
this.rawMessage = function(message) {
this.message({rawMessage: message});
};
this.message = function (message, andLobby) {
if (message.pm) {
var pmuserid = (toUserid(message.name) === me.userid ? toUserid(message.pm) : toUserid(message.name))
if (me.ignore[toUserid(message.name)] && message.name.substr(0, 1) === ' ') return;
selfR.add('|chatmsg-raw|' + '
' + sanitize(message.name.substr(1)) + ':(Private to ' + sanitize(message.pm) + ') ' + messageSanitize(message.message) + '');
} else if (message.rawMessage) {
selfR.add('|chatmsg-raw|' + message.rawMessage);
} else if (message.evalRawMessage) {
selfR.add('|chatmsg-raw|' + eval(message.evalRawMessage));
} else if (message.name) {
selfR.add('|chat|' + message.name.substr(1) + '|' + message.message);
} else if (message.message) {
selfR.add('|chatmsg|' + message.message);
} else {
selfR.add('|chatmsg|' + message);
}
if (andLobby && rooms.lobby) {
rooms.lobby.message(message);
}
};
this.send = function (message) {
emit(socket, 'chat', {room:this.id,message:message});
};
// 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.
this.sendDecision = function (message) {
this.send(message + '|' + this.me.request.rqid);
};
this.add = function (log) {
if (typeof log === 'string') log = log.split('\n');
selfR.update({updates:log});
};
this.update = function (update) {
if (update.updates) {
var updated = false;
for (var i = 0; i < update.updates.length; i++) {
if (!updated && (update.updates[i] === '')) {
selfR.me.callbackWaiting = false;
updated = true;
selfR.controlsElem.html('');
}
if (update.updates[i] === 'RESET') {
selfR.foeHintElem.html('');
var blog = selfR.chatFrameElem.find('.inner').html();
delete selfR.me.side;
selfR.battleEnded = false;
selfR.battle = new Battle(selfR.battleElem, selfR.chatFrameElem);
if (widthClass !== 'tiny-layout') {
selfR.battle.messageSpeed = 80;
}
selfR.battle.setMute(me.mute);
selfR.battle.customCallback = selfR.callback;
selfR.battle.startCallback = selfR.updateJoinButton;
selfR.battle.stagnateCallback = selfR.updateJoinButton;
selfR.battle.endCallback = selfR.updateJoinButton;
selfR.chatFrameElem.find('.inner').html(blog + '
NEW GAME
');
selfR.chatFrameElem.scrollTop(selfR.chatFrameElem.find('.inner').height());
selfR.controlsElem.html('');
selfR.battle.play();
selfR.updateJoinButton();
break;
}
if (update.updates[i].substr(0, 6) === '|chat|' || update.updates[i].substr(0, 9) === '|chatmsg|' || update.updates[i].substr(0, 10) === '|inactive|') {
selfR.battle.instantAdd(update.updates[i]);
} else {
if (update.updates[i].substr(0,10) === '|callback|') selfR.controlsElem.html('');
if (update.updates[i].substr(0,12) === '| callback | ') selfR.controlsElem.html('');
selfR.battle.add(update.updates[i]);
}
}
}
if (update.request) {
selfR.me.request = update.request;
if (selfR.version === 1) {
// We maintain this for now, in case a server updated during
// the brief period where this was the design. However, this
// can probably be removed in a few weeks, if desired.
selfR.send('/ackrequest ' + selfR.me.request.rqid);
}
selfR.me.request.requestType = 'move';
if (selfR.me.request.forceSwitch) {
selfR.me.request.requestType = 'switch';
notify({
type: 'yourSwitch',
room: selfR.id,
user: selfR.battle.yourSide.name
});
selfR.notifying = true;
updateRoomList();
} else if (selfR.me.request.teamPreview) {
selfR.me.request.requestType = 'team';
notify({
type: 'yourSwitch',
room: selfR.id,
user: selfR.battle.yourSide.name
});
selfR.notifying = true;
updateRoomList();
} else if (selfR.me.request.wait) {
selfR.me.request.requestType = 'wait';
} else {
notify({
type: 'yourMove',
room: selfR.id,
user: selfR.battle.yourSide.name
});
selfR.notifying = true;
updateRoomList();
}
//if (selfR.me.callbackWaiting) selfR.callback();
}
if (typeof update.active !== 'undefined') {
if (!update.active && selfR.me.side) {
selfR.controlsElem.html('');
}
}
if (update.side) {
if (update.side === 'none') {
$('#controls').html('');
delete selfR.me.side;
} else {
selfR.me.side = update.side;
}
}
if (update.sideData) {
selfR.updateSide(update.sideData, update.midBattle);
}
selfR.updateMe();
};
this.updateSide = function(sideData, midBattle) {
var sidesSwitched = false;
selfR.me.sideData = sideData; // just for easy debugging
if (selfR.battle.sidesSwitched !== !!(selfR.me.side === 'p2')) {
sidesSwitched = true;
selfR.battle.reset();
selfR.battle.switchSides();
}
for (var i = 0; i < sideData.pokemon.length; i++) {
var pokemonData = sideData.pokemon[i];
var pokemon;
if (i == 0) {
pokemon = selfR.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 < selfR.battle.mySide.active.length) {
pokemon = selfR.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 = selfR.battle.getPokemon('new: '+pokemonData.ident, pokemonData.details);
}
pokemon.healthParse(pokemonData.condition);
if (pokemonData.baseAbility) {
pokemon.baseAbility = pokemonData.baseAbility;
if (!pokemon.ability || pokemon.ability.substr(0,2) === '??') pokemon.ability = pokemon.baseAbility;
}
pokemon.item = pokemonData.item;
pokemon.moves = pokemonData.moves;
}
selfR.battle.mySide.updateSidebar();
if (sidesSwitched) {
if (midBattle) {
selfR.battle.fastForwardTo(-1);
} else {
selfR.battle.play();
}
}
};
this.updateMe = function () {
if (selfR.meIdent.name !== me.name || selfR.meIdent.named !== me.named) {
if (me.named) {
selfR.chatAddElem.html('');
selfR.chatboxElem = selfR.chatAddElem.find('textarea');
// The keypress event does not capture tab, so use keydown.
selfR.chatboxElem.keydown(rooms['lobby'].formKeyDown);
selfR.chatboxElem.autoResize({
animateDuration: 100,
extraSpace: 0
});
selfR.chatboxElem.focus();
} else {
selfR.chatAddElem.html('');
}
selfR.meIdent.name = me.name;
selfR.meIdent.named = me.named;
}
var inner = selfR.chatFrameElem.find('.inner');
if (inner.length) selfR.chatElem = inner;
else selfR.chatElem = null;
selfR.updateJoinButton();
};
this.callback = function (battle, type, moveTarget) {
if (!battle) battle = selfR.battle;
selfR.notifying = false;
if (type === 'restart') {
selfR.me.callbackWaiting = false;
selfR.battleEnded = true;
updateRoomList();
return;
}
var myActive = selfR.battle.mySide.active;
var yourActive = selfR.battle.yourSide.active;
var text = '';
if (yourActive[1]) {
text += '';
}
if (yourActive[0]) {
text += '';
}
if (myActive[0]) {
text += '';
}
if (myActive[1]) {
text += '';
}
selfR.foeHintElem.html(text);
if (!selfR.me.request) {
selfR.controlsElem.html('
Waiting for players...
');
selfR.updateJoinButton();
updateRoomList();
return;
}
if (selfR.me.request.side) {
selfR.updateSide(selfR.me.request.side, true);
}
selfR.me.callbackWaiting = true;
var active = selfR.battle.mySide.active[0];
if (!active) active = {};
if (selfR.battle.kickingInactive) {
selfR.controlsElem.html('
Waiting for opponent...
');
} else {
selfR.controlsElem.html('
Waiting for opponent...
');
}
var act = '';
var switchables = [];
if (selfR.me.request) {
act = selfR.me.request.requestType;
if (selfR.me.request.side) {
switchables = selfR.battle.mySide.pokemon;
}
}
switch (act) {
case 'move':
{
if (type !== 'move2' && type !== 'movetarget') {
selfR.choices = [];
selfR.choiceSwitchFlags = {};
while (switchables[selfR.choices.length] && switchables[selfR.choices.length].fainted) selfR.choices.push('pass');
}
var pos = selfR.choices.length - (type === 'movetarget'?1:0);
var hpbar = '';
{
if (switchables[pos].hp * 5 / switchables[pos].maxhp < 1) {
hpbar = '';
} else if (switchables[pos].hp * 2 / switchables[pos].maxhp < 1) {
hpbar = '';
} else {
hpbar = '';
}
hpbar += ''+switchables[pos].hp+'/'+switchables[pos].maxhp+'';
}
var active = selfR.me.request;
if (active.active) active = active.active[pos];
var moves = active.moves;
var trapped = active.trapped;
var controls = '
';
if (type === 'move2' || type === 'movetarget') {
controls += ' ';
}
// Target selector
if (type === 'movetarget') {
controls += 'At who? '+hpbar+'
';
controls += '
';
var myActive = selfR.battle.mySide.active;
var yourActive = selfR.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 += ' ';
} else if (disabled || pokemon.zerohp) {
controls += ' ';
} else {
controls += ' ';
}
}
controls += '
';
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 += ' ';
} else if (disabled || pokemon.zerohp) {
controls += ' ';
} else {
controls += ' ';
}
}
controls += '
';
self.rawMessage(buffer);
} catch(e) {
}
}, 'json');
return false;
case 'buttonban':
var reason = prompt('Why do you wish to ban this user?');
if (reason === null) return false;
if (reason === false) reason = '';
rooms.lobby.send('/ban ' + target + ', ' + reason);
return false;
case 'buttonmute':
var reason = prompt('Why do you wish to mute this user?');
if (reason === null) return false;
if (reason === false) reason = '';
rooms.lobby.send('/mute ' + target + ', ' + reason);
return false;
case 'buttonunmute':
rooms.lobby.send('/unmute ' + target);
return false;
case 'buttonkick':
var reason = prompt('Why do you wish to kick this user?');
if (reason === null) return false;
if (reason === false) reason = '';
rooms.lobby.send('/kick ' + target + ', ' + reason);
return false;
}
return text;
};
this.popupOpen = function (userid) {
userid = toUserid(userid);
for (var i = 0; i < me.popups.length; i++) {
if (me.popups[i] === userid) return selfR.popupFocus(i);
}
me.popups.push(userid);
selfR.updatePopup();
selfR.popupChatboxElem.focus();
};
this.popupFocus = function (i) {
if (!me.popups.length) return;
if (i == me.popups.length - 1) return;
me.popups = me.popups.concat(me.popups.splice(i, 1));
selfR.updatePopup();
selfR.popupChatboxElem.focus();
};
this.updatePopup = function (data) {
if (selfR.popupState && !me.popups.length) {
selfR.popupElem.html('');
selfR.popupElem.hide();
selfR.popupState = '';
} else if (selfR.popupState !== 'pm-' + me.popups.join(',')) {
var code = '
';
var popupListCode = '';
var name;
for (var i = 0; i < me.popups.length - 1; i++) {
name = sanitize(me.users[me.popups[i]] || me.popups[i]);
popupListCode += '
';
text += '' + selfR.getFormatSelect('search') + ' ';
text += '' + selfR.getTeamSelect(selfR.selectedFormat) + ' ';
if (selfR.goDisabled)
{
text += ' Look for a battle
';
}
else
{
text += ' Look for a battle';
}
text += '' + searcherText + ' ';
text += '
' + roomListCode + '
';
}
selfR.mainTopElem.html(text);
if (!challenge) {
me.lastChallengeNotification = '';
}
};
this.debounceUpdateTimeout = null;
this.debounceUpdateQueued = false;
this.debounceUpdate = function() {
if (!selfR.debounceUpdateTimeout) {
selfR.updateMainElem();
selfR.debounceUpdateQueued = false;
selfR.debounceUpdateTimeout = setTimeout(selfR.debounceUpdateEnd, 600);
} else {
selfR.debounceUpdateQueued = true;
}
};
this.debounceUpdateEnd = function() {
if (selfR.debounceUpdateQueued) {
selfR.updateMainElem();
}
selfR.debounceUpdateTimeout = null;
};
this.updateMainElem = function (force) {
selfR.updateMainTop(force);
var text = '';
text += '
';
var Ranks = {
'~': 2,
'&': 2,
'@': 1,
'%': 1,
'+': 1,
' ': 0
};
var RankOrder = {
'~': 1,
'&': 2,
'@': 3,
'%': 4,
'+': 5,
' ': 6
};
var users = [];
if (selfR.userList) users = Object.keys(selfR.userList).sort(function(a,b){
var aRank = RankOrder[selfR.userList[a].substr(0,1)];
var bRank = RankOrder[selfR.userList[b].substr(0,1)];
if (aRank != bRank) return aRank - bRank;
return (a>b?1:-1);
});
for (var i=0, len=users.length; i';
if (me.named) {
text += '';
} else {
text += '';
}
text += '' + sanitize(group) + '';
if (group === '~' || group === '&') {
text += '' + sanitize(selfR.userList[userid].substr(1)) + '';
} else if (group === '%' || group === '@') {
text += '' + sanitize(selfR.userList[userid].substr(1)) + '';
} else {
text += '' + sanitize(selfR.userList[userid].substr(1)) + '';
}
text += '';
text += '';
}
if (!users.length) {
text += '
No named users online
';
}
if (selfR.userCount.unregistered) {
text += '
';
text += 'Due to lag, ' + selfR.userCount.unregistered + ' unregistered users are hidden.';
text += ' Challenge an unregistered user';
text += '';
text += '
';
var shownRooms = {
lobby: true,
teambuilder: true,
ladder: true
};
var yourRooms = false;
for (var id in rooms) {
if (shownRooms[id]) continue;
shownRooms[id] = true;
if (!yourRooms) code += '
';
break;
case 'pokemon':
var pokemon = curRoom.battle.getPokemon(thing);
if (!pokemon) return;
//fallthrough
case 'sidepokemon':
if (!pokemon) pokemon = curRoom.battle.mySide.pokemon[parseInt(thing)];
text = '
';
text += '
' + pokemon.getFullName() + (pokemon.level !== 100 ? ' L' + pokemon.level + '' : '') + ' ';
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 += '(Transformed into '+pokemon.volatiles.formechange[2]+') ';
} else if (pokemon.volatiles.formechange) {
template = Tools.getTemplate(pokemon.volatiles.formechange[2]);
types = template.types;
text += '(Forme: '+pokemon.volatiles.formechange[2]+') ';
}
if (pokemon.volatiles.typechange) {
text += '(Type changed) ';
types = [pokemon.volatiles.typechange[2]];
}
text += Tools.getTypeIcon(types[0]);
if (types[1]) {
text += ' '+Tools.getTypeIcon(types[1]);
}
text += '
';
}
if (!pokemon.baseAbility && (!pokemon.ability || pokemon.ability.substr(0, 2) === '??')) {
text += '
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 += '
';
focusElem = '#overlay_ok';
break;
case 'init':
if (data) return;
contents = '
Pokemon Showdown is BETA and unfinished. If you are looking for something that isn\'t frequently down for maintenance and bug fixes, please check back in several weeks.
';
contents += '
There is a link to report bugs in the top right. If you find a bug, please report it.
';
contents += '
I understandI don\'t understand
';
focusElem = '#overlay_ok';
break;
case 'register':
if (!data) data = {};
if (me.registered && me.registered.userid === me.userid) return;
if (data.ifuserid !== me.userid) return;
if (data.error) {
contents += '
' + data.error + '
';
} else if (data.reason) {
contents += '
' + data.reason + '
';
} else {
contents += '
Register an account:
';
}
contents += '
' + (data.name || me.name) + '
';
contents += '
';
contents += '
';
contents += '
';
contents += '
';
contents += '
RegisterCancel
';
selectElem = '#overlay_password';
break;
case 'login':
if (!data) data = {};
if (data.error) {
contents += '
' + data.error + '
';
} else if (data.reason) {
contents += '
' + data.reason + '
';
} else {
contents += '
The name you chose is registered.
';
}
contents += '
' + data.name + '
';
contents += '
';
contents += '
Log inCancel
';
selectElem = '#overlay_password';
break;
case 'betalogin':
if (!data) data = {};
contents += '