#pragma semicolon 1

#include <amxmodx>
#include <amxmisc>
#include <reapi>
#include <fakemeta>
#include <xs>
#include <nvault>

#define PLUGIN_NAME     "ReAPI Ozel THook & Yonetim Sistemi"
#define PLUGIN_VERS     "2.0"
#define PLUGIN_AUTH     "MochiFoxy"

#define HOOK_DEFAULT_FLAG   ADMIN_IMMUNITY
#define HOOK_ADMIN_FLAG     ADMIN_BAN

// Task ID
enum (+= 1000)
{
    TASKID_HOOK = 1000
};

// Hook Yetki Durumu
enum
{
    HOOK_STATE_DEFAULT = 0,
    HOOK_STATE_ALLOWED,
    HOOK_STATE_BLOCKED
};

// ----------------------------------------------------
// SPRITE (Cizgi) Listesi
// Not: Sunucunuza ozel harici bir .spr eklemek isterseniz asagidaki listeye kolayca ekleyebilirsiniz.
// ----------------------------------------------------
enum _:SPRITE_DATA
{
    SPR_NAME[32],
    SPR_PATH[64],
    SPR_NOISE,
    SPR_FRAMERATE,
    SPR_SCROLL
};

new const g_eSprites[][SPRITE_DATA] = {
    { "Lazer Isini",            "sprites/laserbeam.spr", 0,  15, 20 },
    { "Elektrik / Yildirim",    "sprites/zbeam4.spr",    35, 20, 10 },
    { "Klasik Retro Isin",      "sprites/dot.spr",       0,  1,  0  }
};
new g_iSpriteId[sizeof(g_eSprites)];

// ----------------------------------------------------
// SES (Sound) Listesi - Kulağa hoş gelen Half-Life & CS sesleri
// ----------------------------------------------------
enum _:SOUND_DATA
{
    SND_NAME[32],
    SND_PATH[64]
};

new const g_eSounds[][SOUND_DATA] = {
    { "Klasik Hook (Crossbow)", "weapons/xbow_hit2.wav" },
    { "Lazer Isini (Sci-Fi)",    "debris/beamstart1.wav" },
    { "Elektrik Zap",            "debris/zap1.wav" },
    { "Enerji Dolum",            "items/suitchargeok1.wav" },
    { "Ok Firlatma",             "weapons/xbow_fire1.wav" },
    { "Melodik Zil",             "buttons/bell1.wav" },
    { "Sessiz (Ses Yok)",        "" }
};

// ----------------------------------------------------
// KALINLIK (Width) Listesi
// ----------------------------------------------------
new const g_iWidths[] = { 4, 12, 25, 45 };
new const g_szWidthNames[][] = {
    "Ince (4px)",
    "Normal (12px)",
    "Kalin (25px)",
    "Cok Kalin (45px)"
};

// ----------------------------------------------------
// RENK & ATIS TIPI Listesi
// ----------------------------------------------------
enum _:COLOR_DATA
{
    CLR_NAME[32],
    CLR_R,
    CLR_G,
    CLR_B,
    bool:CLR_IS_RGB
};

new const g_eColors[][COLOR_DATA] = {
    { "Kirmizi",            255, 0,   0,   false },
    { "Mavi",               0,   120, 255, false },
    { "Yesil",              0,   255, 0,   false },
    { "Sari",               255, 255, 0,   false },
    { "Mor / Pembe",        255, 0,   255, false },
    { "Turkuaz (Cyan)",     0,   255, 255, false },
    { "Turuncu",            255, 140, 0,   false },
    { "Beyaz",              255, 255, 255, false },
    { "Gokkusagi (RGB Mod)",0,   0,   0,   true },
    { "Rastgele Renk",      -1,  -1,  -1,  false }
};

// ----------------------------------------------------
// Degiskenler & Oyuncu Verileri
// ----------------------------------------------------
new bool:g_bHooking[MAX_CLIENTS + 1];
new Float:g_vecHookTo[MAX_CLIENTS + 1][3];
new Float:g_fSavedGravity[MAX_CLIENTS + 1];

new g_iHookState[MAX_CLIENTS + 1];           // Admin yetki duruma gore (DEFAULT, ALLOWED, BLOCKED)
new bool:g_bIsHookMuted[MAX_CLIENTS + 1];    // Admin tarafindan trol ses susturuldu mu?

new g_iUserSprite[MAX_CLIENTS + 1];
new g_iUserSound[MAX_CLIENTS + 1];
new g_iUserWidth[MAX_CLIENTS + 1];
new g_iUserColor[MAX_CLIENTS + 1];

new Float:g_fHookSpeed;
new g_iHookUsage;
new g_szAdminFlag[16];
new g_iRoofOnly;

new Array:g_aAdminAuthIDs;
new g_szAdminFile[128];
new g_iVault;

new bool:g_bGlobalSoundMute = false;

