Initial minecraft-builder-mcp prototype and Gothic hall build

This commit is contained in:
Emil
2026-09-12 23:22:20 +03:00
commit 6682668c16
103 changed files with 10064 additions and 0 deletions
+291
View File
@@ -0,0 +1,291 @@
"""Main Gothic hall geometry in local coordinates; pure voxel composition, no IO."""
def build(v):
"""Compose the north-facing hall, hollow interior, roof, and attached masonry."""
stone = "minecraft:stone_bricks"
ashlar = "minecraft:polished_andesite"
plinth = "minecraft:andesite"
pale = "minecraft:polished_diorite"
dark = "minecraft:deepslate_tiles"
glass = "minecraft:tinted_glass"
wood = "minecraft:dark_oak_planks"
oak = "minecraft:spruce_planks"
def slab(material="stone_brick", half="bottom"):
return f"minecraft:{material}_slab[type={half},waterlogged=false]"
def stair(material, facing, half="bottom"):
return (f"minecraft:{material}_stairs[facing={facing},half={half},"
"shape=straight,waterlogged=false]")
def log(axis="y"):
return f"minecraft:dark_oak_log[axis={axis}]"
def roof_height(x):
return 43 - (abs(x - 19) * 16 + 13) // 14
def pinnacle(x, z, base, height=8):
"""Square masonry shaft with a cap, taper, and slender stone finial."""
v.box(x - 1, base, z - 1, x + 1, base, z + 1, ashlar)
v.box(x, base + 1, z, x, base + height - 3, z, stone)
v.box(x - 1, base + height - 4, z - 1,
x + 1, base + height - 4, z + 1, slab())
v.set(x, base + height - 2, z, ashlar)
v.set(x, base + height - 1, z, slab())
def side_window(face, inner, center, bottom, tops, trim_face):
"""Carved pointed aperture with glass one block behind the wall face."""
half = max(tops)
lo, hi = sorted((face, inner))
for dz in range(-half, half + 1):
top = tops[abs(dz)]
v.clear(lo, bottom, center + dz, hi, top, center + dz)
v.box(inner, bottom, center + dz, inner, top, center + dz, glass)
v.set(trim_face, top + 1, center + dz, ashlar)
for dz in (-half - 1, half + 1):
v.box(trim_face, bottom - 1, center + dz,
trim_face, tops[half] + 1, center + dz, ashlar)
v.box(trim_face, bottom - 1, center - half - 1,
trim_face, bottom - 1, center + half + 1, pale)
# Alternating vertical/horizontal pieces describe the arch, rather than
# colouring an arch outline on an otherwise flat facade.
for dz in range(-half, half + 1):
top = tops[abs(dz)]
if dz:
facing = "south" if dz < 0 else "north"
v.set(trim_face, top + 2, center + dz, stair("stone_brick", facing))
v.set(trim_face, tops[0] + 2, center, slab())
def front_aperture(z_face, z_glass, center, bottom, tops):
for dx in range(-max(tops), max(tops) + 1):
top = tops[abs(dx)]
v.clear(center + dx, bottom, z_face, center + dx, top, z_glass)
v.box(center + dx, bottom, z_glass, center + dx, top, z_glass, glass)
# Dense foundations; two genuinely hollow occupied storeys above them.
v.box(7, 0, 13, 31, 0, 56, plinth)
v.box(7, 1, 13, 31, 1, 56, ashlar)
v.box(7, 2, 13, 31, 27, 56, stone)
v.clear(9, 2, 15, 29, 5, 54)
v.clear(9, 7, 15, 29, 43, 54)
v.box(9, 6, 15, 29, 6, 54, wood)
v.box(17, 6, 15, 21, 6, 53, ashlar)
for x in (9, 29):
v.box(x, 6, 15, x, 6, 54, stone)
for z in (15, 54):
v.box(9, 6, z, 29, 6, z, stone)
# Continuous ledges make the raised main floor legible from the courtyard.
for x in (6, 32):
v.box(x, 0, 13, x, 1, 56, ashlar)
v.box(x, 5, 13, x, 5, 56, stone)
v.box(x, 6, 13, x, 6, 56, slab("smooth_stone"))
v.box(x, 25, 12, x, 25, 57, ashlar)
v.box(x, 26, 12, x, 26, 57, pale)
v.box(x, 27, 12, x, 27, 57, slab())
for z in (12, 57):
v.box(6, 5, z, 32, 5, z, stone)
v.box(6, 6, z, 32, 6, z, slab("smooth_stone"))
v.box(6, 25, z, 32, 25, z, ashlar)
v.box(6, 26, z, 32, 26, z, pale)
# Timber is visible in the short shadow immediately below the roof edge.
for x in (7, 31):
v.box(x, 26, 15, x, 26, 54, log("z"))
for z in range(15, 55, 2):
v.set(x, 25, z, stair("spruce", "east" if x == 7 else "west", "top"))
bays = (18, 26, 34, 42, 50)
piers = (14, 22, 30, 38, 46, 54)
for center in bays:
for face, inner, trim in ((7, 8, 6), (31, 30, 32)):
side_window(face, inner, center, 10, {0: 22, 1: 21, 2: 19}, trim)
# The recessed central mullion and transom divide four dark lancets.
v.box(inner, 10, center, inner, 20, center, ashlar)
v.box(inner, 15, center - 2, inner, 15, center + 2, ashlar)
v.set(inner, 21, center, wood)
# A modest three-wide basement window below each high bay.
side_window(face, inner, center, 2, {0: 4, 1: 3}, trim)
v.set(inner, 2, center, wood)
# Deep, bevelled main-window sill; its ends connect to the piers.
outside = trim - 1 if trim < 19 else trim + 1
v.box(outside, 8, center - 3, outside, 8, center + 3, slab())
v.box(trim, 9, center - 3, trim, 9, center + 3, stone)
# Each buttress has an outward foot and two real setbacks.
for z in piers:
for left in (True, False):
if left:
foot, middle, shaft, outside = (3, 7), (4, 7), (5, 7), 5
else:
foot, middle, shaft, outside = (31, 35), (31, 34), (31, 33), 33
v.box(foot[0], 0, z - 1, foot[1], 1, z + 1, plinth)
v.box(middle[0], 2, z - 1, middle[1], 5, z + 1, stone)
v.box(middle[0], 6, z - 1, middle[1], 6, z + 1, slab("smooth_stone"))
v.box(shaft[0], 7, z - 1, shaft[1], 11, z + 1, stone)
v.box(shaft[0], 12, z - 1, shaft[1], 12, z + 1, ashlar)
v.box(min(outside, 7 if left else 31), 13, z,
max(outside, 7 if left else 31), 24, z, stone)
v.set(outside, 19, z, ashlar)
v.box(shaft[0], 25, z - 1, shaft[1], 25, z + 1, ashlar)
v.set(outside, 26, z, slab("smooth_stone"))
pinnacle(outside + (1 if left else -1), z, 27, 7)
# Recessed rear lancets, kept separate from the side gallery connection.
for center in (12, 19, 26):
front_aperture(55, 56, center, 10, {0: 22, 1: 20})
for dx in (-2, 2):
v.box(center + dx, 9, 57, center + dx, 21, 57, ashlar)
v.set(center, 24, 57, ashlar)
v.box(center - 2, 9, 57, center + 2, 9, 57, pale)
# Steep, two-cell-thick stepped roof. Both sides slope to the central ridge.
for x in range(5, 34):
high = roof_height(x)
if x == 19:
v.box(x, 42, 12, x, 43, 57, dark)
else:
v.box(x, high - 1, 12, x, high, 57, dark)
v.box(x, high, 12, x, high, 57,
stair("deepslate_tile", "east" if x < 19 else "west"))
# A restrained ridge crest uses slabs and piers instead of unsupported fences.
v.box(19, 43, 14, 19, 43, 55, slab("stone_brick"))
for z in range(15, 56, 4):
v.set(19, 43, z, ashlar)
# Front and rear stepped gables: substantial masonry underneath the coping.
for z in (12, 57):
for x in range(6, 33):
high = roof_height(x)
v.box(x, 27, z, x, high, z, stone)
v.set(x, high, z, ashlar)
if x != 19:
v.set(x, high + 1, z, stair("stone_brick", "east" if x < 19 else "west"))
v.box(19, 44, z, 19, 46, z, ashlar)
v.box(18, 47, z, 20, 47, z, stone)
v.set(19, 48, z, slab())
# Tall north facade, projecting three-layer porch, and its open pointed door.
v.box(13, 6, 9, 25, 6, 14, ashlar)
v.box(14, 6, 8, 24, 6, 8, ashlar)
portal_top = {0: 19, 1: 18, 2: 17, 3: 15, 4: 13}
for dx, top in portal_top.items():
for sign in (-1, 1) if dx else (1,):
x = 19 + sign * dx
v.clear(x, 7, 9, x, top, 15)
for z, material, expand in ((9, ashlar, 1), (10, stone, 0), (11, pale, 0)):
for dx in (-5, 5):
v.box(19 + dx, 7, z, 19 + dx, 14, z, material)
for dx, top in portal_top.items():
for sign in (-1, 1) if dx else (1,):
x = 19 + sign * dx
v.box(x, top + 1, z, x, top + 1 + expand, z, material)
for x in (13, 25):
v.box(x, 7, 10, x, 14, 12, stone)
v.box(x, 15, 10, x, 15, 12, slab())
# Open wooden leaves are flush with the inner jambs, not across the entrance.
v.box(15, 7, 14, 15, 12, 15, log())
v.box(23, 7, 14, 23, 12, 15, log())
v.clear(16, 7, 9, 22, 12, 15)
# Two slim flanking lancets and a large upper traceried front window.
for center in (10, 28):
front_aperture(12, 14, center, 10, {0: 23, 1: 21})
for x in (center - 2, center + 2):
v.box(x, 9, 11, x, 22, 11, ashlar)
v.set(center, 25, 11, slab())
v.box(center - 2, 9, 11, center + 2, 9, 11, pale)
front_aperture(12, 14, 19, 29, {0: 39, 1: 38, 2: 36, 3: 33})
for dx, top in {0: 39, 1: 38, 2: 36, 3: 33}.items():
for sign in (-1, 1) if dx else (1,):
v.set(19 + dx * sign, top + 1, 11, ashlar)
for x in (15, 23):
v.box(x, 28, 11, x, 34, 11, ashlar)
for x in (18, 20):
v.box(x, 29, 14, x, 36, 14, ashlar)
v.box(16, 33, 14, 22, 33, 14, ashlar)
v.box(15, 28, 11, 23, 28, 11, pale)
# Small heraldic mosaic echoes the reference's central hanging cross.
v.box(17, 22, 11, 21, 27, 11, dark)
v.box(19, 23, 10, 19, 26, 10, pale)
v.box(18, 25, 10, 20, 25, 10, pale)
v.set(19, 21, 11, dark)
for x in (7, 31):
v.box(x - 1, 2, 11, x + 1, 9, 13, stone)
v.box(x, 10, 11, x, 28, 12, ashlar)
for y in (6, 13, 25):
v.box(x - 1, y, 10, x + 1, y, 13, slab("smooth_stone"))
pinnacle(x, 12, 29, 9)
# Five small pointed dormers on each long roof slope.
for z in bays:
for left in (True, False):
lo, hi, face, inner = (10, 14, 10, 11) if left else (24, 28, 28, 27)
v.clear(lo, 33, z - 1, hi, 36, z + 1)
for dz in (-2, 2):
v.box(lo, 32, z + dz, hi, 37, z + dz, dark)
v.box(face, 32, z - 1, face, 37, z + 1, ashlar)
v.clear(min(face, inner), 34, z, max(face, inner), 36, z)
v.box(inner, 34, z, inner, 36, z, glass)
for dz in range(-2, 3):
top = 39 - abs(dz)
v.box(lo, top, z + dz, hi, top, z + dz, dark)
v.set(face, top, z + dz, ashlar)
v.set(face, 40, z, slab())
# Galleries retain a broad, open central nave and three-block walkways.
for x1, x2 in ((9, 12), (26, 29)):
v.box(x1, 16, 16, x2, 16, 52, wood)
rail = x2 if x1 < 19 else x1
v.box(rail, 17, 16, rail, 17, 52, slab("stone_brick"))
for z in range(16, 53, 4):
v.set(rail, 17, z, ashlar)
v.set(rail, 18, z, slab())
for z in piers[1:]:
if z > 52:
continue
v.box(rail, 7, z, rail, 15, z, stone)
v.box(rail - 1, 7, z - 1, rail + 1, 7, z + 1, ashlar)
v.box(rail - 1, 15, z, rail + 1, 15, z, ashlar)
# A rear bridge connects both galleries to the single staircase.
v.box(9, 16, 51, 29, 16, 53, wood)
v.box(12, 17, 50, 25, 17, 50, slab())
for x in (12, 19, 25):
v.set(x, 17, 50, ashlar)
v.set(x, 18, 50, slab())
for x in (12, 26):
v.box(x, 7, 52, x, 15, 52, stone)
# Transverse oak roof frames are visible from the nave between the dormers.
for z in (22, 30, 38, 46, 54):
v.box(9, 28, z, 29, 28, z, log("x"))
for x in range(9, 30):
y = 40 - abs(x - 19)
v.set(x, y, z, wood)
for x in (9, 29):
v.box(x, 19, z, x, 28, z, log())
# Rear right gallery stair: ten rises, with its complete headroom shaft.
v.clear(26, 7, 39, 28, 19, 49)
for step in range(10):
y, z = 7 + step, 40 + step
if y > 7:
v.box(26, 7, z, 28, y - 1, z, wood)
v.box(26, y, z, 28, y, z, stair("spruce", "south"))
# Separate basement access under the west gallery; the main floor is pierced.
v.clear(10, 2, 43, 11, 9, 48)
for step in range(5):
y, z = 2 + step, 44 + step
if y > 2:
v.box(10, 2, z, 11, y - 1, z, stone)
v.box(10, y, z, 11, y, z, stair("stone_brick", "south"))
# Modest furnishings preserve a five-block processional route to the dais.
v.box(14, 7, 50, 24, 7, 53, wood)
v.box(16, 7, 49, 22, 7, 49, stair("spruce", "south"))
for z in (24, 30, 36, 42):
for x1, x2 in ((14, 16), (22, 24)):
v.box(x1, 7, z, x2, 7, z, stair("spruce", "north"))
v.set(x1, 8, z, slab("spruce"))
v.set(x2, 8, z, slab("spruce"))
# Agreed lower-level passage to the east gallery/wing, cut after decoration.
v.clear(30, 7, 36, 35, 11, 39)
+157
View File
@@ -0,0 +1,157 @@
"""Deterministic voxel blueprint for the generated Gothic hall reference."""
from collections import Counter, defaultdict
import importlib
import re
ORIGIN = (-50, -60, -40)
class Voxels:
def __init__(self):
self.blocks = {}
self.stage = 'site'
self.owners = {}
def set(self, x, y, z, block):
assert all(isinstance(v, int) for v in (x, y, z)), (x, y, z)
assert -2 <= x <= 68 and -1 <= y <= 64 and -2 <= z <= 68, (x, y, z)
if not block.startswith('minecraft:'):
block = 'minecraft:' + block
assert re.fullmatch(r'minecraft:[a-z0-9_]+(?:\[[a-z0-9_=,]+\])?', block), block
if block == 'minecraft:air':
self.blocks.pop((x, y, z), None)
self.owners.pop((x, y, z), None)
else:
self.blocks[(x, y, z)] = block
self.owners[(x, y, z)] = self.stage
def box(self, x1, y1, z1, x2, y2, z2, block):
x1, x2 = sorted((x1, x2))
y1, y2 = sorted((y1, y2))
z1, z2 = sorted((z1, z2))
for y in range(y1, y2 + 1):
for z in range(z1, z2 + 1):
for x in range(x1, x2 + 1):
self.set(x, y, z, block)
def clear(self, x1, y1, z1, x2, y2, z2):
self.box(x1, y1, z1, x2, y2, z2, 'air')
def site(v):
v.box(1, 0, 1, 63, 0, 62, 'smooth_stone')
for z in (1, 62):
v.box(1, 0, z, 63, 0, z, 'polished_andesite')
for x in (1, 63):
v.box(x, 0, 1, x, 0, 62, 'polished_andesite')
# A few continuous paving bands give the terrace scale without noisy texture.
for x in (5, 33, 60):
v.box(x, 0, 2, x, 0, 61, 'stone_bricks')
for z in (4, 59):
v.box(2, 0, z, 62, 0, z, 'stone_bricks')
for step in range(1, 7):
z = step + 1
v.box(13, 1, z, 25, step, z, 'stone_bricks')
v.box(14, step, z, 24, step, z,
'stone_brick_stairs[facing=south,half=bottom,shape=straight,waterlogged=false]')
for x in (12, 26):
v.box(x, 1, z, x, step + 1, z, 'polished_andesite')
v.set(x, step + 2, z, 'stone_brick_slab[type=bottom,waterlogged=false]')
v.box(13, 1, 8, 25, 6, 9, 'stone_bricks')
for x in (8, 30, 35, 61):
for z in (6, 59):
v.box(x, 1, z, x + 1, 1, z + 1, 'polished_andesite')
v.box(x, 2, z, x + 1, 3, z + 1, 'stone_bricks')
v.box(x, 4, z, x + 1, 4, z + 1,
'stone_brick_slab[type=bottom,waterlogged=false]')
def connection(v):
# Two-level enclosed passage between the great hall and companion wing.
v.stage = 'connecting gallery'
v.box(31, 0, 34, 40, 6, 42, 'stone_bricks')
v.box(31, 7, 34, 40, 13, 34, 'stone_bricks')
v.box(31, 7, 42, 40, 13, 42, 'stone_bricks')
v.box(31, 13, 33, 40, 13, 43, 'polished_andesite')
v.box(31, 14, 34, 40, 14, 42, 'deepslate_tiles')
v.box(31, 15, 36, 40, 15, 40, 'deepslate_tiles')
v.box(31, 16, 38, 40, 16, 38, 'deepslate_tiles')
for z, facing, y in [(34, 'south', 14), (35, 'south', 14),
(36, 'south', 15), (37, 'south', 15),
(39, 'north', 15), (40, 'north', 15),
(41, 'north', 14), (42, 'north', 14)]:
v.box(31, y, z, 40, y, z,
f'deepslate_tile_stairs[facing={facing},half=bottom,shape=straight,waterlogged=false]')
v.clear(30, 7, 36, 32, 11, 39)
v.clear(33, 8, 36, 34, 11, 39)
v.clear(35, 9, 36, 40, 12, 39)
v.box(31, 6, 36, 32, 6, 39, 'spruce_planks')
for x, y in [(33, 6), (34, 7)]:
v.box(x, y, 36, x, y, 39,
'spruce_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]')
v.box(35, 8, 36, 40, 8, 39, 'spruce_planks')
def build_scene():
v = Voxels()
site(v)
for name, stage in [('main_hall', 'great hall'), ('side_wing', 'side wing'), ('tower', 'bell tower')]:
v.stage = stage
importlib.import_module('builds.gothic_hall.' + name).build(v)
connection(v)
return v
def boxes(blocks):
"""Merge runs on X, then Z and Y, preserving the exact sparse block mask."""
rows = defaultdict(list)
for (x, y, z), block in blocks.items():
rows[(y, z, block)].append(x)
runs = []
for (y, z, block), xs in sorted(rows.items()):
xs.sort(); start = end = xs[0]
for x in xs[1:]:
if x == end + 1:
end = x
else:
runs.append((start, y, z, end, y, z, block)); start = end = x
runs.append((start, y, z, end, y, z, block))
merged = []
groups = defaultdict(list)
for x1, y1, z1, x2, y2, z2, block in runs:
groups[(x1, x2, y1, block)].append(z1)
for (x1, x2, y, block), zs in sorted(groups.items()):
zs.sort(); start = end = zs[0]
for z in zs[1:]:
if z == end + 1: end = z
else:
merged.append((x1, y, start, x2, y, end, block)); start = end = z
merged.append((x1, y, start, x2, y, end, block))
result = []; groups = defaultdict(list)
for x1, y1, z1, x2, y2, z2, block in merged:
groups[(x1, x2, z1, z2, block)].append(y1)
for (x1, x2, z1, z2, block), ys in sorted(groups.items()):
ys.sort(); start = end = ys[0]
for y in ys[1:]:
if y == end + 1: end = y
else:
result.append((x1, start, z1, x2, end, z2, block)); start = end = y
result.append((x1, start, z1, x2, end, z2, block))
return result
def manifest(v):
tiles = defaultdict(dict)
for p, block in v.blocks.items():
tiles[(p[1] // 8, p[2] // 16, p[0] // 16)][p] = block
batches = []
for key, data in sorted(tiles.items()):
recipe = []
for x1, y1, z1, x2, y2, z2, block in boxes(data):
lo = dict(zip(('x', 'y', 'z'), (x1 + ORIGIN[0], y1 + ORIGIN[1], z1 + ORIGIN[2])))
hi = dict(zip(('x', 'y', 'z'), (x2 + ORIGIN[0], y2 + ORIGIN[1], z2 + ORIGIN[2])))
recipe.append({'type': 'box', 'min': lo, 'max': hi, 'block': block})
positions = list(data)
minimum = [min(p[i] for p in positions) + ORIGIN[i] for i in range(3)]
maximum = [max(p[i] for p in positions) + ORIGIN[i] for i in range(3)]
batches.append({'key': '-'.join(map(str, key)), 'blocks': len(data),
'min': dict(zip(('x', 'y', 'z'), minimum)), 'max': dict(zip(('x', 'y', 'z'), maximum)),
'recipe': {'version': 1, 'operations': recipe},
'stages': dict(Counter(v.owners[p] for p in data))})
return {'name': 'Gothic hall reference reconstruction', 'origin': ORIGIN,
'blocks': len(v.blocks), 'stages': dict(Counter(v.owners.values())),
'palette': dict(Counter(v.blocks.values())), 'batches': batches}
+176
View File
@@ -0,0 +1,176 @@
"""The smaller, two-storey gabled companion to the main Gothic hall.
Coordinates are inclusive and local to the complete composition. ``build``
only writes through the supplied voxel API; it does not contact Minecraft.
"""
STONE = "minecraft:stone_bricks"
TRIM = "minecraft:polished_andesite"
LIGHT = "minecraft:polished_diorite"
GLASS = "minecraft:tinted_glass"
WOOD = "minecraft:spruce_planks"
BEAM = "minecraft:stripped_spruce_log[axis=y]"
ROOF = "minecraft:deepslate_tiles"
AIR = "minecraft:air"
def _stair(material, facing, half="bottom"):
return (
f"minecraft:{material}[facing={facing},half={half},"
"shape=straight,waterlogged=false]"
)
def _wall_block(x, y, z):
"""Sparse, deterministic masonry variation, without a checkerboard."""
n = (x * 31 + y * 17 + z * 47 + x * z * 3) % 53
if n < 3:
return "minecraft:cracked_stone_bricks"
if n < 8:
return "minecraft:andesite"
return STONE
def _lancet(v, left, right, bottom, top, wall, outside, north=False):
"""A stone pointed surround, open reveal, and recessed dark glass."""
center = (left + right) / 2
def point(u, y, depth, block):
if north:
v.set(u, y, depth, block)
else:
v.set(depth, y, u, block)
for u in range(left - 1, right + 2):
cap = top + 1 - int(abs(u - center))
for y in range(bottom - 1, cap + 1):
point(u, y, outside, LIGHT if y == cap else TRIM)
for u in range(left, right + 1):
cap = top - int(abs(u - center))
for y in range(bottom, cap + 1):
point(u, y, outside, AIR)
point(u, y, wall, GLASS)
# The sill projects one block; the glass stays in the wall behind it.
for u in range(left - 1, right + 2):
point(u, bottom - 1, outside, LIGHT)
def _buttress(v, west, z):
"""Three diminishing stages, with projecting pale weathering caps."""
outer, inner = (36, 38) if west else (57, 59)
v.box(outer, 0, z - 1, inner, 1, z + 1, TRIM)
a, b = (37, 38) if west else (57, 58)
v.box(a, 2, z - 1, b, 6, z + 1, STONE)
v.box(outer, 7, z - 1, inner, 7, z + 1, LIGHT)
v.box(a, 8, z, b, 14, z, STONE)
v.box(a, 15, z - 1, b, 15, z + 1, TRIM)
v.box(a, 16, z, b, 18, z, STONE)
v.box(a, 19, z, b, 19, z, LIGHT)
def _entrance(v):
# Two nested pointed archivolts give the entrance visible depth.
for z, left, right, peak, block in (
(14, 44, 51, 8, TRIM),
(15, 45, 50, 7, LIGHT),
):
for x in range(left, right + 1):
cap = peak - int(abs(x - 47.5))
v.box(x, 1, z, x, cap, z, block)
for x in range(46, 50):
cap = 6 - int(abs(x - 47.5))
v.clear(x, 1, 14, x, cap, 17)
# Timber inner jambs suggest the tall wooden doorway, leaving it usable.
v.box(45, 1, 16, 45, 4, 16, BEAM)
v.box(50, 1, 16, 50, 4, 16, BEAM)
v.box(44, 0, 13, 51, 0, 16, TRIM)
v.box(46, 0, 14, 49, 0, 17, "minecraft:smooth_stone")
def build(v):
"""Build within x=36..59, y=0..28, z=13..43."""
# Hollow masonry shell, an accessible ground floor and an upper floor.
v.box(38, 0, 16, 57, 0, 41, TRIM)
v.clear(39, 1, 17, 56, 27, 40)
for y in range(1, 18):
for x in range(38, 58):
for z in (16, 41):
v.set(x, y, z, _wall_block(x, y, z))
for z in range(17, 41):
for x in (38, 57):
v.set(x, y, z, _wall_block(x, y, z))
v.box(39, 0, 17, 56, 0, 40, WOOD)
v.box(39, 8, 17, 56, 8, 40, WOOD)
# Floor bands and the projecting eaves visually join the two storeys.
for y, block in ((0, TRIM), (7, TRIM), (8, LIGHT), (16, TRIM)):
v.box(37, y, 15, 58, y, 15, block)
v.box(37, y, 42, 58, y, 42, block)
v.box(58, y, 16, 58, y, 41, block)
for z in range(16, 42):
# The main composition will connect the gallery through here.
if not (7 <= y <= 11 and 36 <= z <= 39):
v.set(37, y, z, block)
for z in (17, 25, 33, 41):
_buttress(v, True, z)
_buttress(v, False, z)
for x in (38, 57):
v.box(x - 1, 0, 14, x + 1, 1, 16, TRIM)
v.box(x, 2, 14, x, 16, 16, STONE)
v.box(x - 1, 17, 14, x + 1, 17, 16, LIGHT)
v.box(x, 18, 15, x, 20, 15, STONE)
v.set(x, 21, 15, LIGHT)
# A paired rhythm of small lower and tall upper side windows.
for z in (21, 29, 37):
for wall, outside in ((38, 37), (57, 58)):
if wall == 38 and z == 37:
continue # Keep the gallery doorway and its approach clear.
_lancet(v, z - 1, z + 1, 2, 6, wall, outside)
_lancet(v, z - 1, z + 1, 10, 15, wall, outside)
for left, right in ((41, 42), (53, 54)):
_lancet(v, left, right, 2, 6, 16, 15, north=True)
for left, right in ((42, 45), (50, 53)):
_lancet(v, left, right, 10, 15, 16, 15, north=True)
_entrance(v)
# Timber supports remain against the edges, leaving the rooms traversable.
for x in (40, 55):
for z in (18, 39):
v.box(x, 1, z, x, 7, z, BEAM)
for z in (23, 31, 39):
v.box(39, 7, z, 56, 7, z, "minecraft:stripped_spruce_log[axis=x]")
v.clear(53, 8, 28, 55, 11, 35)
for step in range(8):
y, z = step + 1, 28 + step
if y > 1:
v.box(53, 1, z, 55, y - 1, z, WOOD)
v.box(53, y, z, 55, y, z, _stair("spruce_stairs", "south"))
v.clear(53, y + 1, z, 55, y + 2, z)
v.box(52, 9, 28, 52, 9, 35, "minecraft:spruce_slab[type=bottom,waterlogged=false]")
# A steep roof, dark in the middle and bounded by pale stepped gables.
for x in range(37, 59):
rise = min(x - 37, 58 - x)
y = 17 + rise
facing = "east" if x <= 47 else "west"
if 38 <= x <= 57 and y >= 18:
for z in (16, 41):
v.box(x, 18, z, x, y, z, STONE)
v.box(x, y, 15, x, y, 42, _stair("deepslate_tile_stairs", facing))
for z in (14, 43):
v.set(x, y, z, _stair("stone_brick_stairs", facing))
if y > 17:
v.set(x, y - 1, z, TRIM)
v.box(47, 28, 15, 48, 28, 42, ROOF)
for z in (14, 43):
v.box(47, 27, z, 48, 28, z, LIGHT)
_lancet(v, 46, 49, 19, 24, 16, 15, north=True)
for x in (47, 48):
v.box(x, 26, 15, x, 28, 15, TRIM)
# Wooden cornice brackets are visible under the long roof slopes.
for z in (19, 23, 27, 31, 35, 39):
for x, facing in ((37, "east"), (58, "west")):
v.set(x, 16, z, _stair("spruce_stairs", facing, half="top"))
+190
View File
@@ -0,0 +1,190 @@
"""Reference bell tower, in the shared scene's inclusive local coordinates.
The scene supplies set(x, y, z, block), box(x1, y1, z1, x2, y2, z2,
block), and clear(x1, y1, z1, x2, y2, z2). This module has no I/O.
All geometry stays inside x=34..46, y=0..60, z=42..56.
"""
STONE = "minecraft:stone_bricks"
TRIM = "minecraft:polished_andesite"
LIGHT = "minecraft:polished_diorite"
DARK = "minecraft:deepslate_tiles"
GLASS = "minecraft:tinted_glass"
AIR = "minecraft:air"
def _face(face, u, y, depth=0):
"""Positive depth projects outward; -1 is the recessed glazing plane."""
if face == "north":
return u, y, 43 - depth
if face == "south":
return u, y, 55 + depth
if face == "west":
return 35 - depth, y, u
return 45 + depth, y, u
def _put(v, face, u, y, block, depth=0):
v.set(*_face(face, u, y, depth), block)
def _ring(v, x1, z1, x2, z2, y, block):
v.box(x1, y, z1, x2, y, z1, block)
v.box(x1, y, z2, x2, y, z2, block)
v.box(x1, y, z1, x1, y, z2, block)
v.box(x2, y, z1, x2, y, z2, block)
def _lancet(v, face, center, bottom, top, half_width):
"""A pointed opening through a two-block wall, glass one block behind it."""
for y in range(bottom, top + 1):
radius = 0 if y >= top - 1 else half_width
for u in range(center - radius, center + radius + 1):
_put(v, face, u, y, AIR)
_put(v, face, u, y, GLASS, -1)
# The projecting dressings are stone; the glazing never projects.
for u in {center - radius - 1, center + radius + 1}:
_put(v, face, u, y, TRIM, 1)
for u in range(center - half_width - 1, center + half_width + 2):
_put(v, face, u, bottom - 1, "minecraft:stone_brick_slab[type=top]", 1)
_put(v, face, center, top + 1, LIGHT, 1)
def _belfry_arch(v, face, center):
# Seven blocks wide below, narrowing to a high, single-block apex.
for y in range(32, 41):
radius = 3 if y <= 37 else 40 - y
for u in range(center - radius, center + radius + 1):
for depth in (0, -1):
_put(v, face, u, y, AIR, depth)
for u in (center - radius - 1, center + radius + 1):
_put(v, face, u, y, TRIM, 1)
_put(v, face, center, 41, LIGHT, 1)
for u in range(center - 4, center + 5):
_put(v, face, u, 31, "minecraft:stone_brick_slab[type=top]", 1)
def _stairs(v):
# Three narrow alternating flights join the interior floors. A landing at
# each end leaves a three-block-wide central well open through the tower.
for y_floor, xs, reverse in ((1, (37, 38), False),
(10, (42, 43), True),
(19, (37, 38), False)):
facing = "north" if reverse else "south"
state = f"minecraft:stone_brick_stairs[facing={facing},half=bottom,shape=straight]"
for i in range(9):
z = 53 - i if reverse else 45 + i
y = y_floor + i + 1
for x in xs:
# Clearance through the destination floor, including headroom.
v.clear(x, y + 1, z, x, y + 3, z)
v.set(x, y, z, state)
# The flight exits sideways onto the destination floor.
v.clear(39, 29, 53, 41, 32, 53)
v.set(41, 29, 52, "minecraft:stone_brick_stairs[facing=north]")
v.set(41, 30, 51, "minecraft:stone_brick_stairs[facing=north]")
v.clear(41, 30, 52, 41, 33, 52)
v.clear(41, 31, 51, 41, 33, 51)
def _spire(v):
# A hollow, steep rectangular hip roof; alternate contractions avoid a
# stack of disconnected floating rings while giving the spire a fine tip.
for y in range(45, 57):
step = y - 45
rx = 5 - step // 2
rz = 6 - (step + 1) // 2
if rx < 0:
rx = 0
if rz < 0:
rz = 0
x1, x2, z1, z2 = 40 - rx, 40 + rx, 49 - rz, 49 + rz
v.box(x1, y, z1, x2, y, z2, DARK)
if rx > 0 and rz > 0:
v.clear(x1 + 1, y, z1 + 1, x2 - 1, y, z2 - 1)
# Stair edges soften the one-block roof steps without adding width.
if rx > 0 and rz > 0:
for x in range(x1 + 1, x2):
v.set(x, y, z1, "minecraft:deepslate_tile_stairs[facing=south]")
v.set(x, y, z2, "minecraft:deepslate_tile_stairs[facing=north]")
for z in range(z1 + 1, z2):
v.set(x1, y, z, "minecraft:deepslate_tile_stairs[facing=east]")
v.set(x2, y, z, "minecraft:deepslate_tile_stairs[facing=west]")
# The cross is plain masonry, with no unsupported entities or thin blocks.
v.box(40, 57, 49, 40, 60, 49, TRIM)
v.box(39, 59, 49, 41, 59, 49, TRIM)
def build(v):
"""Add a hollow Gothic bell tower and its pointed roof to a scene."""
# Foundation and shell: two-block walls give the apertures real reveals.
v.box(35, 0, 43, 45, 43, 55, STONE)
v.clear(37, 2, 45, 43, 42, 53)
_ring(v, 34, 42, 46, 56, 0, "minecraft:deepslate_bricks")
_ring(v, 34, 42, 46, 56, 1, TRIM)
_ring(v, 34, 42, 46, 56, 3, "minecraft:stone_brick_slab[type=bottom]")
# Dressed corners and stepped buttress feet, all within the tower envelope.
corners = ((34, 42, 35, 43), (45, 42, 46, 43),
(34, 55, 35, 56), (45, 55, 46, 56))
for x1, z1, x2, z2 in corners:
v.box(x1, 1, z1, x2, 5, z2, TRIM)
v.box(x1, 6, z1, x2, 29, z2, STONE)
for y in (6, 15, 28):
v.box(x1, y, z1, x2, y, z2, TRIM)
for x, z in ((35, 43), (45, 43), (35, 55), (45, 55)):
v.box(x, 4, z, x, 43, z, TRIM)
# Strong horizontal courses separate the shaft from the open belfry.
for y in (15, 16, 29, 30, 42, 43):
_ring(v, 34, 42, 46, 56, y, TRIM if y % 2 else STONE)
for y in (10, 19, 28, 30):
v.box(37, y, 45, 43, y, 53, "minecraft:oak_planks")
if y != 30:
v.clear(39, y, 47, 41, y, 51)
v.box(35, 44, 43, 45, 44, 55, STONE)
_ring(v, 34, 42, 46, 56, 44, "minecraft:stone_brick_slab[type=bottom]")
# Paired narrow lower lights and broader, pointed upper lancets.
for face, centers in (("north", (38, 42)), ("south", (38, 42)),
("west", (47, 51)), ("east", (47, 51))):
for center in centers:
_lancet(v, face, center, 6, 12, 0)
_lancet(v, face, center, 19, 26, 1)
_belfry_arch(v, face, 40 if face in ("north", "south") else 49)
# An actual walk-in doorway toward the wing; no decorative solid door.
v.clear(39, 2, 42, 41, 5, 44)
v.clear(40, 6, 42, 40, 6, 44)
for x in (38, 42):
v.box(x, 2, 42, x, 5, 42, TRIM)
v.set(39, 6, 42, TRIM)
v.set(41, 6, 42, TRIM)
v.set(40, 7, 42, LIGHT)
_stairs(v)
# A block-built ochre bell echoes the reference using allowlisted sandstone.
# Its open mouth and central clapper remain visible through all four arches.
v.box(35, 41, 49, 45, 41, 49, "minecraft:spruce_log[axis=x]")
v.box(40, 38, 49, 40, 40, 49, "minecraft:dark_oak_log[axis=y]")
v.box(40, 36, 49, 40, 37, 49, "minecraft:cut_sandstone")
for x, z in ((39, 49), (40, 48), (40, 49), (40, 50), (41, 49)):
v.set(x, 35, z, "minecraft:cut_sandstone")
_ring(v, 39, 48, 41, 50, 34, "minecraft:cut_sandstone")
v.set(40, 34, 49, "minecraft:dark_oak_log[axis=y]")
v.set(40, 33, 49, TRIM)
# A row of small corbels under the cornice is readable from a distance.
for face, us in (("north", (37, 39, 41, 43)), ("south", (37, 39, 41, 43)),
("west", (45, 47, 49, 51, 53)), ("east", (45, 47, 49, 51, 53))):
for u in us:
_put(v, face, u, 41, f"minecraft:stone_brick_stairs[facing={face},half=top]", 1)
_spire(v)
# Four light corner pinnacles frame the darker central spire.
for x, z in ((35, 43), (45, 43), (35, 55), (45, 55)):
v.box(x, 44, z, x, 49, z, TRIM)
for dx, dz in ((-1, 0), (1, 0), (0, -1), (0, 1)):
v.set(x + dx, 46, z + dz, "minecraft:stone_brick_slab[type=top]")
v.set(x, 50, z, "minecraft:stone_brick_slab[type=bottom]")