diff --git a/src/data/ability.ts b/src/data/ability.ts index f2220c850..8ae348f70 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -549,8 +549,9 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr { export class ReverseDrainAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { - if (!!move.getMove().getAttrs(HitHealAttr).length || !!move.getMove().getAttrs(StrengthSapHealAttr).length ) { - pokemon.scene.queueMessage(getPokemonMessage(attacker, ` sucked up the liquid ooze!`)); + const isDrain = args.some(arg => typeof arg === 'object' && 'seedDrain' in arg && !!arg['seedDrain']); + if (!!move.getMove().getAttrs(HitHealAttr).length || !!move.getMove().getAttrs(StrengthSapHealAttr).length || isDrain) { + pokemon.scene.queueMessage(getPokemonMessage(attacker, ' sucked up the liquid ooze!')); return true; } return false; @@ -2942,7 +2943,8 @@ export function initAbilities() { .conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5) .ignorable(), new Ability(Abilities.LIQUID_OOZE, 3) - .attr(ReverseDrainAbAttr), + .attr(ReverseDrainAbAttr) + .bypassFaint(), new Ability(Abilities.OVERGROW, 3) .attr(LowHpMoveTypePowerBoostAbAttr, Type.GRASS), new Ability(Abilities.BLAZE, 3) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index ba00b0906..73212a33d 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -8,7 +8,7 @@ import * as Utils from "../utils"; import { Moves } from "./enums/moves"; import { ChargeAttr, MoveFlags, allMoves } from "./move"; import { Type } from "./type"; -import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability"; +import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, applyPostDefendAbAttrs } from "./ability"; import { Abilities } from "./enums/abilities"; import { BattlerTagType } from "./enums/battler-tag-type"; import { TerrainType } from "./terrain"; @@ -305,6 +305,7 @@ export class InfatuatedTag extends BattlerTag { export class SeedTag extends BattlerTag { private sourceIndex: integer; + private sourceMove: Moves; constructor(sourceId: integer) { super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId); @@ -327,7 +328,9 @@ export class SeedTag extends BattlerTag { super.onAdd(pokemon); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' was seeded!')); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + const source = pokemon.scene.getPokemonById(this.sourceId); + this.sourceIndex = source.getBattlerIndex(); + this.sourceMove = source.getLastXMoves(1).shift().move; } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -342,12 +345,11 @@ export class SeedTag extends BattlerTag { if (!cancelled.value) { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); + const move = source.getMoveset().find(m => m.moveId === this.sourceMove); const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); - const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr); - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), - !reverseDrain ? damage : damage * -1, - !reverseDrain ? getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!') : getPokemonMessage(source, '\'s Leech Seed\nsucked up the liquid ooze!'), - false, true)); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, `'s health is\nsapped by ${move.getName()}!`)); + pokemon.drain(source, damage, ''); + applyPostDefendAbAttrs(ReverseDrainAbAttr, pokemon, source, move, HitResult.STATUS, { seedDrain: true }); } } } diff --git a/src/data/move.ts b/src/data/move.ts index 6e4e3f60f..9d6afb913 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -973,12 +973,7 @@ export class HitHealAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const healAmount = Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1); - const reverseDrain = user.hasAbilityWithAttr(ReverseDrainAbAttr); - user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), - !reverseDrain ? healAmount : healAmount * -1, - !reverseDrain ? getPokemonMessage(target, ` had its\nenergy drained!`) : undefined, - false, true)); - if (reverseDrain) user.turnData.damageTaken += healAmount; + target.drain(user, healAmount, getPokemonMessage(target, ' had its\nenergy drained!')); return true; } @@ -994,11 +989,7 @@ export class StrengthSapHealAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const healAmount = target.stats[Stat.ATK] * (Math.max(2, 2 + target.summonData.battleStats[BattleStat.ATK]) / Math.max(2, 2 - target.summonData.battleStats[BattleStat.ATK])); - const reverseDrain = user.hasAbilityWithAttr(ReverseDrainAbAttr); - user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), - !reverseDrain ? healAmount : healAmount * -1, - !reverseDrain ? getPokemonMessage(user, ` regained\nhealth!`) : undefined, - false, true)); + target.drain(user, healAmount, getPokemonMessage(target, ' regained\nhealth!')); return true; } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 8afff1b27..0ec865dad 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -17,7 +17,7 @@ import { initMoveAnim, loadMoveAnimAssets } from '../data/battle-anims'; import { Status, StatusEffect, getRandomStatus } from '../data/status-effect'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from '../data/pokemon-evolutions'; import { reverseCompatibleTms, tmSpecies } from '../data/tms'; -import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase } from '../phases'; +import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from '../phases'; import { BattleStat } from '../data/battle-stat'; import { BattlerTag, BattlerTagLapseType, EncoreTag, HelpingHandTag, HighestStatBoostTag, TypeBoostTag, getBattlerTag } from '../data/battler-tags'; import { BattlerTagType } from "../data/enums/battler-tag-type"; @@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, ReverseDrainAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -2341,6 +2341,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.battleInfo?.destroy(); super.destroy(); } + + /** Heals the source Pokemon of the given amount, or damages it if this Pokemon has a ReverseDrainAbAttr ability. */ + drain(source: Pokemon, amount: number, healMessage: string): void { + const reverseDrain = this.hasAbilityWithAttr(ReverseDrainAbAttr); + if (reverseDrain) { + source.damageAndUpdate(amount); + source.turnData.damageTaken += amount; + } + else + source.scene.unshiftPhase(new PokemonHealPhase(source.scene, source.getBattlerIndex(), amount, healMessage, false, true)); + } } export default interface Pokemon { diff --git a/src/phases.ts b/src/phases.ts index b697f7acb..3250fe8b8 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -3990,15 +3990,11 @@ export class PokemonHealPhase extends CommonAnimPhase { const hasMessage = !!this.message; let lastStatusEffect = StatusEffect.NONE; - if (!fullHp || this.hpHealed < 0) { + if (!fullHp) { const hpRestoreMultiplier = new Utils.IntegerHolder(1); if (!this.revive) this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); - if (healAmount.value < 0) { - pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL); - healAmount.value = 0; - } // Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock) if (this.preventFullHeal && pokemon.hp + healAmount.value >= pokemon.getMaxHp()) healAmount.value = (pokemon.getMaxHp() - pokemon.hp) - 1;