exports.BattleStatuses = { brn: { effectType: 'Status', onStart: function (target, source, sourceEffect) { if (sourceEffect && sourceEffect.id === 'flameorb') { this.add('-status', target, 'brn', '[from] item: Flame Orb'); return; } this.add('-status', target, 'brn'); }, // Damage reduction is handled directly in the battle-engine.js damage function onResidualOrder: 9, onResidual: function (pokemon) { this.damage(pokemon.maxhp / 8); } }, par: { effectType: 'Status', onStart: function (target) { this.add('-status', target, 'par'); }, onModifySpe: function (speMod, pokemon) { if (!pokemon.hasAbility('quickfeet')) { return this.chain(speMod, 0.25); } }, onBeforeMovePriority: 1, onBeforeMove: function (pokemon) { if (this.random(4) === 0) { this.add('cant', pokemon, 'par'); return false; } } }, slp: { effectType: 'Status', onStart: function (target) { this.add('-status', target, 'slp'); // 1-3 turns this.effectData.startTime = this.random(2, 5); this.effectData.time = this.effectData.startTime; }, onBeforeMovePriority: 10, onBeforeMove: function (pokemon, target, move) { if (pokemon.hasAbility('earlybird')) { pokemon.statusData.time--; } pokemon.statusData.time--; if (pokemon.statusData.time <= 0) { pokemon.cureStatus(); return; } this.add('cant', pokemon, 'slp'); if (move.sleepUsable) { return; } return false; } }, frz: { effectType: 'Status', onStart: function (target) { this.add('-status', target, 'frz'); if (target.template.species === 'Shaymin-Sky' && target.baseTemplate.baseSpecies === 'Shaymin') { var template = this.getTemplate('Shaymin'); target.formeChange(template); target.baseTemplate = template; target.setAbility(template.abilities['0']); target.baseAbility = target.ability; target.details = template.species + (target.level === 100 ? '' : ', L' + target.level) + (target.gender === '' ? '' : ', ' + target.gender) + (target.set.shiny ? ', shiny' : ''); this.add('detailschange', target, target.details); this.add('-formechange', target, 'Shaymin', '[msg]'); } }, onBeforeMovePriority: 10, onBeforeMove: function (pokemon, target, move) { if (move.flags['defrost']) return; if (this.random(5) === 0) { pokemon.cureStatus(); return; } this.add('cant', pokemon, 'frz'); return false; }, onModifyMove: function (move, pokemon) { if (move.flags['defrost']) { this.add('-curestatus', pokemon, 'frz', '[from] move: ' + move); pokemon.setStatus(''); } }, onHit: function (target, source, move) { if (move.thawsTarget || move.type === 'Fire' && move.category !== 'Status') { target.cureStatus(); } } }, psn: { effectType: 'Status', onStart: function (target) { this.add('-status', target, 'psn'); }, onResidualOrder: 9, onResidual: function (pokemon) { this.damage(pokemon.maxhp / 8); } }, tox: { effectType: 'Status', onStart: function (target, source, sourceEffect) { this.effectData.stage = 0; if (sourceEffect && sourceEffect.id === 'toxicorb') { this.add('-status', target, 'tox', '[from] item: Toxic Orb'); return; } this.add('-status', target, 'tox'); }, onSwitchIn: function () { this.effectData.stage = 0; }, onResidualOrder: 9, onResidual: function (pokemon) { if (this.effectData.stage < 15) { this.effectData.stage++; } this.damage(this.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage); } }, confusion: { // this is a volatile status onStart: function (target, source, sourceEffect) { var result = this.runEvent('TryConfusion', target, source, sourceEffect); if (!result) return result; if (sourceEffect && sourceEffect.id === 'lockedmove') { this.add('-start', target, 'confusion', '[fatigue]'); } else { this.add('-start', target, 'confusion'); } this.effectData.time = this.random(2, 6); }, onEnd: function (target) { this.add('-end', target, 'confusion'); }, onBeforeMovePriority: 3, onBeforeMove: function (pokemon) { pokemon.volatiles.confusion.time--; if (!pokemon.volatiles.confusion.time) { pokemon.removeVolatile('confusion'); return; } this.add('-activate', pokemon, 'confusion'); if (this.random(2) === 0) { return; } this.directDamage(this.getDamage(pokemon, pokemon, 40)); return false; } }, flinch: { duration: 1, onBeforeMovePriority: 8, onBeforeMove: function (pokemon) { if (!this.runEvent('Flinch', pokemon)) { return; } this.add('cant', pokemon, 'flinch'); return false; } }, trapped: { noCopy: true, onModifyPokemon: function (pokemon) { pokemon.tryTrap(); }, onStart: function (target) { this.add('-activate', target, 'trapped'); } }, trapper: { noCopy: true }, partiallytrapped: { duration: 5, durationCallback: function (target, source) { if (source.hasItem('gripclaw')) return 8; return this.random(5, 7); }, onStart: function (pokemon, source) { this.add('-activate', pokemon, 'move: ' + this.effectData.sourceEffect, '[of] ' + source); }, onResidualOrder: 11, onResidual: function (pokemon) { if (this.effectData.source && (!this.effectData.source.isActive || this.effectData.source.hp <= 0)) { delete pokemon.volatiles['partiallytrapped']; return; } if (this.effectData.source.hasItem('bindingband')) { this.damage(pokemon.maxhp / 6); } else { this.damage(pokemon.maxhp / 8); } }, onEnd: function (pokemon) { this.add('-end', pokemon, this.effectData.sourceEffect, '[partiallytrapped]'); }, onModifyPokemon: function (pokemon) { pokemon.tryTrap(); } }, lockedmove: { // Outrage, Thrash, Petal Dance... duration: 2, onResidual: function (target) { if (target.status === 'slp') { // don't lock, and bypass confusion for calming delete target.volatiles['lockedmove']; } this.effectData.trueDuration--; }, onStart: function (target, source, effect) { this.effectData.trueDuration = this.random(2, 4); this.effectData.move = effect.id; }, onRestart: function () { if (this.effectData.trueDuration >= 2) { this.effectData.duration = 2; } }, onEnd: function (target) { if (this.effectData.trueDuration > 1) return; target.addVolatile('confusion'); }, onLockMove: function (pokemon) { return this.effectData.move; } }, twoturnmove: { // Skull Bash, SolarBeam, Sky Drop... duration: 2, onStart: function (target, source, effect) { this.effectData.move = effect.id; // source and target are reversed since the event target is the // pokemon using the two-turn move this.effectData.targetLoc = this.getTargetLoc(source, target); target.addVolatile(effect.id, source); }, onEnd: function (target) { target.removeVolatile(this.effectData.move); }, onLockMove: function () { return this.effectData.move; }, onLockMoveTarget: function () { return this.effectData.targetLoc; } }, choicelock: { onStart: function (pokemon) { if (!this.activeMove.id || this.activeMove.sourceEffect && this.activeMove.sourceEffect !== this.activeMove.id) return false; this.effectData.move = this.activeMove.id; }, onDisableMove: function (pokemon) { if (!pokemon.getItem().isChoice || !pokemon.hasMove(this.effectData.move)) { pokemon.removeVolatile('choicelock'); return; } if (pokemon.ignore['Item']) { return; } var moves = pokemon.moveset; for (var i = 0; i < moves.length; i++) { if (moves[i].id !== this.effectData.move) { pokemon.disableMove(moves[i].id, false, this.effectData.sourceEffect); } } } }, mustrecharge: { duration: 2, onBeforeMovePriority: 11, onBeforeMove: function (pokemon) { this.add('cant', pokemon, 'recharge'); pokemon.removeVolatile('mustrecharge'); return false; }, onLockMove: function (pokemon) { this.add('-mustrecharge', pokemon); return 'recharge'; } }, futuremove: { // this is a side condition onStart: function (side) { this.effectData.positions = []; for (var i = 0; i < side.active.length; i++) { this.effectData.positions[i] = null; } }, onResidualOrder: 3, onResidual: function (side) { var finished = true; for (var i = 0; i < side.active.length; i++) { var posData = this.effectData.positions[i]; if (!posData) continue; posData.duration--; if (posData.duration > 0) { finished = false; continue; } // time's up; time to hit! :D var target = side.foe.active[posData.targetPosition]; var move = this.getMove(posData.move); if (target.fainted) { this.add('-hint', '' + move.name + ' did not hit because the target is fainted.'); this.effectData.positions[i] = null; continue; } this.add('-end', target, 'move: ' + move.name); target.removeVolatile('Protect'); target.removeVolatile('Endure'); if (posData.moveData.ignoreImmunity === undefined) { posData.moveData.ignoreImmunity = false; } if (target.hasAbility('wonderguard') && this.gen > 5) { this.debug('Wonder Guard immunity: ' + move.id); if (target.runEffectiveness(move) <= 0) { this.add('-activate', target, 'ability: Wonder Guard'); this.effectData.positions[i] = null; return null; } } // Prior to gen 5, these moves had no STAB and no effectiveness. // This is done here and to moveData's type for two reasons: // - modifyMove event happens before the moveHit function is run. // - moveData here is different from move, as one is generated here and the other by the move itself. // So here we centralise any future hit move getting typeless on hit as it should be. if (this.gen < 5) { posData.moveData.type = '???'; } this.moveHit(target, posData.source, move, posData.moveData); this.effectData.positions[i] = null; } if (finished) { side.removeSideCondition('futuremove'); } } }, stall: { // Protect, Detect, Endure counter duration: 2, counterMax: 729, onStart: function () { this.effectData.counter = 3; }, onStallMove: function () { // this.effectData.counter should never be undefined here. // However, just in case, use 1 if it is undefined. var counter = this.effectData.counter || 1; this.debug("Success chance: " + Math.round(100 / counter) + "%"); return (this.random(counter) === 0); }, onRestart: function () { if (this.effectData.counter < this.effect.counterMax) { this.effectData.counter *= 3; } this.effectData.duration = 2; } }, gem: { duration: 1, affectsFainted: true, onBasePower: function (basePower, user, target, move) { this.debug('Gem Boost'); return this.chainModify([0x14CD, 0x1000]); } }, aura: { duration: 1, onBasePowerPriority: 8, onBasePower: function (basePower, user, target, move) { var modifier = 0x1547; this.debug('Aura Boost'); if (user.volatiles['aurabreak']) { modifier = 0x0C00; this.debug('Aura Boost reverted by Aura Break'); } return this.chainModify([modifier, 0x1000]); } }, // weather is implemented here since it's so important to the game raindance: { effectType: 'Weather', duration: 5, durationCallback: function (source, effect) { if (source && source.hasItem('damprock')) { return 8; } return 5; }, onBasePower: function (basePower, attacker, defender, move) { if (move.type === 'Water') { this.debug('rain water boost'); return this.chainModify(1.5); } if (move.type === 'Fire') { this.debug('rain fire suppress'); return this.chainModify(0.5); } }, onStart: function (battle, source, effect) { if (effect && effect.effectType === 'Ability' && this.gen <= 5) { this.effectData.duration = 0; this.add('-weather', 'RainDance', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-weather', 'RainDance'); } }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'RainDance', '[upkeep]'); this.eachEvent('Weather'); }, onEnd: function () { this.add('-weather', 'none'); } }, primordialsea: { effectType: 'Weather', duration: 0, onTryMove: function (target, source, effect) { if (effect.type === 'Fire' && effect.category !== 'Status') { this.debug('Primordial Sea fire suppress'); this.add('-fail', source, effect, '[from] Primordial Sea'); return null; } }, onBasePower: function (basePower, attacker, defender, move) { if (move.type === 'Water') { this.debug('Rain water boost'); return this.chainModify(1.5); } }, onStart: function () { this.add('-weather', 'PrimordialSea'); }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'PrimordialSea', '[upkeep]'); this.eachEvent('Weather'); }, onEnd: function () { this.add('-weather', 'none'); } }, sunnyday: { effectType: 'Weather', duration: 5, durationCallback: function (source, effect) { if (source && source.hasItem('heatrock')) { return 8; } return 5; }, onBasePower: function (basePower, attacker, defender, move) { if (move.type === 'Fire') { this.debug('Sunny Day fire boost'); return this.chainModify(1.5); } if (move.type === 'Water') { this.debug('Sunny Day water suppress'); return this.chainModify(0.5); } }, onStart: function (battle, source, effect) { if (effect && effect.effectType === 'Ability' && this.gen <= 5) { this.effectData.duration = 0; this.add('-weather', 'SunnyDay', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-weather', 'SunnyDay'); } }, onImmunity: function (type) { if (type === 'frz') return false; }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'SunnyDay', '[upkeep]'); this.eachEvent('Weather'); }, onEnd: function () { this.add('-weather', 'none'); } }, desolateland: { effectType: 'Weather', duration: 0, onTryMove: function (target, source, effect) { if (effect.type === 'Water' && effect.category !== 'Status') { this.debug('Desolate Land water suppress'); this.add('-fail', source, effect, '[from] Desolate Land'); return null; } }, onBasePower: function (basePower, attacker, defender, move) { if (move.type === 'Fire') { this.debug('Sunny Day fire boost'); return this.chainModify(1.5); } }, onStart: function () { this.add('-weather', 'DesolateLand'); }, onImmunity: function (type) { if (type === 'frz') return false; }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'DesolateLand', '[upkeep]'); this.eachEvent('Weather'); }, onEnd: function () { this.add('-weather', 'none'); } }, sandstorm: { effectType: 'Weather', duration: 5, durationCallback: function (source, effect) { if (source && source.hasItem('smoothrock')) { return 8; } return 5; }, // This should be applied directly to the stat before any of the other modifiers are chained // So we give it increased priority. onModifySpDPriority: 10, onModifySpD: function (spd, pokemon) { if (pokemon.hasType('Rock') && this.isWeather('sandstorm')) { return this.modify(spd, 1.5); } }, onStart: function (battle, source, effect) { if (effect && effect.effectType === 'Ability' && this.gen <= 5) { this.effectData.duration = 0; this.add('-weather', 'Sandstorm', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-weather', 'Sandstorm'); } }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'Sandstorm', '[upkeep]'); if (this.isWeather('sandstorm')) this.eachEvent('Weather'); }, onWeather: function (target) { this.damage(target.maxhp / 16); }, onEnd: function () { this.add('-weather', 'none'); } }, hail: { effectType: 'Weather', duration: 5, durationCallback: function (source, effect) { if (source && source.hasItem('icyrock')) { return 8; } return 5; }, onStart: function (battle, source, effect) { if (effect && effect.effectType === 'Ability' && this.gen <= 5) { this.effectData.duration = 0; this.add('-weather', 'Hail', '[from] ability: ' + effect, '[of] ' + source); } else { this.add('-weather', 'Hail'); } }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'Hail', '[upkeep]'); if (this.isWeather('hail')) this.eachEvent('Weather'); }, onWeather: function (target) { this.damage(target.maxhp / 16); }, onEnd: function () { this.add('-weather', 'none'); } }, deltastream: { effectType: 'Weather', duration: 0, onEffectiveness: function (typeMod, target, type, move) { if (move && move.effectType === 'Move' && type === 'Flying' && typeMod > 0) { this.add('-activate', '', 'deltastream'); return 0; } }, onStart: function () { this.add('-weather', 'DeltaStream'); }, onResidualOrder: 1, onResidual: function () { this.add('-weather', 'DeltaStream', '[upkeep]'); this.eachEvent('Weather'); }, onEnd: function () { this.add('-weather', 'none'); } }, arceus: { // Arceus's actual typing is implemented here // Arceus's true typing for all its formes is Normal, and it's only // Multitype that changes its type, but its formes are specified to // be their corresponding type in the Pokedex, so that needs to be // overridden. This is mainly relevant for Hackmons and Balanced // Hackmons. onSwitchInPriority: 101, onSwitchIn: function (pokemon) { var type = 'Normal'; if (pokemon.ability === 'multitype') { type = this.runEvent('Plate', pokemon); if (!type || type === true) { type = 'Normal'; } } pokemon.setType(type, true); } } };