From 1d7d8b1ac7d0b6e13de0f3036fc64dbbce77168f Mon Sep 17 00:00:00 2001 From: alpaca Date: Thu, 9 May 2024 15:15:56 -0400 Subject: [PATCH] Implement Heal Bell + Aromatherapy (#238) * adds frame for StatusCureAttr * heal bell and aromatherapy functioning as intended * refactor attr to solely handle party cure, party interaction unique enough to justify * adds header to PartyStatusCureAttr * code comment * comment formatting improvement * checks for passives as well * code comment * shows ability bar if immune --- src/data/move.ts | 42 ++++++++++++++++++++++++++++++++++++------ src/phases.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 99c4457a5db..b85a3cea29f 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,6 +1,6 @@ import { Moves } from "./enums/moves"; import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { BattleEndPhase, MovePhase, NewBattlePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../phases"; +import { BattleEndPhase, MovePhase, NewBattlePhase, PartyEffectPhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../phases"; import { BattleStat, getBattleStatName } from "./battle-stat"; import { EncoreTag } from "./battler-tags"; import { BattlerTagType } from "./enums/battler-tag-type"; @@ -50,7 +50,8 @@ export enum MoveTarget { ALL, USER_SIDE, ENEMY_SIDE, - BOTH_SIDES + BOTH_SIDES, + PARTY } export enum MoveFlags { @@ -832,6 +833,34 @@ export class HealAttr extends MoveEffectAttr { } } +/** + * Cures the user's party of non-volatile status conditions, ie. Heal Bell, Aromatherapy + * @param {string} message Message to display after using move + * @param {Abilities} abilityCondition Skips mons with this ability, ie. Soundproof + */ +export class PartyStatusCureAttr extends MoveEffectAttr { + private message: string; + private abilityCondition: Abilities; + + constructor(message: string, abilityCondition: Abilities) { + super(); + + this.message = message; + this.abilityCondition = abilityCondition; + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) + return false; + + this.addPartyCurePhase(user); + } + + addPartyCurePhase(user: Pokemon) { + user.scene.unshiftPhase(new PartyStatusCurePhase(user.scene, user, this.message, this.abilityCondition)); + } +} + export class SacrificialFullRestoreAttr extends SacrificialAttr { constructor() { super(); @@ -4003,6 +4032,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet { switch (moveTarget) { case MoveTarget.USER: + case MoveTarget.PARTY: set = [ user]; break; case MoveTarget.NEAR_OTHER: @@ -4628,9 +4658,9 @@ export function initMoves() { .condition((user, target, move) => user.status?.effect === StatusEffect.SLEEP) .ignoresVirtual(), new StatusMove(Moves.HEAL_BELL, Type.NORMAL, -1, 5, -1, 0, 2) + .attr(PartyStatusCureAttr, "A bell chimed!", Abilities.SOUNDPROOF) .soundBased() - .target(MoveTarget.USER_AND_ALLIES) - .unimplemented(), + .target(MoveTarget.PARTY), new AttackMove(Moves.RETURN, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 2) .attr(FriendshipPowerAttr), new AttackMove(Moves.PRESENT, Type.NORMAL, MoveCategory.PHYSICAL, -1, 90, 15, -1, 0, 2) @@ -4901,8 +4931,8 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) .ballBombMove(), new StatusMove(Moves.AROMATHERAPY, Type.GRASS, -1, 5, -1, 0, 3) - .target(MoveTarget.USER_AND_ALLIES) - .unimplemented(), + .attr(PartyStatusCureAttr, "A soothing aroma wafted through the area!", Abilities.SAP_SIPPER) + .target(MoveTarget.PARTY), new StatusMove(Moves.FAKE_TEARS, Type.DARK, 100, 20, -1, 0, 3) .attr(StatChangeAttr, BattleStat.SPDEF, -2), new AttackMove(Moves.AIR_CUTTER, Type.FLYING, MoveCategory.SPECIAL, 60, 95, 25, -1, 0, 3) diff --git a/src/phases.ts b/src/phases.ts index 25b0c3b758f..4be1d7705f3 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -4544,6 +4544,48 @@ export class AddEnemyBuffModifierPhase extends Phase { } } +/** + * Cures the party of all non-volatile status conditions, shows a message + * @param {BattleScene} scene The current scene + * @param {Pokemon} user The user of the move that cures the party + * @param {string} message The message that should be displayed + * @param {Abilities} abilityCondition Pokemon with this ability will not be affected ie. Soundproof + */ +export class PartyStatusCurePhase extends BattlePhase { + private user: Pokemon; + private message: string; + private abilityCondition: Abilities; + + constructor(scene: BattleScene, user: Pokemon, message: string, abilityCondition: Abilities) { + super(scene); + + this.user = user; + this.message = message; + this.abilityCondition = abilityCondition; + } + + start() { + super.start(); + for (let pokemon of this.scene.getParty()) { + if (!pokemon.isOnField() || pokemon === this.user) { + pokemon.resetStatus(false); + pokemon.updateInfo(true); + } else { + if (!pokemon.hasAbility(this.abilityCondition)) { + pokemon.resetStatus(); + pokemon.updateInfo(true); + } else { + // Manually show ability bar, since we're not hooked into the targeting system + pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition)); + } + } + } + if (this.message) + this.scene.queueMessage(this.message); + this.end(); + } +} + export class PartyHealPhase extends BattlePhase { private resumeBgm: boolean;