BattleSim/test/xy/moves.coffee

724 lines
27 KiB
CoffeeScript

{Attachment, Status} = require('../../server/xy/attachment')
{Battle} = require('../../server/xy/battle')
{Pokemon} = require('../../server/xy/pokemon')
{Weather} = require('../../shared/weather')
{Move} = require('../../server/xy/move')
util = require '../../server/xy/util'
{Protocol} = require '../../shared/protocol'
{Factory} = require '../factory'
should = require 'should'
{_} = require 'underscore'
shared = require '../shared'
require '../helpers'
describe "XY Moves:", ->
# Test every single move for their primary effects.
shared.testEveryMove(Battle::MoveList, 'xy')
describe "a critical hit", ->
it "multiplies damage by 1.5x", ->
Move::criticalMultiplier.should.equal(1.5)
it "becomes a 50% chance at a +3 CH level", ->
Move::determineCriticalHitFromLevel(3, .49).should.be.true
Move::determineCriticalHitFromLevel(3, .5).should.be.false
it "becomes a 100% chance at a +4 CH level", ->
Move::determineCriticalHitFromLevel(4, .99).should.be.true
Move::determineCriticalHitFromLevel(4, 1.0).should.be.false
describe "a powder move", ->
it "does not affect Grass-types", ->
shared.create.call(this, gen: 'xy')
powderMove = @battle.findMove((m) -> m.hasFlag("powder"))
@p2.types.push("Grass")
mock1 = @sandbox.mock(powderMove).expects('hit').never()
mock2 = @sandbox.mock(powderMove).expects('fail').once()
@battle.performMove(@p1, powderMove)
mock1.verify()
mock2.verify()
it "affects non-Grass-types", ->
shared.create.call(this, gen: 'xy')
powderMove = @battle.findMove((m) -> m.hasFlag("powder"))
@p2.types = [ "Normal" ]
mock1 = @sandbox.mock(powderMove).expects('hit').once()
mock2 = @sandbox.mock(powderMove).expects('fail').never()
@battle.performMove(@p1, powderMove)
mock1.verify()
mock2.verify()
describe "Dragon Pulse", ->
it "has 85 base power now", ->
shared.create.call(this, gen: 'xy')
@battle.getMove('Dragon Pulse').power.should.equal(85)
describe 'Hidden Power', ->
it "always has 60 base power", ->
shared.create.call(this, gen: 'xy')
hiddenPower = @battle.getMove('Hidden Power')
hiddenPower.power.should.equal(60)
hiddenPower.basePower(@battle, @p1, @p2).should.equal(60)
describe "Facade", ->
it "does not cut attack in half when burned", ->
shared.create.call(this, gen: 'xy')
facade = @battle.getMove('Facade')
facade.burnCalculation(@p1).should.equal(1)
@p1.attach(Status.Burn)
facade.burnCalculation(@p1).should.equal(1)
describe "King's Shield", ->
it "protects against attacks", ->
shared.create.call(this, gen: 'xy')
kingsShield = @battle.getMove("King's Shield")
tackle = @battle.getMove("Tackle")
mock = @sandbox.mock(tackle).expects('hit').never()
@battle.recordMove(@id2, tackle)
@battle.determineTurnOrder()
@battle.performMove(@p1, kingsShield)
@battle.performMove(@p2, tackle)
mock.verify()
it "does not protect against non-damaging moves", ->
shared.create.call(this, gen: 'xy')
kingsShield = @battle.getMove("King's Shield")
willOWisp = @battle.getMove("Will-O-Wisp")
mock = @sandbox.mock(willOWisp).expects('hit').once()
@battle.recordMove(@id2, willOWisp)
@battle.determineTurnOrder()
@battle.performMove(@p1, kingsShield)
@battle.performMove(@p2, willOWisp)
mock.verify()
it "does not protect against attacks it is immune to", ->
shared.create.call(this, gen: 'xy')
@p1.types = [ 'Ghost' ]
kingsShield = @battle.getMove("King's Shield")
tackle = @battle.getMove("Tackle")
mock = @sandbox.mock(tackle).expects('hit').never()
@battle.recordMove(@id2, tackle)
@battle.determineTurnOrder()
@battle.performMove(@p1, kingsShield)
@battle.performMove(@p2, tackle)
mock.verify()
@p2.stages.should.containEql(attack: 0)
it "sharply lowers attacker's Attack if move was a contact move", ->
shared.create.call(this, gen: 'xy')
kingsShield = @battle.getMove("King's Shield")
tackle = @battle.getMove("Tackle")
@battle.recordMove(@id2, tackle)
@battle.determineTurnOrder()
@battle.performMove(@p1, kingsShield)
@p2.stages.attack.should.equal(0)
@battle.performMove(@p2, tackle)
@p2.stages.attack.should.equal(-2)
it "does not lower attacker's Attack if move was not a contact move", ->
shared.create.call(this, gen: 'xy')
kingsShield = @battle.getMove("King's Shield")
ember = @battle.getMove("Ember")
@battle.recordMove(@id2, ember)
@battle.determineTurnOrder()
@battle.performMove(@p1, kingsShield)
@p2.stages.attack.should.equal(0)
@battle.performMove(@p2, ember)
@p2.stages.attack.should.equal(0)
describe "Spiky Shield", ->
it "protects against attacks", ->
shared.create.call(this, gen: 'xy')
spikyShield = @battle.getMove("Spiky Shield")
tackle = @battle.getMove("Tackle")
mock = @sandbox.mock(tackle).expects('hit').never()
@battle.recordMove(@id2, tackle)
@battle.determineTurnOrder()
@battle.performMove(@p1, spikyShield)
@battle.performMove(@p2, tackle)
mock.verify()
it "protects against non-damaging moves", ->
shared.create.call(this, gen: 'xy')
spikyShield = @battle.getMove("Spiky Shield")
willOWisp = @battle.getMove("Will-O-Wisp")
mock = @sandbox.mock(willOWisp).expects('hit').never()
@battle.recordMove(@id2, willOWisp)
@battle.determineTurnOrder()
@battle.performMove(@p1, spikyShield)
@battle.performMove(@p2, willOWisp)
mock.verify()
it "does not protect against attacks it is immune to", ->
shared.create.call(this, gen: 'xy')
@p1.types = [ 'Ghost' ]
spikyShield = @battle.getMove("Spiky Shield")
tackle = @battle.getMove("Tackle")
mock = @sandbox.mock(tackle).expects('hit').never()
@battle.recordMove(@id2, tackle)
@battle.determineTurnOrder()
@battle.performMove(@p1, spikyShield)
@battle.performMove(@p2, tackle)
mock.verify()
@p2.stages.should.containEql(attack: 0)
it "damages attacker by 1/8 if move was a contact move", ->
shared.create.call(this, gen: 'xy')
spikyShield = @battle.getMove("Spiky Shield")
tackle = @battle.getMove("Tackle")
@battle.recordMove(@id2, tackle)
@battle.determineTurnOrder()
@battle.performMove(@p1, spikyShield)
@p2.currentHP.should.not.be.lessThan(@p2.stat('hp'))
@battle.performMove(@p2, tackle)
(@p2.stat('hp') - @p2.currentHP).should.equal(@p2.stat('hp') >> 3)
it "does not damage attacker if move was not a contact move", ->
shared.create.call(this, gen: 'xy')
spikyShield = @battle.getMove("Spiky Shield")
ember = @battle.getMove("Ember")
@battle.recordMove(@id2, ember)
@battle.determineTurnOrder()
@battle.performMove(@p1, spikyShield)
@p2.currentHP.should.not.be.lessThan(@p2.stat('hp'))
@battle.performMove(@p2, ember)
@p2.currentHP.should.not.be.lessThan(@p2.stat('hp'))
describe "Sticky Web", ->
shared.shouldFailIfUsedTwice("Sticky Web", gen: 'xy')
it "lowers a pokemon's speed by 1 when switching in", ->
shared.create.call(this, gen: 'xy', team2: (Factory("Magikarp") for x in [0..1]))
stickyWeb = @battle.getMove("Sticky Web")
@battle.performMove(@p1, stickyWeb)
@battle.performSwitch(@p2, 1)
@team2.first().stages.should.containEql(speed: -1)
it "doesn't lower a pokemon's speed by 1 if immune to ground", ->
shared.create.call(this, gen: 'xy', team2: [ Factory("Magikarp"), Factory("Gyarados") ])
stickyWeb = @battle.getMove("Sticky Web")
@battle.performMove(@p1, stickyWeb)
@battle.performSwitch(@p2, 1)
@team2.first().stages.should.containEql(speed: 0)
describe "Rapid Spin", ->
it "removes Sticky Web", ->
shared.create.call(this, gen: 'xy')
stickyWeb = @battle.getMove("Sticky Web")
rapidSpin = @battle.getMove("Rapid Spin")
@battle.performMove(@p1, stickyWeb)
@team2.has(Attachment.StickyWeb).should.be.true
@battle.performMove(@p2, rapidSpin)
@team2.has(Attachment.StickyWeb).should.be.false
describe "Defog", ->
it "removes Sticky Web as well", ->
shared.create.call(this, gen: 'xy')
defog = @battle.getMove("Defog")
@battle.performMove(@p1, @battle.getMove("Sticky Web"))
@p2.team.has(Attachment.StickyWeb).should.be.true
@battle.performMove(@p1, defog)
@p2.team.has(Attachment.StickyWeb).should.be.false
it "removes hazards from both sides of the field now", ->
shared.create.call(this, gen: 'xy')
defog = @battle.getMove("Defog")
@battle.performMove(@p1, @battle.getMove("Sticky Web"))
@battle.performMove(@p2, @battle.getMove("Sticky Web"))
@p1.team.has(Attachment.StickyWeb).should.be.true
@p2.team.has(Attachment.StickyWeb).should.be.true
@battle.performMove(@p1, defog)
@p1.team.has(Attachment.StickyWeb).should.be.false
@p2.team.has(Attachment.StickyWeb).should.be.false
it "removes screens from only the target's side of the field", ->
shared.create.call(this, gen: 'xy')
defog = @battle.getMove("Defog")
@battle.performMove(@p1, @battle.getMove("Reflect"))
@battle.performMove(@p1, @battle.getMove("Light Screen"))
@battle.performMove(@p2, @battle.getMove("Reflect"))
@battle.performMove(@p2, @battle.getMove("Light Screen"))
@p1.team.has(Attachment.Reflect).should.be.true
@p1.team.has(Attachment.LightScreen).should.be.true
@p2.team.has(Attachment.Reflect).should.be.true
@p2.team.has(Attachment.LightScreen).should.be.true
@battle.performMove(@p1, defog)
@p1.team.has(Attachment.Reflect).should.be.true
@p1.team.has(Attachment.LightScreen).should.be.true
@p2.team.has(Attachment.Reflect).should.be.false
@p2.team.has(Attachment.LightScreen).should.be.false
describe "Knock Off", ->
it "has x1.0 power if the pokemon has no item", ->
shared.create.call(this, gen: 'xy')
knockOff = @battle.getMove("Knock Off")
knockOff.basePower(@battle, @p1, @p2).should.equal(knockOff.power)
it "has x1.5 power if the item can be knocked off", ->
shared.create.call this,
gen: 'xy'
team2: [Factory("Magikarp", item: "Leftovers")]
knockOff = @battle.getMove("Knock Off")
basePower = knockOff.basePower(@battle, @p1, @p2)
basePower.should.equal Math.floor(1.5 * knockOff.power)
it "has x1.0 power if the item cannot be knocked off", ->
shared.create.call this,
gen: 'xy'
team2: [Factory("Magikarp", item: "Air Mail")]
knockOff = @battle.getMove("Knock Off")
knockOff.basePower(@battle, @p1, @p2).should.equal(knockOff.power)
it "has x1.5 power if item can be knocked off but owner has Sticky Hold", ->
shared.create.call this,
gen: 'xy'
team2: [Factory("Magikarp", item: "Leftovers", ability: "Sticky Hold")]
knockOff = @battle.getMove("Knock Off")
basePower = knockOff.basePower(@battle, @p1, @p2)
basePower.should.equal Math.floor(1.5 * knockOff.power)
describe "Protect-like moves", ->
it "determines success chance using a power of 3 instead of 2", ->
shared.create.call(this, gen: 'xy')
for x in [0..7]
attachment = @p1.attach(Attachment.ProtectCounter)
attachment.successChance().should.equal Math.pow(3, x)
attachment = @p1.attach(Attachment.ProtectCounter)
attachment.successChance().should.equal Math.pow(2, 32)
describe "Freeze-Dry", ->
it "is 2x effective against Water-types", ->
shared.create.call(this, gen: 'xy')
@p2.types = [ "Water" ]
freezeDry = @battle.getMove('Freeze-Dry')
spy = @sandbox.spy(freezeDry, 'typeEffectiveness')
@battle.performMove(@p1, freezeDry)
spy.returned(2).should.be.true
it "is 2x effective against Water-types with Normalize", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp", ability: "Normalize")]
@p2.types = [ "Water" ]
freezeDry = @battle.getMove('Freeze-Dry')
spy = @sandbox.spy(freezeDry, 'typeEffectiveness')
@battle.performMove(@p1, freezeDry)
spy.returned(2).should.be.true
it "is normally effective against other types", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp")]
@p2.types = [ "Fire" ]
freezeDry = @battle.getMove('Freeze-Dry')
spy = @sandbox.spy(freezeDry, 'typeEffectiveness')
@battle.performMove(@p1, freezeDry)
spy.returned(.5).should.be.true
describe "Substitute", ->
it "is bypassed by voice moves", ->
shared.create.call(this, gen: 'xy')
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
voiceMove = @battle.findMove (m) ->
!m.isNonDamaging() && m.hasFlag("sound")
spy = @sandbox.spy(voiceMove, 'hit')
@battle.performMove(@p1, voiceMove)
spy.calledOnce.should.be.true
@p2.currentHP.should.be.lessThan(@p2.stat('hp'))
it "is bypassed by Infiltrator", ->
shared.create.call this,
gen: 'xy'
team1: [ Factory("Magikarp", ability: "Infiltrator")]
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
tackle = @battle.getMove("Tackle")
spy = @sandbox.spy(tackle, 'hit')
@battle.performMove(@p1, tackle)
spy.calledOnce.should.be.true
@p2.currentHP.should.be.lessThan(@p2.stat('hp'))
it "is bypassed by Infiltrator even on status moves", ->
shared.create.call this,
gen: 'xy'
team1: [ Factory("Magikarp", ability: "Infiltrator")]
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
toxic = @battle.getMove("Toxic")
spy = @sandbox.spy(toxic, 'hit')
@battle.performMove(@p1, toxic)
spy.calledOnce.should.be.true
@p2.has(Status.Toxic).should.be.true
it "does not block Knock Off + Infiltrator", ->
shared.create.call this,
gen: 'xy'
team1: [ Factory("Magikarp", ability: "Infiltrator")]
team2: [ Factory("Magikarp", item: "Leftovers")]
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
knockOff = @battle.getMove("Knock Off")
spy = @sandbox.spy(knockOff, 'hit')
@battle.performMove(@p1, knockOff)
spy.calledOnce.should.be.true
@p2.hasItem().should.be.false
it "does not block secondary effects + Infiltrator", ->
shared.create.call this,
gen: 'xy'
team1: [ Factory("Magikarp", ability: "Infiltrator")]
shared.biasRNG.call(this, "next", "secondary effect", 0) # always burn
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
flamethrower = @battle.getMove('Flamethrower')
spy = @sandbox.spy(flamethrower, 'hit')
@battle.performMove(@p1, flamethrower)
spy.calledOnce.should.be.true
@p2.has(Status.Burn).should.be.true
testChargeMove = (moveName, vulnerable) ->
describe moveName, ->
it "chooses the player's next action for them", ->
shared.create.call(this, gen: 'xy')
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, gen: 'xy')
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,
gen: 'xy'
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,
gen: 'xy'
team1: [Factory("Magikarp", evs: {speed: 4})]
move = @battle.getMove(moveName)
swift = @battle.getMove("Swift")
@battle.recordMove(@id1, move)
@battle.recordMove(@id2, swift)
mock = @sandbox.mock(swift).expects('hit').never()
@battle.continueTurn()
mock.verify()
it "makes target invulnerable to moves *after* use", ->
shared.create.call this,
gen: 'xy'
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,
gen: 'xy'
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, gen: 'xy')
@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,
gen: 'xy'
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,
gen: 'xy'
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('Fly', ["Gust", "Thunder", "Twister", "Sky Uppercut", "Hurricane", "Smack Down", "Thousand Arrows"])
testChargeMove('Bounce', ["Gust", "Thunder", "Twister", "Sky Uppercut", "Hurricane", "Smack Down", "Thousand Arrows"])
testChargeMove('Geomancy')
testChargeMove('Phantom Force', [])
describe "Toxic", ->
it "cannot miss when used by a Poison type pokemon", ->
shared.create.call(this, gen: 'xy')
@p1.types = [ "Poison" ]
@p2.types = [ "Normal" ]
shared.biasRNG.call(this, "randInt", 'miss', 101)
toxic = @battle.getMove("Toxic")
toxic.willMiss(@battle, @p1, @p2).should.be.false
describe "Parting Shot", ->
it "reduces the attack and special attack of the target by two stages", ->
shared.create.call(this, gen: 'xy')
@battle.performMove(@p1, @battle.getMove("Parting Shot"))
@p2.stages.should.containEql attack: -1, specialAttack: -1
it "forces the owner to switch", ->
shared.create.call(this, gen: 'xy')
@battle.performMove(@p1, @battle.getMove("Parting Shot"))
@battle.requests.should.have.property @id1
describe "Worry Seed", ->
it "does not change some abilities", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Smeargle")]
team2: [Factory("Aegislash", ability: "Stance Change")]
worrySeed = @battle.getMove("Worry Seed")
mock = @sandbox.mock(worrySeed).expects('fail').once()
@battle.performMove(@p1, worrySeed)
mock.verify()
describe "Simple Beam", ->
it "does not change some abilities", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Smeargle")]
team2: [Factory("Aegislash", ability: "Stance Change")]
simpleBeam = @battle.getMove("Simple Beam")
mock = @sandbox.mock(simpleBeam).expects('fail').once()
@battle.performMove(@p1, simpleBeam)
mock.verify()
testTrappingMove = (name) ->
describe name, ->
it "deals 1/8 of the pokemon's max hp every turn", ->
shared.create.call this,
gen: 'xy'
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 / 8)
@p2.currentHP.should.equal expected
it "deals 1/6 of the pokemon's max hp every turn if the user is holding a Binding Band", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp", item: "Binding Band")]
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 / 6)
@p2.currentHP.should.equal expected
testTrappingMove "Bind"
testTrappingMove "Clamp"
testTrappingMove "Fire Spin"
testTrappingMove "Infestation"
testTrappingMove "Magma Storm"
testTrappingMove "Sand Tomb"
testTrappingMove "Wrap"
describe "Entrainment", ->
it "does not change some abilities", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp", ability: "Swift Swim")]
team2: [Factory("Aegislash", ability: "Stance Change")]
entrainment = @battle.getMove("Entrainment")
mock = @sandbox.mock(entrainment).expects('fail').once()
@battle.performMove(@p1, entrainment)
mock.verify()
describe "Nature Power", ->
it "uses Tri Attack in Wi-Fi battles", ->
shared.create.call(this, gen: 'xy')
naturePower = @battle.getMove('Nature Power')
triAttack = @battle.getMove('Tri Attack')
mock = @sandbox.mock(triAttack).expects('execute').once()
.withArgs(@battle, @p1, [ @p2 ])
@battle.performMove(@p1, naturePower)
mock.verify()
describe "Venom Drench", ->
it "lowers the target's attack, special attack, and speed by 1 stage if it is poisoned", ->
shared.create.call(this, gen: 'xy')
@p2.attach(Status.Poison)
@battle.performMove(@p1, @battle.getMove('Venom Drench'))
@p2.stages.should.containEql attack: -1, specialAttack: -1, speed: -1
it "fails if the target isn't poisoned", ->
shared.create.call(this, gen: 'xy')
venomDrench = @battle.getMove("Venom Drench")
mock = @sandbox.mock(venomDrench).expects('fail').once()
@battle.performMove(@p1, venomDrench)
mock.verify()
describe "Topsy-Turvy", ->
it "reverses the target's boosts", ->
shared.create.call(this, gen: 'xy')
@p2.stages.attack = 2
@p2.stages.defense = -3
@p2.stages.speed = 0
@battle.performMove(@p1, @battle.getMove('Topsy-Turvy'))
@p2.stages.should.containEql attack: -2
@p2.stages.should.containEql defense: 3
@p2.stages.should.containEql speed: 0
it "fails if the target has no boosts", ->
shared.create.call(this, gen: 'xy')
topsyTurvy = @battle.getMove('Topsy-Turvy')
mock = @sandbox.mock(topsyTurvy).expects('fail').once()
@battle.performMove(@p1, topsyTurvy)
mock.verify()
describe "Fell Stinger", ->
it "raises the user's Attack 2 stages if the target faints", ->
shared.create.call(this, gen: 'xy')
@p2.currentHP = 1
@battle.performMove(@p1, @battle.getMove("Fell Stinger"))
@p1.stages.should.containEql attack: 2
it "does not raise the user's Attack 2 stages otherwise", ->
shared.create.call(this, gen: 'xy')
@battle.performMove(@p1, @battle.getMove("Fell Stinger"))
@p1.stages.should.containEql attack: 0
describe "Skill Swap", ->
it "can swap the abilities if they are the same", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp", ability: "Swift Swim")]
team2: [Factory("Magikarp", ability: "Swift Swim")]
skillSwap = @battle.getMove("Skill Swap")
mock = @sandbox.mock(skillSwap).expects('fail').never()
@battle.performMove(@p1, skillSwap)
mock.verify()
describe "Metronome", ->
it "reselects if chosen an illegal move", ->
shared.create.call(this, gen: 'xy')
@p1.moves = [ metronome ]
metronome = @battle.getMove("Metronome")
belch = @battle.getMove("Belch")
tackle = @battle.getMove("Tackle")
index = @battle.MoveList.indexOf(belch)
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()
testDelayedAttackMove = (moveName, type) ->
describe moveName, ->
it "does not hit substitutes if the user has Infiltrator and is active", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp", ability: "Infiltrator")]
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.true
it "always hits substitutes if the user is not on the field", ->
shared.create.call this,
gen: 'xy'
team1: [Factory("Magikarp", ability: "Infiltrator"), Factory("Magikarp")]
move = @battle.getMove(moveName)
@battle.performMove(@p1, move)
@p2.attach(Attachment.Substitute, hp: 1)
@battle.endTurn()
@battle.performSwitch(@p1, 1)
@battle.endTurn()
@p2.has(Attachment.Substitute).should.be.true
@battle.endTurn()
@p2.has(Attachment.Substitute).should.be.false
testDelayedAttackMove("Future Sight")
testDelayedAttackMove("Doom Desire")