// ----------------------------------------------------
// PLUGIN INIT & PRECACHE
// ----------------------------------------------------
public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERS, PLUGIN_AUTH);

    // Hook Tusu Komutlari (+thook / -thook ve +hook / -hook)
    register_clcmd("+thook", "@hook_on");
    register_clcmd("-thook", "@hook_off");
    register_clcmd("+hook", "@hook_on");
    register_clcmd("-hook", "@hook_off");

    // Oyuncu Menusu Komutlari
    register_clcmd("say /hook", "@cmd_hook_menu");
    register_clcmd("say_team /hook", "@cmd_hook_menu");
    register_clcmd("say /thook", "@cmd_hook_menu");
    register_clcmd("say_team /thook", "@cmd_hook_menu");
    register_clcmd("say /hookmenu", "@cmd_hook_menu");
    register_clcmd("say_team /hookmenu", "@cmd_hook_menu");
    register_clcmd("say /thookmenu", "@cmd_hook_menu");
    register_clcmd("say_team /thookmenu", "@cmd_hook_menu");
    register_clcmd("hookmenu", "@cmd_hook_menu");
    register_clcmd("thookmenu", "@cmd_hook_menu");

    // Admin Menusu Komutlari
    register_clcmd("say /hookadmin", "@cmd_admin_menu");
    register_clcmd("say_team /hookadmin", "@cmd_admin_menu");
    register_clcmd("say /thookadmin", "@cmd_admin_menu");
    register_clcmd("say_team /thookadmin", "@cmd_admin_menu");
    register_clcmd("thookadmin", "@cmd_admin_menu");
    register_clcmd("amx_thookmenu", "@cmd_admin_menu");
    register_clcmd("amx_hookmenu", "@cmd_admin_menu");

    // CVAR Ayarlari
    bind_pcvar_float(create_cvar("thook_speed", "8.0", _, "Hook Cekilme Hizi", true, 1.0, true, 30.0), g_fHookSpeed);
    bind_pcvar_num(create_cvar("thook_usage", "1", _, "Hook Kullanim Modu (1: Sadece CT, 3: CT + Admin, 8: Sadece Admin, 9: Herkes)", true, 1.0, true, 9.0), g_iHookUsage);
    bind_pcvar_string(create_cvar("thook_admin_flag", "d", _, "Admin Yetki Bayragi (Orn: d, a, l, u)"), g_szAdminFlag, charsmax(g_szAdminFlag));
    bind_pcvar_num(create_cvar("thook_roof_only", "0", _, "Sadece ustu kapali alanlarda hook atilsin (0: Kapali - Her yerde atar, 1: Acik - Sadece kapali alan)"), g_iRoofOnly);

    // ReAPI Spawn ve Death Hooklari
    RegisterHookChain(RG_CBasePlayer_Spawn, "@CBasePlayer_Spawn", .post = false);
    RegisterHookChain(RG_CBasePlayer_Killed, "@CBasePlayer_Killed", .post = false);

    // Dynamic Admin Array
    g_aAdminAuthIDs = ArrayCreate(64);

    // Admin Dosyasi Yukleme (thook_admins.ini)
    get_configsdir(g_szAdminFile, charsmax(g_szAdminFile));
    format(g_szAdminFile, charsmax(g_szAdminFile), "%s/thook_admins.ini", g_szAdminFile);
    load_admin_file();

    // nVault
    g_iVault = nvault_open("thook_settings");
}

public plugin_precache()
{
    // Spritelari Precache Et
    for(new i = 0; i < sizeof(g_eSprites); i++)
    {
        g_iSpriteId[i] = precache_model(g_eSprites[i][SPR_PATH]);
    }

    // Sesleri Precache Et
    for(new i = 0; i < sizeof(g_eSounds); i++)
    {
        if(g_eSounds[i][SND_PATH][0])
        {
            precache_sound(g_eSounds[i][SND_PATH]);
        }
    }
}

public plugin_end()
{
    if(g_aAdminAuthIDs != Invalid_Array)
    {
        ArrayDestroy(g_aAdminAuthIDs);
    }

    if(g_iVault != INVALID_HANDLE)
    {
        nvault_close(g_iVault);
    }
}

// ----------------------------------------------------
// STEAM / VALVE ID ADMIN DOSYASI YONETIMI (thook_admins.ini)
// ----------------------------------------------------
load_admin_file()
{
    ArrayClear(g_aAdminAuthIDs);

    if(!file_exists(g_szAdminFile))
    {
        new iFile = fopen(g_szAdminFile, "wt");
        if(iFile)
        {
            fprintf(iFile, "; ==============================================^n");
            fprintf(iFile, "; THook Ozel Admin SteamID / ValveID Listesi^n");
            fprintf(iFile, "; Her satira yetki vermek istediginiz bir SteamID yaziniz.^n");
            fprintf(iFile, "; Not: Standart adminler (users.ini / web panel) zaten otomatik taninir.^n");
            fprintf(iFile, "; Ornekler:^n");
            fprintf(iFile, "; STEAM_0:0:12345678^n");
            fprintf(iFile, "; VALVE_0:0:87654321^n");
            fprintf(iFile, "; ==============================================^n^n");
            fprintf(iFile, "STEAM_0:0:12345678^n");
            fclose(iFile);
        }
    }

    new iFile = fopen(g_szAdminFile, "rt");
    if(iFile)
    {
        new szLine[128], szAuthId[64];
        while(fgets(iFile, szLine, charsmax(szLine)) > 0)
        {
            trim(szLine);

            if(!szLine[0] || szLine[0] == ';' || szLine[0] == '#')
            {
                continue;
            }

            parse(szLine, szAuthId, charsmax(szAuthId));
            remove_quotes(szAuthId);

            if(szAuthId[0])
            {
                ArrayPushString(g_aAdminAuthIDs, szAuthId);
            }
        }
        fclose(iFile);
    }
}

