From c882c85f1f84dc8f237351ff3292e794e400fd51 Mon Sep 17 00:00:00 2001 From: Shadow Date: Tue, 21 May 2024 23:47:49 -0400 Subject: [PATCH] Fix Beast Boost abilities + revert Soul Heart functionality Split PostKnockout Stat Change abilities to account and add a variation which ensures the attacker's move damage was the final blow. --- src/data/ability.ts | 58 +++++++++++++++++++++++++++++++++++---------- src/phases.ts | 7 +++++- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index f0ee2cccd1b..18dc0d6cc3e 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1303,12 +1303,15 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { } export class PostKnockOutAbAttr extends AbAttr { - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, attacker: Pokemon, args: any[]): boolean | Promise { return false; } } -export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { +/* +* Attribute used for abilities (e.g. Soul Heart) which boost stats from any fainting, including allies +*/ +export class PostAnyKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { private stat: BattleStat | ((p: Pokemon) => BattleStat); private levels: integer; @@ -1319,7 +1322,7 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { this.levels = levels; } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, attacker: Pokemon, args: any[]): boolean | Promise { const stat = typeof this.stat === 'function' ? this.stat(pokemon) : this.stat; @@ -1329,12 +1332,41 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { } } +/* +* Attribute used for abilities (e.g. Beast Boost) which boost stats from an attack's direct damage. +*/ +export class PostDirectKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { + private stat: BattleStat | ((p: Pokemon) => BattleStat); + private levels: integer; + + constructor(stat: BattleStat | ((p: Pokemon) => BattleStat), levels: integer) { + super(); + + this.stat = stat; + this.levels = levels; + } + + applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, attacker: Pokemon, args: any[]): boolean | Promise { + if(attacker === pokemon) + { + const stat = typeof this.stat === 'function' + ? this.stat(pokemon) + : this.stat; + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + + return true; + } + + return false; + } +} + export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { constructor() { super(); } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, attacker: Pokemon, args: any[]): boolean | Promise { if (pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr)) { pokemon.summonData.ability = knockedOut.getAbility().id; pokemon.scene.queueMessage(getPokemonMessage(knockedOut, `'s ${allAbilities[knockedOut.getAbility().id].name} was taken over!`)); @@ -2907,8 +2939,8 @@ export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAtta } export function applyPostKnockOutAbAttrs(attrType: { new(...args: any[]): PostKnockOutAbAttr }, - pokemon: Pokemon, knockedOut: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, knockedOut, args), args); + pokemon: Pokemon, knockedOut: Pokemon, attacker: Pokemon, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, knockedOut, attacker, args), args); } export function applyPostVictoryAbAttrs(attrType: { new(...args: any[]): PostVictoryAbAttr }, @@ -3435,7 +3467,7 @@ export function initAbilities() { .attr(PostDefendAbilityGiveAbAttr, Abilities.MUMMY) .bypassFaint(), new Ability(Abilities.MOXIE, 5) - .attr(PostKnockOutStatChangeAbAttr, BattleStat.ATK, 1), + .attr(PostDirectKnockOutStatChangeAbAttr, BattleStat.ATK, 1), new Ability(Abilities.JUSTIFIED, 5) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.DARK && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1), new Ability(Abilities.RATTLED, 5) @@ -3653,7 +3685,7 @@ export function initAbilities() { .attr(FieldPriorityMoveImmunityAbAttr) .ignorable(), new Ability(Abilities.SOUL_HEART, 7) - .attr(PostVictoryStatChangeAbAttr, BattleStat.SPATK, 1), + .attr(PostAnyKnockOutStatChangeAbAttr, BattleStat.SPATK, 1), new Ability(Abilities.TANGLING_HAIR, 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false), new Ability(Abilities.RECEIVER, 7) @@ -3663,7 +3695,7 @@ export function initAbilities() { .attr(CopyFaintedAllyAbilityAbAttr) .attr(UncopiableAbilityAbAttr), new Ability(Abilities.BEAST_BOOST, 7) - .attr(PostKnockOutStatChangeAbAttr, p => { + .attr(PostDirectKnockOutStatChangeAbAttr, p => { const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3).map(s => s as BattleStat); let highestBattleStat = 0; let highestBattleStatIndex = 0; @@ -3794,18 +3826,18 @@ export function initAbilities() { new Ability(Abilities.DRAGONS_MAW, 8) .attr(MoveTypePowerBoostAbAttr, Type.DRAGON), new Ability(Abilities.CHILLING_NEIGH, 8) - .attr(PostKnockOutStatChangeAbAttr, BattleStat.ATK, 1), + .attr(PostDirectKnockOutStatChangeAbAttr, BattleStat.ATK, 1), new Ability(Abilities.GRIM_NEIGH, 8) - .attr(PostKnockOutStatChangeAbAttr, BattleStat.SPATK, 1), + .attr(PostDirectKnockOutStatChangeAbAttr, BattleStat.SPATK, 1), new Ability(Abilities.AS_ONE_GLASTRIER, 8) .attr(PreventBerryUseAbAttr) - .attr(PostKnockOutStatChangeAbAttr, BattleStat.ATK, 1) + .attr(PostDirectKnockOutStatChangeAbAttr, BattleStat.ATK, 1) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), new Ability(Abilities.AS_ONE_SPECTRIER, 8) .attr(PreventBerryUseAbAttr) - .attr(PostKnockOutStatChangeAbAttr, BattleStat.SPATK, 1) + .attr(PostDirectKnockOutStatChangeAbAttr, BattleStat.SPATK, 1) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), diff --git a/src/phases.ts b/src/phases.ts index b861c82f0e8..d024c901575 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -3234,7 +3234,12 @@ export class FaintPhase extends PokemonPhase { } const alivePlayField = this.scene.getField(true); - alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon)); + if (pokemon.turnData?.attacksReceived?.length) { + const lastAttack = pokemon.turnData.attacksReceived[0]; + alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon, this.scene.getPokemonById(lastAttack.sourceId))); + } else { + alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon, null)); + } if (pokemon.turnData?.attacksReceived?.length) { const defeatSource = this.scene.getPokemonById(pokemon.turnData.attacksReceived[0].sourceId); if (defeatSource?.isOnField()) {