// === BUILD PLAN ===
// Style: Lanna (northern Thai) teak stilt house — CROSS-GABLE (related-graph anchor)
// Footprint: L / cross plan. Enclosed body 12x12 (x22..33, z22..33) + OPEN roofed veranda wing
// 9x7 (x34..42, z25..31) cantilevered EAST over the river + west entry porch (x20..21, z25..29).
// Height: posts (bed/bank Y~9 -> deck Y18), walls Y19..23, main ridge Y29, wing ridge Y24, kalae Y32.
//
// SITE (top-down, X increases east):
// x 2..27 west bank, flat pad Y11 (yard, stair foot)
// x 28..33 bank slope -> shallow water
// x 33..57 river channel (meander, N-S)
//
// LIVING FLOOR:
// x: 20 22 33 34 42
// z22 ############
// z25 pp # shrine #=======# p entry porch, = open veranda wing (no walls)
// z27 pS==D low tbl # jars # S = stair head, D = door in west wall
// z29 pp # |partition|=======#
// z33 ############
//
// WEST ELEVATION: EAST (river) ELEVATION:
// /\ kalae /\ kalae on the wing gable
// /##\ brick tile /##\
// |WpDpW| p = pilaster | ~ | open veranda under the wing gable
// | || | round teak posts | ||| |
// ~~~~~~~ ~~~~~~ water
//
// MATERIALS PLAN
// ground: patches(grass/podzol/coarse_dirt) + gravel shoreline — WHY: calm blotches, organic
// posts : noisePalette dark_oak_log / stripped_dark_oak_log — WHY: weathered round teak piles
// walls : gradientPalette dark_oak(bottom)->stripped_dark_oak_wood->spruce(top), noise 0.25
// — WHY: fakes shadow/soot low + sun-bleached boards high (palette by VOLUME, not random)
// relief: stripped_oak_log PILASTERS every 4 blocks (+1) + RECESSED windows (-1) + CORNICE of
// upside-down stairs — the three anti-flat-wall levers from shape-techniques
// roof : placeGableRoof / placeGableRoofNS prelude helpers, brick_stairs + brick_slab +
// terracotta gable fill — WHY: red-brown clay tile; helpers guarantee ridge + END gables
// accents: spruce trapdoor SHUTTERS (open, flanking), lanterns on chains, decorated pots, bamboo raft
// Rooms: sala (low table, mats, shrine shelf) | partition | sleeping alcove (bed, chest, loom)
// STAIR PLAN: yard walk Y12 -> deck walk Y19, delta 7 -> staircase(14,27,12,19,"east"), 8 cells,
// lands flush at (21,19,27) on the entry porch; door at x22,z27 straight ahead. Width 3.
// === END PLAN ===
var padY = 11;
var groundWalk = padY + 1; // 12
var deckY = 18;
var walkY = deckY + 1; // 19
var wallTopY = walkY + 4; // 23
var roofBaseY = wallTopY; // 23
var mainRidgeY = 29; // 23 + ceil(14/2) - 1
var wingBaseY = 21;
var BX1 = 22, BX2 = 33, BZ1 = 22, BZ2 = 33; // enclosed body
var WX1 = 34, WX2 = 42, WZ1 = 25, WZ2 = 31; // open veranda wing
var PX1 = 20, PX2 = 21, PZ1 = 25, PZ2 = 29; // entry porch
// ---------- PALETTES ----------
var wallPal = gradientPalette(walkY, wallTopY, [
{block:'dark_oak_planks', from:0},
{block:'stripped_dark_oak_wood', from:0.5},
{block:'spruce_planks', from:0.85}], {noise:0.25, seed:44});
var postPal = noisePalette([{block:'dark_oak_log',weight:55},{block:'stripped_dark_oak_log',weight:30},{block:'stripped_spruce_log',weight:15}], {seed:8, freq:0.18});
var deckPal = noisePalette([{block:'spruce_planks',weight:50},{block:'dark_oak_planks',weight:30},{block:'stripped_spruce_wood',weight:20}], {seed:66, freq:0.22});
// ---------- 1. SITE ----------
var land = blob(30, 32, 34, {irregularity:0.38, seed:61});
var h0 = hills({base:12, amp:3, scale:0.05, seed:19});
var riv = river({from:[48,-8], to:[42,72], heightFn:h0, halfWidth:7, widthVar:0.4,
bends:2, amplitude:5, seed:31, bank:2, depth:4, bounds:land.bounds});
var gh = flatten(riv.height, {x0:2, z0:12, x1:26, z1:54}, padY, 7);
var soil = patches([{block:'grass_block',weight:55},{block:'podzol',weight:22},{block:'coarse_dirt',weight:15},{block:'rooted_dirt',weight:8}], 0.05, 12);
var siteMat = function (x, z) {
if (riv.inChannel(x, z)) return 'sand';
if (gh(x, z) <= riv.waterY + 1) return 'gravel';
return soil(x, z);
};
surface(land, gh, siteMat, {sub:'dirt', deep:'stone', depth:5});
checkpoint('SITE');
function gAt(x, z) { var v = Math.floor(gh(x, z)); if (v > deckY - 1) v = deckY - 1; return v; }
// ---------- 2. PILES ----------
var pileList = [];
var ax, az;
for (ax = BX1; ax <= WX2; ax += 3) {
for (az = BZ1; az <= BZ2; az += 3) {
if (ax > BX2 && (az < WZ1 || az > WZ2)) continue;
pileList.push([ax, az]);
}
}
pileList.push([BX2, BZ2]); pileList.push([WX2, WZ1]); pileList.push([WX2, WZ2]);
pileList.push([PX1, PZ1]); pileList.push([PX1, PZ2]);
var i, px, pz;
for (i = 0; i < pileList.length; i++) {
px = pileList[i][0]; pz = pileList[i][1];
fill(px, gAt(px, pz), pz, px, deckY - 1, pz, postPal);
}
// sill beams under the deck (nothing floats, structure reads)
for (az = BZ1; az <= BZ2; az += 3) fill(BX1, deckY - 1, az, WX2, deckY - 1, az, 'spruce_log[axis=x]');
for (ax = BX1; ax <= WX2; ax += 3) fill(ax, deckY - 2, BZ1, ax, deckY - 2, BZ2, 'spruce_log[axis=z]');
checkpoint('PILES');
// ---------- 3. DECK ----------
fill(BX1, deckY, BZ1, BX2, deckY, BZ2, deckPal);
fill(WX1, deckY, WZ1, WX2, deckY, WZ2, deckPal);
fill(PX1, deckY, PZ1, PX2, deckY, PZ2, deckPal);
// fascia board round the whole deck edge (relief under the floor line)
for (az = BZ1 - 1; az <= BZ2 + 1; az++) ps(BX1 - 3, deckY, az, 'air');
for (az = BZ1; az <= BZ2; az++) { ps(BX1 - 1, deckY, az, 'spruce_slab[type=top]'); }
for (ax = BX1; ax <= WX2; ax++) { ps(ax, deckY, BZ1 - 1, 'spruce_slab[type=top]'); ps(ax, deckY, BZ2 + 1, 'spruce_slab[type=top]'); }
for (az = WZ1; az <= WZ2; az++) ps(WX2 + 1, deckY, az, 'spruce_slab[type=top]');
checkpoint('DECK');
// ---------- 4. WALLS + ANTI-FLAT RELIEF ----------
makeWalls(BX1, walkY, BZ1, BX2, wallTopY, BZ2, wallPal);
// PILASTERS every 4 blocks, protruding +1 (shape-techniques anti-flat #1)
var pilZ = [25, 29, 33], pilX = [25, 29, 33];
for (i = 0; i < pilZ.length; i++) {
fill(BX1 - 1, walkY, pilZ[i], BX1 - 1, wallTopY, pilZ[i], 'stripped_oak_log');
}
for (i = 0; i < pilX.length; i++) {
fill(pilX[i], walkY, BZ1 - 1, pilX[i], wallTopY, BZ1 - 1, 'stripped_oak_log');
fill(pilX[i], walkY, BZ2 + 1, pilX[i], wallTopY, BZ2 + 1, 'stripped_oak_log');
}
// corner posts
fill(BX1, walkY, BZ1, BX1, wallTopY, BZ1, 'dark_oak_log');
fill(BX2, walkY, BZ1, BX2, wallTopY, BZ1, 'dark_oak_log');
fill(BX1, walkY, BZ2, BX1, wallTopY, BZ2, 'dark_oak_log');
fill(BX2, walkY, BZ2, BX2, wallTopY, BZ2, 'dark_oak_log');
// CORNICE: upside-down stairs at the roofline, all four sides (+1 protrusion)
for (az = BZ1 - 1; az <= BZ2 + 1; az++) {
ps(BX1 - 1, wallTopY, az, 'spruce_stairs[facing=east,half=top]');
ps(BX2 + 1, wallTopY, az, 'spruce_stairs[facing=west,half=top]');
}
for (ax = BX1 - 1; ax <= BX2 + 1; ax++) {
ps(ax, wallTopY, BZ1 - 1, 'spruce_stairs[facing=south,half=top]');
ps(ax, wallTopY, BZ2 + 1, 'spruce_stairs[facing=north,half=top]');
}
checkpoint('WALLS');
// ---------- 5. RECESSED SHUTTERED WINDOWS + DOOR ----------
// window = opening 1x2, sill stair out, SHUTTER trapdoors open on both flanks
function shuttered(wx, wy, wz, axis, outDx, outDz, shutFace) {
fill(wx, wy, wz, wx, wy + 1, wz, 'air');
ps(wx + outDx, wy - 1, wz + outDz, 'spruce_stairs[facing=' + shutFace + ',half=bottom]'); // sill
if (axis === 'z') {
ps(wx, wy, wz - 1, 'spruce_trapdoor[facing=north,half=bottom,open=true]');
ps(wx, wy, wz + 1, 'spruce_trapdoor[facing=south,half=bottom,open=true]');
} else {
ps(wx - 1, wy, wz, 'spruce_trapdoor[facing=west,half=bottom,open=true]');
ps(wx + 1, wy, wz, 'spruce_trapdoor[facing=east,half=bottom,open=true]');
}
}
shuttered(BX1, walkY + 1, 24, 'z', -1, 0, 'west');
shuttered(BX1, walkY + 1, 31, 'z', -1, 0, 'west');
shuttered(BX2, walkY + 1, 24, 'z', 1, 0, 'east');
shuttered(BX2, walkY + 1, 28, 'z', 1, 0, 'east');
shuttered(24, walkY + 1, BZ1, 'x', 0, -1, 'north');
shuttered(31, walkY + 1, BZ1, 'x', 0, -1, 'north');
shuttered(24, walkY + 1, BZ2, 'x', 0, 1, 'south');
shuttered(31, walkY + 1, BZ2, 'x', 0, 1, 'south');
// door in the west wall, straight off the stair head
fill(BX1, walkY, 27, BX1, walkY + 1, 27, 'air');
ps(BX1, walkY, 27, 'spruce_door[facing=east,half=lower,hinge=left,open=false]');
ps(BX1, walkY + 1, 27, 'spruce_door[facing=east,half=upper,hinge=left,open=false]');
// wide opening from the sala onto the veranda wing (east wall)
fill(BX2, walkY, 27, BX2, walkY + 2, 29, 'air');
pb(BX2, walkY + 3, 27, 'dark_oak_log'); pb(BX2, walkY + 3, 29, 'dark_oak_log');
checkpoint('OPENINGS');
// ---------- 6. ROOFS — prelude gable helpers (no hand-rolled loop) ----------
placeGableRoof(BX1, BX2, BZ1, BZ2, roofBaseY, 'brick_stairs', 'brick_slab', 'terracotta');
placeGableRoofNS(WX1, WX2, WZ1, WZ2, wingBaseY, 'brick_stairs', 'brick_slab', 'terracotta');
// veranda wing posts carry its roof
var wingPosts = [[WX1, WZ1],[WX1, WZ2],[WX2, WZ1],[WX2, WZ2],[38, WZ1],[38, WZ2]];
for (i = 0; i < wingPosts.length; i++) {
fill(wingPosts[i][0], walkY, wingPosts[i][1], wingPosts[i][0], wingBaseY - 1, wingPosts[i][1], 'dark_oak_log');
}
// close the cross-gable junction so no sky slips between the two roofs
fill(WX1 - 1, wingBaseY, WZ1, WX1 - 1, roofBaseY, WZ2, 'terracotta');
// KALAE crossed finials: both main gable ends + the river-facing wing gable
function kalae(cx, cy, cz, alongZ) {
var d;
for (d = 1; d <= 3; d++) {
if (alongZ) {
pb(cx - d, cy + d, cz, 'stripped_spruce_log[axis=y]');
pb(cx + d + 1, cy + d, cz, 'stripped_spruce_log[axis=y]');
} else {
pb(cx, cy + d, cz - d, 'stripped_spruce_log[axis=y]');
pb(cx, cy + d, cz + d + 1, 'stripped_spruce_log[axis=y]');
}
}
}
kalae(27, mainRidgeY, BZ1 - 1, true);
kalae(27, mainRidgeY, BZ2 + 1, true);
kalae(WX2 + 1, wingBaseY + 3, 27, false);
checkpoint('ROOF');
// ---------- 7. INTERIOR: sala + partition + sleeping alcove ----------
// ridge/ceiling beams (lantern anchors)
for (az = 24; az <= 32; az += 4) fill(BX1 + 1, wallTopY, az, BX2 - 1, wallTopY, az, 'stripped_spruce_log[axis=x]');
// woven partition with a doorway
fill(24, walkY, 30, 32, walkY + 2, 30, 'bamboo_planks');
fill(27, walkY, 30, 28, walkY + 1, 30, 'air');
// floor mats in the sala
rug(24, walkY, 24, 6, 5, 'red', 'brown');
// low table + cushions
pb(27, walkY, 26, 'dark_oak_fence'); pb(27, walkY + 1, 26, 'dark_oak_pressure_plate');
pb(28, walkY, 26, 'dark_oak_fence'); pb(28, walkY + 1, 26, 'dark_oak_pressure_plate');
pb(26, walkY, 26, 'orange_carpet'); pb(29, walkY, 26, 'orange_carpet');
pb(27, walkY, 25, 'orange_carpet'); pb(28, walkY, 27, 'orange_carpet');
// shrine shelf on the north wall
ps(26, walkY + 2, BZ1 + 1, 'spruce_slab[type=top]');
ps(27, walkY + 2, BZ1 + 1, 'spruce_slab[type=top]');
pb(26, walkY + 3, BZ1 + 1, 'flower_pot');
ps(27, walkY + 3, BZ1 + 1, 'candle[candles=3,lit=true]');
// water jars + basin by the door
ps(23, walkY, 26, 'decorated_pot');
ps(23, walkY, 28, 'water_cauldron[level=3]');
// sleeping alcove behind the partition
placeBed(25, walkY, 32, 'white', 'north');
pb(24, walkY, 32, 'spruce_fence'); pb(24, walkY + 1, 32, 'oak_pressure_plate'); pb(24, walkY + 2, 32, 'lantern');
pb(31, walkY, 32, 'chest[facing=west]');
ps(31, walkY, 31, 'loom[facing=west]');
ps(29, walkY, 32, 'barrel[facing=up]');
// hanging lanterns from the beams
var lan = [[25, 24],[30, 24],[26, 28],[30, 32]];
for (i = 0; i < lan.length; i++) {
ps(lan[i][0], wallTopY - 1, lan[i][1], 'chain[axis=y]');
ps(lan[i][0], wallTopY - 2, lan[i][1], 'lantern[hanging=true]');
}
checkpoint('INTERIOR');
// ---------- 8. VERANDA WING + PORCH + STAIR ----------
// railing round the open wing, gap where it meets the house
for (ax = WX1; ax <= WX2; ax++) { pb(ax, walkY, WZ1, 'dark_oak_fence'); pb(ax, walkY, WZ2, 'dark_oak_fence'); }
for (az = WZ1; az <= WZ2; az++) pb(WX2, walkY, az, 'dark_oak_fence');
fill(WX1, walkY, 27, WX1, walkY, 29, 'air');
// wing furniture: stools facing the river, water jars, drying rack
chairStair(40, walkY, 26, 'spruce_stairs', 'south');
chairStair(40, walkY, 30, 'spruce_stairs', 'north');
ps(36, walkY, 26, 'decorated_pot');
ps(36, walkY, 30, 'decorated_pot');
pb(38, walkY, 28, 'spruce_fence'); pb(38, walkY + 1, 28, 'spruce_pressure_plate');
ps(41, wingBaseY - 1, 28, 'chain[axis=y]'); ps(41, wingBaseY - 2, 28, 'lantern[hanging=true]');
ps(35, wingBaseY - 1, 28, 'chain[axis=y]'); ps(35, wingBaseY - 2, 28, 'lantern[hanging=true]');
// entry porch railing + broad stair up from the yard
for (az = PZ1; az <= PZ2; az++) { if (az < 26 || az > 28) pb(PX1, walkY, az, 'dark_oak_fence'); }
pb(PX1, walkY, PZ1, 'dark_oak_fence'); pb(PX2, walkY, PZ1, 'dark_oak_fence');
pb(PX1, walkY, PZ2, 'dark_oak_fence'); pb(PX2, walkY, PZ2, 'dark_oak_fence');
var top = staircase(14, 27, groundWalk, walkY, 'east', {width:3, material:'dark_oak_stairs', rail:true, support:'dark_oak_log'});
checkpoint('VERANDA_STAIR');
// ---------- 9. UNDER-HOUSE + SHORE ----------
var uy = gAt(24, 26) + 1;
ps(24, uy, 25, 'barrel[facing=up]'); ps(25, uy, 25, 'barrel[facing=up]'); ps(24, uy + 1, 25, 'barrel[facing=up]');
fill(23, uy, 31, 24, uy + 1, 32, 'stripped_spruce_log[axis=x]'); // woodpile
ps(26, uy, 32, 'cauldron[level=0]');
// yard path + lamp posts (lampPost(x, y, z, height, postId))
var bx, bz, py;
for (bx = 6; bx <= 13; bx++) {
for (bz = 26; bz <= 28; bz++) {
py = Math.floor(gh(bx, bz));
pb(bx, py, bz, (bz === 27) ? 'gravel' : 'coarse_dirt');
}
}
lampPost(13, Math.floor(gh(13, 24)) + 1, 24, 4, 'spruce_fence');
lampPost(13, Math.floor(gh(13, 31)) + 1, 31, 4, 'spruce_fence');
// fence along the yard
for (bz = 20; bz <= 36; bz += 2) pb(8, Math.floor(gh(8, bz)) + 1, bz, 'spruce_fence');
// bamboo raft moored under the veranda wing
for (bx = 44; bx <= 49; bx++) for (bz = 27; bz <= 28; bz++) pb(bx, riv.waterY, bz, 'bamboo_mosaic');
pb(49, riv.waterY + 1, 27, 'bamboo_fence');
// reeds, lilies, bamboo clumps, jungle trees
var sx, sz, gy2;
for (sx = 4; sx <= 58; sx++) {
for (sz = 8; sz <= 56; sz++) {
gy2 = Math.floor(gh(sx, sz));
if (!riv.inChannel(sx, sz) && gy2 === riv.waterY && (sx * 5 + sz * 3) % 9 === 0) {
fill(sx, gy2 + 1, sz, sx, gy2 + 1 + ((sx + sz) % 3), sz, 'sugar_cane');
}
if (riv.inChannel(sx, sz) && (sx * 7 + sz * 11) % 29 === 0) pb(sx, riv.waterY + 1, sz, 'lily_pad');
}
}
var clumps = [[18, 20],[16, 23],[19, 35],[17, 38],[30, 16],[34, 44],[10, 40],[12, 17]];
for (i = 0; i < clumps.length; i++) {
sx = clumps[i][0]; sz = clumps[i][1]; gy2 = Math.floor(gh(sx, sz));
fill(sx, gy2 + 1, sz, sx, gy2 + 5 + (i % 3), sz, 'bamboo[age=1,leaves=large,stage=0]');
}
placeTree(9, Math.floor(gh(9, 20)) + 1, 20, 'jungle');
placeTree(11, Math.floor(gh(11, 45)) + 1, 45, 'jungle');
placeTree(22, Math.floor(gh(22, 48)) + 1, 48, 'oak');
checkpoint('SHORE');
// ---------- 10. FIX PASS: close the roof sky-holes and the hole in the deck ----------
var fs;
for (fs = 0; fs <= 5; fs++) {
fill(BX1 + fs, roofBaseY + fs, BZ1, BX2 - fs, roofBaseY + fs, BZ1, 'terracotta');
fill(BX1 + fs, roofBaseY + fs, BZ2, BX2 - fs, roofBaseY + fs, BZ2, 'terracotta');
}
pb(27, mainRidgeY, BZ1, 'terracotta'); pb(28, mainRidgeY, BZ1, 'terracotta');
pb(27, mainRidgeY, BZ2, 'terracotta'); pb(28, mainRidgeY, BZ2, 'terracotta');
// close the cross-gable junction plane right up to the main roof
fill(WX1 - 1, wingBaseY + 1, WZ1, WX1 - 1, roofBaseY + 3, WZ2, 'terracotta');
// re-lay the living floor and the veranda deck (a hole had opened through them)
fill(BX1, deckY, BZ1, BX2, deckY, BZ2, deckPal);
fill(WX1, deckY, WZ1, WX2, deckY, WZ2, deckPal);
checkpoint('FIX');