bool:is_user_thook_admin(const id)
{
    if(!is_user_connected(id))
    {
        return false;
    }

    // 1. Admin Yetki Bayragi Kontrolu (users.ini, admin.amxx ve web panel yetkileri)
    new iAdminFlagBit = read_flags(g_szAdminFlag);
    if((get_user_flags(id) & iAdminFlagBit) || (get_user_flags(id) & HOOK_ADMIN_FLAG) || (get_user_flags(id) & ADMIN_IMMUNITY) || (get_user_flags(id) & ADMIN_RCON))
    {
        return true;
    }

    // 2. SteamID / ValveID Kontrolu (thook_admins.ini icindeki ozel yetkili kayitlari)
    new szAuthId[64];
    get_user_authid(id, szAuthId, charsmax(szAuthId));

    new iSize = ArraySize(g_aAdminAuthIDs);
    new szStored[64];
    for(new i = 0; i < iSize; i++)
    {
        ArrayGetString(g_aAdminAuthIDs, i, szStored, charsmax(szStored));
        if(equali(szAuthId, szStored))
        {
            return true;
        }
    }

    return false;
}

// ----------------------------------------------------
// OYUNCU GIRIS / CIKIS & AYAR KAYIT (nVault)
// ----------------------------------------------------
stock get_user_save_key(const id, szKey[], const iLen)
{
    get_user_authid(id, szKey, iLen);
    // Non-Steam / LAN ID cakismasini engellemek icin yedek olarak nick kullan
    if(containi(szKey, "LAN") != -1 || szKey[0] == EOS)
    {
        get_user_name(id, szKey, iLen);
    }
}

public client_authorized(id)
{
    load_user_data(id);
}

public client_putinserver(id)
{
    g_bHooking[id] = false;
    g_iHookState[id] = HOOK_STATE_DEFAULT;
    g_bIsHookMuted[id] = false;
    g_fSavedGravity[id] = 1.0;
    load_user_data(id);
}

public client_disconnected(id)
{
    if(g_bHooking[id])
    {
        @hook_off(id);
    }
    save_user_data(id);

    g_bHooking[id] = false;
    g_iHookState[id] = HOOK_STATE_DEFAULT;
    g_bIsHookMuted[id] = false;
}

load_user_data(const id)
{
    if(!is_user_connected(id) || is_user_bot(id) || is_user_hltv(id))
    {
        g_iUserSprite[id] = 0; // Lazer
        g_iUserSound[id]  = 0; // Klasik Hook
        g_iUserWidth[id]  = 1; // Normal (12px)
        g_iUserColor[id]  = 0; // Kirmizi Lazer
        return;
    }

    // Varsayilan Ayarlar
    g_iUserSprite[id] = 0; // Lazer
    g_iUserSound[id]  = 0; // Klasik Hook
    g_iUserWidth[id]  = 1; // Normal (12px)
    g_iUserColor[id]  = 0; // Kirmizi Lazer

    if(g_iVault == INVALID_HANDLE)
    {
        return;
    }

    new szSaveKey[64], szData[64];
    get_user_save_key(id, szSaveKey, charsmax(szSaveKey));

    if(nvault_get(g_iVault, szSaveKey, szData, charsmax(szData)))
    {
        new szSpr[8], szSnd[8], szWdt[8], szClr[8];
        parse(szData, szSpr, charsmax(szSpr), szSnd, charsmax(szSnd), szWdt, charsmax(szWdt), szClr, charsmax(szClr));

        new spr = clamp(str_to_num(szSpr), 0, sizeof(g_eSprites) - 1);
        new snd = clamp(str_to_num(szSnd), 0, sizeof(g_eSounds) - 1);
        new wdt = clamp(str_to_num(szWdt), 0, sizeof(g_iWidths) - 1);
        new clr = clamp(str_to_num(szClr), 0, sizeof(g_eColors) - 1);

        g_iUserSprite[id] = spr;
        g_iUserSound[id]  = snd;
        g_iUserWidth[id]  = wdt;
        g_iUserColor[id]  = clr;
    }
}

save_user_data(const id)
{
    if(!is_user_connected(id) || is_user_bot(id) || is_user_hltv(id) || g_iVault == INVALID_HANDLE)
    {
        return;
    }

    new szSaveKey[64], szData[64];
    get_user_save_key(id, szSaveKey, charsmax(szSaveKey));

    formatex(szData, charsmax(szData), "%d %d %d %d", g_iUserSprite[id], g_iUserSound[id], g_iUserWidth[id], g_iUserColor[id]);
    nvault_set(g_iVault, szSaveKey, szData);
}

// ----------------------------------------------------
// HOOK YETKI VE KONTROLLER
// ----------------------------------------------------
bool:can_user_hook(const id)
{
    if(!is_user_alive(id))
    {
        return false;
    }

    // 1. Admin tarafindan ozellikle YASAKLANMISSA (Blocked) asla kullanamaz!
    if(g_iHookState[id] == HOOK_STATE_BLOCKED)
    {
        return false;
    }

    // 2. Admin tarafindan ozellikle VERILMISSE (Allowed) her zaman kullanabilir!
    if(g_iHookState[id] == HOOK_STATE_ALLOWED)
    {
        return true;
    }

    // 3. Admin ise varsayilan olarak kullanabilir
    if(is_user_thook_admin(id))
    {
        return true;
    }

    // 4. Cvar kurallarina gore kontrol
    return is_continue(id, g_iHookUsage, HOOK_DEFAULT_FLAG);
}

