diff --git a/src/data/ability.ts b/src/data/ability.ts index 90335a85d..dba3aa5dd 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2562,6 +2562,26 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { } } +export class FullHpTypeMultiplierAbAttr extends AbAttr { + public multiplier: number; + + constructor(multiplier: number) { + super(true); + + this.multiplier = multiplier; + } + + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const typeMultiplier = args[0] as Utils.NumberHolder; + if (typeMultiplier.value > 0){ + typeMultiplier.value = this.multiplier; + return true; + } + + return false; + } +} + function applyAbAttrsInternal(attrType: { new(...args: any[]): TAttr }, pokemon: Pokemon, applyFunc: AbAttrApplyFunc, args: any[], isAsync: boolean = false, showAbilityInstant: boolean = false, quiet: boolean = false, passive: boolean = false): Promise { return new Promise(resolve => { @@ -3707,10 +3727,10 @@ export function initAbilities() { .attr(NoTransformAbilityAbAttr) .attr(NoFusionAbilityAbAttr), new Ability(Abilities.TERA_SHELL, 9) + .attr(FullHpTypeMultiplierAbAttr, 0.5) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) - .ignorable() - .unimplemented(), + .ignorable(), new Ability(Abilities.TERAFORM_ZERO, 9) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index a73f14586..63e9a10b7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -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, FullHpTypeMultiplierAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -1442,6 +1442,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); if (typeless) typeMultiplier.value = 1; + if (this.turnData.startedFullHp) + applyAbAttrs(FullHpTypeMultiplierAbAttr, this, null, typeMultiplier); if (types.find(t => move.isTypeImmune(t))) typeMultiplier.value = 0; @@ -3310,6 +3312,7 @@ export class PokemonTurnData { public damageDealt: integer = 0; public damageTaken: integer = 0; public attacksReceived: AttackMoveResult[] = []; + public startedFullHp: boolean; } export enum AiType { diff --git a/src/phases.ts b/src/phases.ts index d8ce55e95..603528ad8 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2433,6 +2433,9 @@ export class MoveEffectPhase extends PokemonPhase { if (this.move.getMove() instanceof AttackMove && !this.move.getMove().getAttrs(FixedDamageAttr).length) this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value; + + // Store if the target was full HP at the start of the hits + targets.map((t) => { t.turnData.startedFullHp = t.getHpRatio() === 1}); } const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; @@ -2537,6 +2540,9 @@ export class MoveEffectPhase extends PokemonPhase { if (hitsTotal > 1) this.scene.queueMessage(i18next.t('battle:attackHitsCount', { count: hitsTotal })); this.scene.applyModifiers(HitHealModifier, this.player, user); + + // Update full HP check only after all hits + this.getTargets().map((t) => { t.turnData.startedFullHp = t.getHpRatio() === 1}); } }