Merge remote-tracking branch 'rogue/beta' into status-ab-activate

This commit is contained in:
Dean 2025-02-26 00:12:59 -08:00
commit 9e97bcd683
17 changed files with 415 additions and 93 deletions

View File

@ -3448,8 +3448,6 @@ export default class BattleScene extends SceneBase {
for (const modifier of modifiers) {
this.addEnemyModifier(modifier, true, true);
}
this.currentBattle.trainer.genAI(party);
}
party.forEach((enemyPokemon: EnemyPokemon, i: number) => {

View File

@ -12,7 +12,7 @@ import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffect
import { Gender } from "./gender";
import type Move from "./move";
import { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
import type { ArenaTrapTag } from "./arena-tag";
import type { ArenaTrapTag, SuppressAbilitiesTag } from "./arena-tag";
import { ArenaTagSide } from "./arena-tag";
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "../modifier/modifier";
import { TerrainType } from "./terrain";
@ -2202,6 +2202,34 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr {
}
}
/**
* Generic class to add an arena tag upon switching in
*/
export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr {
private readonly tagType: ArenaTagType;
private readonly turnCount: number;
private readonly side?: ArenaTagSide;
private readonly quiet?: boolean;
private sourceId: number;
constructor(tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) {
super(false);
this.tagType = tagType;
this.turnCount = turnCount;
this.side = side;
this.quiet = quiet;
}
public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
this.sourceId = pokemon.id;
if (!simulated) {
globalScene.arena.addTag(this.tagType, this.turnCount, undefined, this.sourceId, this.side, this.quiet);
}
return true;
}
}
export class PostSummonMessageAbAttr extends PostSummonAbAttr {
private messageFunc: (pokemon: Pokemon) => string;
@ -3007,6 +3035,26 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
}
}
/**
* Updates the active {@linkcode SuppressAbilitiesTag} when a pokemon with {@linkcode Abilities.NEUTRALIZING_GAS} leaves the field
*/
export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFieldAbAttr {
constructor() {
super(false);
}
public override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
if (!simulated) {
const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
if (suppressTag) {
suppressTag.onSourceLeave(globalScene.arena);
return true;
}
}
return simulated;
}
}
export class PreStatStageChangeAbAttr extends AbAttr {
applyPreStatStageChange(
pokemon: Pokemon | null,
@ -4758,21 +4806,6 @@ export class MoveAbilityBypassAbAttr extends AbAttr {
}
}
export class SuppressFieldAbilitiesAbAttr extends AbAttr {
constructor() {
super(false);
}
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ability = (args[0] as Ability);
if (!ability.hasAttr(UnsuppressableAbilityAbAttr) && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) {
cancelled.value = true;
return true;
}
return false;
}
}
export class AlwaysHitAbAttr extends AbAttr { }
/** Attribute for abilities that allow moves that make contact to ignore protection (i.e. Unseen Fist) */
@ -5185,6 +5218,10 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
showAbilityInstant: boolean = false,
messages: string[] = []
) {
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) {
return;
}
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) {
return;
@ -5478,12 +5515,10 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(
gainedMidTurn: boolean = false
) {
for (const passive of [ false, true ]) {
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) {
continue;
if (pokemon) {
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages);
globalScene.clearPhaseQueueSplice();
}
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages);
globalScene.clearPhaseQueueSplice();
}
}
@ -6012,10 +6047,10 @@ export function applyOnGainAbAttrs(pokemon: Pokemon, passive: boolean = false, s
}
/**
* Clears primal weather during the turn if {@linkcode pokemon}'s ability corresponds to one
* Clears primal weather/neutralizing gas during the turn if {@linkcode pokemon}'s ability corresponds to one
*/
export function applyOnLoseClearWeatherAbAttrs(pokemon: Pokemon, passive: boolean = false, simulated: boolean = false, ...args: any[]): void {
applySingleAbAttrs<PreLeaveFieldClearWeatherAbAttr>(pokemon, passive, PreLeaveFieldClearWeatherAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated);
export function applyOnLoseAbAttrs(pokemon: Pokemon, passive: boolean = false, simulated: boolean = false, ...args: any[]): void {
applySingleAbAttrs<PreLeaveFieldAbAttr>(pokemon, passive, PreLeaveFieldAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated);
}
function queueShowAbility(pokemon: Pokemon, passive: boolean): void {
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive));
@ -6914,12 +6949,11 @@ export function initAbilities() {
new Ability(Abilities.GORILLA_TACTICS, 8)
.attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8)
.attr(SuppressFieldAbilitiesAbAttr)
.attr(PostSummonAddArenaTagAbAttr, ArenaTagType.NEUTRALIZING_GAS, 0)
.attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.partial(), // A bunch of weird interactions with other abilities being suppressed then unsuppressed
.attr(NoTransformAbilityAbAttr),
new Ability(Abilities.PASTEL_VEIL, 8)
.attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)

View File

