Fix FPV NPC chat: overlay no longer blocks dialog, add interact prompt
Browse files- web/3d.html +36 -1
web/3d.html
CHANGED
|
@@ -272,6 +272,9 @@
|
|
| 272 |
<div id="fp-hint" style="display:none;position:fixed;bottom:60px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.7);color:#fff;padding:8px 16px;border-radius:8px;font-size:13px;z-index:50;text-align:center">
|
| 273 |
WASD β move · Mouse β look · E β talk to NPC · ESC β exit
|
| 274 |
</div>
|
|
|
|
|
|
|
|
|
|
| 275 |
<div id="fp-click-overlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:250;cursor:pointer;display:none;align-items:center;justify-content:center">
|
| 276 |
<div style="text-align:center;color:#fff">
|
| 277 |
<div style="font-size:48px;margin-bottom:16px">👁</div>
|
|
@@ -3294,6 +3297,28 @@ controls.addEventListener('change', updateDetailLevel);
|
|
| 3294 |
const clock = new THREE.Clock();
|
| 3295 |
let frameCount = 0;
|
| 3296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3297 |
renderer.setAnimationLoop(animate);
|
| 3298 |
function animate() {
|
| 3299 |
const dt = clock.getDelta();
|
|
@@ -3303,6 +3328,7 @@ function animate() {
|
|
| 3303 |
updateFPMovement();
|
| 3304 |
updateNPCFacing();
|
| 3305 |
updatePlayerPosition();
|
|
|
|
| 3306 |
} else {
|
| 3307 |
controls.update();
|
| 3308 |
updateCameraAnimation();
|
|
@@ -3531,7 +3557,7 @@ fpControls.addEventListener('lock', () => {
|
|
| 3531 |
});
|
| 3532 |
fpControls.addEventListener('unlock', () => {
|
| 3533 |
document.getElementById('fp-crosshair').style.display = 'none';
|
| 3534 |
-
if (fpMode) {
|
| 3535 |
document.getElementById('fp-click-overlay').style.display = 'flex';
|
| 3536 |
}
|
| 3537 |
});
|
|
@@ -3636,16 +3662,25 @@ function startNpcChat(agentId) {
|
|
| 3636 |
document.getElementById('chat-npc-name').textContent = name;
|
| 3637 |
document.getElementById('chat-messages').innerHTML = '';
|
| 3638 |
document.getElementById('chat-text').value = '';
|
|
|
|
|
|
|
| 3639 |
document.getElementById('npc-chat').style.display = 'block';
|
| 3640 |
fpControls.unlock();
|
|
|
|
|
|
|
| 3641 |
addChatMsg('system', `${name} stops and turns to face you.`);
|
| 3642 |
setTimeout(() => document.getElementById('chat-text').focus(), 100);
|
| 3643 |
}
|
| 3644 |
|
| 3645 |
function endNpcChat() {
|
| 3646 |
if (isRecording) toggleMic();
|
|
|
|
| 3647 |
chatTargetId = null;
|
| 3648 |
chatMessages = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3649 |
document.getElementById('npc-chat').style.display = 'none';
|
| 3650 |
if (fpMode) {
|
| 3651 |
const overlay = document.getElementById('fp-click-overlay');
|
|
|
|
| 272 |
<div id="fp-hint" style="display:none;position:fixed;bottom:60px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.7);color:#fff;padding:8px 16px;border-radius:8px;font-size:13px;z-index:50;text-align:center">
|
| 273 |
WASD β move · Mouse β look · E β talk to NPC · ESC β exit
|
| 274 |
</div>
|
| 275 |
+
<div id="fp-interact-prompt" style="display:none;position:fixed;top:55%;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.75);color:#4ecca3;padding:8px 18px;border-radius:8px;font-size:14px;z-index:55;pointer-events:none;border:1px solid rgba(78,204,163,0.3)">
|
| 276 |
+
Press <b>E</b> to talk
|
| 277 |
+
</div>
|
| 278 |
<div id="fp-click-overlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:250;cursor:pointer;display:none;align-items:center;justify-content:center">
|
| 279 |
<div style="text-align:center;color:#fff">
|
| 280 |
<div style="font-size:48px;margin-bottom:16px">👁</div>
|
|
|
|
| 3297 |
const clock = new THREE.Clock();
|
| 3298 |
let frameCount = 0;
|
| 3299 |
|
| 3300 |
+
const _interactRay = new THREE.Raycaster();
|
| 3301 |
+
_interactRay.far = 10;
|
| 3302 |
+
function updateInteractPrompt() {
|
| 3303 |
+
const prompt = document.getElementById('fp-interact-prompt');
|
| 3304 |
+
if (chatTargetId || !fpControls.isLocked) { prompt.style.display = 'none'; return; }
|
| 3305 |
+
_interactRay.setFromCamera(new THREE.Vector2(0, 0), fpCamera);
|
| 3306 |
+
const objs = [];
|
| 3307 |
+
for (const [, m] of agentMeshes) objs.push(m);
|
| 3308 |
+
const hits = _interactRay.intersectObjects(objs, true);
|
| 3309 |
+
if (hits.length > 0) {
|
| 3310 |
+
let p = hits[0].object;
|
| 3311 |
+
while (p.parent && !p.userData?.id) p = p.parent;
|
| 3312 |
+
if (p.userData?.id && p.userData.type === 'agent') {
|
| 3313 |
+
const name = p.userData.data?.name || p.userData.id;
|
| 3314 |
+
prompt.innerHTML = `Press <b>E</b> to talk to <b>${name}</b>`;
|
| 3315 |
+
prompt.style.display = 'block';
|
| 3316 |
+
return;
|
| 3317 |
+
}
|
| 3318 |
+
}
|
| 3319 |
+
prompt.style.display = 'none';
|
| 3320 |
+
}
|
| 3321 |
+
|
| 3322 |
renderer.setAnimationLoop(animate);
|
| 3323 |
function animate() {
|
| 3324 |
const dt = clock.getDelta();
|
|
|
|
| 3328 |
updateFPMovement();
|
| 3329 |
updateNPCFacing();
|
| 3330 |
updatePlayerPosition();
|
| 3331 |
+
if (frameCount % 10 === 0) updateInteractPrompt();
|
| 3332 |
} else {
|
| 3333 |
controls.update();
|
| 3334 |
updateCameraAnimation();
|
|
|
|
| 3557 |
});
|
| 3558 |
fpControls.addEventListener('unlock', () => {
|
| 3559 |
document.getElementById('fp-crosshair').style.display = 'none';
|
| 3560 |
+
if (fpMode && !chatTargetId) {
|
| 3561 |
document.getElementById('fp-click-overlay').style.display = 'flex';
|
| 3562 |
}
|
| 3563 |
});
|
|
|
|
| 3662 |
document.getElementById('chat-npc-name').textContent = name;
|
| 3663 |
document.getElementById('chat-messages').innerHTML = '';
|
| 3664 |
document.getElementById('chat-text').value = '';
|
| 3665 |
+
document.getElementById('fp-interact-prompt').style.display = 'none';
|
| 3666 |
+
document.getElementById('fp-click-overlay').style.display = 'none';
|
| 3667 |
document.getElementById('npc-chat').style.display = 'block';
|
| 3668 |
fpControls.unlock();
|
| 3669 |
+
const mesh = agentMeshes.get(agentId);
|
| 3670 |
+
if (mesh) mesh.userData.chatFrozen = true;
|
| 3671 |
addChatMsg('system', `${name} stops and turns to face you.`);
|
| 3672 |
setTimeout(() => document.getElementById('chat-text').focus(), 100);
|
| 3673 |
}
|
| 3674 |
|
| 3675 |
function endNpcChat() {
|
| 3676 |
if (isRecording) toggleMic();
|
| 3677 |
+
const prevTarget = chatTargetId;
|
| 3678 |
chatTargetId = null;
|
| 3679 |
chatMessages = [];
|
| 3680 |
+
if (prevTarget) {
|
| 3681 |
+
const mesh = agentMeshes.get(prevTarget);
|
| 3682 |
+
if (mesh) mesh.userData.chatFrozen = false;
|
| 3683 |
+
}
|
| 3684 |
document.getElementById('npc-chat').style.display = 'none';
|
| 3685 |
if (fpMode) {
|
| 3686 |
const overlay = document.getElementById('fp-click-overlay');
|