pokemon-showdown/test/simulator/misc/decisions.js
Guangcong Luo 6dd58b40d3 Refactor simulator into new sim/ directory
This is a surprisingly minor refactor considering how many files it
touches, but most of this is only renames.

In terms of file renames:
- `tools.js` is now `sim/dex.js`
- `battle-engine.js` is now `sim/index.js` and its three classes are
  in `sim/battle.js`, `sim/side.js`, and `sim/pokemon.js`
- `prng.js` is now `sim/prng.js`

In terms of variable renames:
- `Tools` is now `Dex`
- `BattleEngine` is now `Sim`
- `BattleEngine.Battle` is now `Sim.Battle`
- `BattleEngine.BattleSide` is now `Sim.Side`
- `BattleEngine.BattlePokemon` is now `Sim.Pokemon`
2017-05-05 16:48:38 -05:00

1350 lines
56 KiB
JavaScript

'use strict';
const assert = require('./../../assert');
const common = require('./../../common');
const BASE_TEAM_ORDER = [1, 2, 3, 4, 5, 6];
const SINGLES_TEAMS = {
illusion: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Charmander', ability: 'blaze', moves: ['tackle']},
{species: 'Charmeleon', ability: 'blaze', moves: ['tackle']},
{species: 'Zoroark', ability: 'illusion', moves: ['tackle']},
], [
{species: 'Squirtle', ability: 'torrent', moves: ['tackle']},
{species: 'Wartortle', ability: 'torrent', moves: ['tackle']},
]],
full: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Charmander', ability: 'blaze', moves: ['tackle']},
{species: 'Charmeleon', ability: 'blaze', moves: ['tackle']},
{species: 'Charizard', ability: 'blaze', moves: ['tackle']},
], [
{species: 'Squirtle', ability: 'torrent', moves: ['tackle']},
{species: 'Wartortle', ability: 'torrent', moves: ['tackle']},
]],
};
const DOUBLES_TEAMS = {
forcePass: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['lunardance']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'levitate', moves: ['roost']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['recover']},
]],
full: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Charmander', ability: 'blaze', moves: ['tackle']},
{species: 'Charmeleon', ability: 'blaze', moves: ['tackle']},
{species: 'Charizard', ability: 'blaze', moves: ['tackle']},
], [
{species: 'Squirtle', ability: 'torrent', moves: ['tackle']},
{species: 'Wartortle', ability: 'torrent', moves: ['tackle']},
]],
default: [[
{species: 'Latias', ability: 'overgrow', moves: ['lunardance']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Bulbasaur', ability: 'levitate', moves: ['synthesis']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['recover']},
]],
};
const TRIPLES_TEAMS = {
forcePass: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Venusaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['recover']},
{species: 'Latias', ability: 'blaze', moves: ['roost']},
]],
full: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Charmander', ability: 'blaze', moves: ['tackle']},
{species: 'Charmeleon', ability: 'blaze', moves: ['tackle']},
{species: 'Charizard', ability: 'blaze', moves: ['tackle']},
], [
{species: 'Squirtle', ability: 'torrent', moves: ['tackle']},
{species: 'Wartortle', ability: 'torrent', moves: ['tackle']},
{species: 'Blastoise', ability: 'torrent', moves: ['tackle']},
]],
default: [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'levitate', moves: ['roost']},
{species: 'Venusaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['recover']},
{species: 'Latias', ability: 'blaze', moves: ['roost']},
]],
};
let battle;
describe('Decisions', function () {
afterEach(function () {
battle.destroy();
});
describe('Generic', function () {
it('should wait for players to send their decisions and run them as soon as they are all received', function (done) {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [{species: "Mew", ability: 'synchronize', moves: ['recover']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Rhydon", ability: 'prankster', moves: ['sketch']}]);
setTimeout(() => {
battle.choose('p2', 'move 1');
}, 20);
setTimeout(() => {
battle.choose('p1', 'move 1');
assert.strictEqual(battle.turn, 2);
done();
}, 40);
});
});
describe('Move requests', function () {
it('should allow specifying moves', function () {
const MOVES = [['growl', 'tackle'], ['growl', 'scratch']];
battle = common.createBattle([
[{species: "Bulbasaur", ability: 'Overgrow', moves: MOVES[0]}],
[{species: "Charmander", ability: 'Blaze', moves: MOVES[1]}],
]);
const activeMons = battle.sides.map(side => side.active[0]);
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
const beforeHP = activeMons.map(pokemon => pokemon.hp);
const beforeAtk = activeMons.map(pokemon => pokemon.boosts.atk);
battle.p1.chooseMove(i + 1);
battle.p2.chooseMove(j + 1);
assert.strictEqual(activeMons[0].lastMove, MOVES[0][i]);
assert.strictEqual(activeMons[1].lastMove, MOVES[1][j]);
if (i >= 1) { // p1 used a damaging move
assert.atMost(activeMons[1].hp, beforeHP[1] - 1);
assert.statStage(activeMons[1], beforeAtk[1]);
} else {
assert.strictEqual(activeMons[1].hp, beforeHP[1]);
assert.statStage(activeMons[1], beforeAtk[1] - 1);
}
if (j >= 1) { // p2 used a damaging move
assert.atMost(activeMons[0].hp, beforeHP[0] - 1);
assert.statStage(activeMons[0], beforeAtk[0]);
} else {
assert.strictEqual(activeMons[0].hp, beforeHP[0]);
assert.statStage(activeMons[0], beforeAtk[0] - 1);
}
}
}
});
it('should allow specifying move targets', function () {
battle = common.createBattle({gameType: 'doubles'}, [
[{species: "Gastrodon", ability: 'stickyhold', moves: ['gastroacid']}, {species: "Venusaur", ability: 'thickfat', moves: ['leechseed']}],
[{species: "Tyranitar", ability: 'unnerve', moves: ['knockoff']}, {species: "Zapdos", ability: 'pressure', moves: ['thunderwave']}],
]);
const p2active = battle.p2.active;
battle.p1.chooseMove(1, 1).chooseMove(1, 2);
battle.p2.chooseMove(1, -2).chooseMove(1, -1);
assert.strictEqual(battle.turn, 2);
assert(p2active[0].volatiles['gastroacid']);
assert(p2active[1].volatiles['leechseed']);
assert.false.holdsItem(p2active[1]);
assert.strictEqual(p2active[0].status, 'par');
});
it('should disallow specifying move targets for targetless moves (randomNormal)', function () {
battle = common.createBattle({gameType: 'doubles'}, [
[{species: "Dragonite", ability: 'multiscale', moves: ['outrage']}, {species: "Blastoise", ability: 'torrent', moves: ['rest']}],
[{species: "Tyranitar", ability: 'unnerve', moves: ['dragondance']}, {species: "Zapdos", ability: 'pressure', moves: ['roost']}],
]);
battle.p1.chooseMove('outrage', 1);
battle.p1.chooseMove('rest');
battle.p2.chooseMove('dragondance');
battle.p2.chooseMove('roost');
assert.notStrictEqual(battle.turn, 2);
});
it('should disallow specifying move targets for targetless moves (scripted)', function () {
battle = common.createBattle({gameType: 'doubles'}, [
[{species: "Dragonite", ability: 'multiscale', moves: ['counter']}, {species: "Blastoise", ability: 'torrent', moves: ['rest']}],
[{species: "Tyranitar", ability: 'unnerve', moves: ['bodyslam']}, {species: "Zapdos", ability: 'pressure', moves: ['drillpeck']}],
]);
battle.p1.chooseMove('counter', 2);
battle.p1.chooseMove('rest');
battle.p2.chooseMove('bodyslam', 1);
battle.p2.chooseMove('drillpeck', 1);
assert.notStrictEqual(battle.turn, 2);
});
it('should disallow specifying move targets for targetless moves (self)', function () {
battle = common.createBattle({gameType: 'doubles'}, [
[{species: "Dragonite", ability: 'multiscale', moves: ['roost']}, {species: "Blastoise", ability: 'torrent', moves: ['rest']}],
[{species: "Tyranitar", ability: 'unnerve', moves: ['dragondance']}, {species: "Zapdos", ability: 'pressure', moves: ['roost']}],
]);
battle.p1.chooseMove('roost', -2);
battle.p1.chooseMove('rest');
battle.p2.chooseMove('dragondance');
battle.p2.chooseMove('roost');
assert.notStrictEqual(battle.turn, 2);
});
it('should allow specifying switch targets', function () {
battle = common.createBattle([[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Charmeleon', ability: 'blaze', moves: ['scratch']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]]);
battle.p1.chooseSwitch(2);
battle.p2.chooseSwitch(3);
assert.species(battle.p1.active[0], 'Ivysaur');
assert.species(battle.p2.active[0], 'Charizard');
battle.p1.chooseSwitch(3);
battle.p2.chooseSwitch(3);
assert.species(battle.p1.active[0], 'Venusaur');
assert.species(battle.p2.active[0], 'Charmander');
battle.p1.chooseSwitch(2);
battle.p2.chooseSwitch(2);
assert.species(battle.p1.active[0], 'Bulbasaur');
assert.species(battle.p2.active[0], 'Charmeleon');
});
it('should allow shifting the Pokémon on the left to the center', function () {
battle = common.createBattle({gameType: 'triples'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['harden']},
{species: "Geodude", ability: 'sturdy', moves: ['defensecurl']},
{species: "Gastly", ability: 'levitate', moves: ['spite']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
{species: "Golem", ability: 'sturdy', moves: ['defensecurl']},
]);
p1.chooseMove(1).chooseMove(1).chooseShift();
p2.chooseMove(1).chooseMove(1).chooseShift();
['Pineco', 'Gastly', 'Geodude'].forEach((species, index) => assert.species(battle.p1.active[index], species));
['Skarmory', 'Golem', 'Aggron'].forEach((species, index) => assert.species(battle.p2.active[index], species));
});
it('should allow shifting the Pokémon on the right to the center', function () {
battle = common.createBattle({gameType: 'triples'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['harden']},
{species: "Geodude", ability: 'sturdy', moves: ['defensecurl']},
{species: "Gastly", ability: 'levitate', moves: ['spite']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
{species: "Golem", ability: 'sturdy', moves: ['defensecurl']},
]);
p1.chooseShift();
p2.chooseShift();
battle.commitDecisions();
['Geodude', 'Pineco', 'Gastly'].forEach((species, index) => assert.species(battle.p1.active[index], species));
['Aggron', 'Skarmory', 'Golem'].forEach((species, index) => assert.species(battle.p2.active[index], species));
});
it('should force Struggle usage on move attempt for no valid moves', function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [{species: "Mew", ability: 'synchronize', moves: ['recover']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Rhydon", ability: 'prankster', moves: ['sketch']}]);
// First turn
battle.choose('p1', 'move 1');
battle.choose('p2', 'move 1');
// Second turn
battle.choose('p1', 'move recover');
battle.choose('p2', 'move sketch');
// Implementation-dependent paths
if (battle.turn === 3) {
assert.strictEqual(battle.p2.active[0].lastMove, 'struggle');
} else {
battle.choose('p2', 'move 1');
assert.strictEqual(battle.turn, 3);
assert.strictEqual(battle.p2.active[0].lastMove, 'struggle');
}
});
it('should not force Struggle usage on move attempt for valid moves', function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [{species: "Mew", ability: 'synchronize', moves: ['recover']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Rhydon", ability: 'prankster', moves: ['struggle', 'surf']}]);
battle.p1.chooseMove(1);
battle.p2.chooseMove(2);
assert.strictEqual(battle.turn, 2);
assert.notStrictEqual(battle.p2.active[0].lastMove, 'struggle');
});
it('should not force Struggle usage on move attempt when choosing a disabled move', function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [{species: "Mew", item: 'assaultvest', ability: 'synchronize', moves: ['recover', 'icebeam']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Rhydon", item: '', ability: 'prankster', moves: ['struggle', 'surf']}]);
const failingAttacker = battle.p1.active[0];
battle.p2.chooseMove(2);
battle.p1.chooseMove(1);
assert.strictEqual(battle.turn, 1);
assert.notStrictEqual(failingAttacker.lastMove, 'struggle');
battle.p1.chooseMove('recover');
assert.strictEqual(battle.turn, 1);
assert.notStrictEqual(failingAttacker.lastMove, 'struggle');
});
it('should send meaningful feedback to players if they try to use a disabled move', function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [{species: "Skarmory", ability: 'sturdy', moves: ['spikes', 'roost']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Smeargle", ability: 'owntempo', moves: ['imprison', 'spikes']}]);
battle.commitDecisions();
const buffer = [];
battle.send = (type, data) => {
if (type === 'sideupdate') buffer.push(Array.isArray(data) ? data.join('\n') : data);
};
battle.p1.chooseMove(1);
assert(buffer.length >= 1);
assert(buffer.some(message => {
return message.startsWith('p1\n') && /\bcant\b/.test(message) && (/\|0\b/.test(message) || /\|p1a\b/.test(message));
}));
});
it('should send meaningful feedback to players if they try to switch a trapped Pokémon out', function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [
{species: "Scizor", ability: 'swarm', moves: ['bulletpunch']},
{species: "Azumarill", ability: 'sapsipper', moves: ['aquajet']},
]);
battle.join('p2', 'Guest 2', 1, [{species: "Gothitelle", ability: 'shadowtag', moves: ['calmmind']}]);
const buffer = [];
battle.send = (type, data) => {
if (type === 'sideupdate') buffer.push(Array.isArray(data) ? data.join('\n') : data);
};
battle.p1.chooseSwitch(2);
assert(buffer.length >= 1);
assert(buffer.some(message => {
return message.startsWith('p1\n') && /\btrapped\b/.test(message) && (/\|0\b/.test(message) || /\|p1a\b/.test(message));
}));
});
});
describe('Switch requests', function () {
it('should allow specifying switch targets', function () {
battle = common.createBattle([[
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Latias', ability: 'blaze', moves: ['lunardance']},
{species: 'Charmeleon', ability: 'blaze', moves: ['scratch']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]]);
battle.commitDecisions();
battle.p1.chooseSwitch(2);
battle.p2.chooseSwitch(3);
assert.species(battle.p1.active[0], 'Ivysaur');
assert.species(battle.p2.active[0], 'Charizard');
});
it('should allow passing when there are not enough available switch-ins', function () {
battle = common.createBattle({gameType: 'doubles'}, [[
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['lunardance']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Latias', ability: 'blaze', moves: ['lunardance']},
{species: 'Charmeleon', ability: 'blaze', moves: ['lunardance']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]]);
battle.commitDecisions();
battle.sides.forEach(side => side.active.forEach(pokemon => assert.fainted(pokemon)));
battle.p1.choosePass();
battle.p1.chooseSwitch(3);
battle.p2.chooseSwitch(3);
battle.p2.choosePass();
['Latias', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
['Charizard', 'Charmeleon'].forEach((species, index) => assert.species(battle.p2.active[index], species));
assert.fainted(battle.p1.active[0]);
assert.fainted(battle.p2.active[1]);
});
it('should allow passing when there are not enough available switch-ins even if an active Pokémon is not fainted', function () {
battle = common.createBattle({gameType: 'triples'}, [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'blaze', moves: ['lunardance']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]]);
battle.commitDecisions();
assert.sets(() => battle.turn, battle.turn + 1, () => {
battle.p1.choosePass();
battle.p1.choosePass();
battle.p1.chooseSwitch(4);
battle.p2.choosePass();
battle.p2.chooseSwitch(4);
battle.p2.choosePass();
}, "The turn should be resolved");
['Bulbasaur', 'Clefable', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
['Charmander', 'Charizard', 'Latias'].forEach((species, index) => assert.species(battle.p2.active[index], species));
});
it('should disallow passing when there are enough available switch-ins', function () {
battle = common.createBattle({gameType: 'doubles'}, [[
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Bulbasaur', ability: 'overgrow', moves: ['lunardance']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Latias', ability: 'blaze', moves: ['lunardance']},
{species: 'Charmander', ability: 'blaze', moves: ['lunardance']},
{species: 'Charmeleon', ability: 'blaze', moves: ['scratch']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]]);
battle.commitDecisions();
battle.sides.forEach(side => side.active.forEach(pokemon => assert.fainted(pokemon)));
assert.constant(() => battle.turn, () => {
battle.p1.choosePass();
battle.p1.chooseSwitch(3);
battle.p2.chooseSwitch(3);
battle.p2.choosePass();
});
battle.sides.forEach(side => side.active.forEach(pokemon => assert.fainted(pokemon)));
});
});
describe('Team Preview requests', function () {
it('should allow specifying the team order', function () {
const TEAMS = [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'blaze', moves: ['lunardance']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]];
for (let i = 0; i < 10; i++) {
const teamOrder = [BASE_TEAM_ORDER, BASE_TEAM_ORDER].map(teamOrder => Dex.shuffle(teamOrder.slice(0, 4)));
battle = common.createBattle({preview: true}, TEAMS);
battle.p1.chooseTeam(teamOrder[0].join(''));
battle.p2.chooseTeam(teamOrder[1].join(''));
battle.p1.pokemon.forEach((pokemon, index) => assert.species(pokemon, TEAMS[0][teamOrder[0][index] - 1].species));
battle.p2.pokemon.forEach((pokemon, index) => assert.species(pokemon, TEAMS[1][teamOrder[1][index] - 1].species));
if (i < 9) battle.destroy();
}
});
it('should autocomplete a single-slot decision in Singles for no Illusion', function () {
// Backwards-compatibility with the client. It should be useful for 3rd party bots/clients (Android?)
for (let i = 0; i < 5; i++) {
battle = common.createBattle({preview: true}, SINGLES_TEAMS.full);
const teamOrder = Dex.shuffle(BASE_TEAM_ORDER.slice()).slice(0, 1);
const fullTeamOrder = teamOrder.concat(BASE_TEAM_ORDER.filter(elem => !teamOrder.includes(elem)));
battle.choose('p1', 'team ' + teamOrder.join(''));
battle.p2.chooseDefault();
fullTeamOrder.forEach((oSlot, index) => assert.species(battle.p1.pokemon[index], SINGLES_TEAMS.full[0][oSlot - 1].species));
if (i < 4) battle.destroy();
}
});
it('should allow specifying the team order in a slot-per-slot basis in Singles with Illusion', function () {
for (let i = 0; i < 5; i++) {
battle = common.createBattle({preview: true}, SINGLES_TEAMS.illusion);
const teamOrder = Dex.shuffle(BASE_TEAM_ORDER.slice());
battle.choose('p1', 'team ' + teamOrder.join(''));
battle.p2.chooseDefault();
teamOrder.forEach((oSlot, index) => assert.species(battle.p1.pokemon[index], SINGLES_TEAMS.illusion[0][oSlot - 1].species));
if (i < 4) battle.destroy();
}
});
it('should allow specifying the team order in a slot-per-slot basis in Doubles', function () {
for (let i = 0; i < 5; i++) {
battle = common.createBattle({preview: true, gameType: 'doubles'}, DOUBLES_TEAMS.full);
const teamOrder = Dex.shuffle(BASE_TEAM_ORDER.slice());
battle.choose('p1', 'team ' + teamOrder.join(''));
battle.p2.chooseDefault();
teamOrder.forEach((oSlot, index) => assert.species(battle.p1.pokemon[index], DOUBLES_TEAMS.full[0][oSlot - 1].species));
if (i < 4) battle.destroy();
}
});
it('should allow specifying the team order in a slot-per-slot basis in Triples', function () {
for (let i = 0; i < 5; i++) {
battle = common.createBattle({preview: true, gameType: 'triples'}, TRIPLES_TEAMS.full);
const teamOrder = Dex.shuffle(BASE_TEAM_ORDER.slice());
battle.choose('p1', 'team ' + teamOrder.join(''));
battle.p2.chooseDefault();
teamOrder.forEach((oSlot, index) => assert.species(battle.p1.pokemon[index], TRIPLES_TEAMS.full[0][oSlot - 1].species));
if (i < 4) battle.destroy();
}
});
it('should autocomplete multi-slot decisions', function () {
for (let i = 0; i < 5; i++) {
battle = common.createBattle({preview: true}, SINGLES_TEAMS.full);
const teamOrder = Dex.shuffle(BASE_TEAM_ORDER.slice()).slice(0, 2);
const fullTeamOrder = teamOrder.concat(BASE_TEAM_ORDER.filter(elem => !teamOrder.includes(elem)));
battle.choose('p1', 'team ' + teamOrder.slice(0, 2).join(''));
battle.p2.chooseDefault();
fullTeamOrder.forEach((oSlot, index) => assert.species(battle.p1.pokemon[index], SINGLES_TEAMS.full[0][oSlot - 1].species));
if (i < 4) battle.destroy();
}
});
});
describe('Logging', function () {
it('should privately log the ID of chosen moves', function () {
battle = common.createBattle([
[{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}],
[{species: "Charmander", ability: 'blaze', moves: ['scratch', 'growl']}],
]);
battle.p1.chooseMove(1);
battle.p2.chooseMove('growl');
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|move tackle|', '|choice||move growl', '|choice|move tackle|move growl'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log the target of targetted chosen moves', function () {
battle = common.createBattle({gameType: 'doubles'}, [[
{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle']},
{species: "Ivysaur", ability: 'overgrow', moves: ['tackle']},
], [
{species: "Charmander", ability: 'blaze', moves: ['scratch']},
{species: "Charizard", ability: 'blaze', moves: ['scratch']},
]]);
battle.p1.chooseMove(1, 1).chooseMove(1, 2);
battle.p2.chooseMove(1, 2).chooseMove(1, 1);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|move tackle 1, move tackle 2|', '|choice||move scratch 2, move scratch 1', '|choice|move tackle 1, move tackle 2|move scratch 2, move scratch 1'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should not log the target of targetless chosen moves', function () {
battle = common.createBattle({gameType: 'doubles'}, [[
{species: "Bulbasaur", ability: 'overgrow', moves: ['magnitude']},
{species: "Ivysaur", ability: 'overgrow', moves: ['rockslide']},
], [
{species: "Charmander", ability: 'blaze', moves: ['scratch']},
{species: "Charizard", ability: 'blaze', moves: ['scratch']},
]]);
battle.p1.chooseMove(1).chooseMove(1);
battle.p2.chooseMove(1, 1).chooseMove(1, 1);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|move magnitude, move rockslide|', '|choice||move scratch 1, move scratch 1', '|choice|move magnitude, move rockslide|move scratch 1, move scratch 1'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log the user intention of mega evolving', function () {
battle = common.createBattle([
[{species: "Venusaur", item: 'venusaurite', ability: 'overgrow', moves: ['tackle']}],
[{species: "Blastoise", item: 'blastoisinite', ability: 'blaze', moves: ['tailwhip']}],
]);
battle.p1.chooseMove(1, null, true);
battle.p2.chooseMove(1, null, true);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|move tackle mega|', '|choice||move tailwhip mega', '|choice|move tackle mega|move tailwhip mega'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log the user intention of mega evolving for Mega-X and Mega-Y', function () {
battle = common.createBattle([
[{species: "Charizard", item: 'charizarditex', ability: 'blaze', moves: ['scratch']}],
[{species: "Charizard", item: 'charizarditey', ability: 'blaze', moves: ['ember']}],
]);
battle.p1.chooseMove(1, null, true);
battle.p2.chooseMove(1, null, true);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|move scratch mega|', '|choice||move ember mega', '|choice|move scratch mega|move ember mega'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log the target of switches', function () {
battle = common.createBattle([[
{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle']},
{species: "Ivysaur", ability: 'overgrow', moves: ['tackle']},
{species: "Venusaur", ability: 'overgrow', moves: ['tackle']},
], [
{species: "Charmander", ability: 'blaze', moves: ['scratch']},
{species: "Charmeleon", ability: 'blaze', moves: ['scratch']},
{species: "Charizard", ability: 'blaze', moves: ['scratch']},
]]);
battle.p1.chooseSwitch(2);
battle.p2.chooseSwitch(3);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|switch 2|', '|choice||switch 3', '|choice|switch 2|switch 3'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log the team order chosen', function () {
battle = common.createBattle({preview: true}, [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Latias', ability: 'blaze', moves: ['lunardance']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]]);
battle.p1.chooseTeam('1342');
battle.p2.chooseTeam('1234');
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|team 1, team 3, team 4, team 2|', '|choice||team 1, team 2, team 3, team 4', '|choice|team 1, team 3, team 4, team 2|team 1, team 2, team 3, team 4'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log shifting decisions for the Pokémon on the left', function () {
battle = common.createBattle({gameType: 'triples'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['harden']},
{species: "Geodude", ability: 'sturdy', moves: ['defensecurl']},
{species: "Gastly", ability: 'levitate', moves: ['haze']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
{species: "Golem", ability: 'sturdy', moves: ['defensecurl']},
]);
p1.chooseShift().chooseMove(1).chooseMove(1);
p2.chooseMove(1).chooseMove(1).chooseMove(1);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|shift, move defensecurl, move haze|', '|choice||move roost, move irondefense, move defensecurl', '|choice|shift, move defensecurl, move haze|move roost, move irondefense, move defensecurl'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
it('should privately log shifting decisions for the Pokémon on the right', function () {
battle = common.createBattle({gameType: 'triples'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['harden']},
{species: "Geodude", ability: 'sturdy', moves: ['defensecurl']},
{species: "Gastly", ability: 'levitate', moves: ['haze']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
{species: "Golem", ability: 'sturdy', moves: ['defensecurl']},
]);
p1.chooseMove(1).chooseMove(1).chooseShift();
p2.chooseMove(1).chooseMove(1).chooseMove(1);
const logText = battle.log.join('\n');
const logs = ['|choice||', '|choice|move harden, move defensecurl, shift|', '|choice||move roost, move irondefense, move defensecurl', '|choice|move harden, move defensecurl, shift|move roost, move irondefense, move defensecurl'];
const subString = '|split\n' + logs.join('\n');
assert(logText.includes(subString), `${logText} does not include ${subString}`);
});
});
});
describe('Decision extensions', function () {
describe('Undo', function () {
const MODES = ['revoke', 'override'];
for (const mode of MODES) {
it(`should disallow to ${mode} decisions after every player has sent an unrevoked decision`, function () {
battle = common.createBattle({cancel: true});
battle.join('p1', 'Guest 1', 1, [{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Charmander", ability: 'blaze', moves: ['tackle', 'growl']}]);
battle.choose('p1', 'move tackle');
battle.choose('p2', 'move growl');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move growl');
assert.strictEqual(battle.turn, 2);
assert.strictEqual(battle.p1.active[0].lastMove, 'tackle');
assert.strictEqual(battle.p2.active[0].lastMove, 'growl');
});
it(`should support to ${mode} move decisions`, function () {
battle = common.createBattle({cancel: true});
battle.join('p1', 'Guest 1', 1, [{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Charmander", ability: 'blaze', moves: ['tackle', 'growl']}]);
battle.choose('p1', 'move tackle');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move growl');
battle.choose('p2', 'move growl');
assert.strictEqual(battle.turn, 2);
assert.strictEqual(battle.p1.active[0].lastMove, 'growl');
});
it(`should disallow to ${mode} move decisions for maybe-disabled Pokémon`, function () {
battle = common.createBattle({cancel: true});
battle.join('p1', 'Guest 1', 1, [{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl', 'synthesis']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Charmander", ability: 'blaze', moves: ['scratch']}]);
const target = battle.p1.active[0];
target.maybeDisabled = true;
battle.makeRequest();
battle.choose('p1', 'move tackle');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move growl');
battle.choose('p2', 'move scratch');
assert.strictEqual(target.lastMove, 'tackle');
});
it(`should disallow to ${mode} move decisions by default`, function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}]);
battle.join('p2', 'Guest 2', 1, [{species: "Charmander", ability: 'blaze', moves: ['tackle', 'growl']}]);
battle.choose('p1', 'move tackle');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move growl');
battle.choose('p2', 'move growl');
assert.strictEqual(battle.turn, 2);
assert.strictEqual(battle.p1.active[0].lastMove, 'tackle');
assert.strictEqual(battle.p2.active[0].lastMove, 'growl');
});
it(`should support to ${mode} switch decisions on move requests`, function () {
const TEAMS = [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Venusaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
]];
battle = common.createBattle({cancel: true}, TEAMS);
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1');
battle.choose('p2', 'move 1');
['Bulbasaur', 'Ivysaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
assert.strictEqual(battle.p1.active[0].lastMove, 'synthesis');
battle.destroy();
battle = common.createBattle({cancel: true}, TEAMS);
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3');
battle.choose('p2', 'move 1');
['Venusaur', 'Ivysaur', 'Bulbasaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
});
it(`should disallow to ${mode} switch decisions on move requests for maybe-trapped Pokémon`, function () {
const TEAMS = [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
]];
battle = common.createBattle({cancel: true}, TEAMS);
battle.p1.active[0].maybeTrapped = true;
battle.makeRequest();
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1');
battle.choose('p2', 'move 1');
assert.species(battle.p1.active[0], 'Ivysaur');
});
it(`should disallow to ${mode} switch decisions on move requests for unconfirmed trapping-immune Pokémon that would otherwise be trapped`, function () {
battle = common.createBattle({cancel: true, pokemon: true, legality: true}, [
[
{species: 'Starmie', ability: 'naturalcure', moves: ['reflecttype', 'recover']},
{species: 'Mandibuzz', ability: 'overcoat', moves: ['knockoff']},
], [
{species: 'Zoroark', ability: 'illusion', moves: ['shadowball, focusblast']},
{species: 'Gothitelle', ability: 'competitive', moves: ['calmmind']},
{species: 'Gengar', ability: 'levitate', moves: ['shadowball, focusblast']},
],
]);
const target = battle.p1.active[0];
battle.p1.chooseMove('reflecttype');
battle.p2.chooseSwitch(3);
// The real Gengar comes in, but p1 only sees a Gengar being switched by another Gengar, implying Illusion.
// For a naive client, Starmie turns into Ghost/Poison, and it will be correct.
assert.strictEqual(target.getTypes().join('/'), 'Ghost/Poison');
// Trapping with Gengar-Mega is a guaranteed trapping, so we are going to get a very meta
// Competitive Gothitelle into the battle field (Frisk is revealed on switch-in).
battle.p1.chooseMove('recover');
battle.p2.chooseSwitch(2);
assert(target.maybeTrapped, `${target} should be flagged as maybe trapped`);
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move recover');
battle.choose('p2', 'move 1');
assert.species(battle.p1.active[0], 'Mandibuzz');
});
it(`should disallow to ${mode} switch decisions on move requests by default`, function () {
const TEAMS = [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Venusaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
]];
battle = common.createBattle(TEAMS);
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1');
battle.choose('p2', 'move 1');
['Ivysaur', 'Bulbasaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
battle.destroy();
battle = common.createBattle(TEAMS);
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3');
battle.choose('p2', 'move 1');
['Ivysaur', 'Bulbasaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
});
it(`should support to ${mode} shift decisions on move requests`, function () {
const TEAMS = [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Venusaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Aggron', ability: 'sturdy', moves: ['irondefense']},
{species: 'Aggron', ability: 'sturdy', moves: ['irondefense']},
{species: 'Aggron', ability: 'sturdy', moves: ['irondefense']},
]];
battle = common.createBattle({gameType: 'triples', cancel: true}, TEAMS);
battle.choose('p1', 'shift, move 1, move 1');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1, move 1, move 1');
battle.choose('p2', 'move 1, move 1, move 1');
['Bulbasaur', 'Ivysaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
assert.strictEqual(battle.p1.active[0].lastMove, 'synthesis');
battle.destroy();
battle = common.createBattle({gameType: 'triples', cancel: true}, TEAMS);
battle.choose('p1', 'move 1, move 1, shift');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1, move 1, move 1');
battle.choose('p2', 'move 1, move 1, move 1');
['Bulbasaur', 'Ivysaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
assert.strictEqual(battle.p1.active[2].lastMove, 'synthesis');
});
it(`should disallow to ${mode} shift decisions by default`, function () {
const TEAMS = [[
{species: 'Bulbasaur', ability: 'overgrow', moves: ['synthesis']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['growth']},
{species: 'Venusaur', ability: 'overgrow', moves: ['synthesis']},
], [
{species: 'Aggron', ability: 'sturdy', moves: ['irondefense']},
{species: 'Aggron', ability: 'sturdy', moves: ['irondefense']},
{species: 'Aggron', ability: 'sturdy', moves: ['irondefense']},
]];
battle = common.createBattle({gameType: 'triples'}, TEAMS);
battle.choose('p1', 'shift, move 1, move 1');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1, move 1, move 1');
battle.choose('p2', 'move 1, move 1, move 1');
['Ivysaur', 'Bulbasaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
assert.strictEqual(battle.p1.active[0].lastMove, 'growth');
battle.destroy();
battle = common.createBattle({gameType: 'triples'}, TEAMS);
battle.choose('p1', 'move 1, move 1, shift');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'move 1, move 1, move 1');
battle.choose('p2', 'move 1, move 1, move 1');
['Bulbasaur', 'Venusaur', 'Ivysaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
assert.strictEqual(battle.p1.active[2].lastMove, 'growth');
});
it(`should support to ${mode} switch decisions on double switch requests`, function () {
battle = common.createBattle({cancel: true});
battle.join('p1', 'Guest 1', 1, [
{species: "Deoxys-Attack", ability: 'pressure', moves: ['explosion']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle']},
{species: "Chikorita", ability: 'overgrow', moves: ['tackle']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Caterpie", ability: 'shielddust', moves: ['tackle']},
{species: "Charmander", ability: 'blaze', moves: ['tackle']},
]);
battle.commitDecisions();
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3');
battle.choose('p2', 'switch 2');
assert.strictEqual(battle.turn, 2);
assert.strictEqual(battle.p1.active[0].template.species, 'Chikorita');
assert.strictEqual(battle.p2.active[0].template.species, 'Charmander');
});
it(`should support to ${mode} pass decisions on double switch requests`, function () {
battle = common.createBattle({cancel: true, gameType: 'doubles'});
battle.join('p1', 'Guest 1', 1, [
{species: "Deoxys-Attack", ability: 'pressure', moves: ['explosion']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle']},
{species: "Chikorita", ability: 'overgrow', moves: ['tackle']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Caterpie", ability: 'shielddust', moves: ['tackle']},
{species: "Charmander", ability: 'blaze', moves: ['tackle']},
{species: "Cyndaquil", ability: 'blaze', moves: ['tackle']},
]);
battle.commitDecisions();
battle.choose('p1', 'pass, switch 3');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3, pass');
battle.choose('p2', 'pass, switch 3');
['Chikorita', 'Bulbasaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
});
it(`should disallow to ${mode} switch decisions on switch requests by default`, function () {
const TEAMS = [[
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Clefable', ability: 'unaware', moves: ['healingwish']},
{species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Charmeleon', ability: 'blaze', moves: ['scratch']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]];
battle = common.createBattle({gameType: 'doubles', cancel: true}, TEAMS);
battle.commitDecisions();
battle.choose('p1', 'switch 3, switch 4');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 4, switch 3');
['Ivysaur', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
});
it(`should disallow to ${mode} pass decisions on switch requests by default`, function () {
const TEAMS = [[
{species: 'Latias', ability: 'levitate', moves: ['lunardance']},
{species: 'Clefable', ability: 'overgrow', moves: ['healingwish']},
{species: 'Venusaur', ability: 'overgrow', moves: ['tackle']},
], [
{species: 'Charmander', ability: 'blaze', moves: ['scratch']},
{species: 'Charmeleon', ability: 'blaze', moves: ['scratch']},
{species: 'Charizard', ability: 'blaze', moves: ['scratch']},
]];
battle = common.createBattle({gameType: 'doubles'}, TEAMS);
battle.commitDecisions();
battle.choose('p1', 'pass, switch 3');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3, pass');
['Latias', 'Venusaur'].forEach((species, index) => assert.species(battle.p1.active[index], species));
});
it(`should disallow to ${mode} switch decisions on double switch requests by default`, function () {
battle = common.createBattle();
battle.join('p1', 'Guest 1', 1, [
{species: "Deoxys-Attack", ability: 'pressure', moves: ['explosion']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle']},
{species: "Chikorita", ability: 'overgrow', moves: ['tackle']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Caterpie", ability: 'shielddust', moves: ['tackle']},
{species: "Charmander", ability: 'blaze', moves: ['tackle']},
]);
battle.commitDecisions();
battle.choose('p1', 'switch 2');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3');
battle.choose('p2', 'switch 2');
assert.species(battle.p1.active[0], 'Bulbasaur');
assert.species(battle.p2.active[0], 'Charmander');
});
it(`should disallow to ${mode} pass decisions on double switch requests by default`, function () {
battle = common.createBattle({gameType: 'doubles'});
battle.join('p1', 'Guest 1', 1, [
{species: "Deoxys-Attack", ability: 'pressure', moves: ['explosion']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle']},
{species: "Chikorita", ability: 'overgrow', moves: ['tackle']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Caterpie", ability: 'shielddust', moves: ['tackle']},
{species: "Charmander", ability: 'blaze', moves: ['tackle']},
{species: "Cyndaquil", ability: 'blaze', moves: ['tackle']},
]);
battle.commitDecisions();
battle.choose('p1', 'pass, switch 3');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'switch 3, pass');
battle.choose('p2', 'pass, switch 3');
['Deoxys-Attack', 'Chikorita'].forEach((species, index) => assert.species(battle.p1.active[index], species));
});
it(`should support to ${mode} team order decision on team preview requests`, function () {
battle = common.createBattle({preview: true, cancel: true}, [
[{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']}, {species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']}],
[{species: 'Charmander', ability: 'blaze', moves: ['scratch']}, {species: 'Charmeleon', ability: 'blaze', moves: ['scratch']}],
]);
battle.choose('p1', 'team 12');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'team 21');
battle.choose('p2', 'team 12');
['Ivysaur', 'Bulbasaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
battle.destroy();
battle = common.createBattle({preview: true, cancel: true}, [
[{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']}, {species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']}],
[{species: 'Charmander', ability: 'blaze', moves: ['scratch']}, {species: 'Charmeleon', ability: 'blaze', moves: ['scratch']}],
]);
battle.choose('p1', 'team 21');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'team 12');
battle.choose('p2', 'team 12');
['Bulbasaur', 'Ivysaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
});
it(`should disallow to ${mode} team order decision on team preview requests by default`, function () {
battle = common.createBattle({preview: true}, [
[{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']}, {species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']}],
[{species: 'Charmander', ability: 'blaze', moves: ['scratch']}, {species: 'Charmeleon', ability: 'blaze', moves: ['scratch']}],
]);
battle.choose('p1', 'team 12');
if (mode === 'revoke') battle.undoChoice('p1');
battle.choose('p1', 'team 21');
battle.choose('p2', 'team 12');
['Bulbasaur', 'Ivysaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species));
});
}
});
});
describe('Decision internals', function () {
afterEach(function () {
battle.destroy();
});
it('should allow input of move commands in a per Pokémon basis', function () {
battle = common.createBattle({gameType: 'doubles'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Mew", ability: 'synchronize', moves: ['recover']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['growl', 'synthesis']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Pupitar", ability: 'shedskin', moves: ['surf']}, // faster than Bulbasaur
{species: "Arceus", ability: 'multitype', moves: ['calmmind']},
]);
assert.strictEqual(battle.turn, 1);
p1.chooseMove(1).chooseMove(1);
p2.chooseMove(1).chooseMove(1);
assert.strictEqual(battle.turn, 2);
assert.statStage(p2.active[0], 'atk', -1);
p1.chooseMove('recover').chooseMove('synthesis');
p2.chooseMove('surf').chooseMove('calmmind');
assert.strictEqual(battle.turn, 3);
assert.fullHP(p1.active[1]);
p1.chooseMove('recover').chooseMove('2');
p2.chooseMove('1').chooseMove('calmmind');
assert.strictEqual(battle.turn, 4);
assert.fullHP(p1.active[1]);
});
it('should allow input of switch commands in a per Pokémon basis', function () {
battle = common.createBattle({gameType: 'doubles'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Mew", ability: 'synchronize', moves: ['selfdestruct']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['selfdestruct']},
{species: "Koffing", ability: 'levitate', moves: ['smog']},
{species: "Ekans", ability: 'shedskin', moves: ['leer']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Deoxys-Defense", ability: 'pressure', moves: ['recover']},
{species: "Arceus", ability: 'multitype', moves: ['recover']},
]);
assert.strictEqual(battle.turn, 1);
p1.chooseMove('selfdestruct').chooseMove('selfdestruct');
p2.chooseMove('recover').chooseMove('recover');
assert.fainted(p1.active[0]);
assert.fainted(p1.active[1]);
p1.chooseSwitch(4).chooseSwitch(3);
assert.strictEqual(battle.turn, 2);
assert.strictEqual(p1.active[0].name, 'Ekans');
assert.strictEqual(p1.active[1].name, 'Koffing');
});
it('should allow input of move and switch commands in a per Pokémon basis', function () {
battle = common.createBattle({gameType: 'doubles'});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Mew", ability: 'synchronize', moves: ['recover']},
{species: "Bulbasaur", ability: 'overgrow', moves: ['growl', 'synthesis']},
{species: "Koffing", ability: 'levitate', moves: ['smog']},
{species: "Ekans", ability: 'shedskin', moves: ['leer']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Deoxys-Defense", ability: 'pressure', moves: ['recover']},
{species: "Arceus", ability: 'multitype', moves: ['recover']},
]);
assert.strictEqual(battle.turn, 1);
p1.chooseMove(1).chooseSwitch(4);
assert(!p2.chooseSwitch(3));
p2.chooseMove(1).chooseMove(1);
assert.strictEqual(battle.turn, 2);
assert.strictEqual(p1.active[0].name, 'Mew');
assert.strictEqual(p1.active[1].name, 'Ekans');
p1.chooseSwitch(4).chooseMove(1);
assert(!p2.chooseSwitch(3));
p2.chooseMove(1).chooseMove(1);
assert.strictEqual(battle.turn, 3);
assert.strictEqual(p1.active[0].name, 'Bulbasaur');
assert.strictEqual(p1.active[1].name, 'Ekans');
});
it('should empty the decisions list when undoing a move', function () {
battle = common.createBattle({gameType: 'doubles', cancel: true});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Koffing", ability: 'levitate', moves: ['smog']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
]);
p1.chooseMove(1);
assert(p1.choice.actions.length > 0);
battle.undoChoice('p1');
assert.false(p1.choice.actions.length > 0);
p1.chooseDefault();
p2.chooseDefault();
assert.fainted(p1.active[0]);
assert.fainted(p1.active[1]);
});
it('should empty the decisions list when undoing a switch', function () {
battle = common.createBattle({gameType: 'doubles', cancel: true});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Koffing", ability: 'levitate', moves: ['smog']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
]);
battle.commitDecisions();
p1.chooseSwitch(3);
assert(p1.choice.actions.length > 0);
battle.undoChoice('p1');
assert.false(p1.choice.actions.length > 0);
p1.choosePass().chooseSwitch(3);
assert.fainted(p1.active[0]);
assert.species(p1.active[1], 'Koffing');
});
it('should empty the decisions list when undoing a pass', function () {
battle = common.createBattle({gameType: 'doubles', cancel: true});
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Koffing", ability: 'levitate', moves: ['smog']},
]);
battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
]);
battle.commitDecisions();
p1.choosePass();
assert(p1.choice.actions.length > 0);
battle.undoChoice('p1');
assert.false(p1.choice.actions.length > 0);
p1.choosePass().chooseSwitch(3);
assert.fainted(p1.active[0]);
assert.species(p1.active[1], 'Koffing');
});
it('should empty the decisions list when undoing a shift', function () {
battle = common.createBattle({gameType: 'triples', cancel: true});
battle.supportCancel = true;
const p1 = battle.join('p1', 'Guest 1', 1, [
{species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Geodude", ability: 'sturdy', moves: ['selfdestruct']},
{species: "Gastly", ability: 'levitate', moves: ['lick']},
]);
const p2 = battle.join('p2', 'Guest 2', 1, [
{species: "Skarmory", ability: 'sturdy', moves: ['roost']},
{species: "Aggron", ability: 'sturdy', moves: ['irondefense']},
{species: "Golem", ability: 'sturdy', moves: ['defensecurl']},
]);
p1.chooseShift();
assert(p1.choice.actions.length > 0);
battle.undoChoice('p1');
assert.false(p1.choice.actions.length > 0);
p1.chooseMove(1).chooseMove(1).chooseShift();
p2.chooseDefault();
assert.fainted(p1.active[0]);
assert.fainted(p1.active[2]);
assert.species(p1.active[1], 'Gastly');
assert.false.fainted(p1.active[1]);
});
});