diff --git a/src/data/ability.ts b/src/data/ability.ts index cc9d10ed68b..e60394c9720 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -9,7 +9,7 @@ import { BattlerTag } from "./battler-tags"; import { BattlerTagType } from "./enums/battler-tag-type"; import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; -import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, allMoves } from "./move"; +import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves } from "./move"; import { ArenaTagSide } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; import { Stat } from "./pokemon-stat"; @@ -521,6 +521,16 @@ 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!`)); + return true; + } + return false; + } +} + export class PostDefendStatChangeAbAttr extends PostDefendAbAttr { private condition: PokemonDefendCondition; private stat: BattleStat; @@ -2549,7 +2559,8 @@ export function initAbilities() { new Ability(Abilities.MARVEL_SCALE, "Marvel Scale", "The Pokémon's marvelous scales boost the Defense stat if it has a status condition.", 3) .conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5) .ignorable(), - new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "The oozed liquid has a strong stench, which damages attackers using any draining move.", 3), + new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze", "The oozed liquid has a strong stench, which damages attackers using any draining move.", 3) + .attr(ReverseDrainAbAttr), new Ability(Abilities.OVERGROW, "Overgrow", "Powers up Grass-type moves when the Pokémon's HP is low.", 3) .attr(LowHpMoveTypePowerBoostAbAttr, Type.GRASS), new Ability(Abilities.BLAZE, "Blaze", "Powers up Fire-type moves when the Pokémon's HP is low.", 3) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index c856fa05d3f..25ff41ec3e2 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, applyAbAttrs } from "./ability"; +import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability"; import { Abilities } from "./enums/abilities"; import { BattlerTagType } from "./enums/battler-tag-type"; import { TerrainType } from "./terrain"; @@ -292,7 +292,11 @@ export class SeedTag extends BattlerTag { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true)); + 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)); } } } diff --git a/src/data/move.ts b/src/data/move.ts index df5a9fdbe85..08f03ac05f1 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -12,7 +12,7 @@ import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; -import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr } from "./ability"; +import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr } from "./ability"; import { Abilities } from "./enums/abilities"; import { allAbilities } from './ability'; import { PokemonHeldItemModifier } from "../modifier/modifier"; @@ -844,8 +844,12 @@ 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 = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr); user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), - Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1), getPokemonMessage(target, ` had its\nenergy drained!`), false, true)); + !reverseDrain ? healAmount : healAmount * -1, + !reverseDrain ? getPokemonMessage(target, ` had its\nenergy drained!`) : undefined, + false, true)); return true; } @@ -860,9 +864,12 @@ 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 = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr); user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), - target.stats[Stat.ATK] * (Math.max(2, 2 + target.summonData.battleStats[BattleStat.ATK]) / Math.max(2, 2 - target.summonData.battleStats[BattleStat.ATK])), - getPokemonMessage(user, ` regained\nhealth!`), false, true)); + !reverseDrain ? healAmount : healAmount * -1, + !reverseDrain ? getPokemonMessage(user, ` regained\nhealth!`) : undefined, + false, true)); return true; } } diff --git a/src/phases.ts b/src/phases.ts index 7a766211f3a..f041d2daae8 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -3796,11 +3796,15 @@ export class PokemonHealPhase extends CommonAnimPhase { const hasMessage = !!this.message; let lastStatusEffect = StatusEffect.NONE; - if (!fullHp) { + if (!fullHp || this.hpHealed < 0) { 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;