Flashbang Ban

Flashbang Ban 0.4

Нет прав для скачивания
Код:
/* История изменений:
    0.2 ->
        * Добавлена опция ответного ослепления (квар 'flashban_reflect_flash')
        * Исправлен неверный lang-ключ 'FLBAN__FLASHED_BY' (спасибо Fanfar: https://dev-cs.ru/members/2074/)
    0.3 ->
        * Добавлен учёт mp_freeforall
        * Добавлена поддержка актуального реапи (ATYPE_BOOL)
        * Отказ от использования get_user_name() в пользу форматирования имени через '%n' (ВНИМАНИЕ, обновлён flashbang_ban.txt)
        * Добавлен учёт выпадания флешки при смерти с зажатой кнопкой атаки (квар flashban_skip_deaththrow)
        * Квар flashban_only_full упразднён
        * Добавлены квары flashban_warns_for_partial, flashban_warns_for_full, flashban_dec_warns_kill
    0.4 ->
        * Добавлен функционал анти-ослепления тиммейтов
            * Квар flashban_immune_flags переименован в flashban_punish_immune_flags
            * Добавлен квар flashban_teamflash_immune_flags
            * Добавлен квар flashban_allow_selfflash
*/

new const PLUGIN_NAME[] = "Flashbang Ban"
new const PLUGIN_VERSION[] = "0.4"
new const PLUGIN_AUTHOR[] = "mx?!"

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

#if REAPI_VERSION < 59177
    #define BOOL_RETURN ATYPE_INTEGER
#else
    #define BOOL_RETURN ATYPE_BOOL
#endif

#define chx charsmax

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

new const SOUND__BLIP1[] = "sound/buttons/blip1.wav"
new const SOUND__ERROR[] = "sound/buttons/button2.wav"
new const SOUND__TUTOR_MSG[] = "sound/events/tutor_msg.wav"

enum _:CVAR_ENUM {
    CVAR__ENABLED,
    CVAR__MAX_WARNS,
    CVAR__BAN_ROUNDS,
    CVAR__DEC_WARNS,
    CVAR__DEC_WARNS_KILL,
    CVAR__SKIP_DEATHTHROW,
    CVAR__WARNS_FOR_PARTIAL,
    CVAR__WARNS_FOR_FULL,
    CVAR__REFLECT,
    CVAR__PUNISH_IMMUNE_FLAGS[32],
    CVAR__BLIND_IMMUNE_FLAGS[32],
    CVAR__FREEFORALL,
    CVAR__ALLOW_SELFFLASH
}

enum _:RGB { any:R, any:G, any:B }

const FULL_BLIND = 255

enum _:DATA_STRUCT {
    DATA__WARNS,
    DATA__BAN
}

new g_eCvar[CVAR_ENUM], g_szAuthID[64], Trie:g_tTrie, g_MsgIdScreenFade, g_eData[MAX_PLAYERS + 1][DATA_STRUCT]

/* -------------------- */

public plugin_init() {
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR)
    register_dictionary("flashbang_ban.txt")

    func_RegCvars()

    RegisterHookChain(RG_RadiusFlash_TraceLine, "RadiusFlash_TraceLine_Post", true)
    RegisterHookChain(RG_PlayerBlind, "PlayerBlind_Pre")
    RegisterHookChain(RG_ThrowFlashbang, "ThrowFlashBang_Post", true)
    RegisterHookChain(RG_CBasePlayer_HasRestrictItem, "CBasePlayer_HasRestrictItem_Pre")
    RegisterHookChain(RG_CSGameRules_RestartRound, "CSGameRules_RestartRound_Pre")
    RegisterHookChain(RG_CBasePlayer_Killed, "CBasePlayer_Killed_Post", true)

    g_tTrie = TrieCreate()
    g_MsgIdScreenFade = get_user_msgid("ScreenFade")
}

/* -------------------- */

