diff --git a/src/data/ability.ts b/src/data/ability.ts index 90335a85d..66899aba6 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -913,6 +913,34 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { } } +export class PreHitAbAttr extends AbAttr { + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return false; + } +} + +export class ExtraHitMoveAbAttr extends PreHitAbAttr { + private extraHits: integer; + private powerMultiplier: number; + + constructor(extraHits: integer, powerMultiplier: number){ + super(true); + this.extraHits = extraHits; + this.powerMultiplier = powerMultiplier; + } + + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const hits = (args[0] as number[]); + if (this.extraHits > 0) { + for (let i = 0; i < this.extraHits; i++) + hits.push(this.powerMultiplier); + return true; + } + + return false; + } +} + export class PreAttackAbAttr extends AbAttr { applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean | Promise { return false; @@ -3285,7 +3313,7 @@ export function initAbilities() { new Ability(Abilities.AERILATE, 6) .attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FLYING, 1.2), new Ability(Abilities.PARENTAL_BOND, 6) - .unimplemented(), + .attr(ExtraHitMoveAbAttr, 1, 0.25), new Ability(Abilities.DARK_AURA, 6) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' is radiating a Dark Aura!')) .attr(FieldMoveTypePowerBoostAbAttr, Type.DARK, 4 / 3), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index a73f14586..9dcbb27ff 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1415,7 +1415,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.getFieldIndex() ? 0 : 1]; } - apply(source: Pokemon, battlerMove: PokemonMove): HitResult { + apply(source: Pokemon, battlerMove: PokemonMove, extraHitModifier: number = 1): HitResult { let result: HitResult; const move = battlerMove.getMove(); let damage = new Utils.NumberHolder(0); @@ -1541,7 +1541,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableDefAttr, source, this, move, targetDef); if (!isTypeImmune) { - damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value); + damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * extraHitModifier * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value); if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled); @@ -3310,6 +3310,7 @@ export class PokemonTurnData { public damageDealt: integer = 0; public damageTaken: integer = 0; public attacksReceived: AttackMoveResult[] = []; + public multipliersExtraHits: number[] = []; } export enum AiType { diff --git a/src/phases.ts b/src/phases.ts index d8ce55e95..40a7141ae 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get import { TempBattleStat } from "./data/temp-battle-stat"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagType } from "./data/enums/arena-tag-type"; -import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr } from "./data/ability"; +import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, ExtraHitMoveAbAttr } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; @@ -2399,12 +2399,14 @@ export class MovePhase extends BattlePhase { export class MoveEffectPhase extends PokemonPhase { public move: PokemonMove; protected targets: BattlerIndex[]; + public multiHitId: integer; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove, multiHitId: integer = 0) { super(scene, battlerIndex); this.move = move; this.targets = targets; + this.multiHitId = multiHitId; } start() { @@ -2432,6 +2434,15 @@ export class MoveEffectPhase extends PokemonPhase { applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount); if (this.move.getMove() instanceof AttackMove && !this.move.getMove().getAttrs(FixedDamageAttr).length) this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); + + // Check for abilities that add extra hits with damage multipliers for them + if (this.move.getMove() instanceof AttackMove) { + const multipliersHits: number[] = []; + applyAbAttrs(ExtraHitMoveAbAttr, user, null, multipliersHits); + user.turnData.multipliersExtraHits = multipliersHits; + hitCount.value *= (multipliersHits.length + 1); + } + user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value; } @@ -2475,7 +2486,9 @@ export class MoveEffectPhase extends PokemonPhase { moveHistoryEntry.result = MoveResult.SUCCESS; - const hitResult = !isProtected ? target.apply(user, this.move) : HitResult.NO_EFFECT; + const multiHitMultiplier = this.multiHitId ? user.turnData.multipliersExtraHits[this.multiHitId - 1] ?? 1 : 1; + + const hitResult = !isProtected ? target.apply(user, this.move, multiHitMultiplier) : HitResult.NO_EFFECT; this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); @@ -2622,7 +2635,11 @@ export class MoveEffectPhase extends PokemonPhase { } getNewHitPhase() { - return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move); + const hitId = new Utils.IntegerHolder(0); + const user = this.getUserPokemon(); + if (user) + hitId.value = (this.multiHitId + 1) % (user.turnData.multipliersExtraHits.length + 1); + return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move, hitId.value); } }