diff --git a/src/app/components/Icons.tsx b/src/app/components/Icons.tsx index 42b78fce..35902cc8 100644 --- a/src/app/components/Icons.tsx +++ b/src/app/components/Icons.tsx @@ -20,6 +20,7 @@ export const Icons = { recipe: , 'tag/block': TAG, 'tag/damage_type': TAG, + 'tag/enchantment': TAG, 'tag/entity_type': TAG, 'tag/fluid': TAG, 'tag/game_event': TAG, diff --git a/src/app/components/previews/RecipePreview.tsx b/src/app/components/previews/RecipePreview.tsx index deaa0b2b..a0e9bb00 100644 --- a/src/app/components/previews/RecipePreview.tsx +++ b/src/app/components/previews/RecipePreview.tsx @@ -1,9 +1,10 @@ import { DataModel } from '@mcschema/core' import { Identifier, ItemStack } from 'deepslate' import { useEffect, useMemo, useRef, useState } from 'preact/hooks' -import { useLocale, useVersion } from '../../contexts/index.js' +import { useLocale } from '../../contexts/index.js' import { useAsync } from '../../hooks/useAsync.js' -import { fetchAllPresets } from '../../services/index.js' +import type { VersionId } from '../../services/index.js' +import { checkVersion, fetchAllPresets } from '../../services/index.js' import { jsonToNbt } from '../../Utils.js' import { Btn, BtnMenu } from '../index.js' import { ItemDisplay } from '../ItemDisplay.jsx' @@ -11,9 +12,8 @@ import type { PreviewProps } from './index.js' const ANIMATION_TIME = 1000 -export const RecipePreview = ({ data }: PreviewProps) => { +export const RecipePreview = ({ data, version }: PreviewProps) => { const { locale } = useLocale() - const { version } = useVersion() const [advancedTooltips, setAdvancedTooltips] = useState(true) const [animation, setAnimation] = useState(0) const overlay = useRef(null) @@ -32,7 +32,7 @@ export const RecipePreview = ({ data }: PreviewProps) => { const recipe = DataModel.unwrapLists(data) const state = JSON.stringify(recipe) const items = useMemo>(() => { - return placeItems(recipe, animation, itemTags ?? new Map()) + return placeItems(version, recipe, animation, itemTags ?? new Map()) }, [state, animation, itemTags]) const gui = useMemo(() => { @@ -101,7 +101,7 @@ function slotStyle(slot: Slot) { } } -function placeItems(recipe: any, animation: number, itemTags: Map) { +function placeItems(version: VersionId, recipe: any, animation: number, itemTags: Map) { const items = new Map() const type: string = recipe.type?.replace(/^minecraft:/, '') if (!type || type.startsWith('crafting_special') || type === 'crafting_decorated_pot') { @@ -111,7 +111,7 @@ function placeItems(recipe: any, animation: number, itemTags: Map) if (type === 'crafting_shapeless') { const ingredients: any[] = Array.isArray(recipe.ingredients) ? recipe.ingredients : [] ingredients.forEach((ingredient, i) => { - const choices = allIngredientChoices(ingredient, itemTags) + const choices = allIngredientChoices(version, ingredient, itemTags) if (i >= 0 && i < 9 && choices.length > 0) { const choice = choices[(3 * i + animation) % choices.length] items.set(`crafting.${i}` as Slot, choice) @@ -120,7 +120,7 @@ function placeItems(recipe: any, animation: number, itemTags: Map) } else if (type === 'crafting_shaped') { const keys = new Map() for (const [key, ingredient] of Object.entries(recipe.key ?? {})) { - const choices = allIngredientChoices(ingredient, itemTags) + const choices = allIngredientChoices(version, ingredient, itemTags) if (choices.length > 0) { const choice = choices[animation % choices.length] keys.set(key, choice) @@ -137,20 +137,20 @@ function placeItems(recipe: any, animation: number, itemTags: Map) } } } else if (type === 'smelting' || type === 'smoking' || type === 'blasting' || type === 'campfire_cooking') { - const choices = allIngredientChoices(recipe.ingredient, itemTags) + const choices = allIngredientChoices(version, recipe.ingredient, itemTags) if (choices.length > 0) { const choice = choices[animation % choices.length] items.set('smelting.ingredient' as Slot, choice) } } else if (type === 'stonecutting') { - const choices = allIngredientChoices(recipe.ingredient, itemTags) + const choices = allIngredientChoices(version, recipe.ingredient, itemTags) if (choices.length > 0) { const choice = choices[animation % choices.length] items.set('stonecutting.ingredient' as Slot, choice) } } else if (type === 'smithing_transform' || type === 'smithing_trim') { for (const ingredient of ['template', 'base', 'addition'] as const) { - const choices = allIngredientChoices(recipe[ingredient], itemTags) + const choices = allIngredientChoices(version, recipe[ingredient], itemTags) if (choices.length > 0) { const choice = choices[animation % choices.length] items.set(`smithing.${ingredient}`, choice) @@ -189,23 +189,43 @@ function placeItems(recipe: any, animation: number, itemTags: Map) return items } -function allIngredientChoices(ingredient: any, itemTags: Map): ItemStack[] { +function allIngredientChoices(version: VersionId, ingredient: any, itemTags: Map): ItemStack[] { if (Array.isArray(ingredient)) { - return ingredient.flatMap(i => allIngredientChoices(i, itemTags)) + return ingredient.flatMap(i => allIngredientChoices(version, i, itemTags)) } - if (typeof ingredient === 'object' && ingredient !== null) { - if (typeof ingredient.item === 'string') { - return [new ItemStack(Identifier.parse(ingredient.item), 1)] - } else if (typeof ingredient.tag === 'string') { - const tag: any = itemTags.get(ingredient.tag.replace(/^minecraft:/, '')) - if (typeof tag === 'object' && tag !== null && Array.isArray(tag.values)) { - return tag.values.flatMap((value: any) => { - if (typeof value !== 'string') return [] - if (value.startsWith('#')) return allIngredientChoices({ tag: value.slice(1) }, itemTags) - return [new ItemStack(Identifier.parse(value), 1)] - }) + + if (checkVersion(version, '1.21.2')) { + if (ingredient !== null) { + if (typeof ingredient === 'string') { + if (ingredient.startsWith('#')) { + return parseTag(version, ingredient.slice(1), itemTags) + } + return [new ItemStack(Identifier.parse(ingredient), 1)] + } + } + + return [new ItemStack(Identifier.create('stone'), 1)] + } else { + if (typeof ingredient === 'object' && ingredient !== null) { + if (typeof ingredient.item === 'string') { + return [new ItemStack(Identifier.parse(ingredient.item), 1)] + } else if (typeof ingredient.tag === 'string') { + return parseTag(version, ingredient.tag, itemTags) } } } + + return [] +} + +function parseTag(version: VersionId, tagId: any, itemTags: Map): ItemStack[] { + const tag: any = itemTags.get(tagId.replace(/^minecraft:/, '')) + if (typeof tag === 'object' && tag !== null && Array.isArray(tag.values)) { + return tag.values.flatMap((value: any) => { + if (typeof value !== 'string') return [] + if (value.startsWith('#')) return parseTag(version, value.slice(1), itemTags) + return [new ItemStack(Identifier.parse(value), 1)] + }) + } return [] } diff --git a/src/config.json b/src/config.json index b78a7d74..a270ba17 100644 --- a/src/config.json +++ b/src/config.json @@ -445,6 +445,15 @@ "schema": "block_tag", "wiki": "https://minecraft.wiki/w/Tag#Java_Edition" }, + { + "id": "tag/enchantment", + "url": "tags/enchantment", + "tags": ["tags"], + "path": "tags/enchantment", + "schema": "enchantment_tag", + "minVersion": "1.20.5", + "wiki": "https://minecraft.wiki/w/Tag#Java_Edition" + }, { "id": "tag/entity_type", "url": "tags/entity-type",