// ═══ renderer-overlays.js ═══ // ============================================================ // RENDERER OVERLAYS — game over, pause, mount drag UI // ============================================================ // ── GAME OVER / BANKRUPT OVERLAY ───────────────────────────── function drawGameOverPanel() { const W = canvas.width, H = canvas.height; // Game over takes over hit detection — clear HUD regions clearHitRegions(); // Full-screen dark tint ctx.fillStyle = 'rgba(0,0,0,0.88)'; ctx.fillRect(0, 0, W, H); const BW = 460, BH = 270; const BX = (W - BW) / 2, BY = (H - BH) / 2; ctx.fillStyle = '#060e16'; ctx.strokeStyle = '#1a3048'; ctx.lineWidth = 1; ctx.beginPath(); ctx.rect(BX, BY, BW, BH); ctx.fill(); ctx.stroke(); const isBankrupt = G.isBankrupt; const titleColor = isBankrupt ? '#ffd700' : '#ff3355'; const title = isBankrupt ? 'BANKRUPT' : 'TOWER FALLEN'; const subText = isBankrupt ? 'You ran out of credits with no way to recover.' : 'The defense has been breached.'; ctx.save(); ctx.textAlign = 'center'; ctx.textBaseline = 'top'; ctx.font = '900 30px Orbitron, "Share Tech Mono", monospace'; ctx.fillStyle = titleColor; ctx.shadowColor = titleColor; ctx.shadowBlur = 24; ctx.fillText(title, W / 2, BY + 28); ctx.shadowBlur = 0; ctx.font = '14px Orbitron, "Share Tech Mono", monospace'; ctx.fillStyle = '#ffd700'; ctx.fillText(`Score: ${G.score} — Kills: ${G.totalKills}`, W / 2, BY + 78); ctx.font = '12px "Share Tech Mono", monospace'; if (G._isNewBest) { ctx.fillStyle = '#ffd700'; ctx.fillText('★ NEW BEST!', W / 2, BY + 108); } else if (_best) { ctx.fillStyle = '#3a6080'; ctx.fillText(`Best: ${_best.score} — ${_best.kills} kills`, W / 2, BY + 108); } ctx.font = '11px "Share Tech Mono", monospace'; ctx.fillStyle = '#3a6080'; ctx.fillText(subText, W / 2, BY + 134); // RESTART button const RBW = 220, RBH = 44; const RBX = W / 2 - RBW / 2; const RBY = BY + 192; const restHov = isHovered(RBX, RBY, RBW, RBH); ctx.fillStyle = restHov ? '#00d4ff' : 'transparent'; ctx.strokeStyle = '#00d4ff'; ctx.lineWidth = 1; ctx.beginPath(); ctx.rect(RBX, RBY, RBW, RBH); ctx.fill(); ctx.stroke(); if (restHov) { ctx.shadowColor = 'rgba(0,212,255,0.35)'; ctx.shadowBlur = 20; } ctx.font = '12px Orbitron, "Share Tech Mono", monospace'; ctx.textBaseline = 'middle'; ctx.fillStyle = restHov ? '#000000' : '#00d4ff'; ctx.fillText('RESTART MISSION', W / 2, RBY + RBH / 2); ctx.shadowBlur = 0; ctx.restore(); addHitRegion(RBX, RBY, RBW, RBH, restartGame); } // ── PAUSE OVERLAY ───────────────────────────────────────────── function drawPauseOverlay() { if (!document.body.classList.contains('paused')) return; const W = canvas.width, H = canvas.height; ctx.fillStyle = 'rgba(0,0,0,0.58)'; ctx.fillRect(0, 0, W, H); const BW = 420, BH = 118; const BX = (W - BW) / 2, BY = (H - BH) / 2; ctx.save(); ctx.fillStyle = 'rgba(6,14,22,0.94)'; ctx.strokeStyle = '#1a3048'; ctx.lineWidth = 1; ctx.beginPath(); ctx.rect(BX, BY, BW, BH); ctx.fill(); ctx.stroke(); ctx.shadowColor = 'rgba(0,212,255,0.18)'; ctx.shadowBlur = 34; ctx.strokeRect(BX, BY, BW, BH); ctx.shadowBlur = 0; ctx.textAlign = 'center'; ctx.textBaseline = 'top'; ctx.font = '900 34px Orbitron, "Share Tech Mono", monospace'; ctx.fillStyle = '#ffd700'; ctx.shadowColor = '#ffd700'; ctx.shadowBlur = 22; ctx.fillText('PAUSED', W / 2, BY + 18); ctx.shadowBlur = 0; ctx.font = '11px Orbitron, "Share Tech Mono", monospace'; ctx.fillStyle = '#3a6080'; ctx.fillText('Esc resumes · Space opens armory · I opens inventory', W / 2, BY + 78); ctx.restore(); } // ── MOUNT POINT TOOLTIP ─────────────────────────────────────── function _drawMountTooltip(mx, my, weapon, socketR) { const def = getWeaponDef(weapon); if (!def) return; const TW = 168, TH = 58; let tx = mx + socketR + 10; let ty = my - TH / 2; if (tx + TW > 1330) tx = mx - socketR - 10 - TW; ty = Math.max(68, Math.min(900 - TH - 4, ty)); ctx.save(); ctx.fillStyle = '#050d18'; ctx.strokeStyle = '#00d4ff'; ctx.lineWidth = 1; ctx.fillRect(tx, ty, TW, TH); ctx.strokeRect(tx, ty, TW, TH); ctx.font = '10px Orbitron, monospace'; ctx.letterSpacing = '1px'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; ctx.fillStyle = '#00d4ff'; ctx.fillText((def.icon || '') + ' ' + def.name, tx + 8, ty + 8); ctx.letterSpacing = '0px'; const parts = []; if (typeof def.damage === 'number') parts.push(`DMG ${def.damage}`); if (typeof def.fireRate === 'number') parts.push(`RPM ${Math.round(3600 / def.fireRate)}`); const el = weapon.elements?.[0] ? ELEMENTS[weapon.elements[0]] : null; if (el) parts.push(el.icon + ' ' + el.name); ctx.font = '9px "Share Tech Mono", monospace'; ctx.fillStyle = '#7aaabb'; ctx.fillText(parts.join(' '), tx + 8, ty + 26); ctx.fillStyle = '#3a6080'; ctx.fillText('drag to move or bag', tx + 8, ty + 41); ctx.restore(); } // ── MOUNT POINT INTERACTION (drawn after overlays so it sits on top) ───── function drawMountInteraction(cx, cy) { if (G.shopOpen) return; const invOpen = document.body.classList.contains('inventory-open'); const totalSlots = Math.max(1, G.tower.weaponSlots); const hpRatio = G.tower.hp / G.tower.maxHp; const hpColor = hpRatio > 0.5 ? '#00d4ff' : hpRatio > 0.25 ? '#ffd700' : '#ff3355'; for (let slotIndex = 0; slotIndex < totalSlots; slotIndex++) { const weapon = G.weapons[slotIndex]; const installed = !!weapon; const mountAngle = HARDPOINT_BASE_ANGLE + (slotIndex / totalSlots) * Math.PI * 2; if (invOpen) { const actual = getSlotHardpoint(slotIndex, cx, cy, totalSlots); const ORBIT = Math.max(78, 64 + totalSlots * 6); const mx = cx + Math.cos(mountAngle) * ORBIT; const my = cy + Math.sin(mountAngle) * ORBIT; const dropR = 20; const hov = isHovered(mx - dropR, my - dropR, dropR * 2, dropR * 2); const dragging = _dragWeapon !== null; ctx.save(); ctx.strokeStyle = '#00aaff33'; ctx.lineWidth = 1; ctx.setLineDash([5, 6]); ctx.beginPath(); ctx.moveTo(actual.x, actual.y); ctx.lineTo(mx, my); ctx.stroke(); ctx.setLineDash([]); ctx.restore(); if (dragging) { ctx.save(); ctx.shadowBlur = hov ? 20 : 8; ctx.shadowColor = '#ffd700'; ctx.strokeStyle = hov ? '#ffd700' : '#00aaff55'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(mx, my, dropR + 5, 0, Math.PI * 2); ctx.stroke(); ctx.shadowBlur = 0; ctx.restore(); } ctx.fillStyle = hov ? '#0c1820' : '#050d16'; ctx.strokeStyle = installed ? (hov ? '#ffd700' : hpColor + 'aa') : (hov ? '#00aaff' : '#1a3240'); ctx.lineWidth = hov ? 2.5 : 1.5; ctx.beginPath(); ctx.arc(mx, my, dropR, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.font = '8px Orbitron, monospace'; ctx.letterSpacing = '1px'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#3a6080'; ctx.fillText(`S${slotIndex + 1}`, mx, my - (installed ? 8 : 0)); ctx.letterSpacing = '0px'; if (installed) { _dragRegions.push({ x: mx - dropR, y: my - dropR, w: dropR * 2, h: dropR * 2, weapon, source: { type: 'slot', slotIndex } }); const def = getWeaponDef(weapon); ctx.font = '14px monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#ffffff'; ctx.fillText(def?.icon || '?', mx, my + 5); } else { ctx.font = '14px "Share Tech Mono", monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#1a3240'; ctx.fillText('+', mx, my); } _mountDropZones.push({ x: mx, y: my, r: dropR, slotIndex }); addHitRegion(mx - dropR, my - dropR, dropR * 2, dropR * 2, () => openWeaponPicker(slotIndex)); if (hov && installed) _drawMountTooltip(mx, my, weapon, dropR); } else { // inventory closed: invisible hit region — click opens picker for this slot const mount = getSlotHardpoint(slotIndex, cx, cy, totalSlots); const r = 10; addHitRegion(mount.x - r, mount.y - r, r * 2, r * 2, () => openWeaponPicker(slotIndex)); } } } // ── DRAG GHOST ──────────────────────────────────────────────── function drawDragGhost() { if (!_dragWeapon || !_hoverPt) return; const def = getWeaponDef(_dragWeapon); if (!def) return; const GW = 90, GH = 68; const gx = _hoverPt.x - GW / 2; const gy = _hoverPt.y - GH / 2; ctx.save(); ctx.globalAlpha = 0.85; ctx.fillStyle = '#060e16'; ctx.strokeStyle = '#ffd700'; ctx.lineWidth = 2; ctx.fillRect(gx, gy, GW, GH); ctx.strokeRect(gx, gy, GW, GH); ctx.globalAlpha = 1; ctx.font = '22px monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = '#ffffff'; ctx.fillText(def.icon || '?', gx + GW / 2, gy + GH / 2 - 8); ctx.font = '8px Orbitron, monospace'; ctx.fillStyle = '#b8d8e8'; ctx.fillText(def.name, gx + GW / 2, gy + GH / 2 + 14); ctx.restore(); }