diff --git a/data/abilities.ts b/data/abilities.ts index 2bbe4b3de8..aab1bcf670 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -4793,6 +4793,16 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { }, symbiosis: { onAllyAfterUseItem(item, pokemon) { + this.effectState.pokemon = pokemon; + // during a move execution, only trigger during the AfterMove event + if (this.activeMove) return; + ((this.effect as any).onAnyAfterMove as () => void).call(this); + }, + onAnyAfterMove() { + const pokemon = this.effectState.pokemon; + if (!pokemon) return; + delete this.effectState.pokemon; + if (pokemon.switchFlag) return; const source = this.effectState.target; const myItem = source.takeItem(); @@ -4806,6 +4816,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { } this.add('-activate', source, 'ability: Symbiosis', myItem, `[of] ${pokemon}`); }, + onEnd() { + delete this.effectState.pokemon; + }, flags: {}, name: "Symbiosis", rating: 0, diff --git a/data/items.ts b/data/items.ts index 024f5bda0a..b71e282978 100644 --- a/data/items.ts +++ b/data/items.ts @@ -1690,11 +1690,9 @@ export const Items: import('../sim/dex-items').ItemDataTable = { for (const pokemon of this.getAllActive()) { if (pokemon.switchFlag === true) return; } - target.switchFlag = true; if (target.useItem()) { + target.switchFlag = true; source.switchFlag = false; - } else { - target.switchFlag = false; } } }, diff --git a/data/mods/gen6/abilities.ts b/data/mods/gen6/abilities.ts index eb65bae007..769b2754e3 100644 --- a/data/mods/gen6/abilities.ts +++ b/data/mods/gen6/abilities.ts @@ -110,6 +110,7 @@ export const Abilities: import('../../../sim/dex-abilities').ModdedAbilityDataTa } this.add('-activate', source, 'ability: Symbiosis', myItem, `[of] ${pokemon}`); }, + onAnyAfterMove: undefined, // no inherit }, weakarmor: { inherit: true, diff --git a/test/sim/abilities/symbiosis.js b/test/sim/abilities/symbiosis.js index 153df37c9e..4941ae6e52 100644 --- a/test/sim/abilities/symbiosis.js +++ b/test/sim/abilities/symbiosis.js @@ -36,7 +36,49 @@ describe('Symbiosis', () => { assert.equal(battle.p1.active[1].item, ''); }); - it('should not trigger on an ally losing their Eject Button in Generation 7 or later', () => { + it('should not consume two White Herbs in one boost', () => { + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Smeargle', item: 'whiteherb', moves: ['sleeptalk'] }, + { species: 'Oranguru', ability: 'symbiosis', item: 'whiteherb', moves: ['sleeptalk'] }, + ], [ + { species: 'Murkrow', moves: ['screech'] }, + { species: 'Smeargle', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices(); + battle.makeChoices(); + assert.equal(battle.p1.active[0].boosts.def, 0); + }); + + it('during switches, should trigger right after the item is consumed', () => { + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Smeargle', ability: 'symbiosis', item: 'lifeorb', moves: ['sleeptalk'] }, + { species: 'Roaring Moon', ability: 'protosynthesis', item: 'boosterenergy', moves: ['sleeptalk'] }, + ], [ + { species: 'Raging Bolt', ability: 'protosynthesis', item: 'boosterenergy', moves: ['sleeptalk'] }, + { species: 'Smeargle', moves: ['sleeptalk'] }, + ]]); + assert.equal(battle.p1.active[1].item, 'lifeorb'); + const log = battle.getDebugLog(); + const symbiosisIndex = log.lastIndexOf('|-activate|p1a: Smeargle|ability: Symbiosis'); + const protosynthesisIndex = log.lastIndexOf('|-activate|p1b: Roaring Moon|ability: Protosynthesis'); + assert(symbiosisIndex < protosynthesisIndex, 'Symbiosis should trigger before Protosynthesis'); + }); + + it('during moves, should trigger after the action is concluded', () => { + battle = common.createBattle({ gameType: 'doubles' }, [[ + { species: 'Smeargle', ability: 'symbiosis', item: 'lifeorb', moves: ['sleeptalk'] }, + { species: 'Venusaur', ability: 'overgrow', item: 'powerherb', moves: ['solarbeam'] }, + ], [ + { species: 'Smeargle', moves: ['sleeptalk'] }, + { species: 'Smeargle', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices(); + const venusaur = battle.p1.active[1]; + assert.equal(venusaur.item, 'lifeorb'); + assert.fullHP(venusaur); + }); + + it('should not trigger on an ally losing their Eject Button', () => { battle = common.createBattle({ gameType: 'doubles' }, [[ { species: 'oranguru', ability: 'symbiosis', item: 'leftovers', moves: ['sleeptalk'] }, { species: 'wynaut', item: 'ejectbutton', moves: ['sleeptalk'] }, @@ -51,22 +93,7 @@ describe('Symbiosis', () => { assert.equal(battle.p1.active[1].item, ''); }); - it('should trigger on an ally losing their Eject Button in Generation 6', () => { - battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ - { species: 'oranguru', ability: 'symbiosis', item: 'leftovers', moves: ['sleeptalk'] }, - { species: 'wynaut', item: 'ejectbutton', moves: ['sleeptalk'] }, - { species: 'corphish', moves: ['sleeptalk'] }, - ], [ - { species: 'wynaut', moves: ['tackle'] }, - { species: 'wynaut', moves: ['sleeptalk'] }, - ]]); - battle.makeChoices('auto', 'move tackle 2, move sleeptalk'); - - assert.equal(battle.p1.active[0].item, ''); - assert.equal(battle.p1.active[1].item, 'leftovers'); - }); - - it.skip(`should not trigger on an ally using their Eject Pack`, () => { + it(`should not trigger on an ally using their Eject Pack`, () => { battle = common.createBattle({ gameType: 'doubles' }, [[ { species: 'oranguru', ability: 'symbiosis', item: 'leftovers', moves: ['sleeptalk'] }, { species: 'wynaut', item: 'ejectpack', moves: ['superpower'] }, @@ -81,49 +108,108 @@ describe('Symbiosis', () => { assert.equal(battle.p1.active[1].item, ''); }); - // See Marty's research for many more examples: https://www.smogon.com/forums/threads/battle-mechanics-research.3489239/post-6401506 - describe.skip('Symbiosis Eject Button Glitch (Gen 6 only)', () => { - it('should cause Leftovers to restore HP 4 times', () => { + describe('[Gen 6]', () => { + it('during moves, should trigger right after the item is consumed', () => { battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ - { species: 'florges', ability: 'symbiosis', item: 'leftovers', moves: ['sleeptalk'] }, - { species: 'roggenrola', level: 50, ability: 'sturdy', item: 'ejectbutton', moves: ['sleeptalk'] }, - { species: 'corphish', moves: ['sleeptalk'] }, + { species: 'Smeargle', ability: 'symbiosis', item: 'lifeorb', moves: ['sleeptalk'] }, + { species: 'Venusaur', ability: 'overgrow', item: 'powerherb', moves: ['solarbeam'] }, ], [ - { species: 'wynaut', moves: ['sleeptalk', 'closecombat'] }, - { species: 'wynaut', moves: ['sleeptalk'] }, + { species: 'Smeargle', moves: ['sleeptalk'] }, + { species: 'Smeargle', moves: ['sleeptalk'] }, ]]); - - battle.makeChoices('auto', 'move closecombat 2, move sleeptalk'); - assert.equal(battle.p1.active[0].item, ''); - assert.equal(battle.p1.active[1].item, 'leftovers'); - battle.makeChoices('switch 3'); - battle.makeChoices('move sleeptalk, switch 3', 'auto'); - - // Close Combat brought Roggenrola down to Sturdy = 1 HP - const roggenrola = battle.p1.active[1]; - const targetHP = 1 + (Math.floor(roggenrola.maxhp / 16) * 4); - assert.equal(targetHP, roggenrola.hp); + battle.makeChoices(); + const venusaur = battle.p1.active[1]; + assert.equal(venusaur.item, 'lifeorb'); + assert.false.fullHP(venusaur); }); - it('should cause Choice items to apply 2 times', () => { + it('should not consume a second Gem in the same turn', () => { battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ - { species: 'florges', ability: 'symbiosis', item: 'choiceband', moves: ['sleeptalk'] }, - { species: 'roggenrola', evs: { atk: 8 }, item: 'ejectbutton', moves: ['smackdown'] }, + { species: 'Smeargle', ability: 'symbiosis', item: 'electricgem', moves: ['sleeptalk'] }, + { species: 'Venusaur', ability: 'overgrow', item: 'electricgem', moves: ['thunderbolt'] }, + ], [ + { species: 'Smeargle', moves: ['sleeptalk'] }, + { species: 'Smeargle', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices(); + assert.equal(battle.p1.active[0].item, ''); + assert.equal(battle.p1.active[1].item, 'electricgem'); + }); + + it('should trigger on an ally losing their Eject Button', () => { + battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ + { species: 'oranguru', ability: 'symbiosis', item: 'leftovers', moves: ['sleeptalk'] }, + { species: 'wynaut', item: 'ejectbutton', moves: ['sleeptalk'] }, { species: 'corphish', moves: ['sleeptalk'] }, ], [ - { species: 'wynaut', moves: ['sleeptalk', 'tackle'] }, - { species: 'torkoal', moves: ['sleeptalk'] }, + { species: 'wynaut', moves: ['tackle'] }, + { species: 'wynaut', moves: ['sleeptalk'] }, ]]); - battle.makeChoices('auto', 'move tackle 2, move sleeptalk'); - battle.makeChoices('switch 3'); - battle.makeChoices('move sleeptalk, switch 3', 'auto'); - battle.makeChoices('move sleeptalk, move smackdown 1', 'auto'); - // Choice Band applied twice, effectively making Roggenrola's Attack 423 - const wynaut = battle.p2.active[0]; - const damage = wynaut.maxhp - wynaut.hp; - assert.bounded(damage, [172, 204]); + assert.equal(battle.p1.active[0].item, ''); + assert.equal(battle.p1.active[1].item, 'leftovers'); + }); + + it('should trigger even if the ally fainted', () => { + battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ + { species: 'oranguru', ability: 'symbiosis', item: 'sitrusberry', moves: ['sleeptalk'] }, + { species: 'wynaut', level: 1, item: 'colburberry', moves: ['sleeptalk'] }, + ], [ + { species: 'wynaut', moves: ['darkpulse'] }, + { species: 'wynaut', moves: ['sleeptalk'] }, + ]]); + battle.makeChoices('auto', 'move darkpulse 2, move sleeptalk'); + + assert.equal(battle.p1.active[0].item, ''); + assert.equal(battle.p1.active[1].item, 'sitrusberry'); + assert.fainted(battle.p1.active[1]); + }); + + // See Marty's research for many more examples: https://www.smogon.com/forums/threads/battle-mechanics-research.3489239/post-6401506 + describe.skip('Symbiosis Eject Button Glitch', () => { + it('should cause Leftovers to restore HP 4 times', () => { + battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ + { species: 'florges', ability: 'symbiosis', item: 'leftovers', moves: ['sleeptalk'] }, + { species: 'roggenrola', level: 50, ability: 'sturdy', item: 'ejectbutton', moves: ['sleeptalk'] }, + { species: 'corphish', moves: ['sleeptalk'] }, + ], [ + { species: 'wynaut', moves: ['sleeptalk', 'closecombat'] }, + { species: 'wynaut', moves: ['sleeptalk'] }, + ]]); + + battle.makeChoices('auto', 'move closecombat 2, move sleeptalk'); + assert.equal(battle.p1.active[0].item, ''); + assert.equal(battle.p1.active[1].item, 'leftovers'); + battle.makeChoices('switch 3'); + battle.makeChoices('move sleeptalk, switch 3', 'auto'); + + // Close Combat brought Roggenrola down to Sturdy = 1 HP + const roggenrola = battle.p1.active[1]; + const targetHP = 1 + (Math.floor(roggenrola.maxhp / 16) * 4); + assert.equal(targetHP, roggenrola.hp); + }); + + it('should cause Choice items to apply 2 times', () => { + battle = common.gen(6).createBattle({ gameType: 'doubles' }, [[ + { species: 'florges', ability: 'symbiosis', item: 'choiceband', moves: ['sleeptalk'] }, + { species: 'roggenrola', evs: { atk: 8 }, item: 'ejectbutton', moves: ['smackdown'] }, + { species: 'corphish', moves: ['sleeptalk'] }, + ], [ + { species: 'wynaut', moves: ['sleeptalk', 'tackle'] }, + { species: 'torkoal', moves: ['sleeptalk'] }, + ]]); + + battle.makeChoices('auto', 'move tackle 2, move sleeptalk'); + battle.makeChoices('switch 3'); + battle.makeChoices('move sleeptalk, switch 3', 'auto'); + battle.makeChoices('move sleeptalk, move smackdown 1', 'auto'); + + // Choice Band applied twice, effectively making Roggenrola's Attack 423 + const wynaut = battle.p2.active[0]; + const damage = wynaut.maxhp - wynaut.hp; + assert.bounded(damage, [172, 204]); + }); }); }); });