Разобраться с функцией rg_set_user_team

Сообщения
21
Реакции
1
Баллы
3
Плагин меняет команды игроков местами каждые N раундов. И вроде он работает очень хорошо, но бывает, что у игрока не меняется скин.
Подскажите, как правильно реализовать подобный плагин, чтобы исключить такую ошибку. При маленьком онлайне на сервере есть боты.
Убивать оставшихся в живых игроков не хочется, но если это единственный путь, как лучше это сделать и чтобы им не засчитали смерть?

C++:
#include <amxmodx>
#include <reapi>

#define PLUGIN_NAME    "Auto Team Swap"
#define PLUGIN_VERSION "1.1"
#define PLUGIN_AUTHOR  "kakavanAI"

// Глобальные переменные
new g_iCurrentRound = 0;      // Счетчик прошедших раундов
new g_pCvarRounds;            // Указатель на cvar количества раундов до смены

/**
 * Инициализация плагина. Регистрирует cvar'ы и хук на окончание раунда.
 */
public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);

    // Cvar: количество раундов, после которых произойдет смена (0 = отключено)
    g_pCvarRounds = register_cvar("ats_rounds", "5");

    // Хук на окончание раунда. post=true означает, что код выполнится ПОСЛЕ оригинальной логики игры
    RegisterHookChain(RG_RoundEnd, "OnRoundEnd", .post = true);
}

public plugin_precache()
{
    precache_sound("darkstar_meat/gong2.wav");
}



/**
 * Хук RG_RoundEnd. Вызывается каждый раз, когда раунд завершается.
 * Увеличивает счетчик и при достижении заданного порога вызывает смену команд.
 */
public OnRoundEnd(const WinStatus:status, const ScenarioEventEndRound:event, const Float:tmDelay)
{
    g_iCurrentRound++;

    new swapRounds = get_pcvar_num(g_pCvarRounds);
    // Проверяем, делится ли текущий раунд на заданное число без остатка
    if (swapRounds > 0 && g_iCurrentRound % swapRounds == 0)
    {
        SwapTeams();
    }

    return HC_CONTINUE;
}

/**
 * Основная функция смены команд.
 * Проходит по всем подключенным игрокам, меняет TERRORIST <-> CT,
 * игнорирует спектаторов и корректно обрабатывает мертвых игроков.
 */
stock SwapTeams()
{
    new players[MAX_PLAYERS], pnum;
    // Получаем список всех подключенных игроков
    get_players(players, pnum, "h");

    new szName[MAX_NAME_LENGTH];

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        
        // Получаем текущую команду игрока через ReAPI member
        new TeamName:currentTeam = get_member(id, m_iTeam);
        
        // Пропускаем спектаторов и игроков без команды
        if (currentTeam != TEAM_TERRORIST && currentTeam != TEAM_CT)
            continue;

        get_user_name(id, szName, charsmax(szName));

        // Определяем новую команду: если был T -> станет CT, и наоборот
        new TeamName:newTeam = (currentTeam == TEAM_TERRORIST) ? TEAM_CT : TEAM_TERRORIST;

        // rg_set_user_team меняет команду БЕЗ убийства игрока.
        // Параметры:
        // id           - индекс игрока
        // newTeam      - новая команда
        // MODEL_AUTO   - автоматически подобрать модель новой команды
        // true         - отправить TeamInfo сообщение клиенту (обновить табличку)
        // false        - НЕ запускать перепроверку условий победы (чтобы не сбросить раунд)
        rg_set_user_team(id, newTeam, MODEL_AUTO, true, false);
        client_cmd(id, "spk darkstar_meat/gong2");

        client_print(id, print_chat, "[ATS] Команды поменялись местами!");

    }

}
 

Вложения

Хорошо, проверьте то, что я вам дал, и скажите, возникнут ли у вас какие-либо проблемы или всё теперь в порядке.
 
За 0
@polki, проверяй
код:
#include <amxmodx>
#include <reapi>
#include <fakemeta>

