#include <windows.h>
#include <iostream>
#include <vector>
#include <cmath>
#include <TlHelp32.h>

struct Vector3 {
    float x, y, z;
    
    Vector3(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
    
    Vector3 operator-(const Vector3& other) const {
        return Vector3(x - other.x, y - other.y, z - other.z);
    }
    
    float Length() const {
        return sqrt(x * x + y * y + z * z);
    }
    
    Vector3 Normalized() const {
        float len = Length();
        if (len > 0) {
            return Vector3(x / len, y / len, z / len);
        }
        return *this;
    }
};

struct PlayerData {
    DWORD address;
    Vector3 position;
    Vector3 viewAngle;
    float health;
    char name[32];
    bool isEnemy;
    bool isValid;
};

class GameMemory {
private:
    HANDLE processHandle;
    DWORD clientBase;
    DWORD engineBase;
    
public:
    GameMemory() : processHandle(NULL), clientBase(0), engineBase(0) {}
    
    bool Initialize() {
        DWORD pid = GetProcessIdByName("TslGame.exe");
        if (pid == 0) {
            std::cout << "Game not found" << std::endl;
            return false;
        }
        
        processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        if (processHandle == NULL) {
            std::cout << "Failed to open process" << std::endl;
            return false;
        }
        
        clientBase = GetModuleBase(pid, "client.dll");
        engineBase = GetModuleBase(pid, "engine.dll");
        
        return (clientBase != 0 && engineBase != 0);
    }
    
    DWORD GetProcessIdByName(const char* processName) {
        DWORD pid = 0;
        HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        
        if (snapshot != INVALID_HANDLE_VALUE) {
            PROCESSENTRY32 processEntry;
            processEntry.dwSize = sizeof(processEntry);
            
            if (Process32First(snapshot, &processEntry)) {
                do {
                    if (_stricmp(processEntry.szExeFile, processName) == 0) {
                        pid = processEntry.th32ProcessID;
                        break;
                    }
                } while (Process32Next(snapshot, &processEntry));
            }
            CloseHandle(snapshot);
        }
        return pid;
    }
    
    DWORD GetModuleBase(DWORD pid, const char* moduleName) {
        DWORD base = 0;
        HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
        
        if (snapshot != INVALID_HANDLE_VALUE) {
            MODULEENTRY32 moduleEntry;
            moduleEntry.dwSize = sizeof(moduleEntry);
            
            if (Module32First(snapshot, &moduleEntry)) {
                do {
                    if (_stricmp(moduleEntry.szModule, moduleName) == 0) {
                        base = (DWORD)moduleEntry.modBaseAddr;
                        break;
                    }
                } while (Module32Next(snapshot, &moduleEntry));
            }
            CloseHandle(snapshot);
        }
        return base;
    }
    
    template<typename T>
    T ReadMemory(DWORD address) {
        T value;
        ReadProcessMemory(processHandle, (LPCVOID)address, &value, sizeof(T), NULL);
        return value;
    }
    
    template<typename T>
    bool WriteMemory(DWORD address, T value) {
        return WriteProcessMemory(processHandle, (LPVOID)address, &value, sizeof(T), NULL);
    }
    
    Vector3 ReadVector3(DWORD address) {
        Vector3 vec;
        ReadProcessMemory(processHandle, (LPCVOID)address, &vec, sizeof(Vector3), NULL);
        return vec;
    }
    
    void Close() {
        if (processHandle) {
            CloseHandle(processHandle);
            processHandle = NULL;
        }
    }
};

class AimAssistant {
private:
    GameMemory memory;
    Vector3 localPlayerPosition;
    Vector3 localPlayerViewAngle;
    float aimSmooth = 0.3f;
    float maxAimDistance = 1000.0f;
    bool aimEnabled = false;
    
public:
    bool Initialize() {
        return memory.Initialize();
    }
    
    PlayerData GetLocalPlayer() {
        PlayerData player;
        player.isValid = false;
        
        DWORD localPlayerAddr = memory.ReadMemory<DWORD>(
            memory.clientBase + 0xDE6964
        );
        
        if (localPlayerAddr) {
            player.address = localPlayerAddr;
            player.position = memory.ReadVector3(localPlayerAddr + 0x138);
            player.viewAngle = memory.ReadVector3(localPlayerAddr + 0x158);
            player.health = memory.ReadMemory<float>(localPlayerAddr + 0x100);
            player.isValid = (player.health > 0);
            
            memory.ReadProcessMemory(memory.processHandle,
                (LPCVOID)(localPlayerAddr + 0x225),
                player.name, 32, NULL);
        }
        
        return player;
    }
    
    std::vector<PlayerData> GetEnemyPlayers() {
        std::vector<PlayerData> enemies;
        
        DWORD entityList = memory.ReadMemory<DWORD>(
            memory.clientBase + 0x4E0102C
        );
        
        if (entityList) {
            for (int i = 0; i < 100; i++) {
                DWORD entityAddr = memory.ReadMemory<DWORD>(
                    entityList + i * 0x10
                );
                
                if (entityAddr) {
                    PlayerData enemy;
                    enemy.address = entityAddr;
                    enemy.position = memory.ReadVector3(entityAddr + 0x138);
                    enemy.health = memory.ReadMemory<float>(entityAddr + 0x100);
                    enemy.isValid = (enemy.health > 0);
                    
                    if (enemy.isValid && enemy.health > 0) {
                        float distance = (enemy.position - localPlayerPosition).Length();
                        if (distance < maxAimDistance) {
                            enemy.isEnemy = true;
                            enemies.push_back(enemy);
                        }
                    }
                }
            }
        }
        
        return enemies;
    }
    
