{"version":3,"sources":["phase.mjs"],"sourcesContent":["// WHAT STATE A SUBSTANCE IS IN, given where it is.\n//\n// BYTE-IDENTICAL twin of functions/lib/phase.mjs; tests/phase.test.mjs fails if they drift. Both ends must agree\n// exactly, because the client decides what a placed block BECOMES and the server validates the same answer — the same\n// contract bit-slot.js, ore-veins.js and transient-shape.js already live under.\n//\n// NO IMPORTS, no THREE, no DOM: this is a table and some arithmetic, and it has to run in a node test.\n//\n// ── WHY THIS EXISTS ──────────────────────────────────────────────────────────────────────────────────────────────\n// A Methane Ice block could sit happily on an 11 degC world. Methane's critical temperature is -82.6 degC, above\n// which it cannot be liquefied at ANY pressure — so that block was not merely melting slowly, it was a solid a\n// hundred degrees past the point where the solid can exist. Meanwhile water ice on the same world would melt, because\n// 196 mbar is well above water's 6.12 mbar triple point.\n//\n// ── THE MODEL ────────────────────────────────────────────────────────────────────────────────────────────────────\n// Real phase boundaries, not a lookup of three temperature bands. Each family carries its triple point, its critical\n// temperature, its normal boiling point and the two latent heats, and the boundaries are the Clausius-Clapeyron\n// integral through those fixed points:\n//\n//     ln(P2/P1) = -(dH/R) * (1/T2 - 1/T1)      =>      T(P) = 1 / ( 1/T1 - (R/dH) * ln(P/P1) )\n//\n// Two boundaries follow from it:\n//   * BOILING, anchored at the normal boiling point (1013.25 mbar) with the enthalpy of VAPORISATION.\n//   * SUBLIMATION, anchored at the triple point with the enthalpy of SUBLIMATION (vaporisation + fusion), used only\n//     below the triple-point pressure where no liquid phase exists at all.\n// Melting is treated as pressure-independent at the triple-point temperature. That is a real approximation and a\n// good one: the melting line is near-vertical over the whole pressure range this game produces (water's is the\n// steepest and it moves about 0.01 degC per bar), and modelling it properly would need a Simon-Glatzel fit per\n// substance for a boundary nobody can observe.\n//\n// The dramatic consequence, and the reason this is a mechanic rather than a correctness fix: BELOW a substance's\n// triple-point pressure there is NO LIQUID. Ice sublimates straight to vapour. Water's triple point is 6.12 mbar, so\n// on a thin world water ice vanishes as gas, and on a thick one it melts into a puddle. Same block, same\n// temperature, different world.\n//\n// ── WHAT IS AND IS NOT MODELLED ──────────────────────────────────────────────────────────────────────────────────\n// Only the volatile families, and only the ones the catalogue actually contains. Iron, Silica, every Mineral and\n// Regolith — everything a player builds with — has no entry here and is returned unchanged at any temperature the\n// game produces. That is deliberate: a phase system that made construction materials unstable would be a chore, not\n// a mechanic. Adding a family is adding a row.\n\nexport const R_GAS = 8.314462618;          // J/(mol K)\nexport const P_STD = 1013.25;              // mbar — one standard atmosphere, where the normal boiling point is defined\nexport const K0 = 273.15;                  // 0 degC in kelvin\n\n// VACUUM IS NOT ZERO PRESSURE, for our purposes. Taken literally, the equilibrium sublimation line runs to absolute\n// zero as pressure runs to zero, so in a true vacuum every ice sublimates at every temperature — which would delete\n// every comet in the galaxy the moment this shipped. That answer is thermodynamically correct and physically absurd:\n// what keeps a real comet's ice for four billion years is not equilibrium, it is RATE. Sublimation into vacuum is\n// slow until the substance's own vapour pressure gets high enough to matter.\n//\n// So an airless body is evaluated at a small floor pressure standing in for \"the vapour pressure at which loss\n// becomes fast enough to see\" — the same reasoning behind the ice lines planetary scientists draw. 1e-3 mbar puts\n// water's vacuum survival line at about -81 degC and nitrogen's at about -241 degC, which is exactly the observed\n// ordering: water ice is stable across the outer system, nitrogen ice only on Pluto and Triton.\nexport const VACUUM_FLOOR_MBAR = 1e-3;\n\n// One row per substance. Temperatures in KELVIN and pressures in MBAR throughout the table (the game speaks degC and\n// mbar at its edges, and the conversion happens in phaseOf, not here).\n//\n// `states` names the CATALOGUE entry for each phase, or null where the game has no such kube. A null is not a gap in\n// the physics — it is a gap in the catalogue, and phaseOf handles it by returning the nearest state that does exist\n// (see nearestState). Solid oxygen is real; a Solid Oxygen kube is not, so oxygen just stays liquid when it freezes.\n//\n// Values are standard reference data (CRC / NIST), not game data. dHvap and dHfus are in J/mol.\nexport const PHASE_FAMILIES = [\n  {\n    id: 'water',\n    label: 'Water',\n    states: { solid: 'Water Ice', liquid: 'Water', gas: 'Water Vapor' },\n    // OTHER SOLIDS OF THE SAME WATER (the owner's building kubes, Oct 13): Ice Bricks are Water Ice cut, and melt where\n    // water would. Each changes state exactly as the family's own solid does, keeping its own name while it is solid.\n    alsoSolid: ['Ice Bricks'],\n    tripleK: 273.16, triplePMbar: 6.1173,\n    critK: 647.10,\n    boilK: 373.15, dHvap: 40650, dHfus: 6010,\n  },\n  {\n    id: 'methane',\n    label: 'Methane',\n    states: { solid: 'Methane Ice', liquid: 'Liquid Methane', gas: 'Methane' },\n    tripleK: 90.694, triplePMbar: 117.0,\n    critK: 190.56,\n    boilK: 111.65, dHvap: 8190, dHfus: 940,\n  },\n  {\n    id: 'ammonia',\n    label: 'Ammonia',\n    states: { solid: 'Ammonia Ice', liquid: 'Liquid Ammonia', gas: 'Ammonia Gas' },\n    tripleK: 195.40, triplePMbar: 60.9,\n    critK: 405.50,\n    boilK: 239.82, dHvap: 23350, dHfus: 5660,\n  },\n  {\n    // CO2 is the one whose triple point sits ABOVE an atmosphere — 5.18 bar — so on essentially every world in the\n    // game it sublimates straight to gas with no liquid phase, which is exactly why dry ice behaves as it does.\n    id: 'co2',\n    label: 'Carbon dioxide',\n    states: { solid: 'CO2 Ice', liquid: 'Liquid CO2', gas: 'Carbon Dioxide' },\n    tripleK: 216.58, triplePMbar: 5185.0,\n    critK: 304.13,\n    boilK: 194.69, dHvap: 15326, dHfus: 9020,   // boilK is the 1 atm SUBLIMATION point; below the triple point that is the line that matters\n  },\n  {\n    id: 'nitrogen',\n    label: 'Nitrogen',\n    states: { solid: 'Nitrogen Ice', liquid: 'Liquid Nitrogen', gas: 'Nitrogen' },\n    tripleK: 63.151, triplePMbar: 125.2,\n    critK: 126.20,\n    boilK: 77.355, dHvap: 5570, dHfus: 720,\n  },\n  {\n    id: 'oxygen',\n    label: 'Oxygen',\n    states: { solid: null, liquid: 'Liquid Oxygen', gas: 'Oxygen' },\n    tripleK: 54.361, triplePMbar: 1.463,\n    critK: 154.58,\n    boilK: 90.188, dHvap: 6820, dHfus: 440,\n  },\n  {\n    id: 'chlorine',\n    label: 'Chlorine',\n    states: { solid: null, liquid: 'Liquid Chlorine', gas: 'Chlorine' },\n    tripleK: 172.17, triplePMbar: 13.9,\n    critK: 416.90,\n    boilK: 239.11, dHvap: 20410, dHfus: 6406,\n  },\n  {\n    id: 'argon',\n    label: 'Argon',\n    states: { solid: null, liquid: 'Liquid Argon', gas: 'Argon Gas' },\n    tripleK: 83.806, triplePMbar: 688.9,\n    critK: 150.69,\n    boilK: 87.302, dHvap: 6430, dHfus: 1180,\n  },\n];\n\n// name -> { family, state } for every catalogue name any family claims. Built once; the table is 8 rows.\nconst _BY_NAME = new Map();\nfor (const f of PHASE_FAMILIES) {\n  for (const st of ['solid', 'liquid', 'gas']) {\n    const n = f.states[st];\n    if (n) _BY_NAME.set(n, { family: f, state: st });\n  }\n  for (const n of f.alsoSolid || []) _BY_NAME.set(n, { family: f, state: 'solid' });\n}\n\n// Is this name something that changes state at all? Everything a player builds with answers false.\nexport function isPhaseMaterial(name) { return _BY_NAME.has(name || ''); }\nexport function familyOf(name) { const e = _BY_NAME.get(name || ''); return e ? e.family : null; }\nexport function stateOfName(name) { const e = _BY_NAME.get(name || ''); return e ? e.state : null; }\nexport function phaseFamilyNames() { return [..._BY_NAME.keys()]; }\n\n// Clausius-Clapeyron, solved for the temperature at which a boundary sits at pressure P.\n// T(P) = 1 / ( 1/T1 - (R/dH) ln(P/P1) ). Guarded so an absurd pressure cannot produce a negative or infinite kelvin.\nfunction ccTempK(pMbar, refK, refPMbar, dH) {\n  if (!(pMbar > 0) || !(dH > 0)) return refK;\n  const inv = 1 / refK - (R_GAS / dH) * Math.log(pMbar / refPMbar);\n  if (!(inv > 0)) return Infinity;            // pressure so high the boundary runs off the top — treat as never boiling\n  return 1 / inv;\n}\n\n// The boiling line: liquid <-> gas, anchored at the normal boiling point.\nexport function boilingK(family, pMbar) { return ccTempK(pMbar, family.boilK, P_STD, family.dHvap); }\n// The sublimation line: solid <-> gas, anchored at the TRIPLE point and using the enthalpy of sublimation. Only\n// meaningful below the triple-point pressure, which is the only place it is consulted.\nexport function sublimationK(family, pMbar) {\n  return ccTempK(pMbar, family.tripleK, family.triplePMbar, family.dHvap + family.dHfus);\n}\n\n// The pressure a phase boundary should be evaluated at. Airless bodies get the vacuum floor, not zero — see\n// VACUUM_FLOOR_MBAR. A body with an atmosphere thinner than the floor is treated as airless for the same reason.\nexport function envPressure(env) {\n  if (env && env.airless) return VACUUM_FLOOR_MBAR;\n  return Math.max(VACUUM_FLOOR_MBAR, (env && env.pressureMbar) || 0);\n}\n\n// The physics answer, ignoring what the catalogue happens to contain: 'solid' | 'liquid' | 'gas'.\nexport function idealState(family, tempC, pressureMbar) {\n  const T = (tempC ?? -273.15) + K0;\n  const P = Math.max(0, pressureMbar || 0);\n  // Above the critical temperature there is no liquid at any pressure. This is the whole methane case: at 11 degC\n  // methane is 94 degC past critical, so no amount of pressure makes it anything but a gas.\n  if (T >= family.critK) return 'gas';\n  // Below the triple-point pressure the liquid phase does not exist — the solid sublimates directly.\n  if (P < family.triplePMbar) return T < sublimationK(family, P) ? 'solid' : 'gas';\n  if (T < family.tripleK) return 'solid';\n  return T < boilingK(family, P) ? 'liquid' : 'gas';\n}\n\n// The state the CATALOGUE can actually represent, walking toward the ideal one. A family with no solid kube freezes\n// only as far as its liquid; a family with no gas kube boils only as far as its liquid. Nothing is ever returned that\n// the game cannot name.\nconst ORDER = ['solid', 'liquid', 'gas'];\nfunction nearestState(family, want) {\n  const i = ORDER.indexOf(want);\n  if (family.states[want]) return want;\n  for (let d = 1; d < ORDER.length; d++) {                    // step back toward the middle, then past it\n    const a = ORDER[i - d], b = ORDER[i + d];\n    if (a && family.states[a]) return a;\n    if (b && family.states[b]) return b;\n  }\n  return null;\n}\n\n// ── THE ONE CALL ─────────────────────────────────────────────────────────────────────────────────────────────────\n// What this name becomes in this environment. Returns the catalogue name to use, which is `name` itself when nothing\n// changes — the overwhelmingly common answer, since only the eight volatile families are in the table at all.\n//\n// env is the shape hazard-engine.js already consumes: { tempC, pressureMbar, airless }. An AIRLESS body is vacuum:\n// pressure 0, so every family is below its triple point and a solid either holds or sublimates.\nexport function phaseOf(name, env) {\n  const entry = _BY_NAME.get(name || '');\n  if (!entry) return name || null;                            // not a phase material — unchanged, always\n  const tempC = env && isFinite(env.tempC) ? env.tempC : -273.15;\n  const want = nearestState(entry.family, idealState(entry.family, tempC, envPressure(env)));\n  if (want === entry.state) return name;                       // still as it is: an Ice Brick that holds is an Ice Brick\n  return want ? entry.family.states[want] : null;\n}\n\n// Did this name change, and how? For the UI, which wants to say WHY rather than silently swap a block.\n// Returns null when nothing happens, else { from, to, fromState, toState, disperses }.\n// `disperses` marks the case the world cannot hold: a solid or liquid that became a GAS is not placed anywhere, it\n// is simply gone — which is what happens when you put methane ice down on a warm world.\nexport function phaseChange(name, env) {\n  const entry = _BY_NAME.get(name || '');\n  if (!entry) return null;\n  const to = phaseOf(name, env);\n  if (!to || to === name) return null;\n  const toState = stateOfName(to);\n  return { from: name, to, fromState: entry.state, toState, disperses: toState === 'gas', family: entry.family.id };\n}\n\n// A human sentence for why. The UI shows this once, at the moment it happens, so it has to carry the actual number\n// that decided it rather than a generic \"too hot\".\nexport function phaseReason(name, env) {\n  const entry = _BY_NAME.get(name || '');\n  if (!entry) return '';\n  const f = entry.family;\n  const tempC = env && isFinite(env.tempC) ? env.tempC : -273.15;\n  const P = envPressure(env);\n  const c = (k) => Math.round((k - K0) * 10) / 10;\n  if (tempC + K0 >= f.critK) return f.label + ' cannot be solid or liquid above ' + c(f.critK) + ' °C at any pressure';\n  if (P < f.triplePMbar) {\n    const s = sublimationK(f, P);\n    return (env && env.airless)\n      ? f.label + ' sublimates above ' + c(s) + ' °C in vacuum'\n      : f.label + ' has no liquid phase below ' + Math.round(f.triplePMbar * 10) / 10 + ' mbar; it sublimates above ' + c(s) + ' °C';\n  }\n  if (tempC + K0 < f.tripleK) return f.label + ' freezes below ' + c(f.tripleK) + ' °C';\n  // Between the melting and boiling lines the answer is LIQUID, and the sentence has to name the boundary the\n  // material actually crossed. Reporting the boiling point here described a transition that did not happen — a\n  // block that melted was explained as though it had boiled.\n  if (tempC + K0 < boilingK(f, P)) return f.label + ' melts above ' + c(f.tripleK) + ' °C and is liquid at ' + Math.round(P) + ' mbar';\n  return f.label + ' boils above ' + c(boilingK(f, P)) + ' °C at ' + Math.round(P) + ' mbar';\n}\n\n// ── WHAT A WORLD'S BASINS CAN HOLD ───────────────────────────────────────────────────────────────────────────────\n// The kind of sea a rocky world's terrain may lay down (transient-shape.js, ROCKY SEAS), decided by the same model that\n// decides every stored block, at the world's AVERAGE temperature and pressure — the world's one temperature, all day\n// and all year (the owner, Oct 12), so a sea is ice or water for good — and never a comet's, which has no basins. The terrain generator knows\n// nothing about temperature; it is handed the answer this gives.\n//\n//   'liquid'  the first of Water, Liquid Methane and Liquid Nitrogen that is LIQUID there. Water where it is between its\n//             melting and boiling lines at that pressure, which needs air above its 6 mbar triple point; methane and\n//             nitrogen only on cold worlds with air thicker than theirs (117 and 125 mbar): Titan's case. None of the\n//             three is ever liquid on an airless world: vacuum is below every triple point, so a liquid there boils.\n//   'ice'     otherwise, where water is SOLID: a frozen sea of Water Ice. That includes airless worlds colder than\n//             about -81 °C, where water ice holds even in vacuum (VACUUM_FLOOR_MBAR), which is Europa's case.\n//   'lava'    at LAVA_SEA_FROM_C and above, the engine's own line for a lava world (system-engine.js FIRST_GAP; world-look.mjs\n//             draws a lava crust from the same line). There is no Lava kube, so `name` is null: see the note below.\n//   null      nothing can lie there: water is a gas (too hot, or too little air for how warm it is), and so is the rest.\n// The order is the preference where two could lie: a world with liquid methane on water ice holds methane seas.\n//\n// LAVA IS NOT IN THE CATALOGUE. Molten rock has no kube, no phase family and no item, so a lava sea is drawn and nothing\n// else: nothing can be scooped, credited or placed from it. Giving it a kube is a content decision (what it is made of,\n// what a bucket of it does, what it does to a suit), and the rest of the game follows from that one line in kubes.json.\nexport const LAVA_SEA_FROM_C = 700;\nexport const SEA_LIQUID_FAMILIES = Object.freeze(['water', 'methane', 'nitrogen']);\nexport function worldSeaOf(env) {\n  if (!env) return null;\n  const tempC = env.tempC;\n  if (typeof tempC !== 'number' || !isFinite(tempC)) return null;\n  // A lava sea carries the temperature it was read at: from version 3 how much of a world is molten follows it\n  // (transient-shape.js VERSION 3 SEAS). Version 2 reads only the kind.\n  if (tempC >= LAVA_SEA_FROM_C) return { kind: 'lava', name: null, family: null, tempC };\n  const P = envPressure(env);\n  for (const id of SEA_LIQUID_FAMILIES) {\n    const f = PHASE_FAMILIES.find((x) => x.id === id);\n    if (idealState(f, tempC, P) === 'liquid') return { kind: 'liquid', name: f.states.liquid, family: id };\n  }\n  const water = PHASE_FAMILIES.find((x) => x.id === 'water');\n  if (idealState(water, tempC, P) === 'solid') return { kind: 'ice', name: water.states.solid, family: 'water' };\n  return null;\n}\n\n// ── WORLD GENERATION ─────────────────────────────────────────────────────────────────────────────────────────────\n// A comet must not be MADE of an ice that cannot survive its own orbit. pickIce drew from all five ice kubes with no\n// temperature gate at all, so a comet whose perihelion takes it inside the habitable zone could be built of Nitrogen\n// Ice — which sublimates at -241 degC in vacuum.\n//\n// The gate is the PERIHELION temperature, not the current one, and that choice is load-bearing. A comet's crust pair\n// is seeded by its address and cached, and the SERVER re-derives the same pair to decide what a mined cell credits\n// (transient-shape.js is byte-identical on both sides for exactly this reason). A pool that depended on where the\n// comet is right now would give a different answer every minute and the two ends would disagree mid-flight. The\n// perihelion is a fixed property of the orbit, so the answer is stable for the whole pass.\n//\n// Cometary nuclei are among the darkest objects known — 67P reflects about 6% — so a fixed albedo is used rather than\n// the per-body crust colour. That keeps this a pure function of (luminosity, perihelion) with no dependency on the\n// rendering catalogue, which is what lets both ends compute it without sharing anything else.\nexport const COMET_ALBEDO = 0.07;\nexport const SIGMA_SB = 5.670e-8;          // W/(m^2 K^4)\nexport const SOLAR_CONSTANT = 1361;        // W/m^2 at the habitable-zone centre\nexport const HZ_BASE = 220;                // world units to the HZ centre — matches system-engine.js\n\n// Equilibrium temperature (degC) of a dark body at perihelion distance q from a star of this luminosity.\nexport function perihelionTempC(luminosity, q) {\n    if (!(q > 0)) return -273.15;\n    const S = SOLAR_CONSTANT * (luminosity ?? 1) * Math.pow(HZ_BASE / q, 2);\n    return Math.pow((S * (1 - COMET_ALBEDO)) / (4 * SIGMA_SB), 0.25) - K0;\n}\n\n// How hot this substance can get in vacuum and still be a solid. Used to rank the ice pool when too little of it\n// survives — a comet always needs two crust materials, so the answer is never \"nothing\".\nexport function vacuumHoldC(name) {\n    const f = familyOf(name);\n    if (!f || !f.states.solid) return Infinity;      // not a phase material: it holds at any temperature\n    return sublimationK(f, VACUUM_FLOOR_MBAR) - K0;\n}\n\n// The ice pool a comet at this perihelion can actually be built from. Always returns at least two entries: if fewer\n// than two survive, the two most refractory are kept, because a comet with one crust material is a rendering bug and\n// a comet with none is not a comet. Order is preserved so the rarity-weighted draw downstream is unchanged.\nexport function cometIcePool(icePool, perihelionC) {\n    const pool = (icePool || []).filter((k) => k && k.name);\n    if (pool.length <= 2) return pool;\n    const survives = pool.filter((k) => vacuumHoldC(k.name) > perihelionC);\n    if (survives.length >= 2) return survives;\n    const ranked = pool.slice().sort((a, b) => vacuumHoldC(b.name) - vacuumHoldC(a.name));\n    const keep = new Set(ranked.slice(0, 2).map((k) => k.name));\n    return pool.filter((k) => keep.has(k.name));     // original order, so the draw is unchanged\n}\n"],"mappings":"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","names":[],"file":"phase.mjs"}