Separate map projects and add reusable island and operator camera tools

This commit is contained in:
Emil
2026-09-13 20:43:42 +03:00
parent 89c53d7058
commit 224b29f0e7
158 changed files with 633 additions and 34966 deletions
-291
View File
@@ -1,291 +0,0 @@
"""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)
@@ -1,120 +0,0 @@
"""Add the architecture polish layer to the finished v1 scene, without I/O.
The input is scene.Voxels after the eighteen finishing corrections. V1 modules
remain immutable; removed blocks disappear from this sparse voxel composition.
"""
def build(v):
"""Restore the roof silhouette and add restrained Gothic stone/iron detail."""
stone = "minecraft:stone_bricks"
trim = "minecraft:polished_andesite"
dark = "minecraft:deepslate_tiles"
glass = "minecraft:gray_stained_glass"
wall = ("minecraft:stone_brick_wall[east=none,north=none,south=none,"
"up=true,waterlogged=false,west=none]")
def stair(material, facing, half="bottom"):
return (f"minecraft:{material}_stairs[facing={facing},half={half},"
"shape=straight,waterlogged=false]")
def bars(north=False, south=False, east=False, west=False):
flags = {"east": east, "north": north, "south": south, "west": west}
return "minecraft:iron_bars[" + ",".join(
f"{name}={str(value).lower()}" for name, value in flags.items()
) + ",waterlogged=false]"
def roof_height(x):
return 43 - (abs(x - 19) * 16 + 13) // 14
# Use the actual input mask so a material replacement cannot fill an opening.
# Preserve the six pale blocks of the heraldic cross on the north facade.
crest = {(19, y, 10) for y in range(23, 27)} | {
(x, 25, 10) for x in range(18, 21)
}
timber = {
p: block for p, block in v.blocks.items()
if 9 <= p[0] <= 29 and 28 <= p[1] <= 40 and 15 <= p[2] <= 54
and (block.startswith("minecraft:dark_oak_log[")
or block == "minecraft:dark_oak_planks")
}
for p, block in tuple(v.blocks.items()):
if block == "minecraft:polished_diorite" and p not in crest:
v.set(*p, trim)
elif block == "minecraft:tinted_glass":
v.set(*p, glass)
elif (block == "minecraft:cut_sandstone"
and 39 <= p[0] <= 41 and 34 <= p[1] <= 37 and 48 <= p[2] <= 50):
v.set(*p, "minecraft:gold_block")
# Remove only the ten old dormer envelopes, then reconstruct the same
# two-cell roof section. These envelopes exclude both gables and all trusses.
for z in (18, 26, 34, 42, 50):
for x1, x2 in ((10, 14), (24, 28)):
v.clear(x1, 32, z - 2, x2, 40, z + 2)
for x in range(x1, x2 + 1):
high = roof_height(x)
for y in (high - 1, high):
if 32 <= y <= 40:
block = dark if y < high else stair(
"deepslate_tile", "east" if x < 19 else "west")
v.box(x, y, z - 2, x, y, z + 2, block)
# Three narrow, low dormers per slope: a one-by-two recessed light, three
# blocks of facade width, and a short roof merging into the original slope.
for z in (22, 34, 46):
for face, inner, back in ((10, 11, 13), (28, 27, 25)):
x1, x2 = sorted((face, back))
near = back - 1 if face < back else back + 1
v.clear(min(face, near), 33, z, max(face, near), 34, z)
for dz in (-1, 1):
v.box(x1, 32, z + dz, x2, 34, z + dz, dark)
facing = "south" if dz < 0 else "north"
v.box(x1, 35, z + dz, x2, 35, z + dz,
stair("deepslate_tile", facing))
v.box(face, 33, z + dz, face, 34, z + dz, trim)
v.set(face, 35, z + dz, stair("stone_brick", facing))
v.box(x1, 36, z, x2, 36, z, dark)
v.box(face, 32, z - 1, face, 32, z + 1, trim)
v.box(inner, 33, z, inner, 34, z, glass)
v.set(face, 35, z, stone)
v.set(face, 36, z, wall)
# The old bright, chunky ridge is now a dark backing for one thin iron rail.
v.box(19, 43, 14, 19, 43, 55, dark)
for z in range(14, 56):
v.set(19, 44, z, bars(north=z > 14, south=z < 55))
# Replace full-block mullions with slender rods in their original glazing
# plane. Connected horizontal bars provide a clear transom without a grille.
for center in (18, 26, 34, 42, 50):
for inner, outside in ((8, 6), (30, 32)):
for y in range(10, 21):
v.set(inner, y, center, bars())
for dz in range(-2, 3):
v.set(inner, 15, center + dz,
bars(north=dz > -2, south=dz < 2))
# A bevel on the underside of each stepped arch reduces square caps.
for dz, top in ((-2, 19), (-1, 21), (1, 21), (2, 19)):
v.set(outside, top + 1, center + dz,
stair("stone_brick", "north" if dz < 0 else "south", "top"))
for x in (18, 20):
for y in range(29, 37):
v.set(x, y, 14, bars())
for x in range(16, 23):
v.set(x, 33, 14, bars(east=x < 22, west=x > 16))
# Taper the tips, retaining the full support blocks installed by finish v1.
for x in (6, 32):
for z in (14, 22, 30, 38, 46, 54):
v.box(x, 32, z, x, 33, z, wall)
for x in (7, 31):
v.box(x, 36, 12, x, 37, 12, wall)
for x in (35, 45):
for z in (43, 55):
v.box(x, 49, z, x, 50, z, wall)
# Two new dormer centers cross truss planes. Keep every original timber
# voxel, including the inclined members, instead of cutting a frame for glass.
for p, block in timber.items():
v.set(*p, block)
-159
View File
@@ -1,159 +0,0 @@
"""Reference-driven finishing layer; the original scene and its ledgers stay immutable."""
from .scene import build_scene as original_scene
from .polish_architecture import build as architecture
STONE = 'minecraft:stone_bricks'
TRIM = 'minecraft:polished_andesite'
LEAVES = 'minecraft:oak_leaves[distance=7,persistent=true,waterlogged=false]'
CHAIN = 'minecraft:iron_chain[axis=y,waterlogged=false]'
SLAB = 'minecraft:stone_brick_slab[type=bottom,waterlogged=false]'
def stair(facing, half='top'):
return f'minecraft:stone_brick_stairs[facing={facing},half={half},shape=straight,waterlogged=false]'
def bars(north=False, east=False, south=False, west=False):
return ('minecraft:iron_bars[' + ','.join(f'{key}={str(value).lower()}' for key, value in
[('north', north), ('east', east), ('south', south), ('west', west)]) + ',waterlogged=false]')
def finished_scene():
v = original_scene()
for x in (6, 32):
for z in (14, 22, 30, 38, 46, 54):
v.set(x, 30, z, STONE)
for x in (7, 31):
v.set(x, 34, 12, STONE)
for z in range(36, 40):
v.set(35, 8, z, stair('east', 'bottom'))
return v
def lamp(v, x, y, z, hanging=True):
v.set(x, y, z, f'minecraft:lantern[hanging={str(hanging).lower()},waterlogged=false]')
def lighting(v):
v.stage = 'lanterns and interior lighting'
# Portal fixtures attach to the existing masonry jambs.
for x in (13, 25):
v.set(x, 14, 9, stair('south'))
v.set(x, 13, 9, CHAIN)
lamp(v, x, 12, 9)
for x, facing, bays in ((4, 'east', (14, 22, 30, 38, 46, 54)),
(34, 'west', (14, 22, 30))):
for z in bays:
v.set(x, 16, z, stair(facing))
v.set(x, 15, z, CHAIN)
lamp(v, x, 14, z)
for x in (43, 52):
v.set(x, 8, 14, stair('south'))
v.set(x, 7, 14, CHAIN)
lamp(v, x, 6, 14)
for z in (21, 29, 37):
v.set(59, 14, z, TRIM)
v.set(60, 14, z, stair('west'))
v.set(60, 13, z, CHAIN)
lamp(v, 60, 12, z)
# Existing low piers become courtyard lamps, with a full supporting cap.
for x in (8, 30, 35, 61):
for z in (6, 59):
v.set(x, 4, z, TRIM)
lamp(v, x, 5, z, False)
# Three chandeliers hang in the clear nave, below the preserved timber ties.
for z in (22, 38, 46):
v.box(19, 19, z, 19, 27, z, CHAIN)
for dx in range(-2, 3):
v.set(19 + dx, 18, z, bars(dx == 0, dx < 2, dx == 0, dx > -2))
for dz in (-2, -1, 1, 2):
v.set(19, 18, z + dz, bars(dz > -2, False, dz < 2, False))
for x, zz in ((17, z), (21, z), (19, z - 2), (19, z + 2)):
lamp(v, x, 17, zz)
for x, facing in ((13, 'west'), (25, 'east')):
for z in (22, 38, 46):
v.set(x, 12, z, stair(facing))
v.set(x, 11, z, CHAIN)
lamp(v, x, 10, z)
# Wing ground floor: lights hang from the existing floor beams.
for x in (43, 50):
for z in (23, 31):
v.set(x, 6, z, CHAIN)
lamp(v, x, 5, z)
for z in (23, 35):
v.box(38, 17, z, 57, 17, z, 'minecraft:spruce_log[axis=x]')
v.box(47, 14, z, 47, 16, z, CHAIN)
lamp(v, 47, 13, z)
# Tower landings and the block-built bell receive a restrained warm light.
for y in (7, 16, 25):
v.box(40, y + 1, 46, 40, y + 2, 46, CHAIN)
lamp(v, 40, y, 46)
for x in (37, 43):
v.box(x, 39, 49, x, 40, 49, CHAIN)
lamp(v, x, 38, 49)
def landscape(v):
v.stage = 'planted stone terrace'
def bed(x1, z1, x2, z2, high=3):
# Soil replaces only the known terrace surface, with a low dressed rim.
for x in range(x1, x2 + 1):
for z in range(z1, z2 + 1):
edge = x in (x1, x2) or z in (z1, z2)
if edge:
if (x, 1, z) not in v.blocks:
v.set(x, 1, z, SLAB)
else:
v.set(x, 0, z, 'minecraft:dirt')
height = high - int((x * 7 + z * 11) % 5 == 0)
for y in range(1, height + 1):
if (x, y, z) not in v.blocks:
v.set(x, y, z, LEAVES)
for z in (18, 26, 34, 42, 50):
bed(1, z - 2, 5, z + 2, 2)
for z in (21, 29, 37):
bed(60, z - 2, 64, z + 2, 2)
bed(5, 9, 12, 11, 2)
bed(26, 9, 32, 11, 2)
bed(39, 11, 43, 13, 2)
bed(52, 11, 56, 13, 2)
bed(9, 59, 28, 61, 2)
bed(48, 45, 59, 47, 2)
def narrow_tree(x, z, height):
# Persistent leaves cannot decay; every addition respects existing masonry.
if (x, 0, z) not in v.blocks or v.blocks[(x, 0, z)].split('[')[0] in (
'minecraft:smooth_stone', 'minecraft:polished_andesite', 'minecraft:stone_bricks'):
v.set(x, 0, z, 'minecraft:moss_block')
for y in range(1, height - 1):
if (x, y, z) not in v.blocks or v.blocks[(x, y, z)] == LEAVES:
v.set(x, y, z, 'minecraft:spruce_log[axis=y]')
for y in range(2, height + 1):
radius = 2 if y <= height - 4 else 1 if y <= height - 1 else 0
for dx in range(-radius, radius + 1):
for dz in range(-radius, radius + 1):
if abs(dx) + abs(dz) > radius + 1:
continue
p = x + dx, y, z + dz
if p not in v.blocks or v.blocks[p] == LEAVES:
v.set(*p, LEAVES)
for x, z, h in ((10, 9, 8), (28, 9, 8), (62, 16, 7), (62, 44, 7),
(6, 60, 8), (32, 60, 7)):
narrow_tree(x, z, h)
# Slight wear follows the edges, leaving the main approach calm and readable.
for (x, y, z), block in list(v.blocks.items()):
if y == 0 and block == 'minecraft:smooth_stone' and (x < 6 or x > 59 or z > 57):
if (x * 31 + z * 17) % 29 == 0:
v.set(x, y, z, 'minecraft:andesite')
if y in (1, 2, 3) and block == STONE and (x < 8 or x > 56) and (x * 13 + y * 7 + z * 19) % 31 == 0:
v.set(x, y, z, 'minecraft:mossy_stone_bricks')
def build_scene():
v = finished_scene()
architecture(v)
lighting(v)
landscape(v)
return v
-157
View File
@@ -1,157 +0,0 @@
"""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
@@ -1,176 +0,0 @@
"""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
@@ -1,190 +0,0 @@
"""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]")