From 697a303a75a1c0e917af1e3c8160e6f0331181e8 Mon Sep 17 00:00:00 2001 From: Justin Oros Date: Wed, 20 May 2026 10:08:10 -0700 Subject: [PATCH] Add '...' chat bubble above player when pressing talk before reaching NPC --- game.js | 33 +++++++++++++++++++++++++++------ index.html | 1 + style.css | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/game.js b/game.js index 44654a1..9ac8356 100644 --- a/game.js +++ b/game.js @@ -386,11 +386,15 @@ class Game { /* Player sprite stays horizontally centred; only Y shifts */ const p = document.getElementById('map-player'); if (p) { - const centreX = viewW / 2 - 16; - const centreY = viewH * 0.65 + this.state.worldY; + const centreX = viewW / 2 - 16; + const centreY = viewH * 0.65 + this.state.worldY; + const bottomPx = viewH - centreY - 40; p.style.left = centreX + 'px'; - p.style.bottom = (viewH - centreY - 40) + 'px'; + p.style.bottom = bottomPx + 'px'; p.className = `map-player walk-${dir}`; + /* Keep "..." bubble above player */ + const bub = document.getElementById('player-bubble'); + if (bub) { bub.style.left = (centreX - 4) + 'px'; bub.style.bottom = (bottomPx + 66) + 'px'; } } this._checkNPCProximity(); @@ -453,8 +457,22 @@ class Game { } _talkToNPC() { + if (!this._nearNPC()) { + /* Too far away — show "..." bubble above player */ + const bub = document.getElementById('player-bubble'); + if (bub) { + bub.classList.remove('visible'); + void bub.offsetWidth; + bub.classList.add('visible'); + clearTimeout(this._bubbleTimer); + this._bubbleTimer = setTimeout(() => bub.classList.remove('visible'), 1500); + } + return; + } const arrow = document.getElementById('map-dir-arrow'); if (arrow) arrow.style.display = 'none'; + const bub = document.getElementById('player-bubble'); + if (bub) bub.classList.remove('visible'); SFX.encounter(); this.startQuestion(); } @@ -467,10 +485,13 @@ class Game { const p = document.getElementById('map-player'); if (p) { - p.style.left = (viewW / 2 - 16) + 'px'; - p.style.bottom = (viewH * 0.28) + 'px'; - + const left = viewW / 2 - 16; + const bottom = viewH * 0.28; + p.style.left = left + 'px'; + p.style.bottom = bottom + 'px'; p.className = 'map-player idle'; + const bub = document.getElementById('player-bubble'); + if (bub) { bub.style.left = (left - 4) + 'px'; bub.style.bottom = (bottom + 66) + 'px'; } } this._applyCamera(); const hint = document.getElementById('map-talk-hint'); diff --git a/index.html b/index.html index 660b966..0e4e978 100644 --- a/index.html +++ b/index.html @@ -109,6 +109,7 @@
+
...
diff --git a/style.css b/style.css index d19cdc1..5b2f3fa 100644 --- a/style.css +++ b/style.css @@ -483,6 +483,38 @@ html, body { .npc-bubble { font-size:14px; font-family:var(--font); color:var(--black); background:var(--yellow); border:2px solid var(--black); width:20px; height:20px; display:flex; align-items:center; justify-content:center; margin:0 auto 2px; animation:bounce .7s ease-in-out infinite; } .map-npc { font-size:clamp(36px,10vw,52px); filter:drop-shadow(2px 3px 0 rgba(0,0,0,.35)); cursor:pointer; } /* Player is fixed to the viewport centre horizontally; only Y moves */ +/* "..." bubble above player when pressing talk away from NPC */ +.player-bubble { + font-size: 11px; + font-family: var(--font); + color: var(--black); + background: #fff; + border: 2px solid var(--black); + padding: 0 5px; + height: 20px; + display: none; + align-items: center; + justify-content: center; + border-radius: 2px; + white-space: nowrap; + position: absolute; + z-index: 100; + box-shadow: 1px 1px 0 rgba(0,0,0,.3); +} +.player-bubble::after { + content: ''; + position: absolute; + bottom: -6px; + left: 50%; + transform: translateX(-50%); + border: 3px solid transparent; + border-top-color: var(--black); +} +.player-bubble.visible { + display: flex; + animation: bounce .5s ease-in-out 3; +} + .map-player { position: absolute; width: 32px; @@ -493,7 +525,7 @@ html, body { background-position: 0 0; image-rendering: pixelated; filter: drop-shadow(2px 3px 0 rgba(0,0,0,.4)); - z-index: 99; + z-index: 10; font-size: 0; color: transparent; }