#define PLUGIN_NAME    "Auto Team Swap"
#define PLUGIN_VERSION "2.0"
#define PLUGIN_AUTHOR  "Joker"

#define SWAP_SOUND     "darkstar_meat/gong3.wav"
#define SWAP_SOUND_CMD "darkstar_meat/gong3"
#define MODEL_PATH_LEN 64

new g_iCurrentRound = 0;
new g_pCvarRounds;
new g_pCvarDebug;
new bool:g_bSwapPending = false;

public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);

    g_pCvarRounds = register_cvar("ats_rounds", "1");
    g_pCvarDebug = register_cvar("ats_debug", "0");

    AutoExecConfig(true, "autoteamswitch");

    RegisterHookChain(RG_RoundEnd, "OnRoundEnd_Post", true);
    RegisterHookChain(RG_CSGameRules_RestartRound, "OnRestartRound_Pre", false);
}

public plugin_precache()
{
    precache_sound(SWAP_SOUND);
}

public OnRoundEnd_Post(const WinStatus:status, const ScenarioEventEndRound:event, const Float:tmDelay)
{
    if (!IsRoundCounted(event))
    {
        ResetSwapState();
        return HC_CONTINUE;
    }

    if (status == WINSTATUS_NONE)
        return HC_CONTINUE;

    new swapRounds = get_pcvar_num(g_pCvarRounds);
    if (swapRounds <= 0)
        return HC_CONTINUE;

    g_iCurrentRound++;
    if (g_iCurrentRound % swapRounds == 0)
        g_bSwapPending = true;

    return HC_CONTINUE;
}

public OnRestartRound_Pre()
{
    if (!g_bSwapPending)
        return HC_CONTINUE;

    g_bSwapPending = false;

    if (IsDebugEnabled())
        Debug_PrintModels("BEFORE_SWAP");

    new swappedPlayers = SwapTeamPlayers();

    if (swappedPlayers <= 0)
        return HC_CONTINUE;

    if (IsDebugEnabled())
        Debug_PrintModels("AFTER_SWAP");

    client_cmd(0, "spk ^"%s^"", SWAP_SOUND_CMD);
    client_print(0, print_chat, "[ATS] Команды поменялись местами!");

    return HC_CONTINUE;
}

stock SwapTeamPlayers()
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    new swappedPlayers = 0;
    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        new TeamName:team = get_member(id, m_iTeam);
        new TeamName:newTeam;

        switch (team)
        {
            case TEAM_TERRORIST:
            {
                newTeam = TEAM_CT;
            }
            case TEAM_CT:
            {
                newTeam = TEAM_TERRORIST;
            }
            default:
            {
                continue;
            }
        }

        if (rg_set_user_team(id, newTeam, MODEL_AUTO, true, false))
            swappedPlayers++;
    }

    return swappedPlayers;
}

stock bool:IsRoundCounted(const ScenarioEventEndRound:event)
{
    switch (event)
    {
        case ROUND_NONE,
             ROUND_GAME_COMMENCE,
             ROUND_GAME_RESTART,
             ROUND_GAME_OVER:
        {
            return false;
        }
    }

    return true;
}

stock ResetSwapState()
{
    g_iCurrentRound = 0;
    g_bSwapPending = false;
}

stock bool:IsDebugEnabled()
{
    return get_pcvar_num(g_pCvarDebug) != 0;
}

stock Debug_PrintModels(const szStage[])
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    server_print("[ATS-DEBUG] === %s ===", szStage);
    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        new szName[MAX_NAME_LENGTH], szModel[MODEL_PATH_LEN], szTeam[8];

        get_user_name(id, szName, charsmax(szName));
        pev(id, pev_model, szModel, charsmax(szModel));
        GetTeamText(get_member(id, m_iTeam), szTeam, charsmax(szTeam));

        server_print("[ATS-DEBUG] ID: %2d | Name: %s | Team: %-4s | Model: %s", id, szName, szTeam, szModel);
    }
    server_print("[ATS-DEBUG] ==========================");
}

