Fixing (most) berry usage

This commit is contained in:
Wlowscha 2025-06-10 00:53:44 +02:00
parent 29af976c49
commit 215ce632ef
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
8 changed files with 64 additions and 67 deletions

View File

@ -57,7 +57,6 @@ import {
getModifierPoolForType,
getPartyLuckValue,
ModifierPoolType,
PokemonHeldItemModifierType,
} from "#app/modifier/modifier-type";
import AbilityBar from "#app/ui/ability-bar";
import {

View File

@ -88,8 +88,9 @@ import type { BattlerIndex } from "#app/battle";
import type Move from "#app/data/moves/move";
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves";
import { HeldItemId } from "#enums/held-item-id";
import { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id";
import { allHeldItems } from "#app/items/all-held-items";
import { berryTypeToHeldItem } from "#app/items/held-items/berry";
export class BlockRecoilDamageAttr extends AbAttr {
constructor() {
@ -5432,10 +5433,14 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean {
// Ensure we have at least 1 recoverable berry (at least 1 berry in berriesEaten is not capped)
const cappedBerries = new Set(
globalScene
.getModifiers(BerryModifier, pokemon.isPlayer())
.filter(bm => bm.pokemonId === pokemon.id && bm.getCountUnderMax() < 1)
.map(bm => bm.berryType),
pokemon
.getHeldItems()
.filter(
bm =>
isItemInCategory(bm, HeldItemCategoryId.BERRY) &&
pokemon.heldItemManager.getStack(bm) < allHeldItems[bm].maxStackCount,
)
.map(bm => allHeldItems[bm].berryType),
);
this.berriesUnderCap = pokemon.battleData.berriesEaten.filter(bt => !cappedBerries.has(bt));
@ -5465,30 +5470,15 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
const randomIdx = randSeedInt(this.berriesUnderCap.length);
const chosenBerryType = this.berriesUnderCap[randomIdx];
pokemon.battleData.berriesEaten.splice(randomIdx, 1); // Remove berry from memory
const chosenBerry = new BerryModifierType(chosenBerryType);
const chosenBerry = berryTypeToHeldItem[chosenBerryType];
// Add the randomly chosen berry or update the existing one
const berryModifier = globalScene.findModifier(
m => m instanceof BerryModifier && m.berryType === chosenBerryType && m.pokemonId === pokemon.id,
pokemon.isPlayer(),
) as BerryModifier | undefined;
if (berryModifier) {
berryModifier.stackCount++;
} else {
const newBerry = new BerryModifier(chosenBerry, pokemon.id, chosenBerryType, 1);
if (pokemon.isPlayer()) {
globalScene.addModifier(newBerry);
} else {
globalScene.addEnemyModifier(newBerry);
}
}
pokemon.heldItemManager.add(chosenBerry);
globalScene.updateModifiers(pokemon.isPlayer());
globalScene.phaseManager.queueMessage(
i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
berryName: chosenBerry.name,
berryName: allHeldItems[chosenBerry].name,
}),
);
return true;
@ -5537,8 +5527,7 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr {
// This doesn't count as "eating" a berry (for unnerve/stuff cheeks/unburden) as no item is consumed.
for (const berryType of pokemon.summonData.berriesEatenLast) {
getBerryEffectFunc(berryType)(pokemon);
const bMod = new BerryModifier(new BerryModifierType(berryType), pokemon.id, berryType, 1);
globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(bMod)); // trigger message
globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(pokemon, berryType)); // trigger message
}
// uncomment to make cheek pouch work with cud chew

View File

@ -87,7 +87,7 @@ export const HeldItemId = {
MACHO_BRACE: 0x0909,
// Evo trackers
GIMMIGHOUL_EVO_TRACKER: 0x0a01,
GIMMIGHOUL_EVO_TRACKER: 0x0A01,
};
export type HeldItemId = (typeof HeldItemId)[keyof typeof HeldItemId];
@ -102,4 +102,28 @@ export const HeldItemNames: Record<HeldItemValue, HeldItemName> = Object.entries
return acc;
},
{} as Record<HeldItemValue, HeldItemName>
);
);
export const HeldItemCategoryId = {
NONE: 0x0000,
BERRY: 0x0100,
CONSUMABLE: 0x0200,
TYPE_ATTACK_BOOSTER: 0x0300,
STAT_BOOSTER: 0x0400,
CRIT_BOOSTER: 0x0500,
GAIN_INCREASE: 0x0600,
UNIQUE: 0x0700,
BASE_STAT_BOOST: 0x0900,
EVO_TRACKER: 0x0A00,
};
export type HeldItemCategoryId = (typeof HeldItemCategoryId)[keyof typeof HeldItemCategoryId];
function getHeldItemCategory(itemId: HeldItemId): HeldItemCategoryId {
return itemId & 0xFF00;
}
export function isItemInCategory(itemId: HeldItemId, category: HeldItemCategoryId): boolean {
return getHeldItemCategory(itemId) === category;
}

View File

