fogón

← Basics3D assetfogon/torre-de-castillo

Torre de castillo

by fogonCC021259 blocks
Torre de castilloDrag to turn it
// «Torre de castillo» · gpt-6-sol (medium)
// Castle turret («Torre de castillo»).

const cx = 24, cy = 24;
const oct = (x, y) => {
  const a = Math.abs(x - cx), b = Math.abs(y - cy);
  return Math.max(a, b) + 0.38 * Math.min(a, b);
};

const stone = ["#a9ab9b", "#babbaa", "#c8c8b4", "#d5d3bd"];
const trim = ["#b9b8a3", "#ceccb4", "#e0ddc4"];

function shade(x, y, z, top = false) {
  let i = y < 24 ? 2 : 1;
  if (x > 25) i++;
  if (z % 5 === 0) i--;
  if (rand() < 0.12) i += rand() < 0.5 ? -1 : 1;
  if (top) i++;
  return stone[Math.max(0, Math.min(3, i))];
}

// Uneven grassy footprint.
for (let x = 5; x <= 43; x++)
  for (let y = 6; y <= 42; y++) {
    const d = oct(x, y);
    if (d < 18.2 || (d < 19.4 && rand() > 0.43)) {
      set(x, y, 0, rand() < 0.3 ? "#596f42" : "#708551");
      if (d > 15.2 || rand() < 0.13)
        set(x, y, 1, ["#729149", "#91a95b", "#5f8144"][Math.floor(rand() * 3)]);
    }
  }

// Stepped stone foundation, with grass showing around it.
for (let z = 1; z <= 5; z++) {
  const radius = z <= 2 ? 16.4 : z === 3 ? 15.7 : 13.3;
  for (let x = 7; x <= 41; x++)
    for (let y = 7; y <= 41; y++)
      if (oct(x, y) <= radius)
        set(x, y, z, z === 3 ? trim[1 + (rand() < 0.15 ? 1 : 0)] : shade(x, y, z, z === 3));
}

// Solid, slightly faceted lower tower and its stone courses.
for (let z = 5; z <= 30; z++)
  for (let x = 10; x <= 38; x++)
    for (let y = 10; y <= 38; y++) {
      const d = oct(x, y);
      if (d > 12.1) continue;
      let c = shade(x, y, z);
      if (d > 10.7) {
        if (z === 10 || z === 17 || z === 24) c = "#898f83";
        else if (z === 11 || z === 18 || z === 25) c = "#c9c8b2";
        // Sparse vertical mortar lines, staggered between courses.
        else if ((Math.floor((x + (Math.floor(z / 5) % 2) * 3) / 6) * 6 ===
                  x + (Math.floor(z / 5) % 2) * 3) && rand() < 0.35)
          c = "#999e91";
      }
      set(x, y, z, c);
    }

// Slim raised stone ribs make the cylindrical walls read at this scale.
for (let x of [13, 18, 23, 28, 33, 37])
  for (let z = 7; z <= 27; z++) {
    for (let y = 9; y <= 24; y++) {
      if (oct(x, y) >= 11.6 && oct(x, y) <= 13.0) {
        set(x, y, z, z % 7 === 0 ? "#a5a99a" : "#c1c3af");
        break;
      }
    }
  }

// Dark recessed arrow slits on the front.
for (const [x, bottom, height] of [[17, 15, 9], [23, 20, 6]]) {
  for (let y = 9; y <= 16; y++)
    if (oct(x, y) >= 10.4 && oct(x, y) <= 13.1) {
      box(x, y, bottom, x + 1, y, bottom + height, "#555b55");
      set(x - 1, y, bottom + height + 1, "#dedac1");
      set(x + 2, y, bottom + height + 1, "#dedac1");
      break;
    }
}
// A slit on the right-hand side.
box(36, 22, 17, 38, 23, 24, "#555c57");
box(38, 21, 25, 38, 24, 25, "#d2d0ba");

