mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-28 09:18:45 +00:00
116 lines
5.1 KiB
TypeScript
116 lines
5.1 KiB
TypeScript
import { Identifier } from 'deepslate'
|
|
import { useMemo, useRef, useState } from 'preact/hooks'
|
|
import { useLocale, useVersion } from '../../contexts/index.js'
|
|
import { useAsync } from '../../hooks/useAsync.js'
|
|
import { checkVersion, fetchAllPresets, fetchItemComponents } from '../../services/index.js'
|
|
import { clamp, jsonToNbt, randomSeed, safeJsonParse } from '../../Utils.js'
|
|
import { Btn, BtnMenu, NumberInput } from '../index.js'
|
|
import { ItemDisplay } from '../ItemDisplay.jsx'
|
|
import { ItemDisplay1204 } from '../ItemDisplay1204.jsx'
|
|
import type { PreviewProps } from './index.js'
|
|
import { generateLootTable } from './LootTable.js'
|
|
import { generateLootTable as generateLootTable1204 } from './LootTable1204.js'
|
|
|
|
export const LootTablePreview = ({ docAndNode }: PreviewProps) => {
|
|
const { locale } = useLocale()
|
|
const { version } = useVersion()
|
|
const use1204 = !checkVersion(version, '1.20.5')
|
|
|
|
const [seed, setSeed] = useState(randomSeed())
|
|
const [luck, setLuck] = useState(0)
|
|
const [daytime, setDaytime] = useState(0)
|
|
const [weather, setWeather] = useState('clear')
|
|
const [mixItems, setMixItems] = useState(true)
|
|
const [advancedTooltips, setAdvancedTooltips] = useState(true)
|
|
const overlay = useRef<HTMLDivElement>(null)
|
|
|
|
const { value: dependencies, loading } = useAsync(() => {
|
|
return Promise.all([
|
|
fetchAllPresets(version, 'tag/item'),
|
|
fetchAllPresets(version, 'loot_table'),
|
|
use1204 ? Promise.resolve(undefined) : fetchItemComponents(version),
|
|
checkVersion(version, '1.21') ? fetchAllPresets(version, 'enchantment') : Promise.resolve(undefined),
|
|
checkVersion(version, '1.21') ? fetchAllPresets(version, 'tag/enchantment') : Promise.resolve(undefined),
|
|
])
|
|
}, [version])
|
|
|
|
const text = docAndNode.doc.getText()
|
|
const items = useMemo(() => {
|
|
if (dependencies === undefined || loading) {
|
|
return []
|
|
}
|
|
const [itemTags, lootTables, itemComponents, enchantments, enchantmentTags] = dependencies
|
|
const table = safeJsonParse(text) ?? {}
|
|
if (use1204) {
|
|
return generateLootTable1204(table, {
|
|
version, seed, luck, daytime, weather,
|
|
stackMixer: mixItems ? 'container' : 'default',
|
|
getItemTag: (id) => (itemTags.get(id.replace(/^minecraft:/, '')) as any)?.values ?? [],
|
|
getLootTable: (id) => lootTables.get(id.replace(/^minecraft:/, '')),
|
|
getPredicate: () => undefined,
|
|
})
|
|
}
|
|
return generateLootTable(table, {
|
|
version, seed, luck, daytime, weather,
|
|
stackMixer: mixItems ? 'container' : 'default',
|
|
getItemTag: (id) => (itemTags.get(id.replace(/^minecraft:/, '')) as any)?.values ?? [],
|
|
getLootTable: (id) => lootTables.get(id.replace(/^minecraft:/, '')),
|
|
getPredicate: () => undefined,
|
|
getEnchantments: () => enchantments ?? new Map(),
|
|
getEnchantmentTag: (id) => (enchantmentTags?.get(id.replace(/^minecraft:/, '')) as any)?.values ?? [],
|
|
getBaseComponents: (id) => new Map([...(itemComponents?.get(Identifier.parse(id).toString()) ?? new Map()).entries()].map(([k, v]) => [k, jsonToNbt(v)])),
|
|
})
|
|
}, [version, seed, luck, daytime, weather, mixItems, text, dependencies, loading])
|
|
|
|
return <>
|
|
<div ref={overlay} class="preview-overlay">
|
|
<img src="/images/container.png" alt="Container background" class="pixelated" draggable={false} />
|
|
{items.map(({ slot, item }) =>
|
|
<div key={slot} style={slotStyle(slot)}>
|
|
{use1204 ?
|
|
<ItemDisplay1204 item={item as any} slotDecoration={true} advancedTooltip={advancedTooltips} /> :
|
|
<ItemDisplay item={item as any} slotDecoration={true} advancedTooltip={advancedTooltips} />}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div class="controls preview-controls">
|
|
<BtnMenu icon="gear" tooltip={locale('settings')} >
|
|
<div class="btn btn-input" onClick={e => e.stopPropagation()}>
|
|
<span>{locale('preview.luck')}</span>
|
|
<NumberInput value={luck} onChange={setLuck} />
|
|
</div>
|
|
<div class="btn btn-input" onClick={e => e.stopPropagation()}>
|
|
<span>{locale('preview.daytime')}</span>
|
|
<NumberInput value={daytime} onChange={setDaytime} />
|
|
</div>
|
|
<div class="btn btn-input" onClick={e => e.stopPropagation()}>
|
|
<span>{locale('preview.weather')}</span>
|
|
<select value={weather} onChange={e => setWeather((e.target as HTMLSelectElement).value)} >
|
|
{['clear', 'rain', 'thunder'].map(v =>
|
|
<option value={v}>{locale(`preview.weather.${v}`)}</option>)}
|
|
</select>
|
|
</div>
|
|
<Btn icon={mixItems ? 'square_fill' : 'square'} label="Fill container randomly" onClick={e => {setMixItems(!mixItems); e.stopPropagation()}} />
|
|
<Btn icon={advancedTooltips ? 'square_fill' : 'square'} label="Advanced tooltips" onClick={e => {setAdvancedTooltips(!advancedTooltips); e.stopPropagation()}} />
|
|
</BtnMenu>
|
|
<Btn icon="sync" tooltip={locale('generate_new_seed')} onClick={() => setSeed(randomSeed())} />
|
|
</div>
|
|
</>
|
|
}
|
|
|
|
const GUI_WIDTH = 176
|
|
const GUI_HEIGHT = 81
|
|
const SLOT_SIZE = 18
|
|
|
|
function slotStyle(slot: number) {
|
|
slot = clamp(slot, 0, 26)
|
|
const x = (slot % 9) * SLOT_SIZE + 7
|
|
const y = (Math.floor(slot / 9)) * SLOT_SIZE + 20
|
|
return {
|
|
left: `${x*100/GUI_WIDTH}%`,
|
|
top: `${y*100/GUI_HEIGHT}%`,
|
|
width: `${SLOT_SIZE*100/GUI_WIDTH}%`,
|
|
height: `${SLOT_SIZE*100/GUI_HEIGHT}%`,
|
|
}
|
|
}
|