[Ability] Add form change support for Flower Gift (#3941)
* add form change support for flower gift * fix nits
This commit is contained in:
parent
3bcee779e2
commit
64368b62bc
|
@ -2428,7 +2428,7 @@ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAtt
|
|||
|
||||
/**
|
||||
* Triggers weather-based form change when summoned into an active weather.
|
||||
* Used by Forecast.
|
||||
* Used by Forecast and Flower Gift.
|
||||
* @extends PostSummonAbAttr
|
||||
*/
|
||||
export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr {
|
||||
|
@ -2451,7 +2451,10 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr {
|
|||
* @returns whether the form change was triggered
|
||||
*/
|
||||
applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||
if (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST) {
|
||||
const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST);
|
||||
const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT);
|
||||
|
||||
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
||||
if (simulated) {
|
||||
return simulated;
|
||||
}
|
||||
|
@ -3124,37 +3127,41 @@ export class PostWeatherChangeAbAttr extends AbAttr {
|
|||
|
||||
/**
|
||||
* Triggers weather-based form change when weather changes.
|
||||
* Used by Forecast.
|
||||
* Used by Forecast and Flower Gift.
|
||||
* @extends PostWeatherChangeAbAttr
|
||||
*/
|
||||
export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr {
|
||||
private ability: Abilities;
|
||||
private formRevertingWeathers: WeatherType[];
|
||||
|
||||
constructor(ability: Abilities) {
|
||||
constructor(ability: Abilities, formRevertingWeathers: WeatherType[]) {
|
||||
super(false);
|
||||
|
||||
this.ability = ability;
|
||||
this.formRevertingWeathers = formRevertingWeathers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the
|
||||
* weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges}
|
||||
* @param {Pokemon} pokemon the Pokemon that changed the weather
|
||||
* @param {Pokemon} pokemon the Pokemon with this ability
|
||||
* @param passive n/a
|
||||
* @param weather n/a
|
||||
* @param args n/a
|
||||
* @returns whether the form change was triggered
|
||||
*/
|
||||
applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean {
|
||||
if (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST) {
|
||||
const isCastformWithForecast = (pokemon.species.speciesId === Species.CASTFORM && this.ability === Abilities.FORECAST);
|
||||
const isCherrimWithFlowerGift = (pokemon.species.speciesId === Species.CHERRIM && this.ability === Abilities.FLOWER_GIFT);
|
||||
|
||||
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
||||
if (simulated) {
|
||||
return simulated;
|
||||
}
|
||||
|
||||
const formRevertingWeathers: WeatherType[] = [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ];
|
||||
const weatherType = pokemon.scene.arena.weather?.weatherType;
|
||||
|
||||
if (weatherType && formRevertingWeathers.includes(weatherType)) {
|
||||
if (weatherType && this.formRevertingWeathers.includes(weatherType)) {
|
||||
pokemon.scene.arena.triggerWeatherBasedFormChangesToNormal();
|
||||
} else {
|
||||
pokemon.scene.arena.triggerWeatherBasedFormChanges();
|
||||
|
@ -4744,7 +4751,8 @@ function setAbilityRevealed(pokemon: Pokemon): void {
|
|||
*/
|
||||
function getPokemonWithWeatherBasedForms(scene: BattleScene) {
|
||||
return scene.getField(true).filter(p =>
|
||||
p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM
|
||||
(p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM)
|
||||
|| (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4943,7 +4951,7 @@ export function initAbilities() {
|
|||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.attr(PostSummonFormChangeByWeatherAbAttr, Abilities.FORECAST)
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, Abilities.FORECAST),
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, Abilities.FORECAST, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG ]),
|
||||
new Ability(Abilities.STICKY_HOLD, 3)
|
||||
.attr(BlockItemTheftAbAttr)
|
||||
.bypassFaint()
|
||||
|
@ -5136,8 +5144,10 @@ export function initAbilities() {
|
|||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.SPDEF, 1.5)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(NoFusionAbilityAbAttr)
|
||||
.ignorable()
|
||||
.partial(),
|
||||
.attr(PostSummonFormChangeByWeatherAbAttr, Abilities.FLOWER_GIFT)
|
||||
.attr(PostWeatherChangeFormChangeAbAttr, Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ])
|
||||
.partial() // Should also boosts stats of ally
|
||||
.ignorable(),
|
||||
new Ability(Abilities.BAD_DREAMS, 4)
|
||||
.attr(PostTurnHurtIfSleepingAbAttr),
|
||||
new Ability(Abilities.PICKPOCKET, 5)
|
||||
|
|
|
@ -5888,9 +5888,9 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr {
|
|||
target.summonData.ability = tempAbilityId;
|
||||
|
||||
user.scene.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", {pokemonName: getPokemonNameWithAffix(user)}));
|
||||
// Swaps Forecast from Castform
|
||||
// Swaps Forecast/Flower Gift from Castform/Cherrim
|
||||
user.scene.arena.triggerWeatherBasedFormChangesToNormal();
|
||||
// Swaps Forecast to Castform (edge case)
|
||||
// Swaps Forecast/Flower Gift to Castform/Cherrim (edge case)
|
||||
user.scene.arena.triggerWeatherBasedFormChanges();
|
||||
|
||||
return true;
|
||||
|
|
|
@ -359,7 +359,7 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
|||
|
||||
/**
|
||||
* Class used for triggering form changes based on weather.
|
||||
* Used by Castform.
|
||||
* Used by Castform and Cherrim.
|
||||
* @extends SpeciesFormChangeTrigger
|
||||
*/
|
||||
export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
||||
|
@ -392,7 +392,7 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
|||
/**
|
||||
* Class used for reverting to the original form when the weather runs out
|
||||
* or when the user loses the ability/is suppressed.
|
||||
* Used by Castform.
|
||||
* Used by Castform and Cherrim.
|
||||
* @extends SpeciesFormChangeTrigger
|
||||
*/
|
||||
export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger {
|
||||
|
@ -930,6 +930,11 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
],
|
||||
[Species.CHERRIM]: [
|
||||
new SpeciesFormChange(Species.CHERRIM, "overcast", "sunshine", new SpeciesFormChangeWeatherTrigger(Abilities.FLOWER_GIFT, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]), true),
|
||||
new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true),
|
||||
new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeActiveTrigger(), true),
|
||||
],
|
||||
};
|
||||
|
||||
export function initPokemonForms() {
|
||||
|
|
|
@ -339,7 +339,10 @@ export class Arena {
|
|||
*/
|
||||
triggerWeatherBasedFormChanges(): void {
|
||||
this.scene.getField(true).forEach( p => {
|
||||
if (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM) {
|
||||
const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM);
|
||||
const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM);
|
||||
|
||||
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
||||
new ShowAbilityPhase(this.scene, p.getBattlerIndex());
|
||||
this.scene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger);
|
||||
}
|
||||
|
@ -351,7 +354,10 @@ export class Arena {
|
|||
*/
|
||||
triggerWeatherBasedFormChangesToNormal(): void {
|
||||
this.scene.getField(true).forEach( p => {
|
||||
if (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM) {
|
||||
const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM);
|
||||
const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM);
|
||||
|
||||
if (isCastformWithForecast || isCherrimWithFlowerGift) {
|
||||
new ShowAbilityPhase(this.scene, p.getBattlerIndex());
|
||||
return this.scene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
import { BattlerIndex } from "#app/battle";
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { WeatherType } from "#app/enums/weather-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Flower Gift", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
const OVERCAST_FORM = 0;
|
||||
const SUNSHINE_FORM = 1;
|
||||
|
||||
/**
|
||||
* Tests reverting to normal form when Cloud Nine/Air Lock is active on the field
|
||||
* @param {GameManager} game The game manager instance
|
||||
* @param {Abilities} ability The ability that is active on the field
|
||||
*/
|
||||
const testRevertFormAgainstAbility = async (game: GameManager, ability: Abilities) => {
|
||||
game.override.starterForms({ [Species.CASTFORM]: SUNSHINE_FORM }).enemyAbility(ability);
|
||||
await game.classicMode.startBattle([Species.CASTFORM]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.formIndex).toBe(OVERCAST_FORM);
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([Moves.SPLASH, Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.SKILL_SWAP])
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyMoveset(SPLASH_ONLY)
|
||||
.enemyAbility(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
// TODO: Uncomment expect statements when the ability is implemented - currently does not increase stats of allies
|
||||
it("increases the Attack and Special Defense stats of the Pokémon with this Ability and its allies by 1.5× during Harsh Sunlight", async () => {
|
||||
game.override.battleType("double");
|
||||
await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]);
|
||||
|
||||
const [ cherrim ] = game.scene.getPlayerField();
|
||||
const cherrimAtkStat = cherrim.getBattleStat(Stat.ATK);
|
||||
const cherrimSpDefStat = cherrim.getBattleStat(Stat.SPDEF);
|
||||
|
||||
// const magikarpAtkStat = magikarp.getBattleStat(Stat.ATK);;
|
||||
// const magikarpSpDefStat = magikarp.getBattleStat(Stat.SPDEF);
|
||||
|
||||
game.move.select(Moves.SUNNY_DAY, 0);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
expect(cherrim.getBattleStat(Stat.ATK)).toBe(Math.floor(cherrimAtkStat * 1.5));
|
||||
expect(cherrim.getBattleStat(Stat.SPDEF)).toBe(Math.floor(cherrimSpDefStat * 1.5));
|
||||
// expect(magikarp.getBattleStat(Stat.ATK)).toBe(Math.floor(magikarpAtkStat * 1.5));
|
||||
// expect(magikarp.getBattleStat(Stat.SPDEF)).toBe(Math.floor(magikarpSpDefStat * 1.5));
|
||||
});
|
||||
|
||||
it("changes the Pokemon's form during Harsh Sunlight", async () => {
|
||||
game.override.weather(WeatherType.HARSH_SUN);
|
||||
await game.classicMode.startBattle([Species.CHERRIM]);
|
||||
|
||||
const cherrim = game.scene.getPlayerPokemon()!;
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("reverts to Overcast Form if a Pokémon on the field has Air Lock", async () => {
|
||||
await testRevertFormAgainstAbility(game, Abilities.AIR_LOCK);
|
||||
});
|
||||
|
||||
it("reverts to Overcast Form if a Pokémon on the field has Cloud Nine", async () => {
|
||||
await testRevertFormAgainstAbility(game, Abilities.CLOUD_NINE);
|
||||
});
|
||||
|
||||
it("reverts to Overcast Form when the Pokémon loses Flower Gift, changes form under Harsh Sunlight/Sunny when it regains it", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP)).weather(WeatherType.HARSH_SUN);
|
||||
|
||||
await game.classicMode.startBattle([Species.CHERRIM]);
|
||||
|
||||
const cherrim = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
|
||||
await game.phaseInterceptor.to("TurnStartPhase");
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(cherrim.formIndex).toBe(OVERCAST_FORM);
|
||||
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
});
|
||||
|
||||
it("reverts to Overcast Form when the Flower Gift is suppressed, changes form under Harsh Sunlight/Sunny when it regains it", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)).weather(WeatherType.HARSH_SUN);
|
||||
|
||||
await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]);
|
||||
|
||||
const cherrim = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(cherrim.summonData.abilitySuppressed).toBe(true);
|
||||
expect(cherrim.formIndex).toBe(OVERCAST_FORM);
|
||||
|
||||
await game.toNextTurn();
|
||||
|
||||
game.doSwitchPokemon(1);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.doSwitchPokemon(1);
|
||||
await game.phaseInterceptor.to("MovePhase");
|
||||
|
||||
expect(cherrim.summonData.abilitySuppressed).toBe(false);
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
});
|
||||
|
||||
it("should be in Overcast Form after the user is switched out", async () => {
|
||||
game.override.weather(WeatherType.SUNNY);
|
||||
|
||||
await game.classicMode.startBattle([Species.CASTFORM, Species.MAGIKARP]);
|
||||
const cherrim = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
|
||||
|
||||
game.doSwitchPokemon(1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(cherrim.formIndex).toBe(OVERCAST_FORM);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue