'use strict'; const assert = require('./../assert'); const common = require('./../common'); let battle; describe('Choice parser', function () { afterEach(() => battle.destroy()); describe('Team Preview requests', function () { it('should accept only `team` choices', function () { battle = common.createBattle({preview: true}, [ [{species: "Mew", ability: 'synchronize', moves: ['recover']}], [{species: "Rhydon", ability: 'prankster', moves: ['splash']}], ]); const validChoice = 'team 1'; assert(battle.choose('p1', validChoice)); battle.p1.clearChoice(); assert(battle.choose('p2', validChoice)); battle.p1.clearChoice(); const badChoices = ['move 1', 'move 2 mega', 'switch 1', 'pass', 'shift']; for (const badChoice of badChoices) { assert.throws(() => battle.choose('p1', badChoice)); } }); it('should reject non-numerical choice details', function () { battle = common.createBattle({preview: true}, [ [{species: "Mew", ability: 'synchronize', moves: ['recover']}], [{species: "Rhydon", ability: 'prankster', moves: ['splash']}], ]); for (const side of battle.sides) { assert.throws(() => battle.choose(side.id, 'team Rhydon')); assert.throws(() => battle.choose(side.id, 'team Mew')); assert.throws(() => battle.choose(side.id, 'team first')); } }); it('should reject zero-based choice details', function () { battle = common.createBattle({preview: true}, [ [{species: "Mew", ability: 'synchronize', moves: ['recover']}], [{species: "Rhydon", ability: 'prankster', moves: ['splash']}], ]); for (const side of battle.sides) { assert.throws( () => battle.choose(side.id, 'team 0'), new RegExp(`\\[Invalid choice\\] Can't choose for Team Preview:`, 'i'), `Input should have been rejected` ); } }); }); describe('Switch requests', function () { describe('Generic', function () { it('should reject non-numerical input for `switch` choices', function () { battle = common.createBattle(); battle.setPlayer('p1', {team: [ {species: "Mew", ability: 'synchronize', moves: ['lunardance']}, {species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}, ]}); battle.setPlayer('p2', {team: [{species: "Rhydon", ability: 'prankster', moves: ['splash']}]}); battle.makeChoices('move lunardance', 'move splash'); assert.throws(() => battle.choose('p1', 'switch first')); assert.throws(() => battle.choose('p1', 'switch second')); }); }); describe('Singles', function () { it('should accept only `switch` choices', function () { battle = common.createBattle(); battle.setPlayer('p1', {team: [ {species: "Mew", ability: 'synchronize', moves: ['lunardance']}, {species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}, ]}); battle.setPlayer('p2', {team: [{species: "Rhydon", ability: 'prankster', moves: ['splash']}]}); battle.makeChoices('move lunardance', 'move splash'); const badChoices = ['move 1', 'move 2 mega', 'team 1', 'pass', 'shift']; for (const badChoice of badChoices) { assert.throws(() => battle.p1.choose(badChoice)); } const validChoice = 'switch Bulbasaur'; assert(battle.p1.choose(validChoice)); battle.p1.clearChoice(); }); }); describe('Doubles/Triples', function () { it('should accept only `switch` and `pass` choices', function () { battle = common.createBattle({gameType: 'doubles'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Koffing", ability: 'levitate', moves: ['smog']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, {species: "Ekans", ability: 'shedskin', moves: ['wrap']}, ]}); battle.makeChoices('move selfdestruct, move selfdestruct', 'move roost, move irondefense'); // Both p1 active Pokémon faint const badChoices = ['move 1', 'move 2 mega', 'team 1', 'shift']; for (const badChoice of badChoices) { assert.throws(() => battle.p1.choose(badChoice)); } assert(battle.p1.choose(`pass, switch 3`), `Choice 'pass, switch 3' should be valid`); }); it('should reject choice details for `pass` choices', function () { battle = common.createBattle({gameType: 'doubles'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Koffing", ability: 'levitate', moves: ['smog']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, {species: "Ekans", ability: 'shedskin', moves: ['wrap']}, ]}); battle.makeChoices('move selfdestruct, move selfdestruct', 'move roost, move irondefense'); // Both p1 active Pokémon faint const switchChoice = 'switch 3'; const passChoice = 'pass'; assert.throws(() => battle.choose('p1', `${switchChoice}, ${passChoice} 1`)); assert.throws(() => battle.choose('p1', `${passChoice} 1, ${switchChoice}`)); assert.throws(() => battle.choose('p1', `${switchChoice}, ${passChoice} a`)); assert.throws(() => battle.choose('p1', `${passChoice} a, ${switchChoice}`)); }); it.skip(`should only allow switching to left slots on double KOs with only one Pokemon remaining`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'tornadus', moves: ['sleeptalk']}, {species: 'landorus', moves: ['earthquake']}, ], [ {species: 'roggenrola', level: 1, moves: ['sleeptalk']}, {species: 'aron', level: 1, moves: ['sleeptalk']}, {species: 'wynaut', moves: ['sleeptalk']}, ]]); battle.makeChoices(); assert.throws(() => battle.choose('p2', 'pass, switch 3'), 'Wynaut should only be able to switch on the left.'); }); }); }); describe('Move requests', function () { describe('Generic', function () { it('should reject `pass` choices for non-fainted Pokémon', function () { battle = common.createBattle(); battle.setPlayer('p1', {team: [{species: "Mew", ability: 'synchronize', moves: ['recover']}]}); battle.setPlayer('p2', {team: [{species: "Rhydon", ability: 'prankster', moves: ['splash']}]}); for (const side of battle.sides) { assert.throws(() => battle.choose(side.id, 'pass')); } }); it('should allow mega evolving and targeting in the same move in either order', function () { battle = common.createBattle({gameType: 'doubles'}); battle.setPlayer('p1', {team: [ {species: "Gengar", ability: 'cursedbody', item: 'gengarite', moves: ['shadowball']}, {species: "Zigzagoon", ability: 'pickup', moves: ['tackle']}, ]}); battle.setPlayer('p2', {team: [ {species: "Blaziken", ability: 'blaze', item: 'firiumz', moves: ['blazekick']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, ]}); const badChoices = [`move 1 1 2`, `move 1 1 mega ultra`, `move 1 mega zmove 2`]; for (const badChoice of badChoices) { const choice = `${badChoice}, move tackle 1`; assert.throws(() => battle.choose('p1', choice)); } assert(battle.choose('p1', `move 1 +1 mega, move tackle 1`)); assert(battle.choose('p2', `move Blaze Kick zmove 1, move irondefense`)); }); it('should allow Dynamax use in multiple possible formats', function () { battle = common.createBattle([[ {species: "Mew", moves: ['psychic']}, ], [ {species: "Mew", moves: ['psychic']}, ]]); battle.makeChoices(`move max mindstorm`, `move psychic max`); assert(battle.p1.active[0].volatiles['dynamax']); assert(battle.p2.active[0].volatiles['dynamax']); }); it('should handle Conversion 2', function () { battle = common.createBattle({gameType: 'doubles'}); battle.setPlayer('p1', {team: [ {species: "Porygon-Z", ability: 'adaptability', item: 'normaliumz', moves: ['conversion', 'conversion2']}, {species: "Porygon", ability: 'download', moves: ['conversion', 'conversion2']}, ]}); battle.setPlayer('p2', {team: [ {species: "Gengar", ability: 'cursedbody', moves: ['lick']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, ]}); assert(battle.choose('p1', `move 1, move Conversion 2 2`)); assert.equal(battle.p1.getChoice(), `move conversion, move conversion2 +2`); battle.p1.clearChoice(); assert.throws(() => battle.choose('p1', `move 1, move Conversion -2`)); battle.p1.clearChoice(); assert(battle.choose('p1', `move Conversion 2 zmove 2, move 1`)); assert.equal(battle.p1.getChoice(), `move conversion2 +2 zmove, move conversion`); battle.p1.clearChoice(); }); }); describe('Singles', function () { it('should accept only `move` and `switch` choices', function () { battle = common.createBattle(); battle.setPlayer('p1', {team: [ {species: "Mew", ability: 'synchronize', moves: ['lunardance', 'recover']}, {species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}, ]}); battle.setPlayer('p2', {team: [ {species: "Rhydon", ability: 'prankster', moves: ['splash', 'horndrill']}, {species: "Charmander", ability: 'blaze', moves: ['tackle', 'growl']}, ]}); const validChoices = ['move 1', 'move 2', 'switch 2']; for (const action of validChoices) { assert(battle.choose('p1', action), `Choice '${action}' should be valid`); battle.p1.clearChoice(); } const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift']; for (const badChoice of badChoices) { assert.throws(() => battle.choose('p1', badChoice)); } }); }); describe('Doubles', function () { it('should enforce `pass` choices for fainted Pokémon', function () { battle = common.createBattle({gameType: 'doubles'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Koffing", ability: 'levitate', moves: ['smog']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, ]}); const p1 = battle.p1; battle.makeChoices('move selfdestruct, move selfdestruct', 'move roost, move irondefense'); // Both p1 active Pokémon faint battle.makeChoices('pass, switch 3', ''); // Koffing switches in at slot #2 assert.fainted(p1.active[0]); assert.species(p1.active[1], 'Koffing'); assert.false.fainted(p1.active[1]); assert(battle.choose('p1', 'move smog 2')); assert.equal(battle.p1.getChoice(), `pass, move smog +2`, `Choice mismatch`); }); }); describe('Triples', function () { it('should accept only `move` and `switch` choices for a healthy Pokémon on the center', function () { battle = common.gen(5).createBattle({gameType: 'triples'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Gastly", ability: 'levitate', moves: ['lick']}, {species: "Forretress", ability: 'levitate', moves: ['spikes']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, {species: "Golem", ability: 'sturdy', moves: ['defensecurl']}, ]}); const validChoices = ['move 1', 'switch 4']; for (const action of validChoices) { const choiceString = `move 1, ${action}, move 1 1`; assert(battle.choose('p1', choiceString), `Choice '${choiceString}' should be valid`); battle.p1.clearChoice(); } const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift']; for (const badChoice of badChoices) { const choiceString = `move 1, ${badChoice}, move 1 1`; assert.throws(() => battle.choose('p1', choiceString)); } }); it('should accept only `move`, `switch` and `shift` choices for a healthy Pokémon on the left', function () { battle = common.gen(5).createBattle({gameType: 'triples'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Gastly", ability: 'levitate', moves: ['lick']}, {species: "Forretress", ability: 'levitate', moves: ['spikes']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, {species: "Golem", ability: 'sturdy', moves: ['defensecurl']}, {species: "Magnezone", ability: 'magnetpull', moves: ['discharge']}, ]}); const validChoices = ['move 1', 'switch 4', 'shift']; for (const action of validChoices) { const choiceString = `${action}, move 1, move 1 1`; assert(battle.choose('p1', choiceString), `Choice '${choiceString}' should be valid`); battle.p1.clearChoice(); } const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass']; for (const badChoice of badChoices) { const choiceString = `${badChoice}, move 1, move 1 1`; assert.throws(() => battle.choose('p1', choiceString)); } }); it('should accept only `move`, `switch` and `shift` choices for a healthy Pokémon on the right', function () { battle = common.gen(5).createBattle({gameType: 'triples'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Gastly", ability: 'levitate', moves: ['lick']}, {species: "Forretress", ability: 'levitate', moves: ['spikes']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, {species: "Golem", ability: 'sturdy', moves: ['defensecurl']}, {species: "Magnezone", ability: 'magnetpull', moves: ['discharge']}, ]}); const validChoices = ['move 1 1', 'switch 4', 'shift']; for (const action of validChoices) { const choiceString = `move 1, move 1, ${action}`; assert(battle.choose('p1', choiceString), `Choice '${choiceString}' should be valid`); battle.p1.clearChoice(); } const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift blah']; for (const badChoice of badChoices) { const choiceString = `move 1, move 1, ${badChoice}`; assert.throws(() => battle.choose('p1', choiceString)); } }); it('should enforce `pass` choices for fainted Pokémon', function () { battle = common.gen(5).createBattle({gameType: 'triples'}); battle.setPlayer('p1', {team: [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']}, {species: "Gastly", ability: 'levitate', moves: ['lunardance']}, {species: "Forretress", ability: 'levitate', moves: ['spikes']}, ]}); battle.setPlayer('p2', {team: [ {species: "Skarmory", ability: 'sturdy', moves: ['roost']}, {species: "Aggron", ability: 'sturdy', moves: ['irondefense']}, {species: "Golem", ability: 'sturdy', moves: ['defensecurl']}, ]}); const p1 = battle.p1; battle.makeChoices('move selfdestruct, move selfdestruct, move lunardance', 'move roost, move irondefense, move defensecurl'); // All p1 active Pokémon faint battle.makeChoices('pass, switch 4, default', ''); // Forretress switches in to slot #2 assert.species(p1.active[1], 'Forretress'); const validChoices = ['move spikes', 'move 1']; for (const action of validChoices) { battle.choose('p1', action); assert.equal(battle.p1.getChoice(), `pass, move spikes, pass`); battle.p1.clearChoice(); battle.choose('p1', `pass, ${action}, pass`); assert.equal(battle.p1.getChoice(), `pass, move spikes, pass`); battle.p1.clearChoice(); battle.choose('p1', `pass, ${action}`); assert.equal(battle.p1.getChoice(), `pass, move spikes, pass`); battle.p1.clearChoice(); battle.choose('p1', `${action}, pass`); assert.equal(battle.p1.getChoice(), `pass, move spikes, pass`); battle.p1.clearChoice(); } }); }); }); });