mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-30 21:42:20 +02:00
Reverted a couple doc changes
This commit is contained in:
parent
c2e7c95620
commit
da6443562b
@ -2335,18 +2335,18 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr {
|
|||||||
// phase list (which could be after CommandPhase for example)
|
// phase list (which could be after CommandPhase for example)
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages));
|
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages));
|
||||||
} else {
|
} else {
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
for (const opponent of pokemon.getOpponents()) {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
if (this.intimidate) {
|
if (this.intimidate) {
|
||||||
applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated);
|
applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated);
|
||||||
applyAbAttrs(PostIntimidateStatStageChangeAbAttr, opponent, cancelled, simulated);
|
applyAbAttrs(PostIntimidateStatStageChangeAbAttr, opponent, cancelled, simulated);
|
||||||
|
|
||||||
if (opponent.getTag(BattlerTagType.SUBSTITUTE)) {
|
if (opponent.getTag(BattlerTagType.SUBSTITUTE)) {
|
||||||
cancelled.value = true;
|
cancelled.value = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!cancelled.value) {
|
||||||
if (!cancelled.value) {
|
globalScene.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages));
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4399,12 +4399,12 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr {
|
|||||||
* @extends AbAttr
|
* @extends AbAttr
|
||||||
*/
|
*/
|
||||||
export class PostMoveUsedAbAttr extends AbAttr {
|
export class PostMoveUsedAbAttr extends AbAttr {
|
||||||
canApplyPostMoveUsed(
|
canApplyPostMoveUsed(
|
||||||
pokemon: Pokemon,
|
pokemon: Pokemon,
|
||||||
move: PokemonMove,
|
move: PokemonMove,
|
||||||
source: Pokemon,
|
source: Pokemon,
|
||||||
targets: BattlerIndex[],
|
targets: BattlerIndex[],
|
||||||
simulated: boolean,
|
simulated: boolean,
|
||||||
args: any[]): boolean {
|
args: any[]): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -4414,7 +4414,7 @@ export class PostMoveUsedAbAttr extends AbAttr {
|
|||||||
move: PokemonMove,
|
move: PokemonMove,
|
||||||
source: Pokemon,
|
source: Pokemon,
|
||||||
targets: BattlerIndex[],
|
targets: BattlerIndex[],
|
||||||
simulated: boolean,
|
simulated: boolean,
|
||||||
args: any[],
|
args: any[],
|
||||||
): void {}
|
): void {}
|
||||||
}
|
}
|
||||||
@ -4899,7 +4899,7 @@ export class BlockRedirectAbAttr extends AbAttr { }
|
|||||||
* @see {@linkcode apply}
|
* @see {@linkcode apply}
|
||||||
*/
|
*/
|
||||||
export class ReduceStatusEffectDurationAbAttr extends AbAttr {
|
export class ReduceStatusEffectDurationAbAttr extends AbAttr {
|
||||||
private statusEffect: StatusEffect;
|
private statusEffect: StatusEffect;
|
||||||
|
|
||||||
constructor(statusEffect: StatusEffect) {
|
constructor(statusEffect: StatusEffect) {
|
||||||
super(false);
|
super(false);
|
||||||
@ -4908,7 +4908,7 @@ private statusEffect: StatusEffect;
|
|||||||
}
|
}
|
||||||
|
|
||||||
override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||||
return args[1] instanceof NumberHolder && args[0] === this.statusEffect;
|
return args[1] instanceof NumberHolder && args[0] === this.statusEffect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5557,7 +5557,7 @@ class ForceSwitchOutHelper {
|
|||||||
* - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated.
|
* - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated.
|
||||||
*/
|
*/
|
||||||
if (switchOutTarget instanceof PlayerPokemon) {
|
if (switchOutTarget instanceof PlayerPokemon) {
|
||||||
if (globalScene.getPlayerParty().every(p => !p.isActive(true))) {
|
if (globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5890,7 +5890,7 @@ export function applyPostMoveUsedAbAttrs(
|
|||||||
move: PokemonMove,
|
move: PokemonMove,
|
||||||
source: Pokemon,
|
source: Pokemon,
|
||||||
targets: BattlerIndex[],
|
targets: BattlerIndex[],
|
||||||
simulated = false,
|
simulated = false,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
): void {
|
): void {
|
||||||
applyAbAttrsInternal<PostMoveUsedAbAttr>(
|
applyAbAttrsInternal<PostMoveUsedAbAttr>(
|
||||||
@ -6901,8 +6901,8 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.ANALYTIC, 5)
|
new Ability(Abilities.ANALYTIC, 5)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => {
|
.attr(MovePowerBoostAbAttr, (user, target, move) => {
|
||||||
// Boost power if all other Pokemon have already moved (no other moves are slated to execute)
|
// Boost power if all other Pokemon have already moved (no other moves are slated to execute)
|
||||||
const laterMovePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.id !== user?.id);
|
const movePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.id !== user?.id);
|
||||||
return isNullOrUndefined(laterMovePhase);
|
return isNullOrUndefined(movePhase);
|
||||||
}, 1.3),
|
}, 1.3),
|
||||||
new Ability(Abilities.ILLUSION, 5)
|
new Ability(Abilities.ILLUSION, 5)
|
||||||
// The Pokemon generate an illusion if it's available
|
// The Pokemon generate an illusion if it's available
|
||||||
|
@ -51,10 +51,6 @@ export enum BattlerTagLapseType {
|
|||||||
MOVE,
|
MOVE,
|
||||||
PRE_MOVE,
|
PRE_MOVE,
|
||||||
AFTER_MOVE,
|
AFTER_MOVE,
|
||||||
/**
|
|
||||||
* TODO: Stop treating this like a catch-all "semi invulnerability" tag;
|
|
||||||
* we may want to use this for other stuff later
|
|
||||||
*/
|
|
||||||
MOVE_EFFECT,
|
MOVE_EFFECT,
|
||||||
TURN_END,
|
TURN_END,
|
||||||
HIT,
|
HIT,
|
||||||
@ -65,7 +61,7 @@ export enum BattlerTagLapseType {
|
|||||||
|
|
||||||
export class BattlerTag {
|
export class BattlerTag {
|
||||||
public tagType: BattlerTagType;
|
public tagType: BattlerTagType;
|
||||||
public lapseTypes: BattlerTagLapseType[]; // TODO: Make this a set
|
public lapseTypes: BattlerTagLapseType[];
|
||||||
public turnCount: number;
|
public turnCount: number;
|
||||||
public sourceMove: Moves;
|
public sourceMove: Moves;
|
||||||
public sourceId?: number;
|
public sourceId?: number;
|
||||||
@ -98,7 +94,7 @@ export class BattlerTag {
|
|||||||
onOverlap(_pokemon: Pokemon): void {}
|
onOverlap(_pokemon: Pokemon): void {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger and tick down this {@linkcode BattlerTag}'s duration.
|
* Tick down this {@linkcode BattlerTag}'s duration.
|
||||||
* @returns `true` if the tag should be kept (`turnCount` > 0`)
|
* @returns `true` if the tag should be kept (`turnCount` > 0`)
|
||||||
*/
|
*/
|
||||||
lapse(_pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean {
|
lapse(_pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean {
|
||||||
@ -186,21 +182,21 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this tag is currently restricting a move's use.
|
* Gets whether this tag is restricting a move.
|
||||||
*
|
*
|
||||||
* @param move - The {@linkcode Moves | move ID} whose usability is being checked.
|
* @param move - {@linkcode Moves} ID to check restriction for.
|
||||||
* @param user - The {@linkcode Pokemon} using the move.
|
* @param user - The {@linkcode Pokemon} involved
|
||||||
* @returns Whether the given move is restricted by this tag.
|
* @returns `true` if the move is restricted by this tag, otherwise `false`.
|
||||||
*/
|
*/
|
||||||
public abstract isMoveRestricted(move: Moves, user?: Pokemon): boolean;
|
public abstract isMoveRestricted(move: Moves, user?: Pokemon): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this tag is restricting a move during target selection.
|
* Checks if this tag is restricting a move based on a user's decisions during the target selection phase
|
||||||
* Returns `false` by default unless overridden by a child class.
|
*
|
||||||
* @param _move - The {@linkcode Moves | move ID} whose selectability is being checked
|
* @param {Moves} _move {@linkcode Moves} move ID to check restriction for
|
||||||
* @param _user - The {@linkcode Pokemon} using the move.
|
* @param {Pokemon} _user {@linkcode Pokemon} the user of the above move
|
||||||
* @param _target - The {@linkcode Pokemon} being targeted
|
* @param {Pokemon} _target {@linkcode Pokemon} the target of the above move
|
||||||
* @returns Whether the given move should be unselectable when choosing targets.
|
* @returns {boolean} `false` unless overridden by the child tag
|
||||||
*/
|
*/
|
||||||
isMoveTargetRestricted(_move: Moves, _user: Pokemon, _target: Pokemon): boolean {
|
isMoveTargetRestricted(_move: Moves, _user: Pokemon, _target: Pokemon): boolean {
|
||||||
return false;
|
return false;
|
||||||
@ -346,10 +342,9 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
* Display the text that occurs when a move is interrupted via Disable.
|
* @param {Pokemon} pokemon {@linkcode Pokemon} attempting to use the restricted move
|
||||||
* @param pokemon - The {@linkcode Pokemon} attempting to use the restricted move
|
* @param {Moves} move {@linkcode Moves} ID of the move being interrupted
|
||||||
* @param move - The {@linkcode Moves | move ID} of the move being interrupted
|
* @returns {string} text to display when the move is interrupted
|
||||||
* @returns The text to display when the given move is interrupted
|
|
||||||
*/
|
*/
|
||||||
override interruptedText(pokemon: Pokemon, move: Moves): string {
|
override interruptedText(pokemon: Pokemon, move: Moves): string {
|
||||||
return i18next.t("battle:disableInterruptedMove", {
|
return i18next.t("battle:disableInterruptedMove", {
|
||||||
@ -367,6 +362,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag used by Gorilla Tactics to restrict the user to using only one move.
|
* Tag used by Gorilla Tactics to restrict the user to using only one move.
|
||||||
|
* @extends MoveRestrictionBattlerTag
|
||||||
*/
|
*/
|
||||||
export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
||||||
private moveId = Moves.NONE;
|
private moveId = Moves.NONE;
|
||||||
@ -416,10 +412,11 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* @override
|
* @override
|
||||||
* @param pokemon - The {@linkcode Pokemon} attempting to select a move
|
* @param {Pokemon} pokemon n/a
|
||||||
* @param _move - Unused
|
* @param {Moves} _move {@linkcode Moves} ID of the move being denied
|
||||||
* @returns The text to display when the move is rendered unselectable
|
* @returns {string} text to display when the move is denied
|
||||||
*/
|
*/
|
||||||
override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
|
override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
|
||||||
return i18next.t("battle:canOnlyUseMove", {
|
return i18next.t("battle:canOnlyUseMove", {
|
||||||
@ -640,7 +637,7 @@ class NoRetreatTag extends TrappedTag {
|
|||||||
*/
|
*/
|
||||||
export class FlinchedTag extends BattlerTag {
|
export class FlinchedTag extends BattlerTag {
|
||||||
constructor(sourceMove: Moves) {
|
constructor(sourceMove: Moves) {
|
||||||
super(BattlerTagType.FLINCHED, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END], 1, sourceMove);
|
super(BattlerTagType.FLINCHED, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END], 0, sourceMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(pokemon: Pokemon): void {
|
onAdd(pokemon: Pokemon): void {
|
||||||
@ -1481,6 +1478,10 @@ export class WrapTag extends DamagingTrapTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class VortexTrapTag extends DamagingTrapTag {
|
export abstract class VortexTrapTag extends DamagingTrapTag {
|
||||||
|
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: number, sourceMove: Moves, sourceId: number) {
|
||||||
|
super(tagType, commonAnim, turnCount, sourceMove, sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
getTrapMessage(pokemon: Pokemon): string {
|
getTrapMessage(pokemon: Pokemon): string {
|
||||||
return i18next.t("battlerTags:vortexOnTrap", {
|
return i18next.t("battlerTags:vortexOnTrap", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
@ -2722,7 +2723,7 @@ export class ExposedTag extends BattlerTag {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag that prevents HP recovery from held items and move effects. It also blocks the usage of recovery moves.
|
* Tag that prevents HP recovery from held items and move effects. It also blocks the usage of recovery moves.
|
||||||
* Applied by moves: {@linkcode Moves.HEAL_BLOCK | Heal Block (5 turns)}, {@linkcode Moves.PSYCHIC_NOISE | Psychic Noise (2 turns)}
|
* Applied by moves: {@linkcode Moves.HEAL_BLOCK | Heal Block (5 turns)}, {@linkcode Moves.PSYCHIC_NOISE | Psychic Noise (2 turns)}
|
||||||
*
|
*
|
||||||
* @extends MoveRestrictionBattlerTag
|
* @extends MoveRestrictionBattlerTag
|
||||||
*/
|
*/
|
||||||
@ -2748,7 +2749,10 @@ export class HealBlockTag extends MoveRestrictionBattlerTag {
|
|||||||
* @returns `true` if the move has a TRIAGE_MOVE flag and is a status move
|
* @returns `true` if the move has a TRIAGE_MOVE flag and is a status move
|
||||||
*/
|
*/
|
||||||
override isMoveRestricted(move: Moves): boolean {
|
override isMoveRestricted(move: Moves): boolean {
|
||||||
return allMoves[move].hasFlag(MoveFlags.TRIAGE_MOVE) && allMoves[move].category === MoveCategory.STATUS;
|
if (allMoves[move].hasFlag(MoveFlags.TRIAGE_MOVE) && allMoves[move].category === MoveCategory.STATUS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3422,8 +3426,7 @@ export class PsychoShiftTag extends BattlerTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag associated with {@linkcode Moves.MAGIC_COAT | Magic Coat} that reflects certain status moves directed at the user.
|
* Tag associated with the move Magic Coat.
|
||||||
* TODO: Move Reflection code out of `move-effect-phase` and into here
|
|
||||||
*/
|
*/
|
||||||
export class MagicCoatTag extends BattlerTag {
|
export class MagicCoatTag extends BattlerTag {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -702,10 +702,10 @@ export default class Move implements Localizable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sees if a move has a custom failure text (by looking at each {@linkcode MoveAttr} of this move)
|
* Sees if a move has a custom failure text (by looking at each {@linkcode MoveAttr} of this move)
|
||||||
* @param user - The {@linkcode Pokemon} using this move
|
* @param user {@linkcode Pokemon} using the move
|
||||||
* @param target - The {@linkcode Pokemon} targeted by this move
|
* @param target {@linkcode Pokemon} target of the move
|
||||||
* @param move - The {@linkcode Move} being used
|
* @param move {@linkcode Move} with this attribute
|
||||||
* @returns A string containing the custom failure text, or `undefined` if no custom text exists.
|
* @returns string of the custom failure text, or `null` if it uses the default text ("But it failed!")
|
||||||
*/
|
*/
|
||||||
getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined {
|
getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined {
|
||||||
for (const attr of this.attrs) {
|
for (const attr of this.attrs) {
|
||||||
@ -1186,7 +1186,7 @@ export class MoveEffectAttr extends MoveAttr {
|
|||||||
*/
|
*/
|
||||||
protected options?: MoveEffectAttrOptions;
|
protected options?: MoveEffectAttrOptions;
|
||||||
|
|
||||||
constructor(selfTarget?: boolean, options?: MoveEffectAttrOptions) {
|
constructor(selfTarget?: boolean, options?: MoveEffectAttrOptions) {
|
||||||
super(selfTarget);
|
super(selfTarget);
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
@ -1373,8 +1373,8 @@ export class PreMoveMessageAttr extends MoveAttr {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute for moves that can be conditionally interrupted to be considered to
|
* Attribute for moves that can be conditionally interrupted to be considered to
|
||||||
* have failed before their "useMove" message is displayed.
|
* have failed before their "useMove" message is displayed. Currently used by
|
||||||
* Currently used by {@linkcode Moves.FOCUS_PUNCH}.
|
* Focus Punch.
|
||||||
* @extends MoveAttr
|
* @extends MoveAttr
|
||||||
*/
|
*/
|
||||||
export class PreUseInterruptAttr extends MoveAttr {
|
export class PreUseInterruptAttr extends MoveAttr {
|
||||||
@ -1383,40 +1383,38 @@ export class PreUseInterruptAttr extends MoveAttr {
|
|||||||
protected conditionFunc: MoveConditionFunc;
|
protected conditionFunc: MoveConditionFunc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new PreUseInterruptAttr.
|
* Create a new MoveInterruptedMessageAttr.
|
||||||
* @param message - Custom failure text to display when the move is interrupted, either as a string or a function producing one.
|
* @param message The message to display when the move is interrupted, or a function that formats the message based on the user, target, and move.
|
||||||
* If ommitted, will display the default failure text upon cancellation.
|
|
||||||
* @param conditionFunc - A {@linkcode MoveConditionFunc} that returns `true` if the move should be canceled.
|
|
||||||
*/
|
*/
|
||||||
constructor(message: string | undefined | ((user: Pokemon, target: Pokemon, move: Move) => string), conditionFunc: MoveConditionFunc) {
|
constructor(message?: string | ((user: Pokemon, target: Pokemon, move: Move) => string), conditionFunc?: MoveConditionFunc) {
|
||||||
super();
|
super();
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.conditionFunc = conditionFunc;
|
this.conditionFunc = conditionFunc ?? (() => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conditionally cancel this pokemon's current move.
|
* Message to display when a move is interrupted.
|
||||||
* @param user - The {@linkcode Pokemon} using this move
|
* @param user {@linkcode Pokemon} using the move
|
||||||
* @param target - The {@linkcode Pokemon} targeted by this move
|
* @param target {@linkcode Pokemon} target of the move
|
||||||
* @param move - The {@linkcode Move} being used
|
* @param move {@linkcode Move} with this attribute
|
||||||
* @returns `true` if the move should be cancelled.
|
|
||||||
*/
|
*/
|
||||||
override apply(user: Pokemon, target: Pokemon, move: Move): boolean {
|
override apply(user: Pokemon, target: Pokemon, move: Move): boolean {
|
||||||
return this.conditionFunc(user, target, move);
|
return this.conditionFunc(user, target, move);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the text displayed upon this move's interruption.
|
* Message to display when a move is interrupted.
|
||||||
* @param user - The {@linkcode Pokemon} using this move
|
* @param user {@linkcode Pokemon} using the move
|
||||||
* @param target - The {@linkcode Pokemon} targeted by this move
|
* @param target {@linkcode Pokemon} target of the move
|
||||||
* @param move - The {@linkcode Move} being used
|
* @param move {@linkcode Move} with this attribute
|
||||||
* @returns A string containing the custom failure text, or `undefined` if no custom text exists.
|
|
||||||
*/
|
*/
|
||||||
override getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined {
|
override getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined {
|
||||||
if (this.conditionFunc(user, target, move)) {
|
if (this.message && this.conditionFunc(user, target, move)) {
|
||||||
return typeof this.message !== "function"
|
const message =
|
||||||
? this.message
|
typeof this.message === "string"
|
||||||
: this.message(user, target, move);
|
? (this.message as string)
|
||||||
|
: this.message(user, target, move);
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1516,7 +1514,7 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr {
|
|||||||
case 0:
|
case 0:
|
||||||
// first hit of move; update initialHp tracker
|
// first hit of move; update initialHp tracker
|
||||||
this.initialHp = target.hp;
|
this.initialHp = target.hp;
|
||||||
default:
|
default:
|
||||||
// multi lens added hit; use initialHp tracker to ensure correct damage
|
// multi lens added hit; use initialHp tracker to ensure correct damage
|
||||||
(args[0] as NumberHolder).value = toDmgValue(this.initialHp / 2);
|
(args[0] as NumberHolder).value = toDmgValue(this.initialHp / 2);
|
||||||
return true;
|
return true;
|
||||||
@ -3059,17 +3057,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
|||||||
this.chargeText = chargeText;
|
this.chargeText = chargeText;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
* Apply the delayed attack, either setting it up or triggering the attack.
|
|
||||||
* @param user - The {@linkcode Pokemon} using the move
|
|
||||||
* @param target - The {@linkcode Pokemon} being targeted
|
|
||||||
* @param move - The {@linkcode Move} being used
|
|
||||||
* @param args -
|
|
||||||
* `[0]` - {@linkcode BooleanHolder} containing whether the move was overriden
|
|
||||||
* `[1]` - Whether the move is supposed to set up a delayed attack (`true`) or activate (`false`)
|
|
||||||
* @returns always `true`
|
|
||||||
*/
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: [BooleanHolder, boolean]): boolean {
|
|
||||||
// Edge case for the move applied on a pokemon that has fainted
|
// Edge case for the move applied on a pokemon that has fainted
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return true;
|
return true;
|
||||||
@ -3105,9 +3093,9 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
|
|||||||
/**
|
/**
|
||||||
* If the user's ally is set to use a different move with this attribute,
|
* If the user's ally is set to use a different move with this attribute,
|
||||||
* defer this move's effects for a combined move on the ally's turn.
|
* defer this move's effects for a combined move on the ally's turn.
|
||||||
* @param user - The {@linkcode Pokemon} using the move
|
* @param user the {@linkcode Pokemon} using this move
|
||||||
* @param target - Unused
|
* @param target n/a
|
||||||
* @param move - The {@linkcode Move} being used
|
* @param move the {@linkcode Move} being used
|
||||||
* @param args
|
* @param args
|
||||||
* - [0] a {@linkcode BooleanHolder} indicating whether the move's base
|
* - [0] a {@linkcode BooleanHolder} indicating whether the move's base
|
||||||
* effects should be overridden this turn.
|
* effects should be overridden this turn.
|
||||||
@ -3696,7 +3684,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr {
|
|||||||
const ppMax = move.pp;
|
const ppMax = move.pp;
|
||||||
const ppUsed = user.moveset.find((m) => m.moveId === move.id)?.ppUsed ?? 0;
|
const ppUsed = user.moveset.find((m) => m.moveId === move.id)?.ppUsed ?? 0;
|
||||||
|
|
||||||
let ppRemains = ppMax - ppUsed;
|
let ppRemains = ppMax - ppUsed;
|
||||||
/** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */
|
/** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */
|
||||||
if (ppRemains < 0) {
|
if (ppRemains < 0) {
|
||||||
ppRemains = 0;
|
ppRemains = 0;
|
||||||
@ -3814,30 +3802,26 @@ export class DoublePowerChanceAttr extends VariablePowerAttr {
|
|||||||
|
|
||||||
export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultiplierAttr {
|
export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultiplierAttr {
|
||||||
constructor(limit: number, resetOnFail: boolean, resetOnLimit?: boolean, ...comboMoves: Moves[]) {
|
constructor(limit: number, resetOnFail: boolean, resetOnLimit?: boolean, ...comboMoves: Moves[]) {
|
||||||
super((user: Pokemon, _target: Pokemon, move: Move): number => {
|
super((user: Pokemon, target: Pokemon, move: Move): number => {
|
||||||
const moveHistory = user.getLastXMoves(-1).slice(1, limit+1); // don't count the first history entry (ie the current move)
|
const moveHistory = user.getLastXMoves(limit + 1).slice(1);
|
||||||
|
|
||||||
let count = 1;
|
let count = 0;
|
||||||
|
let turnMove: TurnMove | undefined;
|
||||||
|
|
||||||
// TODO: Confirm whether mirror moving an echoed voice counts for and/or resets a boost
|
while (
|
||||||
for (const tm of moveHistory) {
|
(
|
||||||
if (
|
(turnMove = moveHistory.shift())?.move === move.id
|
||||||
!(tm.move === move.id || comboMoves.includes(tm.move))
|
|| (comboMoves.length && comboMoves.includes(turnMove?.move ?? Moves.NONE))
|
||||||
|| (resetOnFail && tm.result !== MoveResult.SUCCESS)
|
)
|
||||||
) {
|
&& (!resetOnFail || turnMove?.result === MoveResult.SUCCESS)
|
||||||
|
) {
|
||||||
|
if (count < (limit - 1)) {
|
||||||
|
count++;
|
||||||
|
} else if (resetOnLimit) {
|
||||||
|
count = 0;
|
||||||
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count < limit - 1) {
|
|
||||||
count++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetOnLimit) {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getMultiplier(count);
|
return this.getMultiplier(count);
|
||||||
@ -4017,7 +4001,7 @@ export class HpPowerAttr extends VariablePowerAttr {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute used for moves whose base power scales with the opponent's HP
|
* Attribute used for moves whose base power scales with the opponent's HP
|
||||||
* Used for {@linkcode Moves.CRUSH_GRIP}, {@linkcode Moves.WRING_OUT}, and {@linkcode Moves.HARD_PRESS}
|
* Used for Crush Grip, Wring Out, and Hard Press
|
||||||
* maxBasePower 100 for Hard Press, 120 for others
|
* maxBasePower 100 for Hard Press, 120 for others
|
||||||
*/
|
*/
|
||||||
export class OpponentHighHpPowerAttr extends VariablePowerAttr {
|
export class OpponentHighHpPowerAttr extends VariablePowerAttr {
|
||||||
@ -4333,7 +4317,6 @@ const hasStockpileStacksCondition: MoveConditionFunc = (user) => {
|
|||||||
return !!hasStockpilingTag && hasStockpilingTag.stockpiledCount > 0;
|
return !!hasStockpilingTag && hasStockpilingTag.stockpiledCount > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute used for multi-hit moves that increase power in increments of the
|
* Attribute used for multi-hit moves that increase power in increments of the
|
||||||
* move's base power for each hit, namely Triple Kick and Triple Axel.
|
* move's base power for each hit, namely Triple Kick and Triple Axel.
|
||||||
@ -5434,17 +5417,11 @@ export class FrenzyAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Disable if used via dancer
|
// TODO: Disable if used via dancer
|
||||||
|
|
||||||
// TODO: Add support for moves that don't add the frenzy tag (Uproar, Rollout, etc.)
|
// TODO: Add support for moves that don't add the frenzy tag (Uproar, Rollout, etc.)
|
||||||
|
|
||||||
// If frenzy is not in effect and we don't have anything queued up,
|
if (!user.getTag(BattlerTagType.FRENZY) && !user.getMoveQueue().length) {
|
||||||
// add 1-2 extra instances of the move to the move queue.
|
const turnCount = user.randSeedIntRange(1, 2);
|
||||||
// If frenzy is already in effect, tick down the tag.
|
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }));
|
||||||
if (!user.getTag(BattlerTagType.FRENZY) && user.getMoveQueue().length === 0) {
|
|
||||||
const turnCount = user.randSeedIntRange(1, 2); // excludes initial use
|
|
||||||
for (let i = 0; i < turnCount; i++) {
|
|
||||||
user.pushMoveQueue({ move: move.id, targets: [ target.getBattlerIndex() ], useType: MoveUseType.IGNORE_PP });
|
|
||||||
}
|
|
||||||
user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id);
|
user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id);
|
||||||
} else {
|
} else {
|
||||||
applyMoveAttrs(AddBattlerTagAttr, user, target, move, args);
|
applyMoveAttrs(AddBattlerTagAttr, user, target, move, args);
|
||||||
@ -5817,24 +5794,23 @@ export class ProtectAttr extends AddBattlerTagAttr {
|
|||||||
super(tagType, true);
|
super(tagType, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Condition to fail a protect usage based on random chance.
|
|
||||||
* Chance starts at 100% and is thirded for each prior successful proctect usage.
|
|
||||||
* @returns a function that fails the function if its proc chance roll fails
|
|
||||||
*/
|
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return ((user, target, move): boolean => {
|
return ((user, target, move): boolean => {
|
||||||
const lastMoves = user.getLastXMoves(-1)
|
let timesUsed = 0;
|
||||||
|
const moveHistory = user.getLastXMoves();
|
||||||
|
let turnMove: TurnMove | undefined;
|
||||||
|
|
||||||
let threshold = 1;
|
while (moveHistory.length) {
|
||||||
for (const tm of lastMoves) {
|
turnMove = moveHistory.shift();
|
||||||
if (!allMoves[tm.move].hasAttr(ProtectAttr) || tm.result !== MoveResult.SUCCESS) {
|
if (!allMoves[turnMove?.move ?? Moves.NONE].hasAttr(ProtectAttr) || turnMove?.result !== MoveResult.SUCCESS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
threshold *= 3;
|
timesUsed++;
|
||||||
}
|
}
|
||||||
|
if (timesUsed) {
|
||||||
return threshold === 1 || user.randSeedInt(threshold) === 0;
|
return !user.randSeedInt(Math.pow(3, timesUsed));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7334,14 +7310,13 @@ export class SketchAttr extends MoveEffectAttr {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super(true);
|
super(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User copies the opponent's last used move, if possible.
|
* User copies the opponent's last used move, if possible
|
||||||
* @param user - The {@linkcode Pokemon} using the move
|
* @param {Pokemon} user Pokemon that used the move and will replace Sketch with the copied move
|
||||||
* @param target - The {@linkcode Pokemon} being targeted by the move
|
* @param {Pokemon} target Pokemon that the user wants to copy a move from
|
||||||
* @param move - The {@linkcoed Move} being used
|
* @param {Move} move Move being used
|
||||||
* @param args - Unused
|
* @param {any[]} args Unused
|
||||||
* @returns Whether a move was successfully learnt
|
* @returns {boolean} true if the function succeeds, otherwise false
|
||||||
*/
|
*/
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
@ -7881,6 +7856,7 @@ export class AfterYouAttr extends MoveEffectAttr {
|
|||||||
/**
|
/**
|
||||||
* Move effect to force the target to move last, ignoring priority.
|
* Move effect to force the target to move last, ignoring priority.
|
||||||
* If applied to multiple targets, they move in speed order after all other moves.
|
* If applied to multiple targets, they move in speed order after all other moves.
|
||||||
|
* @extends MoveEffectAttr
|
||||||
*/
|
*/
|
||||||
export class ForceLastAttr extends MoveEffectAttr {
|
export class ForceLastAttr extends MoveEffectAttr {
|
||||||
/**
|
/**
|
||||||
@ -7927,7 +7903,7 @@ const phaseForcedSlower = (phase: MovePhase, target: Pokemon, trickRoom: boolean
|
|||||||
let slower: boolean;
|
let slower: boolean;
|
||||||
// quashed pokemon still have speed ties
|
// quashed pokemon still have speed ties
|
||||||
if (phase.pokemon.getEffectiveStat(Stat.SPD) === target.getEffectiveStat(Stat.SPD)) {
|
if (phase.pokemon.getEffectiveStat(Stat.SPD) === target.getEffectiveStat(Stat.SPD)) {
|
||||||
slower = !target.randSeedInt(2);
|
slower = target.randSeedInt(2) === 0;
|
||||||
} else {
|
} else {
|
||||||
slower = !trickRoom ? phase.pokemon.getEffectiveStat(Stat.SPD) < target.getEffectiveStat(Stat.SPD) : phase.pokemon.getEffectiveStat(Stat.SPD) > target.getEffectiveStat(Stat.SPD);
|
slower = !trickRoom ? phase.pokemon.getEffectiveStat(Stat.SPD) < target.getEffectiveStat(Stat.SPD) : phase.pokemon.getEffectiveStat(Stat.SPD) > target.getEffectiveStat(Stat.SPD);
|
||||||
}
|
}
|
||||||
|
@ -642,7 +642,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
/**
|
/**
|
||||||
* Checks if a pokemon is fainted (ie: its `hp <= 0`).
|
* Checks if a pokemon is fainted (ie: its `hp <= 0`).
|
||||||
* It's usually better to call {@linkcode isAllowedInBattle()}
|
* It's usually better to call {@linkcode isAllowedInBattle()}
|
||||||
* @param checkStatus - Whether to also check the pokemon's status for {@linkcode StatusEffect.FAINT}; default `false`
|
* @param checkStatus `true` to also check that the pokemon's status is {@linkcode StatusEffect.FAINT}
|
||||||
* @returns `true` if the pokemon is fainted
|
* @returns `true` if the pokemon is fainted
|
||||||
*/
|
*/
|
||||||
public isFainted(checkStatus = false): boolean {
|
public isFainted(checkStatus = false): boolean {
|
||||||
@ -3256,9 +3256,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* If it rolls shiny, or if it's already shiny, also sets a random variant and give the Pokemon the associated luck.
|
* If it rolls shiny, or if it's already shiny, also sets a random variant and give the Pokemon the associated luck.
|
||||||
*
|
*
|
||||||
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536`
|
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536`
|
||||||
* @param thresholdOverride - number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
|
* @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
|
||||||
* @param applyModifiersToOverride - Whether to apply Shiny Charm and event modifiers to {@linkcode thresholdOverride}.
|
* @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride}
|
||||||
* Does nothing if {@linkcode thresholdOverride} is not set.
|
|
||||||
* @returns `true` if the Pokemon has been set as a shiny, `false` otherwise
|
* @returns `true` if the Pokemon has been set as a shiny, `false` otherwise
|
||||||
*/
|
*/
|
||||||
public trySetShinySeed(
|
public trySetShinySeed(
|
||||||
@ -3812,7 +3811,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
ui =>
|
ui =>
|
||||||
ui instanceof BattleInfo &&
|
ui instanceof BattleInfo &&
|
||||||
(ui as BattleInfo) instanceof PlayerBattleInfo === this.isPlayer(),
|
(ui as BattleInfo) instanceof PlayerBattleInfo === this.isPlayer(),
|
||||||
)[0] as Phaser.GameObjects.GameObject | undefined;
|
)
|
||||||
|
.find(() => true);
|
||||||
if (!otherBattleInfo || !this.getFieldIndex()) {
|
if (!otherBattleInfo || !this.getFieldIndex()) {
|
||||||
globalScene.fieldUI.sendToBack(this.battleInfo);
|
globalScene.fieldUI.sendToBack(this.battleInfo);
|
||||||
globalScene.sendTextToBack(); // Push the top right text objects behind everything else
|
globalScene.sendTextToBack(); // Push the top right text objects behind everything else
|
||||||
@ -5140,8 +5140,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
/**
|
/**
|
||||||
* Returns a list of the most recent move entries in this Pokemon's move history.
|
* Returns a list of the most recent move entries in this Pokemon's move history.
|
||||||
* The retrieved move entries are sorted in order from NEWEST to OLDEST.
|
* The retrieved move entries are sorted in order from NEWEST to OLDEST.
|
||||||
* @param moveCount The number of move entries to retrieve.\
|
* @param moveCount The number of move entries to retrieve.
|
||||||
* If negative, retrieves the Pokemon's entire move history (equivalent to reversing the output of {@linkcode getMoveHistory()}).
|
* If negative, retrieve the Pokemon's entire move history (equivalent to reversing the output of {@linkcode getMoveHistory()}).
|
||||||
* Default is `1`.
|
* Default is `1`.
|
||||||
* @returns A list of {@linkcode TurnMove}, as specified above.
|
* @returns A list of {@linkcode TurnMove}, as specified above.
|
||||||
*/
|
*/
|
||||||
@ -5660,19 +5660,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
if (effect === StatusEffect.SLEEP) {
|
if (effect === StatusEffect.SLEEP) {
|
||||||
sleepTurnsRemaining = new NumberHolder(this.randSeedIntRange(2, 4));
|
sleepTurnsRemaining = new NumberHolder(this.randSeedIntRange(2, 4));
|
||||||
|
|
||||||
this.setFrameRate(4);
|
this.setFrameRate(4);
|
||||||
|
|
||||||
// If the user is invulnerable, remove their invulnerability when they fall asleep
|
// If the user is invulnerable, lets remove their invulnerability when they fall asleep
|
||||||
// and remove the upcoming attack from the move queue.
|
const invulnerableTags = [
|
||||||
const tag = [
|
|
||||||
BattlerTagType.UNDERGROUND,
|
BattlerTagType.UNDERGROUND,
|
||||||
BattlerTagType.UNDERWATER,
|
BattlerTagType.UNDERWATER,
|
||||||
BattlerTagType.HIDDEN,
|
BattlerTagType.HIDDEN,
|
||||||
BattlerTagType.FLYING,
|
BattlerTagType.FLYING,
|
||||||
].find(t => this.getTag(t));
|
];
|
||||||
|
|
||||||
|
const tag = invulnerableTags.find(t => this.getTag(t));
|
||||||
|
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.removeTag(tag);
|
this.removeTag(tag);
|
||||||
this.getMoveQueue().shift();
|
this.getMoveQueue().pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8137,10 +8140,10 @@ export class PokemonMove {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments this move's {@linkcode ppUsed} variable (up to a maximum of {@link getMovePp}).
|
* Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp}
|
||||||
* @param count - Amount of PP to consume; default `1`
|
* @param count Amount of PP to use
|
||||||
*/
|
*/
|
||||||
usePp(count = 1) {
|
usePp(count: number = 1) {
|
||||||
this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp());
|
this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,11 +135,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* Compute targets and the results of hit checks of the invoked move against all targets,
|
* Compute targets and the results of hit checks of the invoked move against all targets,
|
||||||
* organized by battler index.
|
* organized by battler index.
|
||||||
*
|
*
|
||||||
* **This is *not* a pure function** and has the following side effects:
|
* **This is *not* a pure function**; it has the following side effects
|
||||||
* - Sets `this.hitChecks` to the results of the hit checks against each target
|
* - `this.hitChecks` - The results of the hit checks against each target
|
||||||
* - Sets success/failure of `this.moveHistoryEntry` based on the hit check results
|
* - `this.moveHistoryEntry` - Sets success or failure based on the hit check results
|
||||||
* - Sets `user.turnData.hitCount` and `user.turnData.hitsLeft` to 1 if the move
|
* - user.turnData.hitCount and user.turnData.hitsLeft - Both set to 1 if the
|
||||||
* was unsuccessful against all targets (effectively canceling it)
|
* move was unsuccessful against all targets
|
||||||
*
|
*
|
||||||
* @returns The targets of the invoked move
|
* @returns The targets of the invoked move
|
||||||
* @see {@linkcode hitCheck}
|
* @see {@linkcode hitCheck}
|
||||||
@ -205,9 +205,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the move to each of its resolved targets.
|
* Apply the move to each of the resolved targets.
|
||||||
* @param targets - The resolved set of targets of the move
|
* @param targets - The resolved set of targets of the move
|
||||||
* @throws - Error if there was an unexpected hit check result
|
* @throws Error if there was an unexpected hit check result
|
||||||
*/
|
*/
|
||||||
private applyToTargets(user: Pokemon, targets: Pokemon[]): void {
|
private applyToTargets(user: Pokemon, targets: Pokemon[]): void {
|
||||||
for (const [i, target] of targets.entries()) {
|
for (const [i, target] of targets.entries()) {
|
||||||
@ -229,7 +229,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
case HitCheckResult.NO_EFFECT_NO_MESSAGE:
|
case HitCheckResult.NO_EFFECT_NO_MESSAGE:
|
||||||
case HitCheckResult.PROTECTED:
|
case HitCheckResult.PROTECTED:
|
||||||
case HitCheckResult.TARGET_NOT_ON_FIELD:
|
case HitCheckResult.TARGET_NOT_ON_FIELD:
|
||||||
// Apply effects for ineffective moves (e.g. High Jump Kick crash dmg)
|
|
||||||
applyMoveAttrs(NoEffectAttr, user, target, this.move);
|
applyMoveAttrs(NoEffectAttr, user, target, this.move);
|
||||||
break;
|
break;
|
||||||
case HitCheckResult.MISS:
|
case HitCheckResult.MISS:
|
||||||
@ -643,19 +642,22 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) {
|
||||||
switch (true) {
|
return true;
|
||||||
// No Guard
|
}
|
||||||
case user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr):
|
if (this.move.hasAttr(ToxicAccuracyAttr) && user.isOfType(PokemonType.POISON)) {
|
||||||
// Toxic as poison type
|
return true;
|
||||||
case this.move.hasAttr(ToxicAccuracyAttr) && user.isOfType(PokemonType.POISON):
|
}
|
||||||
// Lock On/Mind Reader
|
// TODO: Fix lock on / mind reader check.
|
||||||
case !!user.getTag(BattlerTagType.IGNORE_ACCURACY):
|
if (
|
||||||
// Spikes and company
|
user.getTag(BattlerTagType.IGNORE_ACCURACY) &&
|
||||||
case isFieldTargeted(this.move):
|
(user.getLastXMoves().find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1
|
||||||
return true;
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isFieldTargeted(this.move)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,16 +27,16 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
/**
|
/**
|
||||||
* This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array.
|
* This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array.
|
||||||
* It also checks for Trick Room and reverses the array if it is present.
|
* It also checks for Trick Room and reverses the array if it is present.
|
||||||
* @returns An array of {@linkcode BattlerIndex}es containing all on-field pokemon sorted in speed order.
|
* @returns {@linkcode BattlerIndex[]} the battle indices of all pokemon on the field ordered by speed
|
||||||
*/
|
*/
|
||||||
getSpeedOrder(): BattlerIndex[] {
|
getSpeedOrder(): BattlerIndex[] {
|
||||||
const playerField = globalScene.getPlayerField().filter(p => p.isActive()) as Pokemon[];
|
const playerField = globalScene.getPlayerField().filter(p => p.isActive()) as Pokemon[];
|
||||||
const enemyField = globalScene.getEnemyField().filter(p => p.isActive()) as Pokemon[];
|
const enemyField = globalScene.getEnemyField().filter(p => p.isActive()) as Pokemon[];
|
||||||
|
|
||||||
// Shuffle the list before sorting so speed ties produce random results
|
// We shuffle the list before sorting so speed ties produce random results
|
||||||
// This is seeded with the current turn to prevent an inconsistency with variable turn order
|
|
||||||
// based on how long since you last reloaded
|
|
||||||
let orderedTargets: Pokemon[] = playerField.concat(enemyField);
|
let orderedTargets: Pokemon[] = playerField.concat(enemyField);
|
||||||
|
// We seed it with the current turn to prevent an inconsistency where it
|
||||||
|
// was varying based on how long since you last reloaded
|
||||||
globalScene.executeWithSeedOffset(
|
globalScene.executeWithSeedOffset(
|
||||||
() => {
|
() => {
|
||||||
orderedTargets = randSeedShuffle(orderedTargets);
|
orderedTargets = randSeedShuffle(orderedTargets);
|
||||||
@ -45,11 +45,11 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
globalScene.waveSeed,
|
globalScene.waveSeed,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check for Trick Room and reverse sort order if active.
|
// Next, a check for Trick Room is applied to determine sort order.
|
||||||
// Notably, Pokerogue does NOT have the "outspeed trick room" glitch at >1809 spd.
|
|
||||||
const speedReversed = new BooleanHolder(false);
|
const speedReversed = new BooleanHolder(false);
|
||||||
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
|
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
|
||||||
|
|
||||||
|
// Adjust the sort function based on whether Trick Room is active.
|
||||||
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
||||||
const aSpeed = a?.getEffectiveStat(Stat.SPD) ?? 0;
|
const aSpeed = a?.getEffectiveStat(Stat.SPD) ?? 0;
|
||||||
const bSpeed = b?.getEffectiveStat(Stat.SPD) ?? 0;
|
const bSpeed = b?.getEffectiveStat(Stat.SPD) ?? 0;
|
||||||
@ -120,8 +120,7 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no difference between the move's calculated priorities,
|
// If there is no difference between the move's calculated priorities, the game checks for differences in battlerBypassSpeed and returns the result.
|
||||||
// check for differences in battlerBypassSpeed and returns the result.
|
|
||||||
if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) {
|
if (battlerBypassSpeed[a].value !== battlerBypassSpeed[b].value) {
|
||||||
return battlerBypassSpeed[a].value ? -1 : 1;
|
return battlerBypassSpeed[a].value ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
|
|
||||||
function confirmSwitch(): void {
|
function confirmSwitch(): void {
|
||||||
const [pokemon1, pokemon2] = game.scene.getPlayerParty();
|
const [pokemon1, pokemon2] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
|
|
||||||
expect(pokemon1.species.speciesId).not.toBe(Species.WIMPOD);
|
expect(pokemon1.species.speciesId).not.toBe(Species.WIMPOD);
|
||||||
@ -55,34 +56,17 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
|
|
||||||
function confirmNoSwitch(): void {
|
function confirmNoSwitch(): void {
|
||||||
const [pokemon1, pokemon2] = game.scene.getPlayerParty();
|
const [pokemon1, pokemon2] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
||||||
|
|
||||||
|
expect(pokemon2.species.speciesId).not.toBe(Species.WIMPOD);
|
||||||
|
|
||||||
expect(pokemon1.species.speciesId).toBe(Species.WIMPOD);
|
expect(pokemon1.species.speciesId).toBe(Species.WIMPOD);
|
||||||
expect(pokemon1.isFainted()).toBe(false);
|
expect(pokemon1.isFainted()).toBe(false);
|
||||||
expect(pokemon1.getHpRatio()).toBeLessThan(0.5);
|
expect(pokemon1.getHpRatio()).toBeLessThan(0.5);
|
||||||
|
|
||||||
expect(pokemon2.species.speciesId).not.toBe(Species.WIMPOD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should switch user out when falling below 50% HP, cancelling any pending moves", async () => {
|
it("triggers regenerator passive single time when switching out with wimp out", async () => {
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
|
||||||
|
|
||||||
const wimpod = game.scene.getPlayerPokemon()!;
|
|
||||||
wimpod.hp *= 0.51;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
|
||||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
|
||||||
game.phaseInterceptor.clearLogs();
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
confirmSwitch();
|
|
||||||
expect(wimpod.turnData.acted).toBe(false);
|
|
||||||
expect(game.phaseInterceptor.log).not.toContain("MoveEffectPhase");
|
|
||||||
});
|
|
||||||
|
|
||||||
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]);
|
||||||
|
|
||||||
@ -96,7 +80,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should cause wild pokemon to flee", async () => {
|
it("It makes wild pokemon flee if 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]);
|
||||||
|
|
||||||
@ -106,11 +90,12 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
game.move.select(Moves.FALSE_SWIPE);
|
game.move.select(Moves.FALSE_SWIPE);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(enemyPokemon.visible).toBe(false);
|
const isVisible = enemyPokemon.visible;
|
||||||
expect(enemyPokemon.switchOutStatus).toBe(true);
|
const hasFled = enemyPokemon.switchOutStatus;
|
||||||
|
expect(!isVisible && hasFled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not trigger when HP is already below half", async () => {
|
it("Does 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;
|
||||||
@ -122,7 +107,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmNoSwitch();
|
confirmNoSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should bypass trapping moves and abilities", async () => {
|
it("Trapping moves do not prevent Wimp Out from activating.", 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]);
|
||||||
|
|
||||||
@ -137,7 +122,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should block switching from U-Turn on activation", async () => {
|
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 () => {
|
||||||
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]);
|
||||||
|
|
||||||
@ -151,7 +136,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not block switching from U-Turn on failed activation", async () => {
|
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 () => {
|
||||||
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;
|
||||||
@ -160,7 +145,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("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.", 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]);
|
||||||
|
|
||||||
@ -177,7 +162,7 @@ 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("should trigger from recoil damage", async () => {
|
it("triggers when recoil damage is taken", async () => {
|
||||||
game.override.moveset([Moves.HEAD_SMASH]).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]);
|
||||||
|
|
||||||
@ -188,7 +173,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not activate when the Pokémon cuts its own HP", async () => {
|
it("It does not activate when the Pokémon cuts its own HP", async () => {
|
||||||
game.override.moveset([Moves.SUBSTITUTE]).enemyMoveset([Moves.SPLASH]);
|
game.override.moveset([Moves.SUBSTITUTE]).enemyMoveset([Moves.SPLASH]);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -201,19 +186,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmNoSwitch();
|
confirmNoSwitch();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not trigger from Sheer Force-boosted moves", async () => {
|
it("Does not trigger when neutralized", async () => {
|
||||||
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
|
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
|
||||||
|
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
|
||||||
|
|
||||||
game.move.select(Moves.ENDURE);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
confirmNoSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not trigger while neutralized", async () => {
|
|
||||||
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS).startingLevel(5);
|
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS).startingLevel(5);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -250,140 +223,106 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Condense into it.eaches
|
it("Wimp Out will activate due to weather damage", async () => {
|
||||||
describe("Post Turn Damage Checks - ", () => {
|
game.override.weather(WeatherType.HAIL).enemyMoveset([Moves.SPLASH]);
|
||||||
beforeEach(() => {
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
game.override.enemyMoveset(Moves.SPLASH);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Wimp Out will activate due to weather damage", async () => {
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
game.override.weather(WeatherType.HAIL);
|
|
||||||
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");
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
confirmSwitch();
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
confirmSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Wimp Out will activate due to post turn status damage", async () => {
|
|
||||||
game.override.statusEffect(StatusEffect.POISON);
|
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
|
||||||
|
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
confirmSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Wimp Out will activate due to leech seed", async () => {
|
|
||||||
game.override.enemyMoveset([Moves.LEECH_SEED]);
|
|
||||||
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();
|
|
||||||
|
|
||||||
confirmSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Wimp Out will activate due to curse damage", async () => {
|
|
||||||
game.override.enemySpecies(Species.DUSKNOIR).enemyMoveset([Moves.CURSE]);
|
|
||||||
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();
|
|
||||||
|
|
||||||
confirmSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Wimp Out will activate due to salt cure damage", async () => {
|
|
||||||
game.override.enemySpecies(Species.NACLI).enemyMoveset([Moves.SALT_CURE]).enemyLevel(1);
|
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.7;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
confirmSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
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("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("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]);
|
|
||||||
|
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
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.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
|
||||||
game.override.enemySpecies(Species.CENTISKORCH).enemyAbility(Abilities.WIMP_OUT).startingWave(4);
|
|
||||||
await game.classicMode.startBattle([Species.TYRUNT]);
|
|
||||||
|
|
||||||
expect(game.phaseInterceptor.log).not.toContain("MovePhase");
|
|
||||||
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("should not trigger on Magic Guard-prevented damage", async () => {
|
it("Does not trigger when enemy has sheer force", async () => {
|
||||||
|
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
|
||||||
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.ENDURE);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmNoSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to post turn status damage", async () => {
|
||||||
|
game.override.statusEffect(StatusEffect.POISON).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.toNextTurn();
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to leech seed", async () => {
|
||||||
|
game.override.enemyMoveset([Moves.LEECH_SEED]);
|
||||||
|
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();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to curse damage", async () => {
|
||||||
|
game.override.enemySpecies(Species.DUSKNOIR).enemyMoveset([Moves.CURSE]);
|
||||||
|
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();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to salt cure damage", async () => {
|
||||||
|
game.override.enemySpecies(Species.NACLI).enemyMoveset([Moves.SALT_CURE]).enemyLevel(1);
|
||||||
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.7;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 () => {
|
||||||
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
|
||||||
@ -392,7 +331,6 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
.weather(WeatherType.HAIL)
|
.weather(WeatherType.HAIL)
|
||||||
.statusEffect(StatusEffect.POISON);
|
.statusEffect(StatusEffect.POISON);
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
@ -403,19 +341,6 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.WIMPOD);
|
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.WIMPOD);
|
||||||
});
|
});
|
||||||
|
|
||||||
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]);
|
|
||||||
vi.spyOn(allMoves[Moves.SLUDGE_BOMB], "chance", "get").mockReturnValue(100);
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(game.scene.getPlayerParty()[1].status?.effect).toEqual(StatusEffect.POISON);
|
|
||||||
confirmSwitch();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Wimp Out activating should not cancel a double battle", async () => {
|
it("Wimp Out activating should not cancel a double battle", 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]);
|
||||||
@ -436,7 +361,59 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
|
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("triggers after last hit of multi hit moves", async () => {
|
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 () => {
|
||||||
|
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);
|
||||||
|
await game.classicMode.startBattle([Species.TYRUNT]);
|
||||||
|
|
||||||
|
expect(game.phaseInterceptor.log).not.toContain("MovePhase");
|
||||||
|
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]);
|
||||||
|
vi.spyOn(allMoves[Moves.SLUDGE_BOMB], "chance", "get").mockReturnValue(100);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerParty()[1].status?.effect).toEqual(StatusEffect.POISON);
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers after last hit of multi hit move", 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]);
|
||||||
|
|
||||||
@ -467,7 +444,6 @@ 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]);
|
||||||
|
Loading…
Reference in New Issue
Block a user