mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-30 05:22:44 +02:00
Merge remote-tracking branch 'upstream/beta' into modifier-fixes
This commit is contained in:
commit
b353890d77
@ -1,6 +1,7 @@
|
||||
VITE_BYPASS_LOGIN=1
|
||||
VITE_BYPASS_TUTORIAL=0
|
||||
VITE_SERVER_URL=http://localhost:8001
|
||||
# IDs for discord/google auth go unused due to VITE_BYPASS_LOGIN
|
||||
VITE_DISCORD_CLIENT_ID=1234567890
|
||||
VITE_GOOGLE_CLIENT_ID=1234567890
|
||||
VITE_I18N_DEBUG=0
|
||||
|
@ -48,7 +48,7 @@ async function promptTestType() {
|
||||
{
|
||||
type: "list",
|
||||
name: "selectedOption",
|
||||
message: "What type of test would you like to create:",
|
||||
message: "What type of test would you like to create?",
|
||||
choices: [...choices.map(choice => ({ name: choice.label, value: choice })), "EXIT"],
|
||||
},
|
||||
]);
|
||||
|
@ -24,7 +24,7 @@ describe("{{description}}", () => {
|
||||
game.override
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
|
@ -4,7 +4,8 @@ import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import {
|
||||
fixedInt,
|
||||
getIvsFromId,
|
||||
@ -2104,12 +2105,15 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
getMaxExpLevel(ignoreLevelCap = false): number {
|
||||
if (Overrides.LEVEL_CAP_OVERRIDE > 0) {
|
||||
return Overrides.LEVEL_CAP_OVERRIDE;
|
||||
const capOverride = Overrides.LEVEL_CAP_OVERRIDE ?? 0;
|
||||
if (capOverride > 0) {
|
||||
return capOverride;
|
||||
}
|
||||
if (ignoreLevelCap || Overrides.LEVEL_CAP_OVERRIDE < 0) {
|
||||
|
||||
if (ignoreLevelCap || capOverride < 0) {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10;
|
||||
const difficultyWaveIndex = this.gameMode.getWaveForDifficulty(waveIndex);
|
||||
const baseLevel = (1 + difficultyWaveIndex / 2 + Math.pow(difficultyWaveIndex / 25, 2)) * 1.2;
|
||||
@ -2973,6 +2977,13 @@ export default class BattleScene extends SceneBase {
|
||||
) {
|
||||
modifiers.splice(m--, 1);
|
||||
}
|
||||
if (
|
||||
modifier instanceof PokemonHeldItemModifier &&
|
||||
!isNullOrUndefined(modifier.getSpecies()) &&
|
||||
!this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
|
||||
) {
|
||||
modifiers.splice(m--, 1);
|
||||
}
|
||||
}
|
||||
for (const modifier of modifiers) {
|
||||
if (modifier instanceof PersistentModifier) {
|
||||
@ -3502,17 +3513,13 @@ export default class BattleScene extends SceneBase {
|
||||
sessionEncounterRate +
|
||||
Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
|
||||
|
||||
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE)
|
||||
? favoredEncounterRate
|
||||
: Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
|
||||
const successRate = Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE ?? favoredEncounterRate;
|
||||
|
||||
// If the most recent ME was 3 or fewer waves ago, can never spawn a ME
|
||||
// MEs can only spawn 3 or more waves after the previous ME, barring overrides
|
||||
const canSpawn =
|
||||
encounteredEvents.length === 0 ||
|
||||
waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex > 3 ||
|
||||
!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE);
|
||||
encounteredEvents.length === 0 || waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex > 3;
|
||||
|
||||
if (canSpawn) {
|
||||
if (canSpawn || Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE !== null) {
|
||||
let roll = MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT;
|
||||
// Always rolls the check on the same offset to ensure no RNG changes from reloading session
|
||||
this.executeWithSeedOffset(
|
||||
|
@ -24,11 +24,11 @@ import { allMoves } from "../data-lists";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { TerrainType } from "#app/data/terrain";
|
||||
import { pokemonFormChanges } from "../pokemon-forms";
|
||||
import {
|
||||
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||
SpeciesFormChangeWeatherTrigger,
|
||||
SpeciesFormChangeAbilityTrigger,
|
||||
} from "../pokemon-forms/form-change-triggers";
|
||||
import { SpeciesFormChangeAbilityTrigger } from "../pokemon-forms/form-change-triggers";
|
||||
import i18next from "i18next";
|
||||
import { Command } from "#enums/command";
|
||||
import { BerryModifierType } from "#app/modifier/modifier-type";
|
||||
@ -3971,27 +3971,32 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr {
|
||||
this.ability = ability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the pokemon has a forme change that is triggered by the weather
|
||||
*
|
||||
* @param pokemon - The pokemon with the forme change ability
|
||||
* @param _passive - unused
|
||||
* @param _simulated - unused
|
||||
* @param _args - unused
|
||||
*/
|
||||
override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean {
|
||||
const isCastformWithForecast =
|
||||
pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST;
|
||||
const isCherrimWithFlowerGift =
|
||||
pokemon.species.speciesId === SpeciesId.CHERRIM && this.ability === AbilityId.FLOWER_GIFT;
|
||||
return isCastformWithForecast || isCherrimWithFlowerGift;
|
||||
return !!pokemonFormChanges[pokemon.species.speciesId]?.some(
|
||||
fc => fc.findTrigger(SpeciesFormChangeWeatherTrigger) && fc.canChange(pokemon),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange} for both
|
||||
* {@linkcode SpeciesFormChange.SpeciesFormChangeWeatherTrigger | SpeciesFormChangeWeatherTrigger} and
|
||||
* {@linkcode SpeciesFormChange.SpeciesFormChangeWeatherTrigger | SpeciesFormChangeRevertWeatherFormTrigger} if it
|
||||
* is the specific Pokemon and ability
|
||||
* @param {Pokemon} pokemon the Pokemon with this ability
|
||||
* @param _passive n/a
|
||||
* @param _args n/a
|
||||
* Trigger the pokemon's forme change by invoking
|
||||
* {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange}
|
||||
*
|
||||
* @param pokemon - The Pokemon with this ability
|
||||
* @param _passive - unused
|
||||
* @param simulated - unused
|
||||
* @param _args - unused
|
||||
*/
|
||||
override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void {
|
||||
if (!simulated) {
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger);
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeRevertWeatherFormTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4617,7 +4622,7 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA
|
||||
* @param stat The stat being affected
|
||||
* @param cancelled Holds whether the stat change was already prevented.
|
||||
* @param args Args[0] is the target pokemon of the stat change.
|
||||
* @returns
|
||||
* @returns `true` if the ability can be applied
|
||||
*/
|
||||
override canApplyPreStatStageChange(
|
||||
_pokemon: Pokemon,
|
||||
@ -4778,17 +4783,17 @@ export class BlockCritAbAttr extends AbAttr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the block crit ability by setting the value in the provided boolean holder to false
|
||||
* @param args - [0] is a boolean holder representing whether the attack can crit
|
||||
* Apply the block crit ability by setting the value in the provided boolean holder to `true`.
|
||||
* @param args - `[0]`: A {@linkcode BooleanHolder} containing whether the attack is prevented from critting.
|
||||
*/
|
||||
override apply(
|
||||
_pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_cancelled: BooleanHolder,
|
||||
args: [BooleanHolder, ...any],
|
||||
args: [BooleanHolder],
|
||||
): void {
|
||||
args[0].value = false;
|
||||
args[0].value = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5301,10 +5306,11 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr {
|
||||
/**
|
||||
* Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the
|
||||
* weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges}
|
||||
* @param {Pokemon} _pokemon the Pokemon with this ability
|
||||
* @param _passive n/a
|
||||
* @param _weather n/a
|
||||
* @param _args n/a
|
||||
* @param _pokemon - The Pokemon with this ability
|
||||
* @param _passive - unused
|
||||
* @param simulated - unused
|
||||
* @param _weather - unused
|
||||
* @param _args - unused
|
||||
*/
|
||||
override applyPostWeatherChange(
|
||||
_pokemon: Pokemon,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,8 @@ import i18next from "i18next";
|
||||
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
||||
import { defaultStarterSpecies } from "#app/constants";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "./moves/pokemon-move";
|
||||
|
@ -5,7 +5,8 @@ import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { Starter } from "#app/ui/starter-select-ui-handler";
|
||||
import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils/common";
|
||||
import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import PokemonSpecies, { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
|
@ -1,9 +1,11 @@
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import type { ModifierTypes } from "#app/modifier/modifier-type";
|
||||
import type { Ability } from "./abilities/ability";
|
||||
import type Move from "./moves/move";
|
||||
|
||||
export const allAbilities: Ability[] = [];
|
||||
export const allMoves: Move[] = [];
|
||||
export const allSpecies: PokemonSpecies[] = [];
|
||||
|
||||
// TODO: Figure out what this is used for and provide an appropriate tsdoc comment
|
||||
export const modifierTypes = {} as ModifierTypes;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type BattleScene from "#app/battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { VariantTier } from "#enums/variant-tier";
|
||||
import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils/common";
|
||||
|
@ -22,7 +22,7 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { randInt } from "#app/utils/common";
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
|
@ -22,7 +22,7 @@ import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-en
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import {
|
||||
applyAbilityOverrideToPokemon,
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
getEncounterPokemonLevelForWave,
|
||||
STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
|
@ -4,7 +4,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
updatePlayerMoney,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
@ -20,7 +20,7 @@ import {
|
||||
TypeRequirement,
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
|
@ -14,7 +14,7 @@ import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { FieldPosition } from "#enums/field-position";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
|
@ -16,7 +16,8 @@ import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-en
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { getTypeRgb } from "#app/data/type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
getHighestLevelPlayerPokemon,
|
||||
koPlayerPokemon,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
|
@ -17,7 +17,7 @@ import { PokeballType } from "#enums/pokeball";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { NumberHolder, randSeedInt } from "#app/utils/common";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import {
|
||||
doPlayerFlee,
|
||||
|
@ -24,7 +24,7 @@ import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { AiType } from "#enums/ai-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
|
@ -15,7 +15,7 @@ import { BiomeId } from "#enums/biome-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import i18next from "i18next";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
getSpriteKeysFromPokemon,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
|
@ -13,7 +13,7 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
|
@ -17,7 +17,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
|
@ -22,7 +22,7 @@ import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/u
|
||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
|
@ -19,7 +19,8 @@ import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||
import { achvs } from "#app/system/achv";
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { allAbilities } from "../data-lists";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import { SpeciesFormChangeItemTrigger } from "../pokemon-forms/form-change-triggers";
|
||||
@ -16,7 +15,6 @@ import type { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
|
||||
export interface EncounterRequirement {
|
||||
@ -834,70 +832,6 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
}
|
||||
}
|
||||
|
||||
export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
||||
requiredEvolutionItem: EvolutionItem[];
|
||||
minNumberOfPokemon: number;
|
||||
invertQuery: boolean;
|
||||
|
||||
constructor(evolutionItems: EvolutionItem | EvolutionItem[], minNumberOfPokemon = 1, invertQuery = false) {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredEvolutionItem = coerceArray(evolutionItems);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
|
||||
}
|
||||
|
||||
filterByEvo(pokemon, evolutionItem) {
|
||||
if (
|
||||
pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) &&
|
||||
pokemonEvolutions[pokemon.species.speciesId].filter(
|
||||
e => e.item === evolutionItem && (!e.condition || e.condition.predicate(pokemon)),
|
||||
).length &&
|
||||
pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
pokemon.isFusion() &&
|
||||
pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) &&
|
||||
pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(
|
||||
e => e.item === evolutionItem && (!e.condition || e.condition.predicate(pokemon)),
|
||||
).length &&
|
||||
pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX
|
||||
);
|
||||
}
|
||||
|
||||
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
||||
if (!this.invertQuery) {
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
this.requiredEvolutionItem.filter(evolutionItem => this.filterByEvo(pokemon, evolutionItem)).length > 0,
|
||||
);
|
||||
}
|
||||
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed evolutionItemss
|
||||
return partyPokemon.filter(
|
||||
pokemon =>
|
||||
this.requiredEvolutionItem.filter(evolutionItems => this.filterByEvo(pokemon, evolutionItems)).length === 0,
|
||||
);
|
||||
}
|
||||
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const requiredItems = this.requiredEvolutionItem.filter(evoItem => this.filterByEvo(pokemon, evoItem));
|
||||
if (requiredItems.length > 0) {
|
||||
return ["evolutionItem", EvolutionItem[requiredItems[0]]];
|
||||
}
|
||||
return ["evolutionItem", ""];
|
||||
}
|
||||
}
|
||||
|
||||
export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||
requiredHeldItemModifiers: string[];
|
||||
minNumberOfPokemon: number;
|
||||
|
@ -48,7 +48,7 @@ import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import type { Variant } from "#app/sprites/variant";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { getNatureName } from "#app/data/nature";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
|
@ -20,7 +20,7 @@ import { PartyUiMode } from "#app/ui/party-ui-handler";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type { PokemonType } from "#enums/pokemon-type";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import {
|
||||
getEncounterText,
|
||||
|
@ -42,6 +42,8 @@ import { starterPassiveAbilities } from "#app/data/balance/passives";
|
||||
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
|
||||
import { hasExpSprite } from "#app/sprites/sprite-utils";
|
||||
import { Gender } from "./gender";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
|
||||
export enum Region {
|
||||
NORMAL,
|
||||
@ -82,24 +84,6 @@ export const normalForm: SpeciesId[] = [
|
||||
SpeciesId.CALYREX,
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets the {@linkcode PokemonSpecies} object associated with the {@linkcode SpeciesId} enum given
|
||||
* @param species - The {@linkcode SpeciesId} to fetch.
|
||||
* If an array of `SpeciesId`s is passed (such as for named trainer spawn pools),
|
||||
* one will be selected at random.
|
||||
* @returns The associated {@linkcode PokemonSpecies} object
|
||||
*/
|
||||
export function getPokemonSpecies(species: SpeciesId | SpeciesId[]): PokemonSpecies {
|
||||
if (Array.isArray(species)) {
|
||||
// TODO: this RNG roll should not be handled by this function
|
||||
species = species[Math.floor(Math.random() * species.length)];
|
||||
}
|
||||
if (species >= 2000) {
|
||||
return allSpecies.find(s => s.speciesId === species)!; // TODO: is this bang correct?
|
||||
}
|
||||
return allSpecies[species - 1];
|
||||
}
|
||||
|
||||
export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): PokemonSpeciesForm {
|
||||
const retSpecies: PokemonSpecies =
|
||||
species >= 2000
|
||||
@ -1449,8 +1433,6 @@ export function getPokerusStarters(): PokemonSpecies[] {
|
||||
return pokerusStarters;
|
||||
}
|
||||
|
||||
export const allSpecies: PokemonSpecies[] = [];
|
||||
|
||||
// biome-ignore format: manually formatted
|
||||
export function initSpecies() {
|
||||
allSpecies.push(
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
randSeedIntRange,
|
||||
} from "#app/utils/common";
|
||||
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { tmSpecies } from "#app/data/balance/tms";
|
||||
import { doubleBattleDialogue } from "../double-battle-dialogue";
|
||||
import { TrainerVariant } from "#enums/trainer-variant";
|
||||
|
@ -3,7 +3,7 @@ import type { BiomeTierTrainerPools, PokemonPools } from "#app/data/balance/biom
|
||||
import { biomePokemonPools, BiomePoolTier, biomeTrainerPools } from "#app/data/balance/biomes";
|
||||
import { randSeedInt, NumberHolder, isNullOrUndefined, type Constructor } from "#app/utils/common";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import {
|
||||
getTerrainClearMessage,
|
||||
getTerrainStartMessage,
|
||||
|
@ -15,12 +15,8 @@ import { allMoves } from "#app/data/data-lists";
|
||||
import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import {
|
||||
default as PokemonSpecies,
|
||||
getFusedSpeciesName,
|
||||
getPokemonSpecies,
|
||||
getPokemonSpeciesForm,
|
||||
} from "#app/data/pokemon-species";
|
||||
import { default as PokemonSpecies, getFusedSpeciesName, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import {
|
||||
NumberHolder,
|
||||
@ -79,11 +75,12 @@ import {
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import { Status, getRandomStatus } from "#app/data/status-effect";
|
||||
import type { SpeciesFormEvolution, SpeciesEvolutionCondition } from "#app/data/balance/pokemon-evolutions";
|
||||
import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
||||
import {
|
||||
pokemonEvolutions,
|
||||
pokemonPrevolutions,
|
||||
FusionSpeciesFormEvolution,
|
||||
validateShedinjaEvo,
|
||||
} from "#app/data/balance/pokemon-evolutions";
|
||||
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms";
|
||||
import {
|
||||
@ -370,7 +367,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.metWave = dataSource.metWave ?? (this.metBiome === -1 ? -1 : 0);
|
||||
this.pauseEvolutions = dataSource.pauseEvolutions;
|
||||
this.pokerus = !!dataSource.pokerus;
|
||||
this.evoCounter = dataSource.evoCounter ?? 0;
|
||||
this.fusionSpecies =
|
||||
dataSource.fusionSpecies instanceof PokemonSpecies
|
||||
? dataSource.fusionSpecies
|
||||
@ -1356,8 +1352,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the critical-hit stage of a move used against this pokemon by
|
||||
* the given source
|
||||
* Calculate the critical-hit stage of a move used **against** this pokemon by
|
||||
* the given source.
|
||||
*
|
||||
* @param source - The {@linkcode Pokemon} using the move
|
||||
* @param move - The {@linkcode Move} being used
|
||||
* @returns The final critical-hit stage value
|
||||
@ -1370,11 +1367,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyAbAttrs("BonusCritAbAttr", source, null, false, critStage);
|
||||
const critBoostTag = source.getTag(CritBoostTag);
|
||||
if (critBoostTag) {
|
||||
if (critBoostTag instanceof DragonCheerTag) {
|
||||
critStage.value += critBoostTag.typesOnAdd.includes(PokemonType.DRAGON) ? 2 : 1;
|
||||
} else {
|
||||
critStage.value += 2;
|
||||
}
|
||||
// Dragon cheer only gives +1 crit stage to non-dragon types
|
||||
critStage.value +=
|
||||
critBoostTag instanceof DragonCheerTag && !critBoostTag.typesOnAdd.includes(PokemonType.DRAGON) ? 1 : 2;
|
||||
}
|
||||
|
||||
console.log(`crit stage: +${critStage.value}`);
|
||||
@ -2519,14 +2514,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (pokemonEvolutions.hasOwnProperty(this.species.speciesId)) {
|
||||
const evolutions = pokemonEvolutions[this.species.speciesId];
|
||||
for (const e of evolutions) {
|
||||
if (
|
||||
!e.item &&
|
||||
this.level >= e.level &&
|
||||
(isNullOrUndefined(e.preFormKey) || this.getFormKey() === e.preFormKey)
|
||||
) {
|
||||
if (e.condition === null || (e.condition as SpeciesEvolutionCondition).predicate(this)) {
|
||||
return e;
|
||||
}
|
||||
if (e.validate(this)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2536,14 +2525,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
e => new FusionSpeciesFormEvolution(this.species.speciesId, e),
|
||||
);
|
||||
for (const fe of fusionEvolutions) {
|
||||
if (
|
||||
!fe.item &&
|
||||
this.level >= fe.level &&
|
||||
(isNullOrUndefined(fe.preFormKey) || this.getFusionFormKey() === fe.preFormKey)
|
||||
) {
|
||||
if (fe.condition === null || (fe.condition as SpeciesEvolutionCondition).predicate(this)) {
|
||||
return fe;
|
||||
}
|
||||
if (fe.validate(this)) {
|
||||
return fe;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2785,17 +2768,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*/
|
||||
public trySetShinySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean {
|
||||
if (!this.shiny) {
|
||||
const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE);
|
||||
if (thresholdOverride === undefined || applyModifiersToOverride) {
|
||||
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
||||
shinyThreshold.value = thresholdOverride;
|
||||
}
|
||||
const shinyThreshold = new NumberHolder(thresholdOverride ?? BASE_SHINY_CHANCE);
|
||||
if (applyModifiersToOverride) {
|
||||
if (timedEventManager.isEventActive()) {
|
||||
shinyThreshold.value *= timedEventManager.getShinyMultiplier();
|
||||
}
|
||||
globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
|
||||
} else {
|
||||
shinyThreshold.value = thresholdOverride;
|
||||
}
|
||||
|
||||
this.shiny = randSeedInt(65536) < shinyThreshold.value;
|
||||
@ -2864,16 +2842,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (!this.species.abilityHidden) {
|
||||
return false;
|
||||
}
|
||||
const haThreshold = new NumberHolder(BASE_HIDDEN_ABILITY_CHANCE);
|
||||
if (thresholdOverride === undefined || applyModifiersToOverride) {
|
||||
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
||||
haThreshold.value = thresholdOverride;
|
||||
}
|
||||
const haThreshold = new NumberHolder(thresholdOverride ?? BASE_HIDDEN_ABILITY_CHANCE);
|
||||
if (applyModifiersToOverride) {
|
||||
if (!this.hasTrainer()) {
|
||||
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, haThreshold);
|
||||
}
|
||||
} else {
|
||||
haThreshold.value = thresholdOverride;
|
||||
}
|
||||
|
||||
if (randSeedInt(65536) < haThreshold.value) {
|
||||
@ -3888,33 +3861,39 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
};
|
||||
}
|
||||
|
||||
/** Calculate whether the given move critically hits this pokemon
|
||||
/**
|
||||
* Determine whether the given move will score a critical hit **against** this Pokemon.
|
||||
* @param source - The {@linkcode Pokemon} using the move
|
||||
* @param move - The {@linkcode Move} being used
|
||||
* @param simulated - If `true`, suppresses changes to game state during calculation (defaults to `true`)
|
||||
* @returns whether the move critically hits the pokemon
|
||||
* @returns Whether the move will critically hit the defender.
|
||||
*/
|
||||
getCriticalHitResult(source: Pokemon, move: Move, simulated = true): boolean {
|
||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide);
|
||||
if (noCritTag || Overrides.NEVER_CRIT_OVERRIDE || move.hasAttr("FixedDamageAttr")) {
|
||||
getCriticalHitResult(source: Pokemon, move: Move): boolean {
|
||||
if (move.hasAttr("FixedDamageAttr")) {
|
||||
// fixed damage moves (Dragon Rage, etc.) will nevet crit
|
||||
return false;
|
||||
}
|
||||
const isCritical = new BooleanHolder(false);
|
||||
|
||||
if (source.getTag(BattlerTagType.ALWAYS_CRIT)) {
|
||||
isCritical.value = true;
|
||||
}
|
||||
applyMoveAttrs("CritOnlyAttr", source, this, move, isCritical);
|
||||
applyAbAttrs("ConditionalCritAbAttr", source, null, simulated, isCritical, this, move);
|
||||
if (!isCritical.value) {
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||
isCritical.value = critChance === 1 || !globalScene.randBattleSeedInt(critChance);
|
||||
}
|
||||
const alwaysCrit = new BooleanHolder(false);
|
||||
applyMoveAttrs("CritOnlyAttr", source, this, move, alwaysCrit);
|
||||
applyAbAttrs("ConditionalCritAbAttr", source, null, false, alwaysCrit, this, move);
|
||||
const alwaysCritTag = !!source.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
const critChance = [24, 8, 2, 1][Phaser.Math.Clamp(this.getCritStage(source, move), 0, 3)];
|
||||
|
||||
applyAbAttrs("BlockCritAbAttr", this, null, simulated, isCritical);
|
||||
let isCritical = alwaysCrit.value || alwaysCritTag || critChance === 1;
|
||||
|
||||
return isCritical.value;
|
||||
// If we aren't already guaranteed to crit, do a random roll & check overrides
|
||||
isCritical ||= Overrides.CRITICAL_HIT_OVERRIDE ?? globalScene.randBattleSeedInt(critChance) === 0;
|
||||
|
||||
// apply crit block effects from lucky chant & co., overriding previous effects
|
||||
const blockCrit = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockCritAbAttr", this, null, false, blockCrit);
|
||||
const blockCritTag = globalScene.arena.getTagOnSide(
|
||||
NoCritTag,
|
||||
this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY,
|
||||
);
|
||||
isCritical &&= !blockCritTag && !blockCrit.value; // need to roll a crit and not be blocked by either crit prevention effect
|
||||
|
||||
return isCritical;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5492,6 +5471,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
this.turnData.berriesEaten.push(berryType);
|
||||
}
|
||||
|
||||
getPersistentTreasureCount(): number {
|
||||
return (
|
||||
this.getHeldItems().filter(m => m.is("DamageMoneyRewardModifier")).length +
|
||||
globalScene.findModifiers(m => m.is("MoneyMultiplierModifier") || m.is("ExtraModifierModifier")).length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class PlayerPokemon extends Pokemon {
|
||||
@ -5830,7 +5816,7 @@ export class PlayerPokemon extends Pokemon {
|
||||
if (evoSpecies?.speciesId === SpeciesId.NINCADA && evolution.speciesId === SpeciesId.NINJASK) {
|
||||
const newEvolution = pokemonEvolutions[evoSpecies.speciesId][1];
|
||||
|
||||
if (newEvolution.condition?.predicate(this)) {
|
||||
if (validateShedinjaEvo()) {
|
||||
const newPokemon = globalScene.addPlayerPokemon(
|
||||
this.species,
|
||||
this.level,
|
||||
@ -5860,7 +5846,6 @@ export class PlayerPokemon extends Pokemon {
|
||||
newPokemon.fusionLuck = this.fusionLuck;
|
||||
newPokemon.fusionTeraType = this.fusionTeraType;
|
||||
newPokemon.usedTMs = this.usedTMs;
|
||||
newPokemon.evoCounter = this.evoCounter;
|
||||
|
||||
globalScene.getPlayerParty().push(newPokemon);
|
||||
newPokemon.evolve(!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution), evoSpecies);
|
||||
@ -5949,7 +5934,6 @@ export class PlayerPokemon extends Pokemon {
|
||||
this.fusionGender = pokemon.gender;
|
||||
this.fusionLuck = pokemon.luck;
|
||||
this.fusionCustomPokemonData = pokemon.customPokemonData;
|
||||
this.evoCounter = Math.max(pokemon.evoCounter, this.evoCounter);
|
||||
if (pokemon.pauseEvolutions || this.pauseEvolutions) {
|
||||
this.pauseEvolutions = true;
|
||||
}
|
||||
@ -6105,18 +6089,6 @@ export class EnemyPokemon extends Pokemon {
|
||||
|
||||
this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0);
|
||||
|
||||
let prevolution: SpeciesId;
|
||||
let speciesId = species.speciesId;
|
||||
while ((prevolution = pokemonPrevolutions[speciesId])) {
|
||||
const evolution = pokemonEvolutions[prevolution].find(
|
||||
pe => pe.speciesId === speciesId && (!pe.evoFormKey || pe.evoFormKey === this.getFormKey()),
|
||||
);
|
||||
if (evolution?.condition?.enforceFunc) {
|
||||
evolution.condition.enforceFunc(this);
|
||||
}
|
||||
speciesId = prevolution;
|
||||
}
|
||||
|
||||
if (this.hasTrainer() && globalScene.currentBattle) {
|
||||
const { waveIndex } = globalScene.currentBattle;
|
||||
const ivs: number[] = [];
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import type { TrainerConfig } from "#app/data/trainers/trainer-config";
|
||||
import type { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
|
@ -5,7 +5,7 @@ import type { Challenge } from "./data/challenge";
|
||||
import { allChallenges, applyChallenges, copyChallenge } from "./data/challenge";
|
||||
import { ChallengeType } from "#enums/challenge-type";
|
||||
import type PokemonSpecies from "./data/pokemon-species";
|
||||
import { allSpecies } from "./data/pokemon-species";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import type { Arena } from "./field/arena";
|
||||
import Overrides from "#app/overrides";
|
||||
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils/common";
|
||||
@ -90,13 +90,14 @@ export class GameMode implements GameModeConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get starting level for game mode.
|
||||
* @returns either:
|
||||
* - override from overrides.ts
|
||||
* - starting level override from overrides.ts
|
||||
* - 20 for Daily Runs
|
||||
* - 5 for all other modes
|
||||
*/
|
||||
getStartingLevel(): number {
|
||||
if (Overrides.STARTING_LEVEL_OVERRIDE) {
|
||||
if (Overrides.STARTING_LEVEL_OVERRIDE > 0) {
|
||||
return Overrides.STARTING_LEVEL_OVERRIDE;
|
||||
}
|
||||
switch (this.modeId) {
|
||||
|
@ -1218,12 +1218,8 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (
|
||||
pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) &&
|
||||
pokemonEvolutions[pokemon.species.speciesId].filter(
|
||||
e =>
|
||||
e.item === this.evolutionItem &&
|
||||
(!e.condition || e.condition.predicate(pokemon)) &&
|
||||
(e.preFormKey === null || e.preFormKey === pokemon.getFormKey()),
|
||||
).length &&
|
||||
pokemonEvolutions[pokemon.species.speciesId].filter(e => e.validate(pokemon, false, this.evolutionItem))
|
||||
.length &&
|
||||
pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX
|
||||
) {
|
||||
return null;
|
||||
@ -1232,12 +1228,8 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
|
||||
pokemon.isFusion() &&
|
||||
pokemon.fusionSpecies &&
|
||||
pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) &&
|
||||
pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(
|
||||
e =>
|
||||
e.item === this.evolutionItem &&
|
||||
(!e.condition || e.condition.predicate(pokemon)) &&
|
||||
(e.preFormKey === null || e.preFormKey === pokemon.getFusionFormKey()),
|
||||
).length &&
|
||||
pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.validate(pokemon, true, this.evolutionItem))
|
||||
.length &&
|
||||
pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX
|
||||
) {
|
||||
return null;
|
||||
@ -1597,12 +1589,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
)
|
||||
.flatMap(p => {
|
||||
const evolutions = pokemonEvolutions[p.species.speciesId];
|
||||
return evolutions.filter(
|
||||
e =>
|
||||
e.item !== EvolutionItem.NONE &&
|
||||
(e.evoFormKey === null || (e.preFormKey || "") === p.getFormKey()) &&
|
||||
(!e.condition || e.condition.predicate(p)),
|
||||
);
|
||||
return evolutions.filter(e => e.isValidItemEvolution(p));
|
||||
}),
|
||||
party
|
||||
.filter(
|
||||
@ -1616,16 +1603,11 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
)
|
||||
.flatMap(p => {
|
||||
const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId];
|
||||
return evolutions.filter(
|
||||
e =>
|
||||
e.item !== EvolutionItem.NONE &&
|
||||
(e.evoFormKey === null || (e.preFormKey || "") === p.getFusionFormKey()) &&
|
||||
(!e.condition || e.condition.predicate(p)),
|
||||
);
|
||||
return evolutions.filter(e => e.validate(p, true));
|
||||
}),
|
||||
]
|
||||
.flat()
|
||||
.flatMap(e => e.item)
|
||||
.flatMap(e => e.evoItem)
|
||||
.filter(i => (!!i && i > 50) === rare);
|
||||
|
||||
if (!evolutionItemPool.length) {
|
||||
@ -1892,7 +1874,8 @@ const modifierTypeInitObj = Object.freeze({
|
||||
new PokemonHeldItemModifierType(
|
||||
"modifierType:ModifierType.EVOLUTION_TRACKER_GIMMIGHOUL",
|
||||
"relic_gold",
|
||||
(type, args) => new EvoTrackerModifier(type, (args[0] as Pokemon).id, SpeciesId.GIMMIGHOUL, 10),
|
||||
(type, args) =>
|
||||
new EvoTrackerModifier(type, (args[0] as Pokemon).id, SpeciesId.GIMMIGHOUL, 10, (args[1] as number) ?? 1),
|
||||
),
|
||||
|
||||
MEGA_BRACELET: () =>
|
||||
|
@ -772,6 +772,10 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
||||
return this.getMaxHeldItemCount(pokemon);
|
||||
}
|
||||
|
||||
getSpecies(): SpeciesId | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract getMaxHeldItemCount(pokemon?: Pokemon): number;
|
||||
}
|
||||
|
||||
@ -918,27 +922,14 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier {
|
||||
return true;
|
||||
}
|
||||
|
||||
getIconStackText(virtual?: boolean): Phaser.GameObjects.BitmapText | null {
|
||||
if (this.getMaxStackCount() === 1 || (virtual && !this.virtualStackCount)) {
|
||||
return null;
|
||||
}
|
||||
getIconStackText(_virtual?: boolean): Phaser.GameObjects.BitmapText | null {
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
const pokemon = globalScene.getPokemonById(this.pokemonId);
|
||||
const count = (pokemon?.getPersistentTreasureCount() || 0) + this.getStackCount();
|
||||
|
||||
this.stackCount = pokemon
|
||||
? pokemon.evoCounter +
|
||||
pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length +
|
||||
globalScene.findModifiers(
|
||||
m =>
|
||||
m instanceof MoneyMultiplierModifier ||
|
||||
m instanceof ExtraModifierModifier ||
|
||||
m instanceof TempExtraModifierModifier,
|
||||
).length
|
||||
: this.stackCount;
|
||||
|
||||
const text = globalScene.add.bitmapText(10, 15, "item-count", this.stackCount.toString(), 11);
|
||||
const text = globalScene.add.bitmapText(10, 15, "item-count", count.toString(), 11);
|
||||
text.letterSpacing = -0.5;
|
||||
if (this.getStackCount() >= this.required) {
|
||||
if (count >= this.required) {
|
||||
text.setTint(0xf89890);
|
||||
}
|
||||
text.setOrigin(0, 0);
|
||||
@ -946,18 +937,13 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier {
|
||||
return text;
|
||||
}
|
||||
|
||||
getMaxHeldItemCount(pokemon: Pokemon): number {
|
||||
this.stackCount =
|
||||
pokemon.evoCounter +
|
||||
pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length +
|
||||
globalScene.findModifiers(
|
||||
m =>
|
||||
m instanceof MoneyMultiplierModifier ||
|
||||
m instanceof ExtraModifierModifier ||
|
||||
m instanceof TempExtraModifierModifier,
|
||||
).length;
|
||||
getMaxHeldItemCount(_pokemon: Pokemon): number {
|
||||
return 999;
|
||||
}
|
||||
|
||||
override getSpecies(): SpeciesId {
|
||||
return this.species;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2402,19 +2388,13 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier {
|
||||
override apply(playerPokemon: PlayerPokemon): boolean {
|
||||
let matchingEvolution = pokemonEvolutions.hasOwnProperty(playerPokemon.species.speciesId)
|
||||
? pokemonEvolutions[playerPokemon.species.speciesId].find(
|
||||
e =>
|
||||
e.item === this.type.evolutionItem &&
|
||||
(e.evoFormKey === null || (e.preFormKey || "") === playerPokemon.getFormKey()) &&
|
||||
(!e.condition || e.condition.predicate(playerPokemon)),
|
||||
e => e.evoItem === this.type.evolutionItem && e.validate(playerPokemon, false, e.item!),
|
||||
)
|
||||
: null;
|
||||
|
||||
if (!matchingEvolution && playerPokemon.isFusion()) {
|
||||
matchingEvolution = pokemonEvolutions[playerPokemon.fusionSpecies!.speciesId].find(
|
||||
e =>
|
||||
e.item === this.type.evolutionItem && // TODO: is the bang correct?
|
||||
(e.evoFormKey === null || (e.preFormKey || "") === playerPokemon.getFusionFormKey()) &&
|
||||
(!e.condition || e.condition.predicate(playerPokemon)),
|
||||
e => e.evoItem === this.type.evolutionItem && e.validate(playerPokemon, true, e.item!),
|
||||
);
|
||||
if (matchingEvolution) {
|
||||
matchingEvolution = new FusionSpeciesFormEvolution(playerPokemon.species.speciesId, matchingEvolution);
|
||||
@ -2934,11 +2914,10 @@ export class MoneyRewardModifier extends ConsumableModifier {
|
||||
|
||||
globalScene.getPlayerParty().map(p => {
|
||||
if (p.species?.speciesId === SpeciesId.GIMMIGHOUL || p.fusionSpecies?.speciesId === SpeciesId.GIMMIGHOUL) {
|
||||
p.evoCounter
|
||||
? (p.evoCounter += Math.min(Math.floor(this.moneyMultiplier), 3))
|
||||
: (p.evoCounter = Math.min(Math.floor(this.moneyMultiplier), 3));
|
||||
const factor = Math.min(Math.floor(this.moneyMultiplier), 3);
|
||||
const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(
|
||||
p,
|
||||
factor,
|
||||
) as EvoTrackerModifier;
|
||||
globalScene.addModifier(modifier);
|
||||
}
|
||||
|
@ -80,7 +80,11 @@ class DefaultOverrides {
|
||||
/** Sets the level cap to this number during experience gain calculations. Set to `0` to disable override & use normal wave-based level caps,
|
||||
or any negative number to set it to 9 quadrillion (effectively disabling it). */
|
||||
readonly LEVEL_CAP_OVERRIDE: number = 0;
|
||||
readonly NEVER_CRIT_OVERRIDE: boolean = false;
|
||||
/**
|
||||
* If defined, overrides random critical hit rolls to always or never succeed.
|
||||
* Ignored if the move is guaranteed to always/never crit.
|
||||
*/
|
||||
readonly CRITICAL_HIT_OVERRIDE: boolean | null = null;
|
||||
/** default 1000 */
|
||||
readonly STARTING_MONEY_OVERRIDE: number = 0;
|
||||
/** Sets all shop item prices to 0 */
|
||||
|
@ -4,7 +4,7 @@ import { globalScene } from "#app/global-scene";
|
||||
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
|
@ -821,7 +821,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
* @param effectiveness - The effectiveness of the move against the target
|
||||
*/
|
||||
protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult {
|
||||
const isCritical = target.getCriticalHitResult(user, this.move, false);
|
||||
const isCritical = target.getCriticalHitResult(user, this.move);
|
||||
|
||||
/*
|
||||
* Apply stat changes from {@linkcode move} and gives it to {@linkcode source}
|
||||
|
@ -3,7 +3,7 @@ import { applyChallenges } from "#app/data/challenge";
|
||||
import { ChallengeType } from "#enums/challenge-type";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
||||
import Overrides from "#app/overrides";
|
||||
import { Phase } from "#app/phase";
|
||||
|
@ -6,7 +6,8 @@ import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { randInt, getEnumKeys, isLocal, executeIf, fixedInt, randSeedItem, NumberHolder } from "#app/utils/common";
|
||||
import Overrides from "#app/overrides";
|
||||
|
@ -3,7 +3,8 @@ import { globalScene } from "#app/global-scene";
|
||||
import type { Gender } from "../data/gender";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species";
|
||||
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { Status } from "../data/status-effect";
|
||||
import Pokemon, { EnemyPokemon, PokemonBattleData, PokemonSummonData } from "../field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
@ -45,7 +46,6 @@ export default class PokemonData {
|
||||
public pauseEvolutions: boolean;
|
||||
public pokerus: boolean;
|
||||
public usedTMs: MoveId[];
|
||||
public evoCounter: number;
|
||||
public teraType: PokemonType;
|
||||
public isTerastallized: boolean;
|
||||
public stellarTypesBoosted: PokemonType[];
|
||||
@ -118,7 +118,6 @@ export default class PokemonData {
|
||||
this.pauseEvolutions = !!source.pauseEvolutions;
|
||||
this.pokerus = !!source.pokerus;
|
||||
this.usedTMs = source.usedTMs ?? [];
|
||||
this.evoCounter = source.evoCounter ?? 0;
|
||||
this.teraType = source.teraType as PokemonType;
|
||||
this.isTerastallized = !!source.isTerastallized;
|
||||
this.stellarTypesBoosted = source.stellarTypesBoosted ?? [];
|
||||
|
@ -3,7 +3,7 @@ import type { SystemSaveData, SessionSaveData } from "#app/system/game-data";
|
||||
import { defaultStarterSpecies } from "#app/constants";
|
||||
import { AbilityAttr } from "#enums/ability-attr";
|
||||
import { DexAttr } from "#enums/dex-attr";
|
||||
import { allSpecies } from "#app/data/pokemon-species";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import { isNullOrUndefined } from "#app/utils/common";
|
||||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator";
|
||||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { SessionSaveData, SystemSaveData } from "#app/system/game-data";
|
||||
import { DexAttr } from "#enums/dex-attr";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import type { SystemSaveData } from "#app/system/game-data";
|
||||
import { DexAttr } from "#enums/dex-attr";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
@ -5,7 +5,7 @@ import { getEnumValues, getEnumKeys, fixedInt, randSeedShuffle } from "#app/util
|
||||
import type { IEggOptions } from "../data/egg";
|
||||
import { Egg, getLegendaryGachaSpeciesForTimestamp } from "../data/egg";
|
||||
import { VoucherType, getVoucherTypeIcon } from "../system/voucher";
|
||||
import { getPokemonSpecies } from "../data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { addWindow } from "./ui-theme";
|
||||
import { Tutorial, handleTutorial } from "../tutorial";
|
||||
import { Button } from "#enums/buttons";
|
||||
|
@ -16,7 +16,9 @@ import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import type { LevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, normalForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpeciesForm, normalForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { starterPassiveAbilities } from "#app/data/balance/passives";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
|
@ -8,7 +8,7 @@ import { UiMode } from "#enums/ui-mode";
|
||||
import { FilterTextRow } from "./filter-text";
|
||||
import { allAbilities } from "#app/data/data-lists";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { allSpecies } from "#app/data/pokemon-species";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import i18next from "i18next";
|
||||
|
||||
export default class PokedexScanUiHandler extends FormModalUiHandler {
|
||||
|
@ -7,7 +7,8 @@ import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
||||
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import type { PokemonForm } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpeciesForm, getPokerusStarters, normalForm } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpeciesForm, getPokerusStarters, normalForm } from "#app/data/pokemon-species";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
|
||||
import { catchableSpecies } from "#app/data/balance/biomes";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
|
@ -19,7 +19,8 @@ import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import type { LevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species";
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { GameModes } from "#enums/game-modes";
|
||||
|
@ -9,7 +9,7 @@ import { version } from "../../package.json";
|
||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
|
||||
|
21
src/utils/pokemon-utils.ts
Normal file
21
src/utils/pokemon-utils.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { allSpecies } from "#app/data/data-lists";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/**
|
||||
* Gets the {@linkcode PokemonSpecies} object associated with the {@linkcode SpeciesId} enum given
|
||||
* @param species - The {@linkcode SpeciesId} to fetch.
|
||||
* If an array of `SpeciesId`s is passed (such as for named trainer spawn pools),
|
||||
* one will be selected at random.
|
||||
* @returns The associated {@linkcode PokemonSpecies} object
|
||||
*/
|
||||
export function getPokemonSpecies(species: SpeciesId | SpeciesId[]): PokemonSpecies {
|
||||
if (Array.isArray(species)) {
|
||||
// TODO: this RNG roll should not be handled by this function
|
||||
species = species[Math.floor(Math.random() * species.length)];
|
||||
}
|
||||
if (species >= 2000) {
|
||||
return allSpecies.find(s => s.speciesId === species)!; // TODO: is this bang correct?
|
||||
}
|
||||
return allSpecies[species - 1];
|
||||
}
|
@ -27,7 +27,7 @@ describe("Ability Activation Order", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -27,7 +27,7 @@ describe("Abilities - Analytic", () => {
|
||||
.moveset([MoveId.SPLASH, MoveId.TACKLE])
|
||||
.ability(AbilityId.ANALYTIC)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.startingLevel(200)
|
||||
.enemyLevel(200)
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
|
@ -35,7 +35,7 @@ describe("Abilities - Commander", () => {
|
||||
.moveset([MoveId.LIQUIDATION, MoveId.MEMENTO, MoveId.SPLASH, MoveId.FLIP_TURN])
|
||||
.ability(AbilityId.COMMANDER)
|
||||
.battleStyle("double")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.TACKLE);
|
||||
|
@ -24,7 +24,7 @@ describe("Abilities - Corrosion", () => {
|
||||
game.override
|
||||
.moveset([MoveId.SPLASH])
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.GRIMER)
|
||||
.enemyAbility(AbilityId.CORROSION)
|
||||
.enemyMoveset(MoveId.TOXIC);
|
||||
|
@ -33,7 +33,7 @@ describe("Abilities - Cud Chew", () => {
|
||||
.startingHeldItems([{ name: "BERRY", type: BerryType.SITRUS, count: 1 }])
|
||||
.ability(AbilityId.CUD_CHEW)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -23,7 +23,7 @@ describe("Abilities - Dry Skin", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemyAbility(AbilityId.DRY_SKIN)
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
.enemySpecies(SpeciesId.CHARMANDER)
|
||||
|
@ -28,7 +28,7 @@ describe("Abilities - Early Bird", () => {
|
||||
.moveset([MoveId.REST, MoveId.BELLY_DRUM, MoveId.SPLASH])
|
||||
.ability(AbilityId.EARLY_BIRD)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -32,7 +32,7 @@ describe("Abilities - Flash Fire", () => {
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.startingLevel(20)
|
||||
.enemyLevel(20)
|
||||
.disableCrits();
|
||||
.criticalHits(false);
|
||||
});
|
||||
|
||||
it("immune to Fire-type moves", async () => {
|
||||
|
@ -32,7 +32,7 @@ describe("Abilities - Flower Veil", () => {
|
||||
.enemySpecies(SpeciesId.BULBASAUR)
|
||||
.ability(AbilityId.FLOWER_VEIL)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -21,26 +21,10 @@ describe("Abilities - Forecast", () => {
|
||||
const RAINY_FORM = 2;
|
||||
const SNOWY_FORM = 3;
|
||||
|
||||
/**
|
||||
* Tests form changes based on weather changes
|
||||
* @param {GameManager} game The game manager instance
|
||||
* @param {WeatherType} weather The active weather to set
|
||||
* @param form The expected form based on the active weather
|
||||
* @param initialForm The initial form pre form change
|
||||
*/
|
||||
const testWeatherFormChange = async (game: GameManager, weather: WeatherType, form: number, initialForm?: number) => {
|
||||
game.override.weather(weather).starterForms({ [SpeciesId.CASTFORM]: initialForm });
|
||||
await game.classicMode.startBattle([SpeciesId.CASTFORM]);
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.formIndex).toBe(form);
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests reverting to normal form when Cloud Nine/Air Lock is active on the field
|
||||
* @param {GameManager} game The game manager instance
|
||||
* @param {AbilityId} ability The ability that is active on the field
|
||||
* @param game - The game manager instance
|
||||
* @param ability - The ability that is active on the field
|
||||
*/
|
||||
const testRevertFormAgainstAbility = async (game: GameManager, ability: AbilityId) => {
|
||||
game.override.starterForms({ [SpeciesId.CASTFORM]: SUNNY_FORM }).enemyAbility(ability);
|
||||
@ -191,10 +175,6 @@ describe("Abilities - Forecast", () => {
|
||||
30 * 1000,
|
||||
);
|
||||
|
||||
it("reverts to Normal Form during Clear weather", async () => {
|
||||
await testWeatherFormChange(game, WeatherType.NONE, NORMAL_FORM, SUNNY_FORM);
|
||||
});
|
||||
|
||||
it("reverts to Normal Form if a Pokémon on the field has Air Lock", async () => {
|
||||
await testRevertFormAgainstAbility(game, AbilityId.AIR_LOCK);
|
||||
});
|
||||
@ -277,4 +257,20 @@ describe("Abilities - Forecast", () => {
|
||||
|
||||
expect(castform.formIndex).toBe(NORMAL_FORM);
|
||||
});
|
||||
|
||||
// NOTE: The following pairs of tests are intentionally testing the same scenario, switching the player and enemy pokemon
|
||||
// as this is a regression test where the order of player and enemy mattered.
|
||||
it("should trigger player's form change when summoned at the same time as an enemy with a weather changing ability", async () => {
|
||||
game.override.enemyAbility(AbilityId.DROUGHT);
|
||||
await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]);
|
||||
const castform = game.scene.getPlayerPokemon()!;
|
||||
expect(castform.formIndex).toBe(SUNNY_FORM);
|
||||
});
|
||||
|
||||
it("should trigger enemy's form change when summoned at the same time as a player with a weather changing ability", async () => {
|
||||
game.override.ability(AbilityId.DROUGHT).enemySpecies(SpeciesId.CASTFORM).enemyAbility(AbilityId.FORECAST);
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
const castform = game.scene.getEnemyPokemon()!;
|
||||
expect(castform.formIndex).toBe(SUNNY_FORM);
|
||||
});
|
||||
});
|
||||
|
@ -33,7 +33,7 @@ describe("Abilities - Good As Gold", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.GOOD_AS_GOLD)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -41,7 +41,7 @@ describe("Abilities - Gulp Missile", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.battleStyle("single")
|
||||
.moveset([MoveId.SURF, MoveId.DIVE, MoveId.SPLASH, MoveId.SUBSTITUTE])
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
|
@ -47,7 +47,7 @@ describe("Abilities - Harvest", () => {
|
||||
.ability(AbilityId.HARVEST)
|
||||
.startingLevel(100)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.statusActivation(false) // Since we're using nuzzle to proc both enigma and sitrus berries
|
||||
.weather(WeatherType.SUNNY) // guaranteed recovery
|
||||
.enemyLevel(1)
|
||||
|
@ -30,7 +30,7 @@ describe("Abilities - Healer", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("double")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Heatproof", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.CHARMANDER)
|
||||
.enemyAbility(AbilityId.HEATPROOF)
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
|
@ -29,7 +29,7 @@ describe("Abilities - Honey Gather", () => {
|
||||
.ability(AbilityId.HONEY_GATHER)
|
||||
.passiveAbility(AbilityId.RUN_AWAY)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Hustle", () => {
|
||||
game.override
|
||||
.ability(AbilityId.HUSTLE)
|
||||
.moveset([MoveId.TACKLE, MoveId.GIGA_DRAIN, MoveId.FISSURE])
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.battleStyle("single")
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
.enemySpecies(SpeciesId.SHUCKLE)
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Immunity", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -31,7 +31,7 @@ describe("Abilities - Infiltrator", () => {
|
||||
.moveset([MoveId.TACKLE, MoveId.WATER_GUN, MoveId.SPORE, MoveId.BABY_DOLL_EYES])
|
||||
.ability(AbilityId.INFILTRATOR)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Insomnia", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -27,7 +27,7 @@ describe("Abilities - Lightningrod", () => {
|
||||
.moveset([MoveId.SPLASH, MoveId.SHOCK_WAVE])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("double")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Limber", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -32,7 +32,7 @@ describe("Abilities - Magic Bounce", () => {
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.moveset([MoveId.GROWL, MoveId.SPLASH])
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.MAGIC_BOUNCE)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -89,9 +89,7 @@ describe("Abilities - Magic Guard", () => {
|
||||
});
|
||||
|
||||
it("ability effect should not persist when the ability is replaced", async () => {
|
||||
game.override
|
||||
.enemyMoveset([MoveId.WORRY_SEED, MoveId.WORRY_SEED, MoveId.WORRY_SEED, MoveId.WORRY_SEED])
|
||||
.statusEffect(StatusEffect.POISON);
|
||||
game.override.enemyMoveset(MoveId.WORRY_SEED).statusEffect(StatusEffect.POISON);
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Magma Armor", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Mimicry", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.MIMICRY)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ describe("Abilities - Mold Breaker", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.MOLD_BREAKER)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -25,7 +25,7 @@ describe("Abilities - Mummy", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.MUMMY)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.TACKLE);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Mycelium Might", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.SHUCKLE)
|
||||
.enemyAbility(AbilityId.CLEAR_BODY)
|
||||
|
||||
|
@ -31,7 +31,7 @@ describe("Abilities - Neutralizing Gas", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.NEUTRALIZING_GAS)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -29,7 +29,7 @@ describe("Abilities - Normalize", () => {
|
||||
.moveset([MoveId.TACKLE])
|
||||
.ability(AbilityId.NORMALIZE)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Oblivious", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -26,7 +26,7 @@ describe("Abilities - Own Tempo", () => {
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -28,7 +28,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.ability(AbilityId.PARENTAL_BOND)
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
.enemyAbility(AbilityId.FUR_COAT)
|
||||
|
@ -23,13 +23,13 @@ describe("Abilities - Perish Song", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.starterSpecies(SpeciesId.CURSOLA)
|
||||
.ability(AbilityId.PERISH_BODY)
|
||||
.moveset([MoveId.SPLASH])
|
||||
.enemyMoveset([MoveId.AQUA_JET]);
|
||||
.moveset(MoveId.SPLASH)
|
||||
.enemyMoveset(MoveId.AQUA_JET);
|
||||
});
|
||||
|
||||
it("should trigger when hit with damaging move", async () => {
|
||||
|
@ -28,7 +28,7 @@ describe("Abilities - Protosynthesis", () => {
|
||||
.moveset([MoveId.SPLASH, MoveId.TACKLE])
|
||||
.ability(AbilityId.PROTOSYNTHESIS)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
|
@ -24,7 +24,7 @@ describe("Abilities - Sand Spit", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.starterSpecies(SpeciesId.SILICOBRA)
|
||||
|
@ -31,7 +31,7 @@ describe("Abilities - Sap Sipper", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.ability(AbilityId.SAP_SIPPER)
|
||||
.enemySpecies(SpeciesId.RATTATA)
|
||||
.enemyAbility(AbilityId.SAP_SIPPER)
|
||||
|
@ -24,12 +24,12 @@ describe("Abilities - Seed Sower", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.starterSpecies(SpeciesId.ARBOLIVA)
|
||||
.ability(AbilityId.SEED_SOWER)
|
||||
.moveset([MoveId.SPLASH]);
|
||||
.moveset(MoveId.SPLASH);
|
||||
});
|
||||
|
||||
it("should trigger when hit with damaging move", async () => {
|
||||
|
@ -24,7 +24,7 @@ describe("Abilities - Serene Grace", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.battleStyle("single")
|
||||
.ability(AbilityId.SERENE_GRACE)
|
||||
.moveset([MoveId.AIR_SLASH])
|
||||
|
@ -31,7 +31,7 @@ describe("Abilities - Sheer Force", () => {
|
||||
.enemySpecies(SpeciesId.ONIX)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset([MoveId.SPLASH])
|
||||
.disableCrits();
|
||||
.criticalHits(false);
|
||||
});
|
||||
|
||||
const SHEER_FORCE_MULT = 1.3;
|
||||
|
69
test/abilities/shell-armor.test.ts
Normal file
69
test/abilities/shell-armor.test.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import type Move from "#app/data/moves/move";
|
||||
import Pokemon from "#app/field/pokemon";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
|
||||
|
||||
describe("Abilities - Shell Armor", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let critSpy: MockInstance<(source: Pokemon, move: Move, simulated?: boolean) => boolean>;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.ability(AbilityId.SHELL_ARMOR)
|
||||
.battleStyle("single")
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.statusEffect(StatusEffect.POISON);
|
||||
|
||||
critSpy = vi.spyOn(Pokemon.prototype, "getCriticalHitResult");
|
||||
});
|
||||
|
||||
it("should prevent natural crit rolls from suceeding", async () => {
|
||||
game.override.criticalHits(true); // force random crit rolls to always succeed
|
||||
await game.classicMode.startBattle([SpeciesId.ABOMASNOW]);
|
||||
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(critSpy).toHaveReturnedWith(false);
|
||||
});
|
||||
|
||||
it("should prevent guaranteed-crit moves from critting", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.ABOMASNOW]);
|
||||
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.FLOWER_TRICK);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(critSpy).toHaveReturnedWith(false);
|
||||
});
|
||||
|
||||
it("should block Merciless guaranteed crits", async () => {
|
||||
game.override.enemyAbility(AbilityId.MERCILESS);
|
||||
await game.classicMode.startBattle([SpeciesId.ABOMASNOW]);
|
||||
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(critSpy).toHaveReturnedWith(false);
|
||||
});
|
||||
});
|
@ -27,7 +27,7 @@ describe("Abilities - Stakeout", () => {
|
||||
.moveset([MoveId.SPLASH, MoveId.SURF])
|
||||
.ability(AbilityId.STAKEOUT)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.startingLevel(100)
|
||||
.enemyLevel(100)
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
|
@ -24,7 +24,7 @@ describe("Abilities - Stall", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.REGIELEKI)
|
||||
.enemyAbility(AbilityId.STALL)
|
||||
.enemyMoveset(MoveId.QUICK_ATTACK)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user