diff --git a/package-lock.json b/package-lock.json index b98d55dc..0446e071 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "brace": "^0.11.1", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.15.6", + "deepslate": "^0.15.7", "deepslate-1.18": "npm:deepslate@^0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13", "highlight.js": "^11.5.1", @@ -1955,9 +1955,9 @@ "dev": true }, "node_modules/deepslate": { - "version": "0.15.6", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.15.6.tgz", - "integrity": "sha512-aUNkqozUTvEuR/tFWCiFCpsJ4Q8AaHyvHY33/T7K+cUY9V6wlrlUwPCgwoaP3G6AMWUm5vN57NSu+dC8ESTDww==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.15.7.tgz", + "integrity": "sha512-HRo31wvMfw4t8osCeiX/A79W1XfP7kyQJJZKjZlKiUutoHErEnpGuEOn0XdKXc4NQXEvrbUmL2yOrNo+DCjdug==", "dependencies": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", @@ -6698,9 +6698,9 @@ "dev": true }, "deepslate": { - "version": "0.15.6", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.15.6.tgz", - "integrity": "sha512-aUNkqozUTvEuR/tFWCiFCpsJ4Q8AaHyvHY33/T7K+cUY9V6wlrlUwPCgwoaP3G6AMWUm5vN57NSu+dC8ESTDww==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.15.7.tgz", + "integrity": "sha512-HRo31wvMfw4t8osCeiX/A79W1XfP7kyQJJZKjZlKiUutoHErEnpGuEOn0XdKXc4NQXEvrbUmL2yOrNo+DCjdug==", "requires": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", diff --git a/package.json b/package.json index 9b0b276a..24f442b3 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "brace": "^0.11.1", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.15.6", + "deepslate": "^0.15.7", "deepslate-1.18": "npm:deepslate@^0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13", "highlight.js": "^11.5.1", diff --git a/src/app/components/ItemDisplay.tsx b/src/app/components/ItemDisplay.tsx index b3262089..6144d951 100644 --- a/src/app/components/ItemDisplay.tsx +++ b/src/app/components/ItemDisplay.tsx @@ -49,7 +49,13 @@ export function ItemDisplay({ item, slotDecoration, advancedTooltip }: Props) { }
} - +
+ +
} diff --git a/src/app/components/ItemTooltip.tsx b/src/app/components/ItemTooltip.tsx index 7c813ab9..215a26b0 100644 --- a/src/app/components/ItemTooltip.tsx +++ b/src/app/components/ItemTooltip.tsx @@ -1,5 +1,6 @@ import type { ItemStack } from 'deepslate' -import { NbtList, NbtType } from 'deepslate' +import { AttributeModifierOperation, MobEffectInstance, NbtList, NbtType, Potion } from 'deepslate' +import { useMemo } from 'preact/hooks' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' import { getEnchantmentData, MaxDamageItems } from '../previews/LootTable.js' @@ -9,27 +10,64 @@ import { TextComponent } from './TextComponent.jsx' interface Props { item: ItemStack, advanced?: boolean, - offset?: [number, number], - swap?: boolean, } -export function ItemTooltip({ item, advanced, offset = [0, 0], swap }: Props) { +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(() => { - const key = `${item.id.namespace}.${item.id.path}` - return getTranslation(version, `item.${key}`) ?? getTranslation(version, `block.${key}`) - }, [version, item.id]) + 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 maxDamage = MaxDamageItems.get(item.id.toString()) const enchantments = (item.is('enchanted_book') ? item.tag.getList('StoredEnchantments', NbtType.Compound) : item.tag.getList('Enchantments', NbtType.Compound)) ?? NbtList.create() - return
+ const effects = isPotion ? Potion.getAllEffects(item) : [] + const attributeModifiers = isPotion ? Potion.getAllAttributeModifiers(item) : [] + + return <> 0 }} /> + {(!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 + })} + } {enchantments.map(enchantment => { const id = enchantment.getString('id') const lvl = enchantment.getNumber('lvl') @@ -52,7 +90,7 @@ export function ItemTooltip({ item, advanced, offset = [0, 0], swap }: Props) { {item.tag.size > 0 && } } -
+ } function fakeTranslation(str: string) { diff --git a/src/app/components/TextComponent.tsx b/src/app/components/TextComponent.tsx index 2e0b26c9..7bff3579 100644 --- a/src/app/components/TextComponent.tsx +++ b/src/app/components/TextComponent.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'preact/hooks' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' -import { getTranslation } from '../services/Resources.js' +import { getLanguage, replaceTranslation } from '../services/Resources.js' interface StyleData { color?: string, @@ -23,6 +23,8 @@ interface Props { shadow?: boolean, } export function TextComponent({ component, base = { color: 'white' }, shadow = true }: Props) { + const { version } = useVersion() + const state = JSON.stringify(component) const parts = useMemo(() => { const parts: PartData[] = [] @@ -30,12 +32,14 @@ export function TextComponent({ component, base = { color: 'white' }, shadow = t return parts }, [state]) + const { value: language } = useAsync(() => getLanguage(version), [version]) + return
{shadow &&
- {parts.map(p => )} + {parts.map(p => )}
}
- {parts.map(p => )} + {parts.map(p => )}
} @@ -104,17 +108,26 @@ const TextColors = { type TextColorKey = keyof typeof TextColors const TextColorKeys = Object.keys(TextColors) -function TextPart({ part, shadow }: { part: PartData, shadow?: boolean }) { +function TextPart({ part, shadow, lang }: { part: PartData, shadow?: boolean, lang: Record }) { if (part.translate) { - const { version } = useVersion() - const { value: translated } = useAsync(() => { - return getTranslation(version, part.translate!, part.with) - }, [version, part.translate, ...part.with ?? []]) - return {translated ?? part.translate} + const str = resolveTranslate(part.translate, part.with, lang) + return {str} } return {part.text} } +function resolveTranslate(translate: string, with_: any[] | undefined, lang: Record): string { + const str = lang[translate] + if (typeof str !== 'string') return translate + const params = with_?.map((c): string => { + if (typeof c === 'string' || typeof c === 'number') return `${c}` + if (c.text) return c.text + if (c.translate) return resolveTranslate(c.translate, c.with, lang) + return '' + }) + return replaceTranslation(str, params) +} + function createStyle(style: StyleData, shadow?: boolean) { return { color: style.color && (TextColorKeys.includes(style.color) diff --git a/src/app/previews/LootTable.ts b/src/app/previews/LootTable.ts index e9ca9c52..9e1c8012 100644 --- a/src/app/previews/LootTable.ts +++ b/src/app/previews/LootTable.ts @@ -359,6 +359,11 @@ const LootFunctions: Record LootFunction> = { } } catch (e) {} }, + set_potion: ({ id }) => (item) => { + if (typeof id === 'string') { + item.tag.set('Potion', new NbtString(Identifier.parse(id).toString())) + } + }, } type LootCondition = (ctx: LootContext) => boolean diff --git a/src/styles/global.css b/src/styles/global.css index 15d09a3e..d25d376d 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1331,6 +1331,10 @@ hr { top: -2px; } +.text-component span:empty:before { + content: '\200b'; +} + .file-view { background-color: var(--background-2); color: var(--text-2);