/*****************************************************************************
Возможности:
1. Настроить сколько игроков выводить в список
2. Настроить минимальное количество игроков для вывода
3. Настроить с какого раунда выводить
4. Время показа
5. Мгновенное закрытие меню по нажатию на клавиши цифр
6. Отключение показа через команду /damage
7. Помимо урона рядом выводит также количество убийств (отключаемо)
8. Префикс перед сообщением в чате
9. Настроить сколько денег давать лучшему игроку раунда
10. Возможность отключения выдачи награды
11. Сохранение статуса отображения с помощью nvault
12. Не показывать топ, если у игрока открыто какое-либо меню
Только на реапи, без реапи делать не буду, не вижу смысла
Благодарности:
Vaqtincha - за куски кода, идею и помощь по коду
Ссылка на оригинал плагина: https://dev-cs.ru/threads/75/
Версии:
v.1.0.0
- Релиз
v.1.0.1
- Добавлена возможность выдавать денежную награду лучшему игроку
v.1.0.2
- Рефаторинг кода
v.1.0.3
- Перенес проверку на минимальное количество игроков с fnCompareDamage() в RoundEnd() и изменен подсчет наносимого урона
v.1.0.4
- Убрал вывод заголовка меню, если никто в раунде никого не ранил
v.1.0.5
- Рефакторинг кода
- Добавлен конфиг файл
- Добавлен LANG-файл с поддержкой русского, английского языков
- Убрал вывод топа если произойдет рестарт или гейм комменсинг
- Добавлен квар переключения показа топа с убийствами и без них
- Добавлена проверка на открытые меню и если у игрока открыто какое-либо меню, то ему не будет показываться топ за раунд
- Добавлена возможность сохранения статуса отображения с помощью nvault
v.1.0.6
- Исправление бага, когда список мог не показываться
*****************************************************************************/
#include <amxmodx>
#include <amxmisc>
#include <reapi>
#include <nvault>
#if AMXX_VERSION_NUM < 183
#include <colorchat>
#define MAX_PLAYERS 32
#define MAX_NAME_LENGTH 32
new MaxClients;
#endif
new const PLUGIN[] = "[ReAPI] TopRoundDamage";
new const VERSION[] = "1.0.6";
new const AUTHOR[] = "Dager* *.* -G-";
new const FILE_CFG[] = "damage_round.cfg";
new const FILE_VAULT[] = "damage_round";
#define IsPlayer(%1) (1 <= %1 <= MaxClients)
#define ClearArr(%1) arrayset(_:%1, _:0.0, sizeof(%1))
#define MENU_KEYS (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)
enum _:ePlayerData
{
PLAYER_ID,
DAMAGE,
KILLS
};
new g_aData[MAX_PLAYERS + 1][ePlayerData],
g_iPlayerDmg[MAX_PLAYERS + 1],
g_iPlayerKills[MAX_PLAYERS + 1],
g_iRoundCount,
bool:g_bSwitch[MAX_PLAYERS + 1],
g_szAuthID[MAX_PLAYERS + 1][35],
g_hVault;
new g_pTopPlayers,
g_pMinPlayers,
g_pRoundNumber,
g_pShowTime,
g_pGiveAward,
g_pGiveMoney,
g_pShowKills,
g_pPruneDays;
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
register_dictionary("damage_round.txt");
g_pTopPlayers = register_cvar("dmgr_top_players", "5");
g_pMinPlayers = register_cvar("dmgr_min_players", "2");
g_pRoundNumber = register_cvar("dmgr_round_number", "1");
g_pShowTime = register_cvar("dmgr_show_time", "5");
g_pGiveAward = register_cvar("dmgr_give_award", "1");
g_pGiveMoney = register_cvar("dmgr_give_money", "500");
g_pShowKills = register_cvar("dmgr_show_kills", "1");
g_pPruneDays = register_cvar("dmgr_prune_days", "30");
register_clcmd("say /damage", "cmdTopDamageSwitch");
register_clcmd("say_team /damage", "cmdTopDamageSwitch");
RegisterHookChain(RG_CSGameRules_RestartRound, "CSGameRules_RestartRound_Pre", false);
RegisterHookChain(RG_CBasePlayer_TakeDamage, "CBasePlayer_TakeDamage", true);
RegisterHookChain(RG_CBasePlayer_Killed, "CBasePlayer_Killed", true);
RegisterHookChain(RG_RoundEnd, "RoundEnd", true);
register_menucmd(register_menuid("TopDmg"), MENU_KEYS, "fnTopDmgHandler");
#if AMXX_VERSION_NUM < 183
MaxClients = get_member_game(m_nMaxPlayers);
#endif
}
public plugin_cfg()
{
new szCfgPath[64], szFileName[128];
get_configsdir(szCfgPath, charsmax(szCfgPath));
formatex(szFileName, charsmax(szFileName), "%s/%s", szCfgPath, FILE_CFG);
if (!file_exists(szFileName))
{
log_amx("Config %s/%s not found", szCfgPath, FILE_CFG);
return;
}
server_cmd("exec %s/%s", szCfgPath, FILE_CFG);
server_exec();
g_hVault = nvault_open(FILE_VAULT);
if (g_hVault != INVALID_HANDLE)
nvault_prune(g_hVault, 0, get_systime() - (get_pcvar_num(g_pPruneDays) * 86400));
else
set_fail_state("Opening nVault file failed!");
}
public client_authorized(id)
{
get_user_authid(id, g_szAuthID[id], charsmax(g_szAuthID[]));
}
public LoadData(id)
{
new szKey[30], szData[3], iDataExists, iTimestamp;
formatex(szKey, charsmax(szKey), "%s_TDR", g_szAuthID[id]);
iDataExists = nvault_lookup(g_hVault, g_szAuthID[id], szData, charsmax(szData), iTimestamp);
if (iDataExists)
{
g_bSwitch[id] = bool:nvault_get(g_hVault, szKey);
// обновление данных (если нашли стимид, значит игрок активный)
nvault_set(g_hVault, szKey, g_bSwitch[id] ? "1" : "0");
}
else
g_bSwitch[id] = true;
}
public client_putinserver(id)
{
if (!is_user_bot(id) || !is_user_hltv(id))
LoadData(id);
g_iPlayerDmg[id] = 0;
g_iPlayerKills[id] = 0;
}
public cmdTopDamageSwitch(id)
{
g_bSwitch[id] = !g_bSwitch[id];
new szSwitch[30], szKey[30];
formatex(szKey, charsmax(szKey), "%s_TDR", g_szAuthID[id]);
nvault_set(g_hVault, szKey, g_bSwitch[id] ? "1" : "0");
formatex(szSwitch, charsmax(szSwitch), "%L", id, g_bSwitch[id] ? "CHAT_DMG_ROUND_SWITCH_ON" : "CHAT_DMG_ROUND_SWITCH_OFF");
client_print_color(id, print_team_default, "%L %L",
id, "CHAT_DMG_ROUND_PREFIX",
id, "CHAT_DMG_ROUND_SWITCH",
szSwitch, get_pcvar_num(g_pTopPlayers)
);
return PLUGIN_CONTINUE;
}
public CSGameRules_RestartRound_Pre()
{
if (get_member_game(m_bCompleteReset))
g_iRoundCount = 0;
g_iRoundCount++;
ClearArr(g_iPlayerDmg);
ClearArr(g_iPlayerKills);
for (new i = 1; i <= MaxClients; i++)
arrayset(g_aData[i], 0, ePlayerData);
}
public CBasePlayer_TakeDamage(const pevVictim, pevInflictor, const pevAttacker, Float:flDamage, bitsDamageType)
{
if (!IsPlayer(pevAttacker) || pevVictim == pevAttacker || (bitsDamageType & DMG_BLAST))
return HC_CONTINUE;
if (rg_is_player_can_takedamage(pevVictim, pevAttacker))
g_iPlayerDmg[pevAttacker] += floatround(flDamage);
return HC_CONTINUE;
}
public CBasePlayer_Killed(const pevVictim, pevAttacker)
{
if (!is_user_connected(pevAttacker) || pevVictim == pevAttacker)
return HC_CONTINUE;
g_iPlayerKills[pevAttacker]++;
return HC_CONTINUE;
}
public RoundEnd(WinStatus:status, ScenarioEventEndRound:event)
{
if (event == ROUND_GAME_COMMENCE || event == ROUND_GAME_RESTART)
return HC_CONTINUE;
if (g_iRoundCount >= get_pcvar_num(g_pRoundNumber))
{
new iPlayers[MAX_PLAYERS], iNum;
get_players(iPlayers, iNum, "h");
if (iNum >= get_pcvar_num(g_pMinPlayers))
set_task(0.1, "fnShowTopRound");
}
return HC_CONTINUE;
}
public fnShowTopRound()
{
new iPlayers[MAX_PLAYERS], iNum, pPlayer;
new szName[MAX_NAME_LENGTH], pBestPlayerId, pBestPlayerDamage;
new szMenu[512], iLen, iTop, iShowKills;
new bool:bMenuDmgShow;
get_players(iPlayers, iNum, "h");
for (new i = 0; i < iNum; i++)
{
pPlayer = iPlayers[i];
g_aData[i][PLAYER_ID] = pPlayer;
g_aData[i][DAMAGE] = _:g_iPlayerDmg[pPlayer];
g_aData[i][KILLS] = _:g_iPlayerKills[pPlayer];
}
SortCustom2D(g_aData, sizeof(g_aData), "SortRoundDamage");
if (get_pcvar_num(g_pGiveAward))
{
pBestPlayerId = g_aData[0][PLAYER_ID];
pBestPlayerDamage = g_aData[0][DAMAGE];
if (IsPlayer(pBestPlayerId) && is_user_connected(pBestPlayerId) && pBestPlayerDamage > 0)
{
get_user_name(pBestPlayerId, szName, charsmax(szName));
rg_add_account(pBestPlayerId, get_pcvar_num(g_pGiveMoney), AS_ADD, true);
client_print_color(0, print_team_default, "%L %L",
LANG_PLAYER, "CHAT_DMG_ROUND_PREFIX",
LANG_PLAYER, "CHAT_DMG_ROUND_BEST_PLAYER",
szName, pBestPlayerDamage, get_pcvar_num(g_pGiveMoney)
);
}
}
iShowKills = get_pcvar_num(g_pShowKills);
iTop = get_pcvar_num(g_pTopPlayers);
if (iShowKills)
iLen = formatex(szMenu, charsmax(szMenu), "%L^n^n", LANG_PLAYER, "MENU_DMG_ROUND_TITLE1");
else
iLen = formatex(szMenu, charsmax(szMenu), "%L^n^n", LANG_PLAYER, "MENU_DMG_ROUND_TITLE2");
for (new i = 0; i < iTop; i++)
{
if (g_aData[i][DAMAGE] <= 0)
continue;
get_user_name(g_aData[i][PLAYER_ID], szName, charsmax(szName));
if (iShowKills)
{
switch (g_aData[i][DAMAGE])
{
case 1..9:
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen, "\w%d. \r[\y00%d\r] [\y%d\r] \w%s^n",
i + 1, g_aData[i][DAMAGE], g_aData[i][KILLS], szName
);
case 10..99:
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen, "\w%d. \r[\y0%d\r] [\y%d\r] \w%s^n",
i + 1, g_aData[i][DAMAGE], g_aData[i][KILLS], szName
);
default:
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen, "\w%d. \r[\y%d\r] [\y%d\r] \w%s^n",
i + 1, g_aData[i][DAMAGE], g_aData[i][KILLS], szName
);
}
}
else
{
switch (g_aData[i][DAMAGE])
{
case 1..9:
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen, "\w%d. \r[\y00%d\r] \w%s^n",
i + 1, g_aData[i][DAMAGE], szName
);
case 10..99:
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen, "\w%d. \r[\y0%d\r] \w%s^n",
i + 1, g_aData[i][DAMAGE], szName
);
default:
iLen += formatex(szMenu[iLen], charsmax(szMenu) - iLen, "\w%d. \r[\y%d\r] \w%s^n",
i + 1, g_aData[i][DAMAGE], szName
);
}
}
bMenuDmgShow = true;
}
if (bMenuDmgShow)
{
for (new i = 0; i < iNum; i++)
{
pPlayer = iPlayers[i];
if (g_bSwitch[pPlayer] && !InMenu(pPlayer))
show_menu(pPlayer, MENU_KEYS, szMenu, get_pcvar_num(g_pShowTime), "TopDmg");
}
}
return PLUGIN_HANDLED;
}
public SortRoundDamage(const elem1[], const elem2[])
{
return (elem1[DAMAGE] < elem2[DAMAGE]) ? 1 : (elem1[DAMAGE] > elem2[DAMAGE]) ? -1 : 0;
}
public fnTopDmgHandler(id, iKey)
{
if (iKey >= 0 || iKey <= 9)
return PLUGIN_CONTINUE;
return PLUGIN_HANDLED;
}
stock InMenu(id)
{
new Menu, New;
new MenuUP = player_menu_info(id, Menu, New);
if (MenuUP || Menu)
return true;
return false;
}
public plugin_end()
{
if (g_hVault != INVALID_HANDLE)
nvault_close(g_hVault);
}