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
This commit is contained in:
+47
-60
@@ -1,12 +1,11 @@
|
||||
// ═══ renderer-shop-overlay.js ═══
|
||||
// ============================================================
|
||||
// RENDERER SHOP — canvas armory overlay
|
||||
// RENDERER SHOP — canvas armory / command overlays
|
||||
// ============================================================
|
||||
|
||||
// ── SHOP OVERLAY ──────────────────────────────────────────────
|
||||
const _SH_HDR_H = 56;
|
||||
const _SH_TAB_H = 38;
|
||||
const _SH_BODY_Y = _SH_HDR_H + _SH_TAB_H; // 94
|
||||
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;
|
||||
@@ -30,7 +29,7 @@ function _shopWrapText(text, maxW, maxLines) {
|
||||
return lines;
|
||||
}
|
||||
|
||||
function _shopUpgNode(sx, screenY, upg, bought, locked, cantAfford, onBuy, onRefund) {
|
||||
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();
|
||||
@@ -59,6 +58,7 @@ function _shopUpgNode(sx, screenY, upg, bought, locked, cantAfford, onBuy, onRef
|
||||
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); }
|
||||
|
||||
@@ -67,19 +67,8 @@ function _shopUpgNode(sx, screenY, upg, bought, locked, cantAfford, onBuy, onRef
|
||||
if (onRefund) _shopRightClick.push({ x: sx, y: screenY, w: _SH_UPG_W, h: _SH_UPG_H, action: onRefund });
|
||||
}
|
||||
|
||||
function drawShopOverlay() {
|
||||
if (!G.shopOpen) 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();
|
||||
|
||||
// ── 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;
|
||||
@@ -88,7 +77,7 @@ function drawShopOverlay() {
|
||||
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('ARMORY', _SH_PAD, _SH_HDR_H / 2);
|
||||
ctx.fillText(title, _SH_PAD, _SH_HDR_H / 2);
|
||||
ctx.shadowBlur = 0; ctx.letterSpacing = '0px';
|
||||
|
||||
ctx.font = '18px Orbitron, "Share Tech Mono", monospace';
|
||||
@@ -105,59 +94,57 @@ function drawShopOverlay() {
|
||||
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, closeShop);
|
||||
addHitRegion(CBX, CBY, CBW, CBH, closeFn);
|
||||
}
|
||||
|
||||
ctx.fillStyle = '#030a12';
|
||||
ctx.fillRect(0, _SH_HDR_H, W, _SH_TAB_H);
|
||||
ctx.strokeStyle = '#1a3048'; ctx.lineWidth = 1;
|
||||
ctx.beginPath(); ctx.moveTo(0, _SH_BODY_Y); ctx.lineTo(W, _SH_BODY_Y); ctx.stroke();
|
||||
// ── ARMORY OVERLAY — weapon buying + per-weapon upgrade trees ──
|
||||
function drawArmoryOverlay() {
|
||||
if (!G.armoryOpen) return;
|
||||
_shopRightClick.length = 0;
|
||||
|
||||
const equippedWeapons = getEquippedWeapons();
|
||||
const tabDefs = [
|
||||
{ id: 'tower', label: '🏰 TOWER' },
|
||||
{ id: 'weapons', label: '🔧 BUY WEAPON' },
|
||||
...equippedWeapons.map(w => {
|
||||
const def = getWeaponDef(w);
|
||||
return { id: w.instanceId, label: (def?.icon || '?') + ' ' + (def?.name || '?') };
|
||||
}),
|
||||
];
|
||||
const W = canvas.width, H = canvas.height;
|
||||
const BODY_H = H - _SH_BODY_Y;
|
||||
|
||||
ctx.font = '11px Orbitron, "Share Tech Mono", monospace'; ctx.letterSpacing = '1.5px';
|
||||
let tx = _SH_PAD;
|
||||
const TB_Y = _SH_HDR_H + 4, TB_H = _SH_TAB_H - 4;
|
||||
for (const tab of tabDefs) {
|
||||
const tw = Math.ceil(ctx.measureText(tab.label).width) + 32;
|
||||
const active = G.shopTab === tab.id;
|
||||
const tabHov = isHovered(tx, TB_Y, tw, TB_H);
|
||||
if (active) {
|
||||
ctx.fillStyle = '#060e18'; ctx.strokeStyle = '#1a3048'; ctx.lineWidth = 1;
|
||||
ctx.fillRect(tx, TB_Y, tw, TB_H + 2);
|
||||
ctx.strokeRect(tx, TB_Y, tw, TB_H);
|
||||
}
|
||||
ctx.fillStyle = active ? '#00d4ff' : (tabHov ? '#b8d8e8' : '#3a6080');
|
||||
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
|
||||
ctx.fillText(tab.label, tx + tw / 2, TB_Y + TB_H / 2);
|
||||
addHitRegion(tx, TB_Y, tw, TB_H, ((tid) => () => setShopTab(tid))(tab.id));
|
||||
tx += tw + 4;
|
||||
}
|
||||
ctx.letterSpacing = '0px';
|
||||
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;
|
||||
|
||||
if (G.shopTab === 'tower') {
|
||||
yOff = _shopDrawTowerContent(yOff, bodyCX, bodyCW, H);
|
||||
} else if (G.shopTab === 'weapons') {
|
||||
yOff = _shopDrawBuyContent(yOff, bodyCX, bodyCW, H);
|
||||
} else {
|
||||
const w = equippedWeapons.find(w => w.instanceId === G.shopTab);
|
||||
if (w) yOff = _shopDrawWeaponContent(yOff, bodyCX, bodyCW, H, w);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user