Загрузка данных


#include <amxmisc>
#include <fakemeta>
#include <hamsandwich>
#include <json>
#include <reapi>
#include <bitmask>

native bio_get_user_money(id);
native bio_set_user_money(id, amount, bool:flash = true);

#if AMXX_VERSION_NUM < 190
    #assert Outdated version of AMX Mod X (AMXX_VERSION_NUM), please update in order to use this plugin properly.
#endif

#pragma semicolon 1

new const TEXT_MESSAGE[][] =
{
    "#Game_Commencing",
    "#Cstrike_Tutor_Round_Over",
    "#Round_Draw",
    "#Game_will_restart_in",
    "#CTs_Win",
    "#Terrorists_Win"
};

new const SEND_AUDIO[][] =
{
    "%!MRAD_rounddraw",
    "%!MRAD_terwin",
    "%!MRAD_ctwin"
};

new const ZOMBIE_SCREAM[][] =
{
    "biohazard/scream1.wav",
    "biohazard/scream2.wav",
    "biohazard/scream3.wav"
};

new const IMMUTABLE_CVARS[][][] =
{
    {"mp_limitteams", "0"},
    {"mp_autoteambalance", "0"},
    {"mp_round_infinite", "b"},
    {"mp_roundover", "1"},
    {"mp_auto_join_team", "1"},
    {"humans_join_team", "CT"},
    {"mp_autokick", "0"}
};

new m_cImmutableCvars[sizeof IMMUTABLE_CVARS];

enum
{
    TASKID_WARMUP
};

enum _:ClassData
{
    ClassName[32],
    Float:ClassHealth,
    Float:ClassSpeed,
    Float:ClassGravity,
    ClassModel[64],
    ClassClaw[64]
};

new Array:m_arrClassesVec = Invalid_Array;

enum _:SpawnData
{
    Float:SpawnOrigin[3],
    Float:SpawnAngles[3]
};

new Array:m_arrSpawnPointsVec = Invalid_Array;
new Array:m_arrDefaultSpawnPointsVec = Invalid_Array;

new m_szMapName[64];
new m_szSpawnFile[PLATFORM_MAX_PATH];

new m_iNumClasses;
new m_iNumSpawnPoints;
new m_iMenuPosition[MAX_CLIENTS + 1];
new m_iLastSpawn;

new Trie:m_trUselessEntities = Invalid_Trie;
new Trie:m_trTextMessage = Invalid_Trie;
new Trie:m_trSendAudio = Invalid_Trie;

new bool:m_bWarmup = true;
new bool:m_bZombieRespawnAllowed;

new m_pCvar_FreezeTime;
new m_pCvar_RoundTime;
new m_pCvar_OverHumansWin;
new m_pCvar_ZombieRespawnTime;
new m_pCvar_ZombieRespawnMinDistance;
new m_pCvar_ZombieRespawnLimit;
new m_pCvar_ZombieRespawnChanceDecay;
new m_pCvar_MoneyAsJoin;
new m_pCvar_MoneyDamageRatio;
new m_pCvar_MoneyInfection;
new m_pCvar_MoneyKill;
new m_pCvar_ZombieRegenAmount;
new m_pCvar_ZombieRegenInterval;
new m_pCvar_ZombieRegenDelay;

new m_fwdSpawn;

new m_varClass[MAX_CLIENTS + 1];
new m_iZombieRespawn[MAX_CLIENTS + 1];
new m_iMoney[MAX_CLIENTS + 1];

new Float:m_flZombieRegenTime[MAX_CLIENTS + 1];
new Float:m_flZombieLastDamage[MAX_CLIENTS + 1];

new m_bitZombie;
new m_bitZombieRespawn;


