fogón

← Pirates and sea3D assetfogon/naufragio

Naufragio

by fogonCC012807 blocks
NaufragioDrag to turn it
// «Naufragio» · gpt-6-sol (medium)
// A shipwreck half-sunk in sand: the broken front half of a wooden ship tilted at an angle, with a snapped mast, torn sail, holes in the hull, seaweed and a few barnacles.

const sand = ["#f5d993", "#efd08a", "#e6c17b"];
const wood = ["#704126", "#84502d", "#995f33", "#ad7140"];
const lightWood = "#c28a50";
const darkWood = "#493022";
const gold = "#f8c843";
const water = ["#38c8c5", "#53d9ce", "#80e7d9"];

function tilt(x, y) {
  return Math.round((x - 24) * 0.13 + (y - 20) * 0.055);
}
function hullWidth(y) {
  if (y < 8 || y > 37) return 0;
  if (y < 18) return 2 + (y - 8) * 1.22;
  if (y < 31) return 14;
  return 14 - (y - 31) * 0.43;
}
function edgeColor(x, y, z) {
  if (z % 3 === 0) return rand() < 0.65 ? lightWood : wood[3];
  return wood[Math.min(3, Math.max(0, Math.floor((z - 3) / 5)))] ;
}

// Low, irregular beach.
for (let x = 3; x <= 44; x++) {
  for (let y = 3; y <= 44; y++) {
    const height = 1 + (rand() < 0.42 ? 1 : 0) +
      (x > 12 && x < 39 && y > 11 && y < 39 ? 1 : 0);
    for (let z = 0; z <= height; z++)
      set(x, y, z, z === height ? sand[Math.floor(rand() * 3)] : "#dcb979");
  }
}

// Bright, shallow water curling around the front corners of the beach.
for (let x = 3; x <= 44; x++) {
  for (let y = 3; y <= 15; y++) {
    if ((x < 12 || x > 38) && y < 12 + Math.sin(x * 0.8) * 2) {
      set(x, y, 2, water[Math.floor(rand() * water.length)]);
      if (rand() < 0.17) set(x, y, 3, "#b9f4e3");
    }
  }
}

// The pointed bow is at -y; the far end is an open, jagged break.
for (let y = 8; y <= 37; y++) {
  const width = hullWidth(y);
  for (let x = Math.ceil(24 - width); x <= Math.floor(24 + width); x++) {
    const t = tilt(x, y);
    for (let h = 3; h <= 16; h++) {
      const half = width * (0.37 + 0.63 * (h - 3) / 13);
      if (Math.abs(x - 24) > half) continue;
      if (y >= 35 && h > 11 && (x + y * 3) % 5 < 2) continue;

      const edge = Math.abs(x - 24) > half - 1.5;
      const brokenEnd = y >= 35 && h > 6;
      let color = h === 16 ? "#aa7040" :
        edge ? edgeColor(x, y, h) : wood[1];
      if (h === 16 && x % 3 === 0) color = "#c18a51";
      if (brokenEnd && rand() < 0.33) color = darkWood;
      set(x, y, h + t, color);
    }
  }
}

// Long deck-plank seams and a few loose, lifted boards.
for (let x = 13; x <= 35; x += 4) {
  for (let y = 15; y <= 33; y++) {
    if (Math.abs(x - 24) < hullWidth(y) - 2)
      set(x, y, 16 + tilt(x, y), "#754625");
  }
}
for (let i = 0; i < 13; i++) {
  const x = 15 + Math.floor(rand() * 19);
  const y = 17 + Math.floor(rand() * 17);
  if (Math.abs(x - 24) < hullWidth(y) - 2)
    box(x, y, 17 + tilt(x, y), x + 2, y, 17 + tilt(x, y), lightWood);
}

// Thick raised gunwales, with intermittent damage toward the snapped end.
for (let y = 12; y <= 35; y++) {
  const w = hullWidth(y);
  for (const side of [-1, 1]) {
    const x = Math.round(24 + side * (w - 0.8));
    for (let h = 17; h <= 20; h++) {
      if (y > 31 && (y + side * 3 + h) % 7 < 3) continue;
      set(x, y, h + tilt(x, y), h === 20 ? lightWood : wood[2]);
      set(x - side, y, h + tilt(x, y), wood[1]);
    }
    if (y % 4 === 0 && y < 32)
      box(x - side, y, 18 + tilt(x, y),
          x - side, y, 21 + tilt(x, y), gold);
  }
}

// Missing planks read as dark, ragged holes in the visible hull sides.
for (const hole of [
  { y: 19, h: 10, ry: 2, rz: 2 },
  { y: 28, h: 12, ry: 2, rz: 2 },
  { y: 24, h: 8, ry: 1, rz: 1 }
]) {
  for (let y = hole.y - hole.ry; y <= hole.y + hole.ry; y++) {
    for (let h = hole.h - hole.rz; h <= hole.h + hole.rz; h++) {
      if (((y - hole.y) / (hole.ry + 0.5)) ** 2 +
          ((h - hole.h) / (hole.rz + 0.5)) ** 2 > 1) continue;
      for (const side of [-1, 1]) {
        const x = Math.round(24 + side * hullWidth(y) *
          (0.37 + 0.63 * (h - 3) / 13));
        set(x, y, h + tilt(x, y), "#302822");
        set(x - side, y, h + tilt(x, y), "#302822");
      }
    }
  }
}

