// === BUILD PLAN ===
// Subject: GIANT HERMIT CRAB (colossal, ~40 long x 34 wide x 26 tall) hauling a spiral conch shell
// Orientation: walking toward +Z / +X (the camera faces those sides) — big claw on the +X side, raised.
//
// SIDE SILHOUETTE (Y up, Z to the right = forward):
// ..@@@@.. @ = conch spiral (grows toward the aperture)
// .@@@@@@@@@. oo o = eye stalks
// .@@@@@@@@@@@. ..####.. # = cephalothorax (orange-red armour)
// '@@@@@@@@' .########.
// \\ / ## ## \ \\ = abdomen inside the shell
// \\____/ /| |\ \ / = jointed legs (knee ABOVE the body)
// ^^ ^^ ^ ^ ^^ ^
// ~~~~~ tide pool water ~~~~~~ mossy rocks ~~~~~
//
// FRONT SILHOUETTE (Y up, X across):
// o o
// .###########. big claw raised on the right (+X)
// / ######### \_ small claw tucked left
// /_/ ##### \_\ \
// ^ ^ ^ ^ ^ ^ ^
//
// MATERIALS PLAN:
// carapace : noisePalette orange_terracotta40/red_terracotta25/orange_concrete20/terracotta15
// — WHY: mottled crustacean armour, warm value contrast against the pale shell
// legs/claw: red_terracotta + orange_concrete + polished_blackstone tips (dark tips read as chitin)
// shell : ALTERNATING BANDS smooth_quartz/calcite/pink bands vs brown_terracotta ridges
// — WHY: the spiral bands are what make a conch READ as a conch
// ground : blob + hills + flatten pad at Y10, carved tide pool with water/sand/prismarine
// props : kelp, sea pickles, coral "starfish", mossy boulders, driftwood
// === END PLAN ===
var padY = 10;
var groundY = padY + 1; // feet rest here
// === TERRAIN : shore pad + carved tide pool ===
var land = blob(34, 32, 33, {irregularity: 0.42, seed: 9});
var h = hills({base: 9, amp: 3, scale: 0.05, seed: 11});
h = flatten(h, {cx: 34, cz: 32, r: 26}, padY, 9);
var pool = blob(26, 46, 13, {irregularity: 0.55, seed: 21});
var shoreMat = function (x, z) {
var n = noise2d('perlin', x, z, {seed: 33});
if (pool(x, z)) return 'sand';
if (n > 0.7) return 'moss_block';
if (n > 0.45) return 'stone';
if (n > 0.25) return 'gravel';
return 'sand';
};
surface(land, h, shoreMat, {sub: 'sand', deep: 'stone', depth: 4});
// carve the pool and fill it with water
for (var wx = 8; wx <= 46; wx++) {
for (var wz = 30; wz <= 62; wz++) {
if (pool(wx, wz)) {
fill(wx, padY - 2, wz, wx, padY + 3, wz, 'air');
pb(wx, padY - 3, wz, noise2d('perlin', wx, wz, {seed: 5}) > 0.5 ? 'sand' : 'prismarine');
fill(wx, padY - 2, wz, wx, padY, wz, 'water');
}
}
}
// === PALETTES ===
var shellA = noisePalette([
{block: 'smooth_quartz', weight: 42}, {block: 'calcite', weight: 28},
{block: 'white_terracotta', weight: 18}, {block: 'bone_block', weight: 12}
], {seed: 4, freq: 0.16});
var shellB = noisePalette([
{block: 'terracotta', weight: 38}, {block: 'brown_terracotta', weight: 30},
{block: 'pink_terracotta', weight: 20}, {block: 'red_sand', weight: 12}
], {seed: 6, freq: 0.18});
var shellRidge = noisePalette([
{block: 'brown_terracotta', weight: 55}, {block: 'granite', weight: 25}, {block: 'terracotta', weight: 20}
], {seed: 7, freq: 0.2});
var carapace = noisePalette([
{block: 'orange_terracotta', weight: 38}, {block: 'red_terracotta', weight: 24},
{block: 'orange_concrete', weight: 22}, {block: 'terracotta', weight: 16}
], {seed: 13, freq: 0.13});
var limbPal = noisePalette([
{block: 'red_terracotta', weight: 42}, {block: 'orange_terracotta', weight: 33},
{block: 'orange_concrete', weight: 25}
], {seed: 17, freq: 0.22});
// ellipsoid sculpting helper (cross-section per Y slice)
function ellipsoid(cx, cy, cz, rx, ry, rz, pat, squashBelly) {
var dx, dy, dz;
for (dy = -ry; dy <= ry; dy++) {
var fy = dy / ry;
var k = Math.sqrt(Math.max(0, 1 - fy * fy));
var ax = rx * k, az = rz * k;
if (squashBelly && dy < 0) { ax = ax * 0.9; az = az * 0.92; }
for (dx = -Math.ceil(ax); dx <= Math.ceil(ax); dx++) {
for (dz = -Math.ceil(az); dz <= Math.ceil(az); dz++) {
var v = (dx * dx) / (ax * ax + 0.001) + (dz * dz) / (az * az + 0.001);
if (v <= 1.02) pb(cx + dx, cy + dy, cz + dz, pat);
}
}
}
}
// === CONCH SHELL : logarithmic spiral of growing spheres, banded ===
var shCx = 32, shCz = 20;
var steps = 30;
for (var t = 0; t < steps; t++) {
var f = t / (steps - 1);
var ang = 2.2 + f * 5.0; // ~1.4 turns
var d = 1.5 + f * f * 13.5; // spiral distance grows fast (log spiral)
var rr = 1.0 + f * 6.4; // whorl thickness grows
var sx = Math.round(shCx + Math.cos(ang) * d);
var sz = Math.round(shCz + Math.sin(ang) * d);
var sy = Math.round(14 + f * 9);
var pal = (t % 5 < 3) ? shellA : shellB;
ellipsoid(sx, sy, sz, Math.round(rr), Math.round(rr * 0.92), Math.round(rr), pal, false);
// spiral ridge knobs on the outer flank (what makes it read as a conch)
if (t > 10 && t % 3 === 0) {
var kx = Math.round(sx + Math.cos(ang) * (rr + 1));
var kz = Math.round(sz + Math.sin(ang) * (rr + 1));
ellipsoid(kx, sy + Math.round(rr * 0.35), kz, 2, 2, 2, shellRidge, false);
pb(kx + 1, sy + Math.round(rr * 0.35) + 2, kz, 'brown_terracotta');
pb(kx, sy + Math.round(rr * 0.35) + 2, kz + 1, 'brown_terracotta');
}
}
// apex spike
ellipsoid(30, 13, 12, 2, 2, 2, shellB, false);
pb(30, 12, 10, 'brown_terracotta');
// aperture lip (flared opening toward the crab)
var apX = Math.round(shCx + Math.cos(2.2 + 5.0) * 15);
var apZ = Math.round(shCz + Math.sin(2.2 + 5.0) * 15);
ellipsoid(apX, 22, apZ, 8, 7, 8, shellA, false);
ellipsoid(apX, 22, apZ + 2, 6, 5, 6, 'air', false);
// lip rim
ellipsoid(apX + 1, 22, apZ + 5, 6, 6, 2, shellB, false);
// === BODY : cephalothorax under/in front of the shell ===
var bx = 34, by = 19, bz = 37;
ellipsoid(bx, by, bz, 10, 6, 8, carapace, true);
// abdomen linking the body into the shell aperture
drawLine(bx, by, bz - 6, apX, 21, apZ + 3, carapace, 5);
// carapace ridge + eye-socket brow (stairs for surface detail on the curve)
for (var rx2 = -8; rx2 <= 8; rx2++) {
pb(bx + rx2, by + 6, bz + Math.round(Math.abs(rx2) * 0.2), 'red_terracotta');
if (rx2 % 3 === 0) ps(bx + rx2, by + 7, bz - 1, 'polished_blackstone_slab[type=bottom]');
}
// front rim / mouthparts
for (var mx = -5; mx <= 5; mx++) {
pb(bx + mx, by - 1, bz + 8, 'brown_terracotta');
if (mx % 2 === 0) pb(bx + mx, by - 2, bz + 7, 'polished_blackstone');
}
ps(bx - 1, by, bz + 9, 'polished_blackstone_stairs[facing=south,half=bottom]');
ps(bx + 1, by, bz + 9, 'polished_blackstone_stairs[facing=south,half=bottom]');
// === EYE STALKS ===
function eyeStalk(ex, ez, lean) {
drawLine(ex, by + 5, ez, ex + lean, by + 11, ez + 2, 'orange_concrete', 1);
ellipsoid(ex + lean, by + 12, ez + 2, 2, 2, 2, 'black_concrete', false);
pb(ex + lean + 1, by + 13, ez + 3, 'white_concrete');
pb(ex + lean - 1, by + 12, ez + 3, 'white_concrete');
}
eyeStalk(bx - 4, bz + 5, -1);
eyeStalk(bx + 4, bz + 5, 1);
// antennae
drawLine(bx - 2, by + 3, bz + 8, bx - 8, by + 9, bz + 17, 'brown_terracotta', 1);
drawLine(bx + 2, by + 3, bz + 8, bx + 9, by + 8, bz + 18, 'brown_terracotta', 1);
// === LEGS : 3 jointed pairs, knee ABOVE the body line, dark chitin tips ===
function leg(side, idx) {
var ax = bx + side * 8;
var az = bz + 4 - idx * 6;
var ay = by + 1;
var kx = bx + side * (15 + idx * 2);
var ky = by + 6 - idx;
var kz = az + 2 - idx;
var fx = bx + side * (17 + idx * 3);
var fz = az + 5 - idx * 3;
var fy = groundY;
drawLine(ax, ay, az, kx, ky, kz, limbPal, 3); // femur, rising outward
drawLine(kx, ky, kz, fx, fy + 1, fz, limbPal, 2); // tibia, dropping to the ground
// pointed dactyl
pb(fx, fy, fz, 'polished_blackstone');
pb(fx, fy - 1, fz, 'polished_blackstone');
// spikes along the femur
pb(kx, ky + 2, kz, 'polished_blackstone');
pb(Math.round((ax + kx) / 2), ky + 1, Math.round((az + kz) / 2), 'polished_blackstone');
// splash where the leg meets the water
if (pool(fx, fz)) { pb(fx + 1, fy, fz, 'water'); pb(fx, fy, fz + 1, 'water'); }
}
leg(1, 0); leg(1, 1); leg(1, 2);
leg(-1, 0); leg(-1, 1); leg(-1, 2);
// === BIG CLAW (raised, +X side) ===
drawLine(bx + 8, by + 2, bz + 4, bx + 15, by + 8, bz + 8, limbPal, 4); // merus
drawLine(bx + 15, by + 8, bz + 8, bx + 19, by + 11, bz + 13, limbPal, 4); // carpus
ellipsoid(bx + 21, by + 12, bz + 16, 5, 4, 6, limbPal, false); // palm (propodus)
ellipsoid(bx + 21, by + 12, bz + 16, 4, 3, 5, 'red_terracotta', false);
// fixed finger (lower)
drawLine(bx + 21, by + 10, bz + 21, bx + 24, by + 9, bz + 26, 'orange_concrete', 2);
pb(bx + 24, by + 9, bz + 27, 'polished_blackstone');
pb(bx + 25, by + 9, bz + 26, 'polished_blackstone');
// movable finger (upper, open)
drawLine(bx + 21, by + 14, bz + 20, bx + 25, by + 16, bz + 25, 'orange_concrete', 2);
pb(bx + 25, by + 16, bz + 26, 'polished_blackstone');
// claw teeth
pb(bx + 22, by + 12, bz + 23, 'bone_block');
pb(bx + 23, by + 13, bz + 24, 'bone_block');
// === SMALL CLAW (left, tucked low) ===
drawLine(bx - 8, by, bz + 5, bx - 13, by - 1, bz + 10, limbPal, 3);
ellipsoid(bx - 15, by - 1, bz + 13, 3, 3, 4, limbPal, false);
drawLine(bx - 15, by - 2, bz + 16, bx - 17, by - 2, bz + 20, 'orange_concrete', 1);
drawLine(bx - 15, by, bz + 16, bx - 18, by + 1, bz + 20, 'orange_concrete', 1);
pb(bx - 17, by - 2, bz + 21, 'polished_blackstone');
pb(bx - 18, by + 1, bz + 21, 'polished_blackstone');
// === SCENE : wet rocks, kelp, starfish, driftwood ===
var rockPal = noisePalette([
{block: 'mossy_cobblestone', weight: 34}, {block: 'stone', weight: 26},
{block: 'andesite', weight: 22}, {block: 'cobblestone', weight: 18}
], {seed: 3, freq: 0.15});
var rocks = [[14, 26], [20, 54], [50, 44], [46, 18], [56, 30], [12, 40], [40, 58], [58, 52]];
for (var i = 0; i < rocks.length; i++) {
var rgx = rocks[i][0], rgz = rocks[i][1];
var ry2 = Math.floor(h(rgx, rgz));
ellipsoid(rgx, ry2 + 1, rgz, 3 + (i % 3), 2 + (i % 2), 3 + ((i + 1) % 3), rockPal, false);
pb(rgx, ry2 + 3 + (i % 2), rgz, 'moss_carpet');
}
// kelp + sea pickles in the pool
for (var kx2 = 10; kx2 <= 44; kx2++) {
for (var kz2 = 32; kz2 <= 60; kz2++) {
if (!pool(kx2, kz2)) continue;
var kn = noise2d('perlin', kx2 * 2.1, kz2 * 2.1, {seed: 44});
if (kn > 0.9) {
pb(kx2, padY - 2, kz2, 'kelp_plant');
pb(kx2, padY - 1, kz2, 'kelp_plant');
pb(kx2, padY, kz2, 'kelp');
} else if (kn > 0.85) {
ps(kx2, padY - 2, kz2, 'sea_pickle[pickles=3,waterlogged=true]');
} else if (kn > 0.82) {
pb(kx2, padY - 3, kz2, 'tube_coral_block');
}
}
}
// starfish on the wet rocks + shore
function starfish(sx2, sz2, block) {
var sy2 = Math.floor(h(sx2, sz2)) + 1;
pb(sx2, sy2, sz2, block);
pb(sx2 + 1, sy2, sz2, block); pb(sx2 - 1, sy2, sz2, block);
pb(sx2, sy2, sz2 + 1, block); pb(sx2, sy2, sz2 - 1, block);
pb(sx2 + 2, sy2, sz2 + 1, block); pb(sx2 - 2, sy2, sz2 - 1, block);
}
starfish(18, 34, 'fire_coral_block');
starfish(48, 50, 'horn_coral_block');
starfish(52, 24, 'fire_coral_block');
// driftwood + shells on the sand
drawLine(50, groundY, 56, 57, groundY + 1, 60, 'stripped_oak_log[axis=x]', 1);
pb(53, groundY + 1, 58, 'stripped_oak_log[axis=z]');
ellipsoid(24, groundY + 1, 24, 2, 1, 2, shellB, false);
ellipsoid(44, groundY + 1, 12, 2, 1, 2, shellA, false);
// scattered shore cover
for (var gx = 6; gx <= 64; gx++) {
for (var gz = 6; gz <= 62; gz++) {
var gn = noise2d('perlin', gx * 1.8, gz * 1.8, {seed: 61});
if (gn > 0.9 && !pool(gx, gz)) {
var gy3 = Math.floor(h(gx, gz));
pb(gx, gy3 + 1, gz, gn > 0.95 ? 'dead_bush' : 'moss_carpet');
}
}
}