From 9f699537bc3c6b5e8336c1cdd33a5de2caad4d61 Mon Sep 17 00:00:00 2001 From: lucfd <83493765+lucfd@users.noreply.github.com> Date: Sun, 19 May 2024 18:47:30 -0400 Subject: [PATCH] Implemented Bug Bite, Pluck, Teatime (#232) * implemented pluck, bug bite * steal blocked by sticky hold * implemented teatime * added stuff cheeks * added berry pouch support * fixed StealEatBerryAttr sometimes eating 2 stacks of a berry * added comments & documentation * added more comments * added comment on stuff cheeks condition * fixed 0 stack berry modifier not disappearing * stuff cheeks bug fix * fix leppa berry logic * removed stuff cheeks --- src/data/berry.ts | 8 ++-- src/data/move.ts | 102 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 9 deletions(-) diff --git a/src/data/berry.ts b/src/data/berry.ts index 1521f3488ef..e13d4532b3e 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -130,9 +130,11 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { return (pokemon: Pokemon) => { if (pokemon.battleData) pokemon.battleData.berriesEaten.push(berryType); - const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio()); - ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` restored PP to its move ${ppRestoreMove.getName()}\nusing its ${getBerryName(berryType)}!`)); + const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio()) ? pokemon.getMoveset().find(m => !m.getPpRatio()) : pokemon.getMoveset().find(m => m.getPpRatio() < 1); + if(ppRestoreMove !== undefined){ + ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` restored PP to its move ${ppRestoreMove.getName()}\nusing its ${getBerryName(berryType)}!`)); + } }; } } \ No newline at end of file diff --git a/src/data/move.ts b/src/data/move.ts index d71edda691d..9043935c176 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -12,10 +12,10 @@ import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; -import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr } from "./ability"; +import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, PreventBerryUseAbAttr, BlockItemTheftAbAttr } from "./ability"; import { Abilities } from "./enums/abilities"; import { allAbilities } from './ability'; -import { PokemonHeldItemModifier } from "../modifier/modifier"; +import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier } from "../modifier/modifier"; import { BattlerIndex } from "../battle"; import { Stat } from "./pokemon-stat"; import { TerrainType } from "./terrain"; @@ -25,6 +25,7 @@ import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../ui/command-ui-handler"; import { Biome } from "./enums/biome"; import i18next, { Localizable } from '../plugins/i18n'; +import { BerryType, BerryEffectFunc, getBerryEffectFunc } from './berry'; export enum MoveCategory { PHYSICAL, @@ -1443,6 +1444,95 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { } } +/** + * Attribute that causes targets of the move to eat a berry. If chosenBerry is not overriden, a random berry will be picked from the target's inventory. + */ +export class EatBerryAttr extends MoveEffectAttr { + protected chosenBerry: BerryModifier; + constructor() { + super(true, MoveEffectTrigger.HIT); + this.chosenBerry = undefined; + } +/** + * Causes the target to eat a berry. + * @param user {@linkcode Pokemon} Pokemon that used the move + * @param target {@linkcode Pokemon} Pokemon that will eat a berry + * @param move {@linkcode Move} The move being used + * @param args Unused + * @returns {boolean} true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) + return false; + + if(this.chosenBerry === undefined) { // if no berry has been provided, pick a random berry from their inventory + const heldBerries = this.getTargetHeldBerries(target); + if(heldBerries.length <= 0) + return false; + this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; + } + + getBerryEffectFunc(this.chosenBerry.berryType)(target); // target eats the berry + + const preserve = new Utils.BooleanHolder(false); + target.scene.applyModifiers(PreserveBerryModifier, target.isPlayer(), target, preserve); + + if (!preserve.value){ // remove the eaten berry if not preserved + if (!--this.chosenBerry.stackCount) + target.scene.removeModifier(this.chosenBerry, !target.isPlayer()); + target.scene.updateModifiers(target.isPlayer()); +} + this.chosenBerry = undefined; + + return true; + } + + getTargetHeldBerries(target: Pokemon): BerryModifier[] { + return target.scene.findModifiers(m => m instanceof BerryModifier + && (m as BerryModifier).pokemonId === target.id, target.isPlayer()) as BerryModifier[]; + } + +} +/** + * Attribute used for moves that steal a random berry from the target. The user then eats the stolen berry. + * Used for Pluck & Bug Bite. + */ +export class StealEatBerryAttr extends EatBerryAttr { + constructor() { + super(); + } +/** + * User steals a random berry from the target and then eats it. + * @param {Pokemon} user Pokemon that used the move and will eat the stolen berry + * @param {Pokemon} target Pokemon that will have its berry stolen + * @param {Move} move Move being used + * @param {any[]} args Unused + * @returns {boolean} true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockItemTheftAbAttr, target, cancelled); // check for abilities that block item theft + if(cancelled.value == true) + return false; + + const heldBerries = this.getTargetHeldBerries(target).filter(i => i.getTransferrable(false)); + + if (heldBerries.length) { // if the target has berries, pick a random berry and steal it + this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; + + if (this.chosenBerry.stackCount == 1) // remove modifier if its the last berry + target.scene.removeModifier(this.chosenBerry, !target.isPlayer()); + target.scene.updateModifiers(target.isPlayer()); + + user.scene.queueMessage(getPokemonMessage(user, ` stole and ate\n${target.name}'s ${this.chosenBerry.type.name}!`)); + return super.apply(user, user, move, args); + } + + return false; + } +} + export class HealStatusEffectAttr extends MoveEffectAttr { private effects: StatusEffect[]; @@ -5556,7 +5646,7 @@ export function initMoves() { .makesContact(false) .ignoresProtect(), new AttackMove(Moves.PLUCK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) - .partial(), + .attr(StealEatBerryAttr), new StatusMove(Moves.TAILWIND, Type.FLYING, -1, 15, -1, 0, 4) .windMove() .attr(AddArenaTagAttr, ArenaTagType.TAILWIND, 4, true) @@ -5793,7 +5883,7 @@ export function initMoves() { new AttackMove(Moves.JUDGMENT, Type.NORMAL, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 4) .partial(), new AttackMove(Moves.BUG_BITE, Type.BUG, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) - .partial(), + .attr(StealEatBerryAttr), new AttackMove(Moves.CHARGE_BEAM, Type.ELECTRIC, MoveCategory.SPECIAL, 50, 90, 10, 70, 0, 4) .attr(StatChangeAttr, BattleStat.SPATK, 1, true), new AttackMove(Moves.WOOD_HAMMER, Type.GRASS, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 4) @@ -6655,8 +6745,8 @@ export function initMoves() { .makesContact(false) .partial(), new StatusMove(Moves.TEATIME, Type.NORMAL, -1, 10, -1, 0, 8) - .target(MoveTarget.ALL) - .unimplemented(), + .attr(EatBerryAttr) + .target(MoveTarget.ALL), new StatusMove(Moves.OCTOLOCK, Type.FIGHTING, 100, 15, -1, 0, 8) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1) .partial(),