bool:is_continue(const id, const usage, const flag)
{
    if(!is_user_alive(id))
    {
        return false;
    }

    switch(usage)
    {
        case 1: return bool:(get_member(id, m_iTeam) == TEAM_CT);
        case 2: return bool:(get_member(id, m_iTeam) == TEAM_CT && get_entvar(id, var_takedamage) == DAMAGE_NO);
        case 3: return bool:((get_member(id, m_iTeam) == TEAM_CT || get_user_flags(id) & flag));
        case 4: return bool:((get_member(id, m_iTeam) == TEAM_CT && get_entvar(id, var_takedamage) == DAMAGE_NO) || get_user_flags(id) & flag);
        case 5: return bool:(get_member(id, m_iTeam) == TEAM_CT && get_user_flags(id) & flag);
        case 6: return bool:(get_member(id, m_iTeam) == TEAM_CT && get_entvar(id, var_takedamage) == DAMAGE_NO && get_user_flags(id) & flag);
        case 7: return bool:(get_entvar(id, var_takedamage) == DAMAGE_NO);
        case 8: return bool:(get_user_flags(id) & flag);
        case 9: return true;
    }
    return false;
}

stock Float:useable(const id)
{
    new Float:vOrigin[3], Float:fDist;
    get_entvar(id, var_origin, vOrigin);
    fDist = vOrigin[2];

    new iIterations = 0;
    while(engfunc(EngFunc_PointContents, vOrigin) == CONTENTS_EMPTY && vOrigin[2] < 4096.0 && ++iIterations < 500)
    {
        vOrigin[2] += 5.0;
    }

    if(engfunc(EngFunc_PointContents, vOrigin) == CONTENTS_SKY)
    {
        return (vOrigin[2] - fDist);
    }

    return 0.0;
}

// ----------------------------------------------------
// HOOK HAREKET VE EFEKT MOTORU
// ----------------------------------------------------
@CBasePlayer_Spawn(const id)
{
    @hook_off(id);
}

@CBasePlayer_Killed(const id)
{
    @hook_off(id);
}

