/**
* Ввод как данные (решение v2): состояние управления — простой объект,
* его заполняют клавиатура демки и виртуальный стик агентного моста.
* Менеджера ввода с DOM-событиями в ядре нет (урок №4: не делать путь
* без потребителя) — кнопки/указатель появятся вместе с потребителями.
*
* Направление движения — в ЭКРАННЫХ осях ([x] вправо, [y] к зрителю):
* игрок думает «вправо относительно экрана». Перевод в мировые оси —
* граница (урок v1 №1: единое мировое пространство, проекция на границе).
*/
/** Состояние управления на шаг логики. */
export interface ControlState {
/** Желаемое движение в экранных осях, компоненты −1..1 (вправо, к зрителю). */
move: readonly [number, number];
}
/**
* Экранное направление → мировая горизонталь при изо-камере с yaw θ
* (θ — угол «target → камера» в плоскости XZ). Экранный «вправо» ложится
* на (sinθ, 0, −cosθ), «к зрителю» — на (cosθ, 0, sinθ); длина результата
* обрезается до 1 (диагональ WASD не быстрее).
*/
export function screenMoveToWorld(
move: readonly [number, number],
yaw: number,
): [number, number] {
const [sx, sy] = move;
const x = sx * Math.sin(yaw) + sy * Math.cos(yaw);
const z = -sx * Math.cos(yaw) + sy * Math.sin(yaw);
const len = Math.hypot(x, z);
if (len <= 1 || len === 0) return [x, z];
return [x / len, z / len];
}