diff --git a/HexTags/hextags.sp b/HexTags/hextags.sp new file mode 100644 index 0000000..a308acd --- /dev/null +++ b/HexTags/hextags.sp @@ -0,0 +1,1342 @@ +/* + * HexTags Plugin. + * by: Hexah + * https://github.com/Hexer10/HexTags + * + * Copyright (C) 2017-2020 Mattia (Hexah|Hexer10|Papero) + * + * This file is part of the HexTags SourceMod Plugin. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +//#define DEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#undef REQUIRE_EXTENSIONS +#undef REQUIRE_PLUGIN +#include +#include +#include +#include +#include +#include +#include +#define REQUIRE_EXTENSIONS +#define REQUIRE_PLUGIN + +#define PLUGIN_AUTHOR "Hexah" +#define PLUGIN_VERSION "2.03" + +#pragma semicolon 1 +#pragma newdecls required + + +PrivateForward pfCustomSelector; + +Handle fTagsUpdated; +Handle fMessageProcess; +Handle fMessageProcessed; +Handle fMessagePreProcess; +Handle hVibilityCookie; +Handle hSelTagCookie; + +ConVar cv_sDefaultGang; +ConVar cv_bParseRoundEnd; +ConVar cv_bEnableTagsList; + +bool bCSGO; +bool bLate; +bool bSQL_Connections; +bool bRankme; +bool bWarden; +bool bMyJBWarden; +bool bGangs; +bool bSteamWorks = true; +bool bHideTag[MAXPLAYERS+1]; +bool bHasRoundEnded; + +int iRank[MAXPLAYERS+1] = {-1, ...}; +int iNextDefTag; +int iSelTagId[MAXPLAYERS+1]; + +char sUserTag[MAXPLAYERS+1][64]; +char sTagConf[PLATFORM_MAX_PATH]; + +ArrayList userTags[MAXPLAYERS+1]; +CustomTags selectedTags[MAXPLAYERS+1]; +KeyValues tagsKv; + + +//Plugin info +public Plugin myinfo = +{ + name = "hextags", + author = PLUGIN_AUTHOR, + description = "Edit Tags & Colors!", + version = PLUGIN_VERSION, + url = "github.com/Hexer10/HexTags" +}; + +//Startup +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + //API + RegPluginLibrary("hextags"); + + CreateNative("HexTags_GetClientTag", Native_GetClientTag); + CreateNative("HexTags_SetClientTag", Native_SetClientTag); + CreateNative("HexTags_ResetClientTag", Native_ResetClientTags); + CreateNative("HexTags_AddCustomSelector", Native_AddCustomSelector); + CreateNative("HexTags_RemoveCustomSelector", Native_RemoveCustomSelector); + + fTagsUpdated = new GlobalForward("HexTags_OnTagsUpdated", ET_Ignore, Param_Cell); + + fMessageProcess = new GlobalForward("HexTags_OnMessageProcess", ET_Single, Param_Cell, Param_String, Param_String); + fMessageProcessed = new GlobalForward("HexTags_OnMessageProcessed", ET_Ignore, Param_Cell, Param_String, Param_String); + fMessagePreProcess = new GlobalForward("HexTags_OnMessagePreProcess", ET_Single, Param_Cell, Param_String, Param_String); + + pfCustomSelector = new PrivateForward(ET_Single, Param_Cell, Param_String); + + EngineVersion engine = GetEngineVersion(); + bCSGO = (engine == Engine_CSGO || engine == Engine_CSS); + + //LateLoad + bLate = late; + return APLRes_Success; +} + +//TODO: Cache client ip instead of getting it every time. +public void OnPluginStart() +{ + //ConVars + CreateConVar("sm_hextags_version", PLUGIN_VERSION, "HexTags plugin version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + cv_sDefaultGang = CreateConVar("sm_hextags_nogang", "", "Text to use if user has no tag - needs hl_gangs."); + cv_bParseRoundEnd = CreateConVar("sm_hextags_roundend", "0", "If 1 the tags will be reloaded even on round end - Suggested to be used with plugins like mostactive or rankme."); + cv_bEnableTagsList = CreateConVar("sm_hextags_enable_tagslist", "0", "Set to 1 to enable the sm_tagslist command."); + + AutoExecConfig(); + + //Reg Cmds + RegAdminCmd("sm_reloadtags", Cmd_ReloadTags, ADMFLAG_GENERIC, "Reload HexTags plugin config"); + RegAdminCmd("sm_toggletags", Cmd_ToggleTags, ADMFLAG_GENERIC, "Toggle the visibility of your tags"); + RegConsoleCmd("sm_tagslist", Cmd_TagsList, "Select your tag!"); + RegConsoleCmd("sm_getteam", Cmd_GetTeam, "Get current team name"); + + //Event hooks + if (!HookEventEx("round_end", Event_RoundEnd)) + LogError("Failed to hook \"round_end\", \"sm_hextags_roundend\" won't produce any effect."); + HookEvent("round_start", Event_RoundStart); + + hVibilityCookie = RegClientCookie("HexTags_Visibility", "Show or hide the tags.", CookieAccess_Private); + hSelTagCookie = RegClientCookie("HexTags_SelectedTag", "Selected Tag", CookieAccess_Private); + +#if defined DEBUG + RegConsoleCmd("sm_gettagvars", Cmd_GetVars); + RegConsoleCmd("sm_firesel", Cmd_FireSel); +#endif +} + +public void OnAllPluginsLoaded() +{ + Debug_Print("Called OnAllPlugins!"); + + if (FindPluginByFile("custom-chatcolors-cp.smx") || LibraryExists("ccc")) + LogMessage("[HexTags] Found Custom Chat Colors running!\n Please avoid running it with this plugin!"); + + bSQL_Connections = true; + bRankme = LibraryExists("rankme"); + bWarden = LibraryExists("warden"); + bMyJBWarden = LibraryExists("myjbwarden"); + bGangs = LibraryExists("hl_gangs"); + bSteamWorks = LibraryExists("SteamWorks"); + + LoadKv(); + if (bLate) + LogMessage("Plugins started, setup clients"); + + if (bLate) for (int i = 1; i <= MaxClients; i++)if (IsClientInGame(i)) + { + OnClientPutInServer(i); + if (!AreClientCookiesCached(i)) + OnClientCookiesCached(i); + + OnClientPostAdminCheck(i); + } + + //Timers + if (bCSGO) + CreateTimer(5.0, Timer_ForceTag, _, TIMER_REPEAT); +} + +public void OnLibraryAdded(const char[] name) +{ + Debug_Print("Called OnLibraryAdded %s", name); + if (StrEqual(name, "rankme")) + { + bRankme = true; + } + else if (StrEqual(name, "warden")) + { + bWarden = true; + } + else if (StrEqual(name, "myjbwarden")) + { + bMyJBWarden = true; + } + else if (StrEqual(name, "hl_gangs")) + { + bGangs = true; + } + else if (StrEqual(name, "SteamWorks", false)) + { + bSteamWorks = true; + } +} + +public void OnLibraryRemoved(const char[] name) +{ + Debug_Print("Called OnLibraryRemoved %s", name); + if (StrEqual(name, "rankme")) + { + bRankme = false; + LoadKv(); + } + else if (StrEqual(name, "warden")) + { + bWarden = false; + LoadKv(); + } + else if (StrEqual(name, "myjbwarden")) + { + bMyJBWarden = false; + LoadKv(); + } + else if (StrEqual(name, "hl_gangs")) + { + bGangs = false; + LoadKv(); + } + else if (StrEqual(name, "SteamWorks", false)) + { + bSteamWorks = false; + LoadKv(); + } +} + +//Thanks to https://forums.alliedmods.net/showpost.php?p=2573907&postcount=6 +public Action OnClientCommandKeyValues(int client, KeyValues kv) +{ + if (bHideTag[client]) + return Plugin_Continue; + + char sKey[64]; + + if (!bCSGO || !kv.GetSectionName(sKey, sizeof(sKey))) + return Plugin_Continue; + +#if defined DEBUG + char sKV[256]; + kv.ExportToString(sKV, sizeof(sKV)); + Debug_Print("Called ClientCmdKv: %s\n%s\n", sKey, sKV); +#endif + + if(StrEqual(sKey, "ClanTagChanged")) + { + kv.GetString("tag", sUserTag[client], sizeof(sUserTag[])); + LoadTags(client); + + if(selectedTags[client].ScoreTag[0] == '\0') + return Plugin_Continue; + + kv.SetString("tag", selectedTags[client].ScoreTag); + Debug_Print("[ClanTagChanged] Setted tag: %s ", selectedTags[client].ScoreTag); + return Plugin_Changed; + } + + return Plugin_Continue; +} + +public void OnClientDisconnect(int client) +{ + ResetTags(client); + iRank[client] = -1; + bHideTag[client] = false; + sUserTag[client][0] = '\0'; + delete userTags[client]; +} + +public void warden_OnWardenCreated(int client) +{ + RequestFrame(Frame_LoadTag, client); +} + +public void warden_OnWardenRemoved(int client) +{ + if (bCSGO) + CS_SetClientClanTag(client, sUserTag[client]); + + RequestFrame(Frame_LoadTag, client); + +} + +public void warden_OnDeputyCreated(int client) +{ + RequestFrame(Frame_LoadTag, client); +} + +public void warden_OnDeputyRemoved(int client) +{ + if (bCSGO) + CS_SetClientClanTag(client, sUserTag[client]); + + RequestFrame(Frame_LoadTag, client); +} + +//Commands +public Action Cmd_ReloadTags(int client, int args) +{ + LoadKv(); + for (int i = 1; i <= MaxClients; i++)if (IsClientInGame(i))LoadTags(i); + + ReplyToCommand(client, "[SM] Tags succesfully reloaded!"); + return Plugin_Handled; +} + +public Action Cmd_ToggleTags(int client, int args) +{ + if (bHideTag[client]) + { + bHideTag[client] = false; + LoadTags(client); + ReplyToCommand(client, "[SM] Your tags are visible again."); + } + else + { + bHideTag[client] = true; + CS_SetClientClanTag(client, sUserTag[client]); + ReplyToCommand(client, "[SM] Your tags are no longer visible."); + } + + SetClientCookie(client, hVibilityCookie, bHideTag[client] ? "0" : "1"); +} + +public Action Cmd_TagsList(int client, int args) +{ + if (!client) + { + ReplyToCommand(client, "[SM] In-game only command."); + return Plugin_Handled; + } + + if (userTags[client] == null) + { + ReplyToCommand(client, "[SM] Tags not yet loaded."); + return Plugin_Handled; + } + + if (!cv_bEnableTagsList.BoolValue) + { + ReplyToCommand(client, "[SM] This feature is not enabled."); + return Plugin_Handled; + } + + if (userTags[client].Length == 0) + { + ReplyToCommand(client, "[SM] No tags available."); + return Plugin_Handled; + } + + Menu menu = new Menu(Handler_TagsMenu); + menu.SetTitle("Choose your tag:"); + static char sIndex[16]; + int len = userTags[client].Length; + CustomTags tags; + for (int i = 0; i < len; i++) + { + userTags[client].GetArray(i, tags, sizeof(tags)); + IntToString(i, sIndex, sizeof(sIndex)); + menu.AddItem(sIndex, tags.TagName); + } + menu.Display(client, MENU_TIME_FOREVER); + return Plugin_Handled; +} + +public int Handler_TagsMenu(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_End) + { + delete menu; + } + else if (action == MenuAction_Select) + { + static char sIndex[16]; + menu.GetItem(param2, sIndex, sizeof(sIndex)); + userTags[param1].GetArray(StringToInt(sIndex), selectedTags[param1], sizeof(CustomTags)); + if (selectedTags[param1].ScoreTag[0] != '\0') + { + CS_SetClientClanTag(param1, selectedTags[param1].ScoreTag); + } + iSelTagId[param1] = selectedTags[param1].SectionId; + + static char sValue[32]; + IntToString(iSelTagId[param1], sValue, sizeof(sValue)); + SetClientCookie(param1, hSelTagCookie, sValue); + PrintToChat(param1, "[SM] Setted %s tags", selectedTags[param1].TagName); + } + +} + +public Action Cmd_GetTeam(int client, int args) +{ + if (!client) + { + ReplyToCommand(client, "[SM] In-game only command."); + return Plugin_Handled; + } + + char sTeam[32]; + GetTeamName(GetClientTeam(client), sTeam, sizeof(sTeam)); + ReplyToCommand(client, "[SM] Current team name: %s", sTeam); + return Plugin_Handled; +} + +#if defined DEBUG +public Action Cmd_GetVars(int client, int args) +{ + ReplyToCommand(client, selectedTags[client].ScoreTag); + ReplyToCommand(client, selectedTags[client].ChatTag); + ReplyToCommand(client, selectedTags[client].ChatColor); + ReplyToCommand(client, selectedTags[client].NameColor); + return Plugin_Handled; +} + +public Action Cmd_FireSel(int client, int args) +{ + int count = pfCustomSelector.FunctionCount; + int res; + + Call_StartForward(pfCustomSelector); + Call_PushCell(client); + Call_PushString("thistoggle"); + Call_Finish(res); + ReplyToCommand(client, "[SM] Fire %i functions, res: %i!", count, res); + return Plugin_Handled; +} +#endif + +//Events +public void OnClientPutInServer(int client) +{ + delete userTags[client]; + userTags[client] = new ArrayList(sizeof(CustomTags)); +} + +public void OnClientPostAdminCheck(int client) +{ + LoadTags(client); +} + +public void OnClientCookiesCached(int client) +{ + static char sValue[32]; + GetClientCookie(client, hVibilityCookie, sValue, sizeof(sValue)); + + bHideTag[client] = sValue[0] == '\0' ? false : !StringToInt(sValue); + + GetClientCookie(client, hSelTagCookie, sValue, sizeof(sValue)); + if (sValue[0] == '\0') + { + return; + } + int id = StringToInt(sValue); + if (!id) + { + LogError("Invalid id: %s", sValue); + } + iSelTagId[client] = id; + +} + +public Action RankMe_OnPlayerLoaded(int client) +{ + RankMe_GetRank(client, RankMe_LoadTags); +} + +public Action RankMe_OnPlayerSaved(int client) +{ + RankMe_GetRank(client, RankMe_LoadTags); +} + +public Action RankMe_LoadTags(int client, int rank, any data) +{ + Debug_Print("Callback load rankme-tags"); + if (IsValidClient(client, true, true)) + { + iRank[client] = rank; + Debug_Print("Callback valid rank %L - %i", client, rank); + char sRank[16]; + IntToString(iRank[client], sRank, sizeof(sRank)); + + if (selectedTags[client].ScoreTag[0] == '\0') + return; + + ReplaceString(selectedTags[client].ScoreTag, sizeof(CustomTags::ScoreTag), "{rmRank}", sRank); + CS_SetClientClanTag(client, selectedTags[client].ScoreTag); //Instantly load the score-tag + } +} + +public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + bHasRoundEnded = true; + if (!cv_bParseRoundEnd.BoolValue) + return; + + for (int i = 1; i <= MaxClients; i++)if (IsClientInGame(i))OnClientPostAdminCheck(i); +} + +public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + bHasRoundEnded = false; +} + +public Action CP_OnChatMessage(int& author, ArrayList recipients, char[] flagstring, char[] name, char[] message, bool& processcolors, bool& removecolors) +{ + //Debug_Setup(true, false, false, true); // Disable chat. + if (bHideTag[author]) + { + return Plugin_Continue; + } + + Action result = Plugin_Continue; + //Call the forward + Call_StartForward(fMessagePreProcess); + Call_PushCell(author); + Call_PushStringEx(name, MAXLENGTH_NAME, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushStringEx(message, MAXLENGTH_MESSAGE, SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_Finish(result); + + if (result >= Plugin_Handled) + { + return Plugin_Continue; + } + + //Add colors & tags + char sNewName[MAXLENGTH_NAME]; + char sNewMessage[MAXLENGTH_MESSAGE]; + // Rainbow name + if (StrEqual(selectedTags[author].NameColor, "{rainbow}")) + { + Debug_Print("Rainbow name"); + char sTemp[MAXLENGTH_MESSAGE]; + + int color; + int len = strlen(name); + for(int i = 0; i < len; i++) + { + if (IsCharSpace(name[i])) + { + Format(sTemp, sizeof(sTemp), "%s%c", sTemp, name[i]); + continue; + } + + int bytes = GetCharBytes(name[i])+1; + char[] c = new char[bytes]; + strcopy(c, bytes, name[i]); + Format(sTemp, sizeof(sTemp), "%s%c%s", sTemp, GetColor(++color), c); + if (IsCharMB(name[i])) + i += bytes-2; + } + Format(sNewName, MAXLENGTH_NAME, "%s%s{default}", selectedTags[author].ChatTag, sTemp); + } + else if (StrEqual(selectedTags[author].NameColor, "{random}")) //Random name + { + Debug_Print("Random name"); + char sTemp[MAXLENGTH_MESSAGE]; + + int len = strlen(name); + for(int i = 0; i < len; i++) + { + if (IsCharSpace(name[i])) + { + Format(sTemp, sizeof(sTemp), "%s%c", sTemp, name[i]); + continue; + } + + int bytes = GetCharBytes(name[i])+1; + char[] c = new char[bytes]; + strcopy(c, bytes, name[i]); + Format(sTemp, sizeof(sTemp), "%s%c%s", sTemp, GetRandomColor(), c); + if (IsCharMB(name[i])) + i += bytes-2; + } + Format(sNewName, MAXLENGTH_NAME, "%s%s{default}", selectedTags[author].ChatTag, sTemp); + } + else + { + Debug_Print("Default name"); + //LogMessage(sNewName); + Format(sNewName, MAXLENGTH_NAME, "%s%s%s{default}", selectedTags[author].ChatTag, selectedTags[author].NameColor, name); + } + Format(sNewMessage, MAXLENGTH_MESSAGE, "%s%s", selectedTags[author].ChatColor, message); + + //Update the params + static char sTime[16]; + FormatTime(sTime, sizeof(sTime), "%H:%M"); + ReplaceString(sNewName, sizeof(sNewName), "{time}", sTime); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{time}", sTime); + + + static char sIP[32]; + static char sCountry[3]; + GetClientIP(author, sIP, sizeof(sIP)); + GeoipCode2(sIP, sCountry); + ReplaceString(sNewName, sizeof(sNewName), "{country}", sCountry); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{country}", sCountry); + + if (bGangs) + { + Debug_Print("Apply gans"); + static char sGang[32]; + Gangs_HasGang(author) ? Gangs_GetGangName(author, sGang, sizeof(sGang)) : cv_sDefaultGang.GetString(sGang, sizeof(sGang)); + + ReplaceString(sNewName, sizeof(sNewName), "{gang}", sGang); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{gang}", sGang); + } + + if (bRankme) + { + Debug_Print("Apply rankme"); + static char sPoints[16]; + IntToString(RankMe_GetPoints(author), sPoints, sizeof(sPoints)); + ReplaceString(sNewName, sizeof(sNewName), "{rmPoints}", sPoints); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{rmPoints}", sPoints); + + static char sRank[16]; + IntToString(iRank[author], sRank, sizeof(sRank)); + ReplaceString(sNewName, sizeof(sNewName), "{rmRank}", sRank); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{rmRank}", sRank); + } + + //Rainbow Chat + if (StrEqual(selectedTags[author].ChatColor, "{rainbow}", false)) + { + Debug_Print("Rainbow chat"); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{rainbow}", ""); + char sTemp[MAXLENGTH_MESSAGE]; + + int color; + int len = strlen(sNewMessage); + for(int i = 0; i < len; i++) + { + if (IsCharSpace(sNewMessage[i])) + { + Format(sTemp, sizeof(sTemp), "%s%c", sTemp, sNewMessage[i]); + continue; + } + + int bytes = GetCharBytes(sNewMessage[i])+1; + char[] c = new char[bytes]; + strcopy(c, bytes, sNewMessage[i]); + Format(sTemp, sizeof(sTemp), "%s%c%s", sTemp, GetColor(++color), c); + if (IsCharMB(sNewMessage[i])) + i += bytes-2; + } + Format(sNewMessage, MAXLENGTH_MESSAGE, "%s", sTemp); + } + + //Random Chat + if (StrEqual(selectedTags[author].ChatColor, "{random}", false)) + { + Debug_Print("Random chat"); + ReplaceString(sNewMessage, sizeof(sNewMessage), "{random}", ""); + char sTemp[MAXLENGTH_MESSAGE]; + + int len = strlen(sNewMessage); + for(int i = 0; i < len; i++) + { + if (IsCharSpace(sNewMessage[i])) + { + Format(sTemp, sizeof(sTemp), "%s%c", sTemp, sNewMessage[i]); + continue; + } + + int bytes = GetCharBytes(sNewMessage[i])+1; + char[] c = new char[bytes]; + strcopy(c, bytes, sNewMessage[i]); + Format(sTemp, sizeof(sTemp), "%s%c%s", sTemp, GetRandomColor(), c); + if (IsCharMB(sNewMessage[i])) + i += bytes-2; + } + Format(sNewMessage, MAXLENGTH_MESSAGE, "%s", sTemp); + } + + static char sPassedName[MAXLENGTH_NAME]; + static char sPassedMessage[MAXLENGTH_NAME]; + + //LogMessage("%s%s", sNewName, sNewMessage); + + sPassedName = sNewName; + sPassedMessage = sNewMessage; + + + result = Plugin_Continue; + //Call the forward + Call_StartForward(fMessageProcess); + Call_PushCell(author); + Call_PushStringEx(sPassedName, sizeof(sPassedName), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushStringEx(sPassedMessage, sizeof(sPassedMessage), SM_PARAM_STRING_UTF8|SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_Finish(result); + + if (result == Plugin_Continue) + { + //Update the name & message + strcopy(name, MAXLENGTH_NAME, sNewName); + strcopy(message, MAXLENGTH_MESSAGE, sNewMessage); + } + else if (result == Plugin_Changed) + { + //Update the name & message + strcopy(name, MAXLENGTH_NAME, sPassedName); + strcopy(message, MAXLENGTH_MESSAGE, sPassedMessage); + } + else + { + //Debug_Setup(); + return Plugin_Continue; + } + + processcolors = true; + removecolors = false; + + //Call the (post)forward + Call_StartForward(fMessageProcessed); + Call_PushCell(author); + Call_PushString(sPassedName); + Call_PushString(sPassedMessage); + Call_Finish(); + + + Debug_Print("Message sent"); + //Debug_Setup(); + return Plugin_Changed; +} + +//Functions +void LoadKv() +{ + static char sConfig[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sConfig, sizeof(sConfig), "configs/hextags.cfg"); //Get cfg file + + if (OpenFile(sConfig, "rt") == null) + SetFailState("Couldn't find: \"%s\"", sConfig); //Check if cfg exist + + KeyValues kv = new KeyValues("HexTags"); //Create the kv + + if (!kv.ImportFromFile(sConfig)) + SetFailState("Couldn't import: \"%s\"", sConfig); //Check if file was imported properly + + if (!kv.GotoFirstSubKey()) + LogMessage("No entries found in: \"%s\"", sConfig); //Notify that there aren't any entries + + delete kv; + strcopy(sTagConf, sizeof(sTagConf), sConfig); + delete tagsKv; +} + +void LoadTags(int client) +{ + if (bHideTag[client]) + return; + + if (!IsValidClient(client, true, true)) + return; + + //Clear the tags when re-checking + ResetTags(client); + + if (tagsKv == null) + { + tagsKv = new KeyValues("HexTags"); + tagsKv.ImportFromFile(sTagConf); + Debug_Print("KeyValue handle: %i", tagsKv); + } + tagsKv.Rewind(); + if (userTags[client] == null) + { + userTags[client] = new ArrayList(sizeof(CustomTags)); + } + ParseConfig(tagsKv, client); + + if (userTags[client].Length > 0) + { + if (iSelTagId[client] == 0 || !cv_bEnableTagsList.BoolValue) + { + //LogMessage("iSelTagId[client] == 0 || !cv_bEnableTagsList.BoolValue"); + userTags[client].GetArray(0, selectedTags[client], sizeof(CustomTags)); + return; + } + tagsKv.Rewind(); + if (!tagsKv.JumpToKeySymbol(iSelTagId[client])) + { + //LogMessage("!tagsKv.JumpToKeySymbol(iSelTagId[client])"); + SetClientCookie(client, hSelTagCookie, ""); + userTags[client].GetArray(0, selectedTags[client], sizeof(CustomTags)); + return; + } + // Key found + int len = userTags[client].Length; + CustomTags tags; + + for (int i = 0; i < len; i++) + { + userTags[client].GetArray(i, tags, sizeof(CustomTags)); + if (tags.SectionId == iSelTagId[client]) + { + selectedTags[client] = tags; + if (selectedTags[client].ScoreTag[0] == '\0') + return; + + CS_SetClientClanTag(client, selectedTags[client].ScoreTag); + return; + } + //LogMessage("Default"); + } + } +} + +void ParseConfig(KeyValues kv, int client) +{ + userTags[client].Clear(); + static char sSectionName[64]; + do + { + if (kv.GotoFirstSubKey()) + { + kv.GetSectionName(sSectionName, sizeof(sSectionName)); + Debug_Print("Current key: %s", sSectionName); + + if (CheckSelector(sSectionName, client)) + { + //LogMessage("Found valid tag to player %N is %s", client, sSectionName); + Debug_Print("*******FOUND VALID SELECTOR -> %s.", sSectionName); + ParseConfig(kv, client); + } + } + else + { + kv.GetSectionName(sSectionName, sizeof(sSectionName)); + if (!CheckSelector(sSectionName, client)) + { + continue; + } + // LogMessage("Found valid tag to player %N is %s", client, sSectionName); + Debug_Print("***********SETTINGS TAGS", sSectionName); + GetTags(client, kv); + } + } while (kv.GotoNextKey()); + //LogMessage("%N set %d %s %s", client, selectedTags[client].SectionId, selectedTags[client].ScoreTag, selectedTags[client].ChatTag); + Debug_Print("-- Section end --"); +} + +bool CheckSelector(const char[] selector, int client) +{ + /* CHECK DEFAULT */ + if (StrEqual(selector, "default", false)) + { + return true; + } + + /* CHECK STEAMID */ + if(strlen(selector) > 11 && StrContains(selector, "STEAM_", true) == 0) + { + char steamid[32]; + if (!GetClientAuthId(client, AuthId_Steam2, steamid, sizeof(steamid))) + return false; + + if (StrEqual(steamid, selector)) + { + return true; + } + + //Replace the STEAM_1 to STEAM_0 or viceversa + (steamid[6] == '1') ? (steamid[6] = '0') : (steamid[6] = '1'); + if (StrEqual(steamid, selector)) + { + return true; + } + } + + + /* PERMISSIONS RELATED CHECKS */ + AdminId admin = GetUserAdmin(client); + if (admin != INVALID_ADMIN_ID) + { + + Debug_Print("Found as admin! %N", client); + /* CHECK ADMIN GROUP */ + if (selector[0] == '@') + { + Debug_Print("Check group: %s",selector); + static char sGroup[32]; + + GroupId group = admin.GetGroup(0, sGroup, sizeof(sGroup)); + if (group != INVALID_GROUP_ID) + { + if (StrEqual(selector[1], sGroup)) + { + return true; + } + } + } + + /* CHECK ADMIN FLAGS (1)*/ + if (strlen(selector) == 1) + { + Debug_Print("Check for flag (1char): ",selector); + AdminFlag flag; + if (FindFlagByChar(CharToLower(selector[0]), flag)) + { + if (admin.HasFlag(flag)) + { + return true; + } + } + } + + /* CHECK ADMIN FLAGS (2)*/ + if (selector[0] == '&') + { + Debug_Print("Check group: %s",selector); + for (int i = 1; i < strlen(selector); i++) + { + AdminFlag flag; + if (FindFlagByChar(selector[i], flag)) + { + if (admin.HasFlag(flag)) + { + return true; + } + } + } + } + Debug_Print("Unmatched admin: %s", selector); + } + + /* CHECK PLAYER TEAM */ + int team = GetClientTeam(client); + static char sTeam[32]; + + GetTeamName(team, sTeam, sizeof(sTeam)); + if (StrEqual(sTeam, selector)) + { + return true; + } + + /* CHECK TIME */ + if (bSQL_Connections && selector[0] == '#') + { + int iPlayTime = UserControl_PlayedTime(client); + if (iPlayTime >= StringToInt(selector[1])) + { + //LogMessage("Player %N played %d >= %s on this map", client, iPlayTime, selector[1]); + return true; + } + } + + /* CHECK WARDEN */ + /*if (bWarden && StrEqual(selector, "warden", false) && warden_iswarden(client)) + { + return true; + }*/ + + /* CHECK DEPUTY */ + /*if (bMyJBWarden && StrEqual(selector, "deputy", false) && warden_deputy_isdeputy(client)) + { + return true; + }*/ + + /* CHECK PRIME */ + /*if (bSteamWorks && StrEqual(selector, "NoPrime", false)) + { + if (k_EUserHasLicenseResultDoesNotHaveLicense == SteamWorks_HasLicenseForApp(client, 624820)) + { + return true; + } + }*/ + + /* CHECK GANG */ + /*if (bGangs && StrEqual(selector, "Gang", false) && Gangs_HasGang(client)) + { + return true; + }*/ + + /* CHECK RANKME */ + /*if (bRankme && selector[0] == '!') + { + int iPoints = RankMe_GetPoints(client); + if (iPoints >= StringToInt(selector[1])) + { + return true; + } + }*/ + + /* CHECK STEAM GROUP */ + /*if (bSteamWorks && selector[0] == '$') + { + if (SteamWorks_GetUserGroupStatus(client, selector[1])) + { + return true; + } + }*/ + + + bool res = false; + + Call_StartForward(pfCustomSelector); + Call_PushCell(client); + Call_PushString(selector); + Call_Finish(res); + + return res; +} + +//Timers +public Action Timer_ForceTag(Handle timer) +{ + if (!bCSGO) + return; + + for (int i = 1; i <= MaxClients; i++)if (IsClientInGame(i) && selectedTags[i].ForceTag && selectedTags[i].ScoreTag[0] != '\0' && !bHideTag[i]) + { + char sTag[32]; + CS_GetClientClanTag(i, sTag, sizeof(sTag)); + if (StrEqual(sTag, selectedTags[i].ScoreTag)) + continue; + + if (!bHasRoundEnded){ + LogMessage("%L was changed by an external plugin, forcing him back to the HexTags' default one!", i, sTag); + } + + CS_SetClientClanTag(i, selectedTags[i].ScoreTag); + } +} + +//Frames +public void Frame_LoadTag(any client) +{ + LoadTags(client); +} + +//Stocks +void GetTags(int client, KeyValues kv) +{ + static char sSection[64]; + static char sDef[8]; + IntToString(iNextDefTag++, sDef, sizeof(sDef)); + + kv.GetSectionName(sSection, sizeof(sSection)); + Debug_Print("Section: %s", sSection); + int id; + if (!kv.GetSectionSymbol(id)) + { + LogError("Unable to get section symbol."); + } + + CustomTags tags; + + tags.SectionId = id; + kv.GetString("TagName", tags.TagName, sizeof(CustomTags::TagName), sDef); + kv.GetString("ScoreTag", tags.ScoreTag, sizeof(CustomTags::ScoreTag), ""); + kv.GetString("ChatTag", tags.ChatTag, sizeof(CustomTags::ChatTag), ""); + kv.GetString("ChatColor", tags.ChatColor, sizeof(CustomTags::ChatColor), ""); + kv.GetString("NameColor", tags.NameColor, sizeof(CustomTags::NameColor), "{teamcolor}"); + tags.ForceTag = kv.GetNum("ForceTag", 1) == 1; + + + Call_StartForward(fTagsUpdated); + Call_PushCell(client); + Call_Finish(); + + if (tags.ScoreTag[0] != '\0' && bCSGO) + { + //Update params + if (StrContains(tags.ScoreTag, "{country}") != -1) + { + static char sIP[32]; + static char sCountry[3]; + if (!GetClientIP(client, sIP, sizeof(sIP))) + LogError("Unable to get %L ip!", client); + GeoipCode2(sIP, sCountry); + ReplaceString(tags.ScoreTag, sizeof(CustomTags::ScoreTag), "{country}", sCountry); + } + if (bGangs && StrContains(tags.ScoreTag, "{gang}") != -1) + { + static char sGang[32]; + Gangs_HasGang(client) ? Gangs_GetGangName(client, sGang, sizeof(sGang)) : cv_sDefaultGang.GetString(sGang, sizeof(sGang)); + ReplaceString(tags.ScoreTag, sizeof(CustomTags::ScoreTag), "{gang}", sGang); + } + if (bRankme && StrContains(tags.ScoreTag, "{rmPoints}") != -1) + { + static char sPoints[16]; + IntToString(RankMe_GetPoints(client), sPoints, sizeof(sPoints)); + ReplaceString(tags.ScoreTag, sizeof(CustomTags::ScoreTag), "{rmPoints}", sPoints); + } + if (bRankme && StrContains(tags.ScoreTag, "{rmRank}") != -1) + { + Debug_Print("Contains rmRank"); + RankMe_GetRank(client, RankMe_LoadTags); + } + + Debug_Print("Setted tag: %s", tags.ScoreTag); + CS_SetClientClanTag(client, tags.ScoreTag); //Instantly load the score-tag + } + if (StrContains(tags.ChatTag, "{rainbow}") == 0) + { + Debug_Print("Found {rainbow} in ChatTag"); + ReplaceString(tags.ChatTag, sizeof(CustomTags::ChatTag), "{rainbow}", ""); + char sTemp[MAXLENGTH_MESSAGE]; + + int color; + int len = strlen(tags.ChatTag); + for(int i = 0; i < len; i++) + { + if (IsCharSpace(tags.ChatTag[i])) + { + Format(sTemp, sizeof(sTemp), "%s%c", sTemp, tags.ChatTag[i]); + continue; + } + + int bytes = GetCharBytes(tags.ChatTag[i])+1; + char[] c = new char[bytes]; + strcopy(c, bytes, tags.ChatTag[i]); + Format(sTemp, sizeof(sTemp), "%s%c%s", sTemp, GetColor(++color), c); + if (IsCharMB(tags.ChatTag[i])) + i += bytes-2; + } + strcopy(tags.ChatTag, sizeof(CustomTags::ChatTag), sTemp); + Debug_Print("Replaced ChatTag with %s", tags.ChatTag); + } + if (StrContains(tags.ChatTag, "{random}") == 0) + { + ReplaceString(tags.ChatTag, sizeof(CustomTags::ChatTag), "{random}", ""); + char sTemp[MAXLENGTH_MESSAGE]; + int len = strlen(tags.ChatTag); + for(int i = 0; i < len; i++) + { + if (IsCharSpace(tags.ChatTag[i])) + { + Format(sTemp, sizeof(sTemp), "%s%c", sTemp, tags.ChatTag[i]); + continue; + } + + int bytes = GetCharBytes(tags.ChatTag[i])+1; + char[] c = new char[bytes]; + strcopy(c, bytes, tags.ChatTag[i]); + Format(sTemp, sizeof(sTemp), "%s%c%s", sTemp, GetRandomColor(), c); + if (IsCharMB(tags.ChatTag[i])) + i += bytes-2; + } + strcopy(tags.ChatTag, sizeof(CustomTags::ChatTag), sTemp); + } + Debug_Print("Succesfully setted tags"); + //LogMessage("%N set %s %s", client, tags.ScoreTag, tags.ChatTag); + userTags[client].PushArray(tags, sizeof(tags)); + //selectedTags[client] = tags; + //selectedTags[client].ForceTag = true; +} + +void ResetTags(int client) +{ + strcopy(selectedTags[client].ScoreTag, sizeof(CustomTags::ScoreTag), ""); + strcopy(selectedTags[client].ChatTag, sizeof(CustomTags::ChatTag), ""); + strcopy(selectedTags[client].ChatColor, sizeof(CustomTags::ChatColor), ""); + strcopy(selectedTags[client].NameColor, sizeof(CustomTags::NameColor), ""); + selectedTags[client].ForceTag = true; +} + +int GetRandomColor() +{ + switch(GetRandomInt(1, 16)) + { + case 1: return '\x01'; + case 2: return '\x02'; + case 3: return '\x03'; + case 4: return '\x03'; + case 5: return '\x04'; + case 6: return '\x05'; + case 7: return '\x06'; + case 8: return '\x07'; + case 9: return '\x08'; + case 10: return '\x09'; + case 11: return '\x10'; + case 12: return '\x0A'; + case 13: return '\x0B'; + case 14: return '\x0C'; + case 15: return '\x0E'; + case 16: return '\x0F'; + } + return '\x01'; +} + +int GetColor(int color) +{ + // TODO: Use modulo operator. + while(color > 7) + color -= 7; + + switch(color) + { + case 1: return '\x02'; + case 2: return '\x10'; + case 3: return '\x09'; + case 4: return '\x06'; + case 5: return '\x0B'; + case 6: return '\x0C'; + case 7: return '\x0E'; + } + return '\x01'; +} + +//API +public int Native_GetClientTag(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if (client < 1 || client > MaxClients) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); + } + if (!IsClientConnected(client)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected", client); + } + + eTags tag = view_as(GetNativeCell(2)); + switch (tag) + { + case (ScoreTag): + { + SetNativeString(3, selectedTags[client].ScoreTag, GetNativeCell(4)); + } + case (ChatTag): + { + SetNativeString(3, selectedTags[client].ChatTag, GetNativeCell(4)); + } + case (ChatColor): + { + SetNativeString(3, selectedTags[client].ChatColor, GetNativeCell(4)); + } + case (NameColor): + { + SetNativeString(3, selectedTags[client].NameColor, GetNativeCell(4)); + } + } + return 0; +} + +public int Native_SetClientTag(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if (client < 1 || client > MaxClients) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); + } + if (!IsClientConnected(client)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected", client); + } + + char sTag[64]; + eTags tag = view_as(GetNativeCell(2)); + + GetNativeString(3, sTag, sizeof(sTag)); + ReplaceString(sTag, sizeof(sTag), "{darkgray}", "{gray2}"); + + switch (tag) + { + case (ScoreTag): + { + strcopy(selectedTags[client].ScoreTag, sizeof(CustomTags::ScoreTag), sTag); + } + case (ChatTag): + { + strcopy(selectedTags[client].ChatTag, sizeof(CustomTags::ChatTag), sTag); + } + case (ChatColor): + { + strcopy(selectedTags[client].ChatColor, sizeof(CustomTags::ChatColor), sTag); + } + case (NameColor): + { + strcopy(selectedTags[client].NameColor, sizeof(CustomTags::NameColor), sTag); + } + } + + + Debug_Print("Called Native_SetClientTag(%i, %i, %s)", client, tag, sTag); + +// strcopy(selectedTags[client][Tag], sizeof(sTags[][]), sTag); + return 0; +} + +public int Native_ResetClientTags(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if (client < 1 || client > MaxClients) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client); + } + if (!IsClientConnected(client)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected", client); + } + + LoadTags(client); + return 0; +} + +public int Native_AddCustomSelector(Handle plugin, int numParams) +{ + return pfCustomSelector.AddFunction(plugin, GetNativeFunction(1)); +} + +public int Native_RemoveCustomSelector(Handle plugin, int numParams) +{ + return pfCustomSelector.RemoveFunction(plugin, GetNativeFunction(1)); +} + + +/* From smlib */ +stock void String_ToLower(const char[] input, char[] output, int size) +{ + size--; + + int x=0; + while (input[x] != '\0' && x < size) { + + output[x] = CharToLower(input[x]); + + x++; + } + + output[x] = '\0'; +} diff --git a/MgeMod/mgemod.sp b/MgeMod/mgemod.sp new file mode 100644 index 0000000..329ceda --- /dev/null +++ b/MgeMod/mgemod.sp @@ -0,0 +1,5601 @@ +#pragma semicolon 1 // Force strict semicolon mode. + +// ====[ INCLUDES ]==================================================== +#include +#include +#include +#include +#include + +// ====[ CONSTANTS ]=================================================== +#define PL_VERSION "development" +#define MAX_FILE_LEN 80 +#define MAXARENAS 31 +#define MAXSPAWNS 15 +#define HUDFADEOUTTIME 120.0 +//#define MAPCONFIGFILE "configs/mgemod_spawns.cfg" +#define STATSCONFIGFILE "configs/mgemod_stats.cfg" +#define SLOT_ONE 1 //arena slot 1 +#define SLOT_TWO 2 //arena slot 2 +#define SLOT_THREE 3 //arena slot 3 +#define SLOT_FOUR 4 //arena slot 4 +//tf teams +#define TEAM_SPEC 1 +#define TEAM_RED 2 +#define TEAM_BLU 3 +#define NEUTRAL 1 +//arena status +#define AS_IDLE 0 +#define AS_PRECOUNTDOWN 1 +#define AS_COUNTDOWN 2 +#define AS_FIGHT 3 +#define AS_AFTERFIGHT 4 +#define AS_REPORTED 5 +//sounds +#define STOCK_SOUND_COUNT 24 +// +#define DEFAULT_CDTIME 3 +// +#define MODEL_POINT "models/props_gameplay/cap_point_base.mdl" +#define MODEL_BRIEFCASE "models/flag/briefcase.mdl" +#define MODEL_AMMOPACK "models/items/ammopack_small.mdl" +#define MODEL_LARGE_AMMOPACK "models/items/ammopack_large.mdl" + +//#define DEBUG_LOG + +// ====[ VARIABLES ]=================================================== +// Handle, String, Float, Bool, NUM, TFCT +new bool:g_bNoStats; +new bool:g_bNoDisplayRating; + +// HUD Handles +new Handle:hm_HP = INVALID_HANDLE, + Handle:hm_Score = INVALID_HANDLE, + Handle:hm_TeammateHP = INVALID_HANDLE, + Handle:hm_KothTimerBLU = INVALID_HANDLE, + Handle:hm_KothTimerRED = INVALID_HANDLE, + Handle:hm_KothCap = INVALID_HANDLE; + +// Global Variables +new String:g_sMapName[64], + bool:g_bBlockFallDamage, + bool:g_bUseSQLite, + bool:g_bAutoCvar, + g_iDefaultFragLimit, + g_iAirshotHeight = 80, + String:g_spawnFile[128]; + +// Database +new Handle:db = INVALID_HANDLE, // Connection to SQL database. + Handle:g_hDBReconnectTimer = INVALID_HANDLE, + String:g_sDBConfig[64], + g_iReconnectInterval; + +// Global CVar Handles +new Handle:gcvar_WfP = INVALID_HANDLE, + Handle:gcvar_fragLimit = INVALID_HANDLE, + Handle:gcvar_allowedClasses = INVALID_HANDLE, + Handle:gcvar_blockFallDamage = INVALID_HANDLE, + Handle:gcvar_dbConfig = INVALID_HANDLE, + Handle:gcvar_midairHP = INVALID_HANDLE, + Handle:gcvar_airshotHeight = INVALID_HANDLE, + Handle:gcvar_RocketForceX = INVALID_HANDLE, + Handle:gcvar_RocketForceY = INVALID_HANDLE, + Handle:gcvar_RocketForceZ = INVALID_HANDLE, + Handle:gcvar_autoCvar = INVALID_HANDLE, + Handle:gcvar_bballParticle_red = INVALID_HANDLE, + Handle:gcvar_bballParticle_blue = INVALID_HANDLE, + Handle:gcvar_noDisplayRating = INVALID_HANDLE, + Handle:gcvar_stats = INVALID_HANDLE, + Handle:gcvar_reconnectInterval = INVALID_HANDLE, + Handle:gcvar_spawnFile = INVALID_HANDLE; + +// Classes +TFClassType:g_tfctClassAllowed[10]; // Special "TFClass_Type" data type. + +// Arena Vars +new Handle: g_tKothTimer[MAXARENAS+1]; +new String:g_sArenaName[MAXARENAS+1][64], + Float:g_fArenaSpawnOrigin[MAXARENAS+1][MAXSPAWNS+1][3], + Float:g_fArenaSpawnAngles[MAXARENAS+1][MAXSPAWNS+1][3], + Float:g_fArenaHPRatio[MAXARENAS+1], + Float:g_fArenaMinSpawnDist[MAXARENAS+1], + Float:g_fArenaRespawnTime[MAXARENAS+1], + Float:g_fKothCappedPercent[MAXARENAS+1], + bool:g_bArenaAmmomod[MAXARENAS+1], + bool:g_bArenaMidair[MAXARENAS+1], + bool:g_bArenaMGE[MAXARENAS+1], + bool:g_bArenaEndif[MAXARENAS+1], + bool:g_bArenaBBall[MAXARENAS+1], + bool:g_bVisibleHoops[MAXARENAS+1], + bool:g_bArenaInfAmmo[MAXARENAS+1], + bool:g_bFourPersonArena[MAXARENAS+1], + bool:g_bArenaShowHPToPlayers[MAXARENAS+1], + bool:g_bArenaUltiduo[MAXARENAS+1], + bool:g_bArenaKoth[MAXARENAS+1], + bool:g_bPlayerTouchPoint[MAXARENAS+1][5], + bool:g_bArenaTurris[MAXARENAS+1], + bool:g_bOvertimePlayed[MAXARENAS+1][4], + bool:g_bTimerRunning[MAXARENAS+1], + g_iArenaCount, + g_fTotalTime[MAXARENAS+1], + g_iCappingTeam[MAXARENAS+1], + Float:g_fCappedTime[MAXARENAS+1], + g_iCapturePoint[MAXARENAS+1], + g_iDefaultCapTime[MAXARENAS+1], + g_iKothTimer[MAXARENAS+1][4], // [what arena is the cap point in][Team Red or Team Blu Time left] + g_iPointState[MAXARENAS+1], //1 = neutral, 2 = RED, 3 = BLU + g_iArenaScore[MAXARENAS+1][3], + g_iArenaQueue[MAXARENAS+1][MAXPLAYERS+1], + g_iArenaStatus[MAXARENAS+1], + g_iArenaCd[MAXARENAS+1],//countdown to round start + g_iArenaFraglimit[MAXARENAS+1], + g_iArenaMinRating[MAXARENAS+1], + g_iArenaMaxRating[MAXARENAS+1], + g_iArenaCdTime[MAXARENAS+1], + g_iArenaSpawns[MAXARENAS+1], + g_iBBallHoop[MAXARENAS+1][3], // [What arena the hoop is in][Hoop 1 or Hoop 2] + g_iBBallIntel[MAXARENAS+1], + g_iArenaEarlyLeave[MAXARENAS+1], + g_iELOMenuPage[MAXARENAS+1], + TFClassType:g_tfctArenaAllowedClasses[MAXARENAS+1][10]; // Special "TFClass_Type" data type. + +// Player vars +new Handle:g_hWelcomeTimer[MAXPLAYERS+1], + String:g_sPlayerSteamID[MAXPLAYERS+1][32],//saving steamid + bool:g_bPlayerTakenDirectHit[MAXPLAYERS+1],//player was hit directly + bool:g_bPlayerRestoringAmmo[MAXPLAYERS+1],//player is awaiting full ammo restore + bool:g_bPlayerHasIntel[MAXPLAYERS+1], + bool:g_bHitBlip[MAXPLAYERS+1], + bool:g_bShowHud[MAXPLAYERS+1] = true, + bool:g_iPlayerWaiting[MAXPLAYERS+1], + bool:g_bCanPlayerSwap[MAXPLAYERS+1], + bool:g_bCanPlayerGetIntel[MAXPLAYERS+1], + g_iPlayerArena[MAXPLAYERS+1], + g_iPlayerSlot[MAXPLAYERS+1], + g_iPlayerHP[MAXPLAYERS+1], //true HP of players + g_iPlayerSpecTarget[MAXPLAYERS+1], + g_iPlayerMaxHP[MAXPLAYERS+1], + g_iClientParticle[MAXPLAYERS+1], + g_iPlayerClip[MAXPLAYERS+1][3], + g_iPlayerWins[MAXPLAYERS+1], + g_iPlayerLosses[MAXPLAYERS+1], + g_iPlayerRating[MAXPLAYERS+1], + g_iPlayerHandicap[MAXPLAYERS+1], + TFClassType:g_tfctPlayerClass[MAXPLAYERS+1]; + +// Bot things +new bool:g_bPlayerAskedForBot[MAXPLAYERS+1]; + +// Midair +new g_iMidairHP; + +// Debug log +new String:g_sLogFile[PLATFORM_MAX_PATH]; + +// Endif +new Float:g_fRocketForceX, + Float:g_fRocketForceY, + Float:g_fRocketForceZ; + +// Bball +new String:g_sBBallParticleRed[64], + String:g_sBBallParticleBlue[64]; + +static const String:stockSounds[][]= // Sounds that do not need to be downloaded. +{ + "vo/intel_teamcaptured.wav", + "vo/intel_teamdropped.wav", + "vo/intel_teamstolen.wav", + "vo/intel_enemycaptured.wav", + "vo/intel_enemydropped.wav", + "vo/intel_enemystolen.wav", + "vo/announcer_ends_5sec.wav", + "vo/announcer_ends_4sec.wav", + "vo/announcer_ends_3sec.wav", + "vo/announcer_ends_2sec.wav", + "vo/announcer_ends_1sec.wav", + "vo/announcer_ends_10sec.wav", + "vo/announcer_control_point_warning.wav", + "vo/announcer_control_point_warning2.wav", + "vo/announcer_control_point_warning3.wav", + "vo/announcer_overtime.wav", + "vo/announcer_overtime2.wav", + "vo/announcer_overtime3.wav", + "vo/announcer_overtime4.wav", + "vo/announcer_we_captured_control.wav", + "vo/announcer_we_lost_control.wav", + "items/spawn_item.wav", + "vo/announcer_victory.wav", + "vo/announcer_you_failed.wav" +}; + +public Plugin:myinfo = +{ + name = "MGEMod", + author = "Lange & Cprice; based on kAmmomod by Krolus.", + description = "Duel mod with realistic game situations.", + version = PL_VERSION, + url = "https://github.com/Langeh/MGEMod, http://steamcommunity.com/id/langeh" +} + +/* +** ------------------------------------------------------------------ +** ____ ______ __ _ +** / __ \____ / ____/__ ______ _____/ /_(_)____ ____ _____ +** / / / / __ \ / /_ / / / / __ \/ ___/ __/ // __ \/ __ \/ ___/ +** / /_/ / / / / / __/ / /_/ / / / / /__/ /_/ // /_/ / / / (__ ) +** \____/_/ /_/ /_/ \__,_/_/ /_/\___/\__/_/ \____/_/ /_/____/ +** +** ------------------------------------------------------------------ +**/ + +/* OnPluginStart() + * + * When the plugin is loaded. + * Cvars, variables, and console commands are initialzed here. + * -------------------------------------------------------------------------- */ +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("mgemod.phrases"); + //ConVars + CreateConVar("sm_mgemod_version", PL_VERSION, "MGEMod version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + gcvar_fragLimit = CreateConVar("mgemod_fraglimit", "3", "Default frag limit in duel", FCVAR_NONE,true, 1.0); + gcvar_allowedClasses = CreateConVar("mgemod_allowed_classes", "soldier demoman scout", "Classes that players allowed to choose by default"); + gcvar_blockFallDamage = CreateConVar("mgemod_blockdmg_fall", "0", "Block falldamage? (0 = Disabled)", FCVAR_NONE, true, 0.0, true, 1.0); + gcvar_dbConfig = CreateConVar("mgemod_dbconfig", "mgemod", "Name of database config"); + gcvar_stats = CreateConVar("mgemod_stats", "1", "Enable/Disable stats."); + gcvar_airshotHeight = CreateConVar("mgemod_airshot_height", "80", "The minimum height at which it will count airshot", FCVAR_NONE, true, 10.0, true, 500.0); + gcvar_RocketForceX = CreateConVar("mgemod_endif_force_x", "1.1", "The amount by which to multiply the X push force on Endif.", FCVAR_NONE, true, 1.0, true, 10.0); + gcvar_RocketForceY = CreateConVar("mgemod_endif_force_y", "1.1", "The amount by which to multiply the Y push force on Endif.", FCVAR_NONE, true, 1.0, true, 10.0); + gcvar_RocketForceZ = CreateConVar("mgemod_endif_force_z", "2.15", "The amount by which to multiply the Z push force on Endif.", FCVAR_NONE, true, 1.0, true, 10.0); + gcvar_autoCvar = CreateConVar("mgemod_autocvar", "1", "Automatically set reccomended game cvars? (0 = Disabled)", FCVAR_NONE, true, 0.0, true, 1.0); + gcvar_bballParticle_red = CreateConVar("mgemod_bball_particle_red", "player_intel_trail_red", "Particle effect to attach to Red players in BBall."); + gcvar_bballParticle_blue = CreateConVar("mgemod_bball_particle_blue", "player_intel_trail_blue", "Particle effect to attach to Blue players in BBall."); + gcvar_WfP = FindConVar("mp_waitingforplayers_cancel"); + gcvar_midairHP = CreateConVar("mgemod_midair_hp", "5", "", FCVAR_NONE, true, 1.0); + gcvar_noDisplayRating = CreateConVar("mgemod_hide_rating", "0", "Hide the in-game display of rating points. They will still be tracked in the database."); + gcvar_reconnectInterval = CreateConVar("mgemod_reconnect_interval", "5", "How long (in minutes) to wait between database reconnection attempts."); + gcvar_spawnFile = CreateConVar("mgemod_spawnfile", "configs/mgemod_spawns.cfg", "Spawn file"); + + // Populate global variables with their corresponding convar values. + g_iDefaultFragLimit = GetConVarInt(gcvar_fragLimit); + g_bBlockFallDamage = GetConVarInt(gcvar_blockFallDamage) ? true : false; + GetConVarString(gcvar_dbConfig,g_sDBConfig,sizeof(g_sDBConfig)); + g_bNoStats = (GetConVarBool(gcvar_stats)) ? false : true; + g_iAirshotHeight = GetConVarInt(gcvar_airshotHeight); + g_iMidairHP = GetConVarInt(gcvar_midairHP); + g_fRocketForceX = GetConVarFloat(gcvar_RocketForceX); + g_fRocketForceY = GetConVarFloat(gcvar_RocketForceY); + g_fRocketForceZ = GetConVarFloat(gcvar_RocketForceZ); + g_bAutoCvar = GetConVarInt(gcvar_autoCvar) ? true : false; + GetConVarString(gcvar_bballParticle_red, g_sBBallParticleRed, sizeof(g_sBBallParticleRed)); + GetConVarString(gcvar_bballParticle_blue, g_sBBallParticleBlue, sizeof(g_sBBallParticleBlue)); + g_bNoDisplayRating = GetConVarInt(gcvar_noDisplayRating) ? true : false; + g_iReconnectInterval = GetConVarInt(gcvar_reconnectInterval); + GetConVarString(gcvar_spawnFile, g_spawnFile, sizeof(g_spawnFile)); + for(new i = 0; i < MAXARENAS+1; ++i) + { + g_bTimerRunning[i] = false; + g_fCappedTime[i] = 0.0; + g_fTotalTime[i] = 0; + } + + + // Parse default list of allowed classes. + ParseAllowedClasses("",g_tfctClassAllowed); + + // Only connect to the SQL DB if stats are enabled. + if(!g_bNoStats) + { + PrepareSQL(); + } + + // Hook convar changes. + HookConVarChange(gcvar_spawnFile, handler_ConVarChange); + HookConVarChange(gcvar_fragLimit, handler_ConVarChange); + HookConVarChange(gcvar_allowedClasses, handler_ConVarChange); + HookConVarChange(gcvar_blockFallDamage, handler_ConVarChange); + HookConVarChange(gcvar_dbConfig, handler_ConVarChange); + HookConVarChange(gcvar_stats, handler_ConVarChange); + HookConVarChange(gcvar_airshotHeight, handler_ConVarChange); + HookConVarChange(gcvar_midairHP, handler_ConVarChange); + HookConVarChange(gcvar_RocketForceX, handler_ConVarChange); + HookConVarChange(gcvar_RocketForceY, handler_ConVarChange); + HookConVarChange(gcvar_RocketForceZ, handler_ConVarChange); + HookConVarChange(gcvar_autoCvar, handler_ConVarChange); + HookConVarChange(gcvar_bballParticle_red, handler_ConVarChange); + HookConVarChange(gcvar_bballParticle_blue, handler_ConVarChange); + HookConVarChange(gcvar_noDisplayRating, handler_ConVarChange); + HookConVarChange(gcvar_reconnectInterval, handler_ConVarChange); + + // Create/register client commands. + RegConsoleCmd("mgemod", Command_Menu, "MGEMod Menu"); + RegConsoleCmd("add", Command_Menu, "Usage: add . Add to an arena."); + RegConsoleCmd("swap", Command_Swap, "Ask your teammate to swap classes with you in ultiduo"); + RegConsoleCmd("remove", Command_Remove, "Remove from current arena."); + RegConsoleCmd("top5", Command_Top5, "Display the Top 5 players."); + RegConsoleCmd("hitblip", Command_ToogleHitblip, "Toggle hitblip."); + RegConsoleCmd("hud", Command_ToggleHud, "Toggle text hud."); + RegConsoleCmd("hidehud", Command_ToggleHud, "Toggle text hud. (alias)"); + RegConsoleCmd("rank", Command_Rank, "Usage: rank . Show that player's rank."); + RegConsoleCmd("stats", Command_Rank, "Alias for \"rank\"."); + RegConsoleCmd("mgehelp", Command_Help); + RegConsoleCmd("first", Command_First, "Join the first available arena."); + RegConsoleCmd("handicap", Command_Handicap, "Reduce your maximum HP. Type '!handicap off' to disable."); + RegConsoleCmd("spec_next", Command_Spec); + RegConsoleCmd("spec_prev", Command_Spec); + RegConsoleCmd("joinclass", Command_JoinClass); + RegAdminCmd("loc", Command_Loc, ADMFLAG_BAN, "Shows client origin and angle vectors"); + RegAdminCmd("botme", Command_AddBot, ADMFLAG_BAN, "Add bot to your arena"); + RegAdminCmd("conntest", Command_ConnectionTest, ADMFLAG_BAN, "MySQL connection test"); + + AddCommandListener(Command_DropItem, "dropitem"); + + // Create the HUD text handles for later use. + hm_HP = CreateHudSynchronizer(); + hm_Score = CreateHudSynchronizer(); + hm_KothTimerBLU = CreateHudSynchronizer(); + hm_KothTimerRED = CreateHudSynchronizer(); + hm_KothCap = CreateHudSynchronizer(); + hm_TeammateHP = CreateHudSynchronizer(); + + // Set up the log file for debug logging. + BuildPath(Path_SM, g_sLogFile, sizeof(g_sLogFile), "logs/mgemod.log"); + + /* This is here in the event of the plugin being hot-loaded while players are in the server. + Should probably delete this, as the rest of the code doesn't really support hot-loading. */ + if(!g_bNoStats) + for (new i=1;i<=MaxClients;i++) + if(IsValidClient(i)) + OnClientPostAdminCheck(i); + + for (new i=0;i<=MaxClients;i++) + { + g_bCanPlayerSwap[i] = true; + g_bCanPlayerGetIntel[i] = true; + } + +} + +/* OnGetGameDescription(String:gameDesc[64]) + * + * Used to change the game description from + * "Team Fortress 2" to "MGEMod vx.x.x" + * -------------------------------------------------------------------------- */ +public Action:OnGetGameDescription(String:gameDesc[64]) +{ + Format(gameDesc, sizeof(gameDesc), "MGEMod v%s",PL_VERSION); + return Plugin_Changed; +} + +/* OnMapStart() +* +* When the map starts. +* Sounds, models, and spawns are loaded here. +* Most events are hooked here as well. +* -------------------------------------------------------------------------- */ +public OnMapStart() +{ + for (new i=0;i0 && other<=MaxClients) + g_bPlayerTakenDirectHit[other] = true; + +} + +/* OnClientPostAdminCheck(client) + * + * Called once a client is authorized and fully in-game. + * Client-specific variables are initialized here. + * -------------------------------------------------------------------------- */ +public OnClientPostAdminCheck(client) +{ + if (client) + { + if (IsFakeClient(client)) + { + for (new i=1;i<=MaxClients;i++) + { + if (g_bPlayerAskedForBot[i]) + { + new arena_index = g_iPlayerArena[i]; + new Handle:pk; + CreateDataTimer(1.5,Timer_AddBotInQueue,pk); + WritePackCell(pk, GetClientUserId(client)); + WritePackCell(pk, arena_index); + g_iPlayerRating[client] = 1551; + g_bPlayerAskedForBot[i] = false; + break; + } + } + } else { + CreateTimer(5.0, Timer_ShowAdv, GetClientUserId(client)); /* Show advice to type !add in chat */ + g_bHitBlip[client] = false; + g_bShowHud[client] = true; + g_bPlayerRestoringAmmo[client] = false; + g_hWelcomeTimer[client] = CreateTimer(15.0, Timer_WelcomePlayer, GetClientUserId(client)); + + if (!g_bNoStats) + { + decl String:steamid_dirty[31], String:steamid[64], String:query[256]; + GetClientAuthId(client, AuthId_Steam2, steamid_dirty, sizeof(steamid_dirty)); + SQL_EscapeString(db, steamid_dirty, steamid, sizeof(steamid)); + strcopy(g_sPlayerSteamID[client],32,steamid); + Format(query, sizeof(query), "SELECT rating, hitblip, wins, losses FROM mgemod_stats WHERE steamid='%s' LIMIT 1", steamid); + SQL_TQuery(db, T_SQLQueryOnConnect, query, client); + } + } + } + + SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage); +} + +/* OnClientDisconnect(client) +* +* When a client disconnects from the server. +* Client-specific timers are killed here. +* -------------------------------------------------------------------------- */ +public OnClientDisconnect(client) +{ + if (IsValidClient(client) && g_iPlayerArena[client]) + { + RemoveFromQueue(client,true); + } + else + { + new arena_index = g_iPlayerArena[client], + player_slot = g_iPlayerSlot[client], + after_leaver_slot = player_slot + 1, + foe_slot = (player_slot==SLOT_ONE || player_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE, + foe = g_iArenaQueue[arena_index][foe_slot]; + + //Turn all this logic into a helper meathod + new player_teammate, + foe2; + + if(g_bFourPersonArena[arena_index]) + { + player_teammate = getTeammate(client, player_slot, arena_index); + foe2 = getTeammate(foe, foe_slot, arena_index); + } + + g_iPlayerArena[client] = 0; + g_iPlayerSlot[client] = 0; + g_iArenaQueue[arena_index][player_slot] = 0; + g_iPlayerHandicap[client] = 0; + + if(g_bFourPersonArena[arena_index]) + { + if (g_iArenaQueue[arena_index][SLOT_FOUR+1]) + { + new next_client = g_iArenaQueue[arena_index][SLOT_FOUR+1]; + g_iArenaQueue[arena_index][SLOT_FOUR+1] = 0; + g_iArenaQueue[arena_index][player_slot] = next_client; + g_iPlayerSlot[next_client] = player_slot; + after_leaver_slot = SLOT_FOUR + 2; + new String:playername[MAX_NAME_LENGTH]; + CreateTimer(2.0,Timer_StartDuel,arena_index); + GetClientName(next_client,playername,sizeof(playername)); + + if (!g_bNoStats && !g_bNoDisplayRating) + CPrintToChatAll("%t","JoinsArena",playername,g_iPlayerRating[next_client],g_sArenaName[arena_index]); + else + CPrintToChatAll("%t","JoinsArenaNoStats",playername,g_sArenaName[arena_index]); + + + } else { + + if (foe && IsFakeClient(foe)) + { + new Handle:cvar = FindConVar("tf_bot_quota"); + new quota = GetConVarInt(cvar); + ServerCommand("tf_bot_quota %d", quota - 1); + } + + if (foe2 && IsFakeClient(foe2)) + { + new Handle:cvar = FindConVar("tf_bot_quota"); + new quota = GetConVarInt(cvar); + ServerCommand("tf_bot_quota %d", quota - 1); + } + + if (player_teammate && IsFakeClient(player_teammate)) + { + new Handle:cvar = FindConVar("tf_bot_quota"); + new quota = GetConVarInt(cvar); + ServerCommand("tf_bot_quota %d", quota - 1); + } + + g_iArenaStatus[arena_index] = AS_IDLE; + return; + } + } + else + { + if (g_iArenaQueue[arena_index][SLOT_TWO+1]) + { + new next_client = g_iArenaQueue[arena_index][SLOT_TWO+1]; + g_iArenaQueue[arena_index][SLOT_TWO+1] = 0; + g_iArenaQueue[arena_index][player_slot] = next_client; + g_iPlayerSlot[next_client] = player_slot; + after_leaver_slot = SLOT_TWO + 2; + new String:playername[MAX_NAME_LENGTH]; + CreateTimer(2.0,Timer_StartDuel,arena_index); + GetClientName(next_client,playername,sizeof(playername)); + + if (!g_bNoStats && !g_bNoDisplayRating) + CPrintToChatAll("%t","JoinsArena",playername,g_iPlayerRating[next_client],g_sArenaName[arena_index]); + else + CPrintToChatAll("%t","JoinsArenaNoStats",playername,g_sArenaName[arena_index]); + + + } else { + if (foe && IsFakeClient(foe)) + { + new Handle:cvar = FindConVar("tf_bot_quota"); + new quota = GetConVarInt(cvar); + ServerCommand("tf_bot_quota %d", quota - 1); + } + + g_iArenaStatus[arena_index] = AS_IDLE; + return; + } + } + + if (g_iArenaQueue[arena_index][after_leaver_slot]) + { + while (g_iArenaQueue[arena_index][after_leaver_slot]) + { + g_iArenaQueue[arena_index][after_leaver_slot-1] = g_iArenaQueue[arena_index][after_leaver_slot]; + g_iPlayerSlot[g_iArenaQueue[arena_index][after_leaver_slot]] -= 1; + after_leaver_slot++; + } + g_iArenaQueue[arena_index][after_leaver_slot-1] = 0; + } + } + + if (g_hWelcomeTimer[client] != INVALID_HANDLE) + { + KillTimer(g_hWelcomeTimer[client]); + g_hWelcomeTimer[client] = INVALID_HANDLE; + } +} + +/* OnGameFrame() + * + * This code is run on every frame. Can be very hardware intensive. + * -------------------------------------------------------------------------- */ +public OnGameFrame() +{ + + new arena_index; + + for (new client = 1; client <= MaxClients; client++) + { + if (IsValidClient(client) && IsPlayerAlive(client)) + { + arena_index = g_iPlayerArena[client]; + if(!g_bArenaBBall[arena_index] && !g_bArenaMGE[arena_index] && !g_bArenaKoth[arena_index]) + { + /* This is a hack that prevents people from getting one-shot by things + like the direct hit in the Ammomod arenas. */ + new replacement_hp = (g_iPlayerMaxHP[client] + 512); + SetEntProp(client, Prop_Send, "m_iHealth", replacement_hp, 1); + } + } + } + for(new arena_index2 = 1; arena_index2 <= g_iArenaCount; ++arena_index2) + { + if(g_bArenaKoth[arena_index2] && g_iArenaStatus[arena_index2] == AS_FIGHT) + { + g_fTotalTime[arena_index2] += 7; + if(g_iPointState[arena_index2] == NEUTRAL || g_iPointState[arena_index2] == TEAM_BLU) + { + //If RED Team is capping and BLU Team isn't and BLU Team has the point increase the cap time + if(!(g_bPlayerTouchPoint[arena_index2][SLOT_TWO] || g_bPlayerTouchPoint[arena_index2][SLOT_FOUR]) && (g_iCappingTeam[arena_index2] == TEAM_RED || g_iCappingTeam[arena_index2] == NEUTRAL)) + { + new cap = 0; + + if(g_bPlayerTouchPoint[arena_index2][SLOT_ONE]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_ONE]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_ONE], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + if(g_bPlayerTouchPoint[arena_index2][SLOT_THREE]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_THREE]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_THREE], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + //Add cap time if needed + if(cap) + { + //True harmonic cap time, yes! + for(; cap > 0; cap--) + { + g_fCappedTime[arena_index2] += FloatDiv(7.0, float(cap)); + } + g_iCappingTeam[arena_index2] = TEAM_RED; + continue; + } + } + + + } + + if(g_iPointState[arena_index2] == NEUTRAL || g_iPointState[arena_index2] == TEAM_RED) + { + //If BLU Team is capping and Team RED isn't and Team RED has the point increase the cap time + if(!(g_bPlayerTouchPoint[arena_index2][SLOT_ONE] || g_bPlayerTouchPoint[arena_index2][SLOT_THREE]) && (g_iCappingTeam[arena_index2] == TEAM_BLU || g_iCappingTeam[arena_index2] == NEUTRAL)) + { + new cap = 0; + + if(g_bPlayerTouchPoint[arena_index2][SLOT_TWO]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_TWO]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_TWO], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + if(g_bPlayerTouchPoint[arena_index2][SLOT_FOUR]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_FOUR]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_FOUR], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + //Add cap time if needed + if(cap) + { + //True harmonic cap time, yes! + for(; cap > 0; cap--) + { + g_fCappedTime[arena_index2] += FloatDiv(7.0, float(cap)); + } + g_iCappingTeam[arena_index2] = TEAM_BLU; + continue; + } + } + + + } + + //If BLU Team is blocking and RED Team isn't capping and BLU Team has the point increase the cap diminish rate + if((g_bPlayerTouchPoint[arena_index2][SLOT_TWO] || g_bPlayerTouchPoint[arena_index2][SLOT_FOUR]) && + (g_iPointState[arena_index2] == NEUTRAL) && g_iCappingTeam[arena_index2] == TEAM_RED && + !(g_bPlayerTouchPoint[arena_index2][SLOT_ONE] || g_bPlayerTouchPoint[arena_index2][SLOT_THREE])) + { + new cap = 0; + + if(g_bPlayerTouchPoint[arena_index2][SLOT_TWO]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_TWO]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_TWO], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + if(g_bPlayerTouchPoint[arena_index2][SLOT_FOUR]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_FOUR]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_FOUR], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + //Add cap time if needed + if(cap) + { + //True harmonic cap time, yes! + for(; cap > 0; cap--) + { + g_fCappedTime[arena_index2] -= FloatDiv(7.0, float(cap)); + } + g_iCappingTeam[arena_index2] = TEAM_BLU; + continue; + } + } + + //If RED Team is blocking and BLU Team isn't capping and RED Team has the point increase the cap diminish rate + if((g_bPlayerTouchPoint[arena_index2][SLOT_ONE] || g_bPlayerTouchPoint[arena_index2][SLOT_THREE]) && + (g_iPointState[arena_index2] == NEUTRAL) && g_iCappingTeam[arena_index2] == TEAM_BLU && + !(g_bPlayerTouchPoint[arena_index2][SLOT_TWO] || g_bPlayerTouchPoint[arena_index2][SLOT_FOUR])) + { + new cap = 0; + + if(g_bPlayerTouchPoint[arena_index2][SLOT_ONE]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_ONE]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_ONE], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + if(g_bPlayerTouchPoint[arena_index2][SLOT_THREE]) + { + cap++; + //If the player is a Scout add one to the cap speed + if(g_tfctPlayerClass[g_iArenaQueue[arena_index2][SLOT_THREE]] ==TF2_GetClass("scout")) + cap++; + + new ent = GetPlayerWeaponSlot(g_iArenaQueue[arena_index2][SLOT_THREE], 2); + new iItemDefinitionIndex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + + //If the player has the Pain Train equipped add one to the cap speed + if(iItemDefinitionIndex == 154) + cap++; + } + //Add cap time if needed + if(cap) + { + //True harmonic cap time, yes! + for(; cap > 0; cap--) + { + g_fCappedTime[arena_index2] -= FloatDiv(7.0, float(cap)); + } + g_iCappingTeam[arena_index2] = TEAM_RED; + continue; + } + } + + //If both teams are touching the point, do nothing + if((g_bPlayerTouchPoint[arena_index2][SLOT_TWO] || g_bPlayerTouchPoint[arena_index2][SLOT_FOUR]) && (g_bPlayerTouchPoint[arena_index2][SLOT_ONE] || g_bPlayerTouchPoint[arena_index2][SLOT_THREE])) + continue; + + // If in overtime, revert cap at 6x speed, if not, revert cap slowly + if (g_bOvertimePlayed[arena_index][TEAM_RED] || g_bOvertimePlayed[arena_index][TEAM_BLU]) + g_fCappedTime[arena_index2] -= 6.0; + else + g_fCappedTime[arena_index2]--; + } + + + } + + + +} + +/* OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype) + * + * When a client takes damage. + * -------------------------------------------------------------------------- */ +public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype) +{ + if(!IsValidClient(victim) || !IsValidClient(attacker)) + return Plugin_Continue; + + // Fall damage negation. + if ((damagetype & DMG_FALL) && g_bBlockFallDamage) + { + damage = 0.0; + return Plugin_Changed; + } + + return Plugin_Continue; +} + +/* OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon) +* +* When a client runs a command. +* Infinite ammo is triggered here. +* -------------------------------------------------------------------------- */ +public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon) +{ + new arena_index = g_iPlayerArena[client]; + if(g_bArenaInfAmmo[arena_index]) + { + if (!g_bPlayerRestoringAmmo[client] && (buttons & IN_ATTACK)) + { + g_bPlayerRestoringAmmo[client] = true; + CreateTimer(0.4,Timer_GiveAmmo,GetClientUserId(client)); + } + } +} + +/* OnTouchPoint(entity, other) +* +* When the point is touched +* ------------------------------------------------------------------------- */ +public Action:OnEndTouchPoint(entity, other) +{ + new client = other; + + if(!IsValidClient(client)) + return; + + new arena_index = g_iPlayerArena[client]; + new client_slot = g_iPlayerSlot[client]; + + g_bPlayerTouchPoint[arena_index][client_slot] = false; + +} + +/* OnTouchPoint(entity, other) +* +* When the point is touched +* ------------------------------------------------------------------------- */ +public Action:OnTouchPoint(entity, other) +{ + new client = other; + + if(!IsValidClient(client)) + return; + + new arena_index = g_iPlayerArena[client]; + new client_slot = g_iPlayerSlot[client]; + + g_bPlayerTouchPoint[arena_index][client_slot] = true; + +} + +/* OnTouchHoop(entity, other) +* +* When a hoop is touched by a player in BBall. +* -------------------------------------------------------------------------- */ +public Action:OnTouchHoop(entity, other) +{ + new client = other; + + if(!IsValidClient(client)) + return; + + new arena_index = g_iPlayerArena[client]; + new fraglimit = g_iArenaFraglimit[arena_index]; + new client_slot = g_iPlayerSlot[client]; + new foe_slot = (client_slot==SLOT_ONE || client_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + new foe = g_iArenaQueue[arena_index][foe_slot]; + new client_teammate; + new foe_teammate; + new foe_team_slot = (foe_slot > 2) ? (foe_slot - 2) : foe_slot; + new client_team_slot = (client_slot > 2) ? (client_slot - 2) : client_slot; + + if(g_bFourPersonArena[arena_index]) + { + client_teammate = getTeammate(client, client_slot, arena_index); + foe_teammate = getTeammate(foe, foe_slot, arena_index); + } + + + + if(!IsValidClient(foe) || !g_bArenaBBall[arena_index]) + return; + + if(entity == g_iBBallHoop[arena_index][foe_slot] && g_bPlayerHasIntel[client]) + { + // Remove the particle effect attached to the player carrying the intel. + RemoveClientParticle(client); + + new String:foe_name[MAX_NAME_LENGTH]; + GetClientName(foe, foe_name, sizeof(foe_name)); + new String:client_name[MAX_NAME_LENGTH]; + GetClientName(client, client_name, sizeof(client_name)); + + CPrintToChat(client, "%t", "bballdunk", foe_name); + + g_bPlayerHasIntel[client] = false; + g_iArenaScore[arena_index][client_team_slot] += 1; + + if (fraglimit>0 && g_iArenaScore[arena_index][client_team_slot] >= fraglimit && g_iArenaStatus[arena_index] >= AS_FIGHT && g_iArenaStatus[arena_index] < AS_REPORTED) + { + g_iArenaStatus[arena_index] = AS_REPORTED; + GetClientName(client,client_name, sizeof(client_name)); + + if(g_bFourPersonArena[arena_index]) + { + new String:client_teammate_name[128]; + new String:foe_teammate_name[128]; + + GetClientName(client_teammate,client_teammate_name, sizeof(client_teammate_name)); + GetClientName(foe_teammate,foe_teammate_name, sizeof(foe_teammate_name)); + + Format(client_name, sizeof(client_name), "%s and %s", client_name, client_teammate_name); + Format(foe_name, sizeof(foe_name), "%s and %s", foe_name, foe_teammate_name); + } + + CPrintToChatAll("%t","XdefeatsY", client_name, g_iArenaScore[arena_index][client_team_slot], foe_name, g_iArenaScore[arena_index][foe_team_slot], fraglimit, g_sArenaName[arena_index]); + + if (!g_bNoStats && !g_bFourPersonArena[arena_index]) + CalcELO(client,foe); + + else if(!g_bNoStats) + CalcELO2(client, client_teammate, foe, foe_teammate); + + if(IsValidEdict(g_iBBallIntel[arena_index]) && g_iBBallIntel[arena_index] > -1) + { + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + RemoveEdict(g_iBBallIntel[arena_index]); + g_iBBallIntel[arena_index] = -1; + } + if(g_bFourPersonArena[arena_index] && g_iArenaQueue[arena_index][SLOT_FOUR+1]) + { + RemoveFromQueue(foe,false); + RemoveFromQueue(foe_teammate,false); + AddInQueue(foe,arena_index,false); + AddInQueue(foe_teammate,arena_index,false); + } + else if (g_iArenaQueue[arena_index][SLOT_TWO+1]) + { + RemoveFromQueue(foe,false); + AddInQueue(foe,arena_index,false); + } else { + CreateTimer(3.0,Timer_StartDuel,arena_index); + } + } else { + ResetPlayer(client); + ResetPlayer(foe); + + if(g_bFourPersonArena[arena_index]) + { + ResetPlayer(client_teammate); + ResetPlayer(foe_teammate); + } + + CreateTimer(0.15, Timer_ResetIntel, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + + ShowPlayerHud(client); + ShowPlayerHud(foe); + + if(g_bFourPersonArena[arena_index]) + { + ShowPlayerHud(client_teammate); + ShowPlayerHud(foe_teammate); + } + + EmitSoundToClient(client, "vo/intel_teamcaptured.wav"); + EmitSoundToClient(foe, "vo/intel_enemycaptured.wav"); + + if(g_bFourPersonArena[arena_index]) + { + //This shouldn't be necessary but I'm getting invalid clients for some reason. + if(IsValidClient(client_teammate)) + EmitSoundToClient(client_teammate, "vo/intel_teamcaptured.wav"); + if(IsValidClient(foe_teammate)) + EmitSoundToClient(foe_teammate, "vo/intel_enemycaptured.wav"); + } + + ShowSpecHudToArena(arena_index); + } +} + +/* OnTouchIntel(entity, other) +* +* When the intel is touched by a player in BBall. +* -------------------------------------------------------------------------- */ +public Action:OnTouchIntel(entity, other) +{ + new client = other; + + if(!IsValidClient(client)) + return; + + if(!g_bCanPlayerGetIntel[client]) + return; + + new arena_index = g_iPlayerArena[client]; + g_bPlayerHasIntel[client] = true; + new String:msg[64]; + Format(msg,sizeof(msg),"You have the intel!"); + PrintCenterText(client,msg); + + if(entity == g_iBBallIntel[arena_index] && IsValidEdict(g_iBBallIntel[arena_index]) && g_iBBallIntel[arena_index] > 0) + { + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + RemoveEdict(g_iBBallIntel[arena_index]); + g_iBBallIntel[arena_index] = -1; + } + + new particle; + new TFTeam:team = TFTeam:GetEntProp(client, Prop_Send, "m_iTeamNum"); + + // Create a fancy lightning effect to make it abundantly clear that the intel has just been picked up. + if (team==TFTeam_Red) + AttachParticle(client,"teleported_red",particle); + else + AttachParticle(client,"teleported_blue",particle); + + // Attach a team-colored particle to give a visual cue that a player is holding the intel, since we can't attach models. + particle = EntRefToEntIndex(g_iClientParticle[client]); + if (particle==0 || !IsValidEntity(particle)) + { + if (team==TFTeam_Red) + AttachParticle(client,g_sBBallParticleRed,particle); + else + AttachParticle(client,g_sBBallParticleBlue,particle); + + g_iClientParticle[client] = EntIndexToEntRef(particle); + } + + ShowPlayerHud(client); + EmitSoundToClient(client, "vo/intel_teamstolen.wav"); + + new foe = g_iArenaQueue[g_iPlayerArena[client]][(g_iPlayerSlot[client]==SLOT_ONE || g_iPlayerSlot[client]==SLOT_THREE) ? SLOT_TWO : SLOT_ONE]; + + + if(IsValidClient(foe)) + { + EmitSoundToClient(foe, "vo/intel_enemystolen.wav"); + ShowPlayerHud(foe); + } + + if(g_bFourPersonArena[g_iPlayerArena[client]]) + { + new foe2 = g_iArenaQueue[g_iPlayerArena[client]][(g_iPlayerSlot[client]==SLOT_ONE || g_iPlayerSlot[client]==SLOT_THREE) ? SLOT_FOUR : SLOT_THREE]; + if(IsValidClient(foe2)) + { + EmitSoundToClient(foe2, "vo/intel_enemystolen.wav"); + ShowPlayerHud(foe2); + } + } +} + +/* +** ------------------------------------------------------------------------------- +** ____ _ ______ __ _ +** / __ \_____(_)_ __ / ____/__ ______ _____/ /_(_)____ ____ _____ +** / /_/ / ___/ /| | / / / /_ / / / / __ \/ ___/ __/ // __ \/ __ \/ ___/ +** / ____/ / / / | |/ /_ / __/ / /_/ / / / / /__/ /_/ // /_/ / / / (__ ) +** /_/ /_/ /_/ |___/(_) /_/ \__,_/_/ /_/\___/\__/_/ \____/_/ /_/____/ +** +** ------------------------------------------------------------------------------- +**/ + +StartCountDown(arena_index) +{ + new red_f1 = g_iArenaQueue[arena_index][SLOT_ONE]; /* Red (slot one) player. */ + new blu_f1 = g_iArenaQueue[arena_index][SLOT_TWO]; /* Blu (slot two) player. */ + + if(g_bFourPersonArena[arena_index]) + { + new red_f2 = g_iArenaQueue[arena_index][SLOT_THREE]; /* 2nd Red (slot three) player. */ + new blu_f2 = g_iArenaQueue[arena_index][SLOT_FOUR]; /* 2nd Blu (slot four) player. */ + + if(red_f1) + ResetPlayer(red_f1); + if(blu_f1) + ResetPlayer(blu_f1); + if(red_f2) + ResetPlayer(red_f2); + if(blu_f2) + ResetPlayer(blu_f2); + + + if (red_f1 && blu_f1 && red_f2 && blu_f2) + { + new Float:enginetime = GetGameTime(); + + for (new i=0;i<=2;i++) + { + new ent = GetPlayerWeaponSlot(red_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+1.1); + + ent = GetPlayerWeaponSlot(blu_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+1.1); + + ent = GetPlayerWeaponSlot(red_f2, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+1.1); + + ent = GetPlayerWeaponSlot(blu_f2, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+1.1); + } + + + g_iArenaCd[arena_index] = g_iArenaCdTime[arena_index] + 1; + g_iArenaStatus[arena_index] = AS_PRECOUNTDOWN; + CreateTimer(0.0,Timer_CountDown,arena_index,TIMER_FLAG_NO_MAPCHANGE); + return 1; + } else { + g_iArenaStatus[arena_index] = AS_IDLE; + return 0; + } + } + else { + if(red_f1) + ResetPlayer(red_f1); + if(blu_f1) + ResetPlayer(blu_f1); + + if (red_f1 && blu_f1) + { + new Float:enginetime = GetGameTime(); + + for (new i=0;i<=2;i++) + { + new ent = GetPlayerWeaponSlot(red_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+1.1); + + ent = GetPlayerWeaponSlot(blu_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+1.1); + } + + g_iArenaCd[arena_index] = g_iArenaCdTime[arena_index] + 1; + g_iArenaStatus[arena_index] = AS_PRECOUNTDOWN; + CreateTimer(0.0,Timer_CountDown,arena_index,TIMER_FLAG_NO_MAPCHANGE); + return 1; + } + else + { + g_iArenaStatus[arena_index] = AS_IDLE; + return 0; + } + } + + +} + +// ====[ HUD ]==================================================== +ShowSpecHudToArena(arena_index) +{ + if (!arena_index) + return; + + for (new i=1;i<=MaxClients;i++) + { + if (IsValidClient(i) && GetClientTeam(i)==TEAM_SPEC && g_iPlayerSpecTarget[i]>0 && g_iPlayerArena[g_iPlayerSpecTarget[i]]==arena_index) + ShowSpecHudToClient(i); + } +} + +ShowCountdownToSpec(arena_index,String:text[]) +{ + if (!arena_index) + return; + + for (new i=1;i<=MaxClients;i++) + { + if (IsValidClient(i) && GetClientTeam(i)==TEAM_SPEC && g_iPlayerArena[g_iPlayerSpecTarget[i]]==arena_index) + PrintCenterText(i,text); + } +} + +ShowPlayerHud(client) +{ + if (!IsValidClient(client)) + return; + + + + // HP + new arena_index = g_iPlayerArena[client]; + new client_slot = g_iPlayerSlot[client]; + new client_foe_slot = (client_slot==SLOT_ONE || client_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + new client_foe = (g_iArenaQueue[g_iPlayerArena[client]][(g_iPlayerSlot[client]==SLOT_ONE || g_iPlayerSlot[client]==SLOT_THREE) ? SLOT_TWO : SLOT_ONE]); //test + new client_teammate; + new client_foe2; + new String:hp_report[128]; + + if(g_bFourPersonArena[arena_index]) + { + client_teammate = getTeammate(client, client_slot, arena_index); + client_foe2 = getTeammate(client_foe, client_foe_slot, arena_index); + + } + + + + + if(g_bArenaKoth[arena_index]) + { + if(g_iArenaStatus[arena_index] == AS_FIGHT || true) + { + //Show the red team timer, if they have it capped make the timer red + if(g_iPointState[arena_index] == TEAM_RED) + SetHudTextParams(0.40, 0.01, HUDFADEOUTTIME, 255,0,0,255); // Red + else + SetHudTextParams(0.40, 0.01, HUDFADEOUTTIME, 255,255,255,255); + + //Set the Text for the timer + ShowSyncHudText(client, hm_KothTimerRED, "%i:%02i", g_iKothTimer[arena_index][TEAM_RED] / 60, g_iKothTimer[arena_index][TEAM_RED] % 60 ); + + //Show the blue team timer, if they have it capped make the timer blue + if(g_iPointState[arena_index] == TEAM_BLU) + SetHudTextParams(0.60, 0.01, HUDFADEOUTTIME, 0,0,255,255); // Blue + else + SetHudTextParams(0.60, 0.01, HUDFADEOUTTIME, 255,255,255,255); + //Set the Text for the timer + ShowSyncHudText(client, hm_KothTimerBLU, "%i:%02i", g_iKothTimer[arena_index][TEAM_BLU] / 60, g_iKothTimer[arena_index][TEAM_BLU] % 60); + + //Show the capture point percent + //set it red if red team is capping + if(g_iCappingTeam[arena_index] == TEAM_RED) + SetHudTextParams(0.50, 0.80, HUDFADEOUTTIME, 255,0,0,255); // Red + //Set it blue if blu team is capping + else if(g_iCappingTeam[arena_index] == TEAM_BLU) + SetHudTextParams(0.50, 0.80, HUDFADEOUTTIME, 0,0,255,255); // Blue + //Set it white if no one is capping + else + SetHudTextParams(0.50, 0.80, HUDFADEOUTTIME, 255,255,255,255); + //Show the text + ShowSyncHudText(client, hm_KothCap, "Point Capture: %.1f", g_fKothCappedPercent[arena_index]); + } + } + + + if(g_bArenaShowHPToPlayers[arena_index]) + { + new Float:hp_ratio = ((float(g_iPlayerHP[client])) / (float(g_iPlayerMaxHP[client])*g_fArenaHPRatio[arena_index])); + if(hp_ratio > 0.66) + SetHudTextParams(0.01, 0.80, HUDFADEOUTTIME, 0,255,0,255); // Green + else if(hp_ratio >= 0.33) + SetHudTextParams(0.01, 0.80, HUDFADEOUTTIME, 255,255,0,255); // Yellow + else if(hp_ratio < 0.33) + SetHudTextParams(0.01, 0.80, HUDFADEOUTTIME, 255,0,0,255); // Red + + ShowSyncHudText(client, hm_HP, "Health : %d", g_iPlayerHP[client]); + } else { + ShowSyncHudText(client, hm_HP, "", g_iPlayerHP[client]); + } + + + + if(g_bArenaBBall[arena_index]) + { + if(g_iArenaStatus[arena_index] == AS_FIGHT) + { + if(g_bPlayerHasIntel[client]) + ShowSyncHudText(client, hm_HP, "You have the intel!", g_iPlayerHP[client]); + else if(g_bFourPersonArena[arena_index] && g_bPlayerHasIntel[client_teammate]) + ShowSyncHudText(client, hm_HP, "Your teammate has the intel!", g_iPlayerHP[client]); + else if(g_bPlayerHasIntel[client_foe] || (g_bFourPersonArena[arena_index] && g_bPlayerHasIntel[client_foe2])) + ShowSyncHudText(client, hm_HP, "Enemy has the intel!", g_iPlayerHP[client]); + else + ShowSyncHudText(client, hm_HP, "Get the intel!", g_iPlayerHP[client]); + } else { + ShowSyncHudText(client, hm_HP, "", g_iPlayerHP[client]); + } + } + + // We want ammomod players to be able to see what their health is, even when they have the text hud turned off. + // We also want to show them BBALL notifications + if(!g_bShowHud[client]) + return; + + // Score + SetHudTextParams(0.01, 0.01, HUDFADEOUTTIME, 255,255,255,255); + new String:report[128]; + new fraglimit = g_iArenaFraglimit[arena_index]; + + if(g_bArenaBBall[arena_index]) + { + if (fraglimit>0) + Format(report,sizeof(report),"Arena %s. Capture Limit(%d)",g_sArenaName[arena_index],fraglimit); + else + Format(report,sizeof(report),"Arena %s. No Capture Limit",g_sArenaName[arena_index]); + } else { + if (fraglimit>0) + Format(report,sizeof(report),"Arena %s. Frag Limit(%d)",g_sArenaName[arena_index],fraglimit); + else + Format(report,sizeof(report),"Arena %s. No Frag Limit",g_sArenaName[arena_index]); + } + + new red_f1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu_f1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new red_f2; + new blu_f2; + if(g_bFourPersonArena[arena_index]) + { + red_f2 = g_iArenaQueue[arena_index][SLOT_THREE]; + blu_f2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + } + + if(g_bFourPersonArena[arena_index]) + { + if (red_f1) + { + if(red_f2) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N and %N : %d",report,red_f1, red_f2,g_iArenaScore[arena_index][SLOT_ONE]); + else + Format(report,sizeof(report),"%s\n%N and %N (%d): %d",report,red_f1, red_f2,g_iPlayerRating[red_f1],g_iArenaScore[arena_index][SLOT_ONE]); + } + else + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,red_f1,g_iArenaScore[arena_index][SLOT_ONE]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,red_f1,g_iPlayerRating[red_f1],g_iArenaScore[arena_index][SLOT_ONE]); + } + + + } + if (blu_f1) + { + if(blu_f2) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N and %N : %d",report,blu_f1, blu_f2,g_iArenaScore[arena_index][SLOT_TWO]); + else + Format(report,sizeof(report),"%s\n%N and %N (%d): %d",report,blu_f1,blu_f2,g_iPlayerRating[blu_f1],g_iArenaScore[arena_index][SLOT_TWO]); + } + else + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,blu_f1,g_iArenaScore[arena_index][SLOT_TWO]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,blu_f1,g_iPlayerRating[blu_f1],g_iArenaScore[arena_index][SLOT_TWO]); + } + } + } + + else + { + if (red_f1) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,red_f1,g_iArenaScore[arena_index][SLOT_ONE]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,red_f1,g_iPlayerRating[red_f1],g_iArenaScore[arena_index][SLOT_ONE]); + } + + if (blu_f1) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,blu_f1,g_iArenaScore[arena_index][SLOT_TWO]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,blu_f1,g_iPlayerRating[blu_f1],g_iArenaScore[arena_index][SLOT_TWO]); + } + } + ShowSyncHudText(client, hm_Score, "%s",report); + + + //Hp of teammate + if(g_bFourPersonArena[arena_index]) + { + + if(client_teammate) + Format(hp_report,sizeof(hp_report),"%N : %d", client_teammate,g_iPlayerHP[client_teammate]); + } + SetHudTextParams(0.01, 0.80, HUDFADEOUTTIME, 255,255,255,255); + ShowSyncHudText(client, hm_TeammateHP, hp_report); +} + +ShowSpecHudToClient(client) +{ + if (!IsValidClient(client) || !IsValidClient(g_iPlayerSpecTarget[client]) || !g_bShowHud[client]) + return; + + new arena_index = g_iPlayerArena[g_iPlayerSpecTarget[client]]; + new red_f1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu_f1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new red_f2; + new blu_f2; + + if(g_bFourPersonArena[arena_index]) + { + red_f2 = g_iArenaQueue[arena_index][SLOT_THREE]; + blu_f2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + } + + new String:hp_report[128]; + + //If its a 2v2 arena show the teamates hp's + if(g_bFourPersonArena[arena_index]) + { + if (red_f1) + Format(hp_report,sizeof(hp_report),"%N : %d", red_f1,g_iPlayerHP[red_f1]); + + if (red_f2) + Format(hp_report,sizeof(hp_report),"%s\n%N : %d", hp_report, red_f2,g_iPlayerHP[red_f2]); + + if (blu_f1) + Format(hp_report,sizeof(hp_report),"%s\n\n%N : %d",hp_report, blu_f1, g_iPlayerHP[blu_f1]); + + if (blu_f2) + Format(hp_report,sizeof(hp_report),"%s\n%N : %d",hp_report,blu_f2, g_iPlayerHP[blu_f2]); + } + else + { + if (red_f1) + Format(hp_report,sizeof(hp_report),"%N : %d", red_f1,g_iPlayerHP[red_f1]); + + if (blu_f1) + Format(hp_report,sizeof(hp_report),"%s\n%N : %d",hp_report,blu_f1, g_iPlayerHP[blu_f1]); + } + + + + SetHudTextParams(0.01, 0.80, HUDFADEOUTTIME, 255,255,255,255); + ShowSyncHudText(client, hm_HP, hp_report); + + // Score + new String:report[128]; + SetHudTextParams(0.01, 0.01, HUDFADEOUTTIME, 255,255,255,255); + + new fraglimit = g_iArenaFraglimit[arena_index]; + + if (g_iArenaStatus[arena_index] != AS_IDLE) + { + if (fraglimit>0) + Format(report,sizeof(report),"Arena %s. Frag Limit(%d)",g_sArenaName[arena_index],fraglimit); + else + Format(report,sizeof(report),"Arena %s. No Frag Limit",g_sArenaName[arena_index]); + } + else + { + Format(report,sizeof(report),"Arena[%s]",g_sArenaName[arena_index]); + } + + if(g_bFourPersonArena[arena_index]) + { + if (red_f1) + { + if(red_f2) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N and %N : %d",report,red_f1, red_f2,g_iArenaScore[arena_index][SLOT_ONE]); + else + Format(report,sizeof(report),"%s\n%N and %N (%d): %d",report,red_f1, red_f2,g_iPlayerRating[red_f1],g_iArenaScore[arena_index][SLOT_ONE]); + } + else + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,red_f1,g_iArenaScore[arena_index][SLOT_ONE]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,red_f1,g_iPlayerRating[red_f1],g_iArenaScore[arena_index][SLOT_ONE]); + } + + + } + if (blu_f1) + { + if(blu_f2) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N and %N : %d",report,blu_f1, blu_f2,g_iArenaScore[arena_index][SLOT_TWO]); + else + Format(report,sizeof(report),"%s\n%N and %N (%d): %d",report,blu_f1,blu_f2,g_iPlayerRating[blu_f1],g_iArenaScore[arena_index][SLOT_TWO]); + } + else + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,blu_f1,g_iArenaScore[arena_index][SLOT_TWO]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,blu_f1,g_iPlayerRating[blu_f1],g_iArenaScore[arena_index][SLOT_TWO]); + } + } + } + + else + { + if (red_f1) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,red_f1,g_iArenaScore[arena_index][SLOT_ONE]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,red_f1,g_iPlayerRating[red_f1],g_iArenaScore[arena_index][SLOT_ONE]); + } + + if (blu_f1) + { + if (g_bNoStats || g_bNoDisplayRating) + Format(report,sizeof(report),"%s\n%N : %d",report,blu_f1,g_iArenaScore[arena_index][SLOT_TWO]); + else + Format(report,sizeof(report),"%s\n%N (%d): %d",report,blu_f1,g_iPlayerRating[blu_f1],g_iArenaScore[arena_index][SLOT_TWO]); + } + } + + ShowSyncHudText(client, hm_Score, "%s",report); +} + +ShowHudToAll() +{ + for(new i = 1; i <= g_iArenaCount; i++) + ShowSpecHudToArena(i); + + for(new i = 1; i <= MAXPLAYERS; i++) + { + if(g_iPlayerArena[i]) + ShowPlayerHud(i); + } +} + +HideHud(client) +{ + if (!IsValidClient(client)) + return; + + ClearSyncHud(client,hm_Score); + ClearSyncHud(client,hm_HP); +} + +// ====[ QUEUE ]==================================================== +RemoveFromQueue(client, bool:calcstats=false, bool:specfix=false) +{ + new arena_index = g_iPlayerArena[client]; + + if (arena_index == 0) + { + return; + } + + new player_slot = g_iPlayerSlot[client]; + g_iPlayerArena[client] = 0; + g_iPlayerSlot[client] = 0; + g_iArenaQueue[arena_index][player_slot] = 0; + g_iPlayerHandicap[client] = 0; + + if (IsValidClient(client) && GetClientTeam(client) != TEAM_SPEC) + { + ForcePlayerSuicide(client); + ChangeClientTeam(client, 1); + + if(specfix) + CreateTimer(0.1, Timer_SpecFix, GetClientUserId(client)); + } + + new after_leaver_slot = player_slot + 1; + + //I beleive I don't need to do this anymore BUT + //If the player was in the arena, and the timer was running, kill it + if(((player_slot <= SLOT_TWO) || (g_bFourPersonArena[arena_index] && player_slot <= SLOT_FOUR)) && g_bTimerRunning[arena_index]) + { + KillTimer(g_tKothTimer[arena_index]); + g_bTimerRunning[arena_index] = false; + } + + if(g_bFourPersonArena[arena_index]) + { + new foe_team_slot; + new player_team_slot; + + if (player_slot <= SLOT_FOUR && player_slot > 0) + { + new foe_slot = (player_slot==SLOT_ONE || player_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + new foe = g_iArenaQueue[arena_index][foe_slot]; + new player_teammate; + new foe2; + + foe_team_slot = (foe_slot > 2) ? (foe_slot - 2) : foe_slot; + player_team_slot = (player_slot > 2) ? (player_slot - 2) : player_slot; + + if(g_bFourPersonArena[arena_index]) + { + player_teammate = getTeammate(client, player_slot, arena_index); + foe2 = getTeammate(foe, foe_slot, arena_index); + + } + + if(g_bArenaBBall[arena_index]) + { + if(IsValidEdict(g_iBBallIntel[arena_index]) && g_iBBallIntel[arena_index] > 0) + { + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + RemoveEdict(g_iBBallIntel[arena_index]); + g_iBBallIntel[arena_index] = -1; + } + + RemoveClientParticle(client); + g_bPlayerHasIntel[client] = false; + + if(foe) + { + RemoveClientParticle(foe); + g_bPlayerHasIntel[foe] = false; + } + + if(foe2) + { + RemoveClientParticle(foe2); + g_bPlayerHasIntel[foe2] = false; + } + + if(player_teammate) + { + RemoveClientParticle(player_teammate); + g_bPlayerHasIntel[player_teammate] = false; + } + } + + if (g_iArenaStatus[arena_index] >= AS_FIGHT && g_iArenaStatus[arena_index] < AS_REPORTED && calcstats && !g_bNoStats && foe) + { + new String:foe_name[MAX_NAME_LENGTH * 2]; + new String:player_name[MAX_NAME_LENGTH * 2]; + new String:foe2_name[MAX_NAME_LENGTH]; + new String:player_teammate_name[MAX_NAME_LENGTH]; + + GetClientName(foe,foe_name, sizeof(foe_name)); + GetClientName(client,player_name, sizeof(player_name)); + GetClientName(foe2,foe2_name, sizeof(foe2_name)); + GetClientName(player_teammate,player_teammate_name, sizeof(player_teammate_name)); + + Format(foe_name, sizeof(foe_name), "%s and %s", foe_name, foe2_name); + Format(player_name, sizeof(player_name), "%s and %s", player_name, player_teammate_name); + + g_iArenaStatus[arena_index] = AS_REPORTED; + + if(g_iArenaScore[arena_index][foe_team_slot] > g_iArenaScore[arena_index][player_team_slot]) + { + if(g_iArenaScore[arena_index][foe_team_slot] >= g_iArenaEarlyLeave[arena_index]) + { + CalcELO(foe,client); + CalcELO(foe2, client); + CPrintToChatAll("%t","XdefeatsYearly", foe_name, g_iArenaScore[arena_index][foe_team_slot], player_name, g_iArenaScore[arena_index][player_team_slot], g_sArenaName[arena_index]); + } + } + } + + if (g_iArenaQueue[arena_index][SLOT_FOUR+1]) + { + new next_client = g_iArenaQueue[arena_index][SLOT_FOUR+1]; + g_iArenaQueue[arena_index][SLOT_FOUR+1] = 0; + g_iArenaQueue[arena_index][player_slot] = next_client; + g_iPlayerSlot[next_client] = player_slot; + after_leaver_slot = SLOT_FOUR + 2; + new String:playername[MAX_NAME_LENGTH]; + CreateTimer(2.0,Timer_StartDuel,arena_index); + GetClientName(next_client,playername,sizeof(playername)); + + if (!g_bNoStats && !g_bNoDisplayRating) + CPrintToChatAll("%t","JoinsArena",playername,g_iPlayerRating[next_client],g_sArenaName[arena_index]); + else + CPrintToChatAll("%t","JoinsArenaNoStats",playername,g_sArenaName[arena_index]); + + + } else { + if (foe && IsFakeClient(foe)) + { + new Handle:cvar = FindConVar("tf_bot_quota"); + new quota = GetConVarInt(cvar); + ServerCommand("tf_bot_quota %d", quota - 1); + } + + g_iArenaStatus[arena_index] = AS_IDLE; + return; + } + } + } + + else + { + if (player_slot==SLOT_ONE || player_slot==SLOT_TWO) + { + new foe_slot = player_slot==SLOT_ONE ? SLOT_TWO : SLOT_ONE; + new foe = g_iArenaQueue[arena_index][foe_slot]; + + if(g_bArenaBBall[arena_index]) + { + if(IsValidEdict(g_iBBallIntel[arena_index]) && g_iBBallIntel[arena_index] > 0) + { + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + RemoveEdict(g_iBBallIntel[arena_index]); + g_iBBallIntel[arena_index] = -1; + } + + RemoveClientParticle(client); + g_bPlayerHasIntel[client] = false; + + if(foe) + { + RemoveClientParticle(foe); + g_bPlayerHasIntel[foe] = false; + } + } + + if (g_iArenaStatus[arena_index] >= AS_FIGHT && g_iArenaStatus[arena_index] < AS_REPORTED && calcstats && !g_bNoStats && foe) + { + new String:foe_name[MAX_NAME_LENGTH]; + new String:player_name[MAX_NAME_LENGTH]; + GetClientName(foe,foe_name, sizeof(foe_name)); + GetClientName(client,player_name, sizeof(player_name)); + + g_iArenaStatus[arena_index] = AS_REPORTED; + + if(g_iArenaScore[arena_index][foe_slot] > g_iArenaScore[arena_index][player_slot]) + { + if(g_iArenaScore[arena_index][foe_slot] >= g_iArenaEarlyLeave[arena_index]) + { + CalcELO(foe,client); + CPrintToChatAll("%t","XdefeatsYearly", foe_name, g_iArenaScore[arena_index][foe_slot], player_name, g_iArenaScore[arena_index][player_slot], g_sArenaName[arena_index]); + } + } + } + + if (g_iArenaQueue[arena_index][SLOT_TWO+1]) + { + new next_client = g_iArenaQueue[arena_index][SLOT_TWO+1]; + g_iArenaQueue[arena_index][SLOT_TWO+1] = 0; + g_iArenaQueue[arena_index][player_slot] = next_client; + g_iPlayerSlot[next_client] = player_slot; + after_leaver_slot = SLOT_TWO + 2; + new String:playername[MAX_NAME_LENGTH]; + CreateTimer(2.0,Timer_StartDuel,arena_index); + GetClientName(next_client,playername,sizeof(playername)); + + if (!g_bNoStats && !g_bNoDisplayRating) + CPrintToChatAll("%t","JoinsArena",playername,g_iPlayerRating[next_client],g_sArenaName[arena_index]); + else + CPrintToChatAll("%t","JoinsArenaNoStats",playername,g_sArenaName[arena_index]); + + + } else { + if (foe && IsFakeClient(foe)) + { + new Handle:cvar = FindConVar("tf_bot_quota"); + new quota = GetConVarInt(cvar); + ServerCommand("tf_bot_quota %d", quota - 1); + } + + g_iArenaStatus[arena_index] = AS_IDLE; + return; + } + } + } + if (g_iArenaQueue[arena_index][after_leaver_slot]) + { + while (g_iArenaQueue[arena_index][after_leaver_slot]) + { + g_iArenaQueue[arena_index][after_leaver_slot-1] = g_iArenaQueue[arena_index][after_leaver_slot]; + g_iPlayerSlot[g_iArenaQueue[arena_index][after_leaver_slot]] -= 1; + after_leaver_slot++; + } + g_iArenaQueue[arena_index][after_leaver_slot-1] = 0; + } +} + +AddInQueue(client,arena_index, bool:showmsg = true, playerPrefTeam = 0) +{ + if(!IsValidClient(client)) + return; + + if (g_iPlayerArena[client]) + { + PrintToChatAll("client <%N> is already on arena %d",client,arena_index); + } + + //Set the player to the preffered team if there is room, otherwise just add him in wherever there is a slot + new player_slot = SLOT_ONE; + if(playerPrefTeam == TEAM_RED) + { + if(!g_iArenaQueue[arena_index][SLOT_ONE]) + player_slot = SLOT_ONE; + else if(g_bFourPersonArena[arena_index] && !g_iArenaQueue[arena_index][SLOT_THREE]) + player_slot = SLOT_THREE; + else + { + while (g_iArenaQueue[arena_index][player_slot]) + player_slot++; + } + } + else if(playerPrefTeam == TEAM_BLU) + { + if(!g_iArenaQueue[arena_index][SLOT_TWO]) + player_slot = SLOT_TWO; + else if(g_bFourPersonArena[arena_index] && !g_iArenaQueue[arena_index][SLOT_FOUR]) + player_slot = SLOT_FOUR; + else + { + while (g_iArenaQueue[arena_index][player_slot]) + player_slot++; + } + } + else + { + while (g_iArenaQueue[arena_index][player_slot]) + player_slot++; + } + + g_iPlayerArena[client] = arena_index; + g_iPlayerSlot[client] = player_slot; + g_iArenaQueue[arena_index][player_slot] = client; + + SetPlayerToAllowedClass(client, arena_index); + + if (showmsg) + { + CPrintToChat(client,"%t","ChoseArena",g_sArenaName[arena_index]); + } + if(g_bFourPersonArena[arena_index]) + { + if (player_slot <= SLOT_FOUR) + { + decl String:name[MAX_NAME_LENGTH]; + GetClientName(client,name,sizeof(name)); + + if(!g_bNoStats && !g_bNoDisplayRating) + CPrintToChatAll("%t","JoinsArena",name,g_iPlayerRating[client],g_sArenaName[arena_index]); + else + CPrintToChatAll("%t","JoinsArenaNoStats",name,g_sArenaName[arena_index]); + + if (g_iArenaQueue[arena_index][SLOT_ONE] && g_iArenaQueue[arena_index][SLOT_TWO] && g_iArenaQueue[arena_index][SLOT_THREE] && g_iArenaQueue[arena_index][SLOT_FOUR]) + { + CreateTimer(1.5,Timer_StartDuel,arena_index); + } + else + CreateTimer(0.1,Timer_ResetPlayer,GetClientUserId(client)); + } else { + if (GetClientTeam(client) != TEAM_SPEC) + ChangeClientTeam(client, TEAM_SPEC); + if (player_slot == SLOT_FOUR + 1) + CPrintToChat(client,"%t","NextInLine"); + else + CPrintToChat(client,"%t","InLine",player_slot-SLOT_FOUR); + } + } + else + { + if (player_slot <= SLOT_TWO) + { + decl String:name[MAX_NAME_LENGTH]; + GetClientName(client,name,sizeof(name)); + + if(!g_bNoStats && !g_bNoDisplayRating) + CPrintToChatAll("%t","JoinsArena",name,g_iPlayerRating[client],g_sArenaName[arena_index]); + else + CPrintToChatAll("%t","JoinsArenaNoStats",name,g_sArenaName[arena_index]); + + if (g_iArenaQueue[arena_index][SLOT_ONE] && g_iArenaQueue[arena_index][SLOT_TWO]) + { + CreateTimer(1.5,Timer_StartDuel,arena_index); + } else + CreateTimer(0.1,Timer_ResetPlayer,GetClientUserId(client)); + } else { + if (GetClientTeam(client) != TEAM_SPEC) + ChangeClientTeam(client, TEAM_SPEC); + if (player_slot == SLOT_TWO + 1) + CPrintToChat(client,"%t","NextInLine"); + else + CPrintToChat(client,"%t","InLine",player_slot-SLOT_TWO); + } + } + + return; +} + +// ====[ STATS ]==================================================== +CalcELO(winner, loser) +{ + if (IsFakeClient(winner) || IsFakeClient(loser) || g_bNoStats) + return; + + // ELO formula + new Float:El = 1/(Pow(10.0, float((g_iPlayerRating[winner]-g_iPlayerRating[loser]))/400)+1); + new k = (g_iPlayerRating[winner]>=2400) ? 10 : 15; + new winnerscore = RoundFloat(k*El); + g_iPlayerRating[winner] += winnerscore; + k = (g_iPlayerRating[loser]>=2400) ? 10 : 15; + new loserscore = RoundFloat(k*El); + g_iPlayerRating[loser] -= loserscore; + + new arena_index = g_iPlayerArena[winner]; + new time = GetTime(); + decl String:query[512], String:sCleanArenaname[128], String:sCleanMapName[128]; + + SQL_EscapeString(db, g_sArenaName[g_iPlayerArena[winner]], sCleanArenaname, sizeof(sCleanArenaname)); + SQL_EscapeString(db, g_sMapName, sCleanMapName, sizeof(sCleanMapName)); + + if(IsValidClient(winner) && !g_bNoDisplayRating) + CPrintToChat(winner, "%t","GainedPoints",winnerscore); + + if(IsValidClient(loser) && !g_bNoDisplayRating) + CPrintToChat(loser, "%t","LostPoints",loserscore); + + //This is necessary for when a player leaves a 2v2 arena that is almost done. + //I don't want to penalize the player that doesn't leave, so only the winners/leavers ELO will be effected. + new winner_team_slot = (g_iPlayerSlot[winner] > 2) ? (g_iPlayerSlot[winner] - 2) : g_iPlayerSlot[winner]; + new loser_team_slot = (g_iPlayerSlot[loser] > 2) ? (g_iPlayerSlot[loser] - 2) : g_iPlayerSlot[loser]; + + // DB entry for this specific duel. + if(g_bUseSQLite) + { + Format(query, sizeof(query), "INSERT INTO tf2_facti13_mge_first.mgemod_duels VALUES ('%s', '%s', %i, %i, %i, %i, '%s', '%s')", + g_sPlayerSteamID[winner], g_sPlayerSteamID[loser], g_iArenaScore[arena_index][winner_team_slot], g_iArenaScore[arena_index][loser_team_slot], g_iArenaFraglimit[arena_index], time, g_sMapName, g_sArenaName[arena_index]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } else { + Format(query, sizeof(query), "INSERT INTO tf2_facti13_mge_first.mgemod_duels (winner, loser, winnerscore, loserscore, winlimit, gametime, mapname, arenaname) VALUES ('%s', '%s', %i, %i, %i, %i, '%s', '%s')", + g_sPlayerSteamID[winner], g_sPlayerSteamID[loser], g_iArenaScore[arena_index][winner_team_slot], g_iArenaScore[arena_index][loser_team_slot], g_iArenaFraglimit[arena_index], time, g_sMapName, g_sArenaName[arena_index]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } + + //winner's stats + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET rating=%i,wins=wins+1,lastplayed=%i WHERE steamid like '%s'", + g_iPlayerRating[winner], time, g_sPlayerSteamID[winner]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + + //loser's stats + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET rating=%i,losses=losses+1,lastplayed=%i WHERE steamid like '%s'", + g_iPlayerRating[loser], time, g_sPlayerSteamID[loser]); + SQL_TQuery(db, SQLErrorCheckCallback, query); +} + +CalcELO2(winner, winner2, loser, loser2) +{ + if (IsFakeClient(winner) || IsFakeClient(loser) || g_bNoStats || IsFakeClient(loser2) || IsFakeClient(winner2)) + return; + + + + new Float:Losers_ELO = float((g_iPlayerRating[loser] + g_iPlayerRating[loser2]) / 2); + new Float:Winners_ELO = float((g_iPlayerRating[winner] + g_iPlayerRating[winner2]) / 2); + // ELO formula + new Float:El = 1/(Pow(10.0, (Winners_ELO-Losers_ELO)/400)+1); + new k = (Winners_ELO>=2400) ? 10 : 15; + new winnerscore = RoundFloat(k*El); + g_iPlayerRating[winner] += winnerscore; + g_iPlayerRating[winner2] += winnerscore; + k = (Losers_ELO>=2400) ? 10 : 15; + new loserscore = RoundFloat(k*El); + g_iPlayerRating[loser] -= loserscore; + g_iPlayerRating[loser2] -= loserscore; + + new winner_team_slot = (g_iPlayerSlot[winner] > 2) ? (g_iPlayerSlot[winner] - 2) : g_iPlayerSlot[winner]; + new loser_team_slot = (g_iPlayerSlot[loser] > 2) ? (g_iPlayerSlot[loser] - 2) : g_iPlayerSlot[loser]; + + new arena_index = g_iPlayerArena[winner]; + new time = GetTime(); + decl String:query[512], String:sCleanArenaname[128], String:sCleanMapName[128]; + + SQL_EscapeString(db, g_sArenaName[g_iPlayerArena[winner]], sCleanArenaname, sizeof(sCleanArenaname)); + SQL_EscapeString(db, g_sMapName, sCleanMapName, sizeof(sCleanMapName)); + + if(IsValidClient(winner) && !g_bNoDisplayRating) + CPrintToChat(winner, "%t","GainedPoints",winnerscore); + + if(IsValidClient(winner2) && !g_bNoDisplayRating) + CPrintToChat(winner2, "%t","GainedPoints",winnerscore); + + if(IsValidClient(loser) && !g_bNoDisplayRating) + CPrintToChat(loser, "%t","LostPoints",loserscore); + + if(IsValidClient(loser2) && !g_bNoDisplayRating) + CPrintToChat(loser2, "%t","LostPoints",loserscore); + + + // DB entry for this specific duel. + if(g_bUseSQLite) + { + Format(query, sizeof(query), "INSERT INTO tf2_facti13_mge_first.mgemod_duels_2v2 VALUES ('%s', '%s', '%s', '%s', %i, %i, %i, %i, '%s', '%s')", + g_sPlayerSteamID[winner], g_sPlayerSteamID[winner2], g_sPlayerSteamID[loser], g_sPlayerSteamID[loser2], g_iArenaScore[arena_index][winner_team_slot], g_iArenaScore[arena_index][loser_team_slot], g_iArenaFraglimit[arena_index], time, g_sMapName, g_sArenaName[arena_index]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } else { + Format(query, sizeof(query), "INSERT INTO tf2_facti13_mge_first.mgemod_duels_2v2 (winner, winner2, loser, loser2, winnerscore, loserscore, winlimit, gametime, mapname, arenaname) VALUES ('%s', '%s', '%s', '%s', %i, %i, %i, %i, '%s', '%s')", + g_sPlayerSteamID[winner], g_sPlayerSteamID[winner2], g_sPlayerSteamID[loser], g_sPlayerSteamID[loser2], g_iArenaScore[arena_index][winner_team_slot], g_iArenaScore[arena_index][loser_team_slot], g_iArenaFraglimit[arena_index], time, g_sMapName, g_sArenaName[arena_index]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } + + //winner's stats + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET rating=%i,wins=wins+1,lastplayed=%i WHERE steamid like '%s'", + g_iPlayerRating[winner], time, g_sPlayerSteamID[winner]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + + //winner's teammate stats + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET rating=%i,wins=wins+1,lastplayed=%i WHERE steamid like '%s'", + g_iPlayerRating[winner2], time, g_sPlayerSteamID[winner2]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + + //loser's stats + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET rating=%i,losses=losses+1,lastplayed=%i WHERE steamid like '%s'", + g_iPlayerRating[loser], time, g_sPlayerSteamID[loser]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + + //loser's teammate stats + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET rating=%i,losses=losses+1,lastplayed=%i WHERE steamid like '%s'", + g_iPlayerRating[loser2], time, g_sPlayerSteamID[loser2]); + SQL_TQuery(db, SQLErrorCheckCallback, query); +} +// ====[ UTIL ]==================================================== +LoadSpawnPoints() +{ + new String:txtfile[256]; + BuildPath(Path_SM, txtfile, sizeof(txtfile), g_spawnFile); + + new String:spawn[64]; + GetCurrentMap(g_sMapName,sizeof(g_sMapName)); + + new Handle:kv = CreateKeyValues("SpawnConfig"); + + new String:spawnCo[6][16]; + new String:kvmap[32]; + new count; + new i; + g_iArenaCount = 0; + + for(i=0; i<=MAXARENAS; i++) + g_iArenaSpawns[i] = 0; + + if (FileToKeyValues(kv, txtfile)) + { + if (KvGotoFirstSubKey(kv)) + { + do + { + KvGetSectionName(kv, kvmap, 64); + if (StrEqual(g_sMapName, kvmap, false)) + { + if (KvGotoFirstSubKey(kv)) + { + do + { + g_iArenaCount++; + KvGetSectionName(kv, g_sArenaName[g_iArenaCount], 64); + new id; + if (KvGetNameSymbol(kv, "1", id)) + { + new String:intstr[4]; + new String:intstr2[4]; + do + { + g_iArenaSpawns[g_iArenaCount]++; + IntToString(g_iArenaSpawns[g_iArenaCount], intstr, sizeof(intstr)); + IntToString(g_iArenaSpawns[g_iArenaCount]+1, intstr2, sizeof(intstr2)); + KvGetString(kv, intstr, spawn, sizeof(spawn)); + + new n_fix = 1; + + count = ExplodeString(spawn, " ", spawnCo, 6+n_fix, 16); + + //PrintToServer("%i %s | %s %f", count, spawn, spawnCo[0+n_fix], StringToFloat(spawnCo[0+n_fix])); + if (count==6+n_fix) + { + for (i=0; i<3; i++) + { + g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][i] = StringToFloat(spawnCo[i+n_fix]); + //PrintToServer("%s | %f", spawnCo[i+n_fix], g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][i]); + } + //PrintToServer("%f %f %f", g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][0],g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][1],g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][2]); + for (i=3; i<6; i++) + { + g_fArenaSpawnAngles[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][i-3] = StringToFloat(spawnCo[i+n_fix]); + } + } else if(count==4+n_fix) { + for (i=0; i<3; i++) + { + g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][i] = StringToFloat(spawnCo[i+n_fix]); + //PrintToServer("%s | %f", spawnCo[i+n_fix], g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][i]); + } + //PrintToServer("%f %f %f", g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][0],g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][1],g_fArenaSpawnOrigin[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][2]); + g_fArenaSpawnAngles[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][0] = 0.0; + g_fArenaSpawnAngles[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][1] = StringToFloat(spawnCo[3+n_fix]); + g_fArenaSpawnAngles[g_iArenaCount][g_iArenaSpawns[g_iArenaCount]][2] = 0.0; + } else { + SetFailState("Error in cfg file. Wrong number of parametrs (%d) on spawn <%i> in arena <%s>",count,g_iArenaSpawns[g_iArenaCount],g_sArenaName[g_iArenaCount]); + } + } while (KvGetNameSymbol(kv, intstr2, id)); + LogMessage("Loaded %d spawns on arena %s.",g_iArenaSpawns[g_iArenaCount], g_sArenaName[g_iArenaCount]); + } else { + LogError("Could not load spawns on arena %s.", g_sArenaName[g_iArenaCount]); + } + + //optional parametrs + g_iArenaFraglimit[g_iArenaCount] = KvGetNum(kv, "fraglimit", g_iDefaultFragLimit); + g_iArenaMinRating[g_iArenaCount] = KvGetNum(kv, "minrating", -1); + g_iArenaMaxRating[g_iArenaCount] = KvGetNum(kv, "maxrating", -1); + g_bArenaMidair[g_iArenaCount] = KvGetNum(kv, "midair", 0) ? true : false ; + g_iArenaCdTime[g_iArenaCount] = KvGetNum(kv, "cdtime", DEFAULT_CDTIME); + g_bArenaMGE[g_iArenaCount] = KvGetNum(kv, "mge", 0) ? true : false ; + g_fArenaHPRatio[g_iArenaCount] = KvGetFloat(kv, "hpratio", 1.5); + g_bArenaEndif[g_iArenaCount] = KvGetNum(kv, "endif", 0) ? true : false ; + g_bArenaBBall[g_iArenaCount] = KvGetNum(kv, "bball", 0) ? true : false ; + g_bVisibleHoops[g_iArenaCount] = KvGetNum(kv, "vishoop", 0) ? true : false ; + g_iArenaEarlyLeave[g_iArenaCount] = KvGetNum(kv, "earlyleave", 0); + g_bArenaInfAmmo[g_iArenaCount] = KvGetNum(kv, "infammo", 1) ? true : false ; + g_bArenaShowHPToPlayers[g_iArenaCount] = KvGetNum(kv, "showhp", 1) ? true : false ; + g_fArenaMinSpawnDist[g_iArenaCount] = KvGetFloat(kv, "mindist", 100.0); + g_bFourPersonArena[g_iArenaCount] = KvGetNum(kv, "4player", 0) ? true : false; + g_fArenaRespawnTime[g_iArenaCount] = KvGetFloat(kv, "respawntime", 0.1); + g_bArenaAmmomod[g_iArenaCount] = KvGetNum(kv, "ammomod", 0) ? true : false; + g_bArenaUltiduo[g_iArenaCount] = KvGetNum(kv, "ultiduo", 0) ? true : false; + g_bArenaKoth[g_iArenaCount] = KvGetNum(kv, "koth", 0) ? true : false; + g_bArenaTurris[g_iArenaCount] = KvGetNum(kv, "turris", 0) ? true : false; + g_iDefaultCapTime[g_iArenaCount] = KvGetNum(kv, "Timer", 180); + //parsing allowed classes for current arena + decl String:sAllowedClasses[128]; + KvGetString(kv, "classes", sAllowedClasses, sizeof(sAllowedClasses)); + LogMessage("%s classes: <%s>", g_sArenaName[g_iArenaCount], sAllowedClasses); + ParseAllowedClasses("sAllowedClasses",g_tfctArenaAllowedClasses[g_iArenaCount]); + } while (KvGotoNextKey(kv)); + } + break; + } + } while (KvGotoNextKey(kv)); + if (g_iArenaCount) + { + LogMessage("Loaded %d arenas. MGEMod enabled.",g_iArenaCount); + CloseHandle(kv); + return true; + } else { + CloseHandle(kv); + return false; + } + } else { + LogError("Error in cfg file."); + return false; + } + } else { + LogError("Error. Can't find cfg file"); + return false; + } +} + +ResetPlayer(client) +{ + new arena_index = g_iPlayerArena[client]; + new player_slot = g_iPlayerSlot[client]; + + + if (!arena_index || !player_slot) + { + return 0; + } + + g_iPlayerSpecTarget[client] = 0; + + if(player_slot == SLOT_ONE || player_slot == SLOT_THREE) + ChangeClientTeam(client, TEAM_RED); + else + ChangeClientTeam(client, TEAM_BLU); + + //This logic doesn't work with 2v2's + //new team = GetClientTeam(client); + //if (player_slot - team != SLOT_ONE - TEAM_RED) + // ChangeClientTeam(client, player_slot + TEAM_RED - SLOT_ONE); + + + new TFClassType:class; + class = g_tfctPlayerClass[client] ? g_tfctPlayerClass[client] : TFClass_Soldier; + + if (!IsPlayerAlive(client) || g_bArenaBBall[arena_index]) + { + if (class != TF2_GetPlayerClass(client)) + TF2_SetPlayerClass(client,class); + + TF2_RespawnPlayer(client); + } else { + TF2_RegeneratePlayer(client); + ExtinguishEntity(client); + } + + g_iPlayerMaxHP[client] = GetEntProp(client, Prop_Data, "m_iMaxHealth"); + + if (g_bArenaMidair[arena_index]) + g_iPlayerHP[client] = g_iMidairHP; + else + g_iPlayerHP[client] = g_iPlayerHandicap[client] ? g_iPlayerHandicap[client] : RoundToNearest(float(g_iPlayerMaxHP[client])*g_fArenaHPRatio[arena_index]); + + if (g_bArenaMGE[arena_index] || g_bArenaBBall[arena_index]) + SetEntProp(client, Prop_Data, "m_iHealth", g_iPlayerHandicap[client] ? g_iPlayerHandicap[client] : RoundToNearest(float(g_iPlayerMaxHP[client])*g_fArenaHPRatio[arena_index])); + + ShowPlayerHud(client); + ResetClientAmmoCounts(client); + CreateTimer(0.1,Timer_Tele,GetClientUserId(client)); + + return 1; +} + +ResetKiller(killer,arena_index) +{ + TF2_RegeneratePlayer(killer); + new reset_hp = g_iPlayerHandicap[killer] ? g_iPlayerHandicap[killer] : RoundToNearest(float(g_iPlayerMaxHP[killer])*g_fArenaHPRatio[arena_index]); + g_iPlayerHP[killer] = reset_hp; + SetEntProp(killer, Prop_Data, "m_iHealth", reset_hp); +} + + +ResetClientAmmoCounts(client) +{ + // Crutch. + g_iPlayerClip[client][SLOT_ONE] = -1; + g_iPlayerClip[client][SLOT_TWO] = -1; + + // Check how much ammo each gun can hold in its clip and store it in a global variable so it can be set to that amount later. + if(IsValidEntity(GetPlayerWeaponSlot(client, 0))) + g_iPlayerClip[client][SLOT_ONE] = GetEntProp(GetPlayerWeaponSlot(client, 0), Prop_Data, "m_iClip1"); + if(IsValidEntity(GetPlayerWeaponSlot(client, 1))) + g_iPlayerClip[client][SLOT_TWO] = GetEntProp(GetPlayerWeaponSlot(client, 1), Prop_Data, "m_iClip1"); +} + +ResetIntel(arena_index, any:client = -1) +{ + if(g_bArenaBBall[arena_index]) + { + if(IsValidEdict(g_iBBallIntel[arena_index]) && g_iBBallIntel[arena_index] > 0) + { + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + RemoveEdict(g_iBBallIntel[arena_index]); + g_iBBallIntel[arena_index] = -1; + } + + if (g_iBBallIntel[arena_index] == -1) + g_iBBallIntel[arena_index] = CreateEntityByName("item_ammopack_small"); + else + LogError("[%s] Intel [%i] already exists.", g_sArenaName[arena_index], g_iBBallIntel[arena_index]); + + + new Float:intel_loc[3]; + + if(client != -1) + { + new client_slot = g_iPlayerSlot[client]; + g_bPlayerHasIntel[client] = false; + + if(client_slot == SLOT_ONE || client_slot == SLOT_THREE) + { + intel_loc[0] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-3][0]; + intel_loc[1] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-3][1]; + intel_loc[2] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-3][2]; + } else if(client_slot == SLOT_TWO || client_slot == SLOT_FOUR) { + intel_loc[0] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-2][0]; + intel_loc[1] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-2][1]; + intel_loc[2] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-2][2]; + } + } else { + intel_loc[0] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-4][0]; + intel_loc[1] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-4][1]; + intel_loc[2] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-4][2]; + } + //Should fix the intel being an ammopack + DispatchKeyValue(g_iBBallIntel[arena_index], "powerup_model", MODEL_BRIEFCASE); + DispatchSpawn(g_iBBallIntel[arena_index]); + TeleportEntity(g_iBBallIntel[arena_index], intel_loc, NULL_VECTOR, NULL_VECTOR); + SetEntProp(g_iBBallIntel[arena_index], Prop_Send, "m_iTeamNum", 1, 4); + SetEntPropFloat(g_iBBallIntel[arena_index], Prop_Send, "m_flModelScale", 1.15); + //Doesn't work anymore + //SetEntityModel(g_iBBallIntel[arena_index], MODEL_BRIEFCASE); + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + SDKHook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + AcceptEntityInput(g_iBBallIntel[arena_index], "Enable"); + } +} + +SetPlayerToAllowedClass(client, arena_index) +{// If a player's class isn't allowed, set it to one that is. + if (g_tfctPlayerClass[client]==TFClassType + || + !g_tfctArenaAllowedClasses + [arena_index] + [g_tfctPlayerClass[client]]) + { + for(new i=1;i<=9;i++) + { + if (g_tfctArenaAllowedClasses[arena_index][i]) + { + if(g_bArenaUltiduo[arena_index] && g_bFourPersonArena[arena_index] && g_iPlayerSlot[client] > SLOT_TWO) + { + new client_teammate; + client_teammate = getTeammate(client, g_iPlayerSlot[client], arena_index); + if(TFClassType:i == g_tfctPlayerClass[client_teammate]) + { + //Tell the player what he did wrong + CPrintToChat(client,"Your team already has that class!"); + //Change him classes and set his class to the only one available + if(g_tfctPlayerClass[client_teammate] == TF2_GetClass("soldier")) + { + g_tfctPlayerClass[client] = TF2_GetClass("medic"); + } + else + { + g_tfctPlayerClass[client] = TF2_GetClass("soldier"); + } + } + } + else + g_tfctPlayerClass[client] = TFClassType:i; + + break; + } + } + } +} + +ParseAllowedClasses(const String:sList[],output[TFClassType]) +{ + new count, String:a_class[9][9]; + + if (strlen(sList)>0) + { + count = ExplodeString(sList, " ", a_class, 9, 9); + } else { + decl String:sDefList[128]; + GetConVarString(gcvar_allowedClasses,sDefList,sizeof(sDefList)); + count = ExplodeString(sDefList, " ", a_class, 9, 9); + } + + for (new i=1;i<=9;i++) + output[i] = 0; + + for (new i=0;i 2) + Format(menu_item,sizeof(menu_item),"%s (2)(%d)", g_sArenaName[i], (numslots - 2)); + else if(numslots > 0) + Format(menu_item,sizeof(menu_item),"%s (%d)", g_sArenaName[i], numslots); + else + Format(menu_item,sizeof(menu_item),"%s", g_sArenaName[i]); + + IntToString(i,si,sizeof(si)); + AddMenuItem(menu, si, menu_item); + } + + Format(menu_item,sizeof(menu_item),"%T","MenuRemove",client); + AddMenuItem(menu, "1000", menu_item); + + SetMenuExitButton(menu, true); + DisplayMenu(menu, client, 0); + + new String:report[128]; + + //listing players + if (!listplayers) + return; + + for (new i=1;i<=g_iArenaCount;i++) + { + new red_f1 = g_iArenaQueue[i][SLOT_ONE]; + new blu_f1 = g_iArenaQueue[i][SLOT_TWO]; + if (red_f1>0 || blu_f1>0) + { + Format(report,sizeof(report),"\x05%s:",g_sArenaName[i]); + + if (!g_bNoDisplayRating) + { + if (red_f1>0 && blu_f1>0) + Format(report,sizeof(report),"%s \x04%N \x03(%d) \x05vs \x04%N (%d) \x05",report,red_f1,g_iPlayerRating[red_f1],blu_f1,g_iPlayerRating[blu_f1]); + else if (red_f1>0) + Format(report,sizeof(report),"%s \x04%N (%d)\x05",report,red_f1,g_iPlayerRating[red_f1]); + else if (blu_f1>0) + Format(report,sizeof(report),"%s \x04%N (%d)\x05",report,blu_f1,g_iPlayerRating[blu_f1]); + } else { + if (red_f1>0 && blu_f1>0) + Format(report,sizeof(report),"%s \x04%N \x05vs \x04%N \x05",report,red_f1,blu_f1); + else if (red_f1>0) + Format(report,sizeof(report),"%s \x04%N \x05",report,red_f1); + else if (blu_f1>0) + Format(report,sizeof(report),"%s \x04%N \x05",report,blu_f1); + } + + if (g_iArenaQueue[i][SLOT_TWO + 1]) + { + Format(report,sizeof(report),"%s Waiting: ",report); + new j = SLOT_TWO + 1; + while (g_iArenaQueue[i][j + 1]) + { + Format(report,sizeof(report),"%s\x04%N \x05, ",report,g_iArenaQueue[i][j]); + j++; + } + Format(report,sizeof(report),"%s\x04%N",report,g_iArenaQueue[i][j]); + } + PrintToChat(client,"%s",report); + } + } +} + +public Menu_Main(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_Select: + { + new client = param1; + if (!client) return; + new String:capt[32]; + new String:sanum[32]; + + GetMenuItem(menu, param2, sanum,sizeof(sanum), _,capt, sizeof(capt)); + new arena_index = StringToInt(sanum); + + if (arena_index>0 && arena_index <=g_iArenaCount) + { + if (arena_index == g_iPlayerArena[client]) + { + //show warn msg + ShowMainMenu(client,false); + return; + } + + //checking rating + new playerrating = g_iPlayerRating[client]; + new minrating = g_iArenaMinRating[arena_index]; + new maxrating = g_iArenaMaxRating[arena_index]; + + if (minrating>0 && playerrating < minrating) + { + CPrintToChat(client,"%t","LowRating",playerrating,minrating); + ShowMainMenu(client,false); + return; + } else if (maxrating>0 && playerrating > maxrating){ + CPrintToChat(client,"%t","HighRating",playerrating,maxrating); + ShowMainMenu(client,false); + return; + } + + if (g_iPlayerArena[client]) + RemoveFromQueue(client, true); + + AddInQueue(client,arena_index); + + } else { + RemoveFromQueue(client, true); + } + } + case MenuAction_Cancel: + { + } + case MenuAction_End: + { + CloseHandle(menu); + } + } +} + +public SwapMenuHandler(Handle:menu, MenuAction:action, param1, param2) +{ + /* If an option was selected, tell the client about the item. */ + if (action == MenuAction_Select) + { + if(param2 == 0) + { + new client = param1; + if (!client) + return; + + new arena_index = g_iPlayerArena[client]; + new client_teammate = getTeammate(client, g_iPlayerSlot[client], arena_index); + swapClasses(client, client_teammate); + + } + else + CloseHandle(menu); + } + /* If the menu was cancelled, print a message to the server about it. */ + else if (action == MenuAction_Cancel) + { + PrintToServer("Client %d's menu was cancelled. Reason: %d", param1, param2); + } + /* If the menu has ended, destroy it */ + else if (action == MenuAction_End) + { + CloseHandle(menu); + } +} + +ShowTop5Menu(client, String:name[][], rating[]) +{ + if (client<=0) + return; + + decl String:title[128]; + decl String:temp[128]; + //decl String:menu_item[128]; + + new Handle:menu = CreateMenu(Menu_Top5); + + Format(title, sizeof(title), "ELO Rankings \n", client); + + new String:si[4]; + + if(!g_bNoDisplayRating) + { + for (new i=0;i<5;i++) + { + new pos = (i + 1) + (g_iELOMenuPage[client] * 5); + IntToString((i + 1), si, sizeof(si)); + //changed menu_item to title + Format(temp, sizeof(temp), "%i %s (%i) \n", pos, name[i], rating[i]); + StrCat(title, sizeof(title), temp); + //AddMenuItem(menu, si, menu_item); + } + } else { + for (new i=0;i<5;i++) + { + new pos = (i + 1) + (g_iELOMenuPage[client] * 5); + IntToString(i, si, sizeof(si)); + //changed menu_item to title + Format(temp, sizeof(temp), "%i %s (%i) \n", pos, name[i], rating[i]); + StrCat(title, sizeof(title), temp); + //AddMenuItem(menu, si, menu_item); + } + } + SetMenuTitle(menu, title); + + AddMenuItem(menu, "1", "Next"); + if(g_iELOMenuPage[client]!=0) + { + AddMenuItem(menu, "2", "back"); + } + + SetMenuExitButton(menu, true); + DisplayMenu(menu, client, 0); +} + +public Menu_Top5(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_Select: + { + new String:info[32]; + GetMenuItem(menu, param2, info, sizeof(info)); + //If he selected next, query the next menu + if(param2 == 0) + { + g_iELOMenuPage[param1]++; + decl String:query[256]; + Format(query, sizeof(query), "SELECT rating,name FROM tf2_facti13_mge_first.mgemod_stats ORDER BY rating DESC LIMIT %i, 5", g_iELOMenuPage[param1] * 5); + //new data[] = {param1, param2+5, false}; + SQL_TQuery(db, T_SQL_Top5, query, param1); + } + //If the player selected back show the previous menu + if(param2 == 1) + { + g_iELOMenuPage[param1]--; + if(g_iELOMenuPage[param1]==0) + { + decl String:query[256]; + Format(query, sizeof(query), "SELECT rating,name FROM tf2_facti13_mge_first.mgemod_stats ORDER BY rating DESC LIMIT 5"); + //new data[] = {param1, param2-5, true}; + SQL_TQuery(db, T_SQL_Top5, query, param1); + } + else + { + decl String:query[256]; + Format(query, sizeof(query), "SELECT rating,name FROM tf2_facti13_mge_first.mgemod_stats ORDER BY rating DESC LIMIT %i, 5", g_iELOMenuPage[param1] * 5); + //new data[] = {param1, param2-5, false}; + SQL_TQuery(db, T_SQL_Top5, query, param1); + } + } + } + case MenuAction_Cancel: + { + } + case MenuAction_End: + { + CloseHandle(menu); + } + } +} +// ====[ ENDIF ]==================================================== +public Action:BoostVectors(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + new Float:vecClient[3]; + new Float:vecBoost[3]; + + GetEntPropVector(client, Prop_Data, "m_vecVelocity", vecClient); + + vecBoost[0] = vecClient[0] * g_fRocketForceX; + vecBoost[1] = vecClient[1] * g_fRocketForceY; + if(vecClient[2] > 0) + { + vecBoost[2] = vecClient[2] * g_fRocketForceZ; + } else { + vecBoost[2] = vecClient[2]; + } + + TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vecBoost); +} + + + +// ====[ CVARS ]==================================================== +public handler_ConVarChange(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + if (convar == gcvar_blockFallDamage) { + StringToInt(newValue) ? (g_bBlockFallDamage = true) : (g_bBlockFallDamage = false); + if (g_bBlockFallDamage) + AddNormalSoundHook(sound_hook); + else + RemoveNormalSoundHook(sound_hook); + } + else if (convar == gcvar_fragLimit) + g_iDefaultFragLimit = StringToInt(newValue); + else if (convar == gcvar_airshotHeight) + g_iAirshotHeight = StringToInt(newValue); + else if (convar == gcvar_midairHP) + g_iMidairHP = StringToInt(newValue); + else if (convar == gcvar_RocketForceX) + g_fRocketForceX = StringToFloat(newValue); + else if (convar == gcvar_RocketForceY) + g_fRocketForceY = StringToFloat(newValue); + else if (convar == gcvar_RocketForceZ) + g_fRocketForceZ = StringToFloat(newValue); + else if (convar == gcvar_autoCvar) + StringToInt(newValue) ? (g_bAutoCvar = true) : (g_bAutoCvar = false); + else if (convar == gcvar_bballParticle_red) + strcopy(g_sBBallParticleRed, sizeof(g_sBBallParticleRed), newValue); + else if (convar == gcvar_bballParticle_blue) + strcopy(g_sBBallParticleBlue, sizeof(g_sBBallParticleBlue), newValue); + else if (convar == gcvar_noDisplayRating) + StringToInt(newValue) ? (g_bNoDisplayRating = true) : (g_bNoDisplayRating = false); + else if (convar == gcvar_stats) + g_bNoStats = (GetConVarBool(gcvar_stats)) ? false : true; + else if (convar == gcvar_reconnectInterval) + g_iReconnectInterval = StringToInt(newValue); + else if (convar == gcvar_dbConfig) + strcopy(g_sDBConfig, sizeof(g_sDBConfig), newValue); + else if (convar == gcvar_spawnFile) + { + strcopy(g_spawnFile, sizeof(g_spawnFile), newValue); + LoadSpawnPoints(); + } +} + +// ====[ COMMANDS ]==================================================== +public Action:Command_Menu(client, args) +{ //handle commands "!ammomod" "!add" and such //building queue's menu and listing arena's + new playerPrefTeam = 0; + + if (!IsValidClient(client)) + return Plugin_Continue; + + new String:sArg[32]; + if(GetCmdArg(1, sArg, sizeof(sArg)) > 0) + { + //If they want to add to a color + new String:cArg[32]; + if(GetCmdArg(2, cArg, sizeof(cArg)) > 0) + { + if(StrContains("blu", cArg, false) >= 0) + { + playerPrefTeam = TEAM_BLU; + } + else if(StrContains("red", cArg, false) >= 0) + { + playerPrefTeam = TEAM_RED; + } + } + // Was the argument an arena_index number? + new iArg = StringToInt(sArg); + if(iArg > 0 && iArg <= g_iArenaCount) + { + if(g_iPlayerArena[client] == iArg) + return Plugin_Handled; + + if (g_iPlayerArena[client]) + RemoveFromQueue(client, true); + + AddInQueue(client,iArg, true, playerPrefTeam); + return Plugin_Handled; + } + + // Was the argument an arena name? + GetCmdArgString(sArg, sizeof(sArg)); + new count; + new found_arena; + for(new i = 1; i <= g_iArenaCount; i++) + { + if(StrContains(g_sArenaName[i], sArg, false) >= 0) + { + count++; + found_arena = i; + if(count > 1) + { + ShowMainMenu(client); + return Plugin_Handled; + } + } + } + + // If there was only one string match, and it was a valid match, place the player in that arena if they aren't already in it. + if(found_arena > 0 && found_arena <= g_iArenaCount && found_arena != g_iPlayerArena[client]) + { + if (g_iPlayerArena[client]) + RemoveFromQueue(client, true); + + AddInQueue(client, found_arena, true, playerPrefTeam); + return Plugin_Handled; + } + + + } + + // Couldn't find a matching arena for the argument. + ShowMainMenu(client); + return Plugin_Handled; +} + +public Action:Command_Swap(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + if(!g_bCanPlayerSwap[client]) + { + PrintToChat(client, "You must wait 60 seconds between swap attempts!"); + return Plugin_Handled; + } + else + { + g_bCanPlayerSwap[client] = false; + CreateTimer(60.0, Timer_ResetSwap, client); + } + + new arena_index = g_iPlayerArena[client]; + + if(!g_bArenaUltiduo[arena_index] || !g_bFourPersonArena[arena_index]) + return Plugin_Continue; + + new client_teammate = getTeammate(client, g_iPlayerSlot[client], arena_index); + ShowSwapMenu(client_teammate); + return Plugin_Handled; + +} + +public Action:Command_Top5(client, args) +{ + if (g_bNoStats || !IsValidClient(client)) + { + PrintToChat(client, "No Stats is true"); + return Plugin_Continue; + } + + g_iELOMenuPage[client] = 0; + decl String:query[256]; + Format(query, sizeof(query), "SELECT rating,name FROM tf2_facti13_mge_first.mgemod_stats ORDER BY rating DESC LIMIT 5"); + SQL_TQuery(db, T_SQL_Top5, query, client); + return Plugin_Continue; +} + +public Action:Command_Remove(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + RemoveFromQueue(client, true); + return Plugin_Handled; +} + +public Action:Command_JoinClass(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + if (args) + { + new arena_index = g_iPlayerArena[client]; + new client_teammate; + if(g_bFourPersonArena[arena_index]) + { + client_teammate = getTeammate(client, g_iPlayerSlot[client], arena_index); + } + new String:s_class[64]; + GetCmdArg(1, s_class, sizeof(s_class)); + new TFClassType:new_class = TF2_GetClass(s_class); + + // Work-around to enable heavy. See https://bugs.alliedmods.net/show_bug.cgi?id=5243 + if (!new_class && StrEqual(s_class, "heavyweapons") && g_tfctArenaAllowedClasses[arena_index][6] == 1) + new_class = TFClass_Heavy; + + if (new_class == g_tfctPlayerClass[client]) + return Plugin_Handled; // no need to do anything, as nothing has changed + + if (arena_index == 0) // if client is on arena + { + if (!g_tfctClassAllowed[new_class]) // checking global class restrctions + { + CPrintToChat(client,"%t","ClassIsNotAllowed"); + return Plugin_Handled; + } else { + //if its ultiduo and a 4 man arena + if(g_bArenaUltiduo[arena_index] && g_bFourPersonArena[arena_index]) + { + //and you try to join as the same class as your teammate + if(new_class == g_tfctPlayerClass[client_teammate]) + { + //Tell the player what he did wrong + CPrintToChat(client,"Your team already has that class!"); + return Plugin_Handled; + } + else + { + TF2_SetPlayerClass(client, new_class); + g_tfctPlayerClass[client] = new_class; + + } + } + else + { + TF2_SetPlayerClass(client, new_class); + g_tfctPlayerClass[client] = new_class; + + } + ChangeClientTeam(client,TEAM_SPEC); + ShowSpecHudToArena(g_iPlayerArena[client]); + } + } + else + { + if (!g_tfctArenaAllowedClasses[arena_index][new_class]) + { + CPrintToChat(client,"%t","ClassIsNotAllowed"); + return Plugin_Handled; + } + + //if its ultiduo and a 4 man arena + if(g_bArenaUltiduo[arena_index] && g_bFourPersonArena[arena_index]) + { + //and you try to join as the same class as your teammate + if(new_class == g_tfctPlayerClass[client_teammate]) + { + //Tell the player what he did wrong + CPrintToChat(client,"Your team already has that class!"); + return Plugin_Handled; + } + else + { + TF2_SetPlayerClass(client, new_class); + g_tfctPlayerClass[client] = new_class; + } + } + + if (g_iPlayerSlot[client]==SLOT_ONE || g_iPlayerSlot[client]==SLOT_TWO || (g_bFourPersonArena[arena_index] && (g_iPlayerSlot[client] == SLOT_FOUR || g_iPlayerSlot[client] == SLOT_THREE))) + { + if (g_iArenaStatus[arena_index] != AS_FIGHT || g_bArenaMGE[arena_index] || g_bArenaEndif[arena_index] || g_bArenaKoth[arena_index]) + { + TF2_SetPlayerClass(client, new_class); + g_tfctPlayerClass[client] = new_class; + if(IsPlayerAlive(client)) + { + if((g_iArenaStatus[arena_index] == AS_FIGHT && g_bArenaMGE[arena_index] || g_bArenaEndif[arena_index])) + { + new killer_slot = (g_iPlayerSlot[client]==SLOT_ONE || g_iPlayerSlot[client]==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + new fraglimit = g_iArenaFraglimit[arena_index]; + new killer = g_iArenaQueue[arena_index][killer_slot]; + new killer_teammate; + new killer_team_slot = (killer_slot > 2) ? (killer_slot - 2) : killer_slot; + new client_team_slot = (g_iPlayerSlot[client] > 2) ? (g_iPlayerSlot[client] - 2) : g_iPlayerSlot[client]; + + if(g_bFourPersonArena[arena_index]) + { + killer_teammate = getTeammate(killer, killer_slot, arena_index); + } + if(g_iArenaStatus[arena_index] == AS_FIGHT && killer) + { + + g_iArenaScore[arena_index][killer_team_slot] += 1; + CPrintToChat(killer,"%t","ClassChangePointOpponent"); + CPrintToChat(client,"%t","ClassChangePoint"); + + if(g_bFourPersonArena[arena_index] && killer_teammate) + CreateTimer(3.0,Timer_NewRound,arena_index); + } + + ShowPlayerHud(client); + + if(IsValidClient(killer)) + { + ResetKiller(killer,arena_index); + ShowPlayerHud(killer); + } + + if(g_bFourPersonArena[arena_index]) + { + if(IsValidClient(killer_teammate)) + { + ResetKiller(killer_teammate,arena_index); + ShowPlayerHud(killer_teammate); + } + if(IsValidClient(client_teammate)) + { + ResetKiller(client_teammate,arena_index); + ShowPlayerHud(client_teammate); + } + } + + if (g_iArenaStatus[arena_index] == AS_FIGHT && fraglimit>0 && g_iArenaScore[arena_index][killer_team_slot] >= fraglimit) + { + new String:killer_name[(MAX_NAME_LENGTH * 2) + 5]; + new String:victim_name[(MAX_NAME_LENGTH * 2) + 5]; + GetClientName(killer,killer_name, sizeof(killer_name)); + GetClientName(client,victim_name, sizeof(victim_name)); + if(g_bFourPersonArena[arena_index]) + { + new String:killer_teammate_name[MAX_NAME_LENGTH]; + new String:victim_teammate_name[MAX_NAME_LENGTH]; + + GetClientName(killer_teammate,killer_teammate_name, sizeof(killer_teammate_name)); + GetClientName(client_teammate,victim_teammate_name, sizeof(victim_teammate_name)); + + Format(killer_name, sizeof(killer_name), "%s and %s", killer_name, killer_teammate_name); + Format(victim_name, sizeof(victim_name), "%s and %s", victim_name, victim_teammate_name); + } + CPrintToChatAll("%t","XdefeatsY", killer_name, g_iArenaScore[arena_index][killer_team_slot], victim_name, g_iArenaScore[arena_index][client_team_slot], fraglimit, g_sArenaName[arena_index]); + + g_iArenaStatus[arena_index] = AS_REPORTED; + + if (!g_bNoStats && g_bFourPersonArena[arena_index] /* && !g_arenaNoStats[arena_index]*/) + CalcELO2(killer, killer_teammate ,client, client_teammate); + else + CalcELO(killer,client); + if(g_bFourPersonArena[arena_index] && g_iArenaQueue[arena_index][SLOT_FOUR+1]) + { + RemoveFromQueue(client,false); + AddInQueue(client,arena_index,false); + + RemoveFromQueue(client_teammate,false); + AddInQueue(client_teammate,arena_index,false); + } + else if (g_iArenaQueue[arena_index][SLOT_TWO+1]) + { + RemoveFromQueue(client,false); + AddInQueue(client,arena_index,false); + } + else + { + CreateTimer(3.0,Timer_StartDuel,arena_index); + } + } + + + } + + CreateTimer(0.1,Timer_ResetPlayer,GetClientUserId(client)); + } + //Reset Handicap on class change to prevent an exploit where players could set their handicap to 299 as soldier + //And then play scout as 299 + g_iPlayerHandicap[client] = 0; + ShowSpecHudToArena(g_iPlayerArena[client]); + return Plugin_Continue; + + } + else + { + CPrintToChat(client, "%t", "NoClassChange"); + return Plugin_Handled; + } + } + else + { + g_tfctPlayerClass[client] = new_class; + ChangeClientTeam(client,TEAM_SPEC); + } + } + } + + return Plugin_Handled; +} + +public Action:Command_Spec(client, args) +{ //detecting spectator target + if (!IsValidClient(client)) + return Plugin_Continue; + + CreateTimer(0.1,Timer_ChangeSpecTarget,GetClientUserId(client)); + return Plugin_Continue; +} + +public Action:Command_AddBot(client, args) +{ //adding bot to client's arena + if (!IsValidClient(client)) + return Plugin_Continue; + + new arena_index = g_iPlayerArena[client]; + new player_slot = g_iPlayerSlot[client]; + + if (arena_index && (player_slot==SLOT_ONE || player_slot==SLOT_TWO || (g_bFourPersonArena[arena_index] && (player_slot==SLOT_THREE || player_slot==SLOT_FOUR)))) + { + ServerCommand("tf_bot_add"); + g_bPlayerAskedForBot[client] = true; + } + return Plugin_Handled; +} + +public Action:Command_Loc(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + new Float:vec[3]; + new Float:ang[3]; + GetClientAbsOrigin(client, vec); + GetClientEyeAngles(client, ang); + PrintToChat(client,"%.0f %.0f %.0f %.0f",vec[0],vec[1],vec[2],ang[1]); + return Plugin_Handled; +} + +public Action:Command_ToogleHitblip(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + g_bHitBlip[client] = !g_bHitBlip[client]; + + if (!g_bNoStats) + { + decl String:query[256]; + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET hitblip=%i WHERE steamid like '%s'", g_bHitBlip[client]?1:0, g_sPlayerSteamID[client]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } + + PrintToChat(client, "\x01Hitblip is \x04%sabled\x01.", g_bHitBlip[client]?"en":"dis"); + return Plugin_Handled; +} + +public Action:Command_ConnectionTest(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + decl String:query[256]; + Format(query, sizeof(query), "SELECT rating FROM tf2_facti13_mge_first.mgemod_stats LIMIT 1"); + SQL_TQuery(db, T_SQL_Test, query, client); + + return Plugin_Handled; +} + +public Action:Command_ToggleHud(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + g_bShowHud[client] = !g_bShowHud[client]; + + if(g_bShowHud[client]) + { + if(g_iPlayerArena[client]) + ShowPlayerHud(client); + else + ShowSpecHudToClient(client); + } else { + HideHud(client); + } + + PrintToChat(client, "\x01HUD is \x04%sabled\x01.", g_bShowHud[client]?"en":"dis"); + return Plugin_Handled; +} + +public Action:Command_Rank(client, args) +{ + if (g_bNoStats || !IsValidClient(client)) + return Plugin_Continue; + + if(args==0) + { + if(g_bNoDisplayRating) + CPrintToChat(client, "%t","MyRankNoRating",g_iPlayerWins[client],g_iPlayerLosses[client]); + else + CPrintToChat(client, "%t","MyRank",g_iPlayerRating[client],g_iPlayerWins[client],g_iPlayerLosses[client]); + } else { + decl String:argstr[64]; + GetCmdArgString(argstr, sizeof(argstr)); + new targ = FindTarget(0, argstr, false, false); + + if(targ == client) + { + if(g_bNoDisplayRating) + CPrintToChat(client, "%t","MyRankNoRating",g_iPlayerWins[client],g_iPlayerLosses[client]); + else + CPrintToChat(client, "%t","MyRank",g_iPlayerRating[client],g_iPlayerWins[client],g_iPlayerLosses[client]); + } else if(targ!=-1) { + if(g_bNoDisplayRating) + PrintToChat(client, "\x03%N\x01 has \x04%i\x01 wins and \x04%i\x01 losses. You have a \x04%i%%\x01 chance of beating him.", targ, g_iPlayerWins[targ], g_iPlayerLosses[targ], RoundFloat((1/(Pow(10.0, float((g_iPlayerRating[targ]-g_iPlayerRating[client]))/400)+1))*100)); + else + PrintToChat(client, "\x03%N\x01's rating is \x04%i\x01. You have a \x04%i%%\x01 chance of beating him.", targ, g_iPlayerRating[targ], RoundFloat((1/(Pow(10.0, float((g_iPlayerRating[targ]-g_iPlayerRating[client]))/400)+1))*100)); + } + } + + return Plugin_Handled; +} + +public Action:Command_Help(client, args) +{ + if (!client || !IsValidClient(client)) + return Plugin_Continue; + + PrintToChat(client, "%t", "Cmd_SeeConsole"); + PrintToConsole(client, "\n\n----------------------------"); + PrintToConsole(client, "%t", "Cmd_MGECmds"); + PrintToConsole(client, "%t", "Cmd_MGEMod"); + PrintToConsole(client, "%t", "Cmd_Add"); + PrintToConsole(client, "%t", "Cmd_Remove"); + PrintToConsole(client, "%t", "Cmd_First"); + PrintToConsole(client, "%t", "Cmd_Top5"); + PrintToConsole(client, "%t", "Cmd_Rank"); + PrintToConsole(client, "%t", "Cmd_HitBlip"); + PrintToConsole(client, "%t", "Cmd_Hud"); + PrintToConsole(client, "%t", "Cmd_Handicap"); + PrintToConsole(client, "----------------------------\n\n"); + + return Plugin_Handled; +} + +public Action:Command_First(client, args) +{ + if (!client || !IsValidClient(client)) + return Plugin_Continue; + + // Try to find an arena with one person in the queue.. + for(new i = 1; i <= g_iArenaCount; i++) + { + if(!g_iArenaQueue[i][SLOT_TWO] && g_iPlayerArena[client] != i) + { + if(g_iArenaQueue[i][SLOT_ONE]) + { + if(g_iPlayerArena[client]) + RemoveFromQueue(client, true); + + AddInQueue(client, i, true); + return Plugin_Handled; + } + } + } + + // Couldn't find an arena with only one person in the queue, so find one with none. + if(!g_iPlayerArena[client]) + { + for(new i = 1; i <= g_iArenaCount; i++) + { + if(!g_iArenaQueue[i][SLOT_TWO] && g_iPlayerArena[client] != i) + { + if(g_iPlayerArena[client]) + RemoveFromQueue(client, true); + + AddInQueue(client, i, true); + return Plugin_Handled; + } + } + } + + // Couldn't find any empty or half-empty arenas, so display the menu. + ShowMainMenu(client); + return Plugin_Handled; +} + +public Action:Command_Handicap(client, args) +{ + if (!IsValidClient(client)) + return Plugin_Continue; + + new arena_index = g_iPlayerArena[client]; + + if (!arena_index || g_bArenaMidair[arena_index]) + { + CPrintToChat(client, "%t", "MustJoinArena"); + g_iPlayerHandicap[client] = 0; + return Plugin_Handled; + } + + if(args==0) + { + if (g_iPlayerHandicap[client] == 0) + CPrintToChat(client, "%t","NoCurrentHandicap",g_iPlayerHandicap[client]); + else + CPrintToChat(client, "%t","CurrentHandicap",g_iPlayerHandicap[client]); + } else { + decl String:argstr[64]; + GetCmdArgString(argstr, sizeof(argstr)); + new argint = StringToInt(argstr); + + if (StrEqual(argstr, "off", false)) + { + CPrintToChat(client, "%t", "HandicapDisabled"); + g_iPlayerHandicap[client] = 0; + return Plugin_Handled; + } + + if (argint > RoundToNearest(float(g_iPlayerMaxHP[client])*g_fArenaHPRatio[arena_index])) + { + CPrintToChat(client, "%t","InvalidHandicap"); + g_iPlayerHandicap[client] = 0; + } else if (argint <= 0) { + CPrintToChat(client, "%t","InvalidHandicap"); + } else { + g_iPlayerHandicap[client] = argint; + + //If the client currently has more health than their handicap allows, lower it to the proper amount. + if (IsPlayerAlive(client) && g_iPlayerHP[client] > g_iPlayerHandicap[client]) + { + if (g_bArenaMGE[arena_index] || g_bArenaBBall[arena_index]) + { + //Prevent an possible exploit where a player could restore their buff if it decayed naturally without them taking damage. + if (GetEntProp(client, Prop_Data, "m_iHealth") > g_iPlayerHandicap[client]) + { + SetEntProp(client, Prop_Data, "m_iHealth", g_iPlayerHandicap[client]); + g_iPlayerHP[client] = g_iPlayerHandicap[client]; + } + } else { + g_iPlayerHP[client] = g_iPlayerHandicap[client]; + } + + //Update overlay huds to reflect health change. + new player_slot = g_iPlayerSlot[client], + foe_slot = player_slot==SLOT_ONE ? SLOT_TWO : SLOT_ONE, + foe = g_iArenaQueue[arena_index][foe_slot], + foe_teammate, + player_teammate; + + + + + + if(g_bFourPersonArena[arena_index]) + { + player_teammate = getTeammate(client, player_slot, arena_index); + foe_teammate = getTeammate(foe, foe_slot, arena_index); + + ShowPlayerHud(player_teammate); + ShowPlayerHud(foe_teammate); + } + + ShowPlayerHud(client); + ShowPlayerHud(foe); + ShowSpecHudToArena(g_iPlayerArena[client]); + } + } + } + + return Plugin_Handled; +} + +/* OnDropIntel(client, command, argc) +* +* When a player drops the intel in BBall. +* -------------------------------------------------------------------------- */ +public Action:Command_DropItem(client, const String:command[], argc) +{ + new arena_index = g_iPlayerArena[client]; + + if(g_bArenaBBall[arena_index]) + { + if (g_bPlayerHasIntel[client]) + { + g_bPlayerHasIntel[client] = false; + new Float:pos[3]; + GetClientAbsOrigin(client, pos); + new Float:dist = DistanceAboveGroundAroundPlayer(client); + if(dist > -1) + pos[2] = pos[2] - dist + 5; + else + pos[2] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-3][2]; + + if (g_iBBallIntel[arena_index] == -1) + g_iBBallIntel[arena_index] = CreateEntityByName("item_ammopack_small"); + else + LogError("[%s] Player dropped the intel, but intel [%i] already exists.", g_sArenaName[arena_index], g_iBBallIntel[arena_index]); + + + //This should fix the ammopack not being turned into a briefcase + DispatchKeyValue(g_iBBallIntel[arena_index], "powerup_model", MODEL_BRIEFCASE); + TeleportEntity(g_iBBallIntel[arena_index], pos, NULL_VECTOR, NULL_VECTOR); + DispatchSpawn(g_iBBallIntel[arena_index]); + SetEntProp(g_iBBallIntel[arena_index], Prop_Send, "m_iTeamNum", 1, 4); + SetEntPropFloat(g_iBBallIntel[arena_index], Prop_Send, "m_flModelScale", 1.15); + //Doesn't work anymore + //SetEntityModel(g_iBBallIntel[arena_index], MODEL_BRIEFCASE); + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + SDKHook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + AcceptEntityInput(g_iBBallIntel[arena_index], "Enable"); + + EmitSoundToClient(client, "vo/intel_teamdropped.wav"); + + RemoveClientParticle(client); + + g_bCanPlayerGetIntel[client] = false; + CreateTimer(0.5, Timer_AllowPlayerCap, client); + } + } + + return Plugin_Continue; +} + +//blocking sounds +public Action:sound_hook(clients[64], &numClients, String:sample[PLATFORM_MAX_PATH], &entity, &channel, &Float:volume, &level, &pitch, &flags) +{ + if(StrContains(sample,"pl_fallpain")>=0 && g_bBlockFallDamage) + { + return Plugin_Handled; + } + + return Plugin_Continue; +} + +// ====[ SQL ]==================================================== +PrepareSQL() // Opens the connection to the database, and creates the tables if they dont exist. +{ + decl String:error[256]; + + if(SQL_CheckConfig(g_sDBConfig)) + db = SQL_Connect(g_sDBConfig, true, error, sizeof(error)); + + if(db == INVALID_HANDLE) + { + LogError("Cant use database config <%s> , trying SQLite ...",g_sDBConfig, error); + db = SQL_Connect("storage-local", true, error, sizeof(error)); + + if(db == INVALID_HANDLE) + SetFailState("Could not connect to database: %s", error); + else + LogError("Success, using SQLite ",g_sDBConfig, error); + } + + decl String:ident[16]; + SQL_ReadDriver(db, ident, sizeof(ident)); + + if(StrEqual(ident, "pgsql", false)) + g_bUseSQLite = false; + else if(StrEqual(ident, "sqlite", false)) + g_bUseSQLite = true; + else + SetFailState("Invalid database."); + + /*if(g_bUseSQLite) + { + SQL_TQuery(db, SQLErrorCheckCallback, "CREATE TABLE IF NOT EXISTS mgemod_stats (rating INTEGER, steamid TEXT, name TEXT, wins INTEGER, losses INTEGER, lastplayed INTEGER, hitblip INTEGER)"); + SQL_TQuery(db, SQLErrorCheckCallback, "CREATE TABLE IF NOT EXISTS mgemod_duels (winner TEXT, loser TEXT, winnerscore INTEGER, loserscore INTEGER, winlimit INTEGER, gametime INTEGER, mapname TEXT, arenaname TEXT) "); + SQL_TQuery(db, SQLErrorCheckCallback, "CREATE TABLE IF NOT EXISTS mgemod_duels_2v2 (winner TEXT, winner2 TEXT, loser TEXT, loser2 TEXT, winnerscore INTEGER, loserscore INTEGER, winlimit INTEGER, gametime INTEGER, mapname TEXT, arenaname TEXT) "); + } else { + SQL_TQuery(db, SQLErrorCheckCallback, "CREATE TABLE IF NOT EXISTS mgemod_stats (rating INT(4) NOT NULL, steamid VARCHAR(32) NOT NULL, name VARCHAR(64) NOT NULL, wins INT(4) NOT NULL, losses INT(4) NOT NULL, lastplayed INT(11) NOT NULL, hitblip INT(2) NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB "); + SQL_TQuery(db, SQLErrorCheckCallback, "CREATE TABLE IF NOT EXISTS mgemod_duels (winner VARCHAR(32) NOT NULL, loser VARCHAR(32) NOT NULL, winnerscore INT(4) NOT NULL, loserscore INT(4) NOT NULL, winlimit INT(4) NOT NULL, gametime INT(11) NOT NULL, mapname VARCHAR(64) NOT NULL, arenaname VARCHAR(32) NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB "); + SQL_TQuery(db, SQLErrorCheckCallback, "CREATE TABLE IF NOT EXISTS mgemod_duels_2v2 (winner VARCHAR(32) NOT NULL, winner2 VARCHAR(32) NOT NULL, loser VARCHAR(32) NOT NULL, loser2 VARCHAR(32) NOT NULL, winnerscore INT(4) NOT NULL, loserscore INT(4) NOT NULL, winlimit INT(4) NOT NULL, gametime INT(11) NOT NULL, mapname VARCHAR(64) NOT NULL, arenaname VARCHAR(32) NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB "); + }*/ + +} + +public T_SQLQueryOnConnect(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + new client = data; + + if(hndl==INVALID_HANDLE) + { + LogError("T_SQLQueryOnConnect failed: %s", error); + return; + } + + if (client < 1 || client > MaxClients || !IsClientConnected(client)) + { + LogError("T_SQLQueryOnConnect failed: client %d <%s> is invalid.", client, g_sPlayerSteamID[client]); + return; + } + + decl String:query[512]; + decl String:namesql_dirty[MAX_NAME_LENGTH], String:namesql[(MAX_NAME_LENGTH*2)+1]; + GetClientName(client, namesql_dirty, sizeof(namesql_dirty)); + SQL_EscapeString(db, namesql_dirty, namesql, sizeof(namesql)); + + if(SQL_FetchRow(hndl)) + { + g_iPlayerRating[client] = SQL_FetchInt(hndl, 0); + g_bHitBlip[client] = SQL_FetchInt(hndl, 1)==1; + g_iPlayerWins[client] = SQL_FetchInt(hndl, 2); + g_iPlayerLosses[client] = SQL_FetchInt(hndl, 3); + + Format(query, sizeof(query), "UPDATE tf2_facti13_mge_first.mgemod_stats SET name like '%s' WHERE steamid like '%s'", namesql, g_sPlayerSteamID[client]); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } else { + if(g_bUseSQLite) + { + Format(query, sizeof(query), "INSERT INTO tf2_facti13_mge_first.mgemod_stats VALUES(1600, '%s', '%s', 0, 0, %i, 1)", g_sPlayerSteamID[client], namesql, GetTime()); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } else { + Format(query, sizeof(query), "INSERT INTO tf2_facti13_mge_first.mgemod_stats (rating, steamid, name, wins, losses, lastplayed, hitblip) VALUES (1600, '%s', '%s', 0, 0, %i, 1)", g_sPlayerSteamID[client], namesql, GetTime()); + SQL_TQuery(db, SQLErrorCheckCallback, query); + } + + g_iPlayerRating[client] = 1600; + g_bHitBlip[client] = false; + } +} + +public T_SQL_Top5(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + new client = data; + + if(hndl==INVALID_HANDLE) + { + LogError("[Top5] Query failed: %s", error); + return; + } + + if(client < 1 || client > MaxClients || !IsClientConnected(client)) + { + LogError("T_SQL_Top5 failed: client %d <%s> is invalid.", client, g_sPlayerSteamID[client]); + return; + } + + if(SQL_GetRowCount(hndl) == 5) + { + new rating[5], + String:name[5][MAX_NAME_LENGTH], + i = 0; + + while(SQL_FetchRow(hndl)) + { + if(i > 5) + break; + + SQL_FetchString(hndl, 1, name[i], 64); + rating[i] = SQL_FetchInt(hndl, 0); + + i++; + } + + ShowTop5Menu(client, name, rating); + } else { + CPrintToChat(client, "%t", "top5error"); + } + +} + +public T_SQL_Test(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + new client = data; + + if(hndl==INVALID_HANDLE) + { + LogError("[Test] Query failed: %s", error); + PrintToChat(client, "[Test] Query failed: %s", error); + return; + } + + if(client < 1 || client > MaxClients || !IsClientConnected(client)) + { + LogError("T_SQL_Test failed: client %d <%s> is invalid.", client, g_sPlayerSteamID[client]); + return; + } + + if(SQL_FetchRow(hndl)) + PrintToChat(client, "\x01Database is \x04Up\x01."); + else + PrintToChat(client, "\x01Database is \x04Down\x01."); +} + +public SQLErrorCheckCallback(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + if(!StrEqual("", error)) + { + LogError("Query failed: %s", error); + + if(!g_bNoStats) + { + g_bNoStats = true; + PrintHintTextToAll("%t", "DatabaseDown", g_iReconnectInterval); + + // Refresh all huds to get rid of stats display. + ShowHudToAll(); + + LogError("Lost connection to database, attempting reconnect in %i minutes.", g_iReconnectInterval); + + if(g_hDBReconnectTimer == INVALID_HANDLE) + g_hDBReconnectTimer = CreateTimer(float(60 * g_iReconnectInterval), Timer_ReconnectToDB, TIMER_FLAG_NO_MAPCHANGE); + } + + } +} + +public SQLDbConnTest(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + if(!StrEqual("", error)) + { + LogError("Query failed: %s", error); + LogError("Database reconnect failed, next attempt in %i minutes.", g_iReconnectInterval); + PrintHintTextToAll("%t", "DatabaseDown", g_iReconnectInterval); + + if(g_hDBReconnectTimer == INVALID_HANDLE) + g_hDBReconnectTimer = CreateTimer(float(60 * g_iReconnectInterval), Timer_ReconnectToDB, TIMER_FLAG_NO_MAPCHANGE); + } else { + g_bNoStats = (GetConVarBool(gcvar_stats)) ? false : true; + + if(!g_bNoStats) + { + for(new i = 1; i <= MaxClients; i++) + { + if(IsValidClient(i)) + { + decl String:steamid_dirty[31], String:steamid[64], String:query[256]; + GetClientAuthId(i, AuthId_Steam2, steamid_dirty, sizeof(steamid_dirty)); + SQL_EscapeString(db, steamid_dirty, steamid, sizeof(steamid)); + strcopy(g_sPlayerSteamID[i],32,steamid); + Format(query, sizeof(query), "SELECT rating, hitblip, wins, losses FROM tf2_facti13_mge_first.mgemod_stats WHERE steamid like '%s' LIMIT 1", steamid); + SQL_TQuery(db, T_SQLQueryOnConnect, query, i); + } + } + + // Refresh all huds to show stats again. + ShowHudToAll(); + + PrintHintTextToAll("%t", "StatsRestored"); + } else { + PrintHintTextToAll("%t", "StatsRestoredDown"); + } + + LogError("Database connection restored."); + } +} + + +/* +** ------------------------------------------------------------------ +** ______ __ +** / ____/_ _____ ____ / /______ +** / __/ | | / / _ \/ __ \/ __/ ___/ +** / /___ | |/ / __/ / / / /_(__ ) +** /_____/ |___/\___/_/ /_/\__/____/ +** +** ------------------------------------------------------------------ +**/ + +public Event_PlayerSpawn(Handle:event,const String:name[],bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + new arena_index = g_iPlayerArena[client]; + + g_tfctPlayerClass[client] = TF2_GetPlayerClass(client); + + ResetClientAmmoCounts(client); + + if(!g_bFourPersonArena[arena_index] && g_iPlayerSlot[client] != SLOT_ONE && g_iPlayerSlot[client] != SLOT_TWO) + ChangeClientTeam(client, TEAM_SPEC); + + else if(g_bFourPersonArena[arena_index] && g_iPlayerSlot[client] != SLOT_ONE && g_iPlayerSlot[client] != SLOT_TWO && (g_iPlayerSlot[client]!=SLOT_THREE && g_iPlayerSlot[client]!=SLOT_FOUR)) + ChangeClientTeam(client, TEAM_SPEC); + + if(g_bArenaMGE[arena_index]) + { + g_iPlayerHP[client] = RoundToNearest(float(g_iPlayerMaxHP[client])*g_fArenaHPRatio[arena_index]); + ShowSpecHudToArena(arena_index); + } + + if(g_bArenaBBall[arena_index]) + g_bPlayerHasIntel[client] = false; +} + +public Event_WinPanel(Handle:event,const String:name[],bool:dontBroadcast) +{ + // Disable stats so people leaving at the end of the map don't lose points. + g_bNoStats = true; +} + +public Action:Event_PlayerHurt(Handle:event,const String:name[],bool:dontBroadcast) +{ + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + + if(!IsValidClient(victim)) + return Plugin_Continue; + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new arena_index = g_iPlayerArena[victim]; + new iDamage = GetEventInt(event, "damageamount"); + + if (attacker > 0 && victim != attacker) // If the attacker wasn't the person being hurt, or the world (fall damage). + { + new bool:shootsRocketsOrPipes = ShootsRocketsOrPipes(attacker); + if(g_bArenaEndif[arena_index]) + { + if(shootsRocketsOrPipes) + CreateTimer(0.01, BoostVectors, GetClientUserId(victim)); + } + + if (g_bPlayerTakenDirectHit[victim]) + { + new bool:isVictimInAir = !(GetEntityFlags(victim) & (FL_ONGROUND)); + + if (isVictimInAir) + { + //airshot + new Float:dist = DistanceAboveGround(victim); + if (dist >= g_iAirshotHeight) + { + if (g_bArenaMidair[arena_index]) + g_iPlayerHP[victim] -= 1; + + if(g_bArenaEndif[arena_index] && dist >= 250) + { + g_iPlayerHP[victim] = -1; + } + } + } + } + } + + g_bPlayerTakenDirectHit[victim] = false; + + if(g_bArenaMGE[arena_index] || g_bArenaBBall[arena_index]) + g_iPlayerHP[victim] = GetClientHealth(victim); + else if (g_bArenaAmmomod[arena_index]) + g_iPlayerHP[victim] -= iDamage; + + //TODO: Look into getting rid of the crutch. Possible memory leak/performance issue? + g_bPlayerRestoringAmmo[attacker] = false; //inf ammo crutch + + if(g_bArenaAmmomod[arena_index] || g_bArenaMidair[arena_index] || g_bArenaEndif[arena_index]) + { + if (g_iPlayerHP[victim] <= 0) + SetEntityHealth(victim,0); + else + SetEntityHealth(victim,g_iPlayerMaxHP[victim]); + } + + ShowPlayerHud(victim); + ShowPlayerHud(attacker); + ShowSpecHudToArena(g_iPlayerArena[victim]); + + return Plugin_Continue; +} + +public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new arena_index = g_iPlayerArena[victim]; + new victim_slot = g_iPlayerSlot[victim]; + + + new killer_slot = (victim_slot==SLOT_ONE || victim_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + new killer = g_iArenaQueue[arena_index][killer_slot]; + new killer_teammate; + new victim_teammate; + + //gets the killer and victims team slot (red 1, blu 2) + new killer_team_slot = (killer_slot > 2) ? (killer_slot - 2) : killer_slot; + new victim_team_slot = (victim_slot > 2) ? (victim_slot - 2) : victim_slot; + + if(g_bFourPersonArena[arena_index]) + { + victim_teammate = getTeammate(victim, victim_slot, arena_index); + killer_teammate = getTeammate(killer, killer_slot, arena_index); + } + + RemoveClientParticle(victim); + + if (!arena_index) + ChangeClientTeam(victim, TEAM_SPEC); + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + if(g_iArenaStatus[arena_index] < AS_FIGHT && IsValidClient(attacker) && IsPlayerAlive(attacker)) + { + TF2_RegeneratePlayer(attacker); + new raised_hp = RoundToNearest(float(g_iPlayerMaxHP[attacker])*g_fArenaHPRatio[arena_index]); + g_iPlayerHP[attacker] = raised_hp; + SetEntProp(attacker, Prop_Data, "m_iHealth", raised_hp); + } + + if (g_iArenaStatus[arena_index]AS_FIGHT) + { + CreateTimer(0.1,Timer_ResetPlayer,GetClientUserId(victim)); + return Plugin_Handled; + } + + if ((g_bFourPersonArena[arena_index] && !IsPlayerAlive(killer)) || (g_bFourPersonArena[arena_index] && !IsPlayerAlive(killer_teammate) && !IsPlayerAlive(killer))) + { + if(g_bArenaAmmomod[arena_index] || g_bArenaMidair[arena_index]) + return Plugin_Handled; + } + + if (!g_bArenaBBall[arena_index] && !g_bArenaKoth[arena_index] && (!g_bFourPersonArena[arena_index] || (g_bFourPersonArena[arena_index] && !IsPlayerAlive(victim_teammate)))) // Kills shouldn't give points in bball. Or if only 1 player in a two person arena dies + g_iArenaScore[arena_index][killer_team_slot] += 1; + + if(!g_bArenaEndif[arena_index]) // Endif does not need to display health, since it is one-shot kills. + { + //We must get the player that shot you last in 4 player arenas + //The valid client check shouldn't be necessary but I'm getting invalid clients here for some reason + //This may be caused by players killing themselves in 1v1 arenas without being attacked, or dieing after + //A player disconnects but before the arena status transitions out of fight mode? + //TODO: check properly + if(g_bFourPersonArena[arena_index] && IsValidClient(attacker) && IsPlayerAlive(attacker)) + { + if((g_bArenaMGE[arena_index] || g_bArenaBBall[arena_index] || g_bArenaKoth[arena_index]) && (victim != attacker)) + CPrintToChat(victim, "%t", "HPLeft", GetClientHealth(attacker)); + else if(victim != attacker) + CPrintToChat(victim, "%t", "HPLeft", g_iPlayerHP[attacker]); + } + //in 1v1 arenas we can assume the person who killed you is the other person in the arena + else if(IsValidClient(killer) && IsPlayerAlive(killer)) + { + if(g_bArenaMGE[arena_index] || g_bArenaBBall[arena_index] || g_bArenaKoth[arena_index]) + CPrintToChat(victim, "%t", "HPLeft", GetClientHealth(killer)); + else + CPrintToChat(victim, "%t", "HPLeft", g_iPlayerHP[killer]); + } + } + + //Currently set up so that if its a 2v2 duel the round will reset after both players on one team die and a point will be added for that round to the other team + //Another possibility is to make it like dm where its instant respawn for every player, killer gets hp, and a point is awarded for every kill + + + new fraglimit = g_iArenaFraglimit[arena_index]; + + + if((!g_bFourPersonArena[arena_index] && (g_bArenaAmmomod[arena_index] || g_bArenaMidair[arena_index])) || + (g_bFourPersonArena[arena_index] && !IsPlayerAlive(victim_teammate) && !g_bArenaBBall[arena_index] && !g_bArenaKoth[arena_index])) + g_iArenaStatus[arena_index] = AS_AFTERFIGHT; + + if (g_iArenaStatus[arena_index] >= AS_FIGHT && g_iArenaStatus[arena_index] < AS_REPORTED && fraglimit > 0 && g_iArenaScore[arena_index][killer_team_slot] >= fraglimit) + { + g_iArenaStatus[arena_index] = AS_REPORTED; + new String:killer_name[128]; + new String:victim_name[128]; + GetClientName(killer,killer_name, sizeof(killer_name)); + GetClientName(victim,victim_name, sizeof(victim_name)); + + + if(g_bFourPersonArena[arena_index]) + { + new String:killer_teammate_name[128]; + new String:victim_teammate_name[128]; + + GetClientName(killer_teammate,killer_teammate_name, sizeof(killer_teammate_name)); + GetClientName(victim_teammate,victim_teammate_name, sizeof(victim_teammate_name)); + + Format(killer_name, sizeof(killer_name), "%s and %s", killer_name, killer_teammate_name); + Format(victim_name, sizeof(victim_name), "%s and %s", victim_name, victim_teammate_name); + } + + CPrintToChatAll("%t","XdefeatsY", killer_name, g_iArenaScore[arena_index][killer_team_slot], victim_name, g_iArenaScore[arena_index][victim_team_slot], fraglimit, g_sArenaName[arena_index]); + + if (!g_bNoStats && !g_bFourPersonArena[arena_index]) + CalcELO(killer,victim); + + else if(!g_bNoStats) + CalcELO2(killer, killer_teammate, victim, victim_teammate); + + if(!g_bFourPersonArena[arena_index]) + { + if (g_iArenaQueue[arena_index][SLOT_TWO+1]) + { + RemoveFromQueue(victim,false,true); + AddInQueue(victim,arena_index,false); + } else { + CreateTimer(3.0,Timer_StartDuel,arena_index); + } + } + else + { + if (g_iArenaQueue[arena_index][SLOT_FOUR+1] && g_iArenaQueue[arena_index][SLOT_FOUR+2]) + { + RemoveFromQueue(victim_teammate,false,true); + RemoveFromQueue(victim,false,true); + AddInQueue(victim_teammate,arena_index,false); + AddInQueue(victim,arena_index,false); + } + else if(g_iArenaQueue[arena_index][SLOT_FOUR+1]) + { + RemoveFromQueue(victim,false,true); + AddInQueue(victim,arena_index,false); + } + else { + CreateTimer(3.0,Timer_StartDuel,arena_index); + } + } + } + else if(g_bArenaAmmomod[arena_index] || g_bArenaMidair[arena_index]) + { + if(!g_bFourPersonArena[arena_index]) + CreateTimer(3.0,Timer_NewRound,arena_index); + + else if(g_bFourPersonArena[arena_index] && !IsPlayerAlive(victim_teammate)) + CreateTimer(3.0,Timer_NewRound,arena_index); + + } + else + { + if(g_bArenaBBall[arena_index]) + { + if (g_bPlayerHasIntel[victim]) + { + g_bPlayerHasIntel[victim] = false; + new Float:pos[3]; + GetClientAbsOrigin(victim, pos); + new Float:dist = DistanceAboveGround(victim); + if(dist > -1) + pos[2] = pos[2] - dist + 5; + else + pos[2] = g_fArenaSpawnOrigin[arena_index][g_iArenaSpawns[arena_index]-3][2]; + + if (g_iBBallIntel[arena_index] == -1) + g_iBBallIntel[arena_index] = CreateEntityByName("item_ammopack_small"); + else + LogError("[%s] Player died with intel, but intel [%i] already exists.", g_sArenaName[arena_index], g_iBBallIntel[arena_index]); + + + //This should fix the ammopack not being turned into a briefcase + DispatchKeyValue(g_iBBallIntel[arena_index], "powerup_model", MODEL_BRIEFCASE); + TeleportEntity(g_iBBallIntel[arena_index], pos, NULL_VECTOR, NULL_VECTOR); + DispatchSpawn(g_iBBallIntel[arena_index]); + SetEntProp(g_iBBallIntel[arena_index], Prop_Send, "m_iTeamNum", 1, 4); + SetEntPropFloat(g_iBBallIntel[arena_index], Prop_Send, "m_flModelScale", 1.15); + //Doesn't work anymore + //SetEntityModel(g_iBBallIntel[arena_index], MODEL_BRIEFCASE); + //SDKUnhook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + SDKHook(g_iBBallIntel[arena_index], SDKHook_StartTouch, OnTouchIntel); + AcceptEntityInput(g_iBBallIntel[arena_index], "Enable"); + + EmitSoundToClient(victim, "vo/intel_teamdropped.wav"); + if(IsValidClient(killer)) + EmitSoundToClient(killer, "vo/intel_enemydropped.wav"); + + } + } else { + if(!g_bFourPersonArena[arena_index] && !g_bArenaKoth[arena_index]) + { + ResetKiller(killer,arena_index); + } + if(g_bFourPersonArena[arena_index] && (GetClientTeam(victim_teammate) == TEAM_SPEC || !IsPlayerAlive(victim_teammate))) + { + //Reset the teams + ResetArena(arena_index); + if(killer_team_slot == SLOT_ONE) + { + ChangeClientTeam(victim, TEAM_BLU); + ChangeClientTeam(victim_teammate, TEAM_BLU); + + ChangeClientTeam(killer_teammate, TEAM_RED); + } + else + { + ChangeClientTeam(victim, TEAM_RED); + ChangeClientTeam(victim_teammate, TEAM_RED); + + ChangeClientTeam(killer_teammate, TEAM_BLU); + } + + //Should there be a 3 second count down in between rounds in 2v2 or just spawn and go? + //Timer_NewRound would create a 3 second count down where as just reseting all the players would make it just go + /* + if(killer) + ResetPlayer(killer); + if(victim_teammate) + ResetPlayer(victim_teammate); + if(victim) + ResetPlayer(victim); + if(killer_teammate) + ResetPlayer(killer_teammate); + + g_iArenaStatus[arena_index] = AS_FIGHT; + */ + CreateTimer(0.1,Timer_NewRound,arena_index); + } + + + } + + + //TODO: Check to see if its koth and apply a spawn penalty if needed depending on who's capping + if(g_bArenaBBall[arena_index] || g_bArenaKoth[arena_index]) + { + CreateTimer(g_fArenaRespawnTime[arena_index],Timer_ResetPlayer,GetClientUserId(victim)); + } + else if(g_bFourPersonArena[arena_index] && victim_teammate && IsPlayerAlive(victim_teammate)) + { + //Set the player as waiting + g_iPlayerWaiting[victim] = true; + //change the player to spec to keep him from respawning + CreateTimer(5.0, Timer_ChangePlayerSpec, victim); + //instead of respawning him + //CreateTimer(g_fArenaRespawnTime[arena_index],Timer_ResetPlayer,GetClientUserId(victim)); + } + else + CreateTimer(g_fArenaRespawnTime[arena_index],Timer_ResetPlayer,GetClientUserId(victim)); + + } + + ShowPlayerHud(victim); + ShowPlayerHud(killer); + + if(g_bFourPersonArena[arena_index]) + { + ShowPlayerHud(victim_teammate); + ShowPlayerHud(killer_teammate); + } + + ShowSpecHudToArena(arena_index); + + return Plugin_Continue; +} + +public Action:Event_PlayerTeam(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event,"userid")); + + if (!client) + return Plugin_Continue; + + new team = GetEventInt(event,"team"); + + if (team == TEAM_SPEC) + { + HideHud(client); + CreateTimer(1.0, Timer_ChangeSpecTarget, GetClientUserId(client)); + new arena_index = g_iPlayerArena[client]; + + if (arena_index && ((!g_bFourPersonArena[arena_index] && g_iPlayerSlot[client] <= SLOT_TWO) || (g_bFourPersonArena[arena_index] && g_iPlayerSlot[client] <= SLOT_FOUR && !isPlayerWaiting(client)))) + { + CPrintToChat(client,"%t","SpecRemove"); + RemoveFromQueue(client,true); + } + } else if (IsValidClient(client)) { // this code fixing spawn exploit + new arena_index = g_iPlayerArena[client]; + + if (arena_index == 0) + { + TF2_SetPlayerClass(client,TFClassType:0); + } + } + + SetEventInt(event, "silent", true); + return Plugin_Changed; +} + +public Action:Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast) +{ + SetConVarInt(gcvar_WfP,1); //cancel waiting for players + + //Be totally certain that the models are chached so they can be hooked + PrecacheModel(MODEL_BRIEFCASE, true); + PrecacheModel(MODEL_AMMOPACK, true); + + for (new i = 0;i<=g_iArenaCount;i++) + { + if(g_bArenaBBall[i]) + { + new Float:hoop_2_loc[3]; + hoop_2_loc[0] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]][0]; + hoop_2_loc[1] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]][1]; + hoop_2_loc[2] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]][2]; + new Float:hoop_1_loc[3]; + hoop_1_loc[0] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]-1][0]; + hoop_1_loc[1] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]-1][1]; + hoop_1_loc[2] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]-1][2]; + + if(IsValidEdict(g_iBBallHoop[i][SLOT_ONE]) && g_iBBallHoop[i][SLOT_ONE] > 0) + { + RemoveEdict(g_iBBallHoop[i][SLOT_ONE]); + g_iBBallHoop[i][SLOT_ONE] = -1; + } else if(g_iBBallHoop[i][SLOT_ONE] != -1) { // g_iBBallHoop[i][SLOT_ONE] equaling -1 is not a bad thing, so don't print an error for it. + //LogError("[%s] Event_RoundStart fired, but could not remove old hoop [%d]!.", g_sArenaName[i], g_iBBallHoop[i][SLOT_ONE]); + //LogError("[%s] Resetting SLOT_ONE hoop array index %i.", g_sArenaName[i], i); + g_iBBallHoop[i][SLOT_ONE] = -1; + } + + if(IsValidEdict(g_iBBallHoop[i][SLOT_TWO]) && g_iBBallHoop[i][SLOT_TWO] > 0) + { + RemoveEdict(g_iBBallHoop[i][SLOT_TWO]); + g_iBBallHoop[i][SLOT_TWO] = -1; + } else if(g_iBBallHoop[i][SLOT_TWO] != -1) { // g_iBBallHoop[i][SLOT_TWO] equaling -1 is not a bad thing, so don't print an error for it. + //LogError("[%s] Event_RoundStart fired, but could not remove old hoop [%d]!.", g_sArenaName[i], g_iBBallHoop[i][SLOT_TWO]); + //LogError("[%s] Resetting SLOT_TWO hoop array index %i.", g_sArenaName[i], i); + g_iBBallHoop[i][SLOT_TWO] = -1; + } + + if (g_iBBallHoop[i][SLOT_ONE] == -1) + { + g_iBBallHoop[i][SLOT_ONE] = CreateEntityByName("item_ammopack_small"); + TeleportEntity(g_iBBallHoop[i][SLOT_ONE], hoop_1_loc, NULL_VECTOR, NULL_VECTOR); + DispatchSpawn(g_iBBallHoop[i][SLOT_ONE]); + SetEntProp(g_iBBallHoop[i][SLOT_ONE], Prop_Send, "m_iTeamNum", 1, 4); + + //SDKUnhook(g_iBBallHoop[i][SLOT_ONE], SDKHook_StartTouch, OnTouchHoop); + SDKHook(g_iBBallHoop[i][SLOT_ONE], SDKHook_StartTouch, OnTouchHoop); + } + + if (g_iBBallHoop[i][SLOT_TWO] == -1) + { + g_iBBallHoop[i][SLOT_TWO] = CreateEntityByName("item_ammopack_small"); + TeleportEntity(g_iBBallHoop[i][SLOT_TWO], hoop_2_loc, NULL_VECTOR, NULL_VECTOR); + DispatchSpawn(g_iBBallHoop[i][SLOT_TWO]); + SetEntProp(g_iBBallHoop[i][SLOT_TWO], Prop_Send, "m_iTeamNum", 1, 4); + + //SDKUnhook(g_iBBallHoop[i][SLOT_TWO], SDKHook_StartTouch, OnTouchHoop); + SDKHook(g_iBBallHoop[i][SLOT_TWO], SDKHook_StartTouch, OnTouchHoop); + } + + if(g_bVisibleHoops[i] == false) + { + // Could have used SetRenderMode here, but it had the unfortunate side-effect of also making the intel invisible. + // Luckily, inputting "Disable" to most entities makes them invisible, so it was a valid workaround. + AcceptEntityInput(g_iBBallHoop[i][SLOT_ONE], "Disable"); + AcceptEntityInput(g_iBBallHoop[i][SLOT_TWO], "Disable"); + } + } + + if(g_bArenaKoth[i]) + { + new Float:point_loc[3]; + point_loc[0] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]][0]; + point_loc[1] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]][1]; + point_loc[2] = g_fArenaSpawnOrigin[i][g_iArenaSpawns[i]][2]; + + if(IsValidEdict(g_iCapturePoint[i]) && g_iCapturePoint[i] > 0) + { + RemoveEdict(g_iCapturePoint[i]); + g_iCapturePoint[i] = -1; + } + // g_iCapturePoint[i] equaling -1 is not a bad thing, so don't print an error for it. + else if(g_iCapturePoint[i] != -1) + { + g_iCapturePoint[i] = -1; + } + + if (g_iCapturePoint[i] == -1) + { + g_iCapturePoint[i] = CreateEntityByName("item_ammopack_small"); + TeleportEntity(g_iCapturePoint[i], point_loc, NULL_VECTOR, NULL_VECTOR); + DispatchSpawn(g_iCapturePoint[i]); + SetEntProp(g_iCapturePoint[i], Prop_Send, "m_iTeamNum", 1, 4); + SetEntityModel(g_iCapturePoint[i], MODEL_POINT); + DispatchKeyValue(g_iCapturePoint[i], "powerup_model", MODEL_BRIEFCASE); + + //SDKUnhook(g_iCapturePoint[i], SDKHook_StartTouch, OnTouchPoint); + SDKHook(g_iCapturePoint[i], SDKHook_StartTouch, OnTouchPoint); + //SDKUnhook(g_iCapturePoint[i], SDKHook_EndTouch, OnEndTouchPoint); + SDKHook(g_iCapturePoint[i], SDKHook_EndTouch, OnEndTouchPoint); + } + + // Could have used SetRenderMode here, but it had the unfortunate side-effect of also making the intel invisible. + // Luckily, inputting "Disable" to most entities makes them invisible, so it was a valid workaround. + AcceptEntityInput(g_iCapturePoint[i], "Disable"); + + } + } + + return Plugin_Continue; +} + +/* +** ------------------------------------------------------------------ +** _______ +** /_ __(_)____ ___ ___ __________ +** / / / // __ `__ \/ _ \/ ___/ ___/ +** / / / // / / / / / __/ / (__ ) +** /_/ /_//_/ /_/ /_/\___/_/ /____/ +** +** ------------------------------------------------------------------ +**/ + +public Action:Timer_WelcomePlayer(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + + if(!IsValidClient(client)) + return; + + CPrintToChat(client, "%t", "Welcome1", PL_VERSION); + if(StrContains(g_sMapName, "mge_", false) == 0) + CPrintToChat(client, "%t", "Welcome2"); + CPrintToChat(client, "%t", "Welcome3"); + g_hWelcomeTimer[client] = INVALID_HANDLE; +} + +public Action:Timer_SpecFix(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + if(!IsValidClient(client)) + return; + + ChangeClientTeam(client, TEAM_RED); + ChangeClientTeam(client, TEAM_SPEC); +} + +public Action:Timer_SpecHudToAllArenas(Handle:timer, any:userid) +{ + for(new i = 1; i <= g_iArenaCount; i++) + ShowSpecHudToArena(i); + + return Plugin_Continue; +} + +public Action:Timer_ResetIntel(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + new arena_index = g_iPlayerArena[client]; + + ResetIntel(arena_index, client); +} + +public Action:Timer_AllowPlayerCap(Handle:timer, any:userid) +{ + g_bCanPlayerGetIntel[userid] = true; +} + +public Action:Timer_CountDown(Handle:timer, any:arena_index) +{ + new red_f1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu_f1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new red_f2; + new blu_f2; + if(g_bFourPersonArena[arena_index]) + { + red_f2 = g_iArenaQueue[arena_index][SLOT_THREE]; + blu_f2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + } + if(g_bFourPersonArena[arena_index]) + { + if (red_f1 && blu_f1 && red_f2 && blu_f2) + { + g_iArenaCd[arena_index]--; + + if (g_iArenaCd[arena_index]>0) + { // blocking +attack + new Float:enginetime = GetGameTime(); + + for (new i=0;i<=2;i++) + { + new ent = GetPlayerWeaponSlot(red_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+float(g_iArenaCd[arena_index])); + + ent = GetPlayerWeaponSlot(blu_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+float(g_iArenaCd[arena_index])); + + ent = GetPlayerWeaponSlot(red_f2, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+float(g_iArenaCd[arena_index])); + + ent = GetPlayerWeaponSlot(blu_f2, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+float(g_iArenaCd[arena_index])); + } + } + + if (g_iArenaCd[arena_index] <= 3 && g_iArenaCd[arena_index] >= 1) + { + new String:msg[64]; + + switch (g_iArenaCd[arena_index]) + { + case 1: msg = "ONE"; + case 2: msg = "TWO"; + case 3: msg = "THREE"; + } + + PrintCenterText(red_f1,msg); + PrintCenterText(blu_f1,msg); + PrintCenterText(red_f2,msg); + PrintCenterText(blu_f2,msg); + ShowCountdownToSpec(arena_index,msg); + g_iArenaStatus[arena_index] = AS_COUNTDOWN; + } else if (g_iArenaCd[arena_index] <= 0) { + g_iArenaStatus[arena_index] = AS_FIGHT; + new String:msg[64]; + Format(msg,sizeof(msg),"FIGHT",g_iArenaCd[arena_index]); + PrintCenterText(red_f1,msg); + PrintCenterText(blu_f1,msg); + PrintCenterText(red_f2,msg); + PrintCenterText(blu_f2,msg); + ShowCountdownToSpec(arena_index,msg); + + //For bball. + if(g_bArenaBBall[arena_index]) + { + ResetIntel(arena_index); + } + + return Plugin_Stop; + } + + + CreateTimer(1.0,Timer_CountDown,arena_index,TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + return Plugin_Stop; + } else { + g_iArenaStatus[arena_index] = AS_IDLE; + g_iArenaCd[arena_index] = 0; + return Plugin_Stop; + } + } + else + { + if (red_f1 && blu_f1) + { + g_iArenaCd[arena_index]--; + + if (g_iArenaCd[arena_index]>0) + { // blocking +attack + new Float:enginetime = GetGameTime(); + + for (new i=0;i<=2;i++) + { + new ent = GetPlayerWeaponSlot(red_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+float(g_iArenaCd[arena_index])); + + ent = GetPlayerWeaponSlot(blu_f1, i); + + if(IsValidEntity(ent)) + SetEntPropFloat(ent, Prop_Send, "m_flNextPrimaryAttack", enginetime+float(g_iArenaCd[arena_index])); + } + } + + if (g_iArenaCd[arena_index] <= 3 && g_iArenaCd[arena_index] >= 1) + { + new String:msg[64]; + + switch (g_iArenaCd[arena_index]) + { + case 1: msg = "ONE"; + case 2: msg = "TWO"; + case 3: msg = "THREE"; + } + + PrintCenterText(red_f1,msg); + PrintCenterText(blu_f1,msg); + ShowCountdownToSpec(arena_index,msg); + g_iArenaStatus[arena_index] = AS_COUNTDOWN; + } else if (g_iArenaCd[arena_index] <= 0) { + g_iArenaStatus[arena_index] = AS_FIGHT; + new String:msg[64]; + Format(msg,sizeof(msg),"FIGHT",g_iArenaCd[arena_index]); + PrintCenterText(red_f1,msg); + PrintCenterText(blu_f1,msg); + ShowCountdownToSpec(arena_index,msg); + + //For bball. + if(g_bArenaBBall[arena_index]) + { + ResetIntel(arena_index); + } + return Plugin_Stop; + } + + CreateTimer(1.0,Timer_CountDown,arena_index,TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + return Plugin_Stop; + } else { + g_iArenaStatus[arena_index] = AS_IDLE; + g_iArenaCd[arena_index] = 0; + return Plugin_Stop; + } + } +} + +public Action:Timer_Tele(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + new arena_index = g_iPlayerArena[client]; + + if (!arena_index) + return; + + new player_slot = g_iPlayerSlot[client]; + if ((!g_bFourPersonArena[arena_index] && player_slot>SLOT_TWO) || (g_bFourPersonArena[arena_index] && player_slot>SLOT_FOUR)) + { + return; + } + + new Float:vel[3]={0.0,0.0,0.0}; + + // BBall and 2v2 arenas handle spawns differently, each team, has their own spawns. + if(g_bArenaBBall[arena_index]) + { + new random_int; + new offset_high, offset_low; + if(g_iPlayerSlot[client] == SLOT_ONE || g_iPlayerSlot[client] == SLOT_THREE) + { + offset_high = ((g_iArenaSpawns[arena_index] - 5) / 2); + random_int = GetRandomInt(1, offset_high); //The first half of the player spawns are for slot one and three. + } else { + offset_high = (g_iArenaSpawns[arena_index] - 5); + offset_low = (((g_iArenaSpawns[arena_index] - 5) / 2) + 1); + random_int = GetRandomInt(offset_low, offset_high); //The last 5 spawns are for the intel and trigger spawns, not players. + } + //PrintToServer("BB: %f %f %f",g_fArenaSpawnOrigin[arena_index][random_int][0],g_fArenaSpawnOrigin[arena_index][random_int][1],g_fArenaSpawnOrigin[arena_index][random_int][2]); + TeleportEntity(client,g_fArenaSpawnOrigin[arena_index][random_int],g_fArenaSpawnAngles[arena_index][random_int],vel); + EmitAmbientSound("items/spawn_item.wav", g_fArenaSpawnOrigin[arena_index][random_int], _, SNDLEVEL_NORMAL, _, 1.0); + ShowPlayerHud(client); + return; + } + else if(g_bArenaKoth[arena_index]) + { + new random_int; + new offset_high, offset_low; + if(g_iPlayerSlot[client] == SLOT_ONE || g_iPlayerSlot[client] == SLOT_THREE) + { + offset_high = ((g_iArenaSpawns[arena_index] - 1) / 2); + random_int = GetRandomInt(1, offset_high); //The first half of the player spawns are for slot one and three. + } else { + offset_high = (g_iArenaSpawns[arena_index] - 1); + offset_low = (((g_iArenaSpawns[arena_index] + 1) / 2)); + random_int = GetRandomInt(offset_low, offset_high); //The last spawn is for the point + } + //PrintToServer("KOTH: %f %f %f",g_fArenaSpawnOrigin[arena_index][random_int][0],g_fArenaSpawnOrigin[arena_index][random_int][1],g_fArenaSpawnOrigin[arena_index][random_int][2]); + TeleportEntity(client,g_fArenaSpawnOrigin[arena_index][random_int],g_fArenaSpawnAngles[arena_index][random_int],vel); + EmitAmbientSound("items/spawn_item.wav", g_fArenaSpawnOrigin[arena_index][random_int], _, SNDLEVEL_NORMAL, _, 1.0); + ShowPlayerHud(client); + return; + } + else if(g_bFourPersonArena[arena_index]) + { + new random_int; + new offset_high, offset_low; + if(g_iPlayerSlot[client] == SLOT_ONE || g_iPlayerSlot[client] == SLOT_THREE) + { + offset_high = ((g_iArenaSpawns[arena_index]) / 2); + random_int = GetRandomInt(1, offset_high); //The first half of the player spawns are for slot one and three. + } else { + offset_high = (g_iArenaSpawns[arena_index]); + offset_low = (((g_iArenaSpawns[arena_index]) / 2) + 1); + random_int = GetRandomInt(offset_low, offset_high); + } + //PrintToServer("4P: %f %f %f",g_fArenaSpawnOrigin[arena_index][random_int][0],g_fArenaSpawnOrigin[arena_index][random_int][1],g_fArenaSpawnOrigin[arena_index][random_int][2]); + TeleportEntity(client,g_fArenaSpawnOrigin[arena_index][random_int],g_fArenaSpawnAngles[arena_index][random_int],vel); + EmitAmbientSound("items/spawn_item.wav", g_fArenaSpawnOrigin[arena_index][random_int], _, SNDLEVEL_NORMAL, _, 1.0); + ShowPlayerHud(client); + return; + } + + // Create an array that can hold all the arena's spawns. + new RandomSpawn[g_iArenaSpawns[arena_index]+1]; + + // Fill the array with the spawns. + for(new i = 0; i < g_iArenaSpawns[arena_index]; i++) + RandomSpawn[i] = i+1; + + // Shuffle them into a random order. + SortIntegers(RandomSpawn, g_iArenaSpawns[arena_index], Sort_Random); + + + // Now when the array is gone through sequentially, it will still provide a random spawn. + new Float:besteffort_dist; + new besteffort_spawn; + for(new i = 0; i < g_iArenaSpawns[arena_index]; i++) + { + new client_slot = g_iPlayerSlot[client]; + new foe_slot = (client_slot==SLOT_ONE || client_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + if(foe_slot) + { + new Float:distance; + new foe = g_iArenaQueue[arena_index][foe_slot]; + if(IsValidClient(foe)) + { + new Float:foe_pos[3]; + GetClientAbsOrigin(foe, foe_pos); + distance = GetVectorDistance(foe_pos, g_fArenaSpawnOrigin[arena_index][RandomSpawn[i]]); + if(distance > g_fArenaMinSpawnDist[arena_index]) + { + //PrintToServer("%f %f %f", g_fArenaSpawnOrigin[arena_index][RandomSpawn[i]][0], g_fArenaSpawnOrigin[arena_index][RandomSpawn[i]][1], g_fArenaSpawnOrigin[arena_index][RandomSpawn[i]][2]); + TeleportEntity(client,g_fArenaSpawnOrigin[arena_index][RandomSpawn[i]],g_fArenaSpawnAngles[arena_index][RandomSpawn[i]],vel); + EmitAmbientSound("items/spawn_item.wav", g_fArenaSpawnOrigin[arena_index][RandomSpawn[i]], _, SNDLEVEL_NORMAL, _, 1.0); + ShowPlayerHud(client); + return; + } else if(distance > besteffort_dist){ + besteffort_dist = distance; + besteffort_spawn = i; + } + } + } + } + + if(besteffort_spawn) + { + // Couldn't find a spawn that was far enough away, so use the one that was the farthest. + //PrintToServer("BE: %f %f %f", g_fArenaSpawnOrigin[arena_index][besteffort_spawn][0], g_fArenaSpawnOrigin[arena_index][besteffort_spawn][1], g_fArenaSpawnOrigin[arena_index][besteffort_spawn][2]); + TeleportEntity(client,g_fArenaSpawnOrigin[arena_index][besteffort_spawn],g_fArenaSpawnAngles[arena_index][besteffort_spawn],vel); + EmitAmbientSound("items/spawn_item.wav", g_fArenaSpawnOrigin[arena_index][besteffort_spawn], _, SNDLEVEL_NORMAL, _, 1.0); + ShowPlayerHud(client); + return; + } else { + // No foe, so just pick a random spawn. + new random_int = GetRandomInt(1, g_iArenaSpawns[arena_index]); + //PrintToServer("NOT BE: %f %f %f",g_fArenaSpawnOrigin[arena_index][random_int][0],g_fArenaSpawnOrigin[arena_index][random_int][1],g_fArenaSpawnOrigin[arena_index][random_int][2]) ; + TeleportEntity(client,g_fArenaSpawnOrigin[arena_index][random_int],g_fArenaSpawnAngles[arena_index][random_int],vel); + EmitAmbientSound("items/spawn_item.wav", g_fArenaSpawnOrigin[arena_index][random_int], _, SNDLEVEL_NORMAL, _, 1.0); + ShowPlayerHud(client); + return; + } +} + +public Action:Timer_NewRound(Handle:timer, any:arena_index) +{ + StartCountDown(arena_index); +} + +public Action:Timer_StartDuel(Handle:timer, any:arena_index) +{ + ResetArena(arena_index); + + if(g_bArenaTurris[arena_index]) + { + CreateTimer(5.0,Timer_RegenArena,arena_index, TIMER_REPEAT); + } + if(g_bArenaKoth[arena_index]) + { + + g_bPlayerTouchPoint[arena_index][SLOT_ONE] = false; + g_bPlayerTouchPoint[arena_index][SLOT_TWO] = false; + g_bPlayerTouchPoint[arena_index][SLOT_THREE] = false; + g_bPlayerTouchPoint[arena_index][SLOT_FOUR] = false; + g_iKothTimer[arena_index][0] = 0; + g_iKothTimer[arena_index][1] = 0; + g_iKothTimer[arena_index][TEAM_RED] = g_iDefaultCapTime[arena_index]; + g_iKothTimer[arena_index][TEAM_BLU] = g_iDefaultCapTime[arena_index]; + g_iCappingTeam[arena_index] = NEUTRAL; + g_iPointState[arena_index] = NEUTRAL; + g_fTotalTime[arena_index] = 0; + g_fCappedTime[arena_index] = 0.0; + g_fKothCappedPercent[arena_index] = 0.0; + g_bOvertimePlayed[arena_index][TEAM_RED] = false; + g_bOvertimePlayed[arena_index][TEAM_BLU] = false; + g_tKothTimer[arena_index] = CreateTimer(1.0,Timer_CountDownKoth,arena_index, TIMER_REPEAT); + g_bTimerRunning[arena_index] = true; + } + + g_iArenaScore[arena_index][SLOT_ONE] = 0; + g_iArenaScore[arena_index][SLOT_TWO] = 0; + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_ONE]); + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_TWO]); + + if(g_bFourPersonArena[arena_index]) + { + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_THREE]); + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_FOUR]); + } + + + + ShowSpecHudToArena(arena_index); + + StartCountDown(arena_index); + +} + +public Action:Timer_ResetPlayer(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + + if (IsValidClient(client)) + ResetPlayer(client); +} + +public Action:Timer_ChangePlayerSpec(Handle:timer, any:player) +{ + if (IsValidClient(player) && !IsPlayerAlive(player)) + ChangeClientTeam(player, TEAM_SPEC); +} + +public Action:Timer_ChangeSpecTarget(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + + if (!client || !IsValidClient(client)) + return Plugin_Stop; + + new target = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); + + if (IsValidClient(target) && g_iPlayerArena[target]){ + g_iPlayerSpecTarget[client] = target; + ShowSpecHudToClient(client); + } else { + HideHud(client); + g_iPlayerSpecTarget[client] = 0; + } + + return Plugin_Stop; +} + +public Action:Timer_ShowAdv(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + + if (IsValidClient(client) && g_iPlayerArena[client]==0) + { + CPrintToChat(client,"%t","Adv"); + CreateTimer(15.0, Timer_ShowAdv, userid); + } + + return Plugin_Continue; +} + +public Action:Timer_GiveAmmo(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + if (!client || !IsValidEntity(client)) + return; + + g_bPlayerRestoringAmmo[client] = false; + + new weapon; + + if (g_iPlayerClip[client][SLOT_ONE] != -1) + { + weapon = GetPlayerWeaponSlot(client, 0); + + if (IsValidEntity(weapon)) + SetEntProp(weapon, Prop_Send, "m_iClip1", g_iPlayerClip[client][SLOT_ONE]); + } + + if (g_iPlayerClip[client][SLOT_TWO] != -1) + { + weapon = GetPlayerWeaponSlot(client, 1); + + if (IsValidEntity(weapon)) + SetEntProp(weapon, Prop_Send, "m_iClip1", g_iPlayerClip[client][SLOT_TWO]); + } + +} + +public Action:Timer_DeleteParticle(Handle:timer, any:particle) +{ + if (IsValidEdict(particle)) + { + new String:classname[64]; + GetEdictClassname(particle, classname, sizeof(classname)); + + if (StrEqual(classname, "info_particle_system", false)) + { + RemoveEdict(particle); + } + } +} + +public Action:Timer_AddBotInQueue(Handle:timer, Handle:pk) +{ + ResetPack(pk); + new client = GetClientOfUserId(ReadPackCell(pk)); + new arena_index = ReadPackCell(pk); + AddInQueue(client,arena_index); +} + +public Action:Timer_ResetSwap(Handle:timer, any:client) +{ + g_bCanPlayerSwap[client] = true; +} + +public Action:Timer_ReconnectToDB(Handle:timer) +{ + g_hDBReconnectTimer = INVALID_HANDLE; + + decl String:query[256]; + Format(query, sizeof(query), "SELECT rating FROM mgemod_stats LIMIT 1"); + SQL_TQuery(db, SQLDbConnTest, query); +} + +public Action:Timer_CountDownKoth(Handle:timer, any:arena_index) +{ + //If there was time spent on the point/time spent reverting the point add/remove perecent to the point for however long they were/n't standing on it + if(g_fCappedTime[arena_index] != 0) + { + if(g_fKothCappedPercent[arena_index] == 0 && g_fCappedTime[arena_index] > 0) + { + new red_1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu_1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new String:SoundFileTemp[64]; + new num = GetRandomInt(1, 3); + if(num == 1) + { + SoundFileTemp = "vo/announcer_control_point_warning.wav"; + } + else if(num == 2) + { + SoundFileTemp = "vo/announcer_control_point_warning2.wav"; + } + else + { + SoundFileTemp = "vo/announcer_control_point_warning3.wav"; + } + + if(g_iCappingTeam[arena_index] == TEAM_BLU) + { + if(IsValidClient(red_1)) + EmitSoundToClient(red_1, SoundFileTemp); + } + else + { + if(IsValidClient(blu_1)) + EmitSoundToClient(blu_1, SoundFileTemp); + } + + if(g_bFourPersonArena[arena_index]) + { + new red_2 = g_iArenaQueue[arena_index][SLOT_THREE]; + new blu_2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + if(g_iCappingTeam[arena_index] == TEAM_BLU) + { + if(IsValidClient(red_2)) + EmitSoundToClient(red_2, SoundFileTemp); + } + else + { + if(IsValidClient(blu_2)) + EmitSoundToClient(blu_2, SoundFileTemp); + } + } + + } + if(g_fTotalTime[arena_index] != 0) + { + new Float:cap = FloatDiv(g_fCappedTime[arena_index] * 8.4, float(g_fTotalTime[arena_index])); + if(!g_bArenaUltiduo[arena_index]) + cap = cap * 1.5; + g_fKothCappedPercent[arena_index] += cap; + } + + g_fCappedTime[arena_index] = 0.0; + } + g_fTotalTime[arena_index] = 0; + //If the cap is below 0 then reset it to 0 + if(g_fKothCappedPercent[arena_index] <= 0) + { + g_fKothCappedPercent[arena_index] = 0.0; + + if(g_iPointState[arena_index] == NEUTRAL) + g_iCappingTeam[arena_index] = NEUTRAL; + } + + if(g_fKothCappedPercent[arena_index] >= 100) + { + new red1; + new red2; + new blu1; + new blu2; + if(g_bFourPersonArena[arena_index]) + { + red1 = g_iArenaQueue[arena_index][SLOT_ONE]; + red2 = g_iArenaQueue[arena_index][SLOT_THREE]; + blu1 = g_iArenaQueue[arena_index][SLOT_TWO]; + blu2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + if(g_iPointState[arena_index] == TEAM_RED) + { + if(IsValidClient(red1)) + EmitSoundToClient(red1, "vo/announcer_we_lost_control.wav"); + if(IsValidClient(red2)) + EmitSoundToClient(red2, "vo/announcer_we_lost_control.wav"); + if(IsValidClient(blu1)) + EmitSoundToClient(blu1, "vo/announcer_we_captured_control.wav"); + if(IsValidClient(blu2)) + EmitSoundToClient(blu2, "vo/announcer_we_captured_control.wav"); + + g_iCappingTeam[arena_index] = TEAM_RED; + g_iPointState[arena_index] = TEAM_BLU; + } + + else if(g_iPointState[arena_index] == TEAM_BLU) + { + if(IsValidClient(red1)) + EmitSoundToClient(red1, "vo/announcer_we_captured_control.wav"); + if(IsValidClient(red2)) + EmitSoundToClient(red2, "vo/announcer_we_captured_control.wav"); + if(IsValidClient(blu1)) + EmitSoundToClient(blu1, "vo/announcer_we_lost_control.wav"); + if(IsValidClient(blu2)) + EmitSoundToClient(blu2, "vo/announcer_we_lost_control.wav"); + g_iCappingTeam[arena_index] = TEAM_BLU; + g_iPointState[arena_index] = TEAM_RED; + } + + + else + { + if(g_iCappingTeam[arena_index] == TEAM_RED) + { + EmitSoundToClient(red1, "vo/announcer_we_captured_control.wav"); + EmitSoundToClient(red2, "vo/announcer_we_captured_control.wav"); + g_iPointState[arena_index] = TEAM_RED; + g_iCappingTeam[arena_index] = TEAM_BLU; + } + else + { + EmitSoundToClient(blu1, "vo/announcer_we_captured_control.wav"); + EmitSoundToClient(blu2, "vo/announcer_we_captured_control.wav"); + g_iPointState[arena_index] = TEAM_BLU; + g_iCappingTeam[arena_index] = TEAM_RED; + } + } + } + else + { + red1 = g_iArenaQueue[arena_index][SLOT_ONE]; + blu1 = g_iArenaQueue[arena_index][SLOT_TWO]; + if(g_iPointState[arena_index] == TEAM_RED) + { + EmitSoundToClient(red1, "vo/announcer_we_lost_control.wav"); + EmitSoundToClient(blu1, "vo/announcer_we_captured_control.wav"); + g_iCappingTeam[arena_index] = TEAM_RED; + g_iPointState[arena_index] = TEAM_BLU; + } + + else if(g_iPointState[arena_index] == TEAM_BLU) + { + EmitSoundToClient(red1, "vo/announcer_we_captured_control.wav"); + EmitSoundToClient(blu1, "vo/announcer_we_lost_control.wav"); + g_iCappingTeam[arena_index] = TEAM_BLU; + g_iPointState[arena_index] = TEAM_RED; + } + + + else + { + if(g_iCappingTeam[arena_index] == TEAM_RED) + { + EmitSoundToClient(red1, "vo/announcer_we_captured_control.wav"); + g_iPointState[arena_index] = TEAM_RED; + g_iCappingTeam[arena_index] = TEAM_BLU; + } + else + { + EmitSoundToClient(blu1, "vo/announcer_we_captured_control.wav"); + g_iPointState[arena_index] = TEAM_BLU; + g_iCappingTeam[arena_index] = TEAM_RED; + } + } + } + + g_fKothCappedPercent[arena_index] = 0.0; + } + else if(g_iKothTimer[arena_index][g_iPointState[arena_index]] > 0) + { + g_iKothTimer[arena_index][g_iPointState[arena_index]]--; + } + + + + if(g_iArenaQueue[arena_index][SLOT_ONE]) + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_ONE]); + if(g_iArenaQueue[arena_index][SLOT_ONE]) + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_TWO]); + + if(g_bFourPersonArena[arena_index]) + { + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_THREE]); + ShowPlayerHud(g_iArenaQueue[arena_index][SLOT_FOUR]); + } + + if(g_iArenaStatus[arena_index] > AS_FIGHT) + { + g_bTimerRunning[arena_index] = false; + return Plugin_Stop; + } + + //Play the count down sounds + if(g_iKothTimer[arena_index][g_iPointState[arena_index]] <= 5 && g_iKothTimer[arena_index][g_iPointState[arena_index]] > 0) + { + new String:SoundFile[64]; + switch(g_iKothTimer[arena_index][g_iPointState[arena_index]]) + { + case 5 : + SoundFile = "vo/announcer_ends_5sec.wav"; + case 4 : + SoundFile = "vo/announcer_ends_4sec.wav"; + case 3 : + SoundFile = "vo/announcer_ends_3sec.wav"; + case 2 : + SoundFile = "vo/announcer_ends_2sec.wav"; + case 1 : + SoundFile = "vo/announcer_ends_1sec.wav"; + default : + SoundFile = "vo/announcer_ends_5sec.wav"; + } + + if(g_bFourPersonArena[arena_index]) + { + new red1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new red2 = g_iArenaQueue[arena_index][SLOT_THREE]; + new blu1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new blu2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + EmitSoundToClient(blu1, SoundFile); + EmitSoundToClient(blu2, SoundFile); + EmitSoundToClient(red1, SoundFile); + EmitSoundToClient(red2, SoundFile); + } + else + { + new red1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu1 = g_iArenaQueue[arena_index][SLOT_TWO]; + EmitSoundToClient(blu1, SoundFile); + EmitSoundToClient(red1, SoundFile); + } + } + + //If the point is capped, the timer for the capped team is out and the other team is not touching the point and has no cap time on the point, end the game. + if(g_iPointState[arena_index] > NEUTRAL && g_iKothTimer[arena_index][g_iPointState[arena_index]] <= 0 && g_fKothCappedPercent[arena_index] <= 0 && !EnemyTeamTouching(g_iPointState[arena_index], arena_index)) + { + g_bTimerRunning[arena_index] = false; + //I know this is shit but fuck the police + EndKoth(arena_index, g_iPointState[arena_index] - 1); + return Plugin_Stop; + } + //If the time is at 0 and a team owns the point and OT hasn't been played already tell the arena it's OT + if(g_iPointState[arena_index] > NEUTRAL && g_iKothTimer[arena_index][g_iPointState[arena_index]] == 0) + { + //Fixes the infinite OT sound bug, so "Overtime!" only gets played once + if(!g_bOvertimePlayed[arena_index][g_iPointState[arena_index]]) + { + + new red1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new num = GetRandomInt(1, 4); + new String:SoundFileTemp[64]; + if(num == 1) + { + SoundFileTemp = "vo/announcer_overtime.wav"; + } + else if(num == 2) + { + SoundFileTemp = "vo/announcer_overtime2.wav"; + } + else if(num == 3) + { + SoundFileTemp = "vo/announcer_overtime3.wav"; + } + else + { + SoundFileTemp = "vo/announcer_overtime4.wav"; + } + EmitSoundToClient(blu1, SoundFileTemp); + EmitSoundToClient(red1, SoundFileTemp); + + + if(g_bFourPersonArena[arena_index]) + { + new blu2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + new red2 = g_iArenaQueue[arena_index][SLOT_THREE]; + EmitSoundToClient(red2, SoundFileTemp); + EmitSoundToClient(blu2, SoundFileTemp); + } + //the overtime sound has been played for this team and doesn't need to be played again for the rest of the round + g_bOvertimePlayed[arena_index][g_iPointState[arena_index]] = true; + } + } + + return Plugin_Continue; +} + +public Action:Timer_RegenArena(Handle:timer, any:arena_index) +{ + if(g_iArenaStatus[arena_index] != AS_FIGHT) + return Plugin_Stop; + + new client = g_iArenaQueue[arena_index][SLOT_ONE]; + new client2 = g_iArenaQueue[arena_index][SLOT_TWO]; + + if(IsPlayerAlive(client)) + { + TF2_RegeneratePlayer(client); + new raised_hp = RoundToNearest(float(g_iPlayerMaxHP[client])*g_fArenaHPRatio[arena_index]); + g_iPlayerHP[client] = raised_hp; + SetEntProp(client, Prop_Data, "m_iHealth", raised_hp); + } + + if(IsPlayerAlive(client2)) + { + TF2_RegeneratePlayer(client2); + new raised_hp2 = RoundToNearest(float(g_iPlayerMaxHP[client2])*g_fArenaHPRatio[arena_index]); + g_iPlayerHP[client2] = raised_hp2; + SetEntProp(client2, Prop_Data, "m_iHealth", raised_hp2); + } + + if(g_bFourPersonArena[arena_index]) + { + new client3 = g_iArenaQueue[arena_index][SLOT_THREE]; + new client4 = g_iArenaQueue[arena_index][SLOT_FOUR]; + if(IsPlayerAlive(client3)) + { + TF2_RegeneratePlayer(client3); + new raised_hp3 = RoundToNearest(float(g_iPlayerMaxHP[client3])*g_fArenaHPRatio[arena_index]); + g_iPlayerHP[client3] = raised_hp3; + SetEntProp(client3, Prop_Data, "m_iHealth", raised_hp3); + } + if(IsPlayerAlive(client4)) + { + TF2_RegeneratePlayer(client4); + new raised_hp4 = RoundToNearest(float(g_iPlayerMaxHP[client4])*g_fArenaHPRatio[arena_index]); + g_iPlayerHP[client4] = raised_hp4; + SetEntProp(client4, Prop_Data, "m_iHealth", raised_hp4); + } + } + + return Plugin_Continue; +} + +/* +** ------------------------------------------------------------------ +** __ ____ +** / |/ (_)__________ +** / /|_/ / // ___/ ___/ +** / / / / /(__ ) /__ +** /_/ /_/_//____/\___/ +** +** ------------------------------------------------------------------ +**/ + +/* TraceEntityFilterPlayer() + * + * Ignores players. + * -------------------------------------------------------------------------- */ +public bool:TraceEntityFilterPlayer(entity, contentsMask) +{ + return entity > MaxClients || !entity; +} + +/* TraceEntityPlayersOnly() + * + * Returns only players. + * -------------------------------------------------------------------------- */ +public bool:TraceEntityPlayersOnly(entity, mask, any:client) +{ + if (IsValidClient(entity) && entity != client) + { + PrintToChatAll("returning true for %d<%N>", entity, entity); + return true; + } else { + PrintToChatAll("returning false for %d<%N>", entity, entity); + return false; + } +} + +/* IsValidClient() + * + * Checks if a client is valid. + * -------------------------------------------------------------------------- */ +bool:IsValidClient(iClient) +{ + if(iClient < 1 || iClient > MaxClients) + return false; + if(!IsClientConnected(iClient)) + return false; + if(IsClientInKickQueue(iClient)) + return false; + if(IsClientSourceTV(iClient)) + return false; + return IsClientInGame(iClient); +} + +/* ShootsRocketsOrPipes() + * + * Does this player's gun shoot rockets or pipes? + * -------------------------------------------------------------------------- */ +bool:ShootsRocketsOrPipes(client) +{ + decl String:weapon[64]; + GetClientWeapon(client, weapon, sizeof(weapon)); + return (StrContains(weapon, "tf_weapon_rocketlauncher") == 0)|| StrEqual(weapon, "tf_weapon_grenadelauncher"); +} + +/* DistanceAboveGround() + * + * How high off the ground is the player? + * -------------------------------------------------------------------------- */ +Float:DistanceAboveGround(victim) +{ + decl Float:vStart[3]; + decl Float:vEnd[3]; + new Float:vAngles[3]={90.0,0.0,0.0}; + GetClientAbsOrigin(victim,vStart); + new Handle:trace = TR_TraceRayFilterEx(vStart, vAngles, MASK_PLAYERSOLID, RayType_Infinite,TraceEntityFilterPlayer); + + new Float:distance = -1.0; + if(TR_DidHit(trace)) + { + TR_GetEndPosition(vEnd, trace); + distance = GetVectorDistance(vStart, vEnd, false); + } else { + LogError("trace error. victim %N(%d)",victim,victim); + } + + CloseHandle(trace); + return distance; +} + +/* DistanceAboveGroundAroundUser() + * + * How high off the ground is the player? + *This is used for dropping + * -------------------------------------------------------------------------- */ +Float:DistanceAboveGroundAroundPlayer(victim) +{ + decl Float:vStart[3]; + decl Float:vEnd[3]; + new Float:vAngles[3]={90.0,0.0,0.0}; + GetClientAbsOrigin(victim,vStart); + new Float:minDist; + + for(new i = 0; i < 5; ++i) + { + new Float: tvStart[3]; + tvStart = vStart; + new Float:tempDist = -1.0; + if(i == 0) + { + new Handle:trace = TR_TraceRayFilterEx(vStart, vAngles, MASK_PLAYERSOLID, RayType_Infinite,TraceEntityFilterPlayer); + + if(TR_DidHit(trace)) + { + TR_GetEndPosition(vEnd, trace); + minDist = GetVectorDistance(vStart, vEnd, false); + } else { + LogError("trace error. victim %N(%d)",victim,victim); + } + CloseHandle(trace); + } + else if(i == 1) + { + tvStart[0] = tvStart[0] + 10; + new Handle:trace = TR_TraceRayFilterEx(tvStart, vAngles, MASK_PLAYERSOLID, RayType_Infinite,TraceEntityFilterPlayer); + + if(TR_DidHit(trace)) + { + TR_GetEndPosition(vEnd, trace); + tempDist = GetVectorDistance(tvStart, vEnd, false); + } else { + LogError("trace error. victim %N(%d)",victim,victim); + } + CloseHandle(trace); + } + else if(i == 2) + { + tvStart[0] = tvStart[0] - 10; + new Handle:trace = TR_TraceRayFilterEx(tvStart, vAngles, MASK_PLAYERSOLID, RayType_Infinite,TraceEntityFilterPlayer); + + if(TR_DidHit(trace)) + { + TR_GetEndPosition(vEnd, trace); + tempDist = GetVectorDistance(tvStart, vEnd, false); + } else { + LogError("trace error. victim %N(%d)",victim,victim); + } + CloseHandle(trace); + } + else if(i == 3) + { + tvStart[1] = vStart[1] + 10; + new Handle:trace = TR_TraceRayFilterEx(tvStart, vAngles, MASK_PLAYERSOLID, RayType_Infinite,TraceEntityFilterPlayer); + + if(TR_DidHit(trace)) + { + TR_GetEndPosition(vEnd, trace); + tempDist = GetVectorDistance(tvStart, vEnd, false); + } else { + LogError("trace error. victim %N(%d)",victim,victim); + } + CloseHandle(trace); + } + else if(i == 4) + { + tvStart[1] = vStart[1] - 10; + new Handle:trace = TR_TraceRayFilterEx(tvStart, vAngles, MASK_PLAYERSOLID, RayType_Infinite,TraceEntityFilterPlayer); + + if(TR_DidHit(trace)) + { + TR_GetEndPosition(vEnd, trace); + tempDist = GetVectorDistance(tvStart, vEnd, false); + } else { + LogError("trace error. victim %N(%d)",victim,victim); + } + CloseHandle(trace); + } + + if((tempDist > -1 && tempDist < minDist) || minDist == -1) + { + minDist = tempDist; + } + } + + return minDist; +} + +/* FindEntityByClassname2() + * + * Finds entites, and won't error out when searching invalid entities. + * -------------------------------------------------------------------------- */ +stock FindEntityByClassname2(startEnt, const String:classname[]) +{ + /* If startEnt isn't valid shifting it back to the nearest valid one */ + while (startEnt > -1 && !IsValidEntity(startEnt)) startEnt--; + + return FindEntityByClassname(startEnt, classname); +} + +/* getTeammate() + * + * Gets a clients teammate if he's in a 4 player arena + * This can actually be replaced by g_iArenaQueue[SLOT_X] but I didn't realize that array existed, so YOLO + *---------------------------------------------------------------------*/ + public getTeammate(myClient, myClientSlot, arena_index) + { + + new client_teammate_slot; + + if(myClientSlot == SLOT_ONE) + { + client_teammate_slot = SLOT_THREE; + } + else if(myClientSlot == SLOT_TWO) + { + client_teammate_slot = SLOT_FOUR; + } + else if(myClientSlot == SLOT_THREE) + { + client_teammate_slot = SLOT_ONE; + } + else + { + client_teammate_slot = SLOT_TWO; + } + + new myClientTeammate = g_iArenaQueue[arena_index][client_teammate_slot]; + return myClientTeammate; + + } + +/* isPlayerWaiting() + * + * Gets if a client is waiting + *---------------------------------------------------------------------*/ +bool:isPlayerWaiting(myClient) +{ + return g_iPlayerWaiting[myClient]; +} + +/* EndUlitduo(any:arena_index, any:winner_team) +* +* Called when someone wins an ultiduo round +* --------------------------------------------------------------------------- */ +public EndKoth(any:arena_index, any:winner_team) +{ + + PlayEndgameSoundsToArena(arena_index, winner_team); + g_iArenaScore[arena_index][winner_team] += 1; + new fraglimit = g_iArenaFraglimit[arena_index]; + new client = g_iArenaQueue[arena_index][winner_team]; + new client_slot = winner_team; + new foe_slot = (client_slot==SLOT_ONE || client_slot==SLOT_THREE) ? SLOT_TWO : SLOT_ONE; + new foe = g_iArenaQueue[arena_index][foe_slot]; + new client_teammate; + new foe_teammate; + + //End the Timer if its still running + //You shouldn't need to do this, but just incase + if(g_bTimerRunning[arena_index]) + { + KillTimer(g_tKothTimer[arena_index]); + g_bTimerRunning[arena_index] = false; + } + + if(g_bFourPersonArena[arena_index]) + { + client_teammate = getTeammate(client, client_slot, arena_index); + foe_teammate = getTeammate(foe, foe_slot, arena_index); + } + + if (fraglimit>0 && g_iArenaScore[arena_index][winner_team] >= fraglimit && g_iArenaStatus[arena_index] >= AS_FIGHT && g_iArenaStatus[arena_index] < AS_REPORTED) + { + g_iArenaStatus[arena_index] = AS_REPORTED; + new String:foe_name[MAX_NAME_LENGTH]; + GetClientName(foe, foe_name, sizeof(foe_name)); + new String:client_name[MAX_NAME_LENGTH]; + GetClientName(client, client_name, sizeof(client_name)); + + if(g_bFourPersonArena[arena_index]) + { + new String:client_teammate_name[128]; + new String:foe_teammate_name[128]; + + GetClientName(client_teammate,client_teammate_name, sizeof(client_teammate_name)); + GetClientName(foe_teammate,foe_teammate_name, sizeof(foe_teammate_name)); + + Format(client_name, sizeof(client_name), "%s and %s", client_name, client_teammate_name); + Format(foe_name, sizeof(foe_name), "%s and %s", foe_name, foe_teammate_name); + } + + CPrintToChatAll("%t","XdefeatsY", client_name, g_iArenaScore[arena_index][winner_team], foe_name, g_iArenaScore[arena_index][foe_slot], fraglimit, g_sArenaName[arena_index]); + + if (!g_bNoStats && !g_bFourPersonArena[arena_index]) + CalcELO(client,foe); + + else if(!g_bNoStats) + CalcELO2(client, client_teammate, foe, foe_teammate); + + if(g_bFourPersonArena[arena_index] && g_iArenaQueue[arena_index][SLOT_FOUR+1]) + { + RemoveFromQueue(foe,false); + RemoveFromQueue(foe_teammate,false); + AddInQueue(foe,arena_index,false); + AddInQueue(foe_teammate,arena_index,false); + } + else if (g_iArenaQueue[arena_index][SLOT_TWO+1]) + { + RemoveFromQueue(foe,false); + AddInQueue(foe,arena_index,false); + } else { + CreateTimer(3.0,Timer_StartDuel,arena_index); + } + } else { + ResetArena(arena_index); + + ResetPlayer(client); + ResetPlayer(foe); + + if(g_bFourPersonArena[arena_index]) + { + ResetPlayer(client_teammate); + ResetPlayer(foe_teammate); + } + + g_bPlayerTouchPoint[arena_index][SLOT_ONE] = false; + g_bPlayerTouchPoint[arena_index][SLOT_TWO] = false; + g_bPlayerTouchPoint[arena_index][SLOT_THREE] = false; + g_bPlayerTouchPoint[arena_index][SLOT_FOUR] = false; + g_iKothTimer[arena_index][TEAM_RED] = g_iDefaultCapTime[arena_index]; + g_iKothTimer[arena_index][TEAM_BLU] = g_iDefaultCapTime[arena_index]; + g_fKothCappedPercent[arena_index] = 0.0; + g_iCappingTeam[arena_index] = NEUTRAL; + g_iPointState[arena_index] = NEUTRAL; + g_fCappedTime[arena_index] = 0.0; + g_bOvertimePlayed[arena_index][TEAM_RED] = false; + g_bOvertimePlayed[arena_index][TEAM_BLU] = false; + g_tKothTimer[arena_index] = CreateTimer(1.0,Timer_CountDownKoth,arena_index,TIMER_REPEAT); + g_bTimerRunning[arena_index] = true; + } + + ShowPlayerHud(client); + ShowPlayerHud(foe); + + if(g_bFourPersonArena[arena_index]) + { + ShowPlayerHud(client_teammate); + ShowPlayerHud(foe_teammate); + } +} + +/* ResetArena(any:arena_index) +* +* Called when a round starts to reset medics ubercharge +* --------------------------------------------------------------------------- */ +public ResetArena(any:arena_index) +{ + //Tell the game this was a forced suicide and it shouldn't do anything about it + + new maxSlots; + if(g_bFourPersonArena[arena_index]) + maxSlots = SLOT_FOUR; + else + maxSlots = SLOT_TWO; + + for(new i = SLOT_ONE; i <= maxSlots; ++i) + { + if(IsValidClient(g_iArenaQueue[arena_index][i]) && IsPlayerAlive(g_iArenaQueue[arena_index][i]) && (g_tfctPlayerClass[g_iArenaQueue[arena_index][i]] == TF2_GetClass("medic"))) + { + new index = GetPlayerWeaponSlot(g_iArenaQueue[arena_index][i], 1); + SetEntPropFloat(index, Prop_Send, "m_flChargeLevel", 0.0); + } + } +} + +/* swapClasses(any:client, any:client_teammate) +* +* Called when players want to swap classes in an ultiduo arena +* --------------------------------------------------------------------------- */ +public swapClasses(any:client, any:client_teammate) +{ + + new TFClassType:client_class = g_tfctPlayerClass[client]; + new TFClassType:client_teammate_class = g_tfctPlayerClass[client_teammate]; + + ForcePlayerSuicide(client); + TF2_SetPlayerClass(client, client_teammate_class, false,true); + ForcePlayerSuicide(client_teammate); + TF2_SetPlayerClass(client_teammate, client_class, false, true); + + g_tfctPlayerClass[client] = client_teammate_class; + g_tfctPlayerClass[client_teammate] = client_class; + +} + +/* EnemyTeamTouching(any:team) +* +* Returns of a player on the other team is touching the point +* --------------------------------------------------------------------------- */ +public bool:EnemyTeamTouching(any:team, any:arena_index) +{ + if(team == TEAM_RED) + { + if(g_bPlayerTouchPoint[arena_index][SLOT_TWO]) + return true; + else if(g_bFourPersonArena[arena_index] && g_bPlayerTouchPoint[arena_index][SLOT_FOUR]) + return true; + else + return false; + } + else + { + if(g_bPlayerTouchPoint[arena_index][SLOT_ONE]) + return true; + else if(g_bFourPersonArena[arena_index] && g_bPlayerTouchPoint[arena_index][SLOT_THREE]) + return true; + else + return false; + } +} + + +public PlayEndgameSoundsToArena(any:arena_index, any:winner_team) +{ + new red_1 = g_iArenaQueue[arena_index][SLOT_ONE]; + new blu_1 = g_iArenaQueue[arena_index][SLOT_TWO]; + new String:SoundFileBlu[124]; + new String:SoundFileRed[124]; + + //If the red team won + if(winner_team == 1) + { + SoundFileRed = "vo/announcer_victory.wav"; + SoundFileBlu = "vo/announcer_you_failed.wav"; + } + //Else the blu team won + else + { + SoundFileBlu = "vo/announcer_victory.wav"; + SoundFileRed = "vo/announcer_you_failed.wav"; + } + if(IsValidClient(red_1)) + EmitSoundToClient(red_1, SoundFileRed); + + if(IsValidClient(blu_1)) + EmitSoundToClient(blu_1, SoundFileBlu); + + if(g_bFourPersonArena[arena_index]) + { + new red_2 = g_iArenaQueue[arena_index][SLOT_THREE]; + new blu_2 = g_iArenaQueue[arena_index][SLOT_FOUR]; + if(g_iCappingTeam[arena_index] == TEAM_BLU) + { + if(IsValidClient(red_2)) + EmitSoundToClient(red_2, SoundFileRed); + } + else + { + if(IsValidClient(blu_2)) + EmitSoundToClient(blu_2, SoundFileBlu); + } + } +} diff --git a/SqlUserConnection/sql_userconnections.inc b/SqlUserConnection/sql_userconnections.inc new file mode 100644 index 0000000..43acfaa --- /dev/null +++ b/SqlUserConnection/sql_userconnections.inc @@ -0,0 +1,30 @@ +#if defined _usercontrol_included + #endinput +#endif +#define _usercontrol_included + +public SharedPlugin:__pl_usercontrol = { + name = "SQL User Control", + file = "sql_userconnections.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_usercontrol_SetNTVOptional(){ + + MarkNativeAsOptional("UserControl_PlayedTime"); + +} +#endif + +/** + * Returns played time on current map + * + * @param client Client Index. + * @return Seconds on server on current map. + */ +native Int:UserControl_PlayedTime(client); \ No newline at end of file diff --git a/sql_userconnections.sp b/SqlUserConnection/sql_userconnections.sp similarity index 99% rename from sql_userconnections.sp rename to SqlUserConnection/sql_userconnections.sp index ad3a14e..a1b36bb 100644 --- a/sql_userconnections.sp +++ b/SqlUserConnection/sql_userconnections.sp @@ -133,6 +133,8 @@ stock void GetPlayTimeTotalCallback(Handle owner, Handle hndl, char [] error, an } else { iPlayedTime[client] = -1; } + + LogMessage("Total gametime on %N is %d seconds", client, iPlayedTime[client]); } public void OnClientDisconnect(int client) diff --git a/plugins/fsb.smx b/plugins/fsb.smx index 3ccc0b5..152c607 100644 Binary files a/plugins/fsb.smx and b/plugins/fsb.smx differ diff --git a/plugins/hextags.smx b/plugins/hextags.smx new file mode 100644 index 0000000..6b0237e Binary files /dev/null and b/plugins/hextags.smx differ diff --git a/plugins/mgemod.smx b/plugins/mgemod.smx new file mode 100644 index 0000000..da3ddb2 Binary files /dev/null and b/plugins/mgemod.smx differ