@hook_on(const id)
{
    if(!is_user_alive(id))
    {
        return PLUGIN_HANDLED;
    }

    if(!can_user_hook(id))
    {
        if(get_member(id, m_iTeam) != TEAM_CT && !is_user_thook_admin(id))
        {
            client_print_color(id, print_team_default, "^4[Hook] ^3Hook yetkisi sadece ^4CT (Anti-Terorist) ^3takimina ozeldir!");
        }
        else
        {
            client_print_color(id, print_team_default, "^4[Hook] ^3Hook kullanma yetkiniz bulunmuyor.");
        }
        return PLUGIN_HANDLED;
    }

    if(g_bHooking[id])
    {
        return PLUGIN_HANDLED;
    }

    // Ustu kapali kontrolu (thook_roof_only cvari ile kontrol edilir, varsayilan: 0 yani her yerde serbest)
    if(g_iRoofOnly && useable(id) > 0.0)
    {
        client_print_color(id, print_team_default, "^4[Hook] ^3Sadece ^1ustu kapali ^3alanlarda kullanabilirsiniz.");
        return PLUGIN_HANDLED;
    }

    // Hedef Koordinati ve Mevcut Yercekimi Degerini Kaydet
    static iOrigin[3], Float:fOrigin[3];
    get_user_origin(id, iOrigin, 3);
    xs_vec_set_float(iOrigin, fOrigin);
    xs_vec_copy(fOrigin, g_vecHookTo[id]);

    get_entvar(id, var_gravity, g_fSavedGravity[id]);
    set_entvar(id, var_gravity, 0.0);

    g_bHooking[id] = true;

    set_task(0.1, "@hook_prethink", id + TASKID_HOOK, .flags = "b");
    @hook_prethink(id + TASKID_HOOK);

    // Ses Calma
    new iSndIndex = clamp(g_iUserSound[id], 0, sizeof(g_eSounds) - 1);
    if(!g_bIsHookMuted[id] && !g_bGlobalSoundMute && iSndIndex < (sizeof(g_eSounds) - 1))
    {
        if(g_eSounds[iSndIndex][SND_PATH][0])
        {
            emit_sound(id, CHAN_AUTO, g_eSounds[iSndIndex][SND_PATH], VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
        }
    }

    return PLUGIN_HANDLED;
}

@hook_off(const id)
{
    if(task_exists(id + TASKID_HOOK))
    {
        remove_task(id + TASKID_HOOK);
    }
    
    g_bHooking[id] = false;

    if(is_user_alive(id))
    {
        // Oyuncunun onceki ozel yercekimi degerini koru (Zombie Plague / Deathrun uyumlu)
        set_entvar(id, var_gravity, g_fSavedGravity[id] > 0.0 ? g_fSavedGravity[id] : 1.0);
    }

    return PLUGIN_HANDLED;
}

@hook_prethink(TaskId)
{
    new id = TaskId - TASKID_HOOK;

    if(!is_user_alive(id) || !g_bHooking[id] || !can_user_hook(id))
    {
        @hook_off(id);
        return;
    }

    // Renk Belirleme
    new r = 255, g = 0, b = 0;
    new iClrIndex = clamp(g_iUserColor[id], 0, sizeof(g_eColors) - 1);

    if(g_eColors[iClrIndex][CLR_IS_RGB])
    {
        get_rainbow_color(r, g, b);
    }
    else if(g_eColors[iClrIndex][CLR_R] == -1)
    {
        get_random_color(r, g, b);
    }
    else
    {
        r = g_eColors[iClrIndex][CLR_R];
        g = g_eColors[iClrIndex][CLR_G];
        b = g_eColors[iClrIndex][CLR_B];
    }

    new iSpriteIndex = clamp(g_iUserSprite[id], 0, sizeof(g_eSprites) - 1);
    new iWidthIndex  = clamp(g_iUserWidth[id], 0, sizeof(g_iWidths) - 1);
    new iWidthValue  = g_iWidths[iWidthIndex];

    // Beam Mesaji Cizdirme (Sprite Ozel Efekti ile)
    create_hook_beam(id, g_vecHookTo[id], g_iSpriteId[iSpriteIndex], iWidthValue, g_eSprites[iSpriteIndex][SPR_NOISE], g_eSprites[iSpriteIndex][SPR_FRAMERATE], g_eSprites[iSpriteIndex][SPR_SCROLL], r, g, b);

    // Hareketi Saglama (Cekilme Fizigi - Sifira Bolunme / NaN Korumali)
    static Float:origin1_f[3];
    get_entvar(id, var_origin, origin1_f);

    static Float:velocity[3];
    xs_vec_sub_scaled_fix(g_vecHookTo[id], origin1_f, 3.0, velocity);

    static Float:y;
    y = velocity[0] * velocity[0] + velocity[1] * velocity[1] + velocity[2] * velocity[2];

    if(y <= 0.0)
    {
        @hook_off(id);
        return;
    }

    new Float:fDistance = floatsqroot(y);
    if(fDistance < 10.0)
    {
        @hook_off(id);
        return;
    }

    static Float:x;
    x = (g_fHookSpeed * 120.0) / fDistance;
    xs_vec_mul_scalar(velocity, x, velocity);

    set_entvar(id, var_velocity, velocity);
}

create_hook_beam(const id, const Float:coord[3], const iSprite, const iWidth, const iNoise, const iFramerate, const iScroll, const R, const G, const B)
{
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(1);              // TE_BEAMENTPOINTS
    write_short(id);            // Baslangic Entity
    write_coord_f(coord[0]);    // Bitis X
    write_coord_f(coord[1]);    // Bitis Y
    write_coord_f(coord[2]);    // Bitis Z
    write_short(iSprite);       // Sprite ID
    write_byte(1);              // Startframe
    write_byte(iFramerate);     // Framerate (animasyon)
    write_byte(1);              // Life (0.1 sn - akici render)
    write_byte(iWidth);         // Kalinlik
    write_byte(iNoise);         // Noise (Elektrik zigzag / dalgalanma)
    write_byte(R);              // Red
    write_byte(G);              // Green
    write_byte(B);              // Blue
    write_byte(220);            // Parlaklik (Brightness)
    write_byte(iScroll);        // Scroll speed
    message_end();
}

stock get_rainbow_color(&r, &g, &b)
{
    static iStep = 0;
    iStep = (iStep + 1) % 6;
    switch(iStep)
    {
        case 0: { r = 255; g = 0;   b = 0;   }
        case 1: { r = 255; g = 140; b = 0;   }
        case 2: { r = 255; g = 255; b = 0;   }
        case 3: { r = 0;   g = 255; b = 50;  }
        case 4: { r = 0;   g = 180; b = 255; }
        case 5: { r = 200; g = 0;   b = 255; }
    }
}

stock get_random_color(&r, &g, &b)
{
    r = random_num(60, 255);
    g = random_num(60, 255);
    b = random_num(60, 255);
}

xs_vec_set_float(const in[], Float:out[])
{
    out[0] = float(in[0]);
    out[1] = float(in[1]);
    out[2] = float(in[2]);
}

xs_vec_sub_scaled_fix(const Float:in1[], const Float:in2[], Float:scalar, Float:out[])
{
    out[0] = (in1[0] - in2[0]) * scalar;
    out[1] = (in1[1] - in2[1]) * scalar;
    out[2] = (in1[2] - in2[2]) * scalar;
}

// ----------------------------------------------------
// OYUNCU KISISELLESTIRME MENUSU
// ----------------------------------------------------
@cmd_hook_menu(const id)
{
    if(!is_user_connected(id))
    {
        return PLUGIN_HANDLED;
    }

    // Menuyu sadece CT takimindakiler veya yetkili adminler acabilir
    if(get_member(id, m_iTeam) != TEAM_CT && !is_user_thook_admin(id))
    {
        client_print_color(id, print_team_default, "^4[Hook] ^3Bu menu sadece ^4CT (Anti-Terorist) ^3takimina ozeldir!");
        return PLUGIN_HANDLED;
    }

    display_main_menu(id);
    return PLUGIN_HANDLED;
}

display_main_menu(const id)
{
    // Menuyu sadece CT takimindakiler veya yetkili adminler acabilir
    if(get_member(id, m_iTeam) != TEAM_CT && !is_user_thook_admin(id))
    {
        client_print_color(id, print_team_default, "^4[Hook] ^3Bu menu sadece ^4CT (Anti-Terorist) ^3takimina ozeldir!");
        return;
    }

    new szTitle[128];
    formatex(szTitle, charsmax(szTitle), "\yCT Hook Kisisellestirme Menusu");

    new iMenu = menu_create(szTitle, "@handle_main_menu");

    new szItem[128];
    new iSpr = clamp(g_iUserSprite[id], 0, sizeof(g_eSprites) - 1);
    new iSnd = clamp(g_iUserSound[id], 0, sizeof(g_eSounds) - 1);
    new iWdt = clamp(g_iUserWidth[id], 0, sizeof(g_iWidths) - 1);
    new iClr = clamp(g_iUserColor[id], 0, sizeof(g_eColors) - 1);

    formatex(szItem, charsmax(szItem), "Isin (Sprite) Secimi \d[\y%s\d]", g_eSprites[iSpr][SPR_NAME]);
    menu_additem(iMenu, szItem, "1");

    formatex(szItem, charsmax(szItem), "Ses Secimi \d[\y%s\d]", g_eSounds[iSnd][SND_NAME]);
    menu_additem(iMenu, szItem, "2");

    formatex(szItem, charsmax(szItem), "Cizgi Kalinligi \d[\y%s\d]", g_szWidthNames[iWdt]);
    menu_additem(iMenu, szItem, "3");

    formatex(szItem, charsmax(szItem), "Renk & Atis Tipi \d[\y%s\d]", g_eColors[iClr][CLR_NAME]);
    menu_additem(iMenu, szItem, "4");

    if(is_user_thook_admin(id))
    {
        menu_additem(iMenu, "\r[ADMIN] \yYetkili Yonetim Paneline Git", "5");
    }

    menu_setprop(iMenu, MPROP_EXITNAME, "Cikis");
    menu_display(id, iMenu, 0);
}

@handle_main_menu(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        return PLUGIN_HANDLED;
    }

    if(get_member(id, m_iTeam) != TEAM_CT && !is_user_thook_admin(id))
    {
        menu_destroy(iMenu);
        client_print_color(id, print_team_default, "^4[Hook] ^3Bu menu sadece ^4CT (Anti-Terorist) ^3takimina ozeldir!");
        return PLUGIN_HANDLED;
    }

    new szData[6], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iChoice = str_to_num(szData);
    switch(iChoice)
    {
        case 1: display_sprite_menu(id);
        case 2: display_sound_menu(id);
        case 3: display_width_menu(id);
        case 4: display_color_menu(id);
        case 5:
        {
            if(is_user_thook_admin(id))
            {
                display_admin_main_menu(id);
            }
        }
    }

    return PLUGIN_HANDLED;
}

