Update loot table preview, item display and tooltips to 1.21

This commit is contained in:
Misode
2024-09-11 02:31:17 +02:00
parent 337b7d9b0a
commit fd6de2ac85
15 changed files with 2073 additions and 408 deletions

View File

@@ -1,13 +1,15 @@
import type { ItemStack } from 'deepslate/core'
import { Identifier } from 'deepslate/core'
import { useEffect, useRef, useState } from 'preact/hooks'
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'
import { useVersion } from '../contexts/Version.jsx'
import { useAsync } from '../hooks/useAsync.js'
import { fetchItemComponents } from '../services/index.js'
import { ResolvedItem } from '../services/ResolvedItem.js'
import { renderItem } from '../services/Resources.js'
import { getCollections } from '../services/Schemas.js'
import { jsonToNbt } from '../Utils.js'
import { ItemTooltip } from './ItemTooltip.jsx'
import { Octicon } from './Octicon.jsx'
import { itemHasGlint } from './previews/LootTable.js'
interface Props {
item: ItemStack,
@@ -16,6 +18,7 @@ interface Props {
advancedTooltip?: boolean,
}
export function ItemDisplay({ item, slotDecoration, tooltip, advancedTooltip }: Props) {
const { version } = useVersion()
const el = useRef<HTMLDivElement>(null)
const [tooltipOffset, setTooltipOffset] = useState<[number, number]>([0, 0])
const [tooltipSwap, setTooltipSwap] = useState(false)
@@ -33,10 +36,20 @@ export function ItemDisplay({ item, slotDecoration, tooltip, advancedTooltip }:
return () => el.current?.removeEventListener('mousemove', onMove)
}, [])
const maxDamage = item.getItem().durability
const { value: baseComponents } = useAsync(() => fetchItemComponents(version), [version])
const itemResolver = useCallback((item: ItemStack) => {
const base = baseComponents?.get(item.id.toString()) ?? new Map()
return new ResolvedItem(item, new Map([...base.entries()].map(([k, v]) => [k, jsonToNbt(v)])))
}, [baseComponents])
const resolvedItem = useMemo(() => {
return itemResolver(item)
}, [item, baseComponents])
const maxDamage = resolvedItem.getMaxDamage()
const damage = resolvedItem.getDamage()
return <div class="item-display" ref={el}>
<ItemItself item={item} />
<ItemItself item={resolvedItem} />
{item.count !== 1 && <>
<svg class="item-count" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="xMinYMid meet">
<text x="95" y="93" font-size="50" textAnchor="end" fontFamily="MinecraftSeven" fill="#373737">{item.count}</text>
@@ -44,9 +57,9 @@ export function ItemDisplay({ item, slotDecoration, tooltip, advancedTooltip }:
</svg>
</>}
{slotDecoration && <>
{(maxDamage && item.tag.getNumber('Damage') > 0) && <svg class="item-durability" width="100%" height="100%" viewBox="0 0 18 18">
{(maxDamage > 0 && damage > 0) && <svg class="item-durability" width="100%" height="100%" viewBox="0 0 18 18">
<rect x="3" y="14" width="13" height="2" fill="#000" />
<rect x="3" y="14" width={`${(maxDamage - item.tag.getNumber('Damage')) / maxDamage * 13}`} height="1" fill={`hsl(${(maxDamage - item.tag.getNumber('Damage')) / maxDamage * 120}deg, 100%, 50%)`} />
<rect x="3" y="14" width={`${(maxDamage - damage) / maxDamage * 13}`} height="1" fill={`hsl(${(maxDamage - damage) / maxDamage * 120}deg, 100%, 50%)`} />
</svg>}
<div class="item-slot-overlay"></div>
</>}
@@ -55,16 +68,17 @@ export function ItemDisplay({ item, slotDecoration, tooltip, advancedTooltip }:
right: (tooltipSwap ? `${tooltipOffset[0]}px` : undefined),
top: `${tooltipOffset[1]}px`,
}}>
<ItemTooltip item={item} advanced={advancedTooltip} />
<ItemTooltip item={resolvedItem} advanced={advancedTooltip} resolver={itemResolver} />
</div>}
</div>
}
function ItemItself({ item }: Props) {
interface ResolvedProps extends Props {
item: ResolvedItem
}
function ItemItself({ item }: ResolvedProps) {
const { version } = useVersion()
const hasGlint = itemHasGlint(item)
if (item.id.namespace !== Identifier.DEFAULT_NAMESPACE) {
return Octicon.package
}
@@ -77,20 +91,20 @@ function ItemItself({ item }: Props) {
const modelPath = `item/${item.id.path}`
if (collections.get('model').includes('minecraft:' + modelPath)) {
return <RenderedItem item={item} hasGlint={hasGlint} />
return <RenderedItem item={item} />
}
return Octicon.package
}
function RenderedItem({ item, hasGlint }: Props & { hasGlint: boolean }) {
function RenderedItem({ item }: ResolvedProps) {
const { version } = useVersion()
const { value: src } = useAsync(() => renderItem(version, item), [version, item])
const { value: src } = useAsync(() => renderItem(version, item.flatten()), [version, item])
if (src) {
return <>
<img src={src} alt={item.id.toString()} class="model" draggable={false} />
{hasGlint && <div class="item-glint" style={{'--mask-image': `url("${src}")`}}></div>}
{item.hasFoil() && <div class="item-glint" style={{'--mask-image': `url("${src}")`}}></div>}
</>
}