mirror of
https://gitlab.com/Deukhoofd/BattleSim.git
synced 2025-10-27 18:00:03 +00:00
Lots of stuff
This commit is contained in:
485
test/xy/abilities.coffee
Normal file
485
test/xy/abilities.coffee
Normal file
@@ -0,0 +1,485 @@
|
||||
{Attachment, Status} = require('../../server/xy/attachment')
|
||||
{Battle} = require('../../server/xy/battle')
|
||||
{Pokemon} = require('../../server/xy/pokemon')
|
||||
{Weather} = require('../../shared/weather')
|
||||
{Ability} = require '../../server/xy/data/abilities'
|
||||
{Item} = require '../../server/xy/data/items'
|
||||
util = require '../../server/xy/util'
|
||||
{Factory} = require '../factory'
|
||||
should = require 'should'
|
||||
shared = require '../shared'
|
||||
|
||||
require '../helpers'
|
||||
|
||||
describe "XY Abilities:", ->
|
||||
testWeatherAbility = (name, weather) ->
|
||||
describe name, ->
|
||||
it "causes #{weather} that ends after 5 turns", ->
|
||||
shared.create.call(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
@battle.weatherDuration.should.equal(5)
|
||||
|
||||
it "does not activate if the weather is already #{weather}", ->
|
||||
shared.build(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
@battle.setWeather(weather, 2)
|
||||
@controller.beginBattle()
|
||||
@battle.weatherDuration.should.equal(2)
|
||||
|
||||
it "is lengthened by rocks", ->
|
||||
for itemName, item of Item
|
||||
break if item.lengthensWeather == weather
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: name, item: itemName)]
|
||||
@battle.weatherDuration.should.equal(8)
|
||||
|
||||
testWeatherAbility("Drizzle", Weather.RAIN)
|
||||
testWeatherAbility("Drought", Weather.SUN)
|
||||
testWeatherAbility("Sand Stream", Weather.SAND)
|
||||
testWeatherAbility("Snow Warning", Weather.HAIL)
|
||||
|
||||
testNormalTypeChangeAbility = (name, type) ->
|
||||
describe name, ->
|
||||
it "changes Normal-type moves used by attacker to #{type}-type", ->
|
||||
shared.create.call(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
spy = @sandbox.spy(@p1, 'editMoveType')
|
||||
tackle = @battle.getMove('Tackle')
|
||||
@battle.performMove(@p1, tackle)
|
||||
spy.returned(type).should.be.true
|
||||
|
||||
it "does not change non-Normal-type moves used by attacker", ->
|
||||
shared.create.call(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
spy = @sandbox.spy(@p1, 'editMoveType')
|
||||
ember = @battle.getMove('Ember')
|
||||
@battle.performMove(@p1, ember)
|
||||
spy.returned(type).should.be.false
|
||||
spy.returned(ember.type).should.be.true
|
||||
|
||||
it "boosts Normal-type moves by 1.3x", ->
|
||||
shared.create.call(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
tackle = @battle.getMove('Tackle')
|
||||
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||||
|
||||
it "does not boost regular #{type}-type moves", ->
|
||||
shared.create.call(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
for move in @battle.MoveList
|
||||
if move.type == type && !move.isNonDamaging() then break
|
||||
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
it "does not boost non-#{type}-type moves", ->
|
||||
shared.create.call(this, gen: 'xy', team1: [Factory("Magikarp", ability: name)])
|
||||
ember = @battle.getMove('Ember')
|
||||
ember.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
it "is unaffected by the original immunities", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: name)]
|
||||
team2: [Factory("Gengar")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
mock = @sandbox.mock(tackle).expects('hit').once()
|
||||
@battle.performMove(@p1, tackle)
|
||||
mock.verify()
|
||||
|
||||
testNormalTypeChangeAbility("Aerilate", "Flying")
|
||||
testNormalTypeChangeAbility("Pixilate", "Fairy")
|
||||
testNormalTypeChangeAbility("Refrigerate", "Ice")
|
||||
|
||||
describe "Shadow Tag", ->
|
||||
it "does not affect ghosts", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Gengar")]
|
||||
team2: [Factory("Magikarp", ability: "Shadow Tag")]
|
||||
@p1.isSwitchBlocked().should.be.false
|
||||
@battle.beginTurn()
|
||||
@p1.isSwitchBlocked().should.be.false
|
||||
|
||||
testAuraAbility = (name, type) ->
|
||||
describe name, ->
|
||||
it "raises base power of #{type} attacks by 1.33x", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: name)]
|
||||
move = @battle.findMove (m) ->
|
||||
m.type == type && !m.isNonDamaging()
|
||||
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1547)
|
||||
|
||||
it "decreases #{type} attacks by 3/4x if Aura Break is on the field", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: name)]
|
||||
team2: [Factory("Magikarp", ability: "Aura Break")]
|
||||
move = @battle.findMove (m) ->
|
||||
m.type == type && !m.isNonDamaging()
|
||||
move.modifyBasePower(@battle, @p1, @p2).should.equal(0xC00)
|
||||
|
||||
it "does nothing to moves not of #{type} type", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: name)]
|
||||
move = @battle.findMove (m) ->
|
||||
m.type != type && !m.isNonDamaging()
|
||||
move.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
testAuraAbility("Dark Aura", "Dark")
|
||||
testAuraAbility("Fairy Aura", "Fairy")
|
||||
|
||||
describe "Gale Wings", ->
|
||||
it "adds 1 to the priority of the user's Flying moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Gale Wings")]
|
||||
gust = @battle.getMove("Gust")
|
||||
@p1.editPriority(0, gust).should.equal(1)
|
||||
|
||||
it "does not change priority otherwise", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Gale Wings")]
|
||||
tackle = @battle.getMove("Tackle")
|
||||
@p1.editPriority(0, tackle).should.equal(0)
|
||||
|
||||
describe "Bulletproof", ->
|
||||
it "makes user immune to bullet moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Bulletproof")]
|
||||
shadowBall = @battle.getMove('Shadow Ball')
|
||||
@p1.isImmune(shadowBall.type, move: shadowBall).should.be.true
|
||||
|
||||
describe "Competitive", ->
|
||||
it "boosts special attack by 2 every time a stat is lowered", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Competitive")]
|
||||
@p1.boost(defense: -1, @p2)
|
||||
@p1.stages.should.containEql(specialAttack: 2)
|
||||
@p1.boost(specialAttack: -1, defense: -2, evasion: 1, @p2)
|
||||
@p1.stages.should.containEql(specialAttack: 5)
|
||||
|
||||
it "does not boost special attack if the stat was self-lowered", ->
|
||||
shared.create.call this,
|
||||
team1: [Factory("Magikarp", ability: "Competitive")]
|
||||
@battle.performMove(@p1, @battle.getMove("Close Combat"))
|
||||
boosts = {specialAttack: 0, defense: -1, specialDefense: -1}
|
||||
@p1.stages.should.containEql(boosts)
|
||||
|
||||
describe "Fur Coat", ->
|
||||
it "modifies physical attacks by 0x800", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", ability: "Fur Coat")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x800)
|
||||
|
||||
it "doesn't modify other attacks", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", ability: "Fur Coat")]
|
||||
thunderbolt = @battle.getMove('Thunderbolt')
|
||||
thunderbolt.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
describe "Gooey", ->
|
||||
it "lowers the attacker's speed by 1 on contact", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", ability: "Gooey")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
@p1.stages.should.containEql(speed: 0)
|
||||
@battle.performMove(@p1, tackle)
|
||||
@p1.stages.should.containEql(speed: -1)
|
||||
|
||||
it "does not lower the attacker's speed by 1 on non-contact", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", ability: "Gooey")]
|
||||
thunderbolt = @battle.getMove('Thunderbolt')
|
||||
@p1.stages.should.containEql(speed: 0)
|
||||
@battle.performMove(@p1, thunderbolt)
|
||||
@p1.stages.should.containEql(speed: 0)
|
||||
|
||||
it "works even if the defender faints", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", ability: "Gooey"), Factory("Magikarp")]
|
||||
@p2.currentHP = 1
|
||||
tackle = @battle.getMove('Tackle')
|
||||
@p1.stages.should.containEql(speed: 0)
|
||||
@battle.performMove(@p1, tackle)
|
||||
@p1.stages.should.containEql(speed: -1)
|
||||
|
||||
describe "Mega Launcher", ->
|
||||
it "boosts pulse moves by x1.5", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Mega Launcher")]
|
||||
waterPulse = @battle.getMove('Water Pulse')
|
||||
waterPulse.modifyBasePower(@battle, @p1, @p2).should.equal(0x1800)
|
||||
|
||||
it "does not boost non-pulse moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Mega Launcher")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
describe "Overcoat", ->
|
||||
it "makes the user immune to weather", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Overcoat")]
|
||||
@p1.isWeatherDamageImmune(Weather.SAND).should.be.true
|
||||
@p1.isWeatherDamageImmune(Weather.HAIL).should.be.true
|
||||
|
||||
it "makes the user immune to powder moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Overcoat")]
|
||||
spore = @battle.getMove("Spore")
|
||||
mock = @sandbox.mock(spore).expects('hit').never()
|
||||
@battle.performMove(@p2, spore)
|
||||
mock.verify()
|
||||
|
||||
describe "Parental Bond", ->
|
||||
it "hits twice if the move has only one target", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
targets = @battle.getTargets(tackle, @p1)
|
||||
tackle.calculateNumberOfHits(@battle, @p1, targets).should.equal(2)
|
||||
|
||||
it "hits once if the move has multiple targets", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
numActive: 2
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond"), Factory("Magikarp")]
|
||||
team2: (Factory("Magikarp") for x in [0..1])
|
||||
earthquake = @battle.getMove('Earthquake')
|
||||
targets = @battle.getTargets(earthquake, @p1)
|
||||
earthquake.calculateNumberOfHits(@battle, @p1, targets).should.equal(1)
|
||||
|
||||
it "hits once if the move is non-damaging", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond")]
|
||||
willOWisp = @battle.getMove('Will-O-Wisp')
|
||||
targets = @battle.getTargets(willOWisp, @p1)
|
||||
willOWisp.calculateNumberOfHits(@battle, @p1, targets).should.equal(1)
|
||||
|
||||
it "hits for half damage the second hit", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
spy = @sandbox.spy(tackle, 'modifyDamage')
|
||||
@battle.performMove(@p1, tackle)
|
||||
spy.calledTwice.should.be.true
|
||||
spy.returnValues[0].should.equal(0x1000)
|
||||
spy.returnValues[1].should.equal(0x800)
|
||||
|
||||
it "hits the same number otherwise if the move is multi-hit", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond")]
|
||||
shared.biasRNG.call(this, "choice", 'num hits', 4)
|
||||
shared.biasRNG.call(this, "randInt", 'num hits', 4)
|
||||
pinMissile = @battle.getMove('Pin Missile')
|
||||
targets = @battle.getTargets(pinMissile, @p1)
|
||||
pinMissile.calculateNumberOfHits(@battle, @p1, targets).should.equal(4)
|
||||
|
||||
it "doesn't hit for half damage on the second hit using multi-hit moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond")]
|
||||
shared.biasRNG.call(this, "choice", 'num hits', 4)
|
||||
shared.biasRNG.call(this, "randInt", 'num hits', 4)
|
||||
pinMissile = @battle.getMove('Pin Missile')
|
||||
spy = @sandbox.spy(pinMissile, 'modifyDamage')
|
||||
@battle.performMove(@p1, pinMissile)
|
||||
spy.callCount.should.equal(4)
|
||||
spy.returnValues[0].should.equal(0x1000)
|
||||
spy.returnValues[1].should.equal(0x1000)
|
||||
|
||||
it "recoils only once, but with both attack damages combined", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Parental Bond")]
|
||||
takeDown = @battle.getMove("Take Down")
|
||||
spy = @sandbox.spy(takeDown, 'hit')
|
||||
@battle.performMove(@p1, takeDown)
|
||||
totalDamage = spy.returnValues.reduce((a, b) -> a + b)
|
||||
recoilDamage = Math.round(totalDamage * -takeDown.recoil / 100)
|
||||
(@p1.stat('hp') - @p1.currentHP).should.equal(recoilDamage)
|
||||
|
||||
describe "Protean", ->
|
||||
it "changes the Pokemon's type when using a move", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Protean")]
|
||||
@p1.types.should.eql([ "Water" ])
|
||||
@controller.makeMove(@id1, "Tackle")
|
||||
@controller.makeMove(@id2, "Splash")
|
||||
@p1.types.should.eql([ "Normal" ])
|
||||
|
||||
describe "Stance Change", ->
|
||||
it "changes to blade forme when using an attacking move", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Aegislash", ability: "Stance Change")]
|
||||
@p1.forme.should.equal("default")
|
||||
@battle.performMove(@p1, @battle.getMove("Shadow Sneak"))
|
||||
@p1.forme.should.equal("blade")
|
||||
|
||||
it "changes to shield forme when using King's Shield", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Aegislash", ability: "Stance Change")]
|
||||
@battle.performMove(@p1, @battle.getMove("Shadow Sneak"))
|
||||
@p1.forme.should.equal("blade")
|
||||
@battle.performMove(@p1, @battle.getMove("King's Shield"))
|
||||
@p1.forme.should.equal("default")
|
||||
|
||||
it "changes to shield forme after switching out and back in", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Aegislash", ability: "Stance Change"), Factory("Magikarp")]
|
||||
@battle.performMove(@p1, @battle.getMove("Shadow Sneak"))
|
||||
@p1.forme.should.equal("blade")
|
||||
@battle.performSwitch(@p1, 1)
|
||||
@battle.performSwitch(@team1.first(), 1)
|
||||
@p1.forme.should.equal("default")
|
||||
|
||||
it "does not change formes when using any other move", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Aegislash", ability: "Stance Change")]
|
||||
@battle.performMove(@p1, @battle.getMove("Swords Dance"))
|
||||
@p1.forme.should.equal("default")
|
||||
@battle.performMove(@p1, @battle.getMove("Shadow Sneak"))
|
||||
@p1.forme.should.equal("blade")
|
||||
@battle.performMove(@p1, @battle.getMove("Swords Dance"))
|
||||
@p1.forme.should.equal("blade")
|
||||
|
||||
it "doesn't attempt to change forme for Pokemon who aren't Aegislash", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Ditto", ability: "Stance Change")]
|
||||
@battle.performMove(@p1, @battle.getMove("Shadow Sneak"))
|
||||
@p1.forme.should.not.equal("blade")
|
||||
|
||||
it "doesn't attempt to change forme to default for non-Aegislashes", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Rotom", forme: "wash", ability: "Stance Change")]
|
||||
@battle.performMove(@p1, @battle.getMove("King's Shield"))
|
||||
@p1.forme.should.not.equal("default")
|
||||
|
||||
it "cannot be skill-swapped"
|
||||
it "cannot be replaced with another ability"
|
||||
|
||||
describe "Strong Jaw", ->
|
||||
it "boosts bite moves by x1.5", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Strong Jaw")]
|
||||
bite = @battle.getMove('Bite')
|
||||
bite.modifyBasePower(@battle, @p1, @p2).should.equal(0x1800)
|
||||
|
||||
it "does not boost non-bite moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Strong Jaw")]
|
||||
thunderbolt = @battle.getMove('Thunderbolt')
|
||||
thunderbolt.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
describe "Tough Claws", ->
|
||||
it "boosts contact moves by x1.3", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Tough Claws")]
|
||||
tackle = @battle.getMove('Tackle')
|
||||
tackle.modifyBasePower(@battle, @p1, @p2).should.equal(0x14CD)
|
||||
|
||||
it "does not boost non-contact moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Tough Claws")]
|
||||
thunderbolt = @battle.getMove('Thunderbolt')
|
||||
thunderbolt.modifyBasePower(@battle, @p1, @p2).should.equal(0x1000)
|
||||
|
||||
describe "Mummy", ->
|
||||
it "doesn't change ability if attacker has Stance Change", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Mummy")]
|
||||
team2: [Factory("Magikarp", ability: "Stance Change")]
|
||||
tackle = @battle.getMove("Tackle")
|
||||
@p2.hasAbility("Mummy").should.be.false
|
||||
@battle.performMove(@p2, tackle)
|
||||
@p2.hasAbility("Mummy").should.be.false
|
||||
|
||||
testAttachmentImmuneAbility = (name, attachments, options = {}) ->
|
||||
describe name, ->
|
||||
it "prevents the pokemon from receiving a specific attachment", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
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,
|
||||
gen: 'xy'
|
||||
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("Aroma Veil", [Attachment.Attract, Attachment.Disable,
|
||||
Attachment.Encore, Attachment.Taunt, Attachment.Torment], cure: false)
|
||||
testAttachmentImmuneAbility("Oblivious", [Attachment.Attract, Attachment.Taunt])
|
||||
testAttachmentImmuneAbility("Sweet Veil", [Status.Sleep], cure: false)
|
||||
|
||||
describe "Effect Spore", ->
|
||||
it "doesn't affect Grass types", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Effect Spore")]
|
||||
team2: [Factory("Bulbasaur")]
|
||||
|
||||
# Sleep
|
||||
shared.biasRNG.call(this, "randInt", 'effect spore', 1)
|
||||
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||||
@p2.has(Status.Sleep).should.be.false
|
||||
|
||||
it "doesn't affect Overcoat Pokemon", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Effect Spore")]
|
||||
team2: [Factory("Magikarp", ability: "Overcoat")]
|
||||
|
||||
# Paralysis
|
||||
shared.biasRNG.call(this, "randInt", 'effect spore', 2)
|
||||
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||||
@p2.has(Status.Paralyze).should.be.false
|
||||
|
||||
it "doesn't affect Safety Goggles holders", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Effect Spore")]
|
||||
team2: [Factory("Magikarp", item: "Safety Goggles")]
|
||||
|
||||
# Poison
|
||||
shared.biasRNG.call(this, "randInt", 'effect spore', 3)
|
||||
@battle.performMove(@p2, @battle.getMove('Tackle'))
|
||||
@p2.has(Status.Poison).should.be.false
|
||||
21
test/xy/attachments.coffee
Normal file
21
test/xy/attachments.coffee
Normal file
@@ -0,0 +1,21 @@
|
||||
{Attachment, Status} = require '../../server/xy/attachment'
|
||||
{Factory} = require '../factory'
|
||||
shared = require '../shared'
|
||||
should = require 'should'
|
||||
require '../helpers'
|
||||
|
||||
describe "XY status:", ->
|
||||
describe "Sleep", ->
|
||||
it "does not reset upon switch out", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p1.attach(Status.Sleep, turns: 3)
|
||||
@p1.get(Status.Sleep).counter.should.equal(0)
|
||||
@battle.performMove(@p1, @battle.getMove("Splash"))
|
||||
@p1.get(Status.Sleep).counter.should.equal(1)
|
||||
@p1.team.switchOut(@p1)
|
||||
@p1.get(Status.Sleep).counter.should.equal(1)
|
||||
|
||||
describe "paralysis", ->
|
||||
it "does not affect electric pokemon", ->
|
||||
shared.create.call(this, team1: [Factory("Pikachu")], gen: 'xy')
|
||||
should.not.exist @p1.attach(Status.Paralyze)
|
||||
117
test/xy/battle.coffee
Normal file
117
test/xy/battle.coffee
Normal file
@@ -0,0 +1,117 @@
|
||||
{Attachment} = require('../../server/xy/attachment')
|
||||
{Battle} = require('../../server/xy/battle')
|
||||
{BattleController} = require('../../server/xy/battle_controller')
|
||||
{Pokemon} = require('../../server/xy/pokemon')
|
||||
{Weather} = require('../../shared/weather')
|
||||
{Ability} = require('../../server/xy/data/abilities')
|
||||
{Factory} = require('../factory')
|
||||
{Player} = require('../../server/player')
|
||||
{Protocol} = require '../../shared/protocol'
|
||||
should = require 'should'
|
||||
sinon = require 'sinon'
|
||||
shared = require '../shared'
|
||||
|
||||
require '../helpers'
|
||||
|
||||
describe "XY Battle:", ->
|
||||
describe "mega evolution", ->
|
||||
it "gets recorded when recording a move", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Charizard", moves: ["Fire Blast"], item: "Charizardite X") ]
|
||||
@battle.pokemonActions.filter((o) -> o.type == 'mega').should.have.length(0)
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
megas = @battle.pokemonActions.filter((o) -> o.type == 'mega')
|
||||
megas.should.have.length(1)
|
||||
megas[0].pokemon.should.equal(@p1)
|
||||
|
||||
it "doesn't get recorded if the pokemon can't mega evolve", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Magikarp", item: "Charizardite X") ]
|
||||
@battle.pokemonActions.filter((o) -> o.type == 'mega').should.have.length(0)
|
||||
@battle.recordMove(@id1, @battle.getMove("Splash"), 0, megaEvolve: true)
|
||||
@battle.pokemonActions.filter((o) -> o.type == 'mega').should.have.length(0)
|
||||
|
||||
it "cannot happen if your partner is already going to mega-evolve", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
numActive: 2
|
||||
team1: (Factory("Charizard", moves: ["Fire Blast"], item: "Charizardite X") for x in [0..1])
|
||||
@battle.pokemonActions.filter((o) -> o.type == 'mega').should.have.length(0)
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.pokemonActions.filter((o) -> o.type == 'mega').should.have.length(1)
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 1, megaEvolve: true)
|
||||
@battle.pokemonActions.filter((o) -> o.type == 'mega').should.have.length(1)
|
||||
|
||||
it "happens after switches", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Charizard", moves: ["Fire Blast"], item: "Charizardite X") ]
|
||||
team2: (Factory("Magikarp") for x in [0..1])
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.recordSwitch(@id2, 1)
|
||||
megaSpy = @sandbox.spy(@battle, 'performMegaEvolution')
|
||||
switchSpy = @sandbox.spy(@battle, 'performSwitch')
|
||||
@battle.continueTurn()
|
||||
switchSpy.calledBefore(megaSpy).should.be.true
|
||||
|
||||
it "changes the pokemon's forme", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Charizard", moves: ["Fire Blast"], item: "Charizardite X") ]
|
||||
team2: (Factory("Magikarp") for x in [0..1])
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.continueTurn()
|
||||
@p1.forme.should.equal('mega-x')
|
||||
|
||||
it "changes the pokemon's ability", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Kangaskhan", moves: ["Return"], item: "Kangaskhanite") ]
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.continueTurn()
|
||||
should.exist(@p1.ability)
|
||||
@p1.ability.should.equal(Ability.ParentalBond)
|
||||
|
||||
it "does not activate a switchout ability while changing ability", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Gardevoir", ability: "Regenerator", item: "Gardevoirite") ]
|
||||
@p1.currentHP = 1
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.continueTurn()
|
||||
should.exist(@p1.ability)
|
||||
@p1.ability.should.equal(Ability.Pixilate)
|
||||
@p1.currentHP.should.equal 1
|
||||
|
||||
it "retains the changed ability upon switching back in", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Kangaskhan", moves: ["Return"], item: "Kangaskhanite"), Factory("Magikarp") ]
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.continueTurn()
|
||||
should.exist(@p1.ability)
|
||||
@p1.ability.should.equal(Ability.ParentalBond)
|
||||
@battle.performSwitch(@team1.first(), 1)
|
||||
@battle.performSwitch(@team1.first(), 1)
|
||||
should.exist(@p1.ability)
|
||||
@p1.ability.should.equal(Ability.ParentalBond)
|
||||
|
||||
describe "#getAction", ->
|
||||
it "does not consider mega evolutions", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Charizard", moves: ["Fire Blast"], item: "Charizardite X") ]
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.getAction(@p1).type.should.equal("move")
|
||||
|
||||
describe "#undoCompletedRequest", ->
|
||||
it "cancels mega evolutions properly", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Charizard", moves: ["Fire Blast"], item: "Charizardite X") ]
|
||||
@battle.recordMove(@id1, @battle.getMove("Fire Blast"), 0, megaEvolve: true)
|
||||
@battle.pokemonActions.should.not.be.empty
|
||||
(=> @battle.undoCompletedRequest(@id1)).should.not.throw()
|
||||
@battle.pokemonActions.should.be.empty
|
||||
20
test/xy/battle_engine.coffee
Normal file
20
test/xy/battle_engine.coffee
Normal file
@@ -0,0 +1,20 @@
|
||||
require '../helpers'
|
||||
|
||||
{Battle} = require('../../server/xy/battle')
|
||||
{Pokemon} = require('../../server/xy/pokemon')
|
||||
{Status, Attachment} = require('../../server/xy/attachment')
|
||||
{Conditions} = require '../../shared/conditions'
|
||||
{Factory} = require '../factory'
|
||||
should = require 'should'
|
||||
shared = require '../shared'
|
||||
{Protocol} = require '../../shared/protocol'
|
||||
|
||||
describe 'Mechanics', ->
|
||||
describe 'a frozen pokemon', ->
|
||||
it "unfreezes if hit by Scald", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
shared.biasRNG.call(this, "next", 'unfreeze chance', 1) # always stays frozen
|
||||
@p1.attach(Status.Freeze)
|
||||
|
||||
@battle.performMove(@p2, @battle.getMove('Scald'))
|
||||
@p1.has(Status.Freeze).should.be.false
|
||||
140
test/xy/items.coffee
Normal file
140
test/xy/items.coffee
Normal file
@@ -0,0 +1,140 @@
|
||||
{Item} = require('../../server/xy/data/items')
|
||||
{Pokemon} = require '../../server/xy/pokemon'
|
||||
{Weather} = require('../../shared/weather')
|
||||
{Attachment, Status} = require '../../server/xy/attachment'
|
||||
{Move} = require '../../server/xy/move'
|
||||
{Factory} = require '../factory'
|
||||
util = require '../../server/xy/util'
|
||||
should = require 'should'
|
||||
{_} = require 'underscore'
|
||||
shared = require '../shared'
|
||||
|
||||
require '../helpers'
|
||||
|
||||
describe "XY Items:", ->
|
||||
describe "Weakness Policy", ->
|
||||
it "raises Attack and Sp. Attack by 2 if hit by a super-effective move", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Weakness Policy')]
|
||||
thunderbolt = @battle.getMove("Thunderbolt")
|
||||
@p1.stages.should.containEql(attack: 0, specialAttack: 0)
|
||||
@battle.performMove(@p2, thunderbolt)
|
||||
@p1.stages.should.containEql(attack: 2, specialAttack: 2)
|
||||
|
||||
it "is consumed after use", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Weakness Policy')]
|
||||
thunderbolt = @battle.getMove("Thunderbolt")
|
||||
@battle.performMove(@p2, thunderbolt)
|
||||
@p1.hasItem().should.be.false
|
||||
|
||||
it "is not used if hit by a non-super-effective move", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Weakness Policy')]
|
||||
ember = @battle.getMove("Ember")
|
||||
@battle.performMove(@p2, ember)
|
||||
@p1.hasItem().should.be.true
|
||||
@p1.stages.should.containEql(attack: 0, specialAttack: 0)
|
||||
|
||||
it "is not used if hit by a super-effective move that is non-damaging", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Weakness Policy')]
|
||||
thunderWave = @battle.getMove("Thunder Wave")
|
||||
@battle.performMove(@p2, thunderWave)
|
||||
@p1.hasItem().should.be.true
|
||||
@p1.stages.should.containEql(attack: 0, specialAttack: 0)
|
||||
|
||||
it "not used if the wearer is behind a substitute", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Weakness Policy')]
|
||||
@p1.attach(Attachment.Substitute, hp: 1)
|
||||
thunderbolt = @battle.getMove("Thunderbolt")
|
||||
@battle.performMove(@p2, thunderbolt)
|
||||
@p1.hasItem().should.be.true
|
||||
@p1.stages.should.containEql(attack: 0, specialAttack: 0)
|
||||
|
||||
describe "Assault Vest", ->
|
||||
it "blocks non-damaging moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Assault Vest')]
|
||||
splash = @battle.getMove("Splash")
|
||||
@p1.isMoveBlocked(splash).should.be.true
|
||||
|
||||
it "doesn't block damaging moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Assault Vest')]
|
||||
tackle = @battle.getMove("Tackle")
|
||||
@p1.isMoveBlocked(tackle).should.be.false
|
||||
|
||||
it "raises special defense by 1.5", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp')]
|
||||
spDef = @p1.stat('specialDefense')
|
||||
@p1.setItem(Item.AssaultVest)
|
||||
@p1.stat('specialDefense').should.equal Math.floor(spDef * 1.5)
|
||||
|
||||
describe "Gems", ->
|
||||
it "now only boosts their respective type by x1.3", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
move = @battle.getMove('Acrobatics')
|
||||
modifier = Item.FlyingGem::modifyBasePower(move, @p1, @p2)
|
||||
modifier.should.equal 0x14CD
|
||||
|
||||
describe "Kee Berry", ->
|
||||
it "raises defense if hit by a physical attack", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Kee Berry')]
|
||||
@p1.stages.should.containEql(defense: 0)
|
||||
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||||
@p1.stages.should.containEql(defense: 1)
|
||||
|
||||
it "is consumed after use", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Kee Berry')]
|
||||
@p1.hasItem().should.be.true
|
||||
@battle.performMove(@p2, @battle.getMove("Tackle"))
|
||||
@p1.hasItem().should.be.false
|
||||
|
||||
describe "Maranga Berry", ->
|
||||
it "raises defense if hit by a special attack", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Maranga Berry')]
|
||||
@p1.stages.should.containEql(specialDefense: 0)
|
||||
@battle.performMove(@p2, @battle.getMove("Ember"))
|
||||
@p1.stages.should.containEql(specialDefense: 1)
|
||||
|
||||
it "is consumed after use", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory('Magikarp', item: 'Maranga Berry')]
|
||||
@p1.hasItem().should.be.true
|
||||
@battle.performMove(@p2, @battle.getMove("Ember"))
|
||||
@p1.hasItem().should.be.false
|
||||
|
||||
describe "Safety Goggles", ->
|
||||
it "makes the user immune to weather", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", item: "Safety Goggles")]
|
||||
@p1.isWeatherDamageImmune(Weather.SAND).should.be.true
|
||||
@p1.isWeatherDamageImmune(Weather.HAIL).should.be.true
|
||||
|
||||
it "makes the user immune to powder moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", item: "Safety Goggles")]
|
||||
spore = @battle.getMove("Spore")
|
||||
mock = @sandbox.mock(spore).expects('hit').never()
|
||||
@battle.performMove(@p2, spore)
|
||||
mock.verify()
|
||||
723
test/xy/moves.coffee
Normal file
723
test/xy/moves.coffee
Normal file
@@ -0,0 +1,723 @@
|
||||
{Attachment, Status} = require('../../server/xy/attachment')
|
||||
{Battle} = require('../../server/xy/battle')
|
||||
{Pokemon} = require('../../server/xy/pokemon')
|
||||
{Weather} = require('../../shared/weather')
|
||||
{Move} = require('../../server/xy/move')
|
||||
util = require '../../server/xy/util'
|
||||
{Protocol} = require '../../shared/protocol'
|
||||
{Factory} = require '../factory'
|
||||
should = require 'should'
|
||||
{_} = require 'underscore'
|
||||
shared = require '../shared'
|
||||
|
||||
require '../helpers'
|
||||
|
||||
describe "XY Moves:", ->
|
||||
# Test every single move for their primary effects.
|
||||
shared.testEveryMove(Battle::MoveList, 'xy')
|
||||
|
||||
describe "a critical hit", ->
|
||||
it "multiplies damage by 1.5x", ->
|
||||
Move::criticalMultiplier.should.equal(1.5)
|
||||
|
||||
it "becomes a 50% chance at a +3 CH level", ->
|
||||
Move::determineCriticalHitFromLevel(3, .49).should.be.true
|
||||
Move::determineCriticalHitFromLevel(3, .5).should.be.false
|
||||
|
||||
it "becomes a 100% chance at a +4 CH level", ->
|
||||
Move::determineCriticalHitFromLevel(4, .99).should.be.true
|
||||
Move::determineCriticalHitFromLevel(4, 1.0).should.be.false
|
||||
|
||||
describe "a powder move", ->
|
||||
it "does not affect Grass-types", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
powderMove = @battle.findMove((m) -> m.hasFlag("powder"))
|
||||
@p2.types.push("Grass")
|
||||
|
||||
mock1 = @sandbox.mock(powderMove).expects('hit').never()
|
||||
mock2 = @sandbox.mock(powderMove).expects('fail').once()
|
||||
@battle.performMove(@p1, powderMove)
|
||||
mock1.verify()
|
||||
mock2.verify()
|
||||
|
||||
it "affects non-Grass-types", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
powderMove = @battle.findMove((m) -> m.hasFlag("powder"))
|
||||
@p2.types = [ "Normal" ]
|
||||
|
||||
mock1 = @sandbox.mock(powderMove).expects('hit').once()
|
||||
mock2 = @sandbox.mock(powderMove).expects('fail').never()
|
||||
@battle.performMove(@p1, powderMove)
|
||||
mock1.verify()
|
||||
mock2.verify()
|
||||
|
||||
describe "Dragon Pulse", ->
|
||||
it "has 85 base power now", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@battle.getMove('Dragon Pulse').power.should.equal(85)
|
||||
|
||||
describe 'Hidden Power', ->
|
||||
it "always has 60 base power", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
hiddenPower = @battle.getMove('Hidden Power')
|
||||
hiddenPower.power.should.equal(60)
|
||||
hiddenPower.basePower(@battle, @p1, @p2).should.equal(60)
|
||||
|
||||
describe "Facade", ->
|
||||
it "does not cut attack in half when burned", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
facade = @battle.getMove('Facade')
|
||||
facade.burnCalculation(@p1).should.equal(1)
|
||||
@p1.attach(Status.Burn)
|
||||
facade.burnCalculation(@p1).should.equal(1)
|
||||
|
||||
describe "King's Shield", ->
|
||||
it "protects against attacks", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
kingsShield = @battle.getMove("King's Shield")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
mock = @sandbox.mock(tackle).expects('hit').never()
|
||||
|
||||
@battle.recordMove(@id2, tackle)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, kingsShield)
|
||||
@battle.performMove(@p2, tackle)
|
||||
mock.verify()
|
||||
|
||||
it "does not protect against non-damaging moves", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
kingsShield = @battle.getMove("King's Shield")
|
||||
willOWisp = @battle.getMove("Will-O-Wisp")
|
||||
mock = @sandbox.mock(willOWisp).expects('hit').once()
|
||||
|
||||
@battle.recordMove(@id2, willOWisp)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, kingsShield)
|
||||
@battle.performMove(@p2, willOWisp)
|
||||
mock.verify()
|
||||
|
||||
it "does not protect against attacks it is immune to", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p1.types = [ 'Ghost' ]
|
||||
kingsShield = @battle.getMove("King's Shield")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
mock = @sandbox.mock(tackle).expects('hit').never()
|
||||
|
||||
@battle.recordMove(@id2, tackle)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, kingsShield)
|
||||
@battle.performMove(@p2, tackle)
|
||||
mock.verify()
|
||||
@p2.stages.should.containEql(attack: 0)
|
||||
|
||||
it "sharply lowers attacker's Attack if move was a contact move", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
kingsShield = @battle.getMove("King's Shield")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
|
||||
@battle.recordMove(@id2, tackle)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, kingsShield)
|
||||
@p2.stages.attack.should.equal(0)
|
||||
@battle.performMove(@p2, tackle)
|
||||
@p2.stages.attack.should.equal(-2)
|
||||
|
||||
it "does not lower attacker's Attack if move was not a contact move", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
kingsShield = @battle.getMove("King's Shield")
|
||||
ember = @battle.getMove("Ember")
|
||||
|
||||
@battle.recordMove(@id2, ember)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, kingsShield)
|
||||
@p2.stages.attack.should.equal(0)
|
||||
@battle.performMove(@p2, ember)
|
||||
@p2.stages.attack.should.equal(0)
|
||||
|
||||
describe "Spiky Shield", ->
|
||||
it "protects against attacks", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
spikyShield = @battle.getMove("Spiky Shield")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
mock = @sandbox.mock(tackle).expects('hit').never()
|
||||
|
||||
@battle.recordMove(@id2, tackle)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, spikyShield)
|
||||
@battle.performMove(@p2, tackle)
|
||||
mock.verify()
|
||||
|
||||
it "protects against non-damaging moves", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
spikyShield = @battle.getMove("Spiky Shield")
|
||||
willOWisp = @battle.getMove("Will-O-Wisp")
|
||||
mock = @sandbox.mock(willOWisp).expects('hit').never()
|
||||
|
||||
@battle.recordMove(@id2, willOWisp)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, spikyShield)
|
||||
@battle.performMove(@p2, willOWisp)
|
||||
mock.verify()
|
||||
|
||||
it "does not protect against attacks it is immune to", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p1.types = [ 'Ghost' ]
|
||||
spikyShield = @battle.getMove("Spiky Shield")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
mock = @sandbox.mock(tackle).expects('hit').never()
|
||||
|
||||
@battle.recordMove(@id2, tackle)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, spikyShield)
|
||||
@battle.performMove(@p2, tackle)
|
||||
mock.verify()
|
||||
@p2.stages.should.containEql(attack: 0)
|
||||
|
||||
it "damages attacker by 1/8 if move was a contact move", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
spikyShield = @battle.getMove("Spiky Shield")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
|
||||
@battle.recordMove(@id2, tackle)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, spikyShield)
|
||||
@p2.currentHP.should.not.be.lessThan(@p2.stat('hp'))
|
||||
@battle.performMove(@p2, tackle)
|
||||
(@p2.stat('hp') - @p2.currentHP).should.equal(@p2.stat('hp') >> 3)
|
||||
|
||||
it "does not damage attacker if move was not a contact move", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
spikyShield = @battle.getMove("Spiky Shield")
|
||||
ember = @battle.getMove("Ember")
|
||||
|
||||
@battle.recordMove(@id2, ember)
|
||||
@battle.determineTurnOrder()
|
||||
@battle.performMove(@p1, spikyShield)
|
||||
@p2.currentHP.should.not.be.lessThan(@p2.stat('hp'))
|
||||
@battle.performMove(@p2, ember)
|
||||
@p2.currentHP.should.not.be.lessThan(@p2.stat('hp'))
|
||||
|
||||
describe "Sticky Web", ->
|
||||
shared.shouldFailIfUsedTwice("Sticky Web", gen: 'xy')
|
||||
|
||||
it "lowers a pokemon's speed by 1 when switching in", ->
|
||||
shared.create.call(this, gen: 'xy', team2: (Factory("Magikarp") for x in [0..1]))
|
||||
stickyWeb = @battle.getMove("Sticky Web")
|
||||
@battle.performMove(@p1, stickyWeb)
|
||||
@battle.performSwitch(@p2, 1)
|
||||
@team2.first().stages.should.containEql(speed: -1)
|
||||
|
||||
it "doesn't lower a pokemon's speed by 1 if immune to ground", ->
|
||||
shared.create.call(this, gen: 'xy', team2: [ Factory("Magikarp"), Factory("Gyarados") ])
|
||||
stickyWeb = @battle.getMove("Sticky Web")
|
||||
@battle.performMove(@p1, stickyWeb)
|
||||
@battle.performSwitch(@p2, 1)
|
||||
@team2.first().stages.should.containEql(speed: 0)
|
||||
|
||||
describe "Rapid Spin", ->
|
||||
it "removes Sticky Web", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
stickyWeb = @battle.getMove("Sticky Web")
|
||||
rapidSpin = @battle.getMove("Rapid Spin")
|
||||
@battle.performMove(@p1, stickyWeb)
|
||||
@team2.has(Attachment.StickyWeb).should.be.true
|
||||
@battle.performMove(@p2, rapidSpin)
|
||||
@team2.has(Attachment.StickyWeb).should.be.false
|
||||
|
||||
describe "Defog", ->
|
||||
it "removes Sticky Web as well", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
defog = @battle.getMove("Defog")
|
||||
@battle.performMove(@p1, @battle.getMove("Sticky Web"))
|
||||
@p2.team.has(Attachment.StickyWeb).should.be.true
|
||||
@battle.performMove(@p1, defog)
|
||||
@p2.team.has(Attachment.StickyWeb).should.be.false
|
||||
|
||||
it "removes hazards from both sides of the field now", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
defog = @battle.getMove("Defog")
|
||||
@battle.performMove(@p1, @battle.getMove("Sticky Web"))
|
||||
@battle.performMove(@p2, @battle.getMove("Sticky Web"))
|
||||
@p1.team.has(Attachment.StickyWeb).should.be.true
|
||||
@p2.team.has(Attachment.StickyWeb).should.be.true
|
||||
|
||||
@battle.performMove(@p1, defog)
|
||||
@p1.team.has(Attachment.StickyWeb).should.be.false
|
||||
@p2.team.has(Attachment.StickyWeb).should.be.false
|
||||
|
||||
it "removes screens from only the target's side of the field", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
defog = @battle.getMove("Defog")
|
||||
@battle.performMove(@p1, @battle.getMove("Reflect"))
|
||||
@battle.performMove(@p1, @battle.getMove("Light Screen"))
|
||||
@battle.performMove(@p2, @battle.getMove("Reflect"))
|
||||
@battle.performMove(@p2, @battle.getMove("Light Screen"))
|
||||
@p1.team.has(Attachment.Reflect).should.be.true
|
||||
@p1.team.has(Attachment.LightScreen).should.be.true
|
||||
@p2.team.has(Attachment.Reflect).should.be.true
|
||||
@p2.team.has(Attachment.LightScreen).should.be.true
|
||||
|
||||
@battle.performMove(@p1, defog)
|
||||
@p1.team.has(Attachment.Reflect).should.be.true
|
||||
@p1.team.has(Attachment.LightScreen).should.be.true
|
||||
@p2.team.has(Attachment.Reflect).should.be.false
|
||||
@p2.team.has(Attachment.LightScreen).should.be.false
|
||||
|
||||
describe "Knock Off", ->
|
||||
it "has x1.0 power if the pokemon has no item", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
knockOff = @battle.getMove("Knock Off")
|
||||
knockOff.basePower(@battle, @p1, @p2).should.equal(knockOff.power)
|
||||
|
||||
it "has x1.5 power if the item can be knocked off", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", item: "Leftovers")]
|
||||
knockOff = @battle.getMove("Knock Off")
|
||||
basePower = knockOff.basePower(@battle, @p1, @p2)
|
||||
basePower.should.equal Math.floor(1.5 * knockOff.power)
|
||||
|
||||
it "has x1.0 power if the item cannot be knocked off", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", item: "Air Mail")]
|
||||
knockOff = @battle.getMove("Knock Off")
|
||||
knockOff.basePower(@battle, @p1, @p2).should.equal(knockOff.power)
|
||||
|
||||
it "has x1.5 power if item can be knocked off but owner has Sticky Hold", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", item: "Leftovers", ability: "Sticky Hold")]
|
||||
knockOff = @battle.getMove("Knock Off")
|
||||
basePower = knockOff.basePower(@battle, @p1, @p2)
|
||||
basePower.should.equal Math.floor(1.5 * knockOff.power)
|
||||
|
||||
describe "Protect-like moves", ->
|
||||
it "determines success chance using a power of 3 instead of 2", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
|
||||
for x in [0..7]
|
||||
attachment = @p1.attach(Attachment.ProtectCounter)
|
||||
attachment.successChance().should.equal Math.pow(3, x)
|
||||
|
||||
attachment = @p1.attach(Attachment.ProtectCounter)
|
||||
attachment.successChance().should.equal Math.pow(2, 32)
|
||||
|
||||
describe "Freeze-Dry", ->
|
||||
it "is 2x effective against Water-types", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p2.types = [ "Water" ]
|
||||
freezeDry = @battle.getMove('Freeze-Dry')
|
||||
spy = @sandbox.spy(freezeDry, 'typeEffectiveness')
|
||||
@battle.performMove(@p1, freezeDry)
|
||||
spy.returned(2).should.be.true
|
||||
|
||||
it "is 2x effective against Water-types with Normalize", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Normalize")]
|
||||
@p2.types = [ "Water" ]
|
||||
freezeDry = @battle.getMove('Freeze-Dry')
|
||||
spy = @sandbox.spy(freezeDry, 'typeEffectiveness')
|
||||
@battle.performMove(@p1, freezeDry)
|
||||
spy.returned(2).should.be.true
|
||||
|
||||
it "is normally effective against other types", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp")]
|
||||
@p2.types = [ "Fire" ]
|
||||
freezeDry = @battle.getMove('Freeze-Dry')
|
||||
spy = @sandbox.spy(freezeDry, 'typeEffectiveness')
|
||||
@battle.performMove(@p1, freezeDry)
|
||||
spy.returned(.5).should.be.true
|
||||
|
||||
describe "Substitute", ->
|
||||
it "is bypassed by voice moves", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
|
||||
voiceMove = @battle.findMove (m) ->
|
||||
!m.isNonDamaging() && m.hasFlag("sound")
|
||||
spy = @sandbox.spy(voiceMove, 'hit')
|
||||
@battle.performMove(@p1, voiceMove)
|
||||
spy.calledOnce.should.be.true
|
||||
@p2.currentHP.should.be.lessThan(@p2.stat('hp'))
|
||||
|
||||
it "is bypassed by Infiltrator", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Magikarp", ability: "Infiltrator")]
|
||||
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
|
||||
tackle = @battle.getMove("Tackle")
|
||||
spy = @sandbox.spy(tackle, 'hit')
|
||||
@battle.performMove(@p1, tackle)
|
||||
spy.calledOnce.should.be.true
|
||||
@p2.currentHP.should.be.lessThan(@p2.stat('hp'))
|
||||
|
||||
it "is bypassed by Infiltrator even on status moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Magikarp", ability: "Infiltrator")]
|
||||
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
|
||||
toxic = @battle.getMove("Toxic")
|
||||
spy = @sandbox.spy(toxic, 'hit')
|
||||
@battle.performMove(@p1, toxic)
|
||||
spy.calledOnce.should.be.true
|
||||
@p2.has(Status.Toxic).should.be.true
|
||||
|
||||
it "does not block Knock Off + Infiltrator", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Magikarp", ability: "Infiltrator")]
|
||||
team2: [ Factory("Magikarp", item: "Leftovers")]
|
||||
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
|
||||
knockOff = @battle.getMove("Knock Off")
|
||||
spy = @sandbox.spy(knockOff, 'hit')
|
||||
@battle.performMove(@p1, knockOff)
|
||||
spy.calledOnce.should.be.true
|
||||
@p2.hasItem().should.be.false
|
||||
|
||||
it "does not block secondary effects + Infiltrator", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [ Factory("Magikarp", ability: "Infiltrator")]
|
||||
shared.biasRNG.call(this, "next", "secondary effect", 0) # always burn
|
||||
@p2.attach(Attachment.Substitute, hp: (@p1.currentHP >> 2))
|
||||
flamethrower = @battle.getMove('Flamethrower')
|
||||
spy = @sandbox.spy(flamethrower, 'hit')
|
||||
@battle.performMove(@p1, flamethrower)
|
||||
spy.calledOnce.should.be.true
|
||||
@p2.has(Status.Burn).should.be.true
|
||||
|
||||
testChargeMove = (moveName, vulnerable) ->
|
||||
describe moveName, ->
|
||||
it "chooses the player's next action for them", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
move = @battle.getMove(moveName)
|
||||
@p1.moves = [ move ]
|
||||
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.continueTurn()
|
||||
@battle.endTurn()
|
||||
@battle.beginTurn()
|
||||
@battle.requests.should.not.have.property(@id1)
|
||||
should.exist(@battle.getAction(@p1))
|
||||
|
||||
it "only spends 1 PP for the entire attack", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
move = @battle.getMove(moveName)
|
||||
@p1.moves = [ move ]
|
||||
@p1.resetAllPP()
|
||||
|
||||
pp = @p1.pp(move)
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.continueTurn()
|
||||
@p1.pp(move).should.equal(pp)
|
||||
@battle.beginTurn()
|
||||
@battle.continueTurn()
|
||||
@p1.pp(move).should.equal(pp - 1)
|
||||
|
||||
it "skips the charge turn if the user is holding a Power Herb", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", item: "Power Herb")]
|
||||
move = @battle.getMove(moveName)
|
||||
|
||||
@p1.hasItem("Power Herb").should.be.true
|
||||
mock = @sandbox.mock(move).expects('execute').once()
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
@p1.hasItem().should.be.false
|
||||
|
||||
if vulnerable?.length?
|
||||
it "makes target invulnerable to moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", evs: {speed: 4})]
|
||||
move = @battle.getMove(moveName)
|
||||
swift = @battle.getMove("Swift")
|
||||
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.recordMove(@id2, swift)
|
||||
|
||||
mock = @sandbox.mock(swift).expects('hit').never()
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
|
||||
it "makes target invulnerable to moves *after* use", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", evs: {speed: 4})]
|
||||
move = @battle.getMove(moveName)
|
||||
tackle = @battle.getMove("Tackle")
|
||||
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.recordMove(@id2, tackle)
|
||||
|
||||
mock = @sandbox.mock(tackle).expects('hit').once()
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
|
||||
it "is vulnerable to attacks from a No Guard pokemon", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Magikarp", ability: "No Guard")]
|
||||
move = @battle.getMove(moveName)
|
||||
tackle = @battle.getMove("Tackle")
|
||||
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.recordMove(@id2, tackle)
|
||||
|
||||
mock = @sandbox.mock(tackle).expects('hit').once()
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
|
||||
it "is vulnerable to attacks if locked on", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@battle.performMove(@p1, @battle.getMove("Lock-On"))
|
||||
@battle.performMove(@p2, @battle.getMove(moveName))
|
||||
tackle = @battle.getMove("Tackle")
|
||||
|
||||
@battle.recordMove(@id1, tackle)
|
||||
|
||||
mock = @sandbox.mock(tackle).expects('hit').once()
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
|
||||
for vulnerableMove in vulnerable
|
||||
it "is vulnerable to #{vulnerableMove}", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", evs: {speed: 4})]
|
||||
move = @battle.getMove(moveName)
|
||||
vulnerable = @battle.getMove(vulnerableMove)
|
||||
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.recordMove(@id2, vulnerable)
|
||||
|
||||
mock = @sandbox.mock(vulnerable).expects('hit').once()
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
else # no vulnerable moves
|
||||
it "doesn't make target invulnerable to moves", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", evs: {speed: 4})]
|
||||
move = @battle.getMove(moveName)
|
||||
tackle = @battle.getMove("Tackle")
|
||||
|
||||
@battle.recordMove(@id1, move)
|
||||
@battle.recordMove(@id2, tackle)
|
||||
|
||||
mock = @sandbox.mock(tackle).expects('hit').once()
|
||||
@battle.continueTurn()
|
||||
mock.verify()
|
||||
|
||||
testChargeMove('Fly', ["Gust", "Thunder", "Twister", "Sky Uppercut", "Hurricane", "Smack Down", "Thousand Arrows"])
|
||||
testChargeMove('Bounce', ["Gust", "Thunder", "Twister", "Sky Uppercut", "Hurricane", "Smack Down", "Thousand Arrows"])
|
||||
testChargeMove('Geomancy')
|
||||
testChargeMove('Phantom Force', [])
|
||||
|
||||
describe "Toxic", ->
|
||||
it "cannot miss when used by a Poison type pokemon", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p1.types = [ "Poison" ]
|
||||
@p2.types = [ "Normal" ]
|
||||
shared.biasRNG.call(this, "randInt", 'miss', 101)
|
||||
toxic = @battle.getMove("Toxic")
|
||||
toxic.willMiss(@battle, @p1, @p2).should.be.false
|
||||
|
||||
describe "Parting Shot", ->
|
||||
it "reduces the attack and special attack of the target by two stages", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@battle.performMove(@p1, @battle.getMove("Parting Shot"))
|
||||
@p2.stages.should.containEql attack: -1, specialAttack: -1
|
||||
|
||||
it "forces the owner to switch", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@battle.performMove(@p1, @battle.getMove("Parting Shot"))
|
||||
@battle.requests.should.have.property @id1
|
||||
|
||||
describe "Worry Seed", ->
|
||||
it "does not change some abilities", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Smeargle")]
|
||||
team2: [Factory("Aegislash", ability: "Stance Change")]
|
||||
worrySeed = @battle.getMove("Worry Seed")
|
||||
mock = @sandbox.mock(worrySeed).expects('fail').once()
|
||||
@battle.performMove(@p1, worrySeed)
|
||||
mock.verify()
|
||||
|
||||
describe "Simple Beam", ->
|
||||
it "does not change some abilities", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Smeargle")]
|
||||
team2: [Factory("Aegislash", ability: "Stance Change")]
|
||||
simpleBeam = @battle.getMove("Simple Beam")
|
||||
mock = @sandbox.mock(simpleBeam).expects('fail').once()
|
||||
@battle.performMove(@p1, simpleBeam)
|
||||
mock.verify()
|
||||
|
||||
testTrappingMove = (name) ->
|
||||
describe name, ->
|
||||
it "deals 1/8 of the pokemon's max hp every turn", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team2: [Factory("Blissey")]
|
||||
@battle.performMove(@p1, @battle.getMove(name))
|
||||
@p2.currentHP = @p2.stat('hp')
|
||||
@battle.endTurn()
|
||||
maxHP = @p2.stat('hp')
|
||||
expected = maxHP - Math.floor(maxHP / 8)
|
||||
@p2.currentHP.should.equal expected
|
||||
|
||||
it "deals 1/6 of the pokemon's max hp every turn if the user is holding a Binding Band", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", item: "Binding Band")]
|
||||
team2: [Factory("Blissey")]
|
||||
@battle.performMove(@p1, @battle.getMove(name))
|
||||
@p2.currentHP = @p2.stat('hp')
|
||||
@battle.endTurn()
|
||||
maxHP = @p2.stat('hp')
|
||||
expected = maxHP - Math.floor(maxHP / 6)
|
||||
@p2.currentHP.should.equal expected
|
||||
|
||||
testTrappingMove "Bind"
|
||||
testTrappingMove "Clamp"
|
||||
testTrappingMove "Fire Spin"
|
||||
testTrappingMove "Infestation"
|
||||
testTrappingMove "Magma Storm"
|
||||
testTrappingMove "Sand Tomb"
|
||||
testTrappingMove "Wrap"
|
||||
|
||||
describe "Entrainment", ->
|
||||
it "does not change some abilities", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Swift Swim")]
|
||||
team2: [Factory("Aegislash", ability: "Stance Change")]
|
||||
entrainment = @battle.getMove("Entrainment")
|
||||
mock = @sandbox.mock(entrainment).expects('fail').once()
|
||||
@battle.performMove(@p1, entrainment)
|
||||
mock.verify()
|
||||
|
||||
describe "Nature Power", ->
|
||||
it "uses Tri Attack in Wi-Fi battles", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
naturePower = @battle.getMove('Nature Power')
|
||||
triAttack = @battle.getMove('Tri Attack')
|
||||
|
||||
mock = @sandbox.mock(triAttack).expects('execute').once()
|
||||
.withArgs(@battle, @p1, [ @p2 ])
|
||||
@battle.performMove(@p1, naturePower)
|
||||
mock.verify()
|
||||
|
||||
describe "Venom Drench", ->
|
||||
it "lowers the target's attack, special attack, and speed by 1 stage if it is poisoned", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p2.attach(Status.Poison)
|
||||
@battle.performMove(@p1, @battle.getMove('Venom Drench'))
|
||||
@p2.stages.should.containEql attack: -1, specialAttack: -1, speed: -1
|
||||
|
||||
it "fails if the target isn't poisoned", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
venomDrench = @battle.getMove("Venom Drench")
|
||||
mock = @sandbox.mock(venomDrench).expects('fail').once()
|
||||
@battle.performMove(@p1, venomDrench)
|
||||
mock.verify()
|
||||
|
||||
describe "Topsy-Turvy", ->
|
||||
it "reverses the target's boosts", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p2.stages.attack = 2
|
||||
@p2.stages.defense = -3
|
||||
@p2.stages.speed = 0
|
||||
|
||||
@battle.performMove(@p1, @battle.getMove('Topsy-Turvy'))
|
||||
|
||||
@p2.stages.should.containEql attack: -2
|
||||
@p2.stages.should.containEql defense: 3
|
||||
@p2.stages.should.containEql speed: 0
|
||||
|
||||
it "fails if the target has no boosts", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
topsyTurvy = @battle.getMove('Topsy-Turvy')
|
||||
|
||||
mock = @sandbox.mock(topsyTurvy).expects('fail').once()
|
||||
@battle.performMove(@p1, topsyTurvy)
|
||||
mock.verify()
|
||||
|
||||
describe "Fell Stinger", ->
|
||||
it "raises the user's Attack 2 stages if the target faints", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p2.currentHP = 1
|
||||
@battle.performMove(@p1, @battle.getMove("Fell Stinger"))
|
||||
@p1.stages.should.containEql attack: 2
|
||||
|
||||
it "does not raise the user's Attack 2 stages otherwise", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@battle.performMove(@p1, @battle.getMove("Fell Stinger"))
|
||||
@p1.stages.should.containEql attack: 0
|
||||
|
||||
describe "Skill Swap", ->
|
||||
it "can swap the abilities if they are the same", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Swift Swim")]
|
||||
team2: [Factory("Magikarp", ability: "Swift Swim")]
|
||||
skillSwap = @battle.getMove("Skill Swap")
|
||||
mock = @sandbox.mock(skillSwap).expects('fail').never()
|
||||
@battle.performMove(@p1, skillSwap)
|
||||
mock.verify()
|
||||
|
||||
describe "Metronome", ->
|
||||
it "reselects if chosen an illegal move", ->
|
||||
shared.create.call(this, gen: 'xy')
|
||||
@p1.moves = [ metronome ]
|
||||
metronome = @battle.getMove("Metronome")
|
||||
belch = @battle.getMove("Belch")
|
||||
tackle = @battle.getMove("Tackle")
|
||||
index = @battle.MoveList.indexOf(belch)
|
||||
reselectIndex = @battle.MoveList.indexOf(tackle)
|
||||
shared.biasRNG.call(this, 'randInt', "metronome", index)
|
||||
shared.biasRNG.call(this, 'randInt', "metronome reselect", reselectIndex)
|
||||
|
||||
mock = @sandbox.mock(tackle).expects('execute').once()
|
||||
@battle.performMove(@p1, metronome)
|
||||
mock.verify()
|
||||
|
||||
testDelayedAttackMove = (moveName, type) ->
|
||||
describe moveName, ->
|
||||
it "does not hit substitutes if the user has Infiltrator and is active", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Infiltrator")]
|
||||
move = @battle.getMove(moveName)
|
||||
@battle.performMove(@p1, move)
|
||||
@p2.attach(Attachment.Substitute, hp: 1)
|
||||
@battle.endTurn()
|
||||
@battle.endTurn()
|
||||
@p2.has(Attachment.Substitute).should.be.true
|
||||
@battle.endTurn()
|
||||
@p2.has(Attachment.Substitute).should.be.true
|
||||
|
||||
it "always hits substitutes if the user is not on the field", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: [Factory("Magikarp", ability: "Infiltrator"), Factory("Magikarp")]
|
||||
move = @battle.getMove(moveName)
|
||||
@battle.performMove(@p1, move)
|
||||
@p2.attach(Attachment.Substitute, hp: 1)
|
||||
@battle.endTurn()
|
||||
@battle.performSwitch(@p1, 1)
|
||||
@battle.endTurn()
|
||||
@p2.has(Attachment.Substitute).should.be.true
|
||||
@battle.endTurn()
|
||||
@p2.has(Attachment.Substitute).should.be.false
|
||||
|
||||
testDelayedAttackMove("Future Sight")
|
||||
testDelayedAttackMove("Doom Desire")
|
||||
70
test/xy/pokemon.coffee
Normal file
70
test/xy/pokemon.coffee
Normal file
@@ -0,0 +1,70 @@
|
||||
{Weather} = require('../../shared/weather')
|
||||
{Pokemon} = require('../../server/xy/pokemon')
|
||||
{Protocol} = require '../../shared/protocol'
|
||||
{Factory} = require '../factory'
|
||||
should = require 'should'
|
||||
shared = require '../shared'
|
||||
|
||||
require '../helpers'
|
||||
|
||||
describe "XY Pokemon:", ->
|
||||
describe '#canMegaEvolve', ->
|
||||
it "returns true if holding its associated mega stone", ->
|
||||
pokemon = new Pokemon(species: "Charizard", item: "Charizardite Y")
|
||||
pokemon.canMegaEvolve().should.be.true
|
||||
|
||||
it "returns false if holding a mega stone meant for another pokemon", ->
|
||||
pokemon = new Pokemon(species: "Charizard", item: "Abomasite")
|
||||
pokemon.canMegaEvolve().should.be.false
|
||||
|
||||
it "returns false if holding a random item", ->
|
||||
pokemon = new Pokemon(species: "Charizard", item: "Leftovers")
|
||||
pokemon.canMegaEvolve().should.be.false
|
||||
|
||||
it "returns false if holding no item", ->
|
||||
pokemon = new Pokemon(species: "Charizard")
|
||||
pokemon.canMegaEvolve().should.be.false
|
||||
|
||||
it "returns false if already another forme", ->
|
||||
pokemon = new Pokemon(species: "Charizard", item: "Charizardite X")
|
||||
pokemon.changeForme("mega-x")
|
||||
pokemon.canMegaEvolve().should.be.false
|
||||
|
||||
it "returns false if the team has already mega evolved", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: (Factory("Charizard", item: "Charizardite X") for x in [0..1])
|
||||
@team1.first().changeForme("mega-x")
|
||||
@team1.at(1).canMegaEvolve().should.be.false
|
||||
|
||||
it "returns true if the team has not already mega evolved", ->
|
||||
shared.create.call this,
|
||||
gen: 'xy'
|
||||
team1: (Factory("Charizard", item: "Charizardite X") for x in [0..1])
|
||||
@team1.at(1).canMegaEvolve().should.be.true
|
||||
|
||||
describe "#blockSwitch", ->
|
||||
it "does not block switches if the Pokemon has a Ghost type", ->
|
||||
pokemon = new Pokemon(species: "Gengar")
|
||||
pokemon.blockSwitch()
|
||||
pokemon.isSwitchBlocked().should.be.false
|
||||
|
||||
it "acts normally otherwise", ->
|
||||
pokemon = new Pokemon(species: "Charizard")
|
||||
pokemon.blockSwitch()
|
||||
pokemon.isSwitchBlocked().should.be.true
|
||||
|
||||
describe "#hasTakeableItem", ->
|
||||
it "returns false if the pokemon holds a mega stone for its species", ->
|
||||
pokemon = new Pokemon(species: "Gengar", item: "Gengarite")
|
||||
pokemon.hasTakeableItem().should.be.false
|
||||
|
||||
it "returns true if the pokemon holds a mega stone for another species", ->
|
||||
pokemon = new Pokemon(species: "Gengar", item: "Kangaskhanite")
|
||||
pokemon.hasTakeableItem().should.be.true
|
||||
|
||||
it "still uses old BW conditions", ->
|
||||
pokemon = new Pokemon(species: "Gengar", item: "Leftovers")
|
||||
pokemon.hasTakeableItem().should.be.true
|
||||
pokemon = new Pokemon(species: "Gengar")
|
||||
pokemon.hasTakeableItem().should.be.false
|
||||
35
test/xy/priorities.coffee
Normal file
35
test/xy/priorities.coffee
Normal file
@@ -0,0 +1,35 @@
|
||||
{Ability} = require('../../server/xy/data/abilities')
|
||||
{Item} = require('../../server/xy/data/items')
|
||||
{Attachment, Status} = require('../../server/xy/attachment')
|
||||
Priorities = require('../../server/xy/priorities')
|
||||
Query = require('../../server/xy/queries')
|
||||
shared = require('../shared')
|
||||
|
||||
require '../helpers'
|
||||
|
||||
describe "XY Priorities:", ->
|
||||
ensureAttachments = (arrayOfAttachments, eventName) ->
|
||||
attachments = (a for a in arrayOfAttachments when a.prototype[eventName]? && a not in Priorities[eventName])
|
||||
attachments = attachments.map((a) -> a.displayName || a::name)
|
||||
if attachments.length > 0
|
||||
throw new Error("#{attachments.join(', ')} must specify their #{eventName} priority.")
|
||||
|
||||
it "ensures all relevant attachments have their specified event names", ->
|
||||
for eventName of Priorities
|
||||
ensureAttachments((klass for name, klass of Attachment), eventName)
|
||||
ensureAttachments((klass for name, klass of Item), eventName)
|
||||
ensureAttachments((klass for name, klass of Ability), eventName)
|
||||
|
||||
describe "Queries", ->
|
||||
it "execute priorities in order", ->
|
||||
shared.create.call(this)
|
||||
@battle.attach(Attachment.TrickRoom)
|
||||
@team2.attach(Attachment.Reflect)
|
||||
@p1.attach(Attachment.Ingrain)
|
||||
spy1 = @sandbox.spy(Attachment.TrickRoom.prototype, 'endTurn')
|
||||
spy2 = @sandbox.spy(Attachment.Reflect.prototype, 'endTurn')
|
||||
spy3 = @sandbox.spy(Attachment.Ingrain.prototype, 'endTurn')
|
||||
|
||||
Query("endTurn", @battle.getAllAttachments())
|
||||
spy3.calledBefore(spy2).should.be.true
|
||||
spy2.calledBefore(spy1).should.be.true
|
||||
22
test/xy/util.coffee
Normal file
22
test/xy/util.coffee
Normal file
@@ -0,0 +1,22 @@
|
||||
util = require '../../server/xy/util'
|
||||
should = require 'should'
|
||||
require '../helpers'
|
||||
|
||||
describe "XY utility functions:", ->
|
||||
it "inherits from BW", ->
|
||||
should.exist(util)
|
||||
util.should.have.property('roundHalfDown')
|
||||
|
||||
it "adds a new Fairy type", ->
|
||||
should.exist(util.Type)
|
||||
should.exist(util.Type.Fairy)
|
||||
|
||||
it "adds strengths and weaknesses of the new Fairy type", ->
|
||||
util.typeEffectiveness("Fairy", [ "Dragon" ]).should.equal(2)
|
||||
util.typeEffectiveness("Dragon", [ "Fairy" ]).should.equal(0)
|
||||
|
||||
it "removes Steel's resistances to Ghost", ->
|
||||
util.typeEffectiveness("Ghost", [ "Steel" ]).should.equal(1)
|
||||
|
||||
it "removes Steel's resistances to Dark", ->
|
||||
util.typeEffectiveness("Dark", [ "Steel" ]).should.equal(1)
|
||||
Reference in New Issue
Block a user