// 1. SPRITE MENUSU
display_sprite_menu(const id)
{
    new iMenu = menu_create("\yIsin (Sprite) Modeli Seciniz:", "@handle_sprite_menu");
    new szItem[64], szNum[4];

    for(new i = 0; i < sizeof(g_eSprites); i++)
    {
        if(i == g_iUserSprite[id])
        {
            formatex(szItem, charsmax(szItem), "\y%s \r(Secili)", g_eSprites[i][SPR_NAME]);
        }
        else
        {
            formatex(szItem, charsmax(szItem), "\w%s", g_eSprites[i][SPR_NAME]);
        }
        num_to_str(i, szNum, charsmax(szNum));
        menu_additem(iMenu, szItem, szNum);
    }

    menu_setprop(iMenu, MPROP_BACKNAME, "Geri");
    menu_setprop(iMenu, MPROP_NEXTNAME, "Ileri");
    menu_setprop(iMenu, MPROP_EXITNAME, "Ana Menuye Don");
    menu_display(id, iMenu, 0);
}

@handle_sprite_menu(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        display_main_menu(id);
        return PLUGIN_HANDLED;
    }

    new szData[6], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iIndex = str_to_num(szData);
    if(iIndex >= 0 && iIndex < sizeof(g_eSprites))
    {
        g_iUserSprite[id] = iIndex;
        save_user_data(id);
        client_print_color(id, print_team_default, "^4[THook] ^3Yeni isin modeli secildi: ^4%s", g_eSprites[iIndex][SPR_NAME]);
    }

    display_main_menu(id);
    return PLUGIN_HANDLED;
}

// 2. SES MENUSU
display_sound_menu(const id)
{
    new iMenu = menu_create("\yHook Sesi Seciniz:", "@handle_sound_menu");
    new szItem[64], szNum[4];

    for(new i = 0; i < sizeof(g_eSounds); i++)
    {
        if(i == g_iUserSound[id])
        {
            formatex(szItem, charsmax(szItem), "\y%s \r(Secili)", g_eSounds[i][SND_NAME]);
        }
        else
        {
            formatex(szItem, charsmax(szItem), "\w%s", g_eSounds[i][SND_NAME]);
        }
        num_to_str(i, szNum, charsmax(szNum));
        menu_additem(iMenu, szItem, szNum);
    }

    menu_setprop(iMenu, MPROP_BACKNAME, "Geri");
    menu_setprop(iMenu, MPROP_NEXTNAME, "Ileri");
    menu_setprop(iMenu, MPROP_EXITNAME, "Ana Menuye Don");
    menu_display(id, iMenu, 0);
}

@handle_sound_menu(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        display_main_menu(id);
        return PLUGIN_HANDLED;
    }

    new szData[6], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iIndex = str_to_num(szData);
    if(iIndex >= 0 && iIndex < sizeof(g_eSounds))
    {
        g_iUserSound[id] = iIndex;
        save_user_data(id);

        if(g_eSounds[iIndex][SND_PATH][0])
        {
            emit_sound(id, CHAN_AUTO, g_eSounds[iIndex][SND_PATH], VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
        }

        client_print_color(id, print_team_default, "^4[THook] ^3Yeni ses secildi: ^4%s", g_eSounds[iIndex][SND_NAME]);
    }

    display_main_menu(id);
    return PLUGIN_HANDLED;
}

// 3. KALINLIK MENUSU
display_width_menu(const id)
{
    new iMenu = menu_create("\yCizgi Kalinligi Seciniz:", "@handle_width_menu");
    new szItem[64], szNum[4];

    for(new i = 0; i < sizeof(g_iWidths); i++)
    {
        if(i == g_iUserWidth[id])
        {
            formatex(szItem, charsmax(szItem), "\y%s \r(Secili)", g_szWidthNames[i]);
        }
        else
        {
            formatex(szItem, charsmax(szItem), "\w%s", g_szWidthNames[i]);
        }
        num_to_str(i, szNum, charsmax(szNum));
        menu_additem(iMenu, szItem, szNum);
    }

    menu_setprop(iMenu, MPROP_EXITNAME, "Ana Menuye Don");
    menu_display(id, iMenu, 0);
}

