mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-05 07:52:17 +02:00
Wimp Out changes
This commit is contained in:
parent
37ad950093
commit
1f7c67423b
@ -21,6 +21,7 @@ import {
|
||||
NeutralDamageAgainstFlyingTypeMultiplierAttr,
|
||||
FixedDamageAttr,
|
||||
type MoveAttr,
|
||||
ForceSwitchOutAttr,
|
||||
} from "#app/data/moves/move";
|
||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
@ -3662,7 +3663,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr {
|
||||
/**
|
||||
* Condition function to applied to abilities related to Sheer Force.
|
||||
* Checks if last move used against target was affected by a Sheer Force user and:
|
||||
* Disables: Color Change, Pickpocket, Berserk, Anger Shell
|
||||
* Disables: Color Change, Pickpocket, Berserk, Anger Shell, Wimp Out and Emergency Exit.
|
||||
* @returns An {@linkcode AbAttrCondition} to disable the ability under the proper conditions.
|
||||
*/
|
||||
function getSheerForceHitDisableAbCondition(): AbAttrCondition {
|
||||
@ -5547,12 +5548,12 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
|
||||
* Shell Bell's modifier (if any).
|
||||
*
|
||||
* @param pokemon - The Pokémon whose Shell Bell recovery is being calculated.
|
||||
* @returns The amount of health recovered by Shell Bell.
|
||||
* @returns The amount of health recovered by Shell Bell, or `0` if none are present.
|
||||
*/
|
||||
function calculateShellBellRecovery(pokemon: Pokemon): number {
|
||||
const shellBellModifier = pokemon.getHeldItems().find(m => m instanceof HitHealModifier);
|
||||
if (shellBellModifier) {
|
||||
return toDmgValue(pokemon.turnData.totalDamageDealt / 8) * shellBellModifier.stackCount;
|
||||
return toDmgValue(pokemon.turnData.lastMoveDamageDealt / 8) * shellBellModifier.stackCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -5565,20 +5566,19 @@ export class PostDamageAbAttr extends AbAttr {
|
||||
public canApplyPostDamage(
|
||||
pokemon: Pokemon,
|
||||
damage: number,
|
||||
passive: boolean,
|
||||
simulated: boolean,
|
||||
args: any[],
|
||||
source?: Pokemon): boolean {
|
||||
source: Pokemon | undefined,
|
||||
args: any[]
|
||||
): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public applyPostDamage(
|
||||
pokemon: Pokemon,
|
||||
damage: number,
|
||||
passive: boolean,
|
||||
simulated: boolean,
|
||||
args: any[],
|
||||
source?: Pokemon,
|
||||
source: Pokemon | undefined,
|
||||
args: any[]
|
||||
): void {}
|
||||
}
|
||||
|
||||
@ -5587,75 +5587,65 @@ export class PostDamageAbAttr extends AbAttr {
|
||||
* This attribute checks various conditions related to the damage received, the moves used by the Pokémon
|
||||
* and its opponents, and determines whether a forced switch-out should occur.
|
||||
*
|
||||
* Used by Wimp Out and Emergency Exit
|
||||
* Used for Wimp Out and Emergency Exit
|
||||
*
|
||||
* @extends PostDamageAbAttr
|
||||
* @see {@linkcode applyPostDamage}
|
||||
*/
|
||||
export class PostDamageForceSwitchAbAttr extends ForceSwitch(PostDamageAbAttr) {
|
||||
private hpRatio: number;
|
||||
|
||||
constructor(selfSwitch = true, switchType: NormalSwitchType = SwitchType.SWITCH, hpRatio = 0.5) {
|
||||
constructor(switchType: NormalSwitchType = SwitchType.SWITCH, hpRatio = 0.5) {
|
||||
super();
|
||||
this.selfSwitch = selfSwitch;
|
||||
this.selfSwitch = false; // TODO: change if any abilities get damage
|
||||
this.switchType = switchType;
|
||||
this.hpRatio = hpRatio;
|
||||
}
|
||||
|
||||
// TODO: Refactor to use more early returns
|
||||
/**
|
||||
* Check to see if the user should be switched out after taking damage.
|
||||
* @param pokemon - The {@linkcode Pokemon} with this ability; will be switched out if conditions are met.
|
||||
* @param damage - The amount of damage dealt by the triggering damage instance.
|
||||
* @param _simulated - unused
|
||||
* @param source - The {@linkcode Pokemon} having damaged the user with an attack, or `undefined`
|
||||
* if the damage source was indirect.
|
||||
* @param _args - unused
|
||||
* @returns Whether this pokemon should be switched out upon move conclusion.
|
||||
*/
|
||||
public override canApplyPostDamage(
|
||||
pokemon: Pokemon,
|
||||
damage: number,
|
||||
passive: boolean,
|
||||
simulated: boolean,
|
||||
args: any[],
|
||||
source?: Pokemon): boolean {
|
||||
const moveHistory = pokemon.getMoveHistory();
|
||||
_simulated: boolean,
|
||||
source: Pokemon | undefined,
|
||||
_args: any[],
|
||||
): boolean {
|
||||
const userLastMove = pokemon.getLastXMoves()[0];
|
||||
// Will not activate when the Pokémon's HP is lowered by cutting its own HP
|
||||
const fordbiddenAttackingMoves = [ Moves.BELLY_DRUM, Moves.SUBSTITUTE, Moves.CURSE, Moves.PAIN_SPLIT ];
|
||||
if (moveHistory.length > 0) {
|
||||
const lastMoveUsed = moveHistory[moveHistory.length - 1];
|
||||
if (fordbiddenAttackingMoves.includes(lastMoveUsed.move)) {
|
||||
return false;
|
||||
}
|
||||
const forbiddenAttackingMoves = new Set<Moves>([ Moves.BELLY_DRUM, Moves.SUBSTITUTE, Moves.CURSE, Moves.PAIN_SPLIT ]);
|
||||
if (!isNullOrUndefined(userLastMove) && forbiddenAttackingMoves.has(userLastMove.move)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.
|
||||
const fordbiddenDefendingMoves = [ Moves.DRAGON_TAIL, Moves.CIRCLE_THROW ];
|
||||
if (source) {
|
||||
const enemyMoveHistory = source.getMoveHistory();
|
||||
if (enemyMoveHistory.length > 0) {
|
||||
const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
|
||||
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop.
|
||||
if (fordbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || enemyLastMoveUsed.move === Moves.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) {
|
||||
return false;
|
||||
// Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force.
|
||||
// TODO: Make this use the sheer force disable condition
|
||||
} else if (allMoves[enemyLastMoveUsed.move].chance >= 0 && source.hasAbility(Abilities.SHEER_FORCE)) {
|
||||
return false;
|
||||
// Activate only after the last hit of multistrike moves
|
||||
} else if (source.turnData.hitsLeft > 1) {
|
||||
return false;
|
||||
}
|
||||
if (source.turnData.hitCount > 1) {
|
||||
damage = pokemon.turnData.damageTaken;
|
||||
}
|
||||
}
|
||||
// Skip last move checks if no enemy move
|
||||
const lastMove = source?.getLastXMoves()[0]
|
||||
if (
|
||||
lastMove &&
|
||||
// Will not activate for forced switch moves (triggers before wimp out activates)
|
||||
(allMoves[lastMove.move].hasAttr(ForceSwitchOutAttr)
|
||||
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop
|
||||
// TODO: Make this check the user's tags rather than the last move used by the target - we could be lifted by another pokemon
|
||||
|| (lastMove.move === Moves.SKY_DROP && lastMove.result === MoveResult.OTHER))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pokemon.hp + damage >= pokemon.getMaxHp() * this.hpRatio) {
|
||||
const shellBellHeal = calculateShellBellRecovery(pokemon);
|
||||
if (pokemon.hp - shellBellHeal < pokemon.getMaxHp() * this.hpRatio) {
|
||||
for (const opponent of pokemon.getOpponents()) {
|
||||
if (!this.canSwitchOut(pokemon, opponent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Check for HP percents - don't switch if the move didn't knock us below our switch threshold
|
||||
// (either because we were below it to begin with or are still above it after the hit).
|
||||
const hpNeededToSwitch = pokemon.getMaxHp() * this.hpRatio;
|
||||
if (pokemon.hp + damage < hpNeededToSwitch || pokemon.hp >= hpNeededToSwitch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return this.canSwitchOut(pokemon, oppponent)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5663,7 +5653,7 @@ export class PostDamageForceSwitchAbAttr extends ForceSwitch(PostDamageAbAttr) {
|
||||
*
|
||||
* @param pokemon The Pokémon that took damage.
|
||||
*/
|
||||
public override applyPostDamage(pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean, args: any[], source?: Pokemon): void {
|
||||
public override applyPostDamage(pokemon: Pokemon, _damage: number, _simulated: boolean, _source: Pokemon | undefined, args: any[]): void {
|
||||
this.doSwitch(pokemon);
|
||||
}
|
||||
}
|
||||
@ -5837,16 +5827,15 @@ export function applyPostDamageAbAttrs(
|
||||
attrType: Constructor<PostDamageAbAttr>,
|
||||
pokemon: Pokemon,
|
||||
damage: number,
|
||||
passive: boolean,
|
||||
simulated = false,
|
||||
args: any[],
|
||||
source?: Pokemon,
|
||||
source: Pokemon | undefined = undefined,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal<PostDamageAbAttr>(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => attr.applyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||
(attr, passive) => attr.canApplyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||
(attr, passive) => attr.applyPostDamage(pokemon, damage, simulated, source, args),
|
||||
(attr, passive) => attr.canApplyPostDamage(pokemon, damage, simulated, source, args),
|
||||
args,
|
||||
);
|
||||
}
|
||||
@ -6942,9 +6931,11 @@ export function initAbilities() {
|
||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
||||
new Ability(Abilities.WIMP_OUT, 7)
|
||||
.attr(PostDamageForceSwitchAbAttr)
|
||||
.condition(getSheerForceHitDisableAbCondition())
|
||||
.edgeCase(), // Should not trigger when hurting itself in confusion, causes Fake Out to fail turn 1 and succeed turn 2 if pokemon is switched out before battle start via playing in Switch Mode
|
||||
new Ability(Abilities.EMERGENCY_EXIT, 7)
|
||||
.attr(PostDamageForceSwitchAbAttr)
|
||||
.condition(getSheerForceHitDisableAbCondition())
|
||||
.edgeCase(), // Should not trigger when hurting itself in confusion, causes Fake Out to fail turn 1 and succeed turn 2 if pokemon is switched out before battle start via playing in Switch Mode
|
||||
new Ability(Abilities.WATER_COMPACTION, 7)
|
||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
||||
|
@ -1551,6 +1551,7 @@ export class MatchHpAttr extends FixedDamageAttr {
|
||||
|
||||
type MoveFilter = (move: Move) => boolean;
|
||||
|
||||
// TODO: fix this to check the last direct damage instance taken
|
||||
export class CounterDamageAttr extends FixedDamageAttr {
|
||||
private moveFilter: MoveFilter;
|
||||
private multiplier: number;
|
||||
@ -1664,8 +1665,8 @@ export class RecoilAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
|
||||
const damageValue = (!this.useHp ? user.turnData.totalDamageDealt : user.getMaxHp()) * this.damageRatio;
|
||||
const minValue = user.turnData.totalDamageDealt ? 1 : 0;
|
||||
const damageValue = (!this.useHp ? user.turnData.lastMoveDamageDealt : user.getMaxHp()) * this.damageRatio;
|
||||
const minValue = user.turnData.lastMoveDamageDealt ? 1 : 0;
|
||||
const recoilDamage = toDmgValue(damageValue, minValue);
|
||||
if (!recoilDamage) {
|
||||
return false;
|
||||
|
@ -342,6 +342,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
public luck: number;
|
||||
public pauseEvolutions: boolean;
|
||||
public pokerus: boolean;
|
||||
// TODO: Document these
|
||||
public switchOutStatus = false;
|
||||
public evoCounter: number;
|
||||
public teraType: PokemonType;
|
||||
@ -4748,13 +4749,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* Given the damage, adds a new DamagePhase and update HP values, etc.
|
||||
*
|
||||
* Checks for 'Indirect' HitResults to account for Endure/Reviver Seed applying correctly
|
||||
* @param damage integer - passed to damage()
|
||||
* @param result an enum if it's super effective, not very, etc.
|
||||
* @param isCritical boolean if move is a critical hit
|
||||
* @param ignoreSegments boolean, passed to damage() and not used currently
|
||||
* @param preventEndure boolean, ignore endure properties of pokemon, passed to damage()
|
||||
* @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage()
|
||||
* @returns integer of damage done
|
||||
* @param damage - Amount of damage to deal
|
||||
* @param result - The {@linkcode HitResult} of the damage instance; default `HitResult.EFFECTIVE`
|
||||
* @param isCritical - Whether the move being used (if any) was a critical hit; default `false`
|
||||
* @param ignoreSegments - Whether to ignore boss segments; default `false` and currently unused
|
||||
* @param preventEndure - Whether to ignore {@linkcode Moves.ENDURE} and similar effects when applying damage; default `false`
|
||||
* @param ignoreFaintPhase - Whether to ignore adding a faint phase if the damage causes the target to faint; default `false`
|
||||
* @returns The amount of damage actually dealt.
|
||||
* @remarks
|
||||
* This will not trigger "on damage" effects for direct damage moves, which instead occurs at the end of `MoveEffectPhase`.
|
||||
*/
|
||||
damageAndUpdate(damage: number,
|
||||
{
|
||||
@ -4762,53 +4765,52 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
isCritical = false,
|
||||
ignoreSegments = false,
|
||||
ignoreFaintPhase = false,
|
||||
source = undefined,
|
||||
}:
|
||||
{
|
||||
result?: DamageResult,
|
||||
isCritical?: boolean,
|
||||
ignoreSegments?: boolean,
|
||||
ignoreFaintPhase?: boolean,
|
||||
source?: Pokemon,
|
||||
} = {}
|
||||
): number {
|
||||
const isIndirectDamage = [ HitResult.INDIRECT, HitResult.INDIRECT_KO ].includes(result);
|
||||
const damagePhase = new DamageAnimPhase(
|
||||
this.getBattlerIndex(),
|
||||
damage,
|
||||
result as DamageResult,
|
||||
result,
|
||||
isCritical
|
||||
);
|
||||
globalScene.unshiftPhase(damagePhase);
|
||||
if (this.switchOutStatus && source) {
|
||||
|
||||
// TODO: Review if wimp out battle skip actually needs this anymore
|
||||
if (this.switchOutStatus) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
damage = this.damage(
|
||||
damage,
|
||||
ignoreSegments,
|
||||
isIndirectDamage,
|
||||
ignoreFaintPhase,
|
||||
);
|
||||
|
||||
// Ensure the battle-info bar's HP is updated, though only if the battle info is visible
|
||||
// TODO: When battle-info UI is refactored, make this only update the HP bar
|
||||
if (this.battleInfo.visible) {
|
||||
this.updateInfo();
|
||||
}
|
||||
|
||||
// Damage amount may have changed, but needed to be queued before calling damage function
|
||||
damagePhase.updateAmount(damage);
|
||||
/**
|
||||
* Run PostDamageAbAttr from any source of damage that is not from a multi-hit
|
||||
* Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr
|
||||
*/
|
||||
if (!source || source.turnData.hitCount <= 1) {
|
||||
|
||||
// Trigger PostDamageAbAttr (ie wimp out) for indirect damage only.
|
||||
if (isIndirectDamage) {
|
||||
applyPostDamageAbAttrs(
|
||||
PostDamageAbAttr,
|
||||
this,
|
||||
damage,
|
||||
this.hasPassive(),
|
||||
false,
|
||||
[],
|
||||
source,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
return damage;
|
||||
@ -7957,8 +7959,13 @@ export class PokemonTurnData {
|
||||
* - `0` = Move is finished
|
||||
*/
|
||||
public hitsLeft = -1;
|
||||
public totalDamageDealt = 0;
|
||||
/**
|
||||
* The amount of damage dealt by this Pokemon's last attack.
|
||||
* Reset upon successfully using a move and used to enable internal tracking of damage amounts.
|
||||
*/
|
||||
public lastMoveDamageDealt = 0;
|
||||
public singleHitDamageDealt = 0;
|
||||
// TODO: Rework this into "damage taken last" counter for metal burst and co.
|
||||
public damageTaken = 0;
|
||||
public attacksReceived: AttackMoveResult[] = [];
|
||||
public order: number;
|
||||
@ -8007,7 +8014,7 @@ export enum HitResult {
|
||||
FAIL,
|
||||
MISS,
|
||||
INDIRECT,
|
||||
IMMUNE,
|
||||
IMMUNE, // TODO: Why is this used exclusively for sheer cold?
|
||||
CONFUSION,
|
||||
INDIRECT_KO,
|
||||
}
|
||||
|
@ -1783,12 +1783,12 @@ export class HitHealModifier extends PokemonHeldItemModifier {
|
||||
* @returns `true` if the {@linkcode Pokemon} was healed
|
||||
*/
|
||||
override apply(pokemon: Pokemon): boolean {
|
||||
if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) {
|
||||
if (pokemon.turnData.lastMoveDamageDealt && !pokemon.isFullHp()) {
|
||||
// TODO: this shouldn't be undefined AFAIK
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount,
|
||||
toDmgValue(pokemon.turnData.lastMoveDamageDealt / 8) * this.stackCount,
|
||||
i18next.t("modifier:hitHealApply", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
typeName: this.type.name,
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
IgnoreMoveEffectsAbAttr,
|
||||
MaxMultiHitAbAttr,
|
||||
PostAttackAbAttr,
|
||||
PostDamageAbAttr,
|
||||
PostDefendAbAttr,
|
||||
ReflectStatusMoveAbAttr,
|
||||
} from "#app/data/abilities/ability";
|
||||
@ -48,7 +47,7 @@ import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { DamageResult, PokemonMove, type TurnMove } from "#app/field/pokemon";
|
||||
import { type DamageResult, PokemonMove, type TurnMove } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { HitResult, MoveResult } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
@ -72,7 +71,7 @@ import { ShowAbilityPhase } from "./show-ability-phase";
|
||||
import { MovePhase } from "./move-phase";
|
||||
import { MoveEndPhase } from "./move-end-phase";
|
||||
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
|
||||
import { TypeDamageMultiplier } from "#app/data/type";
|
||||
import type { TypeDamageMultiplier } from "#app/data/type";
|
||||
import { HitCheckResult } from "#enums/hit-check-result";
|
||||
import type Move from "#app/data/moves/move";
|
||||
import { isFieldTargeted } from "#app/data/moves/move-utils";
|
||||
@ -101,6 +100,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
/** Phases queued during moves */
|
||||
private queuedPhases: Phase[] = [];
|
||||
|
||||
/** The amount of direct attack damage taken by each of this Phase's targets. */
|
||||
private targetDamageTaken: number[] = [];
|
||||
|
||||
/**
|
||||
* @param reflected Indicates that the move was reflected by the user due to magic coat or magic bounce
|
||||
* @param virtual Indicates that the move is a virtual move (i.e. called by metronome)
|
||||
@ -123,6 +125,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
this.targets = targets;
|
||||
|
||||
this.hitChecks = Array(this.targets.length).fill([HitCheckResult.PENDING, 0]);
|
||||
this.targetDamageTaken = Array(this.targets.length).fill(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -785,11 +788,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
}
|
||||
if (this.lastHit) {
|
||||
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
||||
|
||||
// Multi-hit check for Wimp Out/Emergency Exit
|
||||
if (user.turnData.hitCount > 1) {
|
||||
applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -821,6 +819,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
isCritical,
|
||||
});
|
||||
|
||||
// Apply and/or remove type boosting tags (Flash Fire, Charge, etc.)
|
||||
const typeBoost = user.findTag(
|
||||
t => t instanceof TypeBoostTag && t.boostedType === user.getMoveType(this.move),
|
||||
) as TypeBoostTag;
|
||||
@ -828,18 +827,17 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
user.removeTag(typeBoost.tagType);
|
||||
}
|
||||
|
||||
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
||||
|
||||
if (!dmg) {
|
||||
if (dmg === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
||||
target.lapseTags(BattlerTagLapseType.HIT);
|
||||
|
||||
const substitute = target.getTag(SubstituteTag);
|
||||
const isBlockedBySubstitute = substitute && this.move.hitsSubstitute(user, target);
|
||||
const substituteTag = target.getTag(SubstituteTag);
|
||||
const isBlockedBySubstitute = substituteTag && this.move.hitsSubstitute(user, target);
|
||||
if (isBlockedBySubstitute) {
|
||||
substitute.hp -= dmg;
|
||||
substituteTag.hp -= dmg;
|
||||
} else if (!target.isPlayer() && dmg >= target.hp) {
|
||||
globalScene.applyModifiers(EnemyEndureChanceModifier, false, target);
|
||||
}
|
||||
@ -851,7 +849,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
ignoreFaintPhase: true,
|
||||
ignoreSegments: isOneHitKo,
|
||||
isCritical,
|
||||
source: user,
|
||||
});
|
||||
|
||||
if (isCritical) {
|
||||
@ -865,14 +862,13 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
if (user.isPlayer()) {
|
||||
globalScene.validateAchvs(DamageAchv, new NumberHolder(damage));
|
||||
|
||||
if (damage > globalScene.gameData.gameStats.highestDamage) {
|
||||
globalScene.gameData.gameStats.highestDamage = damage;
|
||||
}
|
||||
globalScene.gameData.gameStats.highestDamage = Math.max(damage, globalScene.gameData.gameStats.highestDamage);
|
||||
}
|
||||
|
||||
user.turnData.totalDamageDealt += damage;
|
||||
user.turnData.lastMoveDamageDealt += damage;
|
||||
user.turnData.singleHitDamageDealt = damage;
|
||||
target.battleData.hitCount++;
|
||||
// TODO: this might be incorrect for counter moves
|
||||
target.turnData.damageTaken += damage;
|
||||
|
||||
target.turnData.attacksReceived.unshift({
|
||||
|
@ -49,9 +49,10 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
||||
}
|
||||
if (damage.value) {
|
||||
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
||||
// TODO: why don't we call `damageAndUpdate` here?
|
||||
globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
||||
pokemon.updateInfo();
|
||||
applyPostDamageAbAttrs(PostDamageAbAttr, pokemon, damage.value, pokemon.hasPassive(), false, []);
|
||||
applyPostDamageAbAttrs(PostDamageAbAttr, pokemon, damage.value);
|
||||
}
|
||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end());
|
||||
} else {
|
||||
|
@ -37,7 +37,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
.enemyPassiveAbility(Abilities.NO_GUARD)
|
||||
.startingLevel(90)
|
||||
.enemyLevel(70)
|
||||
.moveset([Moves.SPLASH, Moves.FALSE_SWIPE, Moves.ENDURE])
|
||||
.moveset([Moves.SPLASH, Moves.FALSE_SWIPE, Moves.ENDURE, Moves.THUNDER_PUNCH])
|
||||
.enemyMoveset(Moves.FALSE_SWIPE)
|
||||
.disableCrits();
|
||||
});
|
||||
@ -66,7 +66,40 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(pokemon1.getHpRatio()).toBeLessThan(0.5);
|
||||
}
|
||||
|
||||
it("triggers regenerator passive single time when switching out with wimp out", async () => {
|
||||
it("should switch the user out when falling below half HP, canceling its subsequent moves", async () => {
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
wimpod.hp *= 0.52;
|
||||
|
||||
game.move.select(Moves.THUNDER_PUNCH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
// Wimpod switched out after taking a hit, canceling its upcoming MovePhase before it could attack
|
||||
confirmSwitch();
|
||||
expect(game.scene.getEnemyPokemon()!.getInverseHp()).toBe(0);
|
||||
expect(game.phaseInterceptor.log.reduce((count, phase) => count + (phase === "MoveEffectPhase" ? 1 : 0), 0)).toBe(
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it("should not trigger if user faints from damage", async () => {
|
||||
game.override.enemyMoveset(Moves.BRAVE_BIRD).enemyLevel(1000);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
wimpod.hp *= 0.52;
|
||||
|
||||
game.move.select(Moves.THUNDER_PUNCH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(wimpod.isFainted()).toBe(true);
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("should trigger regenerator passive when switching out", async () => {
|
||||
game.override.passiveAbility(Abilities.REGENERATOR).startingLevel(5).enemyLevel(100);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -80,7 +113,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("It makes wild pokemon flee if triggered", async () => {
|
||||
it("should cause wild pokemon to flee when triggered", async () => {
|
||||
game.override.enemyAbility(Abilities.WIMP_OUT);
|
||||
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
|
||||
|
||||
@ -95,7 +128,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(!isVisible && hasFled).toBe(true);
|
||||
});
|
||||
|
||||
it("Does not trigger when HP already below half", async () => {
|
||||
it("should not trigger when HP already below half", async () => {
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
wimpod.hp = 5;
|
||||
@ -107,7 +140,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("Trapping moves do not prevent Wimp Out from activating.", async () => {
|
||||
it("should bypass trapping moves", async () => {
|
||||
game.override.enemyMoveset([Moves.SPIRIT_SHACKLE]).startingLevel(53).enemyLevel(45);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -122,7 +155,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("If this Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out.", async () => {
|
||||
it("should block U-turn or Volt Switch on activation", async () => {
|
||||
game.override.startingLevel(95).enemyMoveset([Moves.U_TURN]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -136,7 +169,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("If this Ability does not activate due to being hit by U-turn or Volt Switch, the user of that move will be switched out.", async () => {
|
||||
it("should not block U-turn or Volt Switch if not activated", async () => {
|
||||
game.override.startingLevel(190).startingWave(8).enemyMoveset([Moves.U_TURN]);
|
||||
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
|
||||
const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id;
|
||||
@ -145,7 +178,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(game.scene.getEnemyPokemon()?.id !== RIVAL_NINJASK1);
|
||||
});
|
||||
|
||||
it("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.", async () => {
|
||||
it("should not activate from Dragon Tail and Circle Throw", async () => {
|
||||
game.override.startingLevel(69).enemyMoveset([Moves.DRAGON_TAIL]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -162,81 +195,41 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).not.toBe(Species.WIMPOD);
|
||||
});
|
||||
|
||||
it("triggers when recoil damage is taken", async () => {
|
||||
game.override.moveset([Moves.HEAD_SMASH]).enemyMoveset([Moves.SPLASH]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
game.move.select(Moves.HEAD_SMASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("It does not activate when the Pokémon cuts its own HP", async () => {
|
||||
game.override.moveset([Moves.SUBSTITUTE]).enemyMoveset([Moves.SPLASH]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
wimpod.hp *= 0.52;
|
||||
|
||||
game.move.select(Moves.SUBSTITUTE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("Does not trigger when neutralized", async () => {
|
||||
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS).startingLevel(5);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
// TODO: Enable when this behavior is fixed (currently Shell Bell won't activate if Wimp Out activates because
|
||||
// the pokemon is removed from the field before the Shell Bell modifier is applied, so it can't see the
|
||||
// damage dealt and doesn't heal the pokemon)
|
||||
it.todo(
|
||||
"If it falls below half and recovers back above half from a Shell Bell, Wimp Out will activate even after the Shell Bell recovery",
|
||||
async () => {
|
||||
it.each<{ type: string; enemyMove?: Moves; enemyAbility?: Abilities }>([
|
||||
{ type: "weather", enemyMove: Moves.HAIL },
|
||||
{ type: "status", enemyMove: Moves.TOXIC },
|
||||
{ type: "Curse", enemyMove: Moves.CURSE },
|
||||
{ type: "Salt Cure", enemyMove: Moves.SALT_CURE },
|
||||
{ type: "partial trapping moves", enemyMove: Moves.WHIRLPOOL },
|
||||
{ type: "Leech Seed", enemyMove: Moves.LEECH_SEED },
|
||||
{ type: "Nightmare", enemyMove: Moves.NIGHTMARE },
|
||||
{ type: "Aftermath", enemyAbility: Abilities.AFTERMATH },
|
||||
{ type: "Bad Dreams", enemyAbility: Abilities.BAD_DREAMS },
|
||||
])(
|
||||
"should activate from damage caused by $name",
|
||||
async ({ enemyMove = Moves.SPLASH, enemyAbility = Abilities.BALL_FETCH }) => {
|
||||
game.override
|
||||
.moveset([Moves.DOUBLE_EDGE])
|
||||
.enemyMoveset([Moves.SPLASH])
|
||||
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]);
|
||||
.passiveAbility(Abilities.COMATOSE)
|
||||
.enemySpecies(Species.GASTLY)
|
||||
.enemyMoveset(enemyMove)
|
||||
.enemyAbility(enemyAbility)
|
||||
.enemyLevel(1);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
expect(wimpod).toBeDefined();
|
||||
wimpod.hp *= 0.55;
|
||||
|
||||
wimpod.damageAndUpdate(toDmgValue(wimpod.getMaxHp() * 0.4));
|
||||
|
||||
game.move.select(Moves.DOUBLE_EDGE);
|
||||
game.move.select(Moves.THUNDER_PUNCH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(game.scene.getPlayerParty()[1]).toBe(wimpod);
|
||||
expect(wimpod.hp).toBeGreaterThan(toDmgValue(wimpod.getMaxHp() / 2));
|
||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.TYRUNT);
|
||||
confirmSwitch();
|
||||
},
|
||||
);
|
||||
|
||||
it("Wimp Out will activate due to weather damage", async () => {
|
||||
game.override.weather(WeatherType.HAIL).enemyMoveset([Moves.SPLASH]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("Does not trigger when enemy has sheer force", async () => {
|
||||
it("should not trigger from Sheer Force-boosted moves", async () => {
|
||||
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -248,81 +241,79 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to post turn status damage", async () => {
|
||||
game.override.statusEffect(StatusEffect.POISON).enemyMoveset([Moves.SPLASH]);
|
||||
it("should trigger from recoil damage", async () => {
|
||||
game.override.moveset(Moves.HEAD_SMASH).enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.move.select(Moves.HEAD_SMASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to bad dreams", async () => {
|
||||
game.override.statusEffect(StatusEffect.SLEEP).enemyAbility(Abilities.BAD_DREAMS);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
it("should trigger from Flame Burst ally damage in doubles", async () => {
|
||||
game.override.battleStyle("double").enemyMoveset([Moves.FLAME_BURST, Moves.SPLASH]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.ZYGARDE, Species.TYRUNT]);
|
||||
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
expect(wimpod).toBeDefined();
|
||||
wimpod.hp *= 0.55;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
game.move.select(Moves.SPLASH, BattlerIndex.PLAYER);
|
||||
game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2);
|
||||
await game.forceEnemyMove(Moves.FLAME_BURST, BattlerIndex.PLAYER_2);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(2);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to leech seed", async () => {
|
||||
game.override.enemyMoveset([Moves.LEECH_SEED]);
|
||||
it("should not activate when the Pokémon cuts its own HP", async () => {
|
||||
game.override.moveset(Moves.SUBSTITUTE).enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
wimpod.hp *= 0.52;
|
||||
|
||||
confirmSwitch();
|
||||
game.move.select(Moves.SUBSTITUTE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to curse damage", async () => {
|
||||
game.override.enemySpecies(Species.DUSKNOIR).enemyMoveset([Moves.CURSE]);
|
||||
it("should not trigger when neutralized", async () => {
|
||||
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS).startingLevel(5);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to salt cure damage", async () => {
|
||||
game.override.enemySpecies(Species.NACLI).enemyMoveset([Moves.SALT_CURE]).enemyLevel(1);
|
||||
it("should disregard Shell Bell recovery while still activating it before switching", async () => {
|
||||
game.override
|
||||
.moveset([Moves.DOUBLE_EDGE])
|
||||
.enemyMoveset([Moves.SPLASH])
|
||||
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.7;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
const wimpod = game.scene.getPlayerPokemon()!;
|
||||
wimpod.hp *= 0.51;
|
||||
|
||||
game.move.select(Moves.DOUBLE_EDGE);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
expect(game.scene.getPlayerParty()[1]).toBe(wimpod);
|
||||
expect(wimpod.hp).toBeGreaterThan(toDmgValue(wimpod.getMaxHp() / 2));
|
||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.TYRUNT);
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to damaging trap damage", async () => {
|
||||
game.override.enemySpecies(Species.MAGIKARP).enemyMoveset([Moves.WHIRLPOOL]).enemyLevel(1);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.55;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("Magic Guard passive should not allow indirect damage to trigger Wimp Out", async () => {
|
||||
it("should not switch if Magic Guard prevents damage", async () => {
|
||||
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
|
||||
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
||||
game.override
|
||||
@ -341,7 +332,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.WIMPOD);
|
||||
});
|
||||
|
||||
it("Wimp Out activating should not cancel a double battle", async () => {
|
||||
it("should not cancel a double battle on activation", async () => {
|
||||
game.override.battleStyle("double").enemyAbility(Abilities.WIMP_OUT).enemyMoveset([Moves.SPLASH]).enemyLevel(1);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
const enemyLeadPokemon = game.scene.getEnemyParty()[0];
|
||||
@ -361,24 +352,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to aftermath", async () => {
|
||||
game.override
|
||||
.moveset([Moves.THUNDER_PUNCH])
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.AFTERMATH)
|
||||
.enemyMoveset([Moves.SPLASH])
|
||||
.enemyLevel(1);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||
|
||||
game.move.select(Moves.THUNDER_PUNCH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("Activates due to entry hazards", async () => {
|
||||
it("should activate from entry hazard damage", async () => {
|
||||
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
|
||||
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
||||
game.override.enemySpecies(Species.CENTISKORCH).enemyAbility(Abilities.WIMP_OUT).startingWave(4);
|
||||
@ -388,18 +362,6 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(game.phaseInterceptor.log).toContain("BattleEndPhase");
|
||||
});
|
||||
|
||||
it("Wimp Out will activate due to Nightmare", async () => {
|
||||
game.override.enemyMoveset([Moves.NIGHTMARE]).statusEffect(StatusEffect.SLEEP);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
game.scene.getPlayerPokemon()!.hp *= 0.65;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("triggers status on the wimp out user before a new pokemon is switched in", async () => {
|
||||
game.override.enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(80);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
@ -413,7 +375,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("triggers after last hit of multi hit move", async () => {
|
||||
it("triggers after last hit of multi hit moves", async () => {
|
||||
game.override.enemyMoveset(Moves.BULLET_SEED).enemyAbility(Abilities.SKILL_LINK);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -429,7 +391,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("triggers after last hit of multi hit move (multi lens)", async () => {
|
||||
it("triggers after last hit of multi hit moves from multi lens", async () => {
|
||||
game.override.enemyMoveset(Moves.TACKLE).enemyHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
|
||||
@ -444,6 +406,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(enemyPokemon.turnData.hitCount).toBe(2);
|
||||
confirmSwitch();
|
||||
});
|
||||
|
||||
it("triggers after last hit of Parental Bond", async () => {
|
||||
game.override.enemyMoveset(Moves.TACKLE).enemyAbility(Abilities.PARENTAL_BOND);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
@ -461,26 +424,23 @@ describe("Abilities - Wimp Out", () => {
|
||||
});
|
||||
|
||||
// TODO: This interaction is not implemented yet
|
||||
it.todo(
|
||||
"Wimp Out will not activate if the Pokémon's HP falls below half due to hurting itself in confusion",
|
||||
async () => {
|
||||
game.override.moveset([Moves.SWORDS_DANCE]).enemyMoveset([Moves.SWAGGER]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.hp *= 0.51;
|
||||
playerPokemon.setStatStage(Stat.ATK, 6);
|
||||
playerPokemon.addTag(BattlerTagType.CONFUSED);
|
||||
it.todo("should not activate if the Pokémon's HP falls below half due to hurting itself in confusion", async () => {
|
||||
game.override.moveset([Moves.SWORDS_DANCE]).enemyMoveset([Moves.SWAGGER]);
|
||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.hp *= 0.51;
|
||||
playerPokemon.setStatStage(Stat.ATK, 6);
|
||||
playerPokemon.addTag(BattlerTagType.CONFUSED);
|
||||
|
||||
// TODO: add helper function to force confusion self-hits
|
||||
// TODO: add helper function to force confusion self-hits
|
||||
|
||||
while (playerPokemon.getHpRatio() > 0.49) {
|
||||
game.move.select(Moves.SWORDS_DANCE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
}
|
||||
while (playerPokemon.getHpRatio() > 0.49) {
|
||||
game.move.select(Moves.SWORDS_DANCE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
}
|
||||
|
||||
confirmNoSwitch();
|
||||
},
|
||||
);
|
||||
confirmNoSwitch();
|
||||
});
|
||||
|
||||
it("should not activate on wave X0 bosses", async () => {
|
||||
game.override.enemyAbility(Abilities.WIMP_OUT).startingLevel(5850).startingWave(10);
|
||||
@ -499,7 +459,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(isVisible && !hasFled).toBe(true);
|
||||
});
|
||||
|
||||
it("wimp out will not skip battles when triggered in a double battle", async () => {
|
||||
it("should not skip battles when triggered in a double battle", async () => {
|
||||
const wave = 2;
|
||||
game.override
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
@ -527,7 +487,7 @@ describe("Abilities - Wimp Out", () => {
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(wave + 1);
|
||||
});
|
||||
|
||||
it("wimp out should not skip battles when triggering the same turn as another enemy faints", async () => {
|
||||
it("should not skip battles when triggering the same turn as another enemy faints", async () => {
|
||||
const wave = 2;
|
||||
game.override
|
||||
.enemySpecies(Species.WIMPOD)
|
||||
|
@ -44,20 +44,18 @@ describe("Moves - Focus Punch", () => {
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
const enemyStartingHp = enemyPokemon.hp;
|
||||
|
||||
game.move.select(Moves.FOCUS_PUNCH);
|
||||
|
||||
await game.phaseInterceptor.to(MessagePhase);
|
||||
|
||||
expect(enemyPokemon.hp).toBe(enemyStartingHp);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(0);
|
||||
expect(leadPokemon.getMoveHistory().length).toBe(0);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(0);
|
||||
expect(leadPokemon.getMoveHistory().length).toBe(1);
|
||||
expect(leadPokemon.turnData.totalDamageDealt).toBe(enemyStartingHp - enemyPokemon.hp);
|
||||
expect(enemyPokemon.getInverseHp()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should fail if the user is hit", async () => {
|
||||
@ -72,16 +70,16 @@ describe("Moves - Focus Punch", () => {
|
||||
|
||||
game.move.select(Moves.FOCUS_PUNCH);
|
||||
|
||||
await game.phaseInterceptor.to(MessagePhase);
|
||||
await game.phaseInterceptor.to("MessagePhase");
|
||||
|
||||
expect(enemyPokemon.hp).toBe(enemyStartingHp);
|
||||
expect(leadPokemon.getMoveHistory().length).toBe(0);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(enemyPokemon.hp).toBe(enemyStartingHp);
|
||||
expect(leadPokemon.getMoveHistory().length).toBe(1);
|
||||
expect(leadPokemon.turnData.totalDamageDealt).toBe(0);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(0);
|
||||
});
|
||||
|
||||
it("should be cancelled if the user falls asleep mid-turn", async () => {
|
||||
|
@ -178,7 +178,7 @@ describe("Moves - Powder", () => {
|
||||
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||
// enemy should have taken damage from player's Fiery Dance + 2 Powder procs
|
||||
expect(enemyPokemon.hp).toBe(
|
||||
enemyStartingHp - playerPokemon.turnData.totalDamageDealt - 2 * Math.floor(enemyPokemon.getMaxHp() / 4),
|
||||
enemyStartingHp - playerPokemon.turnData.lastMoveDamageDealt - 2 * Math.floor(enemyPokemon.getMaxHp() / 4),
|
||||
);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user