// === BUILD PLAN ===
// Subject: GIANT PANGOLIN (organic, colossal) mid-stride, red-earth savanna
// SIDE SILHOUETTE (Z ->, Y up): snout low at z22 .. arched back peak z42 .. thick tail dragging to z78
// ..........####........
// .......##########.....
// ...###############....
// .####################..____
// snout>#####################______ <tail
// .. || ....... || ......
// fore hind
// FRONT CROSS-SECTION: rounded ellipse, rx=0.82*r, scaled shell on the upper 3/4, pale belly under
// MATERIALS PLAN:
// shell (upper, nr>0.72) : cut_copper + exposed_cut_copper BANDS every 4 z - WHY: reads as overlapping scale rows
// ridges : cut_copper_stairs protruding 1 out on every band - WHY: real 3D overlap, not a painted stripe
// belly (lower quarter) : terracotta / brown_terracotta - WHY: pale soft underside contrast
// core : brown_terracotta - WHY: cheap, never seen
// mound : terracotta/red_sand/brown_terracotta cone + spires
// ground : terrain() red savanna patches + flat pad y=8 (feet at 9)
// === END PLAN ===
var CXm = 48, padY = 8, footY = 9;
// === TERRAIN (superpowers) ===
var ground = terrain({
cx: 48, cz: 50, radius: 42, baseY: padY - 1, amp: 4, scale: 0.045, seed: 21, irregularity: 0.35,
surface: [{ block: 'red_sand', w: 3 }, { block: 'coarse_dirt', w: 3 }, { block: 'terracotta', w: 2 }, { block: 'grass_block', w: 2 }],
sub: 'red_sandstone', deep: 'stone', depth: 4,
pads: [{ cx: 48, cz: 50, r: 30, y: padY, falloff: 12 }]
});
// === SPINE: [z, centreY, radius] head -> tail ===
var SP = [[22, 11.4, 1.4], [25, 11.6, 2.2], [29, 12.6, 3.2], [32, 14.2, 4.3],
[36, 16.2, 6.0], [42, 18.2, 7.6], [50, 17.4, 7.2], [56, 14.6, 5.6],
[64, 11.9, 4.0], [72, 10.2, 2.6], [78, 9.4, 1.3]];
function spineAt(z) {
var i;
for (i = 0; i < SP.length - 1; i++) {
if (z >= SP[i][0] && z <= SP[i + 1][0]) {
var t = (z - SP[i][0]) / (SP[i + 1][0] - SP[i][0]);
return [SP[i][1] + t * (SP[i + 1][1] - SP[i][1]), SP[i][2] + t * (SP[i + 1][2] - SP[i][2])];
}
}
return null;
}
function hashv(a, b, c) {
var v = Math.sin(a * 12.9898 + b * 78.233 + c * 37.719) * 43758.5453;
return v - Math.floor(v);
}
// === BODY: interpolated elliptical cross-sections ===
var z, dx, dy;
for (z = 22; z <= 78; z++) {
var s = spineAt(z);
if (s === null) continue;
var yc = s[0], r = s[1];
var rx = r * 0.84, ry = r;
var band = (z % 4 === 0);
for (dx = -Math.ceil(rx); dx <= Math.ceil(rx); dx++) {
for (dy = -Math.ceil(ry); dy <= Math.ceil(ry); 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 = CXm + dx;
var blk;
if (nr > 0.7 && dy > -ry * 0.3) {
if (band) blk = 'exposed_cut_copper';
else blk = (hashv(xx, yy, z) > 0.85) ? 'exposed_cut_copper' : 'cut_copper';
} else if (nr > 0.68) {
blk = (hashv(xx, yy, z) > 0.7) ? 'brown_terracotta' : 'terracotta';
} else {
blk = 'brown_terracotta';
}
pb(xx, yy, z, blk);
}
}
// protruding SCALE RIDGE on every band row (upper arc)
if (band && r > 2.0) {
var a;
for (a = -78; a <= 78; a += 11) {
var rad = a * Math.PI / 180;
var px = Math.round(CXm + Math.sin(rad) * (rx + 0.75));
var py = Math.round(yc + Math.cos(rad) * (ry + 0.75));
if (py < footY) continue;
ps(px, py, z, 'cut_copper_stairs[facing=north,half=bottom]');
}
}
}
// === HEAD + SNOUT (low to the ground) ===
var hz, hr, hy;
for (hz = 21; hz <= 30; hz++) {
var t2 = (hz - 21) / 9.0;
hr = 0.9 + t2 * 2.4;
hy = 11.2 + t2 * 1.6;
for (dx = -3; dx <= 3; dx++) {
for (dy = -3; dy <= 3; dy++) {
if (dx * dx + dy * dy > hr * hr) continue;
var yh = Math.round(hy + dy);
if (yh < footY) continue;
var top = (dy > 0.3 * hr);
pb(CXm + dx, yh, hz, top ? 'exposed_cut_copper' : 'brown_terracotta');
}
}
}
// eyes + nostril
pb(CXm - 2, 13, 28, 'black_concrete'); pb(CXm + 2, 13, 28, 'black_concrete');
pb(CXm, 11, 20, 'polished_blackstone');
// small scaled crown over the head
ps(CXm, 15, 29, 'cut_copper_stairs[facing=north,half=bottom]');
ps(CXm - 1, 15, 29, 'cut_copper_stairs[facing=north,half=bottom]');
ps(CXm + 1, 15, 29, 'cut_copper_stairs[facing=north,half=bottom]');
// === LIMBS ===
function limb(x0, z0, hipY, r0, r1, lean) {
var y, i2, j2;
var steps = hipY - footY;
for (i2 = 0; i2 <= steps; i2++) {
y = hipY - i2;
var tt = i2 / (steps + 0.001);
var rr = r0 + (r1 - r0) * tt;
var zz = Math.round(z0 + lean * tt);
for (dx = -3; dx <= 3; dx++) {
for (j2 = -3; j2 <= 3; j2++) {
if (dx * dx + j2 * j2 > rr * rr) continue;
pb(x0 + dx, y, zz + j2, (y < footY + 2) ? 'brown_terracotta' : ((hashv(x0 + dx, y, zz + j2) > 0.75) ? 'exposed_cut_copper' : 'terracotta'));
}
}
}
return [x0, footY, Math.round(z0 + lean)];
}
// forelimbs (short, digging) — left one reaches toward the mound
var fl = limb(CXm - 5, 37, 15, 2.4, 1.7, -2);
var fr = limb(CXm + 5, 36, 15, 2.4, 1.7, 0);
// hind limbs (thicker)
var hl = limb(CXm - 5, 51, 14, 2.9, 2.1, 1);
var hr2 = limb(CXm + 5, 51, 14, 2.9, 2.1, 0);
// claws
function claws(x, y, z0, dir) {
var k;
for (k = -1; k <= 1; k++) {
pb(x + k, y, z0 + dir, 'polished_blackstone');
pb(x + k, y, z0 + dir * 2, 'polished_blackstone');
}
}
claws(fl[0], footY, fl[2] - 2, -1);
claws(fr[0], footY, fr[2] - 2, -1);
claws(hl[0], footY, hl[2] + 2, 1);
claws(hr2[0], footY, hr2[2] + 2, 1);
// === TERMITE MOUND (left of the forelimbs) ===
var mx = 37, mz = 34;
var my, mr, ang2;
for (my = footY - 1; my <= 21; my++) {
mr = (21 - my) * 0.52 + 0.8;
for (dx = -8; dx <= 8; dx++) {
for (dy = -8; dy <= 8; dy++) {
var dd = Math.sqrt(dx * dx + dy * dy);
if (dd > mr + hashv(dx, my, dy) * 0.9) continue;
var mb;
var hv = hashv(mx + dx, my, mz + dy);
if (hv > 0.72) mb = 'red_sand';
else if (hv > 0.4) mb = 'terracotta';
else mb = 'brown_terracotta';
pb(mx + dx, my, mz + dy, mb);
}
}
}
// mound spires
var SPI = [[mx - 2, mz - 3, 25], [mx + 2, mz + 3, 24], [mx - 4, mz + 2, 23]];
var si, sy;
for (si = 0; si < SPI.length; si++) {
for (sy = footY; sy <= SPI[si][2]; sy++) {
var sr = Math.max(0, (SPI[si][2] - sy) * 0.22);
for (dx = -3; dx <= 3; dx++) for (dy = -3; dy <= 3; dy++) {
if (Math.sqrt(dx * dx + dy * dy) > sr) continue;
pb(SPI[si][0] + dx, sy, SPI[si][1] + dy, (hashv(dx, sy, dy) > 0.5) ? 'terracotta' : 'brown_terracotta');
}
}
}
// dug scar where the left forelimb bites into the mound
var ex2, ey2;
for (ex2 = 40; ex2 <= 44; ex2++) for (ey2 = footY; ey2 <= footY + 3; ey2++) {
if (hashv(ex2, ey2, 35) > 0.45) pb(ex2, ey2, 35, 'air');
}
var dbz;
for (dbz = 33; dbz <= 38; dbz++) {
pb(43, footY, dbz, 'coarse_dirt'); pb(44, footY, dbz, 'red_sand');
}
// === ACACIA SCRUB ===
function acacia(ax, az, hgt) {
var gy = Math.round(ground(ax, az));
var t3, cx3, cz3;
for (t3 = 1; t3 <= hgt; t3++) pb(ax, gy + t3, az, 'acacia_log');
var lean = 1;
pb(ax + lean, gy + hgt, az, 'acacia_log');
for (cx3 = -4; cx3 <= 4; cx3++) {
for (cz3 = -4; cz3 <= 4; cz3++) {
var dd2 = Math.sqrt(cx3 * cx3 + cz3 * cz3);
if (dd2 > 4.2) continue;
ps(ax + lean + cx3, gy + hgt + 1, az + cz3, 'acacia_leaves[persistent=true]');
if (dd2 < 2.8) ps(ax + lean + cx3, gy + hgt + 2, az + cz3, 'acacia_leaves[persistent=true]');
}
}
}
acacia(18, 62, 6); acacia(74, 30, 5); acacia(78, 70, 6); acacia(24, 18, 5); acacia(60, 84, 6);
// === SCRUB / GRASS TUFTS ===
var gi, gx2, gz2;
for (gi = 0; gi < 260; gi++) {
gx2 = 8 + Math.floor(hashv(gi, 1, 7) * 80);
gz2 = 8 + Math.floor(hashv(gi, 5, 3) * 80);
var gy2 = Math.round(ground(gx2, gz2));
if (gy2 < 4) continue;
var pick = hashv(gx2, 2, gz2);
if (pick > 0.72) pb(gx2, gy2 + 1, gz2, 'dead_bush');
else if (pick > 0.35) pb(gx2, gy2 + 1, gz2, 'short_grass');
else pb(gx2, gy2 + 1, gz2, 'tall_grass');
}