stock GetTeamText(const TeamName:team, output[], len)
{
    switch (team)
    {
        case TEAM_TERRORIST:
        {
            copy(output, len, "T");
        }
        case TEAM_CT:
        {
            copy(output, len, "CT");
        }
        case TEAM_SPECTATOR:
        {
            copy(output, len, "SPEC");
        }
        default:
        {
            copy(output, len, "UN");
        }
    }
}
Главное изменение: убрал rg_swap_all_players(). Он меняет команды, но не пересчитывает модель игрока сразу. Теперь свап делается вручную через:

rg_set_user_team(id, newTeam, MODEL_AUTO, true, false)
Это более правильный вариант через ReAPI: команда и стандартная модель новой команды обновляются в одном вызове.

Что изменено:

  • удален костыль g_bFixModelOnSpawn и хук RG_CBasePlayer_Spawn;
  • модели обновляются сразу при свапе, а не после респавна;
  • добавлен cvar ats_debug;
  • ATS_DEBUG больше не зашит в код;
  • добавлен AutoExecConfig(true, "autoteamswitch");
  • исправлен warning loose indentation;
Квары:

ats_rounds 1 // через сколько раундов менять команды
ats_debug 0 // 1 = печатать BEFORE_SWAP / AFTER_SWAP модели в консоль
Сообщение автоматически объединено:

Плагин меняет команды игроков местами каждые N раундов. И вроде он работает очень хорошо, но бывает, что у игрока не меняется скин.
Подскажите, как правильно реализовать подобный плагин, чтобы исключить такую ошибку. При маленьком онлайне на сервере есть боты.
Убивать оставшихся в живых игроков не хочется, но если это единственный путь, как лучше это сделать и чтобы им не засчитали смерть?

C++:
#include <amxmodx>
#include <reapi>

#define PLUGIN_NAME    "Auto Team Swap"
#define PLUGIN_VERSION "1.1"
#define PLUGIN_AUTHOR  "kakavanAI"

// Глобальные переменные
new g_iCurrentRound = 0;      // Счетчик прошедших раундов
new g_pCvarRounds;            // Указатель на cvar количества раундов до смены

/**
 * Инициализация плагина. Регистрирует cvar'ы и хук на окончание раунда.
 */
public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);

    // Cvar: количество раундов, после которых произойдет смена (0 = отключено)
    g_pCvarRounds = register_cvar("ats_rounds", "5");

    // Хук на окончание раунда. post=true означает, что код выполнится ПОСЛЕ оригинальной логики игры
    RegisterHookChain(RG_RoundEnd, "OnRoundEnd", .post = true);
}

public plugin_precache()
{
    precache_sound("darkstar_meat/gong2.wav");
}



/**
 * Хук RG_RoundEnd. Вызывается каждый раз, когда раунд завершается.
 * Увеличивает счетчик и при достижении заданного порога вызывает смену команд.
 */
public OnRoundEnd(const WinStatus:status, const ScenarioEventEndRound:event, const Float:tmDelay)
{
    g_iCurrentRound++;

    new swapRounds = get_pcvar_num(g_pCvarRounds);
    // Проверяем, делится ли текущий раунд на заданное число без остатка
    if (swapRounds > 0 && g_iCurrentRound % swapRounds == 0)
    {
        SwapTeams();
    }

    return HC_CONTINUE;
}

/**
 * Основная функция смены команд.
 * Проходит по всем подключенным игрокам, меняет TERRORIST <-> CT,
 * игнорирует спектаторов и корректно обрабатывает мертвых игроков.
 */
