diff --git a/src/data/ability.ts b/src/data/ability.ts index 8eeb5af52b8e..ff0a64437bb0 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -848,7 +848,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { if (simulated) { return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined); } else { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + return pokemon.scene.arena.trySetTerrain(this.terrainType, pokemon); } } @@ -1031,7 +1031,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { if (simulated) { return pokemon.scene.arena.weather?.weatherType !== this.weatherType; } - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + return pokemon.scene.arena.trySetWeather(this.weatherType, pokemon); } return false; @@ -2294,7 +2294,7 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { if (simulated) { return pokemon.scene.arena.weather?.weatherType !== this.weatherType; } else { - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + return pokemon.scene.arena.trySetWeather(this.weatherType, pokemon); } } @@ -2315,7 +2315,7 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { if (simulated) { return pokemon.scene.arena.terrain?.terrainType !== this.terrainType; } else { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + return pokemon.scene.arena.trySetTerrain(this.terrainType, pokemon); } } } @@ -2658,7 +2658,7 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } if (turnOffWeather) { - pokemon.scene.arena.trySetWeather(WeatherType.NONE, false); + pokemon.scene.arena.trySetWeather(WeatherType.NONE, pokemon); return true; } @@ -3768,7 +3768,7 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { if (simulated) { return pokemon.scene.arena.weather?.weatherType !== this.weatherType; } else { - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + return pokemon.scene.arena.trySetWeather(this.weatherType, pokemon); } } @@ -3789,7 +3789,7 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { if (simulated) { return pokemon.scene.arena.terrain?.terrainType !== this.terrainType; } else { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + return pokemon.scene.arena.trySetTerrain(this.terrainType, pokemon); } } } @@ -4190,7 +4190,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { } if (turnOffWeather) { - pokemon.scene.arena.trySetWeather(WeatherType.NONE, false); + pokemon.scene.arena.trySetWeather(WeatherType.NONE); return true; } diff --git a/src/data/move.ts b/src/data/move.ts index 6e350315e655..818212848122 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2641,7 +2641,7 @@ export class WeatherChangeAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - return user.scene.arena.trySetWeather(this.weatherType, true); + return user.scene.arena.trySetWeather(this.weatherType, user); } getCondition(): MoveConditionFunc { @@ -2660,7 +2660,7 @@ export class ClearWeatherAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.scene.arena.weather?.weatherType === this.weatherType) { - return user.scene.arena.trySetWeather(WeatherType.NONE, true); + return user.scene.arena.trySetWeather(WeatherType.NONE, user); } return false; @@ -2677,7 +2677,7 @@ export class TerrainChangeAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - return user.scene.arena.trySetTerrain(this.terrainType, true, true); + return user.scene.arena.trySetTerrain(this.terrainType, user, true); } getCondition(): MoveConditionFunc { @@ -2696,7 +2696,7 @@ export class ClearTerrainAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - return user.scene.arena.trySetTerrain(TerrainType.NONE, true, true); + return user.scene.arena.trySetTerrain(TerrainType.NONE, user, true); } } @@ -5998,7 +5998,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { export class ChillyReceptionAttr extends ForceSwitchOutAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - user.scene.arena.trySetWeather(WeatherType.SNOW, true); + user.scene.arena.trySetWeather(WeatherType.SNOW, user); return super.apply(user, target, move, args); } diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 5794277ffe18..9be2ada06de9 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -115,7 +115,7 @@ export const FieryFalloutEncounter: MysteryEncounter = // Load animations/sfx for Volcarona moves loadCustomMovesForEncounter(scene, [ Moves.FIRE_SPIN, Moves.QUIVER_DANCE ]); - scene.arena.trySetWeather(WeatherType.SUNNY, true); + scene.arena.trySetWeather(WeatherType.SUNNY); // TODO: This breaks the duration encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName()); diff --git a/src/field/arena.ts b/src/field/arena.ts index abc2b89569ca..fe162a99aa55 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -31,6 +31,7 @@ import { Abilities } from "#enums/abilities"; import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms"; import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; +import { FieldEffectModifier } from "#app/modifier/modifier"; export class Arena { public scene: BattleScene; @@ -253,7 +254,7 @@ export class Arena { * @param hasPokemonSource boolean if the new weather is from a pokemon * @returns true if new weather set, false if no weather provided or attempting to set the same weather as currently in use */ - trySetWeather(weather: WeatherType, hasPokemonSource: boolean): boolean { + trySetWeather(weather: WeatherType, user?: Pokemon): boolean { if (Overrides.WEATHER_OVERRIDE) { return this.trySetWeatherOverride(Overrides.WEATHER_OVERRIDE); } @@ -264,7 +265,14 @@ export class Arena { const oldWeatherType = this.weather?.weatherType || WeatherType.NONE; - this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null; + const weatherDuration = new Utils.NumberHolder(0); + + if (!Utils.isNullOrUndefined(user)) { + weatherDuration.value = 5; + this.scene.applyModifier(FieldEffectModifier, user.isPlayer(), user, weatherDuration); + } + + this.weather = weather ? new Weather(weather, weatherDuration.value) : null; this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType!, this.weather?.turnsLeft!)); // TODO: is this bang correct? if (this.weather) { @@ -312,14 +320,21 @@ export class Arena { }); } - trySetTerrain(terrain: TerrainType, hasPokemonSource: boolean, ignoreAnim: boolean = false): boolean { + trySetTerrain(terrain: TerrainType, user?: Pokemon, ignoreAnim: boolean = false): boolean { if (this.terrain?.terrainType === (terrain || undefined)) { return false; } const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE; - this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null; + const terrainDuration = new Utils.NumberHolder(0); + + if (!Utils.isNullOrUndefined(user)) { + terrainDuration.value = 5; + this.scene.applyModifier(FieldEffectModifier, user.isPlayer(), user, terrainDuration); + } + + this.terrain = terrain ? new Terrain(terrain, terrainDuration.value) : null; this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType, this.terrain?.terrainType!, this.terrain?.turnsLeft!)); // TODO: are those bangs correct? if (this.terrain) { @@ -683,9 +698,9 @@ export class Arena { resetArenaEffects(): void { // Don't reset weather if a Biome's permanent weather is active if (this.weather?.turnsLeft !== 0) { - this.trySetWeather(WeatherType.NONE, false); + this.trySetWeather(WeatherType.NONE); } - this.trySetTerrain(TerrainType.NONE, false, true); + this.trySetTerrain(TerrainType.NONE, undefined, true); this.removeAllTags(); } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index dfa46ce36677..b959913aa2a0 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -11,7 +11,7 @@ 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, TempExtraModifierModifier + 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, TempExtraModifierModifier, FieldEffectModifier } from "#app/modifier/modifier"; import { ModifierTier } from "#app/modifier/modifier-tier"; import Overrides from "#app/overrides"; @@ -1490,6 +1490,8 @@ export const modifierTypes = { return new BerryModifierType(randBerryType); }), + DOMAIN_EXPANDER: () => new ModifierType("modifierType:ModifierType.DOMAIN_EXPANDER", "domain_expander", (type, args) => new FieldEffectModifier(type, (args[0] as Pokemon).id)), + TM_COMMON: () => new TmModifierTypeGenerator(ModifierTier.COMMON), TM_GREAT: () => new TmModifierTypeGenerator(ModifierTier.GREAT), TM_ULTRA: () => new TmModifierTypeGenerator(ModifierTier.ULTRA), diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 0891262649c5..e01f95e3f088 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1995,6 +1995,29 @@ export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier { } } +export class FieldEffectModifier extends PokemonHeldItemModifier { + constructor(type: ModifierType, pokemonId: number, stackCount?: number) { + super(type, pokemonId, stackCount); + } + + override apply(_pokemon: Pokemon, fieldDuration: NumberHolder): boolean { + fieldDuration.value += 2 * this.stackCount; + return true; + } + + override matchType(modifier: Modifier): boolean { + return modifier instanceof FieldEffectModifier; + } + + override clone(): FieldEffectModifier { + return new FieldEffectModifier(this.type, this.pokemonId, this.stackCount); + } + + override getMaxHeldItemCount(_pokemon?: Pokemon): number { + return 2; + } +} + export abstract class ConsumablePokemonModifier extends ConsumableModifier { public pokemonId: number; diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index c4d919c03258..85d9cc618304 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -541,7 +541,7 @@ export class EncounterPhase extends BattlePhase { */ trySetWeatherIfNewBiome(): void { if (!this.loaded) { - this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); + this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena)); } } } diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index 910306b76ad5..04fb684f9eef 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -44,6 +44,6 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { * Set biome weather. */ trySetWeatherIfNewBiome(): void { - this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); + this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena)); } } diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 60a2e6600db8..9e69a3b69643 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -52,12 +52,12 @@ export class TurnEndPhase extends FieldPhase { this.scene.arena.lapseTags(); if (this.scene.arena.weather && !this.scene.arena.weather.lapse()) { - this.scene.arena.trySetWeather(WeatherType.NONE, false); + this.scene.arena.trySetWeather(WeatherType.NONE); this.scene.arena.triggerWeatherBasedFormChangesToNormal(); } if (this.scene.arena.terrain && !this.scene.arena.terrain.lapse()) { - this.scene.arena.trySetTerrain(TerrainType.NONE, false); + this.scene.arena.trySetTerrain(TerrainType.NONE); } this.end(); diff --git a/src/test/abilities/forecast.test.ts b/src/test/abilities/forecast.test.ts index 18d43a67a9d9..a0fa13366350 100644 --- a/src/test/abilities/forecast.test.ts +++ b/src/test/abilities/forecast.test.ts @@ -172,7 +172,7 @@ describe("Abilities - Forecast", () => { expect(castform.formIndex).toBe(SNOWY_FORM); - game.scene.arena.trySetWeather(WeatherType.FOG, false); + game.scene.arena.trySetWeather(WeatherType.FOG); game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to("TurnStartPhase"); diff --git a/src/test/moves/dive.test.ts b/src/test/moves/dive.test.ts index b60416d7740b..0f2e6b994140 100644 --- a/src/test/moves/dive.test.ts +++ b/src/test/moves/dive.test.ts @@ -124,7 +124,7 @@ describe("Moves - Dive", () => { await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnStartPhase", false); - game.scene.arena.trySetWeather(WeatherType.HARSH_SUN, false); + game.scene.arena.trySetWeather(WeatherType.HARSH_SUN); await game.phaseInterceptor.to("MoveEndPhase"); expect(playerPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);