Files
misode.github.io/src/app/components/ItemTooltip1204.tsx
T
Sol Toder 3f52a908a6
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Fix converting colors with alpha to hex RGB (#705)
Convert negative integers to unsigned integers. Truncate the alpha from colors before passing them to the HTML color picker, since it doesn't allow alpha values (and they'd be in the wrong place if it did).
2025-02-22 15:14:57 +01:00

138 lines
6.1 KiB
TypeScript

import type { ItemStack } from 'deepslate-1.20.4/core'
import { AttributeModifierOperation, Enchantment, Identifier, MobEffectInstance, Potion } from 'deepslate-1.20.4/core'
import { NbtList, NbtType } from 'deepslate-1.20.4/nbt'
import { useLocale } from '../contexts/Locale.jsx'
import { useVersion } from '../contexts/Version.jsx'
import { useAsync } from '../hooks/useAsync.js'
import { getLanguage, getTranslation } from '../services/Resources.js'
import { intToDisplayHexRgb, message } from '../Utils.js'
import { TextComponent } from './TextComponent.jsx'
interface Props {
item: ItemStack,
advanced?: boolean,
}
export function ItemTooltip1204({ item, advanced }: Props) {
const { version } = useVersion()
const { lang } = useLocale()
const { value: language } = useAsync(() => getLanguage(version, lang), [version, lang])
const isPotion = item.is('potion') || item.is('splash_potion') || item.is('lingering_potion')
let displayName = item.tag.getCompound('display').getString('Name')
let name: string | undefined
if (displayName) {
try {
name = JSON.parse(displayName)
} catch (e) {
console.warn(`Error parsing display name '${displayName}': ${message(e)}`)
displayName = ''
}
}
if (name === undefined) {
if (language) {
let descriptionId = `${item.id.namespace}.${item.id.path}`
if (isPotion) {
descriptionId = `${descriptionId}.effect.${Potion.fromNbt(item).name}`
}
name = getTranslation(language, `item.${descriptionId}`)
name ??= getTranslation(language, `block.${descriptionId}`)
}
name ??= item.id.path
.replace(/[_\/]/g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}
const lore: any[] = []
item.tag.getCompound('display').getList('Lore', NbtType.String).forEach((line) => {
try {
lore.push(JSON.parse(line['value']))
} catch (e) {
console.warn(`Error parsing lore line '${line}': ${message(e)}`)
}
})
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 <>
<TextComponent component={name} base={{ color: 'white', italic: displayName.length > 0 }} />
{shouldShow(item, 'additional') && <>
{(!advanced && displayName.length === 0 && item.is('filled_map') && item.tag.hasNumber('map')) && <>
<TextComponent component={{ text: `#${item.tag.getNumber('map')}`, color: 'gray' }} />
</>}
{(item.is('filled_map') && advanced) && <>
<TextComponent component={{ translate: 'filled_map.unknown', color: 'gray' }} />
</>}
{isPotion && effects.length === 0
? <TextComponent component={{ translate: 'effect.none', color: 'gray' }} />
: 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 <TextComponent component={{ ...component, color }} />
})}
{attributeModifiers.length > 0 && <>
<TextComponent component='' />
<TextComponent component={{ translate: 'potion.whenDrank', color: 'dark_purple' }} />
{attributeModifiers.map(([attr, { amount, operation }]) => {
const a = operation === AttributeModifierOperation.addition ? amount * 100 : amount
if (amount > 0) {
return <TextComponent component={{ translate: `attribute.modifier.plus.${operation}`, with: [Math.floor(a * 100) / 100, { translate: `attribute.name.${attr.id.path}` }], color: 'blue' }} />
} else if (amount < 0) {
return <TextComponent component={{ translate: `attribute.modifier.take.${operation}`, with: [Math.floor(a * -100) / 100, { translate: `attribute.name.${attr.id.path}` }], color: 'red' }} />
}
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 <TextComponent component={component} />
})}
{item.tag.hasCompound('display') && <>
{shouldShow(item, 'dye') && item.tag.getCompound('display').hasNumber('color') && (advanced
? <TextComponent component={{ translate: 'item.color', with: [intToDisplayHexRgb(item.tag.getCompound('display').getNumber('color'))], color: 'gray' }} />
: <TextComponent component={{ translate: 'item.dyed', color: 'gray' }} />)}
{lore.map((component) => <TextComponent component={component} base={{ color: 'dark_purple', italic: true }} />)}
</>}
{shouldShow(item, 'unbreakable') && item.tag.getBoolean('Unbreakable') && <TextComponent component={{ translate: 'item.unbreakable', color: 'blue' }} />}
{(advanced && item.tag.getNumber('Damage') > 0 && durability) && <TextComponent component={{ translate: 'item.durability', with: [`${durability - item.tag.getNumber('Damage')}`, `${durability}`] }} />}
{advanced && <>
<TextComponent component={{ text: item.id.toString(), color: 'dark_gray'}} />
{item.tag.size > 0 && <TextComponent component={{ translate: 'item.nbt_tags', with: [item.tag.size], color: 'dark_gray' }} />}
</>}
</>
}
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
}