stock SwapTeams()
{
    new players[MAX_PLAYERS], pnum;
    // Получаем список всех подключенных игроков
    get_players(players, pnum, "h");

    new szName[MAX_NAME_LENGTH];

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
       
        // Получаем текущую команду игрока через ReAPI member
        new TeamName:currentTeam = get_member(id, m_iTeam);
       
        // Пропускаем спектаторов и игроков без команды
        if (currentTeam != TEAM_TERRORIST && currentTeam != TEAM_CT)
            continue;

        get_user_name(id, szName, charsmax(szName));

        // Определяем новую команду: если был T -> станет CT, и наоборот
        new TeamName:newTeam = (currentTeam == TEAM_TERRORIST) ? TEAM_CT : TEAM_TERRORIST;

        // rg_set_user_team меняет команду БЕЗ убийства игрока.
        // Параметры:
        // id           - индекс игрока
        // newTeam      - новая команда
        // MODEL_AUTO   - автоматически подобрать модель новой команды
        // true         - отправить TeamInfo сообщение клиенту (обновить табличку)
        // false        - НЕ запускать перепроверку условий победы (чтобы не сбросить раунд)
        rg_set_user_team(id, newTeam, MODEL_AUTO, true, false);
        client_cmd(id, "spk darkstar_meat/gong2");

        client_print(id, print_chat, "[ATS] Команды поменялись местами!");

    }

}
  1. Обновил версию плагина:
    • 1.9 / 2.0 → 2.1.
  2. Убрал проблемный вызов:
    • удален rg_swap_all_players().
  3. Убрал старый костыль с моделями:
    • удален g_bFixModelOnSpawn;
    • удален хук RG_CBasePlayer_Spawn;
    • удален rg_reset_user_model() на спавне.
  4. Сделал ручной swap игроков:
    • меняются только игроки из TEAM_TERRORIST и TEAM_CT;
    • зрители, unassigned и прочие состояния не трогаются.
  5. Добавил сохранение старых команд и моделей перед свапом:
    • oldTeams[];
    • oldModels[].
  6. Добавил корректную замену моделей:
    • T → CT:
      • terror → gsg9;
      • leet → urban;
      • arctic → sas;
      • guerilla → gign;
    • CT → T:
      • urban → leet;
      • gsg9 → terror;
      • gign → guerilla;
      • sas → arctic.
  7. Добавил принудительное обновление entity model:
    • через engfunc(EngFunc_SetModel, id, modelPath).
  8. Добавил обновление внутреннего индекса модели:
    • set_member(id, m_modelIndexPlayer, pev(id, pev_modelindex)).
  9. Добавил проверку userinfo-модели перед SetModel:
    • если userinfo model не совпал с ожидаемой моделью, entity model не трогается.
  1. Добавил swap счета команд:
    • через rg_update_teamscores(terroristWins, ctWins, false).
  2. Добавил новые cvar:
    • ats_debug 0;
    • ats_require_both_teams 1;
    • ats_swap_scores 1.
  3. Добавил AutoExecConfig(true, "autoteamswitch").
  4. Расширил debug:
    • теперь показывает счет CT/T;
    • количество игроков CT/T;
    • счетчик раундов;
    • m_iModelName;
    • userinfo model;
    • entity pev_model;
    • pev_modelindex;
    • m_modelIndexPlayer.
  5. Добавил защиту:
    • если нет игроков T/CT, swap не выполняется;
    • если ats_require_both_teams 1 и одна команда пустая, swap пропускается.

Обновление:
#include <amxmodx>
#include <reapi>
#include <fakemeta>

#define PLUGIN_NAME    "Auto Team Swap"
#define PLUGIN_VERSION "2.1"
#define PLUGIN_AUTHOR  "SPARTA"

#define SWAP_SOUND     "darkstar_meat/gong3.wav"
#define SWAP_SOUND_CMD "darkstar_meat/gong3"
#define MODEL_NAME_LEN 32
#define MODEL_PATH_LEN 96

new g_iCurrentRound = 0;
new g_pCvarRounds;
new g_pCvarDebug;
new g_pCvarRequireBothTeams;
new g_pCvarSwapScores;
new bool:g_bSwapPending = false;

public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);

    g_pCvarRounds = register_cvar("ats_rounds", "1");
    g_pCvarDebug = register_cvar("ats_debug", "0");
    g_pCvarRequireBothTeams = register_cvar("ats_require_both_teams", "1");
    g_pCvarSwapScores = register_cvar("ats_swap_scores", "1");

    AutoExecConfig(true, "autoteamswitch");

    RegisterHookChain(RG_RoundEnd, "OnRoundEnd_Post", true);
    RegisterHookChain(RG_CSGameRules_RestartRound, "OnRestartRound_Pre", false);
}

