// === BUILD PLAN ===
// Style: Steampunk airship MOORED at its mast (vehicle, bow toward +Z so the iso/front camera sees it)
// Envelope: ellipsoid rx=7 ry=6 rz=17 (~35 long : 14 wide = 2.5:1) centred at (0,27,0) -> doc "medium/large zeppelin"
// Gondola: z -11..+12 (24 long = ~60% of envelope), 5 wide, slung 2 blocks under the envelope on chains
// Mast: iron lattice, z=+24, Y1..Y30, gantry + mooring chains to the nose cone
//
// SIDE PROFILE (bow to the RIGHT, +Z):
// ____---‾‾‾‾‾‾‾‾‾‾‾‾‾---____
// fin<==== copper envelope (ribbed) ====>nose --- chains --> [MAST]
// ‾‾‾| |‾‾‾ chains ‾‾‾| |‾‾‾ |||
// [=== gondola: engine | cabin | bridge ===] |||
// prop O O prop
// ================ stone landing platform ================
//
// SECTION (envelope, looking down the Z axis): TOP VIEW (gondola):
// .....#####..... ..#####..
// ...#########... .#######.
// ..###########.. .#######.
// ...#########... ..#####..
// .....#####.....
//
// MATERIALS PLAN:
// envelope: noise copper_block(30)+exposed_copper(28)+weathered_copper(24)+oxidized_copper(18)
// - WHY: patina blotches give VOLUME on a huge curved surface (a single copper reads flat)
// ribs : waxed_exposed_copper / gold_block every 4 blocks of Z - WHY: brass frame reads the cigar shape
// nose+tail: iron_block + chiseled ribs; fins dark_oak + copper
// gondola : spruce planks + dark_oak trim + glass panes (portholes) - WHY: warm wood vs cold metal
// mast : iron_bars lattice + deepslate bricks base; platform stone/andesite/cobble mix
// Rooms (gondola): BRIDGE (helm, glazed nose, charts), CABIN (bunk, table, lanterns), ENGINE (furnace, pipes, gauges)
// === END PLAN ===
var padY = 0;
var ECX = 0, ECY = 27, ECZ = 0;
var RX = 7, RY = 6, RZ = 17;
var GTOP = 18, GBOT = 14; // gondola shell
var x, y, z, k, i;
// ---------- 1. GROUND + LANDING PLATFORM ----------
var ground = terrain({
cx: 0, cz: 0, radius: 30, baseY: padY, amp: 4, scale: 0.05, seed: 23,
surface: [{ block: 'grass_block', w: 62 }, { block: 'coarse_dirt', w: 20 }, { block: 'gravel', w: 18 }],
sub: 'dirt', deep: 'stone', depth: 4,
pads: [{ cx: 0, cz: 4, r: 22, y: padY, falloff: 8 }]
});
var padMat = noisePalette([
{ block: 'stone_bricks', weight: 34 }, { block: 'andesite', weight: 24 },
{ block: 'cobblestone', weight: 22 }, { block: 'polished_andesite', weight: 20 }
], { freq: 0.2, seed: 4 });
for (x = -16; x <= 16; x++) {
for (z = -16; z <= 26; z++) {
var dd = Math.sqrt(x * x * 1.6 + (z - 5) * (z - 5) * 0.55);
if (dd > 17) continue;
pb(x, padY, z, padMat(x, 0, z));
if (dd > 15.4) { pb(x, padY + 1, z, 'stone_brick_wall'); }
}
}
// mooring rails + gaslamps around the apron
for (k = 0; k < 12; k++) {
var aa = k * 30 * Math.PI / 180;
var lx = Math.round(Math.cos(aa) * 12), lz = 5 + Math.round(Math.sin(aa) * 17);
lampPost(lx, padY + 1, lz, 4, 'iron_bars');
}
// ---------- 2. ENVELOPE (ellipsoid shell, copper patina) ----------
var skin = noisePalette([
{ block: 'copper_block', weight: 30 }, { block: 'exposed_copper', weight: 28 },
{ block: 'weathered_copper', weight: 24 }, { block: 'oxidized_copper', weight: 18 }
], { freq: 0.14, seed: 77 });
function ell(dx, dy, dz, rx, ry, rz) {
return (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry) + (dz * dz) / (rz * rz);
}
for (z = -RZ; z <= RZ; z++) {
for (y = -RY; y <= RY; y++) {
for (x = -RX; x <= RX; x++) {
var v = ell(x, y, z, RX, RY, RZ);
var vin = ell(x, y, z, RX - 1.15, RY - 1.15, RZ - 1.6);
if (v > 1.0 || vin <= 1.0) continue;
var bx = ECX + x, by = ECY + y, bz = ECZ + z;
if (z % 4 === 0) pb(bx, by, bz, (z % 8 === 0) ? 'waxed_exposed_copper' : 'gold_block'); // brass hoop frames
else pb(bx, by, bz, skin(bx, by, bz));
}
}
}
// longitudinal keel batten + nose / tail caps
for (z = -RZ; z <= RZ; z++) {
var frac = 1 - (z * z) / (RZ * RZ);
if (frac <= 0) continue;
var ry2 = Math.round(RY * Math.sqrt(frac));
pb(ECX, ECY - ry2, ECZ + z, 'copper_bulb');
pb(ECX, ECY + ry2, ECZ + z, 'lightning_rod[facing=up]');
}
for (y = -2; y <= 2; y++) {
for (x = -2; x <= 2; x++) {
if (Math.abs(x) + Math.abs(y) > 3) continue;
pb(ECX + x, ECY + y, ECZ + RZ, 'iron_block');
pb(ECX + x, ECY + y, ECZ - RZ, 'iron_block');
}
}
pb(ECX, ECY, ECZ + RZ + 1, 'chiseled_copper');
pb(ECX, ECY, ECZ + RZ + 2, 'lightning_rod[facing=south]');
// ---------- 3. TAIL FINS (vertical + horizontal, braced) ----------
for (k = 0; k <= 6; k++) {
var finLen = 7 - k;
// vertical fins (top and bottom)
for (i = 0; i < finLen; i++) {
pb(ECX, ECY + 6 + k - 2, ECZ - RZ + 1 + i, (i === finLen - 1) ? 'dark_oak_planks' : 'weathered_copper');
pb(ECX, ECY - 6 - k + 2, ECZ - RZ + 1 + i, (i === finLen - 1) ? 'dark_oak_planks' : 'weathered_copper');
// horizontal fins (port and starboard)
pb(ECX + 7 + k - 2, ECY, ECZ - RZ + 1 + i, (i === finLen - 1) ? 'dark_oak_planks' : 'weathered_copper');
pb(ECX - 7 - k + 2, ECY, ECZ - RZ + 1 + i, (i === finLen - 1) ? 'dark_oak_planks' : 'weathered_copper');
}
}
for (k = 0; k < 5; k++) {
ps(ECX, ECY + 5 + k, ECZ - RZ + 2, 'iron_bars');
ps(ECX, ECY - 5 - k, ECZ - RZ + 2, 'iron_bars');
}
// ---------- 4. GONDOLA (hull + interior) ----------
// hull: 5 wide, rounded bilge, bow tapered at +Z
for (z = -11; z <= 12; z++) {
var half = 2;
if (z >= 10) half = 1;
if (z <= -10) half = 1;
for (x = -half; x <= half; x++) {
pb(x, GBOT, z, 'dark_oak_planks'); // keel deck
pb(x, GTOP, z, 'spruce_planks'); // roof
}
// side walls with portholes
for (y = GBOT + 1; y <= GTOP - 1; y++) {
var wall = (y === GBOT + 2 && (z % 3 === 0) && z < 9 && z > -9) ? 'glass_pane' : 'spruce_planks';
pb(-half, y, z, wall); pb(half, y, z, wall);
}
// rounded bilge with upside-down stairs (convex hull)
ps(-half - 1, GBOT, z, 'dark_oak_stairs[facing=west,half=top]');
ps(half + 1, GBOT, z, 'dark_oak_stairs[facing=east,half=top]');
// eaves / rubbing strake
ps(-half - 1, GTOP, z, 'spruce_stairs[facing=west,half=top]');
ps(half + 1, GTOP, z, 'spruce_stairs[facing=east,half=top]');
}
// bow bulkhead + glazed bridge front
for (x = -1; x <= 1; x++) {
for (y = GBOT + 1; y <= GTOP - 1; y++) pb(x, y, 13, (y >= GBOT + 2) ? 'glass_pane' : 'dark_oak_planks');
pb(x, GBOT, 13, 'dark_oak_planks'); pb(x, GTOP, 13, 'spruce_planks');
for (y = GBOT + 1; y <= GTOP - 1; y++) pb(x, y, -12, (y === GBOT + 2) ? 'glass_pane' : 'dark_oak_planks');
pb(x, GBOT, -12, 'dark_oak_planks'); pb(x, GTOP, -12, 'spruce_planks');
}
// rib frames on the gondola sides (wall depth)
for (z = -9; z <= 9; z += 3) {
for (y = GBOT + 1; y <= GTOP - 1; y++) { pb(-3, y, z, 'stripped_dark_oak_log'); pb(3, y, z, 'stripped_dark_oak_log'); }
}
// suspension: chains from the envelope keel to the gondola roof
for (z = -9; z <= 9; z += 3) {
for (x = -2; x <= 2; x += 4) {
for (y = GTOP + 1; y <= 20; y++) ps(x, y, z, 'iron_chain[axis=y]');
}
}
// --- gondola interior: BRIDGE / CABIN / ENGINE ---
var wY = GBOT + 1; // walk level inside the gondola
// bridge (bow)
ps(0, wY, 12, 'lectern[facing=north]');
pb(-1, wY, 12, 'copper_bulb'); pb(1, wY, 12, 'copper_bulb');
chairStair(0, wY, 10, 'spruce_stairs', 'south');
pb(-2, wY, 11, 'barrel'); pb(2, wY, 11, 'cartography_table');
pb(0, GTOP - 1, 11, 'spruce_planks'); ps(0, GTOP - 2, 11, 'lantern[hanging=true]');
rug(-1, wY, 8, 3, 3, 'red', 'orange');
// cabin (midships)
placeBed(-1, wY, 4, 'blue', 'north');
pb(-2, wY, 6, 'spruce_fence'); ps(-2, wY + 1, 6, 'spruce_pressure_plate');
chairStair(-2, wY, 5, 'spruce_stairs', 'south');
pb(2, wY, 5, 'bookshelf'); pb(2, wY, 4, 'chest'); pb(2, wY, 6, 'barrel');
pb(0, GTOP - 1, 4, 'spruce_planks'); ps(0, GTOP - 2, 4, 'lantern[hanging=true]');
placeBed(1, wY, 0, 'green', 'north');
shelfWall(-2, wY, -1, 'north', 3, 2);
rug(-1, wY, 1, 3, 2, 'brown', 'yellow');
// engine room (stern)
ps(0, wY, -9, 'blast_furnace[facing=south,lit=true]');
pb(-1, wY, -9, 'copper_block'); pb(1, wY, -9, 'copper_block');
ps(-2, wY, -8, 'iron_trapdoor[facing=east,half=bottom]');
ps(2, wY, -8, 'iron_trapdoor[facing=west,half=bottom]');
pb(-2, wY, -6, 'barrel'); pb(2, wY, -6, 'cauldron');
pb(0, wY + 1, -10, 'copper_bulb');
pb(0, GTOP - 1, -7, 'spruce_planks'); ps(0, GTOP - 2, -7, 'lantern[hanging=true]');
for (z = -10; z <= -5; z++) { pb(-2, wY + 2, z, 'copper_block'); pb(2, wY + 2, z, 'copper_block'); }
// deck hatch + boarding ladder to the mast gantry
b(3, wY, 8, 'air');
for (y = GBOT; y <= GTOP; y++) ps(4, y, 8, 'ladder[facing=east]');
for (y = GBOT - 1; y >= 3; y--) { pb(5, y, 8, 'iron_bars'); ps(4, y, 8, 'ladder[facing=east]'); }
// ---------- 5. PROPELLERS ON OUTRIGGERS ----------
function prop(px, py, pz, r) {
pb(px, py, pz, 'iron_block');
for (var q = 1; q <= r; q++) {
ps(px, py + q, pz, 'iron_trapdoor[facing=south,half=bottom,open=true]');
ps(px, py - q, pz, 'iron_trapdoor[facing=south,half=bottom,open=true]');
ps(px + q, py, pz, 'iron_trapdoor[facing=east,half=bottom,open=true]');
ps(px - q, py, pz, 'iron_trapdoor[facing=west,half=bottom,open=true]');
}
}
for (k = 0; k < 2; k++) {
var sx = (k === 0) ? 1 : -1;
for (i = 3; i <= 8; i++) pb(sx * i, 22, -6, 'stripped_dark_oak_log'); // outrigger arm
pb(sx * 8, 21, -6, 'copper_block'); pb(sx * 8, 23, -6, 'copper_block');
pb(sx * 8, 22, -7, 'blast_furnace[facing=north,lit=true]');
prop(sx * 8, 22, -9, 3);
for (y = 19; y <= 21; y++) ps(sx * 5, y, -6, 'iron_chain[axis=y]');
}
// ---------- 6. MOORING MAST + GANTRY ----------
for (x = -3; x <= 3; x++) { for (z = 21; z <= 27; z++) { if (Math.abs(x) === 3 || Math.abs(z - 24) === 3) pb(x, padY + 1, z, 'deepslate_bricks'); } }
for (k = 0; k < 4; k++) {
var mx = (k < 2) ? -2 : 2, mz = (k % 2 === 0) ? 22 : 26;
for (y = padY + 1; y <= 28; y++) pb(mx, y, mz, 'iron_bars');
}
for (y = 4; y <= 28; y += 4) {
for (x = -2; x <= 2; x++) { pb(x, y, 22, 'iron_bars'); pb(x, y, 26, 'iron_bars'); }
for (z = 22; z <= 26; z++) { pb(-2, y, z, 'iron_bars'); pb(2, y, z, 'iron_bars'); }
}
for (x = -2; x <= 2; x++) { for (z = 22; z <= 26; z++) pb(x, 29, z, 'iron_block'); }
pb(0, 30, 24, 'copper_bulb'); pb(0, 31, 24, 'lightning_rod[facing=up]');
// gantry walkway from the mast to the gondola bow
for (z = 14; z <= 22; z++) { for (x = -1; x <= 1; x++) pb(x, GBOT - 1, z, 'dark_oak_planks'); pb(-2, GBOT, z, 'dark_oak_fence'); pb(2, GBOT, z, 'dark_oak_fence'); }
for (y = padY + 1; y <= GBOT - 2; y++) { pb(0, y, 21, 'stripped_dark_oak_log'); pb(0, y, 16, 'stripped_dark_oak_log'); }
// mooring cables: nose -> mast, and belly -> deck rings
for (k = 0; k < 7; k++) ps(0, 27 - k, 18 + k, 'iron_chain[axis=y]');
for (k = 0; k < 6; k++) { ps(-2 - k, 21 - k * 2, 6, 'iron_chain[axis=y]'); ps(2 + k, 21 - k * 2, 6, 'iron_chain[axis=y]'); }
// ---------- 7. APRON PROPS + SCENERY ----------
pb(-8, padY + 1, 12, 'barrel'); pb(-9, padY + 1, 12, 'barrel'); pb(-8, padY + 2, 12, 'barrel');
pb(-7, padY + 1, 13, 'chest'); ps(-6, padY + 1, 12, 'anvil[facing=north]');
pb(8, padY + 1, 12, 'cauldron'); pb(9, padY + 1, 12, 'blast_furnace[facing=west,lit=true]');
for (k = 0; k < 5; k++) pb(7 + (k % 2), padY + 1 + Math.floor(k / 2), 14, 'copper_block');
ps(-10, padY + 1, -4, 'campfire[lit=true]');
for (k = 0; k < 24; k++) {
var at = k * 15 * Math.PI / 180;
var tx = Math.round(Math.cos(at) * (22 + (k % 3) * 2));
var tz = 5 + Math.round(Math.sin(at) * (24 + (k % 4) * 2));
var ty = Math.round(ground(tx, tz)) + 1;
if (k % 3 === 0) placeTree(tx, ty, tz, 'oak');
else if (k % 3 === 1) { ps(tx, ty, tz, 'short_grass'); ps(tx + 1, ty, tz, 'poppy'); }
else { ps(tx, ty, tz, 'fern'); ps(tx - 1, ty, tz, 'dandelion'); }
}