mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-06 15:39:27 +02:00
Cleaned up pokemonHealPhase
+ wrapped inside an object
This commit is contained in:
parent
e760ed9949
commit
90c9c71cd9
@ -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,
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
Loading…
Reference in New Issue
Block a user