From ea23ba404d193d73a89ceecdec6bb17cc30e9b36 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Mon, 13 Jul 2026 07:58:43 -0400 Subject: [PATCH] fix(fx): drop per-step red monster flash; big banner on descend/treasure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Red pulse: _fxOnPlayerMoved fired a full-screen red FX.flash whenever a monster was within 3 tiles (throttled 550ms) — the monster usually IS within 3 tiles, so it pulsed on nearly every step. Removed it; the monster is already on-screen, so proximity needs no flash. FX.flash is now reserved for genuine one-shots (win/lose). Descend feedback: taking the stairs only logged a tiny chat line. Add showEventBanner() — a big centered pop-in/hold/fade banner over the game view (pointer-events:none, auto-removed) fired on descend ("LEVEL n / N") and treasure ("TREASURE SECURED"), with a soft one-shot colored flash. Chat lines kept as the log trail. --- html/js/core/game.js | 17 ++++---------- html/js/network/socketHandlers.js | 39 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/html/js/core/game.js b/html/js/core/game.js index 07d6e7d..a7ffaaa 100644 --- a/html/js/core/game.js +++ b/html/js/core/game.js @@ -450,19 +450,10 @@ var Game = { window.FX.sparkle(pp.x, pp.y, '#fbbf24'); } - // Monster proximity: throttled red edge pulse, stronger the closer it is. - var m = GameState._monster; - if (m && typeof m.x === 'number' && typeof m.y === 'number' && window.FX.flash) { - var md = this._fxDist(player.x, player.y, m.x, m.y); - if (md <= 3) { - var now = Date.now(); - if (!this._fxLastMonsterFlash || (now - this._fxLastMonsterFlash) > 550) { - this._fxLastMonsterFlash = now; - var alpha = (md <= 1) ? 0.22 : (md <= 2 ? 0.14 : 0.09); - window.FX.flash('rgba(255,60,60,1)', alpha, 260); - } - } - } + // NOTE: removed the monster-proximity red screen flash — it fired on nearly every step + // (monster usually within 3 tiles) and read as a constant annoying pulse. The monster is + // already visible on-screen, so proximity needs no full-screen flash. Reserve FX.flash for + // genuine one-shot events (win/lose in handleGameOver). }, // Attach + start the FX loop for a fresh game. diff --git a/html/js/network/socketHandlers.js b/html/js/network/socketHandlers.js index c7dc0be..9df9868 100644 --- a/html/js/network/socketHandlers.js +++ b/html/js/network/socketHandlers.js @@ -139,8 +139,11 @@ const SocketHandlers = { var lvl = (data.depth && data.maxDepth) ? ('Level ' + data.depth + ' of ' + data.maxDepth + ' — ') : ''; $m.append($('
  • ') .text('⬇️ Stairs down! ' + lvl + 'keep going — escape before the block!')); + var lvlTitle = (data.depth && data.maxDepth) ? ('⬇ LEVEL ' + data.depth + ' / ' + data.maxDepth) : '⬇ DESCEND'; + SocketHandlers.showEventBanner(lvlTitle, 'Deeper in — escape before the block!', '#ffcc00'); } else if (data.event === 'treasure_found') { $m.append($('
  • ').text('💰 Treasure secured!')); + SocketHandlers.showEventBanner('💰 TREASURE SECURED', 'Now get to the stairs and escape!', '#fbbf24'); } else return; if (typeof UI !== 'undefined' && UI.scrollChat) UI.scrollChat(); }); @@ -333,6 +336,42 @@ const SocketHandlers = { UI.scrollChat(); }, + // A big, one-shot on-screen banner over the game view for headline in-run events (descend / + // treasure). Pops in, holds, fades — pointer-events:none so it never eats input, and auto-removes. + showEventBanner: function(title, sub, color) { + color = color || '#ffcc00'; + var host = document.getElementById('game-display') || document.body; + if (!document.getElementById('evt-banner-style')) { + var st = document.createElement('style'); + st.id = 'evt-banner-style'; + st.textContent = + '@keyframes evtBannerIn{0%{opacity:0;transform:translate(-50%,-50%) scale(.6)}' + + '12%{opacity:1;transform:translate(-50%,-50%) scale(1.12)}' + + '22%{transform:translate(-50%,-50%) scale(1)}' + + '76%{opacity:1;transform:translate(-50%,-50%) scale(1)}' + + '100%{opacity:0;transform:translate(-50%,-50%) scale(1.05)}}'; + document.head.appendChild(st); + } + // Drop any banner still on screen so they never stack. + var prev = document.getElementById('evt-banner'); + if (prev && prev.parentNode) prev.parentNode.removeChild(prev); + + var b = document.createElement('div'); + b.id = 'evt-banner'; + var positioned = host.id === 'game-display'; // #game-display is position:relative + b.style.cssText = (positioned ? 'position:absolute;' : 'position:fixed;') + + 'left:50%;top:34%;z-index:30;pointer-events:none;text-align:center;' + + 'font-family:ui-monospace,monospace;animation:evtBannerIn 2600ms ease-out forwards;'; + b.innerHTML = + '
    ' + title + '
    ' + + (sub ? '
    ' + sub + '
    ' : ''); + host.appendChild(b); + setTimeout(function () { if (b.parentNode) b.parentNode.removeChild(b); }, 2650); + // A little extra pop: a soft colored flash if the FX overlay is up. + try { if (window.FX && window.FX.flash) window.FX.flash(color, 0.16, 320); } catch (e) {} + }, + onStatusUpdate: function(data) { const statusClass = data.type === 'error' ? 'error' : 'status'; const statusColor = data.type === 'error' ? '#f00' :