import { MATCH3D_VISUAL_SEEDS } from '../../services/match3d-runtime'; type Match3DVisualSeed = (typeof MATCH3D_VISUAL_SEEDS)[number]; export type Match3DBlockShape = | 'brick' | 'tile' | 'slope' | 'cylinder' | 'ring' | 'arch' | 'cone'; export type Match3DGeometryShape = Match3DBlockShape; export type Match3DGeometryAsset = { shape: Match3DBlockShape; fill: string; stroke: string; studsX: number; studsY: number; heightScale: number; transparent?: boolean; }; const MATCH3D_GEOMETRY_ASSETS: Record = { 'block-red-2x4': blockAsset('brick', '#e31818', '#8f1111', 4, 2, 0.72), 'block-blue-1x2': blockAsset('brick', '#1478d4', '#0b4f91', 2, 1, 0.82), 'block-yellow-2x2': blockAsset('brick', '#f7c51d', '#a66f00', 2, 2, 0.76), 'block-green-1x4': blockAsset('brick', '#079447', '#055c2f', 4, 1, 0.72), 'block-orange-1x6': blockAsset('brick', '#ff7a12', '#b84708', 6, 1, 0.64), 'block-white-1x1': blockAsset('brick', '#f3f2ec', '#b7b8b2', 1, 1, 0.86), 'block-black-1x8': blockAsset('brick', '#101214', '#030405', 8, 1, 0.54), 'block-tan-2x3': blockAsset('brick', '#d8bd72', '#9b7a35', 3, 2, 0.68), 'block-lime-1x2': blockAsset('brick', '#a5df18', '#6d990b', 2, 1, 0.58), 'block-darkred-2x2': blockAsset('brick', '#b51217', '#76090d', 2, 2, 0.7), 'block-blue-1x4': blockAsset('brick', '#1688df', '#0b5c9e', 4, 1, 0.58), 'block-pink-2x4': blockAsset('brick', '#f66bb5', '#ba2e7e', 4, 2, 0.56), 'block-gray-1x6': blockAsset('brick', '#4c5456', '#232829', 6, 1, 0.5), 'block-lavender-tile-2x2': blockAsset('tile', '#c99fe6', '#8b63ad', 2, 2, 0.28), 'block-teal-tile-1x3': blockAsset('tile', '#11adb0', '#087377', 3, 1, 0.26), 'block-mint-tile-1x4': blockAsset('tile', '#a7c6ac', '#6e9275', 4, 1, 0.24), 'block-magenta-tile-2x2': blockAsset('tile', '#cf0f68', '#8e0644', 2, 2, 0.28), 'block-orange-tile-2x2-stud': blockAsset('tile', '#ff970f', '#b65b05', 2, 2, 0.3), 'block-purple-slope-1x2': blockAsset('slope', '#5e42b6', '#342070', 2, 1, 0.82), 'block-brown-slope-1x2': blockAsset('slope', '#8b421f', '#552414', 2, 1, 0.94), 'block-sky-slope-2x2': blockAsset('slope', '#4db3f2', '#1f78b7', 2, 2, 0.9), 'block-green-cylinder': blockAsset('cylinder', '#159554', '#076236', 1, 1, 1.08), 'block-clear-ring': { ...blockAsset('ring', '#d9e1df', '#aebbbb', 2, 2, 0.38), transparent: true, }, 'block-mint-arch': blockAsset('arch', '#c4ded2', '#83a996', 4, 1, 1.0), 'block-gold-cone': blockAsset('cone', '#d39a10', '#8c6105', 1, 1, 1.18), }; const MATCH3D_UNKNOWN_GEOMETRY_ASSETS: Match3DGeometryAsset[] = [ blockAsset('brick', '#e11d48', '#9f1239', 2, 2, 0.68), blockAsset('tile', '#f59e0b', '#92400e', 3, 1, 0.28), blockAsset('slope', '#8b5cf6', '#5b21b6', 2, 1, 0.86), blockAsset('cylinder', '#10b981', '#065f46', 1, 1, 1.0), ]; const MATCH3D_UNKNOWN_VISUAL_SEEDS: Match3DVisualSeed[] = [ { itemTypeId: 'unknown-rose', visualKey: 'unknown-rose', colorClassName: 'from-rose-400 to-red-600', label: '一', }, { itemTypeId: 'unknown-amber', visualKey: 'unknown-amber', colorClassName: 'from-yellow-300 to-amber-500', label: '二', }, { itemTypeId: 'unknown-violet', visualKey: 'unknown-violet', colorClassName: 'from-violet-400 to-purple-700', label: '三', }, { itemTypeId: 'unknown-emerald', visualKey: 'unknown-emerald', colorClassName: 'from-emerald-300 to-green-600', label: '四', }, ]; function blockAsset( shape: Match3DBlockShape, fill: string, stroke: string, studsX: number, studsY: number, heightScale: number, ): Match3DGeometryAsset { return { shape, fill, stroke, studsX, studsY, heightScale, }; } export function hashVisualKey(visualKey: string) { let hash = 0; for (const char of visualKey) { hash = (hash * 31 + char.charCodeAt(0)) >>> 0; } return hash; } export function resolveVisualSeed(visualKey: string) { const knownSeed = MATCH3D_VISUAL_SEEDS.find( (seed) => seed.visualKey === visualKey, ); if (knownSeed) { return knownSeed; } return MATCH3D_UNKNOWN_VISUAL_SEEDS[ hashVisualKey(visualKey) % MATCH3D_UNKNOWN_VISUAL_SEEDS.length ]!; } export function resolveGeometryAsset(visualKey: string): Match3DGeometryAsset { return ( MATCH3D_GEOMETRY_ASSETS[visualKey] ?? MATCH3D_UNKNOWN_GEOMETRY_ASSETS[ hashVisualKey(visualKey) % MATCH3D_UNKNOWN_GEOMETRY_ASSETS.length ]! ); } function renderBlockIcon(asset: Match3DGeometryAsset) { const shapeProps = { fill: asset.fill, stroke: asset.stroke, strokeWidth: 5, strokeLinejoin: 'round' as const, opacity: asset.transparent ? 0.72 : 1, }; if (asset.shape === 'cylinder') { return ( <> ); } if (asset.shape === 'ring') { return ( <> ); } if (asset.shape === 'arch') { return ( ); } if (asset.shape === 'cone') { return ( ); } if (asset.shape === 'slope') { return ; } const width = Math.min(76, 16 + asset.studsX * 14); const height = Math.min(54, 18 + asset.studsY * 13); const x = 50 - width / 2; const y = 54 - height / 2; const studRadius = asset.shape === 'tile' ? 0 : 5; return ( <> {Array.from({ length: asset.studsX * asset.studsY }, (_, index) => { if (studRadius <= 0) { return null; } const column = index % asset.studsX; const row = Math.floor(index / asset.studsX); return ( ); })} ); } export function Match3DVisualIcon({ visualKey, className = '', }: { visualKey: string; className?: string; }) { const asset = resolveGeometryAsset(visualKey); return ( {renderBlockIcon(asset)} ); }