[ReAPI] DM TeamBalance

[ReAPI] DM TeamBalance 1.0.0

Нет прав для скачивания
Исходный код
Код:
/*  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
*                                                                           *
*   Plugin: Automatic team balance for DM servers                           *
*                                                                           *
*   Official plugin support: https://dev-cs.ru/threads/8029/                *
*   Official repository: https://github.com/Nord1cWarr1or/AutoBalance       *
*   Contacts of the author: Telegram: @NordicWarrior                        *
*                                                                           *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *
*                                                                           *
*   Плагин: Автоматический баланс команд для DM сереров                     *
*                                                                           *
*   Официальная поддержка плагина: https://dev-cs.ru/threads/8029/          *
*   Официальный репозиторий: https://github.com/Nord1cWarr1or/AutoBalance   *
*   Связь с автором: Telegram: @NordicWarrior                               *
*                                                                           *
*   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   */

#include <amxmodx>
#include <amxmisc>
#include <reapi>
#include <xs>
#include <screenfade_util>

new const PLUGIN_VERSION[] = "1.0.0";

#if !defined MAX_MAPNAME_LENGTH
#define MAX_MAPNAME_LENGTH 64
#endif

#define GetCvarDesc(%0) fmt("%L", LANG_SERVER, %0)

#define GetBit(%1,%2) (%1 & (1 << (%2 & 31)))
#define SetBit(%1,%2) %1 |= (1 << (%2 & 31))
#define ClrBit(%1,%2) %1 &= ~(1 << (%2 & 31))

//#define DEBUG

#define AUTO_CFG    // Comment out if you don't want the plugin config to be created automatically in "configs/plugins"

enum _:Cvars
{
    MAX_DIFF,
    MODE,
    Float:TIME_TO_PREPARE,
    ADMIN_FLAG[2],
    BOTS,
    ADMIN_MODE,
    MAX_DIFF_ADMINS,
    SKIP_SUICIDE,
    SKIP_DISCONNECT
};

new g_pCvar[Cvars];

new TeamName:g_iNewPlayerTeam[MAX_PLAYERS + 1];

new g_iBlueColor[3]        = { 0, 0, 255 };
new g_iRedColor[3]        = { 255, 0, 0 };

const TASKID__BALANCE_PLAYER    = 991;
const TASKID__SHOW_HUD            = 992;

new g_bitIsUserConnected;

new g_iPlayersInTeam[TeamName][MAX_PLAYERS], g_iCountPlayersInTeam[TeamName];
new g_iAdminsInTeam[TeamName][MAX_PLAYERS], g_iCountAdminsInTeam[TeamName];

new g_iFwdBalancePlayerPre, g_iFwdBalancePlayerPost;

public plugin_init()
{
    register_plugin("DM AutoBalance", PLUGIN_VERSION, "Nordic Warrior");

    register_dictionary("dm_autobalance.txt");

    RegisterHookChain(RG_CBasePlayer_Killed, "OnPlayerKilledPost", true);

    CreateCvars();

    g_iFwdBalancePlayerPre = CreateMultiForward("OnBalancePlayerPre", ET_STOP, FP_CELL);
    g_iFwdBalancePlayerPost = CreateMultiForward("OnBalancePlayerPost", ET_IGNORE, FP_CELL);

    #if defined AUTO_CFG
    AutoExecConfig(true, "AutoBalance");
    #endif

    #if defined DEBUG
    register_clcmd("say /ct", "CheckTeams");
    #endif
}

public OnConfigsExecuted()
{
    register_cvar("dmtb_NW_version", PLUGIN_VERSION, FCVAR_SERVER|FCVAR_SPONLY|FCVAR_UNLOGGED);
}

public client_putinserver(id)
{
    SetBit(g_bitIsUserConnected, id);
}

public client_remove(id)
{
    if(GetBit(g_bitIsUserConnected, id))
    {
        if(!g_pCvar[SKIP_DISCONNECT])
        {
            CheckTeams();
        }
        ClrBit(g_bitIsUserConnected, id);
    }
}

