mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-02-21 19:55:52 +00:00
[Refactor/Bug] Pokemon.leaveField()
, Fix Related Abilities (#5191)
* Added new AbAttr that triggers whenever a pokemon leaves the field * Use leaveField everywhere * Changing order for PreSwitchOutAbAttr * Don't clearEffects when catching in a mystery encounter * Attempts to make new overrides for testing * New options in overrides * Implemented tests for Desolate Land * Fixing instruct test to not read turnData of fainted mon * Removed post faint clear weather * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Has_passive_ability override now turns off passives if set to "false", defaults to "null" * Updating overrides type definitions * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Suggestions from review * Fixed strings in suggestions * Simplified function to throw balls in tests * Added tsdocs to overrideHelper.ts --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
60990deaf2
commit
6c4dedb73e
@ -2643,55 +2643,6 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out.
|
|
||||||
*/
|
|
||||||
export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param pokemon The {@linkcode Pokemon} with the ability
|
|
||||||
* @param passive N/A
|
|
||||||
* @param args N/A
|
|
||||||
* @returns {boolean} Returns true if the weather clears, otherwise false.
|
|
||||||
*/
|
|
||||||
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
|
||||||
const weatherType = globalScene.arena.weather?.weatherType;
|
|
||||||
let turnOffWeather = false;
|
|
||||||
|
|
||||||
// Clear weather only if user's ability matches the weather and no other pokemon has the ability.
|
|
||||||
switch (weatherType) {
|
|
||||||
case (WeatherType.HARSH_SUN):
|
|
||||||
if (pokemon.hasAbility(Abilities.DESOLATE_LAND)
|
|
||||||
&& globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DESOLATE_LAND)).length === 0) {
|
|
||||||
turnOffWeather = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (WeatherType.HEAVY_RAIN):
|
|
||||||
if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA)
|
|
||||||
&& globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) {
|
|
||||||
turnOffWeather = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (WeatherType.STRONG_WINDS):
|
|
||||||
if (pokemon.hasAbility(Abilities.DELTA_STREAM)
|
|
||||||
&& globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) {
|
|
||||||
turnOffWeather = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simulated) {
|
|
||||||
return turnOffWeather;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (turnOffWeather) {
|
|
||||||
globalScene.arena.trySetWeather(WeatherType.NONE, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
|
export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
|
||||||
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||||
@ -2744,6 +2695,61 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PreLeaveFieldAbAttr extends AbAttr {
|
||||||
|
applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out.
|
||||||
|
*/
|
||||||
|
export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
|
||||||
|
/**
|
||||||
|
* @param pokemon The {@linkcode Pokemon} with the ability
|
||||||
|
* @param passive N/A
|
||||||
|
* @param args N/A
|
||||||
|
* @returns Returns `true` if the weather clears, otherwise `false`.
|
||||||
|
*/
|
||||||
|
applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||||
|
const weatherType = globalScene.arena.weather?.weatherType;
|
||||||
|
let turnOffWeather = false;
|
||||||
|
|
||||||
|
// Clear weather only if user's ability matches the weather and no other pokemon has the ability.
|
||||||
|
switch (weatherType) {
|
||||||
|
case (WeatherType.HARSH_SUN):
|
||||||
|
if (pokemon.hasAbility(Abilities.DESOLATE_LAND)
|
||||||
|
&& globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DESOLATE_LAND)).length === 0) {
|
||||||
|
turnOffWeather = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case (WeatherType.HEAVY_RAIN):
|
||||||
|
if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA)
|
||||||
|
&& globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) {
|
||||||
|
turnOffWeather = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case (WeatherType.STRONG_WINDS):
|
||||||
|
if (pokemon.hasAbility(Abilities.DELTA_STREAM)
|
||||||
|
&& globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) {
|
||||||
|
turnOffWeather = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simulated) {
|
||||||
|
return turnOffWeather;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnOffWeather) {
|
||||||
|
globalScene.arena.trySetWeather(WeatherType.NONE, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class PreStatStageChangeAbAttr extends AbAttr {
|
export class PreStatStageChangeAbAttr extends AbAttr {
|
||||||
applyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
applyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
@ -4171,59 +4177,6 @@ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAtt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon fainting
|
|
||||||
*/
|
|
||||||
export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param pokemon The {@linkcode Pokemon} with the ability
|
|
||||||
* @param passive N/A
|
|
||||||
* @param attacker N/A
|
|
||||||
* @param move N/A
|
|
||||||
* @param hitResult N/A
|
|
||||||
* @param args N/A
|
|
||||||
* @returns {boolean} Returns true if the weather clears, otherwise false.
|
|
||||||
*/
|
|
||||||
applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean {
|
|
||||||
const weatherType = globalScene.arena.weather?.weatherType;
|
|
||||||
let turnOffWeather = false;
|
|
||||||
|
|
||||||
// Clear weather only if user's ability matches the weather and no other pokemon has the ability.
|
|
||||||
switch (weatherType) {
|
|
||||||
case (WeatherType.HARSH_SUN):
|
|
||||||
if (pokemon.hasAbility(Abilities.DESOLATE_LAND)
|
|
||||||
&& globalScene.getField(true).filter(p => p.hasAbility(Abilities.DESOLATE_LAND)).length === 0) {
|
|
||||||
turnOffWeather = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (WeatherType.HEAVY_RAIN):
|
|
||||||
if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA)
|
|
||||||
&& globalScene.getField(true).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) {
|
|
||||||
turnOffWeather = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (WeatherType.STRONG_WINDS):
|
|
||||||
if (pokemon.hasAbility(Abilities.DELTA_STREAM)
|
|
||||||
&& globalScene.getField(true).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) {
|
|
||||||
turnOffWeather = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simulated) {
|
|
||||||
return turnOffWeather;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (turnOffWeather) {
|
|
||||||
globalScene.arena.trySetWeather(WeatherType.NONE, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
||||||
private damageRatio: number;
|
private damageRatio: number;
|
||||||
|
|
||||||
@ -5229,6 +5182,11 @@ export function applyPreSwitchOutAbAttrs(attrType: Constructor<PreSwitchOutAbAtt
|
|||||||
return applyAbAttrsInternal<PreSwitchOutAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, simulated, args), args, true, simulated);
|
return applyAbAttrsInternal<PreSwitchOutAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, simulated, args), args, true, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPreLeaveFieldAbAttrs(attrType: Constructor<PreLeaveFieldAbAttr>,
|
||||||
|
pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise<void> {
|
||||||
|
return applyAbAttrsInternal<PreLeaveFieldAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, args), args, true, simulated);
|
||||||
|
}
|
||||||
|
|
||||||
export function applyPreStatStageChangeAbAttrs(attrType: Constructor<PreStatStageChangeAbAttr>,
|
export function applyPreStatStageChangeAbAttrs(attrType: Constructor<PreStatStageChangeAbAttr>,
|
||||||
pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise<void> {
|
pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise<void> {
|
||||||
return applyAbAttrsInternal<PreStatStageChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), args, false, simulated);
|
return applyAbAttrsInternal<PreStatStageChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), args, false, simulated);
|
||||||
@ -5912,20 +5870,17 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.PRIMORDIAL_SEA, 6)
|
new Ability(Abilities.PRIMORDIAL_SEA, 6)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
||||||
.attr(PreSwitchOutClearWeatherAbAttr)
|
.attr(PreLeaveFieldClearWeatherAbAttr)
|
||||||
.attr(PostFaintClearWeatherAbAttr)
|
|
||||||
.bypassFaint(),
|
.bypassFaint(),
|
||||||
new Ability(Abilities.DESOLATE_LAND, 6)
|
new Ability(Abilities.DESOLATE_LAND, 6)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HARSH_SUN)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HARSH_SUN)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HARSH_SUN)
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HARSH_SUN)
|
||||||
.attr(PreSwitchOutClearWeatherAbAttr)
|
.attr(PreLeaveFieldClearWeatherAbAttr)
|
||||||
.attr(PostFaintClearWeatherAbAttr)
|
|
||||||
.bypassFaint(),
|
.bypassFaint(),
|
||||||
new Ability(Abilities.DELTA_STREAM, 6)
|
new Ability(Abilities.DELTA_STREAM, 6)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.STRONG_WINDS)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.STRONG_WINDS)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.STRONG_WINDS)
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.STRONG_WINDS)
|
||||||
.attr(PreSwitchOutClearWeatherAbAttr)
|
.attr(PreLeaveFieldClearWeatherAbAttr)
|
||||||
.attr(PostFaintClearWeatherAbAttr)
|
|
||||||
.bypassFaint(),
|
.bypassFaint(),
|
||||||
new Ability(Abilities.STAMINA, 7)
|
new Ability(Abilities.STAMINA, 7)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
||||||
|
@ -148,7 +148,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Adds a real Pokemon sprite to the field (required for the animation)
|
// Adds a real Pokemon sprite to the field (required for the animation)
|
||||||
globalScene.getEnemyParty().forEach(enemyPokemon => {
|
globalScene.getEnemyParty().forEach(enemyPokemon => {
|
||||||
globalScene.field.remove(enemyPokemon, true);
|
enemyPokemon.leaveField(true, true, true);
|
||||||
});
|
});
|
||||||
globalScene.currentBattle.enemyParty = [ oricorio ];
|
globalScene.currentBattle.enemyParty = [ oricorio ];
|
||||||
globalScene.field.add(oricorio);
|
globalScene.field.add(oricorio);
|
||||||
|
@ -229,7 +229,7 @@ function handleLoseMinigame() {
|
|||||||
// End the battle
|
// End the battle
|
||||||
if (wobbuffet) {
|
if (wobbuffet) {
|
||||||
wobbuffet.hideInfo();
|
wobbuffet.hideInfo();
|
||||||
globalScene.field.remove(wobbuffet);
|
wobbuffet.leaveField();
|
||||||
}
|
}
|
||||||
transitionMysteryEncounterIntroVisuals(true, true);
|
transitionMysteryEncounterIntroVisuals(true, true);
|
||||||
globalScene.currentBattle.enemyParty = [];
|
globalScene.currentBattle.enemyParty = [];
|
||||||
@ -278,7 +278,7 @@ function handleNextTurn() {
|
|||||||
|
|
||||||
// End the battle
|
// End the battle
|
||||||
wobbuffet.hideInfo();
|
wobbuffet.hideInfo();
|
||||||
globalScene.field.remove(wobbuffet);
|
wobbuffet.leaveField();
|
||||||
globalScene.currentBattle.enemyParty = [];
|
globalScene.currentBattle.enemyParty = [];
|
||||||
globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
|
globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
|
||||||
leaveEncounterWithoutBattle(isHealPhase);
|
leaveEncounterWithoutBattle(isHealPhase);
|
||||||
|
@ -575,7 +575,7 @@ function onGameOver() {
|
|||||||
ease: "Sine.easeIn",
|
ease: "Sine.easeIn",
|
||||||
scale: 0.5,
|
scale: 0.5,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
globalScene.field.remove(pokemon, true);
|
pokemon.leaveField(true, true, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
|||||||
}
|
}
|
||||||
|
|
||||||
globalScene.getEnemyParty().forEach(enemyPokemon => {
|
globalScene.getEnemyParty().forEach(enemyPokemon => {
|
||||||
globalScene.field.remove(enemyPokemon, true);
|
enemyPokemon.leaveField(true, true, true);
|
||||||
});
|
});
|
||||||
battle.enemyParty = [];
|
battle.enemyParty = [];
|
||||||
battle.double = doubleBattle;
|
battle.double = doubleBattle;
|
||||||
@ -810,7 +810,7 @@ export function transitionMysteryEncounterIntroVisuals(hide: boolean = true, des
|
|||||||
globalScene.field.remove(introVisuals, true);
|
globalScene.field.remove(introVisuals, true);
|
||||||
|
|
||||||
enemyPokemon.forEach(pokemon => {
|
enemyPokemon.forEach(pokemon => {
|
||||||
globalScene.field.remove(pokemon, true);
|
pokemon.leaveField(true, true, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
globalScene.currentBattle.mysteryEncounter!.introVisuals = undefined;
|
globalScene.currentBattle.mysteryEncounter!.introVisuals = undefined;
|
||||||
|
@ -592,7 +592,7 @@ export async function catchPokemon(pokemon: EnemyPokemon, pokeball: Phaser.GameO
|
|||||||
};
|
};
|
||||||
const removePokemon = () => {
|
const removePokemon = () => {
|
||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
globalScene.field.remove(pokemon, true);
|
pokemon.leaveField(false, true, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const addToParty = (slotIndex?: number) => {
|
const addToParty = (slotIndex?: number) => {
|
||||||
@ -695,7 +695,7 @@ export async function doPokemonFlee(pokemon: EnemyPokemon): Promise<void> {
|
|||||||
scale: pokemon.getSpriteScale(),
|
scale: pokemon.getSpriteScale(),
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
pokemon.setVisible(false);
|
pokemon.setVisible(false);
|
||||||
globalScene.field.remove(pokemon, true);
|
pokemon.leaveField(true, true, true);
|
||||||
showEncounterText(i18next.t("battle:pokemonFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false)
|
showEncounterText(i18next.t("battle:pokemonFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve();
|
resolve();
|
||||||
@ -723,7 +723,7 @@ export function doPlayerFlee(pokemon: EnemyPokemon): Promise<void> {
|
|||||||
scale: pokemon.getSpriteScale(),
|
scale: pokemon.getSpriteScale(),
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
pokemon.setVisible(false);
|
pokemon.setVisible(false);
|
||||||
globalScene.field.remove(pokemon, true);
|
pokemon.leaveField(true, true, true);
|
||||||
showEncounterText(i18next.t("battle:playerFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false)
|
showEncounterText(i18next.t("battle:playerFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -31,7 +31,7 @@ import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoo
|
|||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||||
import type { Ability, AbAttr } from "#app/data/ability";
|
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, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr } 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, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr, PreLeaveFieldAbAttr, applyPreLeaveFieldAbAttrs } from "#app/data/ability";
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
@ -1422,8 +1422,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
*/
|
*/
|
||||||
public hasPassive(): boolean {
|
public hasPassive(): boolean {
|
||||||
// returns override if valid for current case
|
// returns override if valid for current case
|
||||||
if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer())
|
if (
|
||||||
|| (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
|
(Overrides.HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isPlayer())
|
||||||
|
|| (Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && !this.isPlayer())
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) && this.isPlayer())
|
||||||
|
|| ((Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE || Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE) && !this.isPlayer())
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4142,9 +4150,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param hideInfo Indicates if this should also play the animation to hide the Pokemon's
|
* @param hideInfo Indicates if this should also play the animation to hide the Pokemon's
|
||||||
* info container.
|
* info container.
|
||||||
*/
|
*/
|
||||||
leaveField(clearEffects: boolean = true, hideInfo: boolean = true) {
|
leaveField(clearEffects: boolean = true, hideInfo: boolean = true, destroy: boolean = false) {
|
||||||
this.resetSprite();
|
this.resetSprite();
|
||||||
this.resetTurnData();
|
this.resetTurnData();
|
||||||
|
globalScene.getField(true).filter(p => p !== this).forEach(p => p.removeTagsBySourceId(this.id));
|
||||||
|
|
||||||
if (clearEffects) {
|
if (clearEffects) {
|
||||||
this.destroySubstitute();
|
this.destroySubstitute();
|
||||||
this.resetSummonData(); // this also calls `resetBattleSummonData`
|
this.resetSummonData(); // this also calls `resetBattleSummonData`
|
||||||
@ -4152,9 +4162,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (hideInfo) {
|
if (hideInfo) {
|
||||||
this.hideInfo();
|
this.hideInfo();
|
||||||
}
|
}
|
||||||
globalScene.field.remove(this);
|
// Trigger abilities that activate upon leaving the field
|
||||||
|
applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, this);
|
||||||
this.setSwitchOutStatus(true);
|
this.setSwitchOutStatus(true);
|
||||||
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true);
|
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true);
|
||||||
|
globalScene.field.remove(this, destroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
@ -129,6 +129,7 @@ class DefaultOverrides {
|
|||||||
readonly STARTER_FUSION_SPECIES_OVERRIDE: Species | number = 0;
|
readonly STARTER_FUSION_SPECIES_OVERRIDE: Species | number = 0;
|
||||||
readonly ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
readonly ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||||
readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||||
|
readonly HAS_PASSIVE_ABILITY_OVERRIDE: boolean | null = null;
|
||||||
readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
|
readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
|
||||||
readonly GENDER_OVERRIDE: Gender | null = null;
|
readonly GENDER_OVERRIDE: Gender | null = null;
|
||||||
readonly MOVESET_OVERRIDE: Moves | Array<Moves> = [];
|
readonly MOVESET_OVERRIDE: Moves | Array<Moves> = [];
|
||||||
@ -150,6 +151,7 @@ class DefaultOverrides {
|
|||||||
readonly OPP_LEVEL_OVERRIDE: number = 0;
|
readonly OPP_LEVEL_OVERRIDE: number = 0;
|
||||||
readonly OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
readonly OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||||
readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE;
|
||||||
|
readonly OPP_HAS_PASSIVE_ABILITY_OVERRIDE: boolean | null = null;
|
||||||
readonly OPP_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
|
readonly OPP_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE;
|
||||||
readonly OPP_GENDER_OVERRIDE: Gender | null = null;
|
readonly OPP_GENDER_OVERRIDE: Gender | null = null;
|
||||||
readonly OPP_MOVESET_OVERRIDE: Moves | Array<Moves> = [];
|
readonly OPP_MOVESET_OVERRIDE: Moves | Array<Moves> = [];
|
||||||
|
@ -241,11 +241,10 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
};
|
};
|
||||||
const removePokemon = () => {
|
const removePokemon = () => {
|
||||||
globalScene.addFaintedEnemyScore(pokemon);
|
globalScene.addFaintedEnemyScore(pokemon);
|
||||||
globalScene.getPlayerField().filter(p => p.isActive(true)).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id));
|
|
||||||
pokemon.hp = 0;
|
pokemon.hp = 0;
|
||||||
pokemon.trySetStatus(StatusEffect.FAINT);
|
pokemon.trySetStatus(StatusEffect.FAINT);
|
||||||
globalScene.clearEnemyHeldItemModifiers();
|
globalScene.clearEnemyHeldItemModifiers();
|
||||||
globalScene.field.remove(pokemon, true);
|
pokemon.leaveField(true, true, true);
|
||||||
};
|
};
|
||||||
const addToParty = (slotIndex?: number) => {
|
const addToParty = (slotIndex?: number) => {
|
||||||
const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex);
|
const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex);
|
||||||
|
@ -181,9 +181,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
y: pokemon.y + 150,
|
y: pokemon.y + 150,
|
||||||
ease: "Sine.easeIn",
|
ease: "Sine.easeIn",
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
pokemon.resetSprite();
|
|
||||||
pokemon.lapseTags(BattlerTagLapseType.FAINT);
|
pokemon.lapseTags(BattlerTagLapseType.FAINT);
|
||||||
globalScene.getField(true).filter(p => p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id));
|
|
||||||
|
|
||||||
pokemon.y -= 150;
|
pokemon.y -= 150;
|
||||||
pokemon.trySetStatus(StatusEffect.FAINT);
|
pokemon.trySetStatus(StatusEffect.FAINT);
|
||||||
@ -193,7 +191,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
globalScene.addFaintedEnemyScore(pokemon as EnemyPokemon);
|
globalScene.addFaintedEnemyScore(pokemon as EnemyPokemon);
|
||||||
globalScene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon);
|
globalScene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon);
|
||||||
}
|
}
|
||||||
globalScene.field.remove(pokemon);
|
pokemon.leaveField();
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -64,6 +64,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
|
|
||||||
const pokemon = this.getPokemon();
|
const pokemon = this.getPokemon();
|
||||||
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
|
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
|
||||||
|
|
||||||
if (this.switchType === SwitchType.SWITCH || this.switchType === SwitchType.INITIAL_SWITCH) {
|
if (this.switchType === SwitchType.SWITCH || this.switchType === SwitchType.INITIAL_SWITCH) {
|
||||||
const substitute = pokemon.getTag(SubstituteTag);
|
const substitute = pokemon.getTag(SubstituteTag);
|
||||||
if (substitute) {
|
if (substitute) {
|
||||||
@ -93,8 +94,8 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
ease: "Sine.easeIn",
|
ease: "Sine.easeIn",
|
||||||
scale: 0.5,
|
scale: 0.5,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
pokemon.leaveField(this.switchType === SwitchType.SWITCH, false);
|
|
||||||
globalScene.time.delayedCall(750, () => this.switchAndSummon());
|
globalScene.time.delayedCall(750, () => this.switchAndSummon());
|
||||||
|
pokemon.leaveField(this.switchType === SwitchType.SWITCH, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
139
src/test/abilities/desolate-land.test.ts
Normal file
139
src/test/abilities/desolate-land.test.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { PokeballType } from "#app/enums/pokeball";
|
||||||
|
import { WeatherType } from "#app/enums/weather-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("Abilities - Desolate Land", () => {
|
||||||
|
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)
|
||||||
|
.hasPassiveAbility(true)
|
||||||
|
.enemySpecies(Species.RALTS)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This checks that the weather has changed after the Enemy Pokemon with {@linkcode Abilities.DESOLATE_LAND}
|
||||||
|
* is forcefully moved out of the field from moves such as Roar {@linkcode Moves.ROAR}
|
||||||
|
*/
|
||||||
|
it("should lift only when all pokemon with this ability leave the field", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyMoveset([ Moves.SPLASH, Moves.ROAR ]);
|
||||||
|
await game.classicMode.startBattle([ Species.MAGCARGO, Species.MAGCARGO, Species.MAGIKARP, Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||||
|
|
||||||
|
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||||
|
return min;
|
||||||
|
});
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH, 0, 2);
|
||||||
|
game.move.select(Moves.SPLASH, 1, 2);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.ROAR, 0);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH, 1);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||||
|
return min + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH, 0, 2);
|
||||||
|
game.move.select(Moves.SPLASH, 1, 2);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.ROAR, 1);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH, 0);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.HARSH_SUN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should lift when enemy faints", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.moveset([ Moves.SHEER_COLD ])
|
||||||
|
.ability(Abilities.NO_GUARD)
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemyLevel(1)
|
||||||
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.enemySpecies(Species.MAGCARGO)
|
||||||
|
.enemyHasPassiveAbility(true);
|
||||||
|
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||||
|
|
||||||
|
game.move.select(Moves.SHEER_COLD);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.HARSH_SUN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should lift when pokemon returns upon switching from double to single battle", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("even-doubles")
|
||||||
|
.enemyMoveset([ Moves.SPLASH, Moves.MEMENTO ])
|
||||||
|
.startingWave(12);
|
||||||
|
await game.classicMode.startBattle([ Species.MAGIKARP, Species.MAGCARGO ]);
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH, 0, 2);
|
||||||
|
game.move.select(Moves.SPLASH, 1, 2);
|
||||||
|
await game.forceEnemyMove(Moves.MEMENTO, 0);
|
||||||
|
await game.forceEnemyMove(Moves.MEMENTO, 1);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||||
|
|
||||||
|
await game.toNextWave();
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.HARSH_SUN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should lift when enemy is captured", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.enemySpecies(Species.MAGCARGO)
|
||||||
|
.enemyHasPassiveAbility(true);
|
||||||
|
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||||
|
|
||||||
|
game.scene.pokeballCounts[PokeballType.MASTER_BALL] = 1;
|
||||||
|
|
||||||
|
game.doThrowPokeball(PokeballType.MASTER_BALL);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.HARSH_SUN);
|
||||||
|
});
|
||||||
|
});
|
@ -221,7 +221,8 @@ describe("Moves - Instruct", () => {
|
|||||||
it("should allow for dancer copying of instructed dance move", async () => {
|
it("should allow for dancer copying of instructed dance move", async () => {
|
||||||
game.override
|
game.override
|
||||||
.battleType("double")
|
.battleType("double")
|
||||||
.enemyMoveset([ Moves.INSTRUCT, Moves.SPLASH ]);
|
.enemyMoveset([ Moves.INSTRUCT, Moves.SPLASH ])
|
||||||
|
.enemyLevel(1000);
|
||||||
await game.classicMode.startBattle([ Species.ORICORIO, Species.VOLCARONA ]);
|
await game.classicMode.startBattle([ Species.ORICORIO, Species.VOLCARONA ]);
|
||||||
|
|
||||||
const [ oricorio, volcarona ] = game.scene.getPlayerField();
|
const [ oricorio, volcarona ] = game.scene.getPlayerField();
|
||||||
@ -236,11 +237,9 @@ describe("Moves - Instruct", () => {
|
|||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
// fiery dance triggered dancer successfully for a total of 4 hits
|
// fiery dance triggered dancer successfully for a total of 4 hits
|
||||||
// Volcarona fiery dance has a _small_ chance to 3HKO a shuckle in worst case, so we add the hit count of both
|
// Enemy level is set to a high value so that it does not faint even after all 4 hits
|
||||||
// foes to account for spillover
|
|
||||||
instructSuccess(volcarona, Moves.FIERY_DANCE);
|
instructSuccess(volcarona, Moves.FIERY_DANCE);
|
||||||
expect(game.scene.getEnemyField()[0].turnData.attacksReceived.length +
|
expect(game.scene.getEnemyField()[0].turnData.attacksReceived.length).toBe(4);
|
||||||
game.scene.getEnemyField()[1].turnData.attacksReceived.length).toBe(4);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not repeat move when switching out", async () => {
|
it("should not repeat move when switching out", async () => {
|
||||||
|
@ -24,6 +24,7 @@ import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
|||||||
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
||||||
import ErrorInterceptor from "#app/test/utils/errorInterceptor";
|
import ErrorInterceptor from "#app/test/utils/errorInterceptor";
|
||||||
import type InputsHandler from "#app/test/utils/inputsHandler";
|
import type InputsHandler from "#app/test/utils/inputsHandler";
|
||||||
|
import type BallUiHandler from "#app/ui/ball-ui-handler";
|
||||||
import type BattleMessageUiHandler from "#app/ui/battle-message-ui-handler";
|
import type BattleMessageUiHandler from "#app/ui/battle-message-ui-handler";
|
||||||
import type CommandUiHandler from "#app/ui/command-ui-handler";
|
import type CommandUiHandler from "#app/ui/command-ui-handler";
|
||||||
import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||||
@ -458,6 +459,24 @@ export default class GameManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the BALL option from the command menu, then press Action; in the BALL
|
||||||
|
* menu, select a pokéball type and press Action again to throw it.
|
||||||
|
* @param ballIndex the index of the pokeball to throw
|
||||||
|
*/
|
||||||
|
public doThrowPokeball(ballIndex: number) {
|
||||||
|
this.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||||
|
(this.scene.ui.getHandler() as CommandUiHandler).setCursor(1);
|
||||||
|
(this.scene.ui.getHandler() as CommandUiHandler).processInput(Button.ACTION);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onNextPrompt("CommandPhase", Mode.BALL, () => {
|
||||||
|
const ballHandler = this.scene.ui.getHandler() as BallUiHandler;
|
||||||
|
ballHandler.setCursor(ballIndex);
|
||||||
|
ballHandler.processInput(Button.ACTION); // select ball and throw
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intercepts `TurnStartPhase` and mocks {@linkcode TurnStartPhase.getSpeedOrder}'s return value.
|
* Intercepts `TurnStartPhase` and mocks {@linkcode TurnStartPhase.getSpeedOrder}'s return value.
|
||||||
* Used to manually modify Pokemon turn order.
|
* Used to manually modify Pokemon turn order.
|
||||||
|
@ -181,6 +181,20 @@ export class OverridesHelper extends GameManagerHelper {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the status of the player (pokemon) **passive** {@linkcode Abilities | ability}
|
||||||
|
* @param hasPassiveAbility forces the passive to be active if `true`, inactive if `false`
|
||||||
|
* @returns `this`
|
||||||
|
*/
|
||||||
|
public hasPassiveAbility(hasPassiveAbility: boolean | null): this {
|
||||||
|
vi.spyOn(Overrides, "HAS_PASSIVE_ABILITY_OVERRIDE", "get").mockReturnValue(hasPassiveAbility);
|
||||||
|
if (hasPassiveAbility === null) {
|
||||||
|
this.log("Player Pokemon PASSIVE ability no longer force enabled or disabled!");
|
||||||
|
} else {
|
||||||
|
this.log(`Player Pokemon PASSIVE ability is force ${hasPassiveAbility ? "enabled" : "disabled"}!`);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Override the player (pokemon) {@linkcode Moves | moves}set
|
* Override the player (pokemon) {@linkcode Moves | moves}set
|
||||||
* @param moveset the {@linkcode Moves | moves}set to set
|
* @param moveset the {@linkcode Moves | moves}set to set
|
||||||
@ -325,6 +339,21 @@ export class OverridesHelper extends GameManagerHelper {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the status of the enemy (pokemon) **passive** {@linkcode Abilities | ability}
|
||||||
|
* @param hasPassiveAbility forces the passive to be active if `true`, inactive if `false`
|
||||||
|
* @returns `this`
|
||||||
|
*/
|
||||||
|
public enemyHasPassiveAbility(hasPassiveAbility: boolean | null): this {
|
||||||
|
vi.spyOn(Overrides, "OPP_HAS_PASSIVE_ABILITY_OVERRIDE", "get").mockReturnValue(hasPassiveAbility);
|
||||||
|
if (hasPassiveAbility === null) {
|
||||||
|
this.log("Enemy Pokemon PASSIVE ability no longer force enabled or disabled!");
|
||||||
|
} else {
|
||||||
|
this.log(`Enemy Pokemon PASSIVE ability is force ${hasPassiveAbility ? "enabled" : "disabled"}!`);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the enemy (pokemon) {@linkcode Moves | moves}set
|
* Override the enemy (pokemon) {@linkcode Moves | moves}set
|
||||||
* @param moveset the {@linkcode Moves | moves}set to set
|
* @param moveset the {@linkcode Moves | moves}set to set
|
||||||
|
Loading…
x
Reference in New Issue
Block a user