@handle_width_menu(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        display_main_menu(id);
        return PLUGIN_HANDLED;
    }

    new szData[6], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iIndex = str_to_num(szData);
    if(iIndex >= 0 && iIndex < sizeof(g_iWidths))
    {
        g_iUserWidth[id] = iIndex;
        save_user_data(id);
        client_print_color(id, print_team_default, "^4[THook] ^3Cizgi kalinligi ayarlandi: ^4%s", g_szWidthNames[iIndex]);
    }

    display_main_menu(id);
    return PLUGIN_HANDLED;
}

// 4. RENK & ATIS TIPI MENUSU
display_color_menu(const id)
{
    new iMenu = menu_create("\yHook Rengi & Modu Seciniz:", "@handle_color_menu");
    new szItem[64], szNum[4];

    for(new i = 0; i < sizeof(g_eColors); i++)
    {
        if(i == g_iUserColor[id])
        {
            formatex(szItem, charsmax(szItem), "\y%s \r(Secili)", g_eColors[i][CLR_NAME]);
        }
        else
        {
            formatex(szItem, charsmax(szItem), "\w%s", g_eColors[i][CLR_NAME]);
        }
        num_to_str(i, szNum, charsmax(szNum));
        menu_additem(iMenu, szItem, szNum);
    }

    menu_setprop(iMenu, MPROP_BACKNAME, "Geri");
    menu_setprop(iMenu, MPROP_NEXTNAME, "Ileri");
    menu_setprop(iMenu, MPROP_EXITNAME, "Ana Menuye Don");
    menu_display(id, iMenu, 0);
}

@handle_color_menu(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        display_main_menu(id);
        return PLUGIN_HANDLED;
    }

    new szData[6], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iIndex = str_to_num(szData);
    if(iIndex >= 0 && iIndex < sizeof(g_eColors))
    {
        g_iUserColor[id] = iIndex;
        save_user_data(id);
        client_print_color(id, print_team_default, "^4[THook] ^3Hook rengi/modu secildi: ^4%s", g_eColors[iIndex][CLR_NAME]);
    }

    display_main_menu(id);
    return PLUGIN_HANDLED;
}

// ----------------------------------------------------
// ADMIN YONETIM PANELI (SteamID / ValveID & Yetkili)
// ----------------------------------------------------
@cmd_admin_menu(const id)
{
    if(!is_user_connected(id))
    {
        return PLUGIN_HANDLED;
    }

    if(!is_user_thook_admin(id))
    {
        client_print_color(id, print_team_default, "^4[THook] ^3Bu menuye erismek icin ^4Yetkili / Admin ^3olmalisiniz!");
        return PLUGIN_HANDLED;
    }

    display_admin_main_menu(id);
    return PLUGIN_HANDLED;
}

display_admin_main_menu(const id)
{
    new szTitle[128];
    formatex(szTitle, charsmax(szTitle), "\r[THook Admin Paneli]\y Yonetim Menusu");

    new iMenu = menu_create(szTitle, "@handle_admin_main_menu");

    menu_additem(iMenu, "Oyuncuya Hook Yetkisi Ver / Al", "1");
    menu_additem(iMenu, "Oyuncunun Hook Sesini Sustur (Trol Engelle)", "2");
    menu_additem(iMenu, "\yToplu Islem: \wHerkese Hook \y[VER]", "3");
    menu_additem(iMenu, "\yToplu Islem: \wHerkesten Hook \r[AL]", "4");

    if(g_bGlobalSoundMute)
    {
        menu_additem(iMenu, "\yTum Sunucu Hook Sesi: \r[SUSTURULDU - AC]", "5");
    }
    else
    {
        menu_additem(iMenu, "\yTum Sunucu Hook Sesi: \w[ACIK - KILITLE]", "5");
    }

    menu_additem(iMenu, "\dAdmin Listesini Yenile (thook_admins.ini)", "6");

    menu_setprop(iMenu, MPROP_EXITNAME, "Cikis");
    menu_display(id, iMenu, 0);
}

@handle_admin_main_menu(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        return PLUGIN_HANDLED;
    }

    new szData[6], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iChoice = str_to_num(szData);
    switch(iChoice)
    {
        case 1: display_admin_hook_grant_menu(id);
        case 2: display_admin_mute_menu(id);
        case 3:
        {
            for(new i = 1; i <= MaxClients; i++)
            {
                if(is_user_connected(i))
                {
                    g_iHookState[i] = HOOK_STATE_ALLOWED;
                }
            }
            client_print_color(0, print_team_default, "^4[THook Admin] ^3Yetkili ^4%n ^3tarafindan herkese ^4THook Yetkisi ^3verildi!", id);
            display_admin_main_menu(id);
        }
        case 4:
        {
            for(new i = 1; i <= MaxClients; i++)
            {
                if(is_user_connected(i))
                {
                    g_iHookState[i] = HOOK_STATE_BLOCKED;
                    if(g_bHooking[i])
                    {
                        @hook_off(i);
                    }
                }
            }
            client_print_color(0, print_team_default, "^4[THook Admin] ^3Yetkili ^4%n ^3tarafindan herkesten ^1THook Yetkisi ^3alindi!", id);
            display_admin_main_menu(id);
        }
        case 5:
        {
            g_bGlobalSoundMute = !g_bGlobalSoundMute;
            client_print_color(0, print_team_default, "^4[THook Admin] ^3Yetkili ^4%n ^3tarafindan tum hook sesleri ^4%s^3.", id, g_bGlobalSoundMute ? "susturuldu" : "acildi");
            display_admin_main_menu(id);
        }
        case 6:
        {
            load_admin_file();
            client_print_color(id, print_team_default, "^4[THook Admin] ^3thook_admins.ini dosyasi basariyla yeniden yuklendi! ^4(Toplam: %d Kayit)", ArraySize(g_aAdminAuthIDs));
            display_admin_main_menu(id);
        }
    }

    return PLUGIN_HANDLED;
}

