{"version":3,"sources":["synthesis.mjs"],"sourcesContent":["// Kubicor — synthesis recipe logic. SHARED byte-for-byte between the craft() Cloud Function (the authority) and the\n// client crafting UI (recipe matching + preview). generate-version-hash.js fails the build on drift, like deconstruct.mjs.\n//\n// A synthesis recipe combines fixed integer counts of input kubes into ONE OR MORE output kube stacks:\n//   { id, name, inputs: { kube: perCraft, … }, outputs: [ { name, count }, … ], pattern?: [ [kube|null, … ], … ] }\n// `outputs` is ALWAYS an array (even for a single product) — one shape everywhere, no singular-output fallback.\n// Recipes are AUTHORED (recipes.json); the server validates against the table so a client can't forge one. They may\n// CONDENSE (many small inputs → one denser product) — that's fine and non-exploitable, since you can never get back more\n// than a fixed recipe yields. TIER is set by how many grid SLOTS the recipe fills (TIER_MAX_INPUTS below): handheld up\n// to 4 (a 2x2), Toolbench up to 9 (3x3); there is no tier above it. A tier above handheld needs that bench\n// placed where you craft; a higher bench also covers the lower tiers.\n//\n// STATION is ORTHOGONAL to tier. Tier answers \"how MANY distinct inputs may this recipe have\" (set by the bench you\n// own); a station answers \"which MACHINE does this reaction need\" — a smelt needs a smelter whether it takes two\n// ingredients or eight. A recipe with no `station` is a hand/bench recipe; one WITH a station is craftable only at that\n// machine. The two gates are independent and BOTH must pass.\n\nimport { baseItemOf, variantsOf } from './item-ids.mjs';   // a Toolbench of any material is a Toolbench\n\nexport const BENCH_LEVELS = [null, 'Toolbench'];   // index = tier level (0 = handheld, no bench). Bench II is gone (the owner, Oct 1: nothing needed it)\n// …AND WHAT ELSE WORKS AS ONE (the furniture, Oct 19): a Workbench of any wood is a crafting surface of the Toolbench's level,\n// the same recipes on the same four slots. A kube's level is the higher of the two tables.\nexport const BENCH_ALSO = Object.freeze({ 'Workbench': 1 });\n// SLOT CRAFTING (the owner, Oct 1): no grid and no shapes. Crafting is one line of INGREDIENT SLOTS — 2 in your hands,\n// 4 at a Toolbench and at every machine — and each slot holds ONE KIND of item, 1 to CRAFT_SLOT_CAP of it, in any\n// order. So a level caps the number of KINDS a recipe may take, and nothing else.\nexport const TIER_MAX_INPUTS = [2, 4];   // KINDS per level: 0 = hands, 1 = Toolbench\n\n// The crafting MACHINES, keyed by the `station` a recipe carries. `name` is also the product kube you place/hold (so a\n// station kube resolves to its entry by name); `phase` is the short label the UI shows next to it (\"Induction Smelter:\n// Metallurgy Phase\"); `maxInputs` is the machine's own input capacity — its modal's slot count, NOT a tier cap.\nexport const STATIONS = {\n  'induction-smelter':    { id: 'induction-smelter',    name: 'Induction Smelter',    phase: 'Metallurgy Phase',       maxInputs: 4 },\n  'electrolyzer':         { id: 'electrolyzer',         name: 'Electrolyzer',         phase: 'Electrochemistry Phase', maxInputs: 4 },\n  'chemical-synthesizer': { id: 'chemical-synthesizer', name: 'Chemical Synthesizer', phase: 'Chemistry Phase',        maxInputs: 4 },\n  'quantum-assembler':    { id: 'quantum-assembler',    name: 'Quantum Assembler',    phase: 'Exotic Phase',           maxInputs: 4 },   // every machine holds 4 (Oct 1)\n};\n\n// The station a kube IS, by product name ('Induction Smelter' → the smelter entry). null for every other kube. A\n// MATERIAL VARIANT is what its base is (\"Induction Smelter (Iron)\" is a smelter): item-ids.mjs baseItemOf.\nexport function stationForKube(name) {\n  const base = baseItemOf(name);\n  for (const id in STATIONS) if (STATIONS[id].name === base) return STATIONS[id];\n  return null;\n}\n// The bench level a kube IS: 1 for any Toolbench, whatever it is made of; 0 for anything that is not a bench.\nexport function benchLevelOf(name) {\n  const b = baseItemOf(name), i = BENCH_LEVELS.indexOf(b);\n  const also = Object.prototype.hasOwnProperty.call(BENCH_ALSO, b) ? BENCH_ALSO[b] : 0;\n  return Math.max(i >= 1 ? i : 0, also);\n}\n\n// ONE ITEM PER SLOT. A grid cell holds exactly one kube — a 2x2 recipe therefore costs at most 4 items and a 3x3 at\n// most 9, and there is no such thing as \"2 Silica in each of four cells\". It used to spread a name's count across the\n// cells it occupied, which meant the drawn layout and the real price were different numbers: Toolbench drew as\n// four Silica and charged eight, and the Quantum Assembler drew nine items and charged fifty-four. Fifty-nine other\n// recipes were in the same state. The authored PATTERN was right in every one of them, so the pattern is now the\n// price, and the price is now something you can count on the screen.\n// ONE KIND PER SLOT, UP TO A HUNDRED OF IT (Oct 1). A recipe's count of any one input fits a slot, so it is at most this.\nexport const CRAFT_SLOT_CAP = 100;\n// How many items one run of a recipe takes, across its kinds (for display: \"8 Iron + 4 Carbon\" is 12).\nexport function recipeItemCount(recipe) {\n  let n = 0;\n  for (const k in (recipe.inputs || {})) n += recipe.inputs[k];\n  return n;\n}\n// Tier LEVEL a recipe needs (0 = hands), null if it fits no surface: how many KINDS it takes against TIER_MAX_INPUTS,\n// and no count above what one slot holds.\n// …OR MORE, WHEN THE RECIPE SAYS SO (`bench`, a level): a Cargo Crate is six of one wood \"at the Toolbench\" (the owner's\n// decision, Oct 11), one kind of input, which by its count alone would be made in the hands. `bench` raises the level,\n// never lowers it, so every recipe without one is exactly what it was, and the server's craft() asks for the bench the\n// same way it does for a recipe of three kinds (it reads this function).\nexport function recipeTierLevel(recipe) {\n  const ins = recipe.inputs || {}, n = Object.keys(ins).length;\n  if (n < 1) return null;\n  for (const k in ins) if (!(ins[k] >= 1 && ins[k] <= CRAFT_SLOT_CAP)) return null;\n  const min = Number.isInteger(recipe.bench) && recipe.bench > 0 && recipe.bench < TIER_MAX_INPUTS.length ? recipe.bench : 0;\n  for (let lvl = min; lvl < TIER_MAX_INPUTS.length; lvl++) if (n <= TIER_MAX_INPUTS[lvl]) return lvl;\n  return null;\n}\n\n// Highest bench level the player owns (0 if none). `owns(name)` → truthy when the player holds ≥1 of that kube.\n// `owns(name)` answers for one exact item; a bench of any material counts, so its variants are asked too.\nexport function benchLevelOwned(owns) {\n  let lvl = 0;\n  for (let i = 1; i < BENCH_LEVELS.length; i++)\n    if (owns(BENCH_LEVELS[i]) || variantsOf(BENCH_LEVELS[i]).some((v) => owns(v))) lvl = i;\n  for (const b in BENCH_ALSO) if (BENCH_ALSO[b] > lvl && (owns(b) || variantsOf(b).some((v) => owns(v)))) lvl = BENCH_ALSO[b];\n  return lvl;\n}\n\n// How many times the recipe can run given owned counts: min over inputs of floor(have / need). 0 if any input missing.\nexport function maxCrafts(recipe, countOf) {\n  let m = Infinity;\n  for (const k in recipe.inputs) { const need = recipe.inputs[k]; if (!(need > 0)) return 0; m = Math.min(m, Math.floor((countOf(k) || 0) / need)); }\n  return Number.isFinite(m) ? Math.max(0, m) : 0;\n}\n\n// ── SHAPED RECIPES ────────────────────────────────────────────────────────────────────────────────────────────────\n// A recipe MAY carry `pattern`: rows of kube names (null = empty cell) describing WHERE its inputs go in the grid. A\n// pattern matches under any of the 8 symmetries of a square (4 rotations × mirror) — the player shouldn't have to guess\n// which way round the author drew it. Recipes WITHOUT a pattern stay SHAPELESS and match exactly as they always did,\n// so patterns can be adopted one recipe at a time and the 284 bit⇄kube conversions never need one.\n//\n// Matching is by CANONICAL FORM, not by trying 8 rotations at compare time: reduce a layout to the lexicographically\n// smallest of its own 8 transforms, and two layouts are the same shape iff their canonical strings are ===. One string\n// compare instead of eight, and — because this is the shared module — the client preview and the craft() authority\n// cannot disagree about what a shape is.\n\nconst _enc = (g) => `${g.length}x${g[0].length}:${g.map(row => row.join('|')).join('/')}`;\nconst _rot = (g) => Array.from({ length: g[0].length }, (_, i) => Array.from({ length: g.length }, (_, j) => g[g.length - 1 - j][i]));   // 90° clockwise\nconst _flip = (g) => g.map(row => [...row].reverse());\n\n// Canonical form of a square grid of cells (flat, row-major, length side*side; entry = kube name, null/'' = empty).\n// TRIM TO THE BOUNDING BOX FIRST: a shape's identity is the shape, not where it sits — otherwise the same L drawn in a\n// 3x3 would be a different recipe from the L drawn in a 2x2, and nudging it one cell right would break the craft.\n// Returns '' for an empty grid.\nexport function canonicalPattern(cells, side) {\n  let r0 = side, r1 = -1, c0 = side, c1 = -1;\n  for (let r = 0; r < side; r++) for (let c = 0; c < side; c++) {\n    const v = cells[r * side + c]; if (v == null || v === '') continue;\n    if (r < r0) r0 = r; if (r > r1) r1 = r; if (c < c0) c0 = c; if (c > c1) c1 = c;\n  }\n  if (r1 < 0) return '';\n  const g = [];\n  for (let r = r0; r <= r1; r++) { const row = []; for (let c = c0; c <= c1; c++) { const v = cells[r * side + c]; row.push(v == null ? '' : v); } g.push(row); }\n  // A SYMMETRIC shape maps onto itself under some transforms, so this yields FEWER than 8 members — hence a Set and a\n  // min rather than anything that assumes a fixed count of eight.\n  const forms = new Set();\n  for (let i = 0, cur = g; i < 4; i++, cur = _rot(cur)) { forms.add(_enc(cur)); forms.add(_enc(_flip(cur))); }\n  let best = null;\n  for (const f of forms) if (best === null || f < best) best = f;\n  return best;\n}\n\n// The cell grid canonicalPattern wants, from a crafting surface's slot array (_craft.inputs: STACKS {n,c}, or plain\n// names, or null). `side` defaults the way the UI derives it — ceil(sqrt(slots)) — so a caller can't pick a different\n// grid from the one on screen. Slots past side*side simply don't exist; short arrays read as empty cells.\nexport function patternCells(slots, side) {\n  const s = side || gridSide(slots.length), cells = new Array(s * s).fill(null);\n  for (let i = 0; i < cells.length; i++) { const v = slots[i]; if (v) cells[i] = typeof v === 'string' ? v : (v.n || null); }\n  return cells;\n}\n\n// Edge of the square grid that holds n slots (2x2, 3x3, 4x4 …) — the one derivation, shared, so the grid the player\n// sees and the grid a pattern is matched against are the same grid.\nexport function gridSide(n) { return Math.max(1, Math.ceil(Math.sqrt(n || 0))); }\n\n// Canonical form of a RECIPE's authored pattern, or null if it has none (shapeless). Cached per recipe object: recipes\n// are static data and matchRecipes re-runs on every slot change, so the 8 transforms are computed once, not per click.\nconst _patKey = new WeakMap();\nexport function recipePatternKey(recipe) {\n  if (_patKey.has(recipe)) return _patKey.get(recipe);\n  const rows = recipe && recipe.pattern;\n  let key = null;\n  if (Array.isArray(rows) && rows.length) {\n    const side = Math.max(rows.length, ...rows.map(r => (r || []).length));   // a non-square drawing is embedded in a square; the trim above makes the padding invisible\n    const cells = new Array(side * side).fill(null);\n    for (let r = 0; r < rows.length; r++) for (let c = 0; c < (rows[r] || []).length; c++) cells[r * side + c] = rows[r][c] || null;\n    key = canonicalPattern(cells, side);\n  }\n  _patKey.set(recipe, key);\n  return key;\n}\n\n// Recipes whose input SET exactly equals `names` (the loaded input kube names) AND whose station is the one you're\n// standing at — the craft candidates for those inputs. Exact-set match (order-independent): you must load precisely a\n// recipe's inputs for it to apply. `currentStation` is a STATIONS id, or null/undefined for bare hands / a crafting\n// bench: a station-less recipe matches ONLY there, a station recipe ONLY at its own machine. Tier is gated separately\n// (recipeTierLevel vs the bench owned) — both gates must pass.\n// `cells` is the LAYOUT of those inputs (from patternCells) and only SHAPED recipes read it: they match when their\n// canonical pattern equals the loaded one, up to rotation/reflection. Omitting it therefore hides every shaped recipe\n// rather than waving it through — a caller that doesn't know where the kubes sit cannot prove the shape.\n// Every recipe at this place whose SET of input kinds is exactly what is loaded, in any order (Oct 1: no shapes). Several\n// can match — four Silica in the hands make a Toolbench or split into bits — and the craft row shows each one's output\n// side by side; which one is taken is the player's click. Recipe-file order, so the list is stable.\nexport function matchRecipes(names, recipes, currentStation) {\n  const key = [...new Set(names)].sort().join('|');\n  const at = currentStation || null;\n  return recipes.filter(r => (r.station || null) === at && Object.keys(r.inputs).sort().join('|') === key);\n}\n\n// ---- A CRAFT SURFACE IS USED WHERE IT STANDS (the owner, Oct 6: \"toolbench recipes require a right click on a toolbench\n// to craft them\") ----------------------------------------------------------------------------------------------------\n// A bench or machine recipe is crafted at a PLACED surface, from its own crafting row, opened by right-clicking it on\n// foot. craft() already re-read the cell to prove a surface of that kind stands there; this is the other half, that the\n// player is standing at it. The client sends, with the cell's chunk address, the cell itself in the world's grid frame\n// and where its feet are in that frame; the server then checks\n//   1. the cell agrees with its chunk address: chunk * edge + local is the cell plus the world's half-size, the SAME\n//      half-size on all three axes (the server holds no world size of its own to compare against, so the address and\n//      the coordinates have to vouch for each other);\n//   2. the feet are within CRAFT_REACH of the cell's centre.\n// Like the whereabouts gate it rides with, this is a GAMEPLAY rule, not an anti-cheat boundary (a crafted client can\n// claim any position): nothing is minted, the surface must still really stand there, and the rule is the same for all.\n// CRAFT_REACH is from the FEET: the aim reaches VOX_REACH * 1.8 = 10.8 kubes from the eye (system-view.js), the eye is\n// about 1.6 above the feet, and a cell's centre is up to 0.87 past its face; 14 leaves room for a step taken since.\n// Returns null when the surface is in reach, else why not: 'old' (a client from before this sent no position), 'bad'\n// (malformed, or the address and the coordinates disagree) or 'far'.\nexport const CRAFT_REACH = 14;\nexport function craftSurfaceReach(at, chunkEdge = 32) {\n  if (!at || typeof at !== 'object') return 'bad';\n  const v3 = (p, lim) => !!p && typeof p === 'object' && ['x', 'y', 'z'].every((k) => typeof p[k] === 'number' && Number.isFinite(p[k]) && Math.abs(p[k]) <= lim);\n  if (at.cell == null && at.pos == null) return 'old';\n  if (!v3(at.cell, 1000) || !v3(at.pos, 1000) || !['x', 'y', 'z'].every((k) => Number.isInteger(at.cell[k]))) return 'bad';\n  const cp = String(at.chunkId || '').split('_').map(Number), lp = String(at.localKey || '').split('_').map(Number);\n  if (cp.length !== 3 || lp.length < 3 || ![...cp, ...lp.slice(0, 3)].every(Number.isInteger)) return 'bad';\n  const half = ['x', 'y', 'z'].map((k, i) => cp[i] * chunkEdge + lp[i] - at.cell[k]);\n  if (!(half[0] > 0 && half[0] === half[1] && half[1] === half[2])) return 'bad';\n  const dx = at.pos.x - (at.cell.x + 0.5), dy = at.pos.y - (at.cell.y + 0.5), dz = at.pos.z - (at.cell.z + 0.5);\n  return Math.hypot(dx, dy, dz) <= CRAFT_REACH ? null : 'far';\n}\n"],"mappings":"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","names":["_"],"file":"synthesis.mjs"}