public plugin_precache()
{
    precache_sound(SWAP_SOUND);
}

public OnRoundEnd_Post(const WinStatus:status, const ScenarioEventEndRound:event, const Float:tmDelay)
{
    if (!IsRoundCounted(event))
    {
        ResetSwapState();
        return HC_CONTINUE;
    }

    if (status == WINSTATUS_NONE)
        return HC_CONTINUE;

    new swapRounds = get_pcvar_num(g_pCvarRounds);
    if (swapRounds <= 0)
        return HC_CONTINUE;

    g_iCurrentRound++;
    if (g_iCurrentRound % swapRounds == 0)
        g_bSwapPending = true;

    return HC_CONTINUE;
}

public OnRestartRound_Pre()
{
    if (!g_bSwapPending)
        return HC_CONTINUE;

    g_bSwapPending = false;

    if (IsDebugEnabled())
        Debug_PrintModels("BEFORE_SWAP");

    new swappedPlayers = SwapTeamPlayers();

    if (swappedPlayers <= 0)
        return HC_CONTINUE;

    if (IsDebugEnabled())
        Debug_PrintModels("AFTER_SWAP");

    client_cmd(0, "spk ^"%s^"", SWAP_SOUND_CMD);
    client_print(0, print_chat, "[ATS] Команды поменялись местами!");

    return HC_CONTINUE;
}

stock SwapTeamPlayers()
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    new activePlayers[MAX_PLAYERS], activeCount;
    new TeamName:oldTeams[MAX_PLAYERS];
    new ModelName:oldModels[MAX_PLAYERS];
    new terroristCount, ctCount;

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        new TeamName:team = get_member(id, m_iTeam);

        switch (team)
        {
            case TEAM_TERRORIST:
            {
                terroristCount++;
            }
            case TEAM_CT:
            {
                ctCount++;
            }
            default:
            {
                continue;
            }
        }

        activePlayers[activeCount] = id;
        oldTeams[activeCount] = team;
        oldModels[activeCount] = get_member(id, m_iModelName);
        activeCount++;
    }

    if (activeCount == 0)
        return 0;

    if (get_pcvar_num(g_pCvarRequireBothTeams) && (!terroristCount || !ctCount))
    {
        if (IsDebugEnabled())
            server_print("[ATS-DEBUG] Swap skipped: T=%d CT=%d", terroristCount, ctCount);

        return 0;
    }

    new ctWins = get_member_game(m_iNumCTWins);
    new terroristWins = get_member_game(m_iNumTerroristWins);
    new swappedPlayers = 0;

    for (new i = 0; i < activeCount; i++)
    {
        new id = activePlayers[i];
        new TeamName:newTeam = GetOppositeTeam(oldTeams[i]);
        new ModelName:newModel = GetSwappedModel(oldTeams[i], oldModels[i]);

        if (newTeam == TEAM_UNASSIGNED)
            continue;

        if (rg_set_user_team(id, newTeam, newModel, true, false))
        {
            RefreshPlayerEntityModel(id, newModel);
            swappedPlayers++;
        }
    }

    if (swappedPlayers > 0 && get_pcvar_num(g_pCvarSwapScores))
        rg_update_teamscores(terroristWins, ctWins, false);

    return swappedPlayers;
}

stock TeamName:GetOppositeTeam(const TeamName:team)
{
    switch (team)
    {
        case TEAM_TERRORIST:
        {
            return TEAM_CT;
        }
        case TEAM_CT:
        {
            return TEAM_TERRORIST;
        }
    }

    return TEAM_UNASSIGNED;
}

