/** Shared by the editor and both standalone player presentations. */ export function bindViewInput( canvas: HTMLCanvasElement, runtime: { playing: boolean; paused: boolean; firstPerson: () => boolean; lookBy: (x: number, y: number) => void }, ) { let pointer: { id: number; x: number; y: number } | null = null; const down = (event: PointerEvent) => { if (!runtime.playing || runtime.paused || !runtime.firstPerson()) return; if (event.pointerType === "mouse") { if (event.button !== 0) return; try { void canvas.requestPointerLock?.()?.catch(() => {}); } catch {} } else { pointer = { id: event.pointerId, x: event.clientX, y: event.clientY }; canvas.setPointerCapture(event.pointerId); } }; const move = (event: PointerEvent) => { if (!pointer || event.pointerId !== pointer.id) return; if (!runtime.playing || runtime.paused || !runtime.firstPerson()) { pointer = null; return; } runtime.lookBy((event.clientX - pointer.x) * .004, (pointer.y - event.clientY) * .004); pointer = { id: event.pointerId, x: event.clientX, y: event.clientY }; }; const release = () => { pointer = null; }; canvas.addEventListener("pointerdown", down); canvas.addEventListener("pointermove", move); canvas.addEventListener("pointerup", release); canvas.addEventListener("pointercancel", release); canvas.addEventListener("lostpointercapture", release); canvas.addEventListener("blur", release); return () => { canvas.removeEventListener("pointerdown", down); canvas.removeEventListener("pointermove", move); canvas.removeEventListener("pointerup", release); canvas.removeEventListener("pointercancel", release); canvas.removeEventListener("lostpointercapture", release); canvas.removeEventListener("blur", release); }; }