Files
siege-protocol/js/renderer-shop-overlay.js
44r0n7 626879ed0c Add freshness bar, enhance overlays and renderers
- 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
2026-06-17 11:58:17 -04:00

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();
}