AFK Control

AFK Control 1.1

Нет прав для скачивания
Код:
/*
    This plugin is designed to control AFK players

    Данный плагин предназначен для контроля AFK-игроков
*/

/* Requirements:
    * AMXX 1.9.0 or above
    * ReAPI
*/

/* Changelog:
    1.0 (20.12.2022) by mx?!:
        * First release
    1.1 (17.05.2025) by mx?!:
        * Added support for ECD Helper ( https://fungun.net/shop/?p=show&id=150 )
    1.2 (19.06.2025) by mx?!:
        * ECD Helper support disabled by default
*/

new const PLUGIN_VERSION[] = "1.2"

#include amxmodx
#include amxmisc
#include reapi
#include xs
#include time

// Create cvar config in 'configs/plugins' and run it?
//
// Создавать конфиг с кварами в 'configs/plugins', и запускать его?
#define AUTO_CFG

// Support for ECD Helper. Comment to disable.
//
// Поддержка плагина ECD Helper. Закомментировать для отключения.
//#define ECD_HELPER_SUPPORT

// DHUD Settings https://dev-cs.ru/hud/index.html
//
// DHUD Настройки https://dev-cs.ru/hud/index.html
#define DHUD_SETTINGS 0, 255, 0, -1.0, 0.3, 0, 0.0, 3.0, 0.1, 0.3

#define CheckBit(%0,%1) (%0 & (1 << %1))
#define SetBit(%0,%1) (%0 |= (1 << %1))
#define ClearBit(%0,%1) (%0 &= ~(1 << %1))

#define IsInGame(%0) (TEAM_SPECTATOR > get_member(%0, m_iTeam) > TEAM_UNASSIGNED)

stock const SOUND__TUTOR_MSG[] = "sound/events/tutor_msg.wav"

const MENU_KEYS = MENU_KEY_1

new const MENU_IDENT_STRING[] = "AfkMenu"

const TASKID__RESET_SKIP = 1337
const TASKID__DELAY_TRANSFER = 1338

#if defined ECD_HELPER_SUPPORT
    /**
     * Вернет 1 если игрок проходит сканирование на данный момент
     * Используйте этот натив в плагинах AFK, чтобы добавить проверку, и не кикать игроков
     *
     * @param player                player
     * @return                        1 or 0
     */
    native ecd_is_scanning(player);
#endif

enum _:CVAR_ENUM {
    Float:CVAR_F__CHECK_INTERVAL,
    Float:CVAR_F__WARN_TIME,
    CVAR__WARN_TO_WARN,
    CVAR__WARNS_TO_TRANSFER_C4,
    CVAR__C4_TRANSFER_MODE,
    CVAR__MAX_WARNS,
    CVAR__MAX_KILLED_WARNS,
    CVAR__FREE_SLOTS_TO_KICK_SPEC,
    CVAR__NOTICE_SPEC,
    CVAR__NOTICE_KICK,
    CVAR__SPEC_TRANSFER_FLAG[32],
    CVAR__SPECTATOR_TIME_FLAG[32],
    CVAR__MAX_SPEC_TIME_DEFAULT,
    CVAR__MAX_SPEC_TIME_FLAG,
    CVAR__MENU_TIME,
    Float:CVAR_F__TIME_SKIP_CHECK,
    Float:CVAR_F__MAXSPEED
}

new g_eCvar[CVAR_ENUM], Float:g_fConnectTime[MAX_PLAYERS + 1], Float:g_fSpecStartTime[MAX_PLAYERS + 1]
new g_iMenuID, g_szMenu[MAX_MENU_LENGTH], g_iMaxPlayers
new g_iTimerWarns[MAX_PLAYERS + 1], g_iKilledWarns[MAX_PLAYERS + 1], g_bitPlToSkip, g_bitChecked, bool:g_bOnGround[MAX_PLAYERS + 1]
new g_iSpawnOrigin[MAX_PLAYERS + 1][3]

public plugin_init() {
    register_plugin("AFK Control", PLUGIN_VERSION, "mx?!")
    register_dictionary("afk_control.txt")

    func_RegCvars()

    g_iMaxPlayers = get_maxplayers()

    RegisterHookChain(RG_CBasePlayer_GetIntoGame, "CBasePlayer_GetIntoGame_Post", true)
    RegisterHookChain(RG_CBasePlayer_Spawn, "CBasePlayer_Spawn_Pre", true)
    RegisterHookChain(RG_CBasePlayer_Killed, "CBasePlayer_Killed_Pre")
    RegisterHookChain(RG_CBasePlayer_StartObserver, "CBasePlayer_StartObserver_Post", true)
    RegisterHookChain(RG_HandleMenu_ChooseTeam, "HandleMenu_ChooseTeam_Post", true)

    g_iMenuID = register_menuid(MENU_IDENT_STRING)
    register_menucmd(g_iMenuID, MENU_KEYS, "func_Menu_Handler")

    set_task(3.5, "func_SetTask")
}

func_RegCvars() {
    bind_cvar_float( "afk_time_check", "10",
        .has_min = true, .min_val = 1.0,
        .desc = "Интервал между проверками (в секундах)",
        .bind = g_eCvar[CVAR_F__CHECK_INTERVAL]
    );

    bind_cvar_float( "afk_warn_time", "10",
        .has_min = true, .min_val = 1.0,
        .desc = "Если игрок не двигается # секунд, это считается за AFK",
        .bind = g_eCvar[CVAR_F__WARN_TIME]
    );

    bind_cvar_num( "afk_warn_to_warn", "2",
        .has_min = true, .min_val = 0.0,
        .desc = "На каком # предупреждения за AFK отправить игроку предупреждение о наказании? (0 - не предупреждать)",
        .bind = g_eCvar[CVAR__WARN_TO_WARN]
    );

    bind_cvar_num( "afk_max_warns", "3",
        .has_min = true, .min_val = 0.0,
        .desc = "Через сколько предупреждений по таймеру игрок будет переведён в зрители",
        .bind = g_eCvar[CVAR__MAX_WARNS]
    );

    bind_cvar_num( "afk_max_killed_warns", "3",
        .has_min = true, .min_val = 0.0,
        .desc = "Сколько раз нужно умереть на точке спавна для того, чтобы произошло наказание за AFK (0 - выкл.)",
        .bind = g_eCvar[CVAR__MAX_KILLED_WARNS]
    );

    bind_cvar_num( "afk_warns_to_transfer_c4", "2",
        .has_min = true, .min_val = 0.0,
        .desc = "Через сколько предупреждений передавать бомбу ближайшему тиммейту (0 - не передавать)",
        .bind = g_eCvar[CVAR__WARNS_TO_TRANSFER_C4]
    );

    bind_cvar_num( "afk_c4_transfer_mode", "1",
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0,
        .desc = "Режим передачи бомбы^n\
        0 - Выбросить^n\
        1 - Передать ближайшему тиммейту",
        .bind = g_eCvar[CVAR__C4_TRANSFER_MODE]
    );

    bind_cvar_num( "afk_free_slots_to_kick_spec", "3",
        .has_min = true, .min_val = -1.0,
        .has_max = true, .max_val = 32.0,
        .desc = "Когда на сервере остаётся # или менее свободных слотов, плагин будет пытаться кикать зрителей",
        .bind = g_eCvar[CVAR__FREE_SLOTS_TO_KICK_SPEC]
    );

    bind_cvar_num( "afk_notice_spec", "1",
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0,
        .desc = "Включить оповещение в чат о переводе игрока за наблюдателей",
        .bind = g_eCvar[CVAR__NOTICE_SPEC]
    );

    bind_cvar_num( "afk_notice_kick", "1",
        .has_min = true, .min_val = 0.0,
        .has_max = true, .max_val = 1.0,
        .desc = "Включить оповещение в чат о кике с сервера",
        .bind = g_eCvar[CVAR__NOTICE_KICK]
    );

    bind_cvar_string( "afk_spec_transfer_flag", "abcdefghijklmnopqrstuvwxyz",
        .desc = "Флаг, при наличии которого AFK-игрок сначала переводится в зрители (иначе кикается) (^"^" - кикать всех)",
        .bind = g_eCvar[CVAR__SPEC_TRANSFER_FLAG],
        .maxlen = charsmax(g_eCvar[CVAR__SPEC_TRANSFER_FLAG])
    );

    bind_cvar_string( "afk_spectator_time_flag", "",
        .desc = "Флаг доступа для логики квара afk_max_spec_time_flag (^"^" - выкл.)",
        .bind = g_eCvar[CVAR__SPECTATOR_TIME_FLAG],
        .maxlen = charsmax(g_eCvar[CVAR__SPECTATOR_TIME_FLAG])
    );

    bind_cvar_num( "afk_max_spec_time_default", "60",
        .has_min = true, .min_val = 0.0,
        .desc = "Сколько секунд зритель без afk_spectator_time_flag может быть AFK до запроса активности (0 - без запроса)",
        .bind = g_eCvar[CVAR__MAX_SPEC_TIME_DEFAULT]
    );

    bind_cvar_num( "afk_max_spec_time_flag", "300",
        .has_min = true, .min_val = 0.0,
        .desc = "Сколько секунд зритель с afk_spectator_time_flag может быть AFK до запроса активности (0 - без запроса)",
        .bind = g_eCvar[CVAR__MAX_SPEC_TIME_FLAG]
    );

    bind_cvar_num( "afk_menu_time", "15",
        .desc = "Сколько секунд даётся игроку на ответ на запрос активности (меню 'вы здесь?')",
        .bind = g_eCvar[CVAR__MENU_TIME]
    );

    bind_cvar_float( "afk_time_skip_check", "20",
        .has_min = true, .min_val = 0.0,
        .desc = "Сколько секунд давать игроку на выбор команды после захода на сервер, до того, как начнётся проверка зрителя",
        .bind = g_eCvar[CVAR_F__TIME_SKIP_CHECK]
    );

    bind_pcvar_float(get_cvar_pointer("sv_maxspeed"), g_eCvar[CVAR_F__MAXSPEED])

#if defined AUTO_CFG
    AutoExecConfig(/*.name = "PluginName"*/)
#endif
}

