const TICK_MS = 16;
const GRAVITY = 9.81;
const AIR_RES = 0.02;
const SPEED  = 700;
const MAX_DIST = 9999;
let projectiles = [];
let enemies      = [];

const vec3 = (x, y, z) => ({x, y, z});
const sub  = (a, b) => vec3(a.x - b.x, a.y - b.y, a.z - b.z);
const len  = v => Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
const norm = v => { const l = len(v); return l === 0 ? vec3(0, 0, 0) : vec3(v.x / l, v.y / l, v.z / l); };
const lerp = (a, b, t) => vec3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);

function tick(delta) {
  for (const p of projectiles) {
    const target = findNearestHead(p.pos);
    if (!target) continue;

    const headPos   = predictHead(target, delta);
    const dir       = norm(sub(headPos, p.pos));
    const newVel    = vec3(dir.x * SPEED, dir.y * SPEED, dir.z * SPEED);
    p.vel           = newVel;
    p.pos           = lerp(p.pos, headPos, delta * 144);
  }
}

function findNearestHead(from) {
  let nearest = null, minDist = MAX_DIST;
  for (const e of enemies) {
    const d = len(sub(e.headPos, from));
    if (d < minDist) { minDist = d; nearest = e; }
  }
  return nearest;
}

function predictHead(enemy, delta) {
  const travelTime = len(sub(enemy.headPos, enemy.velocity)) / SPEED;
  return vec3(
    enemy.headPos.x + enemy.velocity.x * travelTime,
    enemy.headPos.y + enemy.velocity.y * travelTime,
    enemy.headPos.z + enemy.velocity.z * travelTime
  );
}

function fireBullet(startPos) {
  projectiles.push({ id: Date.now(), pos: startPos, vel: vec3(0, 0, 0), target: null });
}

enemies.push({ id: 1, headPos: vec3(100, 200, 10), velocity: vec3(2, 1, 0) });
fireBullet(vec3(0, 0, 0));

setInterval(() => tick(16 / 1000), 16);