fogón

← Fantasy3D assetfogon/libro-volador

Libro volador

by fogonCC07880 blocks
Libro voladorDrag to turn it
// «Libro volador» · gpt-6-sol (medium)
// A magic flying book: a thick open old book with a brown leather cover and gold corners, pages fluttering, floating over a small pile of scrolls, with a few glowing letters drifting above the pages.

// Scrolls on the floor beneath the floating book.
const parchment = ["#d8bb83", "#e7cf9a", "#f1dda9"];
const rolls = [
  { x: 10, y: 12, z: 3, length: 25, r: 3 },
  { x: 18, y: 34, z: 3, length: 20, r: 3 },
  { x: 8,  y: 26, z: 4, length: 18, r: 3 }
];

for (const s of rolls) {
  cylinder(s.x, s.y, s.z, s.r, s.length, "#bc9869", "x");
  cylinder(s.x + 1, s.y, s.z, s.r, s.length - 2, parchment[Math.floor(rand() * 3)], "x");

  // Rolled parchment visible at each end.
  for (const end of [s.x, s.x + s.length - 1]) {
    for (let t = 0; t < 19; t++) {
      const a = t * 0.73;
      const r = 0.3 + t * 0.125;
      set(end, s.y + Math.round(Math.cos(a) * r),
               s.z + Math.round(Math.sin(a) * r), "#94704e");
    }
  }
  for (const bx of [s.x + 4, s.x + s.length - 5]) {
    for (let y = s.y - 3; y <= s.y + 3; y++)
      for (let z = Math.max(0, s.z - 3); z <= s.z + 3; z++)
        if ((y - s.y) ** 2 + (z - s.z) ** 2 <= 9 &&
            (y - s.y) ** 2 + (z - s.z) ** 2 >= 5)
          set(bx, y, z, "#a57343");
    set(bx, s.y - 3, s.z, "#e6b959");
  }
}

// Little loose seal and curling ribbon.
sphere(32, 23, 1, 2, "#ad644d");
sphere(32, 22, 2, 1, "#e5ac53");
line(31, 21, 1, 28, 18, 0, 0, "#a34865");
line(33, 21, 1, 36, 18, 0, 0, "#a34865");

// The entire book floats together.
part("book", [24, 23, 16]);
animate("book", { type: "bob", axis: "z", speed: 2, amplitude: 1.5 });

function slope(x) {
  return 12 + Math.round(Math.abs(x - 24) * 0.32);
}

// Thick, upward-tilted leather covers and layered page edges.
for (let x = 5; x <= 43; x++) {
  if (x === 24) continue;
  const outer = x <= 7 || x >= 41;
  for (let y = 11; y <= 35; y++) {
    if (outer && (y <= 12 || y >= 34)) continue; // softened corners
    const z = slope(x);
    const rim = outer || y <= 12 || y >= 34;

    set(x, y, z, (x + y) % 7 === 0 ? "#815439" : "#69432f");
    set(x, y, z + 1, rim ? "#9b693d" : "#b18a5b");

    if (rim) {
      for (let h = 2; h <= 5; h++)
        set(x, y, z + h, h === 5 ? "#bc8950" : "#795037");
    } else {
      for (let h = 2; h <= 4; h++)
        set(x, y, z + h, (h === 3 && y % 3 === 0) ? "#b8a077" : "#e0c799");
      set(x, y, z + 5, rand() < 0.1 ? "#f8e7bd" : "#ecdbaf");
    }
  }
}

// Raised spine and its warm brass tooling.
box(23, 11, 12, 25, 35, 17, "#60402f");
box(24, 12, 16, 24, 34, 18, "#ae7946");
for (const y of [14, 19, 25, 31]) box(23, y, 17, 25, y + 1, 18, "#d8aa5b");
for (const y of [16, 29]) set(24, y, 19, "#f6d578");

// Gold corner guards on all four outer corners.
for (const x of [6, 42]) {
  for (const y of [14, 32]) {
    const z = slope(x) + 5;
    box(x - 1, y - 2, z, x + 1, y - 2, z + 1, "#b88642");
    box(x - 1, y - 2, z, x - 1, y + 1, z + 1, "#e3b65d");
    set(x, y - 1, z + 2, "#ffdc7e");
  }
}

// Printed lines on the two open pages.
for (const side of [-1, 1]) {
  for (const y of [16, 19, 22, 25, 28, 31]) {
    for (let d = 5; d <= 15; d++) {
      if ((d + y) % 9 === 0 || (y === 31 && d > 10)) continue;
      const x = 24 + side * d;
      set(x, y, slope(x) + 6, rand() < 0.13 ? "#a26b80" : "#75605b");
    }
  }
}

// An illuminated symbol painted across the right-hand page.
for (let i = 0; i < 12; i++) {
  const a = i * Math.PI / 6;
  const x = Math.round(34 + 3 * Math.cos(a));
  const y = Math.round(23 + 3 * Math.sin(a));
  set(x, y, slope(x) + 6, i % 2 ? "#aa65bc" : "#64d4c7");
}
set(34, 23, slope(34) + 6, "#ffdf85");
for (const [dx, dy] of [[-1, 0], [1, 0], [0, -1], [0, 1]])
  set(34 + dx, 23 + dy, slope(34 + dx) + 6, "#e69bd2");

// A few pages lift and flutter above the back of the book.
for (const [x0, x1, y0, rise] of [
  [9, 20, 29, 3], [28, 39, 31, 4], [13, 21, 33, 5]
]) {
  for (let x = x0; x <= x1; x++) {
    for (let y = y0; y <= y0 + 2; y++) {
      const z = slope(x) + rise + Math.round((y - y0) * 0.6);
      set(x, y, z, y === y0 + 2 ? "#c3a77d" : "#f4e2b6");
    }
  }
}

// Teal and violet motes rising from the pages.
for (const [x, y, z, c] of [
  [11, 14, 25, "#8bf8ed"], [19, 29, 27, "#cf82f4"],
  [27, 13, 25, "#9a6af3"], [40, 24, 26, "#71eee2"],
  [9, 26, 23, "#c078e9"], [35, 33, 28, "#8cf2dc"]
]) {
  set(x, y, z, c);
  set(x, y, z + 1, "#f2cfff");
}

// Floating, flickering golden letters, separate from the bobbing book.
part("letters", [24, 21, 29]);
animate("letters", {
  type: "colors",
  palette: ["#e8a84d", "#ffcf70", "#fff4ce"],
  pattern: "flicker",
  glow: 0.8
});

function glyph(rows, x0, y, z0) {
  for (let row = 0; row < rows.length; row++)
    for (let col = 0; col < rows[row].length; col++)
      if (rows[row][col] === "#") {
        set(x0 + col, y, z0 + rows.length - 1 - row, "#ffda7c");
        if ((row + col) % 3 === 0)
          set(x0 + col, y + 1, z0 + rows.length - 1 - row, "#ffedb3");
      }
}

glyph([
  ".###.",
  "#...#",
  "#...#",
  "#####",
  "#...#",
  "#...#",
  "#...#"
], 12, 17, 27);

glyph([
  "..#..",
  "..#..",
  "#.#.#",
  ".###.",
  "#####",
  "..#..",
  "..#.."
], 23, 15, 29);

glyph([
  ".#####.",
  "#.....#",
  "#.....#",
  "#.....#",
  ".#...#.",
  ".#...#.",
  "###.###"
], 32, 21, 27);