public func_SetTask() {
    set_task(g_eCvar[CVAR_F__CHECK_INTERVAL], "task_Check")
}

public task_Check() {
    func_SetTask()

    new iAliveTT, iAliveCT, iDeadTT, iDeadCT, bool:bKicked
    rg_initialize_player_counts(iAliveTT, iAliveCT, iDeadTT, iDeadCT)
    new iInGame = iAliveTT + iAliveCT + iDeadTT + iDeadCT

    new pPlayers[MAX_PLAYERS], iPlCount
    get_players(pPlayers, iPlCount)

    if(iInGame > 1) {
        bKicked = CheckAllAlivePlayersForAfk(pPlayers, iPlCount)
    }

    if(bKicked || g_iMaxPlayers - iPlCount > g_eCvar[CVAR__FREE_SLOTS_TO_KICK_SPEC]) {
        return
    }

    CheckAllSpectatorsForAfk(pPlayers, iPlCount)
}

bool:CheckAllAlivePlayersForAfk(const pPlayers[MAX_PLAYERS], iPlCount) {
    if(g_eCvar[CVAR_F__MAXSPEED] <= 2.0) {
        return false
    }

    new bool:bKicked, Float:fGameTime = get_gametime()

    for(new i; i < iPlCount; i++) {
        if(CheckPlayerForAfk(pPlayers[i], fGameTime)) {
            bKicked = true
        }
    }

    g_bitChecked = 0

    return bKicked
}

CheckAllSpectatorsForAfk(const pPlayers[MAX_PLAYERS], iPlCount) {
    new pPlayer, pPlayerToKick, Float:fAfkTime, Float:fMostTime = -1.0, Float:fGameTime = get_gametime()
    new bitSpecFlags = read_flags(g_eCvar[CVAR__SPECTATOR_TIME_FLAG])
    new iMaxSpecTime

    for(new i; i < iPlCount; i++) {
        pPlayer = pPlayers[i]

        if(
            CheckBit(g_bitPlToSkip, pPlayer)
                ||
            is_user_bot(pPlayer)
                ||
            is_user_hltv(pPlayer)
                ||
            is_user_alive(pPlayer)
                ||
            IsInGameEx(pPlayer)
                ||
            IsPlayerJustConnected(pPlayer, fGameTime)
        ) {
            continue
        }
      
    #if defined ECD_HELPER_SUPPORT
        if(ecd_is_scanning(pPlayer)) {
            continue
        }
    #endif

        iMaxSpecTime = g_eCvar[ (get_user_flags(pPlayer) & bitSpecFlags) ? CVAR__MAX_SPEC_TIME_FLAG : CVAR__MAX_SPEC_TIME_DEFAULT ];

        if(!iMaxSpecTime) {
            fAfkTime = fGameTime - g_fSpecStartTime[pPlayer]

            if(fAfkTime > fMostTime) {
                fMostTime = fAfkTime
                pPlayerToKick = pPlayer
            }
        }
        else {
            new iAfkTime = floatround(fGameTime - Float:get_member(pPlayer, m_fLastMovement))

            new iMenuID, iKeys
            get_user_menu(pPlayer, iMenuID, iKeys)

            if(iMenuID == g_iMenuID) {
                if(iAfkTime >= g_eCvar[CVAR__MENU_TIME]) {
                    pPlayerToKick = pPlayer
                    break
                }
            }
            else {
                if(iAfkTime < iMaxSpecTime) {
                    continue
                }

                set_member(pPlayer, m_fLastMovement, get_gametime())

                formatex( g_szMenu, charsmax(g_szMenu),
                    "%L", pPlayer, "AFK__ARE_YOU_THERE" );

                show_menu(pPlayer, MENU_KEYS, g_szMenu, -1, MENU_IDENT_STRING)
            }
        }
    }

    if(pPlayerToKick) {
        KickPlayer(pPlayerToKick, "AFK__SPEC_AFK")
    }
}