public plugin_precache()
{
    register_plugin("Biohazard", "0.0.1", "oxsi. x Vision");

    m_arrClassesVec = ArrayCreate(ClassData);

    m_trTextMessage = TrieCreate();

    for (new i; i < sizeof TEXT_MESSAGE; i++)
        TrieSetCell(m_trTextMessage, TEXT_MESSAGE[i], 0);

    m_trSendAudio = TrieCreate();

    for (new i; i < sizeof SEND_AUDIO; i++)
        TrieSetCell(m_trSendAudio, SEND_AUDIO[i], 0);

    for (new i; i < sizeof ZOMBIE_SCREAM; i++)
        precache_sound(ZOMBIE_SCREAM[i]);

    for (new i; i < sizeof IMMUTABLE_CVARS; i++)
    {
        m_cImmutableCvars[i] = get_cvar_pointer(IMMUTABLE_CVARS[i][0]);

        set_pcvar_string(
            m_cImmutableCvars[i],
            IMMUTABLE_CVARS[i][1]
        );

        hook_cvar_change(
            m_cImmutableCvars[i],
            "@Hook_ForcingCvarChange"
        );
    }

    m_pCvar_FreezeTime = get_cvar_pointer("mp_freezetime");
    m_pCvar_RoundTime = get_cvar_pointer("mp_roundtime");

    set_pcvar_flags(
        m_pCvar_FreezeTime,
        get_pcvar_flags(m_pCvar_FreezeTime) | FCVAR_UNLOGGED
    );

    set_pcvar_flags(
        m_pCvar_RoundTime,
        get_pcvar_flags(m_pCvar_RoundTime) | FCVAR_UNLOGGED
    );

    if (m_bWarmup)
    {
        set_pcvar_num(m_pCvar_FreezeTime, 0);
        set_pcvar_float(m_pCvar_RoundTime, 20.0 / 60.0);

        set_task_ex(
            1.0,
            "@Task_Warmup",
            TASKID_WARMUP,
            .flags = SetTask_Repeat
        );
    }
    else
    {
        set_pcvar_num(m_pCvar_FreezeTime, 15);
        set_pcvar_float(m_pCvar_RoundTime, 180.0 / 60.0);
    }

    if (!Read_JsonField("biohazard"))
        set_fail_state("Failed to read biohazard config.");
}


public plugin_init()
{
    m_pCvar_OverHumansWin =
        register_cvar("bio_over_humans_win", "1");

    m_pCvar_ZombieRespawnTime =
        register_cvar("bio_zombie_respawn", "3.0");

    m_pCvar_ZombieRespawnMinDistance =
        register_cvar("bio_zombie_respawn_min_distance", "1200.0");

    m_pCvar_ZombieRespawnLimit =
        register_cvar("bio_zombie_respawn_limit", "3");

    m_pCvar_ZombieRespawnChanceDecay =
        register_cvar("bio_zombie_chance_decay", "25.0");

    m_pCvar_MoneyAsJoin =
        register_cvar("bio_money_join", "350");

    m_pCvar_MoneyDamageRatio =
        register_cvar("bio_money_damage_ratio", "0.26");

    m_pCvar_MoneyInfection =
        register_cvar("bio_money_infection", "100");

    m_pCvar_MoneyKill =
        register_cvar("bio_money_kill", "100");

    m_pCvar_ZombieRegenAmount =
        register_cvar("bio_zombie_regen_amount", "1.0");

    m_pCvar_ZombieRegenInterval =
        register_cvar("bio_zombie_regen_interval", "0.15");

    m_pCvar_ZombieRegenDelay =
        register_cvar("bio_zombie_regen_delay", "2.48");

    register_clcmd("say /class", "@Open_ClassesMenu");
    register_clcmd("say_team /class", "@Open_ClassesMenu");

    register_clcmd("infect", "cmd_infect");
    register_clcmd("antidote", "cmd_antidote");

    register_menucmd(
        register_menuid("@Display_ClassesMenu"),
        (
            1 << 0 |
            1 << 1 |
            1 << 2 |
            1 << 3 |
            1 << 4 |
            1 << 5 |
            1 << 6 |
            1 << 7 |
            1 << 8 |
            1 << 9
        ),
        "@Handle_ClassesMenu"
    );

    register_message(
        get_user_msgid("TextMsg"),
        "@Message_TextMsg"
    );

    register_message(
        get_user_msgid("SendAudio"),
        "@Message_SendAudio"
    );

    register_message(
        get_user_msgid("Money"),
        "@Message_Money"
    );

    RegisterHookChain(
        RH_SV_ActivateServer,
        "@SV_ActivateServer_Post",
        true
    );

    RegisterHookChain(
        RG_RoundEnd,
        "@RoundEnd_Pre"
    );

    RegisterHookChain(
        RG_RoundEnd,
        "@RoundEnd_Post",
        true
    );

    RegisterHookChain(
        RG_CSGameRules_RestartRound,
        "@CSGameRules_RestartRound_Pre"
    );

    RegisterHookChain(
        RG_CSGameRules_RestartRound,
        "@CSGameRules_RestartRound_Post",
        true
    );

    RegisterHookChain(
        RG_CSGameRules_CheckWinConditions,
        "@CSGameRules_CheckWinConditions_Pre"
    );

    RegisterHookChain(
        RG_CSGameRules_FPlayerCanTakeDamage,
        "@CSGameRules_FPlayerCanTakeDamage_Pre"
    );

    RegisterHookChain(
        RG_CSGameRules_OnRoundFreezeEnd,
        "@CSGameRules_OnRoundFreezeEnd_Pre"
    );

    RegisterHookChain(
        RG_CSGameRules_GetPlayerSpawnSpot,
        "@CSGameRules_GetPlayerSpawnSpot_Pre"
    );

    RegisterHookChain(
        RG_CBasePlayer_Spawn,
        "@CBasePlayer_Spawn_Pre"
    );

    RegisterHookChain(
        RG_CBasePlayer_Spawn,
        "@CBasePlayer_Spawn_Post",
        true
    );

    RegisterHookChain(
        RG_CBasePlayer_HasRestrictItem,
        "@CBasePlayer_HasRestrictItem_Pre"
    );

    RegisterHookChain(
        RG_CBasePlayer_AddAccount,
        "@CBasePlayer_AddAccount_Pre"
    );

    RegisterHookChain(
        RG_CBasePlayer_TakeDamage,
        "@CBasePlayer_TakeDamage_Pre"
    );

    RegisterHookChain(
        RG_CBasePlayer_ResetMaxSpeed,
        "@CBasePlayer_ResetMaxSpeed_Pre"
    );

    RegisterHookChain(
        RG_CBasePlayer_Killed,
        "@CBasePlayer_Killed_Post",
        true
    );

    RegisterHookChain(
        RG_CBasePlayer_PreThink,
        "@CBasePlayer_PreThink_Pre"
    );
}