func_RegCvars() {
    bind_pcvar_num(create_cvar("flashban_enabled", "1", .description = "Working state"), g_eCvar[CVAR__ENABLED])
    bind_pcvar_num(create_cvar("flashban_max_warns", "5", .description = "Warns limit to ban flash", .has_min = true, .min_val = 1.0), g_eCvar[CVAR__MAX_WARNS])
    bind_pcvar_num(create_cvar("flashban_ban_rounds", "3", .description = "How many rounds flash will be banned", .has_min = true, .min_val = 1.0), g_eCvar[CVAR__BAN_ROUNDS])
    bind_pcvar_num(create_cvar("flashban_dec_warns", "1", .description = "How many warns decreased every round", .has_min = true, .min_val = 0.0), g_eCvar[CVAR__DEC_WARNS])
    bind_pcvar_num(create_cvar("flashban_dec_warns_kill", "1", .description = "How many warns decreased every kill", .has_min = true, .min_val = 0.0), g_eCvar[CVAR__DEC_WARNS_KILL])
    bind_pcvar_num(create_cvar("flashban_skip_deaththrow", "1", .description = "Skip warning if flashbang was throwed by dying"), g_eCvar[CVAR__SKIP_DEATHTHROW])
    bind_pcvar_num(create_cvar("flashban_warns_for_partial", "1", .description = "Warns for partial flash"), g_eCvar[CVAR__WARNS_FOR_PARTIAL])
    bind_pcvar_num(create_cvar("flashban_warns_for_full", "2", .description = "Warns for full flash"), g_eCvar[CVAR__WARNS_FOR_FULL])
    bind_pcvar_num(create_cvar("flashban_reflect_flash", "1", .description = "Reflect teamflash to flasher"), g_eCvar[CVAR__REFLECT])

    bind_pcvar_num(get_cvar_pointer("mp_freeforall"), g_eCvar[CVAR__FREEFORALL])

    bind_pcvar_string(create_cvar("flashban_punish_immune_flags", "a", .description = "Punish immunity flags"), g_eCvar[CVAR__PUNISH_IMMUNE_FLAGS], chx(g_eCvar[CVAR__PUNISH_IMMUNE_FLAGS]))
    bind_pcvar_string(create_cvar("flashban_teamflash_immune_flags", "a", .description = "Teamflash immunity flags"), g_eCvar[CVAR__BLIND_IMMUNE_FLAGS], chx(g_eCvar[CVAR__BLIND_IMMUNE_FLAGS]))

    bind_pcvar_num(create_cvar("flashban_allow_selfflash", "1", .description = "Allow selfflash (override immunity)"), g_eCvar[CVAR__ALLOW_SELFFLASH])

#if defined AUTO_CFG
    AutoExecConfig()
#endif
}

/* -------------------- */

bool:ShouldWork() {
    return (g_eCvar[CVAR__ENABLED] && !g_eCvar[CVAR__FREEFORALL])
}

/* -------------------- */

public PlayerBlind_Pre(iVictim, iInflictor, iAttacker, Float:fFadeTime, Float:fFadeHold, iAlpha, Float:fColor[RGB]) {
    if(
        !ShouldWork()
            ||
        iVictim == iAttacker
            ||
        !is_user_connected(iAttacker)
            ||
        get_member(iVictim, m_iTeam) != get_member(iAttacker, m_iTeam)
    ) {
        return
    }

    client_print_color(iVictim, print_team_default, "%L", iVictim, "FLBAN__CHAT_FLASHED_BY", iAttacker)
    console_print(iVictim, "%L", iVictim, "FLBAN__CON_FLASHED_BY", iAttacker)
    rg_send_audio(iVictim, SOUND__TUTOR_MSG)

    if(get_entvar(iInflictor, var_iuser3)) {
        return
    }

    /* --- */

    rg_send_audio(iAttacker, SOUND__TUTOR_MSG)

    if(get_user_flags(iAttacker) & read_flags(g_eCvar[CVAR__PUNISH_IMMUNE_FLAGS])) {
        client_print_color( iAttacker, print_team_default, "%L", iAttacker,
            iAlpha == FULL_BLIND ? "FLBAN__IMM_FLASHED_PLAYER_FULL" : "FLBAN__IMM_FLASHED_PLAYER_PART", iVictim );

        return
    }

    new iWarnsToAdd = g_eCvar[ (iAlpha == FULL_BLIND) ? CVAR__WARNS_FOR_FULL : CVAR__WARNS_FOR_PARTIAL ]

    g_eData[iAttacker][DATA__WARNS] = min(g_eCvar[CVAR__MAX_WARNS], g_eData[iAttacker][DATA__WARNS] + iWarnsToAdd)

    if(g_eCvar[CVAR__REFLECT] && IsNotFullyBlinded(iAttacker, iAlpha)) {
        func_FlashPlayer(iAttacker, fFadeTime, fFadeHold, fColor, iAlpha)
    }

    client_print_color( iAttacker, print_team_default, "%L", iAttacker,
        iAlpha == FULL_BLIND ? "FLBAN__FLASHED_PLAYER_FULL" : "FLBAN__FLASHED_PLAYER_PART",
            iVictim, g_eData[iAttacker][DATA__WARNS], g_eCvar[CVAR__MAX_WARNS] );

    if(g_eData[iAttacker][DATA__WARNS] < g_eCvar[CVAR__MAX_WARNS] || g_eData[iAttacker][DATA__BAN]) {
        return
    }

    client_print(iAttacker, print_center, "%L", iAttacker, "FLBAN__CENTER_FLASH_BANNED")
    g_eData[iAttacker][DATA__BAN] = g_eCvar[CVAR__BAN_ROUNDS]
}

