require '../helpers'

{Attachment, Status} = require('../../server/bw/attachment')
{Battle} = require('../../server/bw/battle')
{Pokemon} = require('../../server/bw/pokemon')
{Weather} = require('../../shared/weather')
{Move} = require('../../server/bw/move')
util = require '../../server/bw/util'
{Protocol} = require '../../shared/protocol'
{Factory} = require '../factory'
should = require 'should'
{_} = require 'underscore'
shared = require '../shared'

describe 'Move:', ->
  # Test every single move for their primary effects.
  shared.testEveryMove(Battle::MoveList, 'bw')

  it "multiplies critical hits by 2x", ->
    Move::criticalMultiplier.should.equal(2)

  it 'takes the name as the first parameter', ->
    new Move('Smooth Move').name.should.equal 'Smooth Move'

  it 'should have a priority of 0 by default', ->
    new Move().priority.should.equal 0

  it 'can change the default priority', ->
    new Move(null, priority: -1).priority.should.equal -1

  it 'should have a type of ??? by default', ->
    new Move().type.should.equal '???'

  it 'can change the default type', ->
    new Move(null, type: 'Grass').type.should.equal 'Grass'

  describe 'critical hit level', ->
    it "is 1 by default", ->
      battle = new Battle('1', [])
      attacker = new Pokemon()
      defender = new Pokemon()
      attacker.activate()
      new Move().criticalHitLevel(battle, attacker, defender).should.equal 1

    it "can be changed from the default", ->
      battle = new Battle('1', [])
      attacker = new Pokemon()
      defender = new Pokemon()
      attacker.activate()
      new Move(null, criticalHitLevel: 2)
        .criticalHitLevel(battle, attacker, defender).should.equal 2

    it "is 3 if the attacker has Focus Energy", ->
      battle = new Battle('1', [])
      attacker = new Pokemon()
      defender = new Pokemon()
      attacker.attach(Attachment.FocusEnergy)
      attacker.activate()
      new Move().criticalHitLevel(battle, attacker, defender).should.equal 3

    it "is 3 if the attacker is Farfetch'd with a Stick", ->
      battle = new Battle('1', [])
      attacker = new Pokemon(species: "Farfetch'd", item: 'Stick')
      defender = new Pokemon()
      attacker.activate()
      new Move().criticalHitLevel(battle, attacker, defender).should.equal 3

    it "is 3 if the attacker is Chansey with a Lucky Punch", ->
      battle = new Battle('1', [])
      attacker = new Pokemon(species: "Chansey", item: 'Lucky Punch')
      defender = new Pokemon()
      attacker.activate()
      new Move().criticalHitLevel(battle, attacker, defender).should.equal 3

    for item in ["Razor Claw", "Scope Lens"]
      do (item) ->
        it "is 2 if the attacker has a #{item}", ->
          battle = new Battle('1', [])
          attacker = new Pokemon(item: item)
          defender = new Pokemon()
          attacker.activate()
          new Move().criticalHitLevel(battle, attacker, defender).should.equal 2

  describe "a critical hit", ->
    it "occurs when the RNG output < the critical hit level", ->
      battle = new Battle('1', [])
      attacker = new Pokemon()
      defender = new Pokemon()
      move = new Move()
      @sandbox.stub(move, 'criticalHitLevel', -> 3)
      @sandbox.stub(battle.rng, 'next', -> 0.2)
      move.isCriticalHit(battle, attacker, defender).should.be.true

    it "does not occur when the RNG output >= the critical hit level", ->
      battle = new Battle('1', [])
      attacker = new Pokemon()
      defender = new Pokemon()
      @sandbox.stub(battle.rng, 'next', -> 0.0700)
      new Move().isCriticalHit(battle, attacker, defender).should.be.false

    it "ignores opponent defensive boosts", ->
      shared.create.call(this)
      defense = @p2.stat('defense')
      specialDefense = @p2.stat('specialDefense')

      @p1.crit = true
      @p2.boost(defense: 1, specialDefense: 2)
      move = new Move(null, damage: 'physical')
      move.pickDefenseStat(@p1, @p2).should.equal(defense)
      move = new Move(null, damage: 'special')
      move.pickDefenseStat(@p1, @p2).should.equal(specialDefense)

    it "does not ignore opponent defensive drops", ->
      shared.create.call(this)
      defense = @p2.stat('defense')
      specialDefense = @p2.stat('specialDefense')

      @p1.crit = true
      @p2.boost(defense: -1, specialDefense: -2)
      move = new Move(null, damage: 'physical')
      move.pickDefenseStat(@p1, @p2).should.equal Math.floor(2 / 3 * defense)
      move = new Move(null, damage: 'special')
      move.pickDefenseStat(@p1, @p2).should.equal(specialDefense >> 1)

    it "ignores user stat drops", ->
      shared.create.call(this)
      attack = @p1.stat('attack')
      specialAttack = @p1.stat('specialAttack')

      @p1.crit = true
      @p1.boost(attack: -1, specialAttack: -2)
      move = new Move(null, damage: 'physical')
      move.pickAttackStat(@p1, @p2).should.equal(attack)
      move = new Move(null, damage: 'special')
      move.pickAttackStat(@p1, @p2).should.equal(specialAttack)

    it "does not ignore user stat boosts", ->
      shared.create.call(this)
      attack = @p1.stat('attack')
      specialAttack = @p1.stat('specialAttack')

      @p1.crit = true
      @p1.boost(attack: 1, specialAttack: 2)
      move = new Move(null, damage: 'physical')
      move.pickAttackStat(@p1, @p2).should.equal Math.floor(1.5 * attack)
      move = new Move(null, damage: 'special')
      move.pickAttackStat(@p1, @p2).should.equal(2 * specialAttack)

    it "does not carry over in multihit moves", ->
      shared.create.call(this)
      move = new Move("multihit", minHits: 4, maxHits: 4, power: 20)

      # Crit only once
      move.isCriticalHit = ->
        move.isCriticalHit = -> false
        true

      mock = @sandbox.mock(@p2).expects('informCriticalHit').once()
      move.execute(@battle, @p1, [ @p2 ])
      mock.verify()

    describe "-1 crit ratios", ->
      beforeEach ->
        @battle = new Battle('1', [])
        @attacker = new Pokemon()
        @move = new Move("TestMove", criticalHitLevel: -1)
        @sandbox.stub(@battle.rng, 'next', -> 1)

      it "fails if the defender has Battle Armor", ->
        defender = new Pokemon(ability: 'Battle Armor')
        @move.isCriticalHit(@battle, @attacker, defender).should.be.false

      it "fails if the defender has Shell Armor", ->
        defender = new Pokemon(ability: 'Shell Armor')
        @move.isCriticalHit(@battle, @attacker, defender).should.be.false

      it "always succeeds if opponent does not have Battle or Shell Armor", ->
        defender = new Pokemon()
        @move.isCriticalHit(@battle, @attacker, defender).should.be.true

  describe 'type effectiveness', ->
    testTypeEffectiveness = (type, defense, expected) ->
      battle = new Battle('1', [])
      attacker = new Pokemon()
      defender = new Pokemon()
      defender.types = defense
      move = new Move(null, type: type)
      move.typeEffectiveness(battle, attacker, defender).should.equal expected

    it 'is 0 if the enemy has an immunity to the type', ->
      testTypeEffectiveness('Ground', ['Electric', 'Flying'], 0)

    it 'is 1 if the enemy is neutral to the type', ->
      testTypeEffectiveness('Ground', ['Normal'], 1)

    it 'is 2 if the enemy is weak to the type', ->
      testTypeEffectiveness('Ground', ['Electric'], 2)

    it 'is 4 if the enemy is extra weak to the type', ->
      testTypeEffectiveness('Rock', ['Fire', 'Flying'], 4)

    it 'is 1/2 if the enemy is resistant to the type', ->
      testTypeEffectiveness('Fire', ['Fire', 'Flying'], .5)

    it 'is 1/4 if the enemy is extra resistant to the type', ->
      testTypeEffectiveness('Fire', ['Fire', 'Water'], .25)

  describe "#burnCalculation", ->
    it "returns 1 normally", ->
      attacker = new Pokemon()
      new Move(null, damage: 'physical')
        .burnCalculation(attacker).should.equal 1

    it "returns .5 with a physical move, no Guts ability, and a burned user", ->
      attacker = new Pokemon()
      attacker.attach(Status.Burn)
      new Move(null, damage: 'physical')
        .burnCalculation(attacker).should.equal .5

    it "returns 1 with a special move, no Guts ability, and a burned user", ->
      attacker = new Pokemon()
      attacker.attach(Status.Burn)
      new Move(null, damage: 'special')
        .burnCalculation(attacker).should.equal 1

    it "returns 1 with a physical move, Guts ability, and a burned user", ->
      attacker = new Pokemon(ability: "Guts")
      attacker.attach(Status.Burn)
      new Move(null, damage: 'physical')
        .burnCalculation(attacker).should.equal 1

  describe 'an attack with 0 accuracy', ->
    it 'can never miss', ->
      battle = new Battle('1', [])
      a = new Pokemon()
      d = new Pokemon()
      new Move(accuracy: 0).willMiss(battle, a, d).should.be.false

  describe 'accuracy and evasion boosts', ->
    it 'heighten and lower the chances of a move hitting', ->
      battle = new Battle('1', [])
      a = new Pokemon()
      d = new Pokemon()
      new Move(null, accuracy: 100).chanceToHit(battle, a, d).should.eql 100

      a.stages.accuracy = 3
      new Move(null, accuracy: 50).chanceToHit(battle, a, d).should.eql 100

      a.stages.accuracy = 3
      d.stages.evasion = 3
      new Move(null, accuracy: 50).chanceToHit(battle, a, d).should.eql 50

    it 'keeps heightening and lowering chances when negative', ->
      battle = new Battle('1', [])
      a = new Pokemon()
      d = new Pokemon()
      new Move(null, accuracy: 100).chanceToHit(battle, a, d).should.eql 100

      a.stages.accuracy = -3
      new Move(null, accuracy: 50).chanceToHit(battle, a, d).should.eql 25

      a.stages.accuracy = -3
      d.stages.evasion = -3
      new Move(null, accuracy: 50).chanceToHit(battle, a, d).should.eql 50

  describe '#hasFlag', ->
    it 'returns true if a move has a specific flag', ->
      new Move(null, flags: ['superman', 'batman'])
        .hasFlag('batman').should.be.true

    it "returns false if a move doesn't have a specific flag", ->
      new Move(null, flags: ['superman', 'batman'])
        .hasFlag('catwoman').should.be.false

  describe '#execute', ->
    it 'calls hit multiple times for multihit moves', ->
      shared.create.call(this)
      move = new Move("multihit", minHits: 4, maxHits: 4)
      mock = @sandbox.mock(move).expects('hit').exactly(4)

      move.execute(@battle, @p1, [@p2])
      mock.verify()

    it 'only hits up until user faints', ->
      shared.create.call(this, team2: [Factory('Ferrothorn', ability: 'Iron Barbs')])
      move = new Move("multihit", minHits: 4, maxHits: 4, flags: ["contact"])
      mock = @sandbox.mock(move).expects('hit').exactly(1)

      @p1.currentHP = 1
      move.execute(@battle, @p1, [@p2])
      mock.verify()

    it 'only hits up until user falls asleep', ->
      shared.create.call(this, team2: [Factory('Shroomish', ability: 'Effect Spore')])
      shared.biasRNG.call(this, "randInt", 'effect spore', 4)  # Never trigger
      move = new Move("multihit", minHits: 4, maxHits: 4, flags: ["contact"])

      hit = move.hit
      times = 0
      @sandbox.stub move, 'hit', =>
        times++
        if times == 2
          shared.biasRNG.call(this, "randInt", 'effect spore', 1)  # Sleep
        else
          shared.biasRNG.call(this, "randInt", 'effect spore', 4)  # Nothing
        hit.apply(move, arguments)

      @p1.currentHP = 1
      move.execute(@battle, @p1, [@p2])
      times.should.equal(2)

  describe "#use", ->
    it "returns false if the target's type is immune", ->
      shared.create.call(this)
      move = new Move("attacking", damage: "special", type: "Normal")
      @p2.types = [ "Ghost" ]
      move.use(@battle, @p1, @p2).should.be.false

    it "returns true if target is immune but move is non-damaging", ->
      shared.create.call(this)
      move = new Move("attacking", damage: "non-damaging", type: "Normal")
      @p2.types = [ "Ghost" ]
      should.not.exist move.use(@battle, @p1, @p2)

  describe '#hit', ->
    it "deals damage no higher than the pokemon's remaining HP", ->
      shared.create.call(this)
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Tackle"))
      @p2.currentHP.should.equal(0)

    it "deals normal damage if the Pokemon has a substitute", ->
      shared.create.call(this)
      spy = @sandbox.spy(@p2, 'damage')

      @p2.currentHP = (@p2.currentHP >> 2) + 1
      @battle.performMove(@p2, @battle.getMove("Substitute"))
      @battle.performMove(@p1, @battle.getMove("Tackle"))
      spy.returned(1).should.be.false

  describe '#weatherModifier', ->
    it "is 0x1800 when the move is Fire-type and the weather is sunny", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SUN)
      move = new Move(null, type: "Fire")
      move.weatherModifier(@battle, @p1, @p2).should.equal(0x1800)

    it "is 0x800 when the move is Fire-type and the weather is rainy", ->
      shared.create.call(this)
      @battle.setWeather(Weather.RAIN)
      move = new Move(null, type: "Fire")
      move.weatherModifier(@battle, @p1, @p2).should.equal(0x800)

    it "is 0x1800 when the move is Water-type and the weather is rainy", ->
      shared.create.call(this)
      @battle.setWeather(Weather.RAIN)
      move = new Move(null, type: "Water")
      move.weatherModifier(@battle, @p1, @p2).should.equal(0x1800)

    it "is 0x1800 when the move is Water-type and the weather is sunny", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SUN)
      move = new Move(null, type: "Water")
      move.weatherModifier(@battle, @p1, @p2).should.equal(0x800)

    it "is 0x1000 otherwise", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SAND)
      move = new Move(null, type: "Fire")
      move.weatherModifier(@battle, @p1, @p2).should.equal(0x1000)

  describe "#baseDamage", ->
    it "reduces damage by ~1.5 if the weather is sandstorm", ->
      shared.create.call(this)
      @p2.types = [ "Rock" ]
      move = new Move(null, power: 50, type: "Fire", damage: 'special')
      damage = move.baseDamage(@battle, @p1, @p2)
      @battle.setWeather(Weather.SAND)
      move.baseDamage(@battle, @p1, @p2).should.be.lessThan(damage)

    it "doesn't apply the sandstorm effect if target isn't Rock-type", ->
      shared.create.call(this)
      @p2.types = [ "Ground" ]
      move = new Move(null, power: 50, type: "Fire", damage: 'special')
      damage = move.baseDamage(@battle, @p1, @p2)
      @battle.setWeather(Weather.SAND)
      move.baseDamage(@battle, @p1, @p2).should.equal(damage)

    it "doesn't apply the sandstorm effect if move is non-special", ->
      shared.create.call(this)
      @p2.types = [ "Rock" ]
      move = new Move(null, power: 50, type: "Fire", damage: 'physical')
      damage = move.baseDamage(@battle, @p1, @p2)
      @battle.setWeather(Weather.SAND)
      move.baseDamage(@battle, @p1, @p2).should.equal(damage)

