Files
Emil 89c53d7058 Expand material support and checkpoint the completed building toolkit
Expose the runtime block/item registry through compact material search and
single-material descriptions. Preserve private block-entity data through
checked edits and durable undo without sending payloads to model context.

Include the completed terrain tools, isolated world/map plugin, station lift,
ACP streaming and guidance fixes, local camera auto-connect, construction
scripts, and their public documentation, references and verification records.
Active station decoration and private runtime data remain outside this commit.

Validation: 145 Maven tests, 47 Bridge tests, successful camera Gradle build,
and isolated Paper verification of all 1,196 block defaults plus 5,392
independent property cases for placement, same-material edits and restoration.
2026-09-13 16:59:13 +03:00

62 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Compile audited access refinements against the original survey plus placed markers."""
import argparse
import importlib.util
import json
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
spec=importlib.util.spec_from_file_location('mark',ROOT/'scripts/mark-shacraft-layout.py')
mark=importlib.util.module_from_spec(spec);spec.loader.exec_module(mark)
def main():
p=argparse.ArgumentParser();p.add_argument('--before',type=Path,required=True)
p.add_argument('--base',type=Path,required=True);p.add_argument('--study',type=Path,required=True)
p.add_argument('--access',type=Path,required=True);p.add_argument('--output',type=Path,required=True)
a=p.parse_args();before=json.loads(a.before.read_text());base=json.loads(a.base.read_text())
study=json.loads(a.study.read_text());access=json.loads(a.access.read_text());desired={}
old={(b['x'],b['y'],b['z']):b['block'] for b in base['blocks']}
def ground(x,z):
i=(z-before['min_z'])*before['width']+x-before['min_x']
return before['surface_y'][i],before['palette'][before['material_index'][i]]
def put(x,z,block,group,y=None):
x,z=round(x),round(z);h,material=ground(x,z);y=max(h,51) if y is None else y
if y<h:raise ValueError('Refusing subsurface edit')
if material=='minecraft:grass_block':material+='[snowy=false]'
key=(x,y,z);expected=old.get(key,material if y==h else 'minecraft:air')
if expected=='minecraft:water':raise ValueError('Refusing water edit')
if expected==block:return
desired[key]={'x':x,'y':y,'z':z,'block':block,'expected':expected,'group':group}
labels=[]
for route in access['routes']:
pts=route['points'];color='minecraft:white_concrete'
for side in (-1,1):
for x,z in mark.raster_line(mark.offset(pts,side),.6):put(x,z,color,route['id'])
if route['role']=='stairs':
for i in range(0,len(pts),6):
aa=mark.offset(pts,-1)[i];bb=mark.offset(pts,1)[i]
for x,z in mark.raster_line([aa,bb],.6):put(x,z,'minecraft:blue_concrete',route['id'])
for label in route.get('labels',[]):
x,z=label['point'];h,_=ground(x,z)
labels.append({'x':x+.5,'y':h+5,'z':z+.5,'text':label['text']})
for route in study['routes']:
if route['role']!='bridge':continue
pts=route['points'];deck=route['deck_y']
for side in (-1,1):
for end in (0,-1):
x,z=map(round,mark.offset(pts,side*(route['width']-1)/2)[end]);h,_=ground(x,z)
for y in range(max(50,h+1),deck+1):put(x,z,'minecraft:white_concrete',route['id']+'-height-stakes',y)
for abutment in route.get('abutments',[]):
if abutment['future_stairs']:
x,z=abutment['point'];h,_=ground(x,z)
labels.append({'x':x+.5,'y':deck+5,'z':z+.5,
'text':f"FUTURE STAIRS / +{deck-h}m\nDECK Y{deck}"})
# The raised pier reservations meet a future upper terminal level.
for z in (-158,-132,-106):labels.append({'x':161.5,'y':83,'z':z+.5,'text':'PIER DECK Y79 / FUTURE ACCESS'})
data={'version':1,'scope':base['scope'],'blocks':sorted(desired.values(),key=lambda b:(b['z'],b['x'],b['y']))}
a.output.write_text(json.dumps(data,separators=(',',':'))+'\n')
a.output.with_suffix('.labels.json').write_text(json.dumps(labels,indent=2)+'\n')
print(json.dumps({'access_blocks':len(desired),'labels':len(labels)}))
if __name__=='__main__':main()