mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 23:56:51 +00:00
Add tooltips for potion effects
This commit is contained in:
@@ -49,7 +49,13 @@ export function ItemDisplay({ item, slotDecoration, advancedTooltip }: Props) {
|
||||
</svg>}
|
||||
<div class="item-slot-overlay"></div>
|
||||
</>}
|
||||
<ItemTooltip item={item} advanced={advancedTooltip} offset={tooltipOffset} swap={tooltipSwap} />
|
||||
<div class="item-tooltip" style={tooltipOffset && {
|
||||
left: (tooltipSwap ? undefined : `${tooltipOffset[0]}px`),
|
||||
right: (tooltipSwap ? `${tooltipOffset[0]}px` : undefined),
|
||||
top: `${tooltipOffset[1]}px`,
|
||||
}}>
|
||||
<ItemTooltip item={item} advanced={advancedTooltip} />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <div class="item-tooltip" style={offset && {
|
||||
left: (swap ? undefined : `${offset[0]}px`),
|
||||
right: (swap ? `${offset[0]}px` : undefined),
|
||||
top: `${offset[1]}px`,
|
||||
}}>
|
||||
const effects = isPotion ? Potion.getAllEffects(item) : []
|
||||
const attributeModifiers = isPotion ? Potion.getAllAttributeModifiers(item) : []
|
||||
|
||||
return <>
|
||||
<TextComponent component={name} base={{ color: 'white', italic: displayName.length > 0 }} />
|
||||
{(!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
|
||||
})}
|
||||
</>}
|
||||
{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) {
|
||||
<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' }} />}
|
||||
</>}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
function fakeTranslation(str: string) {
|
||||
|
||||
@@ -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 <div class="text-component">
|
||||
{shadow && <div style={createStyle(base, true)}>
|
||||
{parts.map(p => <TextPart part={p} shadow={true} />)}
|
||||
{parts.map(p => <TextPart part={p} shadow={true} lang={language ?? {}} />)}
|
||||
</div>}
|
||||
<div class="text-foreground" style={createStyle(base, false)}>
|
||||
{parts.map(p => <TextPart part={p} />)}
|
||||
{parts.map(p => <TextPart part={p} lang={language ?? {}} />)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -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<string, string> }) {
|
||||
if (part.translate) {
|
||||
const { version } = useVersion()
|
||||
const { value: translated } = useAsync(() => {
|
||||
return getTranslation(version, part.translate!, part.with)
|
||||
}, [version, part.translate, ...part.with ?? []])
|
||||
return <span style={createStyle(part, shadow)}>{translated ?? part.translate}</span>
|
||||
const str = resolveTranslate(part.translate, part.with, lang)
|
||||
return <span style={createStyle(part, shadow)}>{str}</span>
|
||||
}
|
||||
return <span style={createStyle(part, shadow)}>{part.text}</span>
|
||||
}
|
||||
|
||||
function resolveTranslate(translate: string, with_: any[] | undefined, lang: Record<string, string>): 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)
|
||||
|
||||
Reference in New Issue
Block a user