// === BUILD PLAN ===
// Subject: GIANT PANGOLIN, colossal statue tier. Construction order per docs: skeleton -> bulk -> curve
// shaping with stairs -> head detail -> armour plates last.
// Proportion target: LENGTH 64 x HEIGHT 14 (elongated, not a hump) ; belly 2 above ground so legs read
// SIDE SILHOUETTE:
// .............#####..........
// ........###############.....
// ...##########################____
// .#>############################____ (> snout low, tail drags)
// ....||.............||............
// CROSS-SECTION: ellipse rx=0.74r (narrow), ry=r ; upper 70% armoured, lower belly pale
// MATERIALS: scales exposed_cut_copper/cut_copper banded, plate rows as stairs IN-PLANE (shingle bevel),
// dorsal ridge protruding 1, belly terracotta, claws polished_blackstone, mound terracotta/red_sand
// === END PLAN ===
var CX = 48, padY = 8, footY = 9;
// === TERRAIN — calmer savanna so the bronze body reads against it ===
var ground = terrain({
cx: 48, cz: 50, radius: 43, baseY: padY - 1, amp: 4, scale: 0.04, seed: 33, irregularity: 0.4,
surface: [{ block: 'grass_block', w: 5 }, { block: 'coarse_dirt', w: 3 }, { block: 'red_sand', w: 2 }],
sub: 'dirt', deep: 'stone', depth: 4,
pads: [{ cx: 48, cz: 50, r: 30, y: padY, falloff: 12 }]
});
function rnd(a, b, c) { var v = Math.sin(a * 21.13 + b * 47.77 + c * 91.31) * 31871.7; return v - Math.floor(v); }
// === 1. SKELETON (spine control points: z, centreY, radius) ===
var SK = [[18, 11.0, 1.2], [23, 11.6, 2.0], [28, 12.4, 2.8], [33, 13.6, 3.6],
[38, 15.4, 5.0], [46, 16.6, 6.0], [54, 16.0, 5.6], [62, 13.6, 4.2],
[70, 11.4, 2.8], [76, 10.2, 1.8], [82, 9.6, 1.0]];
function spine(z) {
var i;
for (i = 0; i < SK.length - 1; i++) {
if (z >= SK[i][0] && z <= SK[i + 1][0]) {
var t = (z - SK[i][0]) / (SK[i + 1][0] - SK[i][0]);
return [SK[i][1] + t * (SK[i + 1][1] - SK[i][1]), SK[i][2] + t * (SK[i + 1][2] - SK[i][2])];
}
}
return null;
}
// === 2/3. BULK + SHINGLE PLATES ===
var z, dx, dy;
for (z = 18; z <= 82; z++) {
var s = spine(z);
if (s === null) continue;
var yc = s[0], r = s[1];
var rx = r * 0.74, ry = r;
var plateRow = (z % 3 === 0);
for (dx = -Math.ceil(rx) - 1; dx <= Math.ceil(rx) + 1; dx++) {
for (dy = -Math.ceil(ry) - 1; dy <= Math.ceil(ry) + 1; dy++) {
var nx = dx / (rx + 0.001), ny = dy / (ry + 0.001);
var nr = Math.sqrt(nx * nx + ny * ny);
if (nr > 1.0) continue;
var yy = Math.round(yc + dy);
if (yy < footY) continue;
var xx = CX + dx;
var shell = (nr > 0.62);
if (!shell) { pb(xx, yy, z, 'brown_terracotta'); continue; }
if (dy < -ry * 0.35) { // pale belly
pb(xx, yy, z, (rnd(xx, yy, z) > 0.6) ? 'brown_terracotta' : 'terracotta');
} else if (plateRow && nr > 0.86) { // shingle bevel row (OUTER skin only -> solid block always behind it)
ps(xx, yy, z, (rnd(xx, yy, z) > 0.5) ? 'cut_copper_stairs[facing=north,half=bottom]' : 'exposed_cut_copper_stairs[facing=north,half=bottom]');
} else {
pb(xx, yy, z, (rnd(xx, yy, z) > 0.45) ? 'cut_copper' : 'exposed_cut_copper');
}
}
}
// dorsal ridge — one row of bigger plates protruding along the spine
if (r > 1.6) {
var ridgeY = Math.round(yc + ry);
pb(CX, ridgeY, z, plateRow ? 'exposed_cut_copper' : 'cut_copper');
if (r > 3.5) {
ps(CX - 1, ridgeY, z, 'cut_copper_stairs[facing=west,half=bottom]');
ps(CX + 1, ridgeY, z, 'cut_copper_stairs[facing=east,half=bottom]');
}
}
}
// === 4. HEAD DETAIL: long snout low to the ground ===
var hz;
for (hz = 12; hz <= 22; hz++) {
var tt = (hz - 12) / 10.0;
var hrr = 0.8 + tt * 1.6;
var hyc = 10.6 + tt * 1.0;
for (dx = -3; dx <= 3; dx++) {
for (dy = -3; dy <= 3; dy++) {
if (dx * dx + dy * dy > hrr * hrr) continue;
var yh = Math.round(hyc + dy);
if (yh < footY) continue;
pb(CX + dx, yh, hz, (dy > 0) ? 'exposed_cut_copper' : 'brown_terracotta');
}
}
}
pb(CX - 2, 13, 25, 'black_concrete'); pb(CX + 2, 13, 25, 'black_concrete'); // eyes
pb(CX, 10, 11, 'polished_blackstone'); // nose tip
ps(CX - 2, 14, 26, 'cut_copper_stairs[facing=north,half=bottom]'); // brow plates
ps(CX + 2, 14, 26, 'cut_copper_stairs[facing=north,half=bottom]');
ps(CX, 15, 27, 'cut_copper_stairs[facing=north,half=bottom]');
// === LEGS (short, thick, 2-block clearance under the belly) ===
function leg(lx, lz, hipY, r0, r1, leanZ) {
var i, y, a, c;
var n = hipY - footY;
for (i = 0; i <= n; i++) {
y = hipY - i;
var t = i / (n + 0.001);
var rr = r0 + (r1 - r0) * t;
var zc = Math.round(lz + leanZ * t);
for (a = -3; a <= 3; a++) for (c = -3; c <= 3; c++) {
if (a * a + c * c > rr * rr) continue;
pb(lx + a, y, zc + c, (y <= footY + 1) ? 'brown_terracotta' : ((rnd(lx + a, y, zc + c) > 0.7) ? 'exposed_cut_copper' : 'terracotta'));
}
}
return zc;
}
var flz = leg(CX - 4, 38, 14, 2.2, 1.6, -5); // left foreleg reaching into the mound
var frz = leg(CX + 4, 38, 14, 2.2, 1.6, -1);
var hlz = leg(CX - 4, 54, 14, 2.7, 2.0, 2);
var hrz = leg(CX + 4, 54, 14, 2.7, 2.0, 0);
function claw(x, z0, dir) {
var k;
for (k = -1; k <= 1; k++) {
pb(x + k, footY, z0 + dir, 'polished_blackstone');
pb(x + k, footY, z0 + 2 * dir, 'polished_blackstone');
if (k === 0) pb(x + k, footY + 1, z0 + 2 * dir, 'polished_blackstone');
}
}
claw(CX - 4, flz - 1, -1); claw(CX + 4, frz - 1, -1);
claw(CX - 4, hlz + 1, 1); claw(CX + 4, hrz + 1, 1);
// === TERMITE MOUND (front-left, the left forepaw digs into its flank) ===
var mx = 36, mz = 27, my;
for (my = footY - 1; my <= 23; my++) {
var mr = (23 - my) * 0.45 + 0.7;
for (dx = -8; dx <= 8; dx++) for (dy = -8; dy <= 8; dy++) {
var dd = Math.sqrt(dx * dx + dy * dy);
if (dd > mr + rnd(dx, my, dy) * 1.0) continue;
var hv = rnd(mx + dx, my, mz + dy);
pb(mx + dx, my, mz + dy, hv > 0.72 ? 'red_sand' : (hv > 0.38 ? 'terracotta' : 'brown_terracotta'));
}
}
var SPR = [[mx - 2, mz - 2, 27], [mx + 2, mz + 2, 26], [mx - 3, mz + 3, 25]];
var si, sy;
for (si = 0; si < SPR.length; si++) {
for (sy = footY; sy <= SPR[si][2]; sy++) {
var sr = Math.max(0, (SPR[si][2] - sy) * 0.2);
for (dx = -2; dx <= 2; dx++) for (dy = -2; dy <= 2; dy++) {
if (Math.sqrt(dx * dx + dy * dy) > sr) continue;
pb(SPR[si][0] + dx, sy, SPR[si][1] + dy, (rnd(dx, sy, dy) > 0.5) ? 'terracotta' : 'brown_terracotta');
}
}
}
// dug scar + spoil heap where the claws bite in
var qx, qy;
for (qx = 40; qx <= 43; qx++) for (qy = footY; qy <= footY + 4; qy++) {
if (rnd(qx, qy, 30) > 0.4) { pb(qx, qy, 30, 'air'); pb(qx, qy, 31, 'air'); }
}
var qz;
for (qz = 30; qz <= 36; qz++) { pb(44, footY, qz, 'coarse_dirt'); pb(45, footY, qz, 'red_sand'); pb(43, footY, qz, 'red_sand'); }
// === ACACIA SCRUB ===
function acacia(ax, az, hgt) {
var gy = Math.round(ground(ax, az)), t, a, c;
for (t = 1; t <= hgt; t++) pb(ax, gy + t, az, 'acacia_log');
pb(ax + 1, gy + hgt, az, 'acacia_log'); pb(ax - 1, gy + hgt, az, 'acacia_log');
for (a = -4; a <= 4; a++) for (c = -4; c <= 4; c++) {
var d = Math.sqrt(a * a + c * c);
if (d > 4.3) continue;
ps(ax + a, gy + hgt + 1, az + c, 'acacia_leaves[persistent=true]');
if (d < 2.6) ps(ax + a, gy + hgt + 2, az + c, 'acacia_leaves[persistent=true]');
}
}
acacia(16, 40, 7); acacia(78, 34, 6); acacia(72, 76, 7); acacia(22, 78, 6);
// === SCRUB, TERMITE TRAILS, ROCKS ===
var gi, gx, gz;
for (gi = 0; gi < 300; gi++) {
gx = 8 + Math.floor(rnd(gi, 3, 9) * 80);
gz = 8 + Math.floor(rnd(gi, 11, 4) * 80);
var gy = Math.round(ground(gx, gz));
if (gy < 4) continue;
var p = rnd(gx, 7, gz);
if (p > 0.8) pb(gx, gy + 1, gz, 'dead_bush');
else if (p > 0.5) pb(gx, gy + 1, gz, 'short_grass');
else if (p > 0.2) pb(gx, gy + 1, gz, 'tall_grass');
else pb(gx, gy, gz, 'cobblestone');
}