/* HLSW Spec Bot
 *
 * OYT panel spec botlari HLSW'de "player" gorunur cunku bot/oyuncu
 * ayrimi baglanti ANINDA Steam sorgu katmanina (steamclient) bildirilir:
 * motorun CreateFakeClient'i CreateUnauthenticatedUserConnection cagirir
 * ve A2S_INFO'daki "bots" alani oradan dolar. Sonradan FL_FAKECLIENT
 * basmak bu sayaci degistirmez.
 *
 * Bu eklenti botu motorun kendi CreateFakeClient'i ile kurar; boylece
 * HLSW / GameTracker / sunucu listeleri botu gercek bot olarak sayar.
 *
 * Moduller: fakemeta, cstrike
 * Cvarlar:
 *   sbot_count   "1"  - kac spec bot (0-4)
 *   sbot_names   "SPEC BOT;SPEC BOT 2;SPEC BOT 3;SPEC BOT 4"
 *   sbot_reserve "2"  - sunucuda en az bu kadar bos slot kalsin;
 *                       kalmazsa bot atilir, yer acilinca geri gelir
 */

#include <amxmodx>
#include <fakemeta>
#include <cstrike>

#define PLUGIN  "HLSW Spec Bot"
#define VERSION "1.0"

#define MAX_SPECBOTS 4

new g_bot[MAX_SPECBOTS];
new g_pCount, g_pNames, g_pReserve;

public plugin_init()
{
    register_plugin(PLUGIN, VERSION, "BlueNiqht");

    g_pCount   = register_cvar("sbot_count", "1");
    g_pNames   = register_cvar("sbot_names", "SPEC BOT;SPEC BOT 2;SPEC BOT 3;SPEC BOT 4");
    g_pReserve = register_cvar("sbot_reserve", "2");
}

public plugin_cfg()
{
    // harita basinda motor tam otursun diye gecikmeli kur
    set_task(8.0, "TaskCreateBots");
    // doluluk denetimi: insanlara yer kalmazsa botu at, yer acilinca geri getir
    set_task(15.0, "TaskCheckSlots", _, _, _, "b");
}

public client_disconnect(id)
{
    // cache'lenmis id'yi hemen sifirla — dusen bota bir daha dokunma
    for (new i = 0; i < MAX_SPECBOTS; i++)
    {
        if (g_bot[i] == id)
            g_bot[i] = 0;
    }
}

public TaskCreateBots()
{
    new want = clamp(get_pcvar_num(g_pCount), 0, MAX_SPECBOTS);
    new name[32];

    for (new i = 0; i < want; i++)
    {
        if (g_bot[i] && is_user_connected(g_bot[i]))
            continue;

        if (FreeSlots() <= get_pcvar_num(g_pReserve))
            return;

        GetBotName(i, name, charsmax(name));
        CreateOneBot(i, name);
    }
}

public TaskCheckSlots()
{
    new frees = FreeSlots();
    new reserve = get_pcvar_num(g_pReserve);

    if (frees < reserve)
    {
        // insanlara yer ac: son bottan baslayarak birini at
        for (new i = MAX_SPECBOTS - 1; i >= 0; i--)
        {
            new id = g_bot[i];
            if (id && is_user_connected(id))
            {
                server_cmd("kick #%d", get_user_userid(id));
                g_bot[i] = 0;
                break;
            }
        }
    }
    else if (frees > reserve)
    {
        TaskCreateBots();
    }
}

CreateOneBot(slot, const name[])
{
    new ent = engfunc(EngFunc_CreateFakeClient, name);
    if (ent <= 0 || ent > get_maxplayers())
        return;

    new reject[128];
    engfunc(EngFunc_FreeEntPrivateData, ent);
    dllfunc(DLLFunc_ClientConnect, ent, name, "127.0.0.1", reject);
    dllfunc(DLLFunc_ClientPutInServer, ent);

    // oyun dll'i flag'i ezmis olabilir, geri bas
    set_pev(ent, pev_flags, pev(ent, pev_flags) | FL_FAKECLIENT);

    cs_set_user_team(ent, CS_TEAM_SPECTATOR);

    // ne olursa olsun etkisiz kalsin
    set_pev(ent, pev_solid, SOLID_NOT);
    set_pev(ent, pev_movetype, MOVETYPE_NOCLIP);
    set_pev(ent, pev_effects, pev(ent, pev_effects) | EF_NODRAW);
    set_pev(ent, pev_takedamage, 0.0);

    g_bot[slot] = ent;
}

FreeSlots()
{
    return get_maxplayers() - get_playersnum();
}

GetBotName(idx, out[], len)
{
    new names[256], left[64], right[256];
    get_pcvar_string(g_pNames, names, charsmax(names));
    copy(right, charsmax(right), names);

    for (new i = 0; i <= idx; i++)
    {
        strtok(right, left, charsmax(left), right, charsmax(right), ';');
        trim(left);
    }

    if (left[0])
        copy(out, len, left);
    else
        formatex(out, len, "SPEC BOT %d", idx + 1);
}
