mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-24 00:09:31 +02:00
Merge branch 'beta' into qol/locales-cleanup
This commit is contained in:
commit
c26cfb4281
@ -17,7 +17,12 @@ If you have the motivation and experience with Typescript/Javascript (or are wil
|
||||
2. Run `npm run start:dev` to locally run the project in `localhost:8000`
|
||||
|
||||
#### Linting
|
||||
We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script.
|
||||
We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script. To view the complete rules, check out the [eslint.config.js](./eslint.config.js) file.
|
||||
|
||||
### 📚 Documentation
|
||||
You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html).
|
||||
For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file.
|
||||
For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file.
|
||||
|
||||
### ❔ FAQ
|
||||
|
||||
|
@ -41,6 +41,11 @@ export default [
|
||||
"keyword-spacing": ["error", { "before": true, "after": true }], // Enforces spacing before and after keywords
|
||||
"comma-spacing": ["error", { "before": false, "after": true }], // Enforces spacing after comma
|
||||
"import-x/extensions": ["error", "never", { "json": "always" }], // Enforces no extension for imports unless json
|
||||
"array-bracket-spacing": ["error", "always", { "objectsInArrays": false, "arraysInArrays": false }], // Enforces consistent spacing inside array brackets
|
||||
"object-curly-spacing": ["error", "always", { "arraysInObjects": false, "objectsInObjects": false }], // Enforces consistent spacing inside braces of object literals, destructuring assignments, and import/export specifiers
|
||||
"computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets
|
||||
"space-infix-ops": ["error", { "int32Hint": false }], // Enforces spacing around infix operators
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], // Disallows multiple empty lines
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -853,14 +853,14 @@
|
||||
"spriteSourceSize": { "x": 7, "y": 2, "w": 27, "h": 26 },
|
||||
"sourceSize": { "w": 40, "h": 30 }
|
||||
},
|
||||
"981_2.png": {
|
||||
"981_2": {
|
||||
"frame": { "x": 108, "y": 87, "w": 23, "h": 30 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 9, "y": 0, "w": 23, "h": 30 },
|
||||
"sourceSize": { "w": 40, "h": 30 }
|
||||
},
|
||||
"981_3.png": {
|
||||
"981_3": {
|
||||
"frame": { "x": 246, "y": 86, "w": 23, "h": 30 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
|
BIN
public/images/ui/friendship.png
Normal file
BIN
public/images/ui/friendship.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 B |
BIN
public/images/ui/friendship_overlay.png
Normal file
BIN
public/images/ui/friendship_overlay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 B |
BIN
public/images/ui/legacy/friendship.png
Normal file
BIN
public/images/ui/legacy/friendship.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 B |
BIN
public/images/ui/legacy/friendship_overlay.png
Normal file
BIN
public/images/ui/legacy/friendship_overlay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 B |
@ -1,57 +1,57 @@
|
||||
import Phaser from "phaser";
|
||||
import UI from "./ui/ui";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species";
|
||||
import UI from "#app/ui/ui";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
||||
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||
import * as Utils from "./utils";
|
||||
import * as Utils from "#app/utils";
|
||||
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
|
||||
import { PokeballType } from "./data/pokeball";
|
||||
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
|
||||
import { Phase } from "./phase";
|
||||
import { initGameSpeed } from "./system/game-speed";
|
||||
import { Arena, ArenaBase } from "./field/arena";
|
||||
import { GameData } from "./system/game-data";
|
||||
import { addTextObject, getTextColor, TextStyle } from "./ui/text";
|
||||
import { allMoves } from "./data/move";
|
||||
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "./modifier/modifier-type";
|
||||
import AbilityBar from "./ui/ability-bar";
|
||||
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, ChangeMovePriorityAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "./data/ability";
|
||||
import Battle, { BattleType, FixedBattleConfig } from "./battle";
|
||||
import { GameMode, GameModes, getGameMode } from "./game-mode";
|
||||
import FieldSpritePipeline from "./pipelines/field-sprite";
|
||||
import SpritePipeline from "./pipelines/sprite";
|
||||
import PartyExpBar from "./ui/party-exp-bar";
|
||||
import { trainerConfigs, TrainerSlot } from "./data/trainer-config";
|
||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
||||
import TrainerData from "./system/trainer-data";
|
||||
import { PokeballType } from "#app/data/pokeball";
|
||||
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
|
||||
import { Phase } from "#app/phase";
|
||||
import { initGameSpeed } from "#app/system/game-speed";
|
||||
import { Arena, ArenaBase } from "#app/field/arena";
|
||||
import { GameData } from "#app/system/game-data";
|
||||
import { addTextObject, getTextColor, TextStyle } from "#app/ui/text";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import AbilityBar from "#app/ui/ability-bar";
|
||||
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "#app/data/ability";
|
||||
import Battle, { BattleType, FixedBattleConfig } from "#app/battle";
|
||||
import { GameMode, GameModes, getGameMode } from "#app/game-mode";
|
||||
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
||||
import SpritePipeline from "#app/pipelines/sprite";
|
||||
import PartyExpBar from "#app/ui/party-exp-bar";
|
||||
import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
|
||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
||||
import TrainerData from "#app/system/trainer-data";
|
||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { pokemonPrevolutions } from "./data/balance/pokemon-evolutions";
|
||||
import PokeballTray from "./ui/pokeball-tray";
|
||||
import InvertPostFX from "./pipelines/invert";
|
||||
import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv";
|
||||
import { Voucher, vouchers } from "./system/voucher";
|
||||
import { Gender } from "./data/gender";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import PokeballTray from "#app/ui/pokeball-tray";
|
||||
import InvertPostFX from "#app/pipelines/invert";
|
||||
import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv";
|
||||
import { Voucher, vouchers } from "#app/system/voucher";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
||||
import { addUiThemeOverrides } from "./ui/ui-theme";
|
||||
import PokemonData from "./system/pokemon-data";
|
||||
import { Nature } from "./data/nature";
|
||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms";
|
||||
import { FormChangePhase } from "./phases/form-change-phase";
|
||||
import { getTypeRgb } from "./data/type";
|
||||
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
|
||||
import CharSprite from "./ui/char-sprite";
|
||||
import DamageNumberHandler from "./field/damage-number-handler";
|
||||
import PokemonInfoContainer from "./ui/pokemon-info-container";
|
||||
import { biomeDepths, getBiomeName } from "./data/balance/biomes";
|
||||
import { SceneBase } from "./scene-base";
|
||||
import CandyBar from "./ui/candy-bar";
|
||||
import { Variant, variantData } from "./data/variant";
|
||||
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { Nature } from "#app/data/nature";
|
||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
|
||||
import { FormChangePhase } from "#app/phases/form-change-phase";
|
||||
import { getTypeRgb } from "#app/data/type";
|
||||
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
||||
import CharSprite from "#app/ui/char-sprite";
|
||||
import DamageNumberHandler from "#app/field/damage-number-handler";
|
||||
import PokemonInfoContainer from "#app/ui/pokemon-info-container";
|
||||
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
|
||||
import { SceneBase } from "#app/scene-base";
|
||||
import CandyBar from "#app/ui/candy-bar";
|
||||
import { Variant, variantData } from "#app/data/variant";
|
||||
import { Localizable } from "#app/interfaces/locales";
|
||||
import Overrides from "#app/overrides";
|
||||
import { InputsController } from "./inputs-controller";
|
||||
import { UiInputs } from "./ui-inputs";
|
||||
import { NewArenaEvent } from "./events/battle-scene";
|
||||
import { ArenaFlyout } from "./ui/arena-flyout";
|
||||
import { InputsController } from "#app/inputs-controller";
|
||||
import { UiInputs } from "#app/ui-inputs";
|
||||
import { NewArenaEvent } from "#app/events/battle-scene";
|
||||
import { ArenaFlyout } from "#app/ui/arena-flyout";
|
||||
import { EaseType } from "#enums/ease-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { BattleStyle } from "#enums/battle-style";
|
||||
@ -66,27 +66,27 @@ import { TimedEventManager } from "#app/timed-event-manager";
|
||||
import { PokemonAnimType } from "#enums/pokemon-anim-type";
|
||||
import i18next from "i18next";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { battleSpecDialogue } from "./data/dialogue";
|
||||
import { LoadingScene } from "./loading-scene";
|
||||
import { LevelCapPhase } from "./phases/level-cap-phase";
|
||||
import { LoginPhase } from "./phases/login-phase";
|
||||
import { MessagePhase } from "./phases/message-phase";
|
||||
import { MovePhase } from "./phases/move-phase";
|
||||
import { NewBiomeEncounterPhase } from "./phases/new-biome-encounter-phase";
|
||||
import { NextEncounterPhase } from "./phases/next-encounter-phase";
|
||||
import { PokemonAnimPhase } from "./phases/pokemon-anim-phase";
|
||||
import { QuietFormChangePhase } from "./phases/quiet-form-change-phase";
|
||||
import { ReturnPhase } from "./phases/return-phase";
|
||||
import { SelectBiomePhase } from "./phases/select-biome-phase";
|
||||
import { ShowTrainerPhase } from "./phases/show-trainer-phase";
|
||||
import { SummonPhase } from "./phases/summon-phase";
|
||||
import { SwitchPhase } from "./phases/switch-phase";
|
||||
import { TitlePhase } from "./phases/title-phase";
|
||||
import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase";
|
||||
import { TurnInitPhase } from "./phases/turn-init-phase";
|
||||
import { ShopCursorTarget } from "./enums/shop-cursor-target";
|
||||
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
|
||||
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||
import { LoadingScene } from "#app/loading-scene";
|
||||
import { LevelCapPhase } from "#app/phases/level-cap-phase";
|
||||
import { LoginPhase } from "#app/phases/login-phase";
|
||||
import { MessagePhase } from "#app/phases/message-phase";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
|
||||
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
||||
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
||||
import { ReturnPhase } from "#app/phases/return-phase";
|
||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
||||
import { SummonPhase } from "#app/phases/summon-phase";
|
||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||
import { TitlePhase } from "#app/phases/title-phase";
|
||||
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
@ -94,7 +94,7 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import { ExpPhase } from "#app/phases/exp-phase";
|
||||
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { ExpGainsSpeed } from "./enums/exp-gains-speed";
|
||||
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
|
||||
|
||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
||||
|
||||
@ -889,6 +889,9 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource);
|
||||
if (Overrides.OPP_FUSION_OVERRIDE) {
|
||||
pokemon.generateFusionSpecies();
|
||||
}
|
||||
|
||||
overrideModifiers(this, false);
|
||||
overrideHeldItems(this, pokemon, false);
|
||||
@ -2356,17 +2359,6 @@ export default class BattleScene extends SceneBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void {
|
||||
const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority);
|
||||
applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority);
|
||||
const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value);
|
||||
if (lowerPriorityPhase) {
|
||||
this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase);
|
||||
} else {
|
||||
this.pushPhase(movePhase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase()
|
||||
* @param phase {@linkcode Phase} the phase to be added
|
||||
@ -2445,7 +2437,10 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) {
|
||||
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
|
||||
success = modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]);
|
||||
const pokemon = this.getPokemonById(modifier.pokemonId);
|
||||
if (pokemon) {
|
||||
success = modifier.apply(pokemon, true);
|
||||
}
|
||||
}
|
||||
if (playSound && !this.sound.get(soundName)) {
|
||||
this.playSound(soundName);
|
||||
@ -2472,7 +2467,7 @@ export default class BattleScene extends SceneBase {
|
||||
for (const p in this.party) {
|
||||
const pokemon = this.party[p];
|
||||
|
||||
const args: any[] = [ pokemon ];
|
||||
const args: unknown[] = [];
|
||||
if (modifier instanceof PokemonHpRestoreModifier) {
|
||||
if (!(modifier as PokemonHpRestoreModifier).fainted) {
|
||||
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
|
||||
@ -2485,8 +2480,8 @@ export default class BattleScene extends SceneBase {
|
||||
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
|
||||
}
|
||||
|
||||
if (modifier.shouldApply(args)) {
|
||||
const result = modifier.apply(args);
|
||||
if (modifier.shouldApply(pokemon, ...args)) {
|
||||
const result = modifier.apply(pokemon, ...args);
|
||||
if (result instanceof Promise) {
|
||||
modifierPromises.push(result.then(s => success ||= s));
|
||||
} else {
|
||||
@ -2498,8 +2493,8 @@ export default class BattleScene extends SceneBase {
|
||||
return Promise.allSettled([ this.party.map(p => p.updateInfo(instant)), ...modifierPromises ]).then(() => resolve(success));
|
||||
} else {
|
||||
const args = [ this ];
|
||||
if (modifier.shouldApply(args)) {
|
||||
const result = modifier.apply(args);
|
||||
if (modifier.shouldApply(...args)) {
|
||||
const result = modifier.apply(...args);
|
||||
if (result instanceof Promise) {
|
||||
return result.then(success => resolve(success));
|
||||
} else {
|
||||
@ -2521,7 +2516,10 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) {
|
||||
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
|
||||
modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]);
|
||||
const pokemon = this.getPokemonById(modifier.pokemonId);
|
||||
if (pokemon) {
|
||||
modifier.apply(pokemon, true);
|
||||
}
|
||||
}
|
||||
for (const rm of modifiersToRemove) {
|
||||
this.removeModifier(rm, true);
|
||||
@ -2755,7 +2753,10 @@ export default class BattleScene extends SceneBase {
|
||||
if (modifierIndex > -1) {
|
||||
modifiers.splice(modifierIndex, 1);
|
||||
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
|
||||
modifier.apply([ this.getPokemonById(modifier.pokemonId), false ]);
|
||||
const pokemon = this.getPokemonById(modifier.pokemonId);
|
||||
if (pokemon) {
|
||||
modifier.apply(pokemon, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2773,16 +2774,36 @@ export default class BattleScene extends SceneBase {
|
||||
return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType);
|
||||
}
|
||||
|
||||
findModifiers(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier[] {
|
||||
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m));
|
||||
/**
|
||||
* Get all of the modifiers that pass the `modifierFilter` function
|
||||
* @param modifierFilter The function used to filter a target's modifiers
|
||||
* @param isPlayer Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
|
||||
* @returns the list of all modifiers that passed the `modifierFilter` function
|
||||
*/
|
||||
findModifiers(modifierFilter: ModifierPredicate, isPlayer: boolean = true): PersistentModifier[] {
|
||||
return (isPlayer ? this.modifiers : this.enemyModifiers).filter(modifierFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first modifier that pass the `modifierFilter` function
|
||||
* @param modifierFilter The function used to filter a target's modifiers
|
||||
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
|
||||
* @returns the first modifier that passed the `modifierFilter` function; `undefined` if none passed
|
||||
*/
|
||||
findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined {
|
||||
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m));
|
||||
return (player ? this.modifiers : this.enemyModifiers).find(modifierFilter);
|
||||
}
|
||||
|
||||
applyShuffledModifiers(scene: BattleScene, modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier[] {
|
||||
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
|
||||
/**
|
||||
* Apply all modifiers that match `modifierType` in a random order
|
||||
* @param scene {@linkcode BattleScene} used to randomize the order of modifiers
|
||||
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
|
||||
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
|
||||
* @param ...args The list of arguments needed to invoke `modifierType.apply`
|
||||
* @returns the list of all modifiers that matched `modifierType` and were applied.
|
||||
*/
|
||||
applyShuffledModifiers<T extends PersistentModifier>(scene: BattleScene, modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
|
||||
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
|
||||
scene.executeWithSeedOffset(() => {
|
||||
const shuffleModifiers = mods => {
|
||||
if (mods.length < 1) {
|
||||
@ -2796,15 +2817,23 @@ export default class BattleScene extends SceneBase {
|
||||
return this.applyModifiersInternal(modifiers, player, args);
|
||||
}
|
||||
|
||||
applyModifiers(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier[] {
|
||||
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
|
||||
/**
|
||||
* Apply all modifiers that match `modifierType`
|
||||
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
|
||||
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
|
||||
* @param ...args The list of arguments needed to invoke `modifierType.apply`
|
||||
* @returns the list of all modifiers that matched `modifierType` and were applied.
|
||||
*/
|
||||
applyModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
|
||||
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
|
||||
return this.applyModifiersInternal(modifiers, player, args);
|
||||
}
|
||||
|
||||
applyModifiersInternal(modifiers: PersistentModifier[], player: boolean, args: any[]): PersistentModifier[] {
|
||||
const appliedModifiers: PersistentModifier[] = [];
|
||||
/** Helper function to apply all passed modifiers */
|
||||
applyModifiersInternal<T extends PersistentModifier>(modifiers: T[], player: boolean, args: Parameters<T["apply"]>): T[] {
|
||||
const appliedModifiers: T[] = [];
|
||||
for (const modifier of modifiers) {
|
||||
if (modifier.apply(args)) {
|
||||
if (modifier.apply(...args)) {
|
||||
console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
|
||||
appliedModifiers.push(modifier);
|
||||
}
|
||||
@ -2813,10 +2842,17 @@ export default class BattleScene extends SceneBase {
|
||||
return appliedModifiers;
|
||||
}
|
||||
|
||||
applyModifier(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier | null {
|
||||
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
|
||||
/**
|
||||
* Apply the first modifier that matches `modifierType`
|
||||
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
|
||||
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
|
||||
* @param ...args The list of arguments needed to invoke `modifierType.apply`
|
||||
* @returns the first modifier that matches `modifierType` and was applied; return `null` if none matched
|
||||
*/
|
||||
applyModifier<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T | null {
|
||||
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
|
||||
for (const modifier of modifiers) {
|
||||
if (modifier.apply(args)) {
|
||||
if (modifier.apply(...args)) {
|
||||
console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
|
||||
return modifier;
|
||||
}
|
||||
@ -2882,7 +2918,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
}
|
||||
|
||||
validateAchv(achv: Achv, args?: any[]): boolean {
|
||||
validateAchv(achv: Achv, args?: unknown[]): boolean {
|
||||
if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) {
|
||||
this.gameData.achvUnlocks[achv.id] = new Date().getTime();
|
||||
this.ui.achvBar.showAchv(achv);
|
||||
@ -2895,7 +2931,7 @@ export default class BattleScene extends SceneBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
validateVoucher(voucher: Voucher, args?: any[]): boolean {
|
||||
validateVoucher(voucher: Voucher, args?: unknown[]): boolean {
|
||||
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) {
|
||||
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
|
||||
this.ui.achvBar.showAchv(voucher);
|
||||
|
@ -1107,12 +1107,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.QUILLADIN,
|
||||
Species.CHESNAUGHT,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.FLETCHLING,
|
||||
@ -2878,12 +2873,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.FLETCHLING,
|
||||
@ -3910,7 +3900,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.YAMPER,
|
||||
Species.BOLTUND,
|
||||
Species.ZAMAZENTA,
|
||||
Species.URSHIFU,
|
||||
Species.ZARUDE,
|
||||
Species.GLASTRIER,
|
||||
Species.WYRDEER,
|
||||
@ -3940,6 +3929,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.ALOLA_NINETALES,
|
||||
Species.ALOLA_PERSIAN,
|
||||
Species.ALOLA_GOLEM,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
Species.HISUI_GROWLITHE,
|
||||
Species.HISUI_ARCANINE,
|
||||
Species.HISUI_TYPHLOSION,
|
||||
@ -6987,14 +6980,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -8301,7 +8287,6 @@ export const tmSpecies: TmSpecies = {
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"sandy",
|
||||
"trash",
|
||||
],
|
||||
Species.ALOLA_SANDSHREW,
|
||||
Species.ALOLA_SANDSLASH,
|
||||
@ -8612,12 +8597,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CHESNAUGHT,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.LITLEO,
|
||||
@ -9406,14 +9386,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -9766,14 +9739,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.VIVILLON,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.ESPURR,
|
||||
Species.MEOWSTIC,
|
||||
@ -11147,14 +11113,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -13657,12 +13616,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.FLETCHLING,
|
||||
Species.FLETCHINDER,
|
||||
Species.TALONFLAME,
|
||||
@ -15326,14 +15280,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -16934,14 +16881,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -18483,14 +18423,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -20250,14 +20183,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -21583,12 +21509,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.LITLEO,
|
||||
@ -22516,7 +22437,6 @@ export const tmSpecies: TmSpecies = {
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"sandy",
|
||||
"trash",
|
||||
],
|
||||
Species.ALOLA_DIGLETT,
|
||||
Species.ALOLA_DUGTRIO,
|
||||
@ -22691,14 +22611,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CHESNAUGHT,
|
||||
Species.VIVILLON,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -23402,12 +23315,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.FLETCHLING,
|
||||
@ -24134,12 +24042,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.KELDEO,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.PANCHAM,
|
||||
Species.PANGORO,
|
||||
Species.HONEDGE,
|
||||
@ -24842,14 +24745,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -25712,14 +25608,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -26695,14 +26584,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -27845,14 +27727,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -28911,14 +28786,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -29514,14 +29382,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.VIVILLON,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.ESPURR,
|
||||
Species.MEOWSTIC,
|
||||
@ -31408,14 +31269,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -32327,14 +32181,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -33037,14 +32884,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -33471,7 +33311,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.ARCTOVISH,
|
||||
Species.ZACIAN,
|
||||
Species.ZAMAZENTA,
|
||||
Species.URSHIFU,
|
||||
Species.ZARUDE,
|
||||
Species.REGIDRAGO,
|
||||
Species.GLASTRIER,
|
||||
@ -33522,6 +33361,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.ALOLA_MUK,
|
||||
Species.GALAR_MEOWTH,
|
||||
Species.GALAR_STUNFISK,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
[
|
||||
Species.CALYREX,
|
||||
"ice",
|
||||
@ -36644,14 +36487,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -37389,14 +37225,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -38266,23 +38095,11 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -39323,12 +39140,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CHESPIN,
|
||||
Species.QUILLADIN,
|
||||
Species.CHESNAUGHT,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.SKIDDO,
|
||||
@ -40356,7 +40168,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.FENNEKIN,
|
||||
Species.BRAIXEN,
|
||||
Species.DELPHOX,
|
||||
[
|
||||
Species.MEOWSTIC,
|
||||
"male",
|
||||
],
|
||||
Species.KLEFKI,
|
||||
Species.PHANTUMP,
|
||||
Species.TREVENANT,
|
||||
@ -41805,7 +41620,6 @@ export const tmSpecies: TmSpecies = {
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"sandy",
|
||||
"trash",
|
||||
],
|
||||
Species.ALOLA_SANDSHREW,
|
||||
Species.ALOLA_SANDSLASH,
|
||||
@ -43701,12 +43515,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.SKIDDO,
|
||||
@ -44160,14 +43969,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.QUILLADIN,
|
||||
Species.CHESNAUGHT,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -44377,14 +44179,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.VIVILLON,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.ESPURR,
|
||||
Species.MEOWSTIC,
|
||||
@ -45256,6 +45051,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.IRON_CROWN,
|
||||
Species.TERAPAGOS,
|
||||
Species.ALOLA_EXEGGUTOR,
|
||||
[
|
||||
Species.INDEEDEE,
|
||||
"male",
|
||||
],
|
||||
],
|
||||
[Moves.GYRO_BALL]: [
|
||||
Species.SQUIRTLE,
|
||||
@ -47501,12 +47300,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.ACCELGOR,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.SKRELP,
|
||||
Species.DRAGALGE,
|
||||
Species.MAREANIE,
|
||||
@ -48143,7 +47937,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.RUNERIGUS,
|
||||
Species.MORPEKO,
|
||||
Species.DURALUDON,
|
||||
Species.URSHIFU,
|
||||
Species.ZARUDE,
|
||||
Species.SPECTRIER,
|
||||
Species.OVERQWIL,
|
||||
@ -48179,6 +47972,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.GALAR_WEEZING,
|
||||
Species.GALAR_MOLTRES,
|
||||
Species.GALAR_YAMASK,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
[
|
||||
Species.CALYREX,
|
||||
"shadow",
|
||||
@ -48456,14 +48253,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.QUILLADIN,
|
||||
Species.CHESNAUGHT,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -49622,7 +49412,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.TORTERRA,
|
||||
Species.BUDEW,
|
||||
Species.ROSERADE,
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"plant",
|
||||
],
|
||||
Species.MOTHIM,
|
||||
Species.CHERUBI,
|
||||
Species.CHERRIM,
|
||||
@ -49698,14 +49491,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CHESNAUGHT,
|
||||
Species.VIVILLON,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -52635,7 +52421,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.TORTERRA,
|
||||
Species.BUDEW,
|
||||
Species.ROSERADE,
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"plant",
|
||||
],
|
||||
Species.SNOVER,
|
||||
Species.ABOMASNOW,
|
||||
Species.TANGROWTH,
|
||||
@ -53751,7 +53540,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.BIBAREL,
|
||||
Species.BUDEW,
|
||||
Species.ROSERADE,
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"plant",
|
||||
],
|
||||
Species.PACHIRISU,
|
||||
Species.CHERUBI,
|
||||
Species.CHERRIM,
|
||||
@ -53863,14 +53655,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -55590,12 +55375,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CHESPIN,
|
||||
Species.QUILLADIN,
|
||||
Species.CHESNAUGHT,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.PANCHAM,
|
||||
Species.PANGORO,
|
||||
Species.HELIOPTILE,
|
||||
@ -55877,7 +55657,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.MR_RIME,
|
||||
Species.MORPEKO,
|
||||
Species.DURALUDON,
|
||||
Species.URSHIFU,
|
||||
Species.SPECTRIER,
|
||||
Species.MEOWSCARADA,
|
||||
Species.SQUAWKABILLY,
|
||||
@ -55918,6 +55697,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.GALAR_MOLTRES,
|
||||
Species.GALAR_SLOWKING,
|
||||
Species.GALAR_STUNFISK,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
[
|
||||
Species.CALYREX,
|
||||
"shadow",
|
||||
@ -56577,14 +56360,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -57019,14 +56795,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.PANCHAM,
|
||||
Species.PANGORO,
|
||||
@ -57354,14 +57123,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.BRAIXEN,
|
||||
Species.DELPHOX,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.ESPURR,
|
||||
Species.MEOWSTIC,
|
||||
@ -58997,7 +58759,6 @@ export const tmSpecies: TmSpecies = {
|
||||
[
|
||||
Species.WORMADAM,
|
||||
"sandy",
|
||||
"trash",
|
||||
],
|
||||
Species.ALOLA_SANDSHREW,
|
||||
Species.ALOLA_SANDSLASH,
|
||||
@ -60070,7 +59831,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DURALUDON,
|
||||
Species.ZACIAN,
|
||||
Species.ZAMAZENTA,
|
||||
Species.URSHIFU,
|
||||
Species.ZARUDE,
|
||||
Species.GLASTRIER,
|
||||
Species.SPECTRIER,
|
||||
@ -60116,6 +59876,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.HISUI_ZOROARK,
|
||||
Species.HISUI_BRAVIARY,
|
||||
Species.BLOODMOON_URSALUNA,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
],
|
||||
[Moves.PHANTOM_FORCE]: [
|
||||
Species.HAUNTER,
|
||||
@ -60446,14 +60210,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.QUILLADIN,
|
||||
Species.CHESNAUGHT,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -60529,16 +60286,12 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.WHIMSICOTT,
|
||||
Species.ALOMOMOLA,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
[
|
||||
Species.MEOWSTIC,
|
||||
"male",
|
||||
],
|
||||
Species.SPRITZEE,
|
||||
Species.AROMATISSE,
|
||||
Species.SYLVEON,
|
||||
@ -61402,14 +61155,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -61899,14 +61645,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.MELOETTA,
|
||||
Species.DELPHOX,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SPRITZEE,
|
||||
Species.AROMATISSE,
|
||||
@ -62617,7 +62356,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.SIRFETCHD,
|
||||
Species.FALINKS,
|
||||
Species.PINCURCHIN,
|
||||
Species.URSHIFU,
|
||||
Species.ZARUDE,
|
||||
Species.GLASTRIER,
|
||||
Species.TAROUNTULA,
|
||||
@ -62647,6 +62385,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.GALAR_ZAPDOS,
|
||||
Species.GALAR_CORSOLA,
|
||||
Species.GALAR_LINOONE,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
[
|
||||
Species.CALYREX,
|
||||
"ice",
|
||||
@ -63541,12 +63283,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.KELDEO,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.INKAY,
|
||||
Species.MALAMAR,
|
||||
Species.BINACLE,
|
||||
@ -64755,7 +64492,6 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.OBSTAGOON,
|
||||
Species.PERRSERKER,
|
||||
Species.MORPEKO,
|
||||
Species.URSHIFU,
|
||||
Species.ZARUDE,
|
||||
Species.GLASTRIER,
|
||||
Species.SPECTRIER,
|
||||
@ -64803,6 +64539,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.GALAR_LINOONE,
|
||||
Species.GALAR_DARMANITAN,
|
||||
Species.GALAR_STUNFISK,
|
||||
[
|
||||
Species.URSHIFU,
|
||||
"single-strike",
|
||||
],
|
||||
[
|
||||
Species.CALYREX,
|
||||
"ice",
|
||||
@ -65933,12 +65673,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DELPHOX,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.BUNNELBY,
|
||||
Species.DIGGERSBY,
|
||||
Species.FLETCHLING,
|
||||
@ -65950,14 +65685,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
[
|
||||
Species.FLOETTE,
|
||||
"red",
|
||||
"yellow",
|
||||
"orange",
|
||||
"blue",
|
||||
"white",
|
||||
],
|
||||
Species.FLORGES,
|
||||
Species.SKIDDO,
|
||||
Species.GOGOAT,
|
||||
@ -66291,6 +66019,13 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.MUNKIDORI,
|
||||
Species.FEZANDIPITI,
|
||||
Species.OGERPON,
|
||||
Species.ARCHALUDON,
|
||||
Species.HYDRAPPLE,
|
||||
Species.GOUGING_FIRE,
|
||||
Species.RAGING_BOLT,
|
||||
Species.IRON_BOULDER,
|
||||
Species.IRON_CROWN,
|
||||
Species.PECHARUNT,
|
||||
Species.GALAR_MEOWTH,
|
||||
Species.GALAR_SLOWPOKE,
|
||||
Species.GALAR_SLOWBRO,
|
||||
@ -66429,16 +66164,8 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.PIPLUP,
|
||||
Species.PRINPLUP,
|
||||
Species.EMPOLEON,
|
||||
[
|
||||
Species.SHELLOS,
|
||||
"east",
|
||||
"west",
|
||||
],
|
||||
[
|
||||
Species.GASTRODON,
|
||||
"east",
|
||||
"west",
|
||||
],
|
||||
Species.MISMAGIUS,
|
||||
Species.HAPPINY,
|
||||
Species.SNOVER,
|
||||
@ -66456,25 +66183,11 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CUBCHOO,
|
||||
Species.BEARTIC,
|
||||
Species.CRYOGONAL,
|
||||
[
|
||||
Species.TORNADUS,
|
||||
"incarnate",
|
||||
"therian",
|
||||
],
|
||||
[
|
||||
Species.KYUREM,
|
||||
"",
|
||||
"black",
|
||||
"white",
|
||||
],
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.SKRELP,
|
||||
Species.DRAGALGE,
|
||||
Species.BERGMITE,
|
||||
@ -66482,11 +66195,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.DIANCIE,
|
||||
Species.PRIMARINA,
|
||||
Species.CRABOMINABLE,
|
||||
[
|
||||
Species.MAGEARNA,
|
||||
"",
|
||||
"original",
|
||||
],
|
||||
Species.INTELEON,
|
||||
Species.FROSMOTH,
|
||||
Species.EISCUE,
|
||||
@ -66779,12 +66488,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.CHESNAUGHT,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.LITLEO,
|
||||
Species.PYROAR,
|
||||
Species.FLABEBE,
|
||||
@ -67007,23 +66711,14 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.WEAVILE,
|
||||
Species.GLACEON,
|
||||
Species.FROSLASS,
|
||||
[
|
||||
Species.PALKIA,
|
||||
"",
|
||||
"origin",
|
||||
],
|
||||
Species.PHIONE,
|
||||
Species.MANAPHY,
|
||||
Species.ARCEUS,
|
||||
Species.OSHAWOTT,
|
||||
Species.DEWOTT,
|
||||
Species.SAMUROTT,
|
||||
[
|
||||
Species.BASCULIN,
|
||||
"red-striped",
|
||||
"blue-striped",
|
||||
"white-striped",
|
||||
],
|
||||
Species.MINCCINO,
|
||||
Species.CINCCINO,
|
||||
Species.DUCKLETT,
|
||||
@ -67036,12 +66731,7 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.KELDEO,
|
||||
Species.FROAKIE,
|
||||
Species.FROGADIER,
|
||||
[
|
||||
Species.GRENINJA,
|
||||
"",
|
||||
"battle-bond",
|
||||
"ash",
|
||||
],
|
||||
Species.FLABEBE,
|
||||
Species.FLOETTE,
|
||||
Species.FLORGES,
|
||||
@ -67304,6 +66994,10 @@ export const tmSpecies: TmSpecies = {
|
||||
Species.FEZANDIPITI,
|
||||
Species.ALOLA_RAICHU,
|
||||
Species.ETERNAL_FLOETTE,
|
||||
[
|
||||
Species.INDEEDEE,
|
||||
"female",
|
||||
],
|
||||
],
|
||||
[Moves.TEMPER_FLARE]: [
|
||||
Species.CHARMANDER,
|
||||
|
@ -2267,7 +2267,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag {
|
||||
* Uses DisabledTag's selectionDeniedText() message
|
||||
*/
|
||||
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:moveDisabled", { moveName: allMoves[move].name });
|
||||
return i18next.t("battle:moveDisabledHealBlock", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name, healBlockName: allMoves[Moves.HEAL_BLOCK].name });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2277,7 +2277,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag {
|
||||
* @returns {string} text to display when the move is interrupted
|
||||
*/
|
||||
override interruptedText(pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
return i18next.t("battle:moveDisabledHealBlock", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name, healBlockName: allMoves[Moves.HEAL_BLOCK].name });
|
||||
}
|
||||
|
||||
override onRemove(pokemon: Pokemon): void {
|
||||
@ -2530,8 +2530,8 @@ export class TormentTag extends MoveRestrictionBattlerTag {
|
||||
return false;
|
||||
}
|
||||
|
||||
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
|
||||
override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
|
||||
return i18next.t("battle:moveDisabledTorment", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) });
|
||||
}
|
||||
}
|
||||
|
||||
@ -2559,12 +2559,12 @@ export class TauntTag extends MoveRestrictionBattlerTag {
|
||||
return allMoves[move].category === MoveCategory.STATUS;
|
||||
}
|
||||
|
||||
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
|
||||
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:moveDisabledTaunt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
}
|
||||
|
||||
override interruptedText(pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
return i18next.t("battle:moveDisabledTaunt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
}
|
||||
}
|
||||
|
||||
@ -2609,12 +2609,12 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
|
||||
return false;
|
||||
}
|
||||
|
||||
override selectionDeniedText(_pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name });
|
||||
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:moveDisabledImprison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
}
|
||||
|
||||
override interruptedText(pokemon: Pokemon, move: Moves): string {
|
||||
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
return i18next.t("battle:moveDisabledImprison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
||||
}
|
||||
}
|
||||
|
||||
|
106
src/data/move.ts
106
src/data/move.ts
@ -25,7 +25,6 @@ import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { MoveUsedEvent } from "#app/events/battle-scene";
|
||||
import { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat";
|
||||
import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
@ -34,6 +33,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
|
||||
import { GameMode } from "#app/game-mode";
|
||||
import { applyChallenges, ChallengeType } from "./challenge";
|
||||
@ -1585,12 +1585,31 @@ export class PartyStatusCureAttr extends MoveEffectAttr {
|
||||
if (!this.canApply(user, target, move, args)) {
|
||||
return false;
|
||||
}
|
||||
this.addPartyCurePhase(user);
|
||||
const partyPokemon = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
||||
partyPokemon.forEach(p => this.cureStatus(p, user.id));
|
||||
|
||||
if (this.message) {
|
||||
user.scene.queueMessage(this.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
addPartyCurePhase(user: Pokemon) {
|
||||
user.scene.unshiftPhase(new PartyStatusCurePhase(user.scene, user, this.message, this.abilityCondition));
|
||||
/**
|
||||
* Tries to cure the status of the given {@linkcode Pokemon}
|
||||
* @param pokemon The {@linkcode Pokemon} to cure.
|
||||
* @param userId The ID of the (move) {@linkcode Pokemon | user}.
|
||||
*/
|
||||
public cureStatus(pokemon: Pokemon, userId: number) {
|
||||
if (!pokemon.isOnField() || pokemon.id === userId) { // user always cures its own status, regardless of ability
|
||||
pokemon.resetStatus(false);
|
||||
pokemon.updateInfo();
|
||||
} else if (!pokemon.hasAbility(this.abilityCondition)) {
|
||||
pokemon.resetStatus();
|
||||
pokemon.updateInfo();
|
||||
} else {
|
||||
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2668,20 +2687,42 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute used for moves that change stat stages
|
||||
* @param stats {@linkcode BattleStat} array of stats to be changed
|
||||
* @param stages stages by which to change the stats, from -6 to 6
|
||||
* @param selfTarget whether the changes are applied to the user (true) or the target (false)
|
||||
* @param condition {@linkcode MoveConditionFunc} optional condition to trigger the stat change
|
||||
* @param firstHitOnly whether the stat change only applies on the first hit of a multi hit move
|
||||
* @param moveEffectTrigger {@linkcode MoveEffectTrigger} the trigger for the effect to take place
|
||||
* @param firstTargetOnly whether, if this is a multi target move, to only apply the effect after the first target is hit, rather than once for each target
|
||||
* @param lastHitOnly whether the effect should only apply after the last hit of a multi hit move
|
||||
*
|
||||
* @extends MoveEffectAttr
|
||||
* @see {@linkcode apply}
|
||||
*/
|
||||
export class StatStageChangeAttr extends MoveEffectAttr {
|
||||
public stats: BattleStat[];
|
||||
public stages: integer;
|
||||
private condition: MoveConditionFunc | null;
|
||||
private showMessage: boolean;
|
||||
|
||||
constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) {
|
||||
super(selfTarget, moveEffectTrigger, firstHitOnly, false, firstTargetOnly);
|
||||
constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false, lastHitOnly: boolean = false) {
|
||||
super(selfTarget, moveEffectTrigger, firstHitOnly, lastHitOnly, firstTargetOnly);
|
||||
this.stats = stats;
|
||||
this.stages = stages;
|
||||
this.condition = condition!; // TODO: is this bang correct?
|
||||
this.showMessage = showMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to change stats of the user or target (depending on value of selfTarget) if conditions are met
|
||||
* @param user {@linkcode Pokemon} the user of the move
|
||||
* @param target {@linkcode Pokemon} the target of the move
|
||||
* @param move {@linkcode Move} the move
|
||||
* @param args unused
|
||||
* @returns whether stat stages were changed
|
||||
*/
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise<boolean> {
|
||||
if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) {
|
||||
return false;
|
||||
@ -3901,7 +3942,14 @@ export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr {
|
||||
/**
|
||||
* Attribute used for tera moves that change category based on the user's Atk and SpAtk stats
|
||||
* Note: Currently, `getEffectiveStat` does not ignore all abilities that affect stats except those
|
||||
* with the attribute of `StatMultiplierAbAttr`
|
||||
* TODO: Remove the `.partial()` tag from Tera Blast and Tera Starstorm when the above issue is resolved
|
||||
* @extends VariableMoveCategoryAttr
|
||||
*/
|
||||
export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const category = (args[0] as Utils.NumberHolder);
|
||||
|
||||
@ -3990,6 +4038,30 @@ export class VariableMoveTypeAttr extends MoveAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute used for Tera Starstorm that changes the move type to Stellar
|
||||
* @extends VariableMoveTypeAttr
|
||||
*/
|
||||
export class TeraStarstormTypeAttr extends VariableMoveTypeAttr {
|
||||
/**
|
||||
*
|
||||
* @param user the {@linkcode Pokemon} using the move
|
||||
* @param target n/a
|
||||
* @param move n/a
|
||||
* @param args[0] {@linkcode Utils.NumberHolder} the move type
|
||||
* @returns `true` if the move type is changed to {@linkcode Type.STELLAR}, `false` otherwise
|
||||
*/
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (user.isTerastallized() && (user.hasFusionSpecies(Species.TERAPAGOS) || user.species.speciesId === Species.TERAPAGOS)) {
|
||||
const moveType = args[0] as Utils.NumberHolder;
|
||||
|
||||
moveType.value = Type.STELLAR;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const moveType = args[0];
|
||||
@ -9149,16 +9221,15 @@ export function initMoves() {
|
||||
.attr(HalfSacrificialAttr),
|
||||
new AttackMove(Moves.EXPANDING_FORCE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 1.5 : 1)
|
||||
.attr(VariableTargetAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 6 : 3),
|
||||
.attr(VariableTargetAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER),
|
||||
new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8)
|
||||
.attr(ClearTerrainAttr)
|
||||
.condition((user, target, move) => !!user.scene.arena.terrain),
|
||||
new AttackMove(Moves.SCALE_SHOT, Type.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, -1, 0, 8)
|
||||
//.attr(StatStageChangeAttr, Stat.SPD, 1, true) // TODO: Have boosts only apply at end of move, not after every hit
|
||||
//.attr(StatStageChangeAttr, Stat.DEF, -1, true)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], 1, true, null, true, false, MoveEffectTrigger.HIT, false, true)
|
||||
.attr(StatStageChangeAttr, [ Stat.DEF ], -1, true, null, true, false, MoveEffectTrigger.HIT, false, true)
|
||||
.attr(MultiHitAttr)
|
||||
.makesContact(false)
|
||||
.partial(),
|
||||
.makesContact(false),
|
||||
new AttackMove(Moves.METEOR_BEAM, Type.ROCK, MoveCategory.SPECIAL, 120, 90, 10, 100, 0, 8)
|
||||
.attr(ChargeAttr, ChargeAnim.METEOR_BEAM_CHARGING, i18next.t("moveTriggers:isOverflowingWithSpacePower", { pokemonName: "{USER}" }), null, true)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true)
|
||||
@ -9424,10 +9495,11 @@ export function initMoves() {
|
||||
.unimplemented(),
|
||||
End Unused */
|
||||
new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
|
||||
.attr(TeraBlastCategoryAttr)
|
||||
.attr(TeraMoveCategoryAttr)
|
||||
.attr(TeraBlastTypeAttr)
|
||||
.attr(TeraBlastPowerAttr)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)),
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR))
|
||||
.partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */
|
||||
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
|
||||
.attr(ProtectAttr, BattlerTagType.SILK_TRAP)
|
||||
.condition(failIfLastCondition),
|
||||
@ -9617,8 +9689,10 @@ export function initMoves() {
|
||||
.attr(ElectroShotChargeAttr)
|
||||
.ignoresVirtual(),
|
||||
new AttackMove(Moves.TERA_STARSTORM, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9)
|
||||
.attr(TeraBlastCategoryAttr)
|
||||
.partial(),
|
||||
.attr(TeraMoveCategoryAttr)
|
||||
.attr(TeraStarstormTypeAttr)
|
||||
.attr(VariableTargetAttr, (user, target, move) => (user.hasFusionSpecies(Species.TERAPAGOS) || user.species.speciesId === Species.TERAPAGOS) && user.isTerastallized() ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER)
|
||||
.partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */
|
||||
new AttackMove(Moves.FICKLE_BEAM, Type.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9)
|
||||
.attr(PreMoveMessageAttr, doublePowerChanceMessageFunc)
|
||||
.attr(DoublePowerChanceAttr),
|
||||
|
@ -1033,4 +1033,3 @@ export class WeightRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -484,6 +484,8 @@ export abstract class PokemonSpeciesForm {
|
||||
frameRate: 12,
|
||||
repeat: -1
|
||||
});
|
||||
} else {
|
||||
scene.anims.get(spriteKey).frameRate = 12;
|
||||
}
|
||||
let spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
|
||||
const useExpSprite = scene.experimentalSprites && scene.hasExpSprite(spriteKey);
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
export enum ArenaTagType {
|
||||
NONE = "NONE",
|
||||
MUD_SPORT = "MUD_SPORT",
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
export enum BattlerTagType {
|
||||
NONE = "NONE",
|
||||
RECHARGING = "RECHARGING",
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
export enum BerryType {
|
||||
SITRUS,
|
||||
LUM,
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
export enum Biome {
|
||||
TOWN,
|
||||
PLAINS,
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
export enum TimeOfDay {
|
||||
ALL = -1,
|
||||
DAWN,
|
||||
|
@ -392,16 +392,16 @@ export class Arena {
|
||||
return true;
|
||||
}
|
||||
|
||||
isMoveWeatherCancelled(user: Pokemon, move: Move) {
|
||||
return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(user, move);
|
||||
public isMoveWeatherCancelled(user: Pokemon, move: Move): boolean {
|
||||
return !!this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(user, move);
|
||||
}
|
||||
|
||||
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move) {
|
||||
return this.terrain && this.terrain.isMoveTerrainCancelled(user, targets, move);
|
||||
public isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
||||
return !!this.terrain && this.terrain.isMoveTerrainCancelled(user, targets, move);
|
||||
}
|
||||
|
||||
getTerrainType() : TerrainType {
|
||||
return this.terrain?.terrainType || TerrainType.NONE;
|
||||
public getTerrainType(): TerrainType {
|
||||
return this.terrain?.terrainType ?? TerrainType.NONE;
|
||||
}
|
||||
|
||||
getAttackTypeMultiplier(attackType: Type, grounded: boolean): number {
|
||||
|
@ -1087,6 +1087,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return !!this.fusionSpecies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the {@linkcode Pokemon} has a fusion with the specified {@linkcode Species}.
|
||||
* @param species the pokemon {@linkcode Species} to check
|
||||
* @returns `true` if the {@linkcode Pokemon} has a fusion with the specified {@linkcode Species}, `false` otherwise
|
||||
*/
|
||||
hasFusionSpecies(species: Species): boolean {
|
||||
return this.fusionSpecies?.speciesId === species;
|
||||
}
|
||||
|
||||
abstract isBoss(): boolean;
|
||||
|
||||
getMoveset(ignoreOverride?: boolean): (PokemonMove | null)[] {
|
||||
@ -1522,7 +1531,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the effectiveness of a move against the Pokémon.
|
||||
* This includes modifiers from move and ability attributes.
|
||||
@ -1956,7 +1964,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
&& species.speciesId !== this.species.speciesId;
|
||||
};
|
||||
|
||||
this.fusionSpecies = this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
|
||||
let fusionOverride: PokemonSpecies | undefined = undefined;
|
||||
|
||||
if (forStarter && this instanceof PlayerPokemon && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) {
|
||||
fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE);
|
||||
} else if (this instanceof EnemyPokemon && Overrides.OPP_FUSION_SPECIES_OVERRIDE) {
|
||||
fusionOverride = getPokemonSpecies(Overrides.OPP_FUSION_SPECIES_OVERRIDE);
|
||||
}
|
||||
|
||||
this.fusionSpecies = fusionOverride ?? this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
|
||||
this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? 2 : this.fusionSpecies.ability2 !== this.fusionSpecies.ability1 ? randAbilityIndex : 0);
|
||||
this.fusionShiny = this.shiny;
|
||||
this.fusionVariant = this.variant;
|
||||
@ -5011,8 +5027,12 @@ export class PokemonBattleSummonData {
|
||||
export class PokemonTurnData {
|
||||
public flinched: boolean = false;
|
||||
public acted: boolean = false;
|
||||
public hitCount: number;
|
||||
public hitsLeft: number;
|
||||
public hitCount: number = 0;
|
||||
/**
|
||||
* - `-1` = Calculate how many hits are left
|
||||
* - `0` = Move is finished
|
||||
*/
|
||||
public hitsLeft: number = -1;
|
||||
public damageDealt: number = 0;
|
||||
public currDamageDealt: number = 0;
|
||||
public damageTaken: number = 0;
|
||||
@ -5098,7 +5118,7 @@ export class PokemonMove {
|
||||
* @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag})
|
||||
* @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`.
|
||||
*/
|
||||
isUsable(pokemon: Pokemon, ignorePp?: boolean, ignoreRestrictionTags?: boolean): boolean {
|
||||
isUsable(pokemon: Pokemon, ignorePp: boolean = false, ignoreRestrictionTags: boolean = false): boolean {
|
||||
if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ export class LoadingScene extends SceneBase {
|
||||
this.loadAtlas("prompt", "ui");
|
||||
this.loadImage("candy", "ui");
|
||||
this.loadImage("candy_overlay", "ui");
|
||||
this.loadImage("friendship", "ui");
|
||||
this.loadImage("friendship_overlay", "ui");
|
||||
this.loadImage("cursor", "ui");
|
||||
this.loadImage("cursor_reverse", "ui");
|
||||
for (const wv of Utils.getEnumValues(WindowVariant)) {
|
||||
|
@ -1,38 +1,35 @@
|
||||
import * as Modifiers from "#app/modifier/modifier";
|
||||
import { MoneyMultiplierModifier } from "#app/modifier/modifier";
|
||||
import { allMoves, AttackMove, selfStatLowerMoves } from "#app/data/move";
|
||||
import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS, PokeballType } from "#app/data/pokeball";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms";
|
||||
import { Type } from "#app/data/type";
|
||||
import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
||||
import * as Utils from "#app/utils";
|
||||
import { getBerryEffectDescription, getBerryName } from "#app/data/berry";
|
||||
import { Unlockables } from "#app/system/unlockables";
|
||||
import { getStatusEffectDescriptor, StatusEffect } from "#app/data/status-effect";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher";
|
||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeCondition, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { allMoves, AttackMove, selfStatLowerMoves } from "#app/data/move";
|
||||
import { getNatureName, getNatureStatMultiplier, Nature } from "#app/data/nature";
|
||||
import i18next from "i18next";
|
||||
import { getModifierTierTextTint } from "#app/ui/text";
|
||||
import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS, PokeballType } from "#app/data/pokeball";
|
||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeCondition, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
||||
import { getStatusEffectDescriptor, StatusEffect } from "#app/data/status-effect";
|
||||
import { Type } from "#app/data/type";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { AddPokeballModifier, AddVoucherModifier, AttackTypeBoosterModifier, BaseStatModifier, BerryModifier, BoostBugSpawnModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, CritBoosterModifier, DamageMoneyRewardModifier, DoubleBattleChanceBoosterModifier, EnemyAttackStatusEffectChanceModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, EvolutionItemModifier, EvolutionStatBoosterModifier, EvoTrackerModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, GigantamaxAccessModifier, HealingBoosterModifier, HealShopCostModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, IvScannerModifier, LevelIncrementBoosterModifier, LockModifierTiersModifier, MapModifier, MegaEvolutionAccessModifier, MoneyInterestModifier, MoneyMultiplierModifier, MoneyRewardModifier, MultipleParticipantExpBonusModifier, PokemonAllMovePpRestoreModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, PokemonInstantReviveModifier, PokemonLevelIncrementModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PokemonNatureChangeModifier, PokemonNatureWeightModifier, PokemonPpRestoreModifier, PokemonPpUpModifier, PokemonStatusHealModifier, PreserveBerryModifier, RememberMoveModifier, ResetNegativeStatStageModifier, ShinyRateBoosterModifier, SpeciesCritBoosterModifier, SpeciesStatBoosterModifier, SurviveDamageModifier, SwitchEffectTransferModifier, TempCritBoosterModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, TerastallizeModifier, TmModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier, type EnemyPersistentModifier, type Modifier, type PersistentModifier } from "#app/modifier/modifier";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import Overrides from "#app/overrides";
|
||||
import { Unlockables } from "#app/system/unlockables";
|
||||
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher";
|
||||
import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
||||
import { getModifierTierTextTint } from "#app/ui/text";
|
||||
import { formatMoney, getEnumKeys, getEnumValues, IntegerHolder, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { PermanentStat, TEMP_BATTLE_STATS, TempBattleStat, Stat, getStatKey } from "#enums/stat";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import { getStatKey, PermanentStat, Stat, TEMP_BATTLE_STATS, TempBattleStat } from "#enums/stat";
|
||||
import i18next from "i18next";
|
||||
|
||||
const outputModifierData = false;
|
||||
const useMaxWeightForOutput = false;
|
||||
|
||||
type Modifier = Modifiers.Modifier;
|
||||
|
||||
export enum ModifierPoolType {
|
||||
PLAYER,
|
||||
WILD,
|
||||
@ -97,7 +94,7 @@ export class ModifierType {
|
||||
// Try multiple pool types in case of stolen items
|
||||
for (const type of poolTypes) {
|
||||
const pool = getModifierPoolForType(type);
|
||||
for (const tier of Utils.getEnumValues(ModifierTier)) {
|
||||
for (const tier of getEnumValues(ModifierTier)) {
|
||||
if (!pool.hasOwnProperty(tier)) {
|
||||
continue;
|
||||
}
|
||||
@ -171,7 +168,7 @@ class AddPokeballModifierType extends ModifierType {
|
||||
private count: integer;
|
||||
|
||||
constructor(iconImage: string, pokeballType: PokeballType, count: integer) {
|
||||
super("", iconImage, (_type, _args) => new Modifiers.AddPokeballModifier(this, pokeballType, count), "pb", "se/pb_bounce_1");
|
||||
super("", iconImage, (_type, _args) => new AddPokeballModifier(this, pokeballType, count), "pb", "se/pb_bounce_1");
|
||||
this.pokeballType = pokeballType;
|
||||
this.count = count;
|
||||
}
|
||||
@ -198,7 +195,7 @@ class AddVoucherModifierType extends ModifierType {
|
||||
private count: integer;
|
||||
|
||||
constructor(voucherType: VoucherType, count: integer) {
|
||||
super("", getVoucherTypeIcon(voucherType), (_type, _args) => new Modifiers.AddVoucherModifier(this, voucherType, count), "voucher");
|
||||
super("", getVoucherTypeIcon(voucherType), (_type, _args) => new AddVoucherModifier(this, voucherType, count), "voucher");
|
||||
this.count = count;
|
||||
this.voucherType = voucherType;
|
||||
}
|
||||
@ -232,7 +229,7 @@ export class PokemonHeldItemModifierType extends PokemonModifierType {
|
||||
constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, group?: string, soundName?: string) {
|
||||
super(localeKey, iconImage, newModifierFunc, (pokemon: PlayerPokemon) => {
|
||||
const dummyModifier = this.newModifier(pokemon);
|
||||
const matchingModifier = pokemon.scene.findModifier(m => m instanceof Modifiers.PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(dummyModifier)) as Modifiers.PokemonHeldItemModifier;
|
||||
const matchingModifier = pokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(dummyModifier)) as PokemonHeldItemModifier;
|
||||
const maxStackCount = dummyModifier.getMaxStackCount(pokemon.scene);
|
||||
if (!maxStackCount) {
|
||||
return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", { "pokemonName": getPokemonNameWithAffix(pokemon) });
|
||||
@ -244,8 +241,8 @@ export class PokemonHeldItemModifierType extends PokemonModifierType {
|
||||
}, group, soundName);
|
||||
}
|
||||
|
||||
newModifier(...args: any[]): Modifiers.PokemonHeldItemModifier {
|
||||
return super.newModifier(...args) as Modifiers.PokemonHeldItemModifier;
|
||||
newModifier(...args: any[]): PokemonHeldItemModifier {
|
||||
return super.newModifier(...args) as PokemonHeldItemModifier;
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,7 +252,7 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
|
||||
protected healStatus: boolean;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, restorePoints: integer, restorePercent: integer, healStatus: boolean = false, newModifierFunc?: NewModifierFunc, selectFilter?: PokemonSelectFilter, group?: string) {
|
||||
super(localeKey, iconImage, newModifierFunc || ((_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.restorePercent, this.healStatus, false)),
|
||||
super(localeKey, iconImage, newModifierFunc || ((_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.restorePercent, this.healStatus, false)),
|
||||
selectFilter || ((pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.hp || (pokemon.isFullHp() && (!this.healStatus || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))))) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -282,7 +279,7 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
|
||||
|
||||
export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
|
||||
constructor(localeKey: string, iconImage: string, restorePercent: integer) {
|
||||
super(localeKey, iconImage, 0, restorePercent, false, (_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, 0, this.restorePercent, false, true),
|
||||
super(localeKey, iconImage, 0, restorePercent, false, (_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, 0, this.restorePercent, false, true),
|
||||
((pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.isFainted()) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -305,7 +302,7 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
|
||||
|
||||
export class PokemonStatusHealModifierType extends PokemonModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, ((_type, args) => new Modifiers.PokemonStatusHealModifier(this, (args[0] as PlayerPokemon).id)),
|
||||
super(localeKey, iconImage, ((_type, args) => new PokemonStatusHealModifier(this, (args[0] as PlayerPokemon).id)),
|
||||
((pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.hp || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -333,7 +330,7 @@ export class PokemonPpRestoreModifierType extends PokemonMoveModifierType {
|
||||
protected restorePoints: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, restorePoints: integer) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonPpRestoreModifier(this, (args[0] as PlayerPokemon).id, (args[1] as integer), this.restorePoints),
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonPpRestoreModifier(this, (args[0] as PlayerPokemon).id, (args[1] as integer), this.restorePoints),
|
||||
(_pokemon: PlayerPokemon) => {
|
||||
return null;
|
||||
}, (pokemonMove: PokemonMove) => {
|
||||
@ -358,7 +355,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
|
||||
protected restorePoints: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, restorePoints: integer) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints),
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.getMoveset().filter(m => m?.ppUsed).length) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -381,7 +378,7 @@ export class PokemonPpUpModifierType extends PokemonMoveModifierType {
|
||||
protected upPoints: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, upPoints: integer) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonPpUpModifier(this, (args[0] as PlayerPokemon).id, (args[1] as integer), this.upPoints),
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonPpUpModifier(this, (args[0] as PlayerPokemon).id, (args[1] as integer), this.upPoints),
|
||||
(_pokemon: PlayerPokemon) => {
|
||||
return null;
|
||||
}, (pokemonMove: PokemonMove) => {
|
||||
@ -403,7 +400,7 @@ export class PokemonNatureChangeModifierType extends PokemonModifierType {
|
||||
protected nature: Nature;
|
||||
|
||||
constructor(nature: Nature) {
|
||||
super("", `mint_${Utils.getEnumKeys(Stat).find(s => getNatureStatMultiplier(nature, Stat[s]) > 1)?.toLowerCase() || "neutral" }`, ((_type, args) => new Modifiers.PokemonNatureChangeModifier(this, (args[0] as PlayerPokemon).id, this.nature)),
|
||||
super("", `mint_${getEnumKeys(Stat).find(s => getNatureStatMultiplier(nature, Stat[s]) > 1)?.toLowerCase() || "neutral" }`, ((_type, args) => new PokemonNatureChangeModifier(this, (args[0] as PlayerPokemon).id, this.nature)),
|
||||
((pokemon: PlayerPokemon) => {
|
||||
if (pokemon.getNature() === this.nature) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -425,7 +422,7 @@ export class PokemonNatureChangeModifierType extends PokemonModifierType {
|
||||
|
||||
export class RememberMoveModifierType extends PokemonModifierType {
|
||||
constructor(localeKey: string, iconImage: string, group?: string) {
|
||||
super(localeKey, iconImage, (type, args) => new Modifiers.RememberMoveModifier(type, (args[0] as PlayerPokemon).id, (args[1] as integer)),
|
||||
super(localeKey, iconImage, (type, args) => new RememberMoveModifier(type, (args[0] as PlayerPokemon).id, (args[1] as integer)),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.getLearnableLevelMoves().length) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -439,7 +436,7 @@ export class DoubleBattleChanceBoosterModifierType extends ModifierType {
|
||||
private maxBattles: number;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, maxBattles: number) {
|
||||
super(localeKey, iconImage, (_type, _args) => new Modifiers.DoubleBattleChanceBoosterModifier(this, maxBattles), "lure");
|
||||
super(localeKey, iconImage, (_type, _args) => new DoubleBattleChanceBoosterModifier(this, maxBattles), "lure");
|
||||
|
||||
this.maxBattles = maxBattles;
|
||||
}
|
||||
@ -458,7 +455,7 @@ export class TempStatStageBoosterModifierType extends ModifierType implements Ge
|
||||
|
||||
constructor(stat: TempBattleStat) {
|
||||
const nameKey = TempStatStageBoosterModifierTypeGenerator.items[stat];
|
||||
super("", nameKey, (_type, _args) => new Modifiers.TempStatStageBoosterModifier(this, this.stat, 5));
|
||||
super("", nameKey, (_type, _args) => new TempStatStageBoosterModifier(this, this.stat, 5));
|
||||
|
||||
this.stat = stat;
|
||||
this.nameKey = nameKey;
|
||||
@ -485,7 +482,7 @@ export class BerryModifierType extends PokemonHeldItemModifierType implements Ge
|
||||
private berryType: BerryType;
|
||||
|
||||
constructor(berryType: BerryType) {
|
||||
super("", `${BerryType[berryType].toLowerCase()}_berry`, (type, args) => new Modifiers.BerryModifier(type, (args[0] as Pokemon).id, berryType), "berry");
|
||||
super("", `${BerryType[berryType].toLowerCase()}_berry`, (type, args) => new BerryModifier(type, (args[0] as Pokemon).id, berryType), "berry");
|
||||
|
||||
this.berryType = berryType;
|
||||
}
|
||||
@ -550,7 +547,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i
|
||||
|
||||
constructor(moveType: Type, boostPercent: integer) {
|
||||
super("", `${getAttackTypeBoosterItemName(moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`,
|
||||
(_type, args) => new Modifiers.AttackTypeBoosterModifier(this, (args[0] as Pokemon).id, moveType, boostPercent));
|
||||
(_type, args) => new AttackTypeBoosterModifier(this, (args[0] as Pokemon).id, moveType, boostPercent));
|
||||
|
||||
this.moveType = moveType;
|
||||
this.boostPercent = boostPercent;
|
||||
@ -573,7 +570,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i
|
||||
export type SpeciesStatBoosterItem = keyof typeof SpeciesStatBoosterModifierTypeGenerator.items;
|
||||
|
||||
/**
|
||||
* Modifier type for {@linkcode Modifiers.SpeciesStatBoosterModifier}
|
||||
* Modifier type for {@linkcode SpeciesStatBoosterModifier}
|
||||
* @extends PokemonHeldItemModifierType
|
||||
* @implements GeneratedPersistentModifierType
|
||||
*/
|
||||
@ -582,7 +579,7 @@ export class SpeciesStatBoosterModifierType extends PokemonHeldItemModifierType
|
||||
|
||||
constructor(key: SpeciesStatBoosterItem) {
|
||||
const item = SpeciesStatBoosterModifierTypeGenerator.items[key];
|
||||
super(`modifierType:SpeciesBoosterItem.${key}`, key.toLowerCase(), (type, args) => new Modifiers.SpeciesStatBoosterModifier(type, (args[0] as Pokemon).id, item.stats, item.multiplier, item.species));
|
||||
super(`modifierType:SpeciesBoosterItem.${key}`, key.toLowerCase(), (type, args) => new SpeciesStatBoosterModifier(type, (args[0] as Pokemon).id, item.stats, item.multiplier, item.species));
|
||||
|
||||
this.key = key;
|
||||
}
|
||||
@ -594,12 +591,12 @@ export class SpeciesStatBoosterModifierType extends PokemonHeldItemModifierType
|
||||
|
||||
export class PokemonLevelIncrementModifierType extends PokemonModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), (_pokemon: PlayerPokemon) => null);
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), (_pokemon: PlayerPokemon) => null);
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
let levels = 1;
|
||||
const hasCandyJar = scene.modifiers.find(modifier => modifier instanceof Modifiers.LevelIncrementBoosterModifier);
|
||||
const hasCandyJar = scene.modifiers.find(modifier => modifier instanceof LevelIncrementBoosterModifier);
|
||||
if (hasCandyJar) {
|
||||
levels += hasCandyJar.stackCount;
|
||||
}
|
||||
@ -609,12 +606,12 @@ export class PokemonLevelIncrementModifierType extends PokemonModifierType {
|
||||
|
||||
export class AllPokemonLevelIncrementModifierType extends ModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, (_type, _args) => new Modifiers.PokemonLevelIncrementModifier(this, -1));
|
||||
super(localeKey, iconImage, (_type, _args) => new PokemonLevelIncrementModifier(this, -1));
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
let levels = 1;
|
||||
const hasCandyJar = scene.modifiers.find(modifier => modifier instanceof Modifiers.LevelIncrementBoosterModifier);
|
||||
const hasCandyJar = scene.modifiers.find(modifier => modifier instanceof LevelIncrementBoosterModifier);
|
||||
if (hasCandyJar) {
|
||||
levels += hasCandyJar.stackCount;
|
||||
}
|
||||
@ -628,7 +625,7 @@ export class BaseStatBoosterModifierType extends PokemonHeldItemModifierType imp
|
||||
|
||||
constructor(stat: PermanentStat) {
|
||||
const key = BaseStatBoosterModifierTypeGenerator.items[stat];
|
||||
super("", key, (_type, args) => new Modifiers.BaseStatModifier(this, (args[0] as Pokemon).id, this.stat));
|
||||
super("", key, (_type, args) => new BaseStatModifier(this, (args[0] as Pokemon).id, this.stat));
|
||||
|
||||
this.stat = stat;
|
||||
this.key = key;
|
||||
@ -654,7 +651,7 @@ export class PokemonBaseStatTotalModifierType extends PokemonHeldItemModifierTyp
|
||||
private readonly statModifier: integer;
|
||||
|
||||
constructor(statModifier: integer) {
|
||||
super("modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE", "berry_juice", (_type, args) => new Modifiers.PokemonBaseStatTotalModifier(this, (args[0] as Pokemon).id, this.statModifier));
|
||||
super("modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE", "berry_juice", (_type, args) => new PokemonBaseStatTotalModifier(this, (args[0] as Pokemon).id, this.statModifier));
|
||||
this.statModifier = statModifier;
|
||||
}
|
||||
|
||||
@ -679,7 +676,7 @@ export class PokemonBaseStatFlatModifierType extends PokemonHeldItemModifierType
|
||||
private readonly stats: Stat[];
|
||||
|
||||
constructor(statModifier: integer, stats: Stat[]) {
|
||||
super("modifierType:ModifierType.MYSTERY_ENCOUNTER_OLD_GATEAU", "old_gateau", (_type, args) => new Modifiers.PokemonBaseStatFlatModifier(this, (args[0] as Pokemon).id, this.statModifier, this.stats));
|
||||
super("modifierType:ModifierType.MYSTERY_ENCOUNTER_OLD_GATEAU", "old_gateau", (_type, args) => new PokemonBaseStatFlatModifier(this, (args[0] as Pokemon).id, this.statModifier, this.stats));
|
||||
this.statModifier = statModifier;
|
||||
this.stats = stats;
|
||||
}
|
||||
@ -700,7 +697,7 @@ class AllPokemonFullHpRestoreModifierType extends ModifierType {
|
||||
private descriptionKey: string;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, descriptionKey?: string, newModifierFunc?: NewModifierFunc) {
|
||||
super(localeKey, iconImage, newModifierFunc || ((_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 0, 100, false)));
|
||||
super(localeKey, iconImage, newModifierFunc || ((_type, _args) => new PokemonHpRestoreModifier(this, -1, 0, 100, false)));
|
||||
|
||||
this.descriptionKey = descriptionKey!; // TODO: is this bang correct?
|
||||
}
|
||||
@ -712,7 +709,7 @@ class AllPokemonFullHpRestoreModifierType extends ModifierType {
|
||||
|
||||
class AllPokemonFullReviveModifierType extends AllPokemonFullHpRestoreModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType", (_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 0, 100, false, true));
|
||||
super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType", (_type, _args) => new PokemonHpRestoreModifier(this, -1, 0, 100, false, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,16 +718,16 @@ export class MoneyRewardModifierType extends ModifierType {
|
||||
private moneyMultiplierDescriptorKey: string;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, moneyMultiplier: number, moneyMultiplierDescriptorKey: string) {
|
||||
super(localeKey, iconImage, (_type, _args) => new Modifiers.MoneyRewardModifier(this, moneyMultiplier), "money", "se/buy");
|
||||
super(localeKey, iconImage, (_type, _args) => new MoneyRewardModifier(this, moneyMultiplier), "money", "se/buy");
|
||||
|
||||
this.moneyMultiplier = moneyMultiplier;
|
||||
this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey;
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
const moneyAmount = new Utils.IntegerHolder(scene.getWaveMoneyAmount(this.moneyMultiplier));
|
||||
const moneyAmount = new IntegerHolder(scene.getWaveMoneyAmount(this.moneyMultiplier));
|
||||
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
|
||||
const formattedMoney = Utils.formatMoney(scene.moneyFormat, moneyAmount.value);
|
||||
const formattedMoney = formatMoney(scene.moneyFormat, moneyAmount.value);
|
||||
|
||||
return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", {
|
||||
moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any),
|
||||
@ -743,7 +740,7 @@ export class ExpBoosterModifierType extends ModifierType {
|
||||
private boostPercent: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, boostPercent: integer) {
|
||||
super(localeKey, iconImage, () => new Modifiers.ExpBoosterModifier(this, boostPercent));
|
||||
super(localeKey, iconImage, () => new ExpBoosterModifier(this, boostPercent));
|
||||
|
||||
this.boostPercent = boostPercent;
|
||||
}
|
||||
@ -757,7 +754,7 @@ export class PokemonExpBoosterModifierType extends PokemonHeldItemModifierType {
|
||||
private boostPercent: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, boostPercent: integer) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonExpBoosterModifier(this, (args[0] as Pokemon).id, boostPercent));
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonExpBoosterModifier(this, (args[0] as Pokemon).id, boostPercent));
|
||||
|
||||
this.boostPercent = boostPercent;
|
||||
}
|
||||
@ -769,7 +766,7 @@ export class PokemonExpBoosterModifierType extends PokemonHeldItemModifierType {
|
||||
|
||||
export class PokemonFriendshipBoosterModifierType extends PokemonHeldItemModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonFriendshipBoosterModifier(this, (args[0] as Pokemon).id));
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonFriendshipBoosterModifier(this, (args[0] as Pokemon).id));
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
@ -781,7 +778,7 @@ export class PokemonMoveAccuracyBoosterModifierType extends PokemonHeldItemModif
|
||||
private amount: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, amount: integer, group?: string, soundName?: string) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonMoveAccuracyBoosterModifier(this, (args[0] as Pokemon).id, amount), group, soundName);
|
||||
super(localeKey, iconImage, (_type, args) => new PokemonMoveAccuracyBoosterModifier(this, (args[0] as Pokemon).id, amount), group, soundName);
|
||||
|
||||
this.amount = amount;
|
||||
}
|
||||
@ -793,7 +790,7 @@ export class PokemonMoveAccuracyBoosterModifierType extends PokemonHeldItemModif
|
||||
|
||||
export class PokemonMultiHitModifierType extends PokemonHeldItemModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, (type, args) => new Modifiers.PokemonMultiHitModifier(type as PokemonMultiHitModifierType, (args[0] as Pokemon).id));
|
||||
super(localeKey, iconImage, (type, args) => new PokemonMultiHitModifier(type as PokemonMultiHitModifierType, (args[0] as Pokemon).id));
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
@ -805,7 +802,7 @@ export class TmModifierType extends PokemonModifierType {
|
||||
public moveId: Moves;
|
||||
|
||||
constructor(moveId: Moves) {
|
||||
super("", `tm_${Type[allMoves[moveId].type].toLowerCase()}`, (_type, args) => new Modifiers.TmModifier(this, (args[0] as PlayerPokemon).id),
|
||||
super("", `tm_${Type[allMoves[moveId].type].toLowerCase()}`, (_type, args) => new TmModifier(this, (args[0] as PlayerPokemon).id),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.getMoveset().filter(m => m?.moveId === moveId).length) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -818,7 +815,7 @@ export class TmModifierType extends PokemonModifierType {
|
||||
|
||||
get name(): string {
|
||||
return i18next.t("modifierType:ModifierType.TmModifierType.name", {
|
||||
moveId: Utils.padInt(Object.keys(tmSpecies).indexOf(this.moveId.toString()) + 1, 3),
|
||||
moveId: padInt(Object.keys(tmSpecies).indexOf(this.moveId.toString()) + 1, 3),
|
||||
moveName: allMoves[this.moveId].name,
|
||||
});
|
||||
}
|
||||
@ -832,7 +829,7 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
|
||||
public evolutionItem: EvolutionItem;
|
||||
|
||||
constructor(evolutionItem: EvolutionItem) {
|
||||
super("", EvolutionItem[evolutionItem].toLowerCase(), (_type, args) => new Modifiers.EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
|
||||
super("", EvolutionItem[evolutionItem].toLowerCase(), (_type, args) => new EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
|
||||
(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 && (pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX)) {
|
||||
@ -868,7 +865,7 @@ export class FormChangeItemModifierType extends PokemonModifierType implements G
|
||||
public formChangeItem: FormChangeItem;
|
||||
|
||||
constructor(formChangeItem: FormChangeItem) {
|
||||
super("", FormChangeItem[formChangeItem].toLowerCase(), (_type, args) => new Modifiers.PokemonFormChangeItemModifier(this, (args[0] as PlayerPokemon).id, formChangeItem, true),
|
||||
super("", FormChangeItem[formChangeItem].toLowerCase(), (_type, args) => new PokemonFormChangeItemModifier(this, (args[0] as PlayerPokemon).id, formChangeItem, true),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
// Make sure the Pokemon has alternate forms
|
||||
if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId)
|
||||
@ -902,7 +899,7 @@ export class FormChangeItemModifierType extends PokemonModifierType implements G
|
||||
|
||||
export class FusePokemonModifierType extends PokemonModifierType {
|
||||
constructor(localeKey: string, iconImage: string) {
|
||||
super(localeKey, iconImage, (_type, args) => new Modifiers.FusePokemonModifier(this, (args[0] as PlayerPokemon).id, (args[1] as PlayerPokemon).id),
|
||||
super(localeKey, iconImage, (_type, args) => new FusePokemonModifier(this, (args[0] as PlayerPokemon).id, (args[1] as PlayerPokemon).id),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (pokemon.isFusion()) {
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -949,7 +946,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
|
||||
let type: Type;
|
||||
|
||||
const randInt = Utils.randSeedInt(totalWeight);
|
||||
const randInt = randSeedInt(totalWeight);
|
||||
let weight = 0;
|
||||
|
||||
for (const t of attackMoveTypeWeights.keys()) {
|
||||
@ -981,7 +978,7 @@ class BaseStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
if (pregenArgs) {
|
||||
return new BaseStatBoosterModifierType(pregenArgs[0]);
|
||||
}
|
||||
const randStat: PermanentStat = Utils.randSeedInt(Stat.SPD + 1);
|
||||
const randStat: PermanentStat = randSeedInt(Stat.SPD + 1);
|
||||
return new BaseStatBoosterModifierType(randStat);
|
||||
});
|
||||
}
|
||||
@ -1002,7 +999,7 @@ class TempStatStageBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
if (pregenArgs && (pregenArgs.length === 1) && TEMP_BATTLE_STATS.includes(pregenArgs[0])) {
|
||||
return new TempStatStageBoosterModifierType(pregenArgs[0]);
|
||||
}
|
||||
const randStat: TempBattleStat = Utils.randSeedInt(Stat.ACC, Stat.ATK);
|
||||
const randStat: TempBattleStat = randSeedInt(Stat.ACC, Stat.ATK);
|
||||
return new TempStatStageBoosterModifierType(randStat);
|
||||
});
|
||||
}
|
||||
@ -1044,8 +1041,8 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
const checkedStats = values[i].stats;
|
||||
|
||||
// If party member already has the item being weighted currently, skip to the next item
|
||||
const hasItem = p.getHeldItems().some(m => m instanceof Modifiers.SpeciesStatBoosterModifier
|
||||
&& (m as Modifiers.SpeciesStatBoosterModifier).contains(checkedSpecies[0], checkedStats[0]));
|
||||
const hasItem = p.getHeldItems().some(m => m instanceof SpeciesStatBoosterModifier
|
||||
&& (m as SpeciesStatBoosterModifier).contains(checkedSpecies[0], checkedStats[0]));
|
||||
|
||||
if (!hasItem) {
|
||||
if (checkedSpecies.includes(speciesId) || (!!fusionSpeciesId && checkedSpecies.includes(fusionSpeciesId))) {
|
||||
@ -1065,7 +1062,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
}
|
||||
|
||||
if (totalWeight !== 0) {
|
||||
const randInt = Utils.randSeedInt(totalWeight, 1);
|
||||
const randInt = randSeedInt(totalWeight, 1);
|
||||
let weight = 0;
|
||||
|
||||
for (const i in weights) {
|
||||
@ -1095,7 +1092,7 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
if (!tierUniqueCompatibleTms.length) {
|
||||
return null;
|
||||
}
|
||||
const randTmIndex = Utils.randSeedInt(tierUniqueCompatibleTms.length);
|
||||
const randTmIndex = randSeedInt(tierUniqueCompatibleTms.length);
|
||||
return new TmModifierType(tierUniqueCompatibleTms[randTmIndex]);
|
||||
});
|
||||
}
|
||||
@ -1123,7 +1120,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new EvolutionItemModifierType(evolutionItemPool[Utils.randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct?
|
||||
return new EvolutionItemModifierType(evolutionItemPool[randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct?
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1137,12 +1134,12 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
|
||||
const formChangeItemPool = [ ...new Set(party.filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)).map(p => {
|
||||
const formChanges = pokemonFormChanges[p.species.speciesId];
|
||||
let formChangeItemTriggers = formChanges.filter(fc => ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || party[0].scene.getModifiers(Modifiers.MegaEvolutionAccessModifier).length)
|
||||
&& ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || party[0].scene.getModifiers(Modifiers.GigantamaxAccessModifier).length)
|
||||
let formChangeItemTriggers = formChanges.filter(fc => ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || party[0].scene.getModifiers(MegaEvolutionAccessModifier).length)
|
||||
&& ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || party[0].scene.getModifiers(GigantamaxAccessModifier).length)
|
||||
&& (!fc.conditions.length || fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p)).length)
|
||||
&& (fc.preFormKey === p.getFormKey()))
|
||||
.map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
|
||||
.filter(t => t && t.active && !p.scene.findModifier(m => m instanceof Modifiers.PokemonFormChangeItemModifier && m.pokemonId === p.id && m.formChangeItem === t.item));
|
||||
.filter(t => t && t.active && !p.scene.findModifier(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === p.id && m.formChangeItem === t.item));
|
||||
|
||||
if (p.species.speciesId === Species.NECROZMA) {
|
||||
// technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break...
|
||||
@ -1177,7 +1174,7 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FormChangeItemModifierType(formChangeItemPool[Utils.randSeedInt(formChangeItemPool.length)]);
|
||||
return new FormChangeItemModifierType(formChangeItemPool[randSeedInt(formChangeItemPool.length)]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1186,7 +1183,7 @@ export class TerastallizeModifierType extends PokemonHeldItemModifierType implem
|
||||
private teraType: Type;
|
||||
|
||||
constructor(teraType: Type) {
|
||||
super("", `${Type[teraType].toLowerCase()}_tera_shard`, (type, args) => new Modifiers.TerastallizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), "tera_shard");
|
||||
super("", `${Type[teraType].toLowerCase()}_tera_shard`, (type, args) => new TerastallizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), "tera_shard");
|
||||
|
||||
this.teraType = teraType;
|
||||
}
|
||||
@ -1208,7 +1205,7 @@ export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemMo
|
||||
private chancePercent: integer;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, chancePercent: integer, group?: string, soundName?: string) {
|
||||
super(localeKey, iconImage, (type, args) => new Modifiers.ContactHeldItemTransferChanceModifier(type, (args[0] as Pokemon).id, chancePercent), group, soundName);
|
||||
super(localeKey, iconImage, (type, args) => new ContactHeldItemTransferChanceModifier(type, (args[0] as Pokemon).id, chancePercent), group, soundName);
|
||||
|
||||
this.chancePercent = chancePercent;
|
||||
}
|
||||
@ -1220,7 +1217,7 @@ export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemMo
|
||||
|
||||
export class TurnHeldItemTransferModifierType extends PokemonHeldItemModifierType {
|
||||
constructor(localeKey: string, iconImage: string, group?: string, soundName?: string) {
|
||||
super(localeKey, iconImage, (type, args) => new Modifiers.TurnHeldItemTransferModifier(type, (args[0] as Pokemon).id), group, soundName);
|
||||
super(localeKey, iconImage, (type, args) => new TurnHeldItemTransferModifier(type, (args[0] as Pokemon).id), group, soundName);
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
@ -1233,7 +1230,7 @@ export class EnemyAttackStatusEffectChanceModifierType extends ModifierType {
|
||||
private effect: StatusEffect;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, chancePercent: integer, effect: StatusEffect, stackCount?: integer) {
|
||||
super(localeKey, iconImage, (type, args) => new Modifiers.EnemyAttackStatusEffectChanceModifier(type, effect, chancePercent, stackCount), "enemy_status_chance");
|
||||
super(localeKey, iconImage, (type, args) => new EnemyAttackStatusEffectChanceModifier(type, effect, chancePercent, stackCount), "enemy_status_chance");
|
||||
|
||||
this.chancePercent = chancePercent;
|
||||
this.effect = effect;
|
||||
@ -1251,7 +1248,7 @@ export class EnemyEndureChanceModifierType extends ModifierType {
|
||||
private chancePercent: number;
|
||||
|
||||
constructor(localeKey: string, iconImage: string, chancePercent: number) {
|
||||
super(localeKey, iconImage, (type, _args) => new Modifiers.EnemyEndureChanceModifier(type, chancePercent), "enemy_endure");
|
||||
super(localeKey, iconImage, (type, _args) => new EnemyEndureChanceModifier(type, chancePercent), "enemy_endure");
|
||||
|
||||
this.chancePercent = chancePercent;
|
||||
}
|
||||
@ -1298,7 +1295,7 @@ function skipInLastClassicWaveOrDefault(defaultWeight: integer) : WeightedModifi
|
||||
*/
|
||||
function lureWeightFunc(maxBattles: number, weight: number): WeightedModifierTypeWeightFunc {
|
||||
return (party: Pokemon[]) => {
|
||||
const lures = party[0].scene.getModifiers(Modifiers.DoubleBattleChanceBoosterModifier);
|
||||
const lures = party[0].scene.getModifiers(DoubleBattleChanceBoosterModifier);
|
||||
return !(party[0].scene.gameMode.isClassic && party[0].scene.currentBattle.waveIndex === 199) && (lures.length === 0 || lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0) ? weight : 0;
|
||||
};
|
||||
}
|
||||
@ -1388,13 +1385,13 @@ export const modifierTypes = {
|
||||
RARE_FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(true),
|
||||
|
||||
EVOLUTION_TRACKER_GIMMIGHOUL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.EVOLUTION_TRACKER_GIMMIGHOUL", "relic_gold",
|
||||
(type, args) => new Modifiers.EvoTrackerModifier(type, (args[0] as Pokemon).id, Species.GIMMIGHOUL, 10)),
|
||||
(type, args) => new EvoTrackerModifier(type, (args[0] as Pokemon).id, Species.GIMMIGHOUL, 10)),
|
||||
|
||||
MEGA_BRACELET: () => new ModifierType("modifierType:ModifierType.MEGA_BRACELET", "mega_bracelet", (type, _args) => new Modifiers.MegaEvolutionAccessModifier(type)),
|
||||
DYNAMAX_BAND: () => new ModifierType("modifierType:ModifierType.DYNAMAX_BAND", "dynamax_band", (type, _args) => new Modifiers.GigantamaxAccessModifier(type)),
|
||||
TERA_ORB: () => new ModifierType("modifierType:ModifierType.TERA_ORB", "tera_orb", (type, _args) => new Modifiers.TerastallizeAccessModifier(type)),
|
||||
MEGA_BRACELET: () => new ModifierType("modifierType:ModifierType.MEGA_BRACELET", "mega_bracelet", (type, _args) => new MegaEvolutionAccessModifier(type)),
|
||||
DYNAMAX_BAND: () => new ModifierType("modifierType:ModifierType.DYNAMAX_BAND", "dynamax_band", (type, _args) => new GigantamaxAccessModifier(type)),
|
||||
TERA_ORB: () => new ModifierType("modifierType:ModifierType.TERA_ORB", "tera_orb", (type, _args) => new TerastallizeAccessModifier(type)),
|
||||
|
||||
MAP: () => new ModifierType("modifierType:ModifierType.MAP", "map", (type, _args) => new Modifiers.MapModifier(type)),
|
||||
MAP: () => new ModifierType("modifierType:ModifierType.MAP", "map", (type, _args) => new MapModifier(type)),
|
||||
|
||||
POTION: () => new PokemonHpRestoreModifierType("modifierType:ModifierType.POTION", "potion", 20, 10),
|
||||
SUPER_POTION: () => new PokemonHpRestoreModifierType("modifierType:ModifierType.SUPER_POTION", "super_potion", 50, 25),
|
||||
@ -1409,8 +1406,8 @@ export const modifierTypes = {
|
||||
|
||||
SACRED_ASH: () => new AllPokemonFullReviveModifierType("modifierType:ModifierType.SACRED_ASH", "sacred_ash"),
|
||||
|
||||
REVIVER_SEED: () => new PokemonHeldItemModifierType("modifierType:ModifierType.REVIVER_SEED", "reviver_seed", (type, args) => new Modifiers.PokemonInstantReviveModifier(type, (args[0] as Pokemon).id)),
|
||||
WHITE_HERB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.WHITE_HERB", "white_herb", (type, args) => new Modifiers.ResetNegativeStatStageModifier(type, (args[0] as Pokemon).id)),
|
||||
REVIVER_SEED: () => new PokemonHeldItemModifierType("modifierType:ModifierType.REVIVER_SEED", "reviver_seed", (type, args) => new PokemonInstantReviveModifier(type, (args[0] as Pokemon).id)),
|
||||
WHITE_HERB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.WHITE_HERB", "white_herb", (type, args) => new ResetNegativeStatStageModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
ETHER: () => new PokemonPpRestoreModifierType("modifierType:ModifierType.ETHER", "ether", 10),
|
||||
MAX_ETHER: () => new PokemonPpRestoreModifierType("modifierType:ModifierType.MAX_ETHER", "max_ether", -1),
|
||||
@ -1440,7 +1437,7 @@ export const modifierTypes = {
|
||||
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage")
|
||||
});
|
||||
}
|
||||
}("modifierType:ModifierType.DIRE_HIT", "dire_hit", (type, _args) => new Modifiers.TempCritBoosterModifier(type, 5)),
|
||||
}("modifierType:ModifierType.DIRE_HIT", "dire_hit", (type, _args) => new TempCritBoosterModifier(type, 5)),
|
||||
|
||||
BASE_STAT_BOOSTER: () => new BaseStatBoosterModifierTypeGenerator(),
|
||||
|
||||
@ -1450,22 +1447,22 @@ export const modifierTypes = {
|
||||
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Nature)) {
|
||||
return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature);
|
||||
}
|
||||
return new PokemonNatureChangeModifierType(Utils.randSeedInt(Utils.getEnumValues(Nature).length) as Nature);
|
||||
return new PokemonNatureChangeModifierType(randSeedInt(getEnumValues(Nature).length) as Nature);
|
||||
}),
|
||||
|
||||
TERA_SHARD: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
|
||||
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Type)) {
|
||||
return new TerastallizeModifierType(pregenArgs[0] as Type);
|
||||
}
|
||||
if (!party[0].scene.getModifiers(Modifiers.TerastallizeAccessModifier).length) {
|
||||
if (!party[0].scene.getModifiers(TerastallizeAccessModifier).length) {
|
||||
return null;
|
||||
}
|
||||
let type: Type;
|
||||
if (!Utils.randSeedInt(3)) {
|
||||
if (!randSeedInt(3)) {
|
||||
const partyMemberTypes = party.map(p => p.getTypes(false, false, true)).flat();
|
||||
type = Utils.randSeedItem(partyMemberTypes);
|
||||
type = randSeedItem(partyMemberTypes);
|
||||
} else {
|
||||
type = Utils.randSeedInt(64) ? Utils.randSeedInt(18) as Type : Type.STELLAR;
|
||||
type = randSeedInt(64) ? randSeedInt(18) as Type : Type.STELLAR;
|
||||
}
|
||||
return new TerastallizeModifierType(type);
|
||||
}),
|
||||
@ -1474,9 +1471,9 @@ export const modifierTypes = {
|
||||
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in BerryType)) {
|
||||
return new BerryModifierType(pregenArgs[0] as BerryType);
|
||||
}
|
||||
const berryTypes = Utils.getEnumValues(BerryType);
|
||||
const berryTypes = getEnumValues(BerryType);
|
||||
let randBerryType: BerryType;
|
||||
const rand = Utils.randSeedInt(12);
|
||||
const rand = randSeedInt(12);
|
||||
if (rand < 2) {
|
||||
randBerryType = BerryType.SITRUS;
|
||||
} else if (rand < 4) {
|
||||
@ -1484,7 +1481,7 @@ export const modifierTypes = {
|
||||
} else if (rand < 6) {
|
||||
randBerryType = BerryType.LEPPA;
|
||||
} else {
|
||||
randBerryType = berryTypes[Utils.randSeedInt(berryTypes.length - 3) + 2];
|
||||
randBerryType = berryTypes[randSeedInt(berryTypes.length - 3) + 2];
|
||||
}
|
||||
return new BerryModifierType(randBerryType);
|
||||
}),
|
||||
@ -1495,10 +1492,10 @@ export const modifierTypes = {
|
||||
|
||||
MEMORY_MUSHROOM: () => new RememberMoveModifierType("modifierType:ModifierType.MEMORY_MUSHROOM", "big_mushroom"),
|
||||
|
||||
EXP_SHARE: () => new ModifierType("modifierType:ModifierType.EXP_SHARE", "exp_share", (type, _args) => new Modifiers.ExpShareModifier(type)),
|
||||
EXP_BALANCE: () => new ModifierType("modifierType:ModifierType.EXP_BALANCE", "exp_balance", (type, _args) => new Modifiers.ExpBalanceModifier(type)),
|
||||
EXP_SHARE: () => new ModifierType("modifierType:ModifierType.EXP_SHARE", "exp_share", (type, _args) => new ExpShareModifier(type)),
|
||||
EXP_BALANCE: () => new ModifierType("modifierType:ModifierType.EXP_BALANCE", "exp_balance", (type, _args) => new ExpBalanceModifier(type)),
|
||||
|
||||
OVAL_CHARM: () => new ModifierType("modifierType:ModifierType.OVAL_CHARM", "oval_charm", (type, _args) => new Modifiers.MultipleParticipantExpBonusModifier(type)),
|
||||
OVAL_CHARM: () => new ModifierType("modifierType:ModifierType.OVAL_CHARM", "oval_charm", (type, _args) => new MultipleParticipantExpBonusModifier(type)),
|
||||
|
||||
EXP_CHARM: () => new ExpBoosterModifierType("modifierType:ModifierType.EXP_CHARM", "exp_charm", 25),
|
||||
SUPER_EXP_CHARM: () => new ExpBoosterModifierType("modifierType:ModifierType.SUPER_EXP_CHARM", "super_exp_charm", 60),
|
||||
@ -1509,51 +1506,51 @@ export const modifierTypes = {
|
||||
|
||||
SOOTHE_BELL: () => new PokemonFriendshipBoosterModifierType("modifierType:ModifierType.SOOTHE_BELL", "soothe_bell"),
|
||||
|
||||
SCOPE_LENS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SCOPE_LENS", "scope_lens", (type, args) => new Modifiers.CritBoosterModifier(type, (args[0] as Pokemon).id, 1)),
|
||||
LEEK: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEEK", "leek", (type, args) => new Modifiers.SpeciesCritBoosterModifier(type, (args[0] as Pokemon).id, 2, [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD])),
|
||||
SCOPE_LENS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SCOPE_LENS", "scope_lens", (type, args) => new CritBoosterModifier(type, (args[0] as Pokemon).id, 1)),
|
||||
LEEK: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEEK", "leek", (type, args) => new SpeciesCritBoosterModifier(type, (args[0] as Pokemon).id, 2, [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ])),
|
||||
|
||||
EVIOLITE: () => new PokemonHeldItemModifierType("modifierType:ModifierType.EVIOLITE", "eviolite", (type, args) => new Modifiers.EvolutionStatBoosterModifier(type, (args[0] as Pokemon).id, [Stat.DEF, Stat.SPDEF], 1.5)),
|
||||
EVIOLITE: () => new PokemonHeldItemModifierType("modifierType:ModifierType.EVIOLITE", "eviolite", (type, args) => new EvolutionStatBoosterModifier(type, (args[0] as Pokemon).id, [ Stat.DEF, Stat.SPDEF ], 1.5)),
|
||||
|
||||
SOUL_DEW: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SOUL_DEW", "soul_dew", (type, args) => new Modifiers.PokemonNatureWeightModifier(type, (args[0] as Pokemon).id)),
|
||||
SOUL_DEW: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SOUL_DEW", "soul_dew", (type, args) => new PokemonNatureWeightModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
NUGGET: () => new MoneyRewardModifierType("modifierType:ModifierType.NUGGET", "nugget", 1, "modifierType:ModifierType.MoneyRewardModifierType.extra.small"),
|
||||
BIG_NUGGET: () => new MoneyRewardModifierType("modifierType:ModifierType.BIG_NUGGET", "big_nugget", 2.5, "modifierType:ModifierType.MoneyRewardModifierType.extra.moderate"),
|
||||
RELIC_GOLD: () => new MoneyRewardModifierType("modifierType:ModifierType.RELIC_GOLD", "relic_gold", 10, "modifierType:ModifierType.MoneyRewardModifierType.extra.large"),
|
||||
|
||||
AMULET_COIN: () => new ModifierType("modifierType:ModifierType.AMULET_COIN", "amulet_coin", (type, _args) => new Modifiers.MoneyMultiplierModifier(type)),
|
||||
GOLDEN_PUNCH: () => new PokemonHeldItemModifierType("modifierType:ModifierType.GOLDEN_PUNCH", "golden_punch", (type, args) => new Modifiers.DamageMoneyRewardModifier(type, (args[0] as Pokemon).id)),
|
||||
COIN_CASE: () => new ModifierType("modifierType:ModifierType.COIN_CASE", "coin_case", (type, _args) => new Modifiers.MoneyInterestModifier(type)),
|
||||
AMULET_COIN: () => new ModifierType("modifierType:ModifierType.AMULET_COIN", "amulet_coin", (type, _args) => new MoneyMultiplierModifier(type)),
|
||||
GOLDEN_PUNCH: () => new PokemonHeldItemModifierType("modifierType:ModifierType.GOLDEN_PUNCH", "golden_punch", (type, args) => new DamageMoneyRewardModifier(type, (args[0] as Pokemon).id)),
|
||||
COIN_CASE: () => new ModifierType("modifierType:ModifierType.COIN_CASE", "coin_case", (type, _args) => new MoneyInterestModifier(type)),
|
||||
|
||||
LOCK_CAPSULE: () => new ModifierType("modifierType:ModifierType.LOCK_CAPSULE", "lock_capsule", (type, _args) => new Modifiers.LockModifierTiersModifier(type)),
|
||||
LOCK_CAPSULE: () => new ModifierType("modifierType:ModifierType.LOCK_CAPSULE", "lock_capsule", (type, _args) => new LockModifierTiersModifier(type)),
|
||||
|
||||
GRIP_CLAW: () => new ContactHeldItemTransferChanceModifierType("modifierType:ModifierType.GRIP_CLAW", "grip_claw", 10),
|
||||
WIDE_LENS: () => new PokemonMoveAccuracyBoosterModifierType("modifierType:ModifierType.WIDE_LENS", "wide_lens", 5),
|
||||
|
||||
MULTI_LENS: () => new PokemonMultiHitModifierType("modifierType:ModifierType.MULTI_LENS", "zoom_lens"),
|
||||
|
||||
HEALING_CHARM: () => new ModifierType("modifierType:ModifierType.HEALING_CHARM", "healing_charm", (type, _args) => new Modifiers.HealingBoosterModifier(type, 1.1)),
|
||||
CANDY_JAR: () => new ModifierType("modifierType:ModifierType.CANDY_JAR", "candy_jar", (type, _args) => new Modifiers.LevelIncrementBoosterModifier(type)),
|
||||
HEALING_CHARM: () => new ModifierType("modifierType:ModifierType.HEALING_CHARM", "healing_charm", (type, _args) => new HealingBoosterModifier(type, 1.1)),
|
||||
CANDY_JAR: () => new ModifierType("modifierType:ModifierType.CANDY_JAR", "candy_jar", (type, _args) => new LevelIncrementBoosterModifier(type)),
|
||||
|
||||
BERRY_POUCH: () => new ModifierType("modifierType:ModifierType.BERRY_POUCH", "berry_pouch", (type, _args) => new Modifiers.PreserveBerryModifier(type)),
|
||||
BERRY_POUCH: () => new ModifierType("modifierType:ModifierType.BERRY_POUCH", "berry_pouch", (type, _args) => new PreserveBerryModifier(type)),
|
||||
|
||||
FOCUS_BAND: () => new PokemonHeldItemModifierType("modifierType:ModifierType.FOCUS_BAND", "focus_band", (type, args) => new Modifiers.SurviveDamageModifier(type, (args[0] as Pokemon).id)),
|
||||
FOCUS_BAND: () => new PokemonHeldItemModifierType("modifierType:ModifierType.FOCUS_BAND", "focus_band", (type, args) => new SurviveDamageModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
QUICK_CLAW: () => new PokemonHeldItemModifierType("modifierType:ModifierType.QUICK_CLAW", "quick_claw", (type, args) => new Modifiers.BypassSpeedChanceModifier(type, (args[0] as Pokemon).id)),
|
||||
QUICK_CLAW: () => new PokemonHeldItemModifierType("modifierType:ModifierType.QUICK_CLAW", "quick_claw", (type, args) => new BypassSpeedChanceModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
KINGS_ROCK: () => new PokemonHeldItemModifierType("modifierType:ModifierType.KINGS_ROCK", "kings_rock", (type, args) => new Modifiers.FlinchChanceModifier(type, (args[0] as Pokemon).id)),
|
||||
KINGS_ROCK: () => new PokemonHeldItemModifierType("modifierType:ModifierType.KINGS_ROCK", "kings_rock", (type, args) => new FlinchChanceModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
LEFTOVERS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEFTOVERS", "leftovers", (type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)),
|
||||
SHELL_BELL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SHELL_BELL", "shell_bell", (type, args) => new Modifiers.HitHealModifier(type, (args[0] as Pokemon).id)),
|
||||
LEFTOVERS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEFTOVERS", "leftovers", (type, args) => new TurnHealModifier(type, (args[0] as Pokemon).id)),
|
||||
SHELL_BELL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SHELL_BELL", "shell_bell", (type, args) => new HitHealModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
TOXIC_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.TOXIC_ORB", "toxic_orb", (type, args) => new Modifiers.TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
|
||||
FLAME_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.FLAME_ORB", "flame_orb", (type, args) => new Modifiers.TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
|
||||
TOXIC_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.TOXIC_ORB", "toxic_orb", (type, args) => new TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
|
||||
FLAME_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.FLAME_ORB", "flame_orb", (type, args) => new TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
BATON: () => new PokemonHeldItemModifierType("modifierType:ModifierType.BATON", "baton", (type, args) => new Modifiers.SwitchEffectTransferModifier(type, (args[0] as Pokemon).id)),
|
||||
BATON: () => new PokemonHeldItemModifierType("modifierType:ModifierType.BATON", "baton", (type, args) => new SwitchEffectTransferModifier(type, (args[0] as Pokemon).id)),
|
||||
|
||||
SHINY_CHARM: () => new ModifierType("modifierType:ModifierType.SHINY_CHARM", "shiny_charm", (type, _args) => new Modifiers.ShinyRateBoosterModifier(type)),
|
||||
ABILITY_CHARM: () => new ModifierType("modifierType:ModifierType.ABILITY_CHARM", "ability_charm", (type, _args) => new Modifiers.HiddenAbilityRateBoosterModifier(type)),
|
||||
SHINY_CHARM: () => new ModifierType("modifierType:ModifierType.SHINY_CHARM", "shiny_charm", (type, _args) => new ShinyRateBoosterModifier(type)),
|
||||
ABILITY_CHARM: () => new ModifierType("modifierType:ModifierType.ABILITY_CHARM", "ability_charm", (type, _args) => new HiddenAbilityRateBoosterModifier(type)),
|
||||
|
||||
IV_SCANNER: () => new ModifierType("modifierType:ModifierType.IV_SCANNER", "scanner", (type, _args) => new Modifiers.IvScannerModifier(type)),
|
||||
IV_SCANNER: () => new ModifierType("modifierType:ModifierType.IV_SCANNER", "scanner", (type, _args) => new IvScannerModifier(type)),
|
||||
|
||||
DNA_SPLICERS: () => new FusePokemonModifierType("modifierType:ModifierType.DNA_SPLICERS", "dna_splicers"),
|
||||
|
||||
@ -1563,39 +1560,39 @@ export const modifierTypes = {
|
||||
VOUCHER_PLUS: () => new AddVoucherModifierType(VoucherType.PLUS, 1),
|
||||
VOUCHER_PREMIUM: () => new AddVoucherModifierType(VoucherType.PREMIUM, 1),
|
||||
|
||||
GOLDEN_POKEBALL: () => new ModifierType("modifierType:ModifierType.GOLDEN_POKEBALL", "pb_gold", (type, _args) => new Modifiers.ExtraModifierModifier(type), undefined, "se/pb_bounce_1"),
|
||||
GOLDEN_POKEBALL: () => new ModifierType("modifierType:ModifierType.GOLDEN_POKEBALL", "pb_gold", (type, _args) => new ExtraModifierModifier(type), undefined, "se/pb_bounce_1"),
|
||||
|
||||
ENEMY_DAMAGE_BOOSTER: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_BOOSTER", "wl_item_drop", (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type, 5)),
|
||||
ENEMY_DAMAGE_REDUCTION: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_REDUCTION", "wl_guard_spec", (type, _args) => new Modifiers.EnemyDamageReducerModifier(type, 2.5)),
|
||||
//ENEMY_SUPER_EFFECT_BOOSTER: () => new ModifierType('Type Advantage Token', 'Increases damage of super effective attacks by 30%', (type, _args) => new Modifiers.EnemySuperEffectiveDamageBoosterModifier(type, 30), 'wl_custom_super_effective'),
|
||||
ENEMY_HEAL: () => new ModifierType("modifierType:ModifierType.ENEMY_HEAL", "wl_potion", (type, _args) => new Modifiers.EnemyTurnHealModifier(type, 2, 10)),
|
||||
ENEMY_DAMAGE_BOOSTER: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_BOOSTER", "wl_item_drop", (type, _args) => new EnemyDamageBoosterModifier(type, 5)),
|
||||
ENEMY_DAMAGE_REDUCTION: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_REDUCTION", "wl_guard_spec", (type, _args) => new EnemyDamageReducerModifier(type, 2.5)),
|
||||
//ENEMY_SUPER_EFFECT_BOOSTER: () => new ModifierType('Type Advantage Token', 'Increases damage of super effective attacks by 30%', (type, _args) => new EnemySuperEffectiveDamageBoosterModifier(type, 30), 'wl_custom_super_effective'),
|
||||
ENEMY_HEAL: () => new ModifierType("modifierType:ModifierType.ENEMY_HEAL", "wl_potion", (type, _args) => new EnemyTurnHealModifier(type, 2, 10)),
|
||||
ENEMY_ATTACK_POISON_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType("modifierType:ModifierType.ENEMY_ATTACK_POISON_CHANCE", "wl_antidote", 5, StatusEffect.POISON, 10),
|
||||
ENEMY_ATTACK_PARALYZE_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType("modifierType:ModifierType.ENEMY_ATTACK_PARALYZE_CHANCE", "wl_paralyze_heal", 2.5, StatusEffect.PARALYSIS, 10),
|
||||
ENEMY_ATTACK_BURN_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType("modifierType:ModifierType.ENEMY_ATTACK_BURN_CHANCE", "wl_burn_heal", 5, StatusEffect.BURN, 10),
|
||||
ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_STATUS_EFFECT_HEAL_CHANCE", "wl_full_heal", (type, _args) => new Modifiers.EnemyStatusEffectHealChanceModifier(type, 2.5, 10)),
|
||||
ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_STATUS_EFFECT_HEAL_CHANCE", "wl_full_heal", (type, _args) => new EnemyStatusEffectHealChanceModifier(type, 2.5, 10)),
|
||||
ENEMY_ENDURE_CHANCE: () => new EnemyEndureChanceModifierType("modifierType:ModifierType.ENEMY_ENDURE_CHANCE", "wl_reset_urge", 2),
|
||||
ENEMY_FUSED_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_FUSED_CHANCE", "wl_custom_spliced", (type, _args) => new Modifiers.EnemyFusionChanceModifier(type, 1)),
|
||||
ENEMY_FUSED_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_FUSED_CHANCE", "wl_custom_spliced", (type, _args) => new EnemyFusionChanceModifier(type, 1)),
|
||||
|
||||
MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
|
||||
if (pregenArgs) {
|
||||
return new PokemonBaseStatTotalModifierType(pregenArgs[0] as number);
|
||||
}
|
||||
return new PokemonBaseStatTotalModifierType(Utils.randSeedInt(20));
|
||||
return new PokemonBaseStatTotalModifierType(randSeedInt(20));
|
||||
}),
|
||||
MYSTERY_ENCOUNTER_OLD_GATEAU: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
|
||||
if (pregenArgs) {
|
||||
return new PokemonBaseStatFlatModifierType(pregenArgs[0] as number, pregenArgs[1] as Stat[]);
|
||||
}
|
||||
return new PokemonBaseStatFlatModifierType(Utils.randSeedInt(20), [Stat.HP, Stat.ATK, Stat.DEF]);
|
||||
return new PokemonBaseStatFlatModifierType(randSeedInt(20), [ Stat.HP, Stat.ATK, Stat.DEF ]);
|
||||
}),
|
||||
MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
|
||||
if (pregenArgs) {
|
||||
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new Modifiers.HealShopCostModifier(type, pregenArgs[0] as number));
|
||||
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new HealShopCostModifier(type, pregenArgs[0] as number));
|
||||
}
|
||||
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new Modifiers.HealShopCostModifier(type, 2.5));
|
||||
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new HealShopCostModifier(type, 2.5));
|
||||
}),
|
||||
MYSTERY_ENCOUNTER_MACHO_BRACE: () => new PokemonHeldItemModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_MACHO_BRACE", "macho_brace", (type, args) => new Modifiers.PokemonIncrementingStatModifier(type, (args[0] as Pokemon).id)),
|
||||
MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET", "golden_net", (type, _args) => new Modifiers.BoostBugSpawnModifier(type)),
|
||||
MYSTERY_ENCOUNTER_MACHO_BRACE: () => new PokemonHeldItemModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_MACHO_BRACE", "macho_brace", (type, args) => new PokemonIncrementingStatModifier(type, (args[0] as Pokemon).id)),
|
||||
MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET", "golden_net", (type, _args) => new BoostBugSpawnModifier(type)),
|
||||
};
|
||||
|
||||
interface ModifierPool {
|
||||
@ -1644,8 +1641,8 @@ const modifierPool: ModifierPool = {
|
||||
new WeightedModifierType(modifierTypes.PP_UP, 2),
|
||||
new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => {
|
||||
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
|
||||
if (i instanceof Modifiers.TurnStatusEffectModifier) {
|
||||
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||
if (i instanceof TurnStatusEffectModifier) {
|
||||
return (i as TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||
}
|
||||
return false;
|
||||
})).length, 3);
|
||||
@ -1672,8 +1669,8 @@ const modifierPool: ModifierPool = {
|
||||
}, 3),
|
||||
new WeightedModifierType(modifierTypes.FULL_RESTORE, (party: Pokemon[]) => {
|
||||
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
|
||||
if (i instanceof Modifiers.TurnStatusEffectModifier) {
|
||||
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||
if (i instanceof TurnStatusEffectModifier) {
|
||||
return (i as TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||
}
|
||||
return false;
|
||||
})).length, 3);
|
||||
@ -1723,7 +1720,7 @@ const modifierPool: ModifierPool = {
|
||||
const { gameMode, gameData } = party[0].scene;
|
||||
if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) {
|
||||
return party.some(p => ((p.getSpeciesForm(true).speciesId in pokemonEvolutions) || (p.isFusion() && (p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions)))
|
||||
&& !p.getHeldItems().some(i => i instanceof Modifiers.EvolutionStatBoosterModifier) && !p.isMax()) ? 10 : 0;
|
||||
&& !p.getHeldItems().some(i => i instanceof EvolutionStatBoosterModifier) && !p.isMax()) ? 10 : 0;
|
||||
}
|
||||
return 0;
|
||||
}),
|
||||
@ -1731,7 +1728,7 @@ const modifierPool: ModifierPool = {
|
||||
new WeightedModifierType(modifierTypes.LEEK, (party: Pokemon[]) => {
|
||||
const checkedSpecies = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ];
|
||||
// If a party member doesn't already have a Leek and is one of the relevant species, Leek can appear
|
||||
return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.SpeciesCritBoosterModifier)
|
||||
return party.some(p => !p.getHeldItems().some(i => i instanceof SpeciesCritBoosterModifier)
|
||||
&& (checkedSpecies.includes(p.getSpeciesForm(true).speciesId)
|
||||
|| (p.isFusion() && checkedSpecies.includes(p.getFusionSpeciesForm(true).speciesId)))) ? 12 : 0;
|
||||
}, 12),
|
||||
@ -1739,7 +1736,7 @@ const modifierPool: ModifierPool = {
|
||||
const checkedAbilities = [ Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.TOXIC_BOOST, Abilities.POISON_HEAL, Abilities.MAGIC_GUARD ];
|
||||
const checkedMoves = [ Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT ];
|
||||
// If a party member doesn't already have one of these two orbs and has one of the above moves or abilities, the orb can appear
|
||||
return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier)
|
||||
return party.some(p => !p.getHeldItems().some(i => i instanceof TurnStatusEffectModifier)
|
||||
&& (checkedAbilities.some(a => p.hasAbility(a, false, true))
|
||||
|| p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId)))) ? 10 : 0;
|
||||
}, 10),
|
||||
@ -1747,13 +1744,13 @@ const modifierPool: ModifierPool = {
|
||||
const checkedAbilities = [ Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.FLARE_BOOST, Abilities.MAGIC_GUARD ];
|
||||
const checkedMoves = [ Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT ];
|
||||
// If a party member doesn't already have one of these two orbs and has one of the above moves or abilities, the orb can appear
|
||||
return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier)
|
||||
return party.some(p => !p.getHeldItems().some(i => i instanceof TurnStatusEffectModifier)
|
||||
&& (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId)))) ? 10 : 0;
|
||||
}, 10),
|
||||
new WeightedModifierType(modifierTypes.WHITE_HERB, (party: Pokemon[]) => {
|
||||
const checkedAbilities = [ Abilities.WEAK_ARMOR, Abilities.CONTRARY, Abilities.MOODY, Abilities.ANGER_SHELL, Abilities.COMPETITIVE, Abilities.DEFIANT ];
|
||||
const weightMultiplier = party.filter(
|
||||
p => !p.getHeldItems().some(i => i instanceof Modifiers.ResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) &&
|
||||
p => !p.getHeldItems().some(i => i instanceof ResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) &&
|
||||
(checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && selfStatLowerMoves.includes(m.moveId)))).length;
|
||||
// If a party member has one of the above moves or abilities and doesn't have max herbs, the herb will appear more frequently
|
||||
return 0 * (weightMultiplier ? 2 : 1) + (weightMultiplier ? weightMultiplier * 0 : 0);
|
||||
@ -2246,7 +2243,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base
|
||||
return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat();
|
||||
}
|
||||
|
||||
export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: Modifiers.PersistentModifier[], scene: BattleScene): Modifiers.EnemyPersistentModifier {
|
||||
export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: PersistentModifier[], scene: BattleScene): EnemyPersistentModifier {
|
||||
let tierStackCount: number;
|
||||
switch (tier) {
|
||||
case ModifierTier.ULTRA:
|
||||
@ -2263,30 +2260,30 @@ export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers:
|
||||
const retryCount = 50;
|
||||
let candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier);
|
||||
let r = 0;
|
||||
let matchingModifier: Modifiers.PersistentModifier | undefined;
|
||||
let matchingModifier: PersistentModifier | undefined;
|
||||
while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) {
|
||||
candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier);
|
||||
}
|
||||
|
||||
const modifier = candidate?.type?.newModifier() as Modifiers.EnemyPersistentModifier;
|
||||
const modifier = candidate?.type?.newModifier() as EnemyPersistentModifier;
|
||||
modifier.stackCount = tierStackCount;
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, upgradeChance: integer = 0): PokemonHeldItemModifierType[] {
|
||||
const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0)?.type as PokemonHeldItemModifierType);
|
||||
const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !randSeedInt(upgradeChance) ? 1 : 0)?.type as PokemonHeldItemModifierType);
|
||||
if (!(waveIndex % 1000)) {
|
||||
ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.PokemonHeldItemModifier[] {
|
||||
const ret: Modifiers.PokemonHeldItemModifier[] = [];
|
||||
export function getDailyRunStarterModifiers(party: PlayerPokemon[]): PokemonHeldItemModifier[] {
|
||||
const ret: PokemonHeldItemModifier[] = [];
|
||||
for (const p of party) {
|
||||
for (let m = 0; m < 3; m++) {
|
||||
const tierValue = Utils.randSeedInt(64);
|
||||
const tierValue = randSeedInt(64);
|
||||
|
||||
let tier: ModifierTier;
|
||||
if (tierValue > 25) {
|
||||
@ -2301,7 +2298,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.P
|
||||
tier = ModifierTier.MASTER;
|
||||
}
|
||||
|
||||
const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier)?.type?.newModifier(p) as Modifiers.PokemonHeldItemModifier;
|
||||
const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier)?.type?.newModifier(p) as PokemonHeldItemModifier;
|
||||
ret.push(modifier);
|
||||
}
|
||||
}
|
||||
@ -2340,7 +2337,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
|
||||
break;
|
||||
}
|
||||
if (tier === undefined) {
|
||||
const tierValue = Utils.randSeedInt(1024);
|
||||
const tierValue = randSeedInt(1024);
|
||||
if (!upgradeCount) {
|
||||
upgradeCount = 0;
|
||||
}
|
||||
@ -2349,7 +2346,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
|
||||
const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4));
|
||||
let upgraded = false;
|
||||
do {
|
||||
upgraded = Utils.randSeedInt(upgradeOdds) < 4;
|
||||
upgraded = randSeedInt(upgradeOdds) < 4;
|
||||
if (upgraded) {
|
||||
upgradeCount++;
|
||||
}
|
||||
@ -2381,7 +2378,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
|
||||
const partyShinyCount = party.filter(p => p.isShiny() && !p.isFainted()).length;
|
||||
const upgradeOdds = Math.floor(32 / ((partyShinyCount + 2) / 2));
|
||||
while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) {
|
||||
if (!Utils.randSeedInt(upgradeOdds)) {
|
||||
if (!randSeedInt(upgradeOdds)) {
|
||||
upgradeCount++;
|
||||
} else {
|
||||
break;
|
||||
@ -2396,7 +2393,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
|
||||
|
||||
const tierThresholds = Object.keys(thresholds[tier]);
|
||||
const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]);
|
||||
const value = Utils.randSeedInt(totalWeight);
|
||||
const value = randSeedInt(totalWeight);
|
||||
let index: integer | undefined;
|
||||
for (const t of tierThresholds) {
|
||||
const threshold = parseInt(t);
|
||||
@ -2456,9 +2453,9 @@ export class ModifierTypeOption {
|
||||
*/
|
||||
export function getPartyLuckValue(party: Pokemon[]): integer {
|
||||
if (party[0].scene.gameMode.isDaily) {
|
||||
const DailyLuck = new Utils.NumberHolder(0);
|
||||
const DailyLuck = new NumberHolder(0);
|
||||
party[0].scene.executeWithSeedOffset(() => {
|
||||
DailyLuck.value = Utils.randSeedInt(15); // Random number between 0 and 14
|
||||
DailyLuck.value = randSeedInt(15); // Random number between 0 and 14
|
||||
}, 0, party[0].scene.seed);
|
||||
return DailyLuck.value;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -100,6 +100,14 @@ class DefaultOverrides {
|
||||
* @example SPECIES_OVERRIDE = Species.Bulbasaur;
|
||||
*/
|
||||
readonly STARTER_SPECIES_OVERRIDE: Species | number = 0;
|
||||
/**
|
||||
* This will force your starter to be a random fusion
|
||||
*/
|
||||
readonly STARTER_FUSION_OVERRIDE: boolean = false;
|
||||
/**
|
||||
* This will override the species of the fusion
|
||||
*/
|
||||
readonly STARTER_FUSION_SPECIES_OVERRIDE: Species | integer = 0;
|
||||
readonly ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||
readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||
readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
|
||||
@ -112,6 +120,14 @@ class DefaultOverrides {
|
||||
// OPPONENT / ENEMY OVERRIDES
|
||||
// --------------------------
|
||||
readonly OPP_SPECIES_OVERRIDE: Species | number = 0;
|
||||
/**
|
||||
* This will make all opponents fused Pokemon
|
||||
*/
|
||||
readonly OPP_FUSION_OVERRIDE: boolean = false;
|
||||
/**
|
||||
* This will override the species of the fusion only when the opponent is already a fusion
|
||||
*/
|
||||
readonly OPP_FUSION_SPECIES_OVERRIDE: Species | integer = 0;
|
||||
readonly OPP_LEVEL_OVERRIDE: number = 0;
|
||||
readonly OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||
readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||
|
@ -57,7 +57,7 @@ export class BattleEndPhase extends BattlePhase {
|
||||
if (m instanceof LapsingPokemonHeldItemModifier) {
|
||||
args.push(this.scene.getPokemonById(m.pokemonId));
|
||||
}
|
||||
if (!m.lapse(args)) {
|
||||
if (!m.lapse(...args)) {
|
||||
this.scene.removeModifier(m);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export class BerryPhase extends FieldPhase {
|
||||
|
||||
this.executeForAll((pokemon) => {
|
||||
const hasUsableBerry = !!this.scene.findModifier((m) => {
|
||||
return m instanceof BerryModifier && m.shouldApply([pokemon]);
|
||||
return m instanceof BerryModifier && m.shouldApply(pokemon);
|
||||
}, pokemon.isPlayer());
|
||||
|
||||
if (hasUsableBerry) {
|
||||
@ -29,7 +29,7 @@ export class BerryPhase extends FieldPhase {
|
||||
new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM)
|
||||
);
|
||||
|
||||
for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon) as BerryModifier[]) {
|
||||
for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) {
|
||||
if (berryModifier.consumed) {
|
||||
if (!--berryModifier.stackCount) {
|
||||
this.scene.removeModifier(berryModifier);
|
||||
|
@ -4,7 +4,7 @@ import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr,
|
||||
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
|
||||
import { MoveAnim } from "#app/data/battle-anims";
|
||||
import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag } from "#app/data/battler-tags";
|
||||
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move";
|
||||
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move";
|
||||
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
@ -14,6 +14,7 @@ import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectC
|
||||
import i18next from "i18next";
|
||||
import * as Utils from "#app/utils";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import { Type } from "#app/data/type";
|
||||
|
||||
export class MoveEffectPhase extends PokemonPhase {
|
||||
public move: PokemonMove;
|
||||
@ -69,7 +70,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
* resolve the move's total hit count. This block combines the
|
||||
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
|
||||
*/
|
||||
if (user.turnData.hitsLeft === undefined) {
|
||||
if (user.turnData.hitsLeft === -1) {
|
||||
const hitCount = new Utils.IntegerHolder(1);
|
||||
// Assume single target for multi hit
|
||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
|
||||
@ -404,7 +405,10 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
|
||||
if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)) {
|
||||
if (semiInvulnerableTag
|
||||
&& !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)
|
||||
&& !(this.move.getMove().hasAttr(ToxicAccuracyAttr) && user.isOfType(Type.POISON))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs, BlockRedirectAbAttr, IncreasePpAbAttr, PokemonTypeChangeAbAttr, PostMoveUsedAbAttr, RedirectMoveAbAttr } from "#app/data/ability";
|
||||
import { CommonAnim } from "#app/data/battle-anims";
|
||||
import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags";
|
||||
@ -15,236 +15,149 @@ import { StatusEffect } from "#app/enums/status-effect";
|
||||
import { MoveUsedEvent } from "#app/events/battle-scene";
|
||||
import Pokemon, { MoveResult, PokemonMove, TurnMove } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { BattlePhase } from "#app/phases/battle-phase";
|
||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import * as Utils from "#app/utils";
|
||||
import i18next from "i18next";
|
||||
import { BattlePhase } from "./battle-phase";
|
||||
import { CommonAnimPhase } from "./common-anim-phase";
|
||||
import { MoveEffectPhase } from "./move-effect-phase";
|
||||
import { MoveEndPhase } from "./move-end-phase";
|
||||
import { ShowAbilityPhase } from "./show-ability-phase";
|
||||
|
||||
export class MovePhase extends BattlePhase {
|
||||
public pokemon: Pokemon;
|
||||
public move: PokemonMove;
|
||||
public targets: BattlerIndex[];
|
||||
protected _pokemon: Pokemon;
|
||||
protected _move: PokemonMove;
|
||||
protected _targets: BattlerIndex[];
|
||||
protected followUp: boolean;
|
||||
protected ignorePp: boolean;
|
||||
protected failed: boolean;
|
||||
protected cancelled: boolean;
|
||||
protected failed: boolean = false;
|
||||
protected cancelled: boolean = false;
|
||||
|
||||
constructor(scene: BattleScene, pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp?: boolean, ignorePp?: boolean) {
|
||||
public get pokemon(): Pokemon {
|
||||
return this._pokemon;
|
||||
}
|
||||
|
||||
protected set pokemon(pokemon: Pokemon) {
|
||||
this._pokemon = pokemon;
|
||||
}
|
||||
|
||||
public get move(): PokemonMove {
|
||||
return this._move;
|
||||
}
|
||||
|
||||
protected set move(move: PokemonMove) {
|
||||
this._move = move;
|
||||
}
|
||||
|
||||
public get targets(): BattlerIndex[] {
|
||||
return this._targets;
|
||||
}
|
||||
|
||||
protected set targets(targets: BattlerIndex[]) {
|
||||
this._targets = targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param followUp Indicates that the move being uses is a "follow-up" - for example, a move being used by Metronome or Dancer.
|
||||
* Follow-ups bypass a few failure conditions, including flinches, sleep/paralysis/freeze and volatile status checks, etc.
|
||||
*/
|
||||
constructor(scene: BattleScene, pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp: boolean = false, ignorePp: boolean = false) {
|
||||
super(scene);
|
||||
|
||||
this.pokemon = pokemon;
|
||||
this.targets = targets;
|
||||
this.move = move;
|
||||
this.followUp = followUp ?? false;
|
||||
this.ignorePp = ignorePp ?? false;
|
||||
this.failed = false;
|
||||
this.cancelled = false;
|
||||
this.followUp = followUp;
|
||||
this.ignorePp = ignorePp;
|
||||
}
|
||||
|
||||
canMove(ignoreDisableTags?: boolean): boolean {
|
||||
/**
|
||||
* Checks if the pokemon is active, if the move is usable, and that the move is targetting something.
|
||||
* @param ignoreDisableTags `true` to not check if the move is disabled
|
||||
* @returns `true` if all the checks pass
|
||||
*/
|
||||
public canMove(ignoreDisableTags: boolean = false): boolean {
|
||||
return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp, ignoreDisableTags) && !!this.targets.length;
|
||||
}
|
||||
|
||||
/**Signifies the current move should fail but still use PP */
|
||||
fail(): void {
|
||||
public fail(): void {
|
||||
this.failed = true;
|
||||
}
|
||||
|
||||
/**Signifies the current move should cancel and retain PP */
|
||||
cancel(): void {
|
||||
public cancel(): void {
|
||||
this.cancelled = true;
|
||||
}
|
||||
|
||||
start() {
|
||||
public start() {
|
||||
super.start();
|
||||
|
||||
console.log(Moves[this.move.moveId]);
|
||||
|
||||
// Check if move is unusable (e.g. because it's out of PP due to a mid-turn Spite).
|
||||
if (!this.canMove(true)) {
|
||||
if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails
|
||||
if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) {
|
||||
this.fail();
|
||||
this.showMoveText();
|
||||
this.showFailedText();
|
||||
}
|
||||
|
||||
return this.end();
|
||||
}
|
||||
|
||||
this.pokemon.turnData.acted = true;
|
||||
|
||||
// Reset hit-related turn data when starting follow-up moves (e.g. Metronomed moves, Dancer repeats)
|
||||
if (this.followUp) {
|
||||
this.pokemon.turnData.hitsLeft = -1;
|
||||
this.pokemon.turnData.hitCount = 0;
|
||||
}
|
||||
|
||||
// Check move to see if arena.ignoreAbilities should be true.
|
||||
if (!this.followUp) {
|
||||
if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) {
|
||||
this.scene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
|
||||
}
|
||||
} else {
|
||||
this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct?
|
||||
this.pokemon.turnData.hitCount = 0; // TODO: is `0` correct?
|
||||
}
|
||||
|
||||
// Move redirection abilities (ie. Storm Drain) only support single target moves
|
||||
const moveTarget = this.targets.length === 1
|
||||
? new Utils.IntegerHolder(this.targets[0])
|
||||
: null;
|
||||
if (moveTarget) {
|
||||
const oldTarget = moveTarget.value;
|
||||
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, moveTarget));
|
||||
this.pokemon.getOpponents().forEach(p => {
|
||||
const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag;
|
||||
if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) {
|
||||
moveTarget.value = p.getBattlerIndex();
|
||||
}
|
||||
});
|
||||
//Check if this move is immune to being redirected, and restore its target to the intended target if it is.
|
||||
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().hasAttr(BypassRedirectAttr))) {
|
||||
//If an ability prevented this move from being redirected, display its ability pop up.
|
||||
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().hasAttr(BypassRedirectAttr)) && oldTarget !== moveTarget.value) {
|
||||
this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr)));
|
||||
}
|
||||
moveTarget.value = oldTarget;
|
||||
}
|
||||
this.targets[0] = moveTarget.value;
|
||||
}
|
||||
this.resolveRedirectTarget();
|
||||
|
||||
// Check for counterattack moves to switch target
|
||||
if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) {
|
||||
if (this.pokemon.turnData.attacksReceived.length) {
|
||||
const attack = this.pokemon.turnData.attacksReceived[0];
|
||||
this.targets[0] = attack.sourceBattlerIndex;
|
||||
this.resolveCounterAttackTarget();
|
||||
|
||||
// account for metal burst and comeuppance hitting remaining targets in double battles
|
||||
// counterattack will redirect to remaining ally if original attacker faints
|
||||
if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) {
|
||||
if (this.scene.getField()[this.targets[0]].hp === 0) {
|
||||
const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField();
|
||||
//@ts-ignore
|
||||
this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); //TODO: fix ts-ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.targets[0] === BattlerIndex.ATTACKER) {
|
||||
this.fail(); // Marks the move as failed for later in doMove
|
||||
this.showMoveText();
|
||||
this.showFailedText();
|
||||
}
|
||||
}
|
||||
this.resolvePreMoveStatusEffects();
|
||||
|
||||
const targets = this.scene.getField(true).filter(p => {
|
||||
if (this.targets.indexOf(p.getBattlerIndex()) > -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this.lapsePreMoveAndMoveTags();
|
||||
|
||||
const doMove = () => {
|
||||
this.pokemon.turnData.acted = true; // Record that the move was attempted, even if it fails
|
||||
this.resolveFinalPreMoveCancellationChecks();
|
||||
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
||||
|
||||
let ppUsed = 1;
|
||||
// Filter all opponents to include only those this move is targeting
|
||||
const targetedOpponents = this.pokemon.getOpponents().filter(o => this.targets.includes(o.getBattlerIndex()));
|
||||
for (const opponent of targetedOpponents) {
|
||||
if (this.move.ppUsed + ppUsed >= this.move.getMovePp()) { // If we're already at max PP usage, stop checking
|
||||
break;
|
||||
}
|
||||
if (opponent.hasAbilityWithAttr(IncreasePpAbAttr)) { // Accounting for abilities like Pressure
|
||||
ppUsed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.followUp && this.canMove() && !this.cancelled) {
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
||||
}
|
||||
|
||||
const moveQueue = this.pokemon.getMoveQueue();
|
||||
if (this.cancelled || this.failed) {
|
||||
if (this.failed) {
|
||||
this.move.usePp(ppUsed); // Only use PP if the move failed
|
||||
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed));
|
||||
this.handlePreMoveFailures();
|
||||
} else {
|
||||
this.useMove();
|
||||
}
|
||||
|
||||
// Record a failed move so Abilities like Truant don't trigger next turn and soft-lock
|
||||
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
|
||||
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc.
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||
moveQueue.shift(); // Remove the second turn of charge moves
|
||||
return this.end();
|
||||
this.end();
|
||||
}
|
||||
|
||||
this.scene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger);
|
||||
/** Check for cancellation edge cases - no targets remaining, or {@linkcode Moves.NONE} is in the queue */
|
||||
protected resolveFinalPreMoveCancellationChecks() {
|
||||
const targets = this.getActiveTargetPokemon();
|
||||
const moveQueue = this.pokemon.getMoveQueue();
|
||||
|
||||
if (this.move.moveId) {
|
||||
this.showMoveText();
|
||||
}
|
||||
|
||||
// This should only happen when there are no valid targets left on the field
|
||||
if ((moveQueue.length && moveQueue[0].move === Moves.NONE) || !targets.length) {
|
||||
if (targets.length === 0 || (moveQueue.length && moveQueue[0].move === Moves.NONE)) {
|
||||
this.showFailedText();
|
||||
this.cancel();
|
||||
|
||||
// Record a failed move so Abilities like Truant don't trigger next turn and soft-lock
|
||||
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
|
||||
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc.
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||
|
||||
moveQueue.shift();
|
||||
return this.end();
|
||||
this.cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!moveQueue.length || !moveQueue.shift()?.ignorePP) && !this.ignorePp) { // using .shift here clears out two turn moves once they've been used
|
||||
this.move.usePp(ppUsed);
|
||||
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed));
|
||||
}
|
||||
|
||||
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
||||
this.scene.currentBattle.lastMove = this.move.moveId;
|
||||
}
|
||||
|
||||
// Assume conditions affecting targets only apply to moves with a single target
|
||||
let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove());
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled);
|
||||
if (success && this.scene.arena.isMoveWeatherCancelled(this.pokemon, this.move.getMove())) {
|
||||
success = false;
|
||||
} else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) {
|
||||
success = false;
|
||||
if (failedText === null) {
|
||||
failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain?.terrainType!); // TODO: is this bang correct?
|
||||
}
|
||||
public getActiveTargetPokemon() {
|
||||
return this.scene.getField(true).filter(p => this.targets.includes(p.getBattlerIndex()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger pokemon type change before playing the move animation
|
||||
* Will still change the user's type when using Roar, Whirlwind, Trick-or-Treat, and Forest's Curse,
|
||||
* regardless of whether the move successfully executes or not.
|
||||
* Handles {@link StatusEffect.SLEEP Sleep}/{@link StatusEffect.PARALYSIS Paralysis}/{@link StatusEffect.FREEZE Freeze} rolls and side effects.
|
||||
*/
|
||||
if (success || [Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE].includes(this.move.moveId)) {
|
||||
applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove());
|
||||
}
|
||||
|
||||
if (success) {
|
||||
this.scene.unshiftPhase(this.getEffectPhase());
|
||||
} else {
|
||||
this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual });
|
||||
if (!cancelled.value) {
|
||||
this.showFailedText(failedText);
|
||||
}
|
||||
}
|
||||
// Checks if Dancer ability is triggered
|
||||
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) {
|
||||
// Pokemon with Dancer can be on either side of the battle so we check in both cases
|
||||
this.scene.getPlayerField().forEach(pokemon => {
|
||||
applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets);
|
||||
});
|
||||
this.scene.getEnemyField().forEach(pokemon => {
|
||||
applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets);
|
||||
});
|
||||
}
|
||||
this.end();
|
||||
};
|
||||
|
||||
protected resolvePreMoveStatusEffects() {
|
||||
if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) {
|
||||
this.pokemon.status.incrementTurn();
|
||||
let activated = false;
|
||||
@ -273,25 +186,257 @@ export class MovePhase extends BattlePhase {
|
||||
if (activated) {
|
||||
this.scene.queueMessage(getStatusEffectActivationText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon)));
|
||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.getBattlerIndex(), undefined, CommonAnim.POISON + (this.pokemon.status.effect - 1)));
|
||||
doMove();
|
||||
} else {
|
||||
if (healed) {
|
||||
} else if (healed) {
|
||||
this.scene.queueMessage(getStatusEffectHealText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon)));
|
||||
this.pokemon.resetStatus();
|
||||
this.pokemon.updateInfo();
|
||||
}
|
||||
doMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lapse {@linkcode BattlerTagLapseType.PRE_MOVE PRE_MOVE} tags that trigger before a move is used, regardless of whether or not it failed.
|
||||
* Also lapse {@linkcode BattlerTagLapseType.MOVE MOVE} tags if the move should be successful.
|
||||
*/
|
||||
protected lapsePreMoveAndMoveTags() {
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
||||
|
||||
// TODO: does this intentionally happen before the no targets/Moves.NONE on queue cancellation case is checked?
|
||||
if (!this.followUp && this.canMove() && !this.cancelled) {
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
||||
}
|
||||
}
|
||||
|
||||
protected useMove() {
|
||||
const targets = this.getActiveTargetPokemon();
|
||||
const moveQueue = this.pokemon.getMoveQueue();
|
||||
|
||||
// form changes happen even before we know that the move wll execute.
|
||||
this.scene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger);
|
||||
|
||||
this.showMoveText();
|
||||
|
||||
// TODO: Clean up implementation of two-turn moves.
|
||||
if (moveQueue.length > 0) { // Using .shift here clears out two turn moves once they've been used
|
||||
this.ignorePp = moveQueue.shift()?.ignorePP ?? false;
|
||||
}
|
||||
|
||||
// "commit" to using the move, deducting PP.
|
||||
if (!this.ignorePp) {
|
||||
const ppUsed = 1 + this.getPpIncreaseFromPressure(targets);
|
||||
|
||||
this.move.usePp(ppUsed);
|
||||
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed));
|
||||
}
|
||||
|
||||
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
||||
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
||||
this.scene.currentBattle.lastMove = this.move.moveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the move is successful (meaning that its damage/effects can be attempted)
|
||||
* by checking that all of the following are true:
|
||||
* - Conditional attributes of the move are all met
|
||||
* - The target's `ForceSwitchOutImmunityAbAttr` is not triggered (see {@linkcode Move.prototype.applyConditions})
|
||||
* - Weather does not block the move
|
||||
* - Terrain does not block the move
|
||||
*
|
||||
* TODO: These steps are straightforward, but the implementation below is extremely convoluted.
|
||||
*/
|
||||
|
||||
const move = this.move.getMove();
|
||||
|
||||
/**
|
||||
* Move conditions assume the move has a single target
|
||||
* TODO: is this sustainable?
|
||||
*/
|
||||
const passesConditions = move.applyConditions(this.pokemon, targets[0], move);
|
||||
const failedDueToWeather: boolean = this.scene.arena.isMoveWeatherCancelled(this.pokemon, move);
|
||||
const failedDueToTerrain: boolean = this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, move);
|
||||
|
||||
const success = passesConditions && !failedDueToWeather && !failedDueToTerrain;
|
||||
|
||||
/**
|
||||
* If the move has not failed, trigger ability-based user type changes and then execute it.
|
||||
*
|
||||
* Notably, Roar, Whirlwind, Trick-or-Treat, and Forest's Curse will trigger these type changes even
|
||||
* if the move fails.
|
||||
*/
|
||||
if (success) {
|
||||
applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove());
|
||||
this.scene.unshiftPhase(new MoveEffectPhase(this.scene, this.pokemon.getBattlerIndex(), this.targets, this.move));
|
||||
|
||||
} else {
|
||||
doMove();
|
||||
if ([ Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE ].includes(this.move.moveId)) {
|
||||
applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove());
|
||||
}
|
||||
|
||||
this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual });
|
||||
|
||||
let failedText: string | undefined;
|
||||
const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new Utils.BooleanHolder(false));
|
||||
|
||||
if (failureMessage) {
|
||||
failedText = failureMessage;
|
||||
} else if (failedDueToTerrain) {
|
||||
failedText = getTerrainBlockMessage(this.pokemon, this.scene.arena.getTerrainType());
|
||||
}
|
||||
|
||||
this.showFailedText(failedText);
|
||||
}
|
||||
|
||||
// Handle Dancer, which triggers immediately after a move is used (rather than waiting on `this.end()`).
|
||||
// Note that the `!this.followUp` check here prevents an infinite Dancer loop.
|
||||
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) {
|
||||
this.scene.getField(true).forEach(pokemon => {
|
||||
applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getEffectPhase(): MoveEffectPhase {
|
||||
return new MoveEffectPhase(this.scene, this.pokemon.getBattlerIndex(), this.targets, this.move);
|
||||
/**
|
||||
* Queues a {@linkcode MoveEndPhase} if the move wasn't a {@linkcode followUp} and {@linkcode canMove()} returns `true`,
|
||||
* then ends the phase.
|
||||
*/
|
||||
public end() {
|
||||
if (!this.followUp && this.canMove()) {
|
||||
this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex()));
|
||||
}
|
||||
|
||||
super.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies PP increasing abilities (currently only {@link Abilities.PRESSURE Pressure}) if they exist on the target pokemon.
|
||||
* Note that targets must include only active pokemon.
|
||||
*
|
||||
* TODO: This hardcodes the PP increase at 1 per opponent, rather than deferring to the ability.
|
||||
*/
|
||||
public getPpIncreaseFromPressure(targets: Pokemon[]) {
|
||||
const foesWithPressure = this.pokemon.getOpponents().filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr(IncreasePpAbAttr));
|
||||
return foesWithPressure.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies `this.targets` in place, based upon:
|
||||
* - Move redirection abilities, effects, etc.
|
||||
* - Counterattacks, which pass a special value into the `targets` constructor param (`[`{@linkcode BattlerIndex.ATTACKER}`]`).
|
||||
*/
|
||||
protected resolveRedirectTarget() {
|
||||
if (this.targets.length === 1) {
|
||||
const currentTarget = this.targets[0];
|
||||
const redirectTarget = new Utils.NumberHolder(currentTarget);
|
||||
|
||||
// check move redirection abilities of every pokemon *except* the user.
|
||||
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget));
|
||||
|
||||
// check for center-of-attention tags (note that this will override redirect abilities)
|
||||
this.pokemon.getOpponents().forEach(p => {
|
||||
const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag;
|
||||
|
||||
// TODO: don't hardcode this interaction.
|
||||
// Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect)
|
||||
if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) {
|
||||
redirectTarget.value = p.getBattlerIndex();
|
||||
}
|
||||
});
|
||||
|
||||
if (currentTarget !== redirectTarget.value) {
|
||||
if (this.move.getMove().hasAttr(BypassRedirectAttr)) {
|
||||
redirectTarget.value = currentTarget;
|
||||
|
||||
} else if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) {
|
||||
redirectTarget.value = currentTarget;
|
||||
this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr)));
|
||||
}
|
||||
|
||||
this.targets[0] = redirectTarget.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counter-attacking moves pass in `[`{@linkcode BattlerIndex.ATTACKER}`]` into the constructor's `targets` param.
|
||||
* This function modifies `this.targets` to reflect the actual battler index of the user's last
|
||||
* attacker.
|
||||
*
|
||||
* If there is no last attacker, or they are no longer on the field, a message is displayed and the
|
||||
* move is marked for failure.
|
||||
*/
|
||||
protected resolveCounterAttackTarget() {
|
||||
if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) {
|
||||
if (this.pokemon.turnData.attacksReceived.length) {
|
||||
const attacker = this.pokemon.scene.getPokemonById(this.pokemon.turnData.attacksReceived[0].sourceId);
|
||||
|
||||
if (attacker?.isActive(true)) {
|
||||
this.targets[0] = attacker.getBattlerIndex();
|
||||
}
|
||||
|
||||
// account for metal burst and comeuppance hitting remaining targets in double battles
|
||||
// counterattack will redirect to remaining ally if original attacker faints
|
||||
if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) {
|
||||
if (this.scene.getField()[this.targets[0]].hp === 0) {
|
||||
const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField();
|
||||
this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex() ?? BattlerIndex.ATTACKER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.targets[0] === BattlerIndex.ATTACKER) {
|
||||
this.fail();
|
||||
this.showMoveText();
|
||||
this.showFailedText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the case where the move was cancelled or failed:
|
||||
* - Uses PP if the move failed (not cancelled) and should use PP (failed moves are not affected by {@link Abilities.PRESSURE Pressure})
|
||||
* - Records a cancelled OR failed move in move history, so abilities like {@link Abilities.TRUANT Truant} don't trigger on the
|
||||
* next turn and soft-lock.
|
||||
* - Lapses `MOVE_EFFECT` tags:
|
||||
* - Semi-invulnerable battler tags (Fly/Dive/etc.) are intended to lapse on move effects, but also need
|
||||
* to lapse on move failure/cancellation.
|
||||
*
|
||||
* TODO: ...this seems weird.
|
||||
* - Lapses `AFTER_MOVE` tags:
|
||||
* - This handles the effects of {@link Moves.SUBSTITUTE Substitute}
|
||||
* - Removes the second turn of charge moves
|
||||
*
|
||||
* TODO: handle charge moves more gracefully
|
||||
*/
|
||||
protected handlePreMoveFailures() {
|
||||
if (this.cancelled || this.failed) {
|
||||
if (this.failed) {
|
||||
const ppUsed = this.ignorePp ? 0 : 1;
|
||||
|
||||
if (ppUsed) {
|
||||
this.move.usePp();
|
||||
}
|
||||
|
||||
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed));
|
||||
}
|
||||
|
||||
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
|
||||
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||
|
||||
this.pokemon.getMoveQueue().shift();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the move's usage text to the player, unless it's a charge turn (ie: {@link Moves.SOLAR_BEAM Solar Beam}),
|
||||
* the pokemon is on a recharge turn (ie: {@link Moves.HYPER_BEAM Hyper Beam}), or a 2-turn move was interrupted (ie: {@link Moves.FLY Fly}).
|
||||
*/
|
||||
protected showMoveText(): void {
|
||||
if (this.move.moveId === Moves.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
showMoveText(): void {
|
||||
if (this.move.getMove().hasAttr(ChargeAttr)) {
|
||||
const lastMove = this.pokemon.getLastXMoves() as TurnMove[];
|
||||
if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) {
|
||||
@ -311,18 +456,10 @@ export class MovePhase extends BattlePhase {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon),
|
||||
moveName: this.move.getName()
|
||||
}), 500);
|
||||
applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true)!, this.move.getMove()); //TODO: is the bang correct here?
|
||||
applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents()[0], this.move.getMove());
|
||||
}
|
||||
|
||||
showFailedText(failedText: string | null = null): void {
|
||||
this.scene.queueMessage(failedText || i18next.t("battle:attackFailed"));
|
||||
}
|
||||
|
||||
end() {
|
||||
if (!this.followUp && this.canMove()) {
|
||||
this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex()));
|
||||
}
|
||||
|
||||
super.end();
|
||||
protected showFailedText(failedText?: string): void {
|
||||
this.scene.queueMessage(failedText ?? i18next.t("battle:attackFailed"));
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import Pokemon from "#app/field/pokemon";
|
||||
import { BattlePhase } from "./battle-phase";
|
||||
import { ShowAbilityPhase } from "./show-ability-phase";
|
||||
|
||||
/**
|
||||
* Cures the party of all non-volatile status conditions, shows a message
|
||||
* @param {BattleScene} scene The current scene
|
||||
* @param {Pokemon} user The user of the move that cures the party
|
||||
* @param {string} message The message that should be displayed
|
||||
* @param {Abilities} abilityCondition Pokemon with this ability will not be affected ie. Soundproof
|
||||
*/
|
||||
export class PartyStatusCurePhase extends BattlePhase {
|
||||
private user: Pokemon;
|
||||
private message: string;
|
||||
private abilityCondition: Abilities;
|
||||
|
||||
constructor(scene: BattleScene, user: Pokemon, message: string, abilityCondition: Abilities) {
|
||||
super(scene);
|
||||
|
||||
this.user = user;
|
||||
this.message = message;
|
||||
this.abilityCondition = abilityCondition;
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
for (const pokemon of this.scene.getParty()) {
|
||||
if (!pokemon.isOnField() || pokemon === this.user) {
|
||||
pokemon.resetStatus(false);
|
||||
pokemon.updateInfo(true);
|
||||
} else {
|
||||
if (!pokemon.hasAbility(this.abilityCondition)) {
|
||||
pokemon.resetStatus();
|
||||
pokemon.updateInfo(true);
|
||||
} else {
|
||||
// Manually show ability bar, since we're not hooked into the targeting system
|
||||
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.message) {
|
||||
this.scene.queueMessage(this.message);
|
||||
}
|
||||
this.end();
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import Pokemon from "#app/field/pokemon";
|
||||
import { BattlePhase } from "#app/phases/battle-phase";
|
||||
|
||||
|
||||
|
||||
export class PokemonAnimPhase extends BattlePhase {
|
||||
/** The type of animation to play in this phase */
|
||||
private key: PokemonAnimType;
|
||||
|
@ -80,7 +80,7 @@ export class SelectStarterPhase extends Phase {
|
||||
starterPokemon.nickname = starter.nickname;
|
||||
}
|
||||
|
||||
if (this.scene.gameMode.isSplicedOnly) {
|
||||
if (this.scene.gameMode.isSplicedOnly || Overrides.STARTER_FUSION_OVERRIDE) {
|
||||
starterPokemon.generateFusionSpecies(true);
|
||||
}
|
||||
starterPokemon.setVisible(false);
|
||||
|
@ -44,7 +44,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
|
||||
return;
|
||||
}
|
||||
|
||||
const damage = Math.ceil(pokemon.getMaxHp() / 16);
|
||||
const damage = Utils.toDmgValue(pokemon.getMaxHp() / 16);
|
||||
|
||||
this.scene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct?
|
||||
pokemon.damageAndUpdate(damage, HitResult.EFFECTIVE, false, false, true);
|
||||
|
@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - BATTLE BOND", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - COSTAR", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Dancer", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Disguise", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -10,7 +10,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Galvanize", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -13,7 +13,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Libero", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -11,7 +11,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Parental Bond", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - POWER CONSTRUCT", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -13,7 +13,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Protean", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -12,7 +12,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Sand Veil", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - SCHOOLING", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - SHIELDS DOWN", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Sturdy", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
import { BerryPhase } from "#app/phases/berry-phase";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - Unseen Fist", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -21,7 +21,6 @@ import { Status, StatusEffect } from "#app/data/status-effect";
|
||||
import { SwitchType } from "#enums/switch-type";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - ZEN MODE", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Abilities - ZERO TO HERO", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -39,7 +39,7 @@ describe("Weather - Hail", () => {
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
game.scene.getField(true).forEach(pokemon => {
|
||||
expect(pokemon.hp).toBeLessThan(pokemon.getMaxHp() - Math.floor(pokemon.getMaxHp() / 16));
|
||||
expect(pokemon.hp).toBe(pokemon.getMaxHp() - Math.max(Math.floor(pokemon.getMaxHp() / 16), 1));
|
||||
});
|
||||
});
|
||||
|
||||
@ -56,6 +56,20 @@ describe("Weather - Hail", () => {
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp() - Math.floor(enemyPokemon.getMaxHp() / 16));
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1));
|
||||
});
|
||||
|
||||
it("does not inflict damage to Ice type Pokemon", async () => {
|
||||
await game.classicMode.startBattle([ Species.CLOYSTER ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1));
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { WeatherType } from "#app/data/weather";
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
@ -37,7 +39,7 @@ describe("Weather - Sandstorm", () => {
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
game.scene.getField(true).forEach(pokemon => {
|
||||
expect(pokemon.hp).toBeLessThan(pokemon.getMaxHp() - Math.floor(pokemon.getMaxHp() / 16));
|
||||
expect(pokemon.hp).toBe(pokemon.getMaxHp() - Math.max(Math.floor(pokemon.getMaxHp() / 16), 1));
|
||||
});
|
||||
});
|
||||
|
||||
@ -53,6 +55,37 @@ describe("Weather - Sandstorm", () => {
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp() - Math.floor(enemyPokemon.getMaxHp() / 16));
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1));
|
||||
});
|
||||
|
||||
it("does not inflict damage to Rock, Ground and Steel type Pokemon", async () => {
|
||||
game.override
|
||||
.battleType("double")
|
||||
.enemySpecies(Species.SANDSHREW)
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.enemyAbility(Abilities.BALL_FETCH);
|
||||
|
||||
await game.classicMode.startBattle([ Species.ROCKRUFF, Species.KLINK ]);
|
||||
|
||||
game.move.select(Moves.SPLASH, 0);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
game.scene.getField(true).forEach(pokemon => {
|
||||
expect(pokemon.hp).toBe(pokemon.getMaxHp());
|
||||
});
|
||||
});
|
||||
|
||||
it("increases Rock type Pokemon Sp.Def by 50%", async () => {
|
||||
await game.classicMode.startBattle([ Species.ROCKRUFF ]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const playerSpdef = playerPokemon.getStat(Stat.SPDEF);
|
||||
expect(playerPokemon.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(playerSpdef * 1.5));
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
const enemySpdef = enemyPokemon.getStat(Stat.SPDEF);
|
||||
expect(enemyPokemon.getEffectiveStat(Stat.SPDEF)).toBe(enemySpdef);
|
||||
});
|
||||
});
|
||||
|
@ -11,7 +11,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Inverse Battle", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -5,6 +5,7 @@ import { Moves } from "#app/enums/moves";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
import { Species } from "#enums/species";
|
||||
|
||||
//const TIMEOUT = 20 * 1000;
|
||||
|
||||
@ -53,12 +54,11 @@ describe("Shop modifications", async () => {
|
||||
|
||||
game.override
|
||||
.startingWave(9)
|
||||
.startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather
|
||||
.startingBiome(Biome.ICE_CAVE)
|
||||
.battleType("single")
|
||||
.startingLevel(100) // Avoid levelling up
|
||||
.enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents()
|
||||
.disableTrainerWaves()
|
||||
.moveset([Moves.KOWTOW_CLEAVE])
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
game.modifiers
|
||||
.addCheck("EVIOLITE")
|
||||
@ -71,9 +71,8 @@ describe("Shop modifications", async () => {
|
||||
});
|
||||
|
||||
it("should not have Eviolite and Mini Black Hole available in Classic if not unlocked", async () => {
|
||||
await game.classicMode.startBattle();
|
||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR ]);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
await game.phaseInterceptor.to("BattleEndPhase");
|
||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||
@ -86,8 +85,7 @@ describe("Shop modifications", async () => {
|
||||
|
||||
it("should have Eviolite and Mini Black Hole available in Daily", async () => {
|
||||
await game.dailyMode.startBattle();
|
||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
await game.phaseInterceptor.to("BattleEndPhase");
|
||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - After You", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Alluring Voice", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
101
src/test/moves/aromatherapy.test.ts
Normal file
101
src/test/moves/aromatherapy.test.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { StatusEffect } from "#app/enums/status-effect";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||
|
||||
describe("Moves - Aromatherapy", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.AROMATHERAPY, Moves.SPLASH ])
|
||||
.statusEffect(StatusEffect.BURN)
|
||||
.battleType("double")
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should cure status effect of the user, its ally, and all party pokemon", async () => {
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftPlayer, rightPlayer, partyPokemon ] = game.scene.getParty();
|
||||
|
||||
vi.spyOn(leftPlayer, "resetStatus");
|
||||
vi.spyOn(rightPlayer, "resetStatus");
|
||||
vi.spyOn(partyPokemon, "resetStatus");
|
||||
|
||||
game.move.select(Moves.AROMATHERAPY, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(rightPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(partyPokemon.resetStatus).toHaveBeenCalledOnce();
|
||||
|
||||
expect(leftPlayer.status?.effect).toBeUndefined();
|
||||
expect(rightPlayer.status?.effect).toBeUndefined();
|
||||
expect(partyPokemon.status?.effect).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not cure status effect of the target/target's allies", async () => {
|
||||
game.override.enemyStatusEffect(StatusEffect.BURN);
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftOpp, rightOpp ] = game.scene.getEnemyField();
|
||||
|
||||
vi.spyOn(leftOpp, "resetStatus");
|
||||
vi.spyOn(rightOpp, "resetStatus");
|
||||
|
||||
game.move.select(Moves.AROMATHERAPY, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||
expect(rightOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(leftOpp.status?.effect).toBeTruthy();
|
||||
expect(rightOpp.status?.effect).toBeTruthy();
|
||||
|
||||
expect(leftOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||
expect(rightOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||
});
|
||||
|
||||
it("should not cure status effect of allies ON FIELD with Sap Sipper, should still cure allies in party", async () => {
|
||||
game.override.ability(Abilities.SAP_SIPPER);
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftPlayer, rightPlayer, partyPokemon ] = game.scene.getParty();
|
||||
|
||||
vi.spyOn(leftPlayer, "resetStatus");
|
||||
vi.spyOn(rightPlayer, "resetStatus");
|
||||
vi.spyOn(partyPokemon, "resetStatus");
|
||||
|
||||
game.move.select(Moves.AROMATHERAPY, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(rightPlayer.resetStatus).toHaveBeenCalledTimes(0);
|
||||
expect(partyPokemon.resetStatus).toHaveBeenCalledOnce();
|
||||
|
||||
expect(leftPlayer.status?.effect).toBeUndefined();
|
||||
expect(rightPlayer.status?.effect).toBe(StatusEffect.BURN);
|
||||
expect(partyPokemon.status?.effect).toBeUndefined();
|
||||
});
|
||||
});
|
@ -12,7 +12,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Astonish", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import { BattlerIndex } from "#app/battle";
|
||||
import { StatusEffect } from "#app/enums/status-effect";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Baneful Bunker", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -11,7 +11,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Beak Blast", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Burning Jealousy", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -11,7 +11,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Ceaseless Edge", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -10,7 +10,6 @@ import { BerryPhase } from "#app/phases/berry-phase";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Crafty Shield", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -11,7 +11,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Focus Punch", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Follow Me", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -7,7 +7,6 @@ import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Gastro Acid", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -7,7 +7,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Glaive Rush", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
101
src/test/moves/heal_bell.test.ts
Normal file
101
src/test/moves/heal_bell.test.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { StatusEffect } from "#app/enums/status-effect";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||
|
||||
describe("Moves - Heal Bell", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.HEAL_BELL, Moves.SPLASH ])
|
||||
.statusEffect(StatusEffect.BURN)
|
||||
.battleType("double")
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should cure status effect of the user, its ally, and all party pokemon", async () => {
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftPlayer, rightPlayer, partyPokemon ] = game.scene.getParty();
|
||||
|
||||
vi.spyOn(leftPlayer, "resetStatus");
|
||||
vi.spyOn(rightPlayer, "resetStatus");
|
||||
vi.spyOn(partyPokemon, "resetStatus");
|
||||
|
||||
game.move.select(Moves.HEAL_BELL, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(rightPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(partyPokemon.resetStatus).toHaveBeenCalledOnce();
|
||||
|
||||
expect(leftPlayer.status?.effect).toBeUndefined();
|
||||
expect(rightPlayer.status?.effect).toBeUndefined();
|
||||
expect(partyPokemon.status?.effect).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not cure status effect of the target/target's allies", async () => {
|
||||
game.override.enemyStatusEffect(StatusEffect.BURN);
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftOpp, rightOpp ] = game.scene.getEnemyField();
|
||||
|
||||
vi.spyOn(leftOpp, "resetStatus");
|
||||
vi.spyOn(rightOpp, "resetStatus");
|
||||
|
||||
game.move.select(Moves.HEAL_BELL, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||
expect(rightOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(leftOpp.status?.effect).toBeTruthy();
|
||||
expect(rightOpp.status?.effect).toBeTruthy();
|
||||
|
||||
expect(leftOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||
expect(rightOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||
});
|
||||
|
||||
it("should not cure status effect of allies ON FIELD with Soundproof, should still cure allies in party", async () => {
|
||||
game.override.ability(Abilities.SOUNDPROOF);
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftPlayer, rightPlayer, partyPokemon ] = game.scene.getParty();
|
||||
|
||||
vi.spyOn(leftPlayer, "resetStatus");
|
||||
vi.spyOn(rightPlayer, "resetStatus");
|
||||
vi.spyOn(partyPokemon, "resetStatus");
|
||||
|
||||
game.move.select(Moves.HEAL_BELL, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(rightPlayer.resetStatus).toHaveBeenCalledTimes(0);
|
||||
expect(partyPokemon.resetStatus).toHaveBeenCalledOnce();
|
||||
|
||||
expect(leftPlayer.status?.effect).toBeUndefined();
|
||||
expect(rightPlayer.status?.effect).toBe(StatusEffect.BURN);
|
||||
expect(partyPokemon.status?.effect).toBeUndefined();
|
||||
});
|
||||
});
|
@ -12,7 +12,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Jaw Lock", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Lash Out", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import GameManager from "../utils/gameManager";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Lucky Chant", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Make It Rain", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -10,7 +10,6 @@ import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Mat Block", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Multi-target damage reduction", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -11,7 +11,6 @@ import { MessagePhase } from "#app/phases/message-phase";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Parting Shot", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -11,7 +11,6 @@ import { BattlerIndex } from "#app/battle";
|
||||
import { MoveResult } from "#app/field/pokemon";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Protect", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Purify", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import { BattlerIndex } from "#app/battle";
|
||||
import { MoveResult } from "#app/field/pokemon";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Quick Guard", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -7,7 +7,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Rage Powder", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -10,7 +10,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Roost", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Safeguard", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
74
src/test/moves/scale_shot.test.ts
Normal file
74
src/test/moves/scale_shot.test.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { Stat } from "#enums/stat";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest";
|
||||
|
||||
describe("Moves - Scale Shot", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SCALE_SHOT ])
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.starterSpecies(Species.MINCCINO)
|
||||
.ability(Abilities.NO_GUARD)
|
||||
.passiveAbility(Abilities.SKILL_LINK)
|
||||
.enemyAbility(Abilities.SHEER_FORCE)
|
||||
.enemyPassiveAbility(Abilities.STALL)
|
||||
.enemyMoveset(Moves.SKILL_SWAP)
|
||||
.enemyLevel(5);
|
||||
});
|
||||
|
||||
it("applies stat changes after last hit", async () => {
|
||||
await game.classicMode.startBattle([ Species.FORRETRESS ]);
|
||||
const minccino = game.scene.getPlayerPokemon()!;
|
||||
game.move.select(Moves.SCALE_SHOT);
|
||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||
expect (minccino?.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect (minccino?.getStatStage(Stat.SPD)).toBe(0);
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect (minccino.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect (minccino.getStatStage(Stat.SPD)).toBe(1);
|
||||
});
|
||||
|
||||
it("unaffected by sheer force", async () => {
|
||||
await game.classicMode.startBattle([ Species.WOBBUFFET ]);
|
||||
const minccino = game.scene.getPlayerPokemon()!;
|
||||
const wobbuffet = game.scene.getEnemyPokemon()!;
|
||||
wobbuffet.setStat(Stat.HP, 100, true);
|
||||
wobbuffet.hp = 100;
|
||||
game.move.select(Moves.SCALE_SHOT);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const hpafter1 = wobbuffet.hp;
|
||||
//effect not nullified by sheer force
|
||||
expect (minccino.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect (minccino.getStatStage(Stat.SPD)).toBe(1);
|
||||
game.move.select(Moves.SCALE_SHOT);
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
const hpafter2 = wobbuffet.hp;
|
||||
//check damage not boosted- make damage before sheer force a little lower than theoretical boosted sheer force damage
|
||||
expect (100 - hpafter1).toBe(hpafter1 - hpafter2);
|
||||
});
|
||||
});
|
@ -11,7 +11,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Shell Trap", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
86
src/test/moves/sparkly_swirl.test.ts
Normal file
86
src/test/moves/sparkly_swirl.test.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { StatusEffect } from "#app/enums/status-effect";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("Moves - Sparkly Swirl", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.enemySpecies(Species.SHUCKLE)
|
||||
.enemyLevel(100)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset([ Moves.SPARKLY_SWIRL, Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPARKLY_SWIRL], "accuracy", "get").mockReturnValue(100);
|
||||
});
|
||||
|
||||
it("should cure status effect of the user, its ally, and all party pokemon", async () => {
|
||||
game.override
|
||||
.battleType("double")
|
||||
.statusEffect(StatusEffect.BURN);
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftPlayer, rightPlayer, partyPokemon ] = game.scene.getParty();
|
||||
const leftOpp = game.scene.getEnemyPokemon()!;
|
||||
|
||||
vi.spyOn(leftPlayer, "resetStatus");
|
||||
vi.spyOn(rightPlayer, "resetStatus");
|
||||
vi.spyOn(partyPokemon, "resetStatus");
|
||||
|
||||
game.move.select(Moves.SPARKLY_SWIRL, 0, leftOpp.getBattlerIndex());
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(rightPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||
expect(partyPokemon.resetStatus).toHaveBeenCalledOnce();
|
||||
|
||||
expect(leftPlayer.status?.effect).toBeUndefined();
|
||||
expect(rightPlayer.status?.effect).toBeUndefined();
|
||||
expect(partyPokemon.status?.effect).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not cure status effect of the target/target's allies", async () => {
|
||||
game.override
|
||||
.battleType("double")
|
||||
.enemyStatusEffect(StatusEffect.BURN);
|
||||
await game.classicMode.startBattle([ Species.RATTATA, Species.RATTATA ]);
|
||||
const [ leftOpp, rightOpp ] = game.scene.getEnemyField();
|
||||
|
||||
vi.spyOn(leftOpp, "resetStatus");
|
||||
vi.spyOn(rightOpp, "resetStatus");
|
||||
|
||||
game.move.select(Moves.SPARKLY_SWIRL, 0, leftOpp.getBattlerIndex());
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||
expect(rightOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(leftOpp.status?.effect).toBeTruthy();
|
||||
expect(rightOpp.status?.effect).toBeTruthy();
|
||||
|
||||
expect(leftOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||
expect(rightOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||
});
|
||||
});
|
@ -7,7 +7,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Spotlight", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
98
src/test/moves/tera_starstorm.test.ts
Normal file
98
src/test/moves/tera_starstorm.test.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Type } from "#app/data/type";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||
|
||||
describe("Moves - Tera Starstorm", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.TERA_STARSTORM, Moves.SPLASH ])
|
||||
.battleType("double")
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.enemyLevel(30)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]);
|
||||
});
|
||||
|
||||
it("changes type to Stellar when used by Terapagos in its Stellar Form", async () => {
|
||||
game.override.battleType("single");
|
||||
await game.classicMode.startBattle([ Species.TERAPAGOS ]);
|
||||
|
||||
const terapagos = game.scene.getPlayerPokemon()!;
|
||||
|
||||
vi.spyOn(terapagos, "getMoveType");
|
||||
|
||||
game.move.select(Moves.TERA_STARSTORM);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(terapagos.isTerastallized()).toBe(true);
|
||||
expect(terapagos.getMoveType).toHaveReturnedWith(Type.STELLAR);
|
||||
});
|
||||
|
||||
it("targets both opponents in a double battle when used by Terapagos in its Stellar Form", async () => {
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.TERAPAGOS ]);
|
||||
|
||||
game.move.select(Moves.TERA_STARSTORM, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.TERA_STARSTORM, 1);
|
||||
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
|
||||
|
||||
const enemyField = game.scene.getEnemyField();
|
||||
|
||||
// Pokemon other than Terapagos should not be affected - only hits one target
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(enemyField.some(pokemon => pokemon.isFullHp())).toBe(true);
|
||||
|
||||
// Terapagos in Stellar Form should hit both targets
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(enemyField.every(pokemon => pokemon.isFullHp())).toBe(false);
|
||||
});
|
||||
|
||||
it("applies the effects when Terapagos in Stellar Form is fused with another Pokemon", async () => {
|
||||
await game.classicMode.startBattle([ Species.TERAPAGOS, Species.CHARMANDER, Species.MAGIKARP ]);
|
||||
|
||||
const fusionedMon = game.scene.getParty()[0];
|
||||
const magikarp = game.scene.getParty()[2];
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
fusionedMon.fusionSpecies = magikarp.species;
|
||||
fusionedMon.fusionFormIndex = magikarp.formIndex;
|
||||
fusionedMon.fusionAbilityIndex = magikarp.abilityIndex;
|
||||
fusionedMon.fusionShiny = magikarp.shiny;
|
||||
fusionedMon.fusionVariant = magikarp.variant;
|
||||
fusionedMon.fusionGender = magikarp.gender;
|
||||
fusionedMon.fusionLuck = magikarp.luck;
|
||||
|
||||
vi.spyOn(fusionedMon, "getMoveType");
|
||||
|
||||
game.move.select(Moves.TERA_STARSTORM, 0);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
// Fusion and terastallized
|
||||
expect(fusionedMon.isFusion()).toBe(true);
|
||||
expect(fusionedMon.isTerastallized()).toBe(true);
|
||||
// Move effects should be applied
|
||||
expect(fusionedMon.getMoveType).toHaveReturnedWith(Type.STELLAR);
|
||||
expect(game.scene.getEnemyField().every(pokemon => pokemon.isFullHp())).toBe(false);
|
||||
});
|
||||
});
|
@ -9,7 +9,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Thousand Arrows", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -8,7 +8,6 @@ import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Thunder Wave", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
89
src/test/moves/toxic.test.ts
Normal file
89
src/test/moves/toxic.test.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
|
||||
describe("Moves - Toxic", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleType("single")
|
||||
.moveset(Moves.TOXIC)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should be guaranteed to hit if user is Poison-type", async () => {
|
||||
vi.spyOn(allMoves[Moves.TOXIC], "accuracy", "get").mockReturnValue(0);
|
||||
await game.classicMode.startBattle([ Species.TOXAPEX ]);
|
||||
|
||||
game.move.select(Moves.TOXIC);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC);
|
||||
});
|
||||
|
||||
it("may miss if user is not Poison-type", async () => {
|
||||
vi.spyOn(allMoves[Moves.TOXIC], "accuracy", "get").mockReturnValue(0);
|
||||
await game.classicMode.startBattle([ Species.UMBREON ]);
|
||||
|
||||
game.move.select(Moves.TOXIC);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(game.scene.getEnemyPokemon()!.status).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should hit semi-invulnerable targets if user is Poison-type", async () => {
|
||||
vi.spyOn(allMoves[Moves.TOXIC], "accuracy", "get").mockReturnValue(0);
|
||||
game.override.enemyMoveset(Moves.FLY);
|
||||
await game.classicMode.startBattle([ Species.TOXAPEX ]);
|
||||
|
||||
game.move.select(Moves.TOXIC);
|
||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC);
|
||||
});
|
||||
|
||||
it("should miss semi-invulnerable targets if user is not Poison-type", async () => {
|
||||
vi.spyOn(allMoves[Moves.TOXIC], "accuracy", "get").mockReturnValue(-1);
|
||||
game.override.enemyMoveset(Moves.FLY);
|
||||
await game.classicMode.startBattle([ Species.UMBREON ]);
|
||||
|
||||
game.move.select(Moves.TOXIC);
|
||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(game.scene.getEnemyPokemon()!.status).toBeUndefined();
|
||||
});
|
||||
|
||||
it("moves other than Toxic should not hit semi-invulnerable targets even if user is Poison-type", async () => {
|
||||
game.override.moveset(Moves.SWIFT);
|
||||
game.override.enemyMoveset(Moves.FLY);
|
||||
await game.classicMode.startBattle([ Species.TOXAPEX ]);
|
||||
|
||||
game.move.select(Moves.SWIFT);
|
||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
|
||||
});
|
||||
});
|
@ -1,12 +1,11 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
import { MoveResult } from "#app/field/pokemon";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Whirlwind", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -40,14 +39,11 @@ describe("Moves - Whirlwind", () => {
|
||||
await game.classicMode.startBattle([ Species.STARAPTOR ]);
|
||||
|
||||
const staraptor = game.scene.getPlayerPokemon()!;
|
||||
const whirlwind = allMoves[Moves.WHIRLWIND];
|
||||
vi.spyOn(whirlwind, "getFailedText");
|
||||
|
||||
game.move.select(move);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(staraptor.findTag((t) => t.tagType === BattlerTagType.FLYING)).toBeDefined();
|
||||
expect(whirlwind.getFailedText).toHaveBeenCalledOnce();
|
||||
expect(game.scene.getEnemyPokemon()!.getLastXMoves(1)[0].result).toBe(MoveResult.MISS);
|
||||
});
|
||||
});
|
||||
|
@ -9,7 +9,6 @@ import { BerryPhase } from "#app/phases/berry-phase";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
|
||||
|
||||
|
||||
describe("Moves - Wide Guard", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -44,15 +44,13 @@ describe("Reload", () => {
|
||||
.startingWave(10)
|
||||
.battleType("single")
|
||||
.startingLevel(100) // Avoid levelling up
|
||||
.enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents()
|
||||
.disableTrainerWaves()
|
||||
.moveset([Moves.KOWTOW_CLEAVE])
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
await game.dailyMode.startBattle();
|
||||
|
||||
// Transition from Wave 10 to Wave 11 in order to trigger biome switch
|
||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
game.onNextPrompt("SelectBiomePhase", Mode.OPTION_SELECT, () => {
|
||||
(game.scene.time as MockClock).overrideDelay = null;
|
||||
@ -79,15 +77,13 @@ describe("Reload", () => {
|
||||
.startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather
|
||||
.battleType("single")
|
||||
.startingLevel(100) // Avoid levelling up
|
||||
.enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents()
|
||||
.disableTrainerWaves()
|
||||
.moveset([Moves.KOWTOW_CLEAVE])
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle(); // Apparently daily mode would override the biome
|
||||
|
||||
// Transition from Wave 10 to Wave 11 in order to trigger biome switch
|
||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
await game.toNextWave();
|
||||
expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase");
|
||||
|
@ -85,6 +85,27 @@ export class OverridesHelper extends GameManagerHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the player (pokemon) to be a random fusion
|
||||
* @returns this
|
||||
*/
|
||||
enableStarterFusion(): this {
|
||||
vi.spyOn(Overrides, "STARTER_FUSION_OVERRIDE", "get").mockReturnValue(true);
|
||||
this.log("Player Pokemon is a random fusion!");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the player (pokemon) fusion species
|
||||
* @param species the fusion species to set
|
||||
* @returns this
|
||||
*/
|
||||
starterFusionSpecies(species: Species | number): this {
|
||||
vi.spyOn(Overrides, "STARTER_FUSION_SPECIES_OVERRIDE", "get").mockReturnValue(species);
|
||||
this.log(`Player Pokemon fusion species set to ${Species[species]} (=${species})!`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the player (pokemons) forms
|
||||
* @param forms the (pokemon) forms to set
|
||||
@ -232,6 +253,27 @@ export class OverridesHelper extends GameManagerHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the enemy (pokemon) to be a random fusion
|
||||
* @returns this
|
||||
*/
|
||||
enableEnemyFusion(): this {
|
||||
vi.spyOn(Overrides, "OPP_FUSION_OVERRIDE", "get").mockReturnValue(true);
|
||||
this.log("Enemy Pokemon is a random fusion!");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the enemy (pokemon) fusion species
|
||||
* @param species the fusion species to set
|
||||
* @returns this
|
||||
*/
|
||||
enemyFusionSpecies(species: Species | number): this {
|
||||
vi.spyOn(Overrides, "OPP_FUSION_SPECIES_OVERRIDE", "get").mockReturnValue(species);
|
||||
this.log(`Enemy Pokemon fusion species set to ${Species[species]} (=${species})!`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the enemy (pokemon) {@linkcode Abilities | ability}
|
||||
* @param ability the (pokemon) {@linkcode Abilities | ability} to set
|
||||
|
@ -212,5 +212,4 @@ export default class MockSprite implements MockGameObject {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -196,7 +196,6 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Clears out the current string stored in all arena effect texts */
|
||||
private clearText() {
|
||||
this.flyoutTextPlayer.text = "";
|
||||
|
@ -6,7 +6,6 @@ export const TOUCH_CONTROL_POSITIONS_LANDSCAPE = "touchControlPositionsLandscape
|
||||
export const TOUCH_CONTROL_POSITIONS_PORTRAIT = "touchControlPositionsPortrait";
|
||||
|
||||
|
||||
|
||||
type ControlPosition = { id: string, x: number, y: number };
|
||||
|
||||
type ConfigurationEventListeners = {
|
||||
|
@ -77,7 +77,6 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi
|
||||
this.settingsContainer.add(deleteText);
|
||||
|
||||
|
||||
|
||||
// Map the 'noKeyboard' layout options for easy access.
|
||||
this.layout["noKeyboard"].optionsContainer = optionsContainer;
|
||||
this.layout["noKeyboard"].label = label;
|
||||
|
@ -2951,7 +2951,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
}
|
||||
|
||||
|
||||
|
||||
setSpeciesDetails(species: PokemonSpecies, shiny?: boolean, formIndex?: integer, female?: boolean, variant?: Variant, abilityIndex?: integer, natureIndex?: integer, forSeen: boolean = false): void {
|
||||
const oldProps = species ? this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null;
|
||||
const oldAbilityIndex = this.abilityCursor > -1 ? this.abilityCursor : this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
||||
|
@ -87,6 +87,10 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
private moveAccuracyText: Phaser.GameObjects.Text;
|
||||
private moveCategoryIcon: Phaser.GameObjects.Sprite;
|
||||
private summaryPageTransitionContainer: Phaser.GameObjects.Container;
|
||||
private friendshipShadow: Phaser.GameObjects.Sprite;
|
||||
private friendshipText: Phaser.GameObjects.Text;
|
||||
private friendshipIcon: Phaser.GameObjects.Sprite;
|
||||
private friendshipOverlay: Phaser.GameObjects.Sprite;
|
||||
|
||||
private descriptionScrollTween: Phaser.Tweens.Tween | null;
|
||||
private moveCursorBlinkTimer: Phaser.Time.TimerEvent | null;
|
||||
@ -187,6 +191,25 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
this.candyCountText.setOrigin(0, 0);
|
||||
this.summaryContainer.add(this.candyCountText);
|
||||
|
||||
this.friendshipIcon = this.scene.add.sprite(13, -60, "friendship");
|
||||
this.friendshipIcon.setScale(0.8);
|
||||
this.summaryContainer.add(this.friendshipIcon);
|
||||
|
||||
this.friendshipOverlay = this.scene.add.sprite(13, -60, "friendship_overlay");
|
||||
this.friendshipOverlay.setScale(0.8);
|
||||
this.summaryContainer.add(this.friendshipOverlay);
|
||||
|
||||
this.friendshipShadow = this.scene.add.sprite(13, -60, "friendship");
|
||||
this.friendshipShadow.setTint(0x000000);
|
||||
this.friendshipShadow.setAlpha(0.50);
|
||||
this.friendshipShadow.setScale(0.8);
|
||||
this.friendshipShadow.setInteractive(new Phaser.Geom.Rectangle(0, 0, 16, 16), Phaser.Geom.Rectangle.Contains);
|
||||
this.summaryContainer.add(this.friendshipShadow);
|
||||
|
||||
this.friendshipText = addTextObject(this.scene, 20, -66, "x0", TextStyle.WINDOW_ALT, { fontSize: "76px" });
|
||||
this.friendshipText.setOrigin(0, 0);
|
||||
this.summaryContainer.add(this.friendshipText);
|
||||
|
||||
this.championRibbon = this.scene.add.image(88, -146, "champion_ribbon");
|
||||
this.championRibbon.setOrigin(0, 0);
|
||||
//this.championRibbon.setScale(0.8);
|
||||
@ -292,6 +315,7 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
this.candyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
|
||||
this.candyOverlay.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
|
||||
|
||||
|
||||
this.numberText.setText(Utils.padInt(this.pokemon.species.speciesId, 4));
|
||||
this.numberText.setColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
|
||||
this.numberText.setShadowColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
|
||||
@ -345,6 +369,15 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
|
||||
this.candyShadow.setCrop(0, 0, 16, candyCropY);
|
||||
|
||||
if (this.friendshipShadow.visible) {
|
||||
this.friendshipShadow.on("pointerover", () => this.scene.ui.showTooltip("", `${i18next.t("pokemonSummary:friendship")}`, true));
|
||||
this.friendshipShadow.on("pointerout", () => this.scene.ui.hideTooltip());
|
||||
}
|
||||
|
||||
this.friendshipText.setText(`${this.pokemon?.friendship || "0"} / 255`);
|
||||
|
||||
this.friendshipShadow.setCrop(0, 0, 16, 16 - (16 * ((this.pokemon?.friendship || 0) / 255)));
|
||||
|
||||
const doubleShiny = isFusion && this.pokemon.shiny && this.pokemon.fusionShiny;
|
||||
const baseVariant = !doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user