mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Correctly implement Cud Chew and Slow Start (#11376)
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run
Co-authored-by: Karthik Bandagonda <32044378+Karthik99999@users.noreply.github.com>
This commit is contained in:
parent
8584c3cf50
commit
3d4f34e784
|
|
@ -740,33 +740,29 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
|
|||
num: 238,
|
||||
},
|
||||
cudchew: {
|
||||
onEatItem(item, pokemon) {
|
||||
if (item.isBerry && pokemon.addVolatile('cudchew')) {
|
||||
pokemon.volatiles['cudchew'].berry = item;
|
||||
onEatItem(item, pokemon, source, effect) {
|
||||
if (item.isBerry && (!effect || !['bugbite', 'pluck'].includes(effect.id))) {
|
||||
this.effectState.berry = item;
|
||||
this.effectState.counter = 2;
|
||||
// This is needed in case the berry was eaten during residuals, preventing the timer from decreasing this turn
|
||||
if (!this.queue.peek()) this.effectState.counter--;
|
||||
}
|
||||
},
|
||||
onEnd(pokemon) {
|
||||
delete pokemon.volatiles['cudchew'];
|
||||
},
|
||||
condition: {
|
||||
noCopy: true,
|
||||
duration: 2,
|
||||
onRestart() {
|
||||
this.effectState.duration = 2;
|
||||
},
|
||||
onResidualOrder: 28,
|
||||
onResidualSubOrder: 2,
|
||||
onEnd(pokemon) {
|
||||
if (pokemon.hp) {
|
||||
const item = this.effectState.berry;
|
||||
this.add('-activate', pokemon, 'ability: Cud Chew');
|
||||
this.add('-enditem', pokemon, item.name, '[eat]');
|
||||
if (this.singleEvent('Eat', item, null, pokemon, null, null)) {
|
||||
this.runEvent('EatItem', pokemon, null, null, item);
|
||||
}
|
||||
if (item.onEat) pokemon.ateBerry = true;
|
||||
onResidualOrder: 28,
|
||||
onResidualSubOrder: 2,
|
||||
onResidual(pokemon) {
|
||||
if (!this.effectState.berry || !pokemon.hp) return;
|
||||
if (--this.effectState.counter <= 0) {
|
||||
const item = this.effectState.berry;
|
||||
this.add('-activate', pokemon, 'ability: Cud Chew');
|
||||
this.add('-enditem', pokemon, item.name, '[eat]');
|
||||
if (this.singleEvent('Eat', item, null, pokemon, null, null)) {
|
||||
this.runEvent('EatItem', pokemon, null, null, item);
|
||||
}
|
||||
},
|
||||
if (item.onEat) pokemon.ateBerry = true;
|
||||
delete this.effectState.berry;
|
||||
delete this.effectState.counter;
|
||||
}
|
||||
},
|
||||
flags: {},
|
||||
name: "Cud Chew",
|
||||
|
|
@ -4233,34 +4229,30 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = {
|
|||
},
|
||||
slowstart: {
|
||||
onStart(pokemon) {
|
||||
pokemon.addVolatile('slowstart');
|
||||
this.add('-start', pokemon, 'ability: Slow Start');
|
||||
this.effectState.counter = 5;
|
||||
},
|
||||
onEnd(pokemon) {
|
||||
delete pokemon.volatiles['slowstart'];
|
||||
this.add('-end', pokemon, 'Slow Start', '[silent]');
|
||||
},
|
||||
condition: {
|
||||
duration: 5,
|
||||
onResidualOrder: 28,
|
||||
onResidualSubOrder: 2,
|
||||
onStart(target) {
|
||||
this.add('-start', target, 'ability: Slow Start');
|
||||
},
|
||||
onResidual(pokemon) {
|
||||
if (!pokemon.activeTurns) {
|
||||
this.effectState.duration! += 1;
|
||||
onResidualOrder: 28,
|
||||
onResidualSubOrder: 2,
|
||||
onResidual(pokemon) {
|
||||
if (pokemon.activeTurns && this.effectState.counter) {
|
||||
this.effectState.counter--;
|
||||
if (!this.effectState.counter) {
|
||||
this.add('-end', pokemon, 'Slow Start');
|
||||
delete this.effectState.counter;
|
||||
}
|
||||
},
|
||||
onModifyAtkPriority: 5,
|
||||
onModifyAtk(atk, pokemon) {
|
||||
}
|
||||
},
|
||||
onModifyAtkPriority: 5,
|
||||
onModifyAtk(atk, pokemon) {
|
||||
if (this.effectState.counter) {
|
||||
return this.chainModify(0.5);
|
||||
},
|
||||
onModifySpe(spe, pokemon) {
|
||||
}
|
||||
},
|
||||
onModifySpe(spe, pokemon) {
|
||||
if (this.effectState.counter) {
|
||||
return this.chainModify(0.5);
|
||||
},
|
||||
onEnd(target) {
|
||||
this.add('-end', target, 'Slow Start');
|
||||
},
|
||||
}
|
||||
},
|
||||
flags: {},
|
||||
name: "Slow Start",
|
||||
|
|
|
|||
|
|
@ -550,8 +550,8 @@ export const Moves: import('../../../sim/dex-moves').ModdedMoveDataTable = {
|
|||
this.debug(`BP: ${move.basePower}`);
|
||||
if (item.isBerry) {
|
||||
move.onHit = function (foe) {
|
||||
if (this.singleEvent('Eat', item, null, foe, null, null)) {
|
||||
this.runEvent('EatItem', foe, null, null, item);
|
||||
if (this.singleEvent('Eat', item, source.itemState, foe, source, move)) {
|
||||
this.runEvent('EatItem', foe, source, move, item);
|
||||
if (item.id === 'leppaberry') foe.staleness = 'external';
|
||||
}
|
||||
if (item.onEat) foe.ateBerry = true;
|
||||
|
|
|
|||
|
|
@ -87,33 +87,18 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa
|
|||
},
|
||||
slowstart: {
|
||||
inherit: true,
|
||||
condition: {
|
||||
duration: 5,
|
||||
onResidualOrder: 28,
|
||||
onResidualSubOrder: 2,
|
||||
onStart(target) {
|
||||
this.add('-start', target, 'ability: Slow Start');
|
||||
},
|
||||
onModifyAtkPriority: 5,
|
||||
onModifyAtk(atk, pokemon, target, move) {
|
||||
// This is because the game checks the move's category in data, rather than what it is currently, unlike e.g. Huge Power
|
||||
if (this.dex.moves.get(move.id).category === 'Physical') {
|
||||
return this.chainModify(0.5);
|
||||
}
|
||||
},
|
||||
onModifySpAPriority: 5,
|
||||
onModifySpA(spa, pokemon, target, move) {
|
||||
// Ordinary Z-moves like Breakneck Blitz will halve the user's Special Attack as well
|
||||
if (this.dex.moves.get(move.id).category === 'Physical') {
|
||||
return this.chainModify(0.5);
|
||||
}
|
||||
},
|
||||
onModifySpe(spe, pokemon) {
|
||||
onModifyAtk(atk, pokemon, target, move) {
|
||||
// This is because the game checks the move's category in data, rather than what it is currently, unlike e.g. Huge Power
|
||||
if (this.effectState.counter && this.dex.moves.get(move.id).category === 'Physical') {
|
||||
return this.chainModify(0.5);
|
||||
},
|
||||
onEnd(target) {
|
||||
this.add('-end', target, 'Slow Start');
|
||||
},
|
||||
}
|
||||
},
|
||||
onModifySpAPriority: 5,
|
||||
onModifySpA(spa, pokemon, target, move) {
|
||||
// Ordinary Z-moves like Breakneck Blitz will halve the user's Special Attack as well
|
||||
if (this.effectState.counter && this.dex.moves.get(move.id).category === 'Physical') {
|
||||
return this.chainModify(0.5);
|
||||
}
|
||||
},
|
||||
},
|
||||
soundproof: {
|
||||
|
|
|
|||
|
|
@ -1982,12 +1982,12 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
|
|||
pp: 20,
|
||||
priority: 0,
|
||||
flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
|
||||
onHit(target, source) {
|
||||
onHit(target, source, move) {
|
||||
const item = target.getItem();
|
||||
if (source.hp && item.isBerry && target.takeItem(source)) {
|
||||
this.add('-enditem', target, item.name, '[from] stealeat', '[move] Bug Bite', `[of] ${source}`);
|
||||
if (this.singleEvent('Eat', item, null, source, null, null)) {
|
||||
this.runEvent('EatItem', source, null, null, item);
|
||||
if (this.singleEvent('Eat', item, target.itemState, source, source, move)) {
|
||||
this.runEvent('EatItem', source, source, move, item);
|
||||
if (item.id === 'leppaberry') target.staleness = 'external';
|
||||
}
|
||||
if (item.onEat) source.ateBerry = true;
|
||||
|
|
@ -5950,9 +5950,12 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
|
|||
move.basePower = item.fling.basePower;
|
||||
this.debug(`BP: ${move.basePower}`);
|
||||
if (item.isBerry) {
|
||||
if (source.hasAbility('cudchew')) {
|
||||
this.singleEvent('EatItem', source.getAbility(), source.abilityState, source, source, move, item);
|
||||
}
|
||||
move.onHit = function (foe) {
|
||||
if (this.singleEvent('Eat', item, null, foe, null, null)) {
|
||||
this.runEvent('EatItem', foe, null, null, item);
|
||||
if (this.singleEvent('Eat', item, source.itemState, foe, source, move)) {
|
||||
this.runEvent('EatItem', foe, source, move, item);
|
||||
if (item.id === 'leppaberry') foe.staleness = 'external';
|
||||
}
|
||||
if (item.onEat) foe.ateBerry = true;
|
||||
|
|
@ -13918,12 +13921,12 @@ export const Moves: import('../sim/dex-moves').MoveDataTable = {
|
|||
pp: 20,
|
||||
priority: 0,
|
||||
flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
|
||||
onHit(target, source) {
|
||||
onHit(target, source, move) {
|
||||
const item = target.getItem();
|
||||
if (source.hp && item.isBerry && target.takeItem(source)) {
|
||||
this.add('-enditem', target, item.name, '[from] stealeat', '[move] Pluck', `[of] ${source}`);
|
||||
if (this.singleEvent('Eat', item, null, source, null, null)) {
|
||||
this.runEvent('EatItem', source, null, null, item);
|
||||
if (this.singleEvent('Eat', item, target.itemState, source, source, move)) {
|
||||
this.runEvent('EatItem', source, source, move, item);
|
||||
if (item.id === 'leppaberry') target.staleness = 'external';
|
||||
}
|
||||
if (item.onEat) source.ateBerry = true;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export interface EventMethods {
|
|||
onDeductPP?: (this: Battle, target: Pokemon, source: Pokemon) => number | void;
|
||||
onDisableMove?: (this: Battle, pokemon: Pokemon) => void;
|
||||
onDragOut?: (this: Battle, pokemon: Pokemon, source?: Pokemon, move?: ActiveMove) => void;
|
||||
onEatItem?: (this: Battle, item: Item, pokemon: Pokemon) => void;
|
||||
onEatItem?: (this: Battle, item: Item, pokemon: Pokemon, source?: Pokemon, effect?: Effect) => void;
|
||||
onEffectiveness?: MoveEventMethods['onEffectiveness'];
|
||||
onEntryHazard?: (this: Battle, pokemon: Pokemon) => void;
|
||||
onFaint?: CommonHandlers['VoidEffect'];
|
||||
|
|
|
|||
|
|
@ -1751,7 +1751,7 @@ export class Pokemon {
|
|||
this.battle.add('-enditem', this, item, '[eat]');
|
||||
|
||||
this.battle.singleEvent('Eat', item, this.itemState, this, source, sourceEffect);
|
||||
this.battle.runEvent('EatItem', this, null, null, item);
|
||||
this.battle.runEvent('EatItem', this, source, sourceEffect, item);
|
||||
|
||||
if (RESTORATIVE_BERRIES.has(item.id)) {
|
||||
switch (this.pendingStaleness) {
|
||||
|
|
|
|||
91
test/sim/abilities/cudchew.js
Normal file
91
test/sim/abilities/cudchew.js
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('./../../assert');
|
||||
const common = require('./../../common');
|
||||
|
||||
let battle;
|
||||
|
||||
describe('Cud Chew', () => {
|
||||
afterEach(() => {
|
||||
battle.destroy();
|
||||
});
|
||||
|
||||
it(`should re-activate a berry, eaten in the previous turn`, () => {
|
||||
battle = common.createBattle([[
|
||||
{ species: 'tauros', ability: 'cudchew', item: 'lumberry', moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: 'toxicroak', moves: ['toxic'] },
|
||||
]]);
|
||||
const tauros = battle.p1.active[0];
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, '');
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, '');
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, 'tox');
|
||||
});
|
||||
|
||||
it(`should re-activate a berry flung in the previous turn, for both the attacker and the target`, () => {
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: 'tauros', ability: 'cudchew', item: 'lumberry', moves: ['fling', 'sleeptalk'] },
|
||||
{ species: 'foongus', moves: ['toxic', 'sleeptalk'] },
|
||||
], [
|
||||
{ species: 'farigiraf', ability: 'cudchew', moves: ['sleeptalk'] },
|
||||
{ species: 'grimer', moves: ['toxic', 'sleeptalk'] },
|
||||
]]);
|
||||
const tauros = battle.p1.active[0];
|
||||
const farigiraf = battle.p2.active[0];
|
||||
battle.makeChoices('move fling 1, move toxic 1', 'move sleeptalk, move toxic 1');
|
||||
assert.equal(tauros.status, 'tox');
|
||||
assert.equal(farigiraf.status, 'tox');
|
||||
battle.makeChoices('move sleeptalk, move sleeptalk', 'move sleeptalk, move sleeptalk');
|
||||
assert.equal(tauros.status, '');
|
||||
assert.equal(farigiraf.status, '');
|
||||
});
|
||||
|
||||
it(`should not re-activate a berry eaten by Bug Bite, for either the attacker or the target`, () => {
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: 'tauros', ability: 'cudchew', moves: ['bugbite', 'sleeptalk'] },
|
||||
{ species: 'foongus', moves: ['toxic', 'sleeptalk'] },
|
||||
], [
|
||||
{ species: 'farigiraf', ability: 'cudchew', item: 'lumberry', moves: ['sleeptalk'] },
|
||||
{ species: 'grimer', moves: ['toxic', 'sleeptalk'] },
|
||||
]]);
|
||||
const tauros = battle.p1.active[0];
|
||||
const farigiraf = battle.p2.active[0];
|
||||
battle.makeChoices('move bugbite 1, move toxic 1', 'move sleeptalk, move toxic 1');
|
||||
assert.equal(tauros.status, 'tox');
|
||||
assert.equal(farigiraf.status, 'tox');
|
||||
battle.makeChoices('move sleeptalk, move sleeptalk', 'move sleeptalk, move sleeptalk');
|
||||
assert.equal(tauros.status, 'tox');
|
||||
assert.equal(farigiraf.status, 'tox');
|
||||
});
|
||||
|
||||
it(`should not be prevented by Unnerve`, () => {
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: 'tauros', ability: 'cudchew', item: 'lumberry', moves: ['sleeptalk'] },
|
||||
{ species: 'wynaut', moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: 'toxicroak', moves: ['toxic'] },
|
||||
{ species: 'magikarp', moves: ['sleeptalk'] },
|
||||
{ species: 'mewtwo', ability: 'unnerve', moves: ['sleeptalk'] },
|
||||
]]);
|
||||
const tauros = battle.p1.active[0];
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, '');
|
||||
battle.makeChoices('auto', 'move toxic 1, switch 3');
|
||||
assert.equal(tauros.status, '');
|
||||
});
|
||||
|
||||
it(`should still activate in the following turn if the berry was consumed during residuals`, () => {
|
||||
battle = common.createBattle([[
|
||||
{ species: 'tauros', ability: 'cudchew', item: 'sitrusberry', moves: ['bellydrum'] },
|
||||
], [
|
||||
{ species: 'toxicroak', moves: ['toxic'] },
|
||||
]]);
|
||||
const tauros = battle.p1.active[0];
|
||||
battle.makeChoices();
|
||||
battle.makeChoices();
|
||||
assert(tauros.hp > tauros.maxhp * 3 / 4, 'Tauros should have eaten its berry twice');
|
||||
});
|
||||
});
|
||||
|
|
@ -274,6 +274,43 @@ describe('Neutralizing Gas', () => {
|
|||
assert.species(eiscue, 'Eiscue-Noice');
|
||||
});
|
||||
|
||||
it(`should delay the activation of Cud Chew`, () => {
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: 'tauros', ability: 'cudchew', item: 'lumberry', moves: ['sleeptalk'] },
|
||||
{ species: 'wynaut', moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: 'toxicroak', moves: ['toxic'] },
|
||||
{ species: 'magikarp', moves: ['sleeptalk'] },
|
||||
{ species: 'weezing', ability: 'neutralizinggas', moves: ['sleeptalk'] },
|
||||
]]);
|
||||
let tauros = battle.p1.active[0];
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, '');
|
||||
battle.makeChoices('auto', 'move toxic 1, switch 3');
|
||||
assert.equal(tauros.status, 'tox');
|
||||
battle.makeChoices('auto', 'move toxic 1, switch 3');
|
||||
assert.equal(tauros.status, '');
|
||||
|
||||
battle = common.createBattle({ gameType: 'doubles' }, [[
|
||||
{ species: 'tauros', ability: 'cudchew', item: 'lumberry', moves: ['sleeptalk'] },
|
||||
{ species: 'wynaut', moves: ['sleeptalk'] },
|
||||
], [
|
||||
{ species: 'toxicroak', moves: ['toxic'] },
|
||||
{ species: 'magikarp', moves: ['sleeptalk', 'uturn'] },
|
||||
{ species: 'weezing', ability: 'neutralizinggas', moves: ['sleeptalk'] },
|
||||
]]);
|
||||
tauros = battle.p1.active[0];
|
||||
battle.makeChoices('auto', 'move toxic 1, move uturn 2');
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, ''); // 0 turns passed before Neutralizing Gas
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, 'tox');
|
||||
battle.makeChoices('auto', 'move toxic 1, switch 3');
|
||||
assert.equal(tauros.status, 'tox'); // 1 turn has passed
|
||||
battle.makeChoices();
|
||||
assert.equal(tauros.status, '');
|
||||
});
|
||||
|
||||
it(`should not work if it was obtained via Transform`, () => {
|
||||
battle = common.createBattle([[
|
||||
{ species: 'Ditto', moves: ['transform'] },
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user