// ═══ renderer-shop-overlay.js ═══ // ============================================================ // RENDERER SHOP — canvas armory / command overlays // ============================================================ const _SH_HDR_H = 56; const _SH_TAB_H = 38; const _SH_BODY_Y = _SH_HDR_H + _SH_TAB_H; // 94 — armory body start (below tabs) const _SH_PAD = 24; const _SH_UPG_W = 130; const _SH_UPG_H = 78; const _SH_ARR_W = 26; let _shopScrollY = 0; let _shopScrollMax = 0; const _shopRightClick = []; function _shopWrapText(text, maxW, maxLines) { if (!text) return []; const words = text.split(' '); const lines = []; let cur = ''; for (const w of words) { const test = cur ? cur + ' ' + w : w; if (ctx.measureText(test).width <= maxW) { cur = test; } else { if (cur) lines.push(cur); cur = w; if (lines.length >= maxLines) break; } } if (cur && lines.length < maxLines) lines.push(cur); return lines; } function _shopUpgNode(sx, screenY, upg, bought, locked, cantAfford, onBuy, onRefund, lockLabel = null) { const hov = !bought && !locked && !cantAfford && isHovered(sx, screenY, _SH_UPG_W, _SH_UPG_H); const border = bought ? '#1a5030' : locked ? '#0e1e28' : cantAfford ? '#1a2030' : hov ? '#ffd700' : '#1a3048'; ctx.save(); ctx.globalAlpha = bought ? 0.75 : locked ? 0.32 : cantAfford ? 0.55 : 1; ctx.fillStyle = bought ? '#071410' : '#060e18'; ctx.strokeStyle = border; ctx.lineWidth = 1; ctx.fillRect(sx, screenY, _SH_UPG_W, _SH_UPG_H); ctx.strokeRect(sx, screenY, _SH_UPG_W, _SH_UPG_H); ctx.save(); ctx.beginPath(); ctx.rect(sx + 5, screenY + 5, _SH_UPG_W - 10, 16); ctx.clip(); ctx.font = '11px Orbitron, "Share Tech Mono", monospace'; ctx.letterSpacing = '0.5px'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; ctx.fillStyle = bought ? '#00ff88' : '#b8d8e8'; ctx.fillText(upg.label, sx + 5, screenY + 5); ctx.restore(); ctx.save(); ctx.beginPath(); ctx.rect(sx + 5, screenY + 22, _SH_UPG_W - 10, 30); ctx.clip(); ctx.font = '10px "Share Tech Mono", monospace'; ctx.letterSpacing = '0px'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; ctx.fillStyle = '#3a6080'; const lines = _shopWrapText(upg.desc, _SH_UPG_W - 12, 2); for (let li = 0; li < lines.length; li++) ctx.fillText(lines[li], sx + 5, screenY + 22 + li * 14); ctx.restore(); ctx.font = '10px "Share Tech Mono", monospace'; ctx.letterSpacing = '0px'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; if (bought) { ctx.fillStyle = '#1a4030'; ctx.fillText('✓ right-click refund', sx + 5, screenY + 58); } else if (locked && lockLabel) { ctx.fillStyle = '#ff6b3566'; ctx.fillText('Req: ' + lockLabel, sx + 5, screenY + 58); } else if (locked) { ctx.fillStyle = '#1a2838'; ctx.fillText('🔒 locked', sx + 5, screenY + 58); } else { ctx.fillStyle = cantAfford ? '#ff3355' : '#ffd700'; ctx.fillText(upg.cost + '¢', sx + 5, screenY + 58); } ctx.restore(); if (onBuy) addHitRegion(sx, screenY, _SH_UPG_W, _SH_UPG_H, onBuy); if (onRefund) _shopRightClick.push({ x: sx, y: screenY, w: _SH_UPG_W, h: _SH_UPG_H, action: onRefund }); } // ── Shared header for both overlays ─────────────────────────── function _drawOverlayHeader(W, title, closeFn) { ctx.fillStyle = '#040c14'; ctx.fillRect(0, 0, W, _SH_HDR_H); ctx.strokeStyle = '#1a3048'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(0, _SH_HDR_H); ctx.lineTo(W, _SH_HDR_H); ctx.stroke(); ctx.font = '900 15px Orbitron, "Share Tech Mono", monospace'; ctx.letterSpacing = '6px'; ctx.textAlign = 'left'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#00d4ff'; ctx.shadowColor = '#00d4ff44'; ctx.shadowBlur = 14; ctx.fillText(title, _SH_PAD, _SH_HDR_H / 2); ctx.shadowBlur = 0; ctx.letterSpacing = '0px'; ctx.font = '18px Orbitron, "Share Tech Mono", monospace'; ctx.textAlign = 'center'; ctx.fillStyle = '#ffd700'; ctx.fillText('💰 ' + G.credits + '¢', W / 2, _SH_HDR_H / 2); const CBW = 160, CBH = 32; const CBX = W - _SH_PAD - CBW, CBY = (_SH_HDR_H - CBH) / 2; const cbHov = isHovered(CBX, CBY, CBW, CBH); ctx.fillStyle = cbHov ? '#3d0808' : 'transparent'; ctx.strokeStyle = '#ff3355'; ctx.lineWidth = 1; ctx.fillRect(CBX, CBY, CBW, CBH); ctx.strokeRect(CBX, CBY, CBW, CBH); ctx.font = '11px Orbitron, monospace'; ctx.letterSpacing = '2px'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#ff3355'; ctx.fillText('✕ CLOSE [Esc]', CBX + CBW / 2, CBY + CBH / 2); ctx.letterSpacing = '0px'; addHitRegion(CBX, CBY, CBW, CBH, closeFn); } // ── ARMORY OVERLAY — weapon buying + per-weapon upgrade trees ── function drawArmoryOverlay() { if (!G.armoryOpen) return; _shopRightClick.length = 0; const W = canvas.width, H = canvas.height; const BODY_H = H - _SH_BODY_Y; ctx.fillStyle = 'rgba(2,8,14,0.97)'; ctx.fillRect(0, 0, W, H); ctx.save(); _drawOverlayHeader(W, 'ARMORY', closeArmory); ctx.save(); ctx.beginPath(); ctx.rect(0, _SH_BODY_Y, W, BODY_H); ctx.clip(); const bodyCX = _SH_PAD, bodyCW = W - _SH_PAD * 2; let yOff = _SH_PAD; yOff = _shopDrawBuyContent(yOff, bodyCX, bodyCW, H); _shopScrollMax = Math.max(0, yOff - BODY_H + _SH_PAD); ctx.restore(); ctx.restore(); } // ── COMMAND OVERLAY — tower/base upgrades ───────────────────── function drawCommandOverlay() { if (!G.commandOpen) return; _shopRightClick.length = 0; const W = canvas.width, H = canvas.height; const BODY_H = H - _SH_BODY_Y; ctx.fillStyle = 'rgba(2,8,14,0.97)'; ctx.fillRect(0, 0, W, H); ctx.save(); _drawOverlayHeader(W, 'COMMAND', closeCommand); ctx.save(); ctx.beginPath(); ctx.rect(0, _SH_BODY_Y, W, BODY_H); ctx.clip(); const bodyCX = _SH_PAD, bodyCW = W - _SH_PAD * 2; let yOff = _SH_PAD; yOff = _shopDrawTowerContent(yOff, bodyCX, bodyCW, H); _shopScrollMax = Math.max(0, yOff - BODY_H + _SH_PAD); ctx.restore(); ctx.restore(); }