Tessarune
Dungeon Mechanisms · dungeon_door.js

Dungeon door

48×48 · 412 lines of JavaScript

A 48×48 camera-facing prop: a door hung in a dressed-stone arch inside a short stub of ashlar wall that runs to the canvas edges, so it drops into a tileset wall. One program draws two styles, a heavy plank door with iron strap hinges and a pull ring, and an iron vault door with rivets and a brass lock plate. Each style has “closed” (8 frames, a glint on the metal), “open” (8 frames, once: the leaf swings inward, overshoots and settles as grit falls from the lintel) and “opened” (8 frames, dust drifting in the dark passage). The leaf darkens as it turns away from the light.

Dungeon door — Rendered output6×
Rendered by this program, variant “open” · 48×48 · 8 frames
Dungeon MechanismsGet the animated sprites: Dungeon Mechanisms on itch.ioVol.2 · 12 traps, doors and breakables

The header. The comment at the top of the file is the brief: size, view, light direction and every variant.

dungeon_door.js · 1–14
// Dungeon door — 48x48, top-down 3/4, key light top-left. Camera-facing (set in a wall that faces the camera).// A door hung in a dressed-stone arch that sits in a short stub of ashlar wall (the stub runs to the canvas edges so// the piece drops into a tileset wall; the portcullis uses the same arch and wall). Two door styles in one program://   wood — a heavy plank door with iron strap hinges and a pull ring//   iron — a vault door of riveted iron plates, heavy strap hinges and a brass lock plate with a keyhole// Built like chest.js: ONE door in ONE wall, modelled as a few solids (the wall box minus the arch hole, the// protruding arch frame, the keystone, and the leaf: a D-shaped slab on a vertical hinge). Every pixel casts the view// ray into them, and a second ray toward the key light decides the cast shadows (the arch shades the recessed leaf,// the jambs shade the passage). Lighting is in world space, so the leaf darkens as it turns away from the light.// Variants (iron: the same names with an -iron suffix)://   closed  loop, 8f — wood: a glint crosses the pull ring · iron: a glint travels across the rivets//   open    one-shot, 8f — the leaf swings inward on its hinge, overshoots and settles; grit falls from the lintel//           (iron: slower to start, heavier, a smaller overshoot). The last frame == opened frame 0.//   opened  loop, 8f — the dark passage beyond, dust motes drifting in the doorway

The last lines. submit declares the canvas and lists the variants the renderer exports.