@ -8,7 +8,7 @@ import type Pokemon from "#app/field/pokemon";
import { HitResult, PokemonMove } from "#app/field/pokemon";
import { StatusEffect } from "#enums/status-effect";
import type { BattlerIndex } from "#app/battle";
import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability";
import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, ProtectStatAbAttr, applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "#app/data/ability";
import { Stat } from "#enums/stat";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
import i18next from "i18next";
@ -1221,6 +1221,76 @@ export class FairyLockTag extends ArenaTag {
}
/**
* Arena tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Neutralizing_Gas_(Ability) Neutralizing Gas}
*
* Keeps track of the number of pokemon on the field with Neutralizing Gas - If it drops to zero, the effect is ended and abilities are reactivated
*
* Additionally ends onLose abilities when it is activated
*/
export class SuppressAbilitiesTag extends ArenaTag {
private sourceCount: number;
private beingRemoved: boolean;
constructor(sourceId: number) {
super(ArenaTagType.NEUTRALIZING_GAS, 0, undefined, sourceId);
this.sourceCount = 1;
this.beingRemoved = false;
}
public override onAdd(arena: Arena): void {
const pokemon = this.getSourcePokemon();
if (pokemon) {
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
for (const fieldPokemon of globalScene.getField()) {
if (fieldPokemon && fieldPokemon.id !== pokemon.id) {
[ true, false ].forEach((passive) => applyOnLoseAbAttrs(fieldPokemon, passive));
}
}
}
}
public override onOverlap(arena: Arena): void {
this.sourceCount++;
}
public onSourceLeave(arena: Arena): void {
this.sourceCount--;
if (this.sourceCount <= 0) {
arena.removeTag(ArenaTagType.NEUTRALIZING_GAS);
} else if (this.sourceCount === 1) {
// With 1 source left, that pokemon's other abilities should reactivate
// This may be confusing for players but would be the most accurate gameplay-wise
// Could have a custom message that plays when a specific pokemon's NG ends? This entire thing exists due to passives after all
const setter = globalScene.getField().filter((p) => p && p.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false))[0];
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr));
}
}
public override onRemove(arena: Arena, quiet: boolean = false) {
this.beingRemoved = true;
if (!quiet) {
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
}
for (const pokemon of globalScene.getField()) {
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
if (pokemon && !pokemon.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false)) {
[ true, false ].forEach((passive) => applyOnGainAbAttrs(pokemon, passive));
}
}
}
public shouldApplyToSelf(): boolean {
return this.sourceCount > 1;
}
public isBeingRemoved() {
return this.beingRemoved;
}
}
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
switch (tagType) {
@ -1281,6 +1351,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove
return new GrassWaterPledgeTag(sourceId, side);
case ArenaTagType.FAIRY_LOCK:
return new FairyLockTag(turnCount, sourceId);
case ArenaTagType.NEUTRALIZING_GAS:
return new SuppressAbilitiesTag(sourceId);
default:
return null;
}

View File

