/**
* Мышь-ввод камеры игры (трек «Режимы камеры»): поворот зажатой ЛКМ —
* drag по канвасу (setPointerCapture держит кнопку даже за пределами),
* колесо — дистанция орбиты (только third person). enabled гасится в
* меню/сумке/катсцене — ставит tick() в main.ts. Конфликтов на канвасе
* нет: слушатели клавиш висят на window, канвас раньше получал только их.
*/
import type { VoxelRenderer } from '@rpg/engine';
export class CameraInput {
private dragging = false;
private px = 0;
private py = 0;
private readonly off: Array<() => void> = [];
/** true — ввод активен (меню/сумка/катсцена гасят). */
enabled = true;
constructor(private readonly canvas: HTMLCanvasElement, private readonly renderer: VoxelRenderer) {
const add = (
t: EventTarget, type: string, f: EventListener, opts?: AddEventListenerOptions,
): void => {
t.addEventListener(type, f, opts);
this.off.push(() => t.removeEventListener(type, f, opts));
};
add(this.canvas, 'pointerdown', (e) => {
const ev = e as PointerEvent;
if (!this.enabled || ev.button !== 0) return;
this.dragging = true;
this.px = ev.clientX;
this.py = ev.clientY;
this.canvas.setPointerCapture(ev.pointerId);
});
add(this.canvas, 'pointermove', (e) => {
if (!this.dragging) return;
const ev = e as PointerEvent;
const dx = ev.clientX - this.px, dy = ev.clientY - this.py;
this.px = ev.clientX;
this.py = ev.clientY;
const o = this.renderer.orbitState();
// скорость как у орбиты ModelViewer: ~0.005 рад/пиксель
this.renderer.setOrbit({ yaw: o.yaw - dx * 0.005, pitch: o.pitch + dy * 0.005 });
});
const end = (): void => { this.dragging = false; };
add(this.canvas, 'pointerup', end);
add(this.canvas, 'pointercancel', end);
add(this.canvas, 'wheel', (e) => {
const ev = e as WheelEvent;
if (!this.enabled || this.renderer.cameraMode() !== 'third') return;
ev.preventDefault(); // страница не скроллится под зумом
const o = this.renderer.orbitState();
this.renderer.setOrbit({ dist: o.dist * (ev.deltaY > 0 ? 1.1 : 0.9) });
}, { passive: false });
}
/** Снять слушатели (страничный рендер демки живёт вечно — для полноты). */
dispose(): void {
for (const f of this.off) f();
this.off.length = 0;
}
}