pokemon-showdown/test/sim/abilities/emergencyexit.js
Guangcong Luo 78439b4a02
Update to ESLint 9 (#10926)
ESLint has a whole new config format, so I figure it's a good time to
make the config system saner.

- First, we no longer have separate eslint-no-types configs. Lint
  performance shouldn't be enough of a problem to justify the
  relevant maintenance complexity.

- Second, our base config should work out-of-the-box now. `npx eslint`
  will work as expected, without any CLI flags. You should still use
  `npm run lint` which adds the `--cached` flag for performance.

- Third, whatever updates I did fixed style linting, which apparently
  has been bugged for quite some time, considering all the obvious
  mixed-tabs-and-spaces issues I found in the upgrade.

Also here are some changes to our style rules. In particular:

- Curly brackets (for objects etc) now have spaces inside them. Sorry
  for the huge change. ESLint doesn't support our old style, and most
  projects use Prettier style, so we might as well match them in this way.
  See https://github.com/eslint-stylistic/eslint-stylistic/issues/415

- String + number concatenation is no longer allowed. We now
  consistently use template strings for this.
2025-02-25 20:03:46 -08:00

396 lines
17 KiB
JavaScript

'use strict';
const assert = require('./../../assert');
const common = require('./../../common');
let battle;
const EMPTY_IVS = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
describe(`Emergency Exit`, () => {
afterEach(() => battle.destroy());
it(`should request switch-out if damaged below 50% HP`, () => {
battle = common.createBattle([[
{ species: "Golisopod", ability: 'emergencyexit', moves: ['sleeptalk'], ivs: EMPTY_IVS },
{ species: "Clefable", ability: 'unaware', moves: ['sleeptalk'] },
], [
{ species: "Raticate", ability: 'noguard', moves: ['superfang'] },
]]);
const eePokemon = battle.p1.active[0];
const foePokemon = battle.p2.active[0];
battle.makeChoices('auto', 'move superfang');
assert.equal(foePokemon.hp, foePokemon.maxhp);
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'switch');
});
it(`should request switch-out at the end of a multi-hit move`, () => {
battle = common.createBattle([
[{ species: "Cinccino", ability: 'skilllink', moves: ['bulletseed'] }],
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['sleeptalk'] }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
]);
battle.makeChoices('move bulletseed', 'move sleeptalk');
battle.makeChoices('move bulletseed', 'move sleeptalk');
assert.equal(battle.requestState, 'switch');
});
it(`should request switch-out if brought below half HP by residual damage`, () => {
battle = common.createBattle([[
{ species: 'Crobat', moves: ['toxic'] },
], [
{ species: 'Mew', ability: 'emergencyexit', moves: ['splash'] },
{ species: 'Shaymin', moves: ['splash'] },
]]);
battle.p2.active[0].hp = Math.floor(battle.p2.active[0].maxhp / 2 + 2);
battle.makeChoices();
assert.equal(battle.requestState, 'switch');
});
it(`should request switch-out if brought below half HP by Photon Geyser`, () => {
battle = common.createBattle([[
{ species: "Mew", moves: ['photongeyser'] },
], [
{ species: "Charmeleon", ability: 'emergencyexit', moves: ['splash'] },
{ species: "Shaymin", moves: ['splash'] },
]]);
battle.makeChoices();
assert.equal(battle.requestState, 'switch');
});
it(`should not request switch-out if attacked and healed by berry`, () => {
battle = common.createBattle([[
{ species: "Golisopod", ability: 'emergencyexit', moves: ['sleeptalk'], item: 'sitrusberry', ivs: EMPTY_IVS },
{ species: "Clefable", ability: 'unaware', moves: ['metronome'] },
], [
{ species: "Raticate", ability: 'guts', moves: ['superfang'] },
]]);
battle.makeChoices();
assert.equal(battle.requestState, 'move');
});
it(`should not request switch-out if fainted`, () => {
battle = common.createBattle({ gameType: 'doubles' }, [[
{ species: 'Vikavolt', item: 'choicespecs', moves: ['thunderbolt'] },
{ species: 'Pyukumuku', moves: ['batonpass'] },
{ species: 'Magikarp', moves: ['splash'] },
], [
{ species: 'Golisopod', ability: 'emergencyexit', moves: ['sleeptalk'] },
{ species: 'Mew', moves: ['sleeptalk'] },
{ species: 'Ditto', moves: ['transform'] },
]]);
battle.makeChoices('move thunderbolt 1, move batonpass', 'move sleeptalk, move sleeptalk');
assert(!battle.p2.activeRequest.forceSwitch);
});
it(`should request switch-out before end-of-turn fainted Pokemon`, () => {
battle = common.createBattle([[
{ species: "Golisopod", item: 'blacksludge', ability: 'emergencyexit', moves: ['payback'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "Swoobat", ability: 'noguard', moves: ['superfang'] },
{ species: "Stufful", moves: ['sleeptalk'] },
]]);
battle.makeChoices();
assert(battle.p1.activeRequest.forceSwitch);
assert.fainted(battle.p2.active[0]);
assert(!battle.p2.activeRequest.forceSwitch);
});
it(`should request switch-out after taking hazard damage`, () => {
battle = common.createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['uturn', 'sleeptalk'] }, { species: "Magikarp", ability: 'swiftswim', moves: ['splash'] }],
[{ species: "Arceus-Flying", ability: 'ironbarbs', moves: ['stealthrock', 'spikes', 'dragonascent'] }],
]);
battle.makeChoices('move uturn', 'move stealthrock');
battle.makeChoices('switch 2', '');
battle.makeChoices('move splash', 'move spikes');
battle.makeChoices('move splash', 'move spikes');
battle.makeChoices('move splash', 'move spikes');
battle.makeChoices('switch 2', 'move dragonascent');
assert(battle.p1.active[0].hp);
assert.equal(battle.requestState, 'switch');
});
it('should request switch-out after taking Life Orb recoil', () => {
battle = common.createBattle([[
{ species: "Golisopod", item: "lifeorb", ability: 'emergencyexit', moves: ['peck'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "stufful", ability: 'compoundeyes', moves: ['superfang'] },
]]);
battle.makeChoices();
assert.equal(battle.requestState, 'switch');
});
it('should request switch-out after taking Mind Blown self-damage', () => {
battle = common.createBattle([[
{ species: "Golisopod", ability: 'emergencyexit', moves: ['mindblown'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "chansey", moves: ['sleeptalk'] },
]]);
battle.makeChoices();
assert.equal(battle.requestState, 'switch');
});
it.skip('should request switch-out after taking recoil and dragging in an opponent', () => {
battle = common.createBattle([[
{ species: "Golisopod", ability: 'emergencyexit', moves: ['dragontail'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "Sharpedo", item: 'rockyhelmet', ability: 'noguard', moves: ['superfang'] },
{ species: "Stufful", moves: ['sleeptalk'] },
]]);
battle.makeChoices();
const log = battle.getDebugLog();
const dragIndex = log.lastIndexOf('|drag|p2a: Stufful|Stufful, M|281/281');
const abilityIndex = log.lastIndexOf('|-activate|p1a: Golisopod|ability: Emergency Exit');
assert(dragIndex < abilityIndex, 'Stufful should be dragged in before Emergency Exit activates');
assert.equal(battle.requestState, 'switch');
});
it(`should not request switch-out after taking entry hazard damage and getting healed by berry`, () => {
battle = common.createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['uturn', 'sleeptalk'], item: 'sitrusberry' }, { species: "Magikarp", ability: 'swiftswim', moves: ['splash'] }],
[{ species: "Ferrothorn", ability: 'ironbarbs', moves: ['stealthrock', 'spikes', 'protect'] }],
]);
battle.makeChoices('move uturn', 'move stealthrock');
battle.makeChoices('switch 2', '');
battle.makeChoices('move splash', 'move spikes');
battle.makeChoices('move splash', 'move spikes');
battle.makeChoices('move splash', 'move spikes');
battle.makeChoices('switch 2', 'move protect');
assert.equal(battle.requestState, 'move');
});
it(`should not request switch-out after taking poison damage and getting healed by berry`, () => {
battle = common.createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['substitute', 'sleeptalk'], item: 'sitrusberry' }, { species: "Magikarp", moves: ['splash'] }],
[{ species: "Gengar", moves: ['toxic', 'nightshade', 'protect'] }],
]);
battle.makeChoices('move substitute', 'move toxic');
battle.makeChoices('move sleeptalk', 'move nightshade');
battle.makeChoices('move sleeptalk', 'move protect');
assert.equal(battle.requestState, 'move');
});
it(`should not request switch-out on usage of Substitute`, () => {
battle = common.createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['substitute'], ivs: EMPTY_IVS }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
[{ species: "Deoxys-Attack", ability: 'pressure', item: 'laggingtail', moves: ['thunderbolt'] }],
]);
const eePokemon = battle.p1.active[0];
battle.makeChoices('move substitute', 'move thunderbolt');
assert.false.atMost(eePokemon.hp, eePokemon.maxhp / 2);
battle.makeChoices('move substitute', 'move thunderbolt');
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'move');
});
it(`should prevent Volt Switch after switches`, () => {
battle = common.createBattle([[
{ species: 'Golisopod', ability: 'emergencyexit', moves: ['sleeptalk'], ivs: EMPTY_IVS },
{ species: 'Clefable', moves: ['sleeptalk'] },
], [
{ species: 'Eelektrik', moves: ['voltswitch'] },
{ species: 'Clefable', moves: ['sleeptalk'] },
]]);
const eePokemon = battle.p1.active[0];
battle.makeChoices('move sleeptalk', 'move voltswitch');
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'switch');
battle.makeChoices('switch 2', '');
assert.species(battle.p1.active[0], 'Clefable');
assert.species(battle.p2.active[0], 'Eelektrik');
});
it(`should not prevent Red Card's activation`, () => {
battle = common.createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', item: 'redcard', moves: ['sleeptalk'], ivs: EMPTY_IVS }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
[{ species: "Raticate", ability: 'guts', moves: ['superfang'] }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
]);
const eePokemon = battle.p1.active[0];
battle.makeChoices('move sleeptalk', 'move superfang');
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.false.holdsItem(eePokemon);
assert.equal(battle.requestState, 'switch');
battle.makeChoices('auto', '');
assert.species(battle.p1.active[0], 'Clefable');
assert.species(battle.p2.active[0], 'Clefable');
});
it(`should not prevent Eject Button's activation`, () => {
battle = common.createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', item: 'ejectbutton', moves: ['sleeptalk'], ivs: EMPTY_IVS }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
[{ species: "Raticate", ability: 'guts', moves: ['superfang'] }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
]);
const eePokemon = battle.p1.active[0];
battle.makeChoices('auto', 'auto');
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.false.holdsItem(eePokemon);
assert.equal(battle.requestState, 'switch');
battle.makeChoices('auto', '');
assert.species(battle.p1.active[0], 'Clefable');
});
it(`should be suppressed by Sheer Force`, () => {
battle = common.createBattle([[
{ species: "Golisopod", ability: 'emergencyexit', moves: ['sleeptalk'], ivs: EMPTY_IVS },
{ species: "Clefable", moves: ['sleeptalk'] },
], [
{ species: "Nidoking", ability: 'sheerforce', moves: ['thunderbolt'] },
]]);
const eePokemon = battle.p1.active[0];
battle.makeChoices();
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'move');
});
it('should not request switchout if its HP is already below 50%', () => {
battle = common.createBattle([[
{ species: "Golisopod", evs: { hp: 4 }, ability: 'emergencyexit', moves: ['sleeptalk', 'tackle'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "stufful", ability: 'compoundeyes', moves: ['superfang', 'sleeptalk'] },
]]);
battle.makeChoices();
battle.makeChoices('switch 2');
// Switch Goliosopod back in
battle.makeChoices('switch 2', 'auto');
battle.makeChoices('move tackle', 'move sleeptalk');
assert.equal(battle.requestState, 'move');
});
it('should request switchout if its HP was restored to above 50% and brought down again', () => {
battle = common.createBattle([[
{ species: "Golisopod", evs: { hp: 4 }, ability: 'emergencyexit', moves: ['sleeptalk'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "stufful", ability: 'compoundeyes', moves: ['superfang', 'healpulse'] },
]]);
battle.makeChoices();
battle.makeChoices('switch 2');
// Switch Goliosopod back in and heal it before switching it back out again
battle.makeChoices('switch 2', 'move healpulse');
battle.makeChoices('auto', 'move superfang');
assert.equal(battle.requestState, 'switch');
});
it('should not request switchout if its HP is already below 50% and an effect heals it', () => {
battle = common.createBattle([[
{ species: "Golisopod", level: 65, item: 'figyberry', ability: 'emergencyexit', moves: ['sleeptalk'] },
{ species: "Wynaut", moves: ['sleeptalk'] },
], [
{ species: "ursaring", ability: 'sheerforce', moves: ['falseswipe', 'crunch'] },
]]);
battle.makeChoices('auto', 'move crunch');
battle.makeChoices();
assert.equal(battle.requestState, 'move');
});
it('should request switchout if its HP drops to below 50% while dynamaxed', () => {
battle = common.gen(8).createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['closecombat'], ivs: EMPTY_IVS, level: 30 }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
[{ species: "Gengar", ability: 'cursedbody', moves: ['nightshade'] }],
]);
const eePokemon = battle.p1.active[0];
battle.makeChoices('move maxknuckle dynamax', 'move nightshade');
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'switch');
});
it('should not request switchout if its HP is below 50% when its dynamax ends', () => {
battle = common.gen(8).createBattle([
[{ species: "Golisopod", ability: 'emergencyexit', moves: ['drillrun'], ivs: EMPTY_IVS }, { species: "Clefable", ability: 'Unaware', moves: ['metronome'] }],
[{ species: "Landorus", ability: 'sheerforce', moves: ['sludgewave'] }],
]);
const eePokemon = battle.p1.active[0];
battle.makeChoices('move maxquake dynamax', 'move sludgewave');
battle.makeChoices();
battle.makeChoices();
assert.false.fainted(eePokemon);
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'move');
});
it.skip(`should request switchout between hazards`, () => {
battle = common.createBattle([[
{ species: 'wynaut', moves: ['sleeptalk', 'uturn'] },
{ species: 'volcarona', ability: 'emergencyexit', evs: { hp: 4 }, moves: ['sleeptalk'] },
], [
{ species: 'landorus', moves: ['stealthrock', 'spikes'] },
]]);
battle.makeChoices();
battle.makeChoices('move uturn', 'move spikes');
battle.makeChoices('switch 2');
const volcarona = battle.p1.active[0];
assert.equal(volcarona.hp, Math.floor(volcarona.maxhp / 2), 'Emergency Exit should trigger before Spikes damage.');
assert.equal(battle.requestState, 'switch');
});
it.skip(`should request switchout between residual damage`, () => {
battle = common.gen(8).createBattle({ gameType: 'doubles' }, [[
{ species: 'Coalossal', level: 1, item: 'Eject Button', moves: ['rockthrow', 'sleeptalk'], gigantamax: true },
{ species: 'Wynaut', level: 1, moves: ['sleeptalk', 'grasspledge'] },
{ species: 'Wynaut', level: 1, moves: ['sleeptalk', 'firepledge'] },
], [
{ species: 'Blissey', moves: ['sleeptalk'] },
{ species: 'Amoonguss', ability: 'noguard', moves: ['sleeptalk', 'superfang'] },
{ species: 'Golisopod', ability: 'emergencyexit', moves: ['sleeptalk'] },
]]);
// Set up Volcalith and Sea of Fire
battle.makeChoices('move rockthrow 1 dynamax, move grasspledge -1', 'auto');
battle.makeChoices('switch 3');
battle.makeChoices('move firepledge 1, move grasspledge 1', 'auto');
// Halve Golisopod's HP
battle.makeChoices('move sleeptalk, move sleeptalk', 'switch 3, move superfang -1');
const golisopod = battle.p2.active[0];
let maxHP = golisopod.maxhp;
let expectedHP = maxHP - Math.floor(maxHP / 2) - Math.floor(maxHP / 6);
assert.equal(golisopod.hp, expectedHP, `Golisopod should have only taken Volcalith damage`);
const amoonguss = battle.p2.active[1];
maxHP = amoonguss.maxhp;
expectedHP = maxHP - (3 * Math.floor(maxHP / 6)) - (2 * Math.floor(maxHP / 8)); // 3 turns of Volcalith, 2 turns of Sea of Fire
assert.equal(amoonguss.hp, expectedHP, `Amoonguss should have taken damage before Golisopod can be replaced.`);
assert.equal(battle.requestState, 'switch');
});
it(`should request a switchout after taking regular recoil damage`, () => {
battle = common.createBattle([[
{ species: 'Golisopod', ability: 'Emergency Exit', moves: ['flareblitz'] },
{ species: 'Wynaut', moves: ['sleeptalk'] },
], [
{ species: 'Chansey', moves: ['sleeptalk'] },
]]);
const eePokemon = battle.p1.active[0];
battle.makeChoices();
assert.atMost(eePokemon.hp, eePokemon.maxhp / 2);
assert.equal(battle.requestState, 'switch');
});
it(`should request a switchout after taking struggle recoil damage`, () => {
battle = common.createBattle([[
{ species: 'Golisopod', item: 'Assault Vest', ability: 'Emergency Exit', moves: ['protect'] },
{ species: 'Wynaut', moves: ['sleeptalk'] },
], [
{ species: 'Wynaut', moves: ['sleeptalk'] },
]]);
battle.makeChoices();
battle.makeChoices();
assert.equal(battle.requestState, 'switch');
});
});