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, getModifierPoolForType,
getPartyLuckValue, getPartyLuckValue,
ModifierPoolType, ModifierPoolType,
PokemonHeldItemModifierType,
} from "#app/modifier/modifier-type"; } from "#app/modifier/modifier-type";
import AbilityBar from "#app/ui/ability-bar"; import AbilityBar from "#app/ui/ability-bar";
import { import {

View File

@ -88,8 +88,9 @@ import type { BattlerIndex } from "#app/battle";
import type Move from "#app/data/moves/move"; import type Move from "#app/data/moves/move";
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag"; import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves"; 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 { allHeldItems } from "#app/items/all-held-items";
import { berryTypeToHeldItem } from "#app/items/held-items/berry";
export class BlockRecoilDamageAttr extends AbAttr { export class BlockRecoilDamageAttr extends AbAttr {
constructor() { constructor() {
@ -5432,10 +5433,14 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr {
override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { 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) // Ensure we have at least 1 recoverable berry (at least 1 berry in berriesEaten is not capped)
const cappedBerries = new Set( const cappedBerries = new Set(
globalScene pokemon
.getModifiers(BerryModifier, pokemon.isPlayer()) .getHeldItems()
.filter(bm => bm.pokemonId === pokemon.id && bm.getCountUnderMax() < 1) .filter(
.map(bm => bm.berryType), 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)); 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 randomIdx = randSeedInt(this.berriesUnderCap.length);
const chosenBerryType = this.berriesUnderCap[randomIdx]; const chosenBerryType = this.berriesUnderCap[randomIdx];
pokemon.battleData.berriesEaten.splice(randomIdx, 1); // Remove berry from memory 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 pokemon.heldItemManager.add(chosenBerry);
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);
}
}
globalScene.updateModifiers(pokemon.isPlayer()); globalScene.updateModifiers(pokemon.isPlayer());
globalScene.phaseManager.queueMessage( globalScene.phaseManager.queueMessage(
i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", { i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
berryName: chosenBerry.name, berryName: allHeldItems[chosenBerry].name,
}), }),
); );
return true; 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. // 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) { for (const berryType of pokemon.summonData.berriesEatenLast) {
getBerryEffectFunc(berryType)(pokemon); getBerryEffectFunc(berryType)(pokemon);
const bMod = new BerryModifier(new BerryModifierType(berryType), pokemon.id, berryType, 1); globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(pokemon, berryType)); // trigger message
globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(bMod)); // trigger message
} }
// uncomment to make cheek pouch work with cud chew // uncomment to make cheek pouch work with cud chew

View File

