Meta Description" name="description" />

Share this result

Previews are deleted daily. Get a permanent share link sent to your inbox:
Script
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Crystal Depths</title> <style> *{margin:0;padding:0;box-sizing:border-box} body{overflow:hidden;background:#000;cursor:crosshair} canvas{display:block} </style> <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet"> </head> <body> <canvas id="game"></canvas> <script> const C=document.getElementById('game'),ctx=C.getContext('2d'); function resize(){C.width=innerWidth;C.height=innerHeight} addEventListener('resize',resize);resize(); /* ===== TILE IDS ===== */ const AIR=0,GRASS=1,DIRT=2,STONE=3,SAND=4,SANDSTONE=5,WOOD=6,LEAVES=7, CACTUS=8,COAL=9,COPPER=10,IRON=11,GOLD=12,CRYSTAL=13,RUBY=14, EMERALD=15,MITHRIL=16,PLANKS=17,BENCH=18,TORCH=19,BRICK=20, OBSIDIAN=21,MUSH_BLK=22,CHEST=23,SPAWNER=24; /* ===== ITEM IDS ===== */ const W_PICK=100,S_PICK=101,CU_PICK=102,IR_PICK=103,CR_PICK=104,MT_PICK=105; const W_SWORD=110,S_SWORD=111,CU_SWORD=112,IR_SWORD=113,CR_SWORD=114,MT_SWORD=115; const BERRY=150,CK_MEAT=151,H_POT=152,M_SOUP=153; /* ===== STATE ===== */ let WW=250,WH=120,SEED=12345,DIFF=1,wSeed=12345; let GRAV=0.42,JUMP_V=-7.5,SPD=2.8,REACH=5.5; let world=[],surfY=[],skyExp=[],biomes=[]; let pl={x:0,y:0,vx:0,vy:0,w:10,h:16,hp:100,maxHp:100,dir:1,gr:false,fr:0,invT:0,atkCd:0,hunger:100}; let inv=new Array(30).fill(null),hSlot=0,cursor=null,curFrom=-1; let mng={on:false,tx:0,ty:0,pr:0}; let enemies=[],particles=[],spTimers=new Map(); let cam={x:0,y:0},shake=0; let keys={},ms={x:0,y:0,l:false,r:false}; let dayT=0.35,invOpen=false,titleScr=true; let spawnX=0,spawnY=0; let tSpr={},tIc={}; let darkCvs,darkCtx2,torchLCvs; let titleParts=[]; let startSel={sz:1,df:1,seed:Math.floor(Math.random()*99999)}; /* ===== TILE DATA ===== */ const TD={}; function dT(id,s,hp,dr,c,n){TD[id]={s,hp,dr,c,n}} dT(GRASS,1,3,DIRT,'#4ade80','Grass');dT(DIRT,1,2,DIRT,'#92700a','Dirt'); dT(STONE,1,5,STONE,'#7a7a7a','Stone');dT(SAND,1,1,SAND,'#d4a853','Sand'); dT(SANDSTONE,1,4,SANDSTONE,'#c49a42','Sandstone');dT(WOOD,1,3,WOOD,'#8B5E14','Wood'); dT(LEAVES,0,1,LEAVES,'#22a855','Leaves');dT(CACTUS,1,3,CACTUS,'#16a34a','Cactus'); dT(COAL,1,5,COAL,'#333','Coal');dT(COPPER,1,6,COPPER,'#d97706','Copper'); dT(IRON,1,8,IRON,'#b0b0b0','Iron');dT(GOLD,1,10,GOLD,'#facc15','Gold'); dT(CRYSTAL,1,12,CRYSTAL,'#22d3ee','Crystal');dT(RUBY,1,14,RUBY,'#e11d48','Ruby'); dT(EMERALD,1,14,EMERALD,'#10b981','Emerald');dT(MITHRIL,1,18,MITHRIL,'#a78bfa','Mithril'); dT(PLANKS,1,3,PLANKS,'#c49a5c','Planks');dT(BENCH,1,5,BENCH,'#a06020','Workbench'); dT(TORCH,0,1,TORCH,'#fbbf24','Torch');dT(BRICK,1,8,BRICK,'#b45309','Brick'); dT(OBSIDIAN,1,20,OBSIDIAN,'#1e1b2e','Obsidian');dT(MUSH_BLK,1,2,MUSH_BLK,'#c084fc','Mushroom'); dT(CHEST,1,3,null,'#92400e','Chest');dT(SPAWNER,1,15,null,'#374151','Spawner'); /* ===== ITEM DATA ===== */ const ID={}; function dI(id,n,t,v){ID[id]={n,t,v}} dI(W_PICK,'Wood Pick','pick',1);dI(S_PICK,'Stone Pick','pick',2.2); dI(CU_PICK,'Copper Pick','pick',3.5);dI(IR_PICK,'Iron Pick','pick',5); dI(CR_PICK,'Crystal Pick','pick',7);dI(MT_PICK,'Mithril Pick','pick',10); dI(W_SWORD,'Wood Sword','sword',8);dI(S_SWORD,'Stone Sword','sword',15); dI(CU_SWORD,'Copper Sword','sword',22);dI(IR_SWORD,'Iron Sword','sword',32); dI(CR_SWORD,'Crystal Sword','sword',45);dI(MT_SWORD,'Mithril Sword','sword',60); dI(BERRY,'Berry','food',{h:15,heal:5});dI(CK_MEAT,'Cooked Meat','food',{h:35,heal:10}); dI(H_POT,'Health Potion','food',{h:0,heal:50});dI(M_SOUP,'Mush Soup','food',{h:25,heal:20}); /* ===== RECIPES ===== */ const recipes=[ {r:PLANKS,c:4,i:[{id:WOOD,c:4}],n:'Planks'}, {r:BENCH,c:1,i:[{id:PLANKS,c:10}],n:'Workbench'}, {r:TORCH,c:4,i:[{id:WOOD,c:1},{id:COAL,c:1}],n:'Torch',b:1}, {r:S_PICK,c:1,i:[{id:WOOD,c:3},{id:STONE,c:8}],n:'Stone Pick',b:1}, {r:S_SWORD,c:1,i:[{id:WOOD,c:2},{id:STONE,c:6}],n:'Stone Sword',b:1}, {r:CU_PICK,c:1,i:[{id:WOOD,c:3},{id:COPPER,c:8}],n:'Copper Pick',b:1}, {r:CU_SWORD,c:1,i:[{id:WOOD,c:2},{id:COPPER,c:6}],n:'Copper Sword',b:1}, {r:IR_PICK,c:1,i:[{id:WOOD,c:3},{id:IRON,c:8}],n:'Iron Pick',b:1}, {r:IR_SWORD,c:1,i:[{id:WOOD,c:2},{id:IRON,c:6}],n:'Iron Sword',b:1}, {r:CR_PICK,c:1,i:[{id:WOOD,c:3},{id:CRYSTAL,c:8}],n:'Crystal Pick',b:1}, {r:CR_SWORD,c:1,i:[{id:WOOD,c:2},{id:CRYSTAL,c:6}],n:'Crystal Sword',b:1}, {r:MT_PICK,c:1,i:[{id:IRON,c:3},{id:MITHRIL,c:8}],n:'Mithril Pick',b:1}, {r:MT_SWORD,c:1,i:[{id:IRON,c:2},{id:MITHRIL,c:6}],n:'Mithril Sword',b:1}, {r:H_POT,c:2,i:[{id:CRYSTAL,c:2},{id:LEAVES,c:4}],n:'Health Potion',b:1}, {r:M_SOUP,c:2,i:[{id:MUSH_BLK,c:3},{id:LEAVES,c:2}],n:'Mush Soup',b:1}, {r:CK_MEAT,c:1,i:[{id:LEAVES,c:3},{id:COAL,c:1}],n:'Cooked Meat',b:1}, {r:BRICK,c:4,i:[{id:STONE,c:4},{id:COAL,c:2}],n:'Brick',b:1}, {r:OBSIDIAN,c:1,i:[{id:STONE,c:5},{id:CRYSTAL,c:3}],n:'Obsidian',b:1} ]; /* ===== NOISE ===== */ function hash(n){n=((n>>16)^n)*45679+wSeed|0;n=((n>>16)^n)*45679|0;n=(n>>16)^n;return(n&0xffff)/0xffff} function hash2(x,y){return hash(x*374761+y*668265)} function n1(x){const i=Math.floor(x),f=x-i,t=f*f*(3-2*f);return hash(i)*(1-t)+hash(i+1)*t} function fbm(x){return n1(x*0.014)*14+n1(x*0.04+100)*6+n1(x*0.09+200)*2.5} function n2(x,y){const ix=Math.floor(x),iy=Math.floor(y),fx=x-ix,fy=y-iy;const tx=fx*fx*(3-2*fx),ty=fy*fy*(3-2*fy);const a=hash2(ix,iy),b=hash2(ix+1,iy),c=hash2(ix,iy+1),d=hash2(ix+1,iy+1);return(a*(1-tx)+b*tx)*(1-ty)+(c*(1-tx)+d*tx)*ty} function inB(x,y){return x>=0&&x<WW&&y>=0&&y<WH} function isSol(tx,ty){if(!inB(tx,ty))return ty>=WH;return TD[world[ty][tx]]?.s||false} function lC(a,b,t){return[Math.round(a[0]+(b[0]-a[0])*t),Math.round(a[1]+(b[1]-a[1])*t),Math.round(a[2]+(b[2]-a[2])*t)]} /* ===== WORLD GEN ===== */ function getBiome(x){const v=n1(x*0.008+wSeed*0.001);if(v<0.28)return'desert';if(v>0.72)return'rocky';return'forest'} function genWorld(){ world=[];surfY=[];skyExp=[];biomes=[]; for(let y=0;y<WH;y++){world[y]=new Uint8Array(WW);skyExp[y]=new Uint8Array(WW)} for(let x=0;x<WW;x++){biomes[x]=getBiome(x);surfY[x]=Math.max(20,Math.min(WH*0.4,Math.floor(WH*0.27+fbm(x))))} // Fill terrain by biome for(let x=0;x<WW;x++){ const sy=surfY[x],bio=biomes[x]; for(let y=sy;y<WH;y++){ if(y===sy){world[y][x]=bio==='desert'?SAND:bio==='rocky'?STONE:GRASS} else if(y<sy+4+Math.floor(n1(x*0.3+500)*3)){world[y][x]=bio==='desert'?SANDSTONE:DIRT} else world[y][x]=STONE; } } // Caves for(let y=0;y<WH;y++)for(let x=0;x<WW;x++){ if(y>surfY[x]+5){const v=n2(x*0.065,y*0.065)+n2(x*0.03+50,y*0.03+50)*0.5;if(v>0.82)world[y][x]=AIR} } // Ores for(let y=0;y<WH;y++)for(let x=0;x<WW;x++){ if(world[y][x]===STONE){ const r=n2(x*0.15+1e3,y*0.15+1e3); if(y>35&&r>0.81)world[y][x]=COAL; else if(y>44&&r>0.84)world[y][x]=COPPER; else if(y>55&&r>0.87)world[y][x]=IRON; else if(y>68&&r>0.90)world[y][x]=GOLD; else if(y>80&&r>0.92)world[y][x]=CRYSTAL; else if(y>85&&r>0.93)world[y][x]=RUBY; else if(y>88&&r>0.935)world[y][x]=EMERALD; else if(y>95&&r>0.94)world[y][x]=MITHRIL; } } // Mushroom zone underground for(let y=Math.floor(WH*0.7);y<WH;y++)for(let x=0;x<WW;x++){ if(world[y][x]===STONE&&n2(x*0.1+2e3,y*0.1+2e3)>0.88)world[y][x]=MUSH_BLK; } // Trees / Cacti for(let x=3;x<WW-3;x++){ const sy=surfY[x],bio=biomes[x]; if(bio==='desert'){ if(world[sy][x]===SAND&&hash(x*17+33)<0.06){ for(let dy=1;dy<=3+Math.floor(hash(x*19)*2);dy++)if(sy-dy>=0)world[sy-dy][x]=CACTUS; x+=3; } }else if(bio==='forest'){ if(world[sy][x]===GRASS&&hash(x*7+99)<0.16){ const h=4+Math.floor(hash(x*13+77)*3); for(let dy=1;dy<=h;dy++)if(sy-dy>=0)world[sy-dy][x]=WOOD; const top=sy-h; for(let ly=top-2;ly<=top;ly++){const w=ly===top-2?1:2;for(let lx=-w;lx<=w;lx++){const tx2=x+lx,ty2=ly;if(inB(tx2,ty2)&&world[ty2][tx2]===AIR)world[ty2][tx2]=LEAVES}} x+=3; } }else{ if(world[sy][x]===STONE&&hash(x*7+99)<0.06){ const h=3+Math.floor(hash(x*13+77)*2); for(let dy=1;dy<=h;dy++)if(sy-dy>=0)world[sy-dy][x]=WOOD; x+=4; } } } // Dungeons const nDng=3+Math.floor(hash(wSeed+999)*5); for(let d=0;d<nDng;d++){ const dw=8+Math.floor(hash(wSeed+d*100)*12),dh=6+Math.floor(hash(wSeed+d*200)*8); const dx=10+Math.floor(hash(wSeed+d*300)*(WW-dw-20)),dy=Math.floor(WH*0.4)+Math.floor(hash(wSeed+d*400)*(WH*0.5-dh)); for(let y=dy;y<dy+dh&&y<WH;y++)for(let x=dx;x<dx+dw&&x<WW;x++){ if(x===dx||x===dx+dw-1||y===dy||y===dy+dh-1)world[y][x]=BRICK; else world[y][x]=AIR; } // Entrance const ex=dx+Math.floor(dw/2); for(let y=dy-1;y<=dy+1&&y<WH;y++)if(y>=0&&inB(ex,y))world[y][ex]=AIR; // Chest const cx=dx+2+Math.floor(hash(wSeed+d*500)*(dw-4)),cy=dy+1+Math.floor(hash(wSeed+d*600)*(dh-3)); if(inB(cx,cy)&&cy<WH)world[cy][cx]=CHEST; // Spawner const sx2=dx+2+Math.floor(hash(wSeed+d*700)*(dw-4)),sy2=dy+1+Math.floor(hash(wSeed+d*800)*(dh-3)); if(inB(sx2,sy2)&&sy2<WH&&world[sy2][sx2]===AIR)world[sy2][sx2]=SPAWNER; } // Sky exposure for(let x=0;x<WW;x++)calcSky(x); // Spawn spawnX=Math.floor(WW/2);spawnY=surfY[spawnX]-2; pl.x=spawnX*T+T/2;pl.y=spawnY*T; cam.x=pl.x-C.width/2;cam.y=pl.y-C.height/2; } function calcSky(x){let bl=false;for(let y=0;y<WH;y++){skyExp[y][x]=bl?0:1;if(TD[world[y][x]]?.s)bl=true}} /* ===== SPRITES ===== */ function initSprites(){ for(const id of[GRASS,DIRT,STONE,SAND,SANDSTONE,WOOD,LEAVES,CACTUS,COAL,COPPER,IRON,GOLD,CRYSTAL,RUBY,EMERALD,MITHRIL,PLANKS,BENCH,TORCH,BRICK,OBSIDIAN,MUSH_BLK,CHEST,SPAWNER]){ const c=document.createElement('canvas');c.width=c.height=T;drawTS(c.getContext('2d'),id);tSpr[id]=c; } for(const id in ID){const c=document.createElement('canvas');c.width=c.height=28;drawTI(c.getContext('2d'),+id,28);tIc[id]=c} // Torch light sprite const r=T*8;torchLCvs=document.createElement('canvas');torchLCvs.width=torchLCvs.height=r*2; const tc=torchLCvs.getContext('2d'),g=tc.createRadialGradient(r,r,0,r,r,r); g.addColorStop(0,'rgba(0,0,0,0.97)');g.addColorStop(0.3,'rgba(0,0,0,0.7)');g.addColorStop(0.7,'rgba(0,0,0,0.25)');g.addColorStop(1,'rgba(0,0,0,0)'); tc.fillStyle=g;tc.fillRect(0,0,r*2,r*2); // Dark canvas darkCvs=document.createElement('canvas');darkCvs.width=C.width;darkCvs.height=C.height; darkCtx2=darkCvs.getContext('2d'); } function drawTS(x,id){ switch(id){ case GRASS:x.fillStyle='#8B6914';x.fillRect(0,0,T,T);x.fillStyle='#6d5510';x.fillRect(3,8,2,2);x.fillRect(10,12,2,1);x.fillStyle='#4ade80';x.fillRect(0,0,T,5);x.fillStyle='#22c55e';for(let i=0;i<5;i++)x.fillRect(i*3+1,1,1,-1-((i*7)%3));break; case DIRT:x.fillStyle='#92700a';x.fillRect(0,0,T,T);x.fillStyle='#7a5c10';x.fillRect(2,3,2,2);x.fillRect(9,8,2,2);break; case STONE:x.fillStyle='#7a7a7a';x.fillRect(0,0,T,T);x.fillStyle='#666';x.fillRect(0,7,T,1);x.fillRect(8,0,1,7);x.fillStyle='#8a8a8a';x.fillRect(3,2,2,2);break; case SAND:x.fillStyle='#d4a853';x.fillRect(0,0,T,T);x.fillStyle='#c49a42';x.fillRect(2,4,1,1);x.fillRect(8,2,1,1);x.fillRect(13,9,1,1);break; case SANDSTONE:x.fillStyle='#c49a42';x.fillRect(0,0,T,T);x.fillStyle='#b08530';x.fillRect(0,4,T,1);x.fillRect(0,10,T,1);x.fillStyle='#d4a853';x.fillRect(3,6,3,2);break; case WOOD:x.fillStyle='#8B5E14';x.fillRect(0,0,T,T);x.fillStyle='#6d4a10';x.fillRect(4,0,1,T);x.fillRect(10,0,1,T);break; case LEAVES:x.fillStyle='#22a855';x.fillRect(0,0,T,T);x.fillStyle='#1a8a44';x.fillRect(2,2,3,3);x.fillRect(9,5,4,3);x.fillStyle='#2ecc60';x.fillRect(0,5,2,2);x.fillRect(12,1,2,2);break; case CACTUS:x.fillStyle='#16a34a';x.fillRect(5,0,6,T);x.fillStyle='#15803d';x.fillRect(0,4,5,3);x.fillRect(11,7,5,3);x.fillStyle='#22c55e';x.fillRect(6,1,4,1);break; case COAL:case COPPER:case IRON:case GOLD:case CRYSTAL:case RUBY:case EMERALD:case MITHRIL: x.fillStyle='#7a7a7a';x.fillRect(0,0,T,T);x.fillStyle='#666';x.fillRect(0,7,T,1); x.fillStyle={[COAL]:'#222',[COPPER]:'#d97706',[IRON]:'#b0b0b0',[GOLD]:'#facc15',[CRYSTAL]:'#22d3ee',[RUBY]:'#e11d48',[EMERALD]:'#10b981',[MITHRIL]:'#a78bfa'}[id]; [[2,3],[8,2],[12,9],[5,11],[1,8],[10,5]].forEach(([sx,sy])=>x.fillRect(sx,sy,2+((sx+sy)%2),2));break; case PLANKS:x.fillStyle='#c49a5c';x.fillRect(0,0,T,T);x.fillStyle='#a67c42';x.fillRect(0,3,T,1);x.fillRect(0,7,T,1);x.fillRect(0,11,T,1);x.fillRect(8,0,1,T);break; case BENCH:x.fillStyle='#a06020';x.fillRect(0,0,T,T);x.fillStyle='#7a4a18';x.fillRect(0,4,T,1);x.fillRect(0,8,T,1);x.fillRect(0,12,T,1);x.fillStyle='#c07030';x.fillRect(2,1,4,3);x.fillRect(10,1,4,3);x.fillRect(2,5,4,3);x.fillRect(10,5,4,3);break; case TORCH:x.fillStyle='#8B5E14';x.fillRect(7,6,2,10);x.fillStyle='#fbbf24';x.fillRect(6,2,4,5);x.fillStyle='#fff7a0';x.fillRect(7,3,2,3);break; case BRICK:x.fillStyle='#92400e';x.fillRect(0,0,T,T);x.fillStyle='#b45309';x.fillRect(0,0,7,7);x.fillRect(9,0,7,7);x.fillRect(4,8,7,7);x.fillRect(0,8,3,7);x.fillRect(12,8,4,7);x.fillStyle='#78350f';x.fillRect(7,0,2,7);x.fillRect(3,8,1,7);x.fillRect(11,8,1,7);break; case OBSIDIAN:x.fillStyle='#1e1b2e';x.fillRect(0,0,T,T);x.fillStyle='#2d2640';x.fillRect(2,2,4,3);x.fillRect(10,8,4,3);x.fillStyle='#151225';x.fillRect(6,10,3,3);break; case MUSH_BLK:x.fillStyle='#7c3aed';x.fillRect(0,0,T,T);x.fillStyle='#c084fc';x.beginPath();x.arc(5,4,4,Math.PI,0);x.fill();x.beginPath();x.arc(12,3,3,Math.PI,0);x.fill();x.fillStyle='#a855f7';x.fillRect(4,6,8,5);break; case CHEST:x.fillStyle='#78350f';x.fillRect(2,4,12,10);x.fillStyle='#92400e';x.fillRect(1,2,14,4);x.fillStyle='#fbbf24';x.fillRect(6,5,4,3);x.fillStyle='#f59e0b';x.fillRect(7,6,2,1);break; case SPAWNER:x.fillStyle='#1f2937';x.fillRect(0,0,T,T);x.strokeStyle='#7f1d1d';x.lineWidth=1;x.strokeRect(3,3,10,10);x.fillStyle='#991b1b';x.fillRect(7,6,2,2);x.fillRect(5,9,2,2);x.fillRect(9,9,2,2);break; } } function drawTI(x,id,s){ const m=s/2;x.save();x.translate(m,m);x.rotate(-Math.PI/4); if(id>=W_PICK&&id<=MT_PICK){ x.fillStyle='#8B6914';x.fillRect(-1.5,-s*0.3,3,s*0.65); const cols={[W_PICK]:'#aaa',[S_PICK]:'#888',[CU_PICK]:'#d97706',[IR_PICK]:'#b0b0b0',[CR_PICK]:'#22d3ee',[MT_PICK]:'#a78bfa'}; x.fillStyle=cols[id];x.fillRect(-s*0.28,-s*0.35,s*0.56,5); }else if(id>=W_SWORD&&id<=MT_SWORD){ x.fillStyle='#8B6914';x.fillRect(-1.5,s*0.05,3,s*0.3);x.fillRect(-s*0.13,0,s*0.26,3); const cols={[W_SWORD]:'#bbb',[S_SWORD]:'#999',[CU_SWORD]:'#d97706',[IR_SWORD]:'#b0b0b0',[CR_SWORD]:'#22d3ee',[MT_SWORD]:'#a78bfa'}; x.fillStyle=cols[id];x.fillRect(-2.5,-s*0.38,5,s*0.4); x.beginPath();x.moveTo(-2.5,-s*0.38);x.lineTo(0,-s*0.48);x.lineTo(2.5,-s*0.38);x.fill(); }else if(id>=BERRY){ x.setTransform(1,0,0,1,0,0); const fc={[BERRY]:'#ef4444',[CK_MEAT]:'#a0522d',[H_POT]:'#166534',[M_SOUP]:'#7c3aed'}[id]||'#888'; x.fillStyle=fc;x.fillRect(m-5,m-3,10,8); if(id===H_POT){x.fillStyle='#15803d';x.fillRect(m-2,m-6,4,4);x.fillStyle='#92400e';x.fillRect(m-2,m-8,4,3)} if(id===M_SOUP){x.fillStyle='#c084fc';x.fillRect(m-3,m-5,6,3)} x.fillStyle='rgba(255,255,255,0.25)';x.fillRect(m-3,m-1,3,5); } x.restore(); } /* ===== INVENTORY ===== */ function addItem(id,count){ if(id<100)for(let i=0;i<30;i++)if(inv[i]&&inv[i].id===id){inv[i].c+=count;return true} for(let i=0;i<30;i++)if(!inv[i]){inv[i]={id,c:count};return true} return false; } function cntItem(id){let t=0;for(let i=0;i<30;i++)if(inv[i]&&inv[i].id===id)t+=inv[i].c;return t} function rmItems(id,count){let rem=count;for(let i=0;i<30;i++){if(inv[i]&&inv[i].id===id){const tk=Math.min(inv[i].c,rem);inv[i].c-=tk;rem-=tk;if(inv[i].c<=0)inv[i]=null;if(rem<=0)return true}}return rem<=0} function nearBench(){const px=Math.floor(pl.x/T),py=Math.floor(pl.y/T);for(let dy=-3;dy<=3;dy++)for(let dx=-3;dx<=3;dx++){const tx=px+dx,ty=py+dy;if(inB(tx,ty)&&world[ty][tx]===BENCH)return true}return false} function canCraft(r){for(const it of r.i)if(cntItem(it.id)<it.c)return false;if(r.b&&!nearBench())return false;return true} function doCraft(r){if(!canCraft(r))return;for(const it of r.i)rmItems(it.id,it.c);addItem(r.r,r.c);spawnP(pl.x,pl.y-pl.h/2,'#4ade80',5)} function chestLoot(depth){ const items=[{id:TORCH,c:3+Math.floor(Math.random()*5)}]; if(depth>20)items.push({id:COPPER,c:3+Math.floor(Math.random()*5)}); if(depth>35)items.push({id:IRON,c:2+Math.floor(Math.random()*4)}); if(depth>50)items.push({id:GOLD,c:1+Math.floor(Math.random()*3)}); if(depth>65)items.push({id:CRYSTAL,c:1+Math.floor(Math.random()*3)}); if(depth>75&&Math.random()>0.4)items.push({id:RUBY,c:1+Math.floor(Math.random()*2)}); if(depth>80&&Math.random()>0.5)items.push({id:EMERALD,c:1+Math.floor(Math.random()*2)}); if(depth>90&&Math.random()>0.6)items.push({id:MITHRIL,c:1+Math.floor(Math.random()*2)}); items.push({id:Math.random()>0.5?H_POT:CK_MEAT,c:1+Math.floor(Math.random()*2)}); return items; } function itemName(id){if(ID[id])return ID[id].n;if(id===200)return'Cu Sword';return TD[id]?.n||'???'} /* ===== PARTICLES ===== */ function spawnP(x,y,col,n){for(let i=0;i<n;i++)particles.push({x,y,vx:(Math.random()-0.5)*4,vy:(Math.random()-0.5)*4-1,life:20+Math.random()*15,ml:35,col,sz:1.5+Math.random()*2})} function updParts(){for(let i=particles.length-1;i>=0;i--){const p=particles[i];p.x+=p.vx;p.y+=p.vy;p.vy+=0.15;p.life--;if(p.life<=0)particles.splice(i,1)}} /* ===== ENEMIES ===== */ function spawnEnemyAt(tx,ty){ if(enemies.length>15)return; const depth=ty-(surfY[Math.min(tx,WW-1)]||35); let type='green',hp=18,dmg=8,col='#4ade80',spd=0.8; const dm=[0.5,1,1.8][DIFF]; if(depth>20||getDayLight()<0.5){type='blue';hp=30;dmg=14;col='#3b82f6';spd=1.2} if(depth>40){type='crystal';hp=50;dmg=22;col='#22d3ee';spd=1.5} if(depth>65){type='shadow';hp=70;dmg=30;col='#a855f7';spd=1.3} enemies.push({x:tx*T+T/2,y:ty*T+T,vx:0,vy:0,w:14,h:12,hp,mhp:hp,dir:1,jt:30,type,col,dmg:dmg*dm,spd,gr:false,fr:0}); } function spawnEnemy(){ if(enemies.length>12)return; const dl=getDayLight();const sr=[0.003,0.015,0.03][DIFF]; if(dl>0.75&&Math.random()>sr)return; const dir=Math.random()<0.5?-1:1; const ex=Math.floor(pl.x/T)+dir*(14+Math.random()*10); if(ex<2||ex>=WW-2)return; let ey=-1;for(let y=0;y<WH-1;y++){if(TD[world[y][ex]]?.s&&y+1<WH&&world[y+1][ex]===AIR){ey=y;break}} if(ey<0)return; spawnEnemyAt(ex,ey); } function updEnemies(){ for(let i=enemies.length-1;i>=0;i--){ const e=enemies[i]; const dx=pl.x-e.x,dy=pl.y-e.y,dist=Math.sqrt(dx*dx+dy*dy); if(dist>300){enemies.splice(i,1);continue} if(dist<180){e.dir=dx>0?1:-1;e.vx=e.dir*e.spd;e.jt--;if(e.jt<=0&&e.gr){e.vy=e.type==='shadow'?-7:-5.5;e.jt=20+Math.random()*25}} else e.vx*=0.9; e.vy+=GRAV;if(e.vy>10)e.vy=10; e.x+=e.vx;rColE(e,'x');e.y+=e.vy;e.gr=false;rColE(e,'y'); if(pl.invT<=0&&Math.abs(pl.x-e.x)<(pl.w+e.w)/2&&Math.abs((pl.y-pl.h/2)-(e.y-e.h/2))<(pl.h+e.h)/2){ pl.hp-=e.dmg;pl.invT=50;pl.vx=(pl.x>e.x?1:-1)*5;pl.vy=-4;shake=8;spawnP(pl.x,pl.y-pl.h/2,'#ef4444',6); } if(e.y>WH*T+100)enemies.splice(i,1); e.fr++; } } function rColE(e,axis){ const l=Math.floor((e.x-e.w/2)/T),r=Math.floor((e.x+e.w/2-0.1)/T); const t=Math.floor((e.y-e.h)/T),b=Math.floor((e.y-0.1)/T); if(axis==='x'){for(let y=t;y<=b;y++){if(e.vx>0&&isSol(r,y)){e.x=r*T-e.w/2;e.vx=0}if(e.vx<0&&isSol(l,y)){e.x=(l+1)*T+e.w/2;e.vx=0}}} else{for(let x=l;x<=r;x++){if(e.vy>0&&isSol(x,b)){e.y=b*T;e.vy=0;e.gr=true}if(e.vy<0&&isSol(x,t)){e.y=(t+1)*T+e.h;e.vy=0}}} } /* ===== SPAWNERS ===== */ function updSpawners(){ const px=Math.floor(pl.x/T),py=Math.floor(pl.y/T); for(let dy=-12;dy<=12;dy++)for(let dx=-12;dx<=12;dx++){ const tx=px+dx,ty=py+dy; if(!inB(tx,ty)||world[ty][tx]!==SPAWNER)continue; const k=tx+','+ty;if(Math.abs(dx)+Math.abs(dy)>10)continue; if(!spTimers.has(k))spTimers.set(k,0); spTimers.set(k,spTimers.get(k)-1); if(spTimers.get(k)<=0&&enemies.length<15){ if(ty-1>=0&&world[ty-1][tx]===AIR)spawnEnemyAt(tx,ty-1); spTimers.set(k,90+Math.floor(Math.random()*90)); } } } /* ===== PLAYER ===== */ function updPlayer(){ pl.vx=0; if(keys['KeyA']||keys['ArrowLeft']){pl.vx=-SPD;pl.dir=-1} if(keys['KeyD']||keys['ArrowRight']){pl.vx=SPD;pl.dir=1} if((keys['KeyW']||keys['ArrowUp']||keys['Space'])&&pl.gr)pl.vy=JUMP_V; pl.vy+=GRAV;if(pl.vy>12)pl.vy=12; pl.x+=pl.vx; let pl2=Math.floor((pl.x-pl.w/2)/T),pr2=Math.floor((pl.x+pl.w/2-0.1)/T); let pt2=Math.floor((pl.y-pl.h)/T),pb2=Math.floor((pl.y-0.1)/T); for(let y=pt2;y<=pb2;y++){if(pl.vx>0&&isSol(pr2,y)){pl.x=pr2*T-pl.w/2;pl.vx=0}if(pl.vx<0&&isSol(pl2,y)){pl.x=(pl2+1)*T+pl.w/2;pl.vx=0}} pl.y+=pl.vy;pl.gr=false; pl2=Math.floor((pl.x-pl.w/2)/T);pr2=Math.floor((pl.x+pl.w/2-0.1)/T); pt2=Math.floor((pl.y-pl.h)/T);pb2=Math.floor((pl.y-0.1)/T); for(let x=pl2;x<=pr2;x++){if(pl.vy>0&&isSol(x,pb2)){pl.y=pb2*T;pl.vy=0;pl.gr=true}if(pl.vy<0&&isSol(x,pt2)){pl.y=(pt2+1)*T+pl.h;pl.vy=0}} pl.x=Math.max(T,Math.min(WW*T-T,pl.x)); if(pl.y>WH*T+50)pl.hp=0; if(Math.abs(pl.vx)>0.1)pl.fr++; if(pl.invT>0)pl.invT--;if(pl.atkCd>0)pl.atkCd--; // Hunger const hr=[0.015,0.04,0.09][DIFF]; pl.hunger=Math.max(0,pl.hunger-hr); // HP regen/drain if(pl.hunger>60&&pl.hp<pl.maxHp)pl.hp=Math.min(pl.maxHp,pl.hp+0.03); else if(pl.hunger<=0)pl.hp-=0.03; if(pl.hp<=0){pl.hp=pl.maxHp;pl.hunger=80;pl.x=spawnX*T+T/2;pl.y=spawnY*T;pl.vx=0;pl.vy=0;pl.invT=90;shake=12} } /* ===== MINING & PLACE ===== */ function updMining(){ if(invOpen||titleScr){mng.on=false;return} const wx=ms.x+cam.x,wy=ms.y+cam.y; const tx=Math.floor(wx/T),ty=Math.floor(wy/T); const px=pl.x/T,py=(pl.y-pl.h/2)/T; const dist=Math.sqrt((tx+0.5-px)**2+(ty+0.5-py)**2); if(!ms.l||dist>REACH||!inB(tx,ty)||world[ty][tx]===AIR){mng.on=false;return} const tt=world[ty][tx],held=inv[hSlot]; // Sword attack if(held&&ID[held.id]?.t==='sword'){ if(pl.atkCd<=0){for(const e of enemies){const ed=Math.sqrt((e.x-pl.x)**2+(e.y-e.h/2-(pl.y-pl.h/2))**2);if(ed/T<2.5){e.hp-=ID[held.id].v;e.vx=(e.x>pl.x?1:-1)*6;e.vy=-3;spawnP(e.x,e.y-e.h/2,e.col,5);shake=3}}pl.atkCd=15} mng.on=false;return; } // Food use if(held&&ID[held.id]?.t==='food'){ const fd=ID[held.id].v; if(pl.hp<pl.maxHp||pl.hunger<100){ pl.hp=Math.min(pl.maxHp,pl.hp+(fd.heal||0)); pl.hunger=Math.min(100,pl.hunger+(fd.h||0)); held.c--;if(held.c<=0)inv[hSlot]=null; spawnP(pl.x,pl.y-pl.h/2,'#4ade80',8);ms.l=false; } mng.on=false;return; } // Mining if(!mng.on||mng.tx!==tx||mng.ty!==ty){mng.on=true;mng.tx=tx;mng.ty=ty;mng.pr=0} let pw=0.4;if(held&&ID[held.id]?.t==='pick')pw=ID[held.id].v; mng.pr+=pw; const td=TD[tt]; if(td&&mng.pr>=td.hp){ if(tt===CHEST){const depth=ty-(surfY[Math.min(tx,WW-1)]||35);chestLoot(depth).forEach(it=>addItem(it.id,it.c));spawnP(tx*T+T/2,ty*T+T/2,'#fbbf24',10)} else if(tt===SPAWNER){spawnP(tx*T+T/2,ty*T+T/2,'#ef4444',12)} else if(td.dr!==null)addItem(td.dr,1); world[ty][tx]=AIR;calcSky(tx);spawnP(tx*T+T/2,ty*T+T/2,td.c,6);mng.on=false; } } function handlePlace(){ if(invOpen||titleScr)return; const wx=ms.x+cam.x,wy=ms.y+cam.y; const tx=Math.floor(wx/T),ty=Math.floor(wy/T); const px=pl.x/T,py=(pl.y-pl.h/2)/T; const dist=Math.sqrt((tx+0.5-px)**2+(ty+0.5-py)**2); if(!ms.r||dist>REACH||!inB(tx,ty)||world[ty][tx]!==AIR)return; const pl2=Math.floor((pl.x-pl.w/2)/T),pr2=Math.floor((pl.x+pl.w/2-0.1)/T); const pt2=Math.floor((pl.y-pl.h)/T),pb2=Math.floor((pl.y-0.1)/T); if(tx>=pl2&&tx<=pr2&&ty>=pt2&&ty<=pb2)return; const held=inv[hSlot]; if(held&&held.id<100&&TD[held.id]){world[ty][tx]=held.id;held.c--;if(held.c<=0)inv[hSlot]=null;calcSky(tx);ms.r=false} } /* ===== DAY/NIGHT ===== */ function getDayLight(){return 0.65+0.35*Math.cos((dayT-0.5)*Math.PI*2)} /* ===== UPDATE ===== */ function update(){ if(titleScr)return; dayT+=0.0001;if(dayT>=1)dayT-=1; updPlayer();updMining();handlePlace();updEnemies();updSpawners();updParts(); if(Math.random()<(0.01+DIFF*0.008))spawnEnemy(); const tx=pl.x-C.width/2,ty=pl.y-C.height/2; cam.x+=(tx-cam.x)*0.12;cam.y+=(ty-cam.y)*0.12; cam.x=Math.max(0,Math.min(WW*T-C.width,cam.x)); cam.y=Math.max(0,Math.min(WH*T-C.height,cam.y)); if(shake>0){shake*=0.85;if(shake<0.3)shake=0} // Resize dark canvas if needed if(darkCvs.width!==C.width||darkCvs.height!==C.height){darkCvs.width=C.width;darkCvs.height=C.height} } /* ===== RENDER ===== */ function rr(ctx,x,y,w,h,r){ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.quadraticCurveTo(x+w,y,x+w,y+r);ctx.lineTo(x+w,y+h-r);ctx.quadraticCurveTo(x+w,y+h,x+w-r,y+h);ctx.lineTo(x+r,y+h);ctx.quadraticCurveTo(x,y+h,x,y+h-r);ctx.lineTo(x,y+r);ctx.quadraticCurveTo(x,y,x+r,y);ctx.closePath()} function drawBg(){ const dl=getDayLight(); const top=lC([10,15,20],[45,74,62],dl),bot=lC([15,25,30],[220,155,110],dl); const g=ctx.createLinearGradient(0,0,0,C.height); g.addColorStop(0,`rgb(${top})`);g.addColorStop(1,`rgb(${bot})`); ctx.fillStyle=g;ctx.fillRect(0,0,C.width,C.height); if(dl<0.6){const sa=Math.min(1,(0.6-dl)/0.25);ctx.fillStyle=`rgba(255,255,220,${sa})`;for(let i=0;i<80;i++){const sx=hash(i*3+10)*C.width,sy=hash(i*3+11)*C.height*0.5,ss=hash(i*3+12)*1.8+0.5;ctx.fillRect(sx,sy,ss,ss)}} ctx.fillStyle=`rgba(${lC([12,22,28],[30,55,45],dl)},0.7)`;drawMtns(cam.x*0.08,0.55,90); ctx.fillStyle=`rgba(${lC([18,28,32],[38,65,50],dl)},0.85)`;drawMtns(cam.x*0.18,0.5,65); } function drawMtns(ox,yF,amp){ctx.beginPath();ctx.moveTo(0,C.height);for(let x=0;x<=C.width;x+=3){const wx=x+ox,yy=C.height*yF-amp*n1(wx*0.003)-amp*0.5*n1(wx*0.008+50);ctx.lineTo(x,yy)}ctx.lineTo(C.width,C.height);ctx.closePath();ctx.fill()} function drawPlayer(x,y){ const bob=pl.gr&&Math.abs(pl.vx)>0.1?Math.sin(pl.fr*0.35)*1.5:0; if(pl.invT>0&&Math.floor(pl.invT/4)%2===0)ctx.globalAlpha=0.5; ctx.save();ctx.translate(x,y+bob);if(pl.dir<0)ctx.scale(-1,1); ctx.fillStyle='rgba(0,0,0,0.25)';ctx.fillRect(-6,0,12,2); ctx.fillStyle='#c2410c';ctx.fillRect(-4,-4,3,4);ctx.fillRect(1,-4,3,4); ctx.fillStyle='#ef4444';ctx.beginPath();ctx.ellipse(0,-10,7,6,0,Math.PI,0);ctx.fill(); ctx.fillStyle='#dc2626';ctx.fillRect(-5,-10,10,4); ctx.fillStyle='#fbbf24';ctx.beginPath();ctx.arc(-3,-14,1.8,0,Math.PI*2);ctx.fill(); ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(2,-12,1.5,0,Math.PI*2);ctx.fill(); ctx.fillStyle='#fef3c7';ctx.fillRect(-5,-10,10,5); ctx.fillStyle='#1e293b';ctx.fillRect(-3,-9,2,2);ctx.fillRect(2,-9,2,2); ctx.fillStyle='#fff';ctx.fillRect(-3,-9,1,1);ctx.fillRect(2,-9,1,1); ctx.fillStyle='#92400e';ctx.fillRect(-1,-6,2,1); const held=inv[hSlot]; if(held){ctx.save();ctx.translate(6,-8);ctx.rotate(0.3);if(held.id>=100&&tIc[held.id])ctx.drawImage(tIc[held.id],-10,-10,20,20);else if(tSpr[held.id])ctx.drawImage(tSpr[held.id],-8,-8,16,16);ctx.restore()} ctx.restore();ctx.globalAlpha=1; } function render(){ ctx.clearRect(0,0,C.width,C.height); ctx.save(); if(shake>0)ctx.translate((Math.random()-0.5)*shake,(Math.random()-0.5)*shake); drawBg(); const sx=Math.floor(cam.x/T)-1,ex=Math.ceil((cam.x+C.width)/T)+1; const sy=Math.floor(cam.y/T)-1,ey=Math.ceil((cam.y+C.height)/T)+1; // Collect torches for glow const torches=[]; for(let y=Math.max(0,sy-9);y<=Math.min(WH-1,ey+9);y+=2)for(let x=Math.max(0,sx-9);x<=Math.min(WW-1,ex+9);x+=2){if(world[y]&&world[y][x]===TORCH)torches.push({x,y})} // Draw tiles for(let ty=Math.max(0,sy);ty<=Math.min(WH-1,ey);ty++){ for(let tx=Math.max(0,sx);tx<=Math.min(WW-1,ex);tx++){ const tt=world[ty][tx];if(tt===AIR)continue; const dx=tx*T-cam.x,dy=ty*T-cam.y; ctx.drawImage(tSpr[tt],dx,dy); // Torch ambient glow if(tt===TORCH){ctx.fillStyle='rgba(251,191,36,0.04)';ctx.fillRect(dx-T*3,dy-T*3,T*7,T*7)} // Mining cracks if(mng.on&&mng.tx===tx&&mng.ty===ty&&TD[tt]){ const prog=mng.pr/TD[tt].hp; ctx.strokeStyle=`rgba(0,0,0,${0.4+prog*0.5})`;ctx.lineWidth=1.5; const nc=Math.floor(prog*6)+1; for(let i=0;i<nc;i++){ctx.beginPath();ctx.moveTo(dx+T/2,dy+T/2);const a=(i/nc)*Math.PI*2+0.3;ctx.lineTo(dx+T/2+Math.cos(a)*T*0.55,dy+T/2+Math.sin(a)*T*0.55);ctx.stroke()} } } } // Enemies for(const e of enemies){ const ex2=e.x-cam.x,ey2=e.y-cam.y; if(ex2<-30||ex2>C.width+30||ey2<-30||ey2>C.height+30)continue; const sq=e.gr?1+Math.sin(e.fr*0.15)*0.08:1; ctx.save();ctx.translate(ex2,ey2);ctx.scale(e.dir,1);ctx.scale(1/sq,sq); ctx.fillStyle=e.col;ctx.beginPath();ctx.ellipse(0,-e.h/2,e.w/2,e.h/2,0,0,Math.PI*2);ctx.fill(); ctx.fillStyle='rgba(255,255,255,0.15)';ctx.beginPath();ctx.ellipse(-2,-e.h/2-2,e.w/4,e.h/4,0,0,Math.PI*2);ctx.fill(); ctx.fillStyle='#fff';ctx.fillRect(1,-e.h/2-2,3,3);ctx.fillStyle='#111';ctx.fillRect(2,-e.h/2-1,2,2); if(e.type==='shadow'){ctx.fillStyle='rgba(168,85,247,0.3)';ctx.beginPath();ctx.ellipse(0,-e.h/2,e.w/2+3,e.h/2+3,0,0,Math.PI*2);ctx.fill()} ctx.restore(); if(e.hp<e.mhp){const bw=16,bx=ex2-bw/2,by=ey2-e.h-6;ctx.fillStyle='#333';ctx.fillRect(bx,by,bw,3);ctx.fillStyle='#ef4444';ctx.fillRect(bx,by,bw*(e.hp/e.mhp),3)} } drawPlayer(pl.x-cam.x,pl.y-cam.y); // Particles for(const p of particles){const px2=p.x-cam.x,py2=p.y-cam.y;ctx.globalAlpha=p.life/p.ml;ctx.fillStyle=p.col;ctx.fillRect(px2-p.sz/2,py2-p.sz/2,p.sz,p.sz)} ctx.globalAlpha=1; // LIGHTING - dark canvas approach const dl=getDayLight(); darkCtx2.fillStyle='rgba(0,0,0,0.93)';darkCtx2.fillRect(0,0,C.width,C.height); darkCtx2.globalCompositeOperation='destination-out'; // Sky light per column darkCtx2.fillStyle=`rgba(0,0,0,${dl})`; for(let tx=Math.max(0,sx);tx<=Math.min(WW-1,ex);tx++){ let startTy=-1; for(let ty=Math.max(0,sy);ty<=Math.min(WH-1,ey);ty++){ if(skyExp[ty][tx]){if(startTy===-1)startTy=ty} else{if(startTy!==-1){const dx=tx*T-cam.x,dy=startTy*T-cam.y,h=(ty-startTy)*T;darkCtx2.fillRect(dx,dy,T,h);startTy=-1}} } if(startTy!==-1){const dx=tx*T-cam.x,dy=startTy*T-cam.y,h=(Math.min(WH-1,ey)-startTy+1)*T;darkCtx2.fillRect(dx,dy,T,h)} } // Torch light const tr=T*8; for(const t of torches){ const dx=(t.x+0.5)*T-cam.x,dy=(t.y+0.5)*T-cam.y; darkCtx2.drawImage(torchLCvs,dx-tr,dy-tr); } darkCtx2.globalCompositeOperation='source-over'; ctx.drawImage(darkCvs,0,0); // Cursor item in inventory if(invOpen&&cursor){ const isT=cursor.id>=100; if(isT)ctx.drawImage(tIc[cursor.id],ms.x-14,ms.y-14); else if(tSpr[cursor.id])ctx.drawImage(tSpr[cursor.id],ms.x-12,ms.y-12,24,24); if(cursor.c>1){ctx.fillStyle='#fff';ctx.font='8px "Press Start 2P"';ctx.fillText(cursor.c,ms.x+8,ms.y+12)} } ctx.restore(); drawHUD(); if(invOpen)drawInvUI(); if(titleScr)drawTitle(); } /* ===== HUD ===== */ function drawHUD(){ const slotS=40,gap=2,hbW=10*(slotS+gap)-gap; const hbX=(C.width-hbW)/2,hbY=C.height-slotS-10; ctx.fillStyle='rgba(0,0,0,0.6)';rr(ctx,hbX-4,hbY-4,hbW+8,slotS+8,6);ctx.fill(); ctx.strokeStyle='rgba(255,255,255,0.15)';ctx.lineWidth=1;rr(ctx,hbX-4,hbY-4,hbW+8,slotS+8,6);ctx.stroke(); for(let i=0;i<10;i++){ const sx2=hbX+i*(slotS+gap),sy2=hbY; ctx.fillStyle=i===hSlot?'rgba(255,255,255,0.15)':'rgba(30,30,30,0.7)';ctx.fillRect(sx2,sy2,slotS,slotS); ctx.strokeStyle=i===hSlot?'rgba(255,200,50,0.7)':'rgba(255,255,255,0.1)';ctx.strokeRect(sx2,sy2,slotS,slotS); const item=inv[i];if(item){ if(item.id>=100&&tIc[item.id])ctx.drawImage(tIc[item.id],sx2+6,sy2+6,28,28); else if(tSpr[item.id])ctx.drawImage(tSpr[item.id],sx2+8,sy2+8,24,24); if(item.c>1){ctx.fillStyle='#fff';ctx.font='7px "Press Start 2P"';ctx.fillText(item.c,sx2+2,sy2+slotS-4)} } ctx.fillStyle='rgba(255,255,255,0.3)';ctx.font='6px "Press Start 2P"';ctx.fillText(i===9?'0':(i+1)+'',sx2+2,sy2+10); } // Tooltip for hovered hotbar slot if(!invOpen){ for(let i=0;i<10;i++){ const sx2=hbX+i*(slotS+gap),sy2=hbY; if(ms.x>=sx2&&ms.x<sx2+slotS&&ms.y>=sy2&&ms.y<sy2+slotS&&inv[i]){ const nm=itemName(inv[i].id); ctx.fillStyle='rgba(0,0,0,0.8)';const tw=ctx.measureText(nm).width+12;ctx.fillRect(ms.x+10,ms.y-20,tw,18); ctx.fillStyle='#fbbf24';ctx.font='7px "Press Start 2P"';ctx.fillText(nm,ms.x+16,ms.y-8); break; } } } // HP bar const hpW=150,hpH=14,hpX=10,hpY=10; ctx.fillStyle='rgba(0,0,0,0.5)';rr(ctx,hpX-2,hpY-2,hpW+4,hpH+4,4);ctx.fill(); ctx.fillStyle='#1c1917';ctx.fillRect(hpX,hpY,hpW,hpH); const hpP=Math.max(0,pl.hp/pl.maxHp); ctx.fillStyle=hpP>0.5?'#22c55e':hpP>0.25?'#eab308':'#ef4444'; rr(ctx,hpX,hpY,hpW*hpP,hpH,3);ctx.fill(); ctx.fillStyle='#fff';ctx.font='7px "Press Start 2P"';ctx.fillText(Math.ceil(pl.hp)+'/'+pl.maxHp,hpX+4,hpY+10); // Hunger bar const huW=120,huH=10,huX=10,huY=hpY+hpH+6; ctx.fillStyle='rgba(0,0,0,0.5)';rr(ctx,huX-2,huY-2,huW+4,huH+4,4);ctx.fill(); ctx.fillStyle='#1c1917';ctx.fillRect(huX,huY,huW,huH); const huP=pl.hunger/100; ctx.fillStyle=huP>0.5?'#d97706':huP>0.25?'#b45309':'#92400e'; rr(ctx,huX,huY,huW*huP,huH,3);ctx.fill(); ctx.fillStyle='rgba(255,255,255,0.7)';ctx.font='6px "Press Start 2P"';ctx.fillText('HUNGER',huX+2,huY+8); // Day/night const dl=getDayLight(); ctx.fillStyle=dl>0.7?'#fbbf24':'#94a3b8';ctx.font='7px "Press Start 2P"'; ctx.fillText(dl>0.7?'DAY':'NIGHT',C.width-55,18); // Depth const depth=Math.max(0,Math.floor(pl.y/T)-surfY[Math.min(Math.floor(pl.x/T),WW-1)]); if(depth>0){ctx.fillStyle='rgba(255,255,255,0.5)';ctx.fillText('Depth: '+depth,C.width-100,36)} // Difficulty label ctx.fillStyle=['#4ade80','#fbbf24','#ef4444'][DIFF];ctx.font='6px "Press Start 2P"'; ctx.fillText(['Peaceful','Normal','Hard'][DIFF],C.width-70,C.height-slotS-20); // Near bench if(nearBench()){ctx.fillStyle='#fbbf24';ctx.font='7px "Press Start 2P"';ctx.textAlign='center';ctx.fillText('Near Workbench',C.width/2,20);ctx.textAlign='left'} // Controls ctx.fillStyle='rgba(255,255,255,0.2)';ctx.font='6px "Press Start 2P"'; ctx.fillText('WASD Move | LMB Mine/Attack/Eat | RMB Place | E Inventory | Scroll Hotbar',10,C.height-slotS-20); } /* ===== INVENTORY UI ===== */ function drawInvUI(){ const slotS=40,gap=3,cols=10,rows=3; const panW=cols*(slotS+gap)-gap,panH=rows*(slotS+gap)-gap+30; const panX=(C.width-panW)/2,panY=(C.height-panH)/2-40; ctx.fillStyle='rgba(10,10,10,0.9)';rr(ctx,panX-8,panY-28,panW+16,panH+36,8);ctx.fill(); ctx.strokeStyle='rgba(255,200,50,0.3)';ctx.lineWidth=1;rr(ctx,panX-8,panY-28,panW+16,panH+36,8);ctx.stroke(); ctx.fillStyle='#fbbf24';ctx.font='9px "Press Start 2P"';ctx.fillText('Inventory',panX,panY-12); for(let r=0;r<rows;r++)for(let c=0;c<cols;c++){ const i=r*cols+c,sx2=panX+c*(slotS+gap),sy2=panY+r*(slotS+gap)+18; ctx.fillStyle=i===hSlot&&r===0?'rgba(255,200,50,0.15)':'rgba(30,30,30,0.8)';ctx.fillRect(sx2,sy2,slotS,slotS); ctx.strokeStyle=i===hSlot&&r===0?'rgba(255,200,50,0.5)':'rgba(255,255,255,0.08)';ctx.strokeRect(sx2,sy2,slotS,slotS); const item=inv[i];if(item){ if(item.id>=100&&tIc[item.id])ctx.drawImage(tIc[item.id],sx2+6,sy2+6,28,28); else if(tSpr[item.id])ctx.drawImage(tSpr[item.id],sx2+8,sy2+8,24,24); if(item.c>1){ctx.fillStyle='#fff';ctx.font='7px "Press Start 2P"';ctx.fillText(item.c,sx2+2,sy2+slotS-4)} } } // Crafting const cpY=panY+panH+12,cpX=panX,cpW=panW; const crH=recipes.length*34+34; ctx.fillStyle='rgba(10,10,10,0.9)';rr(ctx,cpX-8,cpY-24,cpW+16,crH,8);ctx.fill(); ctx.strokeStyle='rgba(100,200,150,0.3)';rr(ctx,cpX-8,cpY-24,cpW+16,crH,8);ctx.stroke(); ctx.fillStyle='#4ade80';ctx.font='9px "Press Start 2P"';ctx.fillText('Crafting',cpX,cpY-8); const bn=nearBench(); for(let ri=0;ri<recipes.length;ri++){ const r=recipes[ri],ry=cpY+ri*34;const cc=canCraft(r); ctx.fillStyle=cc?'rgba(34,197,94,0.12)':'rgba(50,50,50,0.5)';ctx.fillRect(cpX,ry,cpW-4,30); ctx.strokeStyle=cc?'rgba(34,197,94,0.4)':'rgba(255,255,255,0.05)';ctx.strokeRect(cpX,ry,cpW-4,30); const rIsT=r.r>=100; if(rIsT&&tIc[r.r])ctx.drawImage(tIc[r.r],cpX+4,ry+3,24,24); else if(tSpr[r.r])ctx.drawImage(tSpr[r.r],cpX+4,ry+3,24,24); ctx.fillStyle=cc?'#4ade80':'#555';ctx.font='7px "Press Start 2P"';ctx.fillText(r.n+(r.c>1?' x'+r.c:''),cpX+32,ry+13); ctx.font='6px "Press Start 2P"';let ix=cpX+32; for(const it of r.i){ctx.fillStyle=cntItem(it.id)>=it.c?'rgba(255,255,255,0.5)':'#ef4444';ctx.fillText(itemName(it.id)+':'+cntItem(it.id)+'/'+it.c,ix,ry+25);ix+=ctx.measureText(itemName(it.id)+':'+cntItem(it.id)+'/'+it.c).width+10} if(r.b){ctx.fillStyle=bn?'#4ade80':'#ef4444';ctx.font='6px "Press Start 2P"';ctx.fillText(bn?'[Bench OK]':'[Need Bench]',cpX+cpW-90,ry+13)} } } /* ===== TITLE SCREEN ===== */ function drawTitle(){ ctx.fillStyle='rgba(5,8,12,0.92)';ctx.fillRect(0,0,C.width,C.height); // Background particles if(titleParts.length<50)titleParts.push({x:Math.random()*C.width,y:C.height+10,vy:-0.3-Math.random()*0.7,sz:1+Math.random()*2,life:300+Math.random()*200}); for(let i=titleParts.length-1;i>=0;i--){const p=titleParts[i];p.y+=p.vy;p.life--;if(p.life<=0||p.y<-10){titleParts.splice(i,1);continue}const a=Math.min(1,p.life/50)*0.4;ctx.fillStyle=`rgba(34,211,238,${a})`;ctx.fillRect(p.x,p.y,p.sz,p.sz)} // Title const pulse=1+Math.sin(Date.now()*0.003)*0.02; ctx.save();ctx.translate(C.width/2,C.height*0.15);ctx.scale(pulse,pulse); ctx.fillStyle='#22d3ee';ctx.font='32px "Press Start 2P"';ctx.textAlign='center';ctx.fillText('CRYSTAL',0,-22); ctx.fillStyle='#ef4444';ctx.fillText('DEPTHS',0,22);ctx.restore();ctx.textAlign='left'; ctx.fillStyle='rgba(255,255,255,0.4)';ctx.font='8px "Press Start 2P"';ctx.textAlign='center'; ctx.fillText('A Sandbox Survival Adventure',C.width/2,C.height*0.15+55);ctx.textAlign='left'; // Mushroom character ctx.save();ctx.translate(C.width/2,C.height*0.33);ctx.scale(3.5,3.5); ctx.fillStyle='#c2410c';ctx.fillRect(-4,-4,3,4);ctx.fillRect(1,-4,3,4); ctx.fillStyle='#ef4444';ctx.beginPath();ctx.ellipse(0,-10,7,6,0,Math.PI,0);ctx.fill(); ctx.fillStyle='#dc2626';ctx.fillRect(-5,-10,10,4); ctx.fillStyle='#fbbf24';ctx.beginPath();ctx.arc(-3,-14,1.8,0,Math.PI*2);ctx.fill(); ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(2,-12,1.5,0,Math.PI*2);ctx.fill(); ctx.fillStyle='#fef3c7';ctx.fillRect(-5,-10,10,5); ctx.fillStyle='#1e293b';ctx.fillRect(-3,-9,2,2);ctx.fillRect(2,-9,2,2); ctx.fillStyle='#fff';ctx.fillRect(-3,-9,1,1);ctx.fillRect(2,-9,1,1); ctx.fillStyle='#92400e';ctx.fillRect(-1,-6,2,1); ctx.restore(); // Parameter selection const baseY=C.height*0.48; ctx.fillStyle='rgba(255,255,255,0.6)';ctx.font='8px "Press Start 2P"';ctx.textAlign='center'; ctx.fillText('WORLD SIZE',C.width/2,baseY); const sizes=['Small','Medium','Large'],sizeVals=[150,250,350]; for(let i=0;i<3;i++){ const bx=C.width/2-150+i*110,by=baseY+8,bw=100,bh=28; const hov=ms.x>=bx&&ms.x<bx+bw&&ms.y>=by&&ms.y<by+bh; const sel=startSel.sz===i; ctx.fillStyle=sel?'rgba(34,211,238,0.3)':hov?'rgba(255,255,255,0.1)':'rgba(30,30,30,0.7)'; ctx.fillRect(bx,by,bw,bh);ctx.strokeStyle=sel?'#22d3ee':'rgba(255,255,255,0.2)';ctx.strokeRect(bx,by,bw,bh); ctx.fillStyle=sel?'#22d3ee':'#ccc';ctx.font='7px "Press Start 2P"';ctx.fillText(sizes[i]+' ('+sizeVals]+'x'+Math.floor(sizeVals[i]*0.48)+')',bx+bw/2,by+18); } const dfY=baseY+55; ctx.fillStyle='rgba(255,255,255,0.6)';ctx.font='8px "Press Start 2P"';ctx.fillText('DIFFICULTY',C.width/2,dfY); const diffs=['Peaceful','Normal','Hard'],dfCols=['#4ade80','#fbbf24','#ef4444']; for(let i=0;i<3;i++){ const bx=C.width/2-150+i*110,by=dfY+8,bw=100,bh=28; const hov=ms.x>=bx&&ms.x<bx+bw&&ms.y>=by&&ms.y<by+bh; const sel=startSel.df===i; ctx.fillStyle=sel?dfCols[i]+'44':hov?'rgba(255,255,255,0.1)':'rgba(30,30,30,0.7)'; ctx.fillRect(bx,by,bw,bh);ctx.strokeStyle=sel?dfCols[i]:'rgba(255,255,255,0.2)';ctx.strokeRect(bx,by,bw,bh); ctx.fillStyle=sel?dfCols[i]:'#ccc';ctx.font='7px "Press Start 2P"';ctx.fillText(diffs[i],bx+bw/2,by+18); } const sdY=dfY+55; ctx.fillStyle='rgba(255,255,255,0.6)';ctx.font='8px "Press Start 2P"';ctx.fillText('SEED',C.width/2,sdY); const rbx=C.width/2-30,rby=sdY+6,rbw=60,rbh=24; const rHov=ms.x>=rbx&&ms.x<rbx+rbw&&ms.y>=rby&&ms.y<rby+rbh; ctx.fillStyle=rHov?'rgba(255,255,255,0.15)':'rgba(30,30,30,0.7)';ctx.fillRect(rbx,rby,rbw,rbh); ctx.strokeStyle='rgba(255,255,255,0.2)';ctx.strokeRect(rbx,rby,rbw,rbh); ctx.fillStyle='#ccc';ctx.font='6px "Press Start 2P"';ctx.fillText('Random',C.width/2,rby+16); ctx.fillStyle='#fbbf24';ctx.font='10px "Press Start 2P"';ctx.fillText(startSel.seed,C.width/2,sdY+48); // Start button const stx=C.width/2-100,sty=sdY+65,stw=200,sth=40; const stHov=ms.x>=stx&&ms.x<stx+stw&&ms.y>=sty&&ms.y<sty+sth; ctx.fillStyle=stHov?'rgba(239,68,68,0.4)':'rgba(239,68,68,0.2)';ctx.fillRect(stx,sty,stw,sth); ctx.strokeStyle='#ef4444';ctx.lineWidth=2;ctx.strokeRect(stx,sty,stw,sth);ctx.lineWidth=1; ctx.fillStyle='#fff';ctx.font='11px "Press Start 2P"';ctx.fillText('START GAME',C.width/2,sty+26); ctx.textAlign='left'; // Controls ctx.fillStyle='rgba(255,255,255,0.25)';ctx.font='6px "Press Start 2P"';ctx.textAlign='center'; ctx.fillText('WASD: Move & Jump | Mouse: Mine & Place | E: Inventory | Scroll: Hotbar',C.width/2,C.height-30); ctx.fillText('Explore caves, find dungeons, mine ores, craft gear, survive!',C.width/2,C.height-16); ctx.textAlign='left'; } /* ===== INPUT ===== */ addEventListener('keydown',e=>{ keys[e.code]=true; if(e.code==='KeyE'&&!titleScr){ invOpen=!invOpen; if(!invOpen){if(curFrom>=0&&cursor){inv[curFrom]=cursor;cursor=null;curFrom=-1}} cursor=null;curFrom=-1; } if(e.code>='Digit1'&&e.code<='Digit9')hSlot=parseInt(e.code[5])-1; if(e.code==='Digit0')hSlot=9; if(e.code==='Escape'&&invOpen){invOpen=false;if(curFrom>=0&&cursor){inv[curFrom]=cursor;cursor=null;curFrom=-1}cursor=null;curFrom=-1} e.preventDefault(); }); addEventListener('keyup',e=>{keys[e.code]=false}); addEventListener('mousemove',e=>{ms.x=e.clientX;ms.y=e.clientY}); addEventListener('mousedown',e=>{ ms.x=e.clientX;ms.y=e.clientY; if(titleScr){ // Check button clicks const baseY=C.height*0.48; // Size buttons for(let i=0;i<3;i++){const bx=C.width/2-150+i*110,by=baseY+8;if(ms.x>=bx&&ms.x<bx+100&&ms.y>=by&&ms.y<by+28)startSel.sz=i} // Difficulty buttons const dfY=baseY+55; for(let i=0;i<3;i++){const bx=C.width/2-150+i*110,by=dfY+8;if(ms.x>=bx&&ms.x<bx+100&&ms.y>=by&&ms.y<by+28)startSel.df=i} // Random seed const sdY=dfY+55;const rbx=C.width/2-30,rby=sdY+6; if(ms.x>=rbx&&ms.x<rbx+60&&ms.y>=rby&&ms.y<rby+24)startSel.seed=Math.floor(Math.random()*99999); // Start button const sty=sdY+65;if(ms.x>=C.width/2-100&&ms.x<C.width/2+100&&ms.y>=sty&&ms.y<sty+40)startGame(); return; } if(e.button===0)ms.l=true;if(e.button===2)ms.r=true; // Inventory click if(invOpen&&e.button===0){ const slotS=40,gap=3,cols=10,rows=3; const panW=cols*(slotS+gap)-gap,panH=rows*(slotS+gap)-gap+30; const panX=(C.width-panW)/2,panY=(C.height-panH)/2-40; for(let r=0;r<rows;r++)for(let c=0;c<cols;c++){ const i=r*cols+c,sx2=panX+c*(slotS+gap),sy2=panY+r*(slotS+gap)+18; if(ms.x>=sx2&&ms.x<sx2+slotS&&ms.y>=sy2&&ms.y<sy2+slotS){ if(cursor){if(!inv[i]){inv[i]=cursor;cursor=null;curFrom=-1}else if(inv[i].id===cursor.id){inv[i].c+=cursor.c;cursor=null;curFrom=-1}else{const tmp=inv[i];inv[i]=cursor;cursor=tmp;curFrom=i}} else if(inv[i]){cursor=inv[i];inv[i]=null;curFrom=i} } } // Crafting click const cpY=panY+panH+12; for(let ri=0;ri<recipes.length;ri++){ const ry=cpY+ri*34; if(ms.x>=panX&&ms.x<panX+panW-4&&ms.y>=ry&&ms.y<ry+30){doCraft(recipes[ri]);break} } } }); addEventListener('mouseup',e=>{if(e.button===0)ms.l=false;if(e.button===2)ms.r=false}); addEventListener('wheel',e=>{hSlot=(hSlot+(e.deltaY>0?1:-1)+10)%10;e.preventDefault()},{passive:false}); addEventListener('contextmenu',e=>e.preventDefault()); /* ===== START GAME ===== */ function startGame(){ const sizeVals=[150,250,350]; WW=sizeVals[startSel.sz];WH=Math.floor(WW*0.48); SEED=startSel.seed;wSeed=SEED;DIFF=startSel.df; const dm=[0.8,1,1.2][DIFF]; GRAV=0.42;JUMP_V=-7.5;SPD=2.8;REACH=5
Landing Page
This ad does not have a landing page available
Network Timeline
Performance Summary

2

Requests

2

Domains

45KB

Transfer Size

46KB

Content Size

156.0ms

Dom Content Loaded

112.0ms

First Paint

156.0ms

Load Time
Domain Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...
Timings (ms)
Loading...
Total Time
Loading...
Content Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...