// Projecting cornice beneath the battlements.
for (let z = 28; z <= 33; z++) {
  const radius = z === 28 ? 13.2 : z <= 30 ? 14.8 : 15.3;
  for (let x = 7; x <= 41; x++)
    for (let y = 7; y <= 41; y++)
      if (oct(x, y) <= radius) {
        let c = shade(x, y, z, z === 31);
        if (z === 28 || z === 30) c = "#8f9588";
        if (z === 31) c = trim[Math.floor(rand() * 3)];
        set(x, y, z, c);
      }
}

// Open upper platform: thick pale parapet, darker floor within.
for (let z = 34; z <= 37; z++)
  for (let x = 7; x <= 41; x++)
    for (let y = 7; y <= 41; y++) {
      const d = oct(x, y);
      if (d <= 15.1 && d >= 10.7)
        set(x, y, z, z === 37 ? "#dfdcc3" : shade(x, y, z, true));
      else if (z === 34 && d < 10.7)
        set(x, y, z, "#81877c");
    }

// Alternating high merlons and low gaps around the battlement ring.
for (let x = 8; x <= 40; x++)
  for (let y = 8; y <= 40; y++) {
    const d = oct(x, y);
    if (d < 11.1 || d > 15.1) continue;
    const angle = Math.atan2(y - cy, x - cx);
    const segment = Math.floor((angle + Math.PI) / (Math.PI / 8));
    const high = segment % 2 === 0;
    const top = high ? 42 : 38;
    for (let z = 38; z <= top; z++)
      set(x, y, z, z === top ? "#e9e4ca" : shade(x, y, z, true));
  }

// Slightly taller squared corner-like posts at the front of the crown.
for (const [x, y] of [[12, 13], [25, 9], [37, 14], [39, 27], [27, 39]]) {
  box(x - 1, y - 1, 37, x + 1, y + 1, 41, "#d3d0b8");
  box(x - 1, y - 1, 42, x + 1, y + 1, 42, "#e8e3c9");
}

// Front entrance: a deep dark doorway cut into the tower.
for (let x = 27; x <= 33; x++)
  for (let z = 4; z <= 15; z++) {
    const opening = z <= 11 || (z <= 13 && x >= 28 && x <= 32) ||
                    (z <= 15 && x >= 29 && x <= 31);
    if (opening)
      for (let y = 9; y <= 16; y++) set(x, y, z, null);
  }
box(28, 16, 4, 32, 16, 11, "#454b45");
box(29, 16, 12, 31, 16, 14, "#444a44");

// Chunky projecting arch surround and piers.
box(25, 10, 4, 27, 15, 13, "#c9c7ae");
box(33, 10, 4, 35, 15, 13, "#bdbda9");
box(26, 9, 12, 28, 15, 15, "#ded9bf");
box(32, 9, 12, 34, 15, 15, "#d0cbb3");
box(28, 9, 14, 32, 15, 16, "#dfdac0");
box(29, 8, 15, 31, 11, 16, "#e8e1c5");
for (let z of [6, 10]) {
  box(25, 9, z, 27, 10, z, "#a8ab9a");
  box(33, 9, z, 35, 10, z, "#a6a999");
}
box(27, 8, 3, 33, 10, 3, "#e0dcc4");

// A few small mossy patches along the base of the wall.
for (let i = 0; i < 95; i++) {
  const x = 8 + Math.floor(rand() * 33);
  const y = 8 + Math.floor(rand() * 33);
  const z = 2 + Math.floor(rand() * 5);
  if (oct(x, y) > 12 && oct(x, y) < 17 && get(x, y, z))
    set(x, y, z, rand() < 0.5 ? "#758558" : "#879668");
}

// Flagpole rising from the center of the open roof.
box(23, 23, 34, 24, 24, 46, "#796749");
box(23, 22, 43, 24, 22, 46, "#aa8952");
box(22, 22, 46, 25, 24, 46, "#d8ba70");

// Windblown red banner, with a ragged flying end.
for (let x = 25; x <= 36; x++) {
  const tip = x >= 34;
  const upper = x <= 30 ? 45 : 44;
  for (let z = tip ? 44 : 43; z <= upper; z++)
    set(x, 22, z, rand() < 0.25 ? "#a74243" : "#c75450");
}
box(26, 21, 44, 29, 21, 45, "#e27863");
box(30, 22, 43, 32, 22, 43, "#913d42");
set(37, 22, 44, "#a94747");