mirror of
https://github.com/shavitush/bhoptimer.git
synced 2025-12-07 18:38:26 +00:00
commit
f9b67450db
@ -23,7 +23,8 @@ It also contains support for built-in map timers (KZ) and the [Fly](https://gith
|
||||
The chat plugin manipulates chat messages sent by players.
|
||||
It includes custom chat names, tags, colors and all can be defined by the players/admins.
|
||||
Admins need the chat flag, or the "shavit_chat" override (good for a donator perk).
|
||||
There's a user-friendly command named !cchelp so the users can easily understand what's going on.
|
||||
There's a user-friendly command named !cchelp so the users can easily understand what's going on.
|
||||
In addition, it integrates with rankings and allows you to have titles for players according to their ranking, relative ranking or points in the server using !chatranks.
|
||||
|
||||
#### shavit-hud
|
||||
The HUD plugin is `bhoptimer`'s OSD frontend.
|
||||
|
||||
82
addons/sourcemod/configs/shavit-chat.cfg
Normal file
82
addons/sourcemod/configs/shavit-chat.cfg
Normal file
@ -0,0 +1,82 @@
|
||||
// Available settings:
|
||||
// "ranks" - inclusive rank range. (i.e. 1, 2, 10-19 or 0.0%-0.5%). Use a percent sign to use a percentile of total players. Add "p" as a prefix to use points instead of ranks, don't specify a range for points if you only want a minimum to unlock the title.
|
||||
// "name" - custom name appearance. Default: "{name}"
|
||||
// "message" - a prefix to the message itself. Default: ""
|
||||
// "display" - display text in the !chatranks menu. "<n>" for a new line. Filling this is required.
|
||||
// "free" - is this title available for everyone to use? Default: "0"
|
||||
//
|
||||
// Global variables:
|
||||
// {default} - default color
|
||||
// {team} - team color
|
||||
// {green} - green color
|
||||
// {name} - player name
|
||||
// {clan} - clan tag
|
||||
// {rand} - random color.
|
||||
// {message} - message text
|
||||
// {rank} - player rank (whole number)
|
||||
// {rank1} - player rank in percentage (1 decimal point)
|
||||
// {rank2} - player rank in percentage (2 decimal points)
|
||||
// {rank3} - player rank in percentage (3 decimal points)
|
||||
//
|
||||
// Refer to shavit-messages.cfg for color settings.
|
||||
//
|
||||
"Chat"
|
||||
{
|
||||
"0" // unranked
|
||||
{
|
||||
"ranks" "0"
|
||||
"name" "{team}[Unranked] {name}"
|
||||
"display" "[Unranked]<n>Title used by unranked players."
|
||||
}
|
||||
|
||||
"1"
|
||||
{
|
||||
"ranks" "p10000"
|
||||
"free" "0"
|
||||
"name" "{rand}10k! {team}{name}"
|
||||
"display" "10k Challenger<n>You are insane. You are a hero. You are a challenger.<n>A title awarded to the magnificent players who achieve 10,000 points."
|
||||
}
|
||||
|
||||
"2"
|
||||
{
|
||||
"ranks" "1"
|
||||
"name" "{rand}ONE TRUE GOD {team}{name}"
|
||||
"message" "{rand}"
|
||||
"display" "[ONE TRUE GOD]<n>A title awarded only to the very best players."
|
||||
}
|
||||
|
||||
"3"
|
||||
{
|
||||
"ranks" "2"
|
||||
"name" "{green}LEGENDARY {name}"
|
||||
"display" "[LEGENDARY]<n>A title obtained by legendary players."
|
||||
}
|
||||
|
||||
"4"
|
||||
{
|
||||
"ranks" "3"
|
||||
"name" "{green}HERO {team}{name}"
|
||||
"display" "[HERO]<n>You're a hero, and you deserve this title."
|
||||
}
|
||||
|
||||
"5"
|
||||
{
|
||||
"ranks" "4-10"
|
||||
"name" "{rand}scrub{rand}! {name}"
|
||||
"display" "scrub!<n>You're a noob."
|
||||
}
|
||||
|
||||
"6"
|
||||
{
|
||||
"ranks" "11-100%"
|
||||
"name" ":( {name}"
|
||||
"display" "sad face<n>You're terrible. Get good!"
|
||||
}
|
||||
|
||||
"7"
|
||||
{
|
||||
"free" "1"
|
||||
"name" "{rand}:) {name}"
|
||||
"display" ":)<n>Free chat title."
|
||||
}
|
||||
}
|
||||
@ -61,7 +61,7 @@ forward Action CP_OnChatMessage(int& author, ArrayList recipients, char[] flagst
|
||||
forward void CP_OnChatMessagePost(int author, ArrayList recipients, const char[] flagstring, const char[] formatstring, const char[] name, const char[] message, bool processcolors, bool removecolors);
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public __pl_chat_processor_SetNTVOptional()
|
||||
public void __pl_chat_processor_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("ChatProcessor_GetFlagFormatString");
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
#endif
|
||||
#define _shavit_included
|
||||
|
||||
#define SHAVIT_VERSION "1.5b"
|
||||
#define SHAVIT_VERSION "2.0.0"
|
||||
#define STYLE_LIMIT 256
|
||||
#define MAX_ZONES 64
|
||||
|
||||
@ -1102,12 +1102,22 @@ native StringMap Shavit_GetMapTiers();
|
||||
* This native will auto-assign colors and a chat prefix.
|
||||
*
|
||||
* @param client Client index.
|
||||
* @param format Formattiing rules.
|
||||
* @param format Formatting rules.
|
||||
* @param any Variable number of format parameters.
|
||||
* @return PrintToChat()
|
||||
*/
|
||||
native int Shavit_PrintToChat(int client, const char[] format, any ...);
|
||||
|
||||
/**
|
||||
* Logs an entry to bhoptimer's log file.
|
||||
* (addons/sourcemod/logs/shavit.log)
|
||||
*
|
||||
* @param format Formatting rules.
|
||||
* @param any Variable number of format parameters.
|
||||
* @noreturn
|
||||
*/
|
||||
native void Shavit_LogMessage(const char[] format, any ...);
|
||||
|
||||
// same as Shavit_PrintToChat() but loops through the whole server
|
||||
// code stolen from the base halflife.inc file
|
||||
stock void Shavit_PrintToChatAll(const char[] format, any ...)
|
||||
|
||||
@ -22,25 +22,65 @@
|
||||
|
||||
#include <sourcemod>
|
||||
#include <chat-processor>
|
||||
#include <clientprefs>
|
||||
|
||||
#undef REQUIRE_PLUGIN
|
||||
#define USES_CHAT_COLORS
|
||||
#include <shavit>
|
||||
#include <rtler>
|
||||
|
||||
#undef REQUIRE_EXTENSIONS
|
||||
#include <cstrike>
|
||||
|
||||
enum ChatRanksCache
|
||||
{
|
||||
iCRRangeType, // 0 - flat, 1 - percent, 2 - point range
|
||||
Float:fCRFrom,
|
||||
Float:fCRTo,
|
||||
bool:bCRFree,
|
||||
String:sCRName[MAXLENGTH_NAME],
|
||||
String:sCRMessage[MAXLENGTH_MESSAGE],
|
||||
String:sCRDisplay[192],
|
||||
CRCACHE_SIZE
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
Rank_Flat,
|
||||
Rank_Percentage,
|
||||
Rank_Points
|
||||
}
|
||||
|
||||
#pragma newdecls required
|
||||
#pragma semicolon 1
|
||||
#pragma dynamic 131072
|
||||
|
||||
// database
|
||||
Database gH_SQL = null;
|
||||
char gS_MySQLPrefix[32];
|
||||
|
||||
// modules
|
||||
bool gB_Rankings = false;
|
||||
bool gB_RTLer = false;
|
||||
|
||||
// cvars
|
||||
ConVar gCV_RankingsIntegration = null;
|
||||
ConVar gCV_CustomChat = null;
|
||||
|
||||
// cached cvars
|
||||
bool gB_RankingsIntegration = true;
|
||||
int gI_CustomChat = 1;
|
||||
|
||||
// cache
|
||||
EngineVersion gEV_Type = Engine_Unknown;
|
||||
|
||||
Handle gH_ChatCookie = null;
|
||||
|
||||
// -2: auto-assign - user will fallback to this if they're on an index that they don't have access to.
|
||||
// -1: custom ccname/ccmsg
|
||||
int gI_ChatSelection[MAXPLAYERS+1];
|
||||
ArrayList gA_ChatRanks = null;
|
||||
|
||||
bool gB_AllowCustom[MAXPLAYERS+1];
|
||||
|
||||
bool gB_NameEnabled[MAXPLAYERS+1];
|
||||
@ -53,7 +93,7 @@ public Plugin myinfo =
|
||||
{
|
||||
name = "[shavit] Chat",
|
||||
author = "shavit",
|
||||
description = "Custom chat privileges (custom name and message colors).",
|
||||
description = "Custom chat privileges (custom name/message colors), and rankings integration.",
|
||||
version = SHAVIT_VERSION,
|
||||
url = "https://github.com/shavitush/bhoptimer"
|
||||
}
|
||||
@ -79,20 +119,123 @@ public void OnPluginStart()
|
||||
RegConsoleCmd("sm_ccname", Command_CCName, "Toggles/sets a custom chat name. Usage: sm_ccname <text> or sm_ccname \"off\" to disable.");
|
||||
RegConsoleCmd("sm_ccmsg", Command_CCMessage, "Toggles/sets a custom chat message color. Usage: sm_ccmsg <color> or sm_ccmsg \"off\" to disable.");
|
||||
RegConsoleCmd("sm_ccmessage", Command_CCMessage, "Toggles/sets a custom chat message color. Usage: sm_ccmessage <color> or sm_ccmessage \"off\" to disable.");
|
||||
RegConsoleCmd("sm_chatrank", Command_ChatRanks, "View a menu with the chat ranks available to you.");
|
||||
RegConsoleCmd("sm_chatranks", Command_ChatRanks, "View a menu with the chat ranks available to you.");
|
||||
|
||||
RegAdminCmd("sm_cclist", Command_CCList, ADMFLAG_CHAT, "Print the custom chat setting of all online players.");
|
||||
RegAdminCmd("sm_reloadchatranks", Command_ReloadChatRanks, ADMFLAG_ROOT, "Reloads the chatranks config file.");
|
||||
|
||||
gCV_RankingsIntegration = CreateConVar("shavit_chat_rankings", "1", "Integrate with rankings?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
gCV_CustomChat = CreateConVar("shavit_chat_customchat", "1", "Allow custom chat names or message colors?\n0 - Disabled\n1 - Enabled (requires chat flag/'shavit_chat' override)\n2 - Allow use by everyone", 0, true, 0.0, true, 2.0);
|
||||
|
||||
gCV_RankingsIntegration.AddChangeHook(OnConVarChanged);
|
||||
gCV_CustomChat.AddChangeHook(OnConVarChanged);
|
||||
|
||||
AutoExecConfig();
|
||||
|
||||
gH_ChatCookie = RegClientCookie("shavit_chat_selection", "Chat settings", CookieAccess_Protected);
|
||||
gA_ChatRanks = new ArrayList(view_as<int>(CRCACHE_SIZE));
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && !IsFakeClient(i))
|
||||
{
|
||||
OnClientPostAdminCheck(i);
|
||||
|
||||
if(AreClientCookiesCached(i))
|
||||
{
|
||||
OnClientCookiesCached(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SQL_SetPrefix();
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
if(!LoadChatConfig())
|
||||
{
|
||||
SetFailState("Could not load the chat configuration file. Make sure it exists (addons/sourcemod/configs/shavit-chat.cfg) and follows the proper syntax!");
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadChatConfig()
|
||||
{
|
||||
char[] sPath = new char[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-chat.cfg");
|
||||
|
||||
KeyValues kv = new KeyValues("shavit-chat");
|
||||
|
||||
if(!kv.ImportFromFile(sPath) || !kv.GotoFirstSubKey())
|
||||
{
|
||||
delete kv;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
gA_ChatRanks.Clear();
|
||||
|
||||
do
|
||||
{
|
||||
any[] aChatTitle = new any[CRCACHE_SIZE];
|
||||
char[] sRanks = new char[32];
|
||||
kv.GetString("ranks", sRanks, MAXLENGTH_NAME, "0");
|
||||
|
||||
if(sRanks[0] == 'p')
|
||||
{
|
||||
aChatTitle[iCRRangeType] = Rank_Points;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
aChatTitle[iCRRangeType] = (StrContains(sRanks, "%%") == -1)? Rank_Flat:Rank_Percentage;
|
||||
}
|
||||
|
||||
ReplaceString(sRanks, 32, "p", "");
|
||||
ReplaceString(sRanks, 32, "%%", "");
|
||||
|
||||
if(StrContains(sRanks, "-") != -1)
|
||||
{
|
||||
char[][] sExplodedString = new char[2][16];
|
||||
ExplodeString(sRanks, "-", sExplodedString, 2, 64);
|
||||
aChatTitle[fCRFrom] = StringToFloat(sExplodedString[0]);
|
||||
aChatTitle[fCRTo] = StringToFloat(sExplodedString[1]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
float fRank = StringToFloat(sRanks);
|
||||
|
||||
aChatTitle[fCRFrom] = fRank;
|
||||
aChatTitle[fCRTo] = (aChatTitle[iCRRangeType] != Rank_Points)? fRank:2147483648.0;
|
||||
}
|
||||
|
||||
aChatTitle[bCRFree] = view_as<bool>(kv.GetNum("free", false));
|
||||
|
||||
kv.GetString("name", aChatTitle[sCRName], MAXLENGTH_NAME, "{name}");
|
||||
kv.GetString("message", aChatTitle[sCRMessage], MAXLENGTH_MESSAGE, "");
|
||||
kv.GetString("display", aChatTitle[sCRDisplay], 192, "");
|
||||
|
||||
if(strlen(aChatTitle[sCRDisplay]) > 0)
|
||||
{
|
||||
gA_ChatRanks.PushArray(aChatTitle);
|
||||
}
|
||||
}
|
||||
|
||||
while(kv.GotoNextKey());
|
||||
|
||||
delete kv;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
gB_RankingsIntegration = gCV_RankingsIntegration.BoolValue;
|
||||
gI_CustomChat = gCV_CustomChat.IntValue;
|
||||
}
|
||||
|
||||
public void Shavit_OnDatabaseLoaded()
|
||||
{
|
||||
gH_SQL = Shavit_GetDatabase();
|
||||
@ -154,6 +297,11 @@ public void OnLibraryAdded(const char[] name)
|
||||
{
|
||||
gB_RTLer = true;
|
||||
}
|
||||
|
||||
else if(StrEqual(name, "shavit-rankings"))
|
||||
{
|
||||
gB_Rankings = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLibraryRemoved(const char[] name)
|
||||
@ -162,6 +310,28 @@ public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
gB_RTLer = false;
|
||||
}
|
||||
|
||||
else if(StrEqual(name, "shavit-rankings"))
|
||||
{
|
||||
gB_Rankings = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientCookiesCached(int client)
|
||||
{
|
||||
char[] sChatSettings = new char[8];
|
||||
GetClientCookie(client, gH_ChatCookie, sChatSettings, 8);
|
||||
|
||||
if(strlen(sChatSettings) == 0)
|
||||
{
|
||||
SetClientCookie(client, gH_ChatCookie, "-2");
|
||||
gI_ChatSelection[client] = -2;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
gI_ChatSelection[client] = StringToInt(sChatSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
@ -182,7 +352,7 @@ public void OnClientPutInServer(int client)
|
||||
|
||||
public void OnClientPostAdminCheck(int client)
|
||||
{
|
||||
gB_AllowCustom[client] = CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT);
|
||||
gB_AllowCustom[client] = gI_CustomChat > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gI_CustomChat == 2);
|
||||
|
||||
if(gH_SQL != null)
|
||||
{
|
||||
@ -201,14 +371,16 @@ public Action Command_CCHelp(int client, int args)
|
||||
|
||||
Shavit_PrintToChat(client, "%T", "CheckConsole", client);
|
||||
|
||||
PrintToConsole(client, "%T\n", "CCHelp_Intro", client);
|
||||
PrintToConsole(client, "%T", "CCHelp_Generic", client);
|
||||
PrintToConsole(client, "%T", "CCHelp_GenericVariables", client);
|
||||
PrintToConsole(client, "%T\n\n%T\n\n%T\n",
|
||||
"CCHelp_Intro", client,
|
||||
"CCHelp_Generic", client,
|
||||
"CCHelp_GenericVariables", client);
|
||||
|
||||
if(IsSource2013(gEV_Type))
|
||||
{
|
||||
PrintToConsole(client, "%T", "CCHelp_CSS_1", client);
|
||||
PrintToConsole(client, "%T", "CCHelp_CSS_2", client);
|
||||
PrintToConsole(client, "%T\n\n%T",
|
||||
"CCHelp_CSS_1", client,
|
||||
"CCHelp_CSS_2", client);
|
||||
}
|
||||
|
||||
else
|
||||
@ -237,8 +409,6 @@ public Action Command_CCName(int client, int args)
|
||||
|
||||
char[] sArgs = new char[128];
|
||||
GetCmdArgString(sArgs, 128);
|
||||
TrimString(sArgs);
|
||||
FormatColors(sArgs, 128, true, true);
|
||||
|
||||
if(args == 0 || strlen(sArgs) == 0)
|
||||
{
|
||||
@ -287,8 +457,6 @@ public Action Command_CCMessage(int client, int args)
|
||||
|
||||
char[] sArgs = new char[32];
|
||||
GetCmdArgString(sArgs, 32);
|
||||
TrimString(sArgs);
|
||||
FormatColors(sArgs, 32, true, true);
|
||||
|
||||
if(args == 0 || strlen(sArgs) == 0)
|
||||
{
|
||||
@ -319,6 +487,175 @@ public Action Command_CCMessage(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_ChatRanks(int client, int args)
|
||||
{
|
||||
if(client == 0)
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
return ShowChatRanksMenu(client, 0);
|
||||
}
|
||||
|
||||
Action ShowChatRanksMenu(int client, int item)
|
||||
{
|
||||
Menu menu = new Menu(MenuHandler_ChatRanks);
|
||||
menu.SetTitle("%T\n ", "SelectChatRank", client);
|
||||
|
||||
char[] sDisplay = new char[128];
|
||||
FormatEx(sDisplay, 128, "%T\n ", "AutoAssign", client);
|
||||
menu.AddItem("-2", sDisplay, (gI_ChatSelection[client] == -2)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
|
||||
|
||||
if(gB_AllowCustom[client])
|
||||
{
|
||||
FormatEx(sDisplay, 128, "%T\n ", "CustomChat", client);
|
||||
menu.AddItem("-1", sDisplay, (gI_ChatSelection[client] == -1)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
|
||||
}
|
||||
|
||||
int iLength = gA_ChatRanks.Length;
|
||||
|
||||
for(int i = 0; i < iLength; i++)
|
||||
{
|
||||
if(!HasRankAccess(client, i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char[] sInfo = new char[8];
|
||||
IntToString(i, sInfo, 8);
|
||||
|
||||
any[] aCache = new any[CRCACHE_SIZE];
|
||||
gA_ChatRanks.GetArray(i, aCache, view_as<int>(CRCACHE_SIZE));
|
||||
|
||||
strcopy(sDisplay, 192, aCache[sCRDisplay]);
|
||||
ReplaceString(sDisplay, 192, "<n>", "\n");
|
||||
StrCat(sDisplay, 192, "\n "); // to add spacing between each entry
|
||||
|
||||
menu.AddItem(sInfo, sDisplay, (gI_ChatSelection[client] == i)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
|
||||
}
|
||||
|
||||
menu.ExitButton = true;
|
||||
menu.DisplayAt(client, item, MENU_TIME_FOREVER);
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public int MenuHandler_ChatRanks(Menu menu, MenuAction action, int param1, int param2)
|
||||
{
|
||||
if(action == MenuAction_Select)
|
||||
{
|
||||
char[] sInfo = new char[8];
|
||||
menu.GetItem(param2, sInfo, 8);
|
||||
|
||||
int iChoice = StringToInt(sInfo);
|
||||
|
||||
gI_ChatSelection[param1] = iChoice;
|
||||
SetClientCookie(param1, gH_ChatCookie, sInfo);
|
||||
|
||||
Shavit_PrintToChat(param1, "%T", "ChatUpdated", param1);
|
||||
ShowChatRanksMenu(param1, GetMenuSelectionPosition());
|
||||
}
|
||||
|
||||
else if(action == MenuAction_End)
|
||||
{
|
||||
delete menu;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasRankAccess(int client, int rank)
|
||||
{
|
||||
if(rank == -2 ||
|
||||
(rank == -1 && gB_AllowCustom[client]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
else if(!(0 <= rank <= (gA_ChatRanks.Length - 1)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static any aCache[view_as<int>(bCRFree)+1];
|
||||
gA_ChatRanks.GetArray(rank, aCache[0], sizeof(aCache)); // a hack to only retrieve up to what we want
|
||||
|
||||
if(aCache[bCRFree])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!gB_Rankings || !gB_RankingsIntegration)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float fRank = (aCache[iCRRangeType] != Rank_Points)? float(Shavit_GetRank(client)):Shavit_GetPoints(client);
|
||||
|
||||
if(aCache[iCRRangeType] == Rank_Flat || aCache[iCRRangeType] == Rank_Points)
|
||||
{
|
||||
if(aCache[fCRFrom] <= fRank <= aCache[fCRTo])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int iRanked = Shavit_GetRankedPlayers();
|
||||
|
||||
// just in case..
|
||||
if(iRanked == 0)
|
||||
{
|
||||
iRanked = 1;
|
||||
}
|
||||
|
||||
float fPercentile = (fRank / iRanked) * 100.0;
|
||||
|
||||
if(aCache[fCRFrom] <= fPercentile <= aCache[fCRTo])
|
||||
{
|
||||
PrintToServer("%.1f <= %.2f <= %.2f", aCache[fCRFrom], fPercentile, aCache[fCRTo]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetPlayerChatSettings(int client, char[] name, char[] message)
|
||||
{
|
||||
int iRank = gI_ChatSelection[client];
|
||||
|
||||
if(!HasRankAccess(client, iRank))
|
||||
{
|
||||
iRank = -2;
|
||||
}
|
||||
|
||||
int iLength = gA_ChatRanks.Length;
|
||||
|
||||
// if we auto-assign, start looking for an available rank starting from index 0
|
||||
if(iRank == -2)
|
||||
{
|
||||
for(int i = 0; i < iLength; i++)
|
||||
{
|
||||
if(HasRankAccess(client, i))
|
||||
{
|
||||
iRank = i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(0 <= iRank <= (iLength - 1))
|
||||
{
|
||||
any[] aCache = new any[CRCACHE_SIZE];
|
||||
gA_ChatRanks.GetArray(iRank, aCache, view_as<int>(CRCACHE_SIZE));
|
||||
|
||||
strcopy(name, MAXLENGTH_NAME, aCache[sCRName]);
|
||||
strcopy(message, MAXLENGTH_NAME, aCache[sCRMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
public Action Command_CCList(int client, int args)
|
||||
{
|
||||
ReplyToCommand(client, "%T", "CheckConsole", client);
|
||||
@ -327,36 +664,67 @@ public Action Command_CCList(int client, int args)
|
||||
{
|
||||
if(gB_AllowCustom[i])
|
||||
{
|
||||
PrintToConsole(client, "%N (%d/%d) (name: \"%s\"; message: \"%s\")", i, i, GetClientUserId(i), gS_CustomName[i], gS_CustomMessage[i]);
|
||||
PrintToConsole(client, "%N (%d/#%d) (name: \"%s\"; message: \"%s\")", i, i, GetClientUserId(i), gS_CustomName[i], gS_CustomMessage[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_ReloadChatRanks(int client, int args)
|
||||
{
|
||||
if(LoadChatConfig())
|
||||
{
|
||||
ReplyToCommand(client, "Reloaded chatranks config.");
|
||||
}
|
||||
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action CP_OnChatMessage(int &author, ArrayList recipients, char[] flagstring, char[] name, char[] message, bool &processcolors, bool &removecolors)
|
||||
{
|
||||
if(author == 0 || !gB_AllowCustom[author])
|
||||
if(author == 0)
|
||||
{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if(gB_NameEnabled[author] && strlen(gS_CustomName[author]) > 0)
|
||||
char[] sName = new char[MAXLENGTH_NAME];
|
||||
char[] sMessage = new char[MAXLENGTH_MESSAGE];
|
||||
|
||||
if(gB_AllowCustom[author] && gI_ChatSelection[author] == -1)
|
||||
{
|
||||
char[] sName = new char[MAX_NAME_LENGTH];
|
||||
GetClientName(author, sName, MAX_NAME_LENGTH);
|
||||
ReplaceString(gS_CustomName[author], MAXLENGTH_NAME, "{name}", sName);
|
||||
|
||||
strcopy(name, MAXLENGTH_NAME, gS_CustomName[author]);
|
||||
FormatRandom(name, MAXLENGTH_NAME);
|
||||
|
||||
if(gEV_Type == Engine_CSGO)
|
||||
if(gB_NameEnabled[author])
|
||||
{
|
||||
Format(name, MAXLENGTH_NAME, " %s", name);
|
||||
strcopy(sName, MAXLENGTH_NAME, gS_CustomName[author]);
|
||||
}
|
||||
|
||||
if(gB_MessageEnabled[author])
|
||||
{
|
||||
strcopy(sMessage, MAXLENGTH_MESSAGE, gS_CustomMessage[author]);
|
||||
}
|
||||
}
|
||||
|
||||
if(gB_MessageEnabled[author] && strlen(gS_CustomMessage[author]) > 0)
|
||||
else
|
||||
{
|
||||
GetPlayerChatSettings(author, sName, sMessage);
|
||||
}
|
||||
|
||||
if(strlen(sName) > 0)
|
||||
{
|
||||
if(gEV_Type == Engine_CSGO)
|
||||
{
|
||||
FormatEx(name, MAXLENGTH_NAME, " %s", sName);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
strcopy(name, MAXLENGTH_NAME, sName);
|
||||
}
|
||||
|
||||
FormatChat(author, name, MAXLENGTH_NAME);
|
||||
}
|
||||
|
||||
if(strlen(sMessage) > 0)
|
||||
{
|
||||
char[] sTemp = new char[MAXLENGTH_MESSAGE];
|
||||
|
||||
@ -364,34 +732,21 @@ public Action CP_OnChatMessage(int &author, ArrayList recipients, char[] flagstr
|
||||
if(gB_RTLer && RTLify(sTemp, MAXLENGTH_MESSAGE, message) > 0)
|
||||
{
|
||||
TrimString(message);
|
||||
Format(message, MAXLENGTH_MESSAGE, "%s%s", message, gS_CustomMessage[author]);
|
||||
Format(message, MAXLENGTH_MESSAGE, "%s%s", message, sMessage);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Format(message, MAXLENGTH_MESSAGE, "%s%s", gS_CustomMessage[author], message);
|
||||
Format(message, MAXLENGTH_MESSAGE, "%s%s", sMessage, message);
|
||||
}
|
||||
|
||||
FormatRandom(message, MAXLENGTH_MESSAGE);
|
||||
FormatChat(author, message, MAXLENGTH_NAME);
|
||||
}
|
||||
|
||||
#if defined DEBUG
|
||||
PrintToServer("%N %s", author, flagstring);
|
||||
#endif
|
||||
|
||||
bool allchat = (StrContains(flagstring, "_All") != -1);
|
||||
int team = GetClientTeam(author);
|
||||
|
||||
recipients.Clear();
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(i == author || (IsClientInGame(i) && (allchat || GetClientTeam(i) == team)))
|
||||
{
|
||||
recipients.Push(i);
|
||||
}
|
||||
}
|
||||
|
||||
removecolors = true;
|
||||
processcolors = false;
|
||||
|
||||
@ -407,9 +762,12 @@ void FormatColors(char[] buffer, int size, bool colors, bool escape)
|
||||
ReplaceString(buffer, size, gS_GlobalColorNames[i], gS_GlobalColors[i]);
|
||||
}
|
||||
|
||||
for(int i = 0; i < sizeof(gS_CSGOColorNames); i++)
|
||||
if(gEV_Type == Engine_CSGO)
|
||||
{
|
||||
ReplaceString(buffer, size, gS_CSGOColorNames[i], gS_CSGOColors[i]);
|
||||
for(int i = 0; i < sizeof(gS_CSGOColorNames); i++)
|
||||
{
|
||||
ReplaceString(buffer, size, gS_CSGOColorNames[i], gS_CSGOColors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ReplaceString(buffer, size, "^", "\x07");
|
||||
@ -424,8 +782,10 @@ void FormatColors(char[] buffer, int size, bool colors, bool escape)
|
||||
}
|
||||
}
|
||||
|
||||
void FormatRandom(char[] buffer, int size)
|
||||
void FormatChat(int client, char[] buffer, int size)
|
||||
{
|
||||
FormatColors(buffer, size, true, true);
|
||||
|
||||
char[] temp = new char[8];
|
||||
|
||||
do
|
||||
@ -446,6 +806,42 @@ void FormatRandom(char[] buffer, int size)
|
||||
}
|
||||
|
||||
while(ReplaceStringEx(buffer, size, "{rand}", temp) > 0);
|
||||
|
||||
if(gEV_Type != Engine_TF2)
|
||||
{
|
||||
char[] sTag = new char[32];
|
||||
CS_GetClientClanTag(client, sTag, 32);
|
||||
ReplaceString(buffer, size, "{clan}", sTag);
|
||||
}
|
||||
|
||||
if(gB_Rankings)
|
||||
{
|
||||
int iRank = Shavit_GetRank(client);
|
||||
char[] sRank = new char[16];
|
||||
IntToString(iRank, sRank, 16);
|
||||
ReplaceString(buffer, size, "{rank}", sRank);
|
||||
|
||||
int iRanked = Shavit_GetRankedPlayers();
|
||||
|
||||
if(iRanked == 0)
|
||||
{
|
||||
iRanked = 1;
|
||||
}
|
||||
|
||||
float fPercentile = (float(iRank) / iRanked) * 100.0;
|
||||
FormatEx(sRank, 16, "%.01f", fPercentile);
|
||||
ReplaceString(buffer, size, "{rank1}", sRank);
|
||||
|
||||
FormatEx(sRank, 16, "%.02f", fPercentile);
|
||||
ReplaceString(buffer, size, "{rank2}", sRank);
|
||||
|
||||
FormatEx(sRank, 16, "%.03f", fPercentile);
|
||||
ReplaceString(buffer, size, "{rank3}", sRank);
|
||||
}
|
||||
|
||||
char[] sName = new char[MAX_NAME_LENGTH];
|
||||
GetClientName(client, sName, MAX_NAME_LENGTH);
|
||||
ReplaceString(buffer, size, "{name}", sName);
|
||||
}
|
||||
|
||||
int RealRandomInt(int min, int max)
|
||||
|
||||
@ -94,22 +94,16 @@ bool gB_Zones = false;
|
||||
bool gB_WR = false;
|
||||
|
||||
// cvars
|
||||
ConVar gCV_Autobhop = null;
|
||||
ConVar gCV_LeftRight = null;
|
||||
ConVar gCV_Restart = null;
|
||||
ConVar gCV_Pause = null;
|
||||
ConVar gCV_NoStaminaReset = null;
|
||||
ConVar gCV_AllowTimerWithoutZone = null;
|
||||
ConVar gCV_BlockPreJump = null;
|
||||
ConVar gCV_NoZAxisSpeed = null;
|
||||
ConVar gCV_VelocityTeleport = null;
|
||||
|
||||
// cached cvars
|
||||
bool gB_Autobhop = true;
|
||||
bool gB_LeftRight = true;
|
||||
bool gB_Restart = true;
|
||||
bool gB_Pause = true;
|
||||
bool gB_NoStaminaReset = true;
|
||||
bool gB_AllowTimerWithoutZone = false;
|
||||
bool gB_BlockPreJump = false;
|
||||
bool gB_NoZAxisSpeed = true;
|
||||
@ -135,6 +129,7 @@ char gS_ChatStrings[CHATSETTINGS_SIZE][128];
|
||||
// misc cache
|
||||
bool gB_StopChatSound = false;
|
||||
bool gB_HookedJump = false;
|
||||
char gS_LogPath[PLATFORM_MAX_PATH];
|
||||
|
||||
// kz support
|
||||
bool gB_KZMap = false;
|
||||
@ -169,6 +164,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
|
||||
CreateNative("Shavit_IsKZMap", Native_IsKZMap);
|
||||
CreateNative("Shavit_IsPracticeMode", Native_IsPracticeMode);
|
||||
CreateNative("Shavit_LoadSnapshot", Native_LoadSnapshot);
|
||||
CreateNative("Shavit_LogMessage", Native_LogMessage);
|
||||
CreateNative("Shavit_MarkKZMap", Native_MarkKZMap);
|
||||
CreateNative("Shavit_PauseTimer", Native_PauseTimer);
|
||||
CreateNative("Shavit_PrintToChat", Native_PrintToChat);
|
||||
@ -282,29 +278,26 @@ public void OnPluginStart()
|
||||
|
||||
// style commands
|
||||
gSM_StyleCommands = new StringMap();
|
||||
// commands END
|
||||
|
||||
#if defined DEBUG
|
||||
RegConsoleCmd("sm_finishtest", Command_FinishTest);
|
||||
#endif
|
||||
// commands END
|
||||
|
||||
// logs
|
||||
BuildPath(Path_SM, gS_LogPath, PLATFORM_MAX_PATH, "logs/shavit.log");
|
||||
|
||||
CreateConVar("shavit_version", SHAVIT_VERSION, "Plugin version.", (FCVAR_NOTIFY | FCVAR_DONTRECORD));
|
||||
|
||||
gCV_Autobhop = CreateConVar("shavit_core_autobhop", "1", "Enable autobhop?\nWill be forced to not work if STYLE_AUTOBHOP is not defined for a style!", FCVAR_NOTIFY, true, 0.0, true, 1.0);
|
||||
gCV_LeftRight = CreateConVar("shavit_core_blockleftright", "1", "Block +left/right?", 0, true, 0.0, true, 1.0);
|
||||
gCV_Restart = CreateConVar("shavit_core_restart", "1", "Allow commands that restart the timer?", 0, true, 0.0, true, 1.0);
|
||||
gCV_Pause = CreateConVar("shavit_core_pause", "1", "Allow pausing?", 0, true, 0.0, true, 1.0);
|
||||
gCV_NoStaminaReset = CreateConVar("shavit_core_nostaminareset", "1", "Disables the built-in stamina reset.\nAlso known as 'easybhop'.\nWill be forced to not work if STYLE_EASYBHOP is not defined for a style!", 0, true, 0.0, true, 1.0);
|
||||
gCV_AllowTimerWithoutZone = CreateConVar("shavit_core_timernozone", "0", "Allow the timer to start if there's no start zone?", 0, true, 0.0, true, 1.0);
|
||||
gCV_BlockPreJump = CreateConVar("shavit_core_blockprejump", "0", "Prevents jumping in the start zone.", 0, true, 0.0, true, 1.0);
|
||||
gCV_NoZAxisSpeed = CreateConVar("shavit_core_nozaxisspeed", "1", "Don't start timer if vertical speed exists (btimes style).", 0, true, 0.0, true, 1.0);
|
||||
gCV_VelocityTeleport = CreateConVar("shavit_core_velocityteleport", "0", "Teleport the client when changing its velocity? (for special styles)", 0, true, 0.0, true, 1.0);
|
||||
|
||||
gCV_Autobhop.AddChangeHook(OnConVarChanged);
|
||||
gCV_LeftRight.AddChangeHook(OnConVarChanged);
|
||||
gCV_Restart.AddChangeHook(OnConVarChanged);
|
||||
gCV_Pause.AddChangeHook(OnConVarChanged);
|
||||
gCV_NoStaminaReset.AddChangeHook(OnConVarChanged);
|
||||
gCV_AllowTimerWithoutZone.AddChangeHook(OnConVarChanged);
|
||||
gCV_BlockPreJump.AddChangeHook(OnConVarChanged);
|
||||
gCV_NoZAxisSpeed.AddChangeHook(OnConVarChanged);
|
||||
@ -342,11 +335,8 @@ public void OnPluginStart()
|
||||
|
||||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
gB_Autobhop = gCV_Autobhop.BoolValue;
|
||||
gB_LeftRight = gCV_LeftRight.BoolValue;
|
||||
gB_Restart = gCV_Restart.BoolValue;
|
||||
gB_Pause = gCV_Pause.BoolValue;
|
||||
gB_NoStaminaReset = gCV_NoStaminaReset.BoolValue;
|
||||
gB_AllowTimerWithoutZone = gCV_AllowTimerWithoutZone.BoolValue;
|
||||
gB_BlockPreJump = gCV_BlockPreJump.BoolValue;
|
||||
gB_NoZAxisSpeed = gCV_NoZAxisSpeed.BoolValue;
|
||||
@ -801,7 +791,7 @@ void DoJump(int client)
|
||||
}
|
||||
|
||||
// TF2 doesn't use stamina
|
||||
if(gEV_Type != Engine_TF2 && (gB_NoStaminaReset && gA_StyleSettings[gBS_Style[client]][bEasybhop]) || Shavit_InsideZone(client, Zone_Easybhop, gI_Track[client]))
|
||||
if(gEV_Type != Engine_TF2 && (gA_StyleSettings[gBS_Style[client]][bEasybhop]) || Shavit_InsideZone(client, Zone_Easybhop, gI_Track[client]))
|
||||
{
|
||||
SetEntPropFloat(client, Prop_Send, "m_flStamina", 0.0);
|
||||
}
|
||||
@ -1040,11 +1030,11 @@ public int Native_StopChatSound(Handle handler, int numParams)
|
||||
public int Native_PrintToChat(Handle handler, int numParams)
|
||||
{
|
||||
int client = GetNativeCell(1);
|
||||
static int written = 0; // useless?
|
||||
static int iWritten = 0; // useless?
|
||||
|
||||
char[] buffer = new char[300];
|
||||
FormatNativeString(0, 2, 3, 300, written, buffer);
|
||||
Format(buffer, 300, "%s %s%s", gS_ChatStrings[sMessagePrefix], gS_ChatStrings[sMessageText], buffer);
|
||||
char[] sBuffer = new char[300];
|
||||
FormatNativeString(0, 2, 3, 300, iWritten, sBuffer);
|
||||
Format(sBuffer, 300, "%s %s%s", gS_ChatStrings[sMessagePrefix], gS_ChatStrings[sMessageText], sBuffer);
|
||||
|
||||
if(IsSource2013(gEV_Type))
|
||||
{
|
||||
@ -1054,7 +1044,7 @@ public int Native_PrintToChat(Handle handler, int numParams)
|
||||
{
|
||||
BfWriteByte(hSayText2, 0);
|
||||
BfWriteByte(hSayText2, !gB_StopChatSound);
|
||||
BfWriteString(hSayText2, buffer);
|
||||
BfWriteString(hSayText2, sBuffer);
|
||||
}
|
||||
|
||||
EndMessage();
|
||||
@ -1062,7 +1052,7 @@ public int Native_PrintToChat(Handle handler, int numParams)
|
||||
|
||||
else
|
||||
{
|
||||
PrintToChat(client, " %s", buffer);
|
||||
PrintToChat(client, " %s", sBuffer);
|
||||
}
|
||||
|
||||
gB_StopChatSound = false;
|
||||
@ -1177,6 +1167,23 @@ public int Native_LoadSnapshot(Handle handler, int numParams)
|
||||
gI_SHSW_FirstCombination[client] = view_as<int>(snapshot[iSHSWCombination]);
|
||||
}
|
||||
|
||||
public int Native_LogMessage(Handle plugin, int numParams)
|
||||
{
|
||||
char[] sPlugin = new char[32];
|
||||
|
||||
if(!GetPluginInfo(plugin, PlInfo_Name, sPlugin, 32))
|
||||
{
|
||||
GetPluginFilename(plugin, sPlugin, 32);
|
||||
}
|
||||
|
||||
static int iWritten = 0;
|
||||
|
||||
char[] sBuffer = new char[300];
|
||||
FormatNativeString(0, 1, 2, 300, iWritten, sBuffer);
|
||||
|
||||
LogToFileEx(gS_LogPath, "[%s] %s", sPlugin, sBuffer);
|
||||
}
|
||||
|
||||
public int Native_MarkKZMap(Handle handler, int numParams)
|
||||
{
|
||||
gB_KZMap = true;
|
||||
@ -1217,6 +1224,7 @@ void StartTimer(int client, int track)
|
||||
|
||||
if(result == Plugin_Continue)
|
||||
{
|
||||
gB_ClientPaused[client] = false;
|
||||
gI_Strafes[client] = 0;
|
||||
gI_Jumps[client] = 0;
|
||||
gI_TotalMeasures[client] = 0;
|
||||
@ -1862,7 +1870,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
char[] sCheatDetected = new char[64];
|
||||
|
||||
// +left/right block
|
||||
if(gB_LeftRight && (!gB_Zones || !bInStart && ((gA_StyleSettings[gBS_Style[client]][bBlockPLeft] &&
|
||||
if(!gB_Zones || (!bInStart && ((gA_StyleSettings[gBS_Style[client]][bBlockPLeft] &&
|
||||
(buttons & IN_LEFT) > 0) || (gA_StyleSettings[gBS_Style[client]][bBlockPRight] && (buttons & IN_RIGHT) > 0))))
|
||||
{
|
||||
FormatEx(sCheatDetected, 64, "%T", "LeftRightCheat", client);
|
||||
@ -1949,10 +1957,10 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
bool bSHSW = (gA_StyleSettings[gBS_Style[client]][iForceHSW] == 2) && !bInStart; // don't decide on the first valid input until out of start zone!
|
||||
int iCombination = -1;
|
||||
|
||||
bool bForward = ((buttons & IN_FORWARD) > 0 && vel[0] >= 200.0);
|
||||
bool bMoveLeft = ((buttons & IN_MOVELEFT) > 0 && vel[1] <= -200.0);
|
||||
bool bBack = ((buttons & IN_BACK) > 0 && vel[0] <= -200.0);
|
||||
bool bMoveRight = ((buttons & IN_MOVERIGHT) > 0 && vel[1] >= 200.0);
|
||||
bool bForward = ((buttons & IN_FORWARD) > 0 && vel[0] >= 100.0);
|
||||
bool bMoveLeft = ((buttons & IN_MOVELEFT) > 0 && vel[1] <= -100.0);
|
||||
bool bBack = ((buttons & IN_BACK) > 0 && vel[0] <= -100.0);
|
||||
bool bMoveRight = ((buttons & IN_MOVERIGHT) > 0 && vel[1] >= 100.0);
|
||||
|
||||
if(bSHSW)
|
||||
{
|
||||
@ -2055,7 +2063,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
|
||||
}
|
||||
|
||||
if(gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Autobhop && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
|
||||
if(gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
|
||||
{
|
||||
int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
|
||||
SetEntProp(client, Prop_Data, "m_nOldButtons", iOldButtons & ~IN_JUMP);
|
||||
@ -2133,7 +2141,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
{
|
||||
gI_TotalMeasures[client]++;
|
||||
|
||||
if((fAngle > 0.0 && vel[1] < 0.0) || (fAngle < 0.0 && vel[1] > 0.0))
|
||||
if((fAngle > 0.0 && vel[1] <= -100.0) || (fAngle < 0.0 && vel[1] >= 100.0))
|
||||
{
|
||||
gI_GoodGains[client]++;
|
||||
}
|
||||
@ -2143,7 +2151,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
{
|
||||
gI_TotalMeasures[client]++;
|
||||
|
||||
if(vel[0] != 0.0)
|
||||
if(vel[0] <= -100.0 || vel[0] >= 100.0)
|
||||
{
|
||||
gI_GoodGains[client]++;
|
||||
}
|
||||
@ -2167,7 +2175,7 @@ void UpdateAutoBhop(int client)
|
||||
{
|
||||
if(sv_autobunnyhopping != null)
|
||||
{
|
||||
sv_autobunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Autobhop && gB_Auto[client])? "1":"0");
|
||||
sv_autobunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Auto[client])? "1":"0");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,7 +71,6 @@ char gS_RadioCommands[][] = {"coverme", "takepoint", "holdpos", "regroup", "foll
|
||||
|
||||
// cache
|
||||
ConVar sv_disable_immunity_alpha = null;
|
||||
ConVar sv_footsteps = null;
|
||||
ConVar hostname = null;
|
||||
ConVar hostport = null;
|
||||
|
||||
@ -204,9 +203,6 @@ public void OnPluginStart()
|
||||
|
||||
sv_disable_immunity_alpha = FindConVar("sv_disable_immunity_alpha");
|
||||
|
||||
sv_footsteps = FindConVar("sv_footsteps");
|
||||
sv_footsteps.Flags &= ~(FCVAR_NOTIFY | FCVAR_REPLICATED);
|
||||
|
||||
// spectator list
|
||||
RegConsoleCmd("sm_specs", Command_Specs, "Show a list of spectators.");
|
||||
RegConsoleCmd("sm_spectators", Command_Specs, "Show a list of spectators.");
|
||||
@ -285,7 +281,7 @@ public void OnPluginStart()
|
||||
|
||||
// cvars and stuff
|
||||
gCV_GodMode = CreateConVar("shavit_misc_godmode", "3", "Enable godmode for players?\n0 - Disabled\n1 - Only prevent fall/world damage.\n2 - Only prevent damage from other players.\n3 - Full godmode.", 0, true, 0.0, true, 3.0);
|
||||
gCV_PreSpeed = CreateConVar("shavit_misc_prespeed", "1", "Stop prespeeding in the start zone?\n0 - Disabled, fully allow prespeeding.\n1 - Limit relatively to shavit_misc_prestrafelimit.\n2 - Block bunnyhopping in startzone.\n3 - Limit to shavit_misc_prestrafelimit and block bunnyhopping.", 0, true, 0.0, true, 3.0);
|
||||
gCV_PreSpeed = CreateConVar("shavit_misc_prespeed", "1", "Stop prespeeding in the start zone?\n0 - Disabled, fully allow prespeeding.\n1 - Limit relatively to prestrafelimit.\n2 - Block bunnyhopping in startzone.\n3 - Limit to prestrafelimit and block bunnyhopping.\n4 - Limit to prestrafelimit but allow prespeeding.", 0, true, 0.0, true, 4.0);
|
||||
gCV_HideTeamChanges = CreateConVar("shavit_misc_hideteamchanges", "1", "Hide team changes in chat?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
gCV_RespawnOnTeam = CreateConVar("shavit_misc_respawnonteam", "1", "Respawn whenever a player joins a team?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
gCV_RespawnOnRestart = CreateConVar("shavit_misc_respawnonrestart", "1", "Respawn a dead player if they use the timer restart command?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
@ -416,8 +412,6 @@ public void OnClientCookiesCached(int client)
|
||||
gB_Hide[client] = view_as<bool>(StringToInt(sSetting));
|
||||
}
|
||||
|
||||
UpdateFootsteps(client);
|
||||
|
||||
GetClientCookie(client, gH_CheckpointsCookie, sSetting, 8);
|
||||
|
||||
if(strlen(sSetting) == 0)
|
||||
@ -534,7 +528,7 @@ public void OnMapStart()
|
||||
{
|
||||
for(int iTeam = 1; iTeam <= 2; iTeam++)
|
||||
{
|
||||
int iSpawnPoint = CreateEntityByName((iTeam == 1)? "info_player_terrorist":"info_player_counterterrorist");
|
||||
int iSpawnPoint = CreateEntityByName((gEV_Type == Engine_TF2)? "info_player_teamspawn":((iTeam == 1)? "info_player_terrorist":"info_player_counterterrorist"));
|
||||
|
||||
if(DispatchSpawn(iSpawnPoint))
|
||||
{
|
||||
@ -941,18 +935,23 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
if(gI_PreSpeed == 1 || gI_PreSpeed == 3)
|
||||
if(gI_PreSpeed == 1 || gI_PreSpeed >= 3)
|
||||
{
|
||||
float fLimit = gF_PrestrafeLimit;
|
||||
|
||||
float fSpeed[3];
|
||||
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
|
||||
|
||||
float fLimit = view_as<float>(gA_StyleSettings[gBS_Style[client]][fRunspeed]) + gF_PrestrafeLimit;
|
||||
|
||||
// if trying to jump, add a very low limit to stop prespeeding in an elegant way
|
||||
// otherwise, make sure nothing weird is happening (such as sliding at ridiculous speeds, at zone enter)
|
||||
if(fSpeed[2] > 0.0)
|
||||
if(gI_PreSpeed < 4)
|
||||
{
|
||||
fLimit /= 3.0;
|
||||
fLimit = view_as<float>(gA_StyleSettings[gBS_Style[client]][fRunspeed]) + gF_PrestrafeLimit;
|
||||
|
||||
// if trying to jump, add a very low limit to stop prespeeding in an elegant way
|
||||
// otherwise, make sure nothing weird is happening (such as sliding at ridiculous speeds, at zone enter)
|
||||
if(fSpeed[2] > 0.0)
|
||||
{
|
||||
fLimit /= 3.0;
|
||||
}
|
||||
}
|
||||
|
||||
float fSpeedXY = (SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)));
|
||||
@ -993,8 +992,6 @@ public void OnClientPutInServer(int client)
|
||||
gBS_Style[client] = Shavit_GetBhopStyle(client);
|
||||
gB_Hide[client] = false;
|
||||
gI_CheckpointsSettings[client] = CP_DEFAULT;
|
||||
|
||||
UpdateFootsteps(client);
|
||||
}
|
||||
|
||||
if(gH_GetPlayerMaxSpeed != null)
|
||||
@ -1154,7 +1151,6 @@ public Action Command_Hide(int client, int args)
|
||||
}
|
||||
|
||||
gB_Hide[client] = !gB_Hide[client];
|
||||
UpdateFootsteps(client);
|
||||
|
||||
char[] sCookie = new char[4];
|
||||
IntToString(view_as<int>(gB_Hide[client]), sCookie, 4);
|
||||
@ -1456,12 +1452,9 @@ public Action Command_Save(int client, int args)
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
else if((bSaved = SaveCheckpoint(client, (CP_MAX - 1))))
|
||||
{
|
||||
if((bSaved = SaveCheckpoint(client, (CP_MAX - 1))))
|
||||
{
|
||||
gI_CheckpointsCache[client][iCurrentCheckpoint] = CP_MAX;
|
||||
}
|
||||
gI_CheckpointsCache[client][iCurrentCheckpoint] = CP_MAX;
|
||||
}
|
||||
|
||||
if(bSaved)
|
||||
@ -1671,14 +1664,14 @@ bool SaveCheckpoint(int client, int index)
|
||||
int iObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode");
|
||||
int iObserverTarget = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget");
|
||||
|
||||
if(IsClientObserver(client) && !IsFakeClient(iObserverTarget) && IsValidClient(iObserverTarget) && iObserverMode >= 3 && iObserverMode <= 5)
|
||||
if(IsClientObserver(client) && IsValidClient(iObserverTarget) && iObserverMode >= 3 && iObserverMode <= 5)
|
||||
{
|
||||
target = iObserverTarget;
|
||||
}
|
||||
|
||||
else if(!IsPlayerAlive(client))
|
||||
{
|
||||
Shavit_PrintToChat(client, "%T", "CommandAliveSpectate", client, gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
|
||||
Shavit_PrintToChat(client, "%T", "CommandAliveSpectate", client, gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1705,9 +1698,32 @@ bool SaveCheckpoint(int client, int index)
|
||||
cpcache[iCPFlags] = GetEntityFlags(target);
|
||||
|
||||
any snapshot[TIMERSNAPSHOT_SIZE];
|
||||
Shavit_SaveSnapshot(target, snapshot);
|
||||
CopyArray(snapshot, cpcache[aCPSnapshot], TIMERSNAPSHOT_SIZE);
|
||||
|
||||
if(IsFakeClient(target))
|
||||
{
|
||||
// unfortunately replay bots don't have a snapshot, so we can generate a fake one
|
||||
int style = Shavit_GetReplayBotStyle(target);
|
||||
int track = Shavit_GetReplayBotTrack(target);
|
||||
|
||||
snapshot[bTimerEnabled] = true;
|
||||
snapshot[fCurrentTime] = Shavit_GetReplayTime(style, track);
|
||||
snapshot[bClientPaused] = false;
|
||||
snapshot[bsStyle] = style;
|
||||
snapshot[iJumps] = 0;
|
||||
snapshot[iStrafes] = 0;
|
||||
snapshot[iTotalMeasures] = 0;
|
||||
snapshot[iGoodGains] = 0;
|
||||
snapshot[fServerTime] = GetEngineTime();
|
||||
snapshot[iSHSWCombination] = -1;
|
||||
snapshot[iTimerTrack] = track;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Shavit_SaveSnapshot(target, snapshot);
|
||||
}
|
||||
|
||||
CopyArray(snapshot, cpcache[aCPSnapshot], TIMERSNAPSHOT_SIZE);
|
||||
SetCheckpoint(client, index, cpcache);
|
||||
|
||||
return true;
|
||||
@ -1954,7 +1970,7 @@ public Action Shavit_OnStart(int client)
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
if(gB_ResetTargetname)
|
||||
if(gB_ResetTargetname || Shavit_IsPracticeMode(client)) // practice mode can be abused to break map triggers
|
||||
{
|
||||
DispatchKeyValue(client, "targetname", "");
|
||||
}
|
||||
@ -2506,16 +2522,6 @@ bool SetCheckpoint(int client, int index, CheckpointsCache cpcache[PCPCACHE_SIZE
|
||||
return gSM_Checkpoints.SetArray(sKey, cpcache[0], view_as<int>(PCPCACHE_SIZE));
|
||||
}
|
||||
|
||||
void UpdateFootsteps(int client)
|
||||
{
|
||||
if(sv_footsteps != null)
|
||||
{
|
||||
char[] sFootsteps = new char[4];
|
||||
IntToString((gB_Hide[client])? 0:sv_footsteps.IntValue, sFootsteps, 4);
|
||||
sv_footsteps.ReplicateToClient(client, sFootsteps);
|
||||
}
|
||||
}
|
||||
|
||||
void CopyArray(const any[] from, any[] to, int size)
|
||||
{
|
||||
for(int i = 0; i < size; i++)
|
||||
|
||||
@ -135,7 +135,7 @@ public void OnPluginStart()
|
||||
RegConsoleCmd("sm_top", Command_Top, "Show the top 100 players."); // The rewrite of rankings will not have the ability to show over 100 entries. Dynamic fetching can be exploited and overload the database.
|
||||
|
||||
RegAdminCmd("sm_settier", Command_SetTier, ADMFLAG_RCON, "Change the map's tier. Usage: sm_settier <tier>");
|
||||
RegAdminCmd("sm_setmaptier", Command_SetTier, ADMFLAG_RCON, "Prints the map's tier to chat. Usage: sm_setmaptier <tier> (sm_settier alias)");
|
||||
RegAdminCmd("sm_setmaptier", Command_SetTier, ADMFLAG_RCON, "Change the map's tier. Usage: sm_setmaptier <tier> (sm_settier alias)");
|
||||
|
||||
RegAdminCmd("sm_recalcmap", Command_RecalcMap, ADMFLAG_RCON, "Recalculate the current map's records' points.");
|
||||
|
||||
|
||||
@ -86,7 +86,7 @@ int gI_DefaultTeamSlots = 0;
|
||||
|
||||
// server specific
|
||||
float gF_Tickrate = 0.0;
|
||||
char gS_Map[192];
|
||||
char gS_Map[160];
|
||||
int gI_ExpectedBots = 0;
|
||||
ConVar bot_quota = null;
|
||||
any gA_CentralCache[CENTRALBOTCACHE_SIZE];
|
||||
@ -527,28 +527,6 @@ public Action Cron(Handle Timer)
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void OnEntityCreated(int entity, const char[] classname)
|
||||
{
|
||||
// trigger_once | trigger_multiple.. etc
|
||||
// func_door | func_door_rotating
|
||||
if(StrContains(classname, "trigger_") != -1 || StrContains(classname, "_door") != -1)
|
||||
{
|
||||
SDKHook(entity, SDKHook_StartTouch, HookTriggers);
|
||||
SDKHook(entity, SDKHook_EndTouch, HookTriggers);
|
||||
SDKHook(entity, SDKHook_Touch, HookTriggers);
|
||||
}
|
||||
}
|
||||
|
||||
public Action HookTriggers(int entity, int other)
|
||||
{
|
||||
if(other >= 1 && other <= MaxClients && IsFakeClient(other))
|
||||
{
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
bool LoadStyling()
|
||||
{
|
||||
char[] sPath = new char[PLATFORM_MAX_PATH];
|
||||
@ -606,8 +584,8 @@ public void OnMapStart()
|
||||
|
||||
gB_ForciblyStopped = false;
|
||||
|
||||
GetCurrentMap(gS_Map, 192);
|
||||
GetMapDisplayName(gS_Map, gS_Map, 192);
|
||||
GetCurrentMap(gS_Map, 160);
|
||||
GetMapDisplayName(gS_Map, gS_Map, 160);
|
||||
|
||||
if(!gB_Enabled)
|
||||
{
|
||||
@ -1117,7 +1095,7 @@ void UpdateReplayInfo(int client, int style, float time, int track)
|
||||
return;
|
||||
}
|
||||
|
||||
SetEntProp(client, Prop_Data, "m_CollisionGroup", 1);
|
||||
SetEntProp(client, Prop_Data, "m_CollisionGroup", 2);
|
||||
SetEntityMoveType(client, MOVETYPE_NOCLIP);
|
||||
|
||||
bool central = (gA_CentralCache[iCentralClient] == client);
|
||||
@ -1494,9 +1472,9 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
|
||||
MoveType movetype = gA_Frames[style][track].Get(gI_ReplayTick[style], 7);
|
||||
|
||||
if(movetype == MOVETYPE_WALK || movetype == MOVETYPE_LADDER)
|
||||
if(movetype == MOVETYPE_LADDER)
|
||||
{
|
||||
mt = movetype;
|
||||
mt = MOVETYPE_LADDER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1556,7 +1534,13 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
|
||||
|
||||
public Action Timer_EndReplay(Handle Timer, any data)
|
||||
{
|
||||
gB_ForciblyStopped = false;
|
||||
if(gB_CentralBot && gB_ForciblyStopped)
|
||||
{
|
||||
gB_ForciblyStopped = false;
|
||||
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
gI_ReplayTick[data] = 0;
|
||||
|
||||
if(gI_ReplayBotClient[data] != gA_CentralCache[iCentralClient])
|
||||
@ -1684,9 +1668,17 @@ public Action Hook_SayText2(UserMsg msg_id, any msg, const int[] players, int pl
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
// caching usermessage type rather than call it every time
|
||||
static UserMessageType um = view_as<UserMessageType>(-1);
|
||||
|
||||
if(um == view_as<UserMessageType>(-1))
|
||||
{
|
||||
um = GetUserMessageType();
|
||||
}
|
||||
|
||||
char[] sMessage = new char[24];
|
||||
|
||||
if(GetUserMessageType() == UM_Protobuf)
|
||||
if(um == UM_Protobuf)
|
||||
{
|
||||
Protobuf pbmsg = msg;
|
||||
pbmsg.ReadString("msg_name", sMessage, 24);
|
||||
|
||||
@ -774,8 +774,14 @@ public int MenuHandler_DeleteAll(Menu menu, MenuAction action, int param1, int p
|
||||
return 0;
|
||||
}
|
||||
|
||||
char[] sTrack = new char[32];
|
||||
GetTrackName(LANG_SERVER, gI_LastTrack[param1], sTrack, 32);
|
||||
|
||||
Shavit_LogMessage("%L - deleted all %s track records from map `%s`.", param1, sTrack, gS_Map);
|
||||
|
||||
char[] sQuery = new char[256];
|
||||
FormatEx(sQuery, 256, "DELETE FROM %splayertimes WHERE map = '%s' AND track = %d;", gS_MySQLPrefix, gS_Map, gI_LastTrack[param1]);
|
||||
|
||||
gH_SQL.Query(DeleteAll_Callback, sQuery, GetClientSerial(param1), DBPrio_High);
|
||||
}
|
||||
|
||||
@ -903,6 +909,8 @@ public int MenuHandler_DeleteStyleRecords_Confirm(Menu menu, MenuAction action,
|
||||
return 0;
|
||||
}
|
||||
|
||||
Shavit_LogMessage("%L - deleted all %s style records from map `%s`.", param1, gS_StyleStrings[style][sStyleName], gS_Map);
|
||||
|
||||
char[] sQuery = new char[256];
|
||||
FormatEx(sQuery, 256, "DELETE FROM %splayertimes WHERE map = '%s' AND style = %d;", gS_MySQLPrefix, gS_Map, style);
|
||||
|
||||
@ -1135,8 +1143,12 @@ public int DeleteConfirm_Handler(Menu menu, MenuAction action, int param1, int p
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: display record details here (map name, player name/authid, time, rank on map) without dropping threaded queries
|
||||
Shavit_LogMessage("%L - deleted record id %d.", param1, iRecordID);
|
||||
|
||||
char[] sQuery = new char[256];
|
||||
FormatEx(sQuery, 256, "DELETE FROM %splayertimes WHERE id = %d;", gS_MySQLPrefix, iRecordID);
|
||||
|
||||
gH_SQL.Query(DeleteConfirm_Callback, sQuery, GetClientSerial(param1), DBPrio_High);
|
||||
}
|
||||
|
||||
|
||||
@ -956,6 +956,8 @@ public Action Command_DelSpawn(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
Shavit_LogMessage("%L - deleted custom spawn from map `%s`.", client, gS_Map);
|
||||
|
||||
char[] sQuery = new char[256];
|
||||
FormatEx(sQuery, 256, "DELETE FROM %smapzones WHERE type = '%d' AND map = '%s';", gS_MySQLPrefix, Zone_CustomSpawn, gS_Map);
|
||||
|
||||
@ -1227,6 +1229,8 @@ public int DeleteZone_MenuHandler(Menu menu, MenuAction action, int param1, int
|
||||
|
||||
default:
|
||||
{
|
||||
Shavit_LogMessage("%L - deleted %s (id %d) from map `%s`.", param1, gS_ZoneNames[gA_ZoneCache[id][iZoneType]], gA_ZoneCache[id][iDatabaseID], gS_Map);
|
||||
|
||||
char[] sQuery = new char[256];
|
||||
FormatEx(sQuery, 256, "DELETE FROM %smapzones WHERE %s = %d;", gS_MySQLPrefix, (gB_MySQL)? "id":"rowid", gA_ZoneCache[id][iDatabaseID]);
|
||||
|
||||
@ -1321,6 +1325,8 @@ public int DeleteAllZones_MenuHandler(Menu menu, MenuAction action, int param1,
|
||||
return;
|
||||
}
|
||||
|
||||
Shavit_LogMessage("%L - deleted all zones from map `%s`.", param1, gS_Map);
|
||||
|
||||
char[] sQuery = new char[256];
|
||||
FormatEx(sQuery, 256, "DELETE FROM %smapzones WHERE map = '%s';", gS_MySQLPrefix, gS_Map);
|
||||
|
||||
@ -1901,11 +1907,15 @@ void InsertZone(int client)
|
||||
|
||||
if(type == Zone_CustomSpawn)
|
||||
{
|
||||
Shavit_LogMessage("%L - added custom spawn {%.2f, %.2f, %.2f} to map `%s`.", client, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gS_Map);
|
||||
|
||||
FormatEx(sQuery, 512, "INSERT INTO %smapzones (map, type, destination_x, destination_y, destination_z) VALUES ('%s', '%d', '%.03f', '%.03f', '%.03f');", gS_MySQLPrefix, gS_Map, type, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2]);
|
||||
}
|
||||
|
||||
else if(insert) // insert
|
||||
{
|
||||
Shavit_LogMessage("%L - added %s to map `%s`.", client, gS_ZoneNames[type], gS_Map);
|
||||
|
||||
if(type != Zone_Teleport)
|
||||
{
|
||||
FormatEx(sQuery, 512, "INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, track) VALUES ('%s', '%d', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%d');", gS_MySQLPrefix, gS_Map, type, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gI_ZoneTrack[client]);
|
||||
@ -1919,6 +1929,8 @@ void InsertZone(int client)
|
||||
|
||||
else // update
|
||||
{
|
||||
Shavit_LogMessage("%L - updated %s in map `%s`.", client, gS_ZoneNames[type], gS_Map);
|
||||
|
||||
if(gI_ZoneDatabaseID[client] == -1)
|
||||
{
|
||||
for(int i = 0; i < gI_MapZones; i++)
|
||||
@ -2409,7 +2421,7 @@ public void Shavit_OnEnd(int client, int track)
|
||||
|
||||
bool EmptyVector(float vec[3])
|
||||
{
|
||||
return (vec[0] == 0.0 && vec[1] == 0.0 && vec[2] == 0.0);
|
||||
return (IsNullVector(vec) || (vec[0] == 0.0 && vec[1] == 0.0 && vec[2] == 0.0));
|
||||
}
|
||||
|
||||
// returns -1 if there's no zone
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
}
|
||||
"CCHelp_GenericVariables"
|
||||
{
|
||||
"en" "- {name} - your in-game name.\n- {rand} - a random color.\n- {team} - your team color.\n- {green} - green color."
|
||||
"en" "- {name} - your in-game name.\n- {clan} - your clan tag.\n- {rand} - a random color.\n- {team} - your team color.\n- {green} - green color."
|
||||
}
|
||||
"CCHelp_CSS_1"
|
||||
{
|
||||
@ -46,4 +46,17 @@
|
||||
{
|
||||
"en" "- The following colors are also available: {blue}, {bluegrey}, {darkblue}, {darkred}, {gold}, {grey}, {grey2}, {lightgreen}, {lightred}, {lime}, {orchid}, {yellow} and {palered}."
|
||||
}
|
||||
// ---------- Menu ---------- //
|
||||
"SelectChatRank"
|
||||
{
|
||||
"en" "Select chat rank:\n"
|
||||
}
|
||||
"AutoAssign"
|
||||
{
|
||||
"en" "Auto-assign\nAutomatically assign using your rank."
|
||||
}
|
||||
"CustomChat"
|
||||
{
|
||||
"en" "Custom\nUse custom chat settings. See !cchelp for more information."
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user