public func_Menu_Handler(pPlayer, iKey) {
    if(is_user_connected(pPlayer)) {
        set_member(pPlayer, m_fLastMovement, get_gametime())
    }

    return PLUGIN_HANDLED
}

bool:IsPlayerJustConnected(pPlayer, Float:fGameTime) {
    return (
        //!IsInGame(pPlayer)
        get_member(pPlayer, m_iTeam) == TEAM_UNASSIGNED
            &&
        fGameTime - g_fConnectTime[pPlayer] < g_eCvar[CVAR_F__TIME_SKIP_CHECK]
    );
}

bool:CheckPlayerForAfk(pPlayer, Float:fGameTime) {
    SetBit(g_bitChecked, pPlayer)

    if(!is_user_alive(pPlayer) || is_user_bot(pPlayer) || CheckBit(g_bitPlToSkip, pPlayer)) {
        return false
    }

    if(!IsPlayerAfk(pPlayer, fGameTime, true)) {
        g_iTimerWarns[pPlayer] = 0
        return false
    }

    g_iTimerWarns[pPlayer]++

    if(g_iTimerWarns[pPlayer] >= g_eCvar[CVAR__WARNS_TO_TRANSFER_C4]) {
        TryTransferC4(pPlayer)
    }

    if(g_eCvar[CVAR__WARN_TO_WARN] && g_iTimerWarns[pPlayer] == min(g_eCvar[CVAR__WARN_TO_WARN], g_eCvar[CVAR__MAX_WARNS] - 1)) {
        rg_send_audio(pPlayer, SOUND__TUTOR_MSG)
        client_print(pPlayer, print_center, "%l", "AFK__WARN_CENTER")
        client_print_color(pPlayer, print_team_red, "%l", "AFK__WARN_CHAT")
        return false
    }

    if(g_iTimerWarns[pPlayer] >= g_eCvar[CVAR__MAX_WARNS]) {
        TryTransferC4(pPlayer)
        return func_PunishForAFK(pPlayer)
    }

    return false
}

KickPlayer(pPlayer, const szLangKey[]) {
    SetSkip(pPlayer)

    if(g_eCvar[CVAR__NOTICE_KICK]) {
        client_print_color(0, pPlayer, "%L", LANG_PLAYER, "AFK__KICK_AFK_ALL", pPlayer)
    }

    server_cmd("kick #%i ^"%L^"", get_user_userid(pPlayer), pPlayer, szLangKey)
}

bool:IsPlayerAfk(pPlayer, Float:fGameTime, bool:bWriteOldAngle) {
    static Float:fOldViewAngle[MAX_PLAYERS + 1][3]

    static Float:fViewAngle[3], bool:bSameAngle
    get_entvar(pPlayer, var_v_angle, fViewAngle)

    // https://github.com/s1lentq/ReGameDLL_CS/blob/a20362389e7fe5e3fdd1a6befcc854e1f6c8caff/regamedll/dlls/API/CSPlayer.cpp#L521
    bSameAngle = (floatabs(fOldViewAngle[pPlayer][1] - fViewAngle[1]) < 0.1)

    if(bWriteOldAngle) {
        xs_vec_copy(fViewAngle, fOldViewAngle[pPlayer])
    }

    return (
        bSameAngle
            &&
        fGameTime - Float:get_member(pPlayer, m_fLastMovement) >= g_eCvar[CVAR_F__WARN_TIME]
            &&
        Float:get_entvar(pPlayer, var_maxspeed) > 2.0
    );
}