// 1. OYUNCU HOOK VER / AL MENUSU
display_admin_hook_grant_menu(const id)
{
    new iMenu = menu_create("\yOyuncu Hook Yetki Yonetimi:", "@handle_admin_hook_grant");
    new szItem[128], szNum[8];

    for(new i = 1; i <= MaxClients; i++)
    {
        if(!is_user_connected(i))
        {
            continue;
        }

        num_to_str(i, szNum, charsmax(szNum));
        new bool:bHasAccess = can_user_hook(i);

        if(bHasAccess)
        {
            formatex(szItem, charsmax(szItem), "%n \d[\yYETKI VAR\d]", i);
        }
        else
        {
            formatex(szItem, charsmax(szItem), "%n \d[\rYETKI YOK\d]", i);
        }

        menu_additem(iMenu, szItem, szNum);
    }

    menu_setprop(iMenu, MPROP_BACKNAME, "Geri");
    menu_setprop(iMenu, MPROP_NEXTNAME, "Ileri");
    menu_setprop(iMenu, MPROP_EXITNAME, "Geri Don");
    menu_display(id, iMenu, 0);
}

@handle_admin_hook_grant(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        display_admin_main_menu(id);
        return PLUGIN_HANDLED;
    }

    new szData[8], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iTarget = str_to_num(szData);
    if(is_user_connected(iTarget))
    {
        // Eger su an yetkisi varsa -> YASAKLA (BLOCKED)
        if(can_user_hook(iTarget))
        {
            g_iHookState[iTarget] = HOOK_STATE_BLOCKED;
            if(g_bHooking[iTarget])
            {
                @hook_off(iTarget);
            }
            client_print_color(iTarget, print_team_default, "^4[THook] ^3Yetkili ^4%n ^3tarafindan THook yetkiniz ^1alindi^3.", id);
            client_print_color(id, print_team_default, "^4[THook Admin] ^4%n ^3adli oyuncunun THook yetkisi ^1alindi^3.", iTarget);
        }
        else // Eger yetkisi yoksa -> VER (ALLOWED)
        {
            g_iHookState[iTarget] = HOOK_STATE_ALLOWED;
            client_print_color(iTarget, print_team_default, "^4[THook] ^3Yetkili ^4%n ^3tarafindan size ^4THook Yetkisi ^3verildi!", id);
            client_print_color(id, print_team_default, "^4[THook Admin] ^4%n ^3adli oyuncuya THook yetkisi ^4verildi^3.", iTarget);
        }
    }

    display_admin_hook_grant_menu(id);
    return PLUGIN_HANDLED;
}

// 2. TROL SES SUSTURMA (MUTE) MENUSU
display_admin_mute_menu(const id)
{
    new iMenu = menu_create("\yOyuncu Hook Ses Susturma (Trol Engeli):", "@handle_admin_mute");
    new szItem[128], szNum[8];

    for(new i = 1; i <= MaxClients; i++)
    {
        if(!is_user_connected(i))
        {
            continue;
        }

        num_to_str(i, szNum, charsmax(szNum));

        if(g_bIsHookMuted[i])
        {
            formatex(szItem, charsmax(szItem), "%n \d[\rSES SUSTURULDU\d]", i);
        }
        else
        {
            formatex(szItem, charsmax(szItem), "%n \d[\ySES ACIK\d]", i);
        }

        menu_additem(iMenu, szItem, szNum);
    }

    menu_setprop(iMenu, MPROP_BACKNAME, "Geri");
    menu_setprop(iMenu, MPROP_NEXTNAME, "Ileri");
    menu_setprop(iMenu, MPROP_EXITNAME, "Geri Don");
    menu_display(id, iMenu, 0);
}

@handle_admin_mute(const id, const iMenu, const iItem)
{
    if(iItem == MENU_EXIT)
    {
        menu_destroy(iMenu);
        display_admin_main_menu(id);
        return PLUGIN_HANDLED;
    }

    new szData[8], szName[64], iAccess, iCallback;
    menu_item_getinfo(iMenu, iItem, iAccess, szData, charsmax(szData), szName, charsmax(szName), iCallback);
    menu_destroy(iMenu);

    new iTarget = str_to_num(szData);
    if(is_user_connected(iTarget))
    {
        g_bIsHookMuted[iTarget] = !g_bIsHookMuted[iTarget];

        if(g_bIsHookMuted[iTarget])
        {
            client_print_color(iTarget, print_team_default, "^4[THook] ^3Yetkili ^4%n ^3tarafindan hook sesiniz ^1susturuldu^3!", id);
            client_print_color(id, print_team_default, "^4[THook Admin] ^4%n ^3adli oyuncunun hook sesi ^1susturuldu^3.", iTarget);
        }
        else
        {
            client_print_color(iTarget, print_team_default, "^4[THook] ^3Yetkili ^4%n ^3tarafindan hook sesiniz ^4acildi^3.", id);
            client_print_color(id, print_team_default, "^4[THook Admin] ^4%n ^3adli oyuncunun hook sesi ^4acildi^3.", iTarget);
        }
    }

    display_admin_mute_menu(id);
    return PLUGIN_HANDLED;
}
