52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
import type { EnemyAttackTelegraph } from "./enemyAttacks";
|
|
import type { CombatPosition } from "./mobRuntimeRegistry";
|
|
|
|
export interface EnemyTelegraphPlacement {
|
|
readonly sourcePosition: CombatPosition;
|
|
readonly targetPosition: CombatPosition;
|
|
readonly sourceYaw: number;
|
|
}
|
|
|
|
export function enemyTelegraphYaw(placement: EnemyTelegraphPlacement): number {
|
|
const dx = placement.targetPosition[0] - placement.sourcePosition[0];
|
|
const dz = placement.targetPosition[2] - placement.sourcePosition[2];
|
|
return dx * dx + dz * dz > 0.0001 ? Math.atan2(dx, dz) : placement.sourceYaw;
|
|
}
|
|
|
|
export function enemyTelegraphOrigin(
|
|
telegraph: EnemyAttackTelegraph,
|
|
placement: EnemyTelegraphPlacement,
|
|
): CombatPosition {
|
|
return telegraph.anchor === "target"
|
|
? placement.targetPosition
|
|
: placement.sourcePosition;
|
|
}
|
|
|
|
/** Matches the visible warning mesh in the X/Z combat plane. */
|
|
export function enemyTelegraphContainsPosition(
|
|
telegraph: EnemyAttackTelegraph,
|
|
placement: EnemyTelegraphPlacement,
|
|
candidate: CombatPosition,
|
|
): boolean {
|
|
const origin = enemyTelegraphOrigin(telegraph, placement);
|
|
const dx = candidate[0] - origin[0];
|
|
const dz = candidate[2] - origin[2];
|
|
if (telegraph.shape === "circle") {
|
|
const radius = telegraph.radius ?? 0;
|
|
return dx * dx + dz * dz <= radius * radius;
|
|
}
|
|
|
|
const yaw = enemyTelegraphYaw(placement);
|
|
const forwardX = Math.sin(yaw);
|
|
const forwardZ = Math.cos(yaw);
|
|
const forward = dx * forwardX + dz * forwardZ;
|
|
const side = Math.abs(dx * forwardZ - dz * forwardX);
|
|
const length = telegraph.length ?? 0;
|
|
if (forward < 0 || forward > length) return false;
|
|
if (telegraph.shape === "line") return side <= (telegraph.width ?? 0) / 2;
|
|
const distance = Math.hypot(dx, dz);
|
|
if (distance <= 0.0001) return true;
|
|
const halfAngle = ((telegraph.angleDegrees ?? 0) * Math.PI) / 360;
|
|
return Math.acos(Math.min(1, Math.max(-1, forward / distance))) <= halfAngle;
|
|
}
|