mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-09-23 15:03:24 +02:00
Merge b057314f90
into 8d5ba221d8
This commit is contained in:
commit
b7d5e37ffe
@ -31,6 +31,7 @@ import { Stat } from "#enums/stat";
|
|||||||
import type { EnemyPokemon, Pokemon } from "#field/pokemon";
|
import type { EnemyPokemon, Pokemon } from "#field/pokemon";
|
||||||
import { PokemonMove } from "#moves/pokemon-move";
|
import { PokemonMove } from "#moves/pokemon-move";
|
||||||
import { NumberHolder, randSeedInt } from "#utils/common";
|
import { NumberHolder, randSeedInt } from "#utils/common";
|
||||||
|
import { willTerastallize } from "#utils/pokemon-utils";
|
||||||
import { isBeta } from "#utils/utility-vars";
|
import { isBeta } from "#utils/utility-vars";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -676,12 +677,7 @@ export function generateMoveset(pokemon: Pokemon): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Determine whether this pokemon will instantly tera */
|
/** Determine whether this pokemon will instantly tera */
|
||||||
const willTera =
|
const willTera = hasTrainer && willTerastallize(pokemon as EnemyPokemon);
|
||||||
hasTrainer
|
|
||||||
&& globalScene.currentBattle?.trainer?.config.trainerAI.instantTeras.includes(
|
|
||||||
// The cast to EnemyPokemon is safe; includes will just return false if the property doesn't exist
|
|
||||||
(pokemon as EnemyPokemon).initialTeamIndex,
|
|
||||||
);
|
|
||||||
|
|
||||||
adjustDamageMoveWeights(movePool, pokemon, willTera);
|
adjustDamageMoveWeights(movePool, pokemon, willTera);
|
||||||
|
|
||||||
|
@ -107,3 +107,8 @@ export const FAKE_TITLE_LOGO_CHANCE = 10000;
|
|||||||
* Using rare candies will never increase friendship beyond this value.
|
* Using rare candies will never increase friendship beyond this value.
|
||||||
*/
|
*/
|
||||||
export const RARE_CANDY_FRIENDSHIP_CAP = 200;
|
export const RARE_CANDY_FRIENDSHIP_CAP = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of times a player can Terastallize in a single arena run
|
||||||
|
*/
|
||||||
|
export const MAX_TERAS_PER_ARENA = 1;
|
||||||
|
@ -54,7 +54,7 @@ import { MoveEffectTrigger } from "#enums/move-effect-trigger";
|
|||||||
import { MoveFlags } from "#enums/move-flags";
|
import { MoveFlags } from "#enums/move-flags";
|
||||||
import { MoveTarget } from "#enums/move-target";
|
import { MoveTarget } from "#enums/move-target";
|
||||||
import { MultiHitType } from "#enums/multi-hit-type";
|
import { MultiHitType } from "#enums/multi-hit-type";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { MAX_POKEMON_TYPE, PokemonType } from "#enums/pokemon-type";
|
||||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import {
|
import {
|
||||||
@ -94,6 +94,7 @@ import i18next from "i18next";
|
|||||||
import { applyChallenges } from "#utils/challenge-utils";
|
import { applyChallenges } from "#utils/challenge-utils";
|
||||||
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||||
import type { AbstractConstructor } from "#types/type-helpers";
|
import type { AbstractConstructor } from "#types/type-helpers";
|
||||||
|
import { canSpeciesTera, willTerastallize } from "#utils/pokemon-utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
|
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
|
||||||
@ -4977,6 +4978,16 @@ export class VariableMoveTypeAttr extends MoveAttr {
|
|||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the type of the move for the purpose of determining the type-boosting item to spawn
|
||||||
|
* @param user - The Pokémon using the move
|
||||||
|
* @param move - The move being used
|
||||||
|
* @returns An array of types to add to the pool of type-boosting items
|
||||||
|
*/
|
||||||
|
getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
return [move.type];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
|
export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
|
||||||
@ -4988,8 +4999,10 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.ARCEUS) || [ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.SILVALLY)) {
|
if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.ARCEUS) || [ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.SILVALLY)) {
|
||||||
const form = user.species.speciesId === SpeciesId.ARCEUS || user.species.speciesId === SpeciesId.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!;
|
const form = user.species.speciesId === SpeciesId.ARCEUS || user.species.speciesId === SpeciesId.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!;
|
||||||
|
if (form >= 0 && form <= MAX_POKEMON_TYPE && form !== PokemonType.STELLAR) {
|
||||||
moveType.value = PokemonType[PokemonType[form]];
|
moveType.value = form as PokemonType;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5000,6 +5013,14 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
|
|||||||
moveType.value = move.type
|
moveType.value = move.type
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
// Get the type
|
||||||
|
const typeHolder = new NumberHolder(move.type);
|
||||||
|
// Passing user in for target is fine; the parameter is unused anyway
|
||||||
|
this.apply(user, user, move, [ typeHolder ]);
|
||||||
|
return [typeHolder.value];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TechnoBlastTypeAttr extends VariableMoveTypeAttr {
|
export class TechnoBlastTypeAttr extends VariableMoveTypeAttr {
|
||||||
@ -5034,6 +5055,12 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
const typeHolder = new NumberHolder(move.type);
|
||||||
|
this.apply(user, user, move, [ typeHolder ]);
|
||||||
|
return [typeHolder.value];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AuraWheelTypeAttr extends VariableMoveTypeAttr {
|
export class AuraWheelTypeAttr extends VariableMoveTypeAttr {
|
||||||
@ -5059,6 +5086,15 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
// On Morpeko only, allow this to count for both blackglasses and magnet
|
||||||
|
if (this.apply(user, user, move, [new NumberHolder(move.type)])) {
|
||||||
|
return [PokemonType.DARK, PokemonType.ELECTRIC];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [move.type];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RagingBullTypeAttr extends VariableMoveTypeAttr {
|
export class RagingBullTypeAttr extends VariableMoveTypeAttr {
|
||||||
@ -5087,6 +5123,12 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
const typeHolder = new NumberHolder(move.type);
|
||||||
|
this.apply(user, user, move, [ typeHolder ]);
|
||||||
|
return [ typeHolder.value ];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
|
export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
|
||||||
@ -5122,6 +5164,12 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
const typeHolder = new NumberHolder(move.type);
|
||||||
|
this.apply(user, user, move, [ typeHolder ]);
|
||||||
|
return [ typeHolder.value ];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
|
export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
|
||||||
@ -5235,6 +5283,12 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
const typeHolder = new NumberHolder(move.type);
|
||||||
|
this.apply(user, user, move, [ typeHolder ]);
|
||||||
|
return [typeHolder.value];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5261,6 +5315,23 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
const coreType = move.type;
|
||||||
|
const teraType = user.getTeraType();
|
||||||
|
/** Whether the user is allowed to tera. In the case of an enemy Pokémon, whether it *will* tera. */
|
||||||
|
const hasTeraAccess = user.isPlayer() ? canSpeciesTera(user) : willTerastallize(user);
|
||||||
|
if (
|
||||||
|
// tera type matches the move's type; no change
|
||||||
|
!hasTeraAccess
|
||||||
|
|| teraType === coreType
|
||||||
|
|| teraType === PokemonType.STELLAR
|
||||||
|
|| teraType === PokemonType.UNKNOWN
|
||||||
|
) {
|
||||||
|
return [coreType];
|
||||||
|
}
|
||||||
|
return [coreType, teraType];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5304,7 +5375,13 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] {
|
||||||
|
// Instead of calling apply, just return the user's primary type
|
||||||
|
// this avoids inconsistencies when the user's type is temporarily changed
|
||||||
|
// from tera
|
||||||
|
return [user.getTypes(false, true, true, false)[0] ?? move.type];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1288,52 +1288,49 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
|||||||
return new AttackTypeBoosterModifierType(pregenArgs[0] as PokemonType, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
return new AttackTypeBoosterModifierType(pregenArgs[0] as PokemonType, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
const attackMoveTypes = party.flatMap(p =>
|
|
||||||
p
|
|
||||||
.getMoveset()
|
|
||||||
.map(m => m.getMove())
|
|
||||||
.filter(m => m.is("AttackMove"))
|
|
||||||
.map(m => m.type),
|
|
||||||
);
|
|
||||||
if (attackMoveTypes.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const attackMoveTypeWeights = new Map<PokemonType, number>();
|
const attackMoveTypeWeights = new Map<PokemonType, number>();
|
||||||
let totalWeight = 0;
|
let totalWeight = 0;
|
||||||
for (const t of attackMoveTypes) {
|
for (const p of party) {
|
||||||
if (attackMoveTypeWeights.has(t)) {
|
if (!p.isAllowedInChallenge()) {
|
||||||
if (attackMoveTypeWeights.get(t)! < 3) {
|
|
||||||
// attackMoveTypeWeights.has(t) was checked before
|
|
||||||
attackMoveTypeWeights.set(t, attackMoveTypeWeights.get(t)! + 1);
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
for (const pokemonMove of p.getMoveset()) {
|
||||||
attackMoveTypeWeights.set(t, 1);
|
const move = pokemonMove.getMove();
|
||||||
|
if (!move.is("AttackMove")) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
// Account for variable type changing moves
|
||||||
|
// Get a variable type attribute of the move
|
||||||
|
const variableTypeAttr = move.getAttrs("VariableMoveTypeAttr")[0];
|
||||||
|
const types = variableTypeAttr?.getTypesForItemSpawn(p, move) ?? [move.type];
|
||||||
|
for (const type of types) {
|
||||||
|
console.info("%cConsidering type " + PokemonType[type], "color: orange");
|
||||||
|
const currentWeight = attackMoveTypeWeights.get(type) ?? 0;
|
||||||
|
if (currentWeight < 3) {
|
||||||
|
attackMoveTypeWeights.set(type, currentWeight + 1);
|
||||||
totalWeight++;
|
totalWeight++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!totalWeight) {
|
if (attackMoveTypeWeights.size === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let type: PokemonType;
|
|
||||||
|
|
||||||
const randInt = randSeedInt(totalWeight);
|
const randInt = randSeedInt(totalWeight);
|
||||||
|
console.log("%cTotal weight " + totalWeight + ", rolled " + randInt, "color: orange");
|
||||||
let weight = 0;
|
let weight = 0;
|
||||||
|
|
||||||
for (const t of attackMoveTypeWeights.keys()) {
|
for (const [type, typeWeight] of attackMoveTypeWeights.entries()) {
|
||||||
const typeWeight = attackMoveTypeWeights.get(t)!; // guranteed to be defined
|
console.log("%cWeighted type " + PokemonType[type] + " with weight " + typeWeight, "color: orange");
|
||||||
if (randInt <= weight + typeWeight) {
|
if (randInt < weight + typeWeight) {
|
||||||
type = t;
|
return new AttackTypeBoosterModifierType(type, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
weight += typeWeight;
|
weight += typeWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AttackTypeBoosterModifierType(type!, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
|
import { MAX_TERAS_PER_ARENA } from "#app/constants";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { getTypeRgb } from "#data/type";
|
import { getTypeRgb } from "#data/type";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { Command } from "#enums/command";
|
import { Command } from "#enums/command";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
|
||||||
import { TextStyle } from "#enums/text-style";
|
import { TextStyle } from "#enums/text-style";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { TerastallizeAccessModifier } from "#modifiers/modifier";
|
|
||||||
import type { CommandPhase } from "#phases/command-phase";
|
import type { CommandPhase } from "#phases/command-phase";
|
||||||
import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
|
import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
|
||||||
import { addTextObject } from "#ui/text";
|
import { addTextObject } from "#ui/text";
|
||||||
import { UiHandler } from "#ui/ui-handler";
|
import { UiHandler } from "#ui/ui-handler";
|
||||||
|
import { canTerastallize } from "#utils/pokemon-utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class CommandUiHandler extends UiHandler {
|
export class CommandUiHandler extends UiHandler {
|
||||||
@ -198,14 +198,13 @@ export class CommandUiHandler extends UiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canTera(): boolean {
|
canTera(): boolean {
|
||||||
const hasTeraMod = globalScene.getModifiers(TerastallizeAccessModifier).length > 0;
|
|
||||||
const activePokemon = globalScene.getField()[this.fieldIndex];
|
const activePokemon = globalScene.getField()[this.fieldIndex];
|
||||||
const isBlockedForm =
|
|
||||||
activePokemon.isMega() || activePokemon.isMax() || activePokemon.hasSpecies(SpeciesId.NECROZMA, "ultra");
|
|
||||||
const currentTeras = globalScene.arena.playerTerasUsed;
|
const currentTeras = globalScene.arena.playerTerasUsed;
|
||||||
const plannedTera =
|
const canTera = activePokemon.isPlayer() && canTerastallize(activePokemon);
|
||||||
globalScene.currentBattle.preTurnCommands[0]?.command === Command.TERA && this.fieldIndex > 0 ? 1 : 0;
|
const plannedTera = +(
|
||||||
return hasTeraMod && !isBlockedForm && currentTeras + plannedTera < 1;
|
globalScene.currentBattle.preTurnCommands[0]?.command === Command.TERA && this.fieldIndex > 0
|
||||||
|
);
|
||||||
|
return canTera && currentTeras + plannedTera < MAX_TERAS_PER_ARENA;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleTeraButton() {
|
toggleTeraButton() {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import { MAX_TERAS_PER_ARENA } from "#app/constants";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { POKERUS_STARTER_COUNT, speciesStarterCosts } from "#balance/starters";
|
import { POKERUS_STARTER_COUNT, speciesStarterCosts } from "#balance/starters";
|
||||||
import { allSpecies } from "#data/data-lists";
|
import { allSpecies } from "#data/data-lists";
|
||||||
import type { PokemonSpecies, PokemonSpeciesForm } from "#data/pokemon-species";
|
import type { PokemonSpecies, PokemonSpeciesForm } from "#data/pokemon-species";
|
||||||
import type { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
|
||||||
import { randSeedItem } from "./common";
|
import { randSeedItem } from "./common";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,3 +125,51 @@ export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): Po
|
|||||||
}
|
}
|
||||||
return retSpecies;
|
return retSpecies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether an enemy Pokémon will Terastallize the user
|
||||||
|
*
|
||||||
|
* Does not check if the Pokémon is allowed to Terastallize (e.g., if it's a mega)
|
||||||
|
* @param pokemon - The Pokémon to check
|
||||||
|
* @returns Whether the Pokémon will Terastallize
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Should really only be called with an enemy Pokémon, but will technically work with any Pokémon.
|
||||||
|
*
|
||||||
|
* @privateRemarks
|
||||||
|
* Assumes that Pokémon without a trainer will never tera, so this must be changed if
|
||||||
|
* a wild Pokémon is allowed to tera, e.g. for a Mystery Encounter.
|
||||||
|
*/
|
||||||
|
export function willTerastallize(pokemon: Pokemon): boolean {
|
||||||
|
// cast is safe, as if it is just a Pokémon, initialTeamIndex will be undefined triggering the null check
|
||||||
|
const initialTeamIndex = (pokemon as EnemyPokemon).initialTeamIndex;
|
||||||
|
return (
|
||||||
|
initialTeamIndex != null
|
||||||
|
&& pokemon.hasTrainer()
|
||||||
|
&& (globalScene.currentBattle?.trainer?.config.trainerAI.instantTeras.includes(initialTeamIndex) ?? false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the Pokémon's species is tera capable, and that the player has acquired the tera orb.
|
||||||
|
* @param pokemon - The Pokémon to check
|
||||||
|
* @returns Whether
|
||||||
|
*/
|
||||||
|
export function canSpeciesTera(pokemon: Pokemon): boolean {
|
||||||
|
const hasTeraMod = globalScene.findModifier(modifier => modifier.is("TerastallizeAccessModifier")) != null;
|
||||||
|
const isBlockedForm = pokemon.isMega() || pokemon.isMax() || pokemon.hasSpecies(SpeciesId.NECROZMA, "ultra");
|
||||||
|
return hasTeraMod && !isBlockedForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@linkcode canSpeciesTera}, but also checks that the player has not already used their tera in the arena.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* ⚠️ This does not account for tera commands that may be pending, so this should not be used during command selection!
|
||||||
|
* @param pokemon - The Pokémon to check
|
||||||
|
* @returns Whether the Pokémon can Terastallize
|
||||||
|
*/
|
||||||
|
export function canTerastallize(pokemon: PlayerPokemon): boolean {
|
||||||
|
const hasAvailableTeras = globalScene.arena.playerTerasUsed < MAX_TERAS_PER_ARENA;
|
||||||
|
return hasAvailableTeras && canSpeciesTera(pokemon);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user