47 lines
3.5 KiB
Python
47 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Archive native 30.7 structure witnesses over the unchanged 30.6 terrain field."""
|
|
import argparse, hashlib, json, re
|
|
from pathlib import Path
|
|
import matplotlib
|
|
matplotlib.use('Agg')
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.patches import Rectangle
|
|
import numpy as np
|
|
|
|
p=argparse.ArgumentParser(description=__doc__)
|
|
for name in ('terrain','village','transit','aerial','ruins','output'): p.add_argument('--'+name,type=Path,required=True)
|
|
a=p.parse_args()
|
|
data={name:json.loads(getattr(a,name).read_text()) for name in ('terrain','village','transit','aerial','ruins')}
|
|
t=data['terrain']; seed=str(t['seed']); size=t['diameter']
|
|
for name in ('village','transit','aerial','ruins'):
|
|
d=data[name]
|
|
if str(d['seed'])!=seed or d['diameter']!=size or not d.get('passed'): raise ValueError('Unmatched/failed '+name)
|
|
cols=np.asarray(t['variants']['current306']['columns']);xs,zs=np.unique(cols[:,0]),np.unique(cols[:,1]);g=cols.reshape(len(zs),len(xs),5)
|
|
fig,ax=plt.subplots(figsize=(11,10),layout='constrained')
|
|
step=t['map_step'];extent=(xs[0]-step/2,xs[-1]+step/2,zs[-1]+step/2,zs[0]-step/2)
|
|
im=ax.imshow(np.ma.masked_where(g[:,:,2]==0,g[:,:,2]),extent=extent,cmap='terrain',vmin=0,vmax=400,interpolation='nearest')
|
|
for link in data['village']['rail_links']:
|
|
if link['kind']=='FREIGHT': ax.plot([v['x'] for v in link['points']],[v['z'] for v in link['points']],color='#962427',lw=2)
|
|
walks=data['transit'].get('walk',[])
|
|
if isinstance(walks,dict): walks=[walks]
|
|
for walk in walks:
|
|
gates=[v['gate'] for v in walk['surface_accesses']]
|
|
ax.plot([v[0] for v in gates],[v[2] for v in gates],color='#223494',lw=2,ls='--',marker='s',ms=4)
|
|
for b in data['ruins']['bounds']:
|
|
v={k:int(n) for k,n in re.findall(r'(minX|minZ|maxX|maxZ)=(-?\d+)',b)}
|
|
ax.add_patch(Rectangle((v['minX'],v['minZ']),v['maxX']-v['minX']+1,v['maxZ']-v['minZ']+1,fill=False,edgecolor='#02abb2',linewidth=2))
|
|
for site in data['aerial']['geometry']:
|
|
width,depth=(site['width'],site['depth']) if site['facing']%2==0 else (site['depth'],site['width'])
|
|
colour='#8a4196' if site['kind']=='NATURAL_ISLET' else '#41301f'
|
|
ax.add_patch(Rectangle((site['minX'],site['minZ']),width,depth,fill=False,edgecolor=colour,linewidth=1.3))
|
|
ax.scatter([site['minX']+width//2],[site['minZ']+depth//2],color=colour,s=20,marker='x')
|
|
ax.scatter([0],[0],marker='+',s=70,color='black')
|
|
for label,color,style in [('Rails prévus — lacunes réparables','#962427','-'),('Axes des traversées parcourues','#223494','--'),('Ruines asséchées — emprises','#02abb2','-'),('Trésors du terrain aérien — emprises','#8a4196','-'),('Navires restaurés — emprises','#41301f','-')]:ax.plot([],[],color=color,ls=style,label=label)
|
|
ax.legend(loc='upper left',fontsize=8);ax.set(xlabel='X (blocs)',ylabel='Z (blocs) · nord en haut',aspect='equal')
|
|
ax.set_title(f'Sanctuary alpha.30.7 · {size} · graine {seed}\nInstallations sur le relief natif inchangé de 30.6')
|
|
fig.colorbar(im,ax=ax,shrink=.7,label='Altitude du terrain Y')
|
|
fig.supxlabel('Relief échantillonné ; rails planifiés. Traversées, coffres et ruines contrôlés dans les chunks FULL.\nLes rectangles indiquent des réservations, pas la forme construite ni les frontières des biomes.',fontsize=9)
|
|
a.output.mkdir(parents=True,exist_ok=True)
|
|
for ext in ('png','pdf'):fig.savefig(a.output/f'installations-{size}-{seed}.{ext}',dpi=160)
|
|
(a.output/f'installations-{size}-{seed}-provenance.json').write_text(json.dumps({k:{'path':str(getattr(a,k)),'sha256':hashlib.sha256(getattr(a,k).read_bytes()).hexdigest()} for k in data},indent=2)+'\n')
|