Cleaned up pokemonHealPhase + wrapped inside an object

This commit is contained in:
Bertie690 2025-08-05 12:53:10 -04:00
parent e760ed9949
commit 90c9c71cd9
9 changed files with 276 additions and 206 deletions

View File

@ -748,16 +748,12 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr {
const { pokemon, cancelled, simulated, passive } = params;
if (!pokemon.isFullHp() && !simulated) {
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 4),
i18next.t("abilityTriggers:typeImmunityHeal", {
globalScene.phaseManager.unshiftNew("PokemonHealPhase", pokemon.getBattlerIndex(), pokemon.getMaxHp() / 4, {
message: i18next.t("abilityTriggers:typeImmunityHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
true,
);
});
cancelled.value = true; // Suppresses "No Effect" message
}
}
@ -2830,12 +2826,13 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr {
"PokemonHealPhase",
target.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / this.healRatio),
i18next.t("abilityTriggers:postSummonAllyHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(target),
pokemonName: pokemon.name,
}),
true,
!this.showAnim,
{
message: i18next.t("abilityTriggers:postSummonAllyHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(target),
pokemonName: pokemon.name,
}),
skipAnim: !this.showAnim,
},
);
}
}
@ -4476,11 +4473,12 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)),
i18next.t("abilityTriggers:postWeatherLapseHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
true,
{
message: i18next.t("abilityTriggers:postWeatherLapseHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
},
);
}
}
@ -4595,8 +4593,12 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 8),
i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }),
true,
{
message: i18next.t("abilityTriggers:poisonHeal", {
pokemonName: getPokemonNameWithAffix(pokemon),
abilityName,
}),
},
);
}
}
@ -4843,11 +4845,12 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 16),
i18next.t("abilityTriggers:postTurnHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
true,
{
message: i18next.t("abilityTriggers:postTurnHeal", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
},
);
}
}
@ -5224,11 +5227,12 @@ export class HealFromBerryUseAbAttr extends AbAttr {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() * this.healPercent),
i18next.t("abilityTriggers:healFromBerryUse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
true,
{
message: i18next.t("abilityTriggers:healFromBerryUse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName,
}),
},
);
}
}

View File

@ -1076,18 +1076,16 @@ export class SeedTag extends SerializableBattlerTag {
);
// Damage the target and restore our HP (or take damage in the case of liquid ooze)
// TODO: Liquid ooze should queue a damage anim phase directly
const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
const reverseDrain = pokemon.hasAbilityWithAttr("ReverseDrainAbAttr", false);
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
source.getBattlerIndex(),
reverseDrain ? -damage : damage,
i18next.t(reverseDrain ? "battlerTags:seededLapseShed" : "battlerTags:seededLapse", {
globalScene.phaseManager.unshiftNew("PokemonHealPhase", source.getBattlerIndex(), reverseDrain ? -damage : damage, {
message: i18next.t(reverseDrain ? "battlerTags:seededLapseShed" : "battlerTags:seededLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
}),
false,
true,
);
showFullHpMessage: false,
skipAnim: true,
});
return true;
}
@ -1382,10 +1380,11 @@ export class IngrainTag extends TrappedTag {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 16),
i18next.t("battlerTags:ingrainLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
}),
true,
{
message: i18next.t("battlerTags:ingrainLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
}),
},
);
}
@ -1455,11 +1454,12 @@ export class AquaRingTag extends SerializableBattlerTag {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 16),
i18next.t("battlerTags:aquaRingLapse", {
moveName: this.getMoveName(),
pokemonName: getPokemonNameWithAffix(pokemon),
}),
true,
{
message: i18next.t("battlerTags:aquaRingLapse", {
moveName: this.getMoveName(),
pokemonName: getPokemonNameWithAffix(pokemon),
}),
},
);
}

View File

@ -73,16 +73,12 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
{
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: hpHealed });
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
consumer.getBattlerIndex(),
hpHealed.value,
i18next.t("battle:hpHealBerry", {
globalScene.phaseManager.unshiftNew("PokemonHealPhase", consumer.getBattlerIndex(), hpHealed.value, {
message: i18next.t("battle:hpHealBerry", {
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
berryName: getBerryName(berryType),
}),
true,
);
});
}
break;
case BerryType.LUM:

