From 1a2d7e88b20f3dec2668aa7180cd6410fb4d89cb Mon Sep 17 00:00:00 2001 From: Frutescens Date: Tue, 13 Aug 2024 19:38:03 -0700 Subject: [PATCH 01/14] Moved getOrder() into TurnStartPhase --- src/phases.ts | 80 +++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/src/phases.ts b/src/phases.ts index 5b66ee3e771..083e9d4338e 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -705,35 +705,6 @@ export class BattlePhase extends Phase { type PokemonFunc = (pokemon: Pokemon) => void; export abstract class FieldPhase extends BattlePhase { - getOrder(): BattlerIndex[] { - const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; - const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - - // We shuffle the list before sorting so speed ties produce random results - let orderedTargets: Pokemon[] = playerField.concat(enemyField); - // We seed it with the current turn to prevent an inconsistency where it - // was varying based on how long since you last reloaded - this.scene.executeWithSeedOffset(() => { - orderedTargets = Utils.randSeedShuffle(orderedTargets); - }, this.scene.currentBattle.turn, this.scene.waveSeed); - - orderedTargets.sort((a: Pokemon, b: Pokemon) => { - const aSpeed = a?.getBattleStat(Stat.SPD) || 0; - const bSpeed = b?.getBattleStat(Stat.SPD) || 0; - - return bSpeed - aSpeed; - }); - - const speedReversed = new Utils.BooleanHolder(false); - this.scene.arena.applyTags(TrickRoomTag, speedReversed); - - if (speedReversed.value) { - orderedTargets = orderedTargets.reverse(); - } - - return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); - } - executeForAll(func: PokemonFunc): void { const field = this.scene.getField(true).filter(p => p.summonData); field.forEach(pokemon => func(pokemon)); @@ -2305,12 +2276,37 @@ export class TurnStartPhase extends FieldPhase { super(scene); } - start() { - super.start(); + getOrder(): BattlerIndex[] { + const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; + const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; - const field = this.scene.getField(); - const order = this.getOrder(); + // We shuffle the list before sorting so speed ties produce random results + let orderedTargets: Pokemon[] = playerField.concat(enemyField); + // We seed it with the current turn to prevent an inconsistency where it + // was varying based on how long since you last reloaded + this.scene.executeWithSeedOffset(() => { + orderedTargets = Utils.randSeedShuffle(orderedTargets); + }, this.scene.currentBattle.turn, this.scene.waveSeed); + orderedTargets.sort((a: Pokemon, b: Pokemon) => { + const aSpeed = a?.getBattleStat(Stat.SPD) || 0; + const bSpeed = b?.getBattleStat(Stat.SPD) || 0; + + return bSpeed - aSpeed; + }); + + //Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed. + const speedReversed = new Utils.BooleanHolder(false); + this.scene.arena.applyTags(TrickRoomTag, speedReversed); + + if (speedReversed.value) { + orderedTargets = orderedTargets.reverse(); + } + + orderedTargets = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + + //The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw + //The ability Mycelium Might disables Quick Claw's activation when using a status move const battlerBypassSpeed = {}; this.scene.getField(true).filter(p => p.summonData).map(p => { @@ -2324,7 +2320,7 @@ export class TurnStartPhase extends FieldPhase { battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; }); - const moveOrder = order.slice(0); + const moveOrder = orderedTargets.slice(0); moveOrder.sort((a, b) => { const aCommand = this.scene.currentBattle.turnCommands[a]; @@ -2340,6 +2336,7 @@ export class TurnStartPhase extends FieldPhase { const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? + //The game now considers priority const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); @@ -2363,15 +2360,23 @@ export class TurnStartPhase extends FieldPhase { return battlerBypassSpeed[a].value ? -1 : 1; } - const aIndex = order.indexOf(a); - const bIndex = order.indexOf(b); + const aIndex = orderedTargets.indexOf(a); + const bIndex = orderedTargets.indexOf(b); return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; }); + return moveOrder; + } + + start() { + super.start(); + + const field = this.scene.getField(); + const order = this.getOrder(); let orderIndex = 0; - for (const o of moveOrder) { + for (const o of order) { const pokemon = field[o]; const turnCommand = this.scene.currentBattle.turnCommands[o]; @@ -2432,7 +2437,6 @@ export class TurnStartPhase extends FieldPhase { } } - this.scene.pushPhase(new WeatherEffectPhase(this.scene)); for (const o of order) { From 11150254f568b8575211693919c9c5deeb3d8dcd Mon Sep 17 00:00:00 2001 From: Frutescens Date: Tue, 13 Aug 2024 21:01:35 -0700 Subject: [PATCH 02/14] Cleaned up bypass speed checks --- src/data/ability.ts | 9 +++------ src/phases.ts | 46 ++++++++++++++++++++++----------------------- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 7e270f4d3f2..3920a3c2cbb 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4117,19 +4117,16 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { } /** - * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment - * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not + * @argument {boolean} canBypassSpeed - determines if a Pokemon is able to bypass speed at the moment */ apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const bypassSpeed = args[0] as Utils.BooleanHolder; - const canCheckHeldItems = args[1] as Utils.BooleanHolder; + const canBypassSpeed = args[0] as Utils.BooleanHolder; const turnCommand = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; if (this.condition(pokemon, move!) && isCommandFight) { - bypassSpeed.value = false; - canCheckHeldItems.value = false; + canBypassSpeed.value = false; return false; } return true; diff --git a/src/phases.ts b/src/phases.ts index 083e9d4338e..90ea6ef8b38 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2305,21 +2305,6 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); - //The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw - //The ability Mycelium Might disables Quick Claw's activation when using a status move - const battlerBypassSpeed = {}; - - this.scene.getField(true).filter(p => p.summonData).map(p => { - const bypassSpeed = new Utils.BooleanHolder(false); - const canCheckHeldItems = new Utils.BooleanHolder(true); - applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed); - applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, bypassSpeed, canCheckHeldItems); - if (canCheckHeldItems.value) { - this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); - } - battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; - }); - const moveOrder = orderedTargets.slice(0); moveOrder.sort((a, b) => { @@ -2336,7 +2321,7 @@ export class TurnStartPhase extends FieldPhase { const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? - //The game now considers priority + //The game now considers priority and checks if IncrementMovePriorityAttr / ChangeMovePriorityAbAttr applies const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); @@ -2346,20 +2331,33 @@ export class TurnStartPhase extends FieldPhase { applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - if (aPriority.value !== bPriority.value) { - const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); - const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; - if (bracketDifference === 0 && hasSpeedDifference) { + const battlerBypassSpeed = {}; + + //If the two moves compared here share the same base priority, the game then determines if the ability Quick Draw / Quick Claw can activate + //The ability Mycelium Might, however, disables Quick Claw if the holder uses a status move. + if (Math.ceil(aPriority.value) - Math.ceil(bPriority.value) === 0) { + this.scene.getField(true).filter(p => p.summonData).map(p => { + const canBypassSpeed = new Utils.BooleanHolder(true); + applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, canBypassSpeed); + const bypassSpeed = new Utils.BooleanHolder(false); + if (canBypassSpeed.value) { + applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed); + this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); + } + battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; + }); + + if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } + } + + //If the two moves compared here do not share the same base priority, the game does not consider battlerBypassSpeed + if (aPriority.value !== bPriority.value) { return aPriority.value < bPriority.value ? 1 : -1; } } - if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { - return battlerBypassSpeed[a].value ? -1 : 1; - } - const aIndex = orderedTargets.indexOf(a); const bIndex = orderedTargets.indexOf(b); From d03d55f706418b6ef364d4d8692aa3753b384ab3 Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 11:46:03 -0700 Subject: [PATCH 03/14] Revert "Cleaned up bypass speed checks" This reverts commit 11150254f568b8575211693919c9c5deeb3d8dcd. --- src/data/ability.ts | 9 ++++++--- src/phases.ts | 46 +++++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 3920a3c2cbb..7e270f4d3f2 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4117,16 +4117,19 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { } /** - * @argument {boolean} canBypassSpeed - determines if a Pokemon is able to bypass speed at the moment + * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment + * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not */ apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const canBypassSpeed = args[0] as Utils.BooleanHolder; + const bypassSpeed = args[0] as Utils.BooleanHolder; + const canCheckHeldItems = args[1] as Utils.BooleanHolder; const turnCommand = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; if (this.condition(pokemon, move!) && isCommandFight) { - canBypassSpeed.value = false; + bypassSpeed.value = false; + canCheckHeldItems.value = false; return false; } return true; diff --git a/src/phases.ts b/src/phases.ts index 90ea6ef8b38..083e9d4338e 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2305,6 +2305,21 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + //The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw + //The ability Mycelium Might disables Quick Claw's activation when using a status move + const battlerBypassSpeed = {}; + + this.scene.getField(true).filter(p => p.summonData).map(p => { + const bypassSpeed = new Utils.BooleanHolder(false); + const canCheckHeldItems = new Utils.BooleanHolder(true); + applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed); + applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, bypassSpeed, canCheckHeldItems); + if (canCheckHeldItems.value) { + this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); + } + battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; + }); + const moveOrder = orderedTargets.slice(0); moveOrder.sort((a, b) => { @@ -2321,7 +2336,7 @@ export class TurnStartPhase extends FieldPhase { const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? - //The game now considers priority and checks if IncrementMovePriorityAttr / ChangeMovePriorityAbAttr applies + //The game now considers priority const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); @@ -2331,33 +2346,20 @@ export class TurnStartPhase extends FieldPhase { applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - const battlerBypassSpeed = {}; - - //If the two moves compared here share the same base priority, the game then determines if the ability Quick Draw / Quick Claw can activate - //The ability Mycelium Might, however, disables Quick Claw if the holder uses a status move. - if (Math.ceil(aPriority.value) - Math.ceil(bPriority.value) === 0) { - this.scene.getField(true).filter(p => p.summonData).map(p => { - const canBypassSpeed = new Utils.BooleanHolder(true); - applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, canBypassSpeed); - const bypassSpeed = new Utils.BooleanHolder(false); - if (canBypassSpeed.value) { - applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed); - this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); - } - battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; - }); - - if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { + if (aPriority.value !== bPriority.value) { + const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); + const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; + if (bracketDifference === 0 && hasSpeedDifference) { return battlerBypassSpeed[a].value ? -1 : 1; } - } - - //If the two moves compared here do not share the same base priority, the game does not consider battlerBypassSpeed - if (aPriority.value !== bPriority.value) { return aPriority.value < bPriority.value ? 1 : -1; } } + if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { + return battlerBypassSpeed[a].value ? -1 : 1; + } + const aIndex = orderedTargets.indexOf(a); const bIndex = orderedTargets.indexOf(b); From f9ad66135451b4b7d10f36229ea422e173e8c06e Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 12:02:55 -0700 Subject: [PATCH 04/14] Added comments. --- src/phases.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/phases.ts b/src/phases.ts index 083e9d4338e..a96b54f16d6 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2295,7 +2295,7 @@ export class TurnStartPhase extends FieldPhase { return bSpeed - aSpeed; }); - //Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed. + // Next, a check for Trick Room is applied. If Trick Room is present, the order is reversed. const speedReversed = new Utils.BooleanHolder(false); this.scene.arena.applyTags(TrickRoomTag, speedReversed); @@ -2305,8 +2305,9 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); - //The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw - //The ability Mycelium Might disables Quick Claw's activation when using a status move + // The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw + // The ability Mycelium Might disables Quick Claw's activation when using a status move + // This occurs before the main loop because of battles with more than two Pokemon const battlerBypassSpeed = {}; this.scene.getField(true).filter(p => p.summonData).map(p => { @@ -2320,9 +2321,10 @@ export class TurnStartPhase extends FieldPhase { battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; }); - const moveOrder = orderedTargets.slice(0); - - moveOrder.sort((a, b) => { + // The function begins sorting orderedTargets based on command priority, move priority, and possible speed bypasses. + // Non-FIGHT commands (SWITCH, BALL, RUN) have a higher command priority and will always occur before any FIGHT commands. + orderedTargets = orderedTargets.slice(0); + orderedTargets.sort((a, b) => { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; @@ -2336,9 +2338,10 @@ export class TurnStartPhase extends FieldPhase { const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? - //The game now considers priority + // The game now considers priority and applies the relevant move and ability attributes const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); + const isSameBracket = aPriority === bPriority; applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? @@ -2346,16 +2349,19 @@ export class TurnStartPhase extends FieldPhase { applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? + // The game now checks for differences in priority levels. + // If the moves share the same original priority bracket, it can check for differences in battlerBypassSpeed and return the result. + // This conditional is used to ensure that Quick Claw can still activate with abilities like Stall and Mycelium Might (attack moves only) + // Otherwise, the game returns the user of the move with the highest priority. if (aPriority.value !== bPriority.value) { - const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); - const hasSpeedDifference = battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value; - if (bracketDifference === 0 && hasSpeedDifference) { + if (isSameBracket && battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } return aPriority.value < bPriority.value ? 1 : -1; } } + // If there is no difference between the move's calculated priorities, the game checks for differences in battlerBypassSpeed and returns the result. if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; } @@ -2365,7 +2371,8 @@ export class TurnStartPhase extends FieldPhase { return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; }); - return moveOrder; + // The function finally returns BattlerIndex[] object + return orderedTargets; } start() { From a01a8910cc33d15f0f00bb7576f7bbd456beb79f Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 12:26:48 -0700 Subject: [PATCH 05/14] Fixed up some inconsistencies --- src/phases.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/phases.ts b/src/phases.ts index a96b54f16d6..705d6e4790e 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2303,7 +2303,7 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = orderedTargets.reverse(); } - orderedTargets = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + let moveOrder : BattlerIndex[] = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); // The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw // The ability Mycelium Might disables Quick Claw's activation when using a status move @@ -2323,8 +2323,8 @@ export class TurnStartPhase extends FieldPhase { // The function begins sorting orderedTargets based on command priority, move priority, and possible speed bypasses. // Non-FIGHT commands (SWITCH, BALL, RUN) have a higher command priority and will always occur before any FIGHT commands. - orderedTargets = orderedTargets.slice(0); - orderedTargets.sort((a, b) => { + moveOrder = moveOrder.slice(0); + moveOrder.sort((a, b) => { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; @@ -2366,13 +2366,12 @@ export class TurnStartPhase extends FieldPhase { return battlerBypassSpeed[a].value ? -1 : 1; } - const aIndex = orderedTargets.indexOf(a); - const bIndex = orderedTargets.indexOf(b); + const aIndex = moveOrder.indexOf(a); + const bIndex = moveOrder.indexOf(b); return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; }); - // The function finally returns BattlerIndex[] object - return orderedTargets; + return moveOrder; } start() { From e149bb8b7bd54beeb4b720d6bc6a975cfa15bd72 Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 12:47:24 -0700 Subject: [PATCH 06/14] changed isSameBracket check --- src/phases.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index 705d6e4790e..00538bef0cf 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2341,7 +2341,7 @@ export class TurnStartPhase extends FieldPhase { // The game now considers priority and applies the relevant move and ability attributes const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - const isSameBracket = aPriority === bPriority; + const isSameBracket = aPriority.value === bPriority.value; applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? From 1759c72abb4d7726474de1291c6528452f5e4d20 Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 12:53:48 -0700 Subject: [PATCH 07/14] changed isSameBracket check p2 --- src/phases.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index 00538bef0cf..63a68f763b7 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2341,7 +2341,7 @@ export class TurnStartPhase extends FieldPhase { // The game now considers priority and applies the relevant move and ability attributes const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - const isSameBracket = aPriority.value === bPriority.value; + const isSameBracket = Math.abs(aPriority.value - bPriority.value) === 0.5; applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? From 9c26739a0f157a445268f91938009fad1042d196 Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 12:54:38 -0700 Subject: [PATCH 08/14] changed isSameBracket check p3 --- src/phases.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index 63a68f763b7..61f230cf012 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2341,7 +2341,7 @@ export class TurnStartPhase extends FieldPhase { // The game now considers priority and applies the relevant move and ability attributes const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - const isSameBracket = Math.abs(aPriority.value - bPriority.value) === 0.5; + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? @@ -2353,6 +2353,7 @@ export class TurnStartPhase extends FieldPhase { // If the moves share the same original priority bracket, it can check for differences in battlerBypassSpeed and return the result. // This conditional is used to ensure that Quick Claw can still activate with abilities like Stall and Mycelium Might (attack moves only) // Otherwise, the game returns the user of the move with the highest priority. + const isSameBracket = Math.abs(aPriority.value - bPriority.value) === 0.5; if (aPriority.value !== bPriority.value) { if (isSameBracket && battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; From 10281f4a87922810bcee277b320a98df2e14dbfa Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 12:54:51 -0700 Subject: [PATCH 09/14] changed isSameBracket check p3 --- src/phases.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index 61f230cf012..81f4eb99ca9 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2342,7 +2342,6 @@ export class TurnStartPhase extends FieldPhase { const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? From 545e09565bb2ce84c5856bf0c398b9c7d4be47ad Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 13:01:33 -0700 Subject: [PATCH 10/14] Fixed up conditionals + stall/M.m --- src/data/ability.ts | 4 ++-- src/phases.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 7e270f4d3f2..2fd08cf8850 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4657,7 +4657,7 @@ export function initAbilities() { .attr(AlwaysHitAbAttr) .attr(DoubleBattleChanceAbAttr), new Ability(Abilities.STALL, 4) - .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.5), + .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.2), new Ability(Abilities.TECHNICIAN, 4) .attr(MovePowerBoostAbAttr, (user, target, move) => { const power = new Utils.NumberHolder(move.power); @@ -5343,7 +5343,7 @@ export function initAbilities() { .partial() // Healing not blocked by Heal Block .ignorable(), new Ability(Abilities.MYCELIUM_MIGHT, 9) - .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.5) + .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.2) .attr(PreventBypassSpeedChanceAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS) .attr(MoveAbilityBypassAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS), new Ability(Abilities.MINDS_EYE, 9) diff --git a/src/phases.ts b/src/phases.ts index 81f4eb99ca9..34fbf7babb9 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2352,7 +2352,7 @@ export class TurnStartPhase extends FieldPhase { // If the moves share the same original priority bracket, it can check for differences in battlerBypassSpeed and return the result. // This conditional is used to ensure that Quick Claw can still activate with abilities like Stall and Mycelium Might (attack moves only) // Otherwise, the game returns the user of the move with the highest priority. - const isSameBracket = Math.abs(aPriority.value - bPriority.value) === 0.5; + const isSameBracket = Math.ceil(aPriority.value) - Math.ceil(bPriority.value) === 0; if (aPriority.value !== bPriority.value) { if (isSameBracket && battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) { return battlerBypassSpeed[a].value ? -1 : 1; From 08a3e27d776b4167e6d9aaa14d9da1c7c3a19b22 Mon Sep 17 00:00:00 2001 From: Frutescens Date: Wed, 14 Aug 2024 13:49:36 -0700 Subject: [PATCH 11/14] Seems OK --- src/enums/battle-spec.ts | 3 ++- src/phases.ts | 10 +++++++--- src/test/battle/battle-order.test.ts | 10 +++++----- src/test/moves/dynamax_cannon.test.ts | 2 +- src/test/utils/gameManager.ts | 4 ++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/enums/battle-spec.ts b/src/enums/battle-spec.ts index 00bc7f92fea..ca7dbe929e5 100644 --- a/src/enums/battle-spec.ts +++ b/src/enums/battle-spec.ts @@ -1,4 +1,5 @@ export enum BattleSpec { DEFAULT, - FINAL_BOSS + FINAL_BOSS, + FINAL_BOSS_MONOTYPE } diff --git a/src/phases.ts b/src/phases.ts index 34fbf7babb9..43b65a9ddb3 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2276,7 +2276,7 @@ export class TurnStartPhase extends FieldPhase { super(scene); } - getOrder(): BattlerIndex[] { + getSpeedOrder(): BattlerIndex[] { const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[]; const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[]; @@ -2303,8 +2303,12 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = orderedTargets.reverse(); } - let moveOrder : BattlerIndex[] = orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + } + getCommandOrder(): BattlerIndex[] { + + let moveOrder = this.getSpeedOrder(); // The creation of the battlerBypassSpeed object contains checks for the ability Quick Draw and the held item Quick Claw // The ability Mycelium Might disables Quick Claw's activation when using a status move // This occurs before the main loop because of battles with more than two Pokemon @@ -2378,7 +2382,7 @@ export class TurnStartPhase extends FieldPhase { super.start(); const field = this.scene.getField(); - const order = this.getOrder(); + const order = this.getCommandOrder(); let orderIndex = 0; diff --git a/src/test/battle/battle-order.test.ts b/src/test/battle/battle-order.test.ts index 6aa919186b4..6c4a1f7f511 100644 --- a/src/test/battle/battle-order.test.ts +++ b/src/test/battle/battle-order.test.ts @@ -52,7 +52,7 @@ describe("Battle order", () => { }); await game.phaseInterceptor.run(EnemyCommandPhase); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); + const order = phase.getCommandOrder(); expect(order[0]).toBe(2); expect(order[1]).toBe(0); }, 20000); @@ -73,7 +73,7 @@ describe("Battle order", () => { }); await game.phaseInterceptor.run(EnemyCommandPhase); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); + const order = phase.getCommandOrder(); expect(order[0]).toBe(0); expect(order[1]).toBe(2); }, 20000); @@ -113,7 +113,7 @@ describe("Battle order", () => { }); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); + const order = phase.getCommandOrder(); expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(2)); expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(3)); expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(2)); @@ -155,7 +155,7 @@ describe("Battle order", () => { }); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); + const order = phase.getCommandOrder(); expect(order.indexOf(3)).toBeLessThan(order.indexOf(0)); expect(order.indexOf(3)).toBeLessThan(order.indexOf(1)); expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); @@ -196,7 +196,7 @@ describe("Battle order", () => { }); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; - const order = phase.getOrder(); + const order = phase.getCommandOrder(); expect(order.indexOf(1)).toBeLessThan(order.indexOf(0)); expect(order.indexOf(1)).toBeLessThan(order.indexOf(2)); expect(order.indexOf(3)).toBeLessThan(order.indexOf(0)); diff --git a/src/test/moves/dynamax_cannon.test.ts b/src/test/moves/dynamax_cannon.test.ts index 57846c1aef7..9d2be7c6e05 100644 --- a/src/test/moves/dynamax_cannon.test.ts +++ b/src/test/moves/dynamax_cannon.test.ts @@ -164,7 +164,7 @@ describe("Moves - Dynamax Cannon", () => { await game.phaseInterceptor.to(TurnStartPhase, false); // Force user to act before enemy - vi.spyOn((game.scene.getCurrentPhase() as TurnStartPhase), "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex. ENEMY]); + vi.spyOn((game.scene.getCurrentPhase() as TurnStartPhase), "getCommandOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex. ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 935d40324a2..36f45107d9c 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -391,7 +391,7 @@ export default class GameManager { } /** - * Intercepts `TurnStartPhase` and mocks the getOrder's return value {@linkcode TurnStartPhase.getOrder} + * Intercepts `TurnStartPhase` and mocks the getSpeedOrder's return value {@linkcode TurnStartPhase.getSpeedOrder} * Used to modify the turn order. * @param {BattlerIndex[]} order The turn order to set * @example @@ -402,7 +402,7 @@ export default class GameManager { async setTurnOrder(order: BattlerIndex[]): Promise { await this.phaseInterceptor.to(TurnStartPhase, false); - vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order); + vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order); } /** From f683a69a117336395d080a165496155a25557e32 Mon Sep 17 00:00:00 2001 From: Mumble Date: Wed, 14 Aug 2024 20:04:16 -0700 Subject: [PATCH 12/14] Update battle-spec.ts --- src/enums/battle-spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/enums/battle-spec.ts b/src/enums/battle-spec.ts index ca7dbe929e5..00bc7f92fea 100644 --- a/src/enums/battle-spec.ts +++ b/src/enums/battle-spec.ts @@ -1,5 +1,4 @@ export enum BattleSpec { DEFAULT, - FINAL_BOSS, - FINAL_BOSS_MONOTYPE + FINAL_BOSS } From 2f0e765a3d15eeaa10536095ade2a8252accb3ee Mon Sep 17 00:00:00 2001 From: Frutescens Date: Thu, 15 Aug 2024 11:24:48 -0700 Subject: [PATCH 13/14] Updated tests to use new functions introduced. Less intuitive, but faster. --- src/test/abilities/mycelium_might.test.ts | 50 ++++++++++------------- src/test/abilities/stall.test.ts | 48 +++++++++------------- 2 files changed, 41 insertions(+), 57 deletions(-) diff --git a/src/test/abilities/mycelium_might.test.ts b/src/test/abilities/mycelium_might.test.ts index d519eb67626..73ac933df06 100644 --- a/src/test/abilities/mycelium_might.test.ts +++ b/src/test/abilities/mycelium_might.test.ts @@ -1,4 +1,4 @@ -import { MovePhase, TurnEndPhase } from "#app/phases"; +import { EnemyCommandPhase, TurnStartPhase, TurnEndPhase } from "#app/phases"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; @@ -43,63 +43,55 @@ describe("Abilities - Mycelium Might", () => { it("If a Pokemon with Mycelium Might uses a status move, it will always move last but the status move will ignore protective abilities", async() => { await game.startBattle([ Species.REGIELEKI ]); - - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); - const enemyIndex = enemyPokemon?.getBattlerIndex(); game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.run(EnemyCommandPhase); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); - // The player Pokemon (with Mycelium Might) goes last despite having higher speed than the opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + // This means that the commandOrder is equivalent to the speed Order reversed + expect(speedOrder.reverse().every((val, index) => val === commandOrder[index])).toBe(true); await game.phaseInterceptor.to(TurnEndPhase); + // Despite the opponent's ability (Clear Body), its attack stat is still reduced. expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); it("Pokemon with Mycelium Might will go first if a status move that is in a higher priority bracket than the opponent's move is used", async() => { game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([ Species.REGIELEKI ]); - - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); - const enemyIndex = enemyPokemon?.getBattlerIndex(); game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.run(EnemyCommandPhase); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The enemy Pokemon goes second because its move is in a lower priority bracket. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + // This means that the commandOrder should be identical to the speedOrder + expect(speedOrder.every((val, index) => val === commandOrder[index])).toBe(true); await game.phaseInterceptor.to(TurnEndPhase); + // Despite the opponent's ability (Clear Body), its attack stat is still reduced. expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); it("Order is established normally if the Pokemon uses a non-status move", async() => { await game.startBattle([ Species.REGIELEKI ]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.run(EnemyCommandPhase); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The enemy Pokemon (without M.M.) goes second because its speed is lower. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + // This means that the commandOrder should be identical to the speedOrder + expect(speedOrder.every((val, index) => val === commandOrder[index])).toBe(true); }, 20000); }); diff --git a/src/test/abilities/stall.test.ts b/src/test/abilities/stall.test.ts index 44519064300..6cade382ab8 100644 --- a/src/test/abilities/stall.test.ts +++ b/src/test/abilities/stall.test.ts @@ -1,4 +1,4 @@ -import { MovePhase } from "#app/phases"; +import { EnemyCommandPhase, TurnStartPhase } from "#app/phases"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; @@ -41,55 +41,47 @@ describe("Abilities - Stall", () => { it("Pokemon with Stall should move last in its priority bracket regardless of speed", async() => { await game.startBattle([ Species.SHUCKLE ]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.run(EnemyCommandPhase); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The player Pokemon (without Stall) goes first despite having lower speed than the opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes last despite having higher speed than the player Pokemon. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + // This means that the commandOrder is equivalent to the speed Order reversed + expect(speedOrder.reverse().every((val, index) => val === commandOrder[index])).toBe(true); }, 20000); it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async() => { await game.startBattle([ Species.SHUCKLE ]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MovePhase, false); + await game.phaseInterceptor.run(EnemyCommandPhase); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); - - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon goes second because its move is in a lower priority bracket. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + // This means that the commandOrder should be identical to the speedOrder + expect(speedOrder.every((val, index) => val === commandOrder[index])).toBe(true); }, 20000); it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async() => { game.override.ability(Abilities.STALL); await game.startBattle([ Species.SHUCKLE ]); - const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MovePhase, false); - // The opponent Pokemon (with Stall) goes first because it has a higher speed. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); + await game.phaseInterceptor.run(EnemyCommandPhase); + const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const speedOrder = phase.getSpeedOrder(); + const commandOrder = phase.getCommandOrder(); - await game.phaseInterceptor.run(MovePhase); - await game.phaseInterceptor.to(MovePhase, false); + // The opponent Pokemon (with Stall) goes first because it has a higher speed. // The player Pokemon (with Stall) goes second because its speed is lower. - expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); + // This means that the commandOrder should be identical to the speedOrder + expect(speedOrder.every((val, index) => val === commandOrder[index])).toBe(true); }, 20000); }); From d9748a8120e64cbc79292ab33839f80820944c35 Mon Sep 17 00:00:00 2001 From: Mumble Date: Thu, 15 Aug 2024 17:34:16 -0700 Subject: [PATCH 14/14] Update src/phases.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/phases.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases.ts b/src/phases.ts index 43b65a9ddb3..bc018d0f23b 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2303,7 +2303,7 @@ export class TurnStartPhase extends FieldPhase { orderedTargets = orderedTargets.reverse(); } - return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : 0)); + return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER)); } getCommandOrder(): BattlerIndex[] {