{"version":3,"sources":["vessel-life.mjs"],"sourcesContent":["// VESSELS: THE POWER POOL AND THE SUIT DOCKS. Phase 2 was \"living aboard\" (sealed rooms, the airlock, the Air Recycler);\n// the owner, Oct 13: \"I want to eliminate the concept of insides. They should only be viewable on their exterior, sort of\n// like gas giants or asteroids/comets (without mining mode)\". So a vessel has NO INSIDES: what was a room is geometry, the\n// Air Recycler and the Airlock Door are gone from the game (their item ids stay retired: item-ids.mjs RETIRED_ITEMS), and\n// with them the rooms, their air, the chambers, breaches, leaks and the recyclers' draw on the pool. What is left here:\n//\n//   POWER     Placed Solar Panels make sunAt(flux) units each: 1 at the habitable distance, up to SOLAR_MAX near the star,\n//             falling with the square of the distance outward, no eclipse. Placed Batteries store BATTERY_STORE unit-minutes\n//             each. ONE pool per vessel, no cables. Docks draw lumps from what is stored; a ship's EM drives draw on it\n//             (ship.mjs). Nothing else draws on it now, so it only fills between those.\n//   DOCK      A Suit Dock (1×2 upright). ON A VESSEL it stands on the OUTSIDE of the hull: the cell in front of its base and\n//             the one above that are open space reached from beyond the grid (exteriorDock). Docking at one is how a player\n//             rides a vessel: docked = aboard (users/{uid}.docked, and their whereabouts are the vessel), and only docked\n//             players travel with a ship. It recharges the suit and refills its O₂ from the pool, and is where a death\n//             comes back to, docked there again. The owner and builders use it, anyone when its owner made it public.\n//             On a WORLD (no pool there): a respawn point always, and O₂ and energy from a small bank of its own that Solar\n//             Panels touching it fill (worldDockBank); a world's dock may stand anywhere.\n//   RESPAWN   Last dock used (if still usable: still there, still on the outside), then Set home (if still allowed: on a\n//             vessel, docked at its nearest usable outside dock), then the first world (respawnPick). The server walks it.\n//   LIQUIDS   Never poured onto a vessel (LIQUIDS_ABOARD): there is no gravity to hold a pour.\n//\n// Byte-identical in public/ and functions/lib/ (generate-version-hash.js holds them equal), and import-free so the same\n// bytes resolve in both. The server decides with it (functions/vessel-life.js, ships.js and submitEdits); the page draws\n// with it. Pure, so tests/vessel-life.test.mjs pins every rule without a server or a renderer.\n//\n// ─── THE DATA ──────────────────────────────────────────────────────────────────────────────────────────────────────────\n// What a vessel IS is its blocks (world_deltas/{addr}/chunks/0_0_0: the whole 32³ grid is that one chunk) and the public\n// lines in that chunk (`m`): a Suit Dock's { k: 'k', p: 1 } while public, a Cargo Crate's gauge. What is structural — the\n// docks and whether each faces space, the panels and batteries — is DERIVED from those by vesselModel, the same on both\n// sides. What time changes is the small record the server keeps on the vessel, vessels/{id}.life (readLife):\n//   { v: 1, at: ms, sun: units a panel makes here, charge: unit-minutes stored, rev }\n// The record describes the moment `at`; advanceLife carries it to any later moment in closed form, so the page draws the\n// same charge the server would compute, with nothing ticking anywhere. (A record written before Oct 13 also carries\n// `rooms`, which readLife drops.)\n\n// ---- THE PARTS ---------------------------------------------------------------------------------------------------------\nexport const SUIT_DOCK = 'Suit Dock';\nexport const SUIT_DOCK_UPPER = 'Suit Dock Upper';\nexport const SOLAR_PANEL = 'Solar Panel';\nexport const BATTERY = 'Battery';\nexport const DOCKING_PORT = 'Docking Port';\n// RETIRED Oct 13 with the insides: never placed, crafted or held again (tools/vessels-exterior-migrate.mjs refunds any\n// that were), their ids never issued again (tools/item-ids.py RETIRED).\nexport const RETIRED_PARTS = Object.freeze(['Air Recycler', 'Airlock Door', 'Airlock Door Upper']);\nexport const isRetiredPart = (n) => RETIRED_PARTS.includes(n);\n// OPEN WORK: kubes space passes through (building-kubes.mjs: the Steel Grate), bars with holes between. A dock behind a\n// grate still faces space, but nobody stands in a grate.\nexport const OPEN_WORK = Object.freeze(['Steel Grate', 'Steel Grate Bit']);\nexport const isOpenWork = (n) => OPEN_WORK.includes(n);\n// THE TALL PART (1×2 upright, the owner's dock shape): placed as its base, which the server pairs with its UPPER half in\n// the cell above (the vessel's +Y; a world's outward up), and broken as a pair, one item back.\n// THE DOORS (doors.mjs, Oct 20) are tall parts too: every Door, Glass Door and Double Door leaf stands on one shared\n// Door Upper (its panels are drawn from the base, so the upper half needs no look of its own). An upper half's base is\n// then read from the cell below it, never from the upper's name: upperFits says whether that cell's part is its base.\n// (Listed here rather than imported, so this file stays importless: doors.mjs keeps the same families, and\n// tests/doors.test.mjs holds the two to one set.)\nconst DOOR_UPPER = 'Door Upper';\nconst DOOR_WOODS = ['Verge Wood', 'Bloom Wood', 'Tide Wood', 'Glint Wood', 'Solis Wood', 'Scorched Wood', 'Void Wood', 'Gloam Wood', 'Fathom Wood', 'Drift Wood', 'Halo Wood', 'Echo Wood'];\nconst DOOR_METALS = ['Iron', 'Aluminum', 'Copper', 'Titanium', 'Steel'];\nexport const TALL_DOORS = Object.freeze([['Door', [...DOOR_WOODS, ...DOOR_METALS]], ['Glass Door', DOOR_METALS], ['Double Door', [...DOOR_WOODS, ...DOOR_METALS]]]\n  .flatMap(([h, ms]) => [h, ...ms.map((m) => `${h} (${m})`)]));\n// THE LOCKER (the owner, Oct 21: the Wardrobe became the Locker, \"1 kube wide × 2 high (base + upper, like doors/tall\n// parts)\", of any of the 23 woods or the 5 structural metals): every Locker stands on one shared Locker Upper, which wears\n// its base's material (system-view.js reads the base below it). tests/storage-oct21.test.mjs holds this list to the catalogue.\nconst LOCKER_UPPER = 'Locker Upper';\n// …and version 23's nine barks' woods (the owner's Tree Light and Bark picks, Oct 23, w_items: the whole item family).\nconst LOCKER_WOODS = [...DOOR_WOODS, 'Lumina Wood', 'Gleam Wood', 'Starlight Wood', 'Farout Wood', 'Ether Wood', 'Wisp Wood', 'Flash Wood',\n  'Spirit Wood', 'Nova Wood', 'Aura Wood', 'Cinder Wood', 'Arid Wood', 'Radiance Wood', 'Corona Wood', 'Lull Wood', 'Veil Wood', 'Haze Wood',\n  'Ignition Wood', 'Surge Wood', 'Reach Wood'];\nexport const TALL_LOCKERS = Object.freeze(['Locker', ...[...LOCKER_WOODS, ...DOOR_METALS].map((m) => `Locker (${m})`)]);\nexport const TALL = Object.freeze({ [SUIT_DOCK]: SUIT_DOCK_UPPER, ...Object.fromEntries(TALL_DOORS.map((n) => [n, DOOR_UPPER])),\n  ...Object.fromEntries(TALL_LOCKERS.map((n) => [n, LOCKER_UPPER])) });\nconst UPPER_BASE = Object.freeze({ [SUIT_DOCK_UPPER]: SUIT_DOCK, [DOOR_UPPER]: 'Door', [LOCKER_UPPER]: 'Locker' });\nexport const isTallBase = (n) => Object.prototype.hasOwnProperty.call(TALL, n);\nexport const isTallUpper = (n) => Object.prototype.hasOwnProperty.call(UPPER_BASE, n);\nexport const isTallPart = (n) => isTallBase(n) || isTallUpper(n);\nexport const upperOf = (n) => (isTallBase(n) ? TALL[n] : null);\nexport const baseOfUpper = (n) => (isTallUpper(n) ? UPPER_BASE[n] : null);\nexport const upperFits = (upper, base) => isTallBase(base) && TALL[base] === upper;   // is `base` (the cell below) this upper's base?\nexport const isDockName = (n) => n === SUIT_DOCK || n === SUIT_DOCK_UPPER;\n// The parts a page opens with a right-click (use), not a craft panel: the dock.\nexport const isLifePart = (n) => isDockName(n);\n\n// ---- THE NUMBERS -----------------------------------------------------------------------------------------------------\nexport const SOLAR_MAX = 2;             // a panel near its star makes at most twice what it makes at the habitable distance\nexport const BATTERY_STORE = 60;        // unit-minutes a placed Battery holds: an hour of one panel's output\nexport const DOCK_ENERGY_COST = 0.1;    // unit-minutes per unit of suit energy a dock puts back\nexport const DOCK_O2_COST = 2;          // unit-minutes for a whole tank of O₂ (five minutes of breathing)\nexport const WORLD_DOCK_BANK = 10;      // unit-minutes a world dock holds of its own, with no Battery touching it\n\n// ---- WORDS -------------------------------------------------------------------------------------------------------------\nexport const LIQUIDS_ABOARD = 'Liquids stay in their containers on a vessel: there is no gravity to hold a pour.';\nexport const DOCK_DENIED = 'Only the owner and builders can use this dock, unless its owner makes it public.';\nexport const TALL_NO_ROOM = 'This needs two kubes of room, one above the other.';\nexport const DOCK_INSIDE = 'A Suit Dock on a vessel goes on its outside: the two kubes in front of it have to open onto space.';\nexport const DOCK_WALLED = 'That dock is walled in: the two kubes in front of it no longer open onto space.';\nexport const NO_DOCK = 'No suit dock stands there now.';\n\n// ---- THE GRID ----------------------------------------------------------------------------------------------------------\n// 32 on a side, -16..15 (vessels.mjs VESSEL_EDGE), so every cell is in chunk 0_0_0 with local key `${x+16}_${y+16}_${z+16}`.\nexport const HALF = 16;\nconst EDGE = 32, N3 = EDGE * EDGE * EDGE;\nexport const cellIdx = (x, y, z) => (x + HALF) + (y + HALF) * EDGE + (z + HALF) * EDGE * EDGE;\nexport const idxCell = (i) => [(i & 31) - HALF, ((i >> 5) & 31) - HALF, ((i >> 10) & 31) - HALF];\nexport const inGrid = (x, y, z) => x >= -HALF && x < HALF && y >= -HALF && y < HALF && z >= -HALF && z < HALF;\nexport const cellKeyOf = (x, y, z) => `${x + HALF}_${y + HALF}_${z + HALF}`;\nexport function keyCell(lk) {\n  const p = String(lk).split('_');\n  if (p.length !== 3 && p.length !== 4) return null;\n  const v = p.map(Number);\n  if (!v.slice(0, 3).every((n) => Number.isInteger(n) && n >= 0 && n < EDGE)) return null;\n  return { x: v[0] - HALF, y: v[1] - HALF, z: v[2] - HALF, slot: p.length === 4 ? v[3] : null };\n}\n// UP on a vessel is its own +Y (\"down is down\"); on a world it is the outward axis of the cell (bit-slot.js cellUpFace), the\n// rule a world's tall part stands by. Returned as { a: axis 0..2, s: ±1 }, turf.mjs upOf's shape.\nexport function cellUp(x, y, z, vessel) {\n  if (vessel) return { a: 1, s: 1 };\n  const cx = x + 0.5, cy = y + 0.5, cz = z + 0.5, ax = Math.abs(cx), ay = Math.abs(cy), az = Math.abs(cz);\n  const a = (ax >= ay && ax >= az) ? 0 : (ay >= az ? 1 : 2);\n  return { a, s: (a === 0 ? cx : a === 1 ? cy : cz) >= 0 ? 1 : -1 };\n}\nexport function upperCellOf(x, y, z, vessel) { const u = cellUp(x, y, z, vessel), c = [x, y, z]; c[u.a] += u.s; return c; }\nexport function lowerCellOf(x, y, z, vessel) {\n  // The base under an upper half: the cell whose up step lands here. On a vessel that is simply y - 1; on a world the up\n  // axis of the cell below decides, which across a cube's edge may differ from this cell's own.\n  if (vessel) return [x, y - 1, z];\n  for (const a of [0, 1, 2]) for (const s of [1, -1]) {\n    const c = [x, y, z]; c[a] -= s;\n    const u = cellUp(c[0], c[1], c[2], false);\n    if (u.a === a && u.s === s) return c;\n  }\n  return null;\n}\n\n// ---- READING THE BLOCKS --------------------------------------------------------------------------------------------------\n// A cell is OPEN (0: nothing in it), SOLID (1: a whole kube or machine) or PARTIAL (2: bits, a slab, open work, a fluid:\n// space passes it, nobody stands in it). `blocks` is the chunk's map local key → name (decoded; null = broken), `sums` its\n// public lines. opts: isBit(name) (default: names ending \" Bit\"), isFluid(name) (a liquid or a gas kube), isSlab(name).\nexport const dockPublicIn = (sums, baseKey) => !!(sums && sums[baseKey] && sums[baseKey].k === 'k' && sums[baseKey].p);\nexport function readCells(blocks, sums, opts = {}) {\n  const isBit = opts.isBit || ((n) => / Bit$/.test(n));\n  const isFluid = opts.isFluid || (() => false);\n  const isSlab = opts.isSlab || ((n) => n === SOLAR_PANEL);\n  const kind = new Uint8Array(N3), names = new Map(), docks = [];\n  let panels = 0, batteries = 0;\n  const bitCells = [];\n  for (const lk in (blocks || {})) {\n    const v = blocks[lk];\n    if (typeof v !== 'string') continue;\n    const c = keyCell(lk);\n    if (!c) continue;\n    const i = cellIdx(c.x, c.y, c.z);\n    if (c.slot != null) { if (Number.isInteger(c.slot) && c.slot >= 0 && c.slot < 27) bitCells.push(i); continue; }\n    names.set(i, v);\n    if (isRetiredPart(v)) continue;                                      // a retired part not yet migrated: drawn as nothing\n    if (isFluid(v) || isOpenWork(v) || isBit(v)) { kind[i] = 2; continue; }\n    if (isSlab(v)) { if (v === SOLAR_PANEL) panels++; kind[i] = 2; continue; }\n    if (v === BATTERY) batteries++;\n    if (v === SUIT_DOCK) docks.push({ base: i, cell: [c.x, c.y, c.z], key: lk, pub: dockPublicIn(sums, lk) });\n    kind[i] = 1;                                                         // every other kube and machine: the whole cell\n  }\n  for (const i of bitCells) if (kind[i] === 0) kind[i] = 2;              // a cell of bits (no kube owns it)\n  // THE CORE: the vessel's generated kube (vessels.mjs vesselKubeAt), there whatever the edits say (it is never mined).\n  const core = cellIdx(0, 0, 0);\n  if (!names.has(core)) names.set(core, DOCKING_PORT);\n  kind[core] = 1;\n  docks.sort((a, b) => a.base - b.base);\n  return { kind, names, docks, panels, batteries };\n}\n\n// ---- THE OUTSIDE -----------------------------------------------------------------------------------------------------------\n// Every cell space reaches: a flood from beyond the grid's six faces through every cell that is not solid. What is left\n// unreached is walled in by the hull (what used to be a room): geometry, seen from nowhere and used by nothing.\nexport function outsideOf(kind) {\n  const out = new Uint8Array(N3), q = new Int32Array(N3);\n  let head = 0, tail = 0;\n  const seed = (i) => { if (!out[i] && kind[i] !== 1) { out[i] = 1; q[tail++] = i; } };\n  for (let a = 0; a < EDGE; a++) for (let b = 0; b < EDGE; b++) {\n    seed(a + b * EDGE); seed(a + b * EDGE + (EDGE - 1) * EDGE * EDGE);                 // z faces\n    seed(a + b * EDGE * EDGE); seed(a + (EDGE - 1) * EDGE + b * EDGE * EDGE);          // y faces\n    seed(a * EDGE + b * EDGE * EDGE); seed((EDGE - 1) + a * EDGE + b * EDGE * EDGE);   // x faces\n  }\n  while (head < tail) {\n    const i = q[head++], x = i & 31, y = (i >> 5) & 31, z = (i >> 10) & 31;\n    if (x > 0) seed(i - 1); if (x < EDGE - 1) seed(i + 1);\n    if (y > 0) seed(i - EDGE); if (y < EDGE - 1) seed(i + EDGE);\n    if (z > 0) seed(i - EDGE * EDGE); if (z < EDGE - 1) seed(i + EDGE * EDGE);\n  }\n  return out;\n}\n\n// ---- THE MODEL -----------------------------------------------------------------------------------------------------------\n// The cells, the outside, and each Suit Dock with its front: the open cell beside its base (with open space above it) that\n// space reaches, where a docked player is held. A dock with no such front is walled in: not usable on a vessel.\nexport function vesselModel(blocks, sums, opts = {}) {\n  const V = readCells(blocks, sums, opts);\n  const outside = outsideOf(V.kind);\n  const openOut = (x, y, z) => inGrid(x, y, z) ? (V.kind[cellIdx(x, y, z)] === 0 && outside[cellIdx(x, y, z)] === 1) : true;\n  const docks = V.docks.map((d) => {\n    const f = dockFront(d.cell[0], d.cell[1], d.cell[2], openOut, true);\n    return { ...d, front: f ? f.cell : null, facing: f ? f.facing : null, exterior: !!f };\n  });\n  return { cells: V, outside, docks, panels: V.panels, batteries: V.batteries,\n           isOutside: (x, y, z) => !inGrid(x, y, z) || outside[cellIdx(x, y, z)] === 1,\n           openOutside: openOut };\n}\n// The docks on the hull's outside (the ones a player may dock at), and the one at a cell (its base or its upper half).\nexport const exteriorDocks = (model) => (model ? model.docks.filter((d) => d.exterior) : []);\nexport function dockAtCell(model, cell) {\n  if (!model || !Array.isArray(cell) || cell.length !== 3) return null;\n  const [x, y, z] = cell;\n  return model.docks.find((d) => d.cell[0] === x && d.cell[2] === z && (d.cell[1] === y || d.cell[1] === y - 1)) || null;\n}\n// The nearest usable outside dock to a cell (default the core): where Set home on a vessel brings a player back, docked.\n// `ok(dock)` says whether this player may use it.\nexport function nearestExteriorDock(model, ok, near) {\n  const n0 = near || [0, 0, 0];\n  let best = null, bestD = Infinity;\n  for (const d of exteriorDocks(model)) {\n    if (ok && !ok(d)) continue;\n    const dd = (d.cell[0] - n0[0]) ** 2 + (d.cell[1] - n0[1]) ** 2 + (d.cell[2] - n0[2]) ** 2;\n    if (dd < bestD || (dd === bestD && best && d.base < best.base)) { bestD = dd; best = d; }\n  }\n  return best;\n}\n// May a Suit Dock stand at this base cell on a vessel, with these blocks (its upper half placed above it)? null, or why not.\nexport function dockPlacementProblem(model, cell) {\n  const d = dockAtCell(model, cell);\n  if (!d) return NO_DOCK;\n  return d.exterior ? null : DOCK_INSIDE;\n}\n\n// ---- THE RECORD ----------------------------------------------------------------------------------------------------------\nconst num = (v, d = 0) => (typeof v === 'number' && Number.isFinite(v) ? v : d);\nexport function readLife(raw) {\n  const r = raw && typeof raw === 'object' ? raw : {};\n  return { v: 1, at: Math.max(0, num(r.at)), sun: Math.max(0, Math.min(SOLAR_MAX, num(r.sun))), charge: Math.max(0, num(r.charge)), rev: Math.max(0, Math.floor(num(r.rev))) };\n}\n// What a placed Solar Panel makes, from its star's flux there (1 at the habitable-zone centre: energy.mjs insolationAt,\n// temperature.mjs irradianceAt / SOLAR_CONSTANT): the flux itself, capped at twice (the owner: \"capped at 2× near the star,\n// falling with distance² outward\"). No eclipse.\nexport const sunAt = (flux) => Math.max(0, Math.min(SOLAR_MAX, num(flux)));\n\n// THE POOL, from the model and the record: units in (panels × sun), nothing drawing on it continuously (out 0: the docks\n// and the EM drives take lumps), the storage (batteries × BATTERY_STORE). Units are power; storage is unit-minutes.\nexport function poolOf(model, life) {\n  return { in: model.panels * num(life && life.sun), out: 0, cap: model.batteries * BATTERY_STORE };\n}\n// ADVANCE the record from its moment to `toMs` under this model, in closed form: the pool fills at `in` a minute, up to cap.\nexport function advanceLife(life0, model, toMs) {\n  const life = readLife(life0);\n  const dtS = life.at > 0 ? Math.max(0, (num(toMs) - life.at) / 1000) : 0;   // a record never yet written starts now\n  const P = poolOf(model, life);\n  const charge = P.cap > 0 ? Math.max(0, Math.min(P.cap, Math.min(P.cap, life.charge) + P.in * dtS / 60)) : 0;\n  return { v: 1, at: Math.max(life.at, num(toMs)), sun: life.sun, charge, rev: life.rev };\n}\n// What the page shows, and what the dock and the lights read: the record at `nowMs`, and the pool's flows.\nexport function lifeStatus(life0, model, nowMs) {\n  const life = advanceLife(life0, model, nowMs);\n  const P = poolOf(model, life);\n  return { life, in: P.in, out: P.out, cap: P.cap, charge: life.charge, net: P.in - P.out, powered: P.in > 0 || life.charge > 1e-6,\n           panels: model.panels, batteries: model.batteries, sun: life.sun };\n}\n// WHEN THE BLOCKS CHANGE (the server's): the record carried across, `prev` already advanced to the moment of the change.\n// A Battery taken away takes its share of the charge with it.\nexport function reconcileLife(prev0, newModel) {\n  const prev = readLife(prev0);\n  return { v: 1, at: prev.at, sun: prev.sun, charge: Math.min(prev.charge, newModel.batteries * BATTERY_STORE), rev: prev.rev + 1 };\n}\n\n// ---- THE DOCK -------------------------------------------------------------------------------------------------------------\n// Who may use a dock: the owner and builders (of the vessel, or the world's claim), or anyone while it is public.\nexport function canUseDock(holder, uid, pub) {\n  if (!uid) return false;\n  if (pub) return true;\n  return !!(holder && (holder.owner === uid || (Array.isArray(holder.builders) && holder.builders.includes(uid))));\n}\n// The front of a dock: the open cell beside its base with open space above it too (the 1×2 upright space beside the 1×2\n// dock), looking along the level axes in a fixed order. `free(x, y, z)` says whether a cell is open. null when walled in.\nexport function dockFront(x, y, z, free, vessel) {\n  const u = cellUp(x, y, z, vessel), t = [0, 1, 2].filter((a) => a !== u.a);\n  for (const [a, s] of [[t[1], 1], [t[1], -1], [t[0], 1], [t[0], -1]]) {\n    const c = [x, y, z]; c[a] += s;\n    const up = c.slice(); up[u.a] += u.s;\n    if (free(c[0], c[1], c[2]) && free(up[0], up[1], up[2])) return { cell: c, facing: [a === 0 ? s : 0, a === 1 ? s : 0, a === 2 ? s : 0] };\n  }\n  return null;\n}\n// WHAT A DOCK GIVES, from `avail` unit-minutes: O₂ first (it is survival), then energy, each as far as it goes. `o2` is the\n// suit's reserve (0..100), `energy` and `cap` its charge and capacity (energy.mjs).\nexport function dockDraw(avail, o2, energy, cap) {\n  let left = Math.max(0, num(avail));\n  const o2Need = Math.max(0, 100 - num(o2, 100)) / 100;\n  const o2Give = Math.min(o2Need, left / DOCK_O2_COST);\n  left -= o2Give * DOCK_O2_COST;\n  const eNeed = Math.max(0, num(cap) - num(energy));\n  const eGive = Math.min(eNeed, left / DOCK_ENERGY_COST);\n  left -= eGive * DOCK_ENERGY_COST;\n  return { o2: Math.min(100, num(o2, 100) + o2Give * 100), energy: num(energy) + eGive, used: Math.max(0, num(avail) - left),\n           short: o2Give + 1e-9 < o2Need || eGive + 1e-6 < eNeed };\n}\n// A WORLD DOCK'S BANK (no pool on a world): Solar Panels touching it fill it at sunAt(flux) units each, into its own store\n// (one Battery's worth for every Battery touching it, else WORLD_DOCK_BANK), from what it held at its last use. A dock with\n// nothing touching it is a respawn point and nothing more.\nexport function worldDockBank(bank0, at0, nowMs, panels, batteries, flux) {\n  const cap = batteries > 0 ? batteries * BATTERY_STORE : (panels > 0 ? WORLD_DOCK_BANK : 0);\n  const rate = panels * sunAt(flux);\n  const dtM = Math.max(0, (num(nowMs) - num(at0, nowMs)) / 60000);\n  return { charge: Math.max(0, Math.min(cap, num(bank0) + rate * dtM)), cap, rate };\n}\n\n// ---- THE LIGHTS ---------------------------------------------------------------------------------------------------------\n// Machine lights follow power aboard (the owner's pick): every power light lit while the pool can run things, a Battery's\n// gauge at the pool's fill, its status light green while it charges, amber while it powers. A short key per state, one\n// atlas texture each (system-view.js machineAtlasTexture): 'p' + lit + mode + fill in tenths, e.g. 'p1c7'.\nexport function powerKey(status) {\n  if (!status) return 'p0i0';\n  const fill = status.cap > 0 ? Math.round((status.charge / status.cap) * 10) : 0;\n  const mode = status.net > 0.01 && status.cap > 0 && status.charge < status.cap - 1e-6 ? 'c' : status.net < -0.01 && status.charge > 0 ? 'p' : 'i';\n  return 'p' + (status.powered && (status.in > 0 || status.charge > 0) ? 1 : 0) + mode + Math.max(0, Math.min(10, fill));\n}\nexport function powerStateOf(key) {\n  const m = /^p([01])([cpi])(\\d{1,2})$/.exec(String(key || ''));\n  if (!m) return null;\n  return { powered: m[1] === '1', mode: m[2] === 'c' ? 'charging' : m[2] === 'p' ? 'powering' : 'idle', fill: Math.min(10, Number(m[3])) / 10 };\n}\n\n// ---- RESPAWN --------------------------------------------------------------------------------------------------------------\n// The order the owner picked. The server weighs each candidate (it alone can read the records) and takes the first that\n// holds; `cands` is [{ kind, ok, why? }] in any order.\nexport const RESPAWN_ORDER = Object.freeze(['dock', 'home', 'first']);\nexport function respawnPick(cands) {\n  for (const k of RESPAWN_ORDER) { const c = (cands || []).find((x) => x && x.kind === k); if (c && c.ok) return c; }\n  return null;\n}\nexport const respawnText = (kind, where, docked) => (kind === 'dock' ? `You came back ${docked ? 'docked ' : ''}at your suit dock${where ? ' on ' + where : ''}.`\n  : kind === 'home' ? `You came back at home${where ? ', ' + where : ''}${docked ? ', docked at its suit dock' : ''}.` : kind === 'first' ? `You came back on your first world${where ? ', ' + where : ''}.`\n  : 'You came back to your ship in orbit.');\n\n// ---- WORDS FOR THE PAGE -------------------------------------------------------------------------------------------------\nconst r1 = (v) => Math.round(num(v) * 10) / 10;\nexport const powerText = (st) => `${Math.round(st.charge)} of ${Math.round(st.cap)} stored · +${r1(st.in)} in`;\n"],"mappings":"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","names":[],"file":"vessel-life.mjs"}