@ -1,5 +1,6 @@
import type Pokemon from "#app/field/pokemon";
import type { BerryType } from "#enums/berry-type";
import type Move from "../data/moves/move";
import type { BerryModifier } from "../modifier/modifier";
/** Alias for all {@linkcode BattleScene} events */
export enum BattleSceneEventType {
@ -81,12 +82,13 @@ export class MoveUsedEvent extends Event {
* @extends Event
*/
export class BerryUsedEvent extends Event {
/** The {@linkcode BerryModifier} being used */
public berryModifier: BerryModifier;
constructor(berry: BerryModifier) {
/** The {@linkcode BerryType} being used */
public pokemon: Pokemon;
public berryType: BerryType;
constructor(pokemon: Pokemon, berryType: BerryType) {
super(BattleSceneEventType.BERRY_USED);
this.berryModifier = berry;
this.pokemon = pokemon;
this.berryType = berryType;
}
}

View File

@ -153,11 +153,7 @@ export function initHeldItems() {
.unstealable()
.untransferable()
.unsuppressable();
allHeldItems[HeldItemId.OLD_GATEAU] = new BaseStatFlatHeldItem(HeldItemId.OLD_GATEAU, 1, [
Stat.HP,
Stat.ATK,
Stat.DEF,
])
allHeldItems[HeldItemId.OLD_GATEAU] = new BaseStatFlatHeldItem(HeldItemId.OLD_GATEAU, 1)
.unstealable()
.untransferable()
.unsuppressable();

View File

@ -1,4 +1,5 @@
import { getBerryEffectDescription, getBerryEffectFunc, getBerryName } from "#app/data/berry";
import { BerryUsedEvent } from "#app/events/battle-scene";
import type Pokemon from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import { ConsumableHeldItem, ITEM_EFFECT } from "#app/items/held-item";
@ -26,10 +27,8 @@ export const berryTypeToHeldItem: BerryTypeToHeldItemMap = {
};
export interface BERRY_PARAMS {
/** The pokemon with the item */
/** The pokemon with the berry */
pokemon: Pokemon;
/** Whether the move was used by a player pokemon */
isPlayer: boolean;
}
// TODO: Maybe split up into subclasses?
@ -72,7 +71,6 @@ export class BerryHeldItem extends ConsumableHeldItem {
*/
apply(params: BERRY_PARAMS): boolean {
const pokemon = params.pokemon;
const isPlayer = params.isPlayer;
const preserve = new BooleanHolder(false);
globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve);
@ -80,20 +78,15 @@ export class BerryHeldItem extends ConsumableHeldItem {
// munch the berry and trigger unburden-like effects
getBerryEffectFunc(this.berryType)(pokemon);
this.consume(pokemon, isPlayer, consumed);
this.consume(pokemon, pokemon.isPlayer(), consumed);
// TODO: Update this method to work with held items
// Update berry eaten trackers for Belch, Harvest, Cud Chew, etc.
// Don't recover it if we proc berry pouch (no item duplication)
pokemon.recordEatenBerry(this.berryType, consumed);
globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(pokemon, this.berryType));
return true;
}
getMaxHeldItemCount(_pokemon: Pokemon): number {
if ([BerryType.LUM, BerryType.LEPPA, BerryType.SITRUS, BerryType.ENIGMA].includes(this.berryType)) {
return 2;
}
return 3;
}
}

View File

@ -5,14 +5,15 @@ import {
RepeatBerryNextTurnAbAttr,
} from "#app/data/abilities/ability";
import { CommonAnim } from "#app/data/battle-anims";
import { BerryUsedEvent } from "#app/events/battle-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { BerryModifier } from "#app/modifier/modifier";
import i18next from "i18next";
import { BooleanHolder } from "#app/utils/common";
import { FieldPhase } from "./field-phase";
import { globalScene } from "#app/global-scene";
import type Pokemon from "#app/field/pokemon";
import { allHeldItems, applyHeldItems } from "#app/items/all-held-items";
import { ITEM_EFFECT } from "#app/items/held-item";
import { HeldItemCategoryId, isItemInCategory } from "#enums/held-item-id";
/**
* The phase after attacks where the pokemon eat berries.
@ -36,10 +37,10 @@ export class BerryPhase extends FieldPhase {
* @param pokemon - The {@linkcode Pokemon} to check
*/
eatBerries(pokemon: Pokemon): void {
const hasUsableBerry = !!globalScene.findModifier(
m => m instanceof BerryModifier && m.shouldApply(pokemon),
pokemon.isPlayer(),
);
const hasUsableBerry = pokemon.getHeldItems().some(m => {
//TODO: This is bugged, must fix the .shouldApply() function
isItemInCategory(m, HeldItemCategoryId.BERRY) && allHeldItems[m].shouldApply(pokemon);
});
if (!hasUsableBerry) {
return;
@ -64,14 +65,7 @@ export class BerryPhase extends FieldPhase {
CommonAnim.USE_ITEM,
);
for (const berryModifier of globalScene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) {
// No need to track berries being eaten; already done inside applyModifiers
if (berryModifier.consumed) {
berryModifier.consumed = false;
pokemon.loseHeldItem(berryModifier);
}
globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(berryModifier));
}
applyHeldItems(ITEM_EFFECT.BERRY, { pokemon: pokemon });
globalScene.updateModifiers(pokemon.isPlayer());
// AbilityId.CHEEK_POUCH only works once per round of nom noms

View File

@ -177,8 +177,8 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
const berryUsedEvent = event as BerryUsedEvent;
if (
!berryUsedEvent ||
berryUsedEvent.berryModifier.pokemonId !== this.pokemon?.id ||
berryUsedEvent.berryModifier.berryType !== BerryType.LEPPA
berryUsedEvent.pokemon.id !== this.pokemon?.id ||
berryUsedEvent.berryType !== BerryType.LEPPA
) {
// We only care about Leppa berries
return;