{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