TryTransferC4(pPlayer) {
    if(!get_member(pPlayer, m_bHasC4)) {
        return
    }

    if(!g_eCvar[CVAR__C4_TRANSFER_MODE] || !g_eCvar[CVAR__WARNS_TO_TRANSFER_C4]) {
        rg_drop_item(pPlayer, "weapon_c4")
        return
    }

    new pPlayers[MAX_PLAYERS], iPlCount, pNewBomber, pTarget
    new Float:fPlayerOrigin[3], Float:fTargetOrigin[3], Float:fGameTime = get_gametime()
    new Float:fShortestDist = 999999.0

    get_entvar(pPlayer, var_origin, fPlayerOrigin)
    get_players(pPlayers, iPlCount, "ae", "TERRORIST")

    for(new i, Float:fDist; i < iPlCount; i++) {
        pTarget = pPlayers[i]

        if(pTarget == pPlayer || CheckBit(g_bitPlToSkip, pTarget)) {
            continue
        }

        if(CheckBit(g_bitChecked, pTarget)) {
            if(g_iTimerWarns[pTarget] > 0 || g_iKilledWarns[pTarget] > 0) {
                continue
            }
        }
        else if(IsPlayerAfk(pTarget, fGameTime, false)) {
            continue
        }

        get_entvar(pTarget, var_origin, fTargetOrigin)
        fDist = vector_distance(fPlayerOrigin, fTargetOrigin)

        if(fDist < fShortestDist) {
            fShortestDist = fDist
            pNewBomber = pTarget
        }
    }

    if(!pNewBomber || !rg_transfer_c4(pPlayer, pNewBomber)) {
        rg_drop_item(pPlayer, "weapon_c4")
    }
    else {
        set_dhudmessage(DHUD_SETTINGS)
        show_dhudmessage(pNewBomber, "%l", "AFK__YOU_GOT_BOMB")

        for(new i; i < iPlCount; i++) {
            pTarget = pPlayers[i]

            if(pTarget != pNewBomber) {
                client_print(pTarget, print_center, "%l", "AFK__BOMB_TRANSFERED_TO", pNewBomber)
            }
        }
    }
}

public CBasePlayer_GetIntoGame_Post(pPlayer) {
    g_iTimerWarns[pPlayer] = 0
    g_iKilledWarns[pPlayer] = 0
}

public CBasePlayer_Spawn_Pre(pPlayer) {
    g_bOnGround[pPlayer] = false
    remove_task(pPlayer)
    set_task(0.1, "task_GetOrigin", pPlayer, .flags = "b")
}

public task_GetOrigin(pPlayer) {
    if(!is_user_alive(pPlayer)) {
        remove_task(pPlayer)
        return
    }

    if( !(get_entvar(pPlayer, var_flags) & FL_ONGROUND) ) {
        return
    }

    g_bOnGround[pPlayer] = true
    remove_task(pPlayer)
    get_user_origin(pPlayer, g_iSpawnOrigin[pPlayer], Origin_Client)
}

public CBasePlayer_Killed_Pre(pVictim, pKiller, iGibType) {
    if(!g_eCvar[CVAR__MAX_KILLED_WARNS] || is_user_bot(pVictim) || !g_bOnGround[pVictim] || CheckBit(g_bitPlToSkip, pVictim)) {
        return
    }

    new iOrigin[3]
    get_user_origin(pVictim, iOrigin, Origin_Client)

    if(
        IsIntCoordsNearlyEqual(iOrigin[0], g_iSpawnOrigin[pVictim][0])
            &&
        IsIntCoordsNearlyEqual(iOrigin[1], g_iSpawnOrigin[pVictim][1])
            &&
        IsIntCoordsNearlyEqual(iOrigin[2], g_iSpawnOrigin[pVictim][2])
    ) {
        if(++g_iKilledWarns[pVictim] >= g_eCvar[CVAR__MAX_KILLED_WARNS]) {
            remove_task(pVictim + TASKID__RESET_SKIP)
            SetBit(g_bitPlToSkip, pVictim)
            set_task(0.1, "task_DelayTransfer", TASKID__DELAY_TRANSFER + get_user_userid(pVictim))
        }

        return
    }

    g_iKilledWarns[pVictim] = 0
}

stock bool:IsIntCoordsNearlyEqual(iCoord1, iCoord2) {
    const FLEQ_TOLERANCE = 10

    return xs_abs(iCoord1 - iCoord2) <= FLEQ_TOLERANCE

    /*if(iCoord1 == iCoord2) {
        return true
    }

    if(iCoord1 > iCoord2) {
        return _abs(iCoord1 - iCoord2) <= FLEQ_TOLERANCE
    }

    //if iCoord1 < iCoord2
    return _abs(iCoord2 - iCoord1) <= FLEQ_TOLERANCE*/
}

