// === BUILD PLAN ===
// Subject: COLOSSAL PANGOLIN (organic / creature), walking pose, ~80 blocks nose-to-tail
// Setting: red-earth savanna, termite mounds, flat-topped acacias
//
// SIDE SILHOUETTE (Z horizontal left=snout, Y vertical):
// ........########........
// ....############........ <- armoured back, banded scale rows (proud rims)
// ..#####################..
// .####################.####.. <- tail as long as the body, dragging
// ..##..##........##..##..... <- 4 short stout legs
// ========================== <- ground pad Y6, feet at Y7
//
// TOP SILHOUETTE (X horizontal, Z vertical downward = head to tail):
// .......o....... o = small narrow head + tapering snout
// ......###......
// ....#######....
// ...#########... widest at mid-body (rx 8)
// ...#########...
// ....#######....
// .....####......
// .......###..... tail curves off to +X behind the animal
// ........####...
// ..........####.
// ............##.
//
// PROPORTIONS (organic-builds rules): head ~1/5 of body height, legs SHORT and stout
// (pangolin is low-slung), tail = body length, thick at base tapering to 2 blocks.
// +Y is UP: dy>0 = armoured BACK (scales), dy<0 = soft pale BELLY. Checked.
//
// MATERIALS PLAN (what goes where + WHY):
// scales : noisePalette brown_terracotta/stripped_dark_oak/packed_mud/moss — WHY: olive-brown
// keratin with blotchy variation, never monomaterial
// rims : dark_oak_wood ring PROUD by 1 every 3 stations — WHY: this is the
// "overlapping scale armour" read; geometric relief, not just colour
// belly : light_gray/white_terracotta on the lower 1/3 — WHY: pale soft
// underside = the value contrast that makes the silhouette legible
// claws : blackstone taper on the FORELIMBS only — WHY: digging claws
// ground : patches red_sand/terracotta/coarse_dirt + termite mound cones — WHY: red-earth
// === END PLAN ===
var padY = 6, groundY = 7;
// ---------- palettes ----------
var SCALE = noisePalette([
{ block: 'dark_oak_planks', weight: 34 },
{ block: 'dark_oak_wood', weight: 24 },
{ block: 'stripped_dark_oak_log[axis=y]', weight: 18 },
{ block: 'brown_terracotta', weight: 14 },
{ block: 'mud', weight: 10 }
], { seed: 17, freq: 0.14 });
var RIM = noisePalette([
{ block: 'dark_oak_wood', weight: 55 },
{ block: 'stripped_dark_oak_wood', weight: 25 },
{ block: 'brown_terracotta', weight: 20 }
], { seed: 23, freq: 0.2 });
var BELLY = noisePalette([
{ block: 'light_gray_terracotta', weight: 45 },
{ block: 'white_terracotta', weight: 30 },
{ block: 'brown_terracotta', weight: 25 }
], { seed: 31, freq: 0.18 });
var LIMB = noisePalette([
{ block: 'brown_terracotta', weight: 45 },
{ block: 'packed_mud', weight: 30 },
{ block: 'stripped_dark_oak_log[axis=y]', weight: 25 }
], { seed: 41, freq: 0.2 });
var MOUND = noisePalette([
{ block: 'terracotta', weight: 35 },
{ block: 'red_sand', weight: 25 },
{ block: 'red_terracotta', weight: 20 },
{ block: 'coarse_dirt', weight: 20 }
], { seed: 51, freq: 0.1 });
// ---------- 1. TERRAIN: savanna with a flat pad under the animal ----------
var land = blob(64, 46, 56, { irregularity: 0.38, seed: 19 });
var h = hills({ base: padY, amp: 3, scale: 0.03, seed: 8 });
var pad = flatten(h, { x0: 40, z0: 5, x1: 100, z1: 86 }, padY, 10);
var gmat = patches(['red_sand', 'orange_terracotta', 'smooth_red_sandstone', 'sand'], 0.05, 6);
surface(land, pad, gmat, { sub: 'red_sand', deep: 'smooth_red_sandstone', depth: 5 });
// ---------- helpers ----------
// filled ellipsoid band: only cells whose normalised dy is in [lo, hi]
function ellipBand(cx, cy, cz, rx, ry, rz, pat, lo, hi) {
var x0 = Math.floor(cx - rx), x1 = Math.ceil(cx + rx);
var y0 = Math.floor(cy - ry), y1 = Math.ceil(cy + ry);
var z0 = Math.floor(cz - rz), z1 = Math.ceil(cz + rz);
for (var x = x0; x <= x1; x++) {
var nx = (x - cx) / rx;
for (var y = y0; y <= y1; y++) {
if (y < groundY) continue;
var ny = (y - cy) / ry;
if (ny < lo || ny > hi) continue;
for (var z = z0; z <= z1; z++) {
var nz = (z - cz) / rz;
if (nx * nx + ny * ny + nz * nz <= 1.0) pb(x, y, z, pat);
}
}
}
}
// ---------- 2. SPINE PATH (head -> tail tip), x,y,z,rx,ry ----------
var CP = [
[60, 13, 12, 2.0, 2.0],
[60, 14, 17, 3.2, 2.8],
[60, 15, 23, 4.6, 4.2],
[60, 17, 30, 6.8, 6.4],
[60, 18, 38, 7.8, 7.5],
[60, 18, 46, 8.0, 7.7],
[60, 17, 54, 7.4, 7.0],
[62, 15, 61, 6.4, 5.8],
[67, 13, 68, 5.4, 4.8],
[75, 11, 73, 4.4, 3.8],
[84, 10, 74, 3.4, 3.0],
[92, 9, 70, 2.2, 2.0]
];
var stations = [];
var si, k;
for (si = 0; si < CP.length - 1; si++) {
var a = CP[si], bq = CP[si + 1];
var dx = bq[0] - a[0], dy = bq[1] - a[1], dz = bq[2] - a[2];
var dlen = Math.sqrt(dx * dx + dy * dy + dz * dz);
var n = Math.ceil(dlen / 1.2);
for (k = 0; k < n; k++) {
var t = k / n;
stations.push([
a[0] + dx * t, a[1] + dy * t, a[2] + dz * t,
a[3] + (bq[3] - a[3]) * t, a[4] + (bq[4] - a[4]) * t
]);
}
}
stations.push(CP[CP.length - 1]);
// ---------- 3. BODY: pale belly + armoured back, with PROUD SCALE-ROW RIMS ----------
for (si = 0; si < stations.length; si++) {
var s = stations[si];
var rx = s[3], ry = s[4], rz = Math.max(1.6, rx * 0.95);
ellipBand(s[0], s[1], s[2], rx, ry, rz, BELLY, -1.05, -0.42);
ellipBand(s[0], s[1], s[2], rx, ry, rz, SCALE, -0.42, 1.05);
// overlapping keratin scale row: a ring 1 block PROUD on the upper 2/3, every 3rd station
if (si % 3 === 0 && si > 2 && rx > 2.4) {
ellipBand(s[0], s[1], s[2], rx + 0.85, ry + 0.85, Math.max(1.1, rz * 0.42), RIM, -0.25, 1.05);
}
}
// ---------- 4. HEAD DETAIL: narrow snout, eyes, ear, nostril ----------
ellipBand(60, 13.5, 10, 1.6, 1.4, 2.4, SCALE, -1.05, 1.05); // snout tip
pb(60, 13, 9, 'polished_blackstone'); // nose
pb(58, 15, 17, 'black_concrete'); pb(62, 15, 17, 'black_concrete'); // eyes
pb(58, 16, 17, 'dark_oak_wood'); pb(62, 16, 17, 'dark_oak_wood'); // brow
pb(57, 16, 20, 'brown_terracotta'); pb(63, 16, 20, 'brown_terracotta'); // ears
// slight asymmetry: head turned a touch to the left
ellipBand(58.6, 14.2, 15, 2.2, 2.0, 2.2, SCALE, -1.05, 1.05);
// --- FIX: a HEAD that reads as a head. Raise and separate it from the shoulder mass,
// give it a real neck taper and a long narrow snout, in a lighter tone than the armour.
var HEADMAT = noisePalette([
{ block: 'brown_terracotta', weight: 45 },
{ block: 'packed_mud', weight: 30 },
{ block: 'dark_oak_planks', weight: 25 }
], { seed: 19, freq: 0.16 });
// neck: narrow waist between shoulder and skull so the head is not fused to the body
ellipBand(60, 16.5, 24, 3.4, 3.2, 3.0, HEADMAT, -1.05, 1.05);
ellipBand(60, 16.2, 21, 3.0, 2.9, 2.6, HEADMAT, -1.05, 1.05);
// skull, lifted clear of the back line
ellipBand(60, 16.0, 18, 3.4, 3.0, 3.2, HEADMAT, -1.05, 1.05);
// long tapering snout
ellipBand(60, 15.2, 14, 2.4, 2.1, 2.6, HEADMAT, -1.05, 1.05);
ellipBand(60, 14.6, 11, 1.7, 1.5, 2.2, HEADMAT, -1.05, 1.05);
ellipBand(60, 14.2, 9, 1.2, 1.1, 1.6, HEADMAT, -1.05, 1.05);
pb(60, 14, 7, 'polished_blackstone');
pb(59, 14, 8, 'polished_blackstone'); pb(61, 14, 8, 'polished_blackstone');
// pale throat so the head separates from the dark armour
ellipBand(60, 14.6, 20, 3.0, 2.6, 2.8, BELLY, -1.05, -0.35);
// eyes + brow + small ears, placed on the NEW skull
pb(57, 17, 18, 'black_concrete'); pb(63, 17, 18, 'black_concrete');
pb(57, 18, 18, 'dark_oak_wood'); pb(63, 18, 18, 'dark_oak_wood');
pb(58, 18, 21, 'brown_terracotta'); pb(62, 18, 21, 'brown_terracotta');
// ---------- 5. LEGS (short + stout) and CLAWED FOREFEET ----------
function leg(lx, lz, topY, rr, forward) {
var x, y, z;
for (x = Math.round(lx - rr); x <= Math.round(lx + rr); x++) {
for (z = Math.round(lz - rr); z <= Math.round(lz + rr); z++) {
var d = Math.sqrt((x - lx) * (x - lx) + (z - lz) * (z - lz));
if (d > rr) continue;
for (y = groundY; y <= topY; y++) pb(x, y, z, LIMB);
}
}
// foot pad
for (x = Math.round(lx - rr - 1); x <= Math.round(lx + rr + 1); x++) {
for (z = Math.round(lz - rr - 1); z <= Math.round(lz + rr + 1); z++) {
var d2 = Math.sqrt((x - lx) * (x - lx) + (z - lz) * (z - lz));
if (d2 <= rr + 1) pb(x, groundY, z, LIMB);
}
}
if (forward) {
// three big digging claws pointing -Z
var cxs = [lx - 2, lx, lx + 2];
for (var c = 0; c < 3; c++) {
pb(Math.round(cxs[c]), groundY, Math.round(lz - rr - 1), 'polished_blackstone');
pb(Math.round(cxs[c]), groundY, Math.round(lz - rr - 2), 'blackstone');
ps(Math.round(cxs[c]), groundY, Math.round(lz - rr - 3), 'polished_blackstone_stairs[facing=south]');
}
}
}
leg(53, 30, 12, 3.2, true); // front left (stepping forward)
leg(67, 33, 12, 3.2, true); // front right
leg(53, 53, 12, 3.4, false); // hind left
leg(67, 52, 12, 3.4, false); // hind right
// ---------- 6. TAIL SCALE KEEL (extra ridge on the tail top) ----------
for (si = 0; si < stations.length; si++) {
var s2 = stations[si];
if (s2[2] < 56 && s2[0] < 63) continue;
if (s2[3] < 2.2) continue;
pb(Math.round(s2[0]), Math.round(s2[1] + s2[4]), Math.round(s2[2]), 'dark_oak_wood');
}
// ---------- 7. SCENE: termite mounds, acacias, dry scrub, dust ----------
function mound(mx, mz, r, hh) {
makeCone(mx, groundY, mz, MOUND, r, hh, false);
makeCone(mx + 1, groundY, mz + 1, MOUND, Math.max(1, r - 2), hh - 3, false);
pb(mx, groundY + hh, mz, 'red_terracotta');
}
mound(30, 28, 5, 13);
mound(24, 58, 4, 10);
mound(100, 30, 6, 15);
mound(104, 66, 4, 11);
mound(46, 88, 4, 9);
function acacia(tx, tz, hh) {
var y;
for (y = groundY; y < groundY + hh; y++) pb(tx, y, tz, 'acacia_log[axis=y]');
drawLine(tx, groundY + hh - 1, tz, tx - 3, groundY + hh + 1, tz - 2, 'acacia_log', 1);
drawLine(tx, groundY + hh - 1, tz, tx + 3, groundY + hh + 1, tz + 2, 'acacia_log', 1);
var leaf = noisePalette([
{ block: 'acacia_leaves[persistent=true]', weight: 60 },
{ block: 'azalea_leaves[persistent=true]', weight: 25 },
{ block: 'oak_leaves[persistent=true]', weight: 15 }
], { seed: 61 });
makeCylinder(tx, groundY + hh + 1, tz, leaf, 7, 2, false);
makeCylinder(tx, groundY + hh + 3, tz, leaf, 4, 1, false);
}
acacia(22, 40, 9);
acacia(108, 48, 10);
acacia(40, 92, 8);
// dry scrub scattered by low-frequency noise (not per-block confetti)
var qx, qz;
for (qx = 10; qx <= 115; qx += 3) {
for (qz = 8; qz <= 92; qz += 3) {
var nv = noise2d(qx * 0.09, qz * 0.09);
if (nv > 0.34) {
if ((qx + qz) % 6 === 0) pb(qx, groundY, qz, 'dead_bush');
else if ((qx + qz) % 6 === 2) pb(qx, groundY, qz, 'short_grass');
}
}
}