/* -------------------- */

public RadiusFlash_TraceLine_Post(const index, inflictor, attacker, Float:vecSrc[3], Float:vecSpot[3], tracehandle) {
    if(
        !ShouldWork()
            ||
        !is_user_connected(index)
            ||
        !is_user_connected(attacker)
            ||
        get_member(index, m_iTeam) != get_member(attacker, m_iTeam)
    ) {
        return
    }

    if(ShouldAvoidFlash(index, attacker)) {
        // https://github.com/s1lentq/ReGameDLL_CS/blob/efb06a7a201829bdbe13218bc5f5342e1f2ed8f1/regamedll/dlls/combat.cpp#L88
        set_tr2(tracehandle, TR_flFraction, 0.0)
    }
}

/* -------------------- */

bool:ShouldAvoidFlash(victim, attacker) {
    if(get_user_flags(victim) & read_flags(g_eCvar[CVAR__BLIND_IMMUNE_FLAGS])) {
        if(g_eCvar[CVAR__ALLOW_SELFFLASH] && victim == attacker) {
            return false
        }

        return true
    }

    return false
}

/* -------------------- */

public ThrowFlashBang_Post(id, Float:vecStart[3], Float:vecVelocity[3], Float:time) {
    if(!ShouldWork()) {
        return
    }

    new pGrenade = GetHookChainReturn(ATYPE_INTEGER)

    if(is_nullent(pGrenade)) {
        return
    }

    if(g_eData[id][DATA__BAN]) {
        if(!CheckThrowByDeath(id, vecVelocity)) {
            func_BanInfo(id)
        }

        set_entvar(pGrenade, var_flags, FL_KILLME)
        return
    }

    if(g_eCvar[CVAR__SKIP_DEATHTHROW] && CheckThrowByDeath(id, vecVelocity)) {
        set_entvar(pGrenade, var_iuser3, 1)
    }
}

/* -------------------- */

// https://github.com/s1lentq/ReGameDLL_CS/blob/15328fd76421caae2f133a25465dc90da590a6c6/regamedll/dlls/player.cpp#L2142
// https://github.com/s1lentq/ReGameDLL_CS/blob/efb06a7a201829bdbe13218bc5f5342e1f2ed8f1/regamedll/dlls/wpn_shared/wpn_flashbang.cpp#L197
stock CheckThrowByDeath(id, Float:vecVelocity[3]) {
    new Float:fAngles[3]
    get_entvar(id, var_angles, fAngles)
    return xs_vec_equal(fAngles, vecVelocity)
}

/* -------------------- */

public CBasePlayer_HasRestrictItem_Pre(id, ItemID:iItem, ItemRestType:iRestType) {
    if(!g_eData[id][DATA__BAN] || !ShouldWork() || iItem != ITEM_FLASHBANG || iRestType != ITEM_TYPE_BUYING) {
        return HC_CONTINUE
    }

    func_BanInfo(id)
    SetHookChainReturn(BOOL_RETURN, true)
    return HC_SUPERCEDE
}

