pokerogue/src/items/held-item.ts
2025-06-09 16:48:56 +02:00

164 lines
4.3 KiB
TypeScript

import { applyPostItemLostAbAttrs, PostItemLostAbAttr } from "#app/data/abilities/ability";
import type Pokemon from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import { HeldItemNames, type HeldItemId } from "#enums/held-item-id";
import i18next from "i18next";
export const ITEM_EFFECT = {
ATTACK_TYPE_BOOST: 1,
TURN_END_HEAL: 2,
HIT_HEAL: 3,
RESET_NEGATIVE_STAT_STAGE: 4,
EXP_BOOSTER: 5,
// Should we actually distinguish different berry effects?
BERRY: 6,
BASE_STAT_BOOSTER: 7,
INSTANT_REVIVE: 8,
STAT_BOOST: 9,
CRIT_BOOST: 10,
TURN_END_STATUS: 11,
SURVIVE_CHANCE: 12,
BYPASS_SPEED_CHANCE: 13,
FLINCH_CHANCE: 14,
FIELD_EFFECT: 15,
FRIENDSHIP_BOOSTER: 16,
NATURE_WEIGHT_BOOSTER: 17,
ACCURACY_BOOSTER: 18,
MULTI_HIT: 19,
DAMAGE_MONEY_REWARD: 20,
BATON: 21,
TURN_END_ITEM_STEAL: 22,
CONTACT_ITEM_STEAL_CHANCE: 23,
EVO_TRACKER: 40,
BASE_STAT_TOTAL: 50,
BASE_STAT_FLAT: 51,
INCREMENTING_STAT: 52,
} as const;
export type ITEM_EFFECT = (typeof ITEM_EFFECT)[keyof typeof ITEM_EFFECT];
export class HeldItem {
// public pokemonId: number;
public type: HeldItemId;
public maxStackCount: number;
public isTransferable = true;
public isStealable = true;
public isSuppressable = true;
//TODO: If this is actually never changed by any subclass, perhaps it should not be here
public soundName = "se/restore";
constructor(type: HeldItemId, maxStackCount = 1) {
this.type = type;
this.maxStackCount = maxStackCount;
this.isTransferable = true;
this.isStealable = true;
this.isSuppressable = true;
}
get name(): string {
return i18next.t(`modifierType:ModifierType.${HeldItemNames[this.type]}.name`) + " (new)";
}
get description(): string {
return i18next.t(`modifierType:ModifierType.${HeldItemNames[this.type]}.description`);
}
get iconName(): string {
return `${HeldItemNames[this.type]?.toLowerCase()}`;
}
// TODO: Aren't these fine as just properties to set in the subclass definition?
untransferable(): HeldItem {
this.isTransferable = false;
return this;
}
unstealable(): HeldItem {
this.isStealable = false;
return this;
}
unsuppressable(): HeldItem {
this.isSuppressable = false;
return this;
}
getMaxStackCount(): number {
return this.maxStackCount;
}
createSummaryIcon(pokemon: Pokemon): Phaser.GameObjects.Container {
const container = globalScene.add.container(0, 0);
const item = globalScene.add.sprite(0, 12, "items").setFrame(this.iconName).setOrigin(0, 0.5);
container.add(item);
const stackText = this.getIconStackText(pokemon);
if (stackText) {
container.add(stackText);
}
container.setScale(0.5);
return container;
}
createPokemonIcon(pokemon: Pokemon): Phaser.GameObjects.Container {
const container = globalScene.add.container(0, 0);
const pokemonIcon = globalScene.addPokemonIcon(pokemon, -2, 10, 0, 0.5, undefined, true);
container.add(pokemonIcon);
container.setName(pokemon.id.toString());
const item = globalScene.add
.sprite(16, 16, "items")
.setScale(0.5)
.setOrigin(0, 0.5)
.setTexture("items", this.iconName);
container.add(item);
const stackText = this.getIconStackText(pokemon);
if (stackText) {
container.add(stackText);
}
return container;
}
getIconStackText(pokemon: Pokemon): Phaser.GameObjects.BitmapText | null {
if (this.getMaxStackCount() === 1) {
return null;
}
const stackCount = pokemon.heldItemManager.getStack(this.type);
const text = globalScene.add.bitmapText(10, 15, "item-count", stackCount.toString(), 11);
text.letterSpacing = -0.5;
if (stackCount >= this.getMaxStackCount()) {
text.setTint(0xf89890);
}
text.setOrigin(0);
return text;
}
getScoreMultiplier(): number {
return 1;
}
}
export class ConsumableHeldItem extends HeldItem {
// Sometimes berries are not eaten, some stuff may not proc unburden...
consume(pokemon: Pokemon, isPlayer: boolean, remove = true, unburden = true): void {
if (remove) {
pokemon.heldItemManager.remove(this.type, 1);
// TODO: Turn this into updateItemBar or something
globalScene.updateModifiers(isPlayer);
}
if (unburden) {
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
}
}
}