// === BUILD PLAN ===
// Style: Hakka tulou (Fujian rammed-earth communal roundhouse), rustic vernacular
// Footprint: RING (annulus). Outer wall R=22.5 (2 thick), rooms band R15.5-20.5,
// inner gallery wall R=15, open courtyard R<=14, ancestral hall 7x7 at center.
// Height: 3 storeys (walk 1 / 6 / 11), inner wall top Y15, outer parapet Y17, roof ridge Y18.
//
// PLAN (top-down, 1 char = 1 block-ish):
// ########### # = rammed-earth outer wall (2 thick)
// ##ooooooooooo## o = ring rooms (5 deep) + wooden gallery
// #ooo.........ooo# . = open courtyard (packed earth + paving)
// #oo... A ...oo# A = ancestral hall (7x7, tiled gable roof)
// #ooo.........ooo# W = well
// ##ooooooooooo## S = tangential staircases inside the ring
// #####G##### G = single gated entrance (south)
//
// SECTION (radial slice, courtyard on the LEFT, outside on the RIGHT):
// roof ridge Y18 over the outer wall, tiles slope INWARD to the courtyard eave Y13
// __/‾‾‾‾\
// rail | 3F |# 3F walk Y11 (bedrooms, gallery + railing)
// rail | 2F |# 2F walk Y6 (bedrooms/looms, gallery + railing)
// | 1F |# 1F walk Y1 (kitchen, dining hall, workshop, granary)
// ==== pad Y0 ==== flatten() pad inside organic terrain()
//
// MATERIALS PLAN:
// plinth : cobblestone(35)+andesite(25)+mossy_cobblestone(20)+stone(20) - WHY: wet stone base, grounds the mass
// wall lo: packed_mud(35)+mud_bricks(25)+cut_sandstone(15)+smooth_sandstone(25) - WHY: darker, weathered rammed earth
// wall hi: smooth_sandstone(40)+sandstone(25)+packed_mud(20)+yellow_terracotta(15) - WHY: sun-bleached ochre up top (vertical gradient = fake AO)
// roof : deepslate_tile / cobbled_deepslate / stone_brick STAIRS - WHY: grey clay tile, cool grey against warm earth = the tulou contrast
// inner : spruce planks + stripped_spruce_log posts + spruce fence rails - WHY: timber galleries read WARM against grey/ochre
// Rooms: 1F kitchen(W), dining hall(E), workshop(NW), granary(SE); 2F/3F bedrooms; ancestral hall with altar.
// STAIR PLAN: 2 tangential flights INSIDE the ring, walk1->walk2 (d=5 -> 6 cells, north, dir east)
// and walk2->walk3 (south, dir east). staircase() computes treads/holes/landings.
// === END PLAN ===
// ---------- Y LEVELS ----------
var padY = 0;
var floorY = 0;
var walk1 = 1;
var f2Y = 5, walk2 = 6;
var f3Y = 10, walk3 = 11;
var ceilY = 15;
var outerTopY = 17;
var ridgeY = 18;
// ---------- RADII ----------
var R_OUT = 22.5, R_WALL_IN = 20.4, R_ROOM_IN = 15.5, R_INNER = 14.4, R_BALC = 12.6;
function rad(x, z) { return Math.sqrt(x * x + z * z); }
function wsel(list) {
var t = 0, i;
for (i = 0; i < list.length; i++) t += list[i][1];
var r = Math.random() * t;
for (i = 0; i < list.length; i++) { r -= list[i][1]; if (r <= 0) return list[i][0]; }
return list[0][0];
}
var PLINTH = [['cobblestone', 35], ['andesite', 25], ['mossy_cobblestone', 20], ['stone', 20]];
var EARTH_LO = [['packed_mud', 35], ['mud_bricks', 25], ['cut_sandstone', 15], ['smooth_sandstone', 25]];
var EARTH_HI = [['smooth_sandstone', 40], ['sandstone', 25], ['packed_mud', 20], ['yellow_terracotta', 15]];
var TILE_ST = [['deepslate_tile_stairs', 45], ['cobbled_deepslate_stairs', 30], ['stone_brick_stairs', 25]];
var TILE_SOLID = [['deepslate_tiles', 45], ['cobbled_deepslate', 30], ['gray_terracotta', 25]];
var WOOD = [['spruce_planks', 60], ['oak_planks', 25], ['stripped_spruce_log', 15]];
var YARD = [['packed_mud', 30], ['coarse_dirt', 25], ['gravel', 20], ['cobblestone', 15], ['andesite', 10]];
function earth(y) { return y <= 6 ? wsel(EARTH_LO) : wsel(EARTH_HI); }
// gate corridor (south, x in [-1,1], z>0) is kept clear of walls/partitions
function inGate(x, z) { return (z > 13 && x >= -1 && x <= 1); }
// stair footprints inside the ring
function inStairN(x, z) { return (z <= -16 && z >= -20 && x >= -4 && x <= 4); }
function inStairS(x, z) { return (z >= 16 && z <= 20 && x >= -4 && x <= 4); }
// ===================== 1. TERRAIN =====================
var ground = terrain({
cx: 0, cz: 0, radius: 34, baseY: padY, amp: 4, scale: 0.045, seed: 17,
surface: [{ block: 'grass_block', w: 66 }, { block: 'coarse_dirt', w: 20 }, { block: 'podzol', w: 14 }],
sub: 'dirt', deep: 'stone', depth: 4,
pads: [{ cx: 0, cz: 0, r: 26, y: padY, falloff: 9 }]
});
var x, z, y, d, i, k;
// ===================== 2. GROUND SURFACES =====================
for (x = -26; x <= 26; x++) {
for (z = -26; z <= 26; z++) {
d = rad(x, z);
if (d <= R_INNER) { // courtyard: packed earth + stone paving
pb(x, floorY, z, wsel(YARD));
} else if (d <= R_WALL_IN) { // ring floor (rooms + gallery)
pb(x, floorY, z, wsel(WOOD));
}
}
}
// ===================== 3. OUTER RAMMED-EARTH WALL =====================
for (x = -25; x <= 25; x++) {
for (z = -25; z <= 25; z++) {
d = rad(x, z);
if (d > R_OUT || d <= R_WALL_IN) continue;
if (inGate(x, z)) continue;
for (y = 1; y <= outerTopY; y++) pb(x, y, z, earth(y));
}
}
// stone plinth: 1 block PROUDER than the wall, 2 rows -> facade depth at the base
for (x = -25; x <= 25; x++) {
for (z = -25; z <= 25; z++) {
d = rad(x, z);
if (d > 23.4 || d <= R_WALL_IN) continue;
if (inGate(x, z)) continue;
pb(x, 1, z, wsel(PLINTH));
pb(x, 2, z, wsel(PLINTH));
if (d > R_OUT) ps(x, 3, z, wsel(PLINTH) === 'stone' ? 'stone_slab' : 'cobblestone_slab');
}
}
// buttress pilasters every 30 deg (skip the gate arc): 1 block proud, capped with a slab
for (k = 0; k < 12; k++) {
var ang = k * 30 * Math.PI / 180;
var cx = Math.cos(ang), cz = Math.sin(ang);
if (cz > 0.90) continue;
var rr;
for (rr = 21.0; rr <= 23.4; rr += 0.45) {
var px = Math.round(cx * rr), pz = Math.round(cz * rr);
if (inGate(px, pz)) continue;
for (y = 1; y <= 14; y++) pb(px, y, pz, earth(y));
pb(px, 15, pz, 'smooth_sandstone');
ps(px, 16, pz, 'smooth_sandstone_slab');
}
}
// ===================== 4. INNER GALLERY WALL + FLOORS =====================
for (x = -21; x <= 21; x++) {
for (z = -21; z <= 21; z++) {
d = rad(x, z);
// inner wall band: ground floor solid timber, upper floors open (posts only)
if (d > R_INNER && d <= R_ROOM_IN && !inGate(x, z)) {
for (y = 1; y <= 4; y++) pb(x, y, z, wsel(WOOD));
}
// upper floor decks: ring rooms + cantilevered balcony over the courtyard
if (d > R_BALC && d <= R_WALL_IN) {
if (!inGate(x, z) || d > R_INNER) {
pb(x, f2Y, z, wsel(WOOD));
pb(x, f3Y, z, wsel(WOOD));
}
}
// 3rd-floor ceiling over the rooms only (gallery stays open to the eaves)
if (d > 16.3 && d <= R_WALL_IN) pb(x, ceilY, z, wsel(WOOD));
}
}
// gallery posts + railings every ~12 deg, both upper floors
for (k = 0; k < 30; k++) {
var a2 = k * 12 * Math.PI / 180;
var qx = Math.round(Math.cos(a2) * 14.8), qz = Math.round(Math.sin(a2) * 14.8);
for (y = walk2; y <= walk2 + 3; y++) pb(qx, y, qz, 'stripped_spruce_log');
for (y = walk3; y <= walk3 + 3; y++) pb(qx, y, qz, 'stripped_spruce_log');
var bx = Math.round(Math.cos(a2) * 13.2), bz = Math.round(Math.sin(a2) * 13.2);
pb(bx, walk2, bz, 'spruce_fence'); pb(bx, walk3, bz, 'spruce_fence');
if (k % 3 === 0) {
pb(qx, walk2 + 3, qz, 'stripped_spruce_log');
ps(qx, walk2 + 2, qz, 'lantern[hanging=false]');
ps(qx, walk3 + 2, qz, 'lantern[hanging=false]');
}
}
// continuous balcony railing ring
for (x = -15; x <= 15; x++) {
for (z = -15; z <= 15; z++) {
d = rad(x, z);
if (d > 12.4 && d <= 13.5) {
if (pb) { pb(x, walk2, z, 'spruce_fence'); pb(x, walk3, z, 'spruce_fence'); }
}
}
}
// ===================== 5. RADIAL PARTITION WALLS (rooms) =====================
for (k = 0; k < 24; k++) {
var a3 = k * 15 * Math.PI / 180;
var sx = Math.cos(a3), sz = Math.sin(a3);
var rr2;
for (rr2 = 15.6; rr2 <= 20.4; rr2 += 0.45) {
var wx = Math.round(sx * rr2), wz = Math.round(sz * rr2);
if (inGate(wx, wz) || inStairN(wx, wz) || inStairS(wx, wz)) continue;
for (y = 1; y <= 4; y++) pb(wx, y, wz, wsel(WOOD));
for (y = walk2; y <= walk2 + 3; y++) pb(wx, y, wz, wsel(WOOD));
for (y = walk3; y <= walk3 + 3; y++) pb(wx, y, wz, wsel(WOOD));
}
}
// ===================== 6. GATE (south) =====================
for (x = -1; x <= 1; x++) {
for (z = 14; z <= 24; z++) {
d = rad(x, z);
if (d > R_OUT + 1.6) continue;
for (y = 1; y <= 4; y++) b(x, y, z, 'air');
pb(x, floorY, z, 'stone_bricks');
}
}
for (x = -2; x <= 2; x++) {
for (z = 19; z <= 23; z++) {
d = rad(x, z);
if (d > R_OUT || d <= R_WALL_IN) continue;
pb(x, 5, z, 'stone_bricks');
pb(x, 6, z, 'chiseled_sandstone');
}
}
ps(-1, 4, 22, 'stone_brick_stairs[facing=east,half=top]');
ps(1, 4, 22, 'stone_brick_stairs[facing=west,half=top]');
pb(-2, 4, 23, 'lantern'); pb(2, 4, 23, 'lantern');
ps(-2, 3, 23, 'spruce_fence'); ps(2, 3, 23, 'spruce_fence');
// small tiled porch roof over the gate
for (x = -4; x <= 4; x++) {
roofStair(x, 8, 24, wsel(TILE_ST), 'south');
roofStair(x, 9, 23, wsel(TILE_ST), 'south');
pb(x, 8, 23, wsel(TILE_SOLID));
}
// ===================== 7. WINDOWS (upper floors only, deeply recessed) =====================
for (k = 0; k < 24; k++) {
var a4 = (k * 15 + 7.5) * Math.PI / 180;
var ux = Math.cos(a4), uz = Math.sin(a4);
if (uz > 0.93) continue;
var lvl, rr3;
for (lvl = 0; lvl < 2; lvl++) {
var wy = (lvl === 0 ? walk2 : walk3) + 1;
var innermost = null;
for (rr3 = 20.2; rr3 <= 23.4; rr3 += 0.4) {
var vx = Math.round(ux * rr3), vz = Math.round(uz * rr3);
b(vx, wy, vz, 'air'); b(vx, wy + 1, vz, 'air');
if (innermost === null) innermost = [vx, vz];
}
if (innermost) {
pb(innermost[0], wy, innermost[1], 'brown_stained_glass_pane');
pb(innermost[0], wy + 1, innermost[1], 'brown_stained_glass_pane');
}
}
}
// ===================== 8. STAIRCASES (tangential, inside the ring) =====================
staircase(-3, -18, walk1, walk2, 'east', { width: 2, material: 'spruce_stairs', rail: true, support: 'spruce_planks' });
staircase(-3, 18, walk2, walk3, 'east', { width: 2, material: 'spruce_stairs', rail: true, support: 'spruce_planks' });
// keep the stair wells clear of partition leftovers
for (x = -4; x <= 4; x++) {
for (z = -20; z <= -16; z++) { b(x, walk2 + 1, z, 'air'); b(x, walk2 + 2, z, 'air'); }
}
// ===================== 9. RING ROOF (tiles slope INWARD to the courtyard) =====================
function roofYFor(dd) {
if (dd > 24.6) return -1;
if (dd > 23.5) return 16;
if (dd > 22.5) return 17;
if (dd > 20.5) return ridgeY; // ridge band, sits over the outer wall
if (dd > 19.0) return 17;
if (dd > 17.5) return 16;
if (dd > 16.0) return 15;
if (dd > 14.5) return 14;
if (dd > 13.2) return 13; // eave overhangs the courtyard gallery
return -1;
}
for (x = -26; x <= 26; x++) {
for (z = -26; z <= 26; z++) {
d = rad(x, z);
var ry = roofYFor(d);
if (ry < 0) continue;
if (d > 20.5 && d <= 22.5) { // solid ridge cap - never a sky gap
pb(x, ry, z, wsel(TILE_SOLID));
continue;
}
var edge;
if (d > 22.5) { // outward slope: downhill = away from centre
edge = (Math.abs(x) > Math.abs(z)) ? (x > 0 ? 'east' : 'west') : (z > 0 ? 'south' : 'north');
} else { // inward slope: downhill = toward centre
edge = (Math.abs(x) > Math.abs(z)) ? (x > 0 ? 'west' : 'east') : (z > 0 ? 'north' : 'south');
}
roofStair(x, ry, z, wsel(TILE_ST), edge);
if (ry - 1 >= 16) pb(x, ry - 1, z, wsel(TILE_SOLID)); // underlayment above the attic only
}
}
// ===================== 10. ANCESTRAL HALL + COURTYARD =====================
for (x = -3; x <= 3; x++) {
for (z = -3; z <= 3; z++) {
pb(x, floorY, z, 'polished_andesite');
if ((Math.abs(x) === 3 || Math.abs(z) === 3) && !(z === 3 && Math.abs(x) <= 1)) {
for (y = 1; y <= 4; y++) {
if (y <= 2) pb(x, y, z, 'stone_bricks');
else pb(x, y, z, wsel([['dark_oak_planks', 60], ['spruce_planks', 25], ['stripped_dark_oak_log', 15]]));
}
}
}
}
for (x = -3; x <= 3; x += 6) { for (z = -3; z <= 3; z += 6) { for (y = 1; y <= 4; y++) pb(x, y, z, 'dark_oak_log'); } }
placeGableRoofNS(-3, 3, -3, 3, 5, 'deepslate_tile_stairs', 'deepslate_tile_slab', 'dark_oak_planks');
// altar
pb(0, walk1, -2, 'smooth_stone'); ps(0, walk1 + 1, -2, 'lectern[facing=south]');
pb(-1, walk1, -2, 'stone_brick_wall'); pb(-1, walk1 + 1, -2, 'lantern');
pb(1, walk1, -2, 'stone_brick_wall'); pb(1, walk1 + 1, -2, 'lantern');
ps(-2, walk1, -1, 'red_carpet'); ps(2, walk1, -1, 'red_carpet');
rug(-1, walk1, 0, 3, 3, 'red', 'orange');
ps(0, walk1, 2, 'oak_pressure_plate');
ps(-2, walk1, 1, 'flower_pot'); ps(2, walk1, 1, 'flower_pot');
ps(-1, 4, 3, 'red_wall_banner[facing=south]'); ps(1, 4, 3, 'red_wall_banner[facing=south]');
// courtyard: paving ring, well, plants
for (k = 0; k < 60; k++) {
var a5 = k * 6 * Math.PI / 180;
var yx = Math.round(Math.cos(a5) * 9), yz = Math.round(Math.sin(a5) * 9);
pb(yx, floorY, yz, wsel([['stone_bricks', 40], ['andesite', 30], ['cobblestone', 30]]));
}
// well
for (x = 7; x <= 9; x++) { for (z = 6; z <= 8; z++) { pb(x, walk1, z, 'cobblestone'); } }
pb(8, walk1, 7, 'water');
pb(7, walk1 + 1, 6, 'oak_fence'); pb(9, walk1 + 1, 6, 'oak_fence');
pb(7, walk1 + 1, 8, 'oak_fence'); pb(9, walk1 + 1, 8, 'oak_fence');
for (x = 7; x <= 9; x++) { for (z = 6; z <= 8; z++) ps(x, walk1 + 2, z, 'dark_oak_slab[type=bottom]'); }
ps(8, walk1 + 1, 7, 'iron_chain[axis=y]');
// plants & props in the courtyard
ps(-8, walk1, 6, 'flower_pot'); ps(-9, walk1, 7, 'flower_pot');
pb(-8, floorY, 8, 'farmland'); pb(-7, floorY, 8, 'farmland'); pb(-9, floorY, 8, 'farmland');
ps(-8, walk1, 8, 'wheat[age=6]'); ps(-7, walk1, 8, 'wheat[age=5]'); ps(-9, walk1, 8, 'wheat[age=7]');
pb(-6, walk1, -7, 'barrel'); pb(-7, walk1, -7, 'barrel'); pb(-6, walk1 + 1, -7, 'barrel');
pb(6, walk1, -7, 'cauldron'); pb(5, walk1, -8, 'composter');
lampPost(-10, walk1, -3, 3, 'spruce_fence');
lampPost(10, walk1, -3, 3, 'spruce_fence');
lampPost(-10, walk1, 10, 3, 'spruce_fence');
lampPost(10, walk1, 10, 3, 'spruce_fence');
// ===================== 11. INTERIOR - GROUND FLOOR ROOMS =====================
// KITCHEN (west, x ~ -20..-17)
ps(-20, walk1, -1, 'furnace[facing=east,lit=true]');
pb(-20, walk1, 0, 'stripped_spruce_log'); ps(-20, walk1 + 1, 0, 'spruce_stairs[facing=east,half=top]');
ps(-20, walk1, 1, 'water_cauldron[level=3]');
pb(-20, walk1, -2, 'barrel'); pb(-19, walk1, -3, 'smoker[facing=east]');
pb(-17, walk1, 0, 'spruce_fence'); ps(-17, walk1 + 1, 0, 'spruce_pressure_plate');
chairStair(-17, walk1, -1, 'spruce_stairs', 'south');
chairStair(-17, walk1, 1, 'spruce_stairs', 'north');
pb(-18, walk1 + 3, 0, 'spruce_planks'); ps(-18, walk1 + 2, 0, 'lantern[hanging=true]');
rug(-19, walk1, 2, 3, 2, 'yellow', 'brown');
// DINING HALL (east, x ~ 17..20)
for (z = -2; z <= 2; z++) { pb(19, walk1, z, 'dark_oak_fence'); ps(19, walk1 + 1, z, 'dark_oak_pressure_plate'); }
for (z = -2; z <= 2; z++) { chairStair(18, walk1, z, 'spruce_stairs', 'east'); chairStair(20, walk1, z, 'spruce_stairs', 'west'); }
pb(17, walk1, 0, 'barrel'); pb(17, walk1, 2, 'chest');
pb(19, walk1 + 3, 0, 'spruce_planks'); ps(19, walk1 + 2, 0, 'lantern[hanging=true]');
rug(18, walk1, -3, 3, 2, 'red', 'orange');
ps(17, walk1, -2, 'flower_pot');
// WORKSHOP (north-west)
ps(-14, walk1, -13, 'loom[facing=south]'); ps(-15, walk1, -13, 'crafting_table');
ps(-13, walk1, -14, 'smithing_table'); pb(-14, walk1, -15, 'barrel');
pb(-15, walk1 + 3, -14, 'spruce_planks'); ps(-15, walk1 + 2, -14, 'lantern[hanging=true]');
// GRANARY (south-east)
for (x = 13; x <= 16; x++) { pb(x, walk1, 13, 'hay_block'); pb(x, walk1, 14, 'barrel'); }
pb(14, walk1 + 1, 13, 'hay_block'); pb(15, walk1 + 1, 13, 'hay_block');
ps(16, walk1 + 2, 14, 'lantern[hanging=false]');
// ===================== 12. INTERIOR - UPPER BEDROOMS =====================
placeBed(-18, walk2, 2, 'red', 'north');
pb(-20, walk2, 2, 'spruce_fence'); ps(-20, walk2 + 1, 2, 'spruce_pressure_plate'); pb(-20, walk2 + 2, 2, 'lantern');
pb(-18, walk2, 4, 'chest'); rug(-19, walk2, 0, 3, 2, 'blue', 'light_blue');
shelfWall(-20, walk2, -1, 'north', 3, 2);
placeBed(18, walk2, -2, 'blue', 'south');
pb(20, walk2, -2, 'spruce_fence'); ps(20, walk2 + 1, -2, 'spruce_pressure_plate'); pb(20, walk2 + 2, -2, 'lantern');
pb(18, walk2, -4, 'barrel'); rug(18, walk2, 0, 3, 2, 'green', 'lime');
placeBed(-18, walk3, 2, 'brown', 'north');
pb(-20, walk3, 2, 'spruce_fence'); pb(-20, walk3 + 1, 2, 'lantern');
rug(-19, walk3, 0, 3, 2, 'orange', 'yellow');
placeBed(18, walk3, -2, 'white', 'south');
pb(20, walk3, -2, 'spruce_fence'); pb(20, walk3 + 1, -2, 'lantern');
pb(18, walk3, -4, 'chest');
shelfWall(20, walk3, 1, 'south', 3, 2);
// ===================== 13. EXTERIOR: PATH, LIGHTS, VEGETATION =====================
for (z = 24; z <= 33; z++) {
for (x = -2; x <= 2; x++) {
var gy = Math.round(ground(x, z));
pb(x, gy, z, wsel([['dirt_path', 55], ['gravel', 25], ['cobblestone', 20]]));
}
}
lampPost(-4, Math.round(ground(-4, 26)) + 1, 26, 4, 'spruce_fence');
lampPost(4, Math.round(ground(4, 26)) + 1, 26, 4, 'spruce_fence');
lampPost(-4, Math.round(ground(-4, 31)) + 1, 31, 4, 'spruce_fence');
lampPost(4, Math.round(ground(4, 31)) + 1, 31, 4, 'spruce_fence');
for (k = 0; k < 26; k++) {
var a6 = k * 13.8 * Math.PI / 180;
var tx = Math.round(Math.cos(a6) * (26 + (k % 4) * 2.0));
var tz = Math.round(Math.sin(a6) * (26 + (k % 3) * 2.5));
if (tz > 20 && Math.abs(tx) < 6) continue;
var ty = Math.round(ground(tx, tz)) + 1;
if (k % 3 === 0) placeTree(tx, ty, tz, 'oak');
else if (k % 3 === 1) {
pb(tx, ty, tz, wsel([['grass_block', 1]]));
ps(tx, ty, tz, k % 2 === 0 ? 'fern' : 'short_grass');
ps(tx + 1, ty, tz, 'dandelion');
} else {
ps(tx, ty, tz, 'poppy'); ps(tx - 1, ty, tz, 'azure_bluet');
}
}
// terraced vegetable plots + fence outside the gate
for (x = 8; x <= 16; x++) {
for (z = 24; z <= 29; z++) {
var fy = Math.round(ground(x, z));
if ((x + z) % 2 === 0) pb(x, fy, z, 'farmland'); else pb(x, fy, z, 'coarse_dirt');
if ((x + z) % 2 === 0) ps(x, fy + 1, z, 'carrots[age=5]');
}
}
for (x = 7; x <= 17; x++) { var qy = Math.round(ground(x, 30)) + 1; pb(x, qy, 30, 'oak_fence'); }
for (z = 24; z <= 30; z++) { var qy2 = Math.round(ground(17, z)) + 1; pb(17, qy2, z, 'oak_fence'); }
// ===================== 14. FIX PASS (vanos + sellado del faldon) =====================
// (a) un VANO por habitacion desde el patio: la planta baja era inaccesible
for (k = 0; k < 24; k++) {
var afx = (k * 15 + 7.5) * Math.PI / 180;
var rfx;
for (rfx = 13.9; rfx <= 15.9; rfx += 0.35) {
var fx = Math.round(Math.cos(afx) * rfx), fz = Math.round(Math.sin(afx) * rfx);
if (inStairN(fx, fz) || inStairS(fx, fz)) continue;
b(fx, walk1, fz, 'air'); b(fx, walk1 + 1, fz, 'air');
}
var lxx = Math.round(Math.cos(afx) * 15.0), lzz = Math.round(Math.sin(afx) * 15.0);
ps(lxx, walk1 + 2, lzz, 'dark_oak_slab[type=top]');
var pxx = Math.round(Math.cos(afx) * 13.4), pzz = Math.round(Math.sin(afx) * 13.4);
if (k % 4 === 0) { pb(pxx, walk1, pzz, 'stripped_spruce_log'); ps(pxx, walk1 + 1, pzz, 'lantern[hanging=false]'); }
}
// (b) SELLAR el faldon: donde la hilada de fuera sube un nivel, tapar el escalon con teja maciza
for (x = -26; x <= 26; x++) {
for (z = -26; z <= 26; z++) {
d = rad(x, z);
var r0 = roofYFor(d);
if (r0 < 0) continue;
var rOut = roofYFor(d + 1.0);
var rIn = roofYFor(d - 1.0);
if (rOut > r0) pb(x, r0 + 1, z, wsel(TILE_SOLID));
if (rIn > r0 && d < 20.0) pb(x, r0 + 1, z, wsel(TILE_SOLID));
}
}
// (c) dos habitaciones mas amuebladas en planta baja (norte y sur)
ps(3, walk1, -18, 'loom[facing=south]'); pb(2, walk1, -19, 'barrel'); pb(4, walk1, -19, 'chest');
pb(3, walk1 + 3, -18, 'spruce_planks'); ps(3, walk1 + 2, -18, 'lantern[hanging=true]');
rug(2, walk1, -17, 3, 2, 'purple', 'magenta');
pb(-4, walk1, 17, 'cauldron'); pb(-5, walk1, 18, 'barrel'); pb(-3, walk1, 18, 'composter');
chairStair(-4, walk1, 19, 'spruce_stairs', 'north');
pb(-4, walk1 + 3, 18, 'spruce_planks'); ps(-4, walk1 + 2, 18, 'lantern[hanging=true]');