public OnPlayerKilledPost(iVictim, iKiller)
{
    if(!GetBit(g_bitIsUserConnected, iKiller) || (g_pCvar[SKIP_SUICIDE] && iKiller == iVictim))
        return;

    #if defined DEBUG
    log_amx("Player <%n> killed", iVictim);
    #endif

    CheckTeams();
}

public CheckTeams()
{
    if(task_exists(TASKID__BALANCE_PLAYER))
        return PLUGIN_HANDLED;

    ArraysZeroing();

    new iPlayers[MAX_PLAYERS], iPlayersNum;

    get_players_ex(iPlayers, iPlayersNum, g_pCvar[BOTS] ? GetPlayers_ExcludeHLTV : (GetPlayers_ExcludeBots | GetPlayers_ExcludeHLTV));

    new TeamName:iTeam;
    new iPlayer;

    for(new i; i < iPlayersNum; i++)
    {
        iPlayer = iPlayers[i];

        iTeam = get_member(iPlayer, m_iTeam);

        g_iPlayersInTeam[iTeam][g_iCountPlayersInTeam[iTeam]++] = iPlayer;

        if(has_flag(iPlayer, g_pCvar[ADMIN_FLAG]) && g_pCvar[ADMIN_MODE] == 2)
        {
            g_iAdminsInTeam[iTeam][g_iCountAdminsInTeam[iTeam]++] = iPlayer;
        }
    }

    #if defined DEBUG
    log_amx("TE = %i, CT = %i", g_iCountPlayersInTeam[TEAM_TERRORIST], g_iCountPlayersInTeam[TEAM_CT]);

    if(g_pCvar[ADMIN_MODE] == 2)
    {
        log_amx("ADM TE = %i, ADM CT = %i", g_iCountAdminsInTeam[TEAM_TERRORIST], g_iCountAdminsInTeam[TEAM_CT]);
    }
    #endif

    if(xs_abs(g_iCountPlayersInTeam[TEAM_TERRORIST] - g_iCountPlayersInTeam[TEAM_CT]) > g_pCvar[MAX_DIFF])
    {
        new iTeamPlayersForBalance = xs_sign(g_iCountPlayersInTeam[TEAM_TERRORIST] - g_iCountPlayersInTeam[TEAM_CT]);
        
        if(g_pCvar[ADMIN_MODE] == 2 && xs_abs(g_iCountAdminsInTeam[TEAM_TERRORIST] - g_iCountAdminsInTeam[TEAM_CT]) > g_pCvar[MAX_DIFF_ADMINS])
        {
            new iTeamAdminsForBalance = xs_sign(g_iCountAdminsInTeam[TEAM_TERRORIST] - g_iCountAdminsInTeam[TEAM_CT]);

            GetPlayerForBalance(iTeamAdminsForBalance, true);
            return PLUGIN_HANDLED;
        }
        GetPlayerForBalance(iTeamPlayersForBalance);
    }
    else if(g_pCvar[ADMIN_MODE] == 2 && xs_abs(g_iCountAdminsInTeam[TEAM_TERRORIST] - g_iCountAdminsInTeam[TEAM_CT]) > g_pCvar[MAX_DIFF_ADMINS])
    {
        new iTeamAdminsForBalance = xs_sign(g_iCountAdminsInTeam[TEAM_TERRORIST] - g_iCountAdminsInTeam[TEAM_CT]);

        GetPlayerForBalance(iTeamAdminsForBalance, true);
    }
    return PLUGIN_HANDLED;
}

