// === BUILD PLAN ===
// Style: Ethiopian rock-hewn monolithic church (Lalibela / Bete Giyorgis)
// Footprint: GREEK CROSS monolith, arms 16 wide, bounding 30x30, standing free inside a
// 57x57 rectangular TRENCH cut 13 blocks down into a red tuff bedrock plateau.
// Height: trench floor Y2 -> plinth Y3-4 -> body Y5-14 -> roof tiers Y15,Y16. Plateau top Y15.
//
// SITE (top-down, 1 char = 4 blocks):
// ................
// ..############.. # = bedrock plateau (organic relief via hills+flatten+surface)
// ..#..______..#.. _ = trench courtyard (rock floor Y2)
// ..#.._+##+_..#.. + = monolith cross arms
// ..#.._####_..#..
// ..#.._+##+_..#..
// ..#..___||__.#.. || = rock-cut slot canyon + descending staircase (Y16 -> Y3)
// ..############..
//
// CROSS PLAN (interior, walls 3 thick):
// outer A x33..62 z40..55 | outer B x40..55 z33..62
// inner A x36..59 z43..52 | inner B x43..52 z36..59
// nave floor Y5, walkY 6, ceiling Y14 (8 blocks of air), 8 monolithic pillars
//
// FRONT ELEVATION (south arm, +Z = camera side):
// [====== roof tier 2 + carved cross =====]
// [======== roof tier 1 + cornice ========]
// | reg3 o o o | small ogees Y12
// | reg2 ^ ^ ^ | keyhole windows Y10
// | reg1 + + + | latin-cross windows Y7 (orange glass)
// | pilaster relief |
// ===== DOOR + porch + staircase down to courtyard =====
//
// MATERIALS PLAN:
// bedrock : noisePalette granite/terracotta/red_sandstone/brown_terracotta - WHY: one rock mass, organic blotches, reads carved
// monolith : gradientPalette Y3->Y14 brown_terracotta -> terracotta -> red_terracotta -> granite {noise .3}
// - WHY: the monolith is the SAME rock, but sun-bleached higher up; the ramp fakes shadow at the base
// roof : noisePalette polished_granite/terracotta/red_terracotta + chiseled cross motif - WHY: crisper = "carved"
// cornice : polished_granite_stairs upside-down (roofStair top) - WHY: 1-block shadow line = wall_depth
// interior : polished_granite floor + red/orange carpet runner, chiseled_stone_bricks pillars, lanterns+candles
// process : LAYERED (blue_wool body / red_wool roof placeholders -> masks -> replaceMasked palette)
// Rooms: nave (pillars, benches, runner, lanterns), sanctuary (altar, candles, banners), side aisles (rugs, pots)
// STAIR PLAN: descent D=13 -> staircase(47,92, walk16 -> walk3, 'north') width 3 in a slot canyon
// porch D=3 -> staircase(47,68, walk3 -> walk6, 'north') width 2 onto the plinth porch
// === END PLAN ===
// ---------- Y LEVELS ----------
var trenchFloorY = 2; // top block of the courtyard
var courtWalkY = 3; // player feet in the trench
var plinthY = 3;
var floorY = 5; // nave floor block
var walkY = 6; // furniture / doors
var ceilY = 14; // ceiling block layer
var roof1Y = 15;
var roof2Y = 16;
var groundY = 15; // plateau top block (walk 16)
// ---------- CROSS GEOMETRY ----------
var AX1 = 33, AX2 = 62, AZ1 = 40, AZ2 = 55; // E-W arm
var BX1 = 40, BX2 = 55, BZ1 = 33, BZ2 = 62; // N-S arm
var IAX1 = 36, IAX2 = 59, IAZ1 = 43, IAZ2 = 52;
var IBX1 = 43, IBX2 = 52, IBZ1 = 36, IBZ2 = 59;
function inCross(x, z) {
if (x >= AX1 && x <= AX2 && z >= AZ1 && z <= AZ2) return true;
if (x >= BX1 && x <= BX2 && z >= BZ1 && z <= BZ2) return true;
return false;
}
function inCrossGrow(x, z, g) {
if (x >= AX1 - g && x <= AX2 + g && z >= AZ1 - g && z <= AZ2 + g) return true;
if (x >= BX1 - g && x <= BX2 + g && z >= BZ1 - g && z <= BZ2 + g) return true;
return false;
}
function inNave(x, z) {
if (x >= IAX1 && x <= IAX2 && z >= IAZ1 && z <= IAZ2) return true;
if (x >= IBX1 && x <= IBX2 && z >= IBZ1 && z <= IBZ2) return true;
return false;
}
// ===================== 1. BEDROCK PLATEAU (terrain superpowers) =====================
var rockPal = noisePalette([
{ block: 'granite', weight: 30 },
{ block: 'terracotta', weight: 25 },
{ block: 'red_sandstone', weight: 20 },
{ block: 'brown_terracotta', weight: 15 },
{ block: 'stone', weight: 10 }
], { seed: 4, freq: 0.07 });
var mesa = blob(48, 48, 46, { irregularity: 0.22, seed: 5 });
var mx, mz;
for (mx = 0; mx < 96; mx++) {
for (mz = 0; mz < 96; mz++) {
if (mesa(mx, mz)) fill(mx, 0, mz, mx, groundY - 1, mz, rockPal);
}
}
var h = hills({ base: groundY, amp: 6, scale: 0.03, seed: 3, octaves: 2 });
var pad = flatten(h, { x0: 14, z0: 14, x1: 82, z1: 82 }, groundY, 8);
var topMat = patches([
{ block: 'coarse_dirt', w: 30 },
{ block: 'terracotta', w: 25 },
{ block: 'granite', w: 20 },
{ block: 'grass_block', w: 15 },
{ block: 'red_sand', w: 10 }
], 0.05, 9);
surface(mesa, pad, topMat, { sub: 'coarse_dirt', deep: 'granite', depth: 3 });
// ===================== 2. TRENCH + SLOT CANYON =====================
var TX1 = 20, TX2 = 76, TZ1 = 20, TZ2 = 76;
fill(TX1, trenchFloorY + 1, TZ1, TX2, 26, TZ2, 'air');
fill(TX1, trenchFloorY, TZ1, TX2, trenchFloorY, TZ2, noisePalette([
{ block: 'polished_andesite', weight: 35 },
{ block: 'andesite', weight: 25 },
{ block: 'stone', weight: 20 },
{ block: 'granite', weight: 12 },
{ block: 'gravel', weight: 8 }
], { seed: 12, freq: 0.11 }));
// slot canyon: air follows the descending walk line (walk = 3 at z79 -> 16 at z92)
var zz, wlk;
for (zz = 77; zz <= 92; zz++) {
wlk = zz <= 79 ? courtWalkY : courtWalkY + (zz - 79);
fill(44, wlk, zz, 51, 26, zz, 'air');
}
// mouth of the canyon on the plateau
fill(44, groundY + 1, 93, 51, 26, 95, 'air');
// carved votive niches in the trench walls (rock-cut tombs)
var nz;
for (nz = 28; nz <= 68; nz += 10) {
fill(TX1 - 3, courtWalkY + 1, nz, TX1 - 1, courtWalkY + 3, nz + 1, 'air');
fill(TX2 + 1, courtWalkY + 1, nz, TX2 + 3, courtWalkY + 3, nz + 1, 'air');
}
var nx;
for (nx = 28; nx <= 68; nx += 10) {
fill(nx, courtWalkY + 1, TZ1 - 3, nx + 1, courtWalkY + 3, TZ1 - 1, 'air');
}
// ===================== 3. MONOLITH — FORM IN PLACEHOLDERS (LAYERED) =====================
var PH = 'blue_wool';
var PR = 'red_wool';
var gx, gz;
// plinth (2 stepped tiers, wider than the body) + body
for (gx = AX1 - 2; gx <= AX2 + 2; gx++) {
for (gz = BZ1 - 2; gz <= BZ2 + 2; gz++) {
if (inCrossGrow(gx, gz, 2)) fill(gx, plinthY, gz, gx, plinthY, gz, PH);
if (inCrossGrow(gx, gz, 1)) fill(gx, plinthY + 1, gz, gx, plinthY + 1, gz, PH);
if (inCross(gx, gz)) fill(gx, floorY, gz, gx, ceilY, gz, PH);
}
}
// roof tiers
for (gx = AX1 - 1; gx <= AX2 + 1; gx++) {
for (gz = BZ1 - 1; gz <= BZ2 + 1; gz++) {
if (inCrossGrow(gx, gz, 1)) fill(gx, roof1Y, gz, gx, roof1Y, gz, PR);
if (inCrossGrow(gx, gz, -3)) fill(gx, roof2Y, gz, gx, roof2Y, gz, PR);
}
}
// pilaster relief: one proud column on every 5th perimeter cell, pushed OUTWARD
var dirsX = [1, -1, 0, 0];
var dirsZ = [0, 0, 1, -1];
var edgeName = ['east', 'west', 'south', 'north'];
var di;
for (gx = AX1 - 1; gx <= AX2 + 1; gx++) {
for (gz = BZ1 - 1; gz <= BZ2 + 1; gz++) {
if (!inCross(gx, gz)) continue;
for (di = 0; di < 4; di++) {
if (inCross(gx + dirsX[di], gz + dirsZ[di])) continue;
// cornice course all the way round (upside-down stairs = shadow line)
roofStair(gx + dirsX[di], ceilY, gz + dirsZ[di], 'polished_granite_stairs', edgeName[di], 'top');
if ((gx + gz) % 5 === 0) {
fill(gx + dirsX[di], floorY, gz + dirsZ[di], gx + dirsX[di], ceilY - 1, gz + dirsZ[di], PH);
}
}
}
}
// ===================== 4. CARVE THE NAVE (masks: body only, roof untouched) =====================
clearMasked(IAX1, walkY, IAZ1, IAX2, ceilY - 1, IAZ2, onlyMaterial(PH));
clearMasked(IBX1, walkY, IBZ1, IBX2, ceilY - 1, IBZ2, onlyMaterial(PH));
// monolithic pillars back into the nave
var pillars = [[38, 44], [38, 51], [57, 44], [57, 51], [44, 38], [51, 38], [44, 57], [51, 57]];
var pi;
for (pi = 0; pi < pillars.length; pi++) {
fill(pillars[pi][0], walkY, pillars[pi][1], pillars[pi][0], ceilY - 1, pillars[pi][1], PH);
}
// ===================== 5. WINDOWS — 3 REGISTERS, CARVED WITH MASKS =====================
// shape = list of [du, dy] cells on the face; carved through the full 3-block wall thickness
var CROSSWIN = [[0, 0], [0, 1], [0, 2], [0, 3], [-1, 2], [1, 2]];
var KEYWIN = [[0, 0], [0, 1], [-1, 2], [0, 2], [1, 2], [0, 3]];
var OGEEWIN = [[0, 0], [0, 1], [0, 2]];
function carveWin(ax, az, y, shape, axis, glaze) {
// axis 'x' -> face normal is +/-X, opening runs along Z ; axis 'z' -> runs along X
var k, du, dy, cx, cz, t;
for (k = 0; k < shape.length; k++) {
du = shape[k][0];
dy = shape[k][1];
for (t = -3; t <= 3; t++) {
if (axis === 'x') { cx = ax + t; cz = az + du; }
else { cx = ax + du; cz = az + t; }
clearMasked(cx, y + dy, cz, cx, y + dy, cz, onlyMaterial(PH));
}
}
if (glaze) {
for (k = 0; k < shape.length; k++) {
du = shape[k][0];
dy = shape[k][1];
if (axis === 'x') { cx = ax; cz = az + du; } else { cx = ax + du; cz = az; }
pb(cx, y + dy, cz, 'orange_stained_glass');
}
}
}
// arm-end facades (4) — three registers each
var ends = [[AX2 - 1, 47, 'x'], [AX1 + 1, 47, 'x'], [47, BZ1 + 1, 'z'], [47, BZ2 - 1, 'z']];
var ei, e;
for (ei = 0; ei < ends.length; ei++) {
e = ends[ei];
if (e[2] === 'x') {
carveWin(e[0], 44, 7, CROSSWIN, 'x', true);
carveWin(e[0], 51, 7, CROSSWIN, 'x', true);
carveWin(e[0], 47, 10, KEYWIN, 'x', false);
carveWin(e[0], 44, 12, OGEEWIN, 'x', false);
carveWin(e[0], 51, 12, OGEEWIN, 'x', false);
} else {
carveWin(44, e[1], 7, CROSSWIN, 'z', true);
carveWin(51, e[1], 7, CROSSWIN, 'z', true);
carveWin(47, e[1], 10, KEYWIN, 'z', false);
carveWin(44, e[1], 12, OGEEWIN, 'z', false);
carveWin(51, e[1], 12, OGEEWIN, 'z', false);
}
}
// long side walls of both arms
var sx;
for (sx = 37; sx <= 58; sx += 6) {
if (sx > 41 && sx < 54) continue;
carveWin(sx, AZ1 + 1, 7, CROSSWIN, 'z', true);
carveWin(sx, AZ2 - 1, 7, CROSSWIN, 'z', true);
carveWin(sx, AZ1 + 1, 11, OGEEWIN, 'z', false);
carveWin(sx, AZ2 - 1, 11, OGEEWIN, 'z', false);
}
var sz;
for (sz = 37; sz <= 58; sz += 6) {
if (sz > 41 && sz < 54) continue;
carveWin(BX1 + 1, sz, 7, CROSSWIN, 'x', true);
carveWin(BX2 - 1, sz, 7, CROSSWIN, 'x', true);
carveWin(BX1 + 1, sz, 11, OGEEWIN, 'x', false);
carveWin(BX2 - 1, sz, 11, OGEEWIN, 'x', false);
}
// ===================== 6. DOOR + PORCH + STAIRS =====================
clearMasked(47, walkY, BZ2 - 2, 48, walkY + 2, BZ2 + 1, onlyMaterial(PH));
fill(45, plinthY, 63, 50, floorY, 65, PH); // porch platform, top = floorY (walk 6)
fill(45, plinthY, 66, 50, plinthY + 1, 66, PH); // support under the rising steps
fill(45, plinthY, 67, 50, plinthY, 67, PH);
staircase(47, 68, courtWalkY, walkY, 'north', { width: 2, material: 'polished_granite_stairs', support: 'polished_granite' });
// the big rock-cut descent into the trench
staircase(47, 79, courtWalkY, groundY + 1, 'south', { width: 3, material: 'polished_andesite_stairs', support: 'polished_andesite' });
// ===================== 7. TEXTURE LAST (replaceMasked) =====================
var bodyPal = gradientPalette(plinthY, ceilY, [
{ block: 'brown_terracotta', from: 0 },
{ block: 'terracotta', from: 0.3 },
{ block: 'red_terracotta', from: 0.62 },
{ block: 'granite', from: 0.86 }
], { noise: 0.3 });
replaceMasked(AX1 - 4, 0, BZ1 - 4, AX2 + 4, roof2Y + 1, BZ2 + 4, bodyPal, onlyMaterial(PH));
var roofPal = noisePalette([
{ block: 'polished_granite', weight: 45 },
{ block: 'terracotta', weight: 30 },
{ block: 'red_terracotta', weight: 25 }
], { seed: 21, freq: 0.14 });
replaceMasked(AX1 - 4, roof1Y, BZ1 - 4, AX2 + 4, roof2Y, BZ2 + 4, roofPal, onlyMaterial(PR));
// carved nested cross motif on the roof (Bete Giyorgis signature)
function crossMotif(cx, cz, arm, wdt, mat) {
var a, bq;
for (a = -arm; a <= arm; a++) {
for (bq = -wdt; bq <= wdt; bq++) {
pb(cx + a, roof2Y, cz + bq, mat);
pb(cx + bq, roof2Y, cz + a, mat);
}
}
}
crossMotif(47, 47, 11, 3, 'polished_diorite');
crossMotif(47, 47, 8, 2, 'chiseled_stone_bricks');
crossMotif(47, 47, 5, 1, 'polished_andesite');
// ===================== 8. NAVE FLOOR, PILLARS, FURNITURE =====================
var floorPal = noisePalette([
{ block: 'polished_andesite', weight: 40 },
{ block: 'polished_granite', weight: 30 },
{ block: 'smooth_stone', weight: 20 },
{ block: 'polished_diorite', weight: 10 }
], { seed: 33, freq: 0.12 });
for (gx = IAX1; gx <= IAX2; gx++) {
for (gz = IBZ1; gz <= IBZ2; gz++) {
if (inNave(gx, gz)) pb(gx, floorY, gz, floorPal(gx, floorY, gz));
}
}
// pillars: base, shaft, capital
for (pi = 0; pi < pillars.length; pi++) {
var px = pillars[pi][0], pz = pillars[pi][1];
pb(px, walkY, pz, 'chiseled_stone_bricks');
fill(px, walkY + 1, pz, px, ceilY - 2, pz, 'polished_granite');
pb(px, ceilY - 1, pz, 'chiseled_stone_bricks');
ps(px, ceilY - 2, pz + 1, 'stone_brick_stairs[facing=south,half=top]');
ps(px, ceilY - 2, pz - 1, 'stone_brick_stairs[facing=north,half=top]');
ps(px + 1, ceilY - 2, pz, 'stone_brick_stairs[facing=east,half=top]');
ps(px - 1, ceilY - 2, pz, 'stone_brick_stairs[facing=west,half=top]');
}
// carpet runners: nave (N-S) + transept (E-W)
rug(46, walkY, 39, 3, 21, 'red', 'yellow');
rug(38, walkY, 46, 20, 3, 'red', 'yellow');
// SANCTUARY (north arm, z 37..40): raised stone altar + candles + banners
fill(44, walkY, 37, 51, walkY, 38, 'polished_andesite');
fill(45, walkY + 1, 37, 50, walkY + 1, 37, 'chiseled_stone_bricks');
ps(46, walkY + 2, 37, 'candle[candles=3,lit=true]');
ps(49, walkY + 2, 37, 'candle[candles=3,lit=true]');
pb(47, walkY + 2, 37, 'gold_block');
pb(48, walkY + 2, 37, 'gold_block');
ps(45, walkY + 2, 36, 'yellow_wall_banner[facing=south]');
ps(50, walkY + 2, 36, 'red_wall_banner[facing=south]');
ps(47, walkY + 1, 39, 'lectern[facing=south]');
pb(44, walkY + 1, 38, 'lantern');
pb(51, walkY + 1, 38, 'lantern');
// benches in the nave, facing the sanctuary
var bx, bz2;
for (bz2 = 44; bz2 <= 55; bz2 += 3) {
for (bx = 44; bx <= 51; bx++) {
if (bx === 47 || bx === 48) continue;
chairStair(bx, walkY, bz2, 'dark_oak_stairs', 'north');
}
}
// side aisles: pots, barrels, rugs, candle stands
var ai;
for (ai = 0; ai < 4; ai++) {
pb(37 + ai * 7, walkY, 44, 'decorated_pot');
pb(37 + ai * 7, walkY, 51, 'decorated_pot');
}
rug(37, walkY, 47, 4, 3, 'orange', 'brown');
rug(56, walkY, 47, 4, 3, 'orange', 'brown');
pb(37, walkY, 45, 'barrel');
pb(58, walkY, 50, 'barrel');
ps(58, walkY, 45, 'cauldron[level=0]');
// candle stands along the walls
var cs;
for (cs = 44; cs <= 51; cs += 7) {
pb(37, walkY, cs, 'cobblestone_wall');
ps(37, walkY + 1, cs, 'candle[candles=2,lit=true]');
pb(58, walkY, cs, 'cobblestone_wall');
ps(58, walkY + 1, cs, 'candle[candles=2,lit=true]');
}
// hanging lanterns from the ceiling
var lp = [[44, 44], [51, 44], [44, 51], [51, 51], [47, 41], [47, 57], [41, 47], [54, 47]];
var li;
for (li = 0; li < lp.length; li++) {
ps(lp[li][0], ceilY - 1, lp[li][1], 'chain[axis=y]');
ps(lp[li][0], ceilY - 2, lp[li][1], 'lantern[hanging=true]');
}
// ===================== 9. COURTYARD + PLATEAU DETAIL =====================
// torches on the trench rim + processional path to the canyon mouth
var ri;
for (ri = 24; ri <= 72; ri += 8) {
lampPost(TX1 - 2, groundY + 1, ri, 4);
lampPost(TX2 + 2, groundY + 1, ri, 4);
}
for (ri = 24; ri <= 72; ri += 8) {
pb(ri, courtWalkY, TZ1 + 1, 'cobblestone_wall');
ps(ri, courtWalkY + 1, TZ1 + 1, 'lantern');
}
// dirt path across the plateau to the stair mouth
var pz2, pw;
for (pz2 = 84; pz2 <= 95; pz2++) {
for (pw = 44; pw <= 51; pw++) {
pb(pw, Math.round(pad(pw, pz2)), pz2, 'dirt_path');
}
}
// acacia grove + dry scrub on the plateau (outside the trench)
var trees = [[10, 12], [14, 60], [9, 84], [30, 88], [64, 90], [86, 20], [88, 58], [82, 86], [26, 8], [70, 10]];
var ti, ty;
for (ti = 0; ti < trees.length; ti++) {
ty = Math.round(pad(trees[ti][0], trees[ti][1])) + 1;
try { placeTree(trees[ti][0], ty, trees[ti][1], 'acacia'); } catch (err) { }
}
var scx, scz, sy;
for (scx = 4; scx < 94; scx += 3) {
for (scz = 4; scz < 94; scz += 3) {
if (scx > 14 && scx < 82 && scz > 14 && scz < 82) continue;
if (noise2d('perlin', scx, scz, { freq: 0.16, seed: 6 }) > 0.35) {
sy = Math.round(pad(scx, scz)) + 1;
pb(scx, sy, scz, (scx + scz) % 3 === 0 ? 'dead_bush' : 'short_grass');
}
}
}
// boulders spilled in the courtyard corners
fill(24, courtWalkY, 24, 26, courtWalkY + 1, 26, 'mossy_cobblestone');
fill(71, courtWalkY, 71, 73, courtWalkY + 1, 73, 'mossy_cobblestone');
// final guarantee: nothing intrudes into the nave volume above the furniture line
clearMasked(IAX1, walkY + 3, IAZ1, IAX2, ceilY - 1, IAZ2, onlyMaterial('coarse_dirt'));
// ===== FIX: ningun placeholder puede sobrevivir en el volumen =====
replaceMasked(0, 0, 0, 95, 30, 95, bodyPal, onlyMaterial(PH));
replaceMasked(0, 0, 0, 95, 30, 95, roofPal, onlyMaterial(PR));