From 9510bbbe65674f82005000680180a3faf05bf182 Mon Sep 17 00:00:00 2001 From: Shiva Devarajan Date: Thu, 28 Nov 2024 14:52:06 -0500 Subject: [PATCH 1/3] Fix Commander Not hitting hazards --- data/abilities.ts | 1 + test/sim/abilities/commander.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data/abilities.ts b/data/abilities.ts index 97f739368fd4..8125c59b2601 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -612,6 +612,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { commander: { onUpdate(pokemon) { if (this.gameType !== 'doubles') return; + if (!pokemon.isStarted) return; const ally = pokemon.allies()[0]; if (!ally || pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') { // Handle any edge cases diff --git a/test/sim/abilities/commander.js b/test/sim/abilities/commander.js index 9cf3b72af474..400043c3ece7 100644 --- a/test/sim/abilities/commander.js +++ b/test/sim/abilities/commander.js @@ -241,7 +241,7 @@ describe('Commander', function () { assert.false.fullHP(shuckle, `Shuckle should have taken damage from Dazzling Gleam`); }); - it.skip(`should activate after hazards run`, function () { + it(`should activate after hazards run`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'regieleki', moves: ['toxicspikes']}, {species: 'registeel', moves: ['sleeptalk']}, From f8fd67feffc1086dde748fca520a2758f652e10a Mon Sep 17 00:00:00 2001 From: Shiva Devarajan Date: Thu, 28 Nov 2024 14:53:55 -0500 Subject: [PATCH 2/3] Eject pack cannot activate if pokemon have not started --- sim/battle-actions.ts | 1 + sim/battle.ts | 8 ++++++-- test/sim/items/ejectpack.js | 24 +++++++++++++----------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 4128c3f85d54..13d4284b4aeb 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1215,6 +1215,7 @@ export class BattleActions { move: ActiveMove, moveData: ActiveMove, isSecondary?: boolean, isSelf?: boolean ) { let didAnything: number | boolean | null | undefined = damage.reduce(this.combineResults); + targets.sort((a, b) => a && b ? b.speed - a.speed : 0); for (const [i, target] of targets.entries()) { if (target === false) continue; let hitResult; diff --git a/sim/battle.ts b/sim/battle.ts index 343a79353269..fb73c9d81e8f 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -2725,10 +2725,14 @@ export class Battle { } } + const all_started = this.sides.every( + side => side.active.every(pokemon => !pokemon || pokemon.isStarted || pokemon.fainted) + ); + const switches = this.sides.map( - side => side.active.some(pokemon => pokemon && !!pokemon.switchFlag) + side => all_started && + side.active.some(pokemon => pokemon && !!pokemon.switchFlag) ); - for (let i = 0; i < this.sides.length; i++) { let reviveSwitch = false; // Used to ignore the fake switch for Revival Blessing if (switches[i] && !this.canSwitch(this.sides[i])) { diff --git a/test/sim/items/ejectpack.js b/test/sim/items/ejectpack.js index 115cba335364..836db533250d 100644 --- a/test/sim/items/ejectpack.js +++ b/test/sim/items/ejectpack.js @@ -68,7 +68,7 @@ describe(`Eject Pack`, function () { assert.equal(battle.p1.requestState, 'switch'); }); - it.skip(`should not switch out the user if the user acquired the Eject Pack after the stat drop occurred`, function () { + it(`should not switch out the user if the user acquired the Eject Pack after the stat drop occurred`, function () { battle = common.createBattle([[ {species: 'Klefki', ability: 'magician', moves: ['lowsweep']}, {species: 'Wynaut', moves: ['sleeptalk']}, @@ -80,7 +80,7 @@ describe(`Eject Pack`, function () { assert.false.equal(battle.requestState, 'switch'); }); - it.skip(`should wait until after all other end-turn effects have resolved before switching out the holder`, function () { + it(`should wait until after all other end-turn effects have resolved before switching out the holder`, function () { battle = common.createBattle([[ {species: 'Glalie', item: 'ejectpack', ability: 'moody', moves: ['icebeam']}, {species: 'Wynaut', moves: ['sleeptalk']}, @@ -88,12 +88,15 @@ describe(`Eject Pack`, function () { {species: 'Zygarde', item: 'focussash', ability: 'powerconstruct', moves: ['octolock']}, ]]); battle.makeChoices(); - const log = battle.getDebugLog(); - const moodyIndex = log.lastIndexOf('|-ability|p1a: Glalie|Moody|boost'); - const powerConstructIndex = log.lastIndexOf('|-activate|p2a: Zygarde|ability: Power Construct'); - const ejectPackIndex = log.lastIndexOf('|-enditem|p1a: Glalie|Eject Pack'); - assert(moodyIndex < ejectPackIndex, 'Eject Pack should not activate before Moody'); - assert(powerConstructIndex < ejectPackIndex, 'Eject Pack should not activate before Power Construct'); + const boosts = battle.p1.active[0].boosts; + let hasBoost = false; + for (const i in boosts) { + if (boosts[i] > 0) { + hasBoost = true; + } + } + assert.species(battle.p2.active[0], 'Zygarde-Complete'); + assert(hasBoost, 'Boost from Moody should be applied before switch'); }); it.skip(`should not activate when another switching effect was triggered as part of the move`, function () { @@ -112,7 +115,7 @@ describe(`Eject Pack`, function () { assert.species(battle.p2.active[1], 'Wynaut', `Mew should have switched out with its Eject Button.`); }); - it.skip(`should only trigger the fastest Eject Pack when multiple targets with Eject Pack have stats lowered`, function () { + it(`should only trigger the fastest Eject Pack when multiple targets with Eject Pack have stats lowered`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'Hydreigon', moves: ['leer']}, {species: 'Horsea', moves: ['sleeptalk']}, @@ -128,7 +131,7 @@ describe(`Eject Pack`, function () { assert.species(battle.p2.active[1], 'Wynaut'); }); - it.skip(`should not trigger until after all entrance abilities have resolved during simultaneous switches`, function () { + it(`should not trigger until after all entrance abilities have resolved during simultaneous switches`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'Hydreigon', ability: 'intimidate', moves: ['sleeptalk']}, {species: 'Wynaut', moves: ['sleeptalk']}, @@ -137,7 +140,6 @@ describe(`Eject Pack`, function () { {species: 'Mew', level: 1, ability: 'electricsurge', moves: ['sleeptalk']}, {species: 'Wynaut', moves: ['sleeptalk']}, ]]); - battle.makeChoices(); assert(battle.field.isWeather('sunnyday')); assert(battle.field.isTerrain('electricterrain')); assert.equal(battle.p2.requestState, 'switch'); From 698f2129d83e03cc78e8b369ca49f154e2ca23f9 Mon Sep 17 00:00:00 2001 From: Shiva Devarajan Date: Fri, 27 Dec 2024 13:42:44 -0500 Subject: [PATCH 3/3] Allow for multiple switch flags, add a new test --- data/items.ts | 6 ------ sim/battle-actions.ts | 1 - sim/battle.ts | 14 ++++++++++++-- test/sim/items/ejectpack.js | 25 ++++++++++++++++++++++++- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/data/items.ts b/data/items.ts index 99d707c02b1b..528a040554da 100644 --- a/data/items.ts +++ b/data/items.ts @@ -1551,9 +1551,6 @@ export const Items: import('../sim/dex-items').ItemDataTable = { if (source && source !== target && target.hp && move && move.category !== 'Status' && !move.flags['futuremove']) { if (!this.canSwitch(target.side) || target.forceSwitchFlag || target.beingCalledBack || target.isSkyDropped()) return; if (target.volatiles['commanding'] || target.volatiles['commanded']) return; - for (const pokemon of this.getAllActive()) { - if (pokemon.switchFlag === true) return; - } target.switchFlag = true; if (target.useItem()) { source.switchFlag = false; @@ -1584,9 +1581,6 @@ export const Items: import('../sim/dex-items').ItemDataTable = { if (target.hp) { if (!this.canSwitch(target.side)) return; if (target.volatiles['commanding'] || target.volatiles['commanded']) return; - for (const pokemon of this.getAllActive()) { - if (pokemon.switchFlag === true) return; - } if (target.useItem()) target.switchFlag = true; } } diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 13d4284b4aeb..4128c3f85d54 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1215,7 +1215,6 @@ export class BattleActions { move: ActiveMove, moveData: ActiveMove, isSecondary?: boolean, isSelf?: boolean ) { let didAnything: number | boolean | null | undefined = damage.reduce(this.combineResults); - targets.sort((a, b) => a && b ? b.speed - a.speed : 0); for (const [i, target] of targets.entries()) { if (target === false) continue; let hitResult; diff --git a/sim/battle.ts b/sim/battle.ts index fb73c9d81e8f..9fdac4b79aa3 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -2725,14 +2725,24 @@ export class Battle { } } - const all_started = this.sides.every( + const allStarted = this.sides.every( side => side.active.every(pokemon => !pokemon || pokemon.isStarted || pokemon.fainted) ); const switches = this.sides.map( - side => all_started && + side => allStarted && side.active.some(pokemon => pokemon && !!pokemon.switchFlag) ); + + const allActive = this.sides.map(side => side.active).flat(); + const aliveSwitches = allActive.filter(pokemon => !!pokemon?.switchFlag && !pokemon.fainted); + if (aliveSwitches.length > 1) { + const sortedSwitches = aliveSwitches.sort((a, b) => a && b ? b.speed - a.speed : 0); + for (let i = 1; i < sortedSwitches.length; i++) { + sortedSwitches[i].switchFlag = false; + } + } + for (let i = 0; i < this.sides.length; i++) { let reviveSwitch = false; // Used to ignore the fake switch for Revival Blessing if (switches[i] && !this.canSwitch(this.sides[i])) { diff --git a/test/sim/items/ejectpack.js b/test/sim/items/ejectpack.js index 836db533250d..e268be0ca66a 100644 --- a/test/sim/items/ejectpack.js +++ b/test/sim/items/ejectpack.js @@ -110,6 +110,7 @@ describe(`Eject Pack`, function () { {species: 'Wynaut', moves: ['sleeptalk']}, ]]); battle.makeChoices(); + assert.statStage(battle.p2.active[1], 'atk', -1, "Attack should be dropped before switching."); battle.makeChoices(); assert.species(battle.p2.active[0], 'Zeraora', `Zeraora should not have switched out with its Eject Pack.`); assert.species(battle.p2.active[1], 'Wynaut', `Mew should have switched out with its Eject Button.`); @@ -145,6 +146,8 @@ describe(`Eject Pack`, function () { assert.equal(battle.p2.requestState, 'switch'); }); + // This is barely an eject pack bug, this switches should be determined by slot, not pokemon. This requires revamping that system + // Out of scope of this PR. it.skip(`should not prohibit switchins if a switch has already resolved to a slot replaced by Eject Pack`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'Pheromosa', moves: ['sleeptalk']}, @@ -156,8 +159,28 @@ describe(`Eject Pack`, function () { {species: 'Wynaut', moves: ['sleeptalk']}, ]]); battle.makeChoices('switch 3, move sleeptalk', 'move sleeptalk, switch 3'); + assert.species(battle.p2.active[1], 'Mew'); battle.makeChoices(); assert.species(battle.p2.active[0], 'Wynaut'); - assert.species(battle.p2.active[0], 'Morelull'); + assert.species(battle.p2.active[1], 'Morelull'); + }); + + it(`Should be able to switch back in if ejected`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'Typhlosion', moves: ['eruption']}, + {species: 'Wynaut', moves: ['sleeptalk']}, + ], [ + {species: 'Smeargle', ability: 'moody', moves: ['protect'], item: 'ejectpack'}, + {species: 'Shedinja', moves: ['sleeptalk']}, + {species: 'Wynaut', moves: ['sleeptalk']}, + ]]); + // Eruption + battle.makeChoices(); + // Switch Smeargle -> Wynaut + battle.makeChoices(); + // Switch Shedinja -> Smeargle + battle.makeChoices(); + assert.species(battle.p2.active[0], 'Wynaut', 'Should be switched in by eject pack'); + assert.species(battle.p2.active[1], 'Smeargle', 'Should be switched in by Shedinja fainting'); }); });