// === BUILD PLAN ===
// Style: Norwegian stave church — CENTRAL/CRUCIFORM plan with stacked pyramid tiers
// Footprint: core crossing 11x11 (x-5..5, z-5..5) + nave arm 9x9 (z-13..-5)
// + chancel arm 9x7 (z5..11) + round apse r4 @ z14 + 2 transepts (x+-5..9)
// + svalgang veranda posts at x=+-7 along the nave arm
// Height: floorY3 / arms Y11, core Y15, transepts Y8 / pyramid tier1 base Y16
// / drum Y22-26 / pyramid tier2 base Y27 / mast to Y34
//
// GROUND FLOOR (top-down, 1 char = 1 block, Z increases downward):
// ....ooo##D##ooo.... D door z=-13, o svalgang veranda posts x=+-7
// ....o #BBBBB# o.. B benches, S stairwell 5x5 (walk4 -> gallery floor Y8)
// ....o #SSSSS# o..
// ....o #BBBBB# o..
// ..##### ##### transept arms (x+-5..9)
// ..#T# crossing #T# crossing open to the lantern tower, chandelier at centre
// ..##### #####
// # dais AAA # A raised chancel + round apse (cylinder r4)
// \ (o) / apse cone roof
//
// FRONT ELEVATION (north):
// | mast + dragon finial
// /^\ pyramid tier 2 (Y27-31)
// |===| shingled drum w/ louvers (Y22-26)
// /=====\ pyramid tier 1 (Y16-22)
// | o o | clerestory round windows Y13
// /=========\ nave-arm gable (Y12-16)
// _| w w |_ round windows Y8, bracket corbels under eaves
// |o o o o o o o| svalgang veranda posts + rail
// ===rocky knoll===
//
// MATERIALS PLAN:
// base : gradientPalette deepslate->cobbled_deepslate->mossy Y4..Y7 — WHY: cold northern stone sill
// walls : noisePalette spruce_planks45/dark_oak_planks35/stripped_dark_oak_log20 — WHY: tarred staves
// frame : dark_oak_log[axis=y] corner posts + intermediate staves — WHY: vertical rhythm = depth
// roofs : dark_oak_stairs (core tiers, heavy dark mass) / spruce_stairs (arms, aisles, apse)
// brackets: upside-down stairs under every eave (build-rules #22) — WHY: no floating overhangs
// accents : brown_stained_glass round windows, lanterns, chain chandelier, red carpet
// Rooms: Nave arm (pews, aisle, wall lanterns), Crossing (chandelier), Chancel+apse (dais, altar,
// lectern, banners), Gallery Y8 (benches, rail, lantern), Transepts (barrels, pots)
// STAIR PLAN: placeStairwell 5x5 U-turn, lowerWalk Y4 -> upper floor Y8, inside the nave arm
// === END PLAN ===
var padY = 2;
var floorY = 3;
var walkY = 4;
var armTopY = 11;
var coreTopY = 15;
var transTopY = 8;
var timber = noisePalette([
{ block: 'spruce_planks', weight: 45 },
{ block: 'dark_oak_planks', weight: 35 },
{ block: 'stripped_dark_oak_log', weight: 20 }
], { seed: 41, freq: 0.14 });
var sill = gradientPalette(floorY + 1, floorY + 4, [
{ block: 'cobbled_deepslate', from: 0 },
{ block: 'deepslate_tiles', from: 0.5 },
{ block: 'mossy_cobblestone', from: 0.85 }
], { noise: 0.3, seed: 9 });
var boards = noisePalette([
{ block: 'spruce_planks', weight: 60 },
{ block: 'dark_oak_planks', weight: 40 }
], { seed: 17, freq: 0.22 });
function shell(x1, z1, x2, z2, y1, y2, mat) {
var okWalls = false;
try {
makeWalls(x1, y1, z1, x2, y2, z2, mat);
okWalls = true;
} catch (wallErr) {
okWalls = false;
}
if (!okWalls) {
fill(x1, y1, z1, x2, y2, z1, mat);
fill(x1, y1, z2, x2, y2, z2, mat);
fill(x1, y1, z1, x1, y2, z2, mat);
fill(x2, y1, z1, x2, y2, z2, mat);
}
}
// === TERRAIN ===
var ground = null;
try {
ground = terrain({
cx: 0, cz: 0, radius: 32, baseY: -2, amp: 6, scale: 0.055, seed: 61, irregularity: 0.4,
surface: [
{ block: 'grass_block', w: 5 },
{ block: 'stone', w: 3 },
{ block: 'coarse_dirt', w: 2 },
{ block: 'moss_block', w: 1 }
],
sub: 'dirt', deep: 'stone', depth: 4,
pads: [{ cx: 0, cz: 0, r: 18, y: padY, falloff: 9 }]
});
} catch (terrErr) {
fill(-20, padY - 3, -20, 20, padY, 20, 'stone');
fill(-20, padY, -20, 20, padY, 20, 'grass_block');
}
fill(-14, floorY, -20, 14, 40, 20, 'air');
// === STRUCTURE ===
// deck / floor slab over the whole plan
fill(-9, floorY, -13, 9, floorY, 11, boards);
fill(-7, floorY, -16, 7, floorY, 16, boards);
// core crossing
shell(-5, -5, 5, 5, floorY + 1, coreTopY, timber);
// nave arm (north) + chancel arm (south)
shell(-4, -13, 4, -5, floorY + 1, armTopY, timber);
shell(-4, 5, 4, 11, floorY + 1, armTopY, timber);
// transepts (east/west)
shell(-9, -3, -5, 3, floorY + 1, transTopY, timber);
shell(5, -3, 9, 3, floorY + 1, transTopY, timber);
// stone sill band everywhere
shell(-5, -5, 5, 5, floorY + 1, floorY + 3, sill);
shell(-4, -13, 4, -5, floorY + 1, floorY + 3, sill);
shell(-4, 5, 4, 11, floorY + 1, floorY + 3, sill);
shell(-9, -3, -5, 3, floorY + 1, floorY + 3, sill);
shell(5, -3, 9, 3, floorY + 1, floorY + 3, sill);
// round apse (hollow cylinder) at the south end
makeCylinder(0, floorY + 1, 14, timber, 4, 6, false);
fill(-2, floorY, 12, 2, floorY, 17, boards);
// openings: main door, arm->crossing arches, transept arches, apse arch
fill(-1, walkY, -13, 1, walkY + 2, -13, 'air');
ps(0, walkY, -13, 'dark_oak_door[facing=north,half=lower,hinge=left]');
ps(0, walkY + 1, -13, 'dark_oak_door[facing=north,half=upper,hinge=left]');
fill(-2, walkY, -5, 2, walkY + 3, -5, 'air');
fill(-2, walkY, 5, 2, walkY + 3, 5, 'air');
fill(-5, walkY, -1, -5, walkY + 2, 1, 'air');
fill(5, walkY, -1, 5, walkY + 2, 1, 'air');
fill(-2, walkY, 11, 2, walkY + 2, 11, 'air');
fill(-2, walkY, 12, 2, walkY + 2, 12, 'air');
// vertical stave rhythm: corner posts + intermediate staves (facade depth)
function postAt(px, pz, y1, y2) { fill(px, y1, pz, px, y2, pz, 'dark_oak_log[axis=y]'); }
postAt(-5, -5, floorY + 1, coreTopY); postAt(5, -5, floorY + 1, coreTopY);
postAt(-5, 5, floorY + 1, coreTopY); postAt(5, 5, floorY + 1, coreTopY);
postAt(-4, -13, floorY + 1, armTopY); postAt(4, -13, floorY + 1, armTopY);
postAt(-4, 11, floorY + 1, armTopY); postAt(4, 11, floorY + 1, armTopY);
var sv;
for (sv = -12; sv <= -6; sv += 3) {
postAt(-4, sv, floorY + 1, armTopY); postAt(4, sv, floorY + 1, armTopY);
}
for (sv = 6; sv <= 10; sv += 3) {
postAt(-4, sv, floorY + 1, armTopY); postAt(4, sv, floorY + 1, armTopY);
}
for (sv = -3; sv <= 3; sv += 3) {
postAt(-5, sv, floorY + 1, coreTopY); postAt(5, sv, floorY + 1, coreTopY);
}
// bracket corbels under every eave (build-rules #22 — no floating overhangs)
var bc;
for (bc = -13; bc <= 11; bc++) {
if (bc >= -5 && bc <= 5) {
roofStair(-6, coreTopY, bc, 'dark_oak_stairs', 'west', 'top');
roofStair(6, coreTopY, bc, 'dark_oak_stairs', 'east', 'top');
} else {
roofStair(-5, armTopY, bc, 'dark_oak_stairs', 'west', 'top');
roofStair(5, armTopY, bc, 'dark_oak_stairs', 'east', 'top');
}
}
// === ROOF === stacked tiers
placeGableRoof(-4, 4, -13, -5, armTopY + 1, 'spruce_stairs', 'spruce_slab', timber);
placeGableRoof(-4, 4, 5, 11, armTopY + 1, 'spruce_stairs', 'spruce_slab', timber);
placeGableRoofNS(-9, -5, -3, 3, transTopY + 1, 'spruce_stairs', 'spruce_slab', timber);
placeGableRoofNS(5, 9, -3, 3, transTopY + 1, 'spruce_stairs', 'spruce_slab', timber);
// core: pyramid tier 1
makePyramid(0, coreTopY + 1, 0, noisePalette([
{ block: 'dark_oak_stairs', weight: 0 },
{ block: 'dark_oak_planks', weight: 60 },
{ block: 'spruce_planks', weight: 25 },
{ block: 'dark_oak_slab', weight: 15 }
], { seed: 5, freq: 0.3 }), 6, true);
// shingle skirt of stairs around tier 1 so it reads as a roof, not a block mass
var sk;
for (sk = -6; sk <= 6; sk++) {
roofStair(sk, coreTopY + 1, -6, 'dark_oak_stairs', 'north');
roofStair(sk, coreTopY + 1, 6, 'dark_oak_stairs', 'south');
roofStair(-6, coreTopY + 1, sk, 'dark_oak_stairs', 'west');
roofStair(6, coreTopY + 1, sk, 'dark_oak_stairs', 'east');
}
// lantern drum + tier 2 + mast
shell(-3, -3, 3, 3, 22, 26, timber);
fill(-2, 16, -2, 2, 26, 2, 'air');
var lv;
for (lv = -1; lv <= 1; lv++) {
fill(lv, 23, -3, lv, 25, -3, 'dark_oak_fence');
fill(lv, 23, 3, lv, 25, 3, 'dark_oak_fence');
fill(-3, 23, lv, -3, 25, lv, 'dark_oak_fence');
fill(3, 23, lv, 3, 25, lv, 'dark_oak_fence');
}
makePyramid(0, 27, 0, 'dark_oak_planks', 4, true);
var sk2;
for (sk2 = -4; sk2 <= 4; sk2++) {
roofStair(sk2, 27, -4, 'spruce_stairs', 'north');
roofStair(sk2, 27, 4, 'spruce_stairs', 'south');
roofStair(-4, 27, sk2, 'spruce_stairs', 'west');
roofStair(4, 27, sk2, 'spruce_stairs', 'east');
}
fill(0, 31, 0, 0, 33, 0, 'dark_oak_fence');
pb(0, 34, 0, 'lantern');
// apse cone roof
makeCone(0, armTopY, 14, 'spruce_planks', 5, 5, true);
// dragon-head finials on the arm ridges and the drum corners
function finial(fx, fy, fz, face) {
pb(fx, fy, fz, 'dark_oak_fence');
ps(fx, fy + 1, fz, 'dark_oak_stairs[facing=' + face + ',half=top]');
ps(fx, fy + 2, fz, 'dark_oak_trapdoor[facing=' + face + ',half=bottom,open=true]');
}
finial(0, 17, -14, 'north');
finial(0, 17, 12, 'south');
finial(-4, 27, -4, 'north');
finial(4, 27, 4, 'south');
// === SVALGANG (veranda posts + lean-to, build-rules #22) ===
var gz;
for (gz = -13; gz <= -5; gz++) {
roofStair(-5, 8, gz, 'spruce_stairs', 'west');
roofStair(-6, 7, gz, 'spruce_stairs', 'west');
roofStair(-7, 6, gz, 'spruce_stairs', 'west');
roofStair(5, 8, gz, 'spruce_stairs', 'east');
roofStair(6, 7, gz, 'spruce_stairs', 'east');
roofStair(7, 6, gz, 'spruce_stairs', 'east');
if (gz % 2 === 0) {
fill(-7, walkY, gz, -7, 5, gz, 'dark_oak_log[axis=y]');
fill(7, walkY, gz, 7, 5, gz, 'dark_oak_log[axis=y]');
} else {
pb(-7, walkY + 1, gz, 'dark_oak_fence');
pb(7, walkY + 1, gz, 'dark_oak_fence');
}
}
for (gz = -14; gz <= -13; gz++) {
var gx2;
for (gx2 = -7; gx2 <= 7; gx2++) {
roofStair(gx2, (gz === -13) ? 7 : 6, gz, 'spruce_stairs', 'north');
}
}
// re-assert the staves the lean-to crossed
for (sv = -12; sv <= -6; sv += 3) {
postAt(-4, sv, floorY + 1, armTopY); postAt(4, sv, floorY + 1, armTopY);
}
// === WINDOWS === small round windows, above the svalgang roof
var wz;
for (wz = -11; wz <= -6; wz += 2) {
ps(-4, 9, wz, 'brown_stained_glass');
ps(4, 9, wz, 'brown_stained_glass');
}
for (wz = 7; wz <= 10; wz += 2) {
ps(-4, 8, wz, 'brown_stained_glass');
ps(4, 8, wz, 'brown_stained_glass');
}
// clerestory on the core
var wc;
for (wc = -3; wc <= 3; wc += 3) {
ps(-5, 13, wc, 'brown_stained_glass');
ps(5, 13, wc, 'brown_stained_glass');
ps(wc, 13, -5, 'brown_stained_glass');
ps(wc, 13, 5, 'brown_stained_glass');
}
// apse windows
ps(-3, 8, 13, 'brown_stained_glass');
ps(3, 8, 13, 'brown_stained_glass');
ps(0, 8, 17, 'brown_stained_glass');
// === INTERIOR: GALLERY + STAIRWELL ===
fill(-3, 8, -12, 3, 8, -6, 'spruce_planks');
var gr;
for (gr = -3; gr <= 3; gr++) { pb(gr, 9, -6, 'dark_oak_fence'); }
var stairOk = true;
try {
placeStairwell(-2, -11, walkY, 8, 'dark_oak_stairs', 'dark_oak_fence', 'spruce_planks', 'south');
} catch (swErr) {
stairOk = false;
}
if (!stairOk) {
staircase(-2, -7, walkY, 9, 'north', { width: 1, material: 'dark_oak_stairs', rail: true, support: 'dark_oak_planks' });
}
// FIX: a real, visible, navigable stair from the nave floor up to the gallery
fill(1, 9, -6, 3, 9, -6, 'air');
staircase(2, -2, walkY, 9, 'north', { width: 1, material: 'dark_oak_stairs', rail: true, support: 'dark_oak_planks' });
pb(1, 10, -6, 'dark_oak_fence');
chairStair(-2, 9, -8, 'spruce_stairs', 'south');
chairStair(2, 9, -8, 'spruce_stairs', 'south');
pb(3, 9, -11, 'dark_oak_fence');
pb(3, 10, -11, 'lantern');
// === INTERIOR: NAVE ARM ===
var rz, bx;
for (rz = -12; rz <= 4; rz++) { pb(0, walkY, rz, 'red_carpet'); }
for (rz = -6; rz >= -12; rz -= 2) {
for (bx = 2; bx <= 3; bx++) {
chairStair(-bx, walkY, rz, 'spruce_stairs', 'south');
chairStair(bx, walkY, rz, 'spruce_stairs', 'south');
}
}
var lz;
for (lz = -11; lz <= -6; lz += 3) {
pb(-3, walkY + 3, lz, 'dark_oak_fence'); pb(-3, walkY + 4, lz, 'lantern');
pb(3, walkY + 3, lz, 'dark_oak_fence'); pb(3, walkY + 4, lz, 'lantern');
}
// === INTERIOR: CROSSING === chandelier from the tower shaft
var chY;
for (chY = 10, chY = 10; chY <= 25; chY++) { pb(0, chY, 0, 'chain[axis=y]'); }
ps(0, 9, 0, 'lantern[hanging=true]');
rug(-3, walkY, -3, 7, 7, 'red', 'brown');
// transept furnishings
pb(-8, walkY, -2, 'barrel'); pb(-8, walkY, 2, 'barrel');
pb(-7, walkY, 0, 'dark_oak_fence'); pb(-7, walkY + 1, 0, 'lantern');
pb(8, walkY, -2, 'bookshelf'); pb(8, walkY + 1, -2, 'bookshelf');
ps(8, walkY, 1, 'lectern[facing=west]');
pb(7, walkY, 0, 'dark_oak_fence'); pb(7, walkY + 1, 0, 'lantern');
chairStair(6, walkY, 2, 'dark_oak_stairs', 'north');
// === INTERIOR: CHANCEL + APSE ===
fill(-3, walkY, 7, 3, walkY, 11, 'polished_deepslate');
fill(-2, walkY + 1, 12, 2, walkY + 1, 16, 'polished_deepslate');
var stx;
for (stx = -2; stx <= 2; stx++) { climbStair(stx, walkY, 6, 'dark_oak_stairs', 'south'); }
for (stx = -2; stx <= 2; stx++) { climbStair(stx, walkY + 1, 11, 'dark_oak_stairs', 'south'); }
fill(-1, walkY + 2, 15, 1, walkY + 2, 15, 'chiseled_deepslate');
pb(0, walkY + 3, 15, 'lantern');
ps(-2, walkY + 1, 9, 'lectern[facing=east]');
ps(-4, walkY + 4, 9, 'red_wall_banner[facing=east]');
ps(4, walkY + 4, 9, 'red_wall_banner[facing=west]');
pb(-3, walkY + 1, 8, 'dark_oak_fence'); pb(-3, walkY + 2, 8, 'lantern');
pb(3, walkY + 1, 8, 'dark_oak_fence'); pb(3, walkY + 2, 8, 'lantern');
// === DETAIL: LANDSCAPING ===
var pathZ, gy, pxx;
for (pathZ = -16; pathZ >= -27; pathZ--) {
gy = padY;
if (ground) { gy = Math.round(ground(0, pathZ)); }
for (pxx = -1; pxx <= 1; pxx++) {
pb(pxx, gy, pathZ, (pathZ % 4 === 0) ? 'andesite' : 'gravel');
}
}
var lich = [[-12, -16], [12, -15], [-15, 6], [14, 8], [-13, 15], [11, 16]];
var li, lxx, lzz, lyy;
for (li = 0; li < lich.length; li++) {
lxx = lich[li][0]; lzz = lich[li][1];
lyy = padY;
if (ground) { lyy = Math.round(ground(lxx, lzz)); }
pb(lxx, lyy + 1, lzz, 'cobblestone_wall');
ps(lxx, lyy + 2, lzz, 'deepslate_tile_slab[type=bottom]');
pb(lxx + 1, lyy + 1, lzz, 'moss_block');
}
var trees = [[-20, -10], [19, -13], [-22, 8], [20, 13], [-17, 19], [16, -20]];
var ti, txx, tzz, tyy;
for (ti = 0; ti < trees.length; ti++) {
txx = trees[ti][0]; tzz = trees[ti][1];
tyy = padY;
if (ground) { tyy = Math.round(ground(txx, tzz)); }
try {
placeTree(txx, tyy + 1, tzz, 'spruce');
} catch (treeErr) {
fill(txx, tyy + 1, tzz, txx, tyy + 7, tzz, 'spruce_log[axis=y]');
makeSphere(txx, tyy + 7, tzz, 'spruce_leaves[persistent=true]', 3, true);
}
}
try {
lampPost(-3, padY + 1, -16, 4, 'dark_oak_fence');
lampPost(3, padY + 1, -16, 4, 'dark_oak_fence');
} catch (lampErr) {
fill(-3, padY + 1, -16, -3, padY + 4, -16, 'dark_oak_fence');
pb(-3, padY + 5, -16, 'lantern');
fill(3, padY + 1, -16, 3, padY + 4, -16, 'dark_oak_fence');
pb(3, padY + 5, -16, 'lantern');
}
var fi2, fx2, fz2, fy2;
for (fi2 = 0; fi2 < 70; fi2++) {
fx2 = -24 + Math.floor(noise2d('perlin', fi2 * 6.1, 4.4) * 48);
fz2 = -24 + Math.floor(noise2d('perlin', 8.2, fi2 * 9.3) * 48);
if (Math.abs(fx2) < 11 && Math.abs(fz2) < 19) { continue; }
fy2 = padY;
if (ground) { fy2 = Math.round(ground(fx2, fz2)); }
pb(fx2, fy2 + 1, fz2, (fi2 % 5 === 0) ? 'fern' : ((fi2 % 5 === 1) ? 'cornflower' : 'short_grass'));
}