import * as THREE from 'three'; import { STATE, CONFIG } from '../core/State.js'; import { updateHUD } from '../ui/HUD.js'; export const raycaster = new THREE.Raycaster(); raycaster.params.Points.threshold = 0.5; export function setupInputListeners(camera, controls) { let isDragging = false; let dragged = false; let startX = 0, startY = 0; let startRotationY = 0, startRotationX = 0; // HUD Transparency controls.addEventListener('lock', () => { const hud = document.getElementById('hud-sidebar'); if (hud) hud.style.opacity = '0.2'; const guideline = document.getElementById('pointerlock-guideline'); if (guideline) guideline.style.display = 'block'; if (STATE.velocity) STATE.velocity.set(0, 0, 0); if (STATE.moveState) { STATE.moveState.forward = false; STATE.moveState.backward = false; STATE.moveState.left = false; STATE.moveState.right = false; STATE.moveState.up = false; STATE.moveState.down = false; STATE.moveState.shift = false; } }); controls.addEventListener('unlock', () => { const hud = document.getElementById('hud-sidebar'); if (hud) hud.style.opacity = '1.0'; const guideline = document.getElementById('pointerlock-guideline'); if (guideline) guideline.style.display = 'none'; if (STATE.velocity) STATE.velocity.set(0, 0, 0); if (STATE.moveState) { STATE.moveState.forward = false; STATE.moveState.backward = false; STATE.moveState.left = false; STATE.moveState.right = false; STATE.moveState.up = false; STATE.moveState.down = false; STATE.moveState.shift = false; } if (camera && camera.rotation) { camera.rotation.z = 0; } }); const onKeyDown = (e) => { if (!STATE.isReady) return; const active = document.activeElement; const isTyping = active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT' || active.isContentEditable); if (isTyping) return; switch (e.code) { case 'KeyW': STATE.moveState.forward = true; break; case 'KeyS': STATE.moveState.backward = true; break; case 'KeyA': STATE.moveState.left = true; break; case 'KeyD': STATE.moveState.right = true; break; case 'KeyQ': STATE.moveState.up = true; break; case 'KeyE': STATE.moveState.down = true; break; case 'ShiftLeft': STATE.moveState.shift = true; break; case 'ArrowLeft': if (STATE.hunterActive && STATE.hunterResults.length > 0) { STATE.hunterIndex = (STATE.hunterIndex - 1 + STATE.hunterResults.length) % STATE.hunterResults.length; STATE.currentSelectedIndex = STATE.hunterResults[STATE.hunterIndex]; const x = STATE.currentSelectedIndex * CONFIG.pointSpacing; camera.position.x = x; } else { STATE.currentSelectedIndex = Math.max(0, STATE.currentSelectedIndex - 1); } STATE.currentAnalysis = null; updateHUD(); break; case 'ArrowRight': if (STATE.hunterActive && STATE.hunterResults.length > 0) { STATE.hunterIndex = (STATE.hunterIndex + 1) % STATE.hunterResults.length; STATE.currentSelectedIndex = STATE.hunterResults[STATE.hunterIndex]; const x = STATE.currentSelectedIndex * CONFIG.pointSpacing; camera.position.x = x; } else { STATE.currentSelectedIndex = Math.min(STATE.vertexCount - 1, STATE.currentSelectedIndex + 1); } STATE.currentAnalysis = null; updateHUD(); break; case 'KeyF': const idx = STATE.currentSelectedIndex; const mode = STATE.currentZMode; const spacing = CONFIG.pointSpacing || 0.1; let targetX = 0, targetY = 0, targetZ = 0; let camX = 0, camY = 0, camZ = 0; if (mode === 'radial') { // Radial Mode targetX = 0; targetY = 0; targetZ = 0; camX = 5; camY = 5; camZ = (idx * spacing) + 15; } else { // Linear/Log Mode targetX = idx * spacing; targetY = 0; targetZ = 0; camX = targetX; camY = 5; camZ = 10; } if (camera) { camera.position.set(camX, camY, camZ); camera.lookAt(targetX, targetY, targetZ); } break; } }; const onKeyUp = (e) => { switch (e.code) { case 'KeyW': STATE.moveState.forward = false; break; case 'KeyS': STATE.moveState.backward = false; break; case 'KeyA': STATE.moveState.left = false; break; case 'KeyD': STATE.moveState.right = false; break; case 'KeyQ': STATE.moveState.up = false; break; case 'KeyE': STATE.moveState.down = false; break; case 'ShiftLeft': STATE.moveState.shift = false; break; } }; const isInteractiveUIElement = (target) => { if (!target) return false; return target.closest( 'input, button, select, option, label, a, textarea, table, tr, td, th, svg, path, ' + '#sidebar, #hud-sidebar, #relief-hud, #dimension-inspector, .app-panel, .modal-overlay, ' + '[class*="panel"], [id*="panel"], [class*="modal"], [id*="modal"], [class*="hud"], [id*="hud"]' ); }; const safeLock = () => { if (!controls || controls.isLocked) return; try { const p = controls.lock(); if (p && typeof p.catch === 'function') { p.catch(() => {}); } } catch (e) {} }; const safeUnlock = () => { if (!controls || !controls.isLocked) return; try { controls.unlock(); } catch (e) {} }; document.addEventListener('keydown', onKeyDown); document.addEventListener('keyup', onKeyUp); document.addEventListener('mousedown', (e) => { if (!STATE.isReady || e.button !== 0) return; if (isInteractiveUIElement(e.target)) return; dragged = false; if (controls.isLocked) { STATE.isTargetLocked = !STATE.isTargetLocked; // Fijar/liberar la lectura del HUD de Relieve sobre el punto apuntado al hacer clic. if (STATE.isTargetLocked) { STATE.reliefPinned = STATE.reliefHover ? { ...STATE.reliefHover } : null; safeUnlock(); } else { STATE.reliefPinned = null; } } else if (STATE.isTargetLocked) { STATE.isTargetLocked = false; STATE.reliefPinned = null; safeLock(); } else { // Drag-to-look fallback initialization isDragging = true; startX = e.clientX; startY = e.clientY; startRotationY = camera.rotation.y; startRotationX = camera.rotation.x; } }); document.addEventListener('mousemove', (e) => { if (!isDragging || controls.isLocked) return; const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { dragged = true; } const sensitivity = 0.002; camera.rotation.y = startRotationY - deltaX * sensitivity; camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, startRotationX - deltaY * sensitivity)); }); document.addEventListener('mouseup', () => { isDragging = false; }); // Touch events for drag-to-look fallback document.addEventListener('touchstart', (e) => { if (!STATE.isReady || controls.isLocked || e.touches.length === 0) return; if (isInteractiveUIElement(e.target)) return; isDragging = true; dragged = false; startX = e.touches[0].clientX; startY = e.touches[0].clientY; startRotationY = camera.rotation.y; startRotationX = camera.rotation.x; }, { passive: true }); document.addEventListener('touchmove', (e) => { if (!isDragging || controls.isLocked || e.touches.length === 0) return; const deltaX = e.touches[0].clientX - startX; const deltaY = e.touches[0].clientY - startY; if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { dragged = true; } const sensitivity = 0.003; camera.rotation.y = startRotationY - deltaX * sensitivity; camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, startRotationX - deltaY * sensitivity)); }, { passive: true }); document.addEventListener('touchend', () => { isDragging = false; }, { passive: true }); // PointerLockError fallback registration document.addEventListener('pointerlockerror', (e) => { console.warn("PointerLock error encountered, using drag-to-look fallback:", e); }); document.addEventListener('click', (e) => { if (!STATE.isReady || controls.isLocked || STATE.isTargetLocked) return; if (dragged) { dragged = false; return; } if (!isInteractiveUIElement(e.target)) { safeLock(); } }); }