1(function ($) { 2 3 var BattleRoom = this.BattleRoom = ConsoleRoom.extend({ 4 type: 'battle', 5 title: '', 6 minWidth: 320, 7 minMainWidth: 956, 8 maxWidth: 1180, 9 initialize: function (data) { 10 this.choice = undefined; 11 /** are move/switch/team-preview controls currently being shown? */ 12 this.controlsShown = false; 13 14 this.battlePaused = false; 15 this.autoTimerActivated = false; 16 17 this.isSideRoom = Dex.prefs('rightpanelbattles'); 18 19 this.$el.addClass('ps-room-opaque').html('<div class="battle">Battle is here</div><div class="foehint"></div><div class="battle-log" aria-label="Battle Log" role="complementary"></div><div class="battle-log-add">Connecting...</div><ul class="battle-userlist userlist userlist-minimized"></ul><div class="battle-controls" role="complementary" aria-label="Battle Controls"></div><button class="battle-chat-toggle button" name="showChat"><i class="fa fa-caret-left"></i> Chat</button>'); 20 21 this.$battle = this.$el.find('.battle'); 22 this.$controls = this.$el.find('.battle-controls'); 23 this.$chatFrame = this.$el.find('.battle-log'); 24 this.$chatAdd = this.$el.find('.battle-log-add'); 25 this.$foeHint = this.$el.find('.foehint'); 26 27 BattleSound.setMute(Dex.prefs('mute')); 28 this.battle = new Battle({ 29 id: this.id, 30 $frame: this.$battle, 31 $logFrame: this.$chatFrame 32 }); 33 this.battle.roomid = this.id; 34 this.battle.joinButtons = true; 35 this.tooltips = this.battle.scene.tooltips; 36 this.tooltips.listen(this.$controls); 37 38 var self = this; 39 this.battle.subscribe(function () { self.updateControls(); }); 40 41 this.users = {}; 42 this.userCount = {users: 0}; 43 this.$userList = this.$('.userlist'); 44 this.userList = new UserList({ 45 el: this.$userList, 46 room: this 47 }); 48 this.userList.construct(); 49 50 this.$chat = this.$chatFrame.find('.inner'); 51 52 this.$options = this.battle.scene.$options.html('<div style="padding-top: 3px; padding-right: 3px; text-align: right"><button class="icon button" name="openBattleOptions" title="Options">Battle Options</button></div>'); 53 }, 54 events: { 55 'click .replayDownloadButton': 'clickReplayDownloadButton', 56 'change input[name=zmove]': 'updateZMove', 57 'change input[name=dynamax]': 'updateMaxMove' 58 }, 59 battleEnded: false, 60 join: function () { 61 app.send('/join ' + this.id); 62 }, 63 showChat: function () { 64 this.$('.battle-chat-toggle').attr('name', 'hideChat').html('Battle <i class="fa fa-caret-right"></i>'); 65 this.$el.addClass('showing-chat'); 66 }, 67 hideChat: function () { 68 this.$('.battle-chat-toggle').attr('name', 'showChat').html('<i class="fa fa-caret-left"></i> Chat'); 69 this.$el.removeClass('showing-chat'); 70 }, 71 leave: function () { 72 if (!this.expired) app.send('/noreply /leave ' + this.id); 73 if (this.battle) this.battle.destroy(); 74 }, 75 requestLeave: function (e) { 76 if (this.side && this.battle && !this.battleEnded && !this.expired && !this.battle.forfeitPending) { 77 app.addPopup(ForfeitPopup, {room: this, sourceEl: e && e.currentTarget, gameType: 'battle'}); 78 return false; 79 } 80 return true; 81 }, 82 updateLayout: function () { 83 var width = this.$el.width(); 84 if (width < 950 || this.battle.hardcoreMode) { 85 this.battle.messageShownTime = 500; 86 } else { 87 this.battle.messageShownTime = 1; 88 } 89 if (width && width < 640) { 90 var scale = (width / 640); 91 this.$battle.css('transform', 'scale(' + scale + ')'); 92 this.$foeHint.css('transform', 'scale(' + scale + ')'); 93 this.$controls.css('top', 360 * scale + 10); 94 } else { 95 this.$battle.css('transform', 'none'); 96 this.$foeHint.css('transform', 'none'); 97 this.$controls.css('top', 370); 98 } 99 this.$el.toggleClass('small-layout', width < 830); 100 this.$el.toggleClass('tiny-layout', width < 640); 101 if (this.$chat) this.$chatFrame.scrollTop(this.$chat.height()); 102 }, 103 show: function () { 104 Room.prototype.show.apply(this, arguments); 105 this.updateLayout(); 106 }, 107 receive: function (data) { 108 this.add(data); 109 }, 110 focus: function (e) { 111 this.tooltips.hideTooltip(); 112 if (this.battle.paused && !this.battlePaused) { 113 if (Dex.prefs('noanim')) this.battle.seekTurn(Infinity); 114 this.battle.play(); 115 }
116 ConsoleRoom.prototype.focus.call(this, e); 117 }, 118 blur: function () { 119 this.battle.pause(); 120 }, 121 init: function (data) { 122 var log = data.split('\n'); 123 if (data.substr(0, 6) === '|init|') log.shift(); 124 if (log.length && log[0].substr(0, 7) === '|title|') { 125 this.title = log[0].substr(7); 126 log.shift(); 127 app.roomTitleChanged(this); 128 } 129 if (this.battle.stepQueue.length) return; 130 this.battle.stepQueue = log; 131 this.battle.seekTurn(Infinity, true); 132 if (this.battle.ended) this.battleEnded = true; 133 this.updateLayout(); 134 this.updateControls(); 135 }, 136 add: function (data) { 137 if (!data) return; 138 if (data.substr(0, 6) === '|init|') { 139 return this.init(data); 140 } 141 if (data.substr(0, 9) === '|request|') { 142 data = data.slice(9); 143 144 var requestData = null; 145 var choiceText = null; 146 147 var nlIndex = data.indexOf('\n'); 148 if (/[0-9]/.test(data.charAt(0)) && data.charAt(1) === '|') { 149 // message format: 150 // |request|CHOICEINDEX|CHOICEDATA 151 // REQUEST 152 153 // This is backwards compatibility with old code that violates the 154 // expectation that server messages can be streamed line-by-line. 155 // Please do NOT EVER push protocol changes without a pull request. 156 // https://github.com/Zarel/Pokemon-Showdown/commit/e3c6cbe4b91740f3edc8c31a1158b506f5786d72#commitcomment-21278523 157 choiceText = '?'; 158 data = data.slice(2, nlIndex); 159 } else if (nlIndex >= 0) { 160 // message format: 161 // |request|REQUEST 162 // |sentchoice|CHOICE 163 if (data.slice(nlIndex + 1, nlIndex + 13) === '|sentchoice|') { 164 choiceText = data.slice(nlIndex + 13); 165 } 166 data = data.slice(0, nlIndex); 167 } 168 169 try { 170 requestData = JSON.parse(data); 171 } catch (err) {} 172 return this.receiveRequest(requestData, choiceText); 173 } 174 175 var log = data.split('\n'); 176 for (var i = 0; i < log.length; i++) { 177 var logLine = log[i]; 178 179 if (logLine === '|') { 180 this.callbackWaiting = false; 181 this.controlsShown = false; 182 this.$controls.html(''); 183 } 184 185 if (logLine.substr(0, 10) === '|callback|') { 186 // TODO: Maybe a more sophisticated UI for this. 187 // In singles, this isn't really necessary because some elements of the UI will be 188 // immediately disabled. However, in doubles/triples it might not be obvious why 189 // the player is being asked to make a new decision without the following messages. 190 var args = logLine.substr(10).split('|'); 191 var pokemon = isNaN(Number(args[1])) ? this.battle.getPokemon(args[1]) : this.battle.nearSide.active[args[1]]; 192 var requestData = this.request.active[pokemon ? pokemon.slot : 0]; 193 this.choice = undefined; 194 switch (args[0]) { 195 case 'trapped': 196 requestData.trapped = true; 197 var pokeName = pokemon.side.n === 0 ? BattleLog.escapeHTML(pokemon.name) : "The opposing " + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.speciesForme : BattleLog.escapeHTML(pokemon.name)); 198 this.battle.stepQueue.push('|message|' + pokeName + ' is trapped and cannot switch!'); 199 break; 200 case 'cant': 201 for (var i = 0; i < requestData.moves.length; i++) { 202 if (requestData.moves[i].id === args[3]) { 203 requestData.moves[i].disabled = true; 204 } 205 } 206 args.splice(1, 1, pokemon.getIdent()); 207 this.battle.stepQueue.push('|' + args.join('|')); 208 break; 209 } 210 } else if (logLine.substr(0, 7) === '|title|') { // eslint-disable-line no-empty 211 } else if (logLine.substr(0, 5) === '|win|' || logLine === '|tie') { 212 this.battleEnded = true; 213 this.battle.stepQueue.push(logLine); 214 } else if (logLine.substr(0, 6) === '|chat|' || logLine.substr(0, 3) === '|c|' || logLine.substr(0, 4) === '|c:|' || logLine.substr(0, 9) === '|chatmsg|' || logLine.substr(0, 10) === '|inactive|') { 215 this.battle.instantAdd(logLine); 216 } else { 217 this.battle.stepQueue.push(logLine); 218 } 219 } 220 this.battle.add(); 221 if (Dex.prefs('noanim')) this.battle.seekTurn(Infinity); 222 this.updateControls(); 223 }, 224 toggleMessages: function (user) { 225 var $messages = $('.chatmessage-' + user + '.revealed'); 226 var $button = $messages.find('button'); 227 if (!$messages.is(':hidden')) { 228 $messages.hide(); 229 $button.html('<small>(' + ($messages.length) + ' line' + ($messages.length > 1 ? 's' : '') + 'from ' + user + ')</small>'); 230 $button.parent().show(); 231 } else { 232 $button.html('<small>(Hide ' + ($messages.length) + ' line' + ($messages.length > 1 ? 's' : '') + ' from ' + user + ')</small>'); 233 $button.parent().removeClass('revealed'); 234 $messages.show(); 235 } 236 }, 237 setHardcoreMode: function (mode) { 238 this.battle.setHardcoreMode(mode); 239 var id = '#' + this.el.id + ' '; 240 this.$('.hcmode-style').remove(); 241 this.updateLayout(); // set animation delay 242 if (mode) this.$el.prepend('<style class="hcmode-style">' + id + '.battle .turn,' + id + '.battle-history{display:none !important;}</style>'); 243 if (this.choice && this.choice.waiting) { 244 this.updateControlsForPlayer(); 245 } 246 }, 247 248 /********************************************************* 249 * Battle stuff 250 *********************************************************/ 251 252 updateControls: function () { 253 if (this.battle.scene.customControls) return; 254 var controlsShown = this.controlsShown; 255 var switchSidesButton = '<p><button class="button" name="switchSides"><i class="fa fa-random"></i> Switch sides</button></p>'; 256 this.controlsShown = false;
257 258 if (this.battle.seeking !== null) { 259 260 // battle is seeking 261 this.$controls.html(''); 262 return; 263 264 } else if (!this.battle.atQueueEnd) { 265 266 // battle is playing or paused 267 if (!this.side || this.battleEnded) { 268 // spectator 269 if (this.battle.paused) { 270 // paused 271 this.$controls.html('<p><button class="button" name="resume"><i class="fa fa-play"></i><br />Play</button> <button class="button" name="rewindTurn"><i class="fa fa-step-backward"></i><br />Last turn</button><button class="button" name="skipTurn"><i class="fa fa-step-forward"></i><br />Skip turn</button> <button class="button" name="instantReplay"><i class="fa fa-undo"></i><br />First turn</button><button class="button" name="goToEnd"><i class="fa fa-fast-forward"></i><br />Skip to end</button></p>' + switchSidesButton); 272 } else { 273 // playing 274 this.$controls.html('<p><button class="button" name="pause"><i class="fa fa-pause"></i><br />Pause</button> <button class="button" name="rewindTurn"><i class="fa fa-step-backward"></i><br />Last turn</button><button class="button" name="skipTurn"><i class="fa fa-step-forward"></i><br />Skip turn</button> <button class="button" name="instantReplay"><i class="fa fa-undo"></i><br />First turn</button><button class="button" name="goToEnd"><i class="fa fa-fast-forward"></i><br />Skip to end</button></p>' + switchSidesButton); 275 } 276 } else { 277 // is a player 278 this.$controls.html('<p>' + this.getTimerHTML() + '<button class="button" name="skipTurn"><i class="fa fa-step-forward"></i><br />Skip turn</button> <button class="button" name="goToEnd"><i class="fa fa-fast-forward"></i><br />Skip to end</button></p>'); 279 } 280 return; 281 282 } 283 284 if (this.battle.ended) { 285 286 var replayDownloadButton = '<span style="float:right;"><a href="//' + Config.routes.replays + '/" class="button replayDownloadButton"><i class="fa fa-download"></i> Download replay</a><br /><br /><button class="button" name="saveReplay"><i class="fa fa-upload"></i> Upload and share replay</button></span>'; 287 288 // battle has ended 289 if (this.side) { 290 // was a player 291 this.closeNotification('choice'); 292 this.$controls.html('<div class="controls"><p>' + replayDownloadButton + '<button class="button" name="instantReplay"><i class="fa fa-undo"></i><br />Instant replay</button></p><p><button class="button" name="closeAndMainMenu"><strong>Main menu</strong><br /><small>(closes this battle)</small></button> <button class="button" name="closeAndRematch"><strong>Rematch</strong><br /><small>(closes this battle)</small></button></p></div>'); 293 } else { 294 this.$controls.html('<div class="controls"><p>' + replayDownloadButton + '<button class="button" name="instantReplay"><i class="fa fa-undo"></i><br />Instant replay</button></p>' + switchSidesButton + '</div>'); 295 } 296 297 } else if (this.side) { 298 299 // player 300 this.controlsShown = true; 301 if (!controlsShown || this.choice === undefined || this.choice && this.choice.waiting) { 302 // don't update controls (and, therefore, side) if `this.choice === null`: causes damage miscalculations 303 this.updateControlsForPlayer(); 304 } else { 305 this.updateTimer(); 306 } 307 308 } else if (!this.battle.nearSide.name || !this.battle.farSide.name) { 309 310 // empty battle 311 this.$controls.html('<p><em>Waiting for players...</em></p>'); 312 313 } else { 314 315 // full battle 316 if (this.battle.paused) { 317 // paused 318 this.$controls.html('<p><button class="button" name="resume"><i class="fa fa-play"></i><br />Play</button> <button class="button" name="rewindTurn"><i class="fa fa-step-backward"></i><br />Last turn</button><button class="button disabled" disabled><i class="fa fa-step-forward"></i><br />Skip turn</button> <button class="button" name="instantReplay"><i class="fa fa-undo"></i><br />First turn</button><button class="button disabled" disabled><i class="fa fa-fast-forward"></i><br />Skip to end</button></p>' + switchSidesButton + '<p><em>Waiting for players...</em></p>'); 319 } else { 320 // playing 321 this.$controls.html('<p><button class="button" name="pause"><i class="fa fa-pause"></i><br />Pause</button> <button class="button" name="rewindTurn"><i class="fa fa-step-backward"></i><br />Last turn</button><button class="button disabled" disabled><i class="fa fa-step-forward"></i><br />Skip turn</button> <button class="button" name="instantReplay"><i class="fa fa-undo"></i><br />First turn</button><button class="button disabled" disabled><i class="fa fa-fast-forward"></i><br />Skip to end</button></p>' + switchSidesButton + '<p><em>Waiting for players...</em></p>'); 322 } 323 324 } 325 326 // This intentionally doesn't happen if the battle is still playing, 327 // since those early-return.
328 app.topbar.updateTabbar(); 329 }, 330 updateControlsForPlayer: function () { 331 this.callbackWaiting = true; 332 333 var act = ''; 334 var switchables = []; 335 if (this.request) { 336 // TODO: investigate when to do this 337 this.updateSide(); 338 if (this.request.ally) { 339 this.addAlly(this.request.ally); 340 } 341 342 act = this.request.requestType; 343 if (this.request.side) { 344 switchables = this.battle.myPokemon; 345 } 346 if (!this.finalDecision) this.finalDecision = !!this.request.noCancel; 347 } 348 349 if (this.choice && this.choice.waiting) { 350 act = ''; 351 } 352 353 var type = this.choice ? this.choice.type : ''; 354 355 // The choice object: 356 // !this.choice = nothing has been chosen 357 // this.choice.choices = array of choice strings 358 // this.choice.switchFlags = dict of pokemon indexes that have a switch pending 359 // this.choice.switchOutFlags = ??? 360 // this.choice.freedomDegrees = in a switch request: number of empty slots that can't be replaced 361 // this.choice.type = determines what the current choice screen to be displayed is 362 // this.choice.waiting = true if the choice has been sent and we're just waiting for the next turn 363 364 switch (act) { 365 case 'move': 366 if (!this.choice) { 367 this.choice = { 368 choices: [], 369 switchFlags: {}, 370 switchOutFlags: {} 371 }; 372 } 373 this.updateMoveControls(type); 374 break; 375 376 case 'switch': 377 if (!this.choice) { 378 this.choice = { 379 choices: [], 380 switchFlags: {}, 381 switchOutFlags: {}, 382 freedomDegrees: 0, 383 canSwitch: 0 384 }; 385 386 if (this.request.forceSwitch !== true) { 387 var faintedLength = _.filter(this.request.forceSwitch, function (fainted) {return fainted;}).length; 388 var freedomDegrees = faintedLength - _.filter(switchables.slice(this.battle.pokemonControlled), function (mon) {return !mon.fainted;}).length; 389 this.choice.freedomDegrees = Math.max(freedomDegrees, 0); 390 this.choice.canSwitch = faintedLength - this.choice.freedomDegrees; 391 } 392 } 393 this.updateSwitchControls(type); 394 break; 395 396 case 'team': 397 if (this.battle.mySide.pokemon && !this.battle.mySide.pokemon.length) { 398 // too early, we can't determine `this.choice.count` yet 399 // TODO: send teamPreviewCount in the request object 400 this.controlsShown = false; 401 return; 402 } 403 if (!this.choice) { 404 this.choice = { 405 choices: null, 406 teamPreview: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24].slice(0, switchables.length), 407 done: 0, 408 count: 1 409 }; 410 if (this.battle.gameType === 'multi') { 411 this.choice.count = 1; 412 } 413 if (this.battle.gameType === 'doubles') { 414 this.choice.count = 2; 415 } 416 if (this.battle.gameType === 'triples' || this.battle.gameType === 'rotation') { 417 this.choice.count = 3; 418 } 419 // Request full team order if one of our Pokémon has Illusion 420 for (var i = 0; i < switchables.length && i < 6; i++) { 421 if (toID(switchables[i].baseAbility) === 'illusion') { 422 this.choice.count = this.battle.myPokemon.length; 423 } 424 } 425 if (this.battle.teamPreviewCount) { 426 var requestCount = parseInt(this.battle.teamPreviewCount, 10); 427 if (requestCount > 0 && requestCount <= switchables.length) { 428 this.choice.count = requestCount; 429 } 430 } 431 this.choice.choices = new Array(this.choice.count); 432 } 433 this.updateTeamControls(type); 434 break; 435 436 default: 437 this.updateWaitControls(); 438 break; 439 } 440 }, 441 timerInterval: 0, 442 getTimerHTML: function (nextTick) { 443 var time = 'Timer'; 444 var timerTicking = (this.battle.kickingInactive && this.request && !this.request.wait && !(this.choice && this.choice.waiting)) ? ' timerbutton-on' : ''; 445 446 if (!nextTick) { 447 var self = this; 448 if (this.timerInterval) { 449 clearInterval(this.timerInterval); 450 this.timerInterval = 0; 451 } 452 if (timerTicking) this.timerInterval = setInterval(function () { 453 var $timerButton = self.$('.timerbutton'); 454 if ($timerButton.length) { 455 $timerButton.replaceWith(self.getTimerHTML(true)); 456 } else { 457 clearInterval(self.timerInterval); 458 self.timerInterval = 0; 459 } 460 }, 1000); 461 } else if (this.battle.kickingInactive > 1) { 462 this.battle.kickingInactive--; 463 if (this.battle.graceTimeLeft) this.battle.graceTimeLeft--; 464 else if (this.battle.totalTimeLeft) this.battle.totalTimeLeft--; 465 } 466 467 if (this.battle.kickingInactive) { 468 var secondsLeft = this.battle.kickingInactive; 469 if (secondsLeft !== true) { 470 if (secondsLeft <= 10 && timerTicking) { 471 timerTicking = ' timerbutton-critical'; 472 } 473 var minutesLeft = Math.floor(secondsLeft / 60); 474 secondsLeft -= minutesLeft * 60; 475 time = '' + minutesLeft + ':' + (secondsLeft < 10 ? '0' : '') + secondsLeft; 476 477 secondsLeft = this.battle.totalTimeLeft; 478 if (secondsLeft) { 479 minutesLeft = Math.floor(secondsLeft / 60); 480 secondsLeft -= minutesLeft * 60; 481 time += ' | ' + minutesLeft + ':' + (secondsLeft < 10 ? '0' : '') + secondsLeft + ' total'; 482 } 483 } else { 484 time = '-:--'; 485 } 486 } 487 return '<button name="openTimer" class="button timerbutton' + timerTicking + '"><i class="fa fa-hourglass-start"></i> ' + time + '</button>'; 488 }, 489 updateMaxMove: function () { 490 var dynaChecked = this.$('input[name=dynamax]')[0].checked; 491 if (dynaChecked) { 492 this.$('.movebuttons-nomax').hide(); 493 this.$('.movebuttons-max').show(); 494 } else { 495 this.$('.movebuttons-nomax').show(); 496 this.$('.movebuttons-max').hide(); 497 } 498 }, 499 updateZMove: function () { 500 var zChecked = this.$('input[name=zmove]')[0].checked; 501 if (zChecked) { 502 this.$('.movebuttons-noz').hide(); 503 this.$('.movebuttons-z').show(); 504 } else { 505 this.$('.movebuttons-noz').show(); 506 this.$('.movebuttons-z').hide(); 507 } 508 }, 509 updateTimer: function () { 510 this.$('.timerbutton').replaceWith(this.getTimerHTML()); 511 }, 512 openTimer: function () { 513 app.addPopup(TimerPopup, {room: this}); 514 }, 515 updateMoveControls: function (type) { 516 var switchables = this.request && this.request.side ? this.battle.myPokemon : []; 517 518 if (type !== 'movetarget') { 519 while (switchables[this.choice.choices.length] && switchables[this.choice.choices.length].fainted && this.choice.choices.length + 1 < this.battle.nearSide.active.length) { 520 this.choice.choices.push('pass'); 521 } 522 } 523 524 var moveTarget = this.choice ? this.choice.moveTarget : ''; 525 var pos = this.choice.choices.length; 526 if (type === 'movetarget') pos--; 527 528 var hpRatio = switchables[pos].hp / switchables[pos].maxhp; 529 530 var curActive = this.request && this.request.active && this.request.active[pos]; 531 if (!curActive) return; 532 var trapped = curActive.trapped; 533 var canMegaEvo = curActive.canMegaEvo || switchables[pos].canMegaEvo;
534 var canZMove = curActive.canZMove || switchables[pos].canZMove; 535 var canUltraBurst = curActive.canUltraBurst || switchables[pos].canUltraBurst; 536 var canDynamax = curActive.canDynamax || switchables[pos].canDynamax; 537 var maxMoves = curActive.maxMoves || switchables[pos].maxMoves; 538 var gigantamax = curActive.gigantamax; 539 if (canZMove && typeof canZMove[0] === 'string') { 540 canZMove = _.map(canZMove, function (move) { 541 return {move: move, target: Dex.moves.get(move).target}; 542 }); 543 } 544 if (gigantamax) gigantamax = Dex.moves.get(gigantamax); 545 546 this.finalDecisionMove = curActive.maybeDisabled || false; 547 this.finalDecisionSwitch = curActive.maybeTrapped || false; 548 for (var i = pos + 1; i < this.battle.nearSide.active.length; ++i) { 549 var p = this.battle.nearSide.active[i]; 550 if (p && !p.fainted) { 551 this.finalDecisionMove = this.finalDecisionSwitch = false; 552 break; 553 } 554 } 555 556 var requestTitle = ''; 557 if (type === 'move2' || type === 'movetarget') { 558 requestTitle += '<button name="clearChoice">Back</button> '; 559 } 560 561 // Target selector 562 if (type === 'movetarget') { 563 requestTitle += 'At who? '; 564 565 var activePos = this.battle.mySide.n > 1 ? pos + this.battle.pokemonControlled : pos; 566 567 var targetMenus = ['', '']; 568 var nearActive = this.battle.nearSide.active; 569 var farActive = this.battle.farSide.active; 570 var farSlot = farActive.length - 1 - activePos; 571 572 if ((moveTarget === 'adjacentAlly' || moveTarget === 'adjacentFoe') && this.battle.gameType === 'freeforall') { 573 moveTarget = 'normal'; 574 } 575 576 for (var i = farActive.length - 1; i >= 0; i--) { 577 var pokemon = farActive[i]; 578 var tooltipArgs = 'activepokemon|1|' + i; 579 580 var disabled = false; 581 if (moveTarget === 'adjacentAlly' || moveTarget === 'adjacentAllyOrSelf') { 582 disabled = true; 583 } else if (moveTarget === 'normal' || moveTarget === 'adjacentFoe') { 584 if (Math.abs(farSlot - i) > 1) disabled = true; 585 } 586 587 if (disabled) { 588 targetMenus[0] += '<button disabled="disabled"></button> '; 589 } else if (!pokemon || pokemon.fainted) { 590 targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> '; 591 } else { 592 targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.speciesForme : BattleLog.escapeHTML(pokemon.name)) + '<span class="' + 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 } 594 } 595 for (var i = 0; i < nearActive.length; i++) { 596 var pokemon = nearActive[i]; 597 var tooltipArgs = 'activepokemon|0|' + i; 598 599 var disabled = false; 600 if (moveTarget === 'adjacentFoe') { 601 disabled = true; 602 } else if (moveTarget === 'normal' || moveTarget === 'adjacentAlly' || moveTarget === 'adjacentAllyOrSelf') { 603 if (Math.abs(activePos - i) > 1) disabled = true; 604 } 605 if (moveTarget !== 'adjacentAllyOrSelf' && activePos == i) disabled = true; 606 607 if (disabled) { 608 targetMenus[1] += '<button disabled="disabled" style="visibility:hidden"></button> '; 609 } else if (!pokemon || pokemon.fainted) { 610 targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"><span class="picon" style="' + Dex.getPokemonIcon('missingno') + '"></span></button> '; 611 } else { 612 targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="' + 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> '; 613 } 614 } 615 616 this.$controls.html( 617 '<div class="controls">' + 618 '<div class="whatdo">' + requestTitle + this.getTimerHTML() + '</div>' + 619 '<div class="switchmenu" style="display:block">' + targetMenus[0] + '<div style="clear:both"></div> </div>' + 620 '<div class="switchmenu" style="display:block">' + targetMenus[1] + '</div>' + 621 '</div>' 622 ); 623 } else { 624 // Move chooser 625 var hpBar = '<small class="' + (hpRatio < 0.2 ? 'critical' : hpRatio < 0.5 ? 'weak' : 'healthy') + '">HP ' + switchables[pos].hp + '/' + switchables[pos].maxhp + '</small>'; 626 requestTitle += ' What will <strong>' + BattleLog.escapeHTML(switchables[pos].name) + '</strong> do? ' + hpBar; 627 628 var hasMoves = false;
629 var moveMenu = ''; 630 var movebuttons = ''; 631 var activePos = this.battle.mySide.n > 1 ? pos + this.battle.pokemonControlled : pos; 632 var typeValueTracker = new ModifiableValue(this.battle, this.battle.nearSide.active[activePos], this.battle.myPokemon[pos]); 633 var currentlyDynamaxed = (!canDynamax && maxMoves); 634 for (var i = 0; i < curActive.moves.length; i++) { 635 var moveData = curActive.moves[i]; 636 var move = this.battle.dex.moves.get(moveData.move); 637 var name = move.name; 638 var pp = moveData.pp + '/' + moveData.maxpp; 639 if (!moveData.maxpp) pp = '–'; 640 if (move.id === 'Struggle' || move.id === 'Recharge') pp = '–'; 641 if (move.id === 'Recharge') move.type = '–'; 642 if (name.substr(0, 12) === 'Hidden Power') name = 'Hidden Power'; 643 var moveType = this.tooltips.getMoveType(move, typeValueTracker)[0]; 644 var tooltipArgs = 'move|' + moveData.move + '|' + pos; 645 if (moveData.disabled) { 646 movebuttons += '<button disabled="disabled" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '">'; 647 } else { 648 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="' + BattleLog.escapeHTML(tooltipArgs) + '">'; 649 hasMoves = true; 650 } 651 movebuttons += name + '<br /><small class="type">' + (moveType ? Dex.types.get(moveType).name : "Unknown") + '</small> <small class="pp">' + pp + '</small> </button> '; 652 } 653 if (!hasMoves) { 654 moveMenu += '<button class="movebutton" name="chooseMove" value="
6540" data-move="Struggle" data-target="randomNormal">Struggle<br /><small class="type">Normal</small> <small class="pp">–</small> </button> '; 655 } else { 656 if (canZMove || canDynamax || currentlyDynamaxed) { 657 var classType = canZMove ? 'z' : 'max'; 658 if (currentlyDynamaxed) { 659 movebuttons = ''; 660 } else { 661 movebuttons = '<div class="movebuttons-no' + classType + '">' + movebuttons + '</div><div class="movebuttons-' + classType + '" style="display:none">'; 662 } 663 var specialMoves = canZMove ? canZMove : maxMoves.maxMoves; 664 for (var i = 0; i < curActive.moves.length; i++) { 665 if (specialMoves[i]) { 666 // when possible, use Z move to decide type, for cases like Z-Hidden Power 667 var baseMove = this.battle.dex.moves.get(curActive.moves[i].move); 668 // might not exist, such as for Z status moves - fall back on base move to determine type then 669 var specialMove = gigantamax || this.battle.dex.moves.get(specialMoves[i].move); 670 var moveType = this.tooltips.getMoveType(specialMove.exists && !specialMove.isMax ? specialMove : baseMove, typeValueTracker, specialMove.isMax ? gigantamax || switchables[pos].gigantamax || true : undefined)[0]; 671 if (specialMove.isMax && specialMove.name !== 'Max Guard' && !specialMove.id.startsWith('gmax')) { 672 specialMove = this.tooltips.getMaxMoveFromType(moveType); 673 } 674 var tooltipArgs = classType + 'move|' + baseMove.id + '|' + pos; 675 if (specialMove.id.startsWith('gmax')) tooltipArgs += '|' + specialMove.id; 676 var isDisabled = specialMoves[i].disabled ? 'disabled="disabled"' : ''; 677 movebuttons += '<button ' + isDisabled + ' class="type-' + moveType + ' has-tooltip" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(specialMoves[i].move) + '" data-target="' + BattleLog.escapeHTML(specialMoves[i].target) + '" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '">'; 678 var pp = curActive.moves[i].pp + '/' + curActive.moves[i].maxpp; 679 if (canZMove) { 680 pp = '1/1'; 681 } else if (!curActive.moves[i].maxpp) { 682 pp = '–'; 683 } 684 movebuttons += specialMove.name + '<br /><small class="type">' + (moveType ? Dex.types.get(moveType).name : "Unknown") + '</small> <small class="pp">' + pp + '</small> </button> '; 685 } else { 686 movebuttons += '<button disabled="disabled"> </button>'; 687 } 688 } 689 if (!currentlyDynamaxed) movebuttons += '</div>'; 690 } 691 moveMenu += movebuttons; 692 } 693 if (canMegaEvo) { 694 moveMenu += '<br /><label class="megaevo"><input type="checkbox" name="megaevo" /> Mega Evolution</label>'; 695 } else if (canZMove) { 696 moveMenu += '<br /><label class="megaevo"><input type="checkbox" name="zmove" /> Z-Power</label>'; 697 } else if (canUltraBurst) { 698 moveMenu += '<br /><label class="megaevo"><input type="checkbox" name="ultraburst" /> Ultra Burst</label>'; 699 } else if (canDynamax) { 700 moveMenu += '<br /><label class="megaevo"><input type="checkbox" name="dynamax" /> Dynamax</label>'; 701 } 702 if (this.finalDecisionMove) { 703 moveMenu += '<em style="display:block;clear:both">You <strong>might</strong> have some moves disabled, so you won\'t be able to cancel an attack!</em><br/>'; 704 } 705 moveMenu += '<div style="clear:left"></div>'; 706 707 var moveControls = ( 708 '<div class="movecontrols">' + 709 '<div class="moveselect">
709<button name="selectMove">Attack</button></div>' + 710 '<div class="movemenu">' + moveMenu + '</div>' + 711 '</div>' 712 ); 713 714 var shiftControls = ''; 715 if (this.battle.gameType === 'triples' && pos !== 1) { 716 shiftControls += '<div class="shiftselect"><button name="chooseShift">Shift</button></div>'; 717 } 718 719 var switchMenu = ''; 720 if (trapped) { 721 switchMenu += '<em>You are trapped and cannot switch!</em><br />'; 722 switchMenu += this.displayParty(switchables, trapped); 723 } else { 724 switchMenu += this.displayParty(switchables, trapped); 725 if (this.finalDecisionSwitch && this.battle.gen > 2) { 726 switchMenu += '<em style="display:block;clear:both">You <strong>might</strong> be trapped, so you won\'t be able to cancel a switch!</em><br/>'; 727 } 728 } 729 var switchControls = ( 730 '<div class="switchcontrols">' + 731 '<div class="switchselect"><button name="selectSwitch">Switch</button></div>' + 732 '<div class="switchmenu">' + switchMenu + '</div>' + 733 '</div>' 734 ); 735 this.$controls.html( 736 '<div class="controls">' + 737 '<div class="whatdo">' + requestTitle + this.getTimerHTML() + '</div>' + 738 moveControls + shiftControls + switchControls + 739 '</div>' 740 ); 741 } 742 }, 743 displayParty: function (switchables, trapped) { 744 var party = ''; 745 for (var i = 0; i < switchables.length; i++) { 746 var pokemon = switchables[i]; 747 pokemon.name = pokemon.ident.substr(4); 748 var tooltipArgs = 'switchpokemon|' + i; 749 if (pokemon.fainted || i < this.battle.pokemonControlled || this.choice.switchFlags[i] || trapped) { 750 party += '<button class="disabled has-tooltip" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : trapped ? ',trapped' : i < this.battle.nearSide.active.length ? ',active' : '') + '" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (pokemon.hp ? '<span class="' + 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> '; 751 } else { 752 party += '<button name="chooseSwitch" value="' + i + '" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="' + 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> '; 753 } 754 } 755 if (this.battle.mySide.ally) party += this.displayAllyParty(); 756 return party; 757 }, 758 displayAllyParty: function () { 759 var party = ''; 760 if (!this.battle.myAllyPokemon) return ''; 761 var allyParty = this.battle.myAllyPokemon; 762 for (var i = 0; i < allyParty.length; i++) { 763 var pokemon = allyParty[i]; 764 pokemon.name = pokemon.ident.substr(4); 765 var tooltipArgs = 'allypokemon|' + i; 766 party += '<button class="disabled has-tooltip" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + ',notMine' + '" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (pokemon.hp ? '<span class="' + 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> '; 767 } 768 return party; 769 }, 770 updateSwitchControls: function (type) { 771 var pos = this.choice.choices.length; 772 773 if (type !== 'switchposition' && this.request.forceSwitch !== true && !this.choice.freedomDegrees) { 774 while (!this.request.forceSwitch[pos] && pos < 6) { 775 pos = this.choice.choices.push('pass'); 776 } 777 } 778 779 var switchables = this.request && this.request.side ? this.battle.myPokemon : []; 780 var nearActive = this.battle.nearSide.active; 781 782 var requestTitle = ''; 783 if (type === 'switch2' || type === 'switchposition') { 784 requestTitle += '<button name="clearChoice">Back</button> '; 785 } 786 787 // Place selector 788 if (type === 'switchposition') { 789 // TODO? hpbar 790 requestTitle += "Which Pokémon will it switch in for?"; 791 var controls = '<div class="switchmenu" style="display:block">'; 792 for (var i = 0; i < this.battle.pokemonControlled; i++) { 793 var pokemon = this.battle.myPokemon[i]; 794 var tooltipArgs = 'switchpokemon|' + i; 795 if (pokemon && !pokemon.fainted || this.choice.switchOutFlags[i]) { 796 controls += '<button disabled class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="' + 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> '; 797 } else if (!pokemon) { 798 controls += '<button disabled></button> '; 799 } else { 800 controls += '<button name="chooseSwitchTarget" value="' + i + '" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="' + 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> '; 801 } 802 } 803 controls += '</div>'; 804 this.$controls.html( 805 '<div class="controls">' + 806 '<div class="whatdo">' + requestTitle + this.getTimerHTML() + '</div>' + 807 controls + 808 '</div>' 809 ); 810 } else { 811 if (this.choice.freedomDegrees >= 1) { 812 requestTitle += "Choose a Pokémon to send to battle!"; 813 } else { 814 requestTitle += "Switch <strong>" + BattleLog.escapeHTML(switchables[pos].name) + "</strong> to:"; 815 } 816 817 var switchMenu = ''; 818 for (var i = 0; i < switchables.length; i++) { 819 var pokemon = switchables[i]; 820 var tooltipArgs = 'switchpokemon|' + i; 821 if (pokemon.fainted || i < this.battle.pokemonControlled || this.choice.switchFlags[i]) { 822 switchMenu += '<button class="disabled has-tooltip" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.pokemonControlled ? ',active' : '') + '" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '">'; 823 } else { 824 switchMenu += '<button name="chooseSwitch" value="' + i + '" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '">'; 825 } 826 switchMenu += '<span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="' + 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> '; 827 } 828 829 var controls = (
830 '<div class="switchcontrols">' + 831 '<div class="switchselect"><button name="selectSwitch">Switch</button></div>' + 832 '<div class="switchmenu">' + switchMenu + '</div>' + 833 '</div>' 834 ); 835 this.$controls.html( 836 '<div class="controls">' + 837 '<div class="whatdo">' + requestTitle + this.getTimerHTML() + '</div>' + 838 controls + 839 '</div>' 840 ); 841 this.selectSwitch(); 842 } 843 }, 844 updateTeamControls: function (type) { 845 var switchables = this.request && this.request.side ? this.battle.myPokemon : []; 846 var maxIndex = Math.min(switchables.length, 24); 847 848 var requestTitle = ""; 849 if (this.choice.done) { 850 requestTitle = '<button name="clearChoice">Back</button> ' + "What about the rest of your team?"; 851 } else { 852 requestTitle = "How will you start the battle?"; 853 } 854 855 var switchMenu = ''; 856 for (var i = 0; i < maxIndex; i++) { 857 var oIndex = this.choice.teamPreview[i] - 1; 858 var pokemon = switchables[oIndex]; 859 var tooltipArgs = 'switchpokemon|' + oIndex; 860 if (i < this.choice.done) { 861 switchMenu += '<button disabled="disabled" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> '; 862 } else { 863 switchMenu += '<button name="chooseTeamPreview" value="' + i + '" class="has-tooltip" data-tooltip="' + BattleLog.escapeHTML(tooltipArgs) + '"><span class="picon" style="' + Dex.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> '; 864 } 865 } 866 867 var controls = ( 868 '<div class="switchcontrols">' + 869 '<div class="switchselect">
869<button name="selectSwitch">' + (this.choice.done ? '' + "Choose a Pokémon for slot " + (this.choice.done + 1) : "Choose Lead") + '</button></div>' + 870 '<div class="switchmenu">' + switchMenu + '</div>' + 871 '</div>' 872 ); 873 this.$controls.html( 874 '<div class="controls">' + 875 '<div class="whatdo">' + requestTitle + this.getTimerHTML() + '</div>' + 876 controls + 877 '</div>' 878 ); 879 this.selectSwitch(); 880 }, 881 updateWaitControls: function () { 882 var buf = '<div class="controls">'; 883 buf += this.getPlayerChoicesHTML(); 884 if (!this.battle.nearSide.name || !this.battle.farSide.name || !this.request) { 885 if (this.battle.kickingInactive) { 886 buf += '<p><button class="button" name="setTimer" value="off">Stop timer</button> <small>← Your opponent has disconnected. This will give them more time to reconnect.</small></p>'; 887 } else { 888 buf += '<p><button class="button" name="setTimer" value="on">Claim victory</button> <small>← Your opponent has disconnected. Click this if they don\'t reconnect.</small></p>'; 889 } 890 } 891 this.$controls.html(buf + '</div>'); 892 }, 893 894 getPlayerChoicesHTML: function () { 895 var buf = '<p>' + this.getTimerHTML(); 896 if (!this.choice || !this.choice.waiting) { 897 return buf + '<em>Waiting for opponent...</em></p>'; 898 } 899 buf += '<small>'; 900 901 if (this.choice.teamPreview) { 902 var myPokemon = this.battle.mySide.pokemon; 903 var leads = []; 904 for (var i = 0; i < this.choice.count; i++) { 905 leads.push(myPokemon[this.choice.teamPreview[i] - 1].speciesForme); 906 } 907 buf += leads.join(', ') + ' will be sent out first.<br />'; 908 } else if (this.choice.choices && this.request && this.battle.myPokemon) { 909 var myPokemon = this.battle.myPokemon; 910 for (var i = 0; i < this.choice.choices.length; i++) { 911 var parts = this.choice.choices[i].split(' '); 912 switch (parts[0]) { 913 case 'move': 914 var move; 915 if (this.request.active[i].maxMoves && !this.request.active[i].canDynamax) { // it's a max move 916 move = this.request.active[i].maxMoves.maxMoves[parseInt(parts[1], 10) - 1].move; 917 } else { // it's a normal move 918 move = this.request.active[i].moves[parseInt(parts[1], 10) - 1].move; 919 } 920 var target = ''; 921 buf += myPokemon[i].speciesForme + ' will '; 922 if (parts.length > 2) { 923 var targetPos = parts[2]; 924 if (targetPos === 'mega') { 925 buf += 'mega evolve, then '; 926 targetPos = parts[3]; 927 } 928 if (targetPos === 'zmove') { 929 move = this.request.active[i].canZMove[parseInt(parts[1], 10) - 1].move; 930 targetPos = parts[3]; 931 } 932 if (targetPos === 'dynamax') { 933 move = this.request.active[i].maxMoves.maxMoves[parseInt(parts[1], 10) - 1].move; 934 buf += 'Dynamax, then '; 935 targetPos = parts[3]; 936 } 937 if (targetPos) { 938 var targetActive = this.battle.farSide.active; 939 if (targetPos < 0) { 940 // Targeting your own side in doubles / triples 941 targetActive = this.battle.nearSide.active; 942 targetPos = -targetPos; 943 if (this.battle.gameType !== 'freeforall') { 944 target += 'your '; 945 } 946 } 947 if (targetActive[targetPos - 1]) { 948 target += targetActive[targetPos - 1].speciesForme; 949 } else { 950 target += 'slot ' + targetPos; // targeting an empty slot 951 } 952 } 953 } 954 buf += 'use ' + Dex.moves.get(move).name + (target ? ' at ' + target : '') + '.<br />'; 955 break; 956 case 'switch': 957 buf += '' + myPokemon[parts[1] - 1].speciesForme + ' will switch in'; 958 if (myPokemon[i]) { 959 buf += ', replacing ' + myPokemon[i].speciesForme; 960 } 961 buf += '.<br />'; 962 break; 963 case 'shift': 964 buf += myPokemon[i].speciesForme + ' will shift position.<br />'; 965 break; 966 } 967 } 968 } 969 buf += '</small></p>'; 970 if (!this.finalDecision && !this.battle.hardcoreMode) { 971 buf += '<p><small><em>Waiting for opponent...</em></small> <button class="button" name="undoChoice">Cancel</button></p>'; 972 } 973 return buf; 974 }, 975 976 /** 977 * Sends a decision; pass it an array of choices like ['move 1', 'switch 2'] 978 * and it'll send `/choose move 1,switch 2|3` 979 * (where 3 is the rqid). 980 * 981 * (The rqid helps verify that the decision is sent in response to the 982 * correct request.) 983 */ 984 sendDecision: function (message) { 985 if (!$.isArray(message)) return this.send('/' + message + '|' + this.request.rqid); 986 var buf = '/choose '; 987 for (var i = 0; i < message.length; i++) { 988 if (message[i]) buf += message[i] + ','; 989 } 990 this.send(buf.substr(0, buf.length - 1) + '|' + this.request.rqid); 991 }, 992 request: null, 993 receiveRequest: function (request, choiceText) { 994 if (!request) { 995 this.side = ''; 996 return; 997 } 998 999 if (!this.autoTimerActivated && Storage.prefs('autotimer')) { 1000 this.setTimer('on'); 1001 this.autoTimerActivated = true; 1002 } 1003 1004 request.requestType = 'move'; 1005 if (request.forceSwitch) { 1006 request.requestType = 'switch'; 1007 } else if (request.teamPreview) { 1008 request.requestType = 'team'; 1009 } else if (request.wait) { 1010 request.requestType = 'wait'; 1011 } 1012 1013 this.choice = choiceText ? {waiting: true} : null; 1014 this.finalDecision = this.finalDecisionMove = this.finalDecisionSwitch = false;
1015 this.request = request; 1016 if (request.side) { 1017 this.updateSideLocation(request.side); 1018 } 1019 this.notifyRequest(); 1020 this.controlsShown = false; 1021 this.updateControls(); 1022 }, 1023 notifyRequest: function () { 1024 var oName = this.battle.farSide.name; 1025 if (oName) oName = " against " + oName; 1026 switch (this.request.requestType) { 1027 case 'move': 1028 this.notify("Your move!", "Move in your battle" + oName, 'choice'); 1029 break; 1030 case 'switch': 1031 this.notify("Your switch!", "Switch in your battle" + oName, 'choice'); 1032 break; 1033 case 'team': 1034 this.notify("Team preview!", "Choose your team order in your battle" + oName, 'choice'); 1035 break; 1036 } 1037 }, 1038 updateSideLocation: function (sideData) { 1039 if (!sideData.id) return; 1040 this.side = sideData.id; 1041 if (this.battle.mySide.sideid !== this.side) { 1042 this.battle.setPerspective(this.side); 1043 this.$chat = this.$chatFrame.find('.inner'); 1044 } 1045 }, 1046 updateSide: function () { 1047 var sideData = this.request.side; 1048 this.battle.myPokemon = sideData.pokemon; 1049 this.battle.setPerspective(sideData.id); 1050 for (var i = 0; i < sideData.pokemon.length; i++) { 1051 var pokemonData = sideData.pokemon[i]; 1052 if (this.request.active && this.request.active[i]) pokemonData.canGmax = this.request.active[i].gigantamax || false; 1053 this.battle.parseDetails(pokemonData.ident.substr(4), pokemonData.ident, pokemonData.details, pokemonData); 1054 this.battle.parseHealth(pokemonData.condition, pokemonData); 1055 pokemonData.hpDisplay = Pokemon.prototype.hpDisplay; 1056 pokemonData.getPixelRange = Pokemon.prototype.getPixelRange; 1057 pokemonData.getFormattedRange = Pokemon.prototype.getFormattedRange; 1058 pokemonData.getHPColorClass = Pokemon.prototype.getHPColorClass; 1059 pokemonData.getHPColor = Pokemon.prototype.getHPColor; 1060 } 1061 }, 1062 addAlly: function (allyData) { 1063 this.battle.myAllyPokemon = allyData.pokemon; 1064 for (var i = 0; i < allyData.pokemon.length; i++) { 1065 var pokemonData = allyData.pokemon[i]; 1066 this.battle.parseDetails(pokemonData.ident.substr(4), pokemonData.ident, pokemonData.details, pokemonData); 1067 this.battle.parseHealth(pokemonData.condition, pokemonData); 1068 pokemonData.hpDisplay = Pokemon.prototype.hpDisplay; 1069 pokemonData.getPixelRange = Pokemon.prototype.getPixelRange; 1070 pokemonData.getFormattedRange = Pokemon.prototype.getFormattedRange; 1071 pokemonData.getHPColorClass = Pokemon.prototype.getHPColorClass; 1072 pokemonData.getHPColor = Pokemon.prototype.getHPColor; 1073 pokemonData.side = this.battle.mySide.ally; 1074 } 1075 }, 1076 1077 // buttons 1078 joinBattle: function () { 1079 this.send('/joinbattle'); 1080 }, 1081 setTimer: function (setting) { 1082 this.send('/timer ' + setting); 1083 }, 1084 forfeit: function () { 1085 this.send('/forfeit'); 1086 }, 1087 saveReplay: function () { 1088 this.send('/savereplay'); 1089 }, 1090 openBattleOptions: function () { 1091 app.addPopup(BattleOptionsPopup, {battle: this.battle, room: this}); 1092 }, 1093 clickReplayDownloadButton: function (e) { 1094 var filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, ''); 1095 1096 // ladies and gentlemen, JavaScript dates 1097 var date = new Date(); 1098 filename += '-' + date.getFullYear(); 1099 filename += (date.getMonth() >= 9 ? '-' : '-0') + (date.getMonth() + 1); 1100 filename += (date.getDate() >= 10 ? '-' : '-0') + date.getDate(); 1101 1102 filename += '-' + toID(this.battle.p1.name); 1103 filename += '-' + toID(this.battle.p2.name); 1104 1105 e.currentTarget.href = BattleLog.createReplayFileHref(this); 1106 e.currentTarget.download = filename + '.html'; 1107 1108 e.stopPropagation(); 1109 }, 1110 switchSides: function () { 1111 this.battle.switchSides(); 1112 }, 1113 pause: function () { 1114 this.tooltips.hideTooltip(); 1115 this.battlePaused = true; 1116 this.battle.pause(); 1117 this.updateControls(); 1118 }, 1119 resume: function () { 1120 this.tooltips.hideTooltip(); 1121 this.battlePaused = false; 1122 this.battle.play(); 1123 this.updateControls(); 1124 }, 1125 instantReplay: function () { 1126 this.tooltips.hideTooltip(); 1127 this.request = null; 1128 this.battlePaused = false; 1129 this.battle.reset(); 1130 this.battle.play(); 1131 }, 1132 skipTurn: function () { 1133 this.battle.skipTurn(); 1134 }, 1135 rewindTurn: function () { 1136 if (this.battle.turn) { 1137 this.battle.seekTurn(this.battle.turn - 1); 1138 } 1139 }, 1140 goToEnd: function () { 1141 this.battle.seekTurn(Infinity); 1142 }, 1143 register: function (userid) { 1144 var registered = app.user.get('registered'); 1145 if (registered && registered.userid !== userid) registered = false;
1146 if (!registered && userid === app.user.get('userid')) { 1147 app.addPopup(RegisterPopup); 1148 } 1149 }, 1150 closeAndMainMenu: function () { 1151 this.close(); 1152 app.focusRoom(''); 1153 }, 1154 closeAndRematch: function () { 1155 app.rooms[''].requestNotifications(); 1156 app.rooms[''].challenge(this.battle.farSide.name, this.battle.tier); 1157 this.close(); 1158 app.focusRoom(''); 1159 }, 1160 1161 // choice buttons 1162 chooseMove: function (pos, e) { 1163 if (!this.choice) return; 1164 this.tooltips.hideTooltip(); 1165 1166 if (pos !== undefined) { // pos === undefined if called by chooseMoveTarget() 1167 var nearActive = this.battle.nearSide.active; 1168 var isMega = !!(this.$('input[name=megaevo]')[0] || '').checked; 1169 var isZMove = !!(this.$('input[name=zmove]')[0] || '').checked; 1170 var isUltraBurst = !!(this.$('input[name=ultraburst]')[0] || '').checked; 1171 var isDynamax = !!(this.$('input[name=dynamax]')[0] || '').checked; 1172 1173 var target = e.getAttribute('data-target'); 1174 var choosableTargets = {normal: 1, any: 1, adjacentAlly: 1, adjacentAllyOrSelf: 1, adjacentFoe: 1}; 1175 1176 this.choice.choices.push('move ' + pos + (isMega ? ' mega' : '') + (isZMove ? ' zmove' : '') + (isUltraBurst ? ' ultra' : '') + (isDynamax ? ' dynamax' : '')); 1177 if (nearActive.length > 1 && target in choosableTargets) { 1178 this.choice.type = 'movetarget'; 1179 this.choice.moveTarget = target; 1180 this.updateControlsForPlayer(); 1181 return false; 1182 } 1183 } 1184 1185 this.endChoice(); 1186 }, 1187 chooseMoveTarget: function (posString) { 1188 this.choice.choices[this.choice.choices.length - 1] += ' ' + posString; 1189 this.chooseMove(); 1190 }, 1191 chooseShift: function () { 1192 if (!this.choice) return; 1193 this.tooltips.hideTooltip(); 1194 1195 this.choice.choices.push('shift'); 1196 this.endChoice(); 1197 }, 1198 chooseSwitch: function (pos) { 1199 if (!this.choice) return; 1200 this.tooltips.hideTooltip(); 1201 1202 if (pos !== undefined) { // pos === undefined if called by chooseSwitchTarget() 1203 this.choice.switchFlags[pos] = true; 1204 if (this.choice.freedomDegrees >= 1) { 1205 // Request selection of a Pokémon that will be switched out. 1206 this.choice.type = 'switchposition'; 1207 this.updateControlsForPlayer(); 1208 return false; 1209 } 1210 // Default: left to right. 1211 this.choice.switchOutFlags[this.choice.choices.length] = true; 1212 this.choice.choices.push('switch ' + (parseInt(pos, 10) + 1)); 1213 this.endChoice(); 1214 return; 1215 } 1216 1217 // After choosing the position to which a pokemon will switch in (Doubles/Triples end-game). 1218 if (!this.request || this.request.requestType !== 'switch') return false; //?? 1219 if (this.choice.canSwitch > _.filter(this.choice.choices, function (choice) {return choice;}).length) { 1220 // More switches are pending. 1221 this.choice.type = 'switch2'; 1222 this.updateControlsForPlayer(); 1223 return false; 1224 } 1225 1226 this.endTurn(); 1227 }, 1228 chooseSwitchTarget: function (posString) { 1229 var slotSwitchIn = 0; // one-based 1230 for (var i in this.choice.switchFlags) { 1231 if (this.choice.choices.indexOf('switch ' + (+i + 1)) === -1) { 1232 slotSwitchIn = +i + 1; 1233 break; 1234 } 1235 } 1236 this.choice.choices[posString] = 'switch ' + slotSwitchIn; 1237 this.choice.switchOutFlags[posString] = true; 1238 this.chooseSwitch(); 1239 }, 1240 chooseTeamPreview: function (pos) { 1241 if (!this.choice) return; 1242 pos = parseInt(pos, 10); 1243 this.tooltips.hideTooltip(); 1244 if (this.choice.count) { 1245 var temp = this.choice.teamPreview[pos]; 1246 this.choice.teamPreview[pos] = this.choice.teamPreview[this.choice.done]; 1247 this.choice.teamPreview[this.choice.done] = temp; 1248 1249 this.choice.done++; 1250 1251 if (this.choice.done < Math.min(this.choice.teamPreview.length, this.choice.count)) { 1252 this.choice.type = 'team2'; 1253 this.updateControlsForPlayer(); 1254 return false; 1255 } 1256 } else { 1257 this.choice.teamPreview = [pos + 1]; 1258 } 1259 1260 this.endTurn(); 1261 }, 1262 chooseDisabled: function (data) { 1263 this.tooltips.hideTooltip(); 1264 data = data.split(','); 1265 if (data[1] === 'fainted') { 1266 app.addPopupMessage("" + data[0] + " has no energy left to battle!"); 1267 } else if (data[1] === 'notMine') { 1268 app.addPopupMessage("You cannot decide for your partner!"); 1269 } else if (data[1] === 'trapped') { 1270 app.addPopupMessage("You are trapped and cannot select " + data[0] + "!"); 1271 } else if (data[1] === 'active') { 1272 app.addPopupMessage("" + data[0] + " is already in battle!"); 1273 } else { 1274 app.addPopupMessage("" + data[0] + " is already selected!"); 1275 } 1276 }, 1277 endChoice: function () { 1278 var choiceIndex = this.choice.choices.length - 1; 1279 if (!this.nextChoice()) { 1280 this.endTurn(); 1281 } else if (this.request.partial) { 1282 for (var i = choiceIndex; i < this.choice.choices.length; i++) { 1283 this.sendDecision(this.choice.choices[i]); 1284 } 1285 } 1286 }, 1287 nextChoice: function () { 1288 var choices = this.choice.choices; 1289 var nearActive = this.battle.nearSide.active; 1290 1291 if (this.request.requestType === 'switch' && this.request.forceSwitch !== true) { 1292 while (choices.length < this.battle.pokemonControlled && !this.request.forceSwitch[choices.length]) { 1293 choices.push('pass'); 1294 } 1295 if (choices.length < this.battle.pokemonControlled) { 1296 this.choice.type = 'switch2'; 1297 this.updateControlsForPlayer(); 1298 return true; 1299 } 1300 } else if (this.request.requestType === 'move') { 1301 while (choices.length < this.battle.pokemonControlled && !nearActive[choices.length]) { 1302 choices.push('pass'); 1303 } 1304 1305 if (choices.length < this.battle.pokemonControlled) { 1306 this.choice.type = 'move2'; 1307 this.updateControlsForPlayer(); 1308 return true; 1309 } 1310 } 1311 1312 return false;
1313 }, 1314 endTurn: function () { 1315 var act = this.request && this.request.requestType; 1316 if (act === 'team') { 1317 if (this.choice.teamPreview.length >= 10) { 1318 this.sendDecision('team ' + this.choice.teamPreview.join(',')); 1319 } else { 1320 this.sendDecision('team ' + this.choice.teamPreview.join('')); 1321 } 1322 } else { 1323 if (act === 'switch') { 1324 // Assert that the remaining Pokémon won't switch, even though 1325 // the player could have decided otherwise. 1326 for (var i = 0; i < this.battle.pokemonControlled; i++) { 1327 if (!this.choice.choices[i]) this.choice.choices[i] = 'pass'; 1328 } 1329 } 1330 1331 if (this.choice.choices.length >= (this.choice.count || this.battle.pokemonControlled || this.request.active.length)) { 1332 this.sendDecision(this.choice.choices); 1333 } 1334 1335 if (!this.finalDecision) { 1336 var lastChoice = this.choice.choices[this.choice.choices.length - 1]; 1337 if (lastChoice.substr(0, 5) === 'move ' && this.finalDecisionMove) { 1338 this.finalDecisionMove = true; 1339 } else if (lastChoice.substr(0, 7) === 'switch' && this.finalDecisionSwitch) { 1340 this.finalDecisionSwitch = true; 1341 } 1342 } 1343 } 1344 this.closeNotification('choice'); 1345 1346 this.choice.waiting = true; 1347 this.updateControlsForPlayer(); 1348 }, 1349 undoChoice: function (pos) { 1350 this.send('/undo'); 1351 this.notifyRequest(); 1352 1353 this.clearChoice(); 1354 }, 1355 clearChoice: function () { 1356 this.choice = null; 1357 this.updateControlsForPlayer(); 1358 }, 1359 leaveBattle: function () { 1360 this.tooltips.hideTooltip(); 1361 this.send('/leavebattle'); 1362 this.side = ''; 1363 this.closeNotification('choice'); 1364 }, 1365 selectSwitch: function () { 1366 this.tooltips.hideTooltip(); 1367 this.$controls.find('.controls').attr('class', 'controls switch-controls'); 1368 }, 1369 selectMove: function () { 1370 this.tooltips.hideTooltip(); 1371 this.$controls.find('.controls').attr('class', 'controls move-controls'); 1372 } 1373 }, { 1374 readReplayFile: function (file) { 1375 var reader = new FileReader(); 1376 reader.onload = function (e) { 1377 app.removeRoom('battle-uploadedreplay'); 1378 var html = e.target.result; 1379 var titleStart = html.indexOf('<title>'); 1380 var titleEnd = html.indexOf('</title>'); 1381 var title = 'Uploaded Replay'; 1382 if (titleStart >= 0 && titleEnd > titleStart) { 1383 title = html.slice(titleStart + 7, titleEnd - 1); 1384 var colonIndex = title.indexOf(':'); 1385 var hyphenIndex = title.lastIndexOf('-'); 1386 if (hyphenIndex > colonIndex + 2) { 1387 title = title.substring(colonIndex + 2, hyphenIndex - 1); 1388 } else { 1389 title = title.substring(colonIndex + 2); 1390 } 1391 } 1392 var index1 = html.indexOf('<script type="text/plain" class="battle-log-data">'); 1393 var index2 = html.indexOf('<script type="text/plain" class="log">'); 1394 if (index1 < 0 && index2 < 0) return alert("Unrecognized HTML file: Only replay files are supported."); 1395 if (index1 >= 0) { 1396 html = html.slice(index1 + 50); 1397 } else if (index2 >= 0) { 1398 html = html.slice(index2 + 38); 1399 } 1400 var index3 = html.indexOf('</script>'); 1401 html = html.slice(0, index3); 1402 html = html.replace(/\\\//g, '/'); 1403 app.receive('>battle-uploadedreplay\n|init|battle\n|title|' + title + '\n' + html); 1404 app.receive('>battle-uploadedreplay\n|expire|Uploaded replay'); 1405 }; 1406 reader.readAsText(file); 1407 } 1408 }); 1409 1410 var ForfeitPopup = this.ForfeitPopup = Popup.extend({ 1411 type: 'semimodal', 1412 initialize: function (data) { 1413 this.room = data.room; 1414 this.gameType = data.gameType; 1415 var buf = '<form><p>'; 1416 if (this.gameType === 'battle') { 1417 buf += 'Forfeiting makes you lose the battle, and will not earn you or your opponent points from using Saurbot Analyze.'; 1418 } else if (this.gameType === 'help') { 1419 buf += 'Leaving the room will close the ticket.'; 1420 } else { 1421 // game 1422 buf += 'Forfeiting makes you lose the game.'; 1423 } 1424 if (this.gameType === 'help') { 1425 buf += ' Are you sure?</p><p><label><input type="checkbox" name="closeroom" checked /> Close room</label></p>'; 1426 buf += '<p><button type="submit"><strong>Close ticket</strong></button> '; 1427 } else { 1428 buf += ' Are you sure?</p><p><label><input type="checkbox" name="closeroom" checked /> Close after forfeiting</label></p>'; 1429 buf += '<p><button type="submit"><strong>Forfeit</strong></button> '; 1430 } 1431 if (this.gameType === 'battle' && this.room.battle && !this.room.battle.rated) { 1432 buf += '<button type="button" name="replacePlayer">Replace player</button> '; 1433 } 1434 buf += '<button type="button" name="close" class="autofocus">Cancel</button></p></form>'; 1435 this.$el.html(buf); 1436 }, 1437 replacePlayer: function (data) { 1438 var room = this.room; 1439 var self = this;
1440 app.addPopupPrompt("Replacement player's username", "Replace player", function (target) { 1441 if (!target) return; 1442 var side = (room.battle.mySide.id === room.battle.p1.id ? 'p1' : 'p2'); 1443 room.leaveBattle(); 1444 room.send('/addplayer ' + target + ', ' + side); 1445 self.close(); 1446 }); 1447 }, 1448 submit: function (data) { 1449 this.room.send('/forfeit'); 1450 if (this.gameType === 'battle') this.room.battle.forfeitPending = true; 1451 if (this.$('input[name=closeroom]')[0].checked) { 1452 app.removeRoom(this.room.id); 1453 } 1454 this.close(); 1455 } 1456 }); 1457 1458 var BattleOptionsPopup = this.BattleOptionsPopup = Popup.extend({ 1459 initialize: function (data) { 1460 this.battle = data.battle; 1461 this.room = data.room; 1462 var rightPanelBattlesPossible = (MainMenuRoom.prototype.bestWidth + BattleRoom.prototype.minWidth < $(window).width()); 1463 var buf = '<p><strong>In this battle</strong></p>'; 1464 buf += '<p><label class="optlabel"><input type="checkbox" name="hardcoremode"' + (this.battle.hardcoreMode ? ' checked' : '') + '/> Hardcore mode (hide info not shown in-game) (beta)</label></p>'; 1465 buf += '<p><label class="optlabel"><input type="checkbox" name="ignorespects"' + (this.battle.ignoreSpects ? ' checked' : '') + '/> Ignore spectators</label></p>'; 1466 buf += '<p><label class="optlabel"><input type="checkbox" name="ignoreopp"' + (this.battle.ignoreOpponent ? ' checked' : '') + '/> Ignore opponent</label></p>'; 1467 buf += '<p><strong>All battles</strong></p>'; 1468 buf += '<p><label class="optlabel"><input type="checkbox" name="ignorenicks"' + (Dex.prefs('ignorenicks') ? ' checked' : '') + ' /> Ignore nicknames</label></p>'; 1469 buf += '<p><label class="optlabel"><input type="checkbox" name="allignorespects"' + (Dex.prefs('ignorespects') ? ' checked' : '') + '/> Ignore spectators</label></p>'; 1470 buf += '<p><label class="optlabel"><input type="checkbox" name="allignoreopp"' + (Dex.prefs('ignoreopp') ? ' checked' : '') + '/> Ignore opponent</label></p>'; 1471 buf += '<p><label class="optlabel"><input type="checkbox" name="autotimer"' + (Dex.prefs('autotimer') ? ' checked' : '') + '/> Automatically start timer</label></p>'; 1472 if (rightPanelBattlesPossible) buf += '<p><label class="optlabel"><input type="checkbox" name="rightpanelbattles"' + (Dex.prefs('rightpanelbattles') ? ' checked' : '') + ' /> Open new battles on the right side</label></p>'; 1473 buf += '<p><button name="close">Close</button></p>'; 1474 this.$el.html(buf); 1475 }, 1476 events: { 1477 'change input[name=ignorespects]': 'toggleIgnoreSpects', 1478 'change input[name=ignorenicks]': 'toggleIgnoreNicks', 1479 'change input[name=ignoreopp]': 'toggleIgnoreOpponent', 1480 'change input[name=hardcoremode]': 'toggleHardcoreMode', 1481 'change input[name=allignorespects]': 'toggleAllIgnoreSpects', 1482 'change input[name=allignoreopp]': 'toggleAllIgnoreOpponent', 1483 'change input[name=autotimer]': 'toggleAutoTimer', 1484 'change input[name=rightpanelbattles]': 'toggleRightPanelBattles' 1485 }, 1486 toggleHardcoreMode: function (e) { 1487 this.room.setHardcoreMode(!!e.currentTarget.checked); 1488 if (this.battle.hardcoreMode) { 1489 this.battle.add('Hardcore mode ON: Information not available in-game is now hidden.'); 1490 } else { 1491 this.battle.add('Hardcore mode OFF: Information not available in-game is now shown.'); 1492 } 1493 }, 1494 toggleIgnoreSpects: function (e) { 1495 this.battle.ignoreSpects = !!e.currentTarget.checked; 1496 this.battle.add('Spectators ' + (this.battle.ignoreSpects ? '' : 'no longer ') + 'ignored.'); 1497 var $messages = $('.battle-log').find('.chat').has('small').not(':contains(\u2605), :contains(\u2606)'); 1498 if (!$messages.length) return; 1499 if (this.battle.ignoreSpects) { 1500 $messages.hide(); 1501 } else { 1502 $messages.show(); 1503 } 1504 }, 1505 toggleAllIgnoreSpects: function (e) { 1506 var ignoreSpects = !!e.currentTarget.checked; 1507 Storage.prefs('ignorespects', ignoreSpects); 1508 if (ignoreSpects && !this.battle.ignoreSpects) this.$el.find('input[name=ignorespects]').click(); 1509 }, 1510 toggleIgnoreNicks: function (e) { 1511 this.battle.ignoreNicks = !!e.currentTarget.checked; 1512 Storage.prefs('ignorenicks', this.battle.ignoreNicks); 1513 this.battle.add('Nicknames ' + (this.battle.ignoreNicks ? '' : 'no longer ') + 'ignored.'); 1514 this.battle.resetToCurrentTurn(); 1515 }, 1516 toggleIgnoreOpponent: function (e) { 1517 this.battle.ignoreOpponent = !!e.currentTarget.checked; 1518 this.battle.add('Opponent ' + (this.battle.ignoreOpponent ? '' : 'no longer ') + 'ignored.'); 1519 this.battle.resetToCurrentTurn(); 1520 }, 1521 toggleAllIgnoreOpponent: function (e) { 1522 var ignoreOpponent = !!e.currentTarget.checked; 1523 Storage.prefs('ignoreopp', ignoreOpponent); 1524 if (ignoreOpponent && !this.battle.ignoreOpponent) this.$el.find('input[name=ignoreopp]').click(); 1525 }, 1526 toggleAutoTimer: function (e) { 1527 var autoTimer = !!e.currentTarget.checked; 1528 Storage.prefs('autotimer', autoTimer); 1529 if (autoTimer) { 1530 this.room.setTimer('on'); 1531 this.room.autoTimerActivated = true; 1532 } 1533 }, 1534 toggleRightPanelBattles: function (e) {
1535 Storage.prefs('rightpanelbattles', !!e.currentTarget.checked); 1536 } 1537 }); 1538 1539 var TimerPopup = this.TimerPopup = Popup.extend({ 1540 initialize: function (data) { 1541 this.room = data.room; 1542 if (this.room.battle.kickingInactive) { 1543 this.$el.html('<p><button name="timerOff"><strong>Stop timer</strong></button></p>'); 1544 } else { 1545 this.$el.html('<p><button name="timerOn"><strong>Start timer</strong></button></p>'); 1546 } 1547 }, 1548 timerOff: function () { 1549 this.room.setTimer('off'); 1550 this.close(); 1551 }, 1552 timerOn: function () { 1553 this.room.setTimer('on'); 1554 this.close(); 1555 } 1556 }); 1557 1558}).call(this, jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.