/* -------------------- */

public CSGameRules_RestartRound_Pre() {
    if(!ShouldWork()) {
        return
    }

    new iPlayers[MAX_PLAYERS], iPlCount

    get_players(iPlayers, iPlCount, "ch")

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

        if(g_eData[iPlayer][DATA__WARNS]) {
            g_eData[iPlayer][DATA__WARNS] = max(0, g_eData[iPlayer][DATA__WARNS] - g_eCvar[CVAR__DEC_WARNS])
        }

        if(!g_eData[iPlayer][DATA__BAN]) {
            continue
        }

        g_eData[iPlayer][DATA__BAN]--

        if(!g_eData[iPlayer][DATA__BAN]) {
            rg_send_audio(iPlayer, SOUND__BLIP1)
            client_print_color(iPlayer, print_team_default, "%L", iPlayer, "FLBAN__BAN_EXPIRED")
        }
    }
}

/* -------------------- */

public CBasePlayer_Killed_Post(iVictim, iKiller, iGibType) {
    if(is_user_connected(iKiller) && g_eData[iKiller][DATA__WARNS] && iVictim != iKiller) {
        g_eData[iKiller][DATA__WARNS] = max(0, g_eData[iKiller][DATA__WARNS] - g_eCvar[CVAR__DEC_WARNS_KILL])
    }
}

/* -------------------- */

stock func_BanInfo(id) {
    rg_send_audio(id, SOUND__ERROR)
    client_print(id, print_center, "%L", id, "FLBAN__CENTER_FLASH_BANNED")
    client_print_color(id, print_team_red, "%L", id, "FLBAN__INFO", g_eData[id][DATA__BAN])
}

/* -------------------- */

public client_putinserver(id) {
    if(!ShouldWork()) {
        return
    }

    get_user_authid(id, g_szAuthID, chx(g_szAuthID))

    if(TrieGetArray(g_tTrie, g_szAuthID, g_eData[id], sizeof(g_eData[]))) {
        TrieDeleteKey(g_tTrie, g_szAuthID)
    }
}

/* -------------------- */

public client_disconnected(id) {
    if(g_eData[id][DATA__WARNS] || g_eData[id][DATA__BAN]) {
        get_user_authid(id, g_szAuthID, chx(g_szAuthID))
        TrieSetArray(g_tTrie, g_szAuthID, g_eData[id], sizeof(g_eData[]))
        g_eData[id][DATA__WARNS] = 0
        g_eData[id][DATA__BAN] = 0
    }
}

/* -------------------- */

stock func_FlashPlayer(id, Float:fOutTime, Float:fHoldTime, Float:fColor[RGB], iAlpha) {
    message_begin(MSG_ONE_UNRELIABLE, g_MsgIdScreenFade, .player = id)
    write_short(FixedUnsigned16(fOutTime)) // SCREENFADE__OUT_TIME (duration / fade time)
    write_short(FixedUnsigned16(fHoldTime)) // SCREENFADE__HOLD_TIME (hold time)
    write_short(0x0000) // fade in
    write_byte(floatround(fColor[R]))
    write_byte(floatround(fColor[G]))
    write_byte(floatround(fColor[B]))
    write_byte(iAlpha)
    message_end()

    new Float:fGameTime = get_gametime()

    set_member(id, m_blindStartTime, fGameTime)
    set_member(id, m_blindFadeTime, fOutTime)
    set_member(id, m_blindHoldTime, fHoldTime)
    set_member(id, m_blindUntilTime, fGameTime + fOutTime)
    set_member(id, m_blindAlpha, iAlpha)
}

/* -------------------- */

stock FixedUnsigned16(Float:fVal, iScale = (1 << 12)) {
    return clamp(floatround(fVal * iScale), 0, 0xFFFF)
}

/* -------------------- */

stock bool:IsNotFullyBlinded(id, iAlpha) {
    return bool:(
        get_member(id, m_blindAlpha) < iAlpha
            ||
        Float:get_member(id, m_blindStartTime) + Float:get_member(id, m_blindHoldTime) < get_gametime()
    );
}
Назад
Верх