// === BUILD PLAN ===
// Subject: colossal hermit crab dragging a barnacled ceramic amphora as its shell, on a tidal mudflat
// Facing: -X (head/claws west), amphora trailing +X (east). Scale: ~55 long x ~35 tall x ~40 wide
//
// SIDE SILHOUETTE (X horizontal, Y up):
// eyes ..oo. ....--\_ amphora point Y44
// claw <==() .####. ..######## \
// \ ######## .############### |
// \ ########## ################### |
// legs \/ \ | \ / ################ /
// ~~~~~~~~~~~~~~~~~~~~~~~~ mudflat Y10 ~~~~~~
//
// TOP SILHOUETTE (X horizontal, Z down): big RIGHT claw z+, small LEFT claw z-, 3 legs each side
//
// MATERIALS PLAN:
// carapace : noisePalette orange_terracotta(30)+red_terracotta(25)+orange_concrete(18)+brown_terracotta(17)+red_concrete(10)
// — WHY: mottled crustacean shell; noise gives blotches, not confetti
// legs/claw: red_concrete(35)+orange_concrete(30)+brown_terracotta(20)+terracotta(15), claw tips bone_block/white
// amphora : terracotta(38)+brown_terracotta(24)+smooth_red_sandstone(22)+granite(16) with calcite/bone barnacles
// sea dress: moss_block + dried_kelp_block + sea_pickle + kelp strands
// mudflat : patches(mud, clay, gravel, sand) + tide pools of water, driftwood, boulders
// PROCESS: DIRECT sculpting — ellipsoid() cores + tapered tube limbs (sphere sweep), asymmetry after
// === END PLAN ===
var GY = 10; // mudflat surface Y
var BX = 30, BY = 19, BZ = 36; // cephalothorax centre
var shellPal = noisePalette([
{block:"orange_terracotta",weight:30},{block:"red_terracotta",weight:25},
{block:"orange_concrete",weight:18},{block:"brown_terracotta",weight:17},
{block:"red_concrete",weight:10}], {freq:0.13, seed:44});
var limbPal = noisePalette([
{block:"red_concrete",weight:35},{block:"orange_concrete",weight:30},
{block:"brown_terracotta",weight:20},{block:"terracotta",weight:15}], {freq:0.18, seed:7});
var clawPal = noisePalette([
{block:"red_concrete",weight:45},{block:"orange_concrete",weight:35},
{block:"red_terracotta",weight:20}], {freq:0.2, seed:12});
var potPal = noisePalette([
{block:"terracotta",weight:38},{block:"brown_terracotta",weight:24},
{block:"smooth_red_sandstone",weight:22},{block:"granite",weight:16}], {freq:0.1, seed:66});
var crustPal = noisePalette([
{block:"calcite",weight:40},{block:"bone_block",weight:25},
{block:"diorite",weight:20},{block:"moss_block",weight:15}], {freq:0.3, seed:5});
var i, j, k, x, y, z;
function ellipsoid(cx, cy, cz, rx, ry, rz, mat) {
var xx, yy, zz, v;
for (xx = -rx; xx <= rx; xx++) {
for (yy = -ry; yy <= ry; yy++) {
for (zz = -rz; zz <= rz; zz++) {
v = (xx * xx) / (rx * rx) + (yy * yy) / (ry * ry) + (zz * zz) / (rz * rz);
if (v <= 1.02) pb(cx + xx, cy + yy, cz + zz, mat);
}
}
}
}
// tapered tube: sweeps spheres along a segment (jointed limbs, claw jaws, handles)
function tube(x1, y1, z1, x2, y2, z2, r1, r2, mat) {
var steps = Math.max(Math.abs(x2 - x1), Math.abs(y2 - y1), Math.abs(z2 - z1));
var t, rr, n;
if (steps < 1) steps = 1;
for (n = 0; n <= steps; n++) {
t = n / steps;
rr = r1 + (r2 - r1) * t;
if (rr < 1) rr = 1;
makeSphere(Math.round(x1 + (x2 - x1) * t), Math.round(y1 + (y2 - y1) * t), Math.round(z1 + (z2 - z1) * t), mat, Math.round(rr), false);
}
}
// === MUDFLAT (terrain superpowers, flat pad under the creature) ===
var flat = blob(42, 36, 44, {irregularity: 0.4, seed: 23});
var hf = hills({base: GY - 1, amp: 2, seed: 9});
var pad = flatten(hf, {x0: 8, z0: 12, x1: 76, z1: 60}, GY, 12);
var mudMat = patches(["mud", "mud", "clay", "gravel", "sand", "coarse_dirt"], 0.05, 3);
surface(flat, pad, mudMat, {sub: "clay", deep: "stone", depth: 4});
// tide pools: shallow water in scattered dishes
function pool(px, pz, r) {
var xx, zz, d;
for (xx = px - r; xx <= px + r; xx++) {
for (zz = pz - r; zz <= pz + r; zz++) {
d = Math.sqrt((xx - px) * (xx - px) + (zz - pz) * (zz - pz));
if (d > r) continue;
pb(xx, GY, zz, d > r - 1.2 ? "gravel" : "water");
if (d <= r - 1.2) pb(xx, GY - 1, zz, "clay");
if (d > r - 1.2 && (xx + zz) % 3 === 0) ps(xx, GY + 1, zz, "seagrass");
}
}
}
pool(16, 18, 6); pool(66, 20, 5); pool(24, 56, 7); pool(58, 58, 5); pool(44, 14, 4);
// === AMPHORA SHELL (trailing east, mouth facing the crab) ===
ellipsoid(57, 25, 36, 11, 11, 10, potPal);
tube(47, 23, 36, 41, 21, 36, 8, 7, potPal); // neck toward the crab
// wide rim of the mouth
for (i = 0; i < 360; i += 6) {
y = Math.round(21 + 8 * Math.sin(i * Math.PI / 180));
z = Math.round(36 + 8 * Math.cos(i * Math.PI / 180));
pb(41, y, z, "smooth_red_sandstone");
pb(40, y, z, potPal);
}
// hollow the mouth so the crab's body reads as emerging from it
for (x = 40; x <= 48; x++) {
for (y = 14; y <= 30; y++) {
for (z = 28; z <= 44; z++) {
if ((y - 21) * (y - 21) + (z - 36) * (z - 36) <= 30) pb(x, y, z, "air");
}
}
}
// tapering base with a point, tilted up and back
tube(66, 28, 36, 74, 38, 36, 7, 2, potPal);
pb(75, 39, 36, "smooth_red_sandstone");
// two handles
tube(50, 34, 28, 45, 30, 26, 2, 2, potPal);
tube(45, 30, 26, 43, 24, 30, 2, 2, potPal);
tube(50, 34, 44, 45, 30, 46, 2, 2, potPal);
tube(45, 30, 46, 43, 24, 42, 2, 2, potPal);
// incised bands around the belly
for (i = 0; i < 360; i += 4) {
for (j = 0; j < 3; j++) {
y = Math.round(25 + 9.5 * Math.sin(i * Math.PI / 180));
z = Math.round(36 + 9.5 * Math.cos(i * Math.PI / 180));
pb(52 + j * 6, y, z, "smooth_red_sandstone");
}
}
// barnacles + seaweed crust on the upper/back surface
for (x = 44; x <= 72; x++) {
for (y = 16; y <= 38; y++) {
for (z = 24; z <= 48; z++) {
var vv = ((x - 57) * (x - 57)) / 121 + ((y - 25) * (y - 25)) / 121 + ((z - 36) * (z - 36)) / 100;
if (vv > 0.86 && vv <= 1.02) {
if ((x * 7 + y * 13 + z * 5) % 11 === 0) pb(x, y, z, crustPal);
else if ((x * 3 + y * 5 + z * 11) % 17 === 0 && y > 26) pb(x, y, z, "moss_block");
}
}
}
}
// kelp streamers hanging off the shell
for (i = 0; i < 14; i++) {
x = 48 + (i * 5) % 22;
z = 27 + (i * 7) % 19;
y = 30 - (i % 4);
for (k = 0; k < 3 + (i % 4); k++) pb(x, y - k, z, k === 0 ? "moss_block" : "dried_kelp_block");
}
for (i = 0; i < 10; i++) pb(50 + (i * 3) % 18, 34 + (i % 3), 30 + (i * 5) % 13, "sea_pickle");
// === CEPHALOTHORAX (carapace) ===
ellipsoid(BX, BY, BZ, 9, 6, 10, shellPal);
ellipsoid(BX - 5, BY - 1, BZ, 6, 5, 8, shellPal); // head/front section
ellipsoid(BX + 7, BY - 1, BZ, 6, 5, 8, shellPal); // soft abdomen entering the amphora
// dorsal ridge + growth lines
for (z = BZ - 8; z <= BZ + 8; z++) {
if ((z + 40) % 3 !== 0) continue;
for (x = BX - 8; x <= BX + 5; x++) {
y = BY + Math.round(6 * Math.sqrt(Math.max(0, 1 - ((x - BX) * (x - BX)) / 81 - ((z - BZ) * (z - BZ)) / 100)));
if (y > BY) pb(x, y, z, "red_concrete");
}
}
// front brow / rostrum
tube(BX - 11, BY, BZ, BX - 14, BY - 1, BZ, 4, 2, shellPal);
ps(BX - 15, BY - 1, BZ, "red_concrete");
// mouthparts
for (i = 0; i < 3; i++) {
pb(BX - 13, BY - 3 - i, BZ - 1, "brown_terracotta");
pb(BX - 13, BY - 3 - i, BZ + 1, "brown_terracotta");
}
// === STALKED EYES ===
function eye(ez) {
tube(BX - 10, BY + 4, ez, BX - 12, BY + 9, ez, 2, 1, limbPal);
makeSphere(BX - 12, BY + 10, ez, "black_concrete", 2, false);
pb(BX - 13, BY + 11, ez, "white_concrete");
pb(BX - 13, BY + 10, ez - 1, "white_concrete");
}
eye(BZ - 4);
eye(BZ + 4);
// antennae
tube(BX - 13, BY + 1, BZ - 2, BX - 20, BY + 4, BZ - 6, 1, 1, "brown_terracotta");
tube(BX - 13, BY + 1, BZ + 2, BX - 21, BY + 3, BZ + 7, 1, 1, "brown_terracotta");
// === BIG RIGHT CLAW (oversized) ===
tube(BX + 1, BY - 2, BZ + 9, BX - 4, BY - 4, BZ + 17, 4, 4, clawPal); // merus
tube(BX - 4, BY - 4, BZ + 17, BX - 13, BY - 3, BZ + 19, 4, 5, clawPal); // carpus -> palm
ellipsoid(BX - 17, BY - 3, BZ + 19, 6, 5, 4, clawPal); // heavy palm
tube(BX - 21, BY - 1, BZ + 19, BX - 29, BY + 2, BZ + 18, 4, 2, clawPal); // fixed jaw (upper, open)
tube(BX - 21, BY - 5, BZ + 19, BX - 29, BY - 5, BZ + 18, 3, 2, clawPal); // dactyl (lower)
pb(BX - 30, BY + 2, BZ + 18, "bone_block");
pb(BX - 30, BY - 5, BZ + 18, "bone_block");
// serrated teeth on the jaws
for (i = 0; i < 6; i++) {
pb(BX - 22 - i, BY, BZ + 19 - (i % 2), "white_concrete");
pb(BX - 22 - i, BY - 4, BZ + 19 - (i % 2), "white_concrete");
}
// === SMALL LEFT CLAW ===
tube(BX + 1, BY - 3, BZ - 9, BX - 4, BY - 5, BZ - 14, 3, 3, clawPal);
tube(BX - 4, BY - 5, BZ - 14, BX - 11, BY - 5, BZ - 15, 3, 3, clawPal);
ellipsoid(BX - 13, BY - 5, BZ - 15, 4, 3, 3, clawPal);
tube(BX - 16, BY - 4, BZ - 15, BX - 21, BY - 2, BZ - 16, 2, 1, clawPal);
tube(BX - 16, BY - 7, BZ - 15, BX - 21, BY - 6, BZ - 16, 2, 1, clawPal);
pb(BX - 22, BY - 2, BZ - 16, "bone_block");
pb(BX - 22, BY - 6, BZ - 16, "bone_block");
// === SIX JOINTED WALKING LEGS ===
function leg(ax, side, reach, lift) {
var s = side; // +1 = right (z+), -1 = left (z-)
var kx = ax - 2, kz = BZ + s * (11 + reach);
tube(ax, BY - 4, BZ + s * 8, kx, BY + 1, kz, 3, 2, limbPal); // coxa/femur up to the knee
tube(kx, BY + 1, kz, kx - 1, GY + 3 + lift, kz + s * 4, 2, 2, limbPal); // tibia down
tube(kx - 1, GY + 3 + lift, kz + s * 4, kx - 3, GY + 1 + lift, kz + s * 6, 2, 1, limbPal); // dactyl (foot)
pb(kx - 4, GY + lift, kz + s * 6, "black_concrete"); // claw tip on the mud
if (lift === 0) {
// splash / footprint in the mud
pb(kx - 4, GY, kz + s * 6, "mud");
pb(kx - 5, GY, kz + s * 6, "clay");
}
}
leg(BX - 4, 1, 0, 0);
leg(BX + 2, 1, 2, 0);
leg(BX + 8, 1, 4, 2);
leg(BX - 4, -1, 1, 1);
leg(BX + 2, -1, 3, 0);
leg(BX + 8, -1, 4, 0);
// === SCENE DRESSING: boulders, driftwood, shells, scattered kelp ===
function boulder(bx, bz, r) {
ellipsoid(bx, GY + r - 1, bz, r, r - 1, r, noisePalette([{block:"stone",weight:40},{block:"andesite",weight:30},{block:"cobblestone",weight:20},{block:"moss_block",weight:10}], {freq:0.25, seed:bx}));
}
boulder(14, 46, 4); boulder(70, 46, 3); boulder(20, 12, 3); boulder(64, 12, 4); boulder(40, 62, 3);
for (i = 0; i < 3; i++) { ps(14 + i, GY + 4, 46, "sea_pickle"); ps(64 + i - 1, GY + 4, 12, "sea_pickle"); }
// driftwood
tube(8, GY + 1, 30, 18, GY + 2, 26, 2, 1, "stripped_oak_log");
tube(70, GY + 1, 54, 62, GY + 2, 58, 2, 1, "stripped_spruce_log");
// scattered small shells and weed
for (i = 0; i < 60; i++) {
x = 8 + (i * 17) % 68;
z = 12 + (i * 29) % 50;
if (Math.abs(x - 45) < 22 && Math.abs(z - 36) < 16) continue;
if (i % 5 === 0) ps(x, GY + 1, z, "seagrass");
else if (i % 5 === 1) pb(x, GY + 1, z, "dried_kelp_block");
else if (i % 5 === 2) pb(x, GY, z, "clay");
else if (i % 5 === 3) ps(x, GY + 1, z, "dead_bush");
else pb(x, GY + 1, z, "brain_coral_block");
}