stock ModelName:GetSwappedModel(const TeamName:oldTeam, const ModelName:oldModel)
{
    if (oldTeam == TEAM_TERRORIST)
    {
        switch (oldModel)
        {
            case MODEL_T_TERROR:
            {
                return MODEL_CT_GSG9;
            }
            case MODEL_T_LEET:
            {
                return MODEL_CT_URBAN;
            }
            case MODEL_T_ARCTIC:
            {
                return MODEL_CT_SAS;
            }
            case MODEL_T_GUERILLA:
            {
                return MODEL_CT_GIGN;
            }
        }

        return MODEL_CT_URBAN;
    }

    if (oldTeam == TEAM_CT)
    {
        switch (oldModel)
        {
            case MODEL_CT_URBAN:
            {
                return MODEL_T_LEET;
            }
            case MODEL_CT_GSG9:
            {
                return MODEL_T_TERROR;
            }
            case MODEL_CT_GIGN:
            {
                return MODEL_T_GUERILLA;
            }
            case MODEL_CT_SAS:
            {
                return MODEL_T_ARCTIC;
            }
        }

        return MODEL_T_TERROR;
    }

    return MODEL_UNASSIGNED;
}

stock bool:RefreshPlayerEntityModel(const id, const ModelName:model)
{
    new modelName[MODEL_NAME_LEN];
    if (!GetModelName(model, modelName, charsmax(modelName)))
        return false;

    new infoModel[MODEL_NAME_LEN];
    get_user_info(id, "model", infoModel, charsmax(infoModel));
    if (!equal(infoModel, modelName))
        return false;

    new modelPath[MODEL_PATH_LEN];
    formatex(modelPath, charsmax(modelPath), "models/player/%s/%s.mdl", modelName, modelName);

    engfunc(EngFunc_SetModel, id, modelPath);
    set_member(id, m_modelIndexPlayer, pev(id, pev_modelindex));

    return true;
}

stock bool:GetModelName(const ModelName:model, output[], len)
{
    switch (model)
    {
        case MODEL_CT_URBAN:
        {
            copy(output, len, "urban");
        }
        case MODEL_CT_GSG9:
        {
            copy(output, len, "gsg9");
        }
        case MODEL_CT_GIGN:
        {
            copy(output, len, "gign");
        }
        case MODEL_CT_SAS:
        {
            copy(output, len, "sas");
        }
        case MODEL_CT_VIP:
        {
            copy(output, len, "vip");
        }
        case MODEL_T_TERROR:
        {
            copy(output, len, "terror");
        }
        case MODEL_T_LEET:
        {
            copy(output, len, "leet");
        }
        case MODEL_T_ARCTIC:
        {
            copy(output, len, "arctic");
        }
        case MODEL_T_GUERILLA:
        {
            copy(output, len, "guerilla");
        }
        default:
        {
            return false;
        }
    }

    return true;
}

stock bool:IsRoundCounted(const ScenarioEventEndRound:event)
{
    switch (event)
    {
        case ROUND_NONE,
             ROUND_GAME_COMMENCE,
             ROUND_GAME_RESTART,
             ROUND_GAME_OVER:
        {
            return false;
        }
    }

    return true;
}

stock ResetSwapState()
{
    g_iCurrentRound = 0;
    g_bSwapPending = false;
}

stock bool:IsDebugEnabled()
{
    return get_pcvar_num(g_pCvarDebug) != 0;
}

stock Debug_PrintModels(const szStage[])
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    server_print("[ATS-DEBUG] === %s ===", szStage);
    server_print(
        "[ATS-DEBUG] Score CT/T: %d/%d | Players CT/T: %d/%d | RoundCounter: %d",
        get_member_game(m_iNumCTWins),
        get_member_game(m_iNumTerroristWins),
        get_member_game(m_iNumCT),
        get_member_game(m_iNumTerrorist),
        g_iCurrentRound
    );

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        new szName[MAX_NAME_LENGTH], szModel[MODEL_PATH_LEN], szInfoModel[MODEL_NAME_LEN], szTeam[8];

        get_user_name(id, szName, charsmax(szName));
        pev(id, pev_model, szModel, charsmax(szModel));
        get_user_info(id, "model", szInfoModel, charsmax(szInfoModel));
        GetTeamText(get_member(id, m_iTeam), szTeam, charsmax(szTeam));

        server_print(
            "[ATS-DEBUG] ID: %2d | Name: %s | Team: %-4s | ModelId: %d | Info: %s | Entity: %s | Idx: %d/%d",
            id,
            szName,
            szTeam,
            get_member(id, m_iModelName),
            szInfoModel,
            szModel,
            pev(id, pev_modelindex),
            get_member(id, m_modelIndexPlayer)
        );
    }
    server_print("[ATS-DEBUG] ==========================");
}

