import type { ItemStack } from 'deepslate/core' import { AttributeModifierOperation, Enchantment, Identifier, MobEffectInstance, Potion } from 'deepslate/core' import { NbtList, NbtType } from 'deepslate/nbt' import { useMemo } from 'preact/hooks' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' import { getTranslation } from '../services/Resources.js' import { TextComponent } from './TextComponent.jsx' interface Props { item: ItemStack, advanced?: boolean, } export function ItemTooltip({ item, advanced }: Props) { const { version } = useVersion() const isPotion = item.is('potion') || item.is('splash_potion') || item.is('lingering_potion') const descriptionId = useMemo(() => { const d = `${item.id.namespace}.${item.id.path}` if (isPotion) { return `${d}.effect.${Potion.fromNbt(item).name}` } return d }, [item]) const { value: translatedName } = useAsync(() => { return getTranslation(version, `item.${descriptionId}`) ?? getTranslation(version, `block.${descriptionId}`) }, [version, descriptionId]) const displayName = item.tag.getCompound('display').getString('Name') const name = displayName ? JSON.parse(displayName) : (translatedName ?? fakeTranslation(item.id.path)) const durability = item.getItem().durability const enchantments = (item.is('enchanted_book') ? item.tag.getList('StoredEnchantments', NbtType.Compound) : item.tag.getList('Enchantments', NbtType.Compound)) ?? NbtList.create() const effects = isPotion ? Potion.getAllEffects(item) : [] const attributeModifiers = isPotion ? Potion.getAllAttributeModifiers(item) : [] return <> 0 }} /> {shouldShow(item, 'additional') && <> {(!advanced && displayName.length === 0 && item.is('filled_map') && item.tag.hasNumber('map')) && <> } {(item.is('filled_map') && advanced) && <> } {isPotion && effects.length === 0 ? : effects.map(e => { const color = e.effect.category === 'harmful' ? 'red' : 'blue' let component: any = { translate: `effect.${e.effect.id.namespace}.${e.effect.id.path}` } if (e.amplifier > 0) { component = { translate: 'potion.withAmplifier', with: [component, { translate: `potion.potency.${e.amplifier}` }] } } if (e.duration > 20) { component = { translate: 'potion.withDuration', with: [component, MobEffectInstance.formatDuration(e)] } } return })} {attributeModifiers.length > 0 && <> {attributeModifiers.map(([attr, { amount, operation }]) => { const a = operation === AttributeModifierOperation.addition ? amount * 100 : amount if (amount > 0) { return } else if (amount < 0) { return } return null })} } } {shouldShow(item, 'enchantments') && enchantments.map(enchantment => { const id = enchantment.getString('id') const lvl = enchantment.getNumber('lvl') const ench = Enchantment.REGISTRY.get(Identifier.parse(id)) const component: any[] = [{ translate: `enchantment.${id.replace(':', '.')}`, color: ench?.isCurse ? 'red' : 'gray' }] if (lvl !== 1 || ench?.maxLevel !== 1) { component.push(' ', { translate: `enchantment.level.${lvl}`}) } return })} {item.tag.hasCompound('display') && <> {shouldShow(item, 'dye') && item.tag.getCompound('display').hasNumber('color') && (advanced ? : )} {(item.tag.getCompound('display').getList('Lore', NbtType.String)).map((line) => )} } {shouldShow(item, 'unbreakable') && item.tag.getBoolean('Unbreakable') && } {(advanced && item.tag.getNumber('Damage') > 0 && durability) && } {advanced && <> {item.tag.size > 0 && } } } function fakeTranslation(str: string) { return str .replace(/[_\/]/g, ' ') .split(' ') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' ') } const TooltipMasks = { enchantments: 1, modifiers: 2, unbreakable: 4, can_destroy: 8, can_place: 16, additional: 32, dye: 64, upgrades: 128, } function shouldShow(item: ItemStack, mask: keyof typeof TooltipMasks) { const flags = item.tag.getNumber('HideFlags') return (flags & TooltipMasks[mask]) === 0 }