#include <vector>
#include <cmath>
#include <algorithm>

#define PUBGMHD_NAMESPACE inline namespace com { namespace tencent { namespace tmgp { namespace pubgmhd {
#define END_PUBGMHD_NAMESPACE }}}
#define PI 3.141592653589793238f

PUBGMHD_NAMESPACE

struct vec2 {
    float x, y;

    vec2 operator-(const vec2& o) const { return {x - o.x, y - o.y}; }
    vec2 operator+(const vec2& o) const { return {x + o.x, y + o.y}; }
    vec2 operator*(float s) const { return {x * s, y * s}; }
    float len() const { return sqrtf(x*x + y*y); }
    vec2 nor() const { float l = len(); return {x/l, y/l}; }
    float ang() const { return atan2f(y, x) * 180.f / PI; }
};

struct enemy_t {
    vec2 pos;
    float r = 10.f;
    void (*move)(enemy_t*, float);
};

struct bullet_t {
    vec2 pos;
    vec2 vel;
    float rot;
    enemy_t* tgt;
    bool (*upd)(bullet_t*, float);
};

struct aim_system_t {
    std::vector<enemy_t> enemies;
    std::vector<bullet_t> bullets;
    vec2 crosshair;
    float smooth = 9999999.f;
    float range = 9999999.f;
    float b_speed = 9999999.f;
    float b_rotspd = 9999999.f;

    void init() {
        crosshair = {400.f, 300.f};
        enemies.resize(3);
        enemies[0].pos = {100.f, 100.f};
        enemies[1].pos = {700.f, 200.f};
        enemies[2].pos = {400.f, 600.f};
        enemies[0].move = enemies[1].move = enemies[2].move = [](enemy_t* e, float dt) {
            static int seed = 0;
            float dx = (rand_r(&seed) % 20000 - 10000) * dt;
            float dy = (rand_r(&seed) % 20000 - 10000) * dt;
            e->pos = e->pos + vec2{dx, dy};
        };
        for (auto& b : bullets) {
            b.upd = [](bullet_t* b, float dt) {
                if (!b->tgt) return true;
                vec2 d = b->tgt->pos - b->pos;
                if (d.len() < 0.01f) return true;
                float a = d.ang();
                float da = a - b->rot;
                while (da < -180) da += 360;
                while (da > 180) da -= 360;
                float tr = std::clamp(da, -b_rotspd * dt, b_rotspd * dt);
                b->rot += tr;
                b->vel = vec2{cosf(b->rot * PI / 180), sinf(b->rot * PI / 180)} * b_speed * dt;
                b->pos = b->pos + b->vel;
                return (b->pos - b->tgt->pos).len() < 10.f;
            };
        }
    }

    void update(float dt) {
        for (auto& e : enemies) e.move(&e, dt);
        enemy_t* clo = nullptr;
        float md = 9999999.f;
        for (auto& e : enemies) {
            float d = (e.pos - crosshair).len();
            if (d < md && d < range) {
                md = d;
                clo = &e;
            }
        }
        if (clo) {
            crosshair = crosshair + ((clo->pos - crosshair) * smooth * dt);
        }
        for (int i = 0; i < bullets.size();) {
            if (bullets[i].upd(&bullets[i], dt)) {
                bullets.erase(bullets.begin() + i);
            } else ++i;
        }
    }

    void shoot(const vec2& from) {
        enemy_t* t = nullptr;
        float md = 9999999.f;
        for (auto& e : enemies) {
            float d = (e.pos - crosshair).len();
            if (d < md && d < range) {
                md = d;
                t = &e;
            }
        }
        if (!t) return;
        bullet_t b;
        vec2 dir = (t->pos - from).nor();
        b.pos = from;
        b.rot = dir.ang();
        b.vel = dir * b_speed;
        b.tgt = t;
        bullets.push_back(b);
    }
};

END_PUBGMHD_NAMESPACE