stock GetTeamText(const TeamName:team, output[], len)
{
    switch (team)
    {
        case TEAM_TERRORIST:
        {
            copy(output, len, "T");
        }
        case TEAM_CT:
        {
            copy(output, len, "CT");
        }
        case TEAM_SPECTATOR:
        {
            copy(output, len, "SPEC");
        }
        default:
        {
            copy(output, len, "UN");
        }
    }
}
 
Последнее редактирование:
За 0
kakavanAI + Joker Fix:
#include <amxmodx>
#include <reapi>
#include <fakemeta>

#define PLUGIN_NAME    "Auto Team Swap"
#define PLUGIN_VERSION "2.0"
#define PLUGIN_AUTHOR  "kakavanAI + Joker Fix"

// Разкоментирай за debug
#define ATS_DEBUG

new g_iCurrentRound = 0;
new g_pCvarRounds;
new bool:g_bSwapPending = false;

public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);

    g_pCvarRounds = register_cvar("ats_rounds", "1");

    RegisterHookChain(RG_RoundEnd, "OnRoundEnd_Post", true);
    RegisterHookChain(RG_CSGameRules_RestartRound, "OnRestartRound_Pre", false);
}

public plugin_precache()
{
    precache_sound("darkstar_meat/gong3.wav");
}

public OnRoundEnd_Post(const WinStatus:status, const ScenarioEventEndRound:event, const Float:tmDelay)
{
    new swapRounds = get_pcvar_num(g_pCvarRounds);
    if (swapRounds <= 0)
        return HC_CONTINUE;

    g_iCurrentRound++;

    if (g_iCurrentRound % swapRounds == 0)
        g_bSwapPending = true;

    return HC_CONTINUE;
}

public OnRestartRound_Pre()
{
    if (!g_bSwapPending)
        return HC_CONTINUE;

    g_bSwapPending = false;

    #if defined ATS_DEBUG
    Debug_PrintModels("BEFORE_SWAP");
    #endif

    rg_swap_all_players();

    #if defined ATS_DEBUG
    Debug_PrintModels("AFTER_SWAP");
    #endif

    // Даваме 1 frame / малко време на engine-а да обнови team state,
    // после насила презареждаме моделите.
    set_task(0.1, "Task_FixAllPlayerModels");

    client_cmd(0, "spk ^"darkstar_meat/gong3^"");
    client_print(0, print_chat, "[ATS] Отборите бяха разменени!");

    return HC_CONTINUE;
}

public Task_FixAllPlayerModels()
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];

        if (!is_user_connected(id))
            continue;

        // Най-важното: насилствено презареждане на правилния модел
        rg_reset_user_model(id, true);
    }

    #if defined ATS_DEBUG
    Debug_PrintModels("AFTER_FORCE_MODEL_RESET");
    #endif
}

#if defined ATS_DEBUG
stock Debug_PrintModels(const szStage[])
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    server_print("[ATS-DEBUG] === %s ===", szStage);

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        new szName[MAX_NAME_LENGTH], szModel[64];

        get_user_name(id, szName, charsmax(szName));
        pev(id, pev_model, szModel, charsmax(szModel));

        new team = get_user_team(id);
        new szTeam[12];

        switch (team)
        {
            case 1: copy(szTeam, charsmax(szTeam), "T");
            case 2: copy(szTeam, charsmax(szTeam), "CT");
            default: copy(szTeam, charsmax(szTeam), "SPEC/UN");
        }

        server_print("[ATS-DEBUG] ID: %2d | Name: %s | Team: %-7s | Model: %s", id, szName, szTeam, szModel);
    }

    server_print("[ATS-DEBUG] ==========================");
}
#endif
Сообщение автоматически объединено:

kakavanAI + Joker Fix version 2.1:
#include <amxmodx>
#include <reapi>
#include <fakemeta>

#define PLUGIN_NAME    "Auto Team Swap"
#define PLUGIN_VERSION "2.1"
#define PLUGIN_AUTHOR  "kakavanAI + Joker Fix"

// Разкоментирай за debug
#define ATS_DEBUG

new g_iCurrentRound = 0;
new g_pCvarRounds;
new g_pCvarFixDelay;
new bool:g_bSwapPending = false;

public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);

    g_pCvarRounds   = register_cvar("ats_rounds", "1");
    g_pCvarFixDelay = register_cvar("ats_model_fix_delay", "0.1");

    RegisterHookChain(RG_RoundEnd, "OnRoundEnd_Post", true);
    RegisterHookChain(RG_CSGameRules_RestartRound, "OnRestartRound_Pre", false);
}

public plugin_precache()
{
    precache_sound("darkstar_meat/gong3.wav");
}

public OnRoundEnd_Post(const WinStatus:status, const ScenarioEventEndRound:event, const Float:tmDelay)
{
    new swapRounds = get_pcvar_num(g_pCvarRounds);
    if (swapRounds <= 0)
        return HC_CONTINUE;

    g_iCurrentRound++;

    if ((g_iCurrentRound % swapRounds) == 0)
        g_bSwapPending = true;

    return HC_CONTINUE;
}

public OnRestartRound_Pre()
{
    if (!g_bSwapPending)
        return HC_CONTINUE;

    g_bSwapPending = false;

    #if defined ATS_DEBUG
    Debug_PrintModels("BEFORE_SWAP");
    #endif

    rg_swap_all_players();

    #if defined ATS_DEBUG
    Debug_PrintModels("AFTER_SWAP");
    #endif

    new Float:fDelay = get_pcvar_float(g_pCvarFixDelay);
    if (fDelay < 0.0)
        fDelay = 0.1;

    set_task(fDelay, "Task_FixAllPlayerModels");

    client_cmd(0, "spk ^"darkstar_meat/gong3^"");
    client_print(0, print_chat, "[ATS] Отборите бяха разменени!");

    return HC_CONTINUE;
}

public Task_FixAllPlayerModels()
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];

        if (!is_user_connected(id))
            continue;

        rg_reset_user_model(id, true);
    }

    #if defined ATS_DEBUG
    Debug_PrintModels("AFTER_FORCE_MODEL_RESET");
    #endif
}

#if defined ATS_DEBUG
stock Debug_PrintModels(const szStage[])
{
    new players[MAX_PLAYERS], pnum;
    get_players(players, pnum, "h");

    server_print("[ATS-DEBUG] === %s ===", szStage);

    for (new i = 0; i < pnum; i++)
    {
        new id = players[i];
        new szName[MAX_NAME_LENGTH], szModel[64], szTeam[12];

        get_user_name(id, szName, charsmax(szName));
        pev(id, pev_model, szModel, charsmax(szModel));

        switch (get_user_team(id))
        {
            case 1: copy(szTeam, charsmax(szTeam), "T");
            case 2: copy(szTeam, charsmax(szTeam), "CT");
            default: copy(szTeam, charsmax(szTeam), "SPEC/UN");
        }

        server_print("[ATS-DEBUG] ID: %2d | Name: %s | Team: %-7s | Model: %s", id, szName, szTeam, szModel);
    }

    server_print("[ATS-DEBUG] ==========================");
}
#endif
Сообщение автоматически объединено:

Попробуйте оба варианта кода и скажите, подходит ли он вам или нет, напишите после тестирования.


Опробовал я версию 2.1 багов не заметил.

Версию от vipclient еще не пробовал
 
За 0

Кто просматривает тему

Назад
Верх