View File

@ -1983,7 +1983,13 @@ export class HealAttr extends MoveEffectAttr {
*/
protected addHealPhase(target: Pokemon, healRatio: number) {
globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(),
toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim);
toDmgValue(target.getMaxHp() * healRatio),
{
message: i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }),
showFullHpMessage: true,
skipAnim: !this.showAnim,
}
);
}
override getTargetBenefitScore(user: Pokemon, target: Pokemon, _move: Move): number {
@ -2175,16 +2181,19 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr {
const pm = globalScene.phaseManager;
pm.pushPhase(
pm.create("PokemonHealPhase",
pm.create(
"PokemonHealPhase",
user.getBattlerIndex(),
maxPartyMemberHp,
i18next.t(this.moveMessage, { pokemonName: getPokemonNameWithAffix(user) }),
true,
false,
false,
true,
this.restorePP),
true);
{
message: i18next.t(this.moveMessage, { pokemonName: getPokemonNameWithAffix(user) }),
showFullHpMessage: false,
skipAnim: true,
healStatus: true,
fullRestorePP: this.restorePP,
}
),
true);
return true;
}
@ -2280,7 +2289,9 @@ export class HitHealAttr extends MoveEffectAttr {
message = "";
}
}
globalScene.phaseManager.unshiftNew("PokemonHealPhase", user.getBattlerIndex(), healAmount, message, false, true);
globalScene.phaseManager.unshiftNew("PokemonHealPhase", user.getBattlerIndex(), healAmount,
{message, showFullHpMessage: false, skipAnim: true}
);
return true;
}
@ -4313,7 +4324,8 @@ export class PunishmentPowerAttr extends VariablePowerAttr {
}
export class PresentPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
apply(user: Pokemon, target: Pokemon, move: Move, args: [NumberHolder]): boolean {
const power = args[0]
/**
* If this move is multi-hit, and this attribute is applied to any hit
* other than the first, this move cannot result in a heal.
@ -4322,17 +4334,21 @@ export class PresentPowerAttr extends VariablePowerAttr {
const powerSeed = randSeedInt(firstHit ? 100 : 80);
if (powerSeed <= 40) {
(args[0] as NumberHolder).value = 40;
} else if (40 < powerSeed && powerSeed <= 70) {
(args[0] as NumberHolder).value = 80;
} else if (70 < powerSeed && powerSeed <= 80) {
(args[0] as NumberHolder).value = 120;
} else if (80 < powerSeed && powerSeed <= 100) {
// If this move is multi-hit, disable all other hits
power.value = 40;
} else if (powerSeed <= 70) {
power.value = 80;
} else if (powerSeed <= 80) {
power.value = 120;
} else if (powerSeed <= 100) {
// Disable all other hits and heal the target for 25% max HP
user.turnData.hitCount = 1;
user.turnData.hitsLeft = 1;
globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(),
toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true);
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
target.getBattlerIndex(),
toDmgValue(target.getMaxHp() / 4),
{message: i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) })}
)
}
return true;
@ -5853,8 +5869,8 @@ export class ProtectAttr extends AddBattlerTagAttr {
for (const turnMove of user.getLastXMoves(-1).slice()) {
if (
// Quick & Wide guard increment the Protect counter without using it for fail chance
!(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
!(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
turnMove.result !== MoveResult.SUCCESS
) {
break;

View File

@ -155,13 +155,12 @@ export class WishTag extends PositionalTag implements WishArgs {
public override trigger(): void {
// TODO: Rename this locales key - wish shows a message on REMOVAL, not addition
globalScene.phaseManager.queueMessage(
i18next.t("arenaTag:wishTagOnAdd", {
// TODO: What messages does Wish show when healing a Pokemon at full HP?
globalScene.phaseManager.unshiftNew("PokemonHealPhase", this.targetIndex, this.healHp, {
message: i18next.t("arenaTag:wishTagOnAdd", {
pokemonNameWithAffix: this.pokemonName,
}),
);
globalScene.phaseManager.unshiftNew("PokemonHealPhase", this.targetIndex, this.healHp, null, true, false);
});
}
public override shouldTrigger(): boolean {

View File

@ -1674,11 +1674,12 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount,
i18next.t("modifier:turnHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.type.name,
}),
true,
{
message: i18next.t("modifier:turnHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.type.name,
}),
},
);
return true;
}
@ -1766,16 +1767,16 @@ export class HitHealModifier extends PokemonHeldItemModifier {
*/
override apply(pokemon: Pokemon): boolean {
if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) {
// TODO: this shouldn't be undefined AFAIK
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount,
i18next.t("modifier:hitHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.type.name,
}),
true,
toDmgValue((pokemon.turnData.totalDamageDealt * this.stackCount) / 8),
{
message: i18next.t("modifier:hitHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.type.name,
}),
},
);
}
@ -1934,20 +1935,22 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
*/
override apply(pokemon: Pokemon): boolean {
// Restore the Pokemon to half HP
// TODO: This should not use a phase to revive pokemon
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 2),
i18next.t("modifier:pokemonInstantReviveApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.type.name,
}),
false,
false,
true,
{
message: i18next.t("modifier:pokemonInstantReviveApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.type.name,
}),
revive: true,
},
);
// Remove the Pokemon's FAINT status
// TODO: Remove call to `resetStatus` once StatusEffect.FAINT is canned
pokemon.resetStatus(true, false, true, false);
// Reapply Commander on the Pokemon's side of the field, if applicable
@ -3549,24 +3552,24 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier {
* @returns `true` if the {@linkcode Pokemon} was healed
*/
override apply(enemyPokemon: Pokemon): boolean {
if (!enemyPokemon.isFullHp()) {
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
enemyPokemon.getBattlerIndex(),
Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1),
i18next.t("modifier:enemyTurnHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon),
}),
true,
false,
false,
false,
true,
);
return true;
if (enemyPokemon.isFullHp()) {
return false;
}
return false;
// Prevent healing to full from healing tokens
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
enemyPokemon.getBattlerIndex(),
(enemyPokemon.getMaxHp() * this.stackCount * this.healPercent) / 100,
{
message: i18next.t("modifier:enemyTurnHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon),
}),
preventFullHeal: true,
},
);
return true;
}
getMaxStackCount(): number {

View File

@ -1,39 +1,81 @@
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import type { HealBlockTag } from "#data/battler-tags";
import { getStatusEffectHealText } from "#data/status-effect";
import type { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type";
import { HitResult } from "#enums/hit-result";
import { CommonAnim } from "#enums/move-anims-common";
import { StatusEffect } from "#enums/status-effect";
import { HealingBoosterModifier } from "#modifiers/modifier";
import { CommonAnimPhase } from "#phases/common-anim-phase";
import { HealAchv } from "#system/achv";
import { NumberHolder } from "#utils/common";
import { NumberHolder, toDmgValue } from "#utils/common";
import i18next from "i18next";
export class PokemonHealPhase extends CommonAnimPhase {
public readonly phaseName = "PokemonHealPhase";
/** The base amount of HP to heal. */
private hpHealed: number;
private message: string | null;
/**
* The message to display upon healing the target, or `undefined` to show no message.
* Will be overridden by the full HP message if {@linkcode showFullHpMessage} is set to `true`
*/
private message: string | undefined;
/**
* Whether to show a failure message upon healing a Pokemon already at full HP.
* @defaultValue `true`
*/
private showFullHpMessage: boolean;
/**
* Whether to skip showing the healing animation.
* @defaultValue `false`
*/
private skipAnim: boolean;
/**
* Whether to revive the affected Pokemon in addition to healing.
* Revives will not be affected by any Healing Charms.
* @todo Remove post modifier rework as revives will not be using phases to heal stuff
* @defaultValue `false`
*/
private revive: boolean;
/**
* Whether to heal the affected Pokemon's status condition.
* @todo This should not be the healing phase's job
* @defaultValue `false`
*/
private healStatus: boolean;
/**
* Whether to prevent fully healing affected Pokemon, leaving them 1 HP below full.
* @defaultValue `false`
*/
private preventFullHeal: boolean;
/**
* Whether to fully restore PP upon healing.
* @todo This should not be the healing phase's job
* @defaultValue `false`
*/
private fullRestorePP: boolean;
constructor(
battlerIndex: BattlerIndex,
hpHealed: number,
message: string | null,
showFullHpMessage: boolean,
skipAnim = false,
revive = false,
healStatus = false,
preventFullHeal = false,
fullRestorePP = false,
{
message,
showFullHpMessage = true,
skipAnim = false,
revive = false,
healStatus = false,
preventFullHeal = false,
fullRestorePP = false,
}: {
message?: string;
showFullHpMessage?: boolean;
skipAnim?: boolean;
revive?: boolean;
healStatus?: boolean;
preventFullHeal?: boolean;
fullRestorePP?: boolean;
} = {},
) {
super(battlerIndex, undefined, CommonAnim.HEALTH_UP);
@ -47,89 +89,108 @@ export class PokemonHealPhase extends CommonAnimPhase {
this.fullRestorePP = fullRestorePP;
}
start() {
if (!this.skipAnim && (this.revive || this.getPokemon().hp) && !this.getPokemon().isFullHp()) {
override start() {
// Only play animation if not skipped and target is at full HP
if (!this.skipAnim && !this.getPokemon().isFullHp()) {
super.start();
} else {
this.end();
}
this.heal();
super.end();
}
end() {
private heal() {
const pokemon = this.getPokemon();
if (!pokemon.isOnField() || (!this.revive && !pokemon.isActive())) {
return super.end();
// Prevent healing off-field pokemon unless via revives
// TODO: Revival effects shouldn't use this phase
if (!this.revive && !pokemon.isActive(true)) {
super.end();
return;
}
const hasMessage = !!this.message;
const healOrDamage = !pokemon.isFullHp() || this.hpHealed < 0;
const healBlock = pokemon.getTag(BattlerTagType.HEAL_BLOCK) as HealBlockTag;
let lastStatusEffect = StatusEffect.NONE;
// Check for heal block, ending the phase early if healing was prevented
const healBlock = pokemon.getTag(BattlerTagType.HEAL_BLOCK);
if (healBlock && this.hpHealed > 0) {
globalScene.phaseManager.queueMessage(healBlock.onActivation(pokemon));
this.message = null;
return super.end();
super.end();
return;
}
if (healOrDamage) {
const hpRestoreMultiplier = new NumberHolder(1);
if (!this.revive) {
globalScene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
}
const healAmount = new NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
if (healAmount.value < 0) {
pokemon.damageAndUpdate(healAmount.value * -1, { result: HitResult.INDIRECT });
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;
}
healAmount.value = pokemon.heal(healAmount.value);
if (healAmount.value) {
globalScene.damageNumberHandler.add(pokemon, healAmount.value, HitResult.HEAL);
}
if (pokemon.isPlayer()) {
globalScene.validateAchvs(HealAchv, healAmount);
if (healAmount.value > globalScene.gameData.gameStats.highestHeal) {
globalScene.gameData.gameStats.highestHeal = healAmount.value;
}
}
if (this.healStatus && !this.revive && pokemon.status) {
lastStatusEffect = pokemon.status.effect;
pokemon.resetStatus();
}
if (this.fullRestorePP) {
for (const move of this.getPokemon().getMoveset()) {
if (move) {
move.ppUsed = 0;
}
}
}
pokemon.updateInfo().then(() => super.end());
} else if (this.healStatus && !this.revive && pokemon.status) {
lastStatusEffect = pokemon.status.effect;
this.doHealPokemon();
// Cure status as applicable
// TODO: This should not be the job of the healing phase
if (this.healStatus && pokemon.status) {
this.message = getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon));
pokemon.resetStatus();
pokemon.updateInfo().then(() => super.end());
} else if (this.showFullHpMessage) {
this.message = i18next.t("battle:hpIsFull", {
pokemonName: getPokemonNameWithAffix(pokemon),
}
// Restore PP.
// TODO: This should not be the job of the healing phase
if (this.fullRestorePP) {
pokemon.getMoveset().forEach(m => {
m.ppUsed = 0;
});
}
// Show message, update info boxes and then wrap up.
if (this.message) {
globalScene.phaseManager.queueMessage(this.message);
}
pokemon.updateInfo().then(() => super.end());
}
if (this.healStatus && lastStatusEffect && !hasMessage) {
globalScene.phaseManager.queueMessage(
getStatusEffectHealText(lastStatusEffect, getPokemonNameWithAffix(pokemon)),
);
/**
* Heal the Pokemon affected by this Phase.
*/
private doHealPokemon(): void {
const pokemon = this.getPokemon()!;
// If we would heal the user past full HP, don't.
if (this.hpHealed > 0 && pokemon.isFullHp()) {
if (this.showFullHpMessage) {
this.message = i18next.t("battle:hpIsFull", {
pokemonName: getPokemonNameWithAffix(pokemon),
});
}
return;
}
if (!healOrDamage && !lastStatusEffect) {
super.end();
const healAmount = this.getHealAmount();
if (healAmount < 0) {
// If Liquid Ooze is active, damage the user for the healing amount, then return.
// TODO: Consider refactoring liquid ooze to not use a heal phase to do damage
pokemon.damageAndUpdate(-healAmount, { result: HitResult.INDIRECT });
return;
}
// Heal the pokemon, then show damage numbers and validate achievements.
pokemon.heal(healAmount);
globalScene.damageNumberHandler.add(pokemon, healAmount, HitResult.HEAL);
if (pokemon.isPlayer()) {
globalScene.validateAchvs(HealAchv, healAmount);
globalScene.gameData.gameStats.highestHeal = Math.max(globalScene.gameData.gameStats.highestHeal, healAmount);
}
}
/**
* Calculate the amount of HP to be healed during this Phase.
* @returns The updated healing amount, rounded down and capped at the Pokemon's maximum HP.
* @todo Prevent double rounding from callers
*/
private getHealAmount(): number {
if (this.revive) {
return toDmgValue(this.hpHealed);
}
// Apply the effect of healing charms for non-revival items before rounding down and capping at max HP
// (or 1 below max for healing tokens).
// Liquid Ooze damage (being negative) remains uncapped as normal.
const healMulti = new NumberHolder(1);
globalScene.applyModifiers(HealingBoosterModifier, this.player, healMulti);
return toDmgValue(Math.min(this.hpHealed * healMulti.value, this.getPokemon().getMaxHp() - +this.preventFullHeal));
}
}

View File

@ -154,16 +154,11 @@ export class QuietFormChangePhase extends BattlePhase {
this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED);
if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon.isEnemy()) {
globalScene.playBgm();
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
this.pokemon.getBattlerIndex(),
this.pokemon.getMaxHp(),
null,
false,
false,
false,
true,
);
globalScene.phaseManager.unshiftNew("PokemonHealPhase", this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), {
showFullHpMessage: false,
healStatus: true,
fullRestorePP: true,
});
this.pokemon.findAndRemoveTags(() => true);
this.pokemon.bossSegments = 5;
this.pokemon.bossSegmentIndex = 4;

View File

@ -35,15 +35,11 @@ export class TurnEndPhase extends FieldPhase {
globalScene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon);
if (globalScene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
pokemon.getBattlerIndex(),
Math.max(pokemon.getMaxHp() >> 4, 1),
i18next.t("battle:turnEndHpRestore", {
globalScene.phaseManager.unshiftNew("PokemonHealPhase", pokemon.getBattlerIndex(), pokemon.getMaxHp() / 16, {
message: i18next.t("battle:turnEndHpRestore", {
pokemonName: getPokemonNameWithAffix(pokemon),
}),
true,
);
});
}
if (!pokemon.isPlayer()) {