2779 lines
111 KiB
CoffeeScript
2779 lines
111 KiB
CoffeeScript
|
require '../helpers'
|
||
|
|
||
|
{Attachment, Status} = require('../../server/bw/attachment')
|
||
|
{Battle} = require('../../server/bw/battle')
|
||
|
{Pokemon} = require('../../server/bw/pokemon')
|
||
|
{Weather} = require('../../shared/weather')
|
||
|
{Item} = require('../../server/bw/data/items')
|
||
|
{Ability} = require '../../server/bw/data/abilities'
|
||
|
util = require '../../server/bw/util'
|
||
|
{Factory} = require '../factory'
|
||
|
should = require 'should'
|
||
|
sinon = require 'sinon'
|
||
|
{_} = require 'underscore'
|
||
|
shared = require '../shared'
|
||
|
|
||
|
describe "BW Abilities:", ->
|
||
|
describe "Adaptability", ->
|
||
|
it "makes STAB 2x instead of 1.5", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Adaptability")]
|
||
|
hydroPump = @battle.getMove("Hydro Pump")
|
||
|
hydroPump.stabModifier(@battle, @p1, @p2).should.equal(0x2000)
|
||
|
|
||
|
it "still has 1x if the pokemon does not have STAB", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Adaptability")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.stabModifier(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Aftermath", ->
|
||
|
it "deals 25% HP damage to the killer", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Aftermath"), Factory("Magikarp")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
@battle.performFaints()
|
||
|
(@p2.stat('hp') - @p2.currentHP).should.equal(@p2.stat('hp') >> 2)
|
||
|
|
||
|
it "does not deal damage for non-contact moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Aftermath"), Factory("Magikarp")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p2, @battle.getMove("Thunderbolt"))
|
||
|
@battle.performFaints()
|
||
|
@p2.currentHP.should.equal(@p2.stat('hp'))
|
||
|
|
||
|
it "does not deal damage if pokemon died of natural causes", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Aftermath"), Factory("Magikarp")]
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
@p1.currentHP = 2
|
||
|
@p1.attach(Status.Burn)
|
||
|
@sandbox.stub(thunderbolt, 'calculateDamage', -> 1)
|
||
|
@battle.performMove(@p2, thunderbolt)
|
||
|
@battle.endTurn()
|
||
|
@p2.currentHP.should.equal(@p2.stat('hp'))
|
||
|
|
||
|
it "does not crash if the Pokemon never was hit", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Aftermath"), Factory("Magikarp")]
|
||
|
|
||
|
(=> @p1.faint()).should.not.throw()
|
||
|
|
||
|
it "deals a minimum of 1 damage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Aftermath"), Factory("Magikarp")]
|
||
|
team2: [Factory("Shedinja")]
|
||
|
@p1.currentHP = 2
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
@battle.performFaints()
|
||
|
@p2.currentHP.should.equal(0)
|
||
|
|
||
|
testWeatherPreventionAbility = (name) ->
|
||
|
describe name, ->
|
||
|
it "causes the battle to think there is no weather", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
@battle.setWeather(Weather.SAND)
|
||
|
@battle.hasWeather(Weather.SAND).should.be.false
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@battle.hasWeather(Weather.SUN).should.be.false
|
||
|
|
||
|
testWeatherPreventionAbility("Air Lock")
|
||
|
testWeatherPreventionAbility("Cloud Nine")
|
||
|
|
||
|
describe "Analytic", ->
|
||
|
it "multiplies attacks by 1.3 if no one is moving afterward", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Analytic")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||
|
|
||
|
it "doesn't multiply if the user isn't the last to move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Analytic")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@battle.recordMove(@id2, tackle)
|
||
|
@battle.determineTurnOrder()
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Anger Point", ->
|
||
|
it "maximizes Attack on critical hit", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Anger Point")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@sandbox.stub(tackle, 'isCriticalHit', -> true)
|
||
|
@p1.stages.attack.should.equal(0)
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p1.stages.should.containEql(attack: 6)
|
||
|
|
||
|
it "doesn't maximize attack on non-critical hits", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Anger Point")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@sandbox.stub(tackle, 'isCriticalHit', -> false)
|
||
|
@p1.stages.attack.should.equal(0)
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
|
||
|
describe "Anticipation", ->
|
||
|
it "shows a message if an opponent has a super-effective move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Anticipation")]
|
||
|
team2: [Factory("Pikachu", moves: ["Thunderbolt"])]
|
||
|
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@battle.performSwitch(@p1, 1)
|
||
|
spy.calledWithMatch('ANTICIPATION').should.be.true
|
||
|
|
||
|
it "shows a message if an opponent has an OHKO move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Anticipation")]
|
||
|
team2: [Factory("Lapras", moves: ["Sheer Cold"])]
|
||
|
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@battle.performSwitch(@p1, 1)
|
||
|
spy.calledWithMatch('ANTICIPATION').should.be.true
|
||
|
|
||
|
it "doesn't show a message otherwise", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Anticipation")]
|
||
|
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@battle.performSwitch(@p1, 1)
|
||
|
spy.calledWithMatch('ANTICIPATION').should.be.false
|
||
|
|
||
|
it "displays nothing when no opponents left", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
@p2.faint()
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@p1.copyAbility(Ability.Anticipation)
|
||
|
spy.called.should.be.false
|
||
|
|
||
|
describe "Arena Trap", ->
|
||
|
it "blocks switch", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Arena Trap")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
@p2.isSwitchBlocked().should.be.true
|
||
|
|
||
|
it "blocks switch the next turn as well", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Arena Trap")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
@battle.beginTurn()
|
||
|
@p2.isSwitchBlocked().should.be.true
|
||
|
|
||
|
it "doesn't block switch for Flying Pokemon", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Arena Trap")]
|
||
|
team2: [Factory("Gyarados")]
|
||
|
@p2.isSwitchBlocked().should.be.false
|
||
|
|
||
|
it "doesn't block switch for Pokemon immune to Ground by other means", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Arena Trap")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Magnet Rise"))
|
||
|
@battle.beginTurn()
|
||
|
@p2.isSwitchBlocked().should.be.false
|
||
|
|
||
|
describe "Bad Dreams", ->
|
||
|
it "deals 1/8 max HP end of turn if target is asleep", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Bad Dreams")]
|
||
|
team2: [Factory("Celebi")]
|
||
|
(@p2.stat('hp') - @p2.currentHP).should.equal(0)
|
||
|
@p2.attach(Status.Sleep)
|
||
|
@battle.endTurn()
|
||
|
(@p2.stat('hp') - @p2.currentHP).should.equal(@p2.stat('hp') >> 3)
|
||
|
|
||
|
it "deals no damage if target is awake", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Bad Dreams")]
|
||
|
team2: [Factory("Celebi")]
|
||
|
(@p2.stat('hp') - @p2.currentHP).should.equal(0)
|
||
|
@battle.endTurn()
|
||
|
(@p2.stat('hp') - @p2.currentHP).should.equal(0)
|
||
|
|
||
|
it "deals a minimum of 1 damage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Bad Dreams")]
|
||
|
team2: [Factory("Shedinja")]
|
||
|
@p2.attach(Status.Sleep)
|
||
|
@battle.endTurn()
|
||
|
@p2.currentHP.should.equal(0)
|
||
|
|
||
|
testCriticalHitPreventionAbility = (name) ->
|
||
|
describe name, ->
|
||
|
it "prevents critical hits", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Celebi", ability: name)]
|
||
|
shared.biasRNG.call(this, "next", "ch", .2)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@sandbox.stub(tackle, 'criticalHitLevel', -> 3)
|
||
|
tackle.isCriticalHit(@battle, @p1, @p2).should.be.false
|
||
|
|
||
|
testCriticalHitPreventionAbility("Battle Armor")
|
||
|
testCriticalHitPreventionAbility("Shell Armor")
|
||
|
|
||
|
testBoostProtectionAbility = (name, protection) ->
|
||
|
describe name, ->
|
||
|
allBoosts = [ "attack", "defense", "speed", "specialAttack",
|
||
|
"specialDefense", "accuracy", "evasion" ]
|
||
|
protection ||= allBoosts
|
||
|
|
||
|
it "protects against certain stat falls", ->
|
||
|
shared.create.call this, team1: [Factory("Celebi", ability: name)]
|
||
|
boosts = {}
|
||
|
boosts[stat] = -1 for stat in allBoosts
|
||
|
@p1.boost(boosts, @p2)
|
||
|
boosts[stat] = 0 for stat in protection
|
||
|
@p1.stages.should.eql(boosts)
|
||
|
|
||
|
it "does not protect against stat falls if the source is the user", ->
|
||
|
shared.create.call this, team1: [Factory("Celebi", ability: name)]
|
||
|
boosts = {}
|
||
|
boosts[stat] = -1 for stat in allBoosts
|
||
|
@p1.boost(boosts)
|
||
|
@p1.stages.should.containEql(boosts)
|
||
|
|
||
|
testBoostProtectionAbility("Big Pecks", [ "defense" ])
|
||
|
testBoostProtectionAbility("Clear Body")
|
||
|
testBoostProtectionAbility("Hyper Cutter", [ "attack" ])
|
||
|
testBoostProtectionAbility("Keen Eye", [ "accuracy" ])
|
||
|
testBoostProtectionAbility("White Smoke")
|
||
|
|
||
|
testLowHealthAbility = (name, type) ->
|
||
|
describe name, ->
|
||
|
it "increases power of #{type} moves by 1.5 at 1/3 health", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
@p1.currentHP = Math.floor(@p1.stat('hp') / 3)
|
||
|
move = _(@battle.MoveList).find (m) ->
|
||
|
m.type == type && !m.isNonDamaging()
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
|
||
|
it "doesn't increase power if move not of #{type}", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
@p1.currentHP = Math.floor(@p1.stat('hp') / 3)
|
||
|
move = _(@battle.MoveList).find (m) ->
|
||
|
m.type != type && !m.isNonDamaging()
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "doesn't increase power if user isn't under 1/3 health", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
@p1.currentHP = Math.floor(@p1.stat('hp') / 3) + 1
|
||
|
move = _(@battle.MoveList).find (m) ->
|
||
|
m.type == type && !m.isNonDamaging()
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
testLowHealthAbility("Blaze", "Fire")
|
||
|
testLowHealthAbility("Torrent", "Water")
|
||
|
testLowHealthAbility("Overgrow", "Grass")
|
||
|
testLowHealthAbility("Swarm", "Bug")
|
||
|
|
||
|
testWeatherSpeedAbility = (name, weather) ->
|
||
|
describe name, ->
|
||
|
it "doubles speed when the weather becomes #{weather}", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
speed = @p1.stat('speed')
|
||
|
@battle.setWeather(weather)
|
||
|
@p1.stat('speed').should.equal(2 * speed)
|
||
|
|
||
|
it "goes back to normal when the weather becomes something else", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
speed = @p1.stat('speed')
|
||
|
@battle.setWeather(weather)
|
||
|
@battle.setWeather(Weather.NONE)
|
||
|
@p1.stat('speed').should.equal(speed)
|
||
|
|
||
|
it "grants immunity to damage from their respective weather", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
@p1.isWeatherDamageImmune(weather).should.be.true
|
||
|
|
||
|
testWeatherSpeedAbility("Chlorophyll", Weather.SUN)
|
||
|
testWeatherSpeedAbility("Swift Swim", Weather.RAIN)
|
||
|
testWeatherSpeedAbility("Sand Rush", Weather.SAND)
|
||
|
|
||
|
describe "Color Change", ->
|
||
|
it "changes the owner's type to be the move's type that just hit it", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Color Change")]
|
||
|
ember = @battle.getMove("Ember")
|
||
|
@p1.hasType(ember.type).should.be.false
|
||
|
@battle.performMove(@p2, ember)
|
||
|
@p1.hasType(ember.type).should.be.true
|
||
|
|
||
|
it "does not change the owner's type if the move is non-damaging", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Color Change")]
|
||
|
willOWisp = @battle.getMove("Will-O-Wisp")
|
||
|
@p1.hasType(willOWisp.type).should.be.false
|
||
|
@battle.performMove(@p2, willOWisp)
|
||
|
@p1.hasType(willOWisp.type).should.be.false
|
||
|
|
||
|
describe "Compoundeyes", ->
|
||
|
it "increases accuracy of moves by 1.3x", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Compoundeyes")]
|
||
|
willOWisp = @battle.getMove("Will-O-Wisp")
|
||
|
accuracy = willOWisp.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal Math.floor(willOWisp.accuracy * 1.3)
|
||
|
|
||
|
describe "Contrary", ->
|
||
|
it "reverses stat changes from all sources", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Contrary")]
|
||
|
leafStorm = @battle.getMove("Leaf Storm")
|
||
|
growl = @battle.getMove("Growl")
|
||
|
@battle.performMove(@p1, leafStorm)
|
||
|
@p1.stages.should.containEql(specialAttack: 2)
|
||
|
@battle.performMove(@p2, growl)
|
||
|
@p1.stages.should.containEql(attack: 1)
|
||
|
|
||
|
describe "Cursed Body", ->
|
||
|
it "has a 30% chance to disable an attacker's last move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.isMoveBlocked(tackle).should.be.true
|
||
|
|
||
|
it "does not disable the owner", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
recover = @battle.getMove("Recover")
|
||
|
@p1.moves = [ recover ]
|
||
|
@p1.resetAllPP()
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p1, recover)
|
||
|
@p1.isMoveBlocked(recover).should.be.false
|
||
|
|
||
|
it "does not disable if attacker is behind a substitute", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
substitute = @battle.getMove("Substitute")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p2, substitute)
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.isMoveBlocked(tackle).should.be.false
|
||
|
|
||
|
it "does not disable if defender is behind a substitute", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
substitute = @battle.getMove("Substitute")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p1, substitute)
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.isMoveBlocked(tackle).should.be.false
|
||
|
|
||
|
it "has a 70% chance to do nothing", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", .3)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.isMoveBlocked(tackle).should.be.false
|
||
|
|
||
|
it "can work even if the defender faints", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body"), Factory("Magikarp")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
@p1.currentHP = 1
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.isMoveBlocked(tackle).should.be.true
|
||
|
|
||
|
it "lasts some turns", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
for x in [1..2]
|
||
|
@battle.endTurn()
|
||
|
@battle.beginTurn()
|
||
|
@p2.isMoveBlocked(tackle).should.be.true
|
||
|
|
||
|
it "cannot stack with Disable", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Cursed Body")]
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 1) # do not trigger
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@battle.performMove(@p1, @battle.getMove('Disable'))
|
||
|
@p2.isMoveBlocked(tackle).should.be.true
|
||
|
|
||
|
# Disable lasts 4 turns, so go to the last turn.
|
||
|
for x in [1..3]
|
||
|
@battle.endTurn()
|
||
|
@battle.beginTurn()
|
||
|
@p2.isMoveBlocked(tackle).should.be.true
|
||
|
|
||
|
shared.biasRNG.call(this, "next", "cursed body", 0)
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.isMoveBlocked(tackle).should.be.true
|
||
|
|
||
|
# Disable should end.
|
||
|
@battle.endTurn()
|
||
|
@battle.beginTurn()
|
||
|
@p2.isMoveBlocked(tackle).should.be.false
|
||
|
|
||
|
describe "Defeatist", ->
|
||
|
it "halves attack and special attack if HP goes under 50%", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Defeatist")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
|
||
|
# 50%
|
||
|
@p1.currentHP = (@p1.stat('hp') >> 1)
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x800)
|
||
|
thunderbolt.modifyAttack(@battle, @p1, @p2).should.equal(0x800)
|
||
|
|
||
|
# 50% + 1
|
||
|
@p1.currentHP = (@p1.stat('hp') >> 1) + 1
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
thunderbolt.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Defiant", ->
|
||
|
it "boosts attack by 2 every time a stat is lowered", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Defiant")]
|
||
|
@p1.boost(defense: -1, @p2)
|
||
|
@p1.stages.should.containEql(attack: 2)
|
||
|
@p1.boost(attack: -1, defense: -2, evasion: 1, @p2)
|
||
|
@p1.stages.should.containEql(attack: 5)
|
||
|
|
||
|
it "does not boost attack if the stat was self-lowered", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Defiant")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Close Combat"))
|
||
|
@p1.stages.should.containEql(attack: 0, defense: -1, specialDefense: -1)
|
||
|
|
||
|
describe "Download", ->
|
||
|
it "raises attack if foes have total defense < total sp.def", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Abomasnow")]
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
@p1.copyAbility(Ability.Download)
|
||
|
@p1.stages.should.containEql(attack: 1)
|
||
|
|
||
|
it "raises special attack if foes have total sp.def <= total def", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Celebi")]
|
||
|
@p1.stages.should.containEql(specialAttack: 0)
|
||
|
@p1.copyAbility(Ability.Download)
|
||
|
@p1.stages.should.containEql(specialAttack: 1)
|
||
|
|
||
|
it "does not raise anything if all foes have fainted", ->
|
||
|
shared.create.call(this)
|
||
|
@p2.faint()
|
||
|
@p1.stages.should.containEql(attack: 0, specialAttack: 0)
|
||
|
@p1.copyAbility(Ability.Download)
|
||
|
@p1.stages.should.containEql(attack: 0, specialAttack: 0)
|
||
|
|
||
|
testWeatherAbility = (name, weather) ->
|
||
|
describe name, ->
|
||
|
it "causes unending #{weather}", ->
|
||
|
shared.build(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
@battle.hasWeather().should.be.false
|
||
|
@controller.beginBattle()
|
||
|
@battle.hasWeather(weather).should.be.true
|
||
|
@battle.weatherDuration.should.equal(-1)
|
||
|
|
||
|
testWeatherAbility("Drizzle", Weather.RAIN)
|
||
|
testWeatherAbility("Drought", Weather.SUN)
|
||
|
testWeatherAbility("Sand Stream", Weather.SAND)
|
||
|
testWeatherAbility("Snow Warning", Weather.HAIL)
|
||
|
|
||
|
describe "Dry Skin", ->
|
||
|
it "gets healed 25% HP by water moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Dry Skin")])
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p2, @battle.getMove("Hydro Pump"))
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 2))
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
@p1.currentHP.should.be.lessThan(1 + (@p1.stat('hp') >> 2))
|
||
|
|
||
|
it "does not get healed by self-targeting water moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Dry Skin")])
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p1, @battle.getMove("Aqua Ring"))
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
|
||
|
it "gets healed 1/8 HP end of turn in Rain", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Dry Skin")])
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
@battle.setWeather(Weather.RAIN)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 3))
|
||
|
|
||
|
it "gets damaged 25% extra by fire moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Dry Skin")])
|
||
|
ember = @battle.getMove("Ember")
|
||
|
ember.modifyBasePower(@battle, @p2, @p1).should.equal(0x1400)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p2, @p1).should.equal(0x1000)
|
||
|
|
||
|
it "gets hurt 1/8 HP end of turn in Sun", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Dry Skin")])
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(@p1.stat('hp'))
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@battle.endTurn()
|
||
|
(@p1.stat('hp') - @p1.currentHP).should.equal(@p1.stat('hp') >> 3)
|
||
|
|
||
|
it "deals a minimum of 1 damage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Shedinja", ability: "Dry Skin")]
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(0)
|
||
|
|
||
|
it "heals a minimum of 1 hp", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", level: 1, ability: "Dry Skin")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.setWeather(Weather.RAIN)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(2)
|
||
|
|
||
|
describe "Early Bird", ->
|
||
|
it "drops the sleep counter by 2 instead of 1", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Early Bird")])
|
||
|
shared.biasRNG.call(this, "randInt", 'sleep turns', 3)
|
||
|
@p1.attach(Status.Sleep)
|
||
|
@battle.performMove(@p1, @battle.getMove('Tackle'))
|
||
|
@p1.has(Status.Sleep).should.be.true
|
||
|
@battle.performMove(@p1, @battle.getMove('Tackle'))
|
||
|
@p1.has(Status.Sleep).should.be.false
|
||
|
|
||
|
describe "Effect Spore", ->
|
||
|
it "has a 30% chance to inflict poison, paralysis, or sleep on hit", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Effect Spore")])
|
||
|
|
||
|
# Sleep
|
||
|
shared.biasRNG.call(this, "randInt", 'effect spore', 1)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(Status.Sleep).should.be.true
|
||
|
|
||
|
# Paralysis
|
||
|
@p2.cureStatus()
|
||
|
shared.biasRNG.call(this, "randInt", 'effect spore', 2)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(Status.Paralyze).should.be.true
|
||
|
|
||
|
# Poison
|
||
|
@p2.cureStatus()
|
||
|
shared.biasRNG.call(this, "randInt", 'effect spore', 3)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(Status.Poison).should.be.true
|
||
|
|
||
|
it "has a 70% chance to do nothing", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Effect Spore")])
|
||
|
shared.biasRNG.call(this, "randInt", 'effect spore', 4)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.hasStatus().should.be.false
|
||
|
|
||
|
it "doesn't trigger if the hitting move is a non-contact move", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Effect Spore")])
|
||
|
shared.biasRNG.call(this, "randInt", 'effect spore', 1)
|
||
|
@battle.performMove(@p2, @battle.getMove('Thunderbolt'))
|
||
|
@p2.has(Status.Sleep).should.be.false
|
||
|
|
||
|
it "can work even if the defender faints", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Effect Spore"), Factory("Magikarp")]
|
||
|
shared.biasRNG.call(this, "randInt", 'effect spore', 1)
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(Status.Sleep).should.be.true
|
||
|
|
||
|
testFilterAbility = (name) ->
|
||
|
describe name, ->
|
||
|
it "reduces the impact of super-effective moves by 25%", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
seMove = _(@battle.MoveList).find (m) =>
|
||
|
!m.isNonDamaging() && util.typeEffectiveness(m.type, @p1.types) > 1
|
||
|
seMove.modifyDamage(@battle, @p2, @p1).should.equal(0xC00)
|
||
|
|
||
|
it "keeps non-super-effective moves as normal", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
seMove = _(@battle.MoveList).find (m) =>
|
||
|
!m.isNonDamaging() && util.typeEffectiveness(m.type, @p1.types) <= 1
|
||
|
seMove.modifyDamage(@battle, @p2, @p1).should.equal(0x1000)
|
||
|
|
||
|
testFilterAbility("Filter")
|
||
|
testFilterAbility("Solid Rock")
|
||
|
|
||
|
testContactStatusAbility = (name, attachment) ->
|
||
|
describe name, ->
|
||
|
it "has a 30% chance to inflict #{attachment.name} on the attacker", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: "F", ability: name)]
|
||
|
team2: [Factory("Magikarp", gender: "M")]
|
||
|
shared.biasRNG.call(this, "next", 'contact status', 0)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(attachment).should.be.true
|
||
|
|
||
|
it "inflicts no status if the move used is a non-contact move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: "F", ability: name)]
|
||
|
team2: [Factory("Magikarp", gender: "M")]
|
||
|
shared.biasRNG.call(this, "next", 'contact status', 0)
|
||
|
@battle.performMove(@p2, @battle.getMove('Thunderbolt'))
|
||
|
@p2.has(attachment).should.be.false
|
||
|
|
||
|
it "inflicts no status if the hit isn't direct", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
shared.biasRNG.call(this, "next", 'contact status', 0)
|
||
|
@p1.attach(Attachment.Substitute)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(attachment).should.be.false
|
||
|
|
||
|
it "has a 70% chance to do nothing", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: "F", ability: name)]
|
||
|
team2: [Factory("Magikarp", gender: "M")]
|
||
|
shared.biasRNG.call(this, "next", 'contact status', .3)
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p2.has(attachment).should.be.false
|
||
|
|
||
|
it "can work even if the defender faints", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: "F", ability: name), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp", gender: "M")]
|
||
|
shared.biasRNG.call(this, "next", 'contact status', 0)
|
||
|
@p1.currentHP = 1
|
||
|
tackle = @battle.getMove('Tackle')
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.has(attachment).should.be.true
|
||
|
|
||
|
testContactStatusAbility("Cute Charm", Attachment.Attract)
|
||
|
testContactStatusAbility("Flame Body", Status.Burn)
|
||
|
testContactStatusAbility("Poison Point", Status.Poison)
|
||
|
testContactStatusAbility("Static", Status.Paralyze)
|
||
|
|
||
|
testStatusBoostAbility = (name, statuses, spectra) ->
|
||
|
statusNames = statuses.map((s) -> s.name).join(', ')
|
||
|
describe name, ->
|
||
|
it "increases #{spectra} moves by 1.5 if has #{statusNames}", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
move = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.spectra == spectra
|
||
|
|
||
|
for status in statuses
|
||
|
@p1.cureStatus()
|
||
|
@p1.attach(status)
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
|
||
|
it "does not increase non-#{spectra} moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
move = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.spectra != spectra
|
||
|
|
||
|
for status in statuses
|
||
|
@p1.cureStatus()
|
||
|
@p1.attach(status)
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
testStatusBoostAbility("Flare Boost", [Status.Burn], "special")
|
||
|
testStatusBoostAbility("Toxic Boost", [Status.Poison, Status.Toxic], "physical")
|
||
|
|
||
|
describe "Flash Fire", ->
|
||
|
it "makes user invulnerable to Fire-type moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Flash Fire")])
|
||
|
ember = @battle.getMove("Ember")
|
||
|
mock = @sandbox.mock(ember).expects('hit').never()
|
||
|
@battle.performMove(@p2, ember)
|
||
|
mock.verify()
|
||
|
|
||
|
it "powers up user's Fire-type moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Flash Fire")])
|
||
|
ember = @battle.getMove("Ember")
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@battle.performMove(@p2, ember)
|
||
|
ember.modifyBasePower(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "doesn't activate on Protect", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Flash Fire")])
|
||
|
protect = @battle.getMove("Protect")
|
||
|
ember = @battle.getMove("Ember")
|
||
|
@battle.recordMove(@id1, protect)
|
||
|
@battle.recordMove(@id2, ember)
|
||
|
@battle.continueTurn()
|
||
|
@p1.has(Attachment.FlashFire).should.be.false
|
||
|
|
||
|
describe "Flower Gift", ->
|
||
|
it "changes Cherrim's forme"
|
||
|
it "does not change a transformed pokemon's forme"
|
||
|
it "grants x1.5 attack and special defense in Sun"
|
||
|
it "grants x1.5 attack and special defense to allies in Sun"
|
||
|
|
||
|
describe "Forecast", ->
|
||
|
it "changes Castform's forme"
|
||
|
it "does not change a transformed pokemon's forme"
|
||
|
|
||
|
describe "Forewarn", ->
|
||
|
it "considers OHKO moves to have 160 BP", ->
|
||
|
shared.create.call(this)
|
||
|
Ability.Forewarn.consider(@battle.getMove("Fissure")).should.equal(160)
|
||
|
|
||
|
it "considers counter moves to have 120 BP", ->
|
||
|
shared.create.call(this)
|
||
|
{consider} = Ability.Forewarn
|
||
|
consider(@battle.getMove("Counter")).should.equal(120)
|
||
|
consider(@battle.getMove("Mirror Coat")).should.equal(120)
|
||
|
consider(@battle.getMove("Metal Burst")).should.equal(120)
|
||
|
|
||
|
it "considers specific variable power moves to have 80 BP", ->
|
||
|
shared.create.call(this)
|
||
|
{consider} = Ability.Forewarn
|
||
|
consider(@battle.getMove("Crush Grip")).should.equal(80)
|
||
|
consider(@battle.getMove("Dragon Rage")).should.equal(80)
|
||
|
consider(@battle.getMove("Endeavor")).should.equal(80)
|
||
|
consider(@battle.getMove("Flail")).should.equal(80)
|
||
|
consider(@battle.getMove("Frustration")).should.equal(80)
|
||
|
consider(@battle.getMove("Grass Knot")).should.equal(80)
|
||
|
consider(@battle.getMove("Gyro Ball")).should.equal(80)
|
||
|
consider(@battle.getMove("Hidden Power")).should.equal(80)
|
||
|
consider(@battle.getMove("Low Kick")).should.equal(80)
|
||
|
consider(@battle.getMove("Natural Gift")).should.equal(80)
|
||
|
consider(@battle.getMove("Night Shade")).should.equal(80)
|
||
|
consider(@battle.getMove("Psywave")).should.equal(80)
|
||
|
consider(@battle.getMove("Return")).should.equal(80)
|
||
|
consider(@battle.getMove("Reversal")).should.equal(80)
|
||
|
consider(@battle.getMove("Seismic Toss")).should.equal(80)
|
||
|
consider(@battle.getMove("SonicBoom")).should.equal(80)
|
||
|
consider(@battle.getMove("Trump Card")).should.equal(80)
|
||
|
consider(@battle.getMove("Wring Out")).should.equal(80)
|
||
|
|
||
|
it "alerts user about a foe's move with the highest base power", ->
|
||
|
shared.build(this, team1: [Factory("Magikarp", ability: "Forewarn")])
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@controller.beginBattle()
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
spy.calledWith('FOREWARN', @p2, tackle).should.be.true
|
||
|
|
||
|
it "displays nothing when no opponents left", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
@p2.faint()
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@p1.copyAbility(Ability.Forewarn)
|
||
|
spy.called.should.be.false
|
||
|
|
||
|
describe "Friend Guard", ->
|
||
|
it "weakens attacks from allies by 25%", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Friend Guard")]
|
||
|
earthquake = @battle.getMove("Earthquake")
|
||
|
earthquake.modifyDamage(@battle, @p1, @team1.at(1)).should.equal(0xC00)
|
||
|
|
||
|
it "keeps attacks by enemies at normal", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Friend Guard")]
|
||
|
earthquake = @battle.getMove("Earthquake")
|
||
|
earthquake.modifyDamage(@battle, @p2, @team1.at(1)).should.equal(0x1000)
|
||
|
|
||
|
describe "Frisk", ->
|
||
|
it "randomly selects an opponent and displays the item", ->
|
||
|
shared.build this,
|
||
|
team1: [Factory("Magikarp", ability: "Frisk")]
|
||
|
team2: [Factory("Magikarp", item: "Leftovers")]
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@controller.beginBattle()
|
||
|
spy.calledWithMatch('FRISK').should.be.true
|
||
|
|
||
|
it "displays nothing when no opponents left", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
@p2.faint()
|
||
|
spy = @sandbox.spy(@battle, 'cannedText')
|
||
|
@p1.copyAbility(Ability.Frisk)
|
||
|
spy.called.should.be.false
|
||
|
|
||
|
describe "Gluttony", ->
|
||
|
it "makes berries activate at 50% HP", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Salac Berry", ability: "Gluttony")]
|
||
|
@p1.stages.should.containEql(speed: 0)
|
||
|
@p1.currentHP >>= 1
|
||
|
@p1.update()
|
||
|
@p1.stages.should.containEql(speed: 1)
|
||
|
|
||
|
describe "Guts", ->
|
||
|
it "multiplies attack by x1.5 if statused", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Guts")])
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
@p1.attach(Status.Burn)
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
|
||
|
it "does not multiply attack if move is special", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Guts")])
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
@p1.attach(Status.Burn)
|
||
|
thunderbolt.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Harvest", ->
|
||
|
it "has a 50% chance of re-obtaining a berry it used", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Salac Berry", ability: "Harvest")]
|
||
|
shared.biasRNG.call(this, "randInt", "harvest", 1)
|
||
|
@p1.useItem()
|
||
|
@battle.endTurn()
|
||
|
@p1.hasItem("Salac Berry").should.be.true
|
||
|
|
||
|
it "has a 50% chance of doing nothing", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Salac Berry", ability: "Harvest")]
|
||
|
shared.biasRNG.call(this, "randInt", "harvest", 0)
|
||
|
@p1.useItem()
|
||
|
@battle.endTurn()
|
||
|
@p1.hasItem("Salac Berry").should.be.false
|
||
|
|
||
|
it "has a 100% chance to re-obtain the berry in Sun", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Salac Berry", ability: "Harvest")]
|
||
|
shared.biasRNG.call(this, "randInt", "harvest", 0)
|
||
|
@p1.useItem()
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@battle.endTurn()
|
||
|
@p1.hasItem("Salac Berry").should.be.true
|
||
|
|
||
|
it "does not regain a normal item", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Flying Gem", ability: "Harvest")]
|
||
|
shared.biasRNG.call(this, "randInt", "harvest", 1)
|
||
|
@p1.useItem()
|
||
|
@battle.endTurn()
|
||
|
@p1.hasItem("Flying Gem").should.be.false
|
||
|
|
||
|
it "does not regain the berry if it was removed", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Salac Berry", ability: "Harvest")]
|
||
|
shared.biasRNG.call(this, "randInt", "harvest", 1)
|
||
|
@p1.useItem()
|
||
|
@battle.endTurn()
|
||
|
@p1.hasItem("Salac Berry").should.be.true
|
||
|
@p1.removeItem()
|
||
|
@battle.endTurn()
|
||
|
@p1.hasItem("Salac Berry").should.be.false
|
||
|
|
||
|
it "uses a regained Berry immediately if the condition is right", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Sitrus Berry", ability: "Harvest")]
|
||
|
shared.biasRNG.call(this, "randInt", "harvest", 1)
|
||
|
@p1.useItem()
|
||
|
@p1.currentHP = 1
|
||
|
@p1.hasItem("Sitrus Berry").should.be.false
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 2))
|
||
|
@p1.hasItem("Sitrus Berry").should.be.false
|
||
|
|
||
|
describe "Healer", ->
|
||
|
it "has a 30% chance of healing an adjacent ally's status", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 3
|
||
|
team1: [Factory("Magikarp", ability: "Healer"), Factory("Magikarp"), Factory("Magikarp")]
|
||
|
shared.biasRNG.call(this, "randInt", "healer", 3)
|
||
|
@team1.at(0).attach(Status.Burn)
|
||
|
@team1.at(1).attach(Status.Burn)
|
||
|
@team1.at(2).attach(Status.Burn)
|
||
|
@battle.endTurn()
|
||
|
@team1.at(0).hasStatus().should.be.true
|
||
|
@team1.at(1).hasStatus().should.be.false
|
||
|
@team1.at(2).hasStatus().should.be.true
|
||
|
|
||
|
it "has a 70% chance to do nothing", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Healer")]
|
||
|
shared.biasRNG.call(this, "randInt", "healer", 4)
|
||
|
@p1.attach(Status.Burn)
|
||
|
@battle.endTurn()
|
||
|
@p1.hasStatus().should.be.true
|
||
|
|
||
|
describe "Heatproof", ->
|
||
|
it "receives half damage from Fire-type moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Heatproof")])
|
||
|
ember = @battle.getMove("Ember")
|
||
|
ember.modifyBasePower(@battle, @p2, @p1).should.equal(0x800)
|
||
|
|
||
|
it "receives normal damage from non-Fire-type moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Heatproof")])
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p2, @p1).should.equal(0x1000)
|
||
|
|
||
|
it "receives half damage from burn", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Heatproof")])
|
||
|
@p1.attach(Status.Burn)
|
||
|
burn = @p1.get(Status.Burn)
|
||
|
mock = @sandbox.mock(burn).expects('endTurn').returns(@p1.currentHP >> 4)
|
||
|
@battle.endTurn()
|
||
|
mock.verify()
|
||
|
|
||
|
describe "Heavy Metal", ->
|
||
|
it "doubles the user's weight", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Heavy Metal")])
|
||
|
@p1.calculateWeight().should.equal(2 * @p1.weight)
|
||
|
|
||
|
testHugePowerAbility = (name) ->
|
||
|
describe name, ->
|
||
|
it "doubles attack when using a physical move", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x2000)
|
||
|
|
||
|
it "does not double attack when using a special move", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
thunderbolt.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
testHugePowerAbility("Huge Power")
|
||
|
testHugePowerAbility("Pure Power")
|
||
|
|
||
|
describe "Hustle", ->
|
||
|
it "multiplies attack by x1.5", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Hustle")])
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
thunderbolt.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "makes physical moves have 20% less accuracy", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Hustle")])
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
accuracy = tackle.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal Math.floor(tackle.accuracy * 0.8)
|
||
|
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
accuracy = thunderbolt.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal Math.floor(thunderbolt.accuracy * 1.0)
|
||
|
|
||
|
describe "Hydration", ->
|
||
|
it "restores status, in Rain, at the end of the turn", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Hydration")])
|
||
|
@p1.attach(Status.Sleep)
|
||
|
@battle.setWeather(Weather.RAIN)
|
||
|
@battle.endTurn()
|
||
|
@p1.hasStatus().should.be.false
|
||
|
|
||
|
it "does not restore status if the weather is not rainy", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Hydration")])
|
||
|
@p1.attach(Status.Sleep)
|
||
|
@battle.endTurn()
|
||
|
@p1.hasStatus().should.be.true
|
||
|
|
||
|
describe "Ice Body", ->
|
||
|
it "restores 1/16 HP in Hail", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Ice Body")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.setWeather(Weather.HAIL)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 4))
|
||
|
|
||
|
it "restores no HP in other weather", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Ice Body")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
|
||
|
it "grants immunity to Hail", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Ice Body")]
|
||
|
@p1.isWeatherDamageImmune(Weather.HAIL).should.be.true
|
||
|
|
||
|
describe "Illusion", ->
|
||
|
it "masquerades as the last unfainted pokemon in player's party"
|
||
|
it "does not masquerade if all pokemon are fainted"
|
||
|
it "is broken when the user takes direct damage"
|
||
|
|
||
|
describe "Imposter", ->
|
||
|
it "automatically transforms into the adjacent foe pokemon", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: (Factory("Magikarp") for x in [1..2])
|
||
|
team2: [Factory("Celebi"), Factory("Mew")]
|
||
|
@team1.at(1).copyAbility(Ability.Imposter)
|
||
|
@team1.at(1).has(Attachment.Transform).should.be.true
|
||
|
|
||
|
it "does not transform if target is behind a substitute", ->
|
||
|
shared.create.call(this)
|
||
|
@p2.attach(Attachment.Substitute, hp: 1)
|
||
|
@p1.copyAbility(Ability.Imposter)
|
||
|
@p1.has(Attachment.Transform).should.be.false
|
||
|
|
||
|
it "does not transform if target is already transformed", ->
|
||
|
shared.create.call(this)
|
||
|
@battle.performMove(@p2, @battle.getMove("Transform"))
|
||
|
@p2.has(Attachment.Transform).should.be.true
|
||
|
@p1.copyAbility(Ability.Imposter)
|
||
|
@p1.has(Attachment.Transform).should.be.false
|
||
|
|
||
|
it "does not transform if no one is alive", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Celebi")]
|
||
|
@p2.faint()
|
||
|
@p1.copyAbility(Ability.Imposter)
|
||
|
@p1.has(Attachment.Transform).should.be.false
|
||
|
|
||
|
testAttachmentImmuneAbility = (name, attachments, options = {}) ->
|
||
|
describe name, ->
|
||
|
it "prevents the pokemon from receiving a specific attachment", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: "F", ability: name)]
|
||
|
team2: [Factory("Magikarp", gender: "M")]
|
||
|
for attachment in attachments
|
||
|
should.not.exist @p1.attach(attachment, source: @p2)
|
||
|
@p1.has(attachment).should.be.false
|
||
|
|
||
|
shouldCure = options.cure ? true
|
||
|
if shouldCure
|
||
|
it "removes the attachment if the pokemon already has it", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: "F")]
|
||
|
team2: [Factory("Magikarp", gender: "M")]
|
||
|
for attachment in attachments
|
||
|
@p1.attach(attachment, source: @p2)
|
||
|
@p1.has(attachment).should.be.true
|
||
|
@p1.copyAbility(Ability[name.replace(/\s+/g, '')])
|
||
|
@p1.update()
|
||
|
@p1.has(attachment).should.be.false
|
||
|
@p1.copyAbility(null)
|
||
|
|
||
|
testAttachmentImmuneAbility("Immunity", [Status.Poison, Status.Toxic])
|
||
|
testAttachmentImmuneAbility("Inner Focus", [Attachment.Flinch], cure: false)
|
||
|
testAttachmentImmuneAbility("Insomnia", [Status.Sleep])
|
||
|
testAttachmentImmuneAbility("Limber", [Status.Paralyze])
|
||
|
testAttachmentImmuneAbility("Magma Armor", [Status.Freeze])
|
||
|
testAttachmentImmuneAbility("Oblivious", [Attachment.Attract])
|
||
|
testAttachmentImmuneAbility("Own Tempo", [Attachment.Confusion])
|
||
|
testAttachmentImmuneAbility("Vital Spirit", [Status.Sleep])
|
||
|
testAttachmentImmuneAbility("Water Veil", [Status.Burn])
|
||
|
|
||
|
describe "Intimidate", ->
|
||
|
it "lowers the attack of all foe pokemon", ->
|
||
|
shared.create.call this,
|
||
|
team1: [ Factory("Magikarp", ability: "Intimidate") ]
|
||
|
@p2.stages.should.containEql(attack: -1)
|
||
|
|
||
|
it "does not lower attack of Pokemon behind Substitute", ->
|
||
|
shared.create.call this,
|
||
|
team1: [ Factory("Magikarp") ]
|
||
|
@p2.attach(Attachment.Substitute, hp: (@p2.currentHP >> 2))
|
||
|
@p1.copyAbility(Ability.Intimidate)
|
||
|
@p2.stages.should.containEql(attack: 0)
|
||
|
|
||
|
it "lowers attack simultaneously on all begin-turn switch-ins", ->
|
||
|
shared.create.call this,
|
||
|
team1: (Factory("Magikarp", ability: "Intimidate") for x in [1..2])
|
||
|
team2: (Factory("Magikarp", ability: "Intimidate") for x in [1..2])
|
||
|
@p1.stages.should.containEql(attack: -1)
|
||
|
@p2.stages.should.containEql(attack: -1)
|
||
|
@p1.faint()
|
||
|
@p2.faint()
|
||
|
@battle.requestFaintedReplacements()
|
||
|
@controller.makeSwitch(@id1, 1)
|
||
|
@controller.makeSwitch(@id2, 1)
|
||
|
@team1.first().should.not.equal(@p1)
|
||
|
@team2.first().should.not.equal(@p2)
|
||
|
@team1.first().stages.should.containEql(attack: -1)
|
||
|
@team2.first().stages.should.containEql(attack: -1)
|
||
|
|
||
|
describe "Infiltrator", ->
|
||
|
it "ignores Reflect", ->
|
||
|
shared.create.call this,
|
||
|
team1: [ Factory("Magikarp", ability: "Infiltrator") ]
|
||
|
@team2.attach(Attachment.Reflect)
|
||
|
tackle = @battle.getMove('Tackle')
|
||
|
tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "ignores Light Screen", ->
|
||
|
shared.create.call this,
|
||
|
team1: [ Factory("Magikarp", ability: "Infiltrator") ]
|
||
|
@team2.attach(Attachment.LightScreen)
|
||
|
thundershock = @battle.getMove('ThunderShock')
|
||
|
thundershock.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "ignores Safeguard"
|
||
|
it "ignores Mist"
|
||
|
|
||
|
testContactHurtAbility = (name) ->
|
||
|
describe name, ->
|
||
|
it "damages for 1/8 HP on contact moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name), Factory("Magikarp")])
|
||
|
hp = @p2.stat('hp')
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
(hp - @p2.currentHP).should.equal(hp >> 3)
|
||
|
|
||
|
it "does not damage for non-contact moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name), Factory("Magikarp")])
|
||
|
@battle.performMove(@p2, @battle.getMove("Thunderbolt"))
|
||
|
@p2.currentHP.should.equal @p2.stat('hp')
|
||
|
|
||
|
it "does not damage for non-direct hits", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name), Factory("Magikarp")])
|
||
|
@p1.attach(Attachment.Substitute)
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
@p2.currentHP.should.equal @p2.stat('hp')
|
||
|
|
||
|
it "still works even if the owner faints", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name), Factory("Magikarp")])
|
||
|
hp = @p2.stat('hp')
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
(hp - @p2.currentHP).should.equal(hp >> 3)
|
||
|
|
||
|
it "deals a minimum of 1 damage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
team2: [Factory("Shedinja")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
@p2.currentHP.should.equal(0)
|
||
|
|
||
|
testContactHurtAbility("Iron Barbs")
|
||
|
testContactHurtAbility("Rough Skin")
|
||
|
|
||
|
describe "Iron Fist", ->
|
||
|
it "increases base power of punching moves by approximately x1.3", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Iron Fist")])
|
||
|
icePunch = @battle.getMove("Ice Punch")
|
||
|
icePunch.modifyBasePower(@battle, @p1, @p2).should.equal(0x1333)
|
||
|
|
||
|
it "does not increase base power of non-punching moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Iron Fist")])
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Justified", ->
|
||
|
it "boosts attack by 1 after being hit by a Dark move", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Justified")])
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
@battle.performMove(@p2, @battle.getMove("Crunch"))
|
||
|
@p1.stages.should.containEql(attack: 1)
|
||
|
|
||
|
it "doesn't boost attack if the move is non-damaging", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Justified")])
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
@battle.performMove(@p1, @battle.getMove("Nasty Plot"))
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
|
||
|
it "doesn't boost attack if the move hits a substitute", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Justified")])
|
||
|
@p1.attach(Attachment.Substitute, hp: 1)
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
@battle.performMove(@p2, @battle.getMove("Crunch"))
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
|
||
|
describe "Klutz", ->
|
||
|
it "disables user's item upon switch-in", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", item: "Leftovers", ability: "Klutz")]
|
||
|
@battle.performSwitch(@p1, 1)
|
||
|
@team1.first().isItemBlocked().should.be.true
|
||
|
|
||
|
it "disables user's item in the beginning of the turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Leftovers", ability: "Klutz")]
|
||
|
@p1.isItemBlocked().should.be.true
|
||
|
@battle.beginTurn()
|
||
|
@p1.isItemBlocked().should.be.true
|
||
|
|
||
|
describe "Leaf Guard", ->
|
||
|
it "defends against statuses under Sun", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Leaf Guard")]
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@p1.attach(Status.Burn)
|
||
|
@p1.has(Status.Burn).should.be.false
|
||
|
|
||
|
it "does not defend against statuses otherwise", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Leaf Guard")]
|
||
|
@p1.attach(Status.Burn)
|
||
|
@p1.has(Status.Burn).should.be.true
|
||
|
|
||
|
describe "Levitate", ->
|
||
|
it "adds a ground immunity", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Levitate")]
|
||
|
@p1.isImmune('Ground').should.be.true
|
||
|
|
||
|
describe "Light Metal", ->
|
||
|
it "halves the user's weight", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Light Metal")])
|
||
|
@p1.calculateWeight().should.equal(@p1.weight >> 1)
|
||
|
|
||
|
testRedirectAndBoostAbility = (name, type) ->
|
||
|
describe name, ->
|
||
|
it "should redirect attacks of #{type} to user"
|
||
|
|
||
|
it "makes user immune to #{type}", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == type
|
||
|
mock = @sandbox.mock(typedMove).expects('hit').never()
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
mock.verify()
|
||
|
|
||
|
it "boosts special attack on #{type}-type moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == type
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages.should.containEql(specialAttack: 1)
|
||
|
|
||
|
it "does not boost special attack if the user is the target", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.target == 'user' && m.type == type
|
||
|
@battle.performMove(@p1, typedMove)
|
||
|
@p1.stages.should.containEql(specialAttack: 0)
|
||
|
|
||
|
it "does not boost special attack on #{type}-type moves if immune", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == type
|
||
|
@sandbox.stub(@p1, 'isImmune', -> true)
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages.should.containEql(specialAttack: 0)
|
||
|
|
||
|
it "does nothing otherwise", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
mock = @sandbox.mock(tackle).expects('hit').once()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
mock.verify()
|
||
|
@p1.stages.should.containEql(specialAttack: 0)
|
||
|
|
||
|
testRedirectAndBoostAbility("Lightningrod", "Electric")
|
||
|
testRedirectAndBoostAbility("Storm Drain", "Water")
|
||
|
|
||
|
describe "Liquid Ooze", ->
|
||
|
it "causes drain attacks to damage the user as well", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Liquid Ooze")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Giga Drain"))
|
||
|
@p2.currentHP.should.be.lessThan @p2.stat('hp')
|
||
|
|
||
|
it "causes Leech Seed to damage the user as well", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Liquid Ooze")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Leech Seed"))
|
||
|
@battle.endTurn()
|
||
|
@p2.currentHP.should.be.lessThan @p2.stat('hp')
|
||
|
|
||
|
describe "Magic Bounce", ->
|
||
|
it "still has the magic coat effect next turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Bounce")]
|
||
|
@p1.has(Attachment.MagicCoat).should.be.true
|
||
|
@team1.has(Attachment.MagicCoat).should.be.true
|
||
|
|
||
|
@battle.endTurn()
|
||
|
@p1.has(Attachment.MagicCoat).should.be.true
|
||
|
@team1.has(Attachment.MagicCoat).should.be.true
|
||
|
|
||
|
describe "Magic Guard", ->
|
||
|
it "takes no damage from anything that isn't a move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@p1.attach(Status.Burn)
|
||
|
@p1.currentHP.should.equal(@p1.stat('hp'))
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from Struggle", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@battle.performMove(@p1, @battle.getMove('Struggle'))
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from Belly Drum", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@battle.performMove(@p1, @battle.getMove('Belly Drum'))
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from confusion", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
shared.biasRNG.call(this, 'next', 'confusion', 0)
|
||
|
@p1.attach(Attachment.Confusion)
|
||
|
@battle.performMove(@p1, @battle.getMove('Tackle'))
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from using Curse", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Gengar", ability: "Magic Guard")]
|
||
|
@battle.performMove(@p1, @battle.getMove('Curse'))
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from Substitute", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@battle.performMove(@p1, @battle.getMove('Substitute'))
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "takes damage from Pain Split", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p1, @battle.getMove('Pain Split'))
|
||
|
@p1.currentHP.should.be.greaterThan(1)
|
||
|
|
||
|
it "takes damage from Counter", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard", moves: ["Counter"])]
|
||
|
@controller.makeMove(@id1, "Counter")
|
||
|
@controller.makeMove(@id2, "Tackle")
|
||
|
@p1.currentHP.should.be.lessThan(@p1.stat('hp'))
|
||
|
|
||
|
it "restores health from Leftovers", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard", item: "Leftovers")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.be.greaterThan(1)
|
||
|
|
||
|
it "receives no damage from Rocky Helmet", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magic Guard")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
|
||
|
describe "Magnet Pull", ->
|
||
|
it "prevents Steel-type Pokemon from switching", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magnet Pull")]
|
||
|
team2: [Factory("Magnemite")]
|
||
|
@p2.isSwitchBlocked().should.be.true
|
||
|
@battle.beginTurn()
|
||
|
@p2.isSwitchBlocked().should.be.true
|
||
|
|
||
|
it "doesn't prevent non-Steel-type Pokemon from switching", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Magnet Pull")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
@p2.isSwitchBlocked().should.be.false
|
||
|
@battle.beginTurn()
|
||
|
@p2.isSwitchBlocked().should.be.false
|
||
|
|
||
|
describe "Marvel Scale", ->
|
||
|
it "multiplies defense by 1.5 when statused", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Marvel Scale")]
|
||
|
defense = @p1.stat('defense')
|
||
|
@p1.attach(Status.Burn)
|
||
|
@p1.stat('defense').should.equal Math.floor(1.5 * defense)
|
||
|
|
||
|
describe "Minus", ->
|
||
|
it "multiplies special attack by 1.5 if on the field with Plus", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
specialAttack = @p1.stat('specialAttack')
|
||
|
@team1.at(0).copyAbility(Ability.Minus)
|
||
|
@team1.at(1).copyAbility(Ability.Plus)
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
thunderbolt.modifyAttack(@battle, @team1.at(0), @p2).should.equal(0x1800)
|
||
|
|
||
|
testAbilityCancelAbility = (name) ->
|
||
|
describe name, ->
|
||
|
it "emits a catchphrase when switching in or activating", ->
|
||
|
shared.create.call(this)
|
||
|
mock = @sandbox.mock(@battle).expects('cannedText').once()
|
||
|
@p1.copyAbility(Ability[name.replace(/\s+/, '')])
|
||
|
mock.verify()
|
||
|
|
||
|
it "cancels abilities for the duration of the user's move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
team2: [Factory("Magikarp", ability: "Levitate")]
|
||
|
eq = @battle.getMove("Earthquake")
|
||
|
mock = @sandbox.mock(eq).expects('hit').once()
|
||
|
@battle.performMove(@p1, eq)
|
||
|
mock.verify()
|
||
|
|
||
|
it "cancels modifier abilities for the duration of the user's move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
team2: [Factory("Magikarp", ability: "Thick Fat")]
|
||
|
ib = @battle.getMove("Ice Beam")
|
||
|
spy = @sandbox.spy(ib, 'modifyAttack')
|
||
|
spy = spy.withArgs(@battle, @p1, @p2)
|
||
|
@battle.performMove(@p1, ib)
|
||
|
spy.returnValues[0].should.equal(0x1000)
|
||
|
|
||
|
it "resets canceled abilities after the move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
team2: [Factory("Magikarp", ability: "Levitate")]
|
||
|
eq = @battle.getMove("Earthquake")
|
||
|
@p2.isAbilityBlocked().should.be.false
|
||
|
@battle.performMove(@p1, eq)
|
||
|
@p2.isAbilityBlocked().should.be.false
|
||
|
|
||
|
it "does not reset already-canceled abilities after performing move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
team2: [Factory("Magikarp", ability: "Levitate")]
|
||
|
@p2.blockAbility()
|
||
|
eq = @battle.getMove("Earthquake")
|
||
|
@p2.isAbilityBlocked().should.be.true
|
||
|
@battle.performMove(@p1, eq)
|
||
|
@p2.isAbilityBlocked().should.be.true
|
||
|
|
||
|
it "works when using field moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
sr = @battle.getMove("Stealth Rock")
|
||
|
(=> @battle.performMove(@p1, sr)).should.not.throw()
|
||
|
|
||
|
it "ignores Unaware", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Haxorus", ability: name)]
|
||
|
team2: [Factory("Quagsire", ability: "Unaware")]
|
||
|
|
||
|
@p1.boost(attack: 2)
|
||
|
spy = @sandbox.spy(@p1, "editBoosts").withArgs(sinon.match(ignoreOffense: false))
|
||
|
@battle.performMove(@p1, @battle.getMove("Outrage"))
|
||
|
spy.returned(sinon.match(attack: 2)).should.be.true
|
||
|
|
||
|
testAbilityCancelAbility("Mold Breaker")
|
||
|
testAbilityCancelAbility("Teravolt")
|
||
|
testAbilityCancelAbility("Turboblaze")
|
||
|
|
||
|
describe "Moody", ->
|
||
|
it "at turn end, randomly raise a stat by 2 and lower another by 1", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Moody")])
|
||
|
shared.biasRNG.call(this, "randInt", "moody raise", 1)
|
||
|
shared.biasRNG.call(this, "randInt", "moody lower", 0)
|
||
|
@battle.endTurn()
|
||
|
@p1.stages.should.containEql(attack: -1, defense: 2)
|
||
|
|
||
|
it "never raises and lowers the same stat in a single turn", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Moody")])
|
||
|
shared.biasRNG.call(this, "randInt", "moody raise", 0)
|
||
|
shared.biasRNG.call(this, "randInt", "moody lower", 0)
|
||
|
@battle.endTurn()
|
||
|
@p1.stages.should.containEql(attack: 2, defense: -1)
|
||
|
|
||
|
it "does not choose individual stats at max/min", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Moody")])
|
||
|
shared.biasRNG.call(this, "randInt", "moody raise", 1)
|
||
|
shared.biasRNG.call(this, "randInt", "moody lower", 0)
|
||
|
@p1.boost(defense: 6)
|
||
|
@p1.boost(attack: -6)
|
||
|
@battle.endTurn()
|
||
|
@p1.stages.should.containEql(defense: 5, speed: 2)
|
||
|
|
||
|
it "does not try to raise a null stat", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Moody")])
|
||
|
shared.biasRNG.call(this, "randInt", "moody raise", 1)
|
||
|
shared.biasRNG.call(this, "randInt", "moody lower", 0)
|
||
|
@p1.boost(attack: 6, defense: 6, speed: 6, specialAttack: 6)
|
||
|
@p1.boost(specialDefense: 6, accuracy: 6, evasion: 6)
|
||
|
(=> @battle.endTurn()).should.not.throw()
|
||
|
|
||
|
testTypeImmuneAbility = (name, type, stat) ->
|
||
|
describe name, ->
|
||
|
it "makes the user immune to #{type} moves", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == type
|
||
|
mock = @sandbox.mock(typedMove).expects('hit').never()
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
mock.verify()
|
||
|
|
||
|
it "increases #{stat} by 1 if hit by a #{type}-type move", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == type
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages[stat].should.equal(1)
|
||
|
|
||
|
it "increases #{stat} by only 1 even if move is multi-hit", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == type
|
||
|
@sandbox.stub(typedMove, 'calculateNumberOfHits', -> 2)
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages[stat].should.equal(1)
|
||
|
|
||
|
it "does not increase #{stat} by 1 if the user is the target", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.target == 'user' && m.type == type
|
||
|
@battle.performMove(@p1, typedMove)
|
||
|
@p1.stages[stat].should.equal(0)
|
||
|
|
||
|
it "does nothing otherwise", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: name)])
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type != type
|
||
|
mock = @sandbox.mock(typedMove).expects('hit').once()
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages[stat].should.equal(0)
|
||
|
mock.verify()
|
||
|
|
||
|
testTypeImmuneAbility("Motor Drive", "Electric", "speed")
|
||
|
testTypeImmuneAbility("Sap Sipper", "Grass", "attack")
|
||
|
|
||
|
describe "Moxie", ->
|
||
|
it "increases attack every time it faints another target", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Moxie")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.currentHP = 1
|
||
|
@p1.stages.should.containEql(attack: 0)
|
||
|
@battle.performMove(@p1, tackle)
|
||
|
@p1.stages.should.containEql(attack: 1)
|
||
|
|
||
|
describe "Multiscale", ->
|
||
|
it "takes half damage at full HP", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Multiscale")]
|
||
|
iceBeam = @battle.getMove("Ice Beam")
|
||
|
iceBeam.modifyDamage(@battle, @p2, @p1).should.equal(0x800)
|
||
|
|
||
|
it "takes normal damage at other HP", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Multiscale")]
|
||
|
iceBeam = @battle.getMove("Ice Beam")
|
||
|
@p1.currentHP -= 1
|
||
|
iceBeam.modifyDamage(@battle, @p2, @p1).should.equal(0x1000)
|
||
|
|
||
|
describe "Multitype", ->
|
||
|
it "changes Arceus forme for different plates"
|
||
|
|
||
|
describe "Mummy", ->
|
||
|
it "changes the attacker's ability to Mummy on contact", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Mummy")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.hasAbility("Mummy").should.be.false
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.hasAbility("Mummy").should.be.true
|
||
|
|
||
|
it "doesn't change ability if move used isn't a contact move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Mummy")]
|
||
|
iceBeam = @battle.getMove("Ice Beam")
|
||
|
@p2.hasAbility("Mummy").should.be.false
|
||
|
@battle.performMove(@p2, iceBeam)
|
||
|
@p2.hasAbility("Mummy").should.be.false
|
||
|
|
||
|
it "doesn't change ability if attacker has Multitype", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Mummy")]
|
||
|
team2: [Factory("Magikarp", ability: "Multitype")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.hasAbility("Mummy").should.be.false
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.hasAbility("Mummy").should.be.false
|
||
|
|
||
|
it "works even if the defender faints", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Mummy"), Factory("Magikarp")]
|
||
|
@p1.currentHP = 1
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.hasAbility("Mummy").should.be.false
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.hasAbility("Mummy").should.be.true
|
||
|
|
||
|
describe "Natural Cure", ->
|
||
|
it "cures status upon switch out", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Natural Cure")]
|
||
|
@p1.attach(Status.Burn)
|
||
|
@p1.switchOut(@battle)
|
||
|
@p1.hasStatus().should.be.false
|
||
|
|
||
|
describe "No Guard", ->
|
||
|
it "makes every move by this Pokemon never miss", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "No Guard")]
|
||
|
shared.biasRNG.call(this, 'randInt', 'miss', 101)
|
||
|
focusBlast = @battle.getMove("Focus Blast")
|
||
|
focusBlast.willMiss(@battle, @p1, @p2).should.be.false
|
||
|
|
||
|
it "makes every move against this Pokemon never miss", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "No Guard")]
|
||
|
shared.biasRNG.call(this, 'randInt', 'miss', 101)
|
||
|
focusBlast = @battle.getMove("Focus Blast")
|
||
|
focusBlast.willMiss(@battle, @p2, @p1).should.be.false
|
||
|
|
||
|
describe "Normalize", ->
|
||
|
it "makes every move act as if it were Normal type", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Normalize")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
thunderbolt.typeEffectiveness(@battle, @p1, @p2).should.equal(1)
|
||
|
|
||
|
it "has no effect if target is self", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Normalize")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
thunderbolt.typeEffectiveness(@battle, @p1, @p1).should.equal(2)
|
||
|
|
||
|
it "has no effect on struggle", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Normalize")]
|
||
|
team2: [Factory("Skarmory")]
|
||
|
struggle = @battle.getMove("Struggle")
|
||
|
struggle.typeEffectiveness(@battle, @p1, @p2).should.equal(1)
|
||
|
|
||
|
describe "Overcoat", ->
|
||
|
it "gives an immunity to adverse weather effects", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Overcoat")]
|
||
|
@p1.isWeatherDamageImmune(Weather.HAIL).should.be.true
|
||
|
@p1.isWeatherDamageImmune(Weather.SAND).should.be.true
|
||
|
|
||
|
describe "Pickpocket", ->
|
||
|
it "steals the item of an opponent that makes contact", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Pickpocket")]
|
||
|
team2: [Factory("Magikarp", item: "Leftovers")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p1.hasItem("Leftovers").should.be.true
|
||
|
@p2.hasItem("Leftovers").should.be.false
|
||
|
|
||
|
describe "Plus", ->
|
||
|
it "multiplies special attack by 1.5 if on the field with Minus", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
specialAttack = @p1.stat('specialAttack')
|
||
|
@team1.at(0).copyAbility(Ability.Minus)
|
||
|
@team1.at(1).copyAbility(Ability.Plus)
|
||
|
thunderbolt = @battle.getMove("Thunderbolt")
|
||
|
thunderbolt.modifyAttack(@battle, @team1.at(1), @p2).should.equal(0x1800)
|
||
|
|
||
|
describe "Poison Heal", ->
|
||
|
it "prevents normal poison damage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Poison Heal")]
|
||
|
@p1.attach(Status.Poison)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.not.be.lessThan @p1.stat('hp')
|
||
|
@p1.cureStatus()
|
||
|
@p1.attach(Status.Toxic)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.not.be.lessThan @p1.stat('hp')
|
||
|
|
||
|
it "heals 1/8 HP end of turn while poisoned", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Poison Heal")]
|
||
|
@p1.attach(Status.Poison)
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 3))
|
||
|
|
||
|
it "heals 1/8 HP end of turn while toxiced", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Poison Heal")]
|
||
|
@p1.attach(Status.Toxic)
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 3))
|
||
|
|
||
|
describe "Prankster", ->
|
||
|
it "makes non-damaging moves have a priority (priority + 1)", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Prankster")]
|
||
|
spore = @battle.getMove("Spore")
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
action = {type: "move", move: spore, pokemon: @p1}
|
||
|
@battle.actionPriority(action).should.equal(spore.priority + 1)
|
||
|
action.move = tackle
|
||
|
@battle.actionPriority(action).should.equal(tackle.priority)
|
||
|
|
||
|
describe "Pressure", ->
|
||
|
it "reduces a move's PP further by 1 if targetted by foe's move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Pressure")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p2.moves = [ tackle ]
|
||
|
@p2.resetAllPP()
|
||
|
pp = @p2.pp(tackle)
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p2.pp(tackle).should.equal(pp - 2)
|
||
|
|
||
|
it "does not reduce a move's PP by 1 if target is self", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Pressure")]
|
||
|
dd = @battle.getMove("Dragon Dance")
|
||
|
@p1.moves = [ dd ]
|
||
|
@p1.resetAllPP()
|
||
|
pp = @p1.pp(dd)
|
||
|
@battle.performMove(@p1, dd)
|
||
|
@p1.pp(dd).should.equal(pp - 1)
|
||
|
|
||
|
describe "Quick Feet", ->
|
||
|
it "increases speed by x1.5 when statused", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Quick Feet")]
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.attach(Status.Sleep)
|
||
|
@p1.stat('speed').should.equal Math.floor(1.5 * speed)
|
||
|
|
||
|
it "negates speed drop from paralysis", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Quick Feet")]
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.attach(Status.Paralyze)
|
||
|
@p1.stat('speed').should.equal Math.floor(1.5 * speed)
|
||
|
|
||
|
describe "Rain Dish", ->
|
||
|
it "restores 1/16 HP at end of turn under Rain", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Rain Dish")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.setWeather(Weather.RAIN)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 4))
|
||
|
|
||
|
it "does not restore HP at end of turn under non-Rain weather", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Rain Dish")]
|
||
|
@p1.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
|
||
|
describe "Rattled", ->
|
||
|
for type in ["Ghost", "Bug", "Dark"]
|
||
|
do (type) ->
|
||
|
it "raises speed by 1 when hit by a #{type} move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Rattled")]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.type == type
|
||
|
@p1.stages.speed.should.equal(0)
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages.speed.should.equal(1)
|
||
|
|
||
|
it "does not raise speed by 1 when hit by a non-damaging #{type} move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Rattled")]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.isNonDamaging() && m.type == type
|
||
|
@p1.stages.speed.should.equal(0)
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages.speed.should.equal(0)
|
||
|
|
||
|
it "does not raise speed by 1 when a #{type} move hits a substitute", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Rattled")]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.isNonDamaging() && m.type == type
|
||
|
@p1.attach(Attachment.Substitute, hp: 1)
|
||
|
@p1.stages.speed.should.equal(0)
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.stages.speed.should.equal(0)
|
||
|
|
||
|
describe "Reckless", ->
|
||
|
it "gives a 1.2x boost to recoil moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Reckless")]
|
||
|
recoilMoves = @battle.MoveList.filter (m) ->
|
||
|
m.recoil < 0
|
||
|
for move in recoilMoves
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1333)
|
||
|
|
||
|
it "boosts the kick moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Reckless")]
|
||
|
recoilMoves = [ @battle.getMove("Jump Kick"),
|
||
|
@battle.getMove("Hi Jump Kick") ]
|
||
|
for move in recoilMoves
|
||
|
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1333)
|
||
|
|
||
|
it "does not boost struggle", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Reckless")]
|
||
|
struggle = @battle.getMove("Struggle")
|
||
|
struggle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "does not boost drain moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Reckless")]
|
||
|
absorb = @battle.getMove("Absorb")
|
||
|
absorb.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Regenerator", ->
|
||
|
it "restores 1/3 of the user's HP upon switch out", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Regenerator")]
|
||
|
@p1.currentHP = 1
|
||
|
@p1.switchOut(@battle)
|
||
|
hp = Math.floor(@p1.stat('hp') / 3)
|
||
|
@p1.currentHP.should.equal(1 + hp)
|
||
|
|
||
|
describe "Rivalry", ->
|
||
|
it "reduces base power by 25% if user and target are opposite genders", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: 'F', ability: "Rivalry")]
|
||
|
team2: [Factory("Magikarp", gender: 'M')]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0xC00)
|
||
|
|
||
|
it "increases base power by 25% if user and target are the same gender", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: 'F', ability: "Rivalry")]
|
||
|
team2: [Factory("Magikarp", gender: 'F')]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1400)
|
||
|
|
||
|
it "has normal base power if either user or target have no gender", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", gender: 'F', ability: "Rivalry")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Rock Head", ->
|
||
|
it "negates recoil", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Rock Head")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Head Smash"))
|
||
|
@p1.currentHP.should.equal @p1.stat('hp')
|
||
|
|
||
|
describe "Sand Force", ->
|
||
|
it "increases BP of Ground-, Rock-, and Steel-type moves by 30% in sand", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sand Force")]
|
||
|
@battle.setWeather(Weather.SAND)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
earthquake = @battle.getMove("Earthquake")
|
||
|
earthquake.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||
|
rockSlide = @battle.getMove("Rock Slide")
|
||
|
rockSlide.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||
|
meteorMash = @battle.getMove("Meteor Mash")
|
||
|
meteorMash.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||
|
|
||
|
it "does not increase BP if weather is not sand", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sand Force")]
|
||
|
@battle.setWeather(Weather.NONE)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
earthquake = @battle.getMove("Earthquake")
|
||
|
earthquake.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
rockSlide = @battle.getMove("Rock Slide")
|
||
|
rockSlide.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
meteorMash = @battle.getMove("Meteor Mash")
|
||
|
meteorMash.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "grants immunity to sandstorm", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sand Force")]
|
||
|
@p1.isWeatherDamageImmune(Weather.SAND).should.be.true
|
||
|
|
||
|
describe "Sand Veil", ->
|
||
|
it "increases evasion by 25% in Sand", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sand Veil")]
|
||
|
@battle.setWeather(Weather.SAND)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(80)
|
||
|
|
||
|
it "does nothing outside of Sand", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sand Veil")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(100)
|
||
|
|
||
|
it "makes the user immune to Sand", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sand Veil")]
|
||
|
@p1.isWeatherDamageImmune(Weather.SAND).should.be.true
|
||
|
|
||
|
describe "Scrappy", ->
|
||
|
it "negates pokemon immunities", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Scrappy")]
|
||
|
team2: [Factory("Gengar")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
closeCombat = @battle.getMove("Close Combat")
|
||
|
tackle.typeEffectiveness(@battle, @p1, @p2).should.equal(1)
|
||
|
closeCombat.typeEffectiveness(@battle, @p1, @p2).should.equal(.5)
|
||
|
mock1 = @sandbox.mock(tackle).expects('hit').once()
|
||
|
mock2 = @sandbox.mock(closeCombat).expects('hit').once()
|
||
|
@battle.performMove(@p1, tackle)
|
||
|
@battle.performMove(@p1, closeCombat)
|
||
|
mock1.verify()
|
||
|
mock2.verify()
|
||
|
|
||
|
describe "Serene Grace", ->
|
||
|
it "doubles the chance of secondary effects happening", ->
|
||
|
shared.create.call(this)
|
||
|
shared.biasRNG.call(this, "randInt", "secondary effect", 10)
|
||
|
ember = @battle.getMove("Ember")
|
||
|
@sandbox.stub(ember, "baseDamage", -> 1)
|
||
|
|
||
|
@battle.performMove(@p1, ember)
|
||
|
@p2.has(Status.Burn).should.be.false
|
||
|
|
||
|
@p1.copyAbility(Ability.SereneGrace)
|
||
|
@battle.performMove(@p1, ember)
|
||
|
@p2.has(Status.Burn).should.be.true
|
||
|
|
||
|
it "doubles the chance of secondary boosts happening", ->
|
||
|
shared.create.call(this)
|
||
|
shared.biasRNG.call(this, "randInt", "secondary boost", 10)
|
||
|
acid = @battle.getMove("Acid")
|
||
|
@sandbox.stub(acid, "baseDamage", -> 1)
|
||
|
|
||
|
@battle.performMove(@p1, acid)
|
||
|
@p2.stages.should.containEql(specialDefense: 0)
|
||
|
|
||
|
@p1.copyAbility(Ability.SereneGrace)
|
||
|
@battle.performMove(@p1, acid)
|
||
|
@p2.stages.should.containEql(specialDefense: -1)
|
||
|
|
||
|
it "doubles the chance of flinches happening", ->
|
||
|
shared.create.call(this)
|
||
|
shared.biasRNG.call(this, "randInt", "flinch", 30)
|
||
|
ironHead = @battle.getMove("Iron Head")
|
||
|
@sandbox.stub(ironHead, "baseDamage", -> 1)
|
||
|
|
||
|
@battle.performMove(@p1, ironHead)
|
||
|
@p2.has(Attachment.Flinch).should.be.false
|
||
|
|
||
|
@p1.copyAbility(Ability.SereneGrace)
|
||
|
@battle.performMove(@p1, ironHead)
|
||
|
@p2.has(Attachment.Flinch).should.be.true
|
||
|
|
||
|
describe "Shadow Tag", ->
|
||
|
it "prevents foes from switching", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Shadow Tag")]
|
||
|
@p2.isSwitchBlocked().should.be.true
|
||
|
@battle.beginTurn()
|
||
|
@p2.isSwitchBlocked().should.be.true
|
||
|
|
||
|
it "does not affect foes with Shadow Tag", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Shadow Tag")]
|
||
|
team2: [Factory("Magikarp", ability: "Shadow Tag")]
|
||
|
@p2.isSwitchBlocked().should.be.false
|
||
|
@battle.beginTurn()
|
||
|
@p2.isSwitchBlocked().should.be.false
|
||
|
|
||
|
describe "Shed Skin", ->
|
||
|
it "has a 30% chance of removing its status effect at end of turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Shed Skin")]
|
||
|
shared.biasRNG.call(this, "randInt", "shed skin", 3)
|
||
|
@p1.attach(Status.Burn)
|
||
|
@battle.endTurn()
|
||
|
@p1.has(Status.Burn).should.be.false
|
||
|
|
||
|
it "has a 70% chance of doing nothing at end of turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Shed Skin")]
|
||
|
shared.biasRNG.call(this, "randInt", "shed skin", 4)
|
||
|
@p1.attach(Status.Burn)
|
||
|
@battle.endTurn()
|
||
|
@p1.has(Status.Burn).should.be.true
|
||
|
|
||
|
describe "Sheer Force", ->
|
||
|
it "raises power of moves with secondary effects by 30%", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force")]
|
||
|
ember = @battle.getMove("Ember")
|
||
|
ember.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||
|
|
||
|
it "does not raise power of moves without secondary effects", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
it "nullifies secondary effects", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force", item: "Life Orb")]
|
||
|
shared.biasRNG.call(this, 'next', 'secondary effect', 0) # always happens
|
||
|
@battle.performMove(@p1, @battle.getMove("Zap Cannon"))
|
||
|
@p2.has(Status.Paralyze).should.be.false
|
||
|
|
||
|
it "nullifies flinch", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force", item: "Life Orb")]
|
||
|
shared.biasRNG.call(this, 'next', 'flinch', 0) # always happens
|
||
|
@battle.performMove(@p1, @battle.getMove("Iron Head"))
|
||
|
@p2.has(Attachment.Flinch).should.be.false
|
||
|
|
||
|
it "nullifies secondary boosts", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force", item: "Life Orb")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Charge Beam"))
|
||
|
@p1.stages.should.containEql(specialAttack: 0)
|
||
|
|
||
|
it "does not apply to primary boosts", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force", item: "Life Orb")]
|
||
|
cc = @battle.getMove("Close Combat")
|
||
|
cc.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
@battle.performMove(@p1, cc)
|
||
|
@p1.stages.should.containEql(defense: -1, specialDefense: -1)
|
||
|
|
||
|
it "receives no life orb recoil", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force", item: "Life Orb")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Ember"))
|
||
|
@p1.currentHP.should.not.be.lessThan @p1.stat('hp')
|
||
|
|
||
|
it "receives life orb recoil if no secondary effect", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force", item: "Life Orb")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Tackle"))
|
||
|
@p1.currentHP.should.be.lessThan @p1.stat('hp')
|
||
|
|
||
|
it "does not activate Red Card", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp", item: "Red Card"), Factory("Magikarp")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Ember"))
|
||
|
@p1.should.equal @team1.first()
|
||
|
|
||
|
it "activates Red Card if no secondary effect", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp", item: "Red Card"), Factory("Magikarp")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Tackle"))
|
||
|
@p1.should.not.equal @team1.first()
|
||
|
|
||
|
it "does not activate Eject Button", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp", item: "Eject Button"), Factory("Magikarp")]
|
||
|
@battle.recordMove(@id2, @battle.getMove("Tackle"))
|
||
|
should.not.exist @battle.requests[@id2]
|
||
|
@battle.performMove(@p1, @battle.getMove("Ember"))
|
||
|
should.not.exist @battle.requests[@id2]
|
||
|
|
||
|
it "activates Eject Button if no secondary effect", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sheer Force"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp", item: "Eject Button"), Factory("Magikarp")]
|
||
|
@battle.recordMove(@id2, @battle.getMove("Tackle"))
|
||
|
should.not.exist @battle.requests[@id2]
|
||
|
@battle.performMove(@p1, @battle.getMove("Tackle"))
|
||
|
@battle.requests[@id2].should.not.be.empty
|
||
|
|
||
|
it "does not activate Jaboca Berry?"
|
||
|
it "does not activate Rowap Berry?"
|
||
|
|
||
|
describe "Shield Dust", ->
|
||
|
it "prevents secondary effects", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Shield Dust")]
|
||
|
shared.biasRNG.call(this, 'next', 'secondary effect', 0) # always happens
|
||
|
@battle.performMove(@p1, @battle.getMove("Zap Cannon"))
|
||
|
@p2.has(Status.Paralyze).should.be.false
|
||
|
|
||
|
it "prevents secondary flinches", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Shield Dust")]
|
||
|
shared.biasRNG.call(this, 'randInt', 'flinch', 0) # 100% chance
|
||
|
@battle.performMove(@p1, @battle.getMove("Iron Head"))
|
||
|
@p2.has(Attachment.Flinch).should.be.false
|
||
|
|
||
|
it "prevents secondary boosts against the target", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Shield Dust")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Acid Spray"))
|
||
|
@p2.stages.should.containEql(specialDefense: 0)
|
||
|
|
||
|
it "does not prevent secondary boosts for the user", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Shield Dust")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Flame Charge"))
|
||
|
@p1.stages.should.containEql(speed: 1)
|
||
|
|
||
|
describe "Simple", ->
|
||
|
it "doubles stat boosts, negative and positive", ->
|
||
|
shared.create.call(this, team1: [Factory("Magikarp", ability: "Simple")])
|
||
|
@battle.performMove(@p1, @battle.getMove("Curse"))
|
||
|
@p1.stages.should.containEql(attack: 2, defense: 2, speed: -2)
|
||
|
|
||
|
describe "Slow Start", ->
|
||
|
it "halves attack and speed", ->
|
||
|
shared.create.call(this)
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.copyAbility(Ability.SlowStart)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x800)
|
||
|
flamethrower = @battle.getMove("Flamethrower")
|
||
|
flamethrower.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
@p1.stat('speed').should.equal(speed >> 1)
|
||
|
|
||
|
it "returns attack and speed to normal after five turns", ->
|
||
|
shared.create.call(this)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
flamethrower = @battle.getMove("Flamethrower")
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.copyAbility(Ability.SlowStart)
|
||
|
for x in [0...5]
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x800)
|
||
|
flamethrower.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
@p1.stat('speed').should.equal(speed >> 1)
|
||
|
@battle.endTurn()
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
flamethrower.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
@p1.stat('speed').should.equal(speed)
|
||
|
|
||
|
describe "Sniper", ->
|
||
|
it "multiplies damage by a further x1.5 if critting", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sniper")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p1.crit = true
|
||
|
tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
|
||
|
it "has a normal 1x multiplier if not critting", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sniper")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p1.crit = false
|
||
|
tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Snow Cloak", ->
|
||
|
it "increases evasion by 25% in hail", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Snow Cloak")]
|
||
|
@battle.setWeather(Weather.HAIL)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(80)
|
||
|
|
||
|
it "does nothing outside of hail", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Snow Cloak")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(100)
|
||
|
|
||
|
it "makes the user immune to hail", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Snow Cloak")]
|
||
|
@p1.isWeatherDamageImmune(Weather.HAIL).should.be.true
|
||
|
|
||
|
describe "Solar Power", ->
|
||
|
it "increases special attack by 50% in Sun", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Solar Power")]
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
fireBlast = @battle.getMove("Fire Blast")
|
||
|
tackle.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
fireBlast.modifyAttack(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
|
||
|
it "owner loses 1/8 max HP per end of turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Solar Power")]
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(@p1.stat('hp') - (@p1.stat('hp') >> 3))
|
||
|
|
||
|
it "does nothing if the weather is not Sun", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Solar Power")]
|
||
|
fireBlast = @battle.getMove("Fire Blast")
|
||
|
fireBlast.modifyAttack(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(@p1.stat('hp'))
|
||
|
|
||
|
it "deals a minimum of 1 damage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Shedinja", ability: "Solar Power")]
|
||
|
@battle.setWeather(Weather.SUN)
|
||
|
@battle.endTurn()
|
||
|
@p1.currentHP.should.equal(0)
|
||
|
|
||
|
describe "Soundproof", ->
|
||
|
it "makes user immune to sound moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Soundproof")]
|
||
|
bugBuzz = @battle.getMove('Bug Buzz')
|
||
|
perishSong = @battle.getMove('Perish Song')
|
||
|
@p1.isImmune(bugBuzz.type, move: bugBuzz).should.be.true
|
||
|
@p1.isImmune(perishSong.type, move: perishSong).should.be.true
|
||
|
|
||
|
describe "Speed Boost", ->
|
||
|
it "boosts speed at the end of every turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Speed Boost")]
|
||
|
@p1.stages.should.containEql(speed: 0)
|
||
|
@battle.endTurn()
|
||
|
@p1.stages.should.containEql(speed: 1)
|
||
|
@battle.endTurn()
|
||
|
@p1.stages.should.containEql(speed: 2)
|
||
|
|
||
|
it "boosts speed the turn after a pokemon is freshly switched in", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Speed Boost")]
|
||
|
@battle.performSwitch(@p1, 1)
|
||
|
@team1.first().stages.should.containEql(speed: 0)
|
||
|
@battle.endTurn()
|
||
|
@team1.first().stages.should.containEql(speed: 0)
|
||
|
@battle.endTurn()
|
||
|
@team1.first().stages.should.containEql(speed: 1)
|
||
|
@battle.endTurn()
|
||
|
@team1.first().stages.should.containEql(speed: 2)
|
||
|
|
||
|
it "boosts speed the turn after a pokemon is replaced", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Speed Boost")]
|
||
|
@p1.currentHP = 1
|
||
|
@controller.makeMove(@id1, "Tackle")
|
||
|
@controller.makeMove(@id2, "Tackle")
|
||
|
@controller.makeSwitch(@id1, 1)
|
||
|
@battle.turn.should.equal(2)
|
||
|
@team1.first().stages.should.containEql(speed: 0)
|
||
|
@battle.endTurn()
|
||
|
@team1.first().stages.should.containEql(speed: 1)
|
||
|
@battle.endTurn()
|
||
|
@team1.first().stages.should.containEql(speed: 2)
|
||
|
|
||
|
describe "Sticky Hold", ->
|
||
|
it "prevents items from being taken", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Leftovers", ability: "Sticky Hold")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Trick"))
|
||
|
@p1.hasItem("Leftovers").should.be.true
|
||
|
@p2.hasItem().should.be.false
|
||
|
|
||
|
it "does not prevent the user from using Trick", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Leftovers", ability: "Sticky Hold")]
|
||
|
@battle.performMove(@p1, @battle.getMove("Trick"))
|
||
|
@p1.hasItem().should.be.false
|
||
|
@p2.hasItem("Leftovers").should.be.true
|
||
|
|
||
|
describe "Stall", ->
|
||
|
it "forces the user to move last no matter what", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", evs: {speed: 4}, ability: "Stall")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
splash = @battle.getMove("Splash")
|
||
|
@battle.recordMove(@id1, splash)
|
||
|
@battle.recordMove(@id2, splash)
|
||
|
@battle.determineTurnOrder()
|
||
|
pokemon = @battle.pokemonActions.map((o) -> o.pokemon)
|
||
|
pokemon.should.eql [ @p2, @p1 ]
|
||
|
|
||
|
it "moves before pokemon with lagging tail/full incense", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", evs: {speed: 4}, ability: "Stall")]
|
||
|
team2: [Factory("Magikarp", item: "Lagging Tail")]
|
||
|
splash = @battle.getMove("Splash")
|
||
|
@battle.recordMove(@id1, splash)
|
||
|
@battle.recordMove(@id2, splash)
|
||
|
@battle.determineTurnOrder()
|
||
|
pokemon = @battle.pokemonActions.map((o) -> o.pokemon)
|
||
|
pokemon.should.eql [ @p1, @p2 ]
|
||
|
|
||
|
describe "Steadfast", ->
|
||
|
it "raises speed if the Pokemon flinches", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Steadfast")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@p1.attach(Attachment.Flinch)
|
||
|
@p1.beforeMove(tackle, @p1, [ @p2 ])
|
||
|
@p1.stages.should.containEql(speed: 1)
|
||
|
|
||
|
describe "Sturdy", ->
|
||
|
it "prevents the user from being OHKOed at full HP", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sturdy")]
|
||
|
ember = @battle.getMove('Ember')
|
||
|
stub = @sandbox.stub(ember, 'calculateDamage', -> 9999)
|
||
|
@battle.performMove(@p2, ember)
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
|
||
|
it "lets the user be KOed if not at full HP", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Sturdy")]
|
||
|
@p1.currentHP -= 1
|
||
|
ember = @battle.getMove('Ember')
|
||
|
stub = @sandbox.stub(ember, 'calculateDamage', -> 9999)
|
||
|
@battle.performMove(@p2, ember)
|
||
|
@p1.currentHP.should.equal(0)
|
||
|
|
||
|
describe "Suction Cups", ->
|
||
|
it "prevents user from being phased", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Suction Cups"), Factory("Magikarp")]
|
||
|
whirlwind = @battle.getMove('Whirlwind')
|
||
|
mock = @sandbox.mock(@team1).expects('switch').never()
|
||
|
@battle.performMove(@p2, whirlwind)
|
||
|
mock.verify()
|
||
|
|
||
|
describe "Super Luck", ->
|
||
|
it "adds +2 to critical hit level", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Super Luck")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.criticalHitLevel(@battle, @p1, @p2).should.equal(2)
|
||
|
|
||
|
describe "Synchronize", ->
|
||
|
it "afflicts the source of a status with the same status", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Synchronize")]
|
||
|
@p1.attach(Status.Paralyze, source: @p2)
|
||
|
@p2.has(Status.Paralyze).should.be.true
|
||
|
|
||
|
it "doesn't attempt to afflict target if target is self", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Synchronize")]
|
||
|
spy = @sandbox.spy(Status.Burn, 'preattach')
|
||
|
@p1.attach(Status.Burn, source: @p1)
|
||
|
spy.returned(false).should.be.true
|
||
|
|
||
|
it "activates on primary status moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Synchronize")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Will-O-Wisp"))
|
||
|
@p2.has(Status.Burn).should.be.true
|
||
|
|
||
|
it "doesn't activate on Sleep", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Synchronize")]
|
||
|
@p1.attach(Status.Sleep, source: @p2)
|
||
|
@p2.has(Status.Sleep).should.be.false
|
||
|
|
||
|
it "doesn't activate on Freeze", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Synchronize")]
|
||
|
@p1.attach(Status.Freeze, source: @p2)
|
||
|
@p2.has(Status.Freeze).should.be.false
|
||
|
|
||
|
it "activates on secondary effects", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Synchronize")]
|
||
|
shared.biasRNG.call(this, "next", 'secondary effect', 0) # 100% chance
|
||
|
@battle.performMove(@p2, @battle.getMove("Flamethrower"))
|
||
|
@p2.has(Status.Burn).should.be.true
|
||
|
|
||
|
describe "Tangled Feet", ->
|
||
|
it "doubles evasion rate when confused", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Tangled Feet")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(100)
|
||
|
@p1.attach(Attachment.Confusion)
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(50)
|
||
|
@p1.unattach(Attachment.Confusion)
|
||
|
tackle.chanceToHit(@battle, @p2, @p1).should.equal(100)
|
||
|
|
||
|
describe "Technician", ->
|
||
|
it "increases base power of moves with BP <= 60 by x1.5", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Technician")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1800)
|
||
|
|
||
|
it "leaves moves with BP > 60 alone", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Technician")]
|
||
|
iceBeam = @battle.getMove("Ice Beam")
|
||
|
iceBeam.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Telepathy", ->
|
||
|
it "cancels damaging attacks from an ally", ->
|
||
|
shared.create.call this,
|
||
|
numActive: 2
|
||
|
team1: [Factory("Magikarp"), Factory("Magikarp", ability: "Telepathy")]
|
||
|
team2: (Factory("Magikarp") for x in [1..2])
|
||
|
earthquake = @battle.getMove("Earthquake")
|
||
|
mock = @sandbox.mock(earthquake).expects('hit').exactly(2)
|
||
|
@battle.performMove(@p1, earthquake)
|
||
|
@team1.at(1).currentHP.should.equal(@team1.at(1).stat('hp'))
|
||
|
mock.verify()
|
||
|
|
||
|
describe "Thick Fat", ->
|
||
|
it "halves the base power of Fire and Ice moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Thick Fat")]
|
||
|
iceBeam = @battle.getMove("Ice Beam")
|
||
|
iceBeam.modifyAttack(@battle, @p2, @p1).should.equal(0x800)
|
||
|
flamethrower = @battle.getMove("Flamethrower")
|
||
|
flamethrower.modifyAttack(@battle, @p2, @p1).should.equal(0x800)
|
||
|
|
||
|
it "doesn't change base power of other moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Thick Fat")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyAttack(@battle, @p2, @p1).should.equal(0x1000)
|
||
|
|
||
|
describe "Tinted Lens", ->
|
||
|
it "doubles damage when using a not-very-effective move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Tinted Lens")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
surf = @battle.getMove("Surf")
|
||
|
surf.modifyDamage(@battle, @p1, @p2).should.equal(0x2000)
|
||
|
|
||
|
it "doesn't double damage otherwise", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Tinted Lens")]
|
||
|
team2: [Factory("Magikarp")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
tackle.modifyDamage(@battle, @p1, @p2).should.equal(0x1000)
|
||
|
|
||
|
describe "Trace", ->
|
||
|
it "copies a random foe's ability on switch-in", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Trace")]
|
||
|
team2: [Factory("Magikarp", ability: "Truant")]
|
||
|
@p1.hasAbility("Truant").should.be.true
|
||
|
|
||
|
it "does not copy certain abilities", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Trace")]
|
||
|
team2: [Factory("Magikarp", ability: "Forecast")]
|
||
|
@p1.hasAbility("Forecast").should.be.false
|
||
|
|
||
|
it "does not crash if there are no opponents", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp"), Factory("Magikarp")]
|
||
|
@p2.faint()
|
||
|
(=>
|
||
|
@p1.copyAbility(Ability.Trace)
|
||
|
).should.not.throw()
|
||
|
|
||
|
it "traces another ability upon switching out and back in", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Trace"), Factory("Magikarp")]
|
||
|
team2: [Factory("Magikarp", ability: "Intimidate")
|
||
|
Factory("Magikarp", ability: "Magic Bounce")]
|
||
|
@p1.hasAbility("Intimidate").should.be.true
|
||
|
@battle.performSwitch(@p2, 1)
|
||
|
@battle.performSwitch(@p1, 1)
|
||
|
@battle.performSwitch(@team1.first(), 1)
|
||
|
@p1.hasAbility("Magic Bounce").should.be.true
|
||
|
|
||
|
describe "Truant", ->
|
||
|
it "prevents the user from attacking every other turn", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Truant")]
|
||
|
splash = @battle.getMove("Splash")
|
||
|
|
||
|
mock = @sandbox.mock(splash).expects('execute').once()
|
||
|
@battle.performMove(@p1, splash)
|
||
|
mock.verify()
|
||
|
splash.execute.restore()
|
||
|
|
||
|
mock = @sandbox.mock(splash).expects('execute').never()
|
||
|
@battle.performMove(@p1, splash)
|
||
|
mock.verify()
|
||
|
splash.execute.restore()
|
||
|
|
||
|
mock = @sandbox.mock(splash).expects('execute').once()
|
||
|
@battle.performMove(@p1, splash)
|
||
|
mock.verify()
|
||
|
splash.execute.restore()
|
||
|
|
||
|
describe "Unaware", ->
|
||
|
it "ignores attackers' attack and special attack boosts", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Unaware")]
|
||
|
@p2.boost(attack: 1, specialAttack: 2)
|
||
|
spy = @sandbox.spy(@p2, "editBoosts")
|
||
|
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||
|
spy.returned(sinon.match(attack: 0, specialAttack: 0)).should.be.true
|
||
|
|
||
|
it "ignores defenders' defense and special defense boosts", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Unaware")]
|
||
|
@p2.boost(defense: 1, specialDefense: 2)
|
||
|
spy = @sandbox.spy(@p2, "editBoosts")
|
||
|
@battle.performMove(@p1, @battle.getMove("Tackle"))
|
||
|
spy.returned(sinon.match(defense: 0, specialDefense: 0)).should.be.true
|
||
|
|
||
|
it "ignores attackers' accuracy boosts", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Unaware")]
|
||
|
@p2.boost(accuracy: 3)
|
||
|
spy = @sandbox.spy(@p2, "editBoosts")
|
||
|
@battle.getMove("Tackle").chanceToHit(@battle, @p2, @p1)
|
||
|
spy.returned(sinon.match(accuracy: 0)).should.be.true
|
||
|
|
||
|
it "ignores defenders' evasion boosts", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Unaware")]
|
||
|
@p2.boost(evasion: 3)
|
||
|
spy = @sandbox.spy(@p2, "editBoosts")
|
||
|
@battle.getMove("Tackle").chanceToHit(@battle, @p1, @p2)
|
||
|
spy.returned(sinon.match(evasion: 0)).should.be.true
|
||
|
|
||
|
describe "Unburden", ->
|
||
|
it "doubles its speed when the owner's item is removed", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Flying Gem", ability: "Unburden")]
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.removeItem()
|
||
|
@p1.stat('speed').should.equal(2 * speed)
|
||
|
|
||
|
it "activates when its item is swapped for no item", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Flying Gem", ability: "Unburden")]
|
||
|
speed = @p1.stat('speed')
|
||
|
trick = @battle.getMove("Trick")
|
||
|
@battle.performMove(@p2, trick)
|
||
|
@p1.stat('speed').should.equal(2 * speed)
|
||
|
|
||
|
it "does not activate when its item is swapped for another item", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Flying Gem", ability: "Unburden")]
|
||
|
team2: [Factory("Magikarp", item: "Water Gem")]
|
||
|
speed = @p1.stat('speed')
|
||
|
trick = @battle.getMove("Trick")
|
||
|
@battle.performMove(@p2, trick)
|
||
|
@p1.stat('speed').should.equal(speed)
|
||
|
@p1.has(Attachment.Unburden).should.be.false
|
||
|
|
||
|
it "deactivates if it gains another item", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Flying Gem", ability: "Unburden")]
|
||
|
team2: [Factory("Magikarp", item: "Water Gem")]
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.removeItem()
|
||
|
@p1.stat('speed').should.equal(2 * speed)
|
||
|
trick = @battle.getMove("Trick")
|
||
|
@battle.performMove(@p2, trick)
|
||
|
@p1.stat('speed').should.equal(speed)
|
||
|
@p1.has(Attachment.Unburden).should.be.false
|
||
|
|
||
|
it "deactivates if it loses this Ability", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Flying Gem", ability: "Unburden")]
|
||
|
team2: [Factory("Magikarp", ability: "Run Away")]
|
||
|
speed = @p1.stat('speed')
|
||
|
@p1.removeItem()
|
||
|
@p1.stat('speed').should.equal(2 * speed)
|
||
|
rolePlay = @battle.getMove("Role Play")
|
||
|
@battle.performMove(@p1, rolePlay)
|
||
|
@p1.stat('speed').should.equal(speed)
|
||
|
@p1.has(Attachment.Unburden).should.be.false
|
||
|
|
||
|
describe "Unnerve", ->
|
||
|
it "prevents held berries from being eaten", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Unnerve")]
|
||
|
team2: [Factory("Magikarp", item: "Salac Berry")]
|
||
|
@p2.currentHP = 1
|
||
|
@p2.update()
|
||
|
@p2.stages.should.containEql(speed: 0)
|
||
|
@battle.beginTurn()
|
||
|
@p2.update()
|
||
|
@p2.stages.should.containEql(speed: 0)
|
||
|
|
||
|
it "does not prevent regular item usage", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Unnerve")]
|
||
|
team2: [Factory("Magikarp", item: "Leftovers")]
|
||
|
@p2.currentHP = 1
|
||
|
@battle.endTurn()
|
||
|
@p2.currentHP.should.be.greaterThan(1)
|
||
|
|
||
|
it "does not prevent Bug Bite, Pluck, etc. from working", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", item: "Salac Berry", ability: "Unnerve")]
|
||
|
@battle.performMove(@p2, @battle.getMove("Bug Bite"))
|
||
|
@p2.stages.should.containEql(speed: 1)
|
||
|
|
||
|
describe "Victory Star", ->
|
||
|
it "increases accuracy of moves by 10%", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Victory Star")]
|
||
|
willOWisp = @battle.getMove("Will-O-Wisp")
|
||
|
accuracy = willOWisp.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal Math.floor(willOWisp.accuracy * 1.1)
|
||
|
|
||
|
it "increases accuracy of ally moves by 10%"
|
||
|
|
||
|
testTypeAbsorbMove = (name, type) ->
|
||
|
describe name, ->
|
||
|
it "is immune to #{type}-type moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.type == type
|
||
|
mock = @sandbox.mock(typedMove).expects('hit').never()
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
mock.verify()
|
||
|
|
||
|
it "heals 25% HP from #{type}-type moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.type == type
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
@p1.currentHP.should.equal(1 + (@p1.stat('hp') >> 2))
|
||
|
|
||
|
it "does not activate for the user's moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: name)]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
m.target == 'user' && m.type == type
|
||
|
@p1.currentHP = 1
|
||
|
@battle.performMove(@p1, typedMove)
|
||
|
@p1.currentHP.should.equal(1)
|
||
|
|
||
|
testTypeAbsorbMove("Water Absorb", "Water")
|
||
|
testTypeAbsorbMove("Volt Absorb", "Electric")
|
||
|
|
||
|
describe "Weak Armor", ->
|
||
|
it "lowers defense and raises speed on physical attacks", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Weak Armor")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
@p1.stages.should.containEql(defense: -1, speed: 1)
|
||
|
|
||
|
it "does nothing on any other kind of move", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Weak Armor")]
|
||
|
willOWisp = @battle.getMove("Will-O-Wisp")
|
||
|
@battle.performMove(@p2, willOWisp)
|
||
|
@p1.stages.should.containEql(defense: 0, speed: 0)
|
||
|
|
||
|
describe "Wonder Guard", ->
|
||
|
it "renders the Pokemon immune to non-super-effective moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Wonder Guard")]
|
||
|
tackle = @battle.getMove("Tackle")
|
||
|
mock = @sandbox.mock(tackle).expects('hit').never()
|
||
|
@battle.performMove(@p2, tackle)
|
||
|
mock.verify()
|
||
|
|
||
|
it "is not immune to struggle", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Wonder Guard")]
|
||
|
struggle = @battle.getMove("Struggle")
|
||
|
mock = @sandbox.mock(struggle).expects('hit').once()
|
||
|
@battle.performMove(@p2, struggle)
|
||
|
mock.verify()
|
||
|
|
||
|
it "is not immune to non-damaging moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Wonder Guard")]
|
||
|
willOWisp = @battle.getMove("Will-O-Wisp")
|
||
|
mock = @sandbox.mock(willOWisp).expects('hit').once()
|
||
|
@battle.performMove(@p2, willOWisp)
|
||
|
mock.verify()
|
||
|
|
||
|
it "is not immune to super-effective moves", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Wonder Guard")]
|
||
|
typedMove = _(@battle.MoveList).find (m) ->
|
||
|
!m.isNonDamaging() && m.type == 'Electric'
|
||
|
mock = @sandbox.mock(typedMove).expects('hit').once()
|
||
|
@battle.performMove(@p2, typedMove)
|
||
|
mock.verify()
|
||
|
|
||
|
describe "Wonder Skin", ->
|
||
|
it "makes non-damaging moves with over 50% accuracy have 50% accuracy", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Wonder Skin")]
|
||
|
trick = @battle.getMove("Trick")
|
||
|
accuracy = trick.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal 50
|
||
|
|
||
|
it "adjusts accuracy before any other modifiers take effect", ->
|
||
|
shared.create.call this,
|
||
|
team1: [Factory("Magikarp", ability: "Compoundeyes")]
|
||
|
team2: [Factory("Magikarp", ability: "Wonder Skin")]
|
||
|
willOWisp = @battle.getMove("Will-O-Wisp")
|
||
|
accuracy = willOWisp.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal Math.floor(50 * 1.3)
|
||
|
|
||
|
it "does nothing to non-damaging moves that don't check accuracy", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Wonder Skin")]
|
||
|
meanLook = @battle.getMove("Mean Look")
|
||
|
accuracy = meanLook.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal 0
|
||
|
|
||
|
it "does nothing to non-damaging moves with under 50% accuracy", ->
|
||
|
shared.create.call this,
|
||
|
team2: [Factory("Magikarp", ability: "Wonder Skin")]
|
||
|
trick = @battle.getMove("Trick")
|
||
|
trick.accuracy = 40
|
||
|
accuracy = trick.chanceToHit(@battle, @p1, @p2)
|
||
|
accuracy.should.equal 40
|
||
|
|
||
|
describe "Zen Mode", ->
|
||
|
it "changes Darmanitan's forme when going under or above 50% HP"
|