@ -46,7 +46,7 @@ export const speciesEggMoves = {
[Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ],
[Species.KRABBY]: [ Moves.DIRE_CLAW, Moves.DRAGON_HAMMER, Moves.IVY_CUDGEL, Moves.JET_PUNCH ],
[Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.FUSION_FLARE, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
[Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ],
[Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.HEAT_WAVE, Moves.LUMINA_CRASH ],
[Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.SHADOW_SNEAK, Moves.BITTER_BLADE ],
[Species.LICKITUNG]: [ Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.SLACK_OFF, Moves.MAGICAL_TORQUE ],
[Species.KOFFING]: [ Moves.SCALD, Moves.RECOVER, Moves.BODY_PRESS, Moves.MALIGNANT_CHAIN ],
@ -59,7 +59,7 @@ export const speciesEggMoves = {
[Species.SCYTHER]: [ Moves.MIGHTY_CLEAVE, Moves.GEAR_GRIND, Moves.STORM_THROW, Moves.BITTER_BLADE ],
[Species.PINSIR]: [ Moves.HEADLONG_RUSH, Moves.LEECH_LIFE, Moves.CRUSH_GRIP, Moves.EXTREME_SPEED ],
[Species.TAUROS]: [ Moves.SWORDS_DANCE, Moves.FIRE_LASH, Moves.WICKED_TORQUE, Moves.COLLISION_COURSE ],
[Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.DRAGON_ASCENT, Moves.SURGING_STRIKES ],
[Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.KNOCK_OFF, Moves.DRAGON_ASCENT ],
[Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SCALD, Moves.SHELL_SMASH ],
[Species.DITTO]: [ Moves.MIMIC, Moves.SKETCH, Moves.METRONOME, Moves.IMPRISON ],
[Species.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ],
@ -76,7 +76,7 @@ export const speciesEggMoves = {
[Species.CHIKORITA]: [ Moves.SAPPY_SEED, Moves.STONE_AXE, Moves.DRAGON_DANCE, Moves.SPORE ],
[Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ],
[Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.TRIPLE_AXEL, Moves.SURGING_STRIKES ],
[Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.PLAY_ROUGH, Moves.SURGING_STRIKES ],
[Species.SENTRET]: [ Moves.TIDY_UP, Moves.FAKE_OUT, Moves.NUZZLE, Moves.EXTREME_SPEED ],
[Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.AEROBLAST, Moves.BOOMBURST ],
[Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.MAT_BLOCK, Moves.PARTING_SHOT, Moves.SPORE ],
@ -159,7 +159,7 @@ export const speciesEggMoves = {
[Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.STICKY_WEB, Moves.DECORATE, Moves.VICTORY_DANCE ],
[Species.ILLUMISE]: [ Moves.PARTING_SHOT, Moves.GLITZY_GLOW, Moves.POWDER, Moves.QUIVER_DANCE ],
[Species.GULPIN]: [ Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.STRENGTH_SAP ],
[Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.SWORDS_DANCE, Moves.OBSTRUCT, Moves.SURGING_STRIKES ],
[Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.GUNK_SHOT, Moves.OBSTRUCT, Moves.SURGING_STRIKES ],
[Species.WAILMER]: [ Moves.TAKE_HEART, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ],
[Species.NUMEL]: [ Moves.TRICK_ROOM, Moves.ENERGY_BALL, Moves.SLACK_OFF, Moves.BLUE_FLARE ],
[Species.TORKOAL]: [ Moves.MORNING_SUN, Moves.BURNING_BULWARK, Moves.BODY_PRESS, Moves.HYDRO_STEAM ],
@ -235,7 +235,7 @@ export const speciesEggMoves = {
[Species.RIOLU]: [ Moves.THUNDEROUS_KICK, Moves.TACHYON_CUTTER, Moves.TRIPLE_AXEL, Moves.SUNSTEEL_STRIKE ],
[Species.HIPPOPOTAS]: [ Moves.SHORE_UP, Moves.STONE_AXE, Moves.BULK_UP, Moves.SALT_CURE ],
[Species.SKORUPI]: [ Moves.COIL, Moves.DIRE_CLAW, Moves.CRABHAMMER, Moves.WICKED_BLOW ],
[Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_SPINNER, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ],
[Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.TRIPLE_AXEL, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ],
[Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.COIL, Moves.SAPPY_SEED ],
[Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.SPLISHY_SPLASH, Moves.FREEZE_DRY, Moves.OBLIVION_WING ],
[Species.MANTYKE]: [ Moves.SPLISHY_SPLASH, Moves.FREEZY_FROST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ],
@ -267,7 +267,7 @@ export const speciesEggMoves = {
[Species.PANSEAR]: [ Moves.NASTY_PLOT, Moves.HYDRO_STEAM, Moves.EARTH_POWER, Moves.ERUPTION ],
[Species.PANPOUR]: [ Moves.NASTY_PLOT, Moves.ENERGY_BALL, Moves.EARTH_POWER, Moves.WATER_SPOUT ],
[Species.MUNNA]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.LUNAR_BLESSING, Moves.MYSTICAL_POWER ],
[Species.PIDOVE]: [ Moves.GUNK_SHOT, Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ],
[Species.PIDOVE]: [ Moves.SLASH, Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ],
[Species.BLITZLE]: [ Moves.HORN_LEECH, Moves.SWORDS_DANCE, Moves.FLARE_BLITZ, Moves.BOLT_STRIKE ],
[Species.ROGGENROLA]: [ Moves.BODY_PRESS, Moves.CURSE, Moves.SHORE_UP, Moves.DIAMOND_STORM ],
[Species.WOOBAT]: [ Moves.ESPER_WING, Moves.STORED_POWER, Moves.MYSTICAL_FIRE, Moves.OBLIVION_WING ],
@ -282,7 +282,7 @@ export const speciesEggMoves = {
[Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ],
[Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.FIERY_DANCE, Moves.FLOWER_TRICK ],
[Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.SPLISHY_SPLASH, Moves.NO_RETREAT ],
[Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.HEADLONG_RUSH, Moves.FIRE_LASH, Moves.WICKED_BLOW ],
[Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.SUCKER_PUNCH, Moves.FIRE_LASH, Moves.HEADLONG_RUSH ],
[Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.ZIPPY_ZAP, Moves.HEADLONG_RUSH, Moves.PYRO_BALL ],
[Species.MARACTUS]: [ Moves.EARTH_POWER, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ],
[Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ],
@ -327,8 +327,8 @@ export const speciesEggMoves = {
[Species.DEINO]: [ Moves.FIERY_WRATH, Moves.ESPER_WING, Moves.SLUDGE_BOMB, Moves.FICKLE_BEAM ],
[Species.LARVESTA]: [ Moves.THUNDERBOLT, Moves.DAZZLING_GLEAM, Moves.EARTH_POWER, Moves.HYDRO_STEAM ],
[Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.MIGHTY_CLEAVE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.VIRIZION]: [ Moves.SAPPY_SEED, Moves.PSYBLADE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.KNOCK_OFF, Moves.VICTORY_DANCE ],
[Species.VIRIZION]: [ Moves.SAPPY_SEED, Moves.PSYBLADE, Moves.STONE_AXE, Moves.VICTORY_DANCE ],
[Species.TORNADUS]: [ Moves.SANDSEAR_STORM, Moves.PARTING_SHOT, Moves.SPLISHY_SPLASH, Moves.OBLIVION_WING ],
[Species.THUNDURUS]: [ Moves.SANDSEAR_STORM, Moves.HURRICANE, Moves.FROST_BREATH, Moves.ELECTRO_SHOT ],
[Species.RESHIRAM]: [ Moves.ENERGY_BALL, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ],
@ -342,7 +342,7 @@ export const speciesEggMoves = {
[Species.CHESPIN]: [ Moves.COMBAT_TORQUE, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ],
[Species.FENNEKIN]: [ Moves.TWIN_BEAM, Moves.FIERY_DANCE, Moves.THUNDERBOLT, Moves.SPARKLY_SWIRL ],
[Species.FROAKIE]: [ Moves.MOONBLAST, Moves.SHELL_SIDE_ARM, Moves.FIERY_WRATH, Moves.STEAM_ERUPTION ],
[Species.BUNNELBY]: [ Moves.DRAIN_PUNCH, Moves.TIDY_UP, Moves.FACADE, Moves.EXTREME_SPEED ],
[Species.BUNNELBY]: [ Moves.DRAIN_PUNCH, Moves.TIDY_UP, Moves.LANDS_WRATH, Moves.EXTREME_SPEED ],
[Species.FLETCHLING]: [ Moves.DRILL_RUN, Moves.BURNING_BULWARK, Moves.HEAD_SMASH, Moves.VOLT_TACKLE ],
[Species.SCATTERBUG]: [ Moves.FOCUS_BLAST, Moves.AFTER_YOU, Moves.DECORATE, Moves.BLIZZARD ],
[Species.LITLEO]: [ Moves.EARTH_POWER, Moves.NASTY_PLOT, Moves.BURNING_BULWARK, Moves.BLUE_FLARE ],
@ -372,7 +372,7 @@ export const speciesEggMoves = {
[Species.NOIBAT]: [ Moves.AEROBLAST, Moves.OVERDRIVE, Moves.NASTY_PLOT, Moves.CLANGING_SCALES ],
[Species.XERNEAS]: [ Moves.EARTH_POWER, Moves.SPRINGTIDE_STORM, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ],
[Species.YVELTAL]: [ Moves.SLUDGE_WAVE, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ],
[Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.HEAL_ORDER, Moves.CLANGOROUS_SOUL, Moves.DOUBLE_IRON_BASH ],
[Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.V_CREATE, Moves.CLANGOROUS_SOUL, Moves.HEAL_ORDER ],
[Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.FIERY_DANCE, Moves.SHORE_UP, Moves.GEOMANCY ],
[Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.FIERY_WRATH, Moves.SHELL_SMASH ],
[Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.SEARING_SHOT, Moves.THUNDERCLAP ],
@ -415,7 +415,7 @@ export const speciesEggMoves = {
[Species.JANGMO_O]: [ Moves.BODY_PRESS, Moves.SHELL_SIDE_ARM, Moves.SECRET_SWORD, Moves.GLAIVE_RUSH ],
[Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.SWORDS_DANCE, Moves.BOLT_STRIKE ],
[Species.TAPU_LELE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.HEAT_WAVE, Moves.EXPANDING_FORCE ],
[Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.DRAIN_PUNCH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ],
[Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.LANDS_WRATH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ],
[Species.TAPU_FINI]: [ Moves.SPRINGTIDE_STORM, Moves.EARTH_POWER, Moves.RECOVER, Moves.QUIVER_DANCE ],
[Species.COSMOG]: [ Moves.PHOTON_GEYSER, Moves.PRECIPICE_BLADES, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE ],
[Species.NIHILEGO]: [ Moves.STRENGTH_SAP, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.QUIVER_DANCE ],
@ -505,7 +505,7 @@ export const speciesEggMoves = {
[Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.ELECTRO_DRIFT ],
[Species.HISUI_QWILFISH]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.RECOVER, Moves.FISHIOUS_REND ],
[Species.HISUI_SNEASEL]: [ Moves.DRAIN_PUNCH, Moves.KNOCK_OFF, Moves.PSYCHIC_FANGS, Moves.TRIPLE_AXEL ],
[Species.HISUI_ZORUA]: [ Moves.MOONBLAST, Moves.HYPER_VOICE, Moves.PARTING_SHOT, Moves.BLOOD_MOON ],
[Species.HISUI_ZORUA]: [ Moves.MOONBLAST, Moves.SECRET_SWORD, Moves.PARTING_SHOT, Moves.BLOOD_MOON ],
[Species.SPRIGATITO]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.SUCKER_PUNCH, Moves.WICKED_BLOW ],
[Species.FUECOCO]: [ Moves.ALLURING_VOICE, Moves.SLACK_OFF, Moves.OVERDRIVE, Moves.MOONGEIST_BEAM ],
@ -538,7 +538,7 @@ export const speciesEggMoves = {
[Species.CYCLIZAR]: [ Moves.PARTING_SHOT, Moves.FIRE_LASH, Moves.MAGICAL_TORQUE, Moves.GLAIVE_RUSH ],
[Species.ORTHWORM]: [ Moves.SIZZLY_SLIDE, Moves.COIL, Moves.BODY_PRESS, Moves.SHORE_UP ],
[Species.GLIMMET]: [ Moves.CALM_MIND, Moves.GIGA_DRAIN, Moves.FIERY_DANCE, Moves.MALIGNANT_CHAIN ],
[Species.GREAVARD]: [ Moves.SHADOW_BONE, Moves.YAWN, Moves.SHORE_UP, Moves.COLLISION_COURSE ],
[Species.GREAVARD]: [ Moves.SHADOW_BONE, Moves.SIZZLY_SLIDE, Moves.SHORE_UP, Moves.COLLISION_COURSE ],
[Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.FLOATY_FALL, Moves.VICTORY_DANCE ],
[Species.CETODDLE]: [ Moves.ZING_ZAP, Moves.HIGH_HORSEPOWER, Moves.SLACK_OFF, Moves.DRAGON_DANCE ],
[Species.VELUZA]: [ Moves.PSYBLADE, Moves.LEAF_BLADE, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE ],
@ -550,7 +550,7 @@ export const speciesEggMoves = {
[Species.FLUTTER_MANE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.MOONGEIST_BEAM ],
[Species.SLITHER_WING]: [ Moves.MIGHTY_CLEAVE, Moves.THUNDEROUS_KICK, Moves.FIRE_LASH, Moves.VICTORY_DANCE ],
[Species.SANDY_SHOCKS]: [ Moves.MORNING_SUN, Moves.ICE_BEAM, Moves.NASTY_PLOT, Moves.THUNDERCLAP ],
[Species.IRON_TREADS]: [ Moves.FUSION_BOLT, Moves.BULK_UP, Moves.SHORE_UP, Moves.SUNSTEEL_STRIKE ],
[Species.IRON_TREADS]: [ Moves.FUSION_BOLT, Moves.SHIFT_GEAR, Moves.SHORE_UP, Moves.SUNSTEEL_STRIKE ],
[Species.IRON_BUNDLE]: [ Moves.EARTH_POWER, Moves.SPLISHY_SPLASH, Moves.VOLT_SWITCH, Moves.NASTY_PLOT ],
[Species.IRON_HANDS]: [ Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS, Moves.ICE_HAMMER ],
[Species.IRON_JUGULIS]: [ Moves.FIERY_WRATH, Moves.ROOST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ],
@ -562,7 +562,7 @@ export const speciesEggMoves = {
[Species.CHIEN_PAO]: [ Moves.KNOCK_OFF, Moves.PARTING_SHOT, Moves.TRIPLE_AXEL, Moves.BITTER_BLADE ],
[Species.TING_LU]: [ Moves.SHORE_UP, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED, Moves.PRECIPICE_BLADES ],
[Species.CHI_YU]: [ Moves.FIERY_WRATH, Moves.HYDRO_STEAM, Moves.MORNING_SUN, Moves.BLUE_FLARE ],
[Species.ROARING_MOON]: [ Moves.FIRE_LASH, Moves.DRAGON_HAMMER, Moves.SUCKER_PUNCH, Moves.WICKED_BLOW ],
[Species.ROARING_MOON]: [ Moves.FIRE_LASH, Moves.DRAGON_HAMMER, Moves.METEOR_MASH, Moves.DRAGON_ASCENT ],
[Species.IRON_VALIANT]: [ Moves.PLASMA_FISTS, Moves.NO_RETREAT, Moves.SECRET_SWORD, Moves.MAGICAL_TORQUE ],
[Species.KORAIDON]: [ Moves.SUNSTEEL_STRIKE, Moves.SOLAR_BLADE, Moves.DRAGON_DARTS, Moves.BITTER_BLADE ],
[Species.MIRAIDON]: [ Moves.ICE_BEAM, Moves.CLANGOROUS_SOUL, Moves.CORE_ENFORCER, Moves.RISING_VOLTAGE ],
@ -577,7 +577,7 @@ export const speciesEggMoves = {
[Species.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.MORNING_SUN, Moves.ELECTRO_DRIFT ],
[Species.IRON_BOULDER]: [ Moves.PSYBLADE, Moves.KOWTOW_CLEAVE, Moves.STONE_AXE, Moves.BITTER_BLADE ],
[Species.IRON_CROWN]: [ Moves.NASTY_PLOT, Moves.SECRET_SWORD, Moves.PSYSTRIKE, Moves.ELECTRO_DRIFT ],
[Species.TERAPAGOS]: [ Moves.MOONBLAST, Moves.RECOVER, Moves.ICE_BEAM, Moves.SHELL_SMASH ],
[Species.TERAPAGOS]: [ Moves.MOONBLAST, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE, Moves.RECOVER ],
[Species.PECHARUNT]: [ Moves.TAKE_HEART, Moves.BODY_PRESS, Moves.SAPPY_SEED, Moves.ASTRAL_BARRAGE ],
[Species.PALDEA_TAUROS]: [ Moves.NO_RETREAT, Moves.BLAZING_TORQUE, Moves.AQUA_STEP, Moves.THUNDEROUS_KICK ],
[Species.PALDEA_WOOPER]: [ Moves.STONE_AXE, Moves.RECOVER, Moves.BANEFUL_BUNKER, Moves.BARB_BARRAGE ],

View File

@ -555,7 +555,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.FLUTTER_MANE]: { 0: Abilities.DAZZLING },
[Species.SLITHER_WING]: { 0: Abilities.SCRAPPY },
[Species.SANDY_SHOCKS]: { 0: Abilities.ELECTRIC_SURGE },
[Species.IRON_TREADS]: { 0: Abilities.STEELY_SPIRIT },
[Species.IRON_TREADS]: { 0: Abilities.DAUNTLESS_SHIELD },
[Species.IRON_BUNDLE]: { 0: Abilities.SNOW_WARNING },
[Species.IRON_HANDS]: { 0: Abilities.IRON_FIST },
[Species.IRON_JUGULIS]: { 0: Abilities.LIGHTNING_ROD },
@ -567,7 +567,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.CHIEN_PAO]: { 0: Abilities.INTIMIDATE },
[Species.TING_LU]: { 0: Abilities.STAMINA },
[Species.CHI_YU]: { 0: Abilities.BERSERK },
[Species.ROARING_MOON]: { 0: Abilities.TOUGH_CLAWS },
[Species.ROARING_MOON]: { 0: Abilities.INTIMIDATE },
[Species.IRON_VALIANT]: { 0: Abilities.NEUROFORCE },
[Species.KORAIDON]: { 0: Abilities.OPPORTUNIST },
[Species.MIRAIDON]: { 0: Abilities.OPPORTUNIST },
@ -582,7 +582,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.RAGING_BOLT]: { 0: Abilities.BEAST_BOOST },
[Species.IRON_BOULDER]: { 0: Abilities.SHARPNESS },
[Species.IRON_CROWN]: { 0: Abilities.SHARPNESS },
[Species.TERAPAGOS]: { 0: Abilities.SOUL_HEART },
[Species.TERAPAGOS]: { 0: Abilities.SHIELD_DUST },
[Species.PECHARUNT]: { 0: Abilities.TOXIC_CHAIN },
[Species.PALDEA_TAUROS]: { 0: Abilities.ADAPTABILITY },
[Species.PALDEA_WOOPER]: { 0: Abilities.THICK_FAT },

View File

@ -290,7 +290,7 @@ export const speciesStarterCosts = {
[Species.SHAYMIN]: 6,
[Species.ARCEUS]: 9,
[Species.VICTINI]: 7,
[Species.VICTINI]: 6,
[Species.SNIVY]: 3,
[Species.TEPIG]: 3,
[Species.OSHAWOTT]: 3,
@ -611,7 +611,7 @@ export const speciesStarterCosts = {
[Species.RAGING_BOLT]: 7,
[Species.IRON_BOULDER]: 7,
[Species.IRON_CROWN]: 7,
[Species.TERAPAGOS]: 8,
[Species.TERAPAGOS]: 9,
[Species.PECHARUNT]: 6,
[Species.PALDEA_TAUROS]: 5,
[Species.PALDEA_WOOPER]: 3,

View File

@ -7807,11 +7807,12 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr {
return false;
}
target.summonData.abilitySuppressed = true;
globalScene.arena.triggerWeatherBasedFormChangesToNormal();
globalScene.queueMessage(i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) }));
target.suppressAbility();
globalScene.arena.triggerWeatherBasedFormChangesToNormal();
return true;
}

View File

@ -29,4 +29,5 @@ export enum ArenaTagType {
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
FAIRY_LOCK = "FAIRY_LOCK",
NEUTRALIZING_GAS = "NEUTRALIZING_GAS"
}

View File

@ -588,8 +588,8 @@ export class Arena {
// creates a new tag object
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side);
if (newTag) {
this.tags.push(newTag);
newTag.onAdd(this, quiet);
this.tags.push(newTag);
const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {};

View File

@ -63,8 +63,59 @@ import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags";
import { WeatherType } from "#enums/weather-type";
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
import type { Ability, AbAttr } from "#app/data/ability";
import { StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr, applyOnGainAbAttrs, PreLeaveFieldAbAttr, applyPreLeaveFieldAbAttrs, applyOnLoseClearWeatherAbAttrs } from "#app/data/ability";
import {
StatMultiplierAbAttr,
BlockCritAbAttr,
BonusCritAbAttr,
BypassBurnDamageReductionAbAttr,
FieldPriorityMoveImmunityAbAttr,
IgnoreOpponentStatStagesAbAttr,
MoveImmunityAbAttr,
PreDefendFullHpEndureAbAttr,
ReceivedMoveDamageMultiplierAbAttr,
StabBoostAbAttr,
StatusEffectImmunityAbAttr,
TypeImmunityAbAttr,
WeightMultiplierAbAttr,
allAbilities,
applyAbAttrs,
applyStatMultiplierAbAttrs,
applyPreApplyBattlerTagAbAttrs,
applyPreAttackAbAttrs,
applyPreDefendAbAttrs,
applyPreSetStatusAbAttrs,
UnsuppressableAbilityAbAttr,
NoFusionAbilityAbAttr,
MultCritAbAttr,
IgnoreTypeImmunityAbAttr,
DamageBoostAbAttr,
IgnoreTypeStatusEffectImmunityAbAttr,
ConditionalCritAbAttr,
applyFieldStatMultiplierAbAttrs,
FieldMultiplyStatAbAttr,
AddSecondStrikeAbAttr,
UserFieldStatusEffectImmunityAbAttr,
UserFieldBattlerTagImmunityAbAttr,
BattlerTagImmunityAbAttr,
MoveTypeChangeAbAttr,
FullHpResistTypeAbAttr,
applyCheckTrappedAbAttrs,
CheckTrappedAbAttr,
InfiltratorAbAttr,
AlliedFieldDamageReductionAbAttr,
PostDamageAbAttr,
applyPostDamageAbAttrs,
CommanderAbAttr,
applyPostItemLostAbAttrs,
PostItemLostAbAttr,
applyOnGainAbAttrs,
PreLeaveFieldAbAttr,
applyPreLeaveFieldAbAttrs,
applyOnLoseAbAttrs,
PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr
} from "#app/data/ability";
import type PokemonData from "#app/system/pokemon-data";
import { BattlerIndex } from "#app/battle";
import { Mode } from "#app/ui/ui";
@ -1488,7 +1539,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param ability New Ability
*/
public setTempAbility(ability: Ability, passive: boolean = false): void {
applyOnLoseClearWeatherAbAttrs(this, passive);
applyOnLoseAbAttrs(this, passive);
if (passive) {
this.summonData.passiveAbility = ability.id;
} else {
@ -1497,6 +1548,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyOnGainAbAttrs(this, passive);
}
/**
* Suppresses an ability and calls its onlose attributes
*/
public suppressAbility() {
[ true, false ].forEach((passive) => applyOnLoseAbAttrs(this, passive));
this.summonData.abilitySuppressed = true;
}
/**
* Checks if a pokemon has a passive either from:
* - bought with starter candy
@ -1554,17 +1613,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (this.summonData?.abilitySuppressed && !ability.hasAttr(UnsuppressableAbilityAbAttr)) {
return false;
}
if (this.isOnField() && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) {
const suppressed = new Utils.BooleanHolder(false);
globalScene.getField(true).filter(p => p !== this).map(p => {
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, false, suppressed, [ ability ]));
}
if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) {
p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, false, suppressed, [ ability ]));
}
});
if (suppressed.value) {
const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
if (this.isOnField() && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) {
const thisAbilitySuppressing = ability.hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr);
const hasSuppressingAbility = this.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false);
// Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas
// (Balance decided that the other ability of a neutralizing gas pokemon should not be neutralized)
// If the ability itself is neutralizing gas, don't suppress it (handled through arena tag)
const unsuppressable = ability.hasAttr(UnsuppressableAbilityAbAttr) || thisAbilitySuppressing || (hasSuppressingAbility && !suppressAbilitiesTag.shouldApplyToSelf());
if (!unsuppressable) {
return false;
}
}

View File

@ -253,7 +253,10 @@ export class EncounterPhase extends BattlePhase {
globalScene.getEnemyField().forEach(enemy => {
overrideHeldItems(enemy, false);
});
}
if (battle.battleType === BattleType.TRAINER) {
globalScene.currentBattle.trainer!.genAI(globalScene.getEnemyParty());
}
globalScene.ui.setMode(Mode.MESSAGE).then(() => {

View File

@ -629,6 +629,8 @@ export class DropDown extends Phaser.GameObjects.Container {
}
}
}
this.onChange();
}
}

View File

@ -86,6 +86,15 @@ export class FilterBar extends Phaser.GameObjects.Container {
return this.dropDowns[this.columns.indexOf(col)];
}
/**
* Get the DropDownColumn associated to a given index
* @param index the index of the column to retrieve
* @returns the associated DropDownColumn if it exists, undefined otherwise
*/
public getColumn(index: number): DropDownColumn {
return this.columns[index];
}
/**
* Highlight the labels of the FilterBar if the filters are different from their default values
*/
@ -185,6 +194,11 @@ export class FilterBar extends Phaser.GameObjects.Container {
return this.getFilter(col).getVals();
}
public resetSelection(col: DropDownColumn): void {
this.dropDowns[col].resetToDefault();
this.updateFilterLabels();
}
setValsToDefault(): void {
for (const dropDown of this.dropDowns) {
dropDown.resetToDefault();

View File

@ -898,16 +898,11 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (this.filterMode && this.filterBar.openDropDown) {
// CANCEL with a filter menu open > close it
this.filterBar.toggleDropDown(this.filterBarCursor);
// if there are possible pokemon go the first one of the list
if (numberOfStarters > 0) {
this.setFilterMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
success = true;
} else if (this.filterMode && !this.filterBar.getFilter(this.filterBarCursor).hasDefaultValues()) {
this.filterBar.resetSelection(this.filterBarCursor);
this.updateStarters();
success = true;
} else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) {
this.filterText.resetSelection(this.filterTextCursor);
success = true;

View File

@ -1080,10 +1080,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
/**
* Set the selections for all filters to their default starting value
*/
resetFilters() : void {
public resetFilters(): void {
this.filterBar.setValsToDefault();
this.resetCaughtDropdown();
}
/**
* Set default value for the caught dropdown, which only shows caught mons
*/
public resetCaughtDropdown(): void {
const caughtDropDown: DropDown = this.filterBar.getFilter(DropDownColumn.CAUGHT);
this.filterBar.setValsToDefault();
caughtDropDown.resetToDefault();
// initial setting, in caught filter, select the options excluding the uncaught option
for (let i = 0; i < caughtDropDown.options.length; i++) {
@ -1323,16 +1331,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (this.filterMode && this.filterBar.openDropDown) {
// CANCEL with a filter menu open > close it
this.filterBar.toggleDropDown(this.filterBarCursor);
// if there are possible starters go the first one of the list
if (numberOfStarters > 0) {
this.setFilterMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
success = true;
} else if (this.filterMode && !this.filterBar.getFilter(this.filterBar.getColumn(this.filterBarCursor)).hasDefaultValues()) {
if (this.filterBar.getColumn(this.filterBarCursor) === DropDownColumn.CAUGHT) {
this.resetCaughtDropdown();
} else {
this.filterBar.resetSelection(this.filterBarCursor);
}
this.updateStarters();
success = true;
} else if (this.statsMode) {
this.toggleStatsMode(false);
success = true;

View File

@ -0,0 +1,138 @@
import { BattlerIndex } from "#app/battle";
import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Neutralizing Gas", () => {
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.SPLASH ])
.ability(Abilities.NEUTRALIZING_GAS)
.battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH);
});
it("should prevent other abilities from activating", async () => {
game.override.enemyAbility(Abilities.INTIMIDATE);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
// Intimidate is suppressed, so the attack stat should not be lowered
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
});
it("should allow the user's passive to activate", async () => {
game.override.passiveAbility(Abilities.INTREPID_SWORD);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1);
});
it.todo("should activate before other abilities", async () => {
game.override.enemySpecies(Species.ACCELGOR)
.enemyLevel(100)
.enemyAbility(Abilities.INTIMIDATE);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
// Intimidate is suppressed even when the user's speed is lower
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
});
it("should activate other abilities when removed", async () => {
game.override.enemyAbility(Abilities.INTREPID_SWORD)
.enemyPassiveAbility(Abilities.DAUNTLESS_SHIELD)
.enemyMoveset(Moves.ENTRAINMENT);
await game.classicMode.startBattle([ Species.FEEBAS ]);
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(0);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Enemy removes user's ability, so both abilities are activated
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(1);
expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(1);
});
it("should not activate the user's other ability when removed", async () => {
game.override.passiveAbility(Abilities.INTIMIDATE)
.enemyMoveset(Moves.ENTRAINMENT);
await game.classicMode.startBattle([ Species.FEEBAS ]);
// Neutralising gas user's passive is still active
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Intimidate did not reactivate after neutralizing gas was removed
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
});
it("should only deactivate when all setters are off the field", async () => {
game.override.enemyMoveset([ Moves.ENTRAINMENT, Moves.SPLASH ])
.battleType("double");
await game.classicMode.startBattle([ Species.ACCELGOR, Species.ACCELGOR ]);
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); // Now one neut gas user is left
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined(); // No neut gas users are left
});
it("should deactivate when suppressed by gastro acid", async () => {
game.override.enemyMoveset(Moves.GASTRO_ACID);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
});
});

View File

@ -223,14 +223,14 @@ describe("Mystery Encounter Utils", () => {
});
it("excludes species from search", () => {
// Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian
const result = getRandomSpeciesByStarterCost(9, [ Species.KORAIDON, Species.MIRAIDON, Species.ARCEUS, Species.RAYQUAZA, Species.KYOGRE, Species.GROUDON ]);
// Only 9 tiers are: Kyogre, Groudon, Rayquaza, Arceus, Zacian, Koraidon, Miraidon, Terapagos
const result = getRandomSpeciesByStarterCost(9, [ Species.KYOGRE, Species.GROUDON, Species.RAYQUAZA, Species.ARCEUS, Species.KORAIDON, Species.MIRAIDON, Species.TERAPAGOS ]);
const pokeSpecies = getPokemonSpecies(result);
expect(pokeSpecies.speciesId).toBe(Species.ZACIAN);
});
it("gets species of specified types", () => {
// Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian
// Only 9 tiers are: Kyogre, Groudon, Rayquaza, Arceus, Zacian, Koraidon, Miraidon, Terapagos
const result = getRandomSpeciesByStarterCost(9, undefined, [ Type.GROUND ]);
const pokeSpecies = getPokemonSpecies(result);
expect(pokeSpecies.speciesId).toBe(Species.GROUDON);