Finish alpha.30.7 with natural island treasures, direct rails and dry ruins
Build Sanctuary / build (push) Canceled after 0s

This commit is contained in:
koka
2026-09-12 20:29:32 +02:00
parent 0b7e0c6717
commit d5cd895403
34 changed files with 715 additions and 45 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ def main():
raise SystemExit("Empty atlas or duplicate seed/size")
for name in ("scripts/terrain_atlas.py", "scripts/terrain-atlas-requirements.txt",
"scripts/archive_terrain_atlas.py", "docs/terrain-atlas.md",
"scripts/structure_atlas.py", f"docs/generation-alpha{version.split('alpha.')[-1]}.md", f"docs/testing-alpha{version.split('alpha.')[-1]}.md"):
"scripts/structure_atlas.py", "scripts/finishing_atlas.py", f"docs/generation-alpha{version.split('alpha.')[-1]}.md", f"docs/testing-alpha{version.split('alpha.')[-1]}.md"):
files[name] = (ROOT / name).read_bytes()
for path in args.extra:
key = "structures/" + path.name
+46
View File
@@ -0,0 +1,46 @@
#!/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')