7724 lines
270 KiB
CoffeeScript
7724 lines
270 KiB
CoffeeScript
|
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()
|