diff --git a/js/battle-beta.js b/js/battle-beta.js new file mode 100644 index 000000000..39e529603 --- /dev/null +++ b/js/battle-beta.js @@ -0,0 +1,5361 @@ +/* + +License: GPLv2 + + +*/ + +// par: -webkit-filter: sepia(100%) hue-rotate(373deg) saturate(592%); +// -webkit-filter: sepia(100%) hue-rotate(22deg) saturate(820%) brightness(29%); +// psn: -webkit-filter: sepia(100%) hue-rotate(618deg) saturate(285%); +// brn: -webkit-filter: sepia(100%) hue-rotate(311deg) saturate(469%); +// slp: -webkit-filter: grayscale(100%); +// frz: -webkit-filter: sepia(100%) hue-rotate(154deg) saturate(759%) brightness(23%); + +$.extend($.easing, { + ballisticUp: function (x, t, b, c, d) { + return -3 * x * x + 4 * x; + }, + ballisticDown: function (x, t, b, c, d) { + x = 1 - x; + return 1 - (-3 * x * x + 4 * x); + }, + quadUp: function (x, t, b, c, d) { + x = 1 - x; + return 1 - (x * x); + }, + quadDown: function (x, t, b, c, d) { + return x * x; + } +}); + +function BattleSoundLibrary() { + // options + this.effectVolume = 50; + this.bgmVolume = 50; + this.muted = false; + + // effects + this.effectCache = {}; + this.loadEffect = function(url) { + if (this.effectCache[url] && this.effectCache[url] !== this.soundPlaceholder) { + return this.effectCache[url]; + } + try { + this.effectCache[url] = soundManager.createSound({ + id: url, + url: Tools.resourcePrefix + url, + volume: this.effectVolume + }); + } catch (e) {} + if (!this.effectCache[url]) { + this.effectCache[url] = this.soundPlaceholder; + } + return this.effectCache[url]; + }; + this.playEffect = function(url) { + if (!this.muted) this.loadEffect(url).setVolume(this.effectVolume).play(); + }; + + // bgm + this.bgmCache = {}; + this.bgm = null; + this.loadBgm = function(url, loopstart, loopend) { + if (this.bgmCache[url]) { + if (this.bgmCache[url] !== this.soundPlaceholder || loopstart === undefined) { + return this.bgmCache[url]; + } + } + try { + this.bgmCache[url] = soundManager.createSound({ + id: url, + url: Tools.resourcePrefix + url, + volume: this.bgmVolume + }); + } catch (e) {} + if (!this.bgmCache[url]) { + // couldn't load + // suppress crash + return this.bgmCache[url] = this.soundPlaceholder; + } + this.bgmCache[url].onposition(loopend, function() { + this.setPosition(loopstart); + }); + return this.bgmCache[url]; + }; + this.playBgm = function(url, loopstart, loopstop) { + if (this.bgm === this.loadBgm(url, loopstart, loopstop)) { + if (!this.bgm.paused && this.bgm.playState) { + return; + } + } else { + this.stopBgm(); + } + try { + this.bgm = this.loadBgm(url, loopstart, loopstop).setVolume(this.bgmVolume); + if (!this.muted) { + if (this.bgm.paused) { + this.bgm.resume(); + } else { + this.bgm.play(); + } + } + } catch(e) {} + }; + this.pauseBgm = function() { + if (this.bgm) { + this.bgm.pause(); + } + }; + this.stopBgm = function() { + if (this.bgm) { + this.bgm.stop(); + this.bgm = null; + } + }; + + // setting + this.setMute = function(muted) { + muted = !!muted; + if (this.muted == muted) return; + this.muted = muted; + if (muted) { + if (this.bgm) this.bgm.pause(); + } else { + if (this.bgm) this.bgm.play(); + } + }; + this.setBgmVolume = function(bgmVolume) { + this.bgmVolume = bgmVolume; + if (this.bgm) { + try { + this.bgm.setVolume(bgmVolume); + } catch (e) {} + } + }; + this.setEffectVolume = function(effectVolume) { + this.effectVolume = effectVolume; + }; + + // misc + this.soundPlaceholder = { + play: function(){ return this; }, + pause: function(){ return this; }, + stop: function(){ return this; }, + resume: function(){ return this; }, + setVolume: function(){ return this; } + }; +} +var BattleSound = new BattleSoundLibrary(); + +function Pokemon(species) { + var selfP = this; + + this.atk = 0; + this.def = 0; + this.spa = 0; + this.spd = 0; + this.spe = 0; + + this.atkStat = 0; + this.defStat = 0; + this.spaStat = 0; + this.spdStat = 0; + this.speStat = 0; + + this.boosts = {}; + + this.hp = 0; + this.maxhp = 0; + this.hpcolor = ''; + this.moves = []; + this.ability = ''; + this.item = ''; + this.species = species; + this.side = null; + this.fainted = false; + this.zerohp = false; + + this.status = ''; + this.statusStage = 0; + this.volatiles = {}; + this.turnstatuses = {}; + this.movestatuses = {}; + this.lastmove = ''; + + this.name = ''; + this.species = ''; + this.id = ''; + this.statbarElem = null; + + this.getHPColor = function () { + if (selfP.hpcolor) return selfP.hpcolor; + var ratio = selfP.hp / selfP.maxhp; + if (ratio > 0.5) return 'g'; + if (ratio > 0.2) return 'y'; + return 'r'; + }; + this.getHPColorClass = function () { + switch (selfP.getHPColor()) { + case 'y': return ' hpbar-yellow'; + case 'r': return ' hpbar-red'; + } + return ''; + }; + var epsilon = 0.5/714; + this.getPixelRange = function (pixels, color) { + if (pixels === 0) { + return [0, 0]; + } else if (pixels === 1) { + return [0 + epsilon, 2/48 - epsilon]; + } else if (pixels === 9) { + if (color === 'y') { // ratio is > 0.2 + return [0.2 + epsilon, 10/48 - epsilon]; + } else { // ratio is <= 0.2 + return [9/48, 0.2]; + } + } else if (pixels === 24) { + if (color === 'g') { // ratio is > 0.5 + return [0.5 + epsilon, 25/48 - epsilon]; + } else { // ratio is exactly 0.5 + return [0.5, 0.5]; + } + } else if (pixels === 48) { + return [1, 1]; + } else { + return [pixels/48, (pixels + 1)/48 - epsilon]; + } + }; + this.getFormattedRange = function (range, precision, separator) { + if (range[0] === range[1]) { + var percentage = Math.abs(range[0] * 100); + if (Math.floor(percentage) === percentage) { + return percentage + '%'; + } + return percentage.toFixed(precision) + '%'; + } + var lower, upper; + if (precision === 0) { + lower = Math.floor(range[0] * 100); + upper = Math.ceil(range[1] * 100); + } else { + lower = (range[0] * 100).toFixed(precision); + upper = (range[1] * 100).toFixed(precision); + } + return lower + separator + upper + '%'; + }; + this.getDamageRange = function (damage) { + if (damage[1] !== 48) { + var ratio = damage[0] / damage[1]; + return [ratio, ratio]; + } else if (damage[3] === undefined) { + // wrong pixel damage. + // this case exists for backward compatibility only. + return [damage[2] / 100, damage[2] / 100]; + } + // pixel damage + var oldrange = selfP.getPixelRange(damage[3], damage[4]); + var newrange = selfP.getPixelRange(damage[3] + damage[0], selfP.hpcolor); + if (damage[0] === 0) { + // no change in displayed pixel width + return [0, newrange[1] - newrange[0]]; + } + if (oldrange[0] < newrange[0]) { // swap order + var r = oldrange; + oldrange = newrange; + newrange = r; + } + return [oldrange[0] - newrange[1], oldrange[1] - newrange[0]]; + }; + // returns [delta, denominator, percent(, oldnum, oldcolor)] or false + this.healthParse = function (hpstring, parsedamage, heal) { + if (!hpstring || !hpstring.length) return false; + var parenIndex = hpstring.lastIndexOf('('); + if (parenIndex >= 0) { + // old style damage and health reporting + if (parsedamage) { + var damage = parseFloat(hpstring); + // unusual check preseved for backward compatbility + if (isNaN(damage)) damage = 50; + if (heal) { + selfP.hp += selfP.maxhp * damage / 100; + if (selfP.hp > selfP.maxhp) selfP.hp = selfP.maxhp; + } else { + selfP.hp -= selfP.maxhp * damage / 100; + } + // parse the absolute health information + var ret = this.healthParse(hpstring); + if (ret && (ret[1] === 100)) { + // support for old replays with nearest-100th damage and health + return [damage, 100, damage]; + } + // complicated expressions preserved for backward compatibility + var percent = Math.round(Math.ceil(damage * 48 / 100) / 48 * 100); + var pixels = Math.ceil(damage * 48 / 100); + return [pixels, 48, percent]; + } + if (hpstring.substr(hpstring.length-1) !== ')') { + return false; + } + hpstring = hpstring.substr(parenIndex+1, hpstring.length-parenIndex-2); + } + + var hp = hpstring.split(' '); + var status = hp[1]; + hp = hp[0]; + var oldhp = (selfP.zerohp || selfP.fainted) ? 0 : (selfP.hp || 1); + var oldmaxhp = selfP.maxhp; + var oldwidth = selfP.hpWidth(100); + var oldcolor = selfP.hpcolor; + + // hp parse + selfP.hpcolor = ''; + if (hp === '0' || hp === '0.0') { + selfP.hp = 0; + selfP.zerohp = true; + } else if (hp.indexOf('/') > 0) { + var hp = hp.split('/'); + if (isNaN(parseFloat(hp[0])) || isNaN(parseFloat(hp[1]))) { + return false; + } + selfP.hp = parseFloat(hp[0]); + selfP.maxhp = parseFloat(hp[1]); + if (oldmaxhp === 0) { // max hp not known before parsing this message + oldmaxhp = oldhp = selfP.maxhp; + } + if (selfP.hp > selfP.maxhp) selfP.hp = selfP.maxhp; + var colorchar = hp[1].substr(hp[1].length - 1); + if ((colorchar === 'y') || (colorchar === 'g')) { + selfP.hpcolor = colorchar; + } + if (!selfP.hp) { + selfP.zerohp = true; + } + } else if (!isNaN(parseFloat(hp))) { + selfP.hp = selfP.maxhp * parseFloat(hp) / 100; + } + + // status parse + if (!status) { + selfP.status = ''; + } else if (status === 'par' || status === 'brn' || status === 'slp' || status === 'frz' || status === 'tox') { + selfP.status = status; + } else if (status === 'psn' && selfP.status !== 'tox') { + selfP.status = status; + } else if (status === 'fnt') { + selfP.hp = 0; + selfP.zerohp = true; + selfP.fainted = true; + } + + var oldnum = oldhp ? (Math.floor(oldhp / oldmaxhp * selfP.maxhp) || 1) : 0; + var delta = selfP.hp - oldnum; + var deltawidth = selfP.hpWidth(100) - oldwidth; + return [delta, selfP.maxhp, deltawidth, oldnum, oldcolor]; + }; + this.checkDetails = function(details, ident) { + if (details === selfP.details) return true; + if (selfP.details.indexOf('-*') >= 0) { + selfP.needsReplace = true; + details = details.replace(/-[A-Za-z0-9]+(, |$)/, '$1'); + return (details === selfP.details.replace(/-[A-Za-z0-9*]+(, |$)/, '$1')); + } + return false; + }; + this.getIdent = function() { + if (selfP.side.active.length === 1) return selfP.ident; + var slots = ['a','b','c','d','e','f']; + return selfP.ident.substr(0,2) + slots[selfP.slot] + selfP.ident.substr(2); + }; + this.removeVolatile = function (volatile) { + if (!selfP.hasVolatile(volatile)) return; + if (volatile === 'formechange') { + selfP.sprite.removeTransform(); + } + if (selfP.volatiles[volatile][1]) selfP.volatiles[volatile][1].remove(); + delete selfP.volatiles[volatile]; + }; + this.addVolatile = function (volatile) { + var self = selfP.side.battle; + if (selfP.hasVolatile(volatile)) return; + selfP.volatiles[volatile] = [volatile, null]; + if (volatile === 'leechseed') { + selfP.side.battle.spriteElemsFront[selfP.side.n].append(''); + var curelem = selfP.side.battle.spriteElemsFront[selfP.side.n].children().last(); + curelem.css(self.pos({ + display: 'block', + x: selfP.sprite.x - 30, + y: selfP.sprite.y - 40, + z: selfP.sprite.z, + scale: .2, + opacity: .6 + }, BattleEffects.energyball)); + var elem = curelem; + + selfP.side.battle.spriteElemsFront[selfP.side.n].append(''); + curelem = selfP.side.battle.spriteElemsFront[selfP.side.n].children().last(); + curelem.css(self.pos({ + display: 'block', + x: selfP.sprite.x + 40, + y: selfP.sprite.y - 35, + z: selfP.sprite.z, + scale: .2, + opacity: .6 + }, BattleEffects.energyball)); + elem = elem.add(curelem); + + selfP.side.battle.spriteElemsFront[selfP.side.n].append(''); + curelem = selfP.side.battle.spriteElemsFront[selfP.side.n].children().last(); + curelem.css(self.pos({ + display: 'block', + x: selfP.sprite.x + 20, + y: selfP.sprite.y - 25, + z: selfP.sprite.z, + scale: .2, + opacity: .6 + }, BattleEffects.energyball)); + elem = elem.add(curelem); + selfP.volatiles[volatile][1] = elem; + } + }; + this.hasVolatile = function (volatile) { + return !!selfP.volatiles[volatile]; + }; + this.removeTurnstatus = function (volatile) { + if (!selfP.hasTurnstatus(volatile)) return; + if (selfP.turnstatuses[volatile][1]) selfP.turnstatuses[volatile][1].remove(); + delete selfP.turnstatuses[volatile]; + }; + this.addTurnstatus = function (volatile) { + volatile = toId(volatile); + var self = selfP.side.battle; + if (selfP.hasTurnstatus(volatile)) { + if (volatile === 'protect' || volatile === 'magiccoat') { + selfP.turnstatuses[volatile][1].css(self.pos({ + x: selfP.sprite.x, + y: selfP.sprite.y, + z: selfP.sprite.behind(-15), + xscale: 1 * 1.2, + yscale: .7 * 1.2, + opacity: 1 + }, BattleEffects.none), 300).animate(self.pos({ + x: selfP.sprite.x, + y: selfP.sprite.y, + z: selfP.sprite.behind(-15), + xscale: 1, + yscale: .7, + opacity: .4 + }, BattleEffects.none), 300); + } + return; + } + selfP.turnstatuses[volatile] = [volatile, null]; + if (volatile === 'protect' || volatile === 'magiccoat') { + selfP.side.battle.spriteElemsFront[selfP.side.n].append('
'; + } + pokemonhtml = '
' + pokemonhtml + '
'; + if (selfS.n === 1) { + if (selfS.initialized) self.rightbarElem.html('
' + Tools.escapeHTML(selfS.name) + '
' + pokemonhtml + '
').find('.trainer').css('opacity',1); + else self.rightbarElem.find('.trainer').css('opacity',0.4); + } else { + if (selfS.initialized) self.leftbarElem.html('
' + Tools.escapeHTML(selfS.name) + '
' + pokemonhtml + '
').find('.trainer').css('opacity',1); + else self.leftbarElem.find('.trainer').css('opacity',0.4); + } + }; + this.addSideCondition = function (condition) { + condition = toId(condition); + if (selfS.sideConditions[condition]) { + if (condition === 'spikes' || condition === 'toxicspikes') { + selfS.sideConditions[condition][2]++; + if (condition === 'spikes' && selfS.sideConditions[condition][2] == 2) { + self.spriteElemsFront[selfS.n].append(''); + curelem = self.spriteElemsFront[selfS.n].children().last(); + curelem.css(self.pos({ + display: 'block', + x: selfS.x + 50, + y: selfS.y - 40, + z: selfS.z, + scale: .3 + }, BattleEffects.caltrop)); + selfS.sideConditions['spikes'][1] = selfS.sideConditions['spikes'][1].add(curelem); + } else if (condition === 'spikes') { + self.spriteElemsFront[selfS.n].append(''); + curelem = self.spriteElemsFront[selfS.n].children().last(); + curelem.css(self.pos({ + display: 'block', + x: selfS.x + 30, + y: selfS.y - 45, + z: selfS.z, + scale: .3 + }, BattleEffects.caltrop)); + selfS.sideConditions['spikes'][1] = selfS.sideConditions['spikes'][1].add(curelem); + } else if (condition === 'toxicspikes') { + self.spriteElemsFront[selfS.n].append(''); + curelem = self.spriteElemsFront[selfS.n].children().last(); + curelem.css(self.pos({ + display: 'block', + x: selfS.x - 15, + y: selfS.y - 35, + z: selfS.z, + scale: .3 + }, BattleEffects.poisoncaltrop)); + selfS.sideConditions['toxicspikes'][1] = selfS.sideConditions['toxicspikes'][1].add(curelem); + } + } + return; + } + var elem, curelem; + switch (condition) { + case 'reflect': + self.spriteElemsFront[selfS.n].append('