Fixed interaction with instruct and dead mon redirection

This commit is contained in:
Bertie690 2025-01-17 00:02:36 -05:00
parent e0600a9476
commit 21c7546ae5
3 changed files with 49 additions and 7 deletions

View File

@ -6108,7 +6108,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
return false;
}
// Don't allow wild mons to flee with U-turn et al
// Don't allow wild mons to flee with U-turn et al.
if (this.selfSwitch && !user.isPlayer() && move.category !== MoveCategory.STATUS) {
return false;
}
@ -7068,7 +7068,20 @@ export class RepeatMoveAttr extends MoveEffectAttr {
// (mainly for alternating double/single battle shenanigans)
// Rampaging moves (e.g. Outrage) are not included due to being incompatible with Instruct
// TODO: Fix this once dragon darts gets smart targeting
const moveTargets = movesetMove.getMove().isMultiTarget() ? getMoveTargets(target, lastMove.move).targets : lastMove.targets!;
let moveTargets = movesetMove.getMove().isMultiTarget() ? getMoveTargets(target, lastMove.move).targets : lastMove.targets;
/** In the event the instructed move's only target is a fainted opponent, redirect it to an alive ally if possible
Normally, all yet-unexecuted move phases would swap over when the enemy in question faints
(see `redirectPokemonMoves` in `battle-scene.ts`),
but since instruct adds a new move phase pre-emptively, we need to handle this interaction manually.
*/
const firstTarget = globalScene.getField()[moveTargets[0]];
if (globalScene.currentBattle.double && moveTargets.length === 1 && firstTarget.isFainted() && firstTarget !== target.getAlly()) {
const ally = firstTarget.getAlly();
if (ally.isActive()) { // ally exists, is not dead and can sponge the blast
moveTargets = [ ally.getBattlerIndex() ];
}
}
globalScene.queueMessage(i18next.t("moveTriggers:instructingMove", {
userPokemonName: getPokemonNameWithAffix(user),

View File

@ -43,7 +43,9 @@ describe("Moves - Instruct", () => {
});
it("should repeat target's last used move", async () => {
game.override.moveset(Moves.INSTRUCT);
game.override
.moveset(Moves.INSTRUCT)
.enemyLevel(1000); // ensures shuckle no die
await game.classicMode.startBattle([ Species.AMOONGUSS ]);
const enemy = game.scene.getEnemyPokemon()!;
@ -170,6 +172,33 @@ describe("Moves - Instruct", () => {
expect(enemyPokemon.getMoveHistory().length).toBe(1);
});
it("should redirect attacking moves if enemy faints", async () => {
game.override
.battleType("double")
.enemyMoveset(Moves.SPLASH)
.enemySpecies(Species.MAGIKARP)
.enemyLevel(1);
await game.classicMode.startBattle([ Species.HISUI_ELECTRODE, Species.KOMMO_O ]);
const [ electrode, kommo_o ] = game.scene.getPlayerField()!;
game.move.changeMoveset(electrode, Moves.CHLOROBLAST);
game.move.changeMoveset(kommo_o, Moves.INSTRUCT);
game.move.select(Moves.CHLOROBLAST, BattlerIndex.PLAYER);
game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to("BerryPhase");
// Chloroblast always deals 50% max HP% recoil UNLESS you whiff
// due to lack of targets or similar,
// so all we have to do is check whether electrode fainted or not.
// Naturally, both karps should also be dead as well.
expect(electrode.isFainted()).toBe(true);
const [ karp1, karp2 ] = game.scene.getEnemyField()!;
expect(karp1.isFainted()).toBe(true);
expect(karp2.isFainted()).toBe(true);
}),
it("should not repeat move when switching out", async () => {
game.override
.enemyMoveset(Moves.INSTRUCT)

View File

@ -459,9 +459,9 @@ export default class GameManager {
}
/**
* Intercepts `TurnStartPhase` and mocks the getSpeedOrder's return value {@linkcode TurnStartPhase.getSpeedOrder}
* Used to modify the turn order.
* Note: This *DOES NOT* account for priority.
* Intercepts `TurnStartPhase` and mocks {@linkcode TurnStartPhase.getSpeedOrder}'s return value.
* Used to manually modify Pokemon turn order.
* Note: This *DOES NOT* account for priority, only speed.
* @param {BattlerIndex[]} order The turn order to set
* @example
* ```ts
@ -475,7 +475,7 @@ export default class GameManager {
}
/**
* Removes all held items from enemy pokemon
* Removes all held items from enemy pokemon.
*/
removeEnemyHeldItems(): void {
this.scene.clearEnemyHeldItemModifiers();