GetPlayerForBalance(const iTeamToBalance, bool:bAdmins = false)
{
    new iRandomPlayer;

    if(iTeamToBalance == 1)
    {
        if(!bAdmins)
        {
            iRandomPlayer = g_iPlayersInTeam[TEAM_TERRORIST][random(g_iCountPlayersInTeam[TEAM_TERRORIST])];
        }
        else
        {
            iRandomPlayer = g_iAdminsInTeam[TEAM_TERRORIST][random(g_iCountAdminsInTeam[TEAM_TERRORIST])];
        }
        g_iNewPlayerTeam[iRandomPlayer] = TEAM_CT;
    }
    else
    {
        if(!bAdmins)
        {
            iRandomPlayer = g_iPlayersInTeam[TEAM_CT][random(g_iCountPlayersInTeam[TEAM_CT])];
        }
        else
        {
            iRandomPlayer = g_iAdminsInTeam[TEAM_CT][random(g_iCountAdminsInTeam[TEAM_CT])];
        }
        g_iNewPlayerTeam[iRandomPlayer] = TEAM_TERRORIST;
    }

    #if defined DEBUG
    log_amx("Balanced player: <%n>, ID: %i", iRandomPlayer, iRandomPlayer);
    #endif

    if(!bAdmins && has_flag(iRandomPlayer, g_pCvar[ADMIN_FLAG]) && g_pCvar[ADMIN_MODE] != 0)
    {
        #if defined DEBUG
        log_amx("Player <%n> has immunity", iRandomPlayer);
        #endif

        RequestFrame("CheckTeams");
        return PLUGIN_HANDLED;
    }

    NotifyAndBalancePlayer(iRandomPlayer);
    return PLUGIN_HANDLED;
}

NotifyAndBalancePlayer(const id)
{
    new iData[1]; iData[0] = id;

    set_task(g_pCvar[TIME_TO_PREPARE], "BalancePlayer", TASKID__BALANCE_PLAYER, iData, sizeof iData);

    set_dhudmessage(255, 255, 255, -1.0, 0.42, 0, 0.0, 3.0, 0.1, 0.1);
    show_dhudmessage(id, "%l", "DMTB_DHUD_WILL_BALANCED", g_pCvar[TIME_TO_PREPARE]);
}

public BalancePlayer(iData[])
{
    new id = iData[0];

    if(!GetBit(g_bitIsUserConnected, id))
        return PLUGIN_HANDLED;

    new TeamName:iTeam = get_member(id, m_iTeam);
    
    if(iTeam == TEAM_SPECTATOR || iTeam == g_iNewPlayerTeam[id])
    {
        RequestFrame("CheckTeams");
        return PLUGIN_HANDLED;
    }

    new iReturn;
    ExecuteForward(g_iFwdBalancePlayerPre, iReturn, id);

    if(iReturn == PLUGIN_HANDLED)
        return PLUGIN_HANDLED;

    if(user_has_weapon(id, CSW_C4))
        rg_drop_items_by_slot(id, C4_SLOT);

    rg_switch_team(id);

    switch(g_pCvar[MODE])
    {
        case 0:
        {
            user_silentkill(id);
            rg_round_respawn(id);
        }
        case 1: rg_round_respawn(id);
    }

    UTIL_ScreenFade(id, g_iNewPlayerTeam[id] == TEAM_CT ? g_iBlueColor : g_iRedColor, 0.5, 2.5, 100);

    set_task(0.1, "ShowHud", TASKID__SHOW_HUD + id);

    ExecuteForward(g_iFwdBalancePlayerPost, iReturn, id);

    RequestFrame("CheckTeams");
    return PLUGIN_CONTINUE;
}

public ShowHud(id)
{
    id -= TASKID__SHOW_HUD;
    
    set_dhudmessage(255, 255, 255, -1.0, 0.42, 0, 0.0, 3.0, 0.1, 0.1);
    show_dhudmessage(id, "%l", g_iNewPlayerTeam[id] == TEAM_CT ? "DMTB_DHUD_BALANCED_CT" : "DMTB_DHUD_BALANCED_TE");

    ClientPrintToAllExcludeOne(id, id, "%l", g_iNewPlayerTeam[id] == TEAM_CT ? "DMTB_CHAT_BALANCED_CT" : "DMTB_CHAT_BALANCED_TE", id);
}