// Bow timbers, gold-edged prow, and a little curled figurehead.
for (let y = 7; y <= 15; y++) {
  const x = 24;
  const z = 19 + Math.round((15 - y) * 0.48);
  box(x - 1, y, z - 2, x + 1, y, z, wood[2]);
  set(x, y, z + 1, gold);
}
line(24, 7, 23, 24, 5, 20, 1, gold);
line(24, 5, 20, 26, 6, 19, 0, gold);

// Exposed ribs and splintered beams sticking out of the broken back.
for (const x of [14, 19, 25, 31, 35]) {
  const z = 13 + tilt(x, 35);
  line(x, 32, z, x + (x % 3) - 1, 39, z + 2, 1, wood[2]);
  line(x, 35, z, x + (x < 24 ? -2 : 2), 38, z + 4, 0, lightWood);
}
for (let x = 17; x <= 32; x += 3)
  line(x, 35, 10 + tilt(x, 35), x, 37, 15 + tilt(x, 35), 0, darkWood);

// Snapped, leaning mast and its cross-yard.
line(24, 23, 17, 29, 24, 36, 1.5, wood[1]);
line(25, 22, 21, 29, 24, 35, 0, lightWood);
line(29, 24, 35, 31, 25, 38, 0, "#c68b51");
line(29, 24, 35, 27, 24, 37, 0, darkWood);
line(15, 22, 34, 39, 23, 34, 1, wood[2]);
set(15, 22, 34, gold);
set(39, 23, 34, gold);

// Torn black sail hangs in front of the yard.
for (let z = 26; z <= 34; z++) {
  const left = 17 + Math.max(0, 29 - z) * 0.5;
  const right = 37 - Math.max(0, 29 - z) * 0.7;
  for (let x = Math.ceil(left); x <= Math.floor(right); x++) {
    if (z < 29 && ((x >= 31 && (x + z) % 4 < 2) ||
                   (x <= 21 && (x + z) % 5 < 2))) continue;
    if ((x === 20 && z === 29) || (x === 34 && z === 30)) continue;
    const shade = (x + z) % 6 === 0 ? "#41404a" :
                  z > 31 ? "#31313b" : "#242631";
    set(x, 20, z, shade);
    set(x, 21, z, shade);
  }
}
line(17, 21, 34, 19, 20, 26, 0, "#a88767");
line(37, 21, 34, 34, 20, 27, 0, "#a88767");

// Bold white skull and crossed bones on the viewer-facing sail.
for (let x = 25; x <= 30; x++)
  for (let z = 30; z <= 32; z++)
    if (!((x === 25 || x === 30) && z === 32))
      set(x, 19, z, "#fff1d1");
box(26, 19, 29, 29, 19, 30, "#fff1d1");
set(26, 18, 31, "#242631");
set(29, 18, 31, "#242631");
set(27, 18, 30, "#242631");
for (const x of [26, 28]) set(x, 18, 29, "#242631");
line(23, 19, 27, 32, 19, 25, 0, "#fff1d1");
line(23, 19, 25, 32, 19, 27, 0, "#fff1d1");

// Clumps of seaweed at the waterline and caught against the hull.
for (const [x, y, z] of [
  [11, 17, 3], [38, 18, 3], [10, 27, 3], [39, 30, 3],
  [17, 39, 3], [32, 39, 3], [7, 13, 2], [42, 15, 2]
]) {
  line(x, y, z, x + 1, y, z + 4, 0, "#328e55");
  line(x, y, z + 1, x - 1, y - 1, z + 3, 0, "#42aa61");
  set(x + 1, y, z + 4, "#65bb63");
}

// Pale barnacles scattered across the lower planking.
for (const [y, h, side] of [
  [14, 8, -1], [16, 11, 1], [21, 6, -1], [25, 9, 1],
  [30, 7, -1], [32, 12, 1], [18, 7, 1], [27, 6, -1]
]) {
  const x = Math.round(24 + side * hullWidth(y) *
    (0.37 + 0.63 * (h - 3) / 13));
  set(x, y, h + tilt(x, y), "#f2e9ca");
  set(x, y, h + 1 + tilt(x, y), "#fff8db");
  set(x, y + 1, h + tilt(x, y), "#cfceaa");
}

// A few unmistakably shiny pirate coins in the sand.
for (const [x, y] of [[33, 10], [35, 12], [37, 9], [13, 36], [16, 40]]) {
  cylinder(x, y, 3, 1, 1, gold);
  set(x, y, 4, "#ffe875");
}