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' :