public plugin_cfg()
{
    m_arrSpawnPointsVec = ArrayCreate(SpawnData);
    m_arrDefaultSpawnPointsVec = ArrayCreate(1);

    get_mapname(
        m_szMapName,
        charsmax(m_szMapName)
    );

    get_localinfo(
        "amxx_configsdir",
        m_szSpawnFile,
        charsmax(m_szSpawnFile)
    );

    add(
        m_szSpawnFile,
        charsmax(m_szSpawnFile),
        "/biohazard/spawns.json"
    );

    /*
     * Загружаем стандартные spawn entity.
     */
    @Load_DefaultSpawnPoints();

    /*
     * Загружаем custom spawn points
     * только для текущей карты.
     */
    Load_SpawnPoints();

    /*
     * После загрузки spawn points
     * обновляем количество spawn points
     * в GameRules.
     */
    @Forcing_LevelInitialize();
}


public plugin_natives()
{
    register_native(
        "bio_get_user_money",
        "@native_get_user_money"
    );

    register_native(
        "bio_set_user_money",
        "@native_set_user_money"
    );
}


public plugin_end()
{
    if (m_trUselessEntities != Invalid_Trie)
        TrieDestroy(m_trUselessEntities);

    if (m_trTextMessage != Invalid_Trie)
        TrieDestroy(m_trTextMessage);

    if (m_trSendAudio != Invalid_Trie)
        TrieDestroy(m_trSendAudio);

    if (m_arrClassesVec != Invalid_Array)
        ArrayDestroy(m_arrClassesVec);

    if (m_arrSpawnPointsVec != Invalid_Array)
        ArrayDestroy(m_arrSpawnPointsVec);

    if (m_arrDefaultSpawnPointsVec != Invalid_Array)
        ArrayDestroy(m_arrDefaultSpawnPointsVec);
}


public client_putinserver(id)
{
    m_varClass[id] = 0;
}


public client_disconnected(id)
{
    m_varClass[id] = 0;
    m_iZombieRespawn[id] = 0;
    m_iMoney[id] = 0;

    m_flZombieRegenTime[id] = 0.0;
    m_flZombieLastDamage[id] = 0.0;

    BIT_SUB(m_bitZombie, id);
    BIT_SUB(m_bitZombieRespawn, id);
}


public cmd_infect(id)
{
    @ZombieAttributes(id, 0);
}


public cmd_antidote(id)
{
    @HumanAttributes(id);
}


@Open_ClassesMenu(id)
{
    if (!m_iNumClasses)
    {
        client_print(
            id,
            print_chat,
            "Zombie classes not found!"
        );

        return PLUGIN_HANDLED;
    }

    m_iMenuPosition[id] = 0;

    return @Display_ClassesMenu(id, 0);
}


@Display_ClassesMenu(id, pos)
{
    if (pos < 0)
        return PLUGIN_HANDLED;

    new start = pos * 7;

    if (start > m_iNumClasses)
        start = m_iNumClasses;

    start -= start % 7;
    pos = start / 7;

    new end = start + 7;

    if (end > m_iNumClasses)
        end = m_iNumClasses;

    new page =
        (m_iNumClasses / 7) +
        (m_iNumClasses % 7 ? 1 : 0);

    new key = 1 << 9;

    new menu[512];
    new data[ClassData];

    new len = formatex(
        menu,
        charsmax(menu),
        "\yClasses %d/%d^n^n",
        pos + 1,
        page
    );

    new item;

    while (start < end)
    {
        ArrayGetArray(
            m_arrClassesVec,
            start,
            data
        );

        if (m_varClass[id] != start)
            key |= 1 << item;

        len += formatex(
            menu[len],
            charsmax(menu) - len,
            "\