dungeon_door.js · 400–412
submit(Door, {  size: [48, 48], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: N / 10, loop: true },  anchor: { x: 24, y: 45 },  variants: [    { name: "closed", args: ["closed", "wood"] },    { name: "open", args: ["open", "wood"], animate: ONE_SHOT },    { name: "opened", args: ["opened", "wood"] },    { name: "closed-iron", args: ["closed", "iron"] },    { name: "open-iron", args: ["open", "iron"], animate: ONE_SHOT },    { name: "opened-iron", args: ["opened", "iron"] },  ],});
Show the whole program (412 lines)
dungeon_door.js · 412 lines
// Dungeon door — 48x48, top-down 3/4, key light top-left. Camera-facing (set in a wall that faces the camera).// A door hung in a dressed-stone arch that sits in a short stub of ashlar wall (the stub runs to the canvas edges so// the piece drops into a tileset wall; the portcullis uses the same arch and wall). Two door styles in one program://   wood — a heavy plank door with iron strap hinges and a pull ring//   iron — a vault door of riveted iron plates, heavy strap hinges and a brass lock plate with a keyhole// Built like chest.js: ONE door in ONE wall, modelled as a few solids (the wall box minus the arch hole, the// protruding arch frame, the keystone, and the leaf: a D-shaped slab on a vertical hinge). Every pixel casts the view// ray into them, and a second ray toward the key light decides the cast shadows (the arch shades the recessed leaf,// the jambs shade the passage). Lighting is in world space, so the leaf darkens as it turns away from the light.// Variants (iron: the same names with an -iron suffix)://   closed  loop, 8f — wood: a glint crosses the pull ring · iron: a glint travels across the rivets//   open    one-shot, 8f — the leaf swings inward on its hinge, overshoots and settles; grit falls from the lintel//           (iron: slower to start, heavier, a smaller overshoot). The last frame == opened frame 0.//   opened  loop, 8f — the dark passage beyond, dust motes drifting in the doorwayconst K = 0.4, CX = 24, BASE = 46;               // down view: screen rise per unit of depth (as chest.js), wall base rowconst UW = 24, WT = 10, WH = 38;                 // wall stub: half-length, thickness, heightconst SPR = 20, RO = 9, RF = 13, FP = 1.5;       // arch: springing height, opening radius, frame outer radius, frame projectionconst RL = 8.7, RLC = RO - 0.25, DV = 0.5, DT = 2; // leaf: half-width, arch radius, set-back behind the wall face, thicknessconst HU = -RL, HV = DV + DT;                    // hinge axis: the viewer's left edge, at the leaf's back faceconst CH = WH / 7;                               // ashlar course heightconst PW = (2 * RL) / 4;                         // plank widthconst STRAPS = [5, 15.5];                        // strap hinge heightsconst RU = 3.6, RZ = 9.6, RR = 2.1;              // pull ring centre and radius (leaf coordinates)const REST = 78, N = 8;const OPEN_TH = {  wood: [0, 3, 22, 50, 72, 85, 75, REST],        // latch jolt, swing, overshoot, settle == opened  iron: [0, 1, 5, 16, 33, 55, 81, REST],         // strains to start, gathers weight, a small overshoot, settles};const IRON_STRAP_END = 0.8, IRON_KNOB = 1.8;      // iron door: strap hinges end in a round knob at mid-doorconst LOCK = [3.2, 7.0, 8.2, 14.6], KEYHOLE = [5.1, 12.2]; // brass lock plate (u0, u1, z0, z1) and keyhole centrelet STYLE = "wood";const LV = norm3([-0.6, -0.25, 0.76]);           // toward the key light (world: X right, Y away, Z up)const LS = norm3([-0.45, -0.6, 0.66]);           // cast-shadow direction: the same key light, pulled forward so recess shadows stay shortconst BRASS = ["g0", "g1", "g2", "g3"], WOOD = ["w0", "w1", "w2", "w3", "w4"], STONE = ["n0", "n1", "n2", "n3", "n4"], IRON = ["n0", "n1", "n2", "n3", "n4"];const WALLB = [-UW, UW, 0, WT, 0, WH];const KEYB = [-2.3, 2.3, -FP - 0.8, 0, SPR + RO - 0.5, SPR + RF + 0.8];const SILLB = [-RO, RO, -FP, WT, -2, 0];          // the passage floor under the arch (seen only through it)function norm3(v) { const m = Math.hypot(v[0], v[1], v[2]); return [v[0] / m, v[1] / m, v[2] / m]; }const neg = (n) => [-n[0], -n[1], -n[2]];const clamp = (v, a, b) => Math.max(a, Math.min(b, v));// door-local coordinates: u along the wall (viewer's right +), v into the wall (room side < 0), z up. The camera is// chest.js's oblique view: a view ray per pixel, and the projection of a local point back to the screen.const rayAt = (sx, sy) => ({ o: [sx - CX, -30, BASE - sy + 30 * K], d: [0, 1, -K] });const proj = (q) => [CX + q[0], BASE - q[2] - K * q[1]];// ---- ray vs solids. Intervals are {tin, tout, nin, nout} (outward normals) or null.function slab(o, d, i, lo, hi, st) {  const di = d[i], oi = o[i];  if (di < 1e-9 && di > -1e-9) return oi >= lo && oi <= hi;  let t1 = (lo - oi) / di, t2 = (hi - oi) / di, s1 = -1, s2 = 1;  if (t1 > t2) { const k = t1; t1 = t2; t2 = k; s1 = 1; s2 = -1; }  if (t1 > st.tin) { st.tin = t1; st.nin = [0, 0, 0]; st.nin[i] = s1; }  if (t2 < st.tout) { st.tout = t2; st.nout = [0, 0, 0]; st.nout[i] = s2; }  return st.tin <= st.tout;}function box(o, d, b) {  const st = { tin: -1e9, tout: 1e9, nin: [0, 0, 0], nout: [0, 0, 0] };  for (let i = 0; i < 3; i++) if (!slab(o, d, i, b[2 * i], b[2 * i + 1], st)) return null;  return st;}// D-shaped prism along v: |u| <= w, z >= 0, v0 <= v <= v1, and (z <= SPR or inside the circle r about (0, SPR))function inD(u, z, w, r) { return Math.abs(u) <= w && z >= 0 && (z <= SPR || u * u + (z - SPR) * (z - SPR) <= r * r); }function dshape(o, d, w, r, v0, v1) {  const st = { tin: -1e9, tout: 1e9, nin: [0, 0, 0], nout: [0, 0, 0] };  if (!slab(o, d, 0, -w, w, st) || !slab(o, d, 1, v0, v1, st) || !slab(o, d, 2, 0, 1e9, st)) return null;  let hA = -1e9, hB = 1e9, cA = 1, cB = 0;  if (d[2] < 1e-9 && d[2] > -1e-9) { if (o[2] > SPR) { hA = 1; hB = 0; } }  else { const th = (SPR - o[2]) / d[2]; if (d[2] > 0) hB = th; else hA = th; }  const A = d[0] * d[0] + d[2] * d[2], ou = o[0], oz = o[2] - SPR;  if (A < 1e-12) { if (ou * ou + oz * oz <= r * r) { cA = -1e9; cB = 1e9; } }  else {    const B = 2 * (ou * d[0] + oz * d[2]), C = ou * ou + oz * oz - r * r, D = B * B - 4 * A * C;    if (D >= 0) { const s = Math.sqrt(D); cA = (-B - s) / (2 * A); cB = (-B + s) / (2 * A); }  }  const a1 = Math.max(st.tin, hA), b1 = Math.min(st.tout, hB), a2 = Math.max(st.tin, cA), b2 = Math.min(st.tout, cB);  const ok1 = a1 <= b1, ok2 = a2 <= b2;  if (!ok1 && !ok2) return null;  const tin = ok1 && ok2 ? Math.min(a1, a2) : ok1 ? a1 : a2;  const tout = ok1 && ok2 ? Math.max(b1, b2) : ok1 ? b1 : b2;  const capN = (t) => { const u = o[0] + t * d[0], z = o[2] + t * d[2] - SPR, m = Math.hypot(u, z) || 1; return [u / m, 0, z / m]; };  return {    tin, tout,    nin: tin === st.tin ? st.nin : ok2 && tin === cA ? capN(tin) : [0, 0, 1],    nout: tout === st.tout ? st.nout : ok2 && tout === cB ? capN(tout) : [0, 0, 1],  };}// first surface of (A minus B) past epsfunction minus(a, b, eps) {  if (!a || a.tout < eps) return null;  let best = null;  if (a.tin > eps && !(b && a.tin >= b.tin && a.tin <= b.tout)) best = { t: a.tin, n: a.nin, inner: false };  if (b && b.tout > eps && b.tout >= a.tin && b.tout < a.tout && (!best || b.tout < best.t)) best = { t: b.tout, n: neg(b.nout), inner: true };  return best;}// the leaf's frame: rotate about the hinge axis by th degrees (free edge swings into +v)function leafFrame(th) {  const a = (th * Math.PI) / 180, ca = Math.cos(a), sa = Math.sin(a);  return {    pt: (p) => { const ru = p[0] - HU, rv = p[1] - HV; return [HU + ru * ca + rv * sa, HV - ru * sa + rv * ca, p[2]]; },    dir: (v) => [v[0] * ca + v[1] * sa, -v[0] * sa + v[1] * ca, v[2]],    back: (n) => [n[0] * ca - n[1] * sa, n[0] * sa + n[1] * ca, n[2]],  };}function scene(o, d, lf, eps, primary, skipLeaf) {  let best = null;  const take = (h, id, extra) => { if (h && h.t > eps && (!best || h.t < best.t)) best = Object.assign({ t: h.t, n: h.n, id, inner: h.inner }, extra); };  take(minus(box(o, d, WALLB), dshape(o, d, RF, RF, -1e9, 1e9), eps), "wall");  take(minus(dshape(o, d, RF, RF, -FP, WT), dshape(o, d, RO, RO, -1e9, 1e9), eps), "frame");  const kb = box(o, d, KEYB);  if (kb) take({ t: kb.tin, n: kb.nin }, "key");  let gate = true;   // primary rays see the leaf and the passage only through the arch  if (primary) { const t0 = (-FP - o[1]) / d[1]; gate = inD(o[0] + t0 * d[0], o[2] + t0 * d[2], RO, RO); }  if (gate && !skipLeaf) {    const ol = lf.pt(o), dl = lf.dir(d), h = dshape(ol, dl, RL, RLC, DV, DV + DT);    if (h && h.tin > eps) take({ t: h.tin, n: lf.back(h.nin) }, "leaf", { lp: [ol[0] + h.tin * dl[0], ol[1] + h.tin * dl[1], ol[2] + h.tin * dl[2]], ln: h.nin });  }  if (primary && gate) {    const sb = box(o, d, SILLB);    if (sb) take({ t: sb.tin, n: sb.nin }, "frame", { inner: true });    const c = dshape(o, d, RO, RO, WT, 1e9);    if (c) take({ t: c.tout, n: neg(c.nout) }, "corridor");  }  return best;}function classify(h, p) {  const u = p[0], v = p[1], z = p[2], n = h.n;  if (h.id === "wall") {    if (n[2] > 0.5) return { part: "wall", mat: "stone", s: "wt", lift: 0.45, k: Math.floor((u + UW + 3) / 9) + 50 * Math.floor(v / 5.01) };    const c = Math.floor(z / CH), j = Math.floor((u + UW + 7.5 + (c & 1) * 4.5) / 9);    return { part: "wall", mat: "stone", s: "wf", k: c * 64 + j };  }  if (h.id === "frame" && !h.inner) {    const k = z <= SPR ? (u < 0 ? 0 : 20) + Math.floor(z / (SPR / 3)) : 50 + Math.floor(Math.atan2(z - SPR, u) / (Math.PI / 7));    return { part: "arch", mat: "stone", s: n[1] < -0.5 ? "ff" : "fr", k, lift: 0.95 };  }  if (h.id === "key") return { part: "arch", mat: "stone", s: "key", k: 0, lift: 0.9, carve: n[1] < -0.5 && Math.abs(Math.abs(u) + Math.abs(z - 31.3) - 1.3) < 0.45 };  if (h.id === "frame") return { part: "passage", mat: "reveal", s: n[2] > 0.5 ? "floor" : "side", k: 0, v };  if (h.id === "corridor") return { part: "passage", mat: "void", s: "void", k: 0, v };  // the leaf, in its own frame  const lp = h.lp, ln = h.ln, lu = lp[0], lz = lp[2];  if (STYLE === "iron") return classifyIron(lu, lz, ln);  if (ln[1] < -0.5) {    const rd = Math.hypot(lu - RU, lz - RZ);    if (Math.abs(rd - RR) < 0.62) return { part: "ring", mat: "ring", s: "ring", k: 0, a: Math.atan2(lz - RZ, lu - RU) };    if (Math.hypot(lu - RU, lz - (RZ + RR + 0.7)) < 0.95) return { part: "ring", mat: "boss", s: "boss", k: 0 };    for (let i = 0; i < STRAPS.length; i++) {      const tip = RL - 1.2, half = lu > tip - 3 ? 1.05 * Math.max(0.35, (tip - lu) / 3) : 1.05;      if (lu <= tip && Math.abs(lz - STRAPS[i]) < half) {        let rivet = false;        for (let r = 0; r < 4; r++) if (Math.abs(lu - (-RL + PW * (r + 0.5))) < 0.55 && Math.abs(lz - STRAPS[i]) < 0.5) rivet = true;        return { part: "bands", mat: "iron", s: "strap", k: i, rivet, knuckle: lu < -RL + 0.9 };      }    }    const k = clamp(Math.floor((lu + RL) / PW), 0, 3), x = lu + RL - k * PW;    const g0 = 3 + 9 * hash(k, 1), g1 = 13 + 8 * hash(k, 2);    const grain = Math.abs(x - (1.4 + 1.6 * hash(k, 3))) < 0.5 && ((lz > g0 && lz < g0 + 3.5) || (lz > g1 && lz < g1 + 2.5));    return { part: "door", mat: "wood", s: "lf", k, grain };  }  if (ln[1] > 0.5) return { part: "door", mat: "wood", s: "lb", k: clamp(Math.floor((lu + RL) / PW), 0, 3) };  return { part: "door", mat: "wood", s: "le", k: 0, top: ln[2] > 0.3 };}// the vault door: riveted plates (2 columns x 2 rows + the arch plate), strap hinges with knobs, a brass lock platefunction ironRivet(lu, lz) {  // a row of rivets just inside the top and bottom edge of each plate, and a ring of them on the arch plate  const along = (x, step) => { const m = ((x % step) + step) % step; return m < 0.55 || m > step - 0.55; };  if (lz < 20.5) {    for (const zr of [1.4, 9.1, 11.9, 19.1]) if (Math.abs(lz - zr) < 0.55 && Math.abs(lu) < 7.9 && Math.abs(lu - 0.3) > 1.2 && along(lu + 7.3, 3.5)) return true;    return false;  }  const r = Math.hypot(lu, lz - SPR), a = (Math.atan2(lz - SPR, lu) * 180) / Math.PI;  if (Math.abs(r - 6.6) < 0.55 && along(a - 15, 30) && a > 5 && a < 175) return true;  return false;}function classifyIron(lu, lz, ln) {  if (ln[1] >= -0.5) return { part: "door", mat: "plate", s: ln[1] > 0.5 ? "back" : "le", k: 0, top: ln[2] > 0.3 };  if (lu >= LOCK[0] && lu <= LOCK[1] && lz >= LOCK[2] && lz <= LOCK[3]) {    const kx = lu - KEYHOLE[0], kz = lz - KEYHOLE[1];    const key = Math.hypot(kx, kz) < 0.8 || (Math.abs(kx) < 0.4 && kz < 0 && kz > -2.4);    const screw = (Math.abs(lu - LOCK[0] - 0.7) < 0.45 || Math.abs(lu - LOCK[1] + 0.7) < 0.45) && (Math.abs(lz - LOCK[2] - 0.7) < 0.45 || Math.abs(lz - LOCK[3] + 0.7) < 0.45);    return { part: "lock", mat: "brass", s: "lock", k: 0, key, screw };  }  for (let i = 0; i < STRAPS.length; i++) {    const zc = STRAPS[i];    if ((lu <= IRON_STRAP_END && Math.abs(lz - zc) < 1.35) || Math.hypot(lu - IRON_STRAP_END, lz - zc) < IRON_KNOB) {      let rivet = Math.hypot(lu - IRON_STRAP_END, lz - zc) < 0.55;      for (const ur of [-7.3, -4.4, -1.5]) if (Math.abs(lu - ur) < 0.5 && Math.abs(lz - zc) < 0.5) rivet = true;      return { part: "bands", mat: "iron", s: "strap", k: i, rivet, knuckle: lu < -RL + 0.9 };    }  }  const k = lz >= 20.5 ? 4 : (lz < 10.5 ? 0 : 2) + (lu < 0.3 ? 0 : 1);  return { part: "door", mat: "plate", s: "plate", k, rivet: ironRivet(lu, lz), lu, lz };}function cast(th) {  const lf = leafFrame(th);  const buf = new Array(48 * 48).fill(null);  for (let py = 0; py < 48; py++) for (let px = 0; px < 48; px++) {    const { o, d } = rayAt(px + 0.5, py + 0.5);    const h = scene(o, d, lf, 0, true, false);    if (!h) continue;    const p = [o[0] + h.t * d[0], o[1] + h.t * d[1], o[2] + h.t * d[2]];    const c = classify(h, p);    c.d = h.n[0] * LV[0] + h.n[1] * LV[1] + h.n[2] * LV[2];    c.t = h.t; c.v = p[1];    if (c.d > 0 && c.mat !== "void") {      const q = [p[0] + h.n[0] * 0.06, p[1] + h.n[1] * 0.06, p[2] + h.n[2] * 0.06];      c.sh = !!scene(q, LS, lf, 1e-4, false, h.id === "leaf");    }    buf[py * 48 + px] = c;  }  return { buf, proj };}function tone(buf, fx) {  const keys = new Array(48 * 48).fill(null);  for (let i = 0; i < 48 * 48; i++) {    const b = buf[i]; if (!b) continue;    const px = i % 48, py = (i - px) / 48;    const up = py > 0 ? buf[i - 48] : null, dn = py < 47 ? buf[i + 48] : null;    const lf = px > 0 ? buf[i - 1] : null, rt = px < 47 ? buf[i + 1] : null;    const seam = (q) => q && q.s === b.s && q.k !== b.k;    let key;    if (b.mat === "stone") {      let val = 1.3 + 1.25 * b.d + (b.lift || 0);      const h = hash(b.k * 1.7 + b.s.charCodeAt(1), 3.1);      if (h < 0.22) val -= 0.45; else if (h > 0.8) val += 0.4;      if (b.sh) val -= 0.9;      let lv = Math.floor(val);      if (seam(rt) || seam(dn)) lv = -1;      else if (seam(up) || seam(lf)) lv += 1;      else if (b.part === "arch" && ((up && up.part !== "arch") || (lf && lf.part !== "arch"))) lv += 1;      if (!up && lv >= 0) lv += 1;      if (b.carve) lv = Math.min(lv, 1);      key = lv < 0 ? (val < 1.2 ? "ink" : "n0") : STONE[clamp(lv, 0, 4)];    } else if (b.mat === "reveal") {      const dv = b.v + FP;      key = b.sh ? (dv < 4 ? "n0" : "ink") : dv < 2.5 ? "n1" : dv < 6 ? "n0" : "ink";    } else if (b.mat === "void") {      const dv = b.v - WT;      key = dv < 2 ? "n0" : dv < 6 && chk(px, py) ? "n0" : "ink";    } else if (b.mat === "wood") {      let val = 2.05 + 1.2 * b.d;      if (b.s !== "le") { const h = hash(b.k, 7.7); val += h < 0.3 ? -0.3 : h > 0.75 ? 0.3 : 0; }      if (b.sh) val -= 1;      let lv = Math.floor(val);      if (b.s === "lf" || b.s === "lb") {        if (seam(rt)) lv = 0;        else if (seam(lf)) lv += 1;        else if (b.grain) lv -= 1;      } else if (b.top) lv += 1;      if (!up && lv > 0) lv += 1;      key = WOOD[clamp(lv, 0, 4)];    } else if (b.mat === "iron") {      let val = (STYLE === "iron" ? 2.15 : 1.3) + 1.0 * b.d;   // on the vault door the straps stand proud of its plates and catch more light      if (b.sh) val -= 0.8;      let lv = Math.floor(val);      const same = (q) => q && q.mat === "iron" && q.k === b.k;      if (!same(up)) lv += 2; else if (!same(dn)) lv = 0;      if (b.knuckle) lv = Math.max(lv, 2);      if (b.rivet) lv = Math.max(lv + 2, 3);      key = IRON[clamp(lv, 0, 4)];    } else if (b.mat === "plate") {      // iron plates: a lit top/left lip, a dark gap on the right/bottom, domed rivets with a shadow under them      let val = 1.5 + 1.2 * b.d;      if (b.s === "plate") { const h = hash(b.k, 5.3); val += h < 0.35 ? -0.25 : h > 0.7 ? 0.2 : 0; }      if (b.sh) val -= 0.9;      let lv = Math.floor(val);      const rv = (q) => q && q.mat === "plate" && q.rivet;      if (b.s === "plate") {        // each plate is a shallow raised panel: lit lip on its top/left border, shaded on its bottom/right border        const sameP = (q) => q && q.mat === "plate" && q.s === "plate" && q.k === b.k;        if (seam(rt) || seam(dn)) lv = -1;        else if (!sameP(dn) || !sameP(rt)) lv -= 1;        else if (!sameP(up) || !sameP(lf)) lv += 1;        if (b.rivet) lv = fx.glint && fx.glint(b.lu, b.lz) ? 5 : Math.max(lv + 2, 3);        else if (rv(up) || rv(lf)) lv = Math.min(lv, 0);      } else if (b.top) lv += 1;      key = lv >= 5 ? "n5" : lv < 0 ? "ink" : IRON[clamp(lv, 0, 4)];    } else if (b.mat === "brass") {      const same = (q) => q && q.mat === "brass";      let lv = b.d > 0.4 ? 2 : 1;      if (!same(up)) lv = 3; else if (!same(lf)) lv = 2; else if (!same(dn) || !same(rt)) lv = 0;      if (b.sh) lv = Math.max(0, lv - 1);      if (b.screw) lv = 3;      key = b.key ? "ink" : BRASS[clamp(lv, 0, 3)];    } else if (b.mat === "ring") {      const a = (b.a * 180) / Math.PI;   // leaf frame: +90 = top, 180 = left      let lv = a > 80 && a < 200 ? 3 : a < 10 && a > -110 ? 0 : 2;      if (b.d < 0) lv -= 1;      if (b.sh) lv -= 1;      if (fx.ring) lv += fx.ring(a);      key = IRON[clamp(lv, 0, 4)];      if (fx.ring && fx.ring(a) >= 2) key = "n5";    } else if (b.mat === "boss") {      key = up && up.mat === "boss" ? (lf && lf.mat === "boss" ? "n1" : "n2") : "n3";      if (b.sh && key === "n3") key = "n2";    }    keys[i] = key;  }  return keys;}const PARTS = ["wall", "arch", "passage", "door", "bands", "ring", "lock"];function drawDoor(g, th, fx) {  const fr = cast(th), buf = fr.buf, keys = tone(buf, fx);  let x0 = 47, x1 = 0, y0 = 47, y1 = 0;  const seen = new Set();  for (let i = 0; i < 48 * 48; i++) if (buf[i]) { const x = i % 48, y = (i - x) / 48; x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y); seen.add(buf[i].part); }  g.outlined("ink", () => {    for (const part of PARTS) if (seen.has(part)) g.part(part, () => g.field({ x: [x0, x1], y: [y0, y1] }, (x, y) => {      const b = buf[y * 48 + x];      return b && b.part === part ? keys[y * 48 + x] : null;    }));  });  return fr;}// a local point is drawn only where it is in front of what the ray already hit (rays never move in u)function put(g, fr, q, key) {  const [sx, sy] = fr.proj(q), x = Math.floor(sx), y = Math.floor(sy);  if (x < 0 || y < 0 || x > 47 || y > 47) return;  const b = fr.buf[y * 48 + x];  if (b && b.v < q[1]) return;  g.pixel(x, y, key);}// ring glint: a bright point sweeps over the top of the ring (leaf-frame angles), then restsfunction ringGlint(t) {  const f = Math.round(t * N);  if (f < 2 || f > 5) return null;  const at = [165, 125, 85, 45][f - 2];  return (a) => { const d = Math.abs(a - at); return d < 22 ? 2 : d < 50 ? 1 : 0; };}// rivet glint: a bright diagonal band sweeps across the plates left to right, lighting each rivet it crossesfunction rivetGlint(t) {  const f = Math.round(t * N);  if (f < 1 || f > 6) return null;  const gp = -12 + (f - 1) * 5;  return (lu, lz) => Math.abs(lu - (lz - 14) * 0.45 - gp) < 1.6;}const Door = sprite(pal, (g, t, state, style) => {  STYLE = style;  if (state === "closed") {    if (style === "iron") { drawDoor(g, 0, { glint: rivetGlint(t) }); return; }    const fr = drawDoor(g, 0, { ring: ringGlint(t) });    const f = Math.round(t * N);    if (f === 3 || f === 4) {      // the glint peaks as a tiny star on the top of the ring      g.detached("glint", () => {        const q = [RU + (f === 3 ? -0.6 : 0.8), -1, RZ + RR + 0.1];        const [sx, sy] = fr.proj(q), x = Math.floor(sx), y = Math.floor(sy);        g.pixel(x, y, "n5");        if (f === 3) for (const [dx, dy] of [[1, 0], [-1, 0], [0, -1]]) g.pixel(x + dx, y + dy, "n4");      });    }    return;  }  if (state === "open") {    const f = Math.min(N - 1, Math.round(t * N));    const fr = drawDoor(g, OPEN_TH[style][f], {});    // grit shaken loose from the lintel falls through the doorway (gone by the last frame); the iron door sheds more    const grit = style === "iron" ? [[-3, 27, 1], [2, 28, 2], [5.5, 26, 3], [-0.5, 28.5, 2], [4, 27, 1]] : [[-3, 27, 0], [2, 28, 1], [5.5, 26, 2]];    if (f >= 1 && f <= 6) g.detached("grit", () => {      for (const [u0, z0, ph] of grit) {        const k = f - 1 - ph;        if (k < 0 || k > 3) continue;        const z = z0 - 2 - k * (3 + k * 2);        if (z < 1) continue;        put(g, fr, [u0 + (k & 1) * 0.5, -0.8, z], k < 2 ? "n4" : "n3");      }    });    if (f === N - 1) motes(g, fr, 0);    return;  }  motes(g, drawDoor(g, REST, {}), t);});// dust motes drifting in the doorway light (the opened loop, and the last frame of open == its frame 0)function motes(g, fr, t) {  g.detached("dust", () => {    for (const [u0, v0, z0, ph] of [[-1.5, -0.5, 22, 0], [3.5, 1.5, 15, 0.37], [0.5, 3, 8, 0.7], [6, 0.5, 25, 0.15]]) {      const p = (t + ph) % 1;      const q = [u0 + Math.sin(TAU * (p + ph)) * 1.2, v0, z0 - p * 4];      put(g, fr, q, p < 0.2 || p > 0.8 ? "n2" : p > 0.4 && p < 0.6 ? "n4" : "n3");    }  });}const ONE_SHOT = { fps: 10, duration: N / 10, loop: false };submit(Door, {  size: [48, 48], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: N / 10, loop: true },  anchor: { x: 24, y: 45 },  variants: [    { name: "closed", args: ["closed", "wood"] },    { name: "open", args: ["open", "wood"], animate: ONE_SHOT },    { name: "opened", args: ["opened", "wood"] },    { name: "closed-iron", args: ["closed", "iron"] },    { name: "open-iron", args: ["open", "iron"], animate: ONE_SHOT },    { name: "opened-iron", args: ["opened", "iron"] },  ],});

These excerpts omit the boilerplate prepended by the asset pack build script: a single-line runtime header, the shared palette, and common helper utilities (contactShadow, litIndex, flame, hash, chk, TAU). The displayed line numbers match the original source files. Each program was authored by an AI coding agent and adjusted after visual inspection of the rendered frames.

Have your agent write programs like this.

Tessarune gives it the drawing API reference, 25 production manuals and a renderer to check its work against.