public task_DelayTransfer(iUserId) {
    new pPlayer = find_player("k", iUserId - TASKID__DELAY_TRANSFER)

    if(pPlayer) {
        ClearBit(g_bitPlToSkip, pPlayer)
        func_PunishForAFK(pPlayer)
    }
}

SetSkip(pPlayer) {
    remove_task(pPlayer + TASKID__RESET_SKIP)
    SetBit(g_bitPlToSkip, pPlayer)
    set_task(0.1, "ResetSkip", pPlayer + TASKID__RESET_SKIP)
}

public ResetSkip(pPlayer) {
    pPlayer -= TASKID__RESET_SKIP;
    ClearBit(g_bitPlToSkip, pPlayer)
}

bool:func_PunishForAFK(pPlayer) {
    if(get_user_flags(pPlayer) & read_flags(g_eCvar[CVAR__SPEC_TRANSFER_FLAG])) {
        if(IsInGame(pPlayer)) {
            func_MoveToSpec(pPlayer)
        }

        return false
    }

    KickPlayer(pPlayer, "AFK__KICK_AFK")
    return true
}

func_MoveToSpec(pPlayer) {
    g_iTimerWarns[pPlayer] = 0
    g_iKilledWarns[pPlayer] = 0

    SetSkip(pPlayer)

    if(g_eCvar[CVAR__NOTICE_SPEC]) {
        client_print_color(0, pPlayer, "%L", LANG_PLAYER, "AFK__TRANSFER_TO_SPEC_INFO", pPlayer)
    }

    if(is_user_alive(pPlayer)) {
        new Float:fFrags = get_entvar(pPlayer, var_frags)
        new iDeaths = get_member(pPlayer, m_iDeaths)
        user_kill(pPlayer, 0)
        set_member(pPlayer, m_iDeaths, iDeaths)
        set_entvar(pPlayer, var_frags, fFrags)
    }

    if(get_member(pPlayer,m_iMenu) == Menu_ChooseAppearance) {
        rg_internal_cmd(pPlayer, "joinclass", "5")
    }

    set_member(pPlayer, m_bTeamChanged, false)
    rg_internal_cmd(pPlayer, "jointeam", "6")
    set_member(pPlayer, m_bTeamChanged, false)
    amxclient_cmd(pPlayer, "chooseteam")
}

public client_putinserver(pPlayer) {
    g_fConnectTime[pPlayer] = g_fSpecStartTime[pPlayer] = get_gametime()
}

public CBasePlayer_StartObserver_Post(pPlayer) {
    g_fSpecStartTime[pPlayer] = get_gametime()
}

public HandleMenu_ChooseTeam_Post(pPlayer, MenuChooseTeam:iMenuSlot) {
    g_fSpecStartTime[pPlayer] = get_gametime()
}

stock bind_cvar_num(const cvar[], const value[], flags = FCVAR_NONE, const desc[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0, &bind) {
    bind_pcvar_num(create_cvar(cvar, value, flags, desc, has_min, min_val, has_max, max_val), bind)
}

stock bind_cvar_float(const cvar[], const value[], flags = FCVAR_NONE, const desc[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0, &Float:bind) {
    bind_pcvar_float(create_cvar(cvar, value, flags, desc, has_min, min_val, has_max, max_val), bind)
}

stock bind_cvar_string(const cvar[], const value[], flags = FCVAR_NONE, const desc[] = "", bool:has_min = false, Float:min_val = 0.0, bool:has_max = false, Float:max_val = 0.0, bind[], maxlen) {
    bind_pcvar_string(create_cvar(cvar, value, flags, desc, has_min, min_val, has_max, max_val), bind, maxlen)
}

stock bind_cvar_num_by_name(const szCvarName[], &iBindVariable) {
    bind_pcvar_num(get_cvar_pointer(szCvarName), iBindVariable)
}

stock bind_cvar_float_by_name(const szCvarName[], &Float:fBindVariable) {
    bind_pcvar_float(get_cvar_pointer(szCvarName), fBindVariable)
}

stock bool:IsInGameEx(pPlayer) {
    return (
        (TEAM_SPECTATOR > get_member(pPlayer, m_iTeam) > TEAM_UNASSIGNED)
            &&
        get_member(pPlayer, m_iMenu) != Menu_ChooseAppearance
            &&
        get_member(pPlayer, m_iJoiningState) != PICKINGTEAM
    );
}
Назад
Верх