mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-03 23:12:20 +02:00
Merge 5c1bfb4110
into a54cd953a6
This commit is contained in:
commit
0336801b2f
@ -12,22 +12,24 @@ import { BattlerIndex } from "#enums/battler-index";
|
|||||||
import { TrickRoomTag } from "#app/data/arena-tag";
|
import { TrickRoomTag } from "#app/data/arena-tag";
|
||||||
import { SwitchType } from "#enums/switch-type";
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { TurnCommand } from "#app/battle";
|
||||||
|
|
||||||
export class TurnStartPhase extends FieldPhase {
|
export class TurnStartPhase extends FieldPhase {
|
||||||
public readonly phaseName = "TurnStartPhase";
|
public readonly phaseName = "TurnStartPhase";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array.
|
* This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array.
|
||||||
* It also checks for Trick Room and reverses the array if it is present.
|
* It also checks for Trick Room and reverses the array if it is present.
|
||||||
* @returns {@linkcode BattlerIndex[]} the battle indices of all pokemon on the field ordered by speed
|
* @returns An array of {@linkcode BattlerIndex}es containing all on-field Pokemon sorted in speed order.
|
||||||
*/
|
*/
|
||||||
getSpeedOrder(): BattlerIndex[] {
|
getSpeedOrder(): BattlerIndex[] {
|
||||||
const playerField = globalScene.getPlayerField().filter(p => p.isActive()) as Pokemon[];
|
const playerField = globalScene.getPlayerField().filter(p => p.isActive());
|
||||||
const enemyField = globalScene.getEnemyField().filter(p => p.isActive()) as Pokemon[];
|
const enemyField = globalScene.getEnemyField().filter(p => p.isActive());
|
||||||
|
|
||||||
// We shuffle the list before sorting so speed ties produce random results
|
// Shuffle the list before sorting so speed ties produce random results
|
||||||
let orderedTargets: Pokemon[] = playerField.concat(enemyField);
|
// This is seeded with the current turn to prevent turn order varying
|
||||||
// We seed it with the current turn to prevent an inconsistency where it
|
// based on how long since you last reloaded.
|
||||||
// was varying based on how long since you last reloaded
|
let orderedTargets = (playerField as Pokemon[]).concat(enemyField);
|
||||||
globalScene.executeWithSeedOffset(
|
globalScene.executeWithSeedOffset(
|
||||||
() => {
|
() => {
|
||||||
orderedTargets = randSeedShuffle(orderedTargets);
|
orderedTargets = randSeedShuffle(orderedTargets);
|
||||||
@ -36,25 +38,25 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
globalScene.waveSeed,
|
globalScene.waveSeed,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Next, a check for Trick Room is applied to determine sort order.
|
// Check for Trick Room and reverse sort order if active.
|
||||||
|
// Notably, Pokerogue does NOT have the "outspeed trick room" glitch at >1809 spd.
|
||||||
const speedReversed = new BooleanHolder(false);
|
const speedReversed = new BooleanHolder(false);
|
||||||
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
|
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
|
||||||
|
|
||||||
// Adjust the sort function based on whether Trick Room is active.
|
|
||||||
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
||||||
const aSpeed = a?.getEffectiveStat(Stat.SPD) ?? 0;
|
const aSpeed = a.getEffectiveStat(Stat.SPD);
|
||||||
const bSpeed = b?.getEffectiveStat(Stat.SPD) ?? 0;
|
const bSpeed = b.getEffectiveStat(Stat.SPD);
|
||||||
|
|
||||||
return speedReversed.value ? aSpeed - bSpeed : bSpeed - aSpeed;
|
return speedReversed.value ? aSpeed - bSpeed : bSpeed - aSpeed;
|
||||||
});
|
});
|
||||||
|
|
||||||
return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER));
|
return orderedTargets.map(t => t.getFieldIndex() + (t.isEnemy() ? BattlerIndex.ENEMY : BattlerIndex.PLAYER));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This takes the result of getSpeedOrder and applies priority / bypass speed attributes to it.
|
* This takes the result of getSpeedOrder and applies priority / bypass speed attributes to it.
|
||||||
* This also considers the priority levels of various commands and changes the result of getSpeedOrder based on such.
|
* This also considers the priority levels of various commands and changes the result of `getSpeedOrder` based on such.
|
||||||
* @returns {@linkcode BattlerIndex[]} the final sequence of commands for this turn
|
* @returns An array of {@linkcode BattlerIndex}es containing all on-field Pokemon sorted in action order.
|
||||||
*/
|
*/
|
||||||
getCommandOrder(): BattlerIndex[] {
|
getCommandOrder(): BattlerIndex[] {
|
||||||
let moveOrder = this.getSpeedOrder();
|
let moveOrder = this.getSpeedOrder();
|
||||||
@ -115,7 +117,8 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no difference between the move's calculated priorities, the game checks for differences in battlerBypassSpeed and returns the result.
|
// If there is no difference between the move's calculated priorities,
|
||||||
|
// check for differences in battlerBypassSpeed and returns the result.
|
||||||
if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) {
|
if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) {
|
||||||
return battlerBypassSpeed[a].value ? -1 : 1;
|
return battlerBypassSpeed[a].value ? -1 : 1;
|
||||||
}
|
}
|
||||||
@ -136,8 +139,6 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
const field = globalScene.getField();
|
const field = globalScene.getField();
|
||||||
const moveOrder = this.getCommandOrder();
|
const moveOrder = this.getCommandOrder();
|
||||||
|
|
||||||
let orderIndex = 0;
|
|
||||||
|
|
||||||
for (const o of this.getSpeedOrder()) {
|
for (const o of this.getSpeedOrder()) {
|
||||||
const pokemon = field[o];
|
const pokemon = field[o];
|
||||||
const preTurnCommand = globalScene.currentBattle.preTurnCommands[o];
|
const preTurnCommand = globalScene.currentBattle.preTurnCommands[o];
|
||||||
@ -154,90 +155,22 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
|
|
||||||
const phaseManager = globalScene.phaseManager;
|
const phaseManager = globalScene.phaseManager;
|
||||||
|
|
||||||
for (const o of moveOrder) {
|
moveOrder.forEach((o, index) => {
|
||||||
const pokemon = field[o];
|
const pokemon = field[o];
|
||||||
const turnCommand = globalScene.currentBattle.turnCommands[o];
|
const turnCommand = globalScene.currentBattle.turnCommands[o];
|
||||||
|
|
||||||
if (turnCommand?.skip) {
|
if (!turnCommand || turnCommand.skip) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (turnCommand?.command) {
|
|
||||||
case Command.FIGHT: {
|
|
||||||
const queuedMove = turnCommand.move;
|
|
||||||
pokemon.turnData.order = orderIndex++;
|
|
||||||
if (!queuedMove) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const move =
|
|
||||||
pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) ??
|
|
||||||
new PokemonMove(queuedMove.move);
|
|
||||||
if (move.getMove().hasAttr("MoveHeaderAttr")) {
|
|
||||||
phaseManager.unshiftNew("MoveHeaderPhase", pokemon, move);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pokemon.isPlayer() && turnCommand.cursor === -1) {
|
|
||||||
phaseManager.pushNew(
|
|
||||||
"MovePhase",
|
|
||||||
pokemon,
|
|
||||||
turnCommand.targets || turnCommand.move!.targets,
|
|
||||||
move,
|
|
||||||
turnCommand.move!.useMode,
|
|
||||||
); //TODO: is the bang correct here?
|
|
||||||
} else {
|
|
||||||
phaseManager.pushNew(
|
|
||||||
"MovePhase",
|
|
||||||
pokemon,
|
|
||||||
turnCommand.targets || turnCommand.move!.targets,
|
|
||||||
move,
|
|
||||||
queuedMove.useMode,
|
|
||||||
); // TODO: is the bang correct here?
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Command.BALL:
|
|
||||||
phaseManager.unshiftNew("AttemptCapturePhase", turnCommand.targets![0] % 2, turnCommand.cursor!); //TODO: is the bang correct here?
|
|
||||||
break;
|
|
||||||
case Command.POKEMON:
|
|
||||||
{
|
|
||||||
const switchType = turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH;
|
|
||||||
phaseManager.unshiftNew(
|
|
||||||
"SwitchSummonPhase",
|
|
||||||
switchType,
|
|
||||||
pokemon.getFieldIndex(),
|
|
||||||
turnCommand.cursor!,
|
|
||||||
true,
|
|
||||||
pokemon.isPlayer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Command.RUN:
|
|
||||||
{
|
|
||||||
let runningPokemon = pokemon;
|
|
||||||
if (globalScene.currentBattle.double) {
|
|
||||||
const playerActivePokemon = field.filter(pokemon => {
|
|
||||||
if (pokemon) {
|
|
||||||
return pokemon.isPlayer() && pokemon.isActive();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove `turnData.order` -
|
||||||
|
// it is used exclusively for Fusion Flare/Bolt
|
||||||
|
// and uses a really jank implementation
|
||||||
|
if (turnCommand.command === Command.FIGHT) {
|
||||||
|
pokemon.turnData.order = index;
|
||||||
|
}
|
||||||
|
this.handleTurnCommand(turnCommand, pokemon);
|
||||||
});
|
});
|
||||||
// if only one pokemon is alive, use that one
|
|
||||||
if (playerActivePokemon.length > 1) {
|
|
||||||
// find which active pokemon has faster speed
|
|
||||||
const fasterPokemon =
|
|
||||||
playerActivePokemon[0].getStat(Stat.SPD) > playerActivePokemon[1].getStat(Stat.SPD)
|
|
||||||
? playerActivePokemon[0]
|
|
||||||
: playerActivePokemon[1];
|
|
||||||
// check if either active pokemon has the ability "Run Away"
|
|
||||||
const hasRunAway = playerActivePokemon.find(p => p.hasAbility(AbilityId.RUN_AWAY));
|
|
||||||
runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
phaseManager.unshiftNew("AttemptRunPhase", runningPokemon.getFieldIndex());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
phaseManager.pushNew("WeatherEffectPhase");
|
phaseManager.pushNew("WeatherEffectPhase");
|
||||||
phaseManager.pushNew("BerryPhase");
|
phaseManager.pushNew("BerryPhase");
|
||||||
@ -254,4 +187,67 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
*/
|
*/
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleTurnCommand(turnCommand: TurnCommand, pokemon: Pokemon) {
|
||||||
|
switch (turnCommand?.command) {
|
||||||
|
case Command.FIGHT:
|
||||||
|
this.handleFightCommand(turnCommand, pokemon);
|
||||||
|
break;
|
||||||
|
case Command.BALL:
|
||||||
|
globalScene.phaseManager.unshiftNew("AttemptCapturePhase", turnCommand.targets![0] % 2, turnCommand.cursor!); //TODO: is the bang correct here?
|
||||||
|
break;
|
||||||
|
case Command.POKEMON:
|
||||||
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"SwitchSummonPhase",
|
||||||
|
turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH,
|
||||||
|
pokemon.getFieldIndex(),
|
||||||
|
turnCommand.cursor!, // TODO: Is this bang correct?
|
||||||
|
true,
|
||||||
|
pokemon.isPlayer(),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case Command.RUN:
|
||||||
|
{
|
||||||
|
const playerActivePokemon = globalScene.getPokemonAllowedInBattle();
|
||||||
|
if (!globalScene.currentBattle.double || playerActivePokemon.length === 1) {
|
||||||
|
// If not in doubles, attempt to run with the currently active Pokemon.
|
||||||
|
globalScene.phaseManager.unshiftNew("AttemptRunPhase", pokemon.getFieldIndex());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the fastest first pokemon we find with Run Away, or else the faster of the 2 player pokemon.
|
||||||
|
// This intentionally does not check for Trick Room.
|
||||||
|
// TODO: This phase should not take a pokemon at all
|
||||||
|
const sortedPkmn = playerActivePokemon.sort((p1, p2) => p1.getStat(Stat.SPD) - p2.getStat(Stat.SPD));
|
||||||
|
const runningPokemon = sortedPkmn.find(p => p.hasAbility(AbilityId.RUN_AWAY)) ?? sortedPkmn[0];
|
||||||
|
|
||||||
|
globalScene.phaseManager.unshiftNew("AttemptRunPhase", runningPokemon.getFieldIndex());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleFightCommand(turnCommand: TurnCommand, pokemon: Pokemon) {
|
||||||
|
const queuedMove = turnCommand.move;
|
||||||
|
if (!queuedMove) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This seems somewhat dubious
|
||||||
|
const move =
|
||||||
|
pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) ??
|
||||||
|
new PokemonMove(queuedMove.move);
|
||||||
|
|
||||||
|
if (move.getMove().hasAttr("MoveHeaderAttr")) {
|
||||||
|
globalScene.phaseManager.unshiftNew("MoveHeaderPhase", pokemon, move);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScene.phaseManager.pushNew(
|
||||||
|
"MovePhase",
|
||||||
|
pokemon,
|
||||||
|
turnCommand.targets ?? queuedMove.targets,
|
||||||
|
move,
|
||||||
|
queuedMove.useMode,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user