diff --git a/game.js b/game.js index 05458ae..40e2795 100644 --- a/game.js +++ b/game.js @@ -3467,41 +3467,104 @@ ctx.restore(); - // lighting overlay at night + // lighting overlay at night (Minecraft-style: offscreen lightmap, wall occlusion, warm flicker) if(night){ - // Рисуем полупрозрачный тёмный оверлей - ctx.save(); - ctx.fillStyle = 'rgba(0,0,0,0.50)'; - ctx.fillRect(0, 0, W, H); - ctx.restore(); + // 1) Рисуем тёмный оверлей на offscreen canvas + lightC.width = W*dpr; + lightC.height = H*dpr; + lightCtx.setTransform(dpr,0,0,dpr,0,0); + lightCtx.fillStyle = 'rgba(0,0,12,0.82)'; + lightCtx.fillRect(0,0,W,H); - // Освещение от факелов и костров - ctx.save(); - ctx.globalCompositeOperation = 'lighter'; + // 2) Вырезаем «окна света» через destination-out — свет не проходит сквозь стены + lightCtx.globalCompositeOperation = 'destination-out'; - // Функция для рисования света - function drawLight(x, y, radius, intensity) { - const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius); - gradient.addColorStop(0, `rgba(255, 255, 200, ${intensity})`); - gradient.addColorStop(1, `rgba(255, 255, 200, 0)`); - ctx.fillStyle = gradient; - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI * 2); - ctx.fill(); + // Функция: рисуем мягкий луч света с затуханием за стенами + function castLight(sx, sy, radius) { + const flick = 0.88 + Math.sin(now/80 + sx*0.01)*0.06 + Math.sin(now/130 + sy*0.02)*0.06; + const r = radius * flick; + // 12 лучей — достаточно для мягкого круга + const steps = 12; + // Собираем дистанции до стен по лучам + const dists = new Float32Array(steps); + for(let i=0; i TILE*0.3){ + maxDist = step; + break; + } + } + dists[i] = maxDist; + } + // Рисуем сглаженный полигон по dists + const cx = sx-camX, cy = sy-camY; + // Центр: яркая точка + const maxR = Math.max(...dists); + const grad = lightCtx.createRadialGradient(cx, cy, 0, cx, cy, maxR); + grad.addColorStop(0, 'rgba(255,255,255,1)'); + grad.addColorStop(0.5, 'rgba(255,255,255,0.65)'); + grad.addColorStop(1, 'rgba(255,255,255,0)'); + lightCtx.fillStyle = grad; + // Рисуем shape по dists (звездоподобный полигон) + lightCtx.beginPath(); + for(let i=0; i<=steps; i++){ + const idx = i % steps; + const nextIdx = (i+1) % steps; + const avgD = (dists[idx] + dists[nextIdx]) / 2; + const angle = (idx/steps) * Math.PI * 2; + const px = cx + Math.cos(angle) * dists[idx]; + const py = cy + Math.sin(angle) * dists[idx]; + if(i===0) lightCtx.moveTo(px, py); + else lightCtx.lineTo(px, py); + } + lightCtx.closePath(); + lightCtx.fill(); } - // Освещение от факелов (1/3 яркости) + // Источники света for(const b of blocks){ if(b.dead) continue; - if(b.gx < minGX || b.gx > maxGX || b.gy < minGY || b.gy > maxGY) continue; + if(b.gx < minGX-5 || b.gx > maxGX+5 || b.gy < minGY-5 || b.gy > maxGY+5) continue; const def = BLOCKS[b.t]; if(def.lightRadius){ - const wx = b.gx*TILE + TILE/2 - camX; - const wy = b.gy*TILE + TILE/2 - camY; - drawLight(wx, wy, def.lightRadius, 0.33); + castLight(b.gx*TILE + TILE/2, b.gy*TILE + TILE/2, def.lightRadius); } } + // 3) Накладываем lightmap на основной canvas + lightCtx.globalCompositeOperation = 'source-over'; + ctx.drawImage(lightC, 0, 0, W, H); + + // 4) Тёплый оверлей от источников света (additive, мягкий) + ctx.save(); + ctx.globalCompositeOperation = 'lighter'; + for(const b of blocks){ + if(b.dead) continue; + if(b.gx < minGX-3 || b.gx > maxGX+3 || b.gy < minGY-3 || b.gy > maxGY+3) continue; + const def = BLOCKS[b.t]; + if(def.lightRadius){ + const flick = 0.7 + Math.sin(now/90 + b.gx*3.7)*0.15 + Math.sin(now/140 + b.gy*2.3)*0.15; + const wx = b.gx*TILE + TILE/2 - camX; + const wy = b.gy*TILE + TILE/2 - camY; + const r = def.lightRadius * 0.6 * flick; + const grad = ctx.createRadialGradient(wx,wy, 0, wx,wy, r); + grad.addColorStop(0, `rgba(255,180,80,${0.12*flick})`); + grad.addColorStop(0.5, `rgba(255,140,40,${0.06*flick})`); + grad.addColorStop(1, 'rgba(255,100,20,0)'); + ctx.fillStyle = grad; + ctx.beginPath(); + ctx.arc(wx, wy, r, 0, Math.PI*2); + ctx.fill(); + } + } ctx.restore(); } diff --git a/index.html b/index.html index 66e9300..9a6d3e8 100644 --- a/index.html +++ b/index.html @@ -92,6 +92,6 @@ - +