diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index d21ebc73dc62f..6b25f6a32e1cc 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -175,8 +175,9 @@ export class BattleActions { const nextSwitch = this.battle.queue.shift(); switchersIn.push(nextSwitch!.pokemon!); } + this.battle.prng.shuffle(this.battle.speedTieResolution); this.battle.fieldEvent('SwitchIn', switchersIn); - + if (!pokemon.hp) return false; pokemon.isStarted = true; pokemon.draggedIn = null; diff --git a/sim/battle.ts b/sim/battle.ts index 00564644ff907..7d36a39ea0653 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -174,6 +174,7 @@ export class Battle { lastDamage: number; effectOrder: number; quickClawRoll: boolean; + speedTieResolution: number[]; teamGenerator: ReturnType | null; @@ -261,6 +262,10 @@ export class Battle { this.lastDamage = 0; this.effectOrder = 0; this.quickClawRoll = false; + this.speedTieResolution = []; + for (let i = 0; i < this.activePerHalf * 2; i++) { + this.speedTieResolution.push(i / (this.activePerHalf * 2)); + } this.teamGenerator = null; @@ -950,6 +955,14 @@ export class Battle { } if (handler.effectHolder && (handler.effectHolder as Pokemon).getStat) { handler.speed = (handler.effectHolder as Pokemon).speed; + if (callbackName.endsWith('SwitchIn')) { + // Pokemon speeds including ties are resolved before all onSwitchIn handlers and aren't re-sorted in-between + // so we add a fractional speed to each Pokemon's respective event handlers by using their unique field position + // to index a randomly shuffled array of sequential numbers + const allSlots = 'abcdef'; + const speedTieIndex = allSlots.indexOf((handler.effectHolder as Pokemon).getSlot().charAt(2)); + handler.speed += this.speedTieResolution[speedTieIndex]; + } } return handler; } diff --git a/test/sim/misc/multi-battle.js b/test/sim/misc/multi-battle.js index ac39e23a26618..ee8c9a43af9c6 100644 --- a/test/sim/misc/multi-battle.js +++ b/test/sim/misc/multi-battle.js @@ -24,7 +24,7 @@ describe('Free-for-all', function () { battle.makeChoices(); battle.lose('p2'); assert(battle.p2.activeRequest.wait); - battle.makeChoices('auto', '', 'move uturn', 'auto'); + battle.makeChoices('auto', '', 'move uturn 1', 'auto'); battle.lose('p3'); battle.makeChoices(); assert.equal(battle.turn, 4); diff --git a/test/sim/misc/terastal.js b/test/sim/misc/terastal.js index 85642c411d8d5..372bed78b792b 100644 --- a/test/sim/misc/terastal.js +++ b/test/sim/misc/terastal.js @@ -35,9 +35,9 @@ describe("Terastallization", function () { it('should give STAB correctly to the user\'s old types', function () { battle = common.createBattle([[ - {species: 'Ampharos', ability: 'static', moves: ['shockwave', 'swift'], teraType: 'Electric'}, + {species: 'Ampharos', ability: 'shellarmor', moves: ['shockwave', 'swift'], teraType: 'Electric'}, ], [ - {species: 'Ampharos', ability: 'static', moves: ['shockwave', 'swift'], teraType: 'Normal'}, + {species: 'Ampharos', ability: 'shellarmor', moves: ['shockwave', 'swift'], teraType: 'Normal'}, ]]); battle.makeChoices('move shockwave terastallize', 'move shockwave terastallize'); const teraDamage = battle.p2.active[0].maxhp - battle.p2.active[0].hp; @@ -47,7 +47,7 @@ describe("Terastallization", function () { const nonTeraDamage = battle.p1.active[0].maxhp - battle.p1.active[0].hp; // 0 SpA Ampharos Shock Wave vs. 0 HP / 0 SpD Ampharos: 40-48 assert.bounded(nonTeraDamage, [40, 48], - "Terastallizing did not keep old type's STAB; actual damage: " + teraDamage); + "Terastallizing did not keep old type's STAB; actual damage: " + nonTeraDamage); battle = common.createBattle([[ {species: 'Mimikyu', ability: 'disguise', item: 'laggingtail', moves: ['shadowclaw', 'waterfall', 'sleeptalk'], teraType: 'Water'},