// === BUILD PLAN ===
// Style: Lanna (northern Thai) teak stilt house, riverbank
// Footprint: body 13x12 (x20..32, z20..31) raised on posts + open veranda 13x8 (z32..39)
// + exterior stair descending south to the yard. NON-rectangular: house + deck + stair snout.
// Height: posts ~7 (ground Y11-12 -> deck Y18), walls Y19..23, gable ridge Y29, kalae finials Y32.
//
// SITE (top-down, Z increases south):
// z 0..22 river (irregular meander, water)
// z 20..31 HOUSE BODY (north posts stand IN the shallow water)
// z 32..39 VERANDA deck (open, railed) -- roof covers only z32..33
// z 34..45 yard on a flat pad at Y11, exterior stair lands at z38
//
// GROUND FLOOR (living floor, 1 char = 1 block):
// z20 ####W##W#### # dark-teak plank wall / L spruce log post
// z21 L L W window opening w/ trapdoor awning
// z23 W hearth W D door onto the veranda
// z25 W o=table W
// z28 W bed W
// z31 ####W#D#W####
// z32 .P.......P. veranda (open), P = roof post
// z38 ..fff=Sfff.. railing with stair gap at x26
//
// FRONT (south) ELEVATION:
// /\ <- kalae crossed finials
// /##\
// /####\ brick/granite/mud-brick tile gable, ridge beam solid (no ridge_gap)
// |==D==| plank wall + protruding eave band
// | | | round teak posts, cross-braced
// ~~~~~~~ water / bank
//
// MATERIALS PLAN
// ground : patches(grass/podzol/coarse_dirt/moss) - WHY: big calm blotches, no confetti
// posts : stripped_dark_oak_log round teak + spruce_log braces - WHY: dark verticals read as stilts
// deck : noisePalette dark_oak/spruce/stripped_dark_oak planks - WHY: worn plank variation
// walls : noisePalette dark_oak(60)+spruce(25)+stripped_dark_oak_wood(15) planks
// with spruce_log frame posts - WHY: dark teak boards, log frame = contrast + relief
// roof : noisePalette brick_stairs(50)+granite_stairs(28)+mud_brick_stairs(22)
// - WHY: red-brown clay tile with fired-batch variation; fed per-stair so facing survives
// accents: spruce trapdoor shutters/awnings, lanterns on chains, decorated pots
// Rooms: one main room - hearth (stone + campfire), low table + floor mats/cushions,
// bed, loom, barrels/chests, hanging lanterns on exposed ceiling joists.
// STAIR PLAN: ground walk Y12 -> veranda walk Y19, delta 7 -> staircase(26,45,12,19,"north"),
// 8 cells, lands flush at (26,19,38) on the deck. Width 3 (broad).
// === END PLAN ===
// ---------------- Y LEVELS ----------------
var padY = 11; // flat yard pad
var groundWalk = padY + 1; // 12
var deckY = 18; // living-floor blocks
var walkY = deckY + 1; // 19 <- ALL furniture here
var wallTopY = walkY + 4; // 23 top wall row = roof base
var roofBaseY = wallTopY; // 23
var ridgeY = roofBaseY + 6; // 29
var X1 = 20, X2 = 32; // body x span
var Z1 = 20, Z2 = 31; // body z span
var VZ2 = 39; // veranda south edge
// ---------------- PALETTES ----------------
var plankPal = noisePalette([{block:'dark_oak_planks',weight:60},{block:'spruce_planks',weight:25},{block:'stripped_dark_oak_wood',weight:15}], {seed:31, freq:0.16});
var deckPal = noisePalette([{block:'dark_oak_planks',weight:45},{block:'spruce_planks',weight:35},{block:'stripped_dark_oak_wood',weight:20}], {seed:12, freq:0.2});
var roofPal = noisePalette([{block:'brick_stairs',weight:50},{block:'granite_stairs',weight:28},{block:'mud_brick_stairs',weight:22}], {seed:5, freq:0.14});
// ---------------- 1. TERRAIN + RIVER ----------------
var land = blob(32, 30, 34, {irregularity:0.42, seed:23});
var h0 = hills({base:12, amp:2.6, scale:0.045, seed:7});
var riv = river({from:[-8,6], to:[72,17], heightFn:h0, halfWidth:8, widthVar:0.45,
bends:2, amplitude:6, seed:9, bank:2, depth:4, bounds:land.bounds});
var gh = flatten(riv.height, {x0:10, z0:34, x1:52, z1:62}, padY, 7);
var soil = patches([{block:'grass_block',weight:58},{block:'podzol',weight:20},{block:'coarse_dirt',weight:14},{block:'moss_block',weight:8}], 0.055, 4);
var groundMat = 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, groundMat, {sub:'dirt', deep:'stone', depth:5});
checkpoint('TERRAIN');
function groundAt(x, z) { return Math.floor(gh(x, z)); }
// ---------------- 2. STILTS (round teak posts) ----------------
var postXs = [20, 23, 26, 29, 32];
var postZs = [20, 23, 26, 29, 31, 34, 37];
var pi, pj, gy;
for (pi = 0; pi < postXs.length; pi++) {
for (pj = 0; pj < postZs.length; pj++) {
gy = groundAt(postXs[pi], postZs[pj]);
if (gy > deckY - 1) gy = deckY - 1;
fill(postXs[pi], gy, postZs[pj], postXs[pi], deckY - 1, postZs[pj], 'stripped_dark_oak_log');
}
}
// cross beams under the deck (structure reads, nothing floats)
var bz, bx;
for (pj = 0; pj < postZs.length; pj++) {
bz = postZs[pj];
fill(X1, deckY - 1, bz, X2, deckY - 1, bz, 'spruce_log[axis=x]');
}
for (pi = 0; pi < postXs.length; pi++) {
bx = postXs[pi];
fill(bx, deckY - 2, 20, bx, deckY - 2, 37, 'spruce_log[axis=z]');
}
checkpoint('STILTS');
// ---------------- 3. DECK ----------------
fill(X1, deckY, Z1, X2, deckY, VZ2, deckPal);
// deck skirt/fascia: protruding board under the edge = facade relief
for (bz = Z1; bz <= VZ2; bz++) {
ps(X1 - 1, deckY, bz, 'dark_oak_slab[type=top]');
ps(X2 + 1, deckY, bz, 'dark_oak_slab[type=top]');
}
for (bx = X1; bx <= X2; bx++) ps(bx, deckY, VZ2 + 1, 'dark_oak_slab[type=top]');
checkpoint('DECK');
// ---------------- 4. WALLS ----------------
// full perimeter first, then carve openings
makeWalls(X1, walkY, Z1, X2, wallTopY, Z2, plankPal);
// spruce log frame posts (corners + intermediates) = contrast + vertical rhythm
var framePts = [[X1,Z1],[X2,Z1],[X1,Z2],[X2,Z2],[26,Z1],[26,Z2],[X1,25],[X2,25],[X1,28],[X2,28]];
var fi;
for (fi = 0; fi < framePts.length; fi++) {
fill(framePts[fi][0], walkY, framePts[fi][1], framePts[fi][0], wallTopY, framePts[fi][1], 'spruce_log');
}
// protruding pilaster posts OUTSIDE the long walls (real facade depth)
var pilZ = [23, 27, 30];
for (fi = 0; fi < pilZ.length; fi++) {
fill(X1 - 1, walkY, pilZ[fi], X1 - 1, wallTopY - 1, pilZ[fi], 'stripped_dark_oak_log');
fill(X2 + 1, walkY, pilZ[fi], X2 + 1, wallTopY - 1, pilZ[fi], 'stripped_dark_oak_log');
}
// eave band: upside-down stairs all round, protruding one block
for (bz = Z1 - 1; bz <= Z2 + 1; bz++) {
ps(X1 - 1, wallTopY, bz, 'spruce_stairs[facing=east,half=top]');
ps(X2 + 1, wallTopY, bz, 'spruce_stairs[facing=west,half=top]');
}
for (bx = X1 - 1; bx <= X2 + 1; bx++) {
ps(bx, wallTopY, Z1 - 1, 'spruce_stairs[facing=south,half=top]');
ps(bx, wallTopY, Z2 + 1, 'spruce_stairs[facing=north,half=top]');
}
checkpoint('WALLS');
// ---------------- 5. WINDOWS + DOOR ----------------
function sideWindow(wx, wz, outX, awnFacing) {
fill(wx, walkY + 1, wz, wx, walkY + 2, wz, 'air');
ps(wx, walkY, wz, 'spruce_slab[type=top]'); // sill
pb(wx, walkY + 1, wz, 'dark_oak_fence'); // carved bar
ps(outX, walkY + 3, wz, 'spruce_trapdoor[facing=' + awnFacing + ',half=top,open=false]'); // awning
}
var wz2;
var westWin = [23, 24, 27, 28];
for (fi = 0; fi < westWin.length; fi++) {
wz2 = westWin[fi];
sideWindow(X1, wz2, X1 - 1, 'west');
sideWindow(X2, wz2, X2 + 1, 'east');
}
// north wall windows looking at the river
var northWin = [23, 24, 28, 29];
for (fi = 0; fi < northWin.length; fi++) {
fill(northWin[fi], walkY + 1, Z1, northWin[fi], walkY + 2, Z1, 'air');
ps(northWin[fi], walkY, Z1, 'spruce_slab[type=top]');
pb(northWin[fi], walkY + 1, Z1, 'dark_oak_fence');
ps(northWin[fi], walkY + 3, Z1 - 1, 'spruce_trapdoor[facing=north,half=top,open=false]');
}
// door onto the veranda (south wall), clears walkY and walkY+1
fill(26, walkY, Z2, 26, walkY + 1, Z2, 'air');
ps(26, walkY, Z2, 'spruce_door[facing=south,half=lower,hinge=left,open=false]');
ps(26, walkY + 1, Z2, 'spruce_door[facing=south,half=upper,hinge=left,open=false]');
// flanking veranda windows
fill(23, walkY + 1, Z2, 23, walkY + 2, Z2, 'air');
fill(29, walkY + 1, Z2, 29, walkY + 2, Z2, 'air');
ps(23, walkY, Z2, 'spruce_slab[type=top]');
ps(29, walkY, Z2, 'spruce_slab[type=top]');
checkpoint('OPENINGS');
// ---------------- 6. ROOF (steep gable, red-brown tile) ----------------
var rz, s;
for (rz = Z1 - 1; rz <= 33; rz++) {
for (s = 0; s <= 6; s++) {
roofStair(X1 - 1 + s, roofBaseY + s, rz, roofPal(X1 - 1 + s, 0, rz), 'west');
roofStair(X2 + 1 - s, roofBaseY + s, rz, roofPal(X2 + 1 - s, 0, rz), 'east');
}
pb(26, ridgeY, rz, 'stripped_spruce_log[axis=z]'); // SOLID ridge beam - no ridge_gap
}
// gable end walls (fill the triangles - no sky inside)
var gz, ge = [Z1, Z2];
for (fi = 0; fi < ge.length; fi++) {
gz = ge[fi];
for (s = 0; s <= 6; s++) {
fill(X1 + s, roofBaseY + s, gz, X2 - s, roofBaseY + s, gz, plankPal);
}
// barge boards
for (s = 0; s <= 6; s++) {
pb(X1 - 1 + s, roofBaseY + s, gz, 'stripped_spruce_log');
pb(X2 + 1 - s, roofBaseY + s, gz, 'stripped_spruce_log');
}
}
// KALAE - crossed carved finials over both gable ends
var kz = [Z1 - 1, 33];
for (fi = 0; fi < kz.length; fi++) {
gz = kz[fi];
pb(25, ridgeY + 1, gz, 'stripped_spruce_log[axis=y]');
pb(24, ridgeY + 2, gz, 'stripped_spruce_log[axis=y]');
pb(23, ridgeY + 3, gz, 'stripped_spruce_log[axis=y]');
pb(27, ridgeY + 1, gz, 'stripped_spruce_log[axis=y]');
pb(28, ridgeY + 2, gz, 'stripped_spruce_log[axis=y]');
pb(29, ridgeY + 3, gz, 'stripped_spruce_log[axis=y]');
pb(26, ridgeY + 1, gz, 'stripped_spruce_log[axis=x]'); // crossing piece
}
// veranda roof posts (carry the z32..33 overhang)
fill(X1, walkY, 33, X1, roofBaseY - 1, 33, 'stripped_dark_oak_log');
fill(X2, walkY, 33, X2, roofBaseY - 1, 33, 'stripped_dark_oak_log');
ps(X1, roofBaseY - 1, 32, 'spruce_stairs[facing=north,half=top]');
ps(X2, roofBaseY - 1, 32, 'spruce_stairs[facing=north,half=top]');
checkpoint('ROOF');
// ---------------- 7. INTERIOR ----------------
// exposed ceiling joists (also the anchor for hanging lanterns)
var jz = [23, 26, 29];
for (fi = 0; fi < jz.length; fi++) fill(X1 + 1, wallTopY, jz[fi], X2 - 1, wallTopY, jz[fi], 'stripped_spruce_log[axis=x]');
// floor mats
var mx, mz;
for (mx = 22; mx <= 30; mx++) {
for (mz = 22; mz <= 29; mz++) {
if ((mx + mz) % 7 === 0) pb(mx, walkY, mz, 'brown_carpet');
else if ((mx * 2 + mz) % 11 === 0) pb(mx, walkY, mz, 'orange_carpet');
}
}
// low table (fence + plate) with cushions round it
for (mx = 25; mx <= 27; mx++) {
pb(mx, walkY, 25, 'dark_oak_fence');
pb(mx, walkY + 1, 25, 'dark_oak_pressure_plate');
}
pb(25, walkY, 24, 'red_carpet'); pb(27, walkY, 24, 'red_carpet');
pb(25, walkY, 26, 'red_carpet'); pb(27, walkY, 26, 'red_carpet');
// hearth in the NE corner: stone base so no fire on wood
fill(29, walkY, 21, 30, walkY, 22, 'stone_bricks');
ps(30, walkY + 1, 22, 'campfire[lit=true,facing=west]');
ps(29, walkY + 1, 21, 'cauldron[level=0]');
ps(29, walkY, 23, 'barrel[facing=up]');
// bed + nightstand
placeBed(22, walkY, 28, 'white', 'north');
pb(23, walkY, 29, 'spruce_fence'); pb(23, walkY + 1, 29, 'oak_pressure_plate'); pb(23, walkY + 2, 29, 'lantern');
// loom + storage along the west wall
ps(21, walkY, 24, 'loom[facing=east]');
pb(21, walkY, 25, 'chest[facing=east]');
ps(21, walkY, 22, 'barrel[facing=up]');
ps(21, walkY + 1, 22, 'decorated_pot');
// shelf of slabs with pots
ps(30, walkY + 2, 27, 'spruce_slab[type=top]');
ps(30, walkY + 2, 28, 'spruce_slab[type=top]');
pb(30, walkY + 3, 27, 'flower_pot');
// hanging lanterns from the joists
var lanPts = [[24,23],[28,26],[24,29]];
for (fi = 0; fi < lanPts.length; fi++) {
ps(lanPts[fi][0], wallTopY - 1, lanPts[fi][1], 'chain[axis=y]');
ps(lanPts[fi][0], wallTopY - 2, lanPts[fi][1], 'lantern[hanging=true]');
}
checkpoint('INTERIOR');
// ---------------- 8. VERANDA + EXTERIOR STAIR ----------------
// railing (gap at the stair head x25..27 on the south edge)
for (bz = 33; bz <= VZ2; bz++) {
pb(X1, walkY, bz, 'dark_oak_fence');
pb(X2, walkY, bz, 'dark_oak_fence');
}
for (bx = X1; bx <= X2; bx++) {
if (bx < 25 || bx > 27) pb(bx, walkY, VZ2, 'dark_oak_fence');
}
// broad exterior staircase from the yard up to the deck
var top = staircase(26, 45, groundWalk, walkY, 'north', {width:3, material:'dark_oak_stairs', rail:true, support:'stripped_dark_oak_log'});
// veranda furniture kept clear of the stair head and the door line
chairStair(22, walkY, 36, 'spruce_stairs', 'east');
chairStair(30, walkY, 36, 'spruce_stairs', 'west');
pb(23, walkY, 35, 'spruce_fence'); pb(23, walkY + 1, 35, 'oak_pressure_plate');
ps(30, walkY, 34, 'water_cauldron[level=3]');
ps(21, walkY, 34, 'decorated_pot');
ps(31, walkY, 38, 'potted_bamboo');
ps(21, walkY, 38, 'potted_bamboo');
// lanterns hung from the veranda roof edge
ps(22, roofBaseY - 1, 33, 'chain[axis=y]'); ps(22, roofBaseY - 2, 33, 'lantern[hanging=true]');
ps(30, roofBaseY - 1, 33, 'chain[axis=y]'); ps(30, roofBaseY - 2, 33, 'lantern[hanging=true]');
checkpoint('VERANDA_STAIR');
// ---------------- 9. UNDER-HOUSE + LANDSCAPE ----------------
// working space under the house (Thai houses use it): woodpile, barrels, loom
var uy = groundAt(24, 33) + 1;
fill(23, uy, 33, 24, uy + 1, 34, 'stripped_spruce_log[axis=x]');
ps(28, uy, 34, 'barrel[facing=up]');
ps(29, uy, 34, 'barrel[facing=up]');
ps(28, uy + 1, 34, 'barrel[facing=up]');
ps(22, uy, 36, 'loom[facing=south]');
// yard: path from the stair foot, lamp posts, fence, plants
var py;
for (bz = 46; bz <= 55; bz++) {
for (bx = 25; bx <= 27; bx++) {
py = groundAt(bx, bz);
pb(bx, py, bz, (bx === 26) ? 'gravel' : 'coarse_dirt');
}
}
lampPost(23, groundAt(23, 46) + 1, 46, 4, 'spruce_fence');
lampPost(29, groundAt(29, 46) + 1, 46, 4, 'spruce_fence');
// reeds + lilies along the water, bamboo clumps, jungle trees
var sx, sz, gy2;
for (sx = 8; sx <= 56; sx++) {
for (sz = 4; sz <= 34; sz++) {
gy2 = groundAt(sx, sz);
if (gy2 === riv.waterY && !riv.inChannel(sx, sz) && (sx * 7 + sz * 3) % 11 === 0) {
fill(sx, gy2 + 1, sz, sx, gy2 + 1 + ((sx + sz) % 3), sz, 'sugar_cane');
}
if (riv.inChannel(sx, sz) && (sx * 5 + sz * 9) % 23 === 0) pb(sx, riv.waterY + 1, sz, 'lily_pad');
}
}
var bamb = [[15,30],[16,33],[14,36],[42,31],[44,34],[41,37],[38,48],[16,50]];
for (fi = 0; fi < bamb.length; fi++) {
sx = bamb[fi][0]; sz = bamb[fi][1];
gy2 = groundAt(sx, sz);
fill(sx, gy2 + 1, sz, sx, gy2 + 5 + (fi % 3), sz, 'bamboo[age=1,leaves=large,stage=0]');
}
placeTree(13, groundAt(13, 44) + 1, 44, 'jungle');
placeTree(45, groundAt(45, 47) + 1, 47, 'jungle');
placeTree(38, groundAt(38, 56) + 1, 56, 'oak');
checkpoint('LANDSCAPE');