626879ed0c
- Add enemy freshness tracking (novelty bonus for repeated deploys) - Add freshness bar to sidepanel enemy cards with penalty indicator - Major overhaul of renderer-overlays.js (790+ lines for UI polish) - Enhanced combat log, shop overlays, and inventory UI - Improved weapon/upgrade display with partial ownership colors - Added element icons and weakness/resistance indicators to cards - Enhanced radial menu and tooltip system - Add "stale/%" penalty text when freshness depleted - Update play link to ffazeshift.net in index.html
151 lines
5.9 KiB
JavaScript
151 lines
5.9 KiB
JavaScript
// ═══ 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();
|
|
}
|