ArraysZeroing()
{
    arrayset(g_iPlayersInTeam[any:0][0], 0, sizeof g_iPlayersInTeam * sizeof g_iPlayersInTeam[]);
    arrayset(g_iCountPlayersInTeam[any:0], 0, sizeof g_iCountPlayersInTeam);
    arrayset(g_iAdminsInTeam[any:0][0], 0, sizeof g_iAdminsInTeam * sizeof g_iAdminsInTeam[]);
    arrayset(g_iCountAdminsInTeam[any:0], 0, sizeof g_iCountAdminsInTeam);
}

public CreateCvars()
{
    bind_pcvar_num(create_cvar("dmtb_max_diff", "1",
        .description = GetCvarDesc("DMTB_CVAR_MAX_DIFF"),
        .has_min = true, .min_val = 1.0),
        g_pCvar[MAX_DIFF]);

    bind_pcvar_num(create_cvar("dmtb_mode", "1",
        .description = GetCvarDesc("DMTB_CVAR_MODE"),
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0),
        g_pCvar[MODE]);

    bind_pcvar_float(create_cvar("dmtb_time", "3.0",
        .description = GetCvarDesc("DMTB_CVAR_TIME"),
        .has_min = true, .min_val = 1.0),
        g_pCvar[TIME_TO_PREPARE]);

    bind_pcvar_string(create_cvar("dmtb_admin_flag", "a",
        .description = GetCvarDesc("DMTB_CVAR_ADMIN_FLAG")),
        g_pCvar[ADMIN_FLAG], charsmax(g_pCvar[ADMIN_FLAG]));

    bind_pcvar_num(create_cvar("dmtb_bots", "0",
        .description = GetCvarDesc("DMTB_CVAR_BOTS"),
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0),
        g_pCvar[BOTS]);

    bind_pcvar_num(create_cvar("dmtb_admin_mode", "1",
        .description = GetCvarDesc("DMTB_CVAR_ADMIN_MODE"),
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 2.0),
        g_pCvar[ADMIN_MODE]);

    bind_pcvar_num(create_cvar("dmtb_max_diff_admins", "1",
        .description = GetCvarDesc("DMTB_CVAR_MAX_DIFF_ADMINS"),
        .has_min = true, .min_val = 1.0),
        g_pCvar[MAX_DIFF_ADMINS]);

    bind_pcvar_num(create_cvar("dmtb_skip_suicide", "1",
        .description = GetCvarDesc("DMTB_CVAR_SKIP_SUICIDE"),
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0),
        g_pCvar[SKIP_SUICIDE]);

    bind_pcvar_num(create_cvar("dmtb_skip_disconnect", "0",
        .description = GetCvarDesc("DMTB_CVAR_SKIP_DISCONNECT"),
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0),
        g_pCvar[SKIP_DISCONNECT]);
}

stock ClientPrintToAllExcludeOne(const iExcludePlayer, const iSender, const szMessage[], any:...)
{
    new szText[192];

    new iPlayers[MAX_PLAYERS], iNumPlayers;
    get_players(iPlayers, iNumPlayers, "ch");

    for(new i; i < iNumPlayers; i++)
    {
        new iPlayer = iPlayers[i];

        if(iPlayer != iExcludePlayer)
        {
            SetGlobalTransTarget(iPlayer);
            vformat(szText, charsmax(szText), szMessage, 4);
            client_print_color(iPlayer, iSender, szText);
        }
    }
}
Setup
  1. Скопируйте include-файл в папку с include вашего компилятора.
  2. Скомпилируйте файл.
  3. Скомпилированный .amxx-файл поместите в папку plugins вашего сервера.
  4. Пропишите AutoBalance.amxx в файле plugins.ini
  5. Сделайте рестарт сервера или смените карту, чтобы плагин начал свою работу.
Сверху Снизу