diff --git a/src/data/ability.ts b/src/data/ability.ts index f222d5b80d8..757c645cbce 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -51,6 +51,7 @@ export class Ability { } } +type AbAttrApplyFunc = (attr: TAttr) => boolean | Promise; type AbAttrCondition = (pokemon: Pokemon) => boolean; export abstract class AbAttr { @@ -443,7 +444,7 @@ export class BattleStatMultiplierAbAttr extends AbAttr { private multiplier: number; constructor(battleStat: BattleStat, multiplier: number) { - super(); + super(false); this.battleStat = battleStat; this.multiplier = multiplier; @@ -902,6 +903,10 @@ export class RunSuccessAbAttr extends AbAttr { } export class CheckTrappedAbAttr extends AbAttr { + constructor() { + super(false); + } + applyCheckTrapped(pokemon: Pokemon, trapped: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } @@ -942,13 +947,14 @@ export class WeightMultiplierAbAttr extends AbAttr { } } -export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { +function applyAbAttrsInternal(attrType: { new(...args: any[]): TAttr }, + pokemon: Pokemon, applyFunc: AbAttrApplyFunc, isAsync?: boolean, showAbilityInstant?: boolean): Promise { return new Promise(resolve => { if (!pokemon.canApplyAbility()) return resolve(); const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as AbAttr[]; + const attrs = ability.getAttrs(attrType) as TAttr[]; const clearSpliceQueueAndResolve = () => { pokemon.scene.clearPhaseQueueSplice(); @@ -960,18 +966,26 @@ export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: else clearSpliceQueueAndResolve(); }; - const applyAbAttr = (attr: AbAttr) => { + const applyAbAttr = (attr: TAttr) => { if (!canApplyAttr(pokemon, attr)) return applyNextAbAttr(); pokemon.scene.setPhaseQueueSplice(); const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); + if (attr.showAbility) { + if (showAbilityInstant) + pokemon.scene.abilityBar.showAbility(pokemon); + else + queueShowAbility(pokemon); + } const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); + if (message) { + if (isAsync) + pokemon.scene.ui.showText(message, null, () => pokemon.scene.ui.showText(null, 0), null, true); + else + pokemon.scene.queueMessage(message); + } }; - const result = attr.apply(pokemon, cancelled, args); + const result = applyFunc(attr); if (result instanceof Promise) { result.then(success => { if (success) @@ -988,614 +1002,73 @@ export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: }); } +export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, attr => attr.apply(pokemon, cancelled, args)); +} + export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr }, pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PreDefendAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PreDefendAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPreDefend(pokemon, attacker, move, cancelled, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPreDefend(pokemon, attacker, move, cancelled, args)); } export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr }, pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PostDefendAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PostDefendAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPostDefend(pokemon, attacker, move, hitResult, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPostDefend(pokemon, attacker, move, hitResult, args)); } export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr }, pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as BattleStatMultiplierAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: BattleStatMultiplierAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyBattleStat(pokemon, battleStat, statValue, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyBattleStat(pokemon, battleStat, statValue, args)); } export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttackAbAttr }, pokemon: Pokemon, defender: Pokemon, move: PokemonMove, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PreAttackAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PreAttackAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPreAttack(pokemon, defender, move, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPreAttack(pokemon, defender, move, args)); } export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAttackAbAttr }, pokemon: Pokemon, defender: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PostAttackAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PostAttackAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPostAttack(pokemon, defender, move, hitResult, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPostAttack(pokemon, defender, move, hitResult, args)); } export function applyPostSummonAbAttrs(attrType: { new(...args: any[]): PostSummonAbAttr }, pokemon: Pokemon, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PostSummonAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PostSummonAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPostSummon(pokemon, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPostSummon(pokemon, args)); } export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreStatChangeAbAttr }, pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PreStatChangeAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PreStatChangeAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPreStatChange(pokemon, stat, cancelled, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPreStatChange(pokemon, stat, cancelled, args)); } export function applyPreSetStatusAbAttrs(attrType: { new(...args: any[]): PreSetStatusAbAttr }, pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PreSetStatusAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PreSetStatusAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPreSetStatus(pokemon, effect, cancelled, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPreSetStatus(pokemon, effect, cancelled, args)); } export function applyPreApplyBattlerTagAbAttrs(attrType: { new(...args: any[]): PreApplyBattlerTagAbAttr }, pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PreApplyBattlerTagAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PreApplyBattlerTagAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPreApplyBattlerTag(pokemon, tag, cancelled, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPreApplyBattlerTag(pokemon, tag, cancelled, args)); } export function applyPreWeatherEffectAbAttrs(attrType: { new(...args: any[]): PreWeatherEffectAbAttr }, pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PreWeatherEffectAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PreWeatherEffectAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - pokemon.scene.abilityBar.showAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPreWeatherEffect(pokemon, weather, cancelled, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPreWeatherEffect(pokemon, weather, cancelled, args), false, true); } export function applyPostTurnAbAttrs(attrType: { new(...args: any[]): PostTurnAbAttr }, pokemon: Pokemon, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PostTurnAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PostTurnAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPostTurn(pokemon, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPostTurn(pokemon, args)); } export function applyPostWeatherLapseAbAttrs(attrType: { new(...args: any[]): PostWeatherLapseAbAttr }, pokemon: Pokemon, weather: Weather, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - if (weather.isEffectSuppressed(pokemon.scene)) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as PostWeatherLapseAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: PostWeatherLapseAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - if (attr.showAbility) - queueShowAbility(pokemon); - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.queueMessage(message); - }; - const result = attr.applyPostWeatherLapse(pokemon, weather, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyPostWeatherLapse(pokemon, weather, args)); } export function applyCheckTrappedAbAttrs(attrType: { new(...args: any[]): CheckTrappedAbAttr }, pokemon: Pokemon, trapped: Utils.BooleanHolder, ...args: any[]): Promise { - return new Promise(resolve => { - if (!pokemon.canApplyAbility()) - return resolve(); - - const ability = pokemon.getAbility(); - const attrs = ability.getAttrs(attrType) as CheckTrappedAbAttr[]; - - const clearSpliceQueueAndResolve = () => { - pokemon.scene.clearPhaseQueueSplice(); - resolve(); - }; - const applyNextAbAttr = () => { - if (attrs.length) - applyAbAttr(attrs.shift()); - else - clearSpliceQueueAndResolve(); - }; - const applyAbAttr = (attr: CheckTrappedAbAttr) => { - if (!canApplyAttr(pokemon, attr)) - return applyNextAbAttr(); - pokemon.scene.setPhaseQueueSplice(); - const onApplySuccess = () => { - // Don't show ability bar because this call is asynchronous - const message = attr.getTriggerMessage(pokemon); - if (message) - pokemon.scene.ui.showText(message, null, () => pokemon.scene.ui.showText(null, 0), null, true); - }; - const result = attr.applyCheckTrapped(pokemon, trapped, args); - if (result instanceof Promise) { - result.then(success => { - if (success) - onApplySuccess(); - applyNextAbAttr(); - }); - } else { - if (result) - onApplySuccess(); - applyNextAbAttr(); - } - }; - applyNextAbAttr(); - }); + return applyAbAttrsInternal(attrType, pokemon, attr => attr.applyCheckTrapped(pokemon, trapped, args), true); } function canApplyAttr(pokemon: Pokemon, attr: AbAttr): boolean {