function evaluateWhen(properties, condition) { if (!condition) return true; if (condition.OR) return condition.OR.some(sub => evaluateWhen(properties, sub)); if (condition.AND) return condition.AND.every(sub => evaluateWhen(properties, sub)); for (const [key, val] of Object.entries(condition)) { if (key === 'OR' || key === 'AND') continue; const prop = properties[key]; if (typeof val === 'string' && val.includes('|')) { if (!val.split('|').includes(prop)) return false; } else if (prop !== val) { return false; } } return true; } export function resolveBlock(state, properties) { const parts = []; if (state.variants) { // Handle standard variants const propStr = Object.keys(properties).sort().map(k => `${k}=${properties[k]}`).join(','); let variantDef = state.variants[propStr] || state.variants[""] || state.variants["normal"]; // --- NEW ERROR REPORTING --- if (!variantDef) { console.warn("Available states:", Object.keys(state.variants)); throw new Error( `Failed to resolve block variant.\nRequested: "${propStr}"\nCheck the console for available states.` ); } if (Array.isArray(variantDef)) variantDef = variantDef[0]; // Take first random model parts.push(variantDef); } else if (state.multipart) { // Handle multipart for (const part of state.multipart) { if (evaluateWhen(properties, part.when)) { let applyDef = part.apply; if (Array.isArray(applyDef)) applyDef = applyDef[0]; parts.push(applyDef); } } if (parts.length === 0) { console.warn("Possibly malformed multipart block with no parts.") } } return parts; } export function getVariantHash(part) { // Uniquely identifies a baked geometry variant so we can pool instances return `${part.model}#y=${part.y || 0},x=${part.x || 0},uvlock=${!!part.uvlock}`; }