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,
|
NeutralDamageAgainstFlyingTypeMultiplierAttr,
|
||||||
FixedDamageAttr,
|
FixedDamageAttr,
|
||||||
type MoveAttr,
|
type MoveAttr,
|
||||||
|
ForceSwitchOutAttr,
|
||||||
} from "#app/data/moves/move";
|
} from "#app/data/moves/move";
|
||||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||||
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
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.
|
* 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:
|
* 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.
|
* @returns An {@linkcode AbAttrCondition} to disable the ability under the proper conditions.
|
||||||
*/
|
*/
|
||||||
function getSheerForceHitDisableAbCondition(): AbAttrCondition {
|
function getSheerForceHitDisableAbCondition(): AbAttrCondition {
|
||||||
@ -5547,12 +5548,12 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
|
|||||||
* Shell Bell's modifier (if any).
|
* Shell Bell's modifier (if any).
|
||||||
*
|
*
|
||||||
* @param pokemon - The Pokémon whose Shell Bell recovery is being calculated.
|
* @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 {
|
function calculateShellBellRecovery(pokemon: Pokemon): number {
|
||||||
const shellBellModifier = pokemon.getHeldItems().find(m => m instanceof HitHealModifier);
|
const shellBellModifier = pokemon.getHeldItems().find(m => m instanceof HitHealModifier);
|
||||||
if (shellBellModifier) {
|
if (shellBellModifier) {
|
||||||
return toDmgValue(pokemon.turnData.totalDamageDealt / 8) * shellBellModifier.stackCount;
|
return toDmgValue(pokemon.turnData.lastMoveDamageDealt / 8) * shellBellModifier.stackCount;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -5565,20 +5566,19 @@ export class PostDamageAbAttr extends AbAttr {
|
|||||||
public canApplyPostDamage(
|
public canApplyPostDamage(
|
||||||
pokemon: Pokemon,
|
pokemon: Pokemon,
|
||||||
damage: number,
|
damage: number,
|
||||||
passive: boolean,
|
|
||||||
simulated: boolean,
|
simulated: boolean,
|
||||||
args: any[],
|
source: Pokemon | undefined,
|
||||||
source?: Pokemon): boolean {
|
args: any[]
|
||||||
|
): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public applyPostDamage(
|
public applyPostDamage(
|
||||||
pokemon: Pokemon,
|
pokemon: Pokemon,
|
||||||
damage: number,
|
damage: number,
|
||||||
passive: boolean,
|
|
||||||
simulated: boolean,
|
simulated: boolean,
|
||||||
args: any[],
|
source: Pokemon | undefined,
|
||||||
source?: Pokemon,
|
args: any[]
|
||||||
): void {}
|
): 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
|
* 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.
|
* 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}
|
* @see {@linkcode applyPostDamage}
|
||||||
*/
|
*/
|
||||||
export class PostDamageForceSwitchAbAttr extends ForceSwitch(PostDamageAbAttr) {
|
export class PostDamageForceSwitchAbAttr extends ForceSwitch(PostDamageAbAttr) {
|
||||||
private hpRatio: number;
|
private hpRatio: number;
|
||||||
|
|
||||||
constructor(selfSwitch = true, switchType: NormalSwitchType = SwitchType.SWITCH, hpRatio = 0.5) {
|
constructor(switchType: NormalSwitchType = SwitchType.SWITCH, hpRatio = 0.5) {
|
||||||
super();
|
super();
|
||||||
this.selfSwitch = selfSwitch;
|
this.selfSwitch = false; // TODO: change if any abilities get damage
|
||||||
this.switchType = switchType;
|
this.switchType = switchType;
|
||||||
this.hpRatio = hpRatio;
|
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(
|
public override canApplyPostDamage(
|
||||||
pokemon: Pokemon,
|
pokemon: Pokemon,
|
||||||
damage: number,
|
damage: number,
|
||||||
passive: boolean,
|
_simulated: boolean,
|
||||||
simulated: boolean,
|
source: Pokemon | undefined,
|
||||||
args: any[],
|
_args: any[],
|
||||||
source?: Pokemon): boolean {
|
): boolean {
|
||||||
const moveHistory = pokemon.getMoveHistory();
|
const userLastMove = pokemon.getLastXMoves()[0];
|
||||||
// Will not activate when the Pokémon's HP is lowered by cutting its own HP
|
// 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 ];
|
const forbiddenAttackingMoves = new Set<Moves>([ Moves.BELLY_DRUM, Moves.SUBSTITUTE, Moves.CURSE, Moves.PAIN_SPLIT ]);
|
||||||
if (moveHistory.length > 0) {
|
if (!isNullOrUndefined(userLastMove) && forbiddenAttackingMoves.has(userLastMove.move)) {
|
||||||
const lastMoveUsed = moveHistory[moveHistory.length - 1];
|
return false;
|
||||||
if (fordbiddenAttackingMoves.includes(lastMoveUsed.move)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.
|
// Skip last move checks if no enemy move
|
||||||
const fordbiddenDefendingMoves = [ Moves.DRAGON_TAIL, Moves.CIRCLE_THROW ];
|
const lastMove = source?.getLastXMoves()[0]
|
||||||
if (source) {
|
if (
|
||||||
const enemyMoveHistory = source.getMoveHistory();
|
lastMove &&
|
||||||
if (enemyMoveHistory.length > 0) {
|
// Will not activate for forced switch moves (triggers before wimp out activates)
|
||||||
const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
|
(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.
|
// 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) {
|
// TODO: Make this check the user's tags rather than the last move used by the target - we could be lifted by another pokemon
|
||||||
return false;
|
|| (lastMove.move === Moves.SKY_DROP && lastMove.result === MoveResult.OTHER))
|
||||||
// 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
|
return false;
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pokemon.hp + damage >= pokemon.getMaxHp() * this.hpRatio) {
|
// Check for HP percents - don't switch if the move didn't knock us below our switch threshold
|
||||||
const shellBellHeal = calculateShellBellRecovery(pokemon);
|
// (either because we were below it to begin with or are still above it after the hit).
|
||||||
if (pokemon.hp - shellBellHeal < pokemon.getMaxHp() * this.hpRatio) {
|
const hpNeededToSwitch = pokemon.getMaxHp() * this.hpRatio;
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
if (pokemon.hp + damage < hpNeededToSwitch || pokemon.hp >= hpNeededToSwitch) {
|
||||||
if (!this.canSwitchOut(pokemon, opponent)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
* @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);
|
this.doSwitch(pokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5837,16 +5827,15 @@ export function applyPostDamageAbAttrs(
|
|||||||
attrType: Constructor<PostDamageAbAttr>,
|
attrType: Constructor<PostDamageAbAttr>,
|
||||||
pokemon: Pokemon,
|
pokemon: Pokemon,
|
||||||
damage: number,
|
damage: number,
|
||||||
passive: boolean,
|
|
||||||
simulated = false,
|
simulated = false,
|
||||||
args: any[],
|
source: Pokemon | undefined = undefined,
|
||||||
source?: Pokemon,
|
...args: any[]
|
||||||
): void {
|
): void {
|
||||||
applyAbAttrsInternal<PostDamageAbAttr>(
|
applyAbAttrsInternal<PostDamageAbAttr>(
|
||||||
attrType,
|
attrType,
|
||||||
pokemon,
|
pokemon,
|
||||||
(attr, passive) => attr.applyPostDamage(pokemon, damage, passive, simulated, args, source),
|
(attr, passive) => attr.applyPostDamage(pokemon, damage, simulated, source, args),
|
||||||
(attr, passive) => attr.canApplyPostDamage(pokemon, damage, passive, simulated, args, source),
|
(attr, passive) => attr.canApplyPostDamage(pokemon, damage, simulated, source, args),
|
||||||
args,
|
args,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6942,9 +6931,11 @@ export function initAbilities() {
|
|||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
||||||
new Ability(Abilities.WIMP_OUT, 7)
|
new Ability(Abilities.WIMP_OUT, 7)
|
||||||
.attr(PostDamageForceSwitchAbAttr)
|
.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
|
.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)
|
new Ability(Abilities.EMERGENCY_EXIT, 7)
|
||||||
.attr(PostDamageForceSwitchAbAttr)
|
.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
|
.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)
|
new Ability(Abilities.WATER_COMPACTION, 7)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
.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;
|
type MoveFilter = (move: Move) => boolean;
|
||||||
|
|
||||||
|
// TODO: fix this to check the last direct damage instance taken
|
||||||
export class CounterDamageAttr extends FixedDamageAttr {
|
export class CounterDamageAttr extends FixedDamageAttr {
|
||||||
private moveFilter: MoveFilter;
|
private moveFilter: MoveFilter;
|
||||||
private multiplier: number;
|
private multiplier: number;
|
||||||
@ -1664,8 +1665,8 @@ export class RecoilAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const damageValue = (!this.useHp ? user.turnData.totalDamageDealt : user.getMaxHp()) * this.damageRatio;
|
const damageValue = (!this.useHp ? user.turnData.lastMoveDamageDealt : user.getMaxHp()) * this.damageRatio;
|
||||||
const minValue = user.turnData.totalDamageDealt ? 1 : 0;
|
const minValue = user.turnData.lastMoveDamageDealt ? 1 : 0;
|
||||||
const recoilDamage = toDmgValue(damageValue, minValue);
|
const recoilDamage = toDmgValue(damageValue, minValue);
|
||||||
if (!recoilDamage) {
|
if (!recoilDamage) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -342,6 +342,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
public luck: number;
|
public luck: number;
|
||||||
public pauseEvolutions: boolean;
|
public pauseEvolutions: boolean;
|
||||||
public pokerus: boolean;
|
public pokerus: boolean;
|
||||||
|
// TODO: Document these
|
||||||
public switchOutStatus = false;
|
public switchOutStatus = false;
|
||||||
public evoCounter: number;
|
public evoCounter: number;
|
||||||
public teraType: PokemonType;
|
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.
|
* Given the damage, adds a new DamagePhase and update HP values, etc.
|
||||||
*
|
*
|
||||||
* Checks for 'Indirect' HitResults to account for Endure/Reviver Seed applying correctly
|
* Checks for 'Indirect' HitResults to account for Endure/Reviver Seed applying correctly
|
||||||
* @param damage integer - passed to damage()
|
* @param damage - Amount of damage to deal
|
||||||
* @param result an enum if it's super effective, not very, etc.
|
* @param result - The {@linkcode HitResult} of the damage instance; default `HitResult.EFFECTIVE`
|
||||||
* @param isCritical boolean if move is a critical hit
|
* @param isCritical - Whether the move being used (if any) was a critical hit; default `false`
|
||||||
* @param ignoreSegments boolean, passed to damage() and not used currently
|
* @param ignoreSegments - Whether to ignore boss segments; default `false` and currently unused
|
||||||
* @param preventEndure boolean, ignore endure properties of pokemon, passed to damage()
|
* @param preventEndure - Whether to ignore {@linkcode Moves.ENDURE} and similar effects when applying damage; default `false`
|
||||||
* @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage()
|
* @param ignoreFaintPhase - Whether to ignore adding a faint phase if the damage causes the target to faint; default `false`
|
||||||
* @returns integer of damage done
|
* @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,
|
damageAndUpdate(damage: number,
|
||||||
{
|
{
|
||||||
@ -4762,53 +4765,52 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
isCritical = false,
|
isCritical = false,
|
||||||
ignoreSegments = false,
|
ignoreSegments = false,
|
||||||
ignoreFaintPhase = false,
|
ignoreFaintPhase = false,
|
||||||
source = undefined,
|
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
result?: DamageResult,
|
result?: DamageResult,
|
||||||
isCritical?: boolean,
|
isCritical?: boolean,
|
||||||
ignoreSegments?: boolean,
|
ignoreSegments?: boolean,
|
||||||
ignoreFaintPhase?: boolean,
|
ignoreFaintPhase?: boolean,
|
||||||
source?: Pokemon,
|
|
||||||
} = {}
|
} = {}
|
||||||
): number {
|
): number {
|
||||||
const isIndirectDamage = [ HitResult.INDIRECT, HitResult.INDIRECT_KO ].includes(result);
|
const isIndirectDamage = [ HitResult.INDIRECT, HitResult.INDIRECT_KO ].includes(result);
|
||||||
const damagePhase = new DamageAnimPhase(
|
const damagePhase = new DamageAnimPhase(
|
||||||
this.getBattlerIndex(),
|
this.getBattlerIndex(),
|
||||||
damage,
|
damage,
|
||||||
result as DamageResult,
|
result,
|
||||||
isCritical
|
isCritical
|
||||||
);
|
);
|
||||||
globalScene.unshiftPhase(damagePhase);
|
globalScene.unshiftPhase(damagePhase);
|
||||||
if (this.switchOutStatus && source) {
|
|
||||||
|
// TODO: Review if wimp out battle skip actually needs this anymore
|
||||||
|
if (this.switchOutStatus) {
|
||||||
damage = 0;
|
damage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
damage = this.damage(
|
damage = this.damage(
|
||||||
damage,
|
damage,
|
||||||
ignoreSegments,
|
ignoreSegments,
|
||||||
isIndirectDamage,
|
isIndirectDamage,
|
||||||
ignoreFaintPhase,
|
ignoreFaintPhase,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure the battle-info bar's HP is updated, though only if the battle info is visible
|
// 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
|
// TODO: When battle-info UI is refactored, make this only update the HP bar
|
||||||
if (this.battleInfo.visible) {
|
if (this.battleInfo.visible) {
|
||||||
this.updateInfo();
|
this.updateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damage amount may have changed, but needed to be queued before calling damage function
|
// Damage amount may have changed, but needed to be queued before calling damage function
|
||||||
damagePhase.updateAmount(damage);
|
damagePhase.updateAmount(damage);
|
||||||
/**
|
|
||||||
* Run PostDamageAbAttr from any source of damage that is not from a multi-hit
|
// Trigger PostDamageAbAttr (ie wimp out) for indirect damage only.
|
||||||
* Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr
|
if (isIndirectDamage) {
|
||||||
*/
|
|
||||||
if (!source || source.turnData.hitCount <= 1) {
|
|
||||||
applyPostDamageAbAttrs(
|
applyPostDamageAbAttrs(
|
||||||
PostDamageAbAttr,
|
PostDamageAbAttr,
|
||||||
this,
|
this,
|
||||||
damage,
|
damage,
|
||||||
this.hasPassive(),
|
|
||||||
false,
|
false,
|
||||||
[],
|
undefined
|
||||||
source,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return damage;
|
return damage;
|
||||||
@ -7957,8 +7959,13 @@ export class PokemonTurnData {
|
|||||||
* - `0` = Move is finished
|
* - `0` = Move is finished
|
||||||
*/
|
*/
|
||||||
public hitsLeft = -1;
|
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;
|
public singleHitDamageDealt = 0;
|
||||||
|
// TODO: Rework this into "damage taken last" counter for metal burst and co.
|
||||||
public damageTaken = 0;
|
public damageTaken = 0;
|
||||||
public attacksReceived: AttackMoveResult[] = [];
|
public attacksReceived: AttackMoveResult[] = [];
|
||||||
public order: number;
|
public order: number;
|
||||||
@ -8007,7 +8014,7 @@ export enum HitResult {
|
|||||||
FAIL,
|
FAIL,
|
||||||
MISS,
|
MISS,
|
||||||
INDIRECT,
|
INDIRECT,
|
||||||
IMMUNE,
|
IMMUNE, // TODO: Why is this used exclusively for sheer cold?
|
||||||
CONFUSION,
|
CONFUSION,
|
||||||
INDIRECT_KO,
|
INDIRECT_KO,
|
||||||
}
|
}
|
||||||
|
@ -1783,12 +1783,12 @@ export class HitHealModifier extends PokemonHeldItemModifier {
|
|||||||
* @returns `true` if the {@linkcode Pokemon} was healed
|
* @returns `true` if the {@linkcode Pokemon} was healed
|
||||||
*/
|
*/
|
||||||
override apply(pokemon: Pokemon): boolean {
|
override apply(pokemon: Pokemon): boolean {
|
||||||
if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) {
|
if (pokemon.turnData.lastMoveDamageDealt && !pokemon.isFullHp()) {
|
||||||
// TODO: this shouldn't be undefined AFAIK
|
// TODO: this shouldn't be undefined AFAIK
|
||||||
globalScene.unshiftPhase(
|
globalScene.unshiftPhase(
|
||||||
new PokemonHealPhase(
|
new PokemonHealPhase(
|
||||||
pokemon.getBattlerIndex(),
|
pokemon.getBattlerIndex(),
|
||||||
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount,
|
toDmgValue(pokemon.turnData.lastMoveDamageDealt / 8) * this.stackCount,
|
||||||
i18next.t("modifier:hitHealApply", {
|
i18next.t("modifier:hitHealApply", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
typeName: this.type.name,
|
typeName: this.type.name,
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
IgnoreMoveEffectsAbAttr,
|
IgnoreMoveEffectsAbAttr,
|
||||||
MaxMultiHitAbAttr,
|
MaxMultiHitAbAttr,
|
||||||
PostAttackAbAttr,
|
PostAttackAbAttr,
|
||||||
PostDamageAbAttr,
|
|
||||||
PostDefendAbAttr,
|
PostDefendAbAttr,
|
||||||
ReflectStatusMoveAbAttr,
|
ReflectStatusMoveAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
@ -48,7 +47,7 @@ import { MoveTarget } from "#enums/MoveTarget";
|
|||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
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 type Pokemon from "#app/field/pokemon";
|
||||||
import { HitResult, MoveResult } from "#app/field/pokemon";
|
import { HitResult, MoveResult } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
@ -72,7 +71,7 @@ import { ShowAbilityPhase } from "./show-ability-phase";
|
|||||||
import { MovePhase } from "./move-phase";
|
import { MovePhase } from "./move-phase";
|
||||||
import { MoveEndPhase } from "./move-end-phase";
|
import { MoveEndPhase } from "./move-end-phase";
|
||||||
import { HideAbilityPhase } from "#app/phases/hide-ability-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 { HitCheckResult } from "#enums/hit-check-result";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import { isFieldTargeted } from "#app/data/moves/move-utils";
|
import { isFieldTargeted } from "#app/data/moves/move-utils";
|
||||||
@ -101,6 +100,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
/** Phases queued during moves */
|
/** Phases queued during moves */
|
||||||
private queuedPhases: Phase[] = [];
|
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 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)
|
* @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.targets = targets;
|
||||||
|
|
||||||
this.hitChecks = Array(this.targets.length).fill([HitCheckResult.PENDING, 0]);
|
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) {
|
if (this.lastHit) {
|
||||||
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
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,
|
isCritical,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Apply and/or remove type boosting tags (Flash Fire, Charge, etc.)
|
||||||
const typeBoost = user.findTag(
|
const typeBoost = user.findTag(
|
||||||
t => t instanceof TypeBoostTag && t.boostedType === user.getMoveType(this.move),
|
t => t instanceof TypeBoostTag && t.boostedType === user.getMoveType(this.move),
|
||||||
) as TypeBoostTag;
|
) as TypeBoostTag;
|
||||||
@ -828,18 +827,17 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
user.removeTag(typeBoost.tagType);
|
user.removeTag(typeBoost.tagType);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
if (dmg === 0) {
|
||||||
|
|
||||||
if (!dmg) {
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
||||||
target.lapseTags(BattlerTagLapseType.HIT);
|
target.lapseTags(BattlerTagLapseType.HIT);
|
||||||
|
|
||||||
const substitute = target.getTag(SubstituteTag);
|
const substituteTag = target.getTag(SubstituteTag);
|
||||||
const isBlockedBySubstitute = substitute && this.move.hitsSubstitute(user, target);
|
const isBlockedBySubstitute = substituteTag && this.move.hitsSubstitute(user, target);
|
||||||
if (isBlockedBySubstitute) {
|
if (isBlockedBySubstitute) {
|
||||||
substitute.hp -= dmg;
|
substituteTag.hp -= dmg;
|
||||||
} else if (!target.isPlayer() && dmg >= target.hp) {
|
} else if (!target.isPlayer() && dmg >= target.hp) {
|
||||||
globalScene.applyModifiers(EnemyEndureChanceModifier, false, target);
|
globalScene.applyModifiers(EnemyEndureChanceModifier, false, target);
|
||||||
}
|
}
|
||||||
@ -851,7 +849,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
ignoreFaintPhase: true,
|
ignoreFaintPhase: true,
|
||||||
ignoreSegments: isOneHitKo,
|
ignoreSegments: isOneHitKo,
|
||||||
isCritical,
|
isCritical,
|
||||||
source: user,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCritical) {
|
if (isCritical) {
|
||||||
@ -865,14 +862,13 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (user.isPlayer()) {
|
if (user.isPlayer()) {
|
||||||
globalScene.validateAchvs(DamageAchv, new NumberHolder(damage));
|
globalScene.validateAchvs(DamageAchv, new NumberHolder(damage));
|
||||||
|
|
||||||
if (damage > globalScene.gameData.gameStats.highestDamage) {
|
globalScene.gameData.gameStats.highestDamage = Math.max(damage, globalScene.gameData.gameStats.highestDamage);
|
||||||
globalScene.gameData.gameStats.highestDamage = damage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.turnData.totalDamageDealt += damage;
|
user.turnData.lastMoveDamageDealt += damage;
|
||||||
user.turnData.singleHitDamageDealt = damage;
|
user.turnData.singleHitDamageDealt = damage;
|
||||||
target.battleData.hitCount++;
|
target.battleData.hitCount++;
|
||||||
|
// TODO: this might be incorrect for counter moves
|
||||||
target.turnData.damageTaken += damage;
|
target.turnData.damageTaken += damage;
|
||||||
|
|
||||||
target.turnData.attacksReceived.unshift({
|
target.turnData.attacksReceived.unshift({
|
||||||
|
@ -49,9 +49,10 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
if (damage.value) {
|
if (damage.value) {
|
||||||
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
// 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));
|
globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
||||||
pokemon.updateInfo();
|
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());
|
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end());
|
||||||
} else {
|
} else {
|
||||||
|
@ -37,7 +37,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
.enemyPassiveAbility(Abilities.NO_GUARD)
|
.enemyPassiveAbility(Abilities.NO_GUARD)
|
||||||
.startingLevel(90)
|
.startingLevel(90)
|
||||||
.enemyLevel(70)
|
.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)
|
.enemyMoveset(Moves.FALSE_SWIPE)
|
||||||
.disableCrits();
|
.disableCrits();
|
||||||
});
|
});
|
||||||
@ -66,7 +66,40 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(pokemon1.getHpRatio()).toBeLessThan(0.5);
|
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);
|
game.override.passiveAbility(Abilities.REGENERATOR).startingLevel(5).enemyLevel(100);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -80,7 +113,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
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);
|
game.override.enemyAbility(Abilities.WIMP_OUT);
|
||||||
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -95,7 +128,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(!isVisible && hasFled).toBe(true);
|
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]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
const wimpod = game.scene.getPlayerPokemon()!;
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
wimpod.hp = 5;
|
wimpod.hp = 5;
|
||||||
@ -107,7 +140,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmNoSwitch();
|
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);
|
game.override.enemyMoveset([Moves.SPIRIT_SHACKLE]).startingLevel(53).enemyLevel(45);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -122,7 +155,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
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]);
|
game.override.startingLevel(95).enemyMoveset([Moves.U_TURN]);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -136,7 +169,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
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]);
|
game.override.startingLevel(190).startingWave(8).enemyMoveset([Moves.U_TURN]);
|
||||||
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
|
||||||
const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id;
|
const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id;
|
||||||
@ -145,7 +178,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(game.scene.getEnemyPokemon()?.id !== RIVAL_NINJASK1);
|
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]);
|
game.override.startingLevel(69).enemyMoveset([Moves.DRAGON_TAIL]);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
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);
|
expect(game.scene.getPlayerPokemon()!.species.speciesId).not.toBe(Species.WIMPOD);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("triggers when recoil damage is taken", async () => {
|
it.each<{ type: string; enemyMove?: Moves; enemyAbility?: Abilities }>([
|
||||||
game.override.moveset([Moves.HEAD_SMASH]).enemyMoveset([Moves.SPLASH]);
|
{ type: "weather", enemyMove: Moves.HAIL },
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
{ type: "status", enemyMove: Moves.TOXIC },
|
||||||
|
{ type: "Curse", enemyMove: Moves.CURSE },
|
||||||
game.move.select(Moves.HEAD_SMASH);
|
{ type: "Salt Cure", enemyMove: Moves.SALT_CURE },
|
||||||
game.doSelectPartyPokemon(1);
|
{ type: "partial trapping moves", enemyMove: Moves.WHIRLPOOL },
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
{ type: "Leech Seed", enemyMove: Moves.LEECH_SEED },
|
||||||
|
{ type: "Nightmare", enemyMove: Moves.NIGHTMARE },
|
||||||
confirmSwitch();
|
{ type: "Aftermath", enemyAbility: Abilities.AFTERMATH },
|
||||||
});
|
{ type: "Bad Dreams", enemyAbility: Abilities.BAD_DREAMS },
|
||||||
|
])(
|
||||||
it("It does not activate when the Pokémon cuts its own HP", async () => {
|
"should activate from damage caused by $name",
|
||||||
game.override.moveset([Moves.SUBSTITUTE]).enemyMoveset([Moves.SPLASH]);
|
async ({ enemyMove = Moves.SPLASH, enemyAbility = Abilities.BALL_FETCH }) => {
|
||||||
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 () => {
|
|
||||||
game.override
|
game.override
|
||||||
.moveset([Moves.DOUBLE_EDGE])
|
.passiveAbility(Abilities.COMATOSE)
|
||||||
.enemyMoveset([Moves.SPLASH])
|
.enemySpecies(Species.GASTLY)
|
||||||
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]);
|
.enemyMoveset(enemyMove)
|
||||||
|
.enemyAbility(enemyAbility)
|
||||||
|
.enemyLevel(1);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
const wimpod = game.scene.getPlayerPokemon()!;
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(wimpod).toBeDefined();
|
||||||
|
wimpod.hp *= 0.55;
|
||||||
|
|
||||||
wimpod.damageAndUpdate(toDmgValue(wimpod.getMaxHp() * 0.4));
|
game.move.select(Moves.THUNDER_PUNCH);
|
||||||
|
|
||||||
game.move.select(Moves.DOUBLE_EDGE);
|
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(game.scene.getPlayerParty()[1]).toBe(wimpod);
|
confirmSwitch();
|
||||||
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 weather damage", async () => {
|
it("should not trigger from Sheer Force-boosted moves", 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 () => {
|
|
||||||
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
|
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -248,81 +241,79 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmNoSwitch();
|
confirmNoSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Wimp Out will activate due to post turn status damage", async () => {
|
it("should trigger from recoil damage", async () => {
|
||||||
game.override.statusEffect(StatusEffect.POISON).enemyMoveset([Moves.SPLASH]);
|
game.override.moveset(Moves.HEAD_SMASH).enemyMoveset(Moves.SPLASH);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
game.move.select(Moves.HEAD_SMASH);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
await game.toNextTurn();
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Wimp Out will activate due to bad dreams", async () => {
|
it("should trigger from Flame Burst ally damage in doubles", async () => {
|
||||||
game.override.statusEffect(StatusEffect.SLEEP).enemyAbility(Abilities.BAD_DREAMS);
|
game.override.battleStyle("double").enemyMoveset([Moves.FLAME_BURST, Moves.SPLASH]);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
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.move.select(Moves.SPLASH, BattlerIndex.PLAYER);
|
||||||
game.doSelectPartyPokemon(1);
|
game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2);
|
||||||
await game.toNextTurn();
|
await game.forceEnemyMove(Moves.FLAME_BURST, BattlerIndex.PLAYER_2);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(2);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Wimp Out will activate due to leech seed", async () => {
|
it("should not activate when the Pokémon cuts its own HP", async () => {
|
||||||
game.override.enemyMoveset([Moves.LEECH_SEED]);
|
game.override.moveset(Moves.SUBSTITUTE).enemyMoveset(Moves.SPLASH);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
game.doSelectPartyPokemon(1);
|
wimpod.hp *= 0.52;
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
confirmSwitch();
|
game.move.select(Moves.SUBSTITUTE);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmNoSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Wimp Out will activate due to curse damage", async () => {
|
it("should not trigger when neutralized", async () => {
|
||||||
game.override.enemySpecies(Species.DUSKNOIR).enemyMoveset([Moves.CURSE]);
|
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS).startingLevel(5);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
game.doSelectPartyPokemon(1);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
confirmSwitch();
|
confirmNoSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Wimp Out will activate due to salt cure damage", async () => {
|
it("should disregard Shell Bell recovery while still activating it before switching", async () => {
|
||||||
game.override.enemySpecies(Species.NACLI).enemyMoveset([Moves.SALT_CURE]).enemyLevel(1);
|
game.override
|
||||||
|
.moveset([Moves.DOUBLE_EDGE])
|
||||||
|
.enemyMoveset([Moves.SPLASH])
|
||||||
|
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
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);
|
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 () => {
|
it("should not switch if Magic Guard prevents 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 () => {
|
|
||||||
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
|
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.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
||||||
game.override
|
game.override
|
||||||
@ -341,7 +332,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.WIMPOD);
|
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);
|
game.override.battleStyle("double").enemyAbility(Abilities.WIMP_OUT).enemyMoveset([Moves.SPLASH]).enemyLevel(1);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
const enemyLeadPokemon = game.scene.getEnemyParty()[0];
|
const enemyLeadPokemon = game.scene.getEnemyParty()[0];
|
||||||
@ -361,24 +352,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
|
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Wimp Out will activate due to aftermath", async () => {
|
it("should activate from entry hazard damage", 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 () => {
|
|
||||||
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
|
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.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
||||||
game.override.enemySpecies(Species.CENTISKORCH).enemyAbility(Abilities.WIMP_OUT).startingWave(4);
|
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");
|
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 () => {
|
it("triggers status on the wimp out user before a new pokemon is switched in", async () => {
|
||||||
game.override.enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(80);
|
game.override.enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(80);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
@ -413,7 +375,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
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);
|
game.override.enemyMoveset(Moves.BULLET_SEED).enemyAbility(Abilities.SKILL_LINK);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -429,7 +391,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
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 }]);
|
game.override.enemyMoveset(Moves.TACKLE).enemyHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -444,6 +406,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(enemyPokemon.turnData.hitCount).toBe(2);
|
expect(enemyPokemon.turnData.hitCount).toBe(2);
|
||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("triggers after last hit of Parental Bond", async () => {
|
it("triggers after last hit of Parental Bond", async () => {
|
||||||
game.override.enemyMoveset(Moves.TACKLE).enemyAbility(Abilities.PARENTAL_BOND);
|
game.override.enemyMoveset(Moves.TACKLE).enemyAbility(Abilities.PARENTAL_BOND);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
@ -461,26 +424,23 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// TODO: This interaction is not implemented yet
|
// TODO: This interaction is not implemented yet
|
||||||
it.todo(
|
it.todo("should not activate if the Pokémon's HP falls below half due to hurting itself in confusion", async () => {
|
||||||
"Wimp Out will not activate if the Pokémon's HP falls below half due to hurting itself in confusion",
|
game.override.moveset([Moves.SWORDS_DANCE]).enemyMoveset([Moves.SWAGGER]);
|
||||||
async () => {
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
game.override.moveset([Moves.SWORDS_DANCE]).enemyMoveset([Moves.SWAGGER]);
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
playerPokemon.hp *= 0.51;
|
||||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
playerPokemon.setStatStage(Stat.ATK, 6);
|
||||||
playerPokemon.hp *= 0.51;
|
playerPokemon.addTag(BattlerTagType.CONFUSED);
|
||||||
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) {
|
while (playerPokemon.getHpRatio() > 0.49) {
|
||||||
game.move.select(Moves.SWORDS_DANCE);
|
game.move.select(Moves.SWORDS_DANCE);
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmNoSwitch();
|
confirmNoSwitch();
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
it("should not activate on wave X0 bosses", async () => {
|
it("should not activate on wave X0 bosses", async () => {
|
||||||
game.override.enemyAbility(Abilities.WIMP_OUT).startingLevel(5850).startingWave(10);
|
game.override.enemyAbility(Abilities.WIMP_OUT).startingLevel(5850).startingWave(10);
|
||||||
@ -499,7 +459,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(isVisible && !hasFled).toBe(true);
|
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;
|
const wave = 2;
|
||||||
game.override
|
game.override
|
||||||
.enemyMoveset(Moves.SPLASH)
|
.enemyMoveset(Moves.SPLASH)
|
||||||
@ -527,7 +487,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(game.scene.currentBattle.waveIndex).toBe(wave + 1);
|
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;
|
const wave = 2;
|
||||||
game.override
|
game.override
|
||||||
.enemySpecies(Species.WIMPOD)
|
.enemySpecies(Species.WIMPOD)
|
||||||
|
@ -44,20 +44,18 @@ describe("Moves - Focus Punch", () => {
|
|||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.hp;
|
|
||||||
|
|
||||||
game.move.select(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(enemyPokemon.getInverseHp()).toBe(0);
|
||||||
expect(leadPokemon.getMoveHistory().length).toBe(0);
|
expect(leadPokemon.getMoveHistory().length).toBe(0);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
|
expect(enemyPokemon.getInverseHp()).toBe(0);
|
||||||
expect(leadPokemon.getMoveHistory().length).toBe(1);
|
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 () => {
|
it("should fail if the user is hit", async () => {
|
||||||
@ -72,16 +70,16 @@ describe("Moves - Focus Punch", () => {
|
|||||||
|
|
||||||
game.move.select(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(enemyPokemon.hp).toBe(enemyStartingHp);
|
||||||
expect(leadPokemon.getMoveHistory().length).toBe(0);
|
expect(leadPokemon.getMoveHistory().length).toBe(0);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
expect(enemyPokemon.hp).toBe(enemyStartingHp);
|
expect(enemyPokemon.hp).toBe(enemyStartingHp);
|
||||||
expect(leadPokemon.getMoveHistory().length).toBe(1);
|
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 () => {
|
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());
|
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||||
// enemy should have taken damage from player's Fiery Dance + 2 Powder procs
|
// enemy should have taken damage from player's Fiery Dance + 2 Powder procs
|
||||||
expect(enemyPokemon.hp).toBe(
|
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