/**
* Простейшие проверки фигур боя (перенос v1): круг и конус удара.
* v2-отличие: конус меряется прямо в мировых горизонтальных осях (x/z) —
* воксели кубические, анизотропной изо-проекции v1 нет (урок №1: всё в
* мировом пространстве, проекция — только на границе рендера).
*/
/** Горизонтальный вектор/точка мира (бой живёт в плоскости x/z). */
export interface Hor {
x: number;
z: number;
}
/** Дистанция между точками в горизонтальной плоскости. */
export function horDist(a: Hor, b: Hor): number {
return Math.hypot(b.x - a.x, b.z - a.z);
}
/** Нормализованный вектор из a в b (нулевая дистанция — (0,0)). */
export function horNorm(a: Hor, b: Hor): Hor {
const dx = b.x - a.x;
const dz = b.z - a.z;
const d = Math.hypot(dx, dz);
return d < 1e-9 ? { x: 0, z: 0 } : { x: dx / d, z: dz / d };
}
/** Точка внутри круга (включая границу). */
export function inCircle(center: Hor, radius: number, point: Hor): boolean {
const dx = point.x - center.x;
const dz = point.z - center.z;
return dx * dx + dz * dz <= radius * radius;
}
/** Угол между векторами a и b (радианы, 0..PI). */
export function angleBetween(a: Hor, b: Hor): number {
const la = Math.hypot(a.x, a.z);
const lb = Math.hypot(b.x, b.z);
if (la === 0 || lb === 0) return 0;
let cos = (a.x * b.x + a.z * b.z) / (la * lb);
cos = Math.min(1, Math.max(-1, cos));
return Math.acos(cos);
}
/**
* Точка внутри конуса: в пределах range от from и угол до dir не больше halfAngle.
* Нулевое направление — конус «схлопывается» в круг по range (угол не проверяется).
*/
export function inCone(from: Hor, dir: Hor, range: number, halfAngle: number, point: Hor): boolean {
const dx = point.x - from.x;
const dz = point.z - from.z;
if (dx * dx + dz * dz > range * range) return false;
if (dir.x === 0 && dir.z === 0) return true;
return angleBetween(dir, { x: dx, z: dz }) <= halfAngle;
}