import React, { useState } from 'react' import { useInventoryContext } from '../../context/InventoryContext' import { useTextures } from '../../context/TextureContext' import { useScale } from '../../context/ScaleContext' const ENCHANTMENT_NAMES: Record = { 0: 'Protection', 1: 'Fire Protection', 2: 'Feather Falling', 3: 'Blast Protection', 4: 'Projectile Protection', 5: 'Respiration', 6: 'Aqua Affinity', 7: 'Thorns', 8: 'Depth Strider', 9: 'Frost Walker', 10: 'Binding Curse', 16: 'Sharpness', 17: 'Smite', 18: 'Bane of Arthropods', 19: 'Knockback', 20: 'Fire Aspect', 21: 'Looting', 22: 'Sweeping Edge', 32: 'Efficiency', 33: 'Silk Touch', 34: 'Unbreaking', 35: 'Fortune', 48: 'Power', 49: 'Punch', 50: 'Flame', 51: 'Infinity', 61: 'Luck of the Sea', 62: 'Lure', 65: 'Loyalty', 66: 'Impaling', 67: 'Riptide', 68: 'Channeling', 70: 'Multishot', 71: 'Quick Charge', 72: 'Piercing', 73: 'Mending', 74: 'Vanishing Curse', } const ROMAN_NUMERALS: [number, string][] = [ [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'], ] function toRoman(num: number): string { if (num <= 0) return '' let result = '' let remaining = num for (const [value, symbol] of ROMAN_NUMERALS) { while (remaining >= value) { result += symbol remaining -= value } } return result } const SPRITE_BASE = '1.21.11/textures/gui/sprites/container/enchanting_table' interface EnchantmentOptionsProps { properties: Record x: number y: number } const OPTION_KEYS = [ { levelKey: 'topEnchantLevel', idKey: 'topEnchantId', clueKey: 'topLevelClue', slot: 0 }, { levelKey: 'middleEnchantLevel', idKey: 'middleEnchantId', clueKey: 'middleLevelClue', slot: 1 }, { levelKey: 'bottomEnchantLevel', idKey: 'bottomEnchantId', clueKey: 'bottomLevelClue', slot: 2 }, ] export function EnchantmentOptions({ properties, x, y }: EnchantmentOptionsProps) { const { sendAction, resolveEnchantmentName } = useInventoryContext() const textures = useTextures() const { scale } = useScale() const [hoveredSlot, setHoveredSlot] = useState(null) const getName = (id: number): string => { if (id < 0) return '???' return resolveEnchantmentName?.(id) ?? ENCHANTMENT_NAMES[id] ?? `Enchant #${id}` } const getSlotSprite = (disabled: boolean, hovered: boolean) => { if (disabled) return textures.getGuiTextureUrl(`${SPRITE_BASE}/enchantment_slot_disabled.png`) if (hovered) return textures.getGuiTextureUrl(`${SPRITE_BASE}/enchantment_slot_highlighted.png`) return textures.getGuiTextureUrl(`${SPRITE_BASE}/enchantment_slot.png`) } const getLevelSprite = (slot: number, disabled: boolean) => { const n = slot + 1 if (disabled) return textures.getGuiTextureUrl(`${SPRITE_BASE}/level_${n}_disabled.png`) return textures.getGuiTextureUrl(`${SPRITE_BASE}/level_${n}.png`) } return (
{OPTION_KEYS.map(({ levelKey, idKey, clueKey, slot }, i) => { const level = properties[levelKey] ?? 0 const enchId = properties[idKey] ?? -1 const levelClue = properties[clueKey] ?? 0 const disabled = level <= 0 const hovered = hoveredSlot === i && !disabled const name = getName(enchId) const levelSuffix = levelClue > 0 ? ` ${toRoman(levelClue)}` : '' // Vanilla colors const nameColor = disabled ? '#342F25' : hovered ? '#FFFF80' : '#685E4A' const costColor = disabled ? '#407F10' : '#80FF20' return (
!disabled && sendAction({ type: 'enchant', enchantIndex: slot })} onMouseEnter={() => setHoveredSlot(i)} onMouseLeave={() => setHoveredSlot(null)} style={{ position: 'relative', width: 108 * scale, height: 19 * scale, cursor: disabled ? 'default' : 'pointer', overflow: 'hidden', }} > {/* Background sprite */} {/* Level icon (16×16 at +1,+1) */} {!disabled && ( )} {/* Enchantment name text (at +20, +2) */} {!disabled && ( {`${name}${levelSuffix}`} )} {/* Cost number (right-aligned, vertically centered) */} {!disabled && ( {level} )}
) })}
) }