mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-16 14:55:22 +01:00
[Misc] Add dynamic ordering to non-phase interactions (#6581)
* Update interactions to use generator * Fix triggerWeatherBasedFormChanges * Update documentation * Fix arena tag file * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
This commit is contained in:
parent
b88cde162a
commit
c048b34425
@ -803,6 +803,10 @@ export class BattleScene extends SceneBase {
|
|||||||
* @param activeOnly - Whether to consider only active pokemon (as described by {@linkcode Pokemon.isActive()}); default `false`.
|
* @param activeOnly - Whether to consider only active pokemon (as described by {@linkcode Pokemon.isActive()}); default `false`.
|
||||||
* If `true`, will also remove all `null` values from the array.
|
* If `true`, will also remove all `null` values from the array.
|
||||||
* @returns An array of {@linkcode Pokemon}, as described above.
|
* @returns An array of {@linkcode Pokemon}, as described above.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* This should *only* be used in instances where speed order is not relevant.
|
||||||
|
* If speed order matters, use {@linkcode inSpeedOrder}.
|
||||||
*/
|
*/
|
||||||
public getField(activeOnly = false): Pokemon[] {
|
public getField(activeOnly = false): Pokemon[] {
|
||||||
const ret: Pokemon[] = new Array(4).fill(null);
|
const ret: Pokemon[] = new Array(4).fill(null);
|
||||||
|
|||||||
@ -66,6 +66,7 @@ import type { Constructor } from "#types/common";
|
|||||||
import type { Closed, Exact } from "#types/type-helpers";
|
import type { Closed, Exact } from "#types/type-helpers";
|
||||||
import { coerceArray } from "#utils/array";
|
import { coerceArray } from "#utils/array";
|
||||||
import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import { toCamelCase } from "#utils/strings";
|
import { toCamelCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
@ -2857,7 +2858,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
for (const opponent of pokemon.getOpponentsGenerator()) {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
if (this.intimidate) {
|
if (this.intimidate) {
|
||||||
const params: AbAttrParamsWithCancel = { pokemon: opponent, cancelled, simulated };
|
const params: AbAttrParamsWithCancel = { pokemon: opponent, cancelled, simulated };
|
||||||
@ -3168,10 +3169,8 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt
|
|||||||
if (simulated) {
|
if (simulated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
|
||||||
const allowedParty = party.filter(p => p.isAllowedInBattle());
|
|
||||||
|
|
||||||
for (const partyPokemon of allowedParty) {
|
for (const partyPokemon of pokemon.getAlliesGenerator()) {
|
||||||
if (partyPokemon.status && this.statusEffect.includes(partyPokemon.status.effect)) {
|
if (partyPokemon.status && this.statusEffect.includes(partyPokemon.status.effect)) {
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
getStatusEffectHealText(partyPokemon.status.effect, getPokemonNameWithAffix(partyPokemon)),
|
getStatusEffectHealText(partyPokemon.status.effect, getPokemonNameWithAffix(partyPokemon)),
|
||||||
@ -4423,7 +4422,7 @@ export class FriskAbAttr extends PostSummonAbAttr {
|
|||||||
|
|
||||||
override apply({ simulated, pokemon }: AbAttrBaseParams): void {
|
override apply({ simulated, pokemon }: AbAttrBaseParams): void {
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
for (const opponent of pokemon.getOpponentsGenerator()) {
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("abilityTriggers:frisk", {
|
i18next.t("abilityTriggers:frisk", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
@ -4992,7 +4991,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const opp of pokemon.getOpponents()) {
|
for (const opp of pokemon.getOpponentsGenerator()) {
|
||||||
if ((opp.status?.effect !== StatusEffect.SLEEP && !opp.hasAbility(AbilityId.COMATOSE)) || opp.switchOutStatus) {
|
if ((opp.status?.effect !== StatusEffect.SLEEP && !opp.hasAbility(AbilityId.COMATOSE)) || opp.switchOutStatus) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -5545,10 +5544,9 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
// TODO: This should be in speed order
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
globalScene
|
applyAbAttrs("FieldPreventExplosiveMovesAbAttr", { pokemon: p, cancelled, simulated });
|
||||||
.getField(true)
|
}
|
||||||
.forEach(p => applyAbAttrs("FieldPreventExplosiveMovesAbAttr", { pokemon: p, cancelled, simulated }));
|
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -78,6 +78,7 @@ import type {
|
|||||||
} from "#types/arena-tags";
|
} from "#types/arena-tags";
|
||||||
import type { Mutable } from "#types/type-helpers";
|
import type { Mutable } from "#types/type-helpers";
|
||||||
import { BooleanHolder, type NumberHolder, toDmgValue } from "#utils/common";
|
import { BooleanHolder, type NumberHolder, toDmgValue } from "#utils/common";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
/** Interface containing the serializable fields of ArenaTagData. */
|
/** Interface containing the serializable fields of ArenaTagData. */
|
||||||
@ -247,7 +248,7 @@ export abstract class ArenaTag implements BaseArenaTag {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function that retrieves the Pokemon affected.
|
* Helper function that retrieves the Pokemon affected.
|
||||||
* @returns An array containing all {@linkcode Pokemon} affected by this Tag.
|
* @returns An array containing all {@linkcode Pokemon} affected by this Tag, not in speed order.
|
||||||
*/
|
*/
|
||||||
protected getAffectedPokemon(): Pokemon[] {
|
protected getAffectedPokemon(): Pokemon[] {
|
||||||
switch (this.side) {
|
switch (this.side) {
|
||||||
@ -1204,7 +1205,7 @@ export class GravityTag extends SerializableArenaTag {
|
|||||||
|
|
||||||
onAdd(quiet = false): void {
|
onAdd(quiet = false): void {
|
||||||
super.onAdd(quiet);
|
super.onAdd(quiet);
|
||||||
globalScene.getField(true).forEach(pokemon => {
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
if (pokemon !== null) {
|
if (pokemon !== null) {
|
||||||
pokemon.removeTag(BattlerTagType.FLOATING);
|
pokemon.removeTag(BattlerTagType.FLOATING);
|
||||||
pokemon.removeTag(BattlerTagType.TELEKINESIS);
|
pokemon.removeTag(BattlerTagType.TELEKINESIS);
|
||||||
@ -1212,7 +1213,7 @@ export class GravityTag extends SerializableArenaTag {
|
|||||||
pokemon.addTag(BattlerTagType.INTERRUPTED);
|
pokemon.addTag(BattlerTagType.INTERRUPTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1237,10 +1238,13 @@ class TailwindTag extends SerializableArenaTag {
|
|||||||
|
|
||||||
onAdd(quiet = false): void {
|
onAdd(quiet = false): void {
|
||||||
super.onAdd(quiet);
|
super.onAdd(quiet);
|
||||||
|
const source = this.getSourcePokemon();
|
||||||
|
|
||||||
const field = this.getAffectedPokemon();
|
if (source == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const pokemon of field) {
|
for (const pokemon of source.getAlliesGenerator()) {
|
||||||
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
||||||
// TODO: This should not be handled here
|
// TODO: This should not be handled here
|
||||||
if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
||||||
@ -1346,11 +1350,11 @@ class FireGrassPledgeTag extends SerializableArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override lapse(): boolean {
|
override lapse(): boolean {
|
||||||
const field = this.getAffectedPokemon().filter(
|
for (const pokemon of inSpeedOrder(this.side)) {
|
||||||
pokemon => !pokemon.isOfType(PokemonType.FIRE, true, true) && !pokemon.switchOutStatus,
|
if (pokemon.isOfType(PokemonType.FIRE) || pokemon.switchOutStatus) {
|
||||||
);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
field.forEach(pokemon => {
|
|
||||||
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:fireGrassPledgeLapse", {
|
i18next.t("arenaTag:fireGrassPledgeLapse", {
|
||||||
@ -1365,7 +1369,7 @@ class FireGrassPledgeTag extends SerializableArenaTag {
|
|||||||
CommonAnim.MAGMA_STORM,
|
CommonAnim.MAGMA_STORM,
|
||||||
);
|
);
|
||||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||||
});
|
}
|
||||||
|
|
||||||
return super.lapse();
|
return super.lapse();
|
||||||
}
|
}
|
||||||
@ -1497,7 +1501,7 @@ export class SuppressAbilitiesTag extends SerializableArenaTag {
|
|||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
this.playActivationMessage(pokemon);
|
this.playActivationMessage(pokemon);
|
||||||
|
|
||||||
for (const fieldPokemon of globalScene.getField(true)) {
|
for (const fieldPokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
if (fieldPokemon.id !== pokemon.id) {
|
if (fieldPokemon.id !== pokemon.id) {
|
||||||
// TODO: investigate whether we can just remove the foreach and call `applyAbAttrs` directly, providing
|
// TODO: investigate whether we can just remove the foreach and call `applyAbAttrs` directly, providing
|
||||||
// the appropriate attributes (preLEaveField and IllusionBreak)
|
// the appropriate attributes (preLEaveField and IllusionBreak)
|
||||||
@ -1539,7 +1543,7 @@ export class SuppressAbilitiesTag extends SerializableArenaTag {
|
|||||||
this.#beingRemoved = true;
|
this.#beingRemoved = true;
|
||||||
super.onRemove(quiet);
|
super.onRemove(quiet);
|
||||||
|
|
||||||
for (const pokemon of globalScene.getField(true)) {
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
||||||
if (!pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) {
|
if (!pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) {
|
||||||
[true, false].forEach(passive => {
|
[true, false].forEach(passive => {
|
||||||
|
|||||||
@ -98,6 +98,7 @@ import { areAllies } from "#utils/pokemon-utils";
|
|||||||
import { toCamelCase, toTitleCase } from "#utils/strings";
|
import { toCamelCase, toTitleCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import { canSpeciesTera, willTerastallize } from "#utils/pokemon-utils";
|
import { canSpeciesTera, willTerastallize } from "#utils/pokemon-utils";
|
||||||
import type { ReadonlyGenericUint8Array } from "#types/typed-arrays";
|
import type { ReadonlyGenericUint8Array } from "#types/typed-arrays";
|
||||||
|
|
||||||
@ -1031,8 +1032,9 @@ export abstract class Move implements Localizable {
|
|||||||
aura.apply({pokemon: source, simulated, opponent: target, move: this, power});
|
aura.apply({pokemon: source, simulated, opponent: target, move: this, power});
|
||||||
}
|
}
|
||||||
|
|
||||||
const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
for (const p of source.getAlliesGenerator()) {
|
||||||
alliedField.forEach(p => applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", {pokemon: p, opponent: target, move: this, simulated, power}));
|
applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", {pokemon: p, opponent: target, move: this, simulated, power});
|
||||||
|
}
|
||||||
|
|
||||||
power.value *= typeChangeMovePowerMultiplier.value;
|
power.value *= typeChangeMovePowerMultiplier.value;
|
||||||
|
|
||||||
@ -6218,8 +6220,10 @@ export class RemoveAllSubstitutesAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene.getField(true).forEach(pokemon =>
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
pokemon.findAndRemoveTags(tag => tag.tagType === BattlerTagType.SUBSTITUTE));
|
pokemon.findAndRemoveTags(tag => tag.tagType === BattlerTagType.SUBSTITUTE);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8156,7 +8160,9 @@ const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
|
|||||||
// temporary workaround to prevent displaying the message during enemy command phase
|
// temporary workaround to prevent displaying the message during enemy command phase
|
||||||
// TODO: either move this, or make the move condition func have a `simulated` param
|
// TODO: either move this, or make the move condition func have a `simulated` param
|
||||||
const simulated = globalScene.phaseManager.getCurrentPhase()?.is('EnemyCommandPhase');
|
const simulated = globalScene.phaseManager.getCurrentPhase()?.is('EnemyCommandPhase');
|
||||||
globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled, simulated}));
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
|
applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled, simulated});
|
||||||
|
}
|
||||||
// Queue a message if an ability prevented usage of the move
|
// Queue a message if an ability prevented usage of the move
|
||||||
if (!simulated && cancelled.value) {
|
if (!simulated && cancelled.value) {
|
||||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }));
|
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }));
|
||||||
|
|||||||
@ -40,6 +40,7 @@ import type { Constructor } from "#types/common";
|
|||||||
import type { AbstractConstructor } from "#types/type-helpers";
|
import type { AbstractConstructor } from "#types/type-helpers";
|
||||||
import { NumberHolder, randSeedInt } from "#utils/common";
|
import { NumberHolder, randSeedInt } from "#utils/common";
|
||||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
|
|
||||||
export class Arena {
|
export class Arena {
|
||||||
public biomeType: BiomeId;
|
public biomeType: BiomeId;
|
||||||
@ -358,15 +359,12 @@ export class Arena {
|
|||||||
globalScene.phaseManager.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct?
|
globalScene.phaseManager.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct?
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
.getField(true)
|
pokemon.findAndRemoveTags(
|
||||||
.filter(p => p.isOnField())
|
tag => "weatherTypes" in tag && !(tag.weatherTypes as WeatherType[]).find(t => t === weather),
|
||||||
.map(pokemon => {
|
);
|
||||||
pokemon.findAndRemoveTags(
|
applyAbAttrs("PostWeatherChangeAbAttr", { pokemon, weather });
|
||||||
t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather),
|
}
|
||||||
);
|
|
||||||
applyAbAttrs("PostWeatherChangeAbAttr", { pokemon, weather });
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -376,11 +374,11 @@ export class Arena {
|
|||||||
* @param source - The Pokemon causing the changes by removing itself from the field
|
* @param source - The Pokemon causing the changes by removing itself from the field
|
||||||
*/
|
*/
|
||||||
triggerWeatherBasedFormChanges(source?: Pokemon): void {
|
triggerWeatherBasedFormChanges(source?: Pokemon): void {
|
||||||
globalScene.getField(true).forEach(p => {
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
// TODO - This is a bandaid. Abilities leaving the field needs a better approach than
|
// TODO - This is a bandaid. Abilities leaving the field needs a better approach than
|
||||||
// calling this method for every switch out that happens
|
// calling this method for every switch out that happens
|
||||||
if (p === source) {
|
if (p === source) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
const isCastformWithForecast = p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM;
|
const isCastformWithForecast = p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM;
|
||||||
const isCherrimWithFlowerGift = p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM;
|
const isCherrimWithFlowerGift = p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM;
|
||||||
@ -388,23 +386,23 @@ export class Arena {
|
|||||||
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
||||||
globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger);
|
globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to trigger all weather based form changes back into their normal forms
|
* Function to trigger all weather based form changes back into their normal forms
|
||||||
*/
|
*/
|
||||||
triggerWeatherBasedFormChangesToNormal(): void {
|
triggerWeatherBasedFormChangesToNormal(): void {
|
||||||
globalScene.getField(true).forEach(p => {
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
const isCastformWithForecast =
|
const isCastformWithForecast =
|
||||||
p.hasAbility(AbilityId.FORECAST, false, true) && p.species.speciesId === SpeciesId.CASTFORM;
|
p.hasAbility(AbilityId.FORECAST, false, true) && p.species.speciesId === SpeciesId.CASTFORM;
|
||||||
const isCherrimWithFlowerGift =
|
const isCherrimWithFlowerGift =
|
||||||
p.hasAbility(AbilityId.FLOWER_GIFT, false, true) && p.species.speciesId === SpeciesId.CHERRIM;
|
p.hasAbility(AbilityId.FLOWER_GIFT, false, true) && p.species.speciesId === SpeciesId.CHERRIM;
|
||||||
|
|
||||||
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
||||||
return globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger);
|
globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether or not the terrain can be set to {@linkcode terrain} */
|
/** Returns whether or not the terrain can be set to {@linkcode terrain} */
|
||||||
@ -453,16 +451,13 @@ export class Arena {
|
|||||||
globalScene.phaseManager.queueMessage(getTerrainClearMessage(oldTerrainType));
|
globalScene.phaseManager.queueMessage(getTerrainClearMessage(oldTerrainType));
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
.getField(true)
|
pokemon.findAndRemoveTags(
|
||||||
.filter(p => p.isOnField())
|
t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain),
|
||||||
.map(pokemon => {
|
);
|
||||||
pokemon.findAndRemoveTags(
|
applyAbAttrs("PostTerrainChangeAbAttr", { pokemon, terrain });
|
||||||
t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain),
|
applyAbAttrs("TerrainEventTypeChangeAbAttr", { pokemon });
|
||||||
);
|
}
|
||||||
applyAbAttrs("PostTerrainChangeAbAttr", { pokemon, terrain });
|
|
||||||
applyAbAttrs("TerrainEventTypeChangeAbAttr", { pokemon });
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,6 +173,7 @@ import {
|
|||||||
import { calculateBossSegmentDamage } from "#utils/damage";
|
import { calculateBossSegmentDamage } from "#utils/damage";
|
||||||
import { getEnumValues } from "#utils/enums";
|
import { getEnumValues } from "#utils/enums";
|
||||||
import { getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
|
import { getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import { argbFromRgba, QuantizerCelebi, rgbaFromArgb } from "@material/material-color-utilities";
|
import { argbFromRgba, QuantizerCelebi, rgbaFromArgb } from "@material/material-color-utilities";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
@ -2347,15 +2348,14 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/** Holds whether the pokemon is trapped due to an ability */
|
/** Holds whether the pokemon is trapped due to an ability */
|
||||||
const trapped = new BooleanHolder(false);
|
const trapped = new BooleanHolder(false);
|
||||||
/**
|
for (const opponent of inSpeedOrder(this.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER)) {
|
||||||
* Contains opposing Pokemon (Enemy/Player Pokemon) depending on perspective
|
if (opponent.switchOutStatus === false) {
|
||||||
* Afterwards, it filters out Pokemon that have been switched out of the field so trapped abilities/moves do not trigger
|
applyAbAttrs(
|
||||||
*/
|
"CheckTrappedAbAttr",
|
||||||
const opposingFieldUnfiltered = this.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField();
|
{ pokemon: opponent, trapped, opponent: this, simulated },
|
||||||
const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false);
|
trappedAbMessages,
|
||||||
|
);
|
||||||
for (const opponent of opposingField) {
|
}
|
||||||
applyAbAttrs("CheckTrappedAbAttr", { pokemon: opponent, trapped, opponent: this, simulated }, trappedAbMessages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
@ -2468,8 +2468,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
// Do not check queenly majesty unless this is being simulated
|
// Do not check queenly majesty unless this is being simulated
|
||||||
// This is because the move effect phase should not check queenly majesty, as that is handled by the move phase
|
// This is because the move effect phase should not check queenly majesty, as that is handled by the move phase
|
||||||
if (simulated && !cancelledHolder.value) {
|
if (simulated && !cancelledHolder.value) {
|
||||||
const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
for (const p of this.getAlliesGenerator()) {
|
||||||
defendingSidePlayField.forEach((p: (typeof defendingSidePlayField)[0]) => {
|
|
||||||
applyAbAttrs("FieldPriorityMoveImmunityAbAttr", {
|
applyAbAttrs("FieldPriorityMoveImmunityAbAttr", {
|
||||||
pokemon: p,
|
pokemon: p,
|
||||||
opponent: source,
|
opponent: source,
|
||||||
@ -2477,7 +2476,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
cancelled: cancelledHolder,
|
cancelled: cancelledHolder,
|
||||||
simulated,
|
simulated,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3243,7 +3242,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the pokemon that oppose this one and are active
|
* Returns the pokemon that oppose this one and are active in non-speed order
|
||||||
*
|
*
|
||||||
* @param onField - whether to also check if the pokemon is currently on the field (defaults to true)
|
* @param onField - whether to also check if the pokemon is currently on the field (defaults to true)
|
||||||
*/
|
*/
|
||||||
@ -3253,6 +3252,13 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns A generator of pokemon that oppose this one in speed order
|
||||||
|
*/
|
||||||
|
public getOpponentsGenerator(): Generator<Pokemon, number> {
|
||||||
|
return inSpeedOrder(this.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER);
|
||||||
|
}
|
||||||
|
|
||||||
getOpponentDescriptor(): string {
|
getOpponentDescriptor(): string {
|
||||||
return this.isPlayer() ? i18next.t("arenaTag:opposingTeam") : i18next.t("arenaTag:yourTeam");
|
return this.isPlayer() ? i18next.t("arenaTag:opposingTeam") : i18next.t("arenaTag:yourTeam");
|
||||||
}
|
}
|
||||||
@ -3262,12 +3268,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Pokémon on the allied field.
|
* @returns A generator of Pokémon on the allied field in speed order.
|
||||||
*
|
|
||||||
* @returns An array of Pokémon on the allied field.
|
|
||||||
*/
|
*/
|
||||||
getAlliedField(): Pokemon[] {
|
getAlliesGenerator(): Generator<Pokemon, number> {
|
||||||
return this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
return inSpeedOrder(this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4020,16 +4024,15 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: stubTag, cancelled, simulated: true });
|
applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: stubTag, cancelled, simulated: true });
|
||||||
|
|
||||||
const userField = this.getAlliedField();
|
for (const pokemon of this.getAlliesGenerator()) {
|
||||||
userField.forEach(pokemon =>
|
|
||||||
applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", {
|
applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", {
|
||||||
pokemon,
|
pokemon,
|
||||||
tag: stubTag,
|
tag: stubTag,
|
||||||
cancelled,
|
cancelled,
|
||||||
simulated: true,
|
simulated: true,
|
||||||
target: this,
|
target: this,
|
||||||
}),
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
}
|
}
|
||||||
@ -4063,7 +4066,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.getAlliedField()) {
|
for (const pokemon of this.getAlliesGenerator()) {
|
||||||
applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { pokemon, tag: newTag, cancelled, target: this });
|
applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { pokemon, tag: newTag, cancelled, target: this });
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
@ -4811,7 +4814,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.getAlliedField()) {
|
for (const pokemon of this.getAlliesGenerator()) {
|
||||||
applyAbAttrs("UserFieldStatusEffectImmunityAbAttr", {
|
applyAbAttrs("UserFieldStatusEffectImmunityAbAttr", {
|
||||||
pokemon,
|
pokemon,
|
||||||
effect,
|
effect,
|
||||||
@ -5619,10 +5622,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
leaveField(clearEffects = true, hideInfo = true, destroy = false) {
|
leaveField(clearEffects = true, hideInfo = true, destroy = false) {
|
||||||
this.resetSprite();
|
this.resetSprite();
|
||||||
this.resetTurnData();
|
this.resetTurnData();
|
||||||
globalScene
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
.getField(true)
|
if (p !== this) {
|
||||||
.filter(p => p !== this)
|
p.removeTagsBySourceId(this.id);
|
||||||
.forEach(p => p.removeTagsBySourceId(this.id));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (clearEffects) {
|
if (clearEffects) {
|
||||||
this.destroySubstitute();
|
this.destroySubstitute();
|
||||||
|
|||||||
@ -1915,9 +1915,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
|
|||||||
// Remove the Pokemon's FAINT status
|
// Remove the Pokemon's FAINT status
|
||||||
pokemon.resetStatus(true, false, true, false);
|
pokemon.resetStatus(true, false, true, false);
|
||||||
|
|
||||||
// Reapply Commander on the Pokemon's side of the field, if applicable
|
for (const p of pokemon.getAlliesGenerator()) {
|
||||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
|
||||||
for (const p of field) {
|
|
||||||
applyAbAttrs("CommanderAbAttr", { pokemon: p });
|
applyAbAttrs("CommanderAbAttr", { pokemon: p });
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { applyAbAttrs } from "#abilities/apply-ab-attrs";
|
import { applyAbAttrs } from "#abilities/apply-ab-attrs";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { FieldPhase } from "#phases/field-phase";
|
import { FieldPhase } from "#phases/field-phase";
|
||||||
import { NumberHolder } from "#utils/common";
|
import { NumberHolder } from "#utils/common";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class AttemptRunPhase extends FieldPhase {
|
export class AttemptRunPhase extends FieldPhase {
|
||||||
@ -15,16 +17,14 @@ export class AttemptRunPhase extends FieldPhase {
|
|||||||
|
|
||||||
// Increment escape attempts count on entry
|
// Increment escape attempts count on entry
|
||||||
const currentAttempts = globalScene.currentBattle.escapeAttempts++;
|
const currentAttempts = globalScene.currentBattle.escapeAttempts++;
|
||||||
|
|
||||||
const activePlayerField = globalScene.getPlayerField(true);
|
|
||||||
const enemyField = globalScene.getEnemyField();
|
const enemyField = globalScene.getEnemyField();
|
||||||
|
|
||||||
const escapeRoll = globalScene.randBattleSeedInt(100);
|
const escapeRoll = globalScene.randBattleSeedInt(100);
|
||||||
const escapeChance = new NumberHolder(this.calculateEscapeChance(currentAttempts));
|
const escapeChance = new NumberHolder(this.calculateEscapeChance(currentAttempts));
|
||||||
|
|
||||||
activePlayerField.forEach(pokemon => {
|
for (const pokemon of inSpeedOrder(ArenaTagSide.PLAYER)) {
|
||||||
applyAbAttrs("RunSuccessAbAttr", { pokemon, chance: escapeChance });
|
applyAbAttrs("RunSuccessAbAttr", { pokemon, chance: escapeChance });
|
||||||
});
|
}
|
||||||
|
|
||||||
if (escapeRoll < escapeChance.value) {
|
if (escapeRoll < escapeChance.value) {
|
||||||
enemyField.forEach(pokemon => applyAbAttrs("PreLeaveFieldAbAttr", { pokemon }));
|
enemyField.forEach(pokemon => applyAbAttrs("PreLeaveFieldAbAttr", { pokemon }));
|
||||||
@ -56,7 +56,7 @@ export class AttemptRunPhase extends FieldPhase {
|
|||||||
|
|
||||||
globalScene.phaseManager.pushNew("NewBattlePhase");
|
globalScene.phaseManager.pushNew("NewBattlePhase");
|
||||||
} else {
|
} else {
|
||||||
activePlayerField.forEach(p => {
|
globalScene.getPlayerField(true).forEach(p => {
|
||||||
p.turnData.failedRunAway = true;
|
p.turnData.failedRunAway = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
|
|
||||||
export class CheckStatusEffectPhase extends Phase {
|
export class CheckStatusEffectPhase extends Phase {
|
||||||
public readonly phaseName = "CheckStatusEffectPhase";
|
public readonly phaseName = "CheckStatusEffectPhase";
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
const field = globalScene.getField();
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
for (const p of field) {
|
if (p.status?.isPostTurn()) {
|
||||||
if (p?.status?.isPostTurn()) {
|
|
||||||
globalScene.phaseManager.unshiftNew("PostTurnStatusEffectPhase", p.getBattlerIndex());
|
globalScene.phaseManager.unshiftNew("PostTurnStatusEffectPhase", p.getBattlerIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { FRIENDSHIP_LOSS_FROM_FAINT } from "#balance/starters";
|
|||||||
import { allMoves } from "#data/data-lists";
|
import { allMoves } from "#data/data-lists";
|
||||||
import { battleSpecDialogue } from "#data/dialogue";
|
import { battleSpecDialogue } from "#data/dialogue";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers";
|
import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
@ -17,6 +18,7 @@ import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
|
|||||||
import { PokemonInstantReviveModifier } from "#modifiers/modifier";
|
import { PokemonInstantReviveModifier } from "#modifiers/modifier";
|
||||||
import { PokemonMove } from "#moves/pokemon-move";
|
import { PokemonMove } from "#moves/pokemon-move";
|
||||||
import { PokemonPhase } from "#phases/pokemon-phase";
|
import { PokemonPhase } from "#phases/pokemon-phase";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class FaintPhase extends PokemonPhase {
|
export class FaintPhase extends PokemonPhase {
|
||||||
@ -126,8 +128,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
applyAbAttrs("PostFaintAbAttr", { pokemon });
|
applyAbAttrs("PostFaintAbAttr", { pokemon });
|
||||||
}
|
}
|
||||||
|
|
||||||
const alivePlayField = globalScene.getField(true);
|
for (const p of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
for (const p of alivePlayField) {
|
|
||||||
applyAbAttrs("PostKnockOutAbAttr", { pokemon: p, victim: pokemon });
|
applyAbAttrs("PostKnockOutAbAttr", { pokemon: p, victim: pokemon });
|
||||||
}
|
}
|
||||||
if (pokemon.turnData.attacksReceived?.length > 0) {
|
if (pokemon.turnData.attacksReceived?.length > 0) {
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import { BattlePhase } from "#phases/battle-phase";
|
import { BattlePhase } from "#phases/battle-phase";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
|
|
||||||
type PokemonFunc = (pokemon: Pokemon) => void;
|
type PokemonFunc = (pokemon: Pokemon) => void;
|
||||||
|
|
||||||
export abstract class FieldPhase extends BattlePhase {
|
export abstract class FieldPhase extends BattlePhase {
|
||||||
executeForAll(func: PokemonFunc): void {
|
executeForAll(func: PokemonFunc): void {
|
||||||
for (const pokemon of globalScene.getField(true)) {
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
func(pokemon);
|
func(pokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { getStatusEffectActivationText } from "#data/status-effect";
|
|||||||
import { getTerrainBlockMessage } from "#data/terrain";
|
import { getTerrainBlockMessage } from "#data/terrain";
|
||||||
import { getWeatherBlockMessage } from "#data/weather";
|
import { getWeatherBlockMessage } from "#data/weather";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattlerIndex } from "#enums/battler-index";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
@ -35,6 +36,7 @@ import type { TurnMove } from "#types/turn-move";
|
|||||||
import { applyChallenges } from "#utils/challenge-utils";
|
import { applyChallenges } from "#utils/challenge-utils";
|
||||||
import { BooleanHolder, NumberHolder } from "#utils/common";
|
import { BooleanHolder, NumberHolder } from "#utils/common";
|
||||||
import { enumValueToKey } from "#utils/enums";
|
import { enumValueToKey } from "#utils/enums";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class MovePhase extends PokemonPhase {
|
export class MovePhase extends PokemonPhase {
|
||||||
@ -321,17 +323,16 @@ export class MovePhase extends PokemonPhase {
|
|||||||
|
|
||||||
// check move redirection abilities of every pokemon *except* the user.
|
// check move redirection abilities of every pokemon *except* the user.
|
||||||
// TODO: Make storm drain, lightning rod, etc, redirect at this point for type changing moves
|
// TODO: Make storm drain, lightning rod, etc, redirect at this point for type changing moves
|
||||||
globalScene
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
.getField(true)
|
if (pokemon !== this.pokemon) {
|
||||||
.filter(p => p !== this.pokemon)
|
|
||||||
.forEach(pokemon => {
|
|
||||||
applyAbAttrs("RedirectMoveAbAttr", {
|
applyAbAttrs("RedirectMoveAbAttr", {
|
||||||
pokemon,
|
pokemon,
|
||||||
moveId: this.move.moveId,
|
moveId: this.move.moveId,
|
||||||
targetIndex: redirectTarget,
|
targetIndex: redirectTarget,
|
||||||
sourcePokemon: this.pokemon,
|
sourcePokemon: this.pokemon,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */
|
/** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */
|
||||||
let redirectedByAbility = currentTarget !== redirectTarget.value;
|
let redirectedByAbility = currentTarget !== redirectTarget.value;
|
||||||
@ -848,9 +849,9 @@ export class MovePhase extends PokemonPhase {
|
|||||||
// TODO: This needs to go at the end of `MoveEffectPhase` to check move results
|
// TODO: This needs to go at the end of `MoveEffectPhase` to check move results
|
||||||
const dancerModes: MoveUseMode[] = [MoveUseMode.INDIRECT, MoveUseMode.REFLECTED] as const;
|
const dancerModes: MoveUseMode[] = [MoveUseMode.INDIRECT, MoveUseMode.REFLECTED] as const;
|
||||||
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !dancerModes.includes(this.useMode)) {
|
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !dancerModes.includes(this.useMode)) {
|
||||||
globalScene.getField(true).forEach(pokemon => {
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
applyAbAttrs("PostMoveUsedAbAttr", { pokemon, move: this.move, source: user, targets });
|
applyAbAttrs("PostMoveUsedAbAttr", { pokemon, move: this.move, source: user, targets });
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import { getCharVariantFromDialogue } from "#data/dialogue";
|
import { getCharVariantFromDialogue } from "#data/dialogue";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
@ -15,6 +16,7 @@ import { transitionMysteryEncounterIntroVisuals } from "#mystery-encounters/enco
|
|||||||
import type { MysteryEncounterOption, OptionPhaseCallback } from "#mystery-encounters/mystery-encounter-option";
|
import type { MysteryEncounterOption, OptionPhaseCallback } from "#mystery-encounters/mystery-encounter-option";
|
||||||
import { SeenEncounterData } from "#mystery-encounters/mystery-encounter-save-data";
|
import { SeenEncounterData } from "#mystery-encounters/mystery-encounter-save-data";
|
||||||
import { randSeedItem } from "#utils/common";
|
import { randSeedItem } from "#utils/common";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,7 +218,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
|
|||||||
|
|
||||||
// Lapse any residual flinches/endures but ignore all other turn-end battle tags
|
// Lapse any residual flinches/endures but ignore all other turn-end battle tags
|
||||||
const includedLapseTags = [BattlerTagType.FLINCHED, BattlerTagType.ENDURING];
|
const includedLapseTags = [BattlerTagType.FLINCHED, BattlerTagType.ENDURING];
|
||||||
globalScene.getField(true).forEach(pokemon => {
|
for (const pokemon of inSpeedOrder(ArenaTagSide.BOTH)) {
|
||||||
const tags = pokemon.summonData.tags;
|
const tags = pokemon.summonData.tags;
|
||||||
tags
|
tags
|
||||||
.filter(
|
.filter(
|
||||||
@ -229,7 +231,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
|
|||||||
t.onRemove(pokemon);
|
t.onRemove(pokemon);
|
||||||
tags.splice(tags.indexOf(t), 1);
|
tags.splice(tags.indexOf(t), 1);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Remove any status tick phases
|
// Remove any status tick phases
|
||||||
globalScene.phaseManager.removeAllPhasesOfType("PostTurnStatusEffectPhase");
|
globalScene.phaseManager.removeAllPhasesOfType("PostTurnStatusEffectPhase");
|
||||||
@ -427,7 +429,9 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) {
|
if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) {
|
||||||
globalScene.getPlayerField().forEach(pokemon => pokemon.lapseTag(BattlerTagType.COMMANDED));
|
for (const pokemon of inSpeedOrder(ArenaTagSide.PLAYER)) {
|
||||||
|
pokemon.lapseTag(BattlerTagType.COMMANDED);
|
||||||
|
}
|
||||||
globalScene.phaseManager.pushNew("ReturnPhase", 1);
|
globalScene.phaseManager.pushNew("ReturnPhase", 1);
|
||||||
}
|
}
|
||||||
globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", false);
|
globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", false);
|
||||||
|
|||||||
@ -30,8 +30,7 @@ export class PostSummonPhase extends PokemonPhase {
|
|||||||
) {
|
) {
|
||||||
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
||||||
}
|
}
|
||||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField(true) : globalScene.getEnemyField(true);
|
for (const p of pokemon.getAlliesGenerator()) {
|
||||||
for (const p of field) {
|
|
||||||
applyAbAttrs("CommanderAbAttr", { pokemon: p });
|
applyAbAttrs("CommanderAbAttr", { pokemon: p });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -210,7 +210,7 @@ export class StatStageChangePhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stages.value > 0 && this.canBeCopied) {
|
if (stages.value > 0 && this.canBeCopied) {
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
for (const opponent of pokemon.getOpponentsGenerator()) {
|
||||||
applyAbAttrs("StatStageChangeCopyAbAttr", { pokemon: opponent, stats: this.stats, numStages: stages.value });
|
applyAbAttrs("StatStageChangeCopyAbAttr", { pokemon: opponent, stats: this.stats, numStages: stages.value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,12 +5,14 @@ import { SubstituteTag } from "#data/battler-tags";
|
|||||||
import { allMoves } from "#data/data-lists";
|
import { allMoves } from "#data/data-lists";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers";
|
import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers";
|
||||||
import { getPokeballTintColor } from "#data/pokeball";
|
import { getPokeballTintColor } from "#data/pokeball";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { Command } from "#enums/command";
|
import { Command } from "#enums/command";
|
||||||
import { SwitchType } from "#enums/switch-type";
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import { SwitchEffectTransferModifier } from "#modifiers/modifier";
|
import { SwitchEffectTransferModifier } from "#modifiers/modifier";
|
||||||
import { SummonPhase } from "#phases/summon-phase";
|
import { SummonPhase } from "#phases/summon-phase";
|
||||||
|
import { inSpeedOrder } from "#utils/speed-order-generator";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class SwitchSummonPhase extends SummonPhase {
|
export class SwitchSummonPhase extends SummonPhase {
|
||||||
@ -69,9 +71,9 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pokemon = this.getPokemon();
|
const pokemon = this.getPokemon();
|
||||||
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach(enemyPokemon =>
|
for (const enemyPokemon of inSpeedOrder(this.player ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER)) {
|
||||||
enemyPokemon.removeTagsBySourceId(pokemon.id),
|
enemyPokemon.removeTagsBySourceId(pokemon.id);
|
||||||
);
|
}
|
||||||
|
|
||||||
if (this.switchType === SwitchType.SWITCH || this.switchType === SwitchType.INITIAL_SWITCH) {
|
if (this.switchType === SwitchType.SWITCH || this.switchType === SwitchType.INITIAL_SWITCH) {
|
||||||
const substitute = pokemon.getTag(SubstituteTag);
|
const substitute = pokemon.getTag(SubstituteTag);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Pokemon } from "#app/field/pokemon";
|
import type { Pokemon } from "#app/field/pokemon";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { BooleanHolder, randSeedShuffle } from "#app/utils/common";
|
import { BooleanHolder, randSeedShuffle } from "#app/utils/common";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
@ -39,11 +39,16 @@ function shufflePokemonList<T extends Pokemon | hasPokemon>(pokemonList: T[]): T
|
|||||||
return pokemonList;
|
return pokemonList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Type guard for {@linkcode sortBySpeed} to avoid importing {@linkcode Pokemon} */
|
||||||
|
function isPokemon(p: Pokemon | hasPokemon): p is Pokemon {
|
||||||
|
return typeof (p as hasPokemon).getPokemon !== "function";
|
||||||
|
}
|
||||||
|
|
||||||
/** Sorts an array of {@linkcode Pokemon} by speed (without shuffling) */
|
/** Sorts an array of {@linkcode Pokemon} by speed (without shuffling) */
|
||||||
function sortBySpeed<T extends Pokemon | hasPokemon>(pokemonList: T[]): void {
|
function sortBySpeed<T extends Pokemon | hasPokemon>(pokemonList: T[]): void {
|
||||||
pokemonList.sort((a, b) => {
|
pokemonList.sort((a, b) => {
|
||||||
const aSpeed = (a instanceof Pokemon ? a : a.getPokemon()).getEffectiveStat(Stat.SPD);
|
const aSpeed = (isPokemon(a) ? a : a.getPokemon()).getEffectiveStat(Stat.SPD);
|
||||||
const bSpeed = (b instanceof Pokemon ? b : b.getPokemon()).getEffectiveStat(Stat.SPD);
|
const bSpeed = (isPokemon(b) ? b : b.getPokemon()).getEffectiveStat(Stat.SPD);
|
||||||
|
|
||||||
return bSpeed - aSpeed;
|
return bSpeed - aSpeed;
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user