Show ... chat bubble over player when too far from NPC

This commit is contained in:
justin committed 2026-05-20 09:33:06 -07:00
1 parent 2dde0dfd4f
commit aece71a0d4
3 files changed
+25 -8

No files matched your search

+18 -8
View File
@@ -385,11 +385,12 @@ class Game {
/* Player sprite stays horizontally centred; only Y shifts */ /* Player sprite stays horizontally centred; only Y shifts */
const p = document.getElementById('map-player'); const p = document.getElementById('map-player');
if (p) { const pw = document.getElementById('map-player-wrap');
if (p && pw) {
const centreX = viewW / 2 - 16; const centreX = viewW / 2 - 16;
const centreY = viewH * 0.65 + this.state.worldY; const centreY = viewH * 0.65 + this.state.worldY;
p.style.left = centreX + 'px'; pw.style.left = centreX + 'px';
p.style.bottom = (viewH - centreY - 40) + 'px'; pw.style.bottom = (viewH - centreY - 40) + 'px';
p.className = `map-player walk-${dir}`; p.className = `map-player walk-${dir}`;
} }
@@ -454,11 +455,20 @@ class Game {
_talkToNPC() { _talkToNPC() {
if (!this._nearNPC()) { if (!this._nearNPC()) {
this.toast('Walk up to the NPC first!'); 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; return;
} }
const arrow = document.getElementById('map-dir-arrow'); const arrow = document.getElementById('map-dir-arrow');
if (arrow) arrow.style.display = 'none'; if (arrow) arrow.style.display = 'none';
const bub = document.getElementById('player-bubble');
if (bub) bub.classList.remove('visible');
SFX.encounter(); SFX.encounter();
this.startQuestion(); this.startQuestion();
} }
@@ -470,10 +480,10 @@ class Game {
const viewH = world ? world.parentElement.offsetHeight : 300; const viewH = world ? world.parentElement.offsetHeight : 300;
const p = document.getElementById('map-player'); const p = document.getElementById('map-player');
if (p) { const pw = document.getElementById('map-player-wrap');
p.style.left = (viewW / 2 - 16) + 'px'; if (p && pw) {
p.style.bottom = (viewH * 0.28) + 'px'; pw.style.left = (viewW / 2 - 16) + 'px';
pw.style.bottom = (viewH * 0.28) + 'px';
p.className = 'map-player idle'; p.className = 'map-player idle';
} }
this._applyCamera(); this._applyCamera();
+3
View File
@@ -109,7 +109,10 @@
<div class="map-npc" id="map-npc"></div> <div class="map-npc" id="map-npc"></div>
</div> </div>
<!-- Player stays fixed to viewport, only Y moves --> <!-- Player stays fixed to viewport, only Y moves -->
<div class="map-player-wrap" id="map-player-wrap">
<div class="player-bubble" id="player-bubble">...</div>
<div class="map-player" id="map-player"></div> <div class="map-player" id="map-player"></div>
</div>
<!-- Hints overlay the viewport, not the world --> <!-- Hints overlay the viewport, not the world -->
<div class="map-talk-hint" id="map-talk-hint" style="display:none"><span class="hint-pc">Press E</span><span class="hint-touch">Press A</span></div> <div class="map-talk-hint" id="map-talk-hint" style="display:none"><span class="hint-pc">Press E</span><span class="hint-touch">Press A</span></div>
<div class="map-dir-arrow" id="map-dir-arrow" style="display:none">▶</div> <div class="map-dir-arrow" id="map-dir-arrow" style="display:none">▶</div>
+4
View File
@@ -483,7 +483,11 @@ 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; } .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; } .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 */ /* Player is fixed to the viewport centre horizontally; only Y moves */
.map-player-wrap { position:absolute; text-align:center; z-index:99; }
.player-bubble { font-size:11px; font-family:var(--font); color:var(--black); background:#fff; border:2px solid var(--black); padding:0 4px; height:20px; display:none; align-items:center; justify-content:center; margin:0 auto 2px; border-radius:2px; white-space:nowrap; }
.player-bubble.visible { display:flex; animation:bounce .5s ease-in-out 3; }
.map-player { .map-player {
position: relative;
position: absolute; position: absolute;
width: 32px; width: 32px;
height: 64px; height: 64px;