mostly implemented (missing some edge cases/interactions)

This commit is contained in:
snoozbuster 2024-08-03 23:37:28 -07:00
parent a3a42931ba
commit 7517e16c84
2 changed files with 99 additions and 21 deletions

View File

@ -4927,26 +4927,65 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
return resolve(false); return resolve(false);
} }
// Move the switch out logic inside the conditional block
// This ensures that the switch out only happens when the conditions are met
const switchOutTarget = this.user ? user : target; const switchOutTarget = this.user ? user : target;
let willBePursued = false;
if (switchOutTarget.hp > 0 && !this.batonPass && this.user && move.id !== Moves.TELEPORT) {
switchOutTarget.addTag(BattlerTagType.ESCAPING);
const opposingField = user.isPlayer() ? user.scene.getEnemyField() : user.scene.getPlayerField();
const opposingPursuitUsers = opposingField
.filter((op: Pokemon) => op.getTag(BattlerTagType.ANTICIPATING_ACTION)?.sourceMove === Moves.PURSUIT)
.sort((a, b) => b.turnData.order - a.turnData.order);
if (opposingPursuitUsers.length) {
willBePursued = true;
opposingPursuitUsers.forEach(pursuiter => {
if (user.scene.tryRemovePhase(p => p instanceof MovePhase && p.pokemon.id === pursuiter.id)) {
user.scene.prependToPhase(new MovePhase(user.scene, pursuiter, [switchOutTarget.getBattlerIndex()], pursuiter.getMoveset().find(m => m.moveId === Moves.PURSUIT) || new PokemonMove(Moves.PURSUIT), false, false), MoveEndPhase);
}
});
}
}
if (switchOutTarget instanceof PlayerPokemon) { if (switchOutTarget instanceof PlayerPokemon) {
switchOutTarget.leaveField(!this.batonPass); if (!willBePursued) {
switchOutTarget.leaveField(!this.batonPass);
}
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
user.scene.prependToPhase(new SwitchPhase(user.scene, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); user.scene.prependToPhase(
new SwitchPhase(user.scene, switchOutTarget.getFieldIndex(), true, willBePursued),
MoveEndPhase
);
resolve(true); resolve(true);
} else { } else {
resolve(false); resolve(false);
} }
return; return;
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) { } else if (user.scene.currentBattle.battleType !== BattleType.WILD) {
if (!user.scene.currentBattle.trainer) {
return resolve(false); // what are we even doing here
}
// Switch out logic for trainer battles // Switch out logic for trainer battles
switchOutTarget.leaveField(!this.batonPass); if (!willBePursued) {
switchOutTarget.leaveField(!this.batonPass);
}
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
// for opponent switching out // for opponent switching out
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), (user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), false, this.batonPass, false), MoveEndPhase); user.scene.prependToPhase(
new SwitchSummonPhase(
user.scene,
switchOutTarget.getFieldIndex(),
user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot),
willBePursued,
this.batonPass,
false
),
MoveEndPhase
);
} }
} else { } else {
// Switch out logic for everything else (eg: WILD battles) // Switch out logic for everything else (eg: WILD battles)

View File

@ -2332,14 +2332,22 @@ export class TurnStartPhase extends FieldPhase {
const moveOrder = order.slice(0); const moveOrder = order.slice(0);
moveOrder.sort((a, b) => { moveOrder.sort((a, b) => {
const aCommand = this.scene.currentBattle.turnCommands[a]; const aCommand = this.scene.currentBattle.turnCommands[a]!;
const bCommand = this.scene.currentBattle.turnCommands[b]; const bCommand = this.scene.currentBattle.turnCommands[b]!;
if (aCommand?.command !== bCommand?.command) { if (aCommand.command !== bCommand.command) {
if (aCommand?.command === Command.FIGHT) { if (aCommand.command === Command.FIGHT) {
return 1; if (aCommand.move?.move === Moves.PURSUIT && bCommand.command === Command.POKEMON) {
} else if (bCommand?.command === Command.FIGHT) { return -1;
return -1; } else {
return 1;
}
} else if (bCommand.command === Command.FIGHT) {
if (bCommand.move?.move === Moves.PURSUIT && aCommand.command === Command.POKEMON) {
return 1;
} else {
return -1;
}
} }
} else if (aCommand?.command === Command.FIGHT) { } else if (aCommand?.command === Command.FIGHT) {
const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here?
@ -2396,22 +2404,50 @@ export class TurnStartPhase extends FieldPhase {
if (move.getMove().hasAttr(MoveHeaderAttr)) { if (move.getMove().hasAttr(MoveHeaderAttr)) {
this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move)); this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move));
} }
// even though pursuit is ordered before Pokemon commands in the move
// order, the SwitchSummonPhase is unshifted onto the phase list, which
// would cause it to run before pursuit if pursuit was pushed normally.
// the SwitchSummonPhase can't be changed to a push either, because then
// the MoveHeaderPhase for all moves would run prior to the switch-out,
// which is not correct (eg, when focus punching a switching opponent,
// the correct order is switch -> tightening focus message -> attack
// fires, not focus -> switch -> attack). so, we have to specifically
// unshift pursuit when there are other pokemon commands after it, as
// well as order it before any Pokemon commands, otherwise it won't go first.
const remainingMoves = moveOrder.slice(moveOrder.findIndex(mo => mo === o) + 1);
const pendingOpposingPokemonCommands = remainingMoves.filter(o =>
this.scene.currentBattle.turnCommands[o]!.command === Command.POKEMON
&& (pokemon.isPlayer() ? o >= BattlerIndex.ENEMY : o < BattlerIndex.ENEMY)
);
const arePokemonCommandsLeftInQueue = Boolean(pendingOpposingPokemonCommands.length);
const addPhase = (
queuedMove.move === Moves.PURSUIT && arePokemonCommandsLeftInQueue
? this.scene.unshiftPhase
: this.scene.pushPhase
).bind(this.scene);
// pursuit also hits the first pokemon to switch out in doubles,
// regardless of original target
const targets = queuedMove.move === Moves.PURSUIT && arePokemonCommandsLeftInQueue
? [pendingOpposingPokemonCommands[0]]
: turnCommand.targets || turnCommand.move!.targets;
if (pokemon.isPlayer()) { if (pokemon.isPlayer()) {
if (turnCommand.cursor === -1) { if (turnCommand.cursor === -1) {
this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move));//TODO: is the bang correct here? addPhase(new MovePhase(this.scene, pokemon, targets, move));
} else { } else {
const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP);//TODO: is the bang correct here? const playerPhase = new MovePhase(this.scene, pokemon, targets, move, false, queuedMove.ignorePP);
this.scene.pushPhase(playerPhase); addPhase(playerPhase);
} }
} else { } else {
this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP));//TODO: is the bang correct here? addPhase(new MovePhase(this.scene, pokemon, targets, move, false, queuedMove.ignorePP));
} }
break; break;
case Command.BALL: case Command.BALL:
this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here? this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here?
break; break;
case Command.POKEMON: case Command.POKEMON:
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));//TODO: is the bang correct here? pokemon.addTag(BattlerTagType.ESCAPING);
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));
break; break;
case Command.RUN: case Command.RUN:
let runningPokemon = pokemon; let runningPokemon = pokemon;
@ -4694,8 +4730,9 @@ export class SwitchPhase extends BattlePhase {
* @param fieldIndex Field index to switch out * @param fieldIndex Field index to switch out
* @param isModal Indicates if the switch should be forced (true) or is * @param isModal Indicates if the switch should be forced (true) or is
* optional (false). * optional (false).
* @param doReturn Indicates if the party member on the field should be * @param doReturn Indicates if this switch should call back the pokemon at
* recalled to ball or has already left the field. Passed to {@linkcode SwitchSummonPhase}. * the {@linkcode fieldIndex} (true), or if the mon has already been recalled
* (false).
*/ */
constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) { constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) {
super(scene); super(scene);
@ -4723,7 +4760,9 @@ export class SwitchPhase extends BattlePhase {
} }
// Check if there is any space still in field // Check if there is any space still in field
if (this.isModal && this.scene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length >= this.scene.currentBattle.getBattlerCount()) { const numActiveBattlers = this.scene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length;
const willReturnModifer = (this.doReturn ? 1 : 0); // need to subtract this if doReturn is true, because the pokemon in the given index hasn't left the field yet. (used for volt switch + pursuit, etc)
if (this.isModal && numActiveBattlers - willReturnModifer >= this.scene.currentBattle.getBattlerCount()) {
return super.end(); return super.end();
} }