Rename Claude Code combination ids to the -cc suffix; refresh the Opus 4.8 mythos build

claude-fable-5-code -> claude-fable-5-cc, claude-opus-4-8 ->
claude-opus-4-8-cc (the entry id doubles as the outputs/ folder name).
The Opus mythos-craft artifact is a newer build of the same one-shot
provenance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jesse ZhangandClaude Fable 5 committed 2026-06-10 23:07:51 +01:00
1 parent b42022e8ce
commit 8acf16262f
7 files changed
+780 -375

No files matched your search

-375
View File
@@ -1,375 +0,0 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>方块秘境 · Voxel Mythos</title>
<style>
:root { --acid:#c8ff00; --night:#0c0e14; --ink:#e9f0ff; }
* { margin:0; padding:0; box-sizing:border-box; }
html, body { width:100%; height:100%; overflow:hidden; background:#0c0e14; }
body { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; color:var(--ink); -webkit-user-select:none; user-select:none; touch-action:none; }
canvas { display:block; }
#game { position:fixed; inset:0; }
/* Crosshair */
#cross { position:fixed; left:50%; top:50%; width:22px; height:22px; transform:translate(-50%,-50%); pointer-events:none; z-index:20; mix-blend-mode:difference; opacity:.9; }
#cross::before, #cross::after { content:""; position:absolute; background:#fff; }
#cross::before { left:50%; top:0; width:2px; height:100%; transform:translateX(-50%); }
#cross::after { top:50%; left:0; height:2px; width:100%; transform:translateY(-50%); }
/* HUD top-left */
#hud { position:fixed; left:14px; top:12px; z-index:20; font-size:12px; line-height:1.55; text-shadow:0 1px 2px rgba(0,0,0,.8); pointer-events:none; }
#hud b { color:var(--acid); font-weight:700; }
#hud .dim { color:#9aa6c2; }
/* Hotbar */
#hotbar { position:fixed; left:50%; bottom:18px; transform:translateX(-50%); z-index:20; display:flex; gap:6px; padding:6px; background:rgba(8,10,18,.55); border:1px solid rgba(255,255,255,.12); border-radius:12px; backdrop-filter:blur(8px); pointer-events:none; }
.slot { position:relative; width:48px; height:48px; border-radius:8px; background:rgba(255,255,255,.04); border:2px solid rgba(255,255,255,.10); display:flex; align-items:center; justify-content:center; }
.slot.active { border-color:var(--acid); box-shadow:0 0 0 2px rgba(200,255,0,.25), 0 6px 20px rgba(200,255,0,.18); transform:translateY(-3px); }
.slot canvas { width:34px; height:34px; image-rendering:pixelated; border-radius:4px; }
.slot .key { position:absolute; top:-9px; left:-6px; font-size:9px; color:#0c0e14; background:var(--acid); border-radius:5px; padding:0 4px; font-weight:800; }
.slot .nm { position:absolute; bottom:-16px; left:50%; transform:translateX(-50%); font-size:9px; color:#cfd8ef; white-space:nowrap; opacity:0; transition:opacity .15s; }
.slot.active .nm { opacity:1; }
/* Start / pause overlay */
#veil { position:fixed; inset:0; z-index:40; display:flex; align-items:center; justify-content:center; flex-direction:column; gap:22px;
background:radial-gradient(1200px 800px at 50% 20%, rgba(40,60,120,.35), rgba(8,10,18,.92) 60%), #06070c; text-align:center; transition:opacity .4s; }
#veil.hide { opacity:0; pointer-events:none; }
#veil .badge { font-size:11px; letter-spacing:.4em; color:var(--acid); text-transform:uppercase; }
#veil h1 { font-size:clamp(34px, 7vw, 76px); line-height:.96; font-weight:800; letter-spacing:-.02em;
background:linear-gradient(180deg,#fff, #9fb6ff 70%, #6f86ff); -webkit-background-clip:text; background-clip:text; color:transparent; text-shadow:0 0 60px rgba(120,150,255,.25); }
#veil h1 small { display:block; font-size:.34em; letter-spacing:.5em; color:#7d8bb8; -webkit-text-fill-color:#7d8bb8; margin-top:10px; font-weight:600; }
#veil .play { pointer-events:auto; cursor:pointer; font:inherit; font-weight:800; font-size:15px; letter-spacing:.18em; color:#0c0e14; background:var(--acid); border:none; padding:14px 34px; border-radius:999px; text-transform:uppercase; box-shadow:0 14px 40px rgba(200,255,0,.30); transition:transform .15s, box-shadow .15s; }
#veil .play:hover { transform:translateY(-2px); box-shadow:0 18px 50px rgba(200,255,0,.45); }
#veil .play:disabled { background:#39414f; color:#7d8bb8; box-shadow:none; cursor:default; }
#veil .keys { display:flex; gap:18px; flex-wrap:wrap; justify-content:center; max-width:680px; font-size:12px; color:#aab4d0; }
#veil .keys span b { color:#fff; background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.16); border-radius:6px; padding:2px 7px; margin-right:5px; font-weight:700; }
#veil .tip { font-size:11px; color:#6b769a; max-width:560px; line-height:1.7; }
#bar { position:fixed; left:50%; bottom:-2px; transform:translateX(-50%); width:min(420px,70vw); height:3px; background:rgba(255,255,255,.08); z-index:41; border-radius:2px; overflow:hidden; }
#bar i { display:block; height:100%; width:0%; background:var(--acid); transition:width .2s; }
#flash { position:fixed; inset:0; z-index:15; pointer-events:none; background:#fff; opacity:0; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="cross"></div>
<div id="flash"></div>
<div id="hud">
<div><b>方块秘境</b> <span class="dim">Voxel Mythos</span></div>
<div class="dim" id="stat">pos —</div>
<div class="dim" id="biome"></div>
<div class="dim" id="fps"></div>
</div>
<div id="hotbar"></div>
<div id="veil">
<div class="badge">A Hand-built Myth · 神话造物</div>
<h1>方块秘境<small>VOXEL MYTHOS</small></h1>
<button class="play" id="play" disabled>生成世界中…</button>
<div class="keys">
<span><b>W A S D</b>移动</span>
<span><b>SPACE</b>跳跃</span>
<span><b>SHIFT</b>疾跑</span>
<span><b>鼠标</b>环视</span>
<span><b>左键</b>破坏</span>
<span><b>右键</b>放置</span>
<span><b>1-9 / 滚轮</b>切换方块</span>
<span><b>F</b>飞行</span>
</div>
<div class="tip">柏林噪声生成的无限地形之上,矗立着由开发者亲手设计的「天悬神殿」与「世界之树」——光之回廊、水晶尖塔、悬浮岛屿、巨龙守卫与符文法阵环绕其间。降临神域,开始你的造物。</div>
</div>
<div id="bar"><i id="barfill"></i></div>
<script type="importmap">
{ "imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
}}
</script>
<script type="module">
import * as THREE from 'three';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
// ======================================================================
// 方块秘境 · VOXEL MYTHOS — single-file voxel sandbox
// Perlin terrain · first-person · break/place · a hand-designed mythos
// ======================================================================
// ---------- tunables ----------
const CHUNK = 16; // chunk width/depth in blocks
const WORLD_H = 72; // world height
const SEA = 22; // sea level
const RENDER_DIST = 6; // chunk radius around player to keep loaded
const SEED = 1337;
// ---------- block ids ----------
const B = {
AIR:0, GRASS:1, DIRT:2, STONE:3, SAND:4, LOG:5, LEAVES:6, WATER:7,
PLANK:8, COBBLE:9, MARBLE:10, GLOW:11, CRYSTAL:12, GOLD:13, OBSID:14,
RUNE:15, SNOW:16, MOSS:17, LAPIS:18, BEDROCK:19
};
// transparency / pass classification
const TRANSPARENT = new Set([B.AIR, B.WATER, B.LEAVES, B.CRYSTAL]); // don't fully occlude neighbours
const EMISSIVE = new Set([B.GLOW, B.RUNE, B.LAPIS]); // full-bright pass
const GLASSY = new Set([B.WATER, B.CRYSTAL]); // translucent pass
const isAir = id => id === B.AIR;
const isOpaque = id => id !== B.AIR && !TRANSPARENT.has(id);
const isSolid = id => id !== B.AIR && id !== B.WATER; // for collision
// ---------- seeded PRNG ----------
function mulberry32(a){ return function(){ a|=0; a=a+0x6D2B79F5|0; let t=Math.imul(a^a>>>15,1|a); t=t+Math.imul(t^t>>>7,61|t)^t; return ((t^t>>>14)>>>0)/4294967296; }; }
function hash2(x,y){ let h = x*374761393 + y*668265263; h = (h^(h>>>13))*1274126177; return ((h^(h>>>16))>>>0)/4294967296; }
// ---------- Perlin noise (improved, seeded permutation) ----------
const perm = new Uint8Array(512);
(function buildPerm(){
const p = new Uint8Array(256); for(let i=0;i<256;i++) p[i]=i;
const rng = mulberry32(SEED);
for(let i=255;i>0;i--){ const j=(rng()*(i+1))|0; const t=p[i]; p[i]=p[j]; p[j]=t; }
for(let i=0;i<512;i++) perm[i]=p[i&255];
})();
const fade = t => t*t*t*(t*(t*6-15)+10);
const lerp = (a,b,t) => a+(b-a)*t;
function grad2(h,x,y){ switch(h&7){ case 0:return x+y; case 1:return -x+y; case 2:return x-y; case 3:return -x-y; case 4:return x; case 5:return -x; case 6:return y; default:return -y; } }
function perlin2(x,y){
const X=Math.floor(x)&255, Y=Math.floor(y)&255; x-=Math.floor(x); y-=Math.floor(y);
const u=fade(x), v=fade(y);
const aa=perm[perm[X]+Y], ab=perm[perm[X]+Y+1], ba=perm[perm[X+1]+Y], bb=perm[perm[X+1]+Y+1];
return lerp( lerp(grad2(aa,x,y), grad2(ba,x-1,y), u), lerp(grad2(ab,x,y-1), grad2(bb,x-1,y-1), u), v );
}
// fractal brownian motion
function fbm(x,y,oct,lac,gain){ let f=0,amp=0.5,freq=1,norm=0; for(let i=0;i<oct;i++){ f+=amp*perlin2(x*freq,y*freq); norm+=amp; amp*=gain; freq*=lac; } return f/norm; }
// ======================================================================
// PROCEDURAL TEXTURE ATLAS — drawn pixel-by-pixel, NearestFilter
// ======================================================================
const TILE = 16, COLS = 8;
// tile indices
const T = { GRASS_TOP:0, GRASS_SIDE:1, DIRT:2, STONE:3, SAND:4, LOG_TOP:5, LOG_SIDE:6, LEAVES:7,
WATER:8, PLANK:9, COBBLE:10, MARBLE:11, GLOW:12, CRYSTAL:13, GOLD:14, OBSID:15,
RUNE:16, SNOW:17, MOSS:18, LAPIS:19, BEDROCK:20, MARBLE_GOLD:21 };
const TILE_COUNT = 22, ROWS = Math.ceil(TILE_COUNT/COLS);
const atlas = document.createElement('canvas'); atlas.width = COLS*TILE; atlas.height = ROWS*TILE;
const actx = atlas.getContext('2d');
// helpers operating on a TILE×TILE off-screen, then blitted into atlas at index
function makeTile(index, draw){
const c = document.createElement('canvas'); c.width=TILE; c.height=TILE; const g=c.getContext('2d');
const img = g.createImageData(TILE,TILE); const d = img.data;
const put=(x,y,r,gr,b,a=255)=>{ if(x<0||y<0||x>=TILE||y>=TILE)return; const i=(y*TILE+x)*4; d[i]=r;d[i+1]=gr;d[i+2]=b;d[i+3]=a; };
draw(put, mulberry32(SEED*131 + index*7919));
g.putImageData(img,0,0);
const cx=(index%COLS)*TILE, cy=((index/COLS)|0)*TILE;
actx.drawImage(c, cx, cy);
}
// colour util
function vary(base, rng, amt){ const k=(rng()*2-1)*amt; return [clamp8(base[0]+k), clamp8(base[1]+k), clamp8(base[2]+k)]; }
function clamp8(v){ return v<0?0:v>255?255:v|0; }
function mix(a,b,t){ return [a[0]+(b[0]-a[0])*t, a[1]+(b[1]-a[1])*t, a[2]+(b[2]-a[2])*t]; }
// ---- individual tile painters ----
function fill(put, base, rng, amt){ for(let y=0;y<TILE;y++)for(let x=0;x<TILE;x++){ const c=vary(base,rng,amt); put(x,y,c[0],c[1],c[2]); } }
makeTile(T.DIRT, (put,rng)=>{ fill(put,[134,96,67],rng,26);
for(let i=0;i<10;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; const c=vary([96,68,46],rng,18); put(x,y,c[0],c[1],c[2]); } });
makeTile(T.GRASS_TOP, (put,rng)=>{ fill(put,[86,150,58],rng,24);
for(let i=0;i<22;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; const c=rng()<.5?[64,118,42]:[110,176,70]; put(x,y,c[0],c[1],c[2]); } });
makeTile(T.GRASS_SIDE, (put,rng)=>{ // dirt body + green crown w/ dribble
fill(put,[134,96,67],rng,24);
for(let x=0;x<TILE;x++){ const top=2+((rng()*3)|0);
for(let y=0;y<top;y++){ const c=vary([78,142,52],rng,20); put(x,y,c[0],c[1],c[2]); }
if(rng()<.4){ const c=vary([70,128,46],rng,16); put(x,top,c[0],c[1],c[2]); if(rng()<.4) put(x,top+1,c[0]-8,c[1]-8,c[2]-6); } } });
makeTile(T.STONE, (put,rng)=>{ fill(put,[128,128,133],rng,16);
for(let i=0;i<6;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,96,96,102); } });
makeTile(T.COBBLE, (put,rng)=>{ const cell=4; for(let cy=0;cy<TILE;cy+=cell)for(let cx=0;cx<TILE;cx+=cell){ const b=110+((rng()*40)|0);
for(let y=0;y<cell;y++)for(let x=0;x<cell;x++){ const edge=(x===0||y===0); const v=edge?70:b+((rng()*16-8)|0); put(cx+x,cy+y,v,v,v+4); } } });
makeTile(T.SAND, (put,rng)=>{ fill(put,[219,203,140],rng,16);
for(let i=0;i<8;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,198,182,120); } });
makeTile(T.SNOW, (put,rng)=>{ fill(put,[236,242,250],rng,8);
for(let i=0;i<6;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,255,255,255); } });
makeTile(T.LOG_SIDE, (put,rng)=>{ for(let x=0;x<TILE;x++){ const streak=(x%4===0)?-18:0;
for(let y=0;y<TILE;y++){ const c=vary([101,73,42],rng,12); put(x,y,c[0]+streak,c[1]+streak,c[2]+streak); } }
for(let i=0;i<6;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,70,50,28); } });
makeTile(T.LOG_TOP, (put,rng)=>{ const c0=TILE/2-0.5; for(let y=0;y<TILE;y++)for(let x=0;x<TILE;x++){ const d=Math.hypot(x-c0,y-c0); const ring=(Math.floor(d)%2===0); const base=ring?[176,140,95]:[150,115,75]; const c=vary(base,rng,10); put(x,y,c[0],c[1],c[2]); }
put(c0|0,c0|0,120,92,58); });
makeTile(T.LEAVES, (put,rng)=>{ for(let y=0;y<TILE;y++)for(let x=0;x<TILE;x++){ if(rng()<.10){ put(x,y,0,0,0,0); continue; } const r=rng(); const base=r<.4?[38,82,28]:r<.8?[54,110,40]:[78,140,52]; const c=vary(base,rng,12); put(x,y,c[0],c[1],c[2]); } });
makeTile(T.MOSS, (put,rng)=>{ fill(put,[120,120,124],rng,14);
for(let i=0;i<60;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; if(rng()<.5){ const c=vary([62,116,46],rng,18); put(x,y,c[0],c[1],c[2]); } } });
makeTile(T.WATER, (put,rng)=>{ for(let y=0;y<TILE;y++)for(let x=0;x<TILE;x++){ const w=Math.sin((x+y)*0.9)*0.5+0.5; const base=mix([34,104,196],[60,140,226],w*0.6+rng()*0.2); put(x,y,base[0]|0,base[1]|0,base[2]|0,210); } });
makeTile(T.PLANK, (put,rng)=>{ fill(put,[176,134,78],rng,14);
for(let y=0;y<TILE;y++){ if(y%4===0){ for(let x=0;x<TILE;x++) put(x,y,120,86,46); } }
for(let i=0;i<10;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,150,112,62); } });
makeTile(T.MARBLE, (put,rng)=>{ fill(put,[232,230,223],rng,7);
let vx=(rng()*TILE)|0; for(let y=0;y<TILE;y++){ vx+=(rng()*3-1)|0; const c=[188,190,198]; put((vx+TILE)%TILE,y,c[0],c[1],c[2]); }
let vx2=(rng()*TILE)|0; for(let y=0;y<TILE;y++){ vx2+=(rng()*3-1)|0; put((vx2+TILE)%TILE,y,200,202,210); } });
makeTile(T.MARBLE_GOLD, (put,rng)=>{ fill(put,[232,230,223],rng,7);
for(let x=0;x<TILE;x++){ put(x,0,255,210,74); put(x,TILE-1,205,160,40); put(x,1,255,236,150); } });
makeTile(T.GLOW, (put,rng)=>{ fill(put,[255,206,107],rng,14);
for(let i=0;i<26;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,255,238,176); }
for(let i=0;i<8;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; put(x,y,224,150,60); } });
makeTile(T.CRYSTAL, (put,rng)=>{ for(let y=0;y<TILE;y++)for(let x=0;x<TILE;x++){ const band=((x+y)>>1)%3; const base=band===0?[210,250,255]:band===1?[150,235,255]:[96,180,228]; const c=vary(base,rng,10); put(x,y,c[0],c[1],c[2],235); }
for(let i=0;i<5;i++){ const x=(rng()*TILE)|0; put(x,(rng()*TILE)|0,255,255,255,255); } });
makeTile(T.LAPIS, (put,rng)=>{ fill(put,[40,66,176],rng,16);
for(let i=0;i<26;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; const c=rng()<.5?[92,124,232]:[24,40,120]; put(x,y,c[0],c[1],c[2]); } });
makeTile(T.GOLD, (put,rng)=>{ const half=TILE/2; for(let by=0;by<2;by++)for(let bx=0;bx<2;bx++){ for(let y=0;y<half;y++)for(let x=0;x<half;x++){ let c=[255,210,74]; if(x===0||y===0) c=[150,112,30]; else if(x<2&&y<2) c=[255,244,180]; else if(x>half-3&&y>half-3) c=[205,160,40]; const v=vary(c,rng,8); put(bx*half+x,by*half+y,v[0],v[1],v[2]); } } });
makeTile(T.OBSID, (put,rng)=>{ fill(put,[22,18,34],rng,8);
for(let i=0;i<14;i++){ const x=(rng()*TILE)|0,y=(rng()*TILE)|0; const c=vary([74,44,98],rng,18); put(x,y,c[0],c[1],c[2]); }
for(let k=0;k<TILE;k++){ if(k%5===0) put(k,k,120,90,150); } });
makeTile(T.BEDROCK, (put,rng)=>{ for(let y=0;y<TILE;y++)for(let x=0;x<TILE;x++){ const v=30+((rng()*60)|0); put(x,y,v,v,v+3); } });
makeTile(T.RUNE, (put,rng)=>{ fill(put,[58,58,66],rng,12); // dark stone + glowing glyph
const glyph=[ "0001111000","0010000100","0100110010","0101001010","1010000101","1010000101","0101001010","0100110010","0010000100","0001111000" ];
const off=3; for(let y=0;y<10;y++)for(let x=0;x<10;x++){ if(glyph[y][x]==='1'){ const c=vary([120,240,255],rng,16); put(x+off,y+off,c[0],c[1],c[2]); } }
put(8,8,200,250,255); });
// ---------- atlas → texture ----------
const atlasTex = new THREE.CanvasTexture(atlas);
atlasTex.magFilter = THREE.NearestFilter; atlasTex.minFilter = THREE.NearestFilter;
atlasTex.generateMipmaps = false; atlasTex.flipY = false; // canvas (0,0)=top-left maps to UV(0,0)
atlasTex.colorSpace = THREE.SRGBColorSpace; atlasTex.anisotropy = 1;
const EPS = 0.0008; // inset to kill texture bleed
function tileUV(idx){ const col=idx%COLS, row=(idx/COLS)|0;
const u0=(col*TILE)/atlas.width + EPS, v0=(row*TILE)/atlas.height + EPS;
const u1=((col+1)*TILE)/atlas.width - EPS, v1=((row+1)*TILE)/atlas.height - EPS;
return [u0,v0,u1,v1]; // v0=top edge, v1=bottom edge (flipY=false)
}
// block id → face tiles {top, side, bottom}
const FACE = {};
FACE[B.GRASS]={top:T.GRASS_TOP, side:T.GRASS_SIDE, bottom:T.DIRT};
FACE[B.DIRT]={top:T.DIRT,side:T.DIRT,bottom:T.DIRT};
FACE[B.STONE]={top:T.STONE,side:T.STONE,bottom:T.STONE};
FACE[B.SAND]={top:T.SAND,side:T.SAND,bottom:T.SAND};
FACE[B.LOG]={top:T.LOG_TOP,side:T.LOG_SIDE,bottom:T.LOG_TOP};
FACE[B.LEAVES]={top:T.LEAVES,side:T.LEAVES,bottom:T.LEAVES};
FACE[B.WATER]={top:T.WATER,side:T.WATER,bottom:T.WATER};
FACE[B.PLANK]={top:T.PLANK,side:T.PLANK,bottom:T.PLANK};
FACE[B.COBBLE]={top:T.COBBLE,side:T.COBBLE,bottom:T.COBBLE};
FACE[B.MARBLE]={top:T.MARBLE,side:T.MARBLE_GOLD,bottom:T.MARBLE};
FACE[B.GLOW]={top:T.GLOW,side:T.GLOW,bottom:T.GLOW};
FACE[B.CRYSTAL]={top:T.CRYSTAL,side:T.CRYSTAL,bottom:T.CRYSTAL};
FACE[B.GOLD]={top:T.GOLD,side:T.GOLD,bottom:T.GOLD};
FACE[B.OBSID]={top:T.OBSID,side:T.OBSID,bottom:T.OBSID};
FACE[B.RUNE]={top:T.RUNE,side:T.OBSID,bottom:T.OBSID};
FACE[B.SNOW]={top:T.SNOW,side:T.SNOW,bottom:T.SNOW};
FACE[B.MOSS]={top:T.MOSS,side:T.MOSS,bottom:T.MOSS};
FACE[B.LAPIS]={top:T.LAPIS,side:T.LAPIS,bottom:T.LAPIS};
FACE[B.BEDROCK]={top:T.BEDROCK,side:T.BEDROCK,bottom:T.BEDROCK};
// ---------- materials ----------
const matOpaque = new THREE.MeshLambertMaterial({ map:atlasTex, vertexColors:true, alphaTest:0.5, side:THREE.FrontSide });
const matGlass = new THREE.MeshLambertMaterial({ map:atlasTex, vertexColors:true, transparent:true, opacity:0.82, depthWrite:false, side:THREE.DoubleSide });
const matGlow = new THREE.MeshBasicMaterial({ map:atlasTex, vertexColors:true, transparent:true, alphaTest:0.4 });
// ======================================================================
// WORLD MODEL
// - chunks store generated voxel data
// - overrides[] holds everything non-terrain: trees, the mythos, player edits
// - sampleWorld() is a stateless fallback so chunk borders never seam
// ======================================================================
const CSIZE = CHUNK*CHUNK*WORLD_H;
const chunks = new Map(); // "cx,cz" -> { data, group, dirty }
const overrides = new Map(); // "x,y,z" -> id (persistent edits/structures/trees)
const colCache = new Map(); // "x,z" -> column info
const decorated = new Set(); // chunks whose trees were stamped
const ckey = (cx,cz)=> cx+","+cz;
const bkey = (x,y,z)=> x+","+y+","+z;
const cdiv = n => Math.floor(n/CHUNK);
const localIdx = (lx,y,lz)=> (y*CHUNK + lz)*CHUNK + lx;
// write an override (force=false → first writer wins, used by trees so the mythos isn't overwritten)
function ov(x,y,z,id,force){ if(y<0||y>=WORLD_H) return; const k=bkey(x,y,z);
if(!force && overrides.has(k)) return; overrides.set(k,id); }
function columnInfo(x,z){ const k=x+","+z; let c=colCache.get(k); if(c) return c;
const base = fbm(x*0.0065, z*0.0065, 5, 2.0, 0.5); // rolling continents
const ridge = Math.pow(1 - Math.abs(fbm(x*0.012, z*0.012, 3, 2.0, 0.5)), 2); // mountain ridges
let h = SEA + Math.round(base*22) + Math.round(ridge*16) - 3;
h = Math.max(3, Math.min(WORLD_H-12, h));
const temp = perlin2(x*0.0032+50.5, z*0.0032-50.5);
const moist = perlin2(x*0.0032-80.5, z*0.0032+30.5);
const sandy = (h <= SEA+1) || (temp>0.34 && moist<-0.05);
const snowy = (h > SEA+25) || (temp < -0.42 && h>SEA+6);
let topId = sandy ? B.SAND : snowy ? B.SNOW : (h<SEA? B.DIRT : B.GRASS);
c = { h, topId, sandy, snowy }; colCache.set(k,c); return c;
}
function terrainBlock(x,y,z){
if(y<=0) return B.BEDROCK;
if(y>=WORLD_H) return B.AIR;
const info = columnInfo(x,z);
if(y > info.h){ return (y<=SEA) ? B.WATER : B.AIR; }
if(y === info.h) return info.topId;
if(y > info.h-4) return info.sandy ? B.SAND : B.DIRT;
// deep stone with rare veins for flavour
const r = hash2(x*1.7+y*3.1, z*1.3-y*2.7);
if(r < 0.010 && y < SEA-2) return B.LAPIS;
if(r > 0.992) return B.COBBLE;
return B.STONE;
}
// single authoritative lookup (used at borders & by physics/raycast for any coord)
function sampleWorld(x,y,z){ if(y<0) return B.BEDROCK; if(y>=WORLD_H) return B.AIR;
const o = overrides.get(bkey(x,y,z)); if(o!==undefined) return o; return terrainBlock(x,y,z); }
function getBlock(x,y,z){ if(y<0) return B.BEDROCK; if(y>=WORLD_H) return B.AIR;
const ch = chunks.get(ckey(cdiv(x),cdiv(z)));
if(ch){ const lx=x-cdiv(x)*CHUNK, lz=z-cdiv(z)*CHUNK; return ch.data[localIdx(lx,y,lz)]; }
return sampleWorld(x,y,z);
}
// ---------- tree decoration (writes only to overrides; first-writer-wins) ----------
function placeTree(x,by,z){ const r=mulberry32((x*73856093)^(z*19349663));
const th = 4 + ((r()*3)|0);
for(let i=0;i<th;i++) ov(x,by+i,z,B.LOG,false);
const top = by+th;
for(let dy=-2;dy<=1;dy++){ const ly=top+dy; const rad = dy<=0?2:1;
for(let dx=-rad;dx<=rad;dx++)for(let dz=-rad;dz<=rad;dz++){
if(Math.abs(dx)===rad && Math.abs(dz)===rad && r()<0.6) continue; // round the corners
if(dx===0&&dz===0&&dy<0) continue; // keep trunk clear
ov(x+dx, ly, z+dz, B.LEAVES, false);
} }
ov(x,top+1,z,B.LEAVES,false);
}
function decorate(cx,cz){ const k=ckey(cx,cz); if(decorated.has(k)) return; decorated.add(k);
for(let lx=0;lx<CHUNK;lx++)for(let lz=0;lz<CHUNK;lz++){
const x=cx*CHUNK+lx, z=cz*CHUNK+lz;
if(x*x+z*z < 52*52) continue; // keep the mythos plaza clear of forest
const info = columnInfo(x,z);
if(info.topId!==B.GRASS || info.h<=SEA) continue;
if(hash2(x*1.13+0.5, z*2.27+0.5) < 0.016) placeTree(x, info.h+1, z);
}
}
/* @@APPEND@@ */
</script>
</body>
</html>
-300
View File
@@ -1,300 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 600" width="900" height="600" role="img" aria-labelledby="t d">
<title id="t">A pelican riding a bicycle by the sea</title>
<desc id="d">A lively brown pelican with a long pouched beak pedals a red bicycle along a sunlit beach, the turquoise ocean and a sailboat behind it.</desc>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#2f7fc6"/>
<stop offset="0.45" stop-color="#74b6e6"/>
<stop offset="0.80" stop-color="#cfe9f6"/>
<stop offset="1" stop-color="#f6e7c4"/>
</linearGradient>
<radialGradient id="sun" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#fffdf2"/>
<stop offset="0.35" stop-color="#fff0b8"/>
<stop offset="0.7" stop-color="#ffe07f" stop-opacity="0.55"/>
<stop offset="1" stop-color="#ffe07f" stop-opacity="0"/>
</radialGradient>
<linearGradient id="sea" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0f7f96"/>
<stop offset="0.45" stop-color="#15a3b3"/>
<stop offset="1" stop-color="#46c6c4"/>
</linearGradient>
<linearGradient id="sand" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ecd29a"/>
<stop offset="0.5" stop-color="#e7c27c"/>
<stop offset="1" stop-color="#d8ad62"/>
</linearGradient>
<linearGradient id="body" x1="0.2" y1="0" x2="0.7" y2="1">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="0.55" stop-color="#eef2f5"/>
<stop offset="1" stop-color="#cdd7df"/>
</linearGradient>
<linearGradient id="wing" x1="0" y1="0" x2="0.4" y2="1">
<stop offset="0" stop-color="#c6cfd8"/>
<stop offset="1" stop-color="#8b97a4"/>
</linearGradient>
<linearGradient id="beak" x1="0" y1="0" x2="0.4" y2="1">
<stop offset="0" stop-color="#ffcf5c"/>
<stop offset="1" stop-color="#f1972a"/>
</linearGradient>
<linearGradient id="pouch" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f6a93c"/>
<stop offset="1" stop-color="#e07a22"/>
</linearGradient>
<linearGradient id="frame" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ff6a45"/>
<stop offset="1" stop-color="#d6371d"/>
</linearGradient>
<radialGradient id="seaGlitter" cx="0.5" cy="0.2" r="0.7">
<stop offset="0" stop-color="#ffffff" stop-opacity="0.6"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
</radialGradient>
<filter id="soft" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="6"/>
</filter>
<filter id="softer" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="12"/>
</filter>
</defs>
<!-- SKY -->
<rect x="0" y="0" width="900" height="600" fill="url(#sky)"/>
<circle cx="170" cy="120" r="160" fill="url(#sun)"/>
<circle cx="170" cy="120" r="46" fill="#fffdf0"/>
<!-- clouds -->
<g fill="#ffffff" opacity="0.92">
<g filter="url(#soft)">
<ellipse cx="350" cy="92" rx="70" ry="24"/>
<ellipse cx="400" cy="80" rx="46" ry="22"/>
<ellipse cx="305" cy="84" rx="40" ry="18"/>
</g>
<g filter="url(#soft)" opacity="0.85">
<ellipse cx="700" cy="64" rx="80" ry="22"/>
<ellipse cx="752" cy="54" rx="46" ry="20"/>
<ellipse cx="648" cy="58" rx="40" ry="16"/>
</g>
</g>
<!-- seagulls -->
<g fill="none" stroke="#5b6b7d" stroke-width="2.4" stroke-linecap="round">
<path d="M250,150 q11,-11 22,0 q11,-11 22,0"/>
<path d="M520,96 q8,-8 16,0 q8,-8 16,0"/>
<path d="M560,128 q9,-9 18,0 q9,-9 18,0"/>
</g>
<!-- SEA -->
<rect x="0" y="250" width="900" height="158" fill="url(#sea)"/>
<rect x="0" y="250" width="900" height="158" fill="url(#seaGlitter)"/>
<!-- distant sailboat -->
<g transform="translate(150,300)">
<path d="M0,0 L0,-42 L34,-6 Z" fill="#fbfbf7"/>
<path d="M-3,-2 L-22,-44 L-3,-44 Z" fill="#e7ecef"/>
<path d="M-26,2 q26,16 52,0 l-9,12 q-17,8 -34,0 Z" fill="#34424f"/>
</g>
<!-- wave highlights -->
<g stroke="#eafcff" stroke-linecap="round" fill="none" opacity="0.75">
<path d="M60,300 q30,-7 60,0" stroke-width="2.5"/>
<path d="M520,288 q34,-7 68,0" stroke-width="2.5"/>
<path d="M700,320 q40,-8 80,0" stroke-width="3"/>
<path d="M300,345 q44,-9 88,0" stroke-width="3"/>
<path d="M620,360 q40,-8 80,0" stroke-width="3"/>
</g>
<!-- SAND -->
<path d="M0,408 L900,408 L900,600 L0,600 Z" fill="url(#sand)"/>
<!-- wet-sand shoreline + foam -->
<path d="M0,408 q120,18 240,4 q140,-16 260,6 q150,20 400,-2 L900,420 L900,440 q-220,22 -430,2 q-150,-14 -250,4 q-130,16 -220,0 Z" fill="#cfeef0" opacity="0.65"/>
<path d="M0,406 q120,18 240,4 q140,-16 260,6 q150,20 400,-2" fill="none" stroke="#ffffff" stroke-width="3" opacity="0.85"/>
<!-- tyre tracks trailing behind -->
<g fill="none" stroke="#bc9558" stroke-width="3" stroke-linecap="round" opacity="0.7">
<path d="M60,486 q120,-10 200,8"/>
<path d="M64,498 q120,-10 200,8"/>
</g>
<!-- foreground shells & starfish -->
<g opacity="0.95">
<g transform="translate(120,548) rotate(-12)" fill="#e8743c" stroke="#c25827" stroke-width="2">
<path d="M0,-16 L5,-3 L18,-3 L7,5 L11,18 L0,10 L-11,18 L-7,5 L-18,-3 L-5,-3 Z"/>
</g>
<ellipse cx="800" cy="560" rx="16" ry="11" fill="#f4dfc0" stroke="#d8b27e" stroke-width="2"/>
<path d="M790,560 q10,-10 20,0" fill="none" stroke="#d8b27e" stroke-width="1.6"/>
<circle cx="760" cy="538" r="4" fill="#c9a26a"/>
<circle cx="180" cy="572" r="5" fill="#cba978"/>
</g>
<!-- ground shadow under bike -->
<ellipse cx="430" cy="506" rx="220" ry="22" fill="#7a5a28" opacity="0.28" filter="url(#softer)"/>
<!-- ================= BICYCLE ================= -->
<!-- wheels: rear (275,428) & front (585,428), r=72 -->
<g stroke-linecap="round">
<!-- spokes -->
<g stroke="#b9c2cc" stroke-width="1.6">
<line x1="275" y1="428" x2="339" y2="428"/><line x1="275" y1="428" x2="330" y2="460"/><line x1="275" y1="428" x2="307" y2="483"/><line x1="275" y1="428" x2="275" y2="492"/><line x1="275" y1="428" x2="243" y2="483"/><line x1="275" y1="428" x2="220" y2="460"/><line x1="275" y1="428" x2="211" y2="428"/><line x1="275" y1="428" x2="220" y2="396"/><line x1="275" y1="428" x2="243" y2="373"/><line x1="275" y1="428" x2="275" y2="364"/><line x1="275" y1="428" x2="307" y2="373"/><line x1="275" y1="428" x2="330" y2="396"/>
<line x1="585" y1="428" x2="649" y2="428"/><line x1="585" y1="428" x2="640" y2="460"/><line x1="585" y1="428" x2="617" y2="483"/><line x1="585" y1="428" x2="585" y2="492"/><line x1="585" y1="428" x2="553" y2="483"/><line x1="585" y1="428" x2="530" y2="460"/><line x1="585" y1="428" x2="521" y2="428"/><line x1="585" y1="428" x2="530" y2="396"/><line x1="585" y1="428" x2="553" y2="373"/><line x1="585" y1="428" x2="585" y2="364"/><line x1="585" y1="428" x2="617" y2="373"/><line x1="585" y1="428" x2="640" y2="396"/>
</g>
<!-- rims -->
<circle cx="275" cy="428" r="64" fill="none" stroke="#cfd6dd" stroke-width="4"/>
<circle cx="585" cy="428" r="64" fill="none" stroke="#cfd6dd" stroke-width="4"/>
<!-- tyres -->
<circle cx="275" cy="428" r="72" fill="none" stroke="#23292f" stroke-width="9"/>
<circle cx="585" cy="428" r="72" fill="none" stroke="#23292f" stroke-width="9"/>
<!-- hubs -->
<circle cx="275" cy="428" r="8" fill="#5a626b"/><circle cx="275" cy="428" r="3" fill="#2a2f35"/>
<circle cx="585" cy="428" r="8" fill="#5a626b"/><circle cx="585" cy="428" r="3" fill="#2a2f35"/>
</g>
<!-- chain -->
<path d="M420,432 L275,428" fill="none" stroke="#2c333b" stroke-width="6" stroke-linecap="round"/>
<circle cx="275" cy="428" r="13" fill="none" stroke="#3a4049" stroke-width="4"/>
<!-- frame (dark underlay then coloured tube) -->
<g fill="none" stroke-linecap="round">
<g stroke="#9e2a16" stroke-width="13">
<path d="M275,428 L345,318 M275,428 L420,432 M420,432 L345,318 M420,432 L560,326 M345,318 L560,326 M560,326 L585,428"/>
</g>
<g stroke="url(#frame)" stroke-width="9">
<path d="M275,428 L345,318 M275,428 L420,432 M420,432 L345,318 M420,432 L560,326 M345,318 L560,326 M560,326 L585,428"/>
</g>
<!-- steerer + handlebar -->
<path d="M560,326 L580,290" stroke="#9e2a16" stroke-width="13"/>
<path d="M560,326 L580,290" stroke="url(#frame)" stroke-width="9"/>
<path d="M560,290 q24,-6 40,6" stroke="#2c333b" stroke-width="8"/>
</g>
<!-- crank + pedals -->
<circle cx="420" cy="432" r="16" fill="#3a4049"/>
<circle cx="420" cy="432" r="9" fill="#5a626b"/>
<path d="M420,432 L408,378" stroke="#2c333b" stroke-width="6" stroke-linecap="round"/>
<path d="M420,432 L432,488" stroke="#2c333b" stroke-width="6" stroke-linecap="round"/>
<rect x="396" y="372" width="26" height="8" rx="3" fill="#1f242a"/>
<rect x="420" y="484" width="28" height="8" rx="3" fill="#1f242a"/>
<!-- saddle -->
<path d="M316,314 q28,-9 58,0 q-6,12 -30,12 q-22,0 -28,-12 Z" fill="#2c2c2c"/>
<!-- ================= PELICAN ================= -->
<!-- soft contact shadow of bird on frame area -->
<ellipse cx="400" cy="408" rx="120" ry="20" fill="#5a6b78" opacity="0.18" filter="url(#soft)"/>
<!-- far leg (behind) -->
<path d="M412,392 L405,378" stroke="#d9731c" stroke-width="9" stroke-linecap="round"/>
<path d="M408,374 q-12,2 -16,8 q14,2 22,-2 Z" fill="#e8822a" stroke="#cf6a18" stroke-width="1.5"/>
<!-- tail feathers -->
<g fill="#c3ccd5" stroke="#9aa6b1" stroke-width="1.5">
<path d="M306,322 l-44,2 l40,12 Z"/>
<path d="M306,332 l-46,14 l42,8 Z"/>
<path d="M306,344 l-40,24 l40,2 Z"/>
</g>
<!-- body -->
<path d="M300,318
C320,250 402,236 472,258
C512,272 522,312 506,350
C498,380 470,404 430,408
C372,414 322,392 306,356
C300,344 298,330 300,318 Z" fill="url(#body)"/>
<!-- belly warm under-light -->
<path d="M330,372 C372,406 460,402 496,366 C484,392 452,408 422,410 C378,414 344,398 330,372 Z" fill="#f3e6cf" opacity="0.55"/>
<!-- folded wing on the side -->
<path d="M470,278
C432,300 392,326 356,350
C374,356 392,352 408,346
C436,334 464,316 484,294
C480,288 476,282 470,278 Z" fill="url(#wing)"/>
<g fill="none" stroke="#7c8794" stroke-width="2" stroke-linecap="round">
<path d="M462,292 C432,312 402,330 372,346"/>
<path d="M470,302 C444,320 414,336 386,350"/>
<path d="M478,312 C454,328 426,342 400,352"/>
</g>
<!-- dark primary wingtip -->
<path d="M356,350 C366,344 378,344 388,348 C378,356 366,358 356,354 Z" fill="#2f3742"/>
<!-- neck -->
<path d="M470,272
C462,232 478,204 514,194
C544,186 570,196 578,216
C584,232 582,248 570,258
C556,250 544,250 530,256
C510,268 500,288 500,302
C492,290 478,282 470,272 Z" fill="url(#body)"/>
<!-- chestnut nape (breeding plumage) -->
<path d="M474,266 C468,234 480,210 506,200 C496,222 492,246 498,272 C488,272 480,270 474,266 Z" fill="#9a5230" opacity="0.85"/>
<!-- head -->
<circle cx="556" cy="212" r="30" fill="url(#body)"/>
<!-- pale yellow crown wash + crest -->
<path d="M534,196 C540,178 566,176 580,190 C566,192 548,196 538,206 Z" fill="#f6e4a6"/>
<g stroke="#e9d488" stroke-width="3" stroke-linecap="round">
<path d="M540,188 q-6,-12 -2,-22"/>
<path d="M552,184 q-2,-13 4,-22"/>
</g>
<!-- upper mandible -->
<path d="M578,206
C632,206 694,222 748,248
C751,253 749,259 742,260
C700,250 648,242 598,236
C586,234 580,224 578,206 Z" fill="url(#beak)" stroke="#d57e1c" stroke-width="1.4"/>
<!-- pouch (lower mandible / gular sac) -->
<path d="M566,246
C562,300 598,338 654,334
C696,331 728,300 742,260
C736,256 730,255 724,256
C694,272 642,278 600,272
C584,269 574,258 566,246 Z" fill="url(#pouch)" stroke="#cf6c1c" stroke-width="1.4"/>
<!-- pouch shading + seam -->
<path d="M580,262 C586,300 614,326 656,326 C620,322 596,300 588,268 Z" fill="#c8631a" opacity="0.45"/>
<path d="M578,236 C636,244 696,254 742,260" fill="none" stroke="#b85e16" stroke-width="2"/>
<!-- beak hooked tip + nostril -->
<path d="M742,248 q12,2 12,12 q-7,4 -14,0 Z" fill="#e07a1f" stroke="#c0661a" stroke-width="1.2"/>
<path d="M600,216 l26,5" stroke="#c0661a" stroke-width="2" stroke-linecap="round"/>
<!-- top ridge highlight -->
<path d="M584,210 C632,210 692,225 742,250" fill="none" stroke="#fff0c0" stroke-width="2" opacity="0.8"/>
<!-- eye -->
<circle cx="568" cy="206" r="9" fill="#f4e3a0"/>
<circle cx="568" cy="206" r="6.2" fill="#23282f"/>
<circle cx="570.5" cy="203.5" r="2" fill="#ffffff"/>
<!-- near wing-arm gripping handlebar -->
<path d="M494,300
C522,300 552,298 572,298
C578,302 578,312 572,318
C552,320 522,320 500,318
C494,312 492,306 494,300 Z" fill="url(#body)"/>
<g fill="none" stroke="#bcc6cf" stroke-width="2" stroke-linecap="round">
<path d="M506,306 q34,-2 58,-2"/>
<path d="M510,313 q30,0 54,0"/>
</g>
<!-- little grip hand on bar -->
<path d="M566,296 q16,-2 22,8 q-10,8 -24,4 Z" fill="#eef2f5" stroke="#c3ccd5" stroke-width="1.5"/>
<!-- near leg + webbed foot on pedal -->
<path d="M438,400 C446,432 444,456 434,478" fill="none" stroke="#e8822a" stroke-width="10" stroke-linecap="round"/>
<path d="M420,484 q-10,4 -8,14 q22,6 40,-2 q4,-8 -2,-14 q-16,8 -30,2 Z" fill="#f0902f" stroke="#cf6a18" stroke-width="1.6"/>
<g stroke="#cf6a18" stroke-width="1.4">
<path d="M424,492 l-2,8"/><path d="M436,494 l0,8"/><path d="M448,492 l2,7"/>
</g>
<!-- front basket with a fish -->
<path d="M596,300 l52,0 l-7,40 l-38,0 Z" fill="#cf9a5a" stroke="#9c6e34" stroke-width="2"/>
<g stroke="#9c6e34" stroke-width="1.5">
<path d="M600,312 l44,0"/><path d="M601,324 l42,0"/>
<path d="M612,302 l-3,38"/><path d="M624,302 l-1,38"/><path d="M636,302 l2,38"/>
</g>
<!-- fish peeking out -->
<g transform="translate(640,300)">
<path d="M0,0 C-14,-10 -36,-10 -48,-2 C-36,6 -14,6 0,-2 Z" fill="#5fb4d6" stroke="#357f9e" stroke-width="1.5"/>
<path d="M0,-2 l12,-9 l-2,12 l-9,-1 Z" fill="#357f9e"/>
<circle cx="-40" cy="-2" r="2.4" fill="#0e2f3d"/>
</g>
<!-- light sand sparkle / motion puffs -->
<g fill="#ffffff" opacity="0.5">
<circle cx="250" cy="504" r="5"/><circle cx="232" cy="498" r="3"/><circle cx="266" cy="498" r="3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB