451 lines
19 KiB
JavaScript
451 lines
19 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { getItemEmoji } from '../utils/itemUtils';
|
|
import './ItemIcon.css';
|
|
|
|
// All textures are proxied & cached through our server
|
|
const TEXTURE_PROXY_BASE = '/api/texture';
|
|
|
|
// Items whose texture file name differs from their registry name
|
|
const TEXTURE_ALIASES = {
|
|
// Renamed items in 1.20+
|
|
grass: 'short_grass',
|
|
scute: 'turtle_scute',
|
|
// Animated items — pick a representative frame
|
|
compass: 'compass_16',
|
|
recovery_compass: 'recovery_compass_16',
|
|
clock: 'clock_22',
|
|
// Texture file named differently from item ID
|
|
magma_block: 'magma',
|
|
crossbow: 'crossbow_standby',
|
|
enchanted_golden_apple: 'golden_apple',
|
|
light: 'light_15',
|
|
// "Smooth" variants use their parent's face texture
|
|
smooth_sandstone: 'sandstone_top',
|
|
smooth_red_sandstone: 'red_sandstone_top',
|
|
smooth_quartz: 'quartz_block_bottom',
|
|
};
|
|
|
|
// CC:Tweaked texture paths (registry name → actual file in the CC repo)
|
|
const CC_TEXTURE_MAP = {
|
|
turtle_normal: 'block/turtle_normal_front',
|
|
turtle_advanced: 'block/turtle_advanced_front',
|
|
computer_normal: 'block/computer_normal_front',
|
|
computer_advanced: 'block/computer_advanced_front',
|
|
computer_command: 'block/computer_command_front',
|
|
monitor_normal: 'block/monitor_normal_0',
|
|
monitor_advanced: 'block/monitor_advanced_0',
|
|
wired_modem: 'block/wired_modem_face',
|
|
wired_modem_full: 'block/wired_modem_face',
|
|
wireless_modem_normal: 'block/wireless_modem_normal_face',
|
|
wireless_modem_advanced: 'block/wireless_modem_advanced_face',
|
|
speaker: 'block/speaker_front',
|
|
disk_drive: 'block/disk_drive_front',
|
|
printer: 'block/printer_front_empty',
|
|
cable: 'block/cable_core',
|
|
// CC item textures
|
|
pocket_computer_normal: 'item/pocket_computer_normal',
|
|
pocket_computer_advanced: 'item/pocket_computer_advanced',
|
|
disk: 'item/disk_frame',
|
|
printed_book: 'item/printed_book',
|
|
printed_page: 'item/printed_page',
|
|
printed_pages: 'item/printed_pages',
|
|
};
|
|
|
|
// Create mod — items whose textures live in nested directories
|
|
const CREATE_TEXTURE_MAP = {
|
|
// Stone types (deeply nested in palettes/)
|
|
veridium: 'block/palettes/stone_types/natural/veridium_0',
|
|
scorchia: 'block/palettes/stone_types/scorchia',
|
|
asurine: 'block/palettes/stone_types/natural/asurine_0',
|
|
crimsite: 'block/palettes/stone_types/natural/crimsite_0',
|
|
ochrum: 'block/palettes/stone_types/natural/ochrum_0',
|
|
};
|
|
|
|
// Wood types for carpet/wood/log resolution
|
|
const WOOD_TYPE_SET = new Set([
|
|
'oak', 'spruce', 'birch', 'jungle', 'acacia', 'dark_oak',
|
|
'mangrove', 'cherry', 'bamboo', 'crimson', 'warped', 'pale_oak',
|
|
]);
|
|
const DYE_COLORS = new Set([
|
|
'white', 'orange', 'magenta', 'light_blue', 'yellow', 'lime',
|
|
'pink', 'gray', 'light_gray', 'cyan', 'purple', 'blue',
|
|
'brown', 'green', 'red', 'black',
|
|
]);
|
|
|
|
/**
|
|
* Resolve item names that follow a pattern (carpets, wood, etc.)
|
|
* to their actual texture name + folder. Returns { name, isBlock } or null.
|
|
*/
|
|
function resolveDynamicAlias(name) {
|
|
// Carpets use their wool texture: white_carpet → white_wool (block/)
|
|
if (name.endsWith('_carpet')) {
|
|
const color = name.slice(0, -'_carpet'.length);
|
|
if (DYE_COLORS.has(color)) {
|
|
return { name: `${color}_wool`, isBlock: true };
|
|
}
|
|
}
|
|
|
|
// Wood (all-bark block) uses log texture: spruce_wood → spruce_log (block/)
|
|
if (name.endsWith('_wood')) {
|
|
const base = name.slice(0, -'_wood'.length);
|
|
const stripped = base.startsWith('stripped_') ? base.slice('stripped_'.length) : null;
|
|
const woodType = stripped || base;
|
|
if (WOOD_TYPE_SET.has(woodType)) {
|
|
return { name: stripped ? `stripped_${woodType}_log` : `${woodType}_log`, isBlock: true };
|
|
}
|
|
}
|
|
|
|
// Beds, banners, skulls/heads — entity-only textures, skip to emoji immediately
|
|
if (name.endsWith('_bed') && DYE_COLORS.has(name.slice(0, -'_bed'.length))) return { name: null };
|
|
if (name.endsWith('_banner') && DYE_COLORS.has(name.slice(0, -'_banner'.length))) return { name: null };
|
|
if (name.endsWith('_skull') || name.endsWith('_head')) return { name: null };
|
|
|
|
// Other entity/3D-model-only items with no flat texture
|
|
const ENTITY_ONLY = new Set([
|
|
'chest', 'ender_chest', 'trapped_chest', 'shield', 'conduit',
|
|
'bell', 'decorated_pot', 'trident',
|
|
]);
|
|
if (ENTITY_ONLY.has(name)) return { name: null };
|
|
|
|
return null;
|
|
}
|
|
|
|
// Items whose texture lives in the block/ folder instead of item/
|
|
const BLOCK_TEXTURES = new Set([
|
|
'stone', 'granite', 'polished_granite', 'diorite', 'polished_diorite', 'andesite',
|
|
'polished_andesite', 'cobblestone', 'oak_planks', 'spruce_planks', 'birch_planks',
|
|
'jungle_planks', 'acacia_planks', 'dark_oak_planks', 'mangrove_planks', 'cherry_planks',
|
|
'bamboo_planks', 'crimson_planks', 'warped_planks', 'oak_log', 'spruce_log', 'birch_log',
|
|
'jungle_log', 'acacia_log', 'dark_oak_log', 'mangrove_log', 'cherry_log', 'bamboo_block',
|
|
'stripped_oak_log', 'stripped_spruce_log', 'stripped_birch_log', 'stripped_jungle_log',
|
|
'stripped_acacia_log', 'stripped_dark_oak_log', 'stripped_mangrove_log', 'stripped_cherry_log',
|
|
'sand', 'red_sand', 'gravel', 'dirt', 'coarse_dirt', 'rooted_dirt', 'mud',
|
|
'cobblestone', 'mossy_cobblestone', 'obsidian', 'crying_obsidian',
|
|
'netherrack', 'soul_sand', 'soul_soil', 'basalt', 'polished_basalt', 'smooth_basalt',
|
|
'glowstone', 'glass', 'tinted_glass',
|
|
|
|
'bricks', 'stone_bricks', 'mossy_stone_bricks', 'cracked_stone_bricks',
|
|
'chiseled_stone_bricks', 'deepslate_bricks', 'nether_bricks', 'red_nether_bricks',
|
|
'bookshelf', 'clay', 'pumpkin', 'carved_pumpkin', 'jack_o_lantern', 'melon',
|
|
'sponge', 'wet_sponge', 'sandstone', 'red_sandstone', 'prismarine', 'dark_prismarine',
|
|
'sea_lantern', 'hay_block', 'terracotta', 'packed_ice', 'blue_ice',
|
|
'snow_block', 'ice', 'mycelium', 'podzol', 'grass_block', 'moss_block',
|
|
'deepslate', 'cobbled_deepslate', 'polished_deepslate', 'calcite', 'tuff', 'dripstone_block',
|
|
'coal_ore', 'iron_ore', 'gold_ore', 'diamond_ore', 'emerald_ore', 'lapis_ore', 'redstone_ore',
|
|
'copper_ore', 'nether_gold_ore', 'nether_quartz_ore', 'ancient_debris',
|
|
'deepslate_coal_ore', 'deepslate_iron_ore', 'deepslate_gold_ore', 'deepslate_diamond_ore',
|
|
'deepslate_emerald_ore', 'deepslate_lapis_ore', 'deepslate_redstone_ore', 'deepslate_copper_ore',
|
|
'coal_block', 'iron_block', 'gold_block', 'diamond_block', 'emerald_block',
|
|
'lapis_block', 'redstone_block', 'copper_block', 'raw_iron_block', 'raw_gold_block',
|
|
'raw_copper_block', 'netherite_block', 'amethyst_block', 'quartz_block',
|
|
'tnt', 'end_stone', 'end_stone_bricks', 'purpur_block', 'purpur_pillar',
|
|
'magma_block', 'bone_block', 'dried_kelp_block', 'honeycomb_block',
|
|
'slime_block', 'honey_block', 'note_block', 'jukebox',
|
|
'white_wool', 'orange_wool', 'magenta_wool', 'light_blue_wool', 'yellow_wool',
|
|
'lime_wool', 'pink_wool', 'gray_wool', 'light_gray_wool', 'cyan_wool',
|
|
'purple_wool', 'blue_wool', 'brown_wool', 'green_wool', 'red_wool', 'black_wool',
|
|
'white_concrete', 'orange_concrete', 'magenta_concrete', 'light_blue_concrete',
|
|
'yellow_concrete', 'lime_concrete', 'pink_concrete', 'gray_concrete',
|
|
'light_gray_concrete', 'cyan_concrete', 'purple_concrete', 'blue_concrete',
|
|
'brown_concrete', 'green_concrete', 'red_concrete', 'black_concrete',
|
|
'white_terracotta', 'orange_terracotta', 'magenta_terracotta', 'light_blue_terracotta',
|
|
'yellow_terracotta', 'lime_terracotta', 'pink_terracotta', 'gray_terracotta',
|
|
'light_gray_terracotta', 'cyan_terracotta', 'purple_terracotta', 'blue_terracotta',
|
|
'brown_terracotta', 'green_terracotta', 'red_terracotta', 'black_terracotta',
|
|
'white_glazed_terracotta', 'orange_glazed_terracotta', 'magenta_glazed_terracotta',
|
|
'light_blue_glazed_terracotta', 'yellow_glazed_terracotta', 'lime_glazed_terracotta',
|
|
'pink_glazed_terracotta', 'gray_glazed_terracotta', 'light_gray_glazed_terracotta',
|
|
'cyan_glazed_terracotta', 'purple_glazed_terracotta', 'blue_glazed_terracotta',
|
|
'brown_glazed_terracotta', 'green_glazed_terracotta', 'red_glazed_terracotta',
|
|
'black_glazed_terracotta',
|
|
'white_stained_glass', 'orange_stained_glass', 'magenta_stained_glass',
|
|
'light_blue_stained_glass', 'yellow_stained_glass', 'lime_stained_glass',
|
|
'pink_stained_glass', 'gray_stained_glass', 'light_gray_stained_glass',
|
|
'cyan_stained_glass', 'purple_stained_glass', 'blue_stained_glass',
|
|
'brown_stained_glass', 'green_stained_glass', 'red_stained_glass',
|
|
'black_stained_glass',
|
|
'crafting_table', 'furnace', 'blast_furnace', 'smoker', 'smithing_table',
|
|
'fletching_table', 'cartography_table', 'loom', 'stonecutter', 'grindstone',
|
|
'anvil', 'chipped_anvil', 'damaged_anvil', 'enchanting_table',
|
|
'brewing_stand', 'cauldron', 'composter', 'barrel',
|
|
'shulker_box', 'dispenser', 'dropper', 'hopper', 'observer',
|
|
'piston', 'sticky_piston', 'redstone_lamp', 'target', 'lever',
|
|
'beacon', 'conduit', 'lodestone', 'respawn_anchor',
|
|
'cactus', 'sugar_cane', 'bamboo',
|
|
'mushroom_stem', 'brown_mushroom_block', 'red_mushroom_block',
|
|
'oak_leaves', 'spruce_leaves', 'birch_leaves', 'jungle_leaves',
|
|
'acacia_leaves', 'dark_oak_leaves', 'mangrove_leaves', 'cherry_leaves', 'azalea_leaves',
|
|
// Trapdoors
|
|
'oak_trapdoor', 'spruce_trapdoor', 'birch_trapdoor', 'jungle_trapdoor',
|
|
'acacia_trapdoor', 'dark_oak_trapdoor', 'mangrove_trapdoor', 'cherry_trapdoor',
|
|
'bamboo_trapdoor', 'crimson_trapdoor', 'warped_trapdoor', 'iron_trapdoor',
|
|
'copper_trapdoor', 'exposed_copper_trapdoor', 'weathered_copper_trapdoor',
|
|
'oxidized_copper_trapdoor', 'pale_oak_trapdoor',
|
|
// Dirt path and magma
|
|
'dirt_path', 'magma',
|
|
// Aliased texture names that live in block/ (resolved via TEXTURE_ALIASES)
|
|
'sandstone_top', 'red_sandstone_top', 'quartz_block_bottom',
|
|
// Additional blocks commonly seen as items
|
|
'smooth_stone', 'smooth_sandstone', 'smooth_red_sandstone', 'smooth_quartz',
|
|
'chiseled_sandstone', 'cut_sandstone', 'chiseled_red_sandstone', 'cut_red_sandstone',
|
|
'quartz_pillar', 'chiseled_quartz_block', 'quartz_bricks',
|
|
'ladder', 'cobweb', 'torch', 'soul_torch', 'lantern', 'soul_lantern',
|
|
'redstone_torch', 'chain',
|
|
'rail', 'powered_rail', 'detector_rail', 'activator_rail',
|
|
'brown_mushroom', 'red_mushroom',
|
|
'oak_sapling', 'spruce_sapling', 'birch_sapling', 'jungle_sapling',
|
|
'acacia_sapling', 'dark_oak_sapling', 'cherry_sapling',
|
|
'dandelion', 'poppy', 'blue_orchid', 'allium', 'azure_bluet',
|
|
'red_tulip', 'orange_tulip', 'white_tulip', 'pink_tulip',
|
|
'oxeye_daisy', 'cornflower', 'lily_of_the_valley', 'sunflower',
|
|
'lilac', 'rose_bush', 'peony', 'wither_rose', 'torchflower',
|
|
'dead_bush', 'fern', 'short_grass', 'tall_grass', 'large_fern',
|
|
'vine', 'lily_pad', 'seagrass', 'kelp', 'hanging_roots', 'spore_blossom',
|
|
'tube_coral', 'brain_coral', 'bubble_coral', 'fire_coral', 'horn_coral',
|
|
'tube_coral_block', 'brain_coral_block', 'bubble_coral_block', 'fire_coral_block', 'horn_coral_block',
|
|
'tube_coral_fan', 'brain_coral_fan', 'bubble_coral_fan', 'fire_coral_fan', 'horn_coral_fan',
|
|
'white_concrete_powder', 'orange_concrete_powder', 'magenta_concrete_powder',
|
|
'light_blue_concrete_powder', 'yellow_concrete_powder', 'lime_concrete_powder',
|
|
'pink_concrete_powder', 'gray_concrete_powder', 'light_gray_concrete_powder',
|
|
'cyan_concrete_powder', 'purple_concrete_powder', 'blue_concrete_powder',
|
|
'brown_concrete_powder', 'green_concrete_powder', 'red_concrete_powder', 'black_concrete_powder',
|
|
'sculk', 'sculk_catalyst', 'sculk_shrieker', 'sculk_sensor', 'sculk_vein',
|
|
'mud_bricks', 'packed_mud', 'muddy_mangrove_roots',
|
|
'crimson_stem', 'warped_stem', 'stripped_crimson_stem', 'stripped_warped_stem',
|
|
'crimson_nylium', 'warped_nylium', 'shroomlight', 'nether_wart_block', 'warped_wart_block',
|
|
'crying_obsidian', 'blackstone', 'polished_blackstone', 'polished_blackstone_bricks',
|
|
'chiseled_polished_blackstone', 'gilded_blackstone', 'cracked_polished_blackstone_bricks',
|
|
'lodestone', 'respawn_anchor',
|
|
'pointed_dripstone', 'moss_carpet', 'azalea', 'flowering_azalea',
|
|
'powder_snow', 'mangrove_roots',
|
|
'copper_block', 'exposed_copper', 'weathered_copper', 'oxidized_copper',
|
|
'cut_copper', 'exposed_cut_copper', 'weathered_cut_copper', 'oxidized_cut_copper',
|
|
'waxed_copper_block', 'waxed_exposed_copper', 'waxed_weathered_copper', 'waxed_oxidized_copper',
|
|
]);
|
|
|
|
// Some blocks need a specific texture suffix (e.g. furnace_front, oak_log_top)
|
|
// We use _front, _top, or _side variants for recognizable look
|
|
const BLOCK_TEXTURE_SUFFIXES = {
|
|
furnace: '_front',
|
|
blast_furnace: '_front',
|
|
smoker: '_front',
|
|
dispenser: '_front',
|
|
dropper: '_front',
|
|
observer: '_front',
|
|
piston: '_top',
|
|
sticky_piston: '_top',
|
|
barrel: '_top',
|
|
crafting_table: '_top',
|
|
cartography_table: '_top',
|
|
fletching_table: '_top',
|
|
smithing_table: '_top',
|
|
grass_block: '_top',
|
|
mycelium: '_top',
|
|
podzol: '_top',
|
|
dirt_path: '_top',
|
|
quartz_block: '_side',
|
|
pumpkin: '_side',
|
|
carved_pumpkin: '_front',
|
|
jack_o_lantern: '_front',
|
|
jukebox: '_top',
|
|
loom: '_front',
|
|
bee_nest: '_front',
|
|
beehive: '_front',
|
|
respawn_anchor: '_top',
|
|
bone_block: '_side',
|
|
basalt: '_side',
|
|
polished_basalt: '_side',
|
|
purpur_pillar: '_side',
|
|
tnt: '_side',
|
|
composter: '_side',
|
|
enchanting_table: '_top',
|
|
};
|
|
|
|
// Resolve derivative blocks (stairs, slabs, fences, walls, buttons) to parent block texture
|
|
const WOOD_TYPES = new Set([
|
|
'oak', 'spruce', 'birch', 'jungle', 'acacia', 'dark_oak',
|
|
'mangrove', 'cherry', 'bamboo', 'crimson', 'warped',
|
|
]);
|
|
const STONE_ALIASES = {
|
|
brick: 'bricks', stone_brick: 'stone_bricks', mossy_stone_brick: 'mossy_stone_bricks',
|
|
nether_brick: 'nether_bricks', red_nether_brick: 'red_nether_bricks',
|
|
end_stone_brick: 'end_stone_bricks', deepslate_brick: 'deepslate_bricks',
|
|
deepslate_tile: 'deepslate_tiles', polished_blackstone_brick: 'polished_blackstone_bricks',
|
|
mud_brick: 'mud_bricks', quartz: 'quartz_block', purpur: 'purpur_block',
|
|
smooth_stone: 'smooth_stone',
|
|
};
|
|
|
|
function resolveDerivativeTexture(name) {
|
|
const suffixes = ['_stairs', '_slab', '_fence_gate', '_fence', '_wall', '_button', '_pressure_plate'];
|
|
for (const suffix of suffixes) {
|
|
if (!name.endsWith(suffix)) continue;
|
|
const base = name.slice(0, -suffix.length);
|
|
if (BLOCK_TEXTURES.has(base)) return base;
|
|
if (WOOD_TYPES.has(base)) return `${base}_planks`;
|
|
if (STONE_ALIASES[base]) return STONE_ALIASES[base];
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Generate texture URLs to try in order.
|
|
* - CC:Tweaked → curated texture map (1 request)
|
|
* - Create → item/ then block/ (2 requests max)
|
|
* - Unknown mods → empty (instant emoji, no wasted requests)
|
|
* - Vanilla derivatives → parent block texture (1 request)
|
|
* - Vanilla → block/ or item/ with fallback
|
|
*/
|
|
function getTextureUrls(fullItemName) {
|
|
const colonIdx = (fullItemName || '').indexOf(':');
|
|
const namespace = colonIdx >= 0 ? fullItemName.substring(0, colonIdx) : 'minecraft';
|
|
const shortName = colonIdx >= 0 ? fullItemName.substring(colonIdx + 1) : fullItemName;
|
|
const urls = [];
|
|
|
|
// CC:Tweaked → use curated texture map
|
|
if (namespace === 'computercraft') {
|
|
const mapped = CC_TEXTURE_MAP[shortName];
|
|
if (mapped) {
|
|
urls.push(`${TEXTURE_PROXY_BASE}/computercraft/${mapped}.png`);
|
|
} else {
|
|
urls.push(`${TEXTURE_PROXY_BASE}/computercraft/item/${shortName}.png`);
|
|
urls.push(`${TEXTURE_PROXY_BASE}/computercraft/block/${shortName}.png`);
|
|
}
|
|
return urls;
|
|
}
|
|
|
|
// Create mod → curated map first, then item/ then block/
|
|
if (namespace === 'create') {
|
|
const mapped = CREATE_TEXTURE_MAP[shortName];
|
|
if (mapped) {
|
|
urls.push(`${TEXTURE_PROXY_BASE}/create/${mapped}.png`);
|
|
}
|
|
urls.push(`${TEXTURE_PROXY_BASE}/create/item/${shortName}.png`);
|
|
urls.push(`${TEXTURE_PROXY_BASE}/create/block/${shortName}.png`);
|
|
return urls;
|
|
}
|
|
|
|
// Unknown mod namespace → no textures available, instant emoji fallback
|
|
if (namespace !== 'minecraft') {
|
|
return urls;
|
|
}
|
|
|
|
// === Vanilla (minecraft) ===
|
|
|
|
// Dynamic aliases: carpets→wool, wood→log, entity-only→skip
|
|
const dynamic = resolveDynamicAlias(shortName);
|
|
if (dynamic) {
|
|
if (dynamic.name === null) return urls; // entity-only → instant emoji
|
|
if (dynamic.isBlock) {
|
|
urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${dynamic.name}.png`);
|
|
return urls;
|
|
}
|
|
}
|
|
|
|
const alias = TEXTURE_ALIASES[shortName] || shortName;
|
|
|
|
// Derivative blocks (stairs, slabs, fences, walls, buttons) → parent texture
|
|
const parent = resolveDerivativeTexture(alias);
|
|
if (parent) {
|
|
const suffix = BLOCK_TEXTURE_SUFFIXES[parent] || '';
|
|
urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${parent}${suffix}.png`);
|
|
return urls;
|
|
}
|
|
|
|
// Known block → try block/ first (with suffix if applicable)
|
|
if (BLOCK_TEXTURES.has(alias)) {
|
|
const suffix = BLOCK_TEXTURE_SUFFIXES[alias] || '';
|
|
urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${alias}${suffix}.png`);
|
|
if (suffix) urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${alias}.png`);
|
|
}
|
|
|
|
// Try item/ texture
|
|
urls.push(`${TEXTURE_PROXY_BASE}/minecraft/item/${alias}.png`);
|
|
|
|
// If not a known block, also try block/ as last resort
|
|
if (!BLOCK_TEXTURES.has(alias)) {
|
|
urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${alias}.png`);
|
|
}
|
|
|
|
return urls;
|
|
}
|
|
|
|
// Cache of resolved icon URLs (avoid re-fetching on every render)
|
|
const iconCache = new Map();
|
|
|
|
/**
|
|
* Renders a Minecraft item icon using the official game textures.
|
|
* Cascading fallback: mod texture → item texture → block texture → emoji
|
|
*/
|
|
function ItemIcon({ itemName, size = 32 }) {
|
|
const [urlIndex, setUrlIndex] = useState(0);
|
|
const [allFailed, setAllFailed] = useState(false);
|
|
|
|
if (!itemName) {
|
|
return <span className="item-icon-emoji" style={{ fontSize: size * 0.7 }}>📦</span>;
|
|
}
|
|
|
|
// Use full item name as cache key (handles mod namespaces correctly)
|
|
const cacheKey = itemName.replace(/^minecraft:/, '');
|
|
|
|
// Check if we already know this item has no texture
|
|
if (iconCache.get(cacheKey) === 'none' || allFailed) {
|
|
return (
|
|
<span className="item-icon-emoji" style={{ fontSize: size * 0.7 }}>
|
|
{getItemEmoji(itemName)}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
// If we have a cached URL, use it directly
|
|
const cachedUrl = iconCache.get(cacheKey);
|
|
if (cachedUrl && cachedUrl !== 'none') {
|
|
return (
|
|
<img
|
|
className="item-icon-img"
|
|
src={cachedUrl}
|
|
alt={cacheKey}
|
|
width={size}
|
|
height={size}
|
|
loading="lazy"
|
|
/>
|
|
);
|
|
}
|
|
|
|
const urls = getTextureUrls(itemName);
|
|
const currentUrl = urls[urlIndex];
|
|
|
|
if (!currentUrl) {
|
|
iconCache.set(cacheKey, 'none');
|
|
return (
|
|
<span className="item-icon-emoji" style={{ fontSize: size * 0.7 }}>
|
|
{getItemEmoji(itemName)}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<img
|
|
className="item-icon-img"
|
|
src={currentUrl}
|
|
alt={cacheKey}
|
|
width={size}
|
|
height={size}
|
|
loading="lazy"
|
|
onLoad={() => {
|
|
iconCache.set(cacheKey, currentUrl);
|
|
}}
|
|
onError={() => {
|
|
if (urlIndex + 1 < urls.length) {
|
|
setUrlIndex(urlIndex + 1);
|
|
} else {
|
|
iconCache.set(cacheKey, 'none');
|
|
setAllFailed(true);
|
|
}
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export default ItemIcon;
|