describe "BW Moves:", ->
  describe "Splash", ->
    shared.shouldDoNoDamage('Splash')

  describe 'a multi-hit move', ->
    it 'potentially activates Berries after each hit', ->
      shared.create.call this,
        team1: [Factory('Magikarp', item: "Sitrus Berry", ability: "Skill Link")]
        team2: [Factory('Magikarp', item: "Sitrus Berry", ability: "Iron Barbs")]
      hp = @p1.currentHP
      @p1.currentHP = (hp >> 1) + 1
      @p2.currentHP = 4
      move = @battle.getMove('Tail Slap')
      @sandbox.stub(move, 'calculateDamage', -> 1)
      @battle.performMove(@p1, move)
      @p1.currentHP.should.equal (hp >> 1) + 1 - (5 * (hp >> 3)) + (hp >> 2)
      @p2.currentHP.should.equal 4 + (hp >> 2) - 5

  describe 'jump kick attacks', ->
    it 'has 50% recoil if it misses', ->
      shared.create.call(this)
      shared.biasRNG.call(this, "randInt", 'miss', 100)
      oldHP = @p1.stat('hp')
      @battle.performMove(@p1, @battle.getMove('Hi Jump Kick'))
      (oldHP - @p1.currentHP).should.equal Math.floor(oldHP / 2)

    it "has 50% recoil if the target is immune", ->
      shared.create.call(this, team2: [Factory("Gengar")])
      oldHP = @p1.stat('hp')
      @battle.performMove(@p1, @battle.getMove('Hi Jump Kick'))
      (oldHP - @p1.currentHP).should.equal Math.floor(oldHP / 2)

    it "has 50% recoil if the target protects itself", ->
      shared.create.call(this)
      oldHP = @p1.stat('hp')
      @battle.recordMove(@id2, @battle.getMove("Protect"))
      @battle.recordMove(@id1, @battle.getMove("Hi Jump Kick"))
      @battle.continueTurn()
      (oldHP - @p1.currentHP).should.equal Math.floor(oldHP / 2)

  describe 'a drain attack', ->
    it 'recovers a percentage of the damage dealt, rounded down', ->
      shared.create.call this,
        team1: [Factory('Conkeldurr')]
        team2: [Factory('Hitmonchan')]
      startHP = 1
      @p1.currentHP = startHP
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Drain Punch'))
      damage = (hp - @p2.currentHP)
      (@p1.currentHP - startHP).should.equal Math.ceil(damage / 2)

    it 'cannot recover to over 100% HP', ->
      shared.create.call this,
        team1: [Factory('Conkeldurr')]
        team2: [Factory('Hitmonchan')]
      hp = @p1.currentHP = @p1.stat('hp')
      @battle.performMove(@p1, @battle.getMove('Drain Punch'))
      (@p1.currentHP - hp).should.equal 0

    it "does not recover more than the damage dealt", ->
      shared.create.call(this)
      drainPunch = @battle.getMove('Drain Punch')
      @sandbox.stub(drainPunch, 'baseDamage', -> 9999)
      @p2.currentHP = 2
      @p1.currentHP = 1
      @battle.performMove(@p1, drainPunch)
      @p2.currentHP.should.be.lessThan(1)
      @p1.currentHP.should.equal(2)

  describe 'weight-based attacks', ->
    it 'has 80 base power if the pokemon is 50.2kg', ->
      shared.create.call this,
        team1: [Factory('Celebi')]
        team2: [Factory('Hitmonchan')]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Grass Knot'))
      (hp - @p2.currentHP).should.equal 94

    it 'has 120 base power if the pokemon is >200kg', ->
      shared.create.call this,
        team1: [Factory('Celebi')]
        team2: [Factory('Gyarados')]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Grass Knot'))
      (hp - @p2.currentHP).should.equal 153

  describe 'a pokemon using a primary boosting move', ->
    it "doesn't do damage if base power is 0", ->
      shared.create.call this,
        team1: [Factory('Gyarados')]
        team2: [Factory('Hitmonchan')]
      @battle.performMove(@p1, @battle.getMove('Dragon Dance'))
      @p2.currentHP.should.equal @p2.stat('hp')

    it "boosts the pokemon's stats", ->
      shared.create.call this,
        team1: [Factory('Gyarados')]
        team2: [Factory('Hitmonchan')]
      @battle.performMove(@p1, @battle.getMove('Dragon Dance'))
      @p1.stages.should.containEql attack: 1, speed: 1

    it "affects type-immune pokemon", ->
      shared.create.call this,
        team1: [Factory('Audino')]
        team2: [Factory('Gengar')]
      @battle.performMove(@p1, @battle.getMove('Growl'))
      @p2.stages.attack.should.equal -1

    it "has the boosts removed on switch", ->
      shared.create.call(this, team1: (Factory("Magikarp")  for x in [1..2]))
      @battle.performMove(@p1, @battle.getMove('Dragon Dance'))
      @battle.performSwitch(@p1, 1)
      @p1.stages.should.containEql(attack: 0, speed: 0)

  describe 'a pokemon using a damaging move that also boosts stats on hit', ->
    it "deals damage and boosts stats", ->
      shared.create.call this,
        team1: [Factory('Celebi')]
        team2: [Factory('Gyarados')]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Leaf Storm'))
      @p1.stages.specialAttack.should.equal -2
      (hp - @p2.currentHP).should.equal 178

  describe 'Low Sweep', ->
    it "lowers the target's speed by 1", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('Low Sweep'))
      @p2.stages.speed.should.equal(-1)

    it "does not lower the target's speed by 1 if the target faints", ->
      shared.create.call(this)
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Low Sweep'))
      @p2.stages.speed.should.not.equal(-1)

    it "does not lower the target's speed by 1 if it hits a substitute", ->
      shared.create.call(this)
      @p2.attach(Attachment.Substitute)
      @battle.performMove(@p1, @battle.getMove('Low Sweep'))
      @p2.stages.speed.should.not.equal(-1)

  describe 'Flame Charge', ->
    it "raises the user's speed by 1", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('Flame Charge'))
      @p1.stages.speed.should.equal(1)

    it "raises the user's speed by 1 even if the target faints", ->
      shared.create.call(this)
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Flame Charge'))
      @p1.stages.speed.should.equal(1)

    it "raises the user's speed by 1 even if it hits a substitute", ->
      shared.create.call(this)
      @p2.attach(Attachment.Substitute)
      @battle.performMove(@p1, @battle.getMove('Flame Charge'))
      @p1.stages.speed.should.equal(1)

  describe 'a pokemon using a move with a secondary boosting effect', ->
    it "has a chance to activate", ->
      shared.create.call this,
        team1: [Factory('Mew')]
        team2: [Factory('Hitmonchan')]
      shared.biasRNG.call(this, "next", 'secondary boost', 0)  # 100% chance
      attack = @p1.stat('attack')
      speed  = @p1.stat('speed')
      @battle.performMove(@p1, @battle.getMove('AncientPower'))
      @p1.stages.should.containEql {
        attack: 1, defense: 1, speed: 1, specialAttack: 1, specialDefense: 1
      }

  describe 'a pokemon using Acrobatics', ->
    it 'gets double the base power without an item', ->
      shared.create.call this,
        team1: [Factory('Gliscor')]
        team2: [Factory('Regirock')]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Acrobatics'))
      damage = (hp - @p2.currentHP)
      damage.should.equal 36

    it 'has normal base power with an item', ->
      shared.create.call this,
        team1: [Factory('Gliscor', item: 'Leftovers')]
        team2: [Factory('Regirock')]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Acrobatics'))
      damage = (hp - @p2.currentHP)
      damage.should.equal 18

  testRecoilMove = (moveName) ->
    describe "a pokemon using #{moveName}", ->
      it 'receives a percentage of the damage rounded half up', ->
        shared.create.call(this)
        startHP = @p1.currentHP
        hp = @p2.currentHP

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)
        recoil = -move.recoil / 100

        damage = (hp - @p2.currentHP)
        (startHP - @p1.currentHP).should.equal Math.round(damage * recoil)

      it 'receives a minimum of 1 HP of recoil', ->
        shared.create.call(this)
        startHP = @p1.currentHP
        hp = @p2.currentHP

        move = @battle.getMove(moveName)
        stub = @sandbox.stub(move, 'calculateDamage', -> .6)

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        damage = (hp - @p2.currentHP)
        (startHP - @p1.currentHP).should.equal 1

      it "does not recoil if no damage was dealt", ->
        shared.create.call(this)
        @battle.recordMove(@id2, @battle.getMove("Protect"))
        @battle.recordMove(@id1, @battle.getMove(moveName))
        @battle.continueTurn()
        @p1.currentHP.should.equal(@p1.stat('hp'))

  testRecoilMove("Brave Bird")
  testRecoilMove("Double-Edge")
  testRecoilMove("Flare Blitz")
  testRecoilMove("Head Charge")
  testRecoilMove("Head Smash")
  testRecoilMove("Submission")
  testRecoilMove("Take Down")
  testRecoilMove("Volt Tackle")
  testRecoilMove("Wild Charge")
  testRecoilMove("Wood Hammer")

  testDrainMove = (moveName) ->
    describe moveName, ->
      it "absorbs a percentage of the damage rounded up", ->
        shared.create.call(this)
        @p1.currentHP = 1

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)
        drain = move.recoil / 100

        damage = (@p2.stat('hp') - @p2.currentHP)
        (@p1.currentHP - 1).should.equal Math.ceil(damage * drain)

      it 'receives a minimum of 1 HP of drain', ->
        shared.create.call(this)
        @p1.currentHP = @p2.currentHP = 1

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)
        @p1.currentHP.should.equal(2)

      it "does not drain if no damage was dealt", ->
        shared.create.call(this)
        @p1.currentHP = 1
        @battle.recordMove(@id2, @battle.getMove("Protect"))
        @battle.recordMove(@id1, @battle.getMove(moveName))
        @battle.continueTurn()
        @p1.currentHP.should.equal(1)

  testDrainMove("Absorb")
  testDrainMove("Drain Punch")
  testDrainMove("Giga Drain")
  testDrainMove("Horn Leech")
  testDrainMove("Leech Life")
  testDrainMove("Mega Drain")

  describe 'Haze', ->
    shared.shouldDoNoDamage('Haze')

    it 'removes all status boosts from each pokemon', ->
      shared.create.call(this)
      # Create artificial boosts.
      @p1.stages.attack = 2
      @p1.stages.evasion = -1
      @p2.stages.defense = -3
      @p2.stages.specialAttack = 4
      @battle.performMove(@p1, @battle.getMove("Haze"))
      neutralBoosts = {
        attack: 0, defense: 0, specialAttack: 0, specialDefense: 0,
        speed: 0, evasion: 0, accuracy: 0
      }
      @p1.stages.should.eql neutralBoosts
      @p2.stages.should.eql neutralBoosts

  describe 'Seismic Toss and Night Shade', ->
    it 'does exactly the same damage as their level', ->
      shared.create.call this,
        team1: [Factory('Blissey')]
        team2: [Factory('Mew')]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Seismic Toss'))
      (hp - @p2.currentHP).should.equal 100

    it 'triggers Focus Sash', ->
      shared.create.call this,
        team1: [Factory('Blissey')]
        team2: [Factory('Mew', level: 1, item: "Focus Sash")]
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Seismic Toss'))
      @p2.currentHP.should.equal(1)

  describe 'Psywave', ->
    it 'does user.level/2 damage minimum', ->
      shared.create.call this,
        team1: [Factory('Weezing')]
        team2: [Factory('Mew')]
      shared.biasRNG.call(this, "randInt", 'psywave', 5)
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Psywave'))
      (hp - @p2.currentHP).should.equal 50

    it 'does user.level * 1.5 damage maximum', ->
      shared.create.call this,
        team1: [Factory('Weezing')]
        team2: [Factory('Mew')]
      move = @battle.getMove('Psywave')
      shared.biasRNG.call(this, "randInt", 'psywave', 15)
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Psywave'))
      (hp - @p2.currentHP).should.equal 150

    it 'rounds down to the nearest .1 multiplier', ->
      shared.create.call this,
        team1: [Factory('Weezing')]
        team2: [Factory('Mew')]
      move = @battle.getMove('Psywave')
      shared.biasRNG.call(this, "randInt", 'psywave', 6.09)
      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Psywave'))
      (hp - @p2.currentHP).should.equal 60

  describe 'facade', ->
    it 'doubles the base power if burned, poisoned, or paralyzed', ->
      shared.create.call this,
        team1: [Factory('Zangoose')]
        team2: [Factory('Magikarp')]
      hp = @p2.currentHP
      @p1.attach(Status.Paralyze)
      move = @battle.getMove('Facade')
      basePower = move.basePower(@battle, @p1, @p2)
      basePower.should.equal(2 * move.power)

  describe 'reversal and flail', ->
    it 'have 200 base power at 1 hp', ->
      shared.create.call this,
        team1: [Factory('Zangoose')]
        team2: [Factory('Magikarp')]
      @p1.currentHP = 1
      move = @battle.getMove('Flail')
      basePower = move.basePower(@battle, @p1, @p2)
      basePower.should.equal 200

    it 'have 40 base power at 50% hp', ->
      shared.create.call this,
        team1: [Factory('Zangoose')]
        team2: [Factory('Magikarp')]
      @p1.currentHP = Math.floor(@p1.stat('hp') / 2)
      move = @battle.getMove('Flail')
      basePower = move.basePower(@battle, @p1, @p2)
      basePower.should.equal 40

  describe 'eruption and water spout', ->
    beforeEach ->
      shared.create.call this,
        team1: [Factory('Camerupt')]
        team2: [Factory('Mew')]
      @attacker = @p1
      @defender = @p2
      @move = @battle.getMove('Eruption')

    it 'has at least one base power', ->
      @attacker.currentHP = 1
      @move.basePower(@battle, @attacker, @defender).should.equal 1

    it 'has 75 base power at 50% hp', ->
      @attacker.currentHP = @attacker.stat('hp') / 2
      @move.basePower(@battle, @attacker, @defender).should.equal 75

    it 'has 150 base power at 100% hp', ->
      @move.basePower(@battle, @attacker, @defender).should.equal 150

  describe 'gyro ball', ->
    it 'has 150 base power maximum', ->
      shared.create.call this,
        team1: [Factory('Forretress', ivs: {speed: 0})]
        team2: [Factory('Jolteon', evs: {speed: 252}, nature: "Timid")]
      move = @battle.getMove('Gyro Ball')
      attacker = @p1
      defender = @p2
      attacker.stages.speed = -6
      move.basePower(@battle, attacker, defender).should.equal 150

    it 'has variable base power based on speed of target and user', ->
      shared.create.call this,
        team1: [Factory('Electrode', evs: {speed: 252}, nature: "Jolly")]
        team2: [Factory('Magikarp', ivs: {speed: 0})]
      move = @battle.getMove('Gyro Ball')
      attacker = @p1
      defender = @p2
      attacker.stages.speed = -6
      move.basePower(@battle, attacker, defender).should.equal 40

  describe 'brine', ->
    it 'has normal base power if the target has over 50% HP', ->
      shared.create.call this,
        team1: [Factory('Empoleon')]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Brine')
      @p2.currentHP = Math.floor(@p2.currentHP / 2) + 1
      move.basePower(@battle, @p1, @p2).should.equal 65

    it 'doubles base power if the target has 50% or less HP', ->
      shared.create.call this,
        team1: [Factory('Empoleon')]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Brine')
      @p2.currentHP = Math.floor(@p2.currentHP / 2)
      move.basePower(@battle, @p1, @p2).should.equal 130

  describe 'Disable', ->
    shared.shouldDoNoDamage('Disable')

    it 'gives the disabled attachment', ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")
      move = @p2.moves[0]

      @battle.performMove(@p2, move)
      @battle.performMove(@p1, disable)
      @p2.has(Attachment.Disable).should.be.true

    it "prevents the target's last move from being selected", ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")
      move = @p2.moves[0]

      @battle.performMove(@p2, move)
      @battle.performMove(@p1, disable)
      @battle.beginTurn()

      @p2.validMoves().should.not.containEql(move)
      @p2.validMoves().should.have.length(@p2.moves.length - 1)

    it 'wears off after 4 turns', ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")
      move = @p2.moves[0]

      @battle.performMove(@p2, move)
      @battle.performMove(@p1, disable)

      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()

      @p2.has(Attachment.Disable).should.be.false

    it 'stops the execution of a disabled move', ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")
      move = @p2.moves[0]

      @battle.performMove(@p2, move)
      @battle.performMove(@p1, disable)
      mock = @sandbox.mock(move).expects('execute').never()
      @battle.performMove(@p2, move)
      mock.verify()

    it 'fails if the target has not moved since it was active', ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")

      mock = @sandbox.mock(disable).expects('fail').once()
      @battle.performMove(@p1, disable)
      mock.verify()

    it 'fails if the target does not know the move it used', ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")
      struggle = @battle.getMove("Struggle")

      mock = @sandbox.mock(disable).expects('fail').once()
      @battle.performMove(@p2, struggle)
      @battle.performMove(@p1, disable)
      mock.verify()

    it "fails if the target's last move has 0 PP", ->
      shared.create.call(this)
      disable = @battle.getMove("Disable")
      splash = @battle.getMove("Splash")

      @battle.performMove(@p2, splash)
      @p2.setPP(splash, 0)
      mock = @sandbox.mock(disable).expects('fail').once()
      @battle.performMove(@p1, disable)
      mock.verify()

  describe 'hidden power', ->
    it 'has a max power of 70', ->
      ivs = {
        hp: 31, attack: 31, defense: 31,
        specialAttack: 31, specialDefense: 31, speed: 31
      }
      shared.create.call this,
        team1: [Factory('Magikarp', ivs: ivs)]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Hidden Power')
      move.basePower(@battle, @p1, @p2).should.eql 70

    it 'has a min power of 30', ->
      ivs = {
        hp: 0, attack: 0, defense: 0,
        specialAttack: 0, specialDefense: 0, speed: 0
      }
      shared.create.call this,
        team1: [Factory('Magikarp', ivs: ivs)]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Hidden Power')
      move.basePower(@battle, @p1, @p2).should.eql 30

    it 'is dark when all ivs are 31', ->
      ivs = {
        hp: 31, attack: 31, defense: 31,
        specialAttack: 31, specialDefense: 31, speed: 31
      }
      shared.create.call this,
        team1: [Factory('Magikarp', ivs: ivs)]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Hidden Power')
      move.getType(@battle, @p1, @p2).should.eql 'Dark'

    it 'is fighting when all ivs are 0', ->
      ivs = {
        hp: 0, attack: 0, defense: 0,
        specialAttack: 0, specialDefense: 0, speed: 0
      }
      shared.create.call this,
        team1: [Factory('Magikarp', ivs: ivs)]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Hidden Power')
      move.getType(@battle, @p1, @p2).should.eql 'Fighting'

  describe 'yawn', ->
    shared.shouldDoNoDamage('Yawn')
    shared.shouldFailIfUsedTwice("Yawn")

    it 'gives the yawn attachment', ->
      shared.create.call this,
        team1: [Factory('Camerupt')]
        team2: [Factory('Magikarp')]
      @battle.performMove(@p1, @battle.getMove('Yawn'))
      @p2.has(Attachment.Yawn).should.be.true

    it 'puts the opponent to sleep at the end of the second turn', ->
      shared.create.call this,
        team1: [Factory('Camerupt')]
        team2: [Factory('Magikarp')]
      @battle.performMove(@p1, @battle.getMove('Yawn'))
      @battle.endTurn()
      @p2.has(Status.Sleep).should.be.false
      @battle.endTurn()
      @p2.has(Status.Sleep).should.be.true

    it "fails if the target already is statused", ->
      shared.create.call(this)
      yawn = @battle.getMove('Yawn')
      mock = @sandbox.mock(yawn).expects('fail').once()

      @p2.attach(Status.Paralyze)
      @battle.performMove(@p1, yawn)
      mock.verify()

  describe 'an OHKO move', ->
    it 'ignores accuracy/evasion modifiers', ->
      shared.create.call(this)
      @p1.stages.accuracy = -6
      @p2.stages.evasion = 6
      acc = @battle.getMove('Sheer Cold').chanceToHit(@battle, @p1, @p2)

      acc.should.equal 30

    it "does damage equal to the target's total hp", ->
      shared.create.call this,
        team1: [Factory('Lapras')]
        team2: [Factory('Magikarp')]
      @p2.currentHP = Math.floor(@p2.stat('hp') / 2)
      @controller.makeMove(@id1, 'Sheer Cold')
      @controller.makeMove(@id2, 'Splash')

      move = @battle.getMove('Sheer Cold')
      damage = move.calculateDamage(@battle, @p1, @p2)
      damage.should.equal @p2.stat('hp')

  describe 'a recovery move', ->
    shared.shouldDoNoDamage('Recover')

    it "recovers 50% of the target's HP, rounded half up", ->
      shared.create.call(this)
      hp = @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Softboiled'))

      recoverHP = Math.round(@p1.stat('hp') / 2)
      (@p1.currentHP - hp).should.equal recoverHP

    it "fails if the user's HP is full", ->
      shared.create.call(this)
      softboiled = @battle.getMove('Softboiled')
      mock = @sandbox.mock(softboiled).expects('fail').once()
      @battle.performMove(@p1, softboiled)
      mock.verify()

  describe 'Knock Off', ->
    it "deals damage", ->
      shared.create.call this,
        team2: [Factory('Magikarp', item: "Leftovers")]
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      @p2.currentHP.should.be.lessThan @p2.stat('hp')

    it "removes the target's item", ->
      shared.create.call this,
        team2: [Factory('Magikarp', item: "Leftovers")]
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      should.not.exist @p2.item

    it "does not remove unremovable items", ->
      shared.create.call this,
        team2: [Factory('Magikarp', item: "Air Mail")]
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      @p2.hasItem().should.be.true

    it "does not knock off items from pokemon with Sticky Hold", ->
      shared.create.call this,
        team2: [Factory('Magikarp', ability: "Sticky Hold", item: "Leftovers")]
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      @p2.hasItem().should.be.true

    it "takes damage from Rocky Helmet", ->
      shared.create.call this,
        team2: [Factory('Magikarp', item: "Rocky Helmet")]
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      @p2.hasItem().should.be.false
      @p1.currentHP.should.be.lessThan(@p1.stat('hp'))

    it "doesn't knock off if substitute fades", ->
      shared.create.call this,
        team2: [Factory('Magikarp', item: "Leftovers")]
      @p2.attach(Attachment.Substitute, hp: 1)
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      @p2.hasItem().should.be.true

    it "doesn't knock off if user is fainted", ->
      shared.create.call this,
        team2: [Factory('Magikarp', item: "Rocky Helmet")]
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Knock Off"))
      @p2.hasItem().should.be.true
      @p1.isFainted().should.be.true

    it 'removes healing Berries before they can activate', ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', item: "Sitrus Berry")]
      @p2.currentHP = 2
      move = @battle.getMove('Knock Off')
      @sandbox.stub(move, 'calculateDamage', -> 1)
      @battle.performMove(@p1, move)
      @p2.currentHP.should.equal 1

  describe 'trick and switcheroo', ->
    shared.shouldDoNoDamage('Trick')

    it "isn't affected by type-immunities", ->
      shared.create.call this,
        team1: [Factory('Alakazam', item: 'Stick')]
        team2: [Factory('Drapion', item: "Leftovers")]
      item1 = @p1.item
      item2 = @p2.item

      trick = @battle.getMove('Trick')
      @battle.performMove(@p1, trick)
      @p2.item.should.equal item1
      @p1.item.should.equal item2

    it "swaps the target and user's item", ->
      shared.create.call this,
        team1: [Factory('Alakazam', item: 'Stick')]
        team2: [Factory('Magikarp', item: "Leftovers")]
      item1 = @p1.item
      item2 = @p2.item
      @controller.makeMove(@id1, 'Trick')
      @controller.makeMove(@id2, 'Splash')

      @p2.item.should.equal item1
      @p1.item.should.equal item2

    it "fails if the user has Sticky Hold", ->
      shared.create.call this,
        team1: [Factory('Alakazam', ability: "Sticky Hold", item: 'Stick')]
        team2: [Factory('Gastrodon', item: "Leftovers")]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the target has Sticky Hold", ->
      shared.create.call this,
        team1: [Factory('Alakazam', item: 'Stick')]
        team2: [Factory('Magikarp', ability: "Sticky Hold", item: "Leftovers")]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "succeeds if only one pokemon has an item", ->
      shared.create.call this,
        team1: [Factory('Alakazam', item: 'Stick')]
        team2: [Factory('Magikarp')]
      trick = @battle.getMove('Trick')
      item1 = @p1.item
      @battle.performMove(@p1, trick)
      should.not.exist(@p1.item)
      @p2.item.should.equal(item1)

    it "fails if neither the user nor the target has an item", ->
      shared.create.call this,
        team1: [Factory('Alakazam')]
        team2: [Factory('Magikarp')]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the user is holding a Mail", ->
      shared.create.call this,
        team1: [Factory('Alakazam', item: 'Air Mail')]
        team2: [Factory('Magikarp')]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the target is holding a Mail", ->
      shared.create.call this,
        team1: [Factory('Alakazam')]
        team2: [Factory('Magikarp', item: 'Air Mail')]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the user is Giratina-O", ->
      shared.create.call this,
        team1: [Factory('Giratina', forme: "origin")]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the target is Giratina-O", ->
      shared.create.call this,
        team2: [Factory('Giratina', forme: "origin")]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the target has a Substitute", ->
      shared.create.call(this)
      trick = @battle.getMove('Trick')
      substitute = @battle.getMove('Substitute')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p2, substitute)
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the user has Multitype with a plate item", ->
      shared.create.call this,
        team1: [Factory('Magikarp', ability: "Multitype", item: "Grass Plate")]
        team2: [Factory('Magikarp')]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the target has Multitype with a plate item", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', ability: "Multitype", item: "Grass Plate")]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the target is Genesect with a Drive item", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Genesect', item: "Burn Drive")]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

    it "fails if the user is Genesect with a Drive item", ->
      shared.create.call this,
        team1: [Factory('Genesect', item: "Burn Drive")]
        team2: [Factory('Magikarp')]
      trick = @battle.getMove('Trick')
      mock = @sandbox.mock(trick).expects('fail').once()
      @battle.performMove(@p1, trick)
      mock.verify()

  describe 'memento', ->
    shared.shouldDoNoDamage('Memento')

    it "faints the user", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Memento"))
      @p1.isFainted().should.be.true

    it "reduces the attack and special attack of the target by two stages", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Memento"))
      @p2.stages.should.containEql attack: -2, specialAttack: -2

    it "doesn't reduce stats if target is protected, and doesn't faint user", ->
      shared.create.call(this)
      @battle.recordMove(@id2, @battle.getMove("Protect"))
      @battle.recordMove(@id1, @battle.getMove("Memento"))
      @battle.continueTurn()
      @p2.stages.should.not.containEql attack: -2, specialAttack: -2
      @p1.isFainted().should.be.false

    it "doesn't reduce stats if target has a substitute, and doesn't faint user", ->
      shared.create.call(this)
      @battle.performMove(@p2, @battle.getMove("Substitute"))
      @battle.performMove(@p1, @battle.getMove("Memento"))
      @p2.stages.should.not.containEql attack: -2, specialAttack: -2
      @p1.isFainted().should.be.false

    it "doesn't reduce stats if target has Clear Body, but faints user", ->
      shared.create.call(this, team2: [Factory('Magikarp', ability: 'Clear Body')])
      @battle.performMove(@p1, @battle.getMove("Memento"))
      @p2.stages.should.not.containEql attack: -2, specialAttack: -2
      @p1.isFainted().should.be.true

  describe 'magnitude', ->
    it "has variable base power", ->
      shared.create.call this,
        team1: [Factory('Dugtrio')]
        team2: [Factory('Magikarp')]
      move = @battle.getMove('Magnitude')
      shared.biasRNG.call(this, "randInt", 'magnitude', 50)
      move.basePower(@battle, @p1, @p2).should.equal 70

  describe 'Pain Split', ->
    it "doesn't make a pokemon's HP go over their max", ->
      shared.create.call this,
        team1: [Factory('Gengar')]
        team2: [Factory('Blissey')]
      @controller.makeMove(@id1, 'Pain Split')
      @controller.makeMove(@id1, 'Seismic Toss')

      @p1.currentHP.should.equal @p1.stat('hp')

    it "averages user and target current HP", ->
      shared.create.call this,
        team1: [Factory('Gengar')]
        team2: [Factory('Blissey')]
      @p1.currentHP = 2
      @controller.makeMove(@id1, 'Pain Split')
      @controller.makeMove(@id2, 'Seismic Toss')

      @p1.currentHP.should.equal Math.min(326, @p1.stat('hp'))
      @p2.currentHP.should.equal Math.min(326, @p2.stat('hp'))

    it "affects Ghosts", ->
      shared.create.call this,
        team2: [Factory('Gengar')]
      painSplit = @battle.getMove("Pain Split")
      mock = @sandbox.mock(painSplit).expects('hit').once()
      @battle.performMove(@p1, painSplit)
      mock.verify()

    it "is blocked by Protect", ->
      shared.create.call(this)
      @p1.currentHP = 1
      painSplit = @battle.getMove("Pain Split")
      mock = @sandbox.mock(painSplit).expects('hit').never()
      @battle.recordMove(@id1, painSplit)
      @battle.determineTurnOrder()
      @battle.performMove(@p2, @battle.getMove("Protect"))
      @battle.performMove(@p1, painSplit)
      mock.verify()
      @p1.currentHP.should.equal(1)
      @p2.currentHP.should.equal(@p2.stat('hp'))

  describe 'Belly Drum', ->
    shared.shouldDoNoDamage('Belly Drum')

    it "maximizes attack", ->
      shared.create.call this,
        team1: [Factory('Poliwrath')]
        team2: [Factory('Magikarp')]
      @p1.stages.attack = -6
      @controller.makeMove(@id1, 'Belly Drum')
      @controller.makeMove(@id2, 'Splash')

      @p1.stages.attack.should.equal 6

    it "cuts the pokemon's HP by half", ->
      shared.create.call this,
        team1: [Factory('Poliwrath')]
        team2: [Factory('Magikarp')]
      @controller.makeMove(@id1, 'Belly Drum')
      @controller.makeMove(@id2, 'Splash')

      hp = @p1.stat('hp')
      (hp - @p1.currentHP).should.equal Math.floor(hp / 2)

    it "fails if the pokemon's HP is less than half", ->
      shared.create.call this,
        team1: [Factory('Poliwrath')]
        team2: [Factory('Magikarp')]
      hp = @p1.currentHP = Math.floor(@p1.stat('hp') / 2) - 1
      @controller.makeMove(@id1, 'Belly Drum')
      @controller.makeMove(@id2, 'Splash')

      @p1.currentHP.should.equal hp
      @p1.stages.attack.should.equal 0

    it "fails if the pokemon's attack is already maximized", ->
      shared.create.call this,
        team1: [Factory('Poliwrath')]
        team2: [Factory('Magikarp')]
      @p1.boost(attack: 12)
      bd = @battle.getMove('Belly Drum')
      mock = @sandbox.mock(bd).expects('fail').once()

      @battle.performMove(@p1, bd)
      mock.verify()
      @p1.currentHP.should.equal(@p1.stat('hp'))

  describe 'acupressure', ->
    shared.shouldDoNoDamage('Acupressure')

    it "raises a random stat that can be raised", ->
      shared.create.call this,
        team1: [Factory('Shuckle')]
        team2: [Factory('Magikarp')]
      stages = _.clone(@p1.stages)
      @controller.makeMove(@id1, 'Acupressure')
      @controller.makeMove(@id2, 'Splash')

      @p1.stages.should.not.eql stages

    it "fails if the Pokemon has maximum stats", ->
      shared.create.call this,
        team1: [Factory('Shuckle')]
        team2: [Factory('Magikarp')]
      @p1.stages.attack = 6
      @p1.stages.defense = 6
      @p1.stages.specialAttack = 6
      @p1.stages.specialDefense = 6
      @p1.stages.accuracy = 6
      @p1.stages.evasion = 6
      mock = @sandbox.mock(@p2)
      mock.expects('boost').never()

      @controller.makeMove(@id1, 'Acupressure')
      @controller.makeMove(@id2, 'Splash')

      mock.verify()

  describe 'Dragon Rage', ->
    it 'always does 40 damage', ->
      shared.create.call(this)
      move = @battle.getMove('Dragon Rage')
      move.calculateDamage(@battle, @p1, @p2).should.equal 40

  describe 'SonicBoom', ->
    it 'always does 20 damage', ->
      shared.create.call(this)
      sonicBoom = @battle.getMove('SonicBoom')
      sonicBoom.calculateDamage(@battle, @p1, @p2).should.equal 20

  describe 'explosion moves', ->
    it 'faints the user', ->
      shared.create.call this,
        team1: [Factory('Gengar')]
        team2: [Factory('Blissey')]
      @battle.performMove(@p1, @battle.getMove('Explosion'))
      @p1.isFainted().should.be.true

    it 'faints the user even if enemy is immune', ->
      shared.create.call this,
        team1: [Factory('Gengar')]
        team2: [Factory('Gengar')]
      @battle.performMove(@p1, @battle.getMove('Explosion'))
      @p1.isFainted().should.be.true

    it 'fails if an active Pokemon has Damp', ->
      shared.create.call this,
        team1: [Factory('Gengar')]
        team2: [Factory('Politoed', ability: 'Damp')]
      explosion = @battle.getMove('Explosion')
      mock = @sandbox.mock(explosion).expects('fail').once()
      @battle.performMove(@p1, explosion)
      mock.verify()

  describe 'endeavor', ->
    it "brings the target's hp down to the user's hp", ->
      shared.create.call(this)
      hp = 4
      @p1.currentHP = hp
      @battle.performMove(@p1, @battle.getMove('Endeavor'))
      @p2.currentHP.should.equal hp

    it "fails if the target's hp is less than the user's hp", ->
      shared.create.call(this)
      move = @battle.getMove('Endeavor')
      mock = @sandbox.mock(move).expects('fail').once()
      @p2.currentHP = hp = 4

      @battle.performMove(@p1, move)
      mock.verify()

    it "doesn't hit ghost pokemon", ->
      shared.create.call this,
        team1: [Factory('Politoed')]
        team2: [Factory('Gengar')]
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Endeavor'))
      @p2.currentHP.should.equal @p2.stat('hp')

  describe 'a thief move', ->
    it "should steal the target's item", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', item: "Leftovers")]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      @p1.item.should.equal item2
      should.not.exist @p2.item

    it "should not steal the target's item if it has none", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp')]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      should.not.exist @p2.item

    it "should not steal the target's item if user already has an item", ->
      shared.create.call this,
        team1: [Factory('Magikarp', item: "Stick")]
        team2: [Factory('Magikarp', item: "Leftovers")]
      item1 = @p1.item
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      @p1.item.should.equal item1
      @p2.item.should.equal item2

    it "should not steal the target's item if target has Sticky Hold", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', item: "Leftovers", ability: "Sticky Hold")]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      @p2.item.should.equal item2

    it "should not steal target's item if target has Multitype and a plate", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', item: "Draco Plate", ability: "Multitype")]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      @p2.item.should.equal item2

    it "should not steal the target's item if the target has no item", ->
      shared.create.call(this)
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      @p1.hasItem().should.be.false
      @p2.hasItem().should.be.false

    it "should not steal the target's item if target is Giratina-O", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Giratina', forme: "origin", item: "Griseous Orb")]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      @p2.item.should.equal item2

    it "should not steal the target's item if target is Genesect with Drive", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Genesect', item: "Burn Drive")]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      @p2.item.should.equal item2

    it "should not steal the target's item if target holds Mail", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', item: "Air Mail")]
      item2 = @p2.item
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      @p2.item.should.equal item2

    it "should not steal the target's item if user is fainted", ->
      shared.create.call this,
        team1: [Factory('Magikarp')]
        team2: [Factory('Magikarp', item: "Leftovers", ability: "Iron Barbs")]
      item2 = @p2.item
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Thief'))
      should.not.exist @p1.item
      @p2.item.should.equal item2

  describe 'crush grip', ->
    it 'has a base power of 1 minimum', ->
      shared.create.call(this)
      move = @battle.getMove('Crush Grip')
      @p2.currentHP = 1
      move.basePower(@battle, @p1, @p2).should.equal 1

    it 'has a base power of 121 maximum', ->
      shared.create.call(this)
      move = @battle.getMove('Crush Grip')
      @p2.currentHP = @p2.stat('hp')
      move.basePower(@battle, @p1, @p2).should.equal 121

  describe 'hex', ->
    it 'doubles the base power if target is burned, poisoned, or paralyzed', ->
      shared.create.call(this)
      move = @battle.getMove('Hex')
      @p2.attach(Status.Paralyze)
      move.basePower(@battle, @p1, @p2).should.equal 100

  describe 'heavy slam and heat crash', ->
    it 'has variable base power based on the difference in weight', ->
      shared.create.call(this)
      move = @battle.getMove('Heavy Slam')
      move.basePower(@battle, @p1, @p2).should.equal 40
      move.basePower(@battle, @p1, calculateWeight: -> -1000).should.equal 120

  describe 'a status cure move', ->
    it 'heals the entire team of status effects', ->
      shared.create.call this,
        team1: [Factory('Magikarp'), Factory('Magikarp'), Factory('Magikarp')]
      shared.biasRNG.call(this, 'next', 'paralyze chance', 1)  # never pars
      @team1.pokemon.forEach((pokemon) -> pokemon.attach(Status.Paralyze))
      @battle.performMove(@p1, @battle.getMove('Aromatherapy'))

      _.all(@team1.pokemon, (pokemon) -> !pokemon.hasStatus()).should.be.true

  describe 'copycat', ->
    beforeEach ->
      shared.create.call this,
        team1: [Factory('Magikarp', evs: {speed: 4})] # make faster than team2

    it 'copies the last move used', ->
      @battle.lastMove = @battle.getMove('Tackle')
      mock = @sandbox.mock(@battle.getMove('Tackle'))
      mock.expects('execute').once()
      @battle.performMove(@p1, @battle.getMove('Copycat'))
      mock.verify()

    it "changes the move's target properly", ->
      tackle = @battle.getMove('Tackle')
      @battle.lastMove = tackle
      mock = @sandbox.mock(tackle)
      mock.expects('execute').once().withArgs(@battle, @p1, [ @p2 ])
      @battle.performMove(@p1, @battle.getMove('Copycat'))
      mock.verify()

    it 'fails if no last move was used', ->
      @battle.lastMove = null
      mock = @sandbox.mock(@battle.getMove('Copycat'))
      mock.expects('fail').once()
      @battle.performMove(@p1, @battle.getMove('Copycat'))
      mock.verify()

    it 'fails if the last move was copycat', ->
      @battle.lastMove = @battle.getMove('Copycat')
      mock = @sandbox.mock(@battle.getMove('Copycat'))
      mock.expects('fail').once()
      @battle.performMove(@p1, @battle.getMove('Copycat'))
      mock.verify()

  describe 'a move that targets a different stat', ->
    it "uses the correct stat", ->
      shared.create.call(this)
      move = @battle.getMove('Secret Sword')
      defStat = @p2.stat('defense')
      move.pickDefenseStat(@p1, @p2).should.equal defStat

  describe 'foul play', ->
    it "uses the target's attack stat, not the user's", ->
      shared.create.call this,
        team1: [Factory('Celebi')]
      move = @battle.getMove('Foul Play')
      atkStat = @p2.stat('attack')
      move.pickAttackStat(@p1, @p2).should.equal atkStat

  describe 'foul play', ->
    it "uses the target's attack stat, not the user's", ->
      shared.create.call this,
        team1: [Factory('Celebi')]
      move = @battle.getMove('Foul Play')
      atkStat = @p2.stat('attack')
      move.pickAttackStat(@p1, @p2).should.equal atkStat

  describe 'teleport', ->
    it "always fails", ->
      shared.create.call(this)
      move = @battle.getMove('Teleport')
      mock = @sandbox.mock(move).expects('fail').once()
      @battle.performMove(@p1, move)
      mock.verify()

  describe 'Super Fang', ->
    it "deals half of the target's current HP", ->
      shared.create.call(this)
      hp = @p2.currentHP
      hp = @p2.currentHP = (hp - (1 - hp % 2))  # Always odd
      @battle.performMove(@p1, @battle.getMove('Super Fang'))
      @p2.currentHP.should.equal Math.ceil(hp / 2)

    it "deals 1 damage minimum", ->
      shared.create.call(this)
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Super Fang'))
      @p2.currentHP.should.equal 0

  describe "A weather-based recovery move", ->
    it "heals 50% HP in no weather, rounded half down", ->
      shared.create.call(this)
      @battle.setWeather(Weather.NONE)
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Moonlight'))

      hp = util.roundHalfDown(@p1.stat('hp') / 2)
      @p1.currentHP.should.equal(1 + hp)

    it "heals 25% HP in bad weather, rounded half down", ->
      shared.create.call(this, team1: [Factory("Shuckle")])
      @battle.setWeather(Weather.SAND)
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Moonlight'))

      hp = util.roundHalfDown(@p1.stat('hp') / 4)
      @p1.currentHP.should.equal(1 + hp)

    it "heals 66% HP in good weather, rounded half down", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SUN)
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Moonlight'))

      hp = util.roundHalfDown(@p1.stat('hp') * 2 / 3)
      @p1.currentHP.should.equal(1 + hp)

  describe 'a flinching move', ->
    it "prevents the other person from executing their move", ->
      shared.create.call(this)

      mock = @sandbox.mock(@battle.getMove('Splash'))
      mock.expects('execute').never()

      @battle.performMove(@p1, @battle.getMove('Fake Out'))
      @battle.performMove(@p2, @battle.getMove('Splash'))

      mock.verify()

    it "removes the flinch attachment at the end of the turn", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('Fake Out'))
      @battle.endTurn()
      @p2.has(Attachment.Flinch).should.be.false

  describe 'weather ball', ->
    it "is a 50 base power normal move in normal conditions", ->
      shared.create.call(this)
      @battle.setWeather(Weather.NONE)

      move = @battle.getMove('Weather Ball')
      move.getType(@battle, @p1, @p2).should.equal 'Normal'
      move.basePower(@battle, @p1, @p2).should.equal 50

    it "is a 100 base power Water move in rain", ->
      shared.create.call(this)
      @battle.setWeather(Weather.RAIN)

      move = @battle.getMove('Weather Ball')
      move.getType(@battle, @p1, @p2).should.equal 'Water'
      move.basePower(@battle, @p1, @p2).should.equal 100

    it "is a 100 base power Fire move in sun", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SUN)

      move = @battle.getMove('Weather Ball')
      move.getType(@battle, @p1, @p2).should.equal 'Fire'
      move.basePower(@battle, @p1, @p2).should.equal 100

    it "is a 100 base power Ice move in hail", ->
      shared.create.call(this)
      @battle.setWeather(Weather.HAIL)

      move = @battle.getMove('Weather Ball')
      move.getType(@battle, @p1, @p2).should.equal 'Ice'
      move.basePower(@battle, @p1, @p2).should.equal 100

    it "is a 100 base power Rock move in sandstorm", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SAND)

      move = @battle.getMove('Weather Ball')
      move.getType(@battle, @p1, @p2).should.equal 'Rock'
      move.basePower(@battle, @p1, @p2).should.equal 100

  describe 'Autotomize', ->
    it 'changes your weight on success', ->
      shared.create.call(this)
      weight = @p1.calculateWeight()
      @battle.performMove(@p1, @battle.getMove('Autotomize'))

      weight.should.not.equal @p1.calculateWeight()

    it 'cannot go below .1kg', ->
      # Magikarp weighs 100kg.
      shared.create.call this, team1: [ Factory('Magikarp')]
      @battle.performMove(@p1, @battle.getMove('Autotomize'))

      @p1.calculateWeight().should.not.be.lessThan .1

    it 'stacks weight changes', ->
      # Abomasnow weighs 1355kg.
      shared.create.call this, team1: [ Factory('Abomasnow')]

      @battle.performMove(@p1, @battle.getMove('Autotomize'))
      @battle.performMove(@p1, @battle.getMove('Autotomize'))

      @p1.calculateWeight().should.equal 1155

  describe 'heart swap', ->
    shared.shouldDoNoDamage('Heart Swap')

    it 'swaps user and target boosts', ->
      shared.create.call(this)
      @p1.stages.attack = 2
      @p2.stages.speed = -2

      @battle.performMove(@p1, @battle.getMove('Heart Swap'))

      @p1.stages.should.containEql speed: -2
      @p2.stages.should.containEql attack: 2

  describe 'Nightmare', ->
    shared.shouldDoNoDamage('Nightmare')

    it 'fails if the pokemon is awake', ->
      shared.create.call(this)
      mock = @sandbox.mock(@battle.getMove('Nightmare')).expects('fail').once()
      @battle.performMove(@p1, @battle.getMove('Nightmare'))
      mock.verify()

    it 'fails if used twice', ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep turns', 3)
      nightmare = @battle.getMove('Nightmare')
      @p2.attach(Status.Sleep)

      mock = @sandbox.mock(nightmare).expects('fail').once()
      @battle.performMove(@p1, nightmare)
      @battle.performMove(@p1, nightmare)
      mock.verify()

    it "cuts the target's HP by 25% each turn", ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep turns', 3)
      @p2.attach(Status.Sleep)

      hp = @p2.currentHP
      quarter = Math.floor(hp / 4)

      @battle.performMove(@p1, @battle.getMove('Nightmare'))
      @battle.endTurn()
      @p2.currentHP.should.equal(hp - quarter)

      @battle.endTurn()
      @p2.currentHP.should.equal(hp - 2*quarter)

    it "deals 1 damage minimum", ->
      shared.create.call(this, team2: [Factory("Shedinja")])
      shared.biasRNG.call(this, 'randInt', 'sleep turns', 3)
      @p2.attach(Status.Sleep)

      @battle.performMove(@p1, @battle.getMove('Nightmare'))
      @battle.endTurn()
      @p2.currentHP.should.equal(0)

    it "stops the nightmare if the target wakes up", ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep turns', 3)
      @p2.attach(Status.Sleep)

      @battle.performMove(@p1, @battle.getMove('Nightmare'))

      @p2.cureStatus()
      @battle.endTurn()  # The check is in endTurn()
      @p2.has(Attachment.Nightmare).should.be.false

  describe 'Incinerate', ->
    it 'destroys the berry of the target', ->
      shared.create.call this,
        team2: [ Factory('Magikarp', item: 'Bluk Berry') ]

      @battle.performMove(@p1, @battle.getMove('Incinerate'))
      should.not.exist @p2.item

    it 'does not destroy non-berries', ->
      shared.create.call this,
        team2: [ Factory('Magikarp', item: 'Leftovers') ]

      @battle.performMove(@p1, @battle.getMove('Incinerate'))
      should.exist @p2.item

  describe 'Judgment', ->
    it 'is normal type by default', ->
      shared.create.call(this)
      move = @battle.getMove('Judgment')
      move.getType(@battle, @p1, @p2).should.equal 'Normal'

    it 'changes type depending on the Plate held by the user', ->
      shared.create.call this,
        team1: [ Factory('Magikarp', item: 'Earth Plate') ]
      move = @battle.getMove('Judgment')
      move.getType(@battle, @p1, @p2).should.equal 'Ground'

  describe 'Tailwind', ->
    shared.shouldDoNoDamage("Tailwind")
    shared.shouldFailIfUsedTwice("Tailwind")

    it "doubles the speed of the user's team", ->
      shared.create.call this

      oldSpeed = @p1.stat('speed')
      @battle.performMove(@p1, @battle.getMove('Tailwind'))
      newSpeed = @p1.stat('speed')
      newSpeed.should.equal(oldSpeed * 2)

    it "lasts three turns", ->
      shared.create.call(this)

      @battle.performMove(@p1, @battle.getMove('Tailwind'))
      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()

      @p1.team.has(Attachment.Tailwind).should.be.false

  describe 'Taunt', ->
    shared.shouldDoNoDamage("Taunt")
    shared.shouldFailIfUsedTwice("Taunt")

    it 'prevents the target from using a non-attacking move that turn', ->
      shared.create.call(this, team1: [ Factory('Magikarp', evs: {speed: 4}) ])
      move = @battle.getMove('Calm Mind')
      taunt = @battle.getMove('Taunt')
      mock = @sandbox.mock(move)
      mock.expects('execute').never()

      @battle.performMove(@p1, taunt)
      @battle.performMove(@p2, move)

      mock.verify()

    it "lasts 4 turns if the user moved after the target", ->
      shared.create.call(this, team1: [Factory("Magikarp", evs: {speed: 4})])
      @battle.performMove(@p1, @battle.getMove('Splash'))
      @battle.performMove(@p2, @battle.getMove('Taunt'))

      @p1.has(Attachment.Taunt).should.be.true

      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()
      @p1.has(Attachment.Taunt).should.be.true
      @battle.endTurn()

      @p1.has(Attachment.Taunt).should.be.false

    it "lasts 3 turns if the user moved before the target", ->
      shared.create.call(this, team1: [Factory("Magikarp", evs: {speed: 4})])
      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.recordMove(@id1, @battle.getMove('Taunt'))
      @battle.recordMove(@id2, @battle.getMove('Tackle'))
      @battle.continueTurn()

      @p2.has(Attachment.Taunt).should.be.true

      @battle.endTurn()
      @battle.endTurn()
      @p2.has(Attachment.Taunt).should.be.true
      @battle.endTurn()

      @p2.has(Attachment.Taunt).should.be.false

    it 'prevents the target from selecting that move the next turn', ->
      shared.create.call(this)

      @battle.performMove(@p1, @battle.getMove('Taunt'))
      @battle.beginTurn()
      requestedMoves = @battle.requestFor(@p2).moves
      requestedMoves.should.not.containEql 'Splash'

  for moveName in [ "U-turn", "Volt Switch" ]
    do (moveName) ->
      describe moveName, ->
        it 'forces the owner to switch', ->
          shared.create.call(this, team1: (Factory("Magikarp")  for i in [1..2]))
          @battle.performMove(@p1, @battle.getMove(moveName))

          @battle.requests.should.have.property(@id1)

        it "makes a request containing all the possible switches", ->
          shared.create.call(this, team1: (Factory("Magikarp")  for i in [1..3]))
          @battle.performMove(@p1, @battle.getMove(moveName))
          request = @battle.requestFor(@p1)
          request.switches.should.eql([ 1, 2 ])

  describe 'venoshock', ->
    it 'doubles the base power if target is poisoned', ->
      shared.create.call(this)
      move = @battle.getMove("Venoshock")
      @p2.attach(Status.Poison)
      basePower = move.basePower(@battle, @p1, @p2)
      basePower.should.equal(2 * move.power)

    it 'doubles the base power if target is toxiced', ->
      shared.create.call(this)
      move = @battle.getMove("Venoshock")
      @p2.attach(Status.Toxic)
      basePower = move.basePower(@battle, @p1, @p2)
      basePower.should.equal(2 * move.power)

  describe 'Wish', ->
    shared.shouldDoNoDamage("Wish")
    shared.shouldFailIfUsedTwice("Wish")

    it "restores half of the user's total hit points the next end of turn", ->
      shared.create.call(this)
      hp = @p1.currentHP
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Wish'))
      @battle.endTurn()

      @p1.currentHP.should.equal 1

      @battle.endTurn()

      @p1.currentHP.should.equal(Math.round(hp / 2) + 1)

    it "restores the same total amount of HP to an ally", ->
      shared.create.call(this, team1: [Factory("Magikarp"), Factory("Celebi")])
      hp = @p1.currentHP
      @battle.performMove(@p1, @battle.getMove('Wish'))
      @battle.endTurn()
      receiver = @team1.at(1)

      receiver.currentHP = 1
      @battle.performSwitch(@p1, 1)
      @battle.endTurn()

      receiver.currentHP.should.equal(Math.round(hp / 2) + 1)

    it "fails if the pokemon faints", ->
      shared.create.call(this, team1: [Factory("Magikarp"), Factory("Celebi")])
      @battle.performMove(@p1, @battle.getMove('Wish'))
      @battle.endTurn()

      @p1.currentHP = 1
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @battle.endTurn()
      @team1.has(Attachment.Wish).should.be.false

  describe "Counter", ->
    it "returns double the damage if attacked by a physical move", ->
      shared.create.call(this)
      @battle.performMove(@p2, @battle.getMove('Tackle'))
      @battle.performMove(@p1, @battle.getMove('Counter'))

      dhp1 = @p1.stat('hp') - @p1.currentHP
      dhp2 = @p2.stat('hp') - @p2.currentHP
      dhp2.should.equal 2*dhp1

    it "fails if attacked by a special move", ->
      shared.create.call(this)
      mock = @sandbox.mock(@battle.getMove('Counter'))
      mock.expects('fail').once()
      @battle.performMove(@p2, @battle.getMove('ThunderShock'))
      @battle.performMove(@p1, @battle.getMove('Counter'))

      mock.verify()

    it "fails if not hit by an attack this turn", ->
      shared.create.call(this)
      mock = @sandbox.mock(@battle.getMove('Counter'))
      mock.expects('fail').once()
      @controller.makeMove(@id1, "Splash")
      @controller.makeMove(@id2, "Tackle")

      @battle.performMove(@p1, @battle.getMove('Counter'))
      mock.verify()

    it "hits the pokemon who is currently in that slot, not who was", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('U-turn'))
      @battle.performSwitch(@p1, 1)
      @battle.performMove(@p2, @battle.getMove('Counter'))
      @team1.first().currentHP.should.be.lessThan(@team1.first().stat('hp'))
      @team1.at(1).currentHP.should.equal(@team1.at(1).stat('hp'))

    it "doesn't activate Rough Skin if it fails", ->
      shared.create.call this,
        team2: [Factory("Garchomp", ability: "Rough Skin")]

      counter = @battle.getMove("Counter")
      mock = @sandbox.mock(counter)
      mock.expects('fail').once()
      @battle.performMove(@p1, counter)
      mock.verify()
      @p1.currentHP.should.equal(@p1.stat('hp'))

  describe "Perish Song", ->
    it "attaches to every pokemon in the field", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('Perish Song'))

      result = _.all @battle.getActivePokemon(), (pokemon) ->
        pokemon.has(Attachment.PerishSong)
      result.should.be.true

    it "faints pokemon at the end of 4 turns", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('Perish Song'))
      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()

      result = _.all @battle.getActivePokemon(), (pokemon) ->
        !pokemon.isFainted()
      result.should.be.true

      @battle.endTurn()

      result = _.all @battle.getActivePokemon(), (pokemon) ->
        pokemon.isFainted()
      result.should.be.true

    it "fails against Pokemon with Soundproof", ->
      shared.create.call(this, team1: [Factory("Magikarp", ability: "Soundproof")])
      @battle.performMove(@p2, @battle.getMove("Perish Song"))
      @p1.has(Attachment.PerishSong).should.be.false
      @p2.has(Attachment.PerishSong).should.be.true

  describe "Techno Blast", ->
    it "is Fire-type if the user holds a Burn Drive", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Burn Drive")]
      move = @battle.getMove('Techno Blast')
      type = move.getType(@battle, @p1, @p2)
      type.should.equal "Fire"

    it "is Water-type if the user holds a Douse Drive", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Douse Drive")]
      move = @battle.getMove('Techno Blast')
      type = move.getType(@battle, @p1, @p2)
      type.should.equal "Water"

    it "is Electric-type if the user holds a Shock Drive", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Shock Drive")]
      move = @battle.getMove('Techno Blast')
      type = move.getType(@battle, @p1, @p2)
      type.should.equal "Electric"

    it "is Ice-type if the user holds a Chill Drive", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Chill Drive")]
      move = @battle.getMove('Techno Blast')
      type = move.getType(@battle, @p1, @p2)
      type.should.equal "Ice"

    it "is Normal-type otherwise", ->
      shared.create.call(this)
      move = @battle.getMove('Techno Blast')
      type = move.getType(@battle, @p1, @p2)
      type.should.equal "Normal"

  describe "Synchronoise", ->
    it "fails on Pokemon not of the user's type", ->
      shared.create.call this,
        team1: [Factory("Magikarp")]
        team2: [Factory("Celebi")]

      move = @battle.getMove('Synchronoise')
      mock = @sandbox.mock(move)
      mock.expects('fail').once()

      @battle.performMove(@p1, move)
      mock.verify()

    it "works on Pokemon that share one type with the user", ->
      shared.create.call this,
        team1: [Factory("Ferrothorn")]
        team2: [Factory("Celebi")]

      hp = @p2.currentHP
      @battle.performMove(@p1, @battle.getMove('Synchronoise'))
      @p2.currentHP.should.be.lessThan hp

  describe "Roost", ->
    shared.shouldDoNoDamage('Roost')

    it "removes the user's flying type during the turn", ->
      shared.create.call this,
        team1: [Factory("Gliscor")]

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Roost"))

      ('Flying' in @p1.types).should.be.false
      @p1.types.should.eql ['Ground']

    it "turns pure-Flying pokemon into Normal pokemon", ->
      shared.create.call this,
        team1: [Factory("Tornadus")]

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Roost"))

      ('Flying' in @p1.types).should.be.false
      @p1.types.should.eql ['Normal']

    it "keeps the user's types the same if non-Flying", ->
      shared.create.call this,
        team1: [Factory("Celebi")]

      oldTypes = @p1.types
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Roost"))
      @p1.types.should.eql oldTypes

    it "restores the user's old types after the turn", ->
      shared.create.call this,
        team1: [Factory("Gliscor")]

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Roost"))
      @battle.endTurn()
      ('Flying' in @p1.types).should.be.true
      ('Ground' in @p1.types).should.be.true

  describe 'Encore', ->
    shared.shouldDoNoDamage('Encore')

    it "fails if the target has not yet used a move", ->
      shared.create.call this,
        team1: [Factory("Magikarp", evs: {speed: 4})]
      encore = @battle.getMove('Encore')
      mock = @sandbox.mock(encore).expects('fail').once()

      @battle.performMove(@p1, encore)
      mock.verify()

    it "forces the target to repeat its last used move", ->
      shared.create.call(this)

      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.performMove(@p1, @battle.getMove('Encore'))

      @battle.beginTurn()
      @p2.validMoves().should.eql [ @battle.getMove('Splash') ]

    it "changes the target's decision if it has not moved yet", ->
      shared.create.call(this, team1: [Factory("Magikarp", evs: {speed: 4})])

      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.recordMove(@id1, @battle.getMove('Encore'))
      @battle.recordMove(@id2, @battle.getMove('Tackle'))
      @battle.continueTurn()
      @p2.lastMove.should.equal @battle.getMove('Splash')

    it "lasts 4 turns if the user moved after the target", ->
      shared.create.call(this, team1: [Factory("Magikarp", evs: {speed: 4})])
      @battle.performMove(@p1, @battle.getMove('Splash'))
      @battle.performMove(@p2, @battle.getMove('Encore'))

      @p1.has(Attachment.Encore).should.be.true

      @battle.endTurn()
      @battle.endTurn()
      @battle.endTurn()
      @p1.has(Attachment.Encore).should.be.true
      @battle.endTurn()

      @p1.has(Attachment.Encore).should.be.false

    it "lasts 3 turns if the user moved before the target", ->
      shared.create.call(this, team1: [Factory("Magikarp", evs: {speed: 4})])
      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.recordMove(@id1, @battle.getMove('Encore'))
      @battle.recordMove(@id2, @battle.getMove('Tackle'))
      @battle.continueTurn()

      @p2.has(Attachment.Encore).should.be.true

      @battle.endTurn()
      @battle.endTurn()
      @p2.has(Attachment.Encore).should.be.true
      @battle.endTurn()

      @p2.has(Attachment.Encore).should.be.false

    it "fails on certain moves like mimic", ->
      shared.create.call(this)
      encore = @battle.getMove('Encore')
      mock = @sandbox.mock(encore).expects('fail').once()

      @battle.performMove(@p2, @battle.getMove('Mimic'))
      @battle.performMove(@p1, @battle.getMove('Encore'))
      mock.verify()

    it "fails if the pokemon is already encored", ->
      shared.create.call(this)
      encore = @battle.getMove('Encore')
      mock = @sandbox.mock(encore).expects('fail').once()

      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.performMove(@p1, @battle.getMove('Encore'))
      @battle.performMove(@p1, @battle.getMove('Encore'))
      mock.verify()

    it "fails if the move has 0 PP", ->
      shared.create.call(this)
      encore = @battle.getMove('Encore')
      mock = @sandbox.mock(encore).expects('fail').once()

      @p2.setPP(@battle.getMove('Splash'), 1)
      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.performMove(@p1, @battle.getMove('Encore'))
      mock.verify()

    it "removes itself if the pokemon's move reaches 0 PP", ->
      shared.create.call(this)

      @p2.setPP(@battle.getMove('Splash'), 2)
      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.performMove(@p1, @battle.getMove('Encore'))
      @battle.endTurn()

      @p2.has(Attachment.Encore).should.be.true

      @battle.performMove(@p2, @battle.getMove('Splash'))
      @battle.endTurn()

      @p2.has(Attachment.Encore).should.be.false

  describe "Swagger", ->
    it "confuses the target", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Swagger"))
      @p2.has(Attachment.Confusion).should.be.true

    it "boosts the target's attack by two stages", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Swagger"))
      @p2.stages.attack.should.equal 2

  describe "Flatter", ->
    it "confuses the target", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Flatter"))
      @p2.has(Attachment.Confusion).should.be.true

    it "boosts the target's special attack by one stage", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Flatter"))
      @p2.stages.specialAttack.should.equal 1

  describe "Torment", ->
    shared.shouldDoNoDamage("Torment")
    shared.shouldFailIfUsedTwice("Torment")

    it "prevents the target from using its last move", ->
      shared.create.call(this)

      @battle.performMove(@p1, @battle.getMove("Torment"))
      @battle.performMove(@p2, @battle.getMove("Splash"))
      @battle.beginTurn()
      @p2.validMoves().should.eql [ @battle.getMove('Tackle') ]

      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @battle.beginTurn()
      @p2.validMoves().should.eql [ @battle.getMove('Splash') ]

    it "still works even if a new pokemon has just switched in", ->
      shared.create.call(this, team2: [Factory("Magikarp"), Factory("Magikarp")])

      @battle.performSwitch(@p2, 1)
      @battle.performMove(@p1, @battle.getMove("Torment"))
      @battle.beginTurn()
      @team2.first().validMoves().should.eql [ @battle.getMove('Splash'), @battle.getMove('Tackle') ]

    xit "does not force the Outrage user to struggle", ->
    xit "does not prevent consecutive use of Struggle", ->

  describe "Psych Up", ->
    it "copies the target's stat changes", ->
      shared.create.call(this)

      @p1.stages.specialAttack = 5
      @p1.stages.evasion = 2
      @p2.stages.attack = 6
      @p2.stages.defense = -2
      @p2.stages.speed = -1
      @battle.performMove(@p1, @battle.getMove("Psych Up"))
      @p1.stages.should.eql {
        attack: 6, defense: -2, specialAttack: 0, specialDefense: 0,
        speed: -1, accuracy: 0, evasion: 0
      }

  describe "Spikes", ->
    it "puts a layer of spikes on the opponents' field", ->
      shared.create.call(this)

      @team2.has(Attachment.Spikes).should.be.false
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @team2.has(Attachment.Spikes).should.be.true

    it "fails if there are 3 layers", ->
      shared.create.call(this)

      mock = @sandbox.mock(@battle.getMove('Spikes')).expects('fail').once()

      for i in [1..4]
        @battle.performMove(@p1, @battle.getMove("Spikes"))

      mock.verify()

    it "deals a minimum of 1 damage", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Shedinja")]

      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @battle.performSwitch(@team2.first(), 1)

      pokemon = @team2.first()
      (pokemon.stat('hp') - pokemon.currentHP).should.equal(1)

    it "does damage to pokemon switching in according to # of layers", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Magikarp")]

      hp = @team2.first().stat('hp')
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performSwitch(@p2, 1)

      (hp - @team2.first().currentHP).should.equal Math.floor(hp / 8)
      @team2.first().currentHP = hp

      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performSwitch(@team2.first(), 1)

      (hp - @team2.first().currentHP).should.equal Math.floor(hp / 6)
      @team2.first().currentHP = hp

      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performSwitch(@team2.first(), 1)

      (hp - @team2.first().currentHP).should.equal Math.floor(hp / 4)

    it "does not affect pokemon with immunity to ground", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Magikarp", item: "Air Balloon")]

      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performSwitch(@team2.first(), 1)

      @team2.first().currentHP.should.equal @team2.first().stat('hp')

    it "affects replacements", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Magikarp")]

      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.recordSwitch(@id2, 1)
      @battle.performReplacements()

      hp = @team2.first().stat('hp')
      (hp - @team2.first().currentHP).should.equal Math.floor(hp / 8)

  describe "Stealth Rock", ->
    shared.shouldDoNoDamage("Stealth Rock")
    shared.shouldFailIfUsedTwice("Stealth Rock")

    it "puts a layer of rocks on the opponents' field", ->
      shared.create.call(this)

      @team2.has(Attachment.StealthRock).should.be.false
      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @team2.has(Attachment.StealthRock).should.be.true

    it "does damage to pokemon switching in according to type", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Moltres")]

      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @battle.performSwitch(@team2.first(), 1)

      pokemon = @team2.first()

      hp = pokemon.stat('hp')
      (hp - pokemon.currentHP).should.equal Math.floor(hp / 2)

      @battle.performSwitch(@team2.first(), 1)
      pokemon = @team2.first()
      hp = pokemon.stat('hp')
      (hp - pokemon.currentHP).should.equal Math.floor(hp / 8)

    it "deals a minimum of 1 damage", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Aron", level: 1)]

      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @battle.performSwitch(@team2.first(), 1)

      pokemon = @team2.first()
      (pokemon.stat('hp') - pokemon.currentHP).should.equal(1)

    it "affects replacements", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Magikarp")]

      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @battle.recordSwitch(@id2, 1)
      @battle.performReplacements()

      hp = @team2.first().stat('hp')
      (hp - @team2.first().currentHP).should.equal Math.floor(hp / 8)

  describe "Toxic Spikes", ->
    it "puts a layer of toxic spikes on the opponents' field", ->
      shared.create.call(this)

      @team2.has(Attachment.ToxicSpikes).should.be.false
      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @team2.has(Attachment.ToxicSpikes).should.be.true

    it "fails if there are 2 layers", ->
      shared.create.call(this)

      mock = @sandbox.mock(@battle.getMove('Toxic Spikes'))
      mock.expects('fail').once()

      for i in [1..3]
        @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))

      mock.verify()

    it "poisons or severely poisons the switch-in if not immune", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Magikarp")]

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.performSwitch(@team2.first(), 1)
      @team2.first().has(Status.Poison).should.be.true

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.performSwitch(@team2.first(), 1)
      @team2.first().has(Status.Toxic).should.be.true

    it "does not affect the pokemon if it's immune to Poison", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Ferrothorn")]

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.performSwitch(@team2.first(), 1)
      @team2.first().has(Status.Poison).should.be.false

    it "does not affect the pokemon if it's immune to Ground", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Gyarados")]

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.performSwitch(@team2.first(), 1)
      @team2.first().has(Status.Poison).should.be.false

    it "disappears if the pokemon switching in is a grounded Poison", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Drapion")]

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.performSwitch(@team2.first(), 1)
      @team2.has(Attachment.ToxicSpikes).should.be.false

    it "doesn't disappear if the pokemon switching in is a flying Poison", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Drapion", item: "Air Balloon")]

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.performSwitch(@team2.first(), 1)
      @team2.has(Attachment.ToxicSpikes).should.be.true

    it "affects replacements", ->
      shared.create.call this,
        team2: [Factory("Magikarp"), Factory("Magikarp")]

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @battle.recordSwitch(@id2, 1)
      @battle.performReplacements()

      @team2.first().has(Status.Poison).should.be.true

  testWeatherMove = (moveName, weather, item) ->
    describe moveName, ->
      it "changes the weather to #{weather.toLowerCase()} for 5 turns", ->
        shared.create.call(this)

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.weather.should.equal(weather)
        @battle.weatherDuration.should.equal 5

      it "changes the weather to #{weather.toLowerCase()} for 8 turns if user holds a #{item}", ->
        shared.create.call this,
          team1: [Factory("Magikarp", item: item)]

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.weather.should.equal(weather)
        @battle.weatherDuration.should.equal 8

      it "fails if the weather is already #{weather}", ->
        shared.create.call this,
          team1: [Factory("Magikarp", item: item)]
        @battle.setWeather(weather)

        theMove = @battle.getMove(moveName)
        mock = @sandbox.mock(theMove).expects('fail').once()
        @battle.performMove(@p1, theMove)
        mock.verify()

  testWeatherMove("Rain Dance", Weather.RAIN, "Damp Rock")
  testWeatherMove("Sunny Day",  Weather.SUN,  "Heat Rock")
  testWeatherMove("Hail",       Weather.HAIL, "Icy Rock")
  testWeatherMove("Sandstorm",  Weather.SAND, "Smooth Rock")

  testStatusMove = (moveName, status) ->
    describe moveName, ->
      it "changes the status on a Pokemon if it has no status", ->
        shared.create.call(this)

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        @p2.has(status).should.be.true

      it "does not change the status if Pokemon already has a status", ->
        shared.create.call(this)

        oldStatus = if status == Status.Paralyze
            Status.Sleep
          else
            Status.Paralyze
        @p2.attach(oldStatus)

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        @p2.has(status).should.be.false
        @p2.has(oldStatus).should.be.true

  testStatusMove("Dark Void", Status.Sleep)
  testStatusMove("GrassWhistle", Status.Sleep)
  testStatusMove("Hypnosis", Status.Sleep)
  testStatusMove("Lovely Kiss", Status.Sleep)
  testStatusMove("Poison Gas", Status.Poison)
  testStatusMove("PoisonPowder", Status.Poison)
  testStatusMove("Sing", Status.Sleep)
  testStatusMove("Sleep Powder", Status.Sleep)
  testStatusMove("Spore", Status.Sleep)
  testStatusMove("Stun Spore", Status.Paralyze)
  testStatusMove("Thunder Wave", Status.Paralyze)
  testStatusMove("Toxic", Status.Toxic)
  testStatusMove("Will-O-Wisp", Status.Burn)

  testEffectMove = (moveName, Effect) ->
    describe moveName, ->
      it "adds the effect to the Pokemon if it doesn't have it", ->
        shared.create.call(this)

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        @p2.has(Effect).should.be.true

      it "fails if the Pokemon already has it", ->
        shared.create.call(this)

        move = @battle.getMove(moveName)
        mock = @sandbox.mock(move)
        mock.expects('fail').once()

        shared.biasRNG.call(this, "randInt", 'confusion turns', 4)
        @p2.attach(Effect, {@battle})

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        mock.verify()

  testEffectMove 'Confuse Ray', Attachment.Confusion
  testEffectMove 'Supersonic', Attachment.Confusion
  testEffectMove 'Sweet Kiss', Attachment.Confusion
  testEffectMove 'Teeter Dance', Attachment.Confusion

  describe "Trump Card", ->
    it "has 40 base power by default", ->
      shared.create.call(this)

      @battle.getMove('Trump Card').basePower(@battle, @p1, @p2).should.equal 40

    it "has 50 base power if the move has 3 PP after use", ->
      shared.create.call this,
        team1: [Factory("Corphish")]

      pp = @p1.pp(@battle.getMove('Trump Card'))
      for x in [0...pp - 3]
        @p1.reducePP(@battle.getMove('Trump Card'))
      @p1.pp(@battle.getMove('Trump Card')).should.equal 3

      @battle.getMove('Trump Card').basePower(@battle, @p1, @p2).should.equal 50

    it "has 60 base power if the move has 2 PP after use", ->
      shared.create.call this,
        team1: [Factory("Corphish")]

      pp = @p1.pp(@battle.getMove('Trump Card'))
      for x in [0...pp - 2]
        @p1.reducePP(@battle.getMove('Trump Card'))
      @p1.pp(@battle.getMove('Trump Card')).should.equal 2

      @battle.getMove('Trump Card').basePower(@battle, @p1, @p2).should.equal 60

    it "has 80 base power if the move has 1 PP after use", ->
      shared.create.call this,
        team1: [Factory("Corphish")]

      pp = @p1.pp(@battle.getMove('Trump Card'))
      for x in [0...pp - 1]
        @p1.reducePP(@battle.getMove('Trump Card'))
      @p1.pp(@battle.getMove('Trump Card')).should.equal 1

      @battle.getMove('Trump Card').basePower(@battle, @p1, @p2).should.equal 80

    it "has 200 base power if the move has 0 PP after use", ->
      shared.create.call this,
        team1: [Factory("Corphish")]

      pp = @p1.pp(@battle.getMove('Trump Card'))
      for x in [0...pp - 0]
        @p1.reducePP(@battle.getMove('Trump Card'))
      @p1.pp(@battle.getMove('Trump Card')).should.equal 0

      @battle.getMove('Trump Card').basePower(@battle, @p1, @p2).should.equal 200

  testRandomSwitchMove = (moveName) ->
    describe moveName, ->
      it "should switch opponent out to a random member", ->
        shared.create.call(this, team2: [Factory("Magikarp"), Factory("Abra")])

        target = @team2.at(1)
        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        @team2.first().should.equal target

      it "does not force a switch if the target faints", ->
        shared.create.call(this, team1: [Factory("Magikarp")], team2: [Factory("Magikarp"), Factory("Abra")])

        move = @battle.getMove(moveName)

        mock = @sandbox.mock(@team2)
        # Whirlwind, Roar
        if !move.isNonDamaging()
          mock.expects("switch").never()

        @team2.at(0).setHP(1)
        @battle.performMove(@p1, move)

        mock.verify()

      it "does not force a switch if the hit was indirect", ->
        shared.create.call(this, team1: [Factory("Magikarp")], team2: [Factory("Magikarp"), Factory("Abra")])

        move = @battle.getMove(moveName)

        mock = @sandbox.mock(@team2)
        # Whirlwind, Roar
        if !move.isNonDamaging()
          mock.expects("switch").never()

        @p2.attach(Attachment.Substitute, hp: 1)
        @battle.performMove(@p1, move)

        mock.verify()

      it "does not force a switch if the user faints", ->
        shared.create.call(this, team1: [Factory("Magikarp")], team2: [Factory("Magikarp", ability: "Iron Barbs"), Factory("Abra")])

        move = @battle.getMove(moveName)

        mock = @sandbox.mock(@team2)
        # Whirlwind, Roar
        if !move.isNonDamaging()
          mock.expects("switch").never()
        else
          mock.expects("switch").once()

        @p1.currentHP = 1
        @battle.performMove(@p1, move)

        mock.verify()

      it "should not force switches if opponent is the last pokemon", ->
        shared.create.call(this, team2: [Factory("Magikarp")])

        mock = @sandbox.mock(@team2)
        mock.expects("switch").never()

        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)

        mock.verify()

      it "cancels the opponent's move if user moves first", ->
        shared.create.call this,
          team1: [ Factory("Magikarp", moves: [ moveName ], evs: {speed: 4}), Factory("Magikarp")]
          team2: [ Factory("Magikarp", moves: [ moveName ]), Factory("Magikarp")]

        spy1 = @sandbox.spy(@team1, 'switch')
        spy2 = @sandbox.spy(@team2, 'switch')
        @controller.makeMove(@id1, moveName)
        @controller.makeMove(@id2, moveName)
        spy1.callCount.should.equal(0)
        spy2.callCount.should.equal(1)

  testRandomSwitchMove "Roar"
  testRandomSwitchMove "Whirlwind"
  testRandomSwitchMove "Dragon Tail"
  testRandomSwitchMove "Circle Throw"

  testTrappingMove = (name) ->
    describe name, ->
      it "blocks switching", ->
        shared.create.call(this)

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()
        @battle.beginTurn()
        @p2.isSwitchBlocked().should.be.true
        @p2.has(Attachment.Trap).should.be.true
        @p1.has(Attachment.TrapLeash).should.be.true

      it "deals 1/16 of the pokemon's max hp every turn", ->
        shared.create.call(this, team2: [Factory("Blissey")])

        @battle.performMove(@p1, @battle.getMove(name))
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
        maxHP = @p2.stat('hp')
        expected = maxHP - Math.floor(maxHP / 16)
        @p2.currentHP.should.equal expected

      it "deals a minimum of 1 damage", ->
        shared.create.call(this, team2: [Factory("Shedinja")])
        @p2.types = [ "Normal" ]
        move = @battle.getMove(name)
        @sandbox.stub(move, 'calculateDamage', -> 0)

        @battle.performMove(@p1, move)
        @battle.endTurn()
        @p2.currentHP.should.equal(0)

      it "lasts several turns", ->
        shared.create.call(this, team2: [Factory("Blissey")])
        shared.biasRNG.call(this, "randInt", 'trapping move', 5)

        @battle.performMove(@p1, @battle.getMove(name))
        @p2.currentHP = @p2.stat('hp')

        # loop for 5 more turns. One of the turns has already passed.
        # These moves hurt for 5 moves and wear off on the 6th.
        for i in [1..5]
          @p2.has(Attachment.Trap).should.be.true
          @battle.endTurn()

        # Test if the actual damage checks out. It should have damaged only 5 times
        maxHP = @p2.stat('hp')
        expected = maxHP - (Math.floor(maxHP / 16) * 5)
        @p2.currentHP.should.equal expected

      it "wears off after a certain number of turns", ->
        shared.create.call(this, team2: [Factory("Blissey")])
        shared.biasRNG.call(this, "randInt", 'trapping move', 5)

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()

        # loop for 5 more turns. One of the turns has already passed.
        # These moves hurt for 5 moves and wear off on the 6th. We need
        # turn number 6 to pass before the attachment should wear off.
        for i in [1..5]
          @battle.endTurn()
          @battle.beginTurn()

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.Trap).should.be.false
        @p1.has(Attachment.TrapLeash).should.be.false

      it "does not reset the duration if used twice", ->
        shared.create.call(this, team2: [Factory("Blissey")])
        shared.biasRNG.call(this, "randInt", 'trapping move', 5)

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()

        # loop for 4 more turns. These moves wear off after numTurns + 1.
        # 2 have already passed.
        for i in [1..4]
          @battle.endTurn()
          @battle.beginTurn()

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.Trap).should.be.false
        @p1.has(Attachment.TrapLeash).should.be.false

      it "wears off if the user switches", ->
        shared.create.call(this, team1: [Factory("Blissey"), Factory("Magikarp")])

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()

        @controller.makeSwitch(@id1, 1)
        @controller.makeMove(@id2, "Splash")

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.Trap).should.be.false

      it "wears off if the user faints", ->
        shared.create.call(this, team1: [Factory("Heatran")],  team2: [Factory("Blissey")])
        @p1.currentHP = 1

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()
        @battle.beginTurn()

        @p2.isSwitchBlocked().should.be.true
        @p2.has(Attachment.Trap).should.be.true

        @battle.recordMove(@id2, @battle.getMove("Tackle"))

        @battle.continueTurn()
        @battle.endTurn()
        @battle.beginTurn()

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.Trap).should.be.false

      it "properly removes from a list of attachments", ->
        shared.create.call(this, team1: [Factory("Blissey"), Factory("Magikarp")])

        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()

        @battle.recordMove(@id1, @battle.getMove("Protect"))
        @battle.recordMove(@id2, @battle.getMove("Splash"))
        @battle.continueTurn()
        @battle.endTurn()
        @battle.beginTurn()

        @controller.makeSwitch(@id1, 1)
        @controller.makeMove(@id2, "Splash")

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.Trap).should.be.false

      it "is always 7 turns if the user is holding grip claw", ->
        shared.create.call(this, team1: [Factory("Magikarp", item: "Grip Claw")])
        shared.biasRNG.call(this, "randInt", 'trapping move', 5)

        move = @battle.getMove(name)
        @battle.performMove(@p1, move)
        @battle.performMove(@p2, @battle.getMove("Recover"))

        # The user is damaged 7 times, but the attachment actually lasts
        # for 8 turns including the turn it is first used.
        for i in [1..8]
          @p2.has(Attachment.Trap).should.be.true
          @battle.endTurn()

        @p2.has(Attachment.Trap).should.be.false

      it "does not start the effect if it hits a substitute", ->
        shared.create.call(this, team2: [Factory("Blissey")])

        @p2.attach(Attachment.Substitute, hp: 1)
        @battle.performMove(@p1, @battle.getMove(name))
        @battle.endTurn()

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.Trap).should.be.false
        @p1.has(Attachment.TrapLeash).should.be.false

  testTrappingMove "Bind"
  testTrappingMove "Clamp"
  testTrappingMove "Fire Spin"
  testTrappingMove "Magma Storm"
  testTrappingMove "Sand Tomb"
  testTrappingMove "Whirlpool"
  testTrappingMove "Wrap"

  describe "Attract", ->
    shared.shouldDoNoDamage('Attract')
    shared.shouldFailIfUsedTwice "Attract",
      team1: [Factory("Magikarp", gender: "M", evs: {speed: 4})]
      team2: [Factory("Magikarp", gender: "F")]

    it "has a 50% chance to immobilize a pokemon", ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "M", evs: {speed: 4})]
        team2: [Factory("Magikarp", gender: "F")]
      shared.biasRNG.call(this, "next", 'attract chance', 0)  # 100% immobilizes

      mock = @sandbox.mock(@battle.getMove('Tackle')).expects('execute').never()
      @battle.performMove(@p1, @battle.getMove("Attract"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      mock.verify()

    it "has a 50% chance to not immobilize a pokemon", ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "M", evs: {speed: 4})]
        team2: [Factory("Magikarp", gender: "F")]
      shared.biasRNG.call(this, "next", 'attract chance', .5)  # 0% immobilizes

      mock = @sandbox.mock(@battle.getMove('Tackle')).expects('execute').once()
      @battle.performMove(@p1, @battle.getMove("Attract"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      mock.verify()

    it "fails if the Pokemon are not opposite genders", ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "F")]
        team2: [Factory("Magikarp", gender: "F")]

      mock = @sandbox.mock(@battle.getMove('Attract')).expects('fail').once()
      @battle.performMove(@p1, @battle.getMove("Attract"))
      mock.verify()

    it "disappears if the source is no longer active", ->
      shared.create.call this,
        team1: (Factory("Magikarp", gender: "M")  for x in [1..2])
        team2: [Factory("Magikarp", gender: "F")]
      @battle.performMove(@p1, @battle.getMove('Attract'))
      @battle.performMove(@p2, @battle.getMove('Splash'))
      @p2.has(Attachment.Attract).should.be.true
      @battle.performSwitch(@team1.first(), 1)
      @battle.performMove(@p2, @battle.getMove('Splash'))
      @p2.has(Attachment.Attract).should.be.false

  describe "Reflect", ->
    it "halves physical damage", ->
      shared.create.call(this)
      tackle = @battle.getMove('Tackle')
      tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

      @team2.attach(Attachment.Reflect)
      tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x800)

    it "does not halve non-physical damage", ->
      shared.create.call(this)
      thundershock = @battle.getMove('ThunderShock')
      thundershock.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

      @team2.attach(Attachment.Reflect)
      thundershock.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

    it "lasts five turns", ->
      shared.create.call(this)

      @battle.performMove(@p1, @battle.getMove('Reflect'))

      for i in [1..5]
        @team1.has(Attachment.Reflect).should.be.true
        @battle.endTurn()
      @team1.has(Attachment.Reflect).should.be.false

    it "fails if the user already used it", ->
      shared.create.call(this)
      mock = @sandbox.mock(@battle.getMove('Reflect'))
      mock.expects('fail').once()

      @battle.performMove(@p1, @battle.getMove('Reflect'))
      @battle.performMove(@p1, @battle.getMove('Reflect'))

      mock.verify()

    it "does not halve on critical hits", ->
      shared.create.call(this)
      tackle = @battle.getMove('Tackle')
      @team2.attach(Attachment.Reflect)
      @p1.crit = true
      tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

  describe "Light Screen", ->
    it "halves special damage", ->
      shared.create.call(this)
      thundershock = @battle.getMove('ThunderShock')
      thundershock.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

      @team2.attach(Attachment.LightScreen)
      thundershock.modifyDamage(@battle, @p1, @p2).should.equal(0x800)

    it "does not halve non-physical damage", ->
      shared.create.call(this)
      tackle = @battle.getMove('Tackle')
      tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

      @team2.attach(Attachment.LightScreen)
      tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

    it "lasts five turns", ->
      shared.create.call(this)

      @battle.performMove(@p1, @battle.getMove('Light Screen'))

      for i in [1..5]
        @team1.has(Attachment.LightScreen).should.be.true
        @battle.endTurn()
      @team1.has(Attachment.LightScreen).should.be.false

    it "fails if the user already used it", ->
      shared.create.call(this)
      mock = @sandbox.mock(@battle.getMove('Light Screen'))
      mock.expects('fail').once()

      @battle.performMove(@p1, @battle.getMove('Light Screen'))
      @battle.performMove(@p1, @battle.getMove('Light Screen'))

      mock.verify()

    it "does not halve on critical hits", ->
      shared.create.call(this)
      thundershock = @battle.getMove('ThunderShock')
      @team2.attach(Attachment.LightScreen)
      @p1.crit = true
      thundershock.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)

  describe "Rapid Spin", ->
    it "removes spikes", ->
      shared.create.call this

      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @team2.has(Attachment.Spikes).should.be.true

      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))
      @team2.has(Attachment.Spikes).should.be.false

    it "removes stealth rock", ->
      shared.create.call this

      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @team2.has(Attachment.StealthRock).should.be.true

      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))
      @team2.has(Attachment.StealthRock).should.be.false

    it "removes toxic spikes", ->
      shared.create.call this

      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @team2.has(Attachment.ToxicSpikes).should.be.true

      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))
      @team2.has(Attachment.ToxicSpikes).should.be.false

    it "removes multiple layers of entry hazards", ->
      shared.create.call this

      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @team2.has(Attachment.Spikes).should.be.true

      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))
      @team2.has(Attachment.Spikes).should.be.false

    it "removes trapping moves", ->
      shared.create.call this

      @battle.performMove(@p1, @battle.getMove("Fire Spin"))

      @p2.has(Attachment.Trap).should.be.true
      @p1.has(Attachment.TrapLeash).should.be.true
      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))
      @p2.has(Attachment.Trap).should.be.false
      @p1.has(Attachment.TrapLeash).should.be.false

    it "removes leech seed", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Leech Seed"))
      @p2.has(Attachment.LeechSeed).should.be.true
      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))
      @p2.has(Attachment.LeechSeed).should.be.false

    it "does not remove entry hazards if the user faints from rough skin", ->
      shared.create.call(this, team1: [Factory("Magikarp", ability: "Rough Skin")])

      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))

      @team2.has(Attachment.Spikes).should.be.true

    it "does remove entry hazards if the user faints from life orb", ->
      shared.create.call(this, team2: [Factory("Magikarp", item: "Life Orb")])

      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @battle.performMove(@p2, @battle.getMove("Rapid Spin"))

      @team2.has(Attachment.Spikes).should.be.false

  describe "Brick Break", ->
    it "shatters Light Screen", ->
      shared.create.call(this)
      @team2.attach(Attachment.LightScreen)
      @battle.performMove(@p1, @battle.getMove('Brick Break'))
      @team2.has(Attachment.LightScreen).should.be.false

    it "shatters Reflect", ->
      shared.create.call(this)
      @team2.attach(Attachment.Reflect)
      @battle.performMove(@p1, @battle.getMove('Brick Break'))
      @team2.has(Attachment.Reflect).should.be.false

    it "shatters both at the same time", ->
      shared.create.call(this)
      @team2.attach(Attachment.Reflect)
      @team2.attach(Attachment.LightScreen)
      @battle.performMove(@p1, @battle.getMove('Brick Break'))
      @team2.has(Attachment.Reflect).should.be.false
      @team2.has(Attachment.LightScreen).should.be.false

    it "shatters before damage calculation", ->
      shared.create.call(this)
      move = @battle.getMove("Brick Break")
      damage = move.calculateDamage(@battle, @p1, @p2)
      @team2.attach(Attachment.Reflect)
      @team2.attach(Attachment.LightScreen)

      spy = @sandbox.spy(move, 'calculateDamage')
      @battle.performMove(@p1, move)
      spy.returned(damage).should.be.true

    it "does not shatter if the target is immune", ->
      shared.create.call(this)
      @team2.attach(Attachment.Reflect)
      @team2.attach(Attachment.LightScreen)

      @sandbox.stub(@p2, 'isImmune', -> true)
      @battle.performMove(@p1, @battle.getMove('Brick Break'))
      @team2.has(Attachment.Reflect).should.be.true
      @team2.has(Attachment.LightScreen).should.be.true

    it "does not shatter if Brick Break misses", ->
      shared.create.call(this)
      @team2.attach(Attachment.Reflect)
      @team2.attach(Attachment.LightScreen)

      shared.biasRNG.call(this, 'randInt', 'miss', 101)
      @battle.performMove(@p1, @battle.getMove('Brick Break'))
      @team2.has(Attachment.Reflect).should.be.true
      @team2.has(Attachment.LightScreen).should.be.true

  describe "Return", ->
    it "has 102 base power", ->
      shared.create.call(this)
      move = @battle.getMove("Return")
      bp = move.basePower(@battle, @p1, @p2)
      bp.should.equal 102

  describe "Frustration", ->
    it "has 102 base power", ->
      shared.create.call(this)
      move = @battle.getMove("Frustration")
      bp = move.basePower(@battle, @p1, @p2)
      bp.should.equal 102

  describe "Fake Out", ->
    it "flinches the enemy", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Fake Out"))
      @p2.has(Attachment.Flinch).should.be.true

    it "fails if the Pokemon has been in play one turn or more", ->
      shared.create.call(this)
      @battle.endTurn()
      @battle.beginTurn()
      fakeOut = @battle.getMove("Fake Out")
      mock = @sandbox.mock(fakeOut).expects('fail').once()
      @battle.performMove(@p1, fakeOut)
      mock.verify()

    it "works if the Pokemon is newly switched", ->
      shared.create.call(this, team1: (Factory("Magikarp")  for x in [1..2]))
      @battle.performSwitch(@team1.first(), 1)
      @battle.endTurn()
      @battle.beginTurn()
      fakeOut = @battle.getMove("Fake Out")
      mock = @sandbox.mock(fakeOut).expects('fail').never()
      @battle.performMove(@team1.first(), fakeOut)
      mock.verify()

  describe "Focus Energy", ->
    it "adds a Focus Energy attachment to the user", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Focus Energy"))
      @p1.has(Attachment.FocusEnergy).should.be.true

    it "fails the second time it is used", ->
      shared.create.call(this)
      move = @battle.getMove("Focus Energy")
      mock = @sandbox.mock(move)
      mock.expects('fail').once()

      @battle.performMove(@p1, move)
      @battle.performMove(@p1, move)

      mock.verify()

  testIdentifyMove = (moveName, types) ->
    describe moveName, ->
      shared.shouldDoNoDamage(moveName)
      shared.shouldFailIfUsedTwice(moveName)

      it "makes the target vulnerable to #{types} moves", ->
        shared.create.call(this, team2: [Factory("Spiritomb")])
        _.each(types, ((type) -> @isImmune(type).should.be.true), @p2)
        @battle.performMove(@p1, @battle.getMove(moveName))
        @p2.has(Attachment.Identify).should.be.true
        _.each(types, ((type) -> @isImmune(type).should.be.false), @p2)

      it "makes the target's evasion be ignored", ->
        shared.create.call(this)
        @p2.boost(evasion: 2)
        @p2.editBoosts().evasion.should.equal 2
        @battle.performMove(@p1, @battle.getMove(moveName))
        @p2.editBoosts().evasion.should.equal 0

  testIdentifyMove("Foresight", ["Normal", "Fighting"])
  testIdentifyMove("Odor Sleuth", ["Normal", "Fighting"])
  testIdentifyMove("Miracle Eye", ["Psychic"])

  describe "Conversion", ->
    it "changes the user's type to a random type based on moves", ->
      shared.create.call this,
        team1: [Factory("Porygon", moves: [
          "Thunderbolt", "Ice Beam", "Conversion" ])]
      shared.biasRNG.call(this, "randInt", 'conversion types', 0)
      @p1.types.should.eql [ "Normal" ]
      @battle.performMove(@p1, @battle.getMove("Conversion"))
      @p1.types.should.eql [ 'Electric' ]

    it "ignores Conversion as part of the move types", ->
      shared.create.call this,
        team1: [Factory("Porygon", moves: [ "Conversion", "Ice Beam" ])]
      shared.biasRNG.call(this, "randInt", 'conversion types', 0)
      @p1.types = [ "Fake Type" ]
      @battle.performMove(@p1, @battle.getMove("Conversion"))
      @p1.types.should.eql [ 'Ice' ]

    it "fails if there is no type to convert to", ->
      shared.create.call this,
        team1: [Factory("Porygon", moves: [ "Conversion" ])]
      move = @battle.getMove("Conversion")
      mock = @sandbox.mock(move)
      mock.expects('fail').once()

      @battle.performMove(@p1, move)
      mock.verify()

  describe "Conversion 2", ->
    it "fails if the target has not yet moved", ->
      shared.create.call(this)
      move = @battle.getMove("Conversion 2")
      mock = @sandbox.mock(move).expects('fail').once()

      @p2.lastMove = null
      @battle.performMove(@p1, move)
      mock.verify()

    it "changes user's type to one resisting or is immune to target's move", ->
      shared.create.call(this)
      @p1.types = [ "Normal" ]

      move = @battle.getMove("Ember")
      type = move.type
      @p2.lastMove = move
      @battle.performMove(@p1, @battle.getMove("Conversion 2"))
      @p1.types.should.have.length 1
      util.typeEffectiveness(type, @p1.types).should.be.lessThan 1

  describe "Defense Curl", ->
    it "raises defense by 1 stage", ->
      shared.create.call(this)
      @p1.stages.defense.should.equal 0
      @battle.performMove(@p1, @battle.getMove("Defense Curl"))
      @p1.stages.defense.should.equal 1

    it "attaches a volatile attachment", ->
      shared.create.call(this)
      @p1.has(Attachment.DefenseCurl).should.be.false
      @battle.performMove(@p1, @battle.getMove("Defense Curl"))
      @p1.has(Attachment.DefenseCurl).should.be.true

  describe "Focus Punch", ->
    it "causes the user to flinch if hit", ->
      shared.create.call(this)
      @battle.recordMove(@id1, @battle.getMove("Focus Punch"))
      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.continueTurn()
      @p2.currentHP.should.not.be.lessThan @p2.stat('hp')

    it "does not cause flinching if hit by a non-damaging move", ->
      shared.create.call(this)
      @battle.recordMove(@id1, @battle.getMove("Focus Punch"))
      @battle.recordMove(@id2, @battle.getMove("Will-O-Wisp"))
      @battle.continueTurn()
      @p2.currentHP.should.be.lessThan @p2.stat('hp')

    it "does not cause flinching if behind a substitute", ->
      shared.create.call(this)
      @p1.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
      @battle.recordMove(@id1, @battle.getMove("Focus Punch"))
      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.continueTurn()
      @p2.currentHP.should.be.lessThan @p2.stat('hp')

    it "does not cause flinching if sub fades", ->
      shared.create.call(this)
      @p1.attach(Attachment.Substitute, hp: 1)
      @battle.recordMove(@id1, @battle.getMove("Focus Punch"))
      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.continueTurn()
      @p2.currentHP.should.be.lessThan @p2.stat('hp')

    it "does not cause flinching if hit by a move last turn", ->
      shared.create.call(this)
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @battle.endTurn()
      @battle.beginTurn()
      @battle.recordMove(@id1, @battle.getMove("Focus Punch"))
      @battle.continueTurn()
      @p2.currentHP.should.be.lessThan @p2.stat('hp')

    it "does not cause flinching afterwards", ->
      shared.create.call(this, team2: [Factory("Magikarp", evs: {speed: 4})])
      focusPunch = @battle.getMove("Focus Punch")
      @battle.recordMove(@id1, @battle.getMove("Focus Punch"))
      @battle.recordMove(@id2, @battle.getMove("Splash"))
      @battle.continueTurn()
      @battle.endTurn()
      @battle.beginTurn()

      hp = @p2.currentHP
      @battle.recordMove(@id1, @battle.getMove("Tackle"))
      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.continueTurn()
      @p2.currentHP.should.be.lessThan(hp)

  describe "Magnet Rise", ->
    shared.shouldDoNoDamage("Magnet Rise")
    shared.shouldFailIfUsedTwice("Magnet Rise")

    it "makes the user immune to ground moves", ->
      shared.create.call(this)
      @p1.isImmune("Ground").should.be.false
      @battle.performMove(@p1, @battle.getMove("Magnet Rise"))
      @p1.isImmune("Ground").should.be.true

    it "lasts 5 turns", ->
      shared.create.call(this)
      @p1.isImmune("Ground").should.be.false
      @battle.performMove(@p1, @battle.getMove("Magnet Rise"))
      for i in [1..5]
        @p1.isImmune("Ground").should.be.true
        @battle.endTurn()
      @p1.isImmune("Ground").should.be.false

  testLockOnMove = (moveName) ->
    describe moveName, ->
      shared.shouldDoNoDamage(moveName)
      shared.shouldFailIfUsedTwice(moveName)

      it "makes the user's next move never miss on this target", ->
        shared.create.call(this)
        @battle.performMove(@p1, @battle.getMove(moveName))
        shared.biasRNG.call(this, 'randInt', 'miss', 101)
        missMove = @battle.getMove("Tackle")
        missMove.willMiss(@battle, @p1, @p2)
          .should.be.false

      it "lasts only two turns", ->
        shared.create.call(this)
        @battle.performMove(@p1, @battle.getMove(moveName))
        for i in [1..2]
          @p1.has(Attachment.LockOn).should.be.true
          @battle.endTurn()
        @p1.has(Attachment.LockOn).should.be.false

      it "hits through two-turn fade-away moves", ->
        shared.create.call(this)
        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.performMove(@p2, @battle.getMove("Fly"))
        missMove = @battle.getMove("Tackle")
        missMove.willMiss(@battle, @p1, @p2)
          .should.be.false

      it "does not hit through Protect", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        mock = @sandbox.mock(move).expects('hit').never()

        @battle.recordMove(@id2, move)
        @battle.determineTurnOrder()
        @battle.performMove(@p1, @battle.getMove("Protect"))
        @battle.performMove(@p2, move)
        mock.verify()

      it "does not affect accuracy on another target", ->
        shared.create.call this,
          team2: [Factory("Magikarp"), Factory("Magikarp")]
        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.performSwitch(@p2, 1)
        shared.biasRNG.call(this, 'randInt', 'miss', 101)
        missMove = @battle.getMove("Tackle")
        missMove.willMiss(@battle, @p1, @team2.first())
          .should.be.true

      it "re-locks on when used on another target", ->
        shared.create.call this,
          team2: [Factory("Magikarp"), Factory("Magikarp")]
        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.performSwitch(@p2, 1)
        @battle.performMove(@p1, @battle.getMove(moveName))
        @p1.has(Attachment.LockOn).should.be.true

  testLockOnMove("Lock-On")
  testLockOnMove("Mind Reader")

  describe "Minimize", ->
    it "boosts the user's evasion by 2", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Minimize"))
      @p1.stages.evasion.should.equal 2

    it "adds a Minimize volatile attachment to the user", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Minimize"))
      @p1.has(Attachment.Minimize).should.be.true

  testStompMove = (moveName) ->
    describe moveName, ->
      it "doubles its base power when the target is minimized", ->
        shared.create.call(this)
        @p2.attach(Attachment.Minimize)
        move = @battle.getMove(moveName)
        bp = move.basePower(@battle, @p1, @p2)
        bp.should.equal(move.power * 2)

      it "has normal base power otherwise", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        bp = move.basePower(@battle, @p1, @p2)
        bp.should.equal(move.power)

      it "has a 30% chance to flinch", ->
        shared.create.call(this)
        shared.biasRNG.call(this, 'randInt', 'flinch', 0)  # 100% chance
        @battle.performMove(@p1, @battle.getMove(moveName))
        @p2.has(Attachment.Flinch).should.be.true

  testStompMove("Stomp")
  testStompMove("Steamroller")

  testMeanLookMove = (moveName) ->
    describe moveName, ->
      shared.shouldDoNoDamage(moveName)
      shared.shouldFailIfUsedTwice(moveName)

      it "blocks the target from switching", ->
        shared.create.call(this)
        @p2.isSwitchBlocked().should.be.false

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.beginTurn()
        @p2.isSwitchBlocked().should.be.true

      it "wears off if the user faints", ->
        shared.create.call(this)
        @p1.currentHP = 1

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.endTurn()
        @battle.beginTurn()

        @p2.isSwitchBlocked().should.be.true
        @p2.has(Attachment.MeanLook).should.be.true

        @battle.recordMove(@id2, @battle.getMove("Tackle"))

        @battle.continueTurn()
        @battle.endTurn()
        @battle.beginTurn()

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.MeanLook).should.be.false

      it "wears off if the user switches", ->
        shared.create.call(this, team1: [Factory("Blissey"), Factory("Magikarp")])

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.endTurn()

        @controller.makeSwitch(@id1, 1)
        @controller.makeMove(@id2, "Splash")

        @p2.isSwitchBlocked().should.be.false
        @p2.has(Attachment.MeanLook).should.be.false

  testMeanLookMove("Block")
  testMeanLookMove("Mean Look")
  testMeanLookMove("Spider Web")

  testRechargeMove = (moveName) ->
    describe moveName, ->
      it "blocks the target from switching the next turn", ->
        shared.create.call(this)
        @p1.isSwitchBlocked().should.be.false

        theMove = @battle.getMove(moveName)
        @sandbox.stub(theMove, 'baseDamage', -> 2)
        @battle.performMove(@p1, theMove)
        @battle.endTurn()
        @battle.beginTurn()
        @p1.isSwitchBlocked().should.be.true

      it "blocks the target from picking a new move the next turn", ->
        shared.create.call(this)

        theMove = @battle.getMove(moveName)
        @sandbox.stub(theMove, 'baseDamage', -> 2)
        @battle.performMove(@p1, theMove)
        @battle.endTurn()
        @battle.beginTurn()
        for move in @p1.moves
          @p1.isMoveBlocked(move).should.be.true

      it "automatically selects a special recharge move the next turn", ->
        shared.create.call(this)
        specialMove = @battle.getMove("Recharge")

        theMove = @battle.getMove(moveName)
        @sandbox.stub(theMove, 'baseDamage', -> 2)
        @battle.performMove(@p1, theMove)
        @battle.endTurn()
        @battle.beginTurn()

        @battle.requests.should.not.have.property @id1
        action = @battle.getAction(@p1)
        should.exist(action)
        action.should.have.property("move")
        action.move.should.equal(specialMove)

      it "prevents the user from moving the next turn", ->
        shared.create.call(this)

        spy = @sandbox.spy(@p1, 'beforeMove')
        theMove = @battle.getMove(moveName)
        @sandbox.stub(theMove, 'baseDamage', -> 2)
        @battle.performMove(@p1, theMove)
        @battle.endTurn()
        @battle.beginTurn()
        @battle.continueTurn()
        spy.returned(false).should.be.true

  testRechargeMove("Hyper Beam")
  testRechargeMove("Giga Impact")
  testRechargeMove("Blast Burn")
  testRechargeMove("Frenzy Plant")
  testRechargeMove("Hydro Cannon")
  testRechargeMove("Roar of Time")
  testRechargeMove("Rock Wrecker")

  describe 'Clear Smog', ->
    it 'removes all status boosts on the target pokemon', ->
      shared.create.call(this)
      # Create artificial boosts.
      @p2.stages.defense = -3
      @p2.stages.specialAttack = 4
      @battle.performMove(@p1, @battle.getMove("Clear Smog"))
      neutralBoosts = {
        attack: 0, defense: 0, specialAttack: 0, specialDefense: 0,
        speed: 0, evasion: 0, accuracy: 0
      }
      @p2.stages.should.eql neutralBoosts

    it "does not reset stats if it hits a substitute", ->
      shared.create.call(this)
      @p2.attach(Attachment.Substitute, hp: 1)
      # Create artificial boosts.
      @p2.stages.defense = -3
      @p2.stages.specialAttack = 4
      @battle.performMove(@p1, @battle.getMove("Clear Smog"))
      neutralBoosts = {
        attack: 0, defense: 0, specialAttack: 0, specialDefense: 0,
        speed: 0, evasion: 0, accuracy: 0
      }
      @p2.stages.should.not.eql neutralBoosts

  testMomentumMove = (moveName) ->
    describe moveName, ->
      it "prevents the user from switching", ->
        shared.create.call(this)
        @p1.isSwitchBlocked().should.be.false

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.endTurn()
        @battle.beginTurn()
        @p1.isSwitchBlocked().should.be.true

      it "locks the user into using this particular move", ->
        shared.create.call this,
          team1: [ Factory("Shuckle") ]

        momentumMove = @battle.getMove(moveName)
        @p1.moves = [ momentumMove, @battle.getMove("Rest") ]
        @p1.resetAllPP()

        @battle.performMove(@p1, momentumMove)
        @battle.endTurn()
        @battle.beginTurn()
        for move in @p1.moves
          if move == momentumMove
            @p1.isMoveBlocked(move).should.be.false
          else
            @p1.isMoveBlocked(move).should.be.true

      it "stops if it misses", ->
        shared.create.call(this)
        shared.biasRNG.call(this, "randInt", 'miss', 100)

        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.endTurn()
        @battle.beginTurn()
        @p1.has(Attachment.Momentum).should.be.false

      it "lasts 5 turns", ->
        shared.create.call(this)

        for i in [1..5]
          @battle.performMove(@p1, @battle.getMove(moveName))
          @p1.has(Attachment.Momentum).should.be.true
          @battle.endTurn()
          @battle.beginTurn()
        @p1.has(Attachment.Momentum).should.be.false

      it "doubles base power every time", ->
        shared.create.call(this)

        move = @battle.getMove(moveName)
        basePower = move.power
        for i in [1..5]
          bp = move.basePower(@battle, @p1, @p2)
          bp.should.equal(basePower * Math.pow(2, i - 1))
          @battle.performMove(@p1, move)
          @battle.endTurn()
          @battle.beginTurn()
          @p2.currentHP = @p2.stat('hp')

      it "doubles base power again if user has defense curl's effect", ->
        shared.create.call(this)

        @p1.attach(Attachment.DefenseCurl)

        move = @battle.getMove(moveName)
        basePower = 2 * move.power
        for i in [1..5]
          bp = move.basePower(@battle, @p1, @p2)
          bp.should.equal(basePower * Math.pow(2, i - 1))
          @battle.performMove(@p1, move)
          @battle.endTurn()
          @battle.beginTurn()
          @p2.currentHP = @p2.stat('hp')

  testMomentumMove("Rollout")
  testMomentumMove("Ice Ball")

  describe "Me First", ->
    it "fails if the user goes second", ->
      shared.create.call(this)

      move = @battle.getMove("Me First")
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.recordMove(@id1, move)
      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.determineTurnOrder()
      @battle.delay @p1
      @battle.popAction(@p2)
      @battle.performMove(@p1, move)

      mock.verify()

    it "uses the target's move", ->
      shared.create.call(this)

      move = @battle.getMove("Tackle")
      mock = @sandbox.mock(move).expects('execute').once()

      @battle.recordMove(@id1, @battle.getMove("Me First"))
      @battle.recordMove(@id2, move)
      @battle.determineTurnOrder()
      @battle.bump @p1
      @battle.performMove(@p1, @battle.getMove("Me First"))

      mock.verify()

    it "has 1.5x the base power of the target's move", ->
      shared.create.call(this)

      move = @battle.getMove("Tackle")

      @battle.recordMove(@id1, @battle.getMove("Me First"))
      @battle.recordMove(@id2, move)
      @battle.determineTurnOrder()
      @battle.bump @p1
      @battle.performMove(@p1, @battle.getMove("Me First"))

      move.modifyAttack(@battle, @p1, @p2).should.equal 0x1800

    for moveName in [ "Chatter", "Counter", "Covet", "Focus Punch", "Me First",
                      "Metal Burst", "Mirror Coat", "Struggle", "Thief" ]
      do (moveName) ->
        it "fails if the target is using #{moveName}", ->
          shared.create.call(this)

          move = @battle.getMove("Me First")
          mock = @sandbox.mock(move).expects('fail').once()

          @battle.recordMove(@id1, move)
          @battle.recordMove(@id2, @battle.getMove(moveName))
          @battle.determineTurnOrder()
          @battle.bump @p1
          @battle.performMove(@p1, move)

          mock.verify()

    it "fails if the target is using a non-damaging move", ->
      shared.create.call(this)

      move = @battle.getMove("Me First")
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.recordMove(@id1, move)
      @battle.recordMove(@id2, @battle.getMove("Splash"))
      @battle.determineTurnOrder()
      @battle.bump @p1
      @battle.performMove(@p1, move)

      mock.verify()

  describe "Dream Eater", ->
    it "fails if the target is not asleep", ->
      shared.create.call(this)
      move = @battle.getMove("Dream Eater")
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.performMove(@p1, move)

      mock.verify()

    it "does not fail if the target is asleep", ->
      shared.create.call(this)
      move = @battle.getMove("Dream Eater")
      mock = @sandbox.mock(move).expects('fail').never()

      @p2.attach(Status.Sleep)
      @battle.performMove(@p1, move)

      mock.verify()

    it "drains 1/2 of damage", ->
      shared.create.call(this)
      @p1.currentHP = initialHP = 1

      @p2.attach(Status.Sleep)
      @battle.performMove(@p1, @battle.getMove("Dream Eater"))

      damage = @p2.stat('hp') - @p2.currentHP
      healed = Math.floor(damage / 2)
      @p1.currentHP.should.equal(initialHP + healed)

  describe "Camouflage", ->
    it "changes the user's type to Ground type in Wi-Fi battles", ->
      shared.create.call(this)

      @p1.types = [ "Normal" ]
      @battle.performMove(@p1, @battle.getMove("Camouflage"))
      @p1.types.should.eql [ "Ground" ]

  describe "Charge", ->
    shared.shouldDoNoDamage("Charge")
    it "raises the user's special defense by 1", ->
      shared.create.call(this)

      @p1.stages.specialDefense.should.equal 0
      @battle.performMove(@p1, @battle.getMove("Charge"))
      @p1.stages.specialDefense.should.equal 1

    it "doubles the base power of the user's next move", ->
      shared.create.call(this)
      move = @battle.getMove("Thunderbolt")

      move.modifyAttack(@battle, @p1, @p2).should.equal 0x1000
      @battle.performMove(@p1, @battle.getMove("Charge"))
      move.modifyAttack(@battle, @p1, @p2).should.equal 0x2000

    it "doesn't double the next move if it is non-electric type", ->
      shared.create.call(this)
      move = @battle.getMove("Flamethrower")

      move.modifyAttack(@battle, @p1, @p2).should.equal 0x1000
      @battle.performMove(@p1, @battle.getMove("Charge"))
      move.modifyAttack(@battle, @p1, @p2).should.equal 0x1000

    it "can be used twice in a row", ->
      shared.create.call(this)
      move = @battle.getMove("Charge")
      mock = @sandbox.mock(move).expects('fail').never()

      @battle.performMove(@p1, move)
      @battle.endTurn()
      @battle.performMove(@p1, move)
      @battle.endTurn()

      @p1.has(Attachment.Charge).should.be.true
      mock.verify()

  describe "Tri Attack", ->
    it "has a 20% chance to activate its secondary effect", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", 'secondary effect', 0)  # 100% chance
      @battle.performMove(@p1, @battle.getMove("Tri Attack"))
      @p2.hasStatus().should.be.true

    it "has a 1/3 chance for the secondary effect to be paralysis", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", 'secondary effect', 0)  # 100% chance
      shared.biasRNG.call(this, "randInt", 'tri attack effect', 0)  # par
      @battle.performMove(@p1, @battle.getMove("Tri Attack"))
      @p2.has(Status.Paralyze).should.be.true

    it "has a 1/3 chance for the secondary effect to be burn", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", 'secondary effect', 0)  # 100% chance
      shared.biasRNG.call(this, "randInt", 'tri attack effect', 1)  # brn
      @battle.performMove(@p1, @battle.getMove("Tri Attack"))
      @p2.has(Status.Burn).should.be.true

    it "has a 1/3 chance for the secondary effect to be freeze", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", 'secondary effect', 0)  # 100% chance
      shared.biasRNG.call(this, "randInt", 'tri attack effect', 2)  # frz
      @battle.performMove(@p1, @battle.getMove("Tri Attack"))
      @p2.has(Status.Freeze).should.be.true

  describe "Mirror Move", ->
    it "copies the opponent's last move", ->
      shared.create.call(this)
      move = @battle.getMove("Tackle")
      mock = @sandbox.mock(move).expects('execute').once()
      @p2.lastMove = move

      @battle.performMove(@p1, @battle.getMove("Mirror Move"))
      mock.verify()

    it "fails if the opponent has not moved the past turn", ->
      shared.create.call(this)
      move = @battle.getMove("Mirror Move")
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.performMove(@p1, move)
      mock.verify()

    it "fails if the move does not have a `mirror` flag", ->
      shared.create.call(this)
      @p2.lastMove = @battle.getMove("Dragon Dance")
      move = @battle.getMove("Mirror Move")
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.performMove(@p1, move)
      mock.verify()

    # TODO: Find out if this is true: when is lastMove nullified?
    it "fails if the pokemon was unable to move the previous turn"

  describe "Leech Seed", ->
    shared.shouldFailIfUsedTwice("Leech Seed")

    it "saps 1/8 of the target's max HP each turn", ->
      shared.create.call(this)
      move = @battle.getMove("Leech Seed")

      @p1.currentHP = 1
      @battle.performMove(@p1, move)
      @battle.endTurn()

      p = @p2
      fullHP = p.stat('hp')
      (fullHP - p.currentHP).should.equal Math.floor(fullHP / 8)
      @p1.currentHP.should.equal(fullHP - p.currentHP + 1)

    it "saps up to 1/8 HP", ->
      shared.create.call(this)
      move = @battle.getMove("Leech Seed")

      @p1.currentHP = 1
      @p2.currentHP = 1
      @battle.performMove(@p1, move)
      @battle.endTurn()

      @p1.currentHP.should.equal 2

    it "saps a minimum of 1 damage", ->
      shared.create.call(this, team2: [Factory("Shedinja")])
      move = @battle.getMove("Leech Seed")

      @battle.performMove(@p1, move)
      @battle.endTurn()

      @p2.currentHP.should.equal(0)

    it "always misses on Grass type Pokemon", ->
      shared.create.call(this)
      move = @battle.getMove("Leech Seed")
      mock = @sandbox.mock(move).expects('afterMiss').once()
      @p2.types = [ "Water", "Grass"]

      @battle.performMove(@p1, move)
      mock.verify()

    it "does not trigger if the user has fainted", ->
      shared.create.call(this, team1: (Factory("Magikarp") for x in [1..2]))
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Leech Seed"))
      @p1.faint()
      @battle.endTurn()
      @p2.currentHP.should.equal @p2.stat('hp')

    it "leeches next pokemon to switch in at that slot", ->
      shared.create.call(this, team1: (Factory("Magikarp") for x in [1..2]))
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Leech Seed"))
      @p1.faint()
      @battle.endTurn()
      @battle.performSwitch(@team1.first(), 1)
      @p2.currentHP.should.equal @p2.stat('hp')

      newFirst = @team1.first()
      newFirst.currentHP = 1
      @battle.endTurn()
      hp = @p2.stat('hp') - @p2.currentHP
      hp.should.be.greaterThan(0)
      newFirst.currentHP.should.equal(hp + 1)

    it "stops if the target has fainted", ->
      shared.create.call(this, team2: (Factory("Magikarp") for x in [1..2]))
      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Leech Seed"))
      @p2.faint()
      @battle.endTurn()
      @battle.performSwitch(@team2.first(), 1)
      @team2.first().currentHP.should.equal @team2.first().stat('hp')
      @p1.currentHP.should.equal(1)

  testProtectCounterMove = (moveName) ->
    it "fails if the user moves last this turn", ->
      shared.create.call(this)
      move = @battle.getMove(moveName)
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.determineTurnOrder()
      @battle.performMove(@p1, move)
      mock.verify()

    it "has decreasing chances of success", ->
      shared.create.call(this)
      p = @p1

      for x in [0..7]
        attachment = p.attach(Attachment.ProtectCounter)
        attachment.successChance().should.equal Math.pow(2, x)

      attachment = p.attach(Attachment.ProtectCounter)
      attachment.successChance().should.equal Math.pow(2, 32)

    it "fails if not successful", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "randInt", 'protect', 2)
      move = @battle.getMove(moveName)
      mock = @sandbox.mock(move).expects('fail').once()

      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.determineTurnOrder()
      @battle.performMove(@p1, move)
      mock.verify()

    it "resets to 100% chance of success if move fails", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "randInt", 'protect', 2)
      move = @battle.getMove(moveName)

      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.determineTurnOrder()
      @battle.performMove(@p1, move)
      @p1.has(Attachment.ProtectCounter).should.be.false

    it "resets to 100% chance of success if user selects a different move", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "randInt", 'protect', 1)
      move = @battle.getMove(moveName)

      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.determineTurnOrder()
      @battle.performMove(@p1, move)
      @battle.endTurn()
      @p1.has(Attachment.ProtectCounter).should.be.true

      @battle.performMove(@p1, @battle.getMove('Splash'))
      @battle.endTurn()

      @p1.has(Attachment.ProtectCounter).should.be.false

  testProtectMove = (moveName) ->
    describe moveName, ->
      testProtectCounterMove(moveName)

      it "completely protects the user from attacks", ->
        shared.create.call(this)
        move = @battle.getMove("Tackle")
        mock = @sandbox.mock(move).expects('hit').never()

        @battle.recordMove(@id2, move)
        @battle.determineTurnOrder()
        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.performMove(@p2, move)
        mock.verify()

      it "does not carry over to the next turn", ->
        shared.create.call(this)
        move = @battle.getMove("Tackle")

        @battle.recordMove(@id2, move)
        @battle.determineTurnOrder()
        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.performMove(@p2, move)
        @battle.endTurn()

        mock = @sandbox.mock(move).expects('hit').once()
        @battle.performMove(@p2, move)
        mock.verify()

      it "does not protect the user from attacks without the protect flag", ->
        shared.create.call(this)
        move = @battle.getMove("Feint")
        mock = @sandbox.mock(move).expects('hit').once()

        @battle.recordMove(@id2, move)
        @battle.determineTurnOrder()
        @battle.performMove(@p1, @battle.getMove(moveName))
        @battle.performMove(@p2, move)
        mock.verify()

  testProtectMove 'Protect'
  testProtectMove 'Detect'

  describe "Endure", ->
    testProtectCounterMove("Endure")

    it "always survives moves that would otherwise KO with 1 HP", ->
      shared.create.call(this)
      move = @battle.getMove("Tackle")
      hp = @p1.currentHP
      @sandbox.stub(move, 'baseDamage', -> hp)

      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.determineTurnOrder()
      @battle.performMove(@p1, @battle.getMove("Endure"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p1.currentHP.should.equal 1

    it "disappears at the end of the turn", ->
      shared.create.call(this)

      @battle.recordMove(@id2, @battle.getMove("Tackle"))
      @battle.determineTurnOrder()
      @battle.performMove(@p1, @battle.getMove("Endure"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))

      @p1.has(Attachment.Endure).should.be.true
      @battle.endTurn()
      @p1.has(Attachment.Endure).should.be.false

  describe "Feint", ->
    it "removes the Protect attachment, if any, on the target", ->
      shared.create.call(this)
      @p1.attach(Attachment.Protect)
      @p1.has(Attachment.Protect).should.be.true

      @battle.performMove(@p2, @battle.getMove("Feint"))
      @p1.has(Attachment.Protect).should.be.false

    it "removes the Wide Guard attachment, if any, on the target"
    it "removes the Quick Guard attachment, if any, on the target"

  describe "Payback", ->
    it "doubles base power if target has made a move this turn", ->
      shared.create.call(this)
      move = @battle.getMove("Payback")
      spy  = @sandbox.spy(move, 'basePower')

      @battle.performMove(@p2, @battle.getMove("Splash"))
      @battle.performMove(@p1, move)
      spy.returned(2 * move.power).should.be.true

    it "doesn't double base power otherwise", ->
      shared.create.call(this)
      move = @battle.getMove("Payback")
      spy  = @sandbox.spy(move, 'basePower')

      @battle.recordMove(@id2, @battle.getMove("Splash"))
      @battle.performMove(@p1, move)
      spy.returned(move.power).should.be.true

    it "doesn't double BP if target moved last turn, but not this turn", ->
      shared.create.call(this)
      move = @battle.getMove("Payback")
      spy  = @sandbox.spy(move, 'basePower')

      @battle.performMove(@p2, @battle.getMove("Splash"))

      @battle.endTurn()
      @battle.beginTurn()

      @battle.recordMove(@id2, @battle.getMove("Splash"))
      @battle.performMove(@p1, move)

      spy.returned(move.power).should.be.true

  describe "Curse", ->
    it "silently chooses a random enemy as the target", ->
      shared.create.call(this)

      move = @battle.getMove("Curse")
      targets = move.getTargets(@battle, @p1)

      should.exist targets
      targets.should.eql [ @p2 ]

    describe "for Ghost types", ->
      it "curses the opponent", ->
        shared.create.call(this)
        @p1.types = [ "Ghost" ]

        @battle.performMove(@p1, @battle.getMove("Curse"))
        @p2.has(Attachment.Curse).should.be.true

      it "damages the user for half of its HP, rounded down", ->
        shared.create.call(this)
        p = @p1
        p.types = [ "Ghost" ]
        maxHP = p.stat('hp')

        @battle.performMove(@p1, @battle.getMove("Curse"))
        p.currentHP.should.equal(maxHP - Math.floor(maxHP / 2))

      it "causes the opponent to lose 25% of their HP at end of turns", ->
        shared.create.call(this)
        @p1.types = [ "Ghost" ]
        p = @p2
        maxHP = p.stat('hp')
        quarterHP = Math.floor(maxHP / 4)

        @battle.performMove(@p1, @battle.getMove("Curse"))
        p.currentHP.should.equal(maxHP)

        @battle.endTurn()
        p.currentHP.should.equal(maxHP - quarterHP)

        @battle.endTurn()
        p.currentHP.should.equal(maxHP - 2 * quarterHP)

      it "causes the opponent to lose a minimum of 1 HP per turn", ->
        shared.create.call(this, team2: [Factory("Shedinja"), Factory("Magikarp")])
        @p1.types = [ "Ghost" ]
        @battle.performMove(@p1, @battle.getMove("Curse"))
        @battle.endTurn()
        @p2.currentHP.should.equal(0)

      it "can faint the user", ->
        shared.create.call(this)
        p = @p1
        p.types = [ "Ghost" ]
        p.currentHP = 1

        @battle.performMove(@p1, @battle.getMove("Curse"))
        p.isFainted().should.be.true

    describe "for non-Ghost types", ->
      it "raises Attack and Defense and lowers Speed", ->
        shared.create.call(this)
        p = @p1
        p.types = [ "Normal" ]

        @battle.performMove(@p1, @battle.getMove("Curse"))
        p.stages.should.containEql attack: 1, defense: 1, speed: -1

  testBasePowerBoostMove = (moveName, rawBasePower, maxBasePower, which) ->
    describe moveName, ->
      it "increases base power by 20 for each positive stat boost on #{which}", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        poke = { user: @p1, target: @p2 }
        {user, target} = poke

        move.basePower(@battle, user, target).should.equal rawBasePower

        poke[which].boost(attack: 1)
        move.basePower(@battle, user, target).should.equal rawBasePower + 20

        poke[which].boost(defense: 1)
        move.basePower(@battle, user, target).should.equal rawBasePower + 40

        poke[which].boost(speed: 2, specialAttack: -1)
        move.basePower(@battle, user, target).should.equal rawBasePower + 80

      it "has a maximum of #{maxBasePower} base power", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        poke = { user: @p1, target: @p2 }
        {user, target} = poke

        # Total base power would theoretically be rawBasePower + 840
        stats = {
          speed: 6, specialAttack: 6, attack: 6,
          defense: 6, specialDefense: 6, evasion: 6, accuracy: 6
        }
        poke[which].boost(stats)
        move.basePower(@battle, user, target).should.equal maxBasePower

  testBasePowerBoostMove("Stored Power", 20, 860, "user")
  testBasePowerBoostMove("Punishment",   60, 200, "target")

  describe "Destiny Bond", ->
    it "causes the attacker to faint the turn of use if the user faints", ->
      shared.create.call(this)

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Destiny Bond"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p2.isFainted().should.be.true

    it "causes the attacker to faint any time before the user moves again", ->
      shared.create.call(this)

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Destiny Bond"))

      @battle.endTurn()
      @battle.beginTurn()
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p2.isFainted().should.be.true

    it "does not cause attacker to faint after user moves again", ->
      shared.create.call(this)

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Destiny Bond"))
      @battle.performMove(@p1, @battle.getMove("Splash"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p2.isFainted().should.be.false

    it "does not cause attacker to faint after user attempts to move again", ->
      shared.create.call(this)

      @p1.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("Destiny Bond"))

      @battle.endTurn()
      @battle.beginTurn()
      @p1.attach(Status.Sleep)
      @battle.performMove(@p1, @battle.getMove("Tackle"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p2.isFainted().should.be.false

    it "does not faint attacker if pokemon fainted naturally", ->
      shared.create.call(this)
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @battle.performMove(@p1, @battle.getMove("Destiny Bond"))
      @p1.faint()
      @battle.endTurn()
      @p2.isFainted().should.be.false

    it "does not faint attacker if opponent made move, but did not faint", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SAND)
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @battle.performMove(@p1, @battle.getMove("Destiny Bond"))
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p1.currentHP = 1
      @battle.endTurn()
      @p1.isFainted().should.be.true
      @p2.isFainted().should.be.false

    it "does not cause a party member to faint"
    it "handles 2+ pokemon destiny-bonding and all fainting at once"

  describe "Pursuit", ->
    it "doubles base power if the target switches", ->
      shared.create.call this,
        team2: [ Factory("Magikarp"), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      spy = @sandbox.spy(pursuit, 'basePower')

      @battle.recordSwitch(@id2, 1)
      @battle.recordMove(@id1, pursuit)
      @battle.continueTurn()
      spy.alwaysReturned(2 * pursuit.power).should.be.true

    it "doubles BP if a faster target uses a damaging switch move", ->
      shared.create.call this,
        team2: [ Factory("Magikarp", evs: {speed: 4}), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      spy = @sandbox.spy(pursuit, 'basePower')

      @battle.recordMove(@id2, @battle.getMove("U-turn"))
      @battle.recordMove(@id1, pursuit)
      @battle.continueTurn()
      @battle.recordSwitch(@id2, 1)  # battle.forceSwitch makes a request
      @battle.continueTurn()
      spy.alwaysReturned(2 * pursuit.power).should.be.true

    it "doesn't double BP if a slower target uses a damaging switch move", ->
      shared.create.call this,
        team1: [ Factory("Magikarp", evs: {speed: 4}) ]
        team2: [ Factory("Magikarp"), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      spy = @sandbox.spy(pursuit, 'basePower')

      @battle.recordMove(@id1, pursuit)
      @battle.recordMove(@id2, @battle.getMove("U-turn"))
      @battle.continueTurn()
      @battle.recordSwitch(@id2, 1)  # battle.forceSwitch makes a request
      @battle.continueTurn()
      spy.alwaysReturned(pursuit.power).should.be.true

    it "has perfect accuracy if target is switching", ->
      shared.create.call this,
        team2: [ Factory("Magikarp"), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      spy = @sandbox.spy(pursuit, 'chanceToHit')

      pursuit = @battle.getMove("Pursuit")
      @battle.recordSwitch(@id2, 1)
      @battle.recordMove(@id1, pursuit)
      @battle.continueTurn()
      spy.alwaysReturned(0).should.be.true

    it "runs only once", ->
      shared.create.call this,
        team2: [ Factory("Magikarp"), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      mock = @sandbox.mock(pursuit).expects('execute').once()

      pursuit = @battle.getMove("Pursuit")
      @battle.recordSwitch(@id2, 1)
      @battle.recordMove(@id1, pursuit)
      @battle.continueTurn()
      @battle.recordSwitch(@id2, 1)  # battle.forceSwitch makes a request
      @battle.continueTurn()
      mock.verify()

    it "doesn't double BP on a faster Baton Passer", ->
      shared.create.call this,
        team1: [ Factory("Magikarp") ]
        team2: [ Factory("Magikarp", evs: {speed: 4}), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      spy = @sandbox.spy(pursuit, 'basePower')

      @battle.recordMove(@id1, pursuit)
      @battle.recordMove(@id2, @battle.getMove("Baton Pass"))
      @battle.continueTurn()
      @battle.recordSwitch(@id2, 1)  # battle.forceSwitch makes a request
      @battle.continueTurn()
      spy.alwaysReturned(pursuit.power).should.be.true

    it "emits the move name when target switches", ->
      shared.create.call this,
        team1: [ Factory("Magikarp") ]
        team2: [ Factory("Magikarp", evs: {speed: 4}), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      spy = @sandbox.spy(@battle, 'tell')

      @battle.recordMove(@id1, pursuit)
      @battle.recordSwitch(@id2, 1)
      @battle.continueTurn()
      spy.calledWith(Protocol.MAKE_MOVE).should.be.true

    it "records the move when target switches", ->
      shared.create.call this,
        team1: [ Factory("Magikarp") ]
        team2: [ Factory("Magikarp", evs: {speed: 4}), Factory("Magikarp") ]
      pursuit = @battle.getMove("Pursuit")
      mock = @sandbox.mock(@p1).expects('recordMove').once()

      @battle.recordMove(@id1, pursuit)
      @battle.recordSwitch(@id2, 1)
      @battle.continueTurn()
      mock.verify()

    it "does not trigger if asleep", ->
      shared.create.call(this)

      @p1.attach(Status.Sleep, turns: 4)
      pursuit = @battle.getMove("Pursuit")
      mock = @sandbox.mock(pursuit).expects('hit').never()

      @battle.recordMove(@id1, pursuit)
      @battle.recordSwitch(@id2, 1)
      @battle.continueTurn()
      mock.verify()

    it "does not trigger on team members"

  describe 'Power Swap', ->
    it 'swaps attack and special attack boosts with the target', ->
      shared.create.call(this)
      @p1.boost(attack: 1, specialAttack: -3, speed: 1)
      @p2.boost(attack: 2, specialAttack: 6, defense: -1)

      @battle.performMove(@p1, @battle.getMove('Power Swap'))

      @p1.stages.should.containEql attack: 2, specialAttack: 6, speed: 1
      @p2.stages.should.containEql attack: 1, specialAttack: -3, defense: -1

  describe 'Guard Swap', ->
    it 'swaps defense and special defense boosts with the target', ->
      shared.create.call(this)
      @p1.boost(attack: 1, specialDefense: -3, defense: 1)
      @p2.boost(speed: 2, specialDefense: 6, defense: -1)

      @battle.performMove(@p1, @battle.getMove('Guard Swap'))

      @p1.stages.should.containEql defense: -1, specialDefense: 6, attack: 1
      @p2.stages.should.containEql defense: 1, specialDefense: -3, speed: 2

  describe 'Spite', ->
    it 'reduces the last move used by the target by 4', ->
      shared.create.call(this)

      move = @p2.moves[0]
      pp = @p2.pp(move)
      @battle.performMove(@p2, move)
      @battle.performMove(@p1, @battle.getMove('Spite'))
      @p2.pp(move).should.equal(pp - 4 - 1)

    it 'fails if the target has not recorded their last move', ->
      shared.create.call(this)
      spite = @battle.getMove('Spite')
      mock = @sandbox.mock(spite).expects('fail').once()

      @battle.performMove(@p1, spite)
      mock.verify()

    it "fails if the target's move has 0 PP", ->
      shared.create.call(this)
      spite = @battle.getMove('Spite')
      move = @p2.moves[0]
      mock = @sandbox.mock(spite).expects('fail').once()
      @p2.setPP(move, 0)

      @battle.performMove(@p2, move)
      @battle.performMove(@p1, spite)
      mock.verify()

    it 'fails if the target no longer knows the move', ->
      shared.create.call(this)
      spite = @battle.getMove('Spite')
      move = @p2.moves[0]
      mock = @sandbox.mock(spite).expects('fail').once()

      @battle.performMove(@p2, move)
      @p2.moves.splice(@p2.moves.indexOf(move), 1)
      @battle.performMove(@p1, spite)
      mock.verify()

  describe 'Wring Out', ->
    it 'has 120 power at maximum HP', ->
      shared.create.call(this)
      move = @battle.getMove('Wring Out')
      move.basePower(@battle, @p1, @p2).should.equal 120

    it 'has 59 power at half-of-odd HP (rounded down)', ->
      shared.create.call(this)
      @p1.currentHP >>= 1
      move = @battle.getMove('Wring Out')
      move.basePower(@battle, @p1, @p2).should.equal 59

    it 'has 1 power minimum', ->
      shared.create.call(this)
      @p1.currentHP = 1
      move = @battle.getMove('Wring Out')
      move.basePower(@battle, @p1, @p2).should.equal 1

  describe 'Assurance', ->
    it 'doubles base power if the user was damaged this turn', ->
      shared.create.call(this)
      move = @battle.getMove('Assurance')

      @battle.performMove(@p2, @battle.getMove('Tackle'))
      power = move.basePower(@battle, @p1, @p2)
      power.should.equal(2 * move.power)

    it "doesn't double base power if the user was damaged a different turn", ->
      shared.create.call(this)
      move = @battle.getMove('Assurance')

      @battle.performMove(@p2, @battle.getMove('Tackle'))
      @battle.endTurn()
      @battle.beginTurn()
      power = move.basePower(@battle, @p1, @p2)
      power.should.equal(move.power)

    it "doesn't double base power if the user has never used a move", ->
      shared.create.call(this)
      move = @battle.getMove('Assurance')
      power = move.basePower(@battle, @p1, @p2)
      power.should.equal(move.power)

    it "doesn't double base power if hit by a non-damaging move", ->
      shared.create.call(this)
      move = @battle.getMove('Assurance')
      @battle.performMove(@p2, @battle.getMove('Will-O-Wisp'))
      power = move.basePower(@battle, @p1, @p2)
      power.should.equal(move.power)

  describe "Substitute", ->
    shared.shouldDoNoDamage("Substitute")
    shared.shouldFailIfUsedTwice("Substitute")

    it "removes 25% of the owner's health, rounded down", ->
      shared.create.call(this)
      sub = @battle.getMove('Substitute')
      hp  = @p1.stat('hp')
      @battle.performMove(@p1, sub)
      @p1.currentHP.should.equal(hp - (hp >> 2))

    it "fails if the pokemon has 25% HP or less", ->
      shared.create.call(this)
      sub  = @battle.getMove('Substitute')
      hp   = @p1.stat('hp')
      mock = @sandbox.mock(sub).expects('fail').once()
      @p1.currentHP = hp >> 2
      @battle.performMove(@p1, sub)
      mock.verify()

    it "fails if the pokemon does not have enough total HP", ->
      shared.create.call(this, team1: [Factory("Shedinja")])
      sub  = @battle.getMove('Substitute')
      hp   = @p1.stat('hp')
      mock = @sandbox.mock(sub).expects('fail').once()
      @battle.performMove(@p1, sub)
      mock.verify()

    it "takes damage for the user", ->
      shared.create.call(this)
      sub   = @battle.getMove('Substitute')
      subHP = (@p1.stat('hp') >> 2)
      @battle.performMove(@p1, sub)
      hp    = @p1.currentHP

      attachment = @p1.get(Attachment.Substitute)
      attachment.hp.should.equal subHP
      @battle.performMove(@p2, @battle.getMove('Tackle'))

      attachment.hp.should.be.lessThan subHP
      @p1.currentHP.should.equal hp

    it "breaks after taking too much damage", ->
      shared.create.call(this)
      tackle = @battle.getMove('Tackle')
      sub    = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      hp     = @p1.currentHP
      @p1.has(Attachment.Substitute).should.be.true

      @sandbox.stub(tackle, 'baseDamage', -> 9999)
      @battle.performMove(@p2, tackle)
      @p1.has(Attachment.Substitute).should.be.false
      @p1.currentHP.should.equal hp

    it "fails most non-damaging moves", ->
      shared.create.call(this)
      hypnosis = @battle.getMove('Hypnosis')
      sub      = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      @p1.has(Attachment.Substitute).should.be.true

      mock = @sandbox.mock(hypnosis).expects('fail').once()
      @battle.performMove(@p2, hypnosis)
      mock.verify()

    it "does not fail stat-up moves", ->
      shared.create.call(this)
      dragonDance = @battle.getMove('Dragon Dance')
      sub         = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      @p1.has(Attachment.Substitute).should.be.true

      mock = @sandbox.mock(dragonDance).expects('fail').never()
      @battle.performMove(@p1, dragonDance)
      mock.verify()

    it "does not fail non-damaging moves with an authentic flag", ->
      shared.create.call(this)
      foresight = @battle.getMove('Foresight')
      sub       = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      @p1.has(Attachment.Substitute).should.be.true

      mock = @sandbox.mock(foresight).expects('fail').never()
      @battle.performMove(@p2, foresight)
      mock.verify()

    it "still stores the actual damage", ->
      shared.create.call(this)
      gigaDrain = @battle.getMove('Giga Drain')
      sub       = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      @p1.has(Attachment.Substitute).should.be.true

      spy = @sandbox.spy(@p2, 'drain')
      @battle.performMove(@p2, gigaDrain)
      spy.calledWith(0).should.be.false
      @p1.lastHitBy.damage.should.be.greaterThan 0

    it "blocks secondary effects", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", "secondary effect", 0)  # always burn
      flamethrower = @battle.getMove('Flamethrower')
      sub          = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      @p1.has(Attachment.Substitute).should.be.true

      @sandbox.stub(flamethrower, 'baseDamage', -> 1)
      @battle.performMove(@p2, flamethrower)
      @p1.has(Status.Burn).should.be.false

    it "blocks secondary effects even if sub fades", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", "secondary effect", 0)  # always burn
      flamethrower = @battle.getMove('Flamethrower')
      sub          = @battle.getMove('Substitute')

      @battle.performMove(@p1, sub)
      @p1.has(Attachment.Substitute).should.be.true

      @sandbox.stub(flamethrower, 'baseDamage', -> 9999)
      @battle.performMove(@p2, flamethrower)
      @p1.has(Status.Burn).should.be.false

    it "does not block sandstorm damage", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SAND)
      @battle.performMove(@p1, @battle.getMove('Substitute'))

      hp = @p1.currentHP
      @battle.endTurn()
      @p1.currentHP.should.be.lessThan(hp)

    it "does not block leech seed damage", ->
      shared.create.call(this)
      @battle.performMove(@p2, @battle.getMove('Leech Seed'))
      @battle.performMove(@p1, @battle.getMove('Substitute'))

      hp = @p1.currentHP
      @battle.endTurn()
      @p1.currentHP.should.be.lessThan(hp)

    it "is baton-passable"

  describe "Sucker Punch", ->
    it "fails if the target is not moving after the user", ->
      shared.create.call(this)
      suckerPunch = @battle.getMove('Sucker Punch')

      mock = @sandbox.mock(suckerPunch).expects('fail').once()
      @battle.performMove(@p1, suckerPunch)
      mock.verify()

    it "executes normally if the target is moving after the user", ->
      shared.create.call(this)
      suckerPunch = @battle.getMove('Sucker Punch')
      tackle = @battle.getMove('Tackle')

      mock = @sandbox.mock(suckerPunch).expects('afterSuccessfulHit').once()
      @battle.recordMove(@id2, tackle)
      @battle.determineTurnOrder()
      @battle.performMove(@p1, suckerPunch)
      mock.verify()

    it "fails if the target is using a status move", ->
      shared.create.call(this)
      suckerPunch = @battle.getMove('Sucker Punch')
      willOWisp = @battle.getMove('Will-O-Wisp')

      mock = @sandbox.mock(suckerPunch).expects('fail').once()
      @battle.recordMove(@id2, willOWisp)
      @battle.determineTurnOrder()
      @battle.performMove(@p1, suckerPunch)
      mock.verify()

  describe 'Grudge', ->
    it "causes the last move to lose all its PP if the user faints", ->
      shared.create.call(this)
      grudge = @battle.getMove("Grudge")
      tackle = @battle.getMove('Tackle')
      @p2.moves = [ tackle ]

      @p1.currentHP = 1
      @battle.performMove(@p1, grudge)
      @battle.performMove(@p2, tackle)
      @battle.performFaints()
      @p1.isFainted().should.be.true
      @p2.pp(tackle).should.equal 0

    it "causes the attacker to lose PP any time before the user moves again", ->
      shared.create.call(this)
      grudge = @battle.getMove("Grudge")
      tackle = @battle.getMove('Tackle')
      @p2.moves = [ tackle ]

      @p1.currentHP = 1
      @battle.performMove(@p1, grudge)
      @battle.endTurn()
      @battle.beginTurn()

      @battle.performMove(@p2, tackle)
      @battle.performFaints()
      @p2.pp(tackle).should.equal 0

    it "does not cause attacker to lose PP after user moves again", ->
      shared.create.call(this)
      grudge = @battle.getMove("Grudge")
      splash = @battle.getMove('Splash')
      tackle = @battle.getMove('Tackle')
      @p2.moves = [ tackle ]

      @p1.currentHP = 1
      @battle.performMove(@p1, grudge)
      @battle.performMove(@p1, splash)
      @battle.performMove(@p2, tackle)
      @battle.performFaints()
      @p2.pp(tackle).should.not.equal 0

    it "does not trigger from natural causes", ->
      shared.create.call(this)
      grudge = @battle.getMove("Grudge")
      splash = @battle.getMove('Splash')
      leechSeed = @battle.getMove('Leech Seed')
      @p2.moves = [ leechSeed ]
      @p2.resetAllPP()

      @p1.currentHP = 1
      @battle.performMove(@p1, grudge)
      @battle.performMove(@p2, leechSeed)
      @battle.endTurn()
      @p2.pp(leechSeed).should.equal(@p2.maxPP(leechSeed) - 1)

  describe "Stockpile", ->
    it "raises def and spdef", ->
      shared.create.call(this)
      stockpile = @battle.getMove("Stockpile")

      @p1.stages.should.containEql defense: 0, specialDefense: 0
      @battle.performMove(@p1, stockpile)
      @p1.stages.should.containEql defense: 1, specialDefense: 1

    it "cannot raise if stockpile is at its limit", ->
      shared.create.call(this)
      stockpile = @battle.getMove("Stockpile")

      for i in [0...Attachment.Stockpile::maxLayers]
        @p1.attach(Attachment.Stockpile)

      @p1.stages.should.containEql defense: 0, specialDefense: 0
      @battle.performMove(@p1, stockpile)
      @p1.stages.should.containEql defense: 0, specialDefense: 0

    it "fails if stockpile is at its limit", ->
      shared.create.call(this)
      stockpile = @battle.getMove("Stockpile")
      mock = @sandbox.mock(stockpile).expects('fail').once()

      for i in [0...Attachment.Stockpile::maxLayers]
        @p1.attach(Attachment.Stockpile)

      @battle.performMove(@p1, stockpile)
      mock.verify()

  describe 'Spit Up', ->
    it 'fails if user has no stockpiles', ->
      shared.create.call(this)
      spitUp = @battle.getMove("Spit Up")
      mock = @sandbox.mock(spitUp).expects('fail').once()

      @battle.performMove(@p1, spitUp)
      mock.verify()

    it "has 100 base power with 1 layer of stockpiles", ->
      shared.create.call(this)
      spitUp = @battle.getMove("Spit Up")
      @p1.attach(Attachment.Stockpile)

      spitUp.basePower(@battle, @p1, @p2).should.equal 100

    it "has 200 base power with 2 layers of stockpiles", ->
      shared.create.call(this)
      spitUp = @battle.getMove("Spit Up")
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)

      spitUp.basePower(@battle, @p1, @p2).should.equal 200

    it "has 300 base power with 3 layers of stockpiles", ->
      shared.create.call(this)
      spitUp = @battle.getMove("Spit Up")
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)

      spitUp.basePower(@battle, @p1, @p2).should.equal 300

    it "resets stockpile count to 0", ->
      shared.create.call(this)
      spitUp = @battle.getMove("Spit Up")
      @p1.attach(Attachment.Stockpile)

      @p1.get(Attachment.Stockpile).layers.should.equal 1
      @battle.performMove(@p1, spitUp)
      @p1.has(Attachment.Stockpile).should.be.false

    it "loses def/sp. def according to number of stockpiles", ->
      shared.create.call(this)
      spitUp = @battle.getMove("Spit Up")
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)

      @p1.stages.should.containEql defense: 0, specialDefense: 0
      @battle.performMove(@p1, spitUp)
      @p1.stages.should.containEql defense: -2, specialDefense: -2

  describe 'Swallow', ->
    it 'fails if user has no stockpiles', ->
      shared.create.call(this)
      swallow = @battle.getMove("Swallow")
      mock = @sandbox.mock(swallow).expects('fail').once()

      @battle.performMove(@p1, swallow)
      mock.verify()

    it "heals 25% of its HP, rounded half-down, with 1 layer of stockpiles", ->
      shared.create.call(this)
      swallow = @battle.getMove("Swallow")
      @p1.attach(Attachment.Stockpile)

      @p1.currentHP = 1
      @battle.performMove(@p1, swallow)
      @p1.currentHP.should.equal(1 + util.roundHalfDown(@p1.stat('hp') / 4))

    it "heals 50% of its HP, rounded half-down, with 2 layers of stockpiles", ->
      shared.create.call(this)
      swallow = @battle.getMove("Swallow")
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)

      @p1.currentHP = 1
      @battle.performMove(@p1, swallow)
      @p1.currentHP.should.equal(1 + util.roundHalfDown(@p1.stat('hp') / 2))

    it "heals all of its HP with 3 layers of stockpiles", ->
      shared.create.call(this)
      swallow = @battle.getMove("Swallow")
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)

      @p1.currentHP = 1
      @battle.performMove(@p1, swallow)
      @p1.currentHP.should.equal @p1.stat('hp')

    it "resets stockpile count to 0", ->
      shared.create.call(this)
      swallow = @battle.getMove("Swallow")
      @p1.attach(Attachment.Stockpile)

      @p1.get(Attachment.Stockpile).layers.should.equal 1
      @battle.performMove(@p1, swallow)
      @p1.has(Attachment.Stockpile).should.be.false

    it "loses def/sp. def according to number of stockpiles", ->
      shared.create.call(this)
      swallow = @battle.getMove("Swallow")
      @p1.attach(Attachment.Stockpile)
      @p1.attach(Attachment.Stockpile)

      @p1.stages.should.containEql defense: 0, specialDefense: 0
      @battle.performMove(@p1, swallow)
      @p1.stages.should.containEql defense: -2, specialDefense: -2

  describe 'Rage', ->
    it "raises the user's attack if hit by a move", ->
      shared.create.call(this)
      rage = @battle.getMove('Rage')
      tackle = @battle.getMove('Tackle')

      @p1.stages.attack.should.equal 0
      @battle.performMove(@p1, rage)
      @battle.performMove(@p2, tackle)
      @p1.stages.attack.should.equal 1

    it "doesn't raise the user's attack if user chooses another move", ->
      shared.create.call(this)
      rage = @battle.getMove('Rage')
      tackle = @battle.getMove('Tackle')

      @p1.stages.attack.should.equal 0
      @battle.performMove(@p1, rage)
      @battle.performMove(@p1, tackle)
      @battle.performMove(@p2, tackle)
      @p1.stages.attack.should.equal 0

    it "doesn't raise the user's attack if hit by a non-damaging move", ->
      shared.create.call(this)
      rage = @battle.getMove('Rage')
      willOWisp = @battle.getMove('Will-O-Wisp')

      @p1.stages.attack.should.equal 0
      @battle.performMove(@p1, rage)
      @battle.performMove(@p2, willOWisp)
      @p1.stages.attack.should.equal 0

  testRevengeMove = (moveName) ->
    describe moveName, ->
      it "doubles base power if hit by an attack that turn", ->
        shared.create.call(this)
        revenge = @battle.getMove(moveName)
        tackle  = @battle.getMove("Tackle")

        @battle.performMove(@p2, tackle)
        revenge.basePower(@battle, @p1, @p2).should.equal(2 * revenge.power)

      it "doesn't double base power if not hit by an attack that turn", ->
        shared.create.call(this)
        revenge = @battle.getMove(moveName)
        revenge.basePower(@battle, @p1, @p2).should.equal(revenge.power)

      it "doesn't double base power if hit by a non-damaging move", ->
        shared.create.call(this)
        revenge = @battle.getMove(moveName)
        willOWisp  = @battle.getMove("Will-O-Wisp")

        @battle.performMove(@p2, willOWisp)
        revenge.basePower(@battle, @p1, @p2).should.equal(revenge.power)

      it "doesn't double base power if hit last turn, but not this turn", ->
        shared.create.call(this)
        revenge = @battle.getMove(moveName)
        tackle  = @battle.getMove("Tackle")

        @battle.performMove(@p2, tackle)
        @battle.beginTurn()
        revenge.basePower(@battle, @p1, @p2).should.equal(revenge.power)

  testRevengeMove("Avalanche")
  testRevengeMove("Revenge")

  testIgnoreStagesMove = (moveName) ->
    describe moveName, ->
      it "ignores the target's defensive and evasive stat stages", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        stats = [ 'defense', 'specialDefense', 'evasion' ]
        values = ( @p2.stat(stat)  for stat in stats )
        spy = @sandbox.spy(@p2, 'stat')
        spy.withArgs(stat)  for stat in stats

        @p2.boost(defense: -3, specialDefense: 2, evasion: 6)
        @battle.performMove(@p1, move)

        for i in [0...stats.length]
          stat  = stats[i]
          value = values[i]
          continue  if spy.withArgs(stat).returnValues.length == 0
          spy.withArgs(stat).alwaysReturned(value).should.be.true

  testIgnoreStagesMove("Chip Away")
  testIgnoreStagesMove("Sacred Sword")

  describe 'Captivate', ->
    it 'fails if the user and target are not opposite genders', ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "M")]
        team2: [Factory("Magikarp", gender: "M")]
      captivate = @battle.getMove('Captivate')
      mock = @sandbox.mock(captivate).expects('fail').once()

      @battle.performMove(@p1, captivate)
      mock.verify()

    it "lowers the target's special attack by 2", ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "M")]
        team2: [Factory("Magikarp", gender: "F")]
      captivate = @battle.getMove('Captivate')

      @p2.stages.specialAttack.should.equal 0
      @battle.performMove(@p1, captivate)
      @p2.stages.specialAttack.should.equal -2

  testStatusCureAttackMove = (moveName, status) ->
    describe moveName, ->
      it "doubles base power if the target has #{status.name}", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)

        @p2.attach(status)
        move.basePower(@battle, @p1, @p2).should.equal(2 * move.power)

      it "doesn't double if the target doesn't have #{status.name}", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)

        move.basePower(@battle, @p1, @p2).should.equal(move.power)

      it "cures the target of #{status.name}", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)

        @p2.attach(status)
        @p2.has(status).should.be.true
        @battle.performMove(@p1, move)
        @p2.hasStatus().should.be.false

      it "does not cure #{status.name} if the user faints", ->
        shared.create.call(this, team2: [Factory("Magikarp", ability: "Iron Barbs")])

        move = @battle.getMove(moveName)

        @p1.currentHP = 1
        @p2.attach(status)
        @p2.has(status).should.be.true
        @battle.performMove(@p1, move)
        @p2.hasStatus().should.be.true

  testStatusCureAttackMove("Wake-Up Slap", Status.Sleep)
  testStatusCureAttackMove("SmellingSalt", Status.Paralyze)

  describe 'Aqua Ring', ->
    it "restores 1/16 max HP at the end of each turn", ->
      shared.create.call(this)
      aquaRing = @battle.getMove('Aqua Ring')
      delta = @p1.currentHP >> 4

      @p1.currentHP = 1
      @battle.performMove(@p1, aquaRing)
      @p1.currentHP.should.equal(1)
      @battle.endTurn()
      @p1.currentHP.should.equal(1 + delta)

  describe 'Ingrain', ->
    it "restores 1/16 max HP at the end of each turn", ->
      shared.create.call(this)
      ingrain = @battle.getMove('Ingrain')
      delta = @p1.currentHP >> 4

      @p1.currentHP = 1
      @battle.performMove(@p1, ingrain)
      @p1.currentHP.should.equal(1)
      @battle.endTurn()
      @p1.currentHP.should.equal(1 + delta)

    it "prevents switching", ->
      shared.create.call(this)
      ingrain = @battle.getMove('Ingrain')

      @battle.performMove(@p1, ingrain)
      @battle.beginTurn()
      @p1.isSwitchBlocked().should.be.true

    it "prevents self from being phased", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      ingrain = @battle.getMove('Ingrain')
      whirlwind = @battle.getMove('Whirlwind')

      @battle.performMove(@p1, ingrain)

      mock = @sandbox.mock(@team1).expects('switch').never()
      @battle.performMove(@p2, whirlwind)
      mock.verify()

    it "lets self be switched out if using self-switching moves", ->
      shared.create.call(this)
      ingrain = @battle.getMove('Ingrain')
      uTurn = @battle.getMove('U-turn')

      @battle.performMove(@p1, ingrain)

      mock = @sandbox.mock(@battle).expects('requestActions').once()
      @battle.performMove(@p1, uTurn)
      mock.verify()

    it "makes self vulnerable to Ground moves", ->
      shared.create.call(this, team1: [ Factory("Gyarados") ])
      ingrain = @battle.getMove('Ingrain')
      uTurn = @battle.getMove('U-turn')

      @p1.isImmune('Ground').should.be.true
      @battle.performMove(@p1, ingrain)
      @p1.isImmune('Ground').should.be.false

    it "causes Telekinesis to fail", ->
      shared.create.call(this)
      ingrain = @battle.getMove('Ingrain')
      telekinesis = @battle.getMove('Telekinesis')

      @battle.performMove(@p1, ingrain)

      mock = @sandbox.mock(telekinesis).expects('fail').once()
      @battle.performMove(@p2, telekinesis)
      mock.verify()

  describe "Embargo", ->
    it "prevents the target's use of items", ->
      shared.create.call this,
        team2: [ Factory("Magikarp", item: "Leftovers") ]
      embargo = @battle.getMove('Embargo')
      mock = @sandbox.mock(@p2.get(@p2.item)).expects('endTurn').never()

      @battle.performMove(@p1, embargo)
      @battle.endTurn()
      mock.verify()

    it "lasts 5 turns", ->
      shared.create.call this,
        team2: [ Factory("Magikarp", item: "Leftovers") ]
      embargo = @battle.getMove('Embargo')
      mock = @sandbox.mock(@p2.get(@p2.item)).expects('endTurn').never()
      @battle.performMove(@p1, embargo)

      for i in [0...5]
        @p2.has(Attachment.Embargo).should.be.true
        @battle.endTurn()
      @p2.has(Attachment.Embargo).should.be.false

    it "prevents the target's use of items in subsequent turns", ->
      shared.create.call this,
        team2: [ Factory("Magikarp", item: "Leftovers") ]
      embargo = @battle.getMove('Embargo')
      mock = @sandbox.mock(@p2.get(@p2.item)).expects('endTurn').never()
      @battle.performMove(@p1, embargo)

      for i in [0...5]
        @battle.endTurn()
        @battle.beginTurn()
      mock.verify()

  testChargeMove = (moveName, vulnerable) ->
    describe moveName, ->
      it "chooses the player's next action for them", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        @p1.moves = [ move ]

        @battle.recordMove(@id1, move)
        @battle.continueTurn()
        @battle.endTurn()
        @battle.beginTurn()
        @battle.requests.should.not.have.property(@id1)
        should.exist(@battle.getAction(@p1))

      it "only spends 1 PP for the entire attack", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        @p1.moves = [ move ]
        @p1.resetAllPP()

        pp = @p1.pp(move)
        @battle.recordMove(@id1, move)
        @battle.continueTurn()
        @p1.pp(move).should.equal(pp)
        @battle.beginTurn()
        @battle.continueTurn()
        @p1.pp(move).should.equal(pp - 1)

      it "skips the charge turn if the user is holding a Power Herb", ->
        shared.create.call this,
          team1: [Factory("Magikarp", item: "Power Herb")]
        move = @battle.getMove(moveName)

        @p1.hasItem("Power Herb").should.be.true
        mock = @sandbox.mock(move).expects('execute').once()
        @battle.recordMove(@id1, move)
        @battle.continueTurn()
        mock.verify()
        @p1.hasItem().should.be.false

      if vulnerable?.length?
        it "makes target invulnerable to moves", ->
          shared.create.call this,
            team1: [Factory("Magikarp", evs: {speed: 4})]
          move = @battle.getMove(moveName)
          clearSmog = @battle.getMove("Clear Smog")

          @battle.recordMove(@id1, move)
          @battle.recordMove(@id2, clearSmog)

          mock = @sandbox.mock(clearSmog).expects('hit').never()
          @battle.continueTurn()
          mock.verify()

        it "makes target invulnerable to moves *after* use", ->
          shared.create.call this,
            team2: [Factory("Magikarp", evs: {speed: 4})]
          move = @battle.getMove(moveName)
          tackle = @battle.getMove("Tackle")

          @battle.recordMove(@id1, move)
          @battle.recordMove(@id2, tackle)

          mock = @sandbox.mock(tackle).expects('hit').once()
          @battle.continueTurn()
          mock.verify()

        it "is vulnerable to attacks from a No Guard pokemon", ->
          shared.create.call this,
            team2: [Factory("Magikarp", ability: "No Guard")]
          move = @battle.getMove(moveName)
          tackle = @battle.getMove("Tackle")

          @battle.recordMove(@id1, move)
          @battle.recordMove(@id2, tackle)

          mock = @sandbox.mock(tackle).expects('hit').once()
          @battle.continueTurn()
          mock.verify()

        it "is vulnerable to attacks if locked on", ->
          shared.create.call(this)
          @battle.performMove(@p1, @battle.getMove("Lock-On"))
          @battle.performMove(@p2, @battle.getMove(moveName))
          tackle = @battle.getMove("Tackle")

          @battle.recordMove(@id1, tackle)

          mock = @sandbox.mock(tackle).expects('hit').once()
          @battle.continueTurn()
          mock.verify()

        for vulnerableMove in vulnerable
          it "is vulnerable to #{vulnerableMove}", ->
            shared.create.call this,
              team1: [Factory("Magikarp", evs: {speed: 4})]
            move = @battle.getMove(moveName)
            vulnerable = @battle.getMove(vulnerableMove)

            @battle.recordMove(@id1, move)
            @battle.recordMove(@id2, vulnerable)

            mock = @sandbox.mock(vulnerable).expects('hit').once()
            @battle.continueTurn()
            mock.verify()
      else # no vulnerable moves
        it "doesn't make target invulnerable to moves", ->
          shared.create.call this,
            team1: [Factory("Magikarp", evs: {speed: 4})]
          move = @battle.getMove(moveName)
          tackle = @battle.getMove("Tackle")

          @battle.recordMove(@id1, move)
          @battle.recordMove(@id2, tackle)

          mock = @sandbox.mock(tackle).expects('hit').once()
          @battle.continueTurn()
          mock.verify()

  testChargeMove('Skull Bash')
  testChargeMove('Razor Wind')
  testChargeMove('Shadow Force', [])
  testChargeMove('Ice Burn')
  testChargeMove('Freeze Shock')
  testChargeMove('Fly', ["Gust", "Thunder", "Twister", "Sky Uppercut", "Hurricane", "Smack Down"])
  testChargeMove('Bounce', ["Gust", "Thunder", "Twister", "Sky Uppercut", "Hurricane", "Smack Down"])  # Additional 30% chance to par
  testChargeMove('Dig', ["Earthquake", "Magnitude"])
  testChargeMove('Dive', ["Surf", "Whirlpool"])
  testChargeMove('SolarBeam')
  testChargeMove('Sky Attack')

  describe "Skull Bash", ->
    it "raises defense of the user on the charge turn", ->
      shared.create.call(this)
      skullBash = @battle.getMove('Skull Bash')
      @p1.stages.defense.should.equal(0)
      @battle.performMove(@p1, skullBash)
      @p1.stages.defense.should.equal(1)

  describe "Sky Attack", ->
    it "has a 30% chance to flinch after execution", ->
      shared.create.call(this)
      skyAttack = @battle.getMove('Sky Attack')
      shared.biasRNG.call(this, "randInt", 'flinch', 0)  # 100% chance

      @battle.recordMove(@id1, skyAttack)
      @battle.continueTurn()
      @p2.has(Attachment.Flinch).should.be.false
      @battle.beginTurn()
      @battle.continueTurn()
      @p2.has(Attachment.Flinch).should.be.true

  for moveName in [ "Gust", "Twister" ]
    for chargeMoveName in [ "Fly", "Bounce" ]
      do (moveName, chargeMoveName) ->
        describe moveName, ->
          it "deals double damage to Pokemon using #{chargeMoveName}", ->
            shared.create.call(this)
            chargeMove = @battle.getMove(chargeMoveName)
            move = @battle.getMove(moveName)

            # Make the Pokemon charge
            @battle.recordMove(@id2, chargeMove)
            @battle.continueTurn()

            move.basePower(@battle, @p1, @p2).should.equal(2 * move.power)

  for moveName in [ "Surf", "Whirlpool" ]
    do (moveName) ->
      describe moveName, ->
        it "deals double damage to Pokemon using Dive", ->
          shared.create.call(this)
          dive = @battle.getMove("Dive")
          move = @battle.getMove(moveName)

          # Make the Pokemon charge
          @battle.recordMove(@id2, dive)
          @battle.continueTurn()

          move.basePower(@battle, @p1, @p2).should.equal(2 * move.power)

  for moveName in [ "Earthquake", "Magnitude" ]
    do (moveName) ->
      describe moveName, ->
        it "deals double damage to Pokemon using Dig", ->
          shared.create.call(this)
          dig = @battle.getMove("Dig")
          move = @battle.getMove(moveName)
          power = move.power
          if moveName == 'Magnitude'
            power = 50
            shared.biasRNG.call(this, 'randInt', 'magnitude', 20)

          # Make the Pokemon charge
          @battle.recordMove(@id2, dig)
          @battle.continueTurn()

          move.basePower(@battle, @p1, @p2).should.equal(2 * power)

  describe "SolarBeam", ->
    it "skips the charge turn under Sun", ->
      shared.create.call(this)
      solarBeam = @battle.getMove("SolarBeam")
      @battle.setWeather(Weather.SUN)

      mock = @sandbox.mock(solarBeam).expects('execute').once()
      @battle.recordMove(@id1, solarBeam)
      @battle.continueTurn()
      mock.verify()

    for weather in [ Weather.RAIN, Weather.SAND, Weather.HAIL ]
      do (weather) ->
        it "halves base power under #{weather}", ->
          shared.create.call(this)
          move = @battle.getMove("SolarBeam")
          @battle.setWeather(weather)

          move.basePower(@battle, @p1, @p2).should.equal(move.power >> 1)

  describe "Fury Cutter", ->
    it "doubles base power after every use, to a max of 160", ->
      shared.create.call(this)
      furyCutter = @battle.getMove("Fury Cutter")
      furyCutter.basePower(@battle, @p1, @p2).should.equal(20)
      @battle.performMove(@p1, furyCutter)
      furyCutter.basePower(@battle, @p1, @p2).should.equal(40)
      @battle.performMove(@p1, furyCutter)
      furyCutter.basePower(@battle, @p1, @p2).should.equal(80)
      @battle.performMove(@p1, furyCutter)
      furyCutter.basePower(@battle, @p1, @p2).should.equal(160)
      @battle.performMove(@p1, furyCutter)
      furyCutter.basePower(@battle, @p1, @p2).should.equal(160)

    it "resets to normal base power if using a different move", ->
      shared.create.call(this)
      furyCutter = @battle.getMove("Fury Cutter")
      splash = @battle.getMove("Splash")

      @battle.performMove(@p1, furyCutter)
      furyCutter.basePower(@battle, @p1, @p2).should.equal(40)
      @battle.performMove(@p1, splash)
      furyCutter.basePower(@battle, @p1, @p2).should.equal(20)

  describe "Imprison", ->
    shared.shouldDoNoDamage("Imprison")
    shared.shouldFailIfUsedTwice("Imprison")

    it "prevents the opponent from selecting moves that the user knows", ->
      shared.create.call(this)
      imprison = @battle.getMove("Imprison")
      furyCutter = @battle.getMove("Fury Cutter")
      tackle = @battle.getMove("Tackle")
      splash = @battle.getMove("Splash")
      @p1.moves = [ imprison, tackle, splash ]
      @p2.moves = [ furyCutter, tackle, splash ]
      @p1.resetAllPP()
      @p2.resetAllPP()

      @p2.validMoves().should.eql [ furyCutter, tackle, splash ]
      @battle.performMove(@p1, imprison)
      @battle.beginTurn()
      @p2.validMoves().should.eql [ furyCutter ]

    it "prevents the opponent from executing moves that the user knows", ->
      shared.create.call(this)
      imprison = @battle.getMove("Imprison")
      furyCutter = @battle.getMove("Fury Cutter")
      tackle = @battle.getMove("Tackle")
      splash = @battle.getMove("Splash")
      @p1.moves = [ imprison, tackle, splash ]
      @p2.moves = [ furyCutter, tackle, splash ]
      @p1.resetAllPP()
      @p2.resetAllPP()

      @battle.performMove(@p1, imprison)
      mock = @sandbox.mock(tackle).expects('execute').never()
      @battle.performMove(@p2, tackle)
      mock.verify()

    it "lets opponents use moves again after user switches out", ->
      shared.create.call this,
        team1: [Factory("Magikarp"), Factory("Magikarp")]
      imprison = @battle.getMove("Imprison")
      furyCutter = @battle.getMove("Fury Cutter")
      tackle = @battle.getMove("Tackle")
      splash = @battle.getMove("Splash")
      @p1.moves = [ imprison, tackle, splash ]
      @p2.moves = [ furyCutter, tackle, splash ]
      @p1.resetAllPP()
      @p2.resetAllPP()

      @battle.performMove(@p1, imprison)
      @battle.beginTurn()
      @p2.validMoves().should.eql [ furyCutter ]

      @battle.performSwitch(@team1.first(), 1)
      @battle.beginTurn()
      @p2.validMoves().should.eql [ furyCutter, tackle, splash ]

  describe "Present", ->
    it "has a 40% chance for 40 base power", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", "present", .6)
      present = @battle.getMove("Present")

      spy = @sandbox.spy(present, 'basePower')
      @battle.performMove(@p1, present)
      spy.alwaysReturned(40).should.be.true

    it "has a 30% chance for 80 base power", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", "present", .3)
      present = @battle.getMove("Present")

      spy = @sandbox.spy(present, 'basePower')
      @battle.performMove(@p1, present)
      spy.alwaysReturned(80).should.be.true

    it "has a 10% chance for 120 base power", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", "present", 0)
      present = @battle.getMove("Present")

      spy = @sandbox.spy(present, 'basePower')
      @battle.performMove(@p1, present)
      spy.alwaysReturned(120).should.be.true

    it "has a 20% chance to heal target by 25% HP, rounded down", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "next", "present", .1)
      present = @battle.getMove("Present")

      @p2.currentHP = 1
      spy = @sandbox.spy(present, 'basePower')
      @battle.performMove(@p1, present)
      spy.alwaysReturned(0).should.be.true
      @p2.currentHP.should.equal(1 + (@p2.stat('hp') >> 2))

  describe "Final Gambit", ->
    it "faints the user", ->
      shared.create.call(this)
      finalGambit = @battle.getMove("Final Gambit")
      @battle.performMove(@p1, finalGambit)
      @p1.isFainted().should.be.true

    it "deals damage equal to the user's HP to the target", ->
      shared.create.call(this, team2: [Factory("Magikarp", evs: {hp: 4})])
      finalGambit = @battle.getMove("Final Gambit")
      @battle.performMove(@p1, finalGambit)
      @p2.currentHP.should.equal(1)

  describe "Lucky Chant", ->
    shared.shouldDoNoDamage("Lucky Chant")
    shared.shouldFailIfUsedTwice("Lucky Chant")

    it "prevents critical hits on the defender's team", ->
      shared.create.call(this)
      luckyChant = @battle.getMove("Lucky Chant")
      stormThrow = @battle.getMove("Storm Throw")
      @battle.performMove(@p1, luckyChant)

      stormThrow.isCriticalHit(@battle, @p2, @p1).should.be.false

    it "lasts 5 turns", ->
      shared.create.call(this)
      luckyChant = @battle.getMove("Lucky Chant")
      @battle.performMove(@p1, luckyChant)

      for i in [0...5]
        @team1.has(Attachment.LuckyChant).should.be.true
        @battle.endTurn()
      @team1.has(Attachment.LuckyChant).should.be.false

  describe "Lunar Dance", ->
    it "faints the user", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      lunarDance = @battle.getMove("Lunar Dance")
      @battle.performMove(@p1, lunarDance)

      @p1.isFainted().should.be.true

    it "fails if the user is the last active pokemon", ->
      shared.create.call(this, team1: [Factory('Magikarp')])
      lunarDance = @battle.getMove("Lunar Dance")

      mock = @sandbox.mock(lunarDance).expects('fail').once()
      @battle.performMove(@p1, lunarDance)
      mock.verify()
      @p1.isFainted().should.be.false

    it "completely restores the switchin's HP, PP, and status", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      lunarDance = @battle.getMove("Lunar Dance")
      benched = @team1.at(1)
      benched.attach(Status.Burn)
      benched.currentHP = 1
      benched.setPP(benched.moves[0], 1)

      @battle.performMove(@p1, lunarDance)
      @battle.performSwitch(@team1.first(), 1)
      benched.currentHP.should.equal(benched.stat('hp'))
      benched.hasStatus().should.be.false
      for move in benched.moves
        benched.pp(move).should.equal(benched.maxPP(move))

    it "disappears afterward", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      lunarDance = @battle.getMove("Lunar Dance")

      @battle.performMove(@p1, lunarDance)
      @team1.has(Attachment.LunarDance).should.be.true
      @battle.performSwitch(@team1.first(), 1)
      @team1.has(Attachment.LunarDance).should.be.false

    it "works for 2v2"

  describe "Healing Wish", ->
    it "faints the user", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      healingWish = @battle.getMove("Healing Wish")
      @battle.performMove(@p1, healingWish)

      @p1.isFainted().should.be.true

    it "fails if the user is the last active pokemon", ->
      shared.create.call(this, team1: [Factory('Magikarp')])
      healingWish = @battle.getMove("Healing Wish")

      mock = @sandbox.mock(healingWish).expects('fail').once()
      @battle.performMove(@p1, healingWish)
      mock.verify()
      @p1.isFainted().should.be.false

    it "completely restores the switchin's HP and status", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      healingWish = @battle.getMove("Healing Wish")
      benched = @team1.at(1)
      benched.attach(Status.Burn)
      benched.currentHP = 1
      benched.setPP(benched.moves[0], 1)

      @battle.performMove(@p1, healingWish)
      @battle.performSwitch(@team1.first(), 1)
      benched.currentHP.should.equal(benched.stat('hp'))
      benched.hasStatus().should.be.false

    it "disappears afterward", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      healingWish = @battle.getMove("Healing Wish")

      @battle.performMove(@p1, healingWish)
      @team1.has(Attachment.HealingWish).should.be.true
      @battle.performSwitch(@team1.first(), 1)
      @team1.has(Attachment.HealingWish).should.be.false

    it "works for 2v2"

  describe "Last Resort", ->
    it "fails if the pokemon only has one move", ->
      shared.create.call(this)
      lastResort = @battle.getMove("Last Resort")
      @p1.moves = [ lastResort ]

      mock = @sandbox.mock(lastResort).expects('fail').once()
      @battle.performMove(@p1, lastResort)
      mock.verify()

    it "works if the pokemon uses all other moves before Last Resort", ->
      shared.create.call(this)
      lastResort = @battle.getMove("Last Resort")
      splash = @battle.getMove("Splash")
      @p1.moves = [ lastResort, splash ]

      @battle.performMove(@p1, splash)
      mock = @sandbox.mock(lastResort).expects('afterSuccessfulHit').once()
      @battle.performMove(@p1, lastResort)
      mock.verify()

    it "fails if using all moves, but switches out and back in", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      lastResort = @battle.getMove("Last Resort")
      splash = @battle.getMove("Splash")
      @p1.moves = [ lastResort, splash ]

      mock = @sandbox.mock(lastResort).expects('fail').once()
      @battle.performMove(@p1, splash)
      @battle.performSwitch(@team1.first(), 1)
      @battle.performMove(@team1.first(), lastResort)
      @battle.performSwitch(@team1.first(), 1)
      mock.verify()

    it "fails if the pokemon does not know Last Resort", ->
      shared.create.call(this)
      splash = @battle.getMove("Splash")
      tackle = @battle.getMove("Tackle")
      @p1.moves = [ splash, tackle ]
      lastResort = @battle.getMove("Last Resort")

      mock = @sandbox.mock(lastResort).expects('fail').once()
      @battle.performMove(@p1, lastResort)
      mock.verify()

    it "fails if the pokemon has not used another move since it was active", ->
      shared.create.call(this)
      lastResort = @battle.getMove("Last Resort")
      splash = @battle.getMove("Splash")
      @p1.moves = [ lastResort, splash ]

      mock = @sandbox.mock(lastResort).expects('fail').once()
      @battle.performMove(@p1, lastResort)
      mock.verify()

  describe "Assist", ->
    it "fails if no team member exists", ->
      shared.create.call this,
        team1: [ Factory("Magikarp") ]
      assist = @battle.getMove("Assist")

      mock = @sandbox.mock(assist).expects('fail').once()
      @battle.performMove(@p1, assist)
      mock.verify()

    it "chooses a team member's move at random", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      assist = @battle.getMove("Assist")
      move = @team1.at(1).moves[0]
      shared.biasRNG.call(this, "randInt", "assist", 0)

      mock = @sandbox.mock(move)
      mock.expects('execute').once().withArgs(@battle, @p1, [ @p1 ])
      @battle.performMove(@p1, assist)
      mock.verify()

    it "fails if all team member moves are illegal", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Magikarp") ]
      assist = @battle.getMove("Assist")
      @team1.at(1).moves = []
      @team1.at(1).moves.push @battle.getMove("Assist")
      @team1.at(1).moves.push @battle.getMove("Bestow")
      @team1.at(1).moves.push @battle.getMove("Chatter")
      @team1.at(1).moves.push @battle.getMove("Circle Throw")
      @team1.at(1).moves.push @battle.getMove("Copycat")
      @team1.at(1).moves.push @battle.getMove("Counter")
      @team1.at(1).moves.push @battle.getMove("Covet")
      @team1.at(1).moves.push @battle.getMove("Destiny Bond")
      @team1.at(1).moves.push @battle.getMove("Detect")
      @team1.at(1).moves.push @battle.getMove("Dragon Tail")
      @team1.at(1).moves.push @battle.getMove("Endure")
      @team1.at(1).moves.push @battle.getMove("Feint")
      @team1.at(1).moves.push @battle.getMove("Focus Punch")
      @team1.at(1).moves.push @battle.getMove("Follow Me")
      @team1.at(1).moves.push @battle.getMove("Helping Hand")
      @team1.at(1).moves.push @battle.getMove("Me First")
      @team1.at(1).moves.push @battle.getMove("Metronome")
      @team1.at(1).moves.push @battle.getMove("Mimic")
      @team1.at(1).moves.push @battle.getMove("Mirror Coat")
      @team1.at(1).moves.push @battle.getMove("Mirror Move")
      @team1.at(1).moves.push @battle.getMove("Nature Power")
      @team1.at(1).moves.push @battle.getMove("Protect")
      @team1.at(1).moves.push @battle.getMove("Rage Powder")
      @team1.at(1).moves.push @battle.getMove("Sketch")
      @team1.at(1).moves.push @battle.getMove("Sleep Talk")
      @team1.at(1).moves.push @battle.getMove("Snatch")
      @team1.at(1).moves.push @battle.getMove("Struggle")
      @team1.at(1).moves.push @battle.getMove("Switcheroo")
      @team1.at(1).moves.push @battle.getMove("Thief")
      @team1.at(1).moves.push @battle.getMove("Transform")
      @team1.at(1).moves.push @battle.getMove("Trick")

      mock = @sandbox.mock(assist).expects('fail').once()
      @battle.performMove(@p1, assist)
      mock.verify()

  describe "Metronome", ->
    it "chooses a random move to execute", ->
      shared.create.call(this)
      @p1.moves = []
      metronome = @battle.getMove("Metronome")
      tackle = @battle.getMove("Tackle")
      index = @battle.MoveList.indexOf(tackle)
      shared.biasRNG.call(this, 'randInt', "metronome", index)

      mock = @sandbox.mock(tackle).expects('execute').once()
      @battle.performMove(@p1, metronome)
      mock.verify()

    it "reselects if chosen an illegal move", ->
      shared.create.call(this)
      @p1.moves = [ metronome ]
      metronome = @battle.getMove("Metronome")
      technoBlast = @battle.getMove("Techno Blast")
      tackle = @battle.getMove("Tackle")
      index = @battle.MoveList.indexOf(technoBlast)
      reselectIndex = @battle.MoveList.indexOf(tackle)
      shared.biasRNG.call(this, 'randInt', "metronome", index)
      shared.biasRNG.call(this, 'randInt', "metronome reselect", reselectIndex)

      mock = @sandbox.mock(tackle).expects('execute').once()
      @battle.performMove(@p1, metronome)
      mock.verify()

  describe "Magic Coat", ->
    shared.shouldDoNoDamage("Magic Coat")

    it "causes certain moves directed at the user to be bounced back", ->
      shared.create.call(this)
      whirlwind = @battle.getMove("Whirlwind")
      magicCoat = @battle.getMove("Magic Coat")

      spy = @sandbox.spy(whirlwind, 'execute')
      @battle.performMove(@p1, magicCoat)
      @battle.performMove(@p2, whirlwind)
      spy.args.some((array) =>
        [battle, user, targets] = array
        battle == @battle && user == @p1 && targets[0] == @p2
      ).should.be.true

    it "bounces back spikes", ->
      shared.create.call(this)
      spikes = @battle.getMove("Spikes")
      magicCoat = @battle.getMove("Magic Coat")

      spy = @sandbox.spy(spikes, 'execute')
      @battle.performMove(@p1, magicCoat)
      @battle.performMove(@p2, spikes)
      spy.args.some((array) =>
        [battle, user, targets] = array
        battle == @battle && user == @p1 && targets[0] == @id2
      ).should.be.true
      @team1.has(Attachment.Spikes).should.be.false
      @team2.has(Attachment.Spikes).should.be.true

    it "cannot bounce spikes more than once", ->
      shared.create.call(this)
      spikes = @battle.getMove("Spikes")
      magicCoat = @battle.getMove("Magic Coat")

      @battle.performMove(@p1, magicCoat)
      @battle.performMove(@p2, magicCoat)

      (=>
        @battle.performMove(@p2, spikes)
      ).should.not.throw(/Maximum call stack size exceeded/)

    it "does not bounce certain moves back", ->
      shared.create.call(this)
      tackle = @battle.getMove("Tackle")
      magicCoat = @battle.getMove("Magic Coat")

      spy = @sandbox.spy(tackle, 'execute')
      @battle.performMove(@p1, magicCoat)
      @battle.performMove(@p2, tackle)
      spy.args.some((array) =>
        [battle, user, targets] = array
        battle == @battle && user == @p1 && targets[0] == @p2
      ).should.be.false

    it "lasts until the end of the turn", ->
      shared.create.call(this)
      magicCoat = @battle.getMove("Magic Coat")

      @battle.performMove(@p1, magicCoat)
      @p1.has(Attachment.MagicCoat).should.be.true
      @battle.endTurn()
      @p1.has(Attachment.MagicCoat).should.be.false

    it "cannot bounce more than once in the same turn", ->
      shared.create.call(this)
      willOWisp = @battle.getMove("Will-O-Wisp")
      thunderWave = @battle.getMove("Thunder Wave")
      magicCoat = @battle.getMove("Magic Coat")

      @battle.performMove(@p1, magicCoat)
      @battle.performMove(@p2, willOWisp)

      spy = @sandbox.spy(thunderWave, 'execute')
      @battle.performMove(@p2, thunderWave)
      spy.args.some((array) =>
        [battle, user, targets] = array
        battle == @battle && user == @p1 && targets[0] == @p2
      ).should.be.false

    it "cannot bounce a certain move more than once in the same turn", ->
      shared.create.call(this)
      willOWisp = @battle.getMove("Will-O-Wisp")
      magicCoat = @battle.getMove("Magic Coat")

      @battle.performMove(@p1, magicCoat)
      @battle.performMove(@p2, magicCoat)

      spy = @sandbox.spy(willOWisp, 'execute')
      (=>
        @battle.performMove(@p2, willOWisp)
      ).should.not.throw(/Maximum call stack size exceeded/)
      spy.calledTwice.should.be.true
      spy.args.some((array) =>
        [battle, user, targets] = array
        battle == @battle && user == @p1 && targets[0] == @p2
      ).should.be.true
      spy.args.some((array) =>
        [battle, user, targets] = array
        battle == @battle && user == @p2 && targets[0] == @p1
      ).should.be.true

  describe "Telekinesis", ->
    shared.shouldDoNoDamage("Telekinesis")
    shared.shouldFailIfUsedTwice("Telekinesis")

    it "makes the target immune to ground moves", ->
      shared.create.call(this)
      telekinesis = @battle.getMove("Telekinesis")

      @p1.isImmune('Ground').should.be.false
      @battle.performMove(@p2, telekinesis)
      @p1.isImmune('Ground').should.be.true

    it "lasts 3 turns", ->
      shared.create.call(this)
      telekinesis = @battle.getMove("Telekinesis")

      @battle.performMove(@p2, telekinesis)
      for x in [0...3]
        @p1.isImmune('Ground').should.be.true
        @battle.endTurn()
      @p1.isImmune('Ground').should.be.false

    it "makes the target unable to avoid attacks other than ohko moves", ->
      shared.create.call(this)
      telekinesis = @battle.getMove("Telekinesis")
      inferno = @battle.getMove("Inferno")
      sheerCold = @battle.getMove("Sheer Cold")

      inferno.chanceToHit(@battle, @p2, @p1).should.equal(50)
      sheerCold.chanceToHit(@battle, @p2, @p1).should.equal(30)
      @battle.performMove(@p2, telekinesis)
      inferno.chanceToHit(@battle, @p2, @p1).should.equal(0)
      sheerCold.chanceToHit(@battle, @p2, @p1).should.equal(30)

  describe "Smack Down", ->
    it "doesn't crash on secondary effect", ->
      shared.create.call(this, team2: [ Factory("Gyarados") ])
      shared.biasRNG.call(this, "next", "secondary effect", 0)
      smackDown = @battle.getMove("Smack Down")
      (=> @battle.performMove(@p1, smackDown)).should.not.throw()

    it "removes target's Ground immunity", ->
      shared.create.call(this, team2: [ Factory("Gyarados") ])
      smackDown = @battle.getMove("Smack Down")

      @p2.isImmune("Ground").should.be.true
      @battle.performMove(@p1, smackDown)
      @p2.isImmune("Ground").should.be.false

    it "stops Fly", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      fly = @battle.getMove("Fly")

      @battle.recordMove(@id1, fly)
      @battle.continueTurn()

      @p1.has(Attachment.Charging).should.be.true
      @battle.performMove(@p2, smackDown)
      @p1.has(Attachment.Charging).should.be.false

    it "stops Bounce", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      bounce = @battle.getMove("Bounce")

      @battle.recordMove(@id1, bounce)
      @battle.continueTurn()

      @p1.has(Attachment.Charging).should.be.true
      @battle.performMove(@p2, smackDown)
      @p1.has(Attachment.Charging).should.be.false

    it "does not stop other charge moves like Dive", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      dive = @battle.getMove("Dive")

      @battle.recordMove(@id1, dive)
      @battle.continueTurn()

      @p1.has(Attachment.Charging).should.be.true
      @battle.performMove(@p2, smackDown)
      @p1.has(Attachment.Charging).should.be.true

    it "stops Magnet Rise", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      magnetRise = @battle.getMove("Magnet Rise")

      @battle.performMove(@p1, magnetRise)

      @p1.has(Attachment.MagnetRise).should.be.true
      @battle.performMove(@p2, smackDown)
      @p1.has(Attachment.MagnetRise).should.be.false

    it "stops Telekinesis", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      telekinesis = @battle.getMove("Telekinesis")

      @battle.performMove(@p2, telekinesis)

      @p1.has(Attachment.Telekinesis).should.be.true
      @battle.performMove(@p2, smackDown)
      @p1.has(Attachment.Telekinesis).should.be.false

    it "makes Magnet Rise execution fail", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      magnetRise = @battle.getMove("Magnet Rise")

      @battle.performMove(@p1, smackDown)

      mock = @sandbox.mock(magnetRise).expects('fail').once()
      @battle.performMove(@p2, magnetRise)
      mock.verify()

    it "causes Telekinesis execution to fail", ->
      shared.create.call(this)
      smackDown = @battle.getMove("Smack Down")
      telekinesis = @battle.getMove("Telekinesis")

      @battle.performMove(@p1, smackDown)

      mock = @sandbox.mock(telekinesis).expects('fail').once()
      @battle.performMove(@p1, telekinesis)
      mock.verify()

  describe "Echoed Voice", ->
    it "has 80 base power the second turn in a row it is used", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")

      @battle.performMove(@p1, echoedVoice)
      @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(80)

    it "has 120 base power the third turn in a row it is used", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")

      for i in [1...3]
        @battle.performMove(@p1, echoedVoice)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(120)
    it "has 160 base power the fourth turn in a row it is used", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")

      for i in [1...4]
        @battle.performMove(@p1, echoedVoice)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(160)

    it "has 200 base power the fifth turn in a row it is used", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")

      for i in [1...5]
        @battle.performMove(@p1, echoedVoice)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(200)

    it "has 200 base power the sixth and above turn in a row it is used", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")

      for i in [1...6]
        @battle.performMove(@p1, echoedVoice)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(200)

    it "resets to 40 base power if nobody uses this move the previous turn", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")
      splash = @battle.getMove("Splash")

      @battle.performMove(@p1, echoedVoice)
      @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(80)

      @battle.performMove(@p1, splash)
      @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(40)

    it "has the same base power that turn for all users", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")
      splash = @battle.getMove("Splash")

      @battle.performMove(@p1, echoedVoice)
      @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(80)
      echoedVoice.basePower(@battle, @p2, @p1).should.equal(80)

    it "does not reset even if missing", ->
      shared.create.call(this)
      echoedVoice = @battle.getMove("Echoed Voice")

      @battle.performMove(@p1, echoedVoice)
      @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(80)

      @sandbox.stub(echoedVoice, 'willMiss', -> true)
      @battle.performMove(@p1, echoedVoice)
      @battle.endTurn()
      echoedVoice.basePower(@battle, @p1, @p2).should.equal(120)

  describe "Struggle", ->
    it "deals typless damage", ->
      shared.create.call(this)
      struggle = @battle.getMove('Struggle')

      for type of util.Type
        util.typeEffectiveness(struggle.type, [type]).should.equal(1)

    it "deals 25% in recoil to the attacker, rounded down", ->
      shared.create.call(this)
      struggle = @battle.getMove('Struggle')

      hp = @p1.currentHP
      @p1.currentHP.should.equal @p1.stat('hp')
      @battle.performMove(@p1, struggle)
      (hp - @p1.currentHP).should.equal(hp >> 2)

  describe "Nature Power", ->
    it "uses Earthquake in Wi-Fi battles", ->
      shared.create.call(this)
      naturePower = @battle.getMove('Nature Power')
      earthquake = @battle.getMove('Earthquake')

      mock = @sandbox.mock(earthquake).expects('execute').once()
                                      .withArgs(@battle, @p1, [ @p2 ])
      @battle.performMove(@p1, naturePower)
      mock.verify()

  testRampageMove = (moveName) ->
    describe moveName, ->
      it "lasts 2-3 turns", ->
        shared.create.call(this)
        shared.biasRNG.call(this, 'randInt', 'rampage turns', 2)
        rampageMove = @battle.getMove(moveName)

        @battle.performMove(@p1, rampageMove)
        @p1.has(Attachment.Rampage).should.be.true
        @battle.endTurn()
        @battle.performMove(@p1, rampageMove)
        @battle.endTurn()
        @p1.has(Attachment.Rampage).should.be.false

      it "locks the user into that move until execution ends", ->
        shared.create.call(this)
        shared.biasRNG.call(this, 'randInt', 'rampage turns', 2)
        rampageMove = @battle.getMove(moveName)
        splash = @battle.getMove("Splash")
        @p1.moves = [ rampageMove, splash ]
        @p1.resetAllPP()

        @battle.performMove(@p1, rampageMove)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
        @battle.beginTurn()
        @p1.validMoves().should.eql [ rampageMove ]

        @battle.performMove(@p1, rampageMove)
        @battle.endTurn()
        @battle.beginTurn()
        @p1.validMoves().should.eql [ rampageMove, splash ]

      it "blocks switching until execution ends", ->
        shared.create.call(this)
        shared.biasRNG.call(this, 'randInt', 'rampage turns', 2)
        rampageMove = @battle.getMove(moveName)

        @battle.performMove(@p1, rampageMove)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
        @battle.beginTurn()
        @p1.isSwitchBlocked().should.be.true

        @battle.performMove(@p1, rampageMove)
        @battle.endTurn()
        @battle.beginTurn()
        @p1.isSwitchBlocked().should.be.false

      it "confuses the user after use", ->
        shared.create.call(this)
        shared.biasRNG.call(this, 'randInt', 'rampage turns', 2)
        rampageMove = @battle.getMove(moveName)

        @battle.performMove(@p1, rampageMove)
        @p2.currentHP = @p2.stat('hp')
        @battle.endTurn()
        @battle.beginTurn()
        @p1.has(Attachment.Confusion).should.be.false

        @battle.performMove(@p1, rampageMove)
        @battle.endTurn()
        @battle.beginTurn()
        @p1.has(Attachment.Confusion).should.be.true

      it "stops immediately if the user's move did not hit", ->
        shared.create.call(this)
        shared.biasRNG.call(this, 'randInt', 'rampage turns', 3)
        rampageMove = @battle.getMove(moveName)
        protect = @battle.getMove("Protect")

        @battle.performMove(@p1, rampageMove)
        @battle.endTurn()
        @battle.beginTurn()

        @battle.recordMove(@id2, protect)
        @battle.recordMove(@id1, rampageMove)
        @battle.continueTurn()
        @battle.endTurn()
        @p1.has(Attachment.Rampage).should.be.false

  testRampageMove("Outrage")
  testRampageMove("Petal Dance")
  testRampageMove("Thrash")

  test2To5MulthitMove = (moveName) ->
    describe moveName, ->
      it "deals 2 to 5 hits with a probability distribution of 1/3, 1/3, 1/6, 1/6", ->
        shared.create.call(this)

        move = @battle.getMove(moveName)
        spy = @sandbox.spy(@battle.rng, 'choice')
        spy = spy.withArgs([2, 2, 3, 3, 4, 5], 'num hits')

        @battle.performMove(@p1, move)
        spy.calledOnce.should.be.true

      it "hits 5 times if the user has Skill Link", ->
        shared.create.call(this, team1: [Factory('Cloyster', ability: 'Skill Link')])

        # try to "trick" the system into giving an invalid result. It should always hit
        # 5 times regardless of the rng
        shared.biasRNG.call(this, "choice", 'num hits', 2)
        shared.biasRNG.call(this, "randInt", 'num hits', 2)

        move = @battle.getMove(moveName)
        move.calculateNumberOfHits(@battle, @p1, [ @p2 ]).should.equal(5)

      it "only hits up until the target Pokemon faints", ->
        shared.create.call(this, team1: [Factory('Cloyster', ability: 'Skill Link')])

        # try to "trick" the system into giving an invalid result. It should always hit
        # 5 times regardless of the rng
        shared.biasRNG.call(this, "choice", 'num hits', 3)
        shared.biasRNG.call(this, "randInt", 'num hits', 3)

        @p2.currentHP = 1
        move = @battle.getMove(moveName)
        spy = @sandbox.spy(move, 'hit')
        @battle.performMove(@p1, move)
        spy.calledOnce.should.be.true

  test2To5MulthitMove('Arm Thrust')
  test2To5MulthitMove('Barrage')
  test2To5MulthitMove('Bone Rush')
  test2To5MulthitMove('Bullet Seed')
  test2To5MulthitMove('Comet Punch')
  test2To5MulthitMove('DoubleSlap')
  test2To5MulthitMove('Fury Attack')
  test2To5MulthitMove('Fury Swipes')
  test2To5MulthitMove('Icicle Spear')
  test2To5MulthitMove('Pin Missile')
  test2To5MulthitMove('Rock Blast')
  test2To5MulthitMove('Spike Cannon')
  test2To5MulthitMove('Tail Slap')

  describe "Trick Room", ->
    it "lasts 5 turns", ->
      shared.create.call(this)
      trickRoom = @battle.getMove("Trick Room")
      @battle.performMove(@p1, trickRoom)
      for x in [0...5]
        @battle.has(Attachment.TrickRoom).should.be.true
        @battle.endTurn()
      @battle.has(Attachment.TrickRoom).should.be.false

    it "ends a previous use of Trick Room", ->
      shared.create.call(this)
      trickRoom = @battle.getMove("Trick Room")

      @battle.performMove(@p1, trickRoom)
      @battle.has(Attachment.TrickRoom).should.be.true
      @battle.performMove(@p1, trickRoom)
      @battle.has(Attachment.TrickRoom).should.be.false

    it "reverses the order that moves are performed in", ->
      shared.create.call(this, team1: [Factory("Magikarp", evs: {speed: 4})])
      trickRoom = @battle.getMove("Trick Room")
      splash = @battle.getMove("Splash")
      @battle.performMove(@p1, trickRoom)

      @battle.recordMove(@id1, splash)
      @battle.recordMove(@id2, splash)
      @battle.determineTurnOrder().map((o) -> o.pokemon).should.eql [ @p2, @p1 ]

  describe "Transform", ->
    shared.shouldDoNoDamage("Transform")
    shared.shouldFailIfUsedTwice("Transform")

    it "fails on a substitute", ->
      shared.create.call(this)
      substitute = @battle.getMove("Substitute")
      transform = @battle.getMove("Transform")

      @battle.performMove(@p2, substitute)
      mock = @sandbox.mock(transform).expects('fail').once()
      @battle.performMove(@p1, transform)
      mock.verify()

    it "changes the user's type to match the target's", ->
      shared.create.call(this, team2: [Factory("Celebi")])
      transform = @battle.getMove("Transform")

      @battle.performMove(@p1, transform)
      @p1.types.should.eql @p2.types

    it "forces a sprite change", ->
      shared.create.call(this, team2: [Factory("Celebi")])
      transform = @battle.getMove("Transform")

      mock = @sandbox.mock(@p1).expects('changeSprite').once()
      @battle.performMove(@p1, transform)
      mock.verify()

    it "does not change species or forme", ->
      shared.create.call(this, team2: [Factory("Latias")])
      transform = @battle.getMove("Transform")

      oldSpecies = @p1.species
      oldForme = @p1.forme
      @battle.performMove(@p1, transform)
      @p1.species.should.equal(oldSpecies)
      @p1.forme.should.equal(oldForme)

    it "changes the user's stats to match the target's, except HP", ->
      shared.create.call(this, team2: [Factory("Celebi", evs: {speed: 252})])
      transform = @battle.getMove("Transform")

      @battle.performMove(@p1, transform)
      for stat of @p2.baseStats
        if stat == 'hp'
          @p1.stat(stat).should.not.equal(@p2.stat(stat))
        else
          @p1.stat(stat).should.equal(@p2.stat(stat))

    it "copies the target's gender", ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "F")]
        team2: [Factory("Celebi")]
      transform = @battle.getMove("Transform")

      @battle.performMove(@p1, transform)
      @p1.gender.should.equal(@p2.gender)

    it "copies the target's moveset, setting each PP and max PP to 5", ->
      shared.create.call(this, team2: [Factory("Celebi")])
      transform = @battle.getMove("Transform")
      @p2.boost(attack: 3, speed: -2, accuracy: 1)

      @battle.performMove(@p1, transform)
      @p1.moves.should.eql @p2.moves
      for move in @p1.moves
        @p1.pp(move).should.equal(5)
        @p1.maxPP(move).should.equal(5)

    it "copies the target's stat boosts", ->
      shared.create.call(this, team2: [Factory("Celebi")])
      transform = @battle.getMove("Transform")
      @p2.boost(attack: 3, speed: -2, accuracy: 1)

      @battle.performMove(@p1, transform)
      @p1.stages.should.containEql(attack: 3, speed: -2, accuracy: 1)

    it "copies the target's ability", ->
      shared.create.call this,
        team2: [Factory("Celebi", ability: "Natural Cure")]
      transform = @battle.getMove("Transform")

      @battle.performMove(@p1, transform)
      @p1.hasAbility("Natural Cure").should.be.true

    it "copies the target's weight", ->
      shared.create.call this,
        team2: [Factory("Celebi")]
      transform = @battle.getMove("Transform")

      @battle.performMove(@p1, transform)
      @p1.weight.should.equal(@p2.weight)

    it "copies the target's gender", ->
      shared.create.call this,
        team1: [Factory("Magikarp", gender: "F")]
        team2: [Factory("Celebi")]
      transform = @battle.getMove("Transform")

      @battle.performMove(@p1, transform)
      @p1.gender.should.equal(@p2.gender)

    it "restores the original stats after switching out", ->
      shared.create.call this,
        team1: [Factory("Ditto"), Factory("Magikarp")]
        team2: [Factory("Celebi", ability: "Natural Cure", evs: {speed: 252})]
      transform = @battle.getMove("Transform")
      baseStats = _.clone(@p1.baseStats)
      evs = _.clone(@p1.evs)
      stats = {}
      for stat of baseStats
        stats[stat] = @p1.stat(stat)

      @battle.performMove(@p1, transform)
      @p1.baseStats.should.not.eql(baseStats)
      @p1.evs.should.not.eql(evs)
      for stat, value of stats
        @p1.stat(stat).should.not.equal(value)

      @battle.performSwitch(@p1, 1)
      @p1.baseStats.should.eql(baseStats)
      @p1.evs.should.eql(evs)
      for stat, value of stats
        @p1.stat(stat).should.equal(value)

    it "restores the original species and type after switching out", ->
      shared.create.call this,
        team1: [Factory("Ditto"), Factory("Magikarp")]
        team2: [Factory("Celebi", ability: "Natural Cure")]
      transform = @battle.getMove("Transform")
      species = @p1.species
      types = _.clone(@p1.types)
      @battle.performMove(@p1, transform)
      @battle.performSwitch(@team1.first(), 1)
      @p1.types.should.eql(types)
      @p1.species.should.eql(species)

    it "restores the original ability after switching out", ->
      # Ditto has a fake ability so we can test abilities being restored.
      shared.create.call this,
        team1: [Factory("Ditto", ability: "Sniper"), Factory("Magikarp")]
        team2: [Factory("Celebi", ability: "Natural Cure")]
      transform = @battle.getMove("Transform")
      ability = @p1.ability
      @battle.performMove(@p1, transform)
      @battle.performSwitch(@team1.first(), 1)
      @p1.ability.should.eql(ability)

    it "restores original moveset after switch, but Transform PP decreases", ->
      shared.create.call this,
        team1: [Factory("Ditto"), Factory("Magikarp")]
        team2: [Factory("Celebi", ability: "Natural Cure")]
      transform = @battle.getMove("Transform")
      moves = _.clone(@p1.moves)
      ppHash = _.clone(@p1.ppHash)
      maxPPHash = _.clone(@p1.maxPPHash)
      @battle.performMove(@p1, transform)
      @battle.performSwitch(@team1.first(), 1)
      @p1.moves.should.eql(moves)
      @p1.maxPPHash.should.eql(maxPPHash)
      # Transform's PP should go down!
      ppHash[transform.name] -= 1
      @p1.ppHash.should.eql(ppHash)

    it "restores original gender after switch", ->
      shared.create.call this,
        team1: [Factory("Ditto"), Factory("Magikarp")]
        team2: [Factory("Celebi", ability: "Natural Cure")]
      transform = @battle.getMove("Transform")
      gender = @p1.gender
      @battle.performMove(@p1, transform)
      @battle.performSwitch(@team1.first(), 1)
      @p1.gender.should.eql(gender)

    it "restores original weight after switch", ->
      shared.create.call this,
        team1: [Factory("Ditto"), Factory("Magikarp")]
        team2: [Factory("Celebi", ability: "Natural Cure")]
      transform = @battle.getMove("Transform")
      weight = @p1.weight
      @battle.performMove(@p1, transform)
      @battle.performSwitch(@team1.first(), 1)
      @p1.weight.should.eql(weight)

    it "fails if the target is transformed", ->
      shared.create.call(this)
      transform = @battle.getMove("Transform")

      @battle.performMove(@p2, transform)
      mock = @sandbox.mock(transform).expects('fail').once()
      @battle.performMove(@p1, transform)
      mock.verify()

    it "fails if the target is under an illusion"
    it "fails if the user is under an illusion"
    it "cannot change formes if it has the ability to do so"

  describe "Fling", ->
    it "fails if the pokemon has no item", ->
      shared.create.call(this)
      fling = @battle.getMove("Fling")
      mock = @sandbox.mock(fling).expects('fail').once()
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      mock.verify()

    it "fails if the item is not removeable", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Acro Bike")]
      fling = @battle.getMove("Fling")
      mock = @sandbox.mock(fling).expects('fail').once()
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      mock.verify()

    it "fails if the user is blocked from using items", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Armor Fossil")]
      fling = @battle.getMove("Fling")
      @p1.blockItem()
      mock = @sandbox.mock(fling).expects('fail').once()
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      mock.verify()

    it "has a base power depending on the item held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Armor Fossil")]
      fling = @battle.getMove("Fling")
      fling.beforeTurn(@battle, @p1)
      @p1.beforeMove(fling, @p1, [@p2])
      fling.basePower(@battle, @p1, @p2).should.equal(100)

    it "inflicts Burn on the target if Flame Orb is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Flame Orb")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Status.Burn).should.be.true

    it "inflicts Toxic on the target if Toxic Orb is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Toxic Orb")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Status.Toxic).should.be.true

    it "flinches the target if King's Rock is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "King's Rock")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Attachment.Flinch).should.be.true

    it "flinches the target if Razor Fang is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Razor Fang")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Attachment.Flinch).should.be.true

    it "paralyzes the target if Light Ball is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Light Ball")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Status.Paralyze).should.be.true

    it "removes negative status effects if Mental Herb is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Mental Herb")]
      fling = @battle.getMove("Fling")
      @p2.attach(Attachment.Torment)
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Attachment.Torment).should.be.false

    it "removes negative stat boosts if White Herb is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "White Herb")]
      fling = @battle.getMove("Fling")
      @p2.boost(attack: 1, accuracy: -2)
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.stages.should.containEql(attack: 1, accuracy: 0)

    it "inflicts Poison on the target if Poison Barb is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Poison Barb")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Status.Poison).should.be.true

    it "causes target to eat the flung berry if a berry is held", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Salac Berry")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.stages.speed.should.equal(1)

    it "does not activate an item effect when hitting a substitute", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Toxic Orb")]
      fling = @battle.getMove("Fling")
      @p2.attach(Attachment.Substitute, hp: 1)
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p2.has(Status.Toxic).should.be.false

    it "loses the user's item", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Poison Barb")]
      fling = @battle.getMove("Fling")
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      @p1.hasItem().should.be.false

    it "loses the item even if execution is canceled by protect", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Poison Herb")]
      protect = @battle.getMove("Protect")
      fling = @battle.getMove("Fling")
      mock = @sandbox.mock(fling).expects('hit').never()
      @battle.recordMove(@id2, protect)
      @battle.recordMove(@id1, fling)
      @battle.continueTurn()
      mock.verify()
      @p1.hasItem().should.be.false

  describe "Bug Bite", ->
    it "eats the target's berry and gets its effects", ->
      shared.create.call this,
        team2: [Factory("Magikarp", item: "Salac Berry")]
      bugBite = @battle.getMove("Bug Bite")
      @battle.performMove(@p1, bugBite)
      @p2.hasItem().should.be.false
      @p1.stages.should.containEql(speed: 1)

    it "does eat the target's berry if the target fainted", ->
      shared.create.call this,
        team2: [Factory("Magikarp", item: "Salac Berry")]
      bugBite = @battle.getMove("Bug Bite")
      @p2.currentHP = 1
      @battle.performMove(@p1, bugBite)
      @p2.isFainted().should.be.true
      @p1.stages.should.containEql(speed: 1)

    it "does not eat the target's berry if the user fainted", ->
      shared.create.call this,
        team2: [Factory("Magikarp", item: "Salac Berry", ability: "Iron Barbs")]
      bugBite = @battle.getMove("Bug Bite")
      @p1.currentHP = 1
      @battle.performMove(@p1, bugBite)
      @p1.isFainted().should.be.true
      @p2.hasItem().should.be.true

  describe "Beat Up", ->
    it "deals 1 less hit for each unhealthy member in the user's party", ->
      shared.create.call this,
        team1: (Factory("Magikarp")  for i in [0...6])
      @team1.at(5).attach(Status.Paralyze)
      @team1.at(1).faint()
      beatUp = @battle.getMove("Beat Up")
      mock = @sandbox.mock(beatUp).expects('hit').exactly(4)
      @battle.performMove(@p1, beatUp)
      mock.verify()

    it "has 5 + X/10 base power for X in team, where X is base attack", ->
      shared.create.call this,
        team1: [  Factory("Magikarp"), Factory("Scyther"), Factory("Celebi"),
                  Factory("Totodile"), Factory("Rayquaza"), Factory("Seedot") ]
      beatUp = @battle.getMove("Beat Up")
      spy = @sandbox.spy(beatUp, "basePower")
      @battle.performMove(@p1, beatUp)
      for pokemon in @team1.pokemon
        basePower = 5 + Math.floor(pokemon.baseStats.attack / 10)
        spy.returned(basePower).should.be.true

    it "works for teams smaller than 6", ->
      shared.create.call this,
        team1: (Factory("Magikarp")  for i in [0...4])
      beatUp = @battle.getMove("Beat Up")
      mock = @sandbox.mock(beatUp).expects('hit').exactly(4)
      @battle.performMove(@p1, beatUp)
      mock.verify()

    it "doesn't crash if all pokemon are statused or fainted", ->
      shared.create.call this,
        team1: (Factory("Magikarp")  for i in [0...2])
      @team1.at(1).faint()
      @team1.at(0).attach(Status.Burn)
      beatUp = @battle.getMove("Beat Up")
      (=>
        @battle.performMove(@p1, beatUp)
      ).should.not.throw()

    it "doesn't crash when used by a pokemon with technician", ->
      shared.create.call(this, team1: [Factory("Magikarp", ability: "Technician")])
      beatUp = @battle.getMove("Beat Up")
      (=>
        @battle.performMove(@p1, beatUp)
      ).should.not.throw()

  describe "Psycho Shift", ->
    it "fails if the user doesn't have a status", ->
      shared.create.call(this)
      psychoShift = @battle.getMove("Psycho Shift")
      mock = @sandbox.mock(psychoShift).expects('fail').once()
      @battle.performMove(@p1, psychoShift)
      mock.verify()

    it "fails if the target already has a status", ->
      shared.create.call(this)
      @p1.attach(Status.Toxic)
      @p2.attach(Status.Paralyze)
      psychoShift = @battle.getMove("Psycho Shift")
      mock = @sandbox.mock(psychoShift).expects('fail').once()
      @battle.performMove(@p1, psychoShift)
      mock.verify()

    it "cures the user of its status", ->
      shared.create.call(this)
      @p1.attach(Status.Poison)
      psychoShift = @battle.getMove("Psycho Shift")
      @battle.performMove(@p1, psychoShift)
      @p1.hasStatus().should.be.false

    it "gives the user's former status to the target", ->
      shared.create.call(this)
      @p1.attach(Status.Burn)
      psychoShift = @battle.getMove("Psycho Shift")
      @battle.performMove(@p1, psychoShift)
      @p2.has(Status.Burn).should.be.true

  describe "Gravity", ->
    shared.shouldDoNoDamage("Gravity")
    shared.shouldFailIfUsedTwice("Gravity")

    it "raises accuracy of all attacks by 5/3", ->
      shared.create.call(this)
      gravity = @battle.getMove("Gravity")
      focusBlast = @battle.getMove("Focus Blast")
      @battle.performMove(@p1, gravity)
      accuracy = focusBlast.chanceToHit(@battle, @p1, @p2)
      accuracy.should.equal Math.floor(focusBlast.accuracy * 5 / 3)

    it "lasts 5 turns", ->
      shared.create.call(this)
      gravity = @battle.getMove("Gravity")
      @battle.performMove(@p1, gravity)
      for i in [0...5]
        @battle.has(Attachment.Gravity).should.be.true
        @battle.endTurn()
      @battle.has(Attachment.Gravity).should.be.false

    it "makes Flying Pokemon no longer immune to Ground", ->
      shared.create.call(this)
      @p2.types = [ "Flying" ]
      @p2.isImmune("Ground").should.be.true
      gravity = @battle.getMove("Gravity")
      @battle.performMove(@p1, gravity)
      @p2.isImmune("Ground").should.be.false

    it "makes Levitating Pokemon no longer immune to Ground", ->
      shared.create.call(this, team2: [Factory("Magikarp", ability: "Levitate")])
      @p2.isImmune("Ground").should.be.true
      gravity = @battle.getMove("Gravity")
      @battle.performMove(@p1, gravity)
      @p2.isImmune("Ground").should.be.false

    it "deals damage to Flying Pokemon", ->
      shared.create.call(this)
      @p2.types = [ "Flying" ]
      gravity = @battle.getMove("Gravity")
      earthquake = @battle.getMove("Earthquake")
      earthquake.typeEffectiveness(@battle, @p1, @p2).should.equal(0)
      @battle.performMove(@p1, gravity)
      earthquake.typeEffectiveness(@battle, @p1, @p2).should.equal(1)

    # TODO: Sky Drop
    for moveName in [ "Bounce", "Fly" ]
      do (moveName) ->
        it "grounds pokemon using #{moveName}", ->
          shared.create.call(this)
          move = @battle.getMove(moveName)
          gravity = @battle.getMove("Gravity")
          @battle.recordMove(@id2, move)
          @battle.continueTurn()
          @p2.has(Attachment.Charging).should.be.true

          @battle.recordMove(@id1, gravity)
          @battle.continueTurn()
          @p2.has(Attachment.Charging).should.be.false

    it "removes Telekinesis", ->
      shared.create.call(this)
      telekinesis = @battle.getMove("Telekinesis")
      gravity = @battle.getMove("Gravity")
      @battle.performMove(@p1, telekinesis)
      @p2.has(Attachment.Telekinesis).should.be.true
      @battle.performMove(@p1, gravity)
      @p2.has(Attachment.Telekinesis).should.be.false

    it "removes Magnet Rise", ->
      shared.create.call(this)
      magnetRise = @battle.getMove("Magnet Rise")
      gravity = @battle.getMove("Gravity")
      @battle.performMove(@p2, magnetRise)
      @p2.has(Attachment.MagnetRise).should.be.true
      @battle.performMove(@p1, gravity)
      @p2.has(Attachment.MagnetRise).should.be.false

    for moveName in [ "Jump Kick", "Hi Jump Kick", "Bounce", "Fly", "Sky Drop",
                      "Splash", "Telekinesis" ]
      do (moveName) ->
        it "disables #{moveName}", ->
          shared.create.call(this)
          move = @battle.getMove(moveName)
          gravity = @battle.getMove("Gravity")
          @p1.moves = [ move ]
          @p2.moves = [ move ]
          @battle.performMove(@p1, gravity)
          @battle.beginTurn()
          @p1.validMoves().should.not.containEql(move)
          @p2.validMoves().should.not.containEql(move)

        it "prevents the execution of #{moveName}", ->
          shared.create.call(this)
          move = @battle.getMove(moveName)
          gravity = @battle.getMove("Gravity")
          @battle.performMove(@p1, gravity)
          mock = @sandbox.mock(move).expects('execute').never()
          @battle.performMove(@p2, move)
          mock.verify()

  testDelayedAttackMove = (moveName, type) ->
    describe moveName, ->
      shared.shouldFailIfUsedTwice(moveName)

      it "waits three turns before attacking", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)
        for x in [0...3]
          @p2.currentHP.should.equal @p2.stat('hp')
          @battle.endTurn()
        @p2.currentHP.should.be.lessThan @p2.stat('hp')

      it "does not initialize and error if the target has fainted", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        @p2.faint()
        (=> @battle.performMove(@p1, move)).should.not.throw()

      it "does not hit if the target has fainted", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)
        for x in [0...2]
          @battle.endTurn()
        @p2.faint()
        mock = @sandbox.mock(move).expects('hit').never()
        @battle.endTurn()
        mock.verify()

      it "is considered a direct attack", ->
        shared.create.call(this)
        move = @battle.getMove(moveName)
        @battle.performMove(@p1, move)
        @p2.attach(Attachment.Substitute, hp: 1)
        @battle.endTurn()
        @battle.endTurn()
        @p2.has(Attachment.Substitute).should.be.true
        @battle.endTurn()
        @p2.has(Attachment.Substitute).should.be.false

      it "can target multiple positions"

  testDelayedAttackMove("Future Sight")
  testDelayedAttackMove("Doom Desire")

  describe "Baton Pass", ->
    it "fails if there is no benched pokemon to BP to", ->
      shared.create.call(this, team1: [Factory('Magikarp')])
      batonPass = @battle.getMove("Baton Pass")
      mock = @sandbox.mock(batonPass).expects('fail').once()
      @battle.performMove(@p1, batonPass)
      mock.verify()

    it "switches to another pokemon", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Celebi") ]
      batonPass = @battle.getMove("Baton Pass")
      @battle.performMove(@p1, batonPass)
      @battle.requests.should.have.property @id1

    it "passes certain attachments to the next pokemon", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Celebi") ]
      batonPass = @battle.getMove("Baton Pass")
      @p1.attach(Attachment.Ingrain)
      @p1.attach(Attachment.Torment)
      @battle.performMove(@p1, batonPass)
      @battle.performSwitch(@team1.first(), 1)
      @team1.first().has(Attachment.Torment).should.be.false
      @team1.first().has(Attachment.Ingrain).should.be.true

    it "continues Perish Song's counter", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Celebi") ]
      batonPass = @battle.getMove("Baton Pass")
      perishSong = @battle.getMove("Perish Song")
      @battle.performMove(@p1, perishSong)
      for i in [0...3]
        @battle.endTurn()
      @battle.performMove(@p1, batonPass)
      @battle.performSwitch(@team1.first(), 1)
      @team1.first().isFainted().should.be.false
      @battle.endTurn()
      @team1.first().isFainted().should.be.true

    it "passes status boosts to the next pokemon", ->
      shared.create.call this,
        team1: [ Factory("Magikarp"), Factory("Celebi") ]
      batonPass = @battle.getMove("Baton Pass")
      @p1.boost(attack: 1, evasion: -3)
      @battle.performMove(@p1, batonPass)
      @battle.performSwitch(@team1.first(), 1)
      @team1.first().stages.should.containEql(attack: 1, evasion: -3)

  describe "Thunder Wave", ->
    it "is affected by immunities", ->
      shared.create.call(this)
      thunderWave = @battle.getMove("Thunder Wave")
      @p2.types = [ 'Ground' ]
      @battle.performMove(@p1, thunderWave)
      @p2.has(Status.Paralyze).should.be.false

  describe "Thunder", ->
    it "has 50% accuracy in Sun", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SUN)
      thunder = @battle.getMove("Thunder")
      thunder.chanceToHit(@battle, @p1, @p2).should.equal(50)

    it "has perfect accuracy in Rain", ->
      shared.create.call(this)
      @battle.setWeather(Weather.RAIN)
      thunder = @battle.getMove("Thunder")
      thunder.chanceToHit(@battle, @p1, @p2).should.equal(0)

    it "has normal accuracy otherwise", ->
      shared.create.call(this)
      @battle.setWeather(Weather.HAIL)
      thunder = @battle.getMove("Thunder")
      thunder.chanceToHit(@battle, @p1, @p2).should.equal(70)

  describe "Hurricane", ->
    it "has 50% accuracy in Sun", ->
      shared.create.call(this)
      @battle.setWeather(Weather.SUN)
      hurricane = @battle.getMove("Hurricane")
      hurricane.chanceToHit(@battle, @p1, @p2).should.equal(50)

    it "has perfect accuracy in Rain", ->
      shared.create.call(this)
      @battle.setWeather(Weather.RAIN)
      hurricane = @battle.getMove("Hurricane")
      hurricane.chanceToHit(@battle, @p1, @p2).should.equal(0)

    it "has normal accuracy otherwise", ->
      shared.create.call(this)
      @battle.setWeather(Weather.HAIL)
      hurricane = @battle.getMove("Hurricane")
      hurricane.chanceToHit(@battle, @p1, @p2).should.equal(70)

  describe "Blizzard", ->
    it "has perfect accuracy in Hail", ->
      shared.create.call(this)
      @battle.setWeather(Weather.HAIL)
      blizzard = @battle.getMove("Blizzard")
      blizzard.chanceToHit(@battle, @p1, @p2).should.equal(0)

    it "has normal accuracy otherwise", ->
      shared.create.call(this)
      blizzard = @battle.getMove("Blizzard")
      blizzard.chanceToHit(@battle, @p1, @p2).should.equal(70)

  describe "Sleep Talk", ->
    it "fails if the pokemon is awake", ->
      shared.create.call(this)
      sleepTalk = @battle.getMove("Sleep Talk")
      mock = @sandbox.mock(sleepTalk).expects('fail').once()
      @battle.performMove(@p1, sleepTalk)
      mock.verify()

    it "performs a random move when the pokemon is asleep", ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep talk', 0)
      tackle = @battle.getMove("Tackle")
      sleepTalk = @battle.getMove("Sleep Talk")
      @p1.moves = [ tackle, sleepTalk ]
      @p1.resetAllPP()
      @p1.attach(Status.Sleep)
      spy = @sandbox.spy(@battle, 'executeMove')
      @battle.performMove(@p1, sleepTalk)
      spy.calledWith(tackle).should.be.true

    it "does not choose sleep talk or other moves", ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep talk', 0)
      tackle = @battle.getMove("Tackle")
      sleepTalk = @battle.getMove("Sleep Talk")
      assist = @battle.getMove("Assist")
      @p1.moves = [ sleepTalk, assist, tackle ]
      @p1.resetAllPP()
      @p1.attach(Status.Sleep)
      spy = @sandbox.spy(@battle, 'executeMove')
      @battle.performMove(@p1, sleepTalk)
      spy.calledWith(tackle).should.be.true

    it "fails if there are no viable moves", ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep talk', 0)
      sleepTalk = @battle.getMove("Sleep Talk")
      @p1.moves = [ sleepTalk ]
      @p1.resetAllPP()
      @p1.attach(Status.Sleep)
      mock = @sandbox.mock(sleepTalk).expects('fail').once()
      @battle.performMove(@p1, sleepTalk)
      mock.verify()

    it "changes the move's target properly", ->
      shared.create.call(this)
      shared.biasRNG.call(this, 'randInt', 'sleep talk', 0)
      tackle = @battle.getMove("Tackle")
      sleepTalk = @battle.getMove("Sleep Talk")
      @p1.moves = [ tackle, sleepTalk ]
      @p1.resetAllPP()
      @p1.attach(Status.Sleep)

      mock = @sandbox.mock(tackle)
      mock.expects('execute').once().withArgs(@battle, @p1, [ @p2 ])
      @battle.performMove(@p1, sleepTalk)
      mock.verify()

  describe "Rest", ->
    it "fails if the pokemon is at full HP", ->
      shared.create.call(this)
      rest = @battle.getMove("Rest")
      mock = @sandbox.mock(rest).expects('fail').once()

      @p1.setHP(@p1.stat('hp'))
      @battle.performMove(@p1, rest)
      mock.verify()

    it "restores the pokemon to full HP otherwise", ->
      shared.create.call(this)
      rest = @battle.getMove("Rest")

      @p1.setHP(1)
      @battle.performMove(@p1, rest)
      @p1.currentHP.should.equal @p1.stat('hp')

    it "sleeps the pokemon", ->
      shared.create.call(this)
      rest = @battle.getMove("Rest")

      @p1.setHP(1)
      @p1.has(Status.Sleep).should.be.false
      @battle.performMove(@p1, rest)
      @p1.has(Status.Sleep).should.be.true

    it "fails if the pokemon is already asleep", ->
      shared.create.call(this)
      rest = @battle.getMove("Rest")
      mock = @sandbox.mock(rest).expects('fail').once()

      @p1.setHP(1)
      @p1.attach(Status.Sleep)
      # execute instead of perform to avoid beforeMove callbacks.
      @battle.executeMove(rest, @p1, [ @p1 ])
      mock.verify()

    it "cures the pokemon of adverse status effects", ->
      shared.create.call(this)
      rest = @battle.getMove("Rest")

      @p1.setHP(1)
      @p1.attach(Status.Burn)
      @p1.has(Status.Burn).should.be.true
      @battle.performMove(@p1, rest)
      @p1.has(Status.Sleep).should.be.true
      @p1.has(Status.Burn).should.be.false

    it "always forces sleep turns to be 2", ->
      shared.create.call(this)
      shared.biasRNG.call(this, "randInt", "sleep turns", 3)
      rest = @battle.getMove("Rest")

      @p1.setHP(1)
      @battle.performMove(@p1, rest)
      attachment = @p1.get(Status.Sleep)
      should.exist(attachment)
      attachment.should.have.property("turns")
      attachment.turns.should.equal(2)

    it "fails if the Pokemon cannot be slept", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Vital Spirit")]
      rest = @battle.getMove("Rest")
      mock = @sandbox.mock(rest).expects('fail').once()

      @p1.setHP(1)
      @battle.performMove(@p1, rest)
      mock.verify()

  describe "Defog", ->
    it "lowers target's evasion by 1", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p1, defog)
      @p2.stages.should.containEql(evasion: -1)

    it "removes Reflect on target's side", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p2, @battle.getMove("Reflect"))
      @p2.team.has(Attachment.Reflect).should.be.true
      @battle.performMove(@p1, defog)
      @p2.team.has(Attachment.Reflect).should.be.false

    it "removes Light Screen on target's side", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p2, @battle.getMove("Light Screen"))
      @p2.team.has(Attachment.LightScreen).should.be.true
      @battle.performMove(@p1, defog)
      @p2.team.has(Attachment.LightScreen).should.be.false

    it "removes Spikes on target's side", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p1, @battle.getMove("Spikes"))
      @p2.team.has(Attachment.Spikes).should.be.true
      @battle.performMove(@p1, defog)
      @p2.team.has(Attachment.Spikes).should.be.false

    it "removes Stealth Rock on target's side", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p1, @battle.getMove("Stealth Rock"))
      @p2.team.has(Attachment.StealthRock).should.be.true
      @battle.performMove(@p1, defog)
      @p2.team.has(Attachment.StealthRock).should.be.false

    it "removes Toxic Spikes on target's side", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p1, @battle.getMove("Toxic Spikes"))
      @p2.team.has(Attachment.ToxicSpikes).should.be.true
      @battle.performMove(@p1, defog)
      @p2.team.has(Attachment.ToxicSpikes).should.be.false

    it "removes Safeguard on target's side", ->
      shared.create.call(this, gen: 'bw')
      defog = @battle.getMove("Defog")
      @battle.performMove(@p2, @battle.getMove("Safeguard"))
      @p2.team.has(Attachment.Safeguard).should.be.true
      @battle.performMove(@p1, defog)
      @p2.team.has(Attachment.Safeguard).should.be.false

    it "removes Mist on target's side"

  describe "Refresh", ->
    it "fails if the Pokemon has no status", ->
      shared.create.call(this, gen: 'bw')
      refresh = @battle.getMove("Refresh")
      mock = @sandbox.mock(refresh).expects('fail').once()
      @battle.performMove(@p1, refresh)
      mock.verify()

    it "restores a Pokemon's status", ->
      shared.create.call(this, gen: 'bw')
      refresh = @battle.getMove("Refresh")
      @p1.attach(Status.Burn)
      @p1.has(Status.Burn).should.be.true
      @battle.performMove(@p1, refresh)
      @p1.has(Status.Burn).should.be.false

    it "sends a message about status being restored", ->
      shared.create.call(this, gen: 'bw')
      refresh = @battle.getMove("Refresh")
      @p1.attach(Status.Burn)
      spy = @sandbox.spy(@battle, 'message')
      @battle.performMove(@p1, refresh)
      spy.args.join(',').should.containEql("#{@p1.name} healed its burn!")

  describe "Growth", ->
    it "boosts the pokemon's stats", ->
      shared.create.call(this, gen: 'bw')
      @battle.setWeather(Weather.SUN)
      @battle.performMove(@p1, @battle.getMove('Growth'))
      @p1.stages.should.containEql attack: 2, specialAttack: 2

  describe "Reflect Type", ->
    it "copies the target's types", ->
      shared.create.call(this, team1: [Factory("Magikarp")], team2: [Factory("Charmander")])
      @battle.performMove(@p1, @battle.getMove('Reflect Type'))
      @p1.types.should.containEql "Fire"

    it "fails if the user has multitype", ->
      shared.create.call(this, team1: [Factory("Arceus", ability: "Multitype")], team2: [Factory("Toxicroak")])
      @battle.performMove(@p1, @battle.getMove('Reflect Type'))
      @p1.types.should.containEql "Normal"

  describe "Retaliate", ->
    it "doubles in power if a pokemon on the user's side fainted last turn", ->
      shared.create.call this,
        team1: [Factory("Magikarp"), Factory("Slaking")]
      @p2.currentHP = 1
      @battle.beginTurn()
      @battle.recordMove(@id1, @battle.getMove("Aqua Jet"))
      @battle.recordMove(@id2, @battle.getMove("Splash"))
      @battle.continueTurn()
      @battle.endTurn()
      @battle.beginTurn()

      retaliate = @battle.getMove("Retaliate")
      spy = @sandbox.spy(retaliate, 'basePower')
      @battle.performMove(@p2, retaliate)
      spy.returned(140).should.be.true

    it "deals normal damage otherwise", ->
      shared.create.call(this)

      retaliate = @battle.getMove("Retaliate")
      spy = @sandbox.spy(retaliate, 'basePower')
      @battle.performMove(@p1, retaliate)
      spy.returned(70).should.be.true

  describe "Role Play", ->
    it "copies the target's ability", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Swift Swim")]
        team2: [Factory("Magikarp", ability: "Levitate")]
      @battle.performMove(@p1, @battle.getMove('Role Play'))
      @p1.hasAbility("Levitate").should.be.true

    it "does not copy certain abilities", ->
      shared.create.call this,
        team1: [Factory("Magikarp"), ability: "Swift Swim"]
        team2: [Factory("Arceus", ability: "Multitype")]
      @battle.performMove(@p1, @battle.getMove('Role Play'))
      @p1.hasAbility("Multitype").should.be.false

  describe "Relic Song", ->
    it "transforms Meloetta", ->
      shared.create.call this,
        team1: [Factory("Meloetta")]
        team2: [Factory("Arceus")]
      @battle.performMove(@p1, @battle.getMove('Relic Song'))
      @p1.forme.should.equal("pirouette")
      @battle.performMove(@p1, @battle.getMove('Relic Song'))
      @p1.forme.should.equal("default")

    it "changes Meloetta to default forme after switching out", ->
      shared.create.call this,
        team1: [Factory("Meloetta"), Factory("Magikarp")]
        team2: [Factory("Arceus")]
      @battle.performMove(@p1, @battle.getMove('Relic Song'))
      @p1.forme.should.equal("pirouette")
      @battle.performSwitch(@p1, 1)
      @battle.performSwitch(@team1.first(), 1)
      @p1.forme.should.equal("default")

    it "does not transform Pokemon that are not Meloetta", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Magikarp")]
      @p1.forme.should.equal("default")
      @battle.performMove(@p1, @battle.getMove('Relic Song'))
      @p1.forme.should.equal("default")

  describe "Safeguard", ->
    it "protects the user's team from status", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Safeguard"))
      @battle.performMove(@p2, @battle.getMove("Thunder Wave"))
      @p1.has(Status.Paralyze).should.be.false

    it "protects the user's team from confusion", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Safeguard"))
      @battle.performMove(@p2, @battle.getMove("Confuse Ray"))
      @p1.has(Attachment.Confusion).should.be.false

    it "doesn't protect against status inflicted by the user", ->
      shared.create.call(this)
      @p1.team.attach(Attachment.Safeguard)
      @p1.setHP(1)
      @battle.performMove(@p1, @battle.getMove("Rest"))
      @p1.has(Status.Sleep).should.be.true

    it "protects against yawn being inflicted", ->
      shared.create.call(this)
      @p1.team.attach(Attachment.Safeguard)
      @battle.performMove(@p2, @battle.getMove("Yawn"))
      @p1.has(Attachment.Yawn).should.be.false

    it "doesn't prevent yawn's effect", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove('Yawn'))
      @battle.endTurn()
      @p2.has(Status.Sleep).should.be.false
      @p2.team.attach(Attachment.Safeguard)
      @battle.endTurn()
      @p2.has(Status.Sleep).should.be.true

  describe "Soak", ->
    it "changes the target's type to Water", ->
      shared.create.call this,
        team1: [Factory("Magikarp")]
        team2: [Factory("Smeargle")]
      @p2.types.should.not.eql ['Water']
      @battle.performMove(@p1, @battle.getMove('Soak'))
      @p2.types.should.eql ['Water']

    it "does not affect pure Water type Pokemon", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Magikarp")]
      @p2.types.should.eql ['Water']
      soak = @battle.getMove("Soak")
      mock = @sandbox.mock(soak).expects('fail').once()
      @battle.performMove(@p1, soak)
      mock.verify()

    it "does not affect Arceus", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Arceus")]
      soak = @battle.getMove("Soak")
      mock = @sandbox.mock(soak).expects('fail').once()
      @battle.performMove(@p1, soak)
      mock.verify()

  describe 'Electro Ball', ->
    it 'has variable base power based on speed of user and target', ->
      shared.create.call this,
        team1: [Factory('Electrode', evs: {speed: 252}, nature: "Timid")]
        team2: [Factory('Smeargle')]
      move = @battle.getMove('Electro Ball')
      move.basePower(@battle, @p1, @p2).should.equal 80

  describe "Snore", ->
    it "fails if the pokemon is awake", ->
      shared.create.call(this)
      snore = @battle.getMove("Snore")
      mock = @sandbox.mock(snore).expects('fail').once()
      @battle.performMove(@p1, snore)
      mock.verify()

    it "attacks the target when the user is asleep", ->
      shared.create.call this,
      snore = @battle.getMove("Snore")
      @p1.attach(Status.Sleep)
      mock = @sandbox.mock(snore).expects('hit').once()
      @battle.performMove(@p1, snore)
      mock.verify()

  describe "Worry Seed", ->
    it "changes the target's ability to Insomnia", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Magikarp", ability: "Swift Swim")]
      @battle.performMove(@p1, @battle.getMove('Worry Seed'))
      @p2.hasAbility("Insomnia").should.be.true

    it "does not change some abilities", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Arceus", ability: "Multitype")]
      worrySeed = @battle.getMove("Worry Seed")
      mock = @sandbox.mock(worrySeed).expects('fail').once()
      @battle.performMove(@p1, worrySeed)
      mock.verify()

  describe "Simple Beam", ->
    it "changes the target's ability to Simple", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Magikarp", ability: "Swift Swim")]
      @battle.performMove(@p1, @battle.getMove('Simple Beam'))
      @p2.hasAbility("Simple").should.be.true

    it "does not change some abilities", ->
      shared.create.call this,
        team1: [Factory("Smeargle")]
        team2: [Factory("Arceus", ability: "Multitype")]
      simpleBeam = @battle.getMove("Simple Beam")
      mock = @sandbox.mock(simpleBeam).expects('fail').once()
      @battle.performMove(@p1, simpleBeam)
      mock.verify()

  describe "Entrainment", ->
    it "copies the user's ability to the target", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Swift Swim")]
        team2: [Factory("Smeargle", ability: "Own Tempo")]
      @battle.performMove(@p1, @battle.getMove('Entrainment'))
      @p2.hasAbility("Swift Swim").should.be.true

    it "does not copy some abilities", ->
      shared.create.call this,
        team1: [Factory("Arceus", ability: "Multitype")]
        team2: [Factory("Magikarp", ability: "Swift Swim")]
      entrainment = @battle.getMove("Entrainment")
      mock = @sandbox.mock(entrainment).expects('fail').once()
      @battle.performMove(@p1, entrainment)
      mock.verify()

  describe "Natural Gift", ->
    it "changes power and type depending on the berry held by the user", ->
      shared.create.call this,
        team1: [Factory("Smeargle", item: 'Liechi Berry')]
        team2: [Factory("Magikarp")]
      move = @battle.getMove('Natural Gift')
      basePower = move.basePower(@battle, @p1, @p2)
      basePower.should.equal(80)
      move.typeEffectiveness(@battle, @p1, @p2).should.equal(2)

    it "only works with certain items", ->
      shared.create.call this,
        team1: [Factory("Smeargle", item: 'Leftovers')]
      naturalGift = @battle.getMove('Natural Gift')
      mock = @sandbox.mock(naturalGift).expects('fail').once()
      @battle.performMove(@p1, naturalGift)
      mock.verify()

    it "should remove the item even if missing"

  describe "False Swipe", ->
    it "leaves the target with at least 1 HP", ->
      shared.create.call(this)
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove("False Swipe"))
      @p2.currentHP.should.equal 1

    it "does normal damage if the target has a substitute", ->
      shared.create.call(this)
      @p2.currentHP = 1
      @p2.attach(Attachment.Substitute)
      falseSwipe = @battle.getMove("False Swipe")
      spy = @sandbox.spy(falseSwipe, 'calculateDamage')
      @battle.performMove(@p1, falseSwipe)
      spy.returned(0).should.be.false

  describe "Gastro Acid", ->
    it "suppresses the target's ability", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Gastro Acid"))
      @p2.has(Attachment.AbilitySuppress).should.be.true
      @p2.isAbilityBlocked().should.be.true

    it "continues turn by turn", ->
      shared.create.call(this)
      @battle.performMove(@p1, @battle.getMove("Gastro Acid"))
      @battle.endTurn()
      @battle.beginTurn()
      @p2.has(Attachment.AbilitySuppress).should.be.true
      @p2.isAbilityBlocked().should.be.true

    it "fails against some abilities", ->
      shared.create.call(this, team2: [Factory("Arceus", ability: "Multitype")])
      gastroAcid = @battle.getMove("Gastro Acid")
      mock = @sandbox.mock(gastroAcid).expects('fail').once()
      @battle.performMove(@p1, gastroAcid)
      mock.verify()

  describe "Helping Hand", ->
    it "fails if there is no target", ->
      shared.create.call(this)
      helpingHand = @battle.getMove("Helping Hand")
      mock = @sandbox.mock(helpingHand).expects('fail').once()
      @battle.performMove(@p1, helpingHand)
      mock.verify()

  describe "Skill Swap", ->
    it "swaps the user's ability with the target's", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Swift Swim")]
        team2: [Factory("Smeargle", ability: "Own Tempo")]
      @battle.performMove(@p1, @battle.getMove('Skill Swap'))
      @p1.hasAbility("Own Tempo").should.be.true
      @p2.hasAbility("Swift Swim").should.be.true

    it "cannot swap some abilities", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Swift Swim")]
        team2: [Factory("Shedinja", ability: "Wonder Guard")]
      skillSwap = @battle.getMove("Skill Swap")
      mock = @sandbox.mock(skillSwap).expects('fail').once()
      @battle.performMove(@p1, skillSwap)
      mock.verify()

    it "cannot swap the abilities if they are the same", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Swift Swim")]
        team2: [Factory("Magikarp", ability: "Swift Swim")]
      skillSwap = @battle.getMove("Skill Swap")
      mock = @sandbox.mock(skillSwap).expects('fail').once()
      @battle.performMove(@p1, skillSwap)
      mock.verify()

    it "activates abilities after swapping", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Swift Swim")]
        team2: [Factory("Gyarados", ability: "Intimidate")]
      @battle.performMove(@p1, @battle.getMove('Skill Swap'))
      @p1.hasAbility("Intimidate").should.be.true
      @p2.hasAbility("Swift Swim").should.be.true
      @p2.stages.should.containEql(attack: -1)

  describe "Heal Block", ->
    it "lasts 5 turns", ->
      shared.create.call(this)
      healBlock = @battle.getMove("Heal Block")
      @battle.performMove(@p1, healBlock)
      for x in [0...5]
        @p2.has(Attachment.HealBlock).should.be.true
        @battle.endTurn()
      @p2.has(Attachment.HealBlock).should.be.false

    it 'prevents the target from executing a healing move', ->
      shared.create.call(this, team1: [ Factory('Magikarp', evs: {speed: 4}) ])
      move = @battle.getMove('Recover')
      healBlock = @battle.getMove('Heal Block')
      mock = @sandbox.mock(move)
      mock.expects('execute').never()

      @battle.performMove(@p1, healBlock)
      @battle.performMove(@p2, move)

      mock.verify()

    it 'prevents the target from selecting a healing move', ->
      shared.create.call this,
        team2: [Factory("Magikarp", moves: [ "Splash", "Recover" ])]

      @battle.performMove(@p1, @battle.getMove('Heal Block'))
      @battle.beginTurn()
      requestedMoves = @battle.requestFor(@p2).moves
      requestedMoves.should.not.containEql 'Recover'

    it "prevents Abilities that heal from healing", ->
      shared.create.call this,
        team2: [Factory("Magikarp", ability: "Water Absorb")]
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Heal Block'))
      @battle.endTurn()
      @battle.performMove(@p1, @battle.getMove('Water Gun'))
      @p2.currentHP.should.equal 1

    it "does not prevent Regenerator from healing", ->
      shared.create.call this,
        team1: [Factory("Magikarp", ability: "Regenerator")]
      @p1.currentHP = 1
      @battle.performMove(@p2, @battle.getMove('Heal Block'))
      @p1.switchOut(@battle)
      hp = Math.floor(@p1.stat('hp') / 3)
      @p1.currentHP.should.equal(1 + hp)

    it "prevents items that heal from healing", ->
      shared.create.call this,
        team2: [Factory("Magikarp", item: "Leftovers")]
      @p2.currentHP = 1
      @battle.performMove(@p1, @battle.getMove('Heal Block'))
      @battle.endTurn()
      @p2.currentHP.should.equal 1

    it "prevents Berries that heal from attempting to activate", ->
      shared.create.call this,
        team2: [Factory("Magikarp", item: "Sitrus Berry")]
      item = @p2.item
      hp = @p2.currentHP
      @p2.currentHP >>= 1
      @battle.performMove(@p1, @battle.getMove('Heal Block'))
      @battle.endTurn()
      @battle.performMove(@p1, @battle.getMove('Tackle'))
      @p2.currentHP.should.be.lessThan hp >> 1
      @p2.item.should.equal item

  describe "Recycle", ->
    it "restores the user's last used item", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Salac Berry")]
      @p1.useItem()
      @battle.performMove(@p1, @battle.getMove("Recycle"))
      @p1.hasItem("Salac Berry").should.be.true

    it "fails if the user has an item", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Salac Berry")]
      recycle = @battle.getMove("Recycle")
      @p1.useItem()
      @battle.performMove(@p1, recycle)
      @p1.hasItem("Salac Berry").should.be.true

      mock = @sandbox.mock(recycle).expects('fail').once()
      @battle.performMove(@p1, recycle)
      mock.verify()

    it "fails if the user has no last used item", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Air Balloon")]
      recycle = @battle.getMove("Recycle")
      @battle.performMove(@p2, @battle.getMove("Tackle"))
      @p1.hasItem("Air Balloon").should.be.false

      mock = @sandbox.mock(recycle).expects('fail').once()
      @battle.performMove(@p1, recycle)
      mock.verify()

    it "cannot restore an item that was forcefully removed", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Salac Berry")]
      recycle = @battle.getMove("Recycle")
      @battle.performMove(@p2, @battle.getMove("Covet"))
      @p1.hasItem("Salac Berry").should.be.false
      @p2.hasItem("Salac Berry").should.be.true

      mock = @sandbox.mock(recycle).expects('fail').once()
      @battle.performMove(@p1, recycle)
      mock.verify()

    it "can restore the last used item if the current item is forcefully removed", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Salac Berry")]
        team2: [Factory("Magikarp", item: "Figy Berry")]
      recycle = @battle.getMove("Recycle")
      @p1.useItem()
      @battle.performMove(@p2, @battle.getMove("Trick"))
      @p1.hasItem("Figy Berry").should.be.true
      @battle.performMove(@p2, @battle.getMove("Knock Off"))

      @battle.performMove(@p1, recycle)
      @p1.hasItem("Salac Berry").should.be.true

    it "cannot restore the last used item if it was restored and forcefully removed", ->
      shared.create.call this,
        team1: [Factory("Magikarp", item: "Salac Berry")]
      recycle = @battle.getMove("Recycle")
      @p1.useItem()
      @battle.performMove(@p1, recycle)
      @p1.hasItem("Salac Berry").should.be.true
      @battle.performMove(@p2, @battle.getMove("Knock Off"))

      mock = @sandbox.mock(recycle).expects('fail').once()
      @battle.performMove(@p1, recycle)
      mock.verify()
