{"version":3,"sources":["biolith.mjs"],"sourcesContent":["// BIOLITH KUBES (the owner's proposal, confirmed Oct 13: \"confirmed biolith kubes. go ahead and implement\"). A biolith\n// is a crafted building kube: a world's regolith bound with life grown in the SAME pigment, smelted into masonry. One\n// regolith pigment × one of nine patterns, \"Solis Biolith Bricks\", in that regolith's palette colour exactly (the\n// biomass's family never shows, and nothing about a biolith glows). Each is a kube and a bit, like every material\n// (\"kubes and bits for both. slabs will be constructed out of bits\").\n//\n// THE RULE IS DATA, THE RECIPES ARE WRITTEN OUT. This module says which pigments, which patterns and which biomass\n// make a biolith; tools/gen-biolith-catalogue.mjs writes the catalogue entries (products.json, permanent ids from a\n// block of their own) and one concrete recipe per pigment, biomass and pattern into recipes.json, as\n// tools/recipe-variants.mjs does for \"any wood\" and for the same reason: craft() spends a recipe's inputs BY NAME,\n// matchRecipes compares exact name sets, and every tool and test reads recipes.json as a list of real kubes. So the\n// server validates a biolith craft exactly as it validates any other (its recipes.json is the page's, byte for byte),\n// and tests/biolith.test.mjs holds the written entries to what this module derives.\n//\n// THE RECIPE (decisions recipe, bench, choose, like): at the Induction Smelter, 1 <Pigment> Regolith + 1 <Pigment>\n// biomass → 2 <Pigment> Biolith <Pattern>; the pattern is chosen at the smelter, where all nine recipes for the same\n// two inputs show side by side (synthesis.mjs matchRecipes: several recipes may share an input set, the click picks).\n// \"Like colours\" is the EXACT pigment name. BIOMASS is any material that LIFE grows (organics.mjs FAMILIES of carbon,\n// silicon or methane chemistry) in that pigment — never glow tissue (the owner: \"glow biomass shouldn't be used in\n// biolith\"), and never the world's minerals or what others left (stone, clay, rime, firn, tektite, gem, vein, sulfur,\n// sandstone, crystal, alloy, beacon, Cor). Biomass is taken as a KUBE: life mines as bits, and eight bits of one\n// material already merge into its kube by hand (its \"(merge bits)\" recipe), so bits need no recipes of their own here.\n// A biolith kube splits into eight biolith bits and eight merge back, as every material does.\n//\n// THE SIX PIGMENTS NOTHING GREW. Until world version 16, six regolith pigments had no life-grown material (Lull, Veil,\n// Haze, Ignition, Surge, Reach), so no biolith of theirs could be made. The owner's pick, \"add life that grows them\": from\n// version 16 they are variants of three families (organics.mjs FAMILIES v16: Lull Calcite, Veil and Haze Slime Lattice,\n// Ignition, Surge and Reach Petal), so all 32 pigments' bioliths can be made. The rule here never changed: a pigment's\n// bioliths exist once some life grows it, each with the id the formula gives it (BIOLITH_ID_BASE).\n//\n// Pure: no DOM, no imports beyond organics.mjs (whose own are pure), so a tool, a test and the page read one rule.\nimport { ORGANIC_MATERIALS } from './organics.mjs';\n\n// The regolith pigments, in kubes.json's order (the order the ids are issued in).\nexport const BIOLITH_PIGMENTS = Object.freeze(['Lumina', 'Gleam', 'Drift', 'Lull', 'Veil', 'Haze', 'Void', 'Ignition', 'Cinder', 'Arid',\n  'Scorched', 'Radiance', 'Nova', 'Solis', 'Corona', 'Farout', 'Starlight', 'Flash', 'Wisp', 'Spirit', 'Fathom', 'Gloam', 'Halo',\n  'Bloom', 'Verge', 'Tide', 'Aura', 'Ether', 'Surge', 'Reach', 'Glint', 'Echo']);\n\n// The nine patterns the owner kept (Oct 13), in the proposal's order. `id` keys the texture (texture-biolith.js), `noun`\n// ends the item's name, `label` is the bench's word for it, and `text` says what it looks like.\nexport const BIOLITH_PATTERNS = Object.freeze([\n  { id: 'running', noun: 'Bricks',         label: 'Running bond', text: 'bricks in courses, each course offset by half a brick' },\n  { id: 'stack',   noun: 'Stacked Bricks', label: 'Stack bond',   text: 'bricks straight above each other, the joints in a grid' },\n  { id: 'herring', noun: 'Herringbone',    label: 'Herringbone',  text: 'bricks laid in a zigzag of right angles' },\n  { id: 'basket',  noun: 'Basketweave',    label: 'Basketweave',  text: 'pairs of bricks in squares, each square turned a quarter' },\n  { id: 'flag',    noun: 'Flagstone',      label: 'Flagstone',    text: 'irregular flat stones, like a path' },\n  { id: 'tile',    noun: 'Tiles',          label: 'Square tiles', text: 'four by four square tiles' },\n  { id: 'smooth',  noun: 'Polished',       label: 'Polished',     text: 'one smooth face with soft clouding' },\n  { id: 'fluted',  noun: 'Fluted',         label: 'Fluted',       text: 'upright grooves, lit on one side' },\n  { id: 'glyph',   noun: 'Carved Kube',    label: 'Carved kube',  text: 'a framed panel with the kube carved in it, one on each face' },\n].map((p) => Object.freeze(p)));\nconst PATTERN_BY_ID = new Map(BIOLITH_PATTERNS.map((p) => [p.id, p]));\nconst PATTERN_BY_NOUN = new Map(BIOLITH_PATTERNS.map((p) => [p.noun, p]));\n\n// THE ID BLOCK (permanent, tools/item-ids.py never reuses an id): kubes from 1000, one per pigment × pattern in the\n// orders above, then their bits from 1288. 1000-1575 in all, whether or not a pigment's bioliths exist yet.\nexport const BIOLITH_ID_BASE = 1000;\nexport const BIOLITH_BIT_ID_BASE = BIOLITH_ID_BASE + BIOLITH_PIGMENTS.length * BIOLITH_PATTERNS.length;\nexport function biolithIdOf(pigment, patternId, bit = false) {\n  const i = BIOLITH_PIGMENTS.indexOf(pigment), k = BIOLITH_PATTERNS.findIndex((p) => p.id === patternId);\n  if (i < 0 || k < 0) return null;\n  return (bit ? BIOLITH_BIT_ID_BASE : BIOLITH_ID_BASE) + i * BIOLITH_PATTERNS.length + k;\n}\n\n// Like masonry (decision props): density 2.2, about one and a half times regolith's mining time, mined to itself. The\n// game prices a mine by density alone (genDensity.mjs mineTimeForDensity: 1 s at 3.05 to 5 s at 3.95, so 2.2 would be the\n// 1 s minimum), so a biolith carries its MINING density beside its own (`mineDensity`, read by densityForKube and the\n// page's mine timers): 3.89, which mines in 4.73 s, one and a half times the 3.15 s the 32 regoliths take on average\n// (tests/biolith.test.mjs works it out from kubes.json). A bit mines in an eighth of that, as every bit does.\nexport const BIOLITH_DENSITY = '2.20';\nexport const BIOLITH_MINE_DENSITY = '3.89';\nexport const BIOLITH_MINE_FACTOR = 1.5;\n\nexport const biolithName = (pigment, patternId) => `${pigment} Biolith ${PATTERN_BY_ID.get(patternId).noun}`;\nexport const biolithPattern = (id) => PATTERN_BY_ID.get(id) || null;\n\n// A name → { pigment, pattern (id), bit } when it is a biolith or a biolith's bit, else null.\nconst NAME_RE = /^(\\S+) Biolith (.+?)( Bit)?$/;\nexport function biolithOf(name) {\n  const m = typeof name === 'string' ? NAME_RE.exec(name) : null;\n  if (!m || !BIOLITH_PIGMENTS.includes(m[1])) return null;\n  const p = PATTERN_BY_NOUN.get(m[2]);\n  return p ? { pigment: m[1], pattern: p.id, bit: !!m[3] } : null;\n}\nexport const isBiolith = (name) => biolithOf(name) !== null;\n\n// BIOMASS: what life grows, by chemistry, and never glow tissue.\nexport const BIOMASS_CHEMS = Object.freeze(['carbon', 'silicon', 'methane']);\nexport const isBiomassMaterial = (m) => !!m && BIOMASS_CHEMS.includes(m.chem) && !m.glow;\nconst BIOMASS_BY_PIGMENT = new Map();\nfor (const m of ORGANIC_MATERIALS) if (isBiomassMaterial(m)) (BIOMASS_BY_PIGMENT.get(m.variant) || BIOMASS_BY_PIGMENT.set(m.variant, []).get(m.variant)).push(m.name);\n// The biomass a pigment's bioliths take, by name, in organics.mjs's order ([] for a pigment nothing grows).\nexport const biomassOf = (pigment) => (BIOMASS_BY_PIGMENT.get(pigment) || []).slice();\nexport const isBiomass = (name) => { for (const l of BIOMASS_BY_PIGMENT.values()) if (l.includes(name)) return true; return false; };\n// The pigments whose bioliths can be made: those some life grows.\nexport const biolithPigments = () => BIOLITH_PIGMENTS.filter((p) => BIOMASS_BY_PIGMENT.has(p));\n\nconst slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\nconst article = (s) => (/^[AEIOU]/.test(s) ? 'an' : 'a');\n\n// THE CATALOGUE ENTRIES, without ids (the generator gives each its block id). `regolithHex(pigment)` is that regolith's\n// catalogue colour, which the biolith wears exactly.\nexport function biolithItems(regolithHex) {\n  const out = [];\n  for (const pg of biolithPigments()) for (const p of BIOLITH_PATTERNS) {\n    const name = biolithName(pg, p.id), hex = regolithHex(pg), list = biomassOf(pg);\n    out.push({\n      id: biolithIdOf(pg, p.id), name, state: 'solid', hex, 'threejs-properties': { metalness: 0.0, roughness: 0.75 },\n      rarity: '—', density: BIOLITH_DENSITY, mineDensity: BIOLITH_MINE_DENSITY, craftOnly: true, biolith: p.id,\n      desc: `${pg} masonry, ${p.text}. Smelted at an Induction Smelter, two at a time, from 1 ${pg} Regolith and 1 ${pg} biomass ` +\n            `(${list.join(', ')}), choosing ${p.noun} there.`,\n    });\n    out.push({\n      id: biolithIdOf(pg, p.id, true), name: name + ' Bit', hex, density: BIOLITH_DENSITY, mineDensity: BIOLITH_MINE_DENSITY, bitOf: name,\n      // \"of a Solis Biolith Carved Kube kube\" would say kube twice: the one name that ends in the word drops it.\n      desc: `One eighth of ${article(name)} ${name}${/ Kube$/.test(name) ? '' : ' kube'}. Eight bits merge back into one.`,\n    });\n  }\n  return out;\n}\n\n// THE RECIPES: for each pigment some life grows, each of its biomass and each pattern, the smelt; then each biolith's\n// merge and split. `variantOf` groups one pattern's smelts as one recipe in the recipe book (its note says which).\nexport function biolithRecipes() {\n  const smelts = [], conv = [];\n  for (const pg of biolithPigments()) {\n    for (const bio of biomassOf(pg)) for (const p of BIOLITH_PATTERNS) {\n      const name = biolithName(pg, p.id);\n      smelts.push({ id: `biolith-${p.id}-${slug(bio)}`, name, inputs: { [`${pg} Regolith`]: 1, [bio]: 1 }, outputs: [{ name, count: 2 }],\n        station: 'induction-smelter', variantOf: `biolith-${p.id}`,\n        variantNote: `any of the ${biolithPigments().length} pigments: its regolith with any biomass of the same pigment, never glow tissue` });\n    }\n    for (const p of BIOLITH_PATTERNS) {\n      const name = biolithName(pg, p.id);\n      conv.push({ id: 'merge-' + slug(name), name: `${name} (merge bits)`, conversion: true, inputs: { [name + ' Bit']: 8 }, outputs: [{ name, count: 1 }] });\n      conv.push({ id: 'split-' + slug(name), name: `${name} (split to bits)`, conversion: true, inputs: { [name]: 1 }, outputs: [{ name: name + ' Bit', count: 8 }] });\n    }\n  }\n  return [...smelts, ...conv];\n}\nexport const isBiolithRecipe = (r) => !!r && typeof r.id === 'string' && (/^biolith-/.test(r.id) || (/^(merge|split)-/.test(r.id) && (r.outputs || []).some((o) => isBiolith(o && o.name))));\n"],"mappings":"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","names":[],"file":"biolith.mjs"}