@ -87,7 +87,7 @@ export const HeldItemId = {
MACHO_BRACE: 0x0909, MACHO_BRACE: 0x0909,
// Evo trackers // Evo trackers
GIMMIGHOUL_EVO_TRACKER: 0x0a01, GIMMIGHOUL_EVO_TRACKER: 0x0A01,
}; };
export type HeldItemId = (typeof HeldItemId)[keyof typeof HeldItemId]; export type HeldItemId = (typeof HeldItemId)[keyof typeof HeldItemId];
@ -102,4 +102,28 @@ export const HeldItemNames: Record<HeldItemValue, HeldItemName> = Object.entries
return acc; return acc;
}, },
{} as Record<HeldItemValue, HeldItemName> {} 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 Move from "../data/moves/move";
import type { BerryModifier } from "../modifier/modifier";
/** Alias for all {@linkcode BattleScene} events */ /** Alias for all {@linkcode BattleScene} events */
export enum BattleSceneEventType { export enum BattleSceneEventType {
@ -81,12 +82,13 @@ export class MoveUsedEvent extends Event {
* @extends Event * @extends Event
*/ */
export class BerryUsedEvent extends Event { export class BerryUsedEvent extends Event {
/** The {@linkcode BerryModifier} being used */ /** The {@linkcode BerryType} being used */
public berryModifier: BerryModifier; public pokemon: Pokemon;
constructor(berry: BerryModifier) { public berryType: BerryType;
constructor(pokemon: Pokemon, berryType: BerryType) {
super(BattleSceneEventType.BERRY_USED); super(BattleSceneEventType.BERRY_USED);
this.pokemon = pokemon;
this.berryModifier = berry; this.berryType = berryType;
} }
} }

View File

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

View File

@ -1,4 +1,5 @@
import { getBerryEffectDescription, getBerryEffectFunc, getBerryName } from "#app/data/berry"; import { getBerryEffectDescription, getBerryEffectFunc, getBerryName } from "#app/data/berry";
import { BerryUsedEvent } from "#app/events/battle-scene";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { ConsumableHeldItem, ITEM_EFFECT } from "#app/items/held-item"; import { ConsumableHeldItem, ITEM_EFFECT } from "#app/items/held-item";
@ -26,10 +27,8 @@ export const berryTypeToHeldItem: BerryTypeToHeldItemMap = {
}; };
export interface BERRY_PARAMS { export interface BERRY_PARAMS {
/** The pokemon with the item */ /** The pokemon with the berry */
pokemon: Pokemon; pokemon: Pokemon;
/** Whether the move was used by a player pokemon */
isPlayer: boolean;
} }
// TODO: Maybe split up into subclasses? // TODO: Maybe split up into subclasses?
@ -72,7 +71,6 @@ export class BerryHeldItem extends ConsumableHeldItem {
*/ */
apply(params: BERRY_PARAMS): boolean { apply(params: BERRY_PARAMS): boolean {
const pokemon = params.pokemon; const pokemon = params.pokemon;
const isPlayer = params.isPlayer;
const preserve = new BooleanHolder(false); const preserve = new BooleanHolder(false);
globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve); globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve);
@ -80,20 +78,15 @@ export class BerryHeldItem extends ConsumableHeldItem {
// munch the berry and trigger unburden-like effects // munch the berry and trigger unburden-like effects
getBerryEffectFunc(this.berryType)(pokemon); getBerryEffectFunc(this.berryType)(pokemon);
this.consume(pokemon, isPlayer, consumed); this.consume(pokemon, pokemon.isPlayer(), consumed);
// TODO: Update this method to work with held items // TODO: Update this method to work with held items
// Update berry eaten trackers for Belch, Harvest, Cud Chew, etc. // Update berry eaten trackers for Belch, Harvest, Cud Chew, etc.
// Don't recover it if we proc berry pouch (no item duplication) // Don't recover it if we proc berry pouch (no item duplication)
pokemon.recordEatenBerry(this.berryType, consumed); pokemon.recordEatenBerry(this.berryType, consumed);
globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(pokemon, this.berryType));
return true; 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, RepeatBerryNextTurnAbAttr,
} from "#app/data/abilities/ability"; } from "#app/data/abilities/ability";
import { CommonAnim } from "#app/data/battle-anims"; import { CommonAnim } from "#app/data/battle-anims";
import { BerryUsedEvent } from "#app/events/battle-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { BerryModifier } from "#app/modifier/modifier";
import i18next from "i18next"; import i18next from "i18next";
import { BooleanHolder } from "#app/utils/common"; import { BooleanHolder } from "#app/utils/common";
import { FieldPhase } from "./field-phase"; import { FieldPhase } from "./field-phase";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type Pokemon from "#app/field/pokemon"; 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. * 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 * @param pokemon - The {@linkcode Pokemon} to check
*/ */
eatBerries(pokemon: Pokemon): void { eatBerries(pokemon: Pokemon): void {
const hasUsableBerry = !!globalScene.findModifier( const hasUsableBerry = pokemon.getHeldItems().some(m => {
m => m instanceof BerryModifier && m.shouldApply(pokemon), //TODO: This is bugged, must fix the .shouldApply() function
pokemon.isPlayer(), isItemInCategory(m, HeldItemCategoryId.BERRY) && allHeldItems[m].shouldApply(pokemon);
); });
if (!hasUsableBerry) { if (!hasUsableBerry) {
return; return;
@ -64,14 +65,7 @@ export class BerryPhase extends FieldPhase {
CommonAnim.USE_ITEM, CommonAnim.USE_ITEM,
); );
for (const berryModifier of globalScene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) { applyHeldItems(ITEM_EFFECT.BERRY, { pokemon: 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));
}
globalScene.updateModifiers(pokemon.isPlayer()); globalScene.updateModifiers(pokemon.isPlayer());
// AbilityId.CHEEK_POUCH only works once per round of nom noms // 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; const berryUsedEvent = event as BerryUsedEvent;
if ( if (
!berryUsedEvent || !berryUsedEvent ||
berryUsedEvent.berryModifier.pokemonId !== this.pokemon?.id || berryUsedEvent.pokemon.id !== this.pokemon?.id ||
berryUsedEvent.berryModifier.berryType !== BerryType.LEPPA berryUsedEvent.berryType !== BerryType.LEPPA
) { ) {
// We only care about Leppa berries // We only care about Leppa berries
return; return;