    PlayerData FindBestTarget(const std::vector<PlayerData>& enemies) {
        PlayerData bestTarget;
        float bestScore = -1.0f;
        
        for (const auto& enemy : enemies) {
            if (!enemy.isValid) continue;
            
            Vector3 delta = enemy.position - localPlayerPosition;
            float distance = delta.Length();
            
            Vector3 aimAngle = CalculateAngle(localPlayerPosition, enemy.position);
            float fov = CalculateFOV(localPlayerViewAngle, aimAngle);
            
            float score = (maxAimDistance - distance) / maxAimDistance * 0.7f +
                         (180.0f - fov) / 180.0f * 0.3f;
            
            if (score > bestScore) {
                bestScore = score;
                bestTarget = enemy;
            }
        }
        
        return bestTarget;
    }
    
    Vector3 CalculateAngle(const Vector3& source, const Vector3& destination) {
        Vector3 delta = destination - source;
        Vector3 angle;
        
        angle.x = -atan2(delta.z, sqrt(delta.x * delta.x + delta.y * delta.y)) * 180.0f / 3.14159f;
        angle.y = atan2(delta.y, delta.x) * 180.0f / 3.14159f;
        angle.z = 0;
        
        return angle;
    }
    
    float CalculateFOV(const Vector3& viewAngle, const Vector3& aimAngle) {
        Vector3 delta = aimAngle - viewAngle;
        
        delta.x = fmod(delta.x + 180.0f, 360.0f) - 180.0f;
        delta.y = fmod(delta.y + 180.0f, 360.0f) - 180.0f;
        
        return sqrt(delta.x * delta.x + delta.y * delta.y);
    }
    
    void SmoothAim(Vector3& currentAngle, const Vector3& targetAngle) {
        Vector3 delta = targetAngle - currentAngle;
        
        delta.x = fmod(delta.x + 180.0f, 360.0f) - 180.0f;
        delta.y = fmod(delta.y + 180.0f, 360.0f) - 180.0f;
        
        delta.x *= aimSmooth;
        delta.y *= aimSmooth;
        
        currentAngle = currentAngle + delta;
    }
    
    void Update() {
        PlayerData localPlayer = GetLocalPlayer();
        if (!localPlayer.isValid) return;
        
        localPlayerPosition = localPlayer.position;
        localPlayerViewAngle = localPlayer.viewAngle;
        
        std::vector<PlayerData> enemies = GetEnemyPlayers();
        if (enemies.empty()) return;
        
        PlayerData target = FindBestTarget(enemies);
        if (!target.isValid) return;
        
        if (aimEnabled) {
            Vector3 aimAngle = CalculateAngle(localPlayerPosition, target.position);
            SmoothAim(localPlayerViewAngle, aimAngle);
            
            WriteViewAngle(localPlayerViewAngle);
        }
    }
    
    void WriteViewAngle(const Vector3& angle) {
        DWORD clientState = memory.ReadMemory<DWORD>(memory.engineBase + 0x59F19C);
        if (clientState) {
            memory.WriteMemory<Vector3>(clientState + 0x4D90, angle);
        }
    }
    
    void SetAimEnabled(bool enabled) {
        aimEnabled = enabled;
    }
    
    void SetAimSmooth(float smooth) {
        aimSmooth = std::max(0.1f, std::min(1.0f, smooth));
    }
    
    void Cleanup() {
        memory.Close();
    }
};

int main() {
    AimAssistant assistant;
    
    if (!assistant.Initialize()) {
        std::cout << "Failed to initialize assistant" << std::endl;
        return 1;
    }
    
    std::cout << "Aim Assistant Initialized" << std::endl;
    std::cout << "Press F1 to toggle aim" << std::endl;
    std::cout << "Press F2 to increase smooth" << std::endl;
    std::cout << "Press F3 to decrease smooth" << std::endl;
    std::cout << "Press END to exit" << std::endl;
    
    assistant.SetAimSmooth(0.3f);
    bool aimActive = false;
    
    while (true) {
        if (GetAsyncKeyState(VK_F1) & 1) {
            aimActive = !aimActive;
            assistant.SetAimEnabled(aimActive);
            std::cout << "Aim: " << (aimActive ? "ON" : "OFF") << std::endl;
        }
        
        if (GetAsyncKeyState(VK_F2) & 1) {
            static float smooth = 0.3f;
            smooth = std::min(1.0f, smooth + 0.1f);
            assistant.SetAimSmooth(smooth);
            std::cout << "Smooth: " << smooth << std::endl;
        }
        
        if (GetAsyncKeyState(VK_F3) & 1) {
            static float smooth = 0.3f;
            smooth = std::max(0.1f, smooth - 0.1f);
            assistant.SetAimSmooth(smooth);
            std::cout << "Smooth: " << smooth << std::endl;
        }
        
        if (GetAsyncKeyState(VK_END) & 1) {
            break;
        }
        
        if (aimActive) {
            assistant.Update();
        }
        
        Sleep(10);
    }
    
    assistant.Cleanup();
    std::cout << "Program terminated" << std::endl;
    
    return 0;
}