1
0
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:
Deukhoofd
2016-02-01 23:19:30 +01:00
commit d7316d5799
6681 changed files with 527969 additions and 0 deletions

485
test/xy/abilities.coffee Normal file
View 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

View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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)