mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-21 07:42:25 +02:00
Merge branch 'beta' of https://github.com/pagefaultgames/pokerogue into ability_display
This commit is contained in:
commit
8c82ced5d6
42
CREDITS.md
42
CREDITS.md
@ -343,34 +343,39 @@ In addition to the lists below, please check [the PokéRogue wiki](https://wiki.
|
|||||||
|
|
||||||
# 💻 Development
|
# 💻 Development
|
||||||
|
|
||||||
## Server Owner/Administrator
|
## Server Developers
|
||||||
- pancakes aka patapancakes
|
- pancakes aka patapancakes
|
||||||
|
|
||||||
## Senior Developers
|
## Current and former Development Team members
|
||||||
- Walker
|
- bennybroseph
|
||||||
- NightKev
|
- Brain Frog
|
||||||
- Moka
|
|
||||||
- Temp aka Tempo-anon
|
|
||||||
- Madmadness65
|
|
||||||
|
|
||||||
## Developers
|
|
||||||
- CodeTappert
|
- CodeTappert
|
||||||
|
- Dakurei
|
||||||
- flx-sta
|
- flx-sta
|
||||||
- innerthunder
|
|
||||||
- frutescens
|
- frutescens
|
||||||
|
- Greenlamp
|
||||||
|
- ImperialSympathizer
|
||||||
|
- innerthunder
|
||||||
|
- KimJeongSun
|
||||||
|
- Madmadness65
|
||||||
|
- Moka
|
||||||
|
- Navori
|
||||||
|
- NightKev
|
||||||
- Opaquer
|
- Opaquer
|
||||||
|
- OrangeRed
|
||||||
|
- Sam aka Flashfyre (initial developer, started PokéRogue)
|
||||||
|
- sirzento
|
||||||
- SN34KZ
|
- SN34KZ
|
||||||
- Swain aka torranx
|
- Swain aka torranx
|
||||||
|
- Temp aka Tempo-anon
|
||||||
## Junior Developers
|
- Walker
|
||||||
- KimJeongSun
|
- Xavion
|
||||||
- ImperialSympathizer
|
|
||||||
|
|
||||||
## Bug/Issue Managers
|
## Bug/Issue Managers
|
||||||
- Snailman
|
|
||||||
- Daleks
|
- Daleks
|
||||||
- Lily
|
- Lily
|
||||||
- PigeonBar
|
- PigeonBar
|
||||||
|
- Snailman
|
||||||
|
|
||||||
## Other Code Contributors
|
## Other Code Contributors
|
||||||
- Admiral-Billy
|
- Admiral-Billy
|
||||||
@ -378,10 +383,7 @@ In addition to the lists below, please check [the PokéRogue wiki](https://wiki.
|
|||||||
- arColm
|
- arColm
|
||||||
- Arxalc
|
- Arxalc
|
||||||
- AsdarDevelops
|
- AsdarDevelops
|
||||||
- bennybroseph
|
|
||||||
- Brain Frog
|
|
||||||
- Corrade
|
- Corrade
|
||||||
- Dakurei
|
|
||||||
- DustinLin
|
- DustinLin
|
||||||
- ElizaAlex
|
- ElizaAlex
|
||||||
- EmberCM
|
- EmberCM
|
||||||
@ -391,7 +393,6 @@ In addition to the lists below, please check [the PokéRogue wiki](https://wiki.
|
|||||||
- francktrouillez
|
- francktrouillez
|
||||||
- FredeX
|
- FredeX
|
||||||
- geeilhan
|
- geeilhan
|
||||||
- Greenlamp
|
|
||||||
- happinyz
|
- happinyz
|
||||||
- hayuna
|
- hayuna
|
||||||
- InfernoVulpix
|
- InfernoVulpix
|
||||||
@ -411,7 +412,6 @@ In addition to the lists below, please check [the PokéRogue wiki](https://wiki.
|
|||||||
- Neverblade
|
- Neverblade
|
||||||
- NxKarim
|
- NxKarim
|
||||||
- okimin
|
- okimin
|
||||||
- OrangeRed
|
|
||||||
- PigeonBar
|
- PigeonBar
|
||||||
- PrabbyDD
|
- PrabbyDD
|
||||||
- prateau
|
- prateau
|
||||||
@ -421,10 +421,8 @@ In addition to the lists below, please check [the PokéRogue wiki](https://wiki.
|
|||||||
- RedstonewolfX
|
- RedstonewolfX
|
||||||
- ReneGV
|
- ReneGV
|
||||||
- rnicar245
|
- rnicar245
|
||||||
- Sam aka Flashfyre (initial developer, started PokéRogue)
|
|
||||||
- schmidtc1
|
- schmidtc1
|
||||||
- shayebeadling
|
- shayebeadling
|
||||||
- sirzento
|
|
||||||
- snoozbuster
|
- snoozbuster
|
||||||
- sodaMelon
|
- sodaMelon
|
||||||
- td76099
|
- td76099
|
||||||
|
@ -2740,56 +2740,6 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out.
|
|
||||||
*/
|
|
||||||
export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
|
|
||||||
private turnOffWeather: boolean;
|
|
||||||
|
|
||||||
canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
|
||||||
this.turnOffWeather = false;
|
|
||||||
|
|
||||||
const weatherType = globalScene.arena.weather?.weatherType;
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
return 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) {
|
|
||||||
return 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) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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> {
|
|
||||||
if (!simulated) {
|
|
||||||
globalScene.arena.trySetWeather(WeatherType.NONE, false);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
|
export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
|
||||||
canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||||
return !pokemon.isFullHp();
|
return !pokemon.isFullHp();
|
||||||
@ -2840,6 +2790,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 {
|
||||||
canApplyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
canApplyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
return true;
|
return true;
|
||||||
@ -4338,56 +4343,6 @@ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAtt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon fainting
|
|
||||||
*/
|
|
||||||
export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr {
|
|
||||||
canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean {
|
|
||||||
const weatherType = globalScene.arena.weather?.weatherType;
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (WeatherType.HEAVY_RAIN):
|
|
||||||
if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA)
|
|
||||||
&& globalScene.getField(true).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (WeatherType.STRONG_WINDS):
|
|
||||||
if (pokemon.hasAbility(Abilities.DELTA_STREAM)
|
|
||||||
&& globalScene.getField(true).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 {
|
|
||||||
if (!simulated) {
|
|
||||||
globalScene.arena.trySetWeather(WeatherType.NONE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
||||||
private damageRatio: number;
|
private damageRatio: number;
|
||||||
|
|
||||||
@ -5439,6 +5394,11 @@ export function applyPreSwitchOutAbAttrs(attrType: Constructor<PreSwitchOutAbAtt
|
|||||||
(attr, passive) => attr.canApplyPreSwitchOut(pokemon, passive, simulated, args), args, true, simulated);
|
(attr, passive) => attr.canApplyPreSwitchOut(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),
|
return applyAbAttrsInternal<PreStatStageChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||||
@ -6165,20 +6125,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);
|
||||||
|
@ -168,6 +168,7 @@ async function doBiomeTransitionDialogueAndBattleInit() {
|
|||||||
// Show dialogue and transition biome
|
// Show dialogue and transition biome
|
||||||
await showEncounterText(`${namespace}:transport`);
|
await showEncounterText(`${namespace}:transport`);
|
||||||
await Promise.all([ animateBiomeChange(newBiome), transitionMysteryEncounterIntroVisuals() ]);
|
await Promise.all([ animateBiomeChange(newBiome), transitionMysteryEncounterIntroVisuals() ]);
|
||||||
|
globalScene.updateBiomeWaveText();
|
||||||
globalScene.playBgm();
|
globalScene.playBgm();
|
||||||
await showEncounterText(`${namespace}:attacked`);
|
await showEncounterText(`${namespace}:attacked`);
|
||||||
|
|
||||||
|
@ -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…
Reference in New Issue
Block a user