commit 527e7ec05e907255f3dfe177ca034bbc19d2239b Author: xen <24222257+xen-000@users.noreply.github.com> Date: Wed Mar 25 20:04:16 2020 +0200 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..043bc3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/include +/spcomp +/spcomp.exe diff --git a/AdvancedTargeting/scripting/AdvancedTargeting.sp b/AdvancedTargeting/scripting/AdvancedTargeting.sp new file mode 100644 index 0000000..dd2b59d --- /dev/null +++ b/AdvancedTargeting/scripting/AdvancedTargeting.sp @@ -0,0 +1,610 @@ +#pragma semicolon 1 + +#pragma dynamic 128*1024 + +#include +#include +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include +#include +#define REQUIRE_PLUGIN + +#undef REQUIRE_EXTENSIONS +#tryinclude +#define REQUIRE_EXTENSIONS + +#pragma newdecls required + +Handle g_FriendsArray[MAXPLAYERS + 1] = {INVALID_HANDLE, ...}; + +bool g_Plugin_voiceannounce_ex = false; +bool g_Extension_Voice = false; +bool g_bLateLoad = false; + +#include //#define STEAM_API_KEY here + +public Plugin myinfo = +{ + name = "Advanced Targeting", + author = "BotoX + Obus", + description = "Adds extra targeting methods", + version = "1.3", + url = "https://github.com/CSSZombieEscape/sm-plugins/tree/master/AdvancedTargeting/" +} + +public void OnPluginStart() +{ + AddMultiTargetFilter("@admins", Filter_Admin, "Admins", false); + AddMultiTargetFilter("@!admins", Filter_NotAdmin, "Not Admins", false); + AddMultiTargetFilter("@friends", Filter_Friends, "Steam Friends", false); + AddMultiTargetFilter("@!friends", Filter_NotFriends, "Not Steam Friends", false); + AddMultiTargetFilter("@random", Filter_Random, "a Random Player", false); + AddMultiTargetFilter("@randomct", Filter_RandomCT, "a Random CT", false); + AddMultiTargetFilter("@randomt", Filter_RandomT, "a Random T", false); + AddMultiTargetFilter("@alivect", Filter_AliveCT, "Alive Humans", false); + AddMultiTargetFilter("@alivet", Filter_AliveT, "Alive Zombies", false); + AddMultiTargetFilter("@talking", Filter_Talking, "Talking", false); + AddMultiTargetFilter("@!talking", Filter_NotTalking, "Not Talking", false); + AddMultiTargetFilter("@speaking", Filter_Talking, "Talking", false); + AddMultiTargetFilter("@!speaking", Filter_NotTalking, "Not Talking", false); + + RegConsoleCmd("sm_admins", Command_Admins, "Currently online admins."); + RegConsoleCmd("sm_friends", Command_Friends, "Currently online friends."); + + if(g_bLateLoad) + { + char sSteam32ID[32]; + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && IsClientAuthorized(i) && + GetClientAuthId(i, AuthId_Steam2, sSteam32ID, sizeof(sSteam32ID))) + { + OnClientAuthorized(i, sSteam32ID); + } + } + } +} + +public void OnPluginEnd() +{ + RemoveMultiTargetFilter("@admins", Filter_Admin); + RemoveMultiTargetFilter("@!admins", Filter_NotAdmin); + RemoveMultiTargetFilter("@friends", Filter_Friends); + RemoveMultiTargetFilter("@!friends", Filter_NotFriends); + RemoveMultiTargetFilter("@random", Filter_Random); + RemoveMultiTargetFilter("@randomct", Filter_RandomCT); + RemoveMultiTargetFilter("@randomt", Filter_RandomT); + RemoveMultiTargetFilter("@alivect", Filter_AliveCT); + RemoveMultiTargetFilter("@alivet", Filter_AliveT); + RemoveMultiTargetFilter("@talking", Filter_Talking); + RemoveMultiTargetFilter("@!talking", Filter_NotTalking); + RemoveMultiTargetFilter("@speaking", Filter_Talking); + RemoveMultiTargetFilter("@!speaking", Filter_NotTalking); +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + CreateNative("IsClientFriend", Native_IsClientFriend); + CreateNative("ReadClientFriends", Native_ReadClientFriends); + RegPluginLibrary("AdvancedTargeting"); + + g_bLateLoad = late; + return APLRes_Success; +} + +public void OnAllPluginsLoaded() +{ + g_Plugin_voiceannounce_ex = LibraryExists("voiceannounce_ex"); + g_Extension_Voice = LibraryExists("Voice"); + + LogMessage("AdvancedTargeting capabilities:\nVoiceAnnounce: %s\nVoice: %s", + (g_Plugin_voiceannounce_ex ? "loaded" : "not loaded"), + (g_Extension_Voice ? "loaded" : "not loaded")); +} + +public void OnLibraryAdded(const char[] name) +{ + if(StrEqual(name, "voiceannounce_ex")) + g_Plugin_voiceannounce_ex = true; + else if(StrEqual(name, "Voice")) + g_Extension_Voice = true; +} + +public void OnLibraryRemoved(const char[] name) +{ + if(StrEqual(name, "voiceannounce_ex")) + g_Plugin_voiceannounce_ex = false; + else if(StrEqual(name, "Voice")) + g_Extension_Voice = false; +} + +public Action Command_Admins(int client, int args) +{ + char aBuf[1024]; + char aBuf2[MAX_NAME_LENGTH]; + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && GetAdminFlag(GetUserAdmin(i), Admin_Generic)) + { + GetClientName(i, aBuf2, sizeof(aBuf2)); + StrCat(aBuf, sizeof(aBuf), aBuf2); + StrCat(aBuf, sizeof(aBuf), ", "); + } + } + + if(strlen(aBuf)) + { + aBuf[strlen(aBuf) - 2] = 0; + ReplyToCommand(client, "[SM] Admins currently online: %s", aBuf); + } + else + ReplyToCommand(client, "[SM] Admins currently online: none"); + + return Plugin_Handled; +} + +public Action Command_Friends(int client, int args) +{ + if(!client) + return Plugin_Handled; + + if(g_FriendsArray[client] == INVALID_HANDLE) + { + PrintToChat(client, "[SM] Could not read your friendslist, your profile must be set to public!"); + return Plugin_Handled; + } + + char aBuf[1024]; + char aBuf2[MAX_NAME_LENGTH]; + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && IsClientAuthorized(i)) + { + int Steam3ID = GetSteamAccountID(i); + + if(FindValueInArray(g_FriendsArray[client], Steam3ID) != -1) + { + GetClientName(i, aBuf2, sizeof(aBuf2)); + StrCat(aBuf, sizeof(aBuf), aBuf2); + StrCat(aBuf, sizeof(aBuf), ", "); + } + } + } + + if(strlen(aBuf)) + { + aBuf[strlen(aBuf) - 2] = 0; + PrintToChat(client, "[SM] Friends currently online: %s", aBuf); + } + else + PrintToChat(client, "[SM] Friends currently online: none"); + + return Plugin_Handled; +} + +public bool Filter_AliveCT(const char[] sPattern, Handle hClients, int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == CS_TEAM_CT && IsPlayerAlive(i)) + { + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_AliveT(const char[] sPattern, Handle hClients, int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == CS_TEAM_T && IsPlayerAlive(i)) + { + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_Admin(const char[] sPattern, Handle hClients, int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && GetAdminFlag(GetUserAdmin(i), Admin_Generic)) + { + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_NotAdmin(const char[] sPattern, Handle hClients, int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && !GetAdminFlag(GetUserAdmin(i), Admin_Generic)) + { + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_Friends(const char[] sPattern, Handle hClients, int client) +{ + if(g_FriendsArray[client] == INVALID_HANDLE) + { + PrintToChat(client, "[SM] Could not read your friendslist, your profile must be set to public!"); + return false; + } + + for(int i = 1; i <= MaxClients; i++) + { + if(i != client && IsClientInGame(i) && !IsFakeClient(i) && IsClientAuthorized(i)) + { + int Steam3ID = GetSteamAccountID(i); + + if(FindValueInArray(g_FriendsArray[client], Steam3ID) != -1) + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_NotFriends(const char[] sPattern, Handle hClients, int client) +{ + if(g_FriendsArray[client] == INVALID_HANDLE) + { + PrintToChat(client, "[SM] Could not read your friendslist, your profile must be set to public!"); + return false; + } + + for(int i = 1; i <= MaxClients; i++) + { + if(i != client && IsClientInGame(i) && !IsFakeClient(i) && IsClientAuthorized(i)) + { + int Steam3ID = GetSteamAccountID(i); + + if(FindValueInArray(g_FriendsArray[client], Steam3ID) == -1) + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_Random(const char[] sPattern, Handle hClients, int client) +{ + int iRand = GetRandomInt(1, MaxClients); + + if(IsClientInGame(iRand) && IsPlayerAlive(iRand)) + PushArrayCell(hClients, iRand); + else + Filter_Random(sPattern, hClients, client); + + return true; +} + +public bool Filter_RandomCT(const char[] sPattern, Handle hClients, int client) +{ + int iCTCount = GetTeamClientCount(CS_TEAM_CT); + + if(!iCTCount) + return false; + + int[] iCTs = new int[iCTCount]; + + int iCurIndex; + + for(int i = 1; i <= MaxClients; i++) + { + if(!IsClientInGame(i) || GetClientTeam(i) != CS_TEAM_CT) + continue; + + if(!IsPlayerAlive(i)) + { + iCTCount--; + continue; + } + + iCTs[iCurIndex] = i; + iCurIndex++; + } + + PushArrayCell(hClients, iCTs[GetRandomInt(0, iCTCount-1)]); + + return true; +} + +public bool Filter_RandomT(const char[] sPattern, Handle hClients, int client) +{ + int iTCount = GetTeamClientCount(CS_TEAM_T); + + if(!iTCount) + return false; + + int[] iTs = new int[iTCount]; + + int iCurIndex; + + for(int i = 1; i <= MaxClients; i++) + { + if(!IsClientInGame(i) || GetClientTeam(i) != CS_TEAM_T) + continue; + + if(!IsPlayerAlive(i)) + { + iTCount--; + continue; + } + + iTs[iCurIndex] = i; + iCurIndex++; + } + + PushArrayCell(hClients, iTs[GetRandomInt(0, iTCount-1)]); + + return true; +} + +stock bool _IsClientSpeaking(int client) +{ + #if defined _voiceannounceex_included_ + if(g_Plugin_voiceannounce_ex) + return IsClientSpeaking(client); + #endif + + #if defined _voice_included + if(g_Extension_Voice) + return IsClientTalking(client); + #endif + + return false; +} + +public bool Filter_Talking(const char[] sPattern, Handle hClients, int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && _IsClientSpeaking(i)) + { + PushArrayCell(hClients, i); + } + } + + return true; +} + +public bool Filter_NotTalking(const char[] sPattern, Handle hClients, int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && !_IsClientSpeaking(i)) + { + PushArrayCell(hClients, i); + } + } + + return true; +} + +public void OnClientAuthorized(int client, const char[] auth) +{ + if(IsFakeClient(client)) + return; + + char sSteam64ID[32]; + Steam32IDtoSteam64ID(auth, sSteam64ID, sizeof(sSteam64ID)); + + static char sRequest[256]; + FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamUser/GetFriendList/v0001/?key=%s&steamid=%s&relationship=friend&format=vdf", STEAM_API_KEY, sSteam64ID); + + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); + if (!hRequest || + !SteamWorks_SetHTTPRequestContextValue(hRequest, client) || + !SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + CloseHandle(hRequest); + } +} + +public void OnClientDisconnect(int client) +{ + if(g_FriendsArray[client] != INVALID_HANDLE) + CloseHandle(g_FriendsArray[client]); + + g_FriendsArray[client] = INVALID_HANDLE; +} + +public int OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, int client) +{ + if(bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) + { + // Private profile or maybe steam down? + //LogError("SteamAPI HTTP Response failed: %d", eStatusCode); + CloseHandle(hRequest); + return; + } + + int Length; + SteamWorks_GetHTTPResponseBodySize(hRequest, Length); + + char[] sData = new char[Length]; + SteamWorks_GetHTTPResponseBodyData(hRequest, sData, Length); + //SteamWorks_GetHTTPResponseBodyCallback(hRequest, APIWebResponse, client); + + CloseHandle(hRequest); + + APIWebResponse(sData, client); +} + +public void APIWebResponse(const char[] sData, int client) +{ + KeyValues Response = new KeyValues("SteamAPIResponse"); + if(!Response.ImportFromString(sData, "SteamAPIResponse")) + { + LogError("ImportFromString(sData, \"SteamAPIResponse\") failed."); + delete Response; + return; + } + + if(!Response.JumpToKey("friends")) + { + LogError("JumpToKey(\"friends\") failed."); + delete Response; + return; + } + + // No friends? + if(!Response.GotoFirstSubKey()) + { + //LogError("GotoFirstSubKey() failed."); + delete Response; + return; + } + + if(g_FriendsArray[client] != INVALID_HANDLE) + CloseHandle(g_FriendsArray[client]); + + g_FriendsArray[client] = CreateArray(); + + char sCommunityID[32]; + do + { + Response.GetString("steamid", sCommunityID, sizeof(sCommunityID)); + + PushArrayCell(g_FriendsArray[client], Steam64toSteam3(sCommunityID)); + } + while(Response.GotoNextKey()); + + delete Response; +} + + +stock bool Steam32IDtoSteam64ID(const char[] sSteam32ID, char[] sSteam64ID, int Size) +{ + if(strlen(sSteam32ID) < 11 || strncmp(sSteam32ID[0], "STEAM_0:", 8)) + { + sSteam64ID[0] = 0; + return false; + } + + int iUpper = 765611979; + int isSteam64ID = StringToInt(sSteam32ID[10]) * 2 + 60265728 + sSteam32ID[8] - 48; + + int iDiv = isSteam64ID / 100000000; + int iIdx = 9 - (iDiv ? (iDiv / 10 + 1) : 0); + iUpper += iDiv; + + IntToString(isSteam64ID, sSteam64ID[iIdx], Size - iIdx); + iIdx = sSteam64ID[9]; + IntToString(iUpper, sSteam64ID, Size); + sSteam64ID[9] = iIdx; + + return true; +} + +stock int Steam64toSteam3(const char[] sSteam64ID) +{ + if(strlen(sSteam64ID) != 17) + return 0; + + // convert SteamID64 to array of integers + int aSteam64ID[17]; + for(int i = 0; i < 17; i++) + aSteam64ID[i] = sSteam64ID[i] - 48; + + // subtract individual SteamID64 identifier (0x0110000100000000) + int aSteam64IDIdent[] = {7, 6, 5, 6, 1, 1, 9, 7, 9, 6, 0, 2, 6, 5, 7, 2, 8}; + int Carry = 0; + for(int i = 16; i >= 0; i--) + { + if(aSteam64ID[i] < aSteam64IDIdent[i] + Carry) + { + aSteam64ID[i] = aSteam64ID[i] - aSteam64IDIdent[i] - Carry + 10; + Carry = 1; + } + else + { + aSteam64ID[i] = aSteam64ID[i] - aSteam64IDIdent[i] - Carry; + Carry = 0; + } + } + + char aBuf[17]; + int j = 0; + bool ZereosDone = false; + for(int i = 0; i < 17; i++) + { + if(!ZereosDone && !aSteam64ID[i]) + continue; + ZereosDone = true; + + aBuf[j++] = aSteam64ID[i] + 48; + } + + return StringToInt(aBuf); +} + +public int Native_IsClientFriend(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + int friend = GetNativeCell(2); + + if(client > MaxClients || client <= 0 || friend > MaxClients || friend <= 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); + return -1; + } + + if(!IsClientInGame(client) || !IsClientInGame(friend)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); + return -1; + } + + if(IsFakeClient(client) || IsFakeClient(friend)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is fake-client."); + return -1; + } + + if(g_FriendsArray[client] == INVALID_HANDLE) + return -1; + + if(IsClientAuthorized(friend)) + { + int Steam3ID = GetSteamAccountID(friend); + + if(FindValueInArray(g_FriendsArray[client], Steam3ID) != -1) + return 1; + } + + return 0; +} + +public int Native_ReadClientFriends(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if(client > MaxClients || client <= 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); + return -1; + } + + if(g_FriendsArray[client] != INVALID_HANDLE) + return 1; + + return 0; +} diff --git a/AdvancedTargeting/scripting/include/AdvancedTargeting.inc b/AdvancedTargeting/scripting/include/AdvancedTargeting.inc new file mode 100644 index 0000000..8ffd3ad --- /dev/null +++ b/AdvancedTargeting/scripting/include/AdvancedTargeting.inc @@ -0,0 +1,26 @@ +#if defined _AdvancedTargeting_Included + #endinput +#endif +#define _AdvancedTargeting_Included + +native int IsClientFriend(int client, int friend); +native int ReadClientFriends(int client); + +public SharedPlugin __pl_AdvancedTargeting = +{ + name = "AdvancedTargeting", + file = "AdvancedTargeting.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_AdvancedTargeting_SetNTVOptional() +{ + MarkNativeAsOptional("IsClientFriend"); + MarkNativeAsOptional("ReadClientFriends"); +} +#endif diff --git a/AntiBhopCheat/scripting/AntiBhopCheat.sp b/AntiBhopCheat/scripting/AntiBhopCheat.sp new file mode 100644 index 0000000..4b2f3d6 --- /dev/null +++ b/AntiBhopCheat/scripting/AntiBhopCheat.sp @@ -0,0 +1,652 @@ +#include +#include +#include +#include + +#include +#include "CJump.inc" +#include "CStreak.inc" +#include "CPlayer.inc" + +#define MAX_STREAKS 5 +#define VALID_MIN_JUMPS 3 +#define VALID_MAX_TICKS 5 +#define VALID_MIN_VELOCITY 250 + +int g_aButtons[MAXPLAYERS + 1]; +bool g_bOnGround[MAXPLAYERS + 1]; +bool g_bHoldingJump[MAXPLAYERS + 1]; +bool g_bInJump[MAXPLAYERS + 1]; + +CPlayer g_aPlayers[MAXPLAYERS + 1]; + +// Api +Handle g_hOnClientDetected; + +public Plugin myinfo = +{ + name = "AntiBhopCheat", + author = "BotoX", + description = "Detect all kinds of bhop cheats", + version = "0.0", + url = "" +}; + + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + + RegAdminCmd("sm_stats", Command_Stats, ADMFLAG_GENERIC, "sm_stats <#userid|name>"); + RegAdminCmd("sm_streak", Command_Streak, ADMFLAG_GENERIC, "sm_streak <#userid|name> [streak]"); + + /* Handle late load */ + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientConnected(client)) + { + if(IsClientConnected(client)) + OnClientConnected(client); + } + } + + // Api + g_hOnClientDetected = CreateGlobalForward("AntiBhopCheat_OnClientDetected", ET_Ignore, Param_Cell, Param_String, Param_String); +} + +public void OnClientConnected(int client) +{ + if(g_aPlayers[client]) + { + g_aPlayers[client].Dispose(); + g_aPlayers[client] = null; + } + + g_aPlayers[client] = new CPlayer(client); +} + +public void OnClientDisconnect(int client) +{ + if(g_aPlayers[client]) + { + g_aPlayers[client].Dispose(); + g_aPlayers[client] = null; + } + + g_bOnGround[client] = false; + g_bHoldingJump[client] = false; + g_bInJump[client] = false; +} + +public Action OnPlayerRunCmd(int client, int &buttons) +{ + g_aButtons[client] = buttons; + return Plugin_Continue; +} + +public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2]) +{ + if(!IsPlayerAlive(client)) + return; + + CPlayer Player = g_aPlayers[client]; + + MoveType ClientMoveType = GetEntityMoveType(client); + bool bInWater = GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2; + + bool bPrevOnGround = g_bOnGround[client]; + bool bOnGround = !bInWater && GetEntityFlags(client) & FL_ONGROUND; + + bool bPrevHoldingJump = g_bHoldingJump[client]; + bool bHoldingJump = view_as(g_aButtons[client] & IN_JUMP); + + bool bInJump = g_bInJump[client]; + + float fVecVelocity[3]; + GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fVecVelocity); + fVecVelocity[2] = 0.0; + float fVelocity = GetVectorLength(fVecVelocity); + + if(bInJump && (bInWater || ClientMoveType == MOVETYPE_LADDER || ClientMoveType == MOVETYPE_NOCLIP)) + bOnGround = true; + + if(bOnGround) + { + if(!bPrevOnGround) + { + g_bOnGround[client] = true; + g_bInJump[client] = false; + if(bInJump) + OnTouchGround(Player, tickcount, fVelocity); + } + } + else + { + if(bPrevOnGround) + g_bOnGround[client] = false; + } + + if(bHoldingJump) + { + if(!bPrevHoldingJump && !bOnGround && (bPrevOnGround || bInJump)) + { + g_bHoldingJump[client] = true; + g_bInJump[client] = true; + OnPressJump(Player, tickcount, fVelocity, bPrevOnGround); + } + } + else + { + if(bPrevHoldingJump) + { + g_bHoldingJump[client] = false; + OnReleaseJump(Player, tickcount, fVelocity); + } + } +} + +// TODO: Release after touch ground + +void OnTouchGround(CPlayer Player, int iTick, float fVelocity) +{ + //PrintToServer("%d - OnTouchGround", iTick); + + CStreak CurStreak = Player.hStreak; + ArrayList hJumps = CurStreak.hJumps; + CJump hJump = hJumps.Get(hJumps.Length - 1); + + hJump.iEndTick = iTick; + hJump.fEndVel = fVelocity; + + int iLength = hJumps.Length; + if(iLength == VALID_MIN_JUMPS) + { + CurStreak.bValid = true; + + // Current streak is valid, push onto hStreaks ArrayList + ArrayList hStreaks = Player.hStreaks; + if(hStreaks.Length == MAX_STREAKS) + { + // Keep the last 10 streaks + CStreak hStreak = hStreaks.Get(0); + hStreak.Dispose(); + hStreaks.Erase(0); + } + hStreaks.Push(CurStreak); + + for(int i = 0; i < iLength - 1; i++) + { + CJump hJump_ = hJumps.Get(i); + DoStats(Player, CurStreak, hJump_); + } + } + else if(iLength > VALID_MIN_JUMPS) + { + CJump hJump_ = hJumps.Get(hJumps.Length - 2); + DoStats(Player, CurStreak, hJump_); + } +} + +void OnPressJump(CPlayer Player, int iTick, float fVelocity, bool bLeaveGround) +{ + //PrintToServer("%d - OnPressJump %d", iTick, bLeaveGround); + + CStreak CurStreak = Player.hStreak; + ArrayList hJumps = CurStreak.hJumps; + CJump hJump; + + if(bLeaveGround) + { + int iPrevJump = -1; + // Check if we should start a new streak + if(hJumps.Length) + { + // Last jump was more than VALID_MAX_TICKS ticks ago or not valid and fVelocity < VALID_MIN_VELOCITY + hJump = hJumps.Get(hJumps.Length - 1); + if(hJump.iEndTick < iTick - VALID_MAX_TICKS || fVelocity < VALID_MIN_VELOCITY) + { + if(CurStreak.bValid) + { + CurStreak.iEndTick = hJump.iEndTick; + + DoStats(Player, CurStreak, hJump); + CheckStats(Player, CurStreak); + } + else + CurStreak.Dispose(); + + CurStreak = new CStreak(); + Player.hStreak = CurStreak; + hJumps = CurStreak.hJumps; + } + else + { + iPrevJump = iTick - hJump.iEndTick; + hJump.iNextJump = iPrevJump; + } + } + + hJump = new CJump(); + hJump.iStartTick = iTick; + hJump.fStartVel = fVelocity; + if(iPrevJump != -1) + hJump.iPrevJump = iPrevJump; + hJumps.Push(hJump); + } + else + hJump = hJumps.Get(hJumps.Length - 1); + + ArrayList hPresses = hJump.hPresses; + hPresses.Push(iTick); +} + +void OnReleaseJump(CPlayer Player, int iTick, float fVelocity) +{ + //PrintToServer("%d - OnReleaseJump", iTick); + + CStreak CurStreak = Player.hStreak; + ArrayList hJumps = CurStreak.hJumps; + CJump hJump = hJumps.Get(hJumps.Length - 1); + ArrayList hPresses = hJump.hPresses; + + hPresses.Set(hPresses.Length - 1, iTick, 1); +} + +void DoStats(CPlayer Player, CStreak CurStreak, CJump hJump) +{ + int aJumps[3] = {0, 0, 0}; + int iPresses = 0; + int iTicks = 0; + int iLastJunk = 0; + + CurStreak.iJumps++; + Player.iJumps++; + + ArrayList hPresses = hJump.hPresses; + int iStartTick = hJump.iStartTick; + int iEndTick = hJump.iEndTick; + int iPrevJump = hJump.iPrevJump; + int iNextJump = hJump.iNextJump; + + if(iPrevJump > 0) + { + int iPerf = iPrevJump - 1; + if(iPerf > 2) + iPerf = 2; + + aJumps[iPerf]++; + } + + iPresses = hPresses.Length; + iTicks = iEndTick - iStartTick; + iLastJunk = iEndTick - hPresses.Get(iPresses - 1, 1); + + float PressesPerTick = (iPresses * 4.0) / float(iTicks); + if(PressesPerTick >= 0.85) + { + CurStreak.iHyperJumps++; + Player.iHyperJumps++; + } + + if(iNextJump != -1 && iNextJump <= 1 && (iLastJunk > 5 || iPresses <= 2) && hJump.fEndVel >= 285.0) + { + CurStreak.iHackJumps++; + Player.iHackJumps++; + } + + int aGlobalJumps[3]; + Player.GetJumps(aGlobalJumps); + aGlobalJumps[0] += aJumps[0]; + aGlobalJumps[1] += aJumps[1]; + aGlobalJumps[2] += aJumps[2]; + Player.SetJumps(aGlobalJumps); + + int aStreakJumps[3]; + CurStreak.GetJumps(aStreakJumps); + aStreakJumps[0] += aJumps[0]; + aStreakJumps[1] += aJumps[1]; + aStreakJumps[2] += aJumps[2]; + CurStreak.SetJumps(aStreakJumps); +} + +void CheckStats(CPlayer Player, CStreak Streak) +{ + int client = Player.iClient; + int iStreakJumps = Streak.iJumps; + if(iStreakJumps >= 6) + { + float HackRatio = Streak.iHackJumps / float(iStreakJumps); + if(HackRatio >= 0.80) + { + Player.iHackFlagged += 1; + char sBuffer[32]; + Format(sBuffer, sizeof(sBuffer), "bhop hack streak %d\n", Player.iHackFlagged); + NotifyAdmins(client, sBuffer); + } + + float HyperRatio = Streak.iHyperJumps / float(iStreakJumps); + if(HyperRatio >= 0.80) + { + Player.iHyperFlagged += 1; + CPrintToChat(client, "{green}[SM]{default} Turn off your bhop macro/script or hyperscroll!"); + CPrintToChat(client, "{green}[SM]{default} Your bhop is {red}turned off{default} until you bhop legit again."); + LimitBhop(client, true); + } + else if(IsBhopLimited(client)) + { + LimitBhop(client, false); + CPrintToChat(client, "{green}[SM]{default} Your bhop is {green}turned on{default} again."); + } + } + + int iGlobalJumps = Player.iJumps; + if(iGlobalJumps >= 35) + { + float HackRatio = Player.iHackJumps / float(iGlobalJumps); + if(HackRatio >= 0.60 && !Player.bHackGlobal) + { + Player.bHackGlobal = true; + NotifyAdmins(client, "bhop hack global"); + } + + float HyperRatio = Player.iHyperJumps / float(iGlobalJumps); + if(HyperRatio >= 0.50) + { + if(!Player.bHyperGlobal) + { + Player.bHyperGlobal = true; + CPrintToChat(client, "{green}[SM]{default} Turn off your bhop macro/script or hyperscroll!"); + CPrintToChat(client, "{green}[SM]{default} Your bhop is {red}turned off{default} until you bhop legit again."); + LimitBhop(client, true); + } + } + else if(Player.bHyperGlobal && IsBhopLimited(client)) + { + LimitBhop(client, false); + CPrintToChat(client, "{green}[SM]{default} Your bhop is {green}turned on{default} again."); + } + } +} + +NotifyAdmins(int client, const char[] sReason) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i) && CheckCommandAccess(i, "sm_stats", ADMFLAG_GENERIC)) + { + CPrintToChat(i, "{green}[SM]{default} %L has been detected for {red}%s{default}, please check your console!", client, sReason); + FormatStats(i, client); + PrintToConsole(client, "%s", "\n"); + FormatStreak(i, client, 0); + } + } + + char sBuffer[2000]; + int len = FormatStats(-1, client, sBuffer, sizeof(sBuffer)); + sBuffer[len++] = '\n'; + len += FormatStreak(-1, client, 0, sBuffer[len], sizeof(sBuffer) - len); + + Forward_OnDetected(client, sReason, sBuffer); +} + +public Action Command_Stats(int client, int argc) +{ + if(argc < 1 || argc > 2) + { + ReplyToCommand(client, "[SM] Usage: sm_stats <#userid|name>"); + return Plugin_Handled; + } + + char sArg[65]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + + GetCmdArg(1, sArg, sizeof(sArg)); + + if((iTargetCount = ProcessTargetString(sArg, client, iTargets, MAXPLAYERS, COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + for(int i = 0; i < iTargetCount; i++) + { + FormatStats(client, iTargets[i]); + PrintToConsole(client, "%s", "\n"); + + for(int j = 0; j < 3; j++) + { + FormatStreak(client, iTargets[i], j); + PrintToConsole(client, "%s", "\n"); + } + } + + return Plugin_Handled; +} + +public Action Command_Streak(int client, int argc) +{ + if(argc < 1 || argc > 2) + { + ReplyToCommand(client, "[SM] Usage: sm_streak <#userid|name> [streak]"); + return Plugin_Handled; + } + + char sArg[65]; + char sArg2[8]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + int iStreak = -1; + + GetCmdArg(1, sArg, sizeof(sArg)); + + if(argc == 2) + { + GetCmdArg(2, sArg2, sizeof(sArg2)); + iStreak = StringToInt(sArg2); + } + + if((iTargetCount = ProcessTargetString(sArg, client, iTargets, MAXPLAYERS, COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + for(int i = 0; i < iTargetCount; i++) + { + FormatStreak(client, iTargets[i], iStreak); + } + + return Plugin_Handled; +} + +int FormatStats(int client, int iTarget, char[] sBuf=0, int len=0) +{ + int iBuf = ConsoleFormat(client, sBuf, len, "[SM] Bunnyhop stats for %L\n", iTarget); + + CPlayer Player = g_aPlayers[iTarget]; + + int iGlobalJumps = Player.iJumps; + float HyperRatio = Player.iHyperJumps / float(iGlobalJumps); + float HackRatio = Player.iHackJumps / float(iGlobalJumps); + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "Global jumps: %d | Hyper?: %.1f%% | Hack?: %.1f%%\n", + iGlobalJumps, HyperRatio * 100.0, HackRatio * 100.0); + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "bHackGlobal: %d | bHyperGlobal: %d | iHackFlagged: %d | iHyperFlagged: %d\n", + Player.bHackGlobal, Player.bHyperGlobal, Player.iHackFlagged, Player.iHyperFlagged); + + int aGlobalJumps[3]; + Player.GetJumps(aGlobalJumps); + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "Global jumps perf group (1 2 +): %1.f%% %1.f%% %1.f%%\n", + (aGlobalJumps[0] / float(iGlobalJumps)) * 100.0, + (aGlobalJumps[1] / float(iGlobalJumps)) * 100.0, + (aGlobalJumps[2] / float(iGlobalJumps)) * 100.0); + + return iBuf; +} + +int FormatStreak(int client, int iTarget, int iStreak, char[] sBuf=0, int len=0) +{ + int iBuf = ConsoleFormat(client, sBuf, len, "[SM] Bunnyhop streak %d for %L\n", iStreak, iTarget); + + CPlayer Player = g_aPlayers[iTarget]; + ArrayList hStreaks = Player.hStreaks; + CStreak hStreak = Player.hStreak; + int iStreaks = hStreaks.Length; + + // Try showing latest valid streak + if(iStreak <= 0 && !hStreak.bValid) + { + if(iStreaks) + hStreak = hStreaks.Get(iStreaks - 1); + } + else if(iStreak > 0) + { + if(iStreak > MAX_STREAKS) + { + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "[SM] Streak is out of bounds (max. %d)!\n", MAX_STREAKS); + return iBuf; + } + + int iIndex = iStreaks - iStreak; + if(iIndex < 0) + { + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "[SM] Only %d streaks are available for this player right now!\n", iStreaks); + return iBuf; + } + + hStreak = hStreaks.Get(iIndex); + } + + int iStreakJumps = hStreak.iJumps; + float HyperRatio = hStreak.iHyperJumps / float(iStreakJumps); + float HackRatio = hStreak.iHackJumps / float(iStreakJumps); + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "Streak jumps: %d | Hyper?: %.1f%% | Hack?: %.1f%%\n", + iStreakJumps, HyperRatio * 100.0, HackRatio * 100.0); + + int aStreakJumps[3]; + hStreak.GetJumps(aStreakJumps); + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "Streak jumps perf group (1 2 +): %1.f%% %1.f%% %1.f%%\n", + (aStreakJumps[0] / float(iStreakJumps)) * 100.0, + (aStreakJumps[1] / float(iStreakJumps)) * 100.0, + (aStreakJumps[2] / float(iStreakJumps)) * 100.0); + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "#%2s %7s %5s %8s %4s %6s %s\n", + "id", " outvel", " gain", " avgdist", " num", " avg+-", "pattern"); + + ArrayList hJumps = hStreak.hJumps; + float fPrevVel = 0.0; + int iPrevEndTick = -1; + + for(int i = 0; i < hJumps.Length; i++) + { + CJump hJump = hJumps.Get(i); + ArrayList hPresses = hJump.hPresses; + + float fInVel = hJump.fStartVel; + float fOutVel = hJump.fEndVel; + int iEndTick = hJump.iEndTick; + + static char sPattern[512]; + int iPatternLen = 0; + int iPrevTick = -1; + int iTicks; + + if(iPrevEndTick != -1) + { + iTicks = hJump.iStartTick - iPrevEndTick; + for(int k = 0; k < iTicks && k < 16; k++) + sPattern[iPatternLen++] = '|'; + } + + float fAvgDist = 0.0; + float fAvgDownUp = 0.0; + + int iPresses = hPresses.Length; + for(int j = 0; j < iPresses; j++) + { + int aJunkJump[2]; + hPresses.GetArray(j, aJunkJump); + + if(iPrevTick != -1) + { + iTicks = aJunkJump[0] - iPrevTick; + for(int k = 0; k < iTicks && k < 16; k++) + sPattern[iPatternLen++] = '.'; + + fAvgDist += iTicks; + } + + sPattern[iPatternLen++] = '^'; + + iTicks = aJunkJump[1] - aJunkJump[0]; + for(int k = 0; k < iTicks && k < 16; k++) + sPattern[iPatternLen++] = ','; + + fAvgDownUp += iTicks; + + sPattern[iPatternLen++] = 'v'; + + iPrevTick = aJunkJump[1]; + } + + fAvgDist /= iPresses; + fAvgDownUp /= iPresses; + + iTicks = iEndTick - iPrevTick; + for(int k = 0; k < iTicks && k < 16; k++) + sPattern[iPatternLen++] = '.'; + + sPattern[iPatternLen++] = '|'; + sPattern[iPatternLen++] = '\0'; + + if(fPrevVel == 0.0) + fPrevVel = fInVel; + + iBuf += ConsoleFormat(client, sBuf[iBuf], len-iBuf, "#%2d %7.1f %4d%% %8.2f %4d %6.2f %s\n", + i, + fOutVel, + fPrevVel == 0.0 ? 100 : RoundFloat((fOutVel / fPrevVel) * 100.0 - 100.0), + fAvgDist, + iPresses, + fAvgDownUp, + sPattern); + + iPrevEndTick = iEndTick; + fPrevVel = fOutVel; + } + + return iBuf; +} + +bool Forward_OnDetected(int client, const char[] reason, const char[] stats) +{ + Call_StartForward(g_hOnClientDetected); + Call_PushCell(client); + Call_PushString(reason); + Call_PushString(stats); + Call_Finish(); +} + +stock int ConsoleFormat(int client, char[] buffer, int maxlength, const char[] format, any ...) +{ + if(client >= 1 && client <= MAXPLAYERS) + { + char sBuf[1024]; + sBuf[VFormat(sBuf, sizeof(sBuf), format, 5) - 1] = 0; + PrintToConsole(client, "%s", sBuf); + } + + if(maxlength > 0) + return VFormat(buffer, maxlength, format, 5); + + return 0; +} diff --git a/AntiBhopCheat/scripting/CJump.inc b/AntiBhopCheat/scripting/CJump.inc new file mode 100644 index 0000000..6bcc240 --- /dev/null +++ b/AntiBhopCheat/scripting/CJump.inc @@ -0,0 +1,118 @@ +#if defined _class_cjump_ + #endinput +#endif +#define _class_cjump_ + +methodmap CJump < Basic +{ + public CJump() + { + Basic myclass = new Basic(); + + myclass.SetInt("iStartTick", -1); + myclass.SetInt("iEndTick", -1); + myclass.SetFloat("fStartVel", 0.0); + myclass.SetFloat("fEndVel", 0.0); + + myclass.SetInt("iPrevJump", -1); + myclass.SetInt("iNextJump", -1); + + myclass.SetHandle("hPresses", new ArrayList(2)); + + return view_as(myclass); + } + + property int iStartTick + { + public get() + { + return this.GetInt("iStartTick"); + } + public set(int value) + { + this.SetInt("iStartTick", value); + } + } + + property int iEndTick + { + public get() + { + return this.GetInt("iEndTick"); + } + public set(int value) + { + this.SetInt("iEndTick", value); + } + } + + property float fStartVel + { + public get() + { + return this.GetFloat("fStartVel"); + } + public set(float value) + { + this.SetFloat("fStartVel", value); + } + } + + property float fEndVel + { + public get() + { + return this.GetFloat("fEndVel"); + } + public set(float value) + { + this.SetFloat("fEndVel", value); + } + } + + property int iPrevJump + { + public get() + { + return this.GetInt("iPrevJump"); + } + public set(int value) + { + this.SetInt("iPrevJump", value); + } + } + + property int iNextJump + { + public get() + { + return this.GetInt("iNextJump"); + } + public set(int value) + { + this.SetInt("iNextJump", value); + } + } + + property ArrayList hPresses + { + public get() + { + return view_as(this.GetHandle("hPresses")); + } + public set(ArrayList value) + { + this.SetHandle("hPresses", value); + } + } + + public void Dispose(bool disposemembers=true) + { + if(disposemembers) + { + delete this.hPresses; + } + + delete this; + } +} diff --git a/AntiBhopCheat/scripting/CPlayer.inc b/AntiBhopCheat/scripting/CPlayer.inc new file mode 100644 index 0000000..58a35ee --- /dev/null +++ b/AntiBhopCheat/scripting/CPlayer.inc @@ -0,0 +1,193 @@ +#if defined _class_cplayer_ + #endinput +#endif +#define _class_cplayer_ + +methodmap CPlayer < Basic +{ + public CPlayer(int client) + { + Basic myclass = new Basic(); + + myclass.SetInt("iClient", client); + myclass.SetBool("bHackGlobal", false); + myclass.SetBool("bHyperGlobal", false); + myclass.SetInt("iHackFlagged", 0); + myclass.SetInt("iHyperFlagged", 0); + + myclass.SetInt("iJumps", 0); + myclass.SetInt("iHyperJumps", 0); + myclass.SetInt("iHackJumps", 0); + myclass.SetArray("aJumps", {0, 0, 0}, 3); + + myclass.SetInt("iLastStreakTick", -1); + myclass.SetHandle("hStreak", new CStreak()); + myclass.SetHandle("hStreaks", new ArrayList(1)); + + return view_as(myclass); + } + + property int iClient + { + public get() + { + return this.GetInt("iClient"); + } + public set(int value) + { + this.SetInt("iClient", value); + } + } + + property bool bHackGlobal + { + public get() + { + return this.GetBool("bHackGlobal"); + } + public set(bool value) + { + this.SetBool("bHackGlobal", value); + } + } + + property bool bHyperGlobal + { + public get() + { + return this.GetBool("bHyperGlobal"); + } + public set(bool value) + { + this.SetBool("bHyperGlobal", value); + } + } + + property int iHackFlagged + { + public get() + { + return this.GetInt("iHackFlagged"); + } + public set(int value) + { + this.SetInt("iHackFlagged", value); + } + } + + property int iHyperFlagged + { + public get() + { + return this.GetInt("iHyperFlagged"); + } + public set(int value) + { + this.SetInt("iHyperFlagged", value); + } + } + + property int iJumps + { + public get() + { + return this.GetInt("iJumps"); + } + public set(int value) + { + this.SetInt("iJumps", value); + } + } + + property int iHyperJumps + { + public get() + { + return this.GetInt("iHyperJumps"); + } + public set(int value) + { + this.SetInt("iHyperJumps", value); + } + } + + property int iHackJumps + { + public get() + { + return this.GetInt("iHackJumps"); + } + public set(int value) + { + this.SetInt("iHackJumps", value); + } + } + + property int iLastStreakTick + { + public get() + { + return this.GetInt("iLastStreakTick"); + } + public set(int value) + { + this.SetInt("iLastStreakTick", value); + } + } + + public void GetJumps(int value[3]) + { + this.GetArray("aJumps", value, sizeof(value)); + } + + public void SetJumps(const int value[3]) + { + this.SetArray("aJumps", value, sizeof(value)); + } + + property CStreak hStreak + { + public get() + { + return view_as(this.GetHandle("hStreak")); + } + public set(CStreak value) + { + this.SetHandle("hStreak", value); + } + } + + property ArrayList hStreaks + { + public get() + { + return view_as(this.GetHandle("hStreaks")); + } + public set(ArrayList value) + { + this.SetHandle("hStreaks", value); + } + } + + public void Dispose(bool disposemembers=true) + { + if(disposemembers) + { + ArrayList hStreaks = this.hStreaks; + + CStreak hStreak; + for(int i = 0; i < hStreaks.Length; i++) + { + hStreak = view_as(hStreaks.Get(i)); + hStreak.Dispose(); + } + + delete hStreaks; + + if(this.hStreak != hStreak) + this.hStreak.Dispose(); + } + + delete this; + } +} diff --git a/AntiBhopCheat/scripting/CStreak.inc b/AntiBhopCheat/scripting/CStreak.inc new file mode 100644 index 0000000..f4b4d28 --- /dev/null +++ b/AntiBhopCheat/scripting/CStreak.inc @@ -0,0 +1,163 @@ +#if defined _class_cstreak_ + #endinput +#endif +#define _class_cstreak_ + +methodmap CStreak < Basic +{ + public CStreak() + { + Basic myclass = new Basic(); + + myclass.SetBool("bValid", false); + myclass.SetInt("iStartTick", -1); + myclass.SetInt("iEndTick", -1); + myclass.SetFloat("fStartVel", 0.0); + myclass.SetFloat("fEndVel", 0.0); + + myclass.SetInt("iJumps", 0); + myclass.SetInt("iHyperJumps", 0); + myclass.SetInt("iHackJumps", 0); + myclass.SetArray("aJumps", {0, 0, 0}, 3); + + myclass.SetHandle("hJumps", new ArrayList(1)); + + return view_as(myclass); + } + + property bool bValid + { + public get() + { + return this.GetBool("bValid"); + } + public set(bool value) + { + this.SetBool("bValid", value); + } + } + + property int iStartTick + { + public get() + { + return this.GetInt("iStartTick"); + } + public set(int value) + { + this.SetInt("iStartTick", value); + } + } + + property int iEndTick + { + public get() + { + return this.GetInt("iEndTick"); + } + public set(int value) + { + this.SetInt("iEndTick", value); + } + } + + property float fStartVel + { + public get() + { + return this.GetFloat("fStartVel"); + } + public set(float value) + { + this.SetFloat("fStartVel", value); + } + } + + property float fEndVel + { + public get() + { + return this.GetFloat("fEndVel"); + } + public set(float value) + { + this.SetFloat("fEndVel", value); + } + } + + property int iJumps + { + public get() + { + return this.GetInt("iJumps"); + } + public set(int value) + { + this.SetInt("iJumps", value); + } + } + + property int iHyperJumps + { + public get() + { + return this.GetInt("iHyperJumps"); + } + public set(int value) + { + this.SetInt("iHyperJumps", value); + } + } + + property int iHackJumps + { + public get() + { + return this.GetInt("iHackJumps"); + } + public set(int value) + { + this.SetInt("iHackJumps", value); + } + } + + public void GetJumps(int value[3]) + { + this.GetArray("aJumps", value, sizeof(value)); + } + + public void SetJumps(const int value[3]) + { + this.SetArray("aJumps", value, sizeof(value)); + } + + property ArrayList hJumps + { + public get() + { + return view_as(this.GetHandle("hJumps")); + } + public set(ArrayList value) + { + this.SetHandle("hJumps", value); + } + } + + public void Dispose(bool disposemembers=true) + { + if(disposemembers) + { + ArrayList hJumps = this.hJumps; + + for(int i = 0; i < hJumps.Length; i++) + { + CJump hJump = view_as(hJumps.Get(i)); + hJump.Dispose(); + } + + delete hJumps; + } + + delete this; + } +} diff --git a/AntiBhopCheat/scripting/include/AntiBhopCheat.inc b/AntiBhopCheat/scripting/include/AntiBhopCheat.inc new file mode 100644 index 0000000..0b968fb --- /dev/null +++ b/AntiBhopCheat/scripting/include/AntiBhopCheat.inc @@ -0,0 +1,6 @@ +#if defined _AntiBhopCheat_Included + #endinput +#endif +#define _AntiBhopCheat_Included + +forward void AntiBhopCheat_OnClientDetected(int client, char[] sReason, char[] sStats); \ No newline at end of file diff --git a/BSPConvarAllower/content/bspconvar_whitelist_permanent.txt b/BSPConvarAllower/content/bspconvar_whitelist_permanent.txt new file mode 100644 index 0000000..9cd9071 --- /dev/null +++ b/BSPConvarAllower/content/bspconvar_whitelist_permanent.txt @@ -0,0 +1,25 @@ +"convars" +{ + say 1 + exec 1 + sm_cvar 1 + sm_say 1 + + zr_infect_mzombie_ratio 1 + zr_infect_mzombie_respawn 1 + zr_infect_spawntime_max 1 + zr_infect_spawntime_min 1 + zr_roundend_overlay 1 + zr_roundend_overlays_zombie 1 + zr_roundend_overlays_human 1 + zr_ambientsounds_volume 1 + zr_respawn 1 + zr_respawn_delay 1 + zr_respawn_team_zombie 1 + zr_respawn_team_zombie_world 1 + zr_class_modify 1 + zr_roundend_overlays_zombie 1 + zr_roundend_overlays_human 1 + + sm_gravity 1 +} diff --git a/BSPConvarAllower/scripting/BspConvarAllower.sp b/BSPConvarAllower/scripting/BspConvarAllower.sp new file mode 100644 index 0000000..4c468d9 --- /dev/null +++ b/BSPConvarAllower/scripting/BspConvarAllower.sp @@ -0,0 +1,104 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include + +KeyValues kv; +KeyValues config; +Handle hAcceptInput; + +public Plugin myinfo = { + name = "BSP ConVar Allower", + author = "SHUFEN from POSSESSION.tokyo, fix by xen", + description = "Allows server commands to maps", + version = "0.2", + url = "https://possession.tokyo" +}; + +public void OnPluginStart() { + if (!CheckTxtFile_bspconvar_whitelist()) return; + + if (hAcceptInput == null) { + char tmpOffset[148]; + + switch(GetEngineVersion()) { + case Engine_CSGO: + tmpOffset = "sdktools.games\\engine.csgo"; + default: + SetFailState("This plugin is only for CS:GO"); + } + + Handle temp = LoadGameConfigFile(tmpOffset); + + if (temp == null) + SetFailState("Why you no has gamedata?"); + + int offset = GameConfGetOffset(temp, "AcceptInput"); + hAcceptInput = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, AcceptInput); + DHookAddParam(hAcceptInput, HookParamType_CharPtr); + DHookAddParam(hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(hAcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); + DHookAddParam(hAcceptInput, HookParamType_Int); + + delete temp; + } +} + +public void OnPluginEnd() { + if (kv != INVALID_HANDLE) + delete kv; + if (config != INVALID_HANDLE) + delete config; +} + +bool CheckTxtFile_bspconvar_whitelist() { + if (kv != INVALID_HANDLE) + delete kv; + kv = new KeyValues("convars"); + if (!kv.ImportFromFile("bspconvar_whitelist.txt")) { + SetFailState("Couldn't get KeyValues from bspconvar_whitelist.txt"); + return false; + } + if (config != INVALID_HANDLE) + delete config; + config = new KeyValues("convars"); + if (!config.ImportFromFile("bspconvar_whitelist_permanent.txt")) { + SetFailState("Couldn't get KeyValues from bspconvar_whitelist_permanent.txt"); + return false; + } + return true; +} + +public void OnEntityCreated(int entity, const char[] classname) { + if (StrEqual(classname, "point_servercommand", false)) + DHookEntity(hAcceptInput, false, entity); +} + +public MRESReturn AcceptInput(int entity, Handle hReturn, Handle hParams) { + if (!IsValidEntity(entity)) + return MRES_Ignored; + + char eCommand[128], eParam[256], eServerCommand[64]; + + DHookGetParamString(hParams, 1, eCommand, 128); + + if (StrEqual(eCommand, "Command", false)) { + int type = DHookGetParamObjectPtrVar(hParams, 4, 16, ObjectValueType_Int); + if (type == 2) { + DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, eParam, 256); + + SplitString(eParam, " ", eServerCommand, 64); + if (kv.JumpToKey(eServerCommand, false) || config.JumpToKey(eServerCommand, false)) { + PrintToServer("[BSPConvarAllower] Allowing \"%s\"", eParam); + ServerCommand(eParam); + } + kv.Rewind(); + config.Rewind(); + } + } + + return MRES_Ignored; +} diff --git a/BossHP/configs/MapBossHP/Template.txt b/BossHP/configs/MapBossHP/Template.txt new file mode 100644 index 0000000..2e3f28c --- /dev/null +++ b/BossHP/configs/MapBossHP/Template.txt @@ -0,0 +1,87 @@ +"math_counter" +{ + "config" + { + "ForceEnable" "" //强制显示实体血量(bhud) 1=开启(默认) 0=关闭 + "RoundEndShowTopDamage" "" //回合结束显示输出排行 1=开启(默认) 0=关闭 + "ShowTopDamageDuringBOSS" "" //打BOSS期间显示输出排行 1=开启 0=关闭(默认) + "BossBeatenShowTopDamage" "" + "CrosshairChannel" "" //击中BOSS准星通道 范围1-6 默认=5 + "BossRewardMoney" "" //击中BOSS奖励金钱 默认=10 + "BossHpKeepTime" "" //BOSS HP数值维持显示时间 (数值无变动超过该秒数将不再显示) 默认=10 + "BossDieKeepTime" "" //BOSS阵亡后HP数值维持显示时间 默认=1 该秒数必须低于上面的秒数 + "DisplayWhenHPAdded" "" + "MaxLegalBreakableHP" "" + "MaxLegalMathCounterHP" "" + } + "config" + { + "ForceEnable" "" + "RoundEndShowTopDamage" "" + "ShowTopDamageDuringBOSS" "" + "BossBeatenShowTopDamage" "" + "CrosshairChannel" "" + "BossRewardMoney" "" + "BossHpKeepTime" "" + "BossDieKeepTime" "" + "DisplayWhenHPAdded" "" + "MaxLegalBreakableHP" "" + "MaxLegalMathCounterHP" "" + } + + + "0" //math_counter + { + "HP_counter" "" //实体名 + "HPbar_counter" "" //实体名 + "HPinit_counter" "" //实体名 + "CustomText" "" //Boss名 + } + "0" + { + "HP_counter" "" + "HPbar_counter" "" + "HPinit_counter" "" + "CustomText" "" + } + + + "0" //FFXIV V2_8 V4_10 + { + "HP_Group" + { + "0" "" + "1" "" + "2" "" + "3" "" + "4" "" + } + "CustomText" "" + } + "0" + { + "HP_Group" + { + "0" "" + "1" "" + "2" "" + "3" "" + "4" "" + } + "CustomText" "" + } + + + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "" //实体名 + "CustomText" "" //BOSS名 + } + "1" + { + "Type" "breakable" + "BreakableName" "" + "CustomText" "" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_666_crazy_escape_v1_1.txt b/BossHP/configs/MapBossHP/ze_666_crazy_escape_v1_1.txt new file mode 100644 index 0000000..4cc7bc6 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_666_crazy_escape_v1_1.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "0" + { + "HP_counter" "nrk1_boss_laser_counter" + "HP_Mode" "1" + "HPbar_counter" "nrk1_boss_hp_iterations" + "HPinit_counter" "nrk1_boss_hp_backup" + "CustomText" "" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "nrk1_counter" + "HP_Mode" "1" + "HPbar_counter" "nrk1_hp_iterations" + "HPinit_counter" "nrk1_hp_backup" + "CustomText" "" + "HPbar_mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_8bit_csgo2.txt b/BossHP/configs/MapBossHP/ze_8bit_csgo2.txt new file mode 100644 index 0000000..864454f --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_8bit_csgo2.txt @@ -0,0 +1,52 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "Boss_box_1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Boss_box_2" + } + + "1" + { + "Type" "breakable" + "BreakableName" "Boss_box_3" + } + + "1" + { + "Type" "breakable" + "BreakableName" "Boss_box_4" + } + + "1" + { + "Type" "breakable" + "BreakableName" "Boss_box_5" + } + "1" + { + "Type" "breakable" + "BreakableName" "Boss6_1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Ponch_Box_1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Govno_Box" + } + "1" + { + "Type" "breakable" + "BreakableName" "Pidaras_Boss6" + } + +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_A_E_S_T_H_E_T_I_C_v1_1.txt b/BossHP/configs/MapBossHP/ze_A_E_S_T_H_E_T_I_C_v1_1.txt new file mode 100644 index 0000000..18a4516 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_A_E_S_T_H_E_T_I_C_v1_1.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Mini_HP1" + } + "1" + { + "HP_counter" "Boss_Mini_HP2" + } + "2" + { + "HP_counter" "EyesHP" + } +} diff --git a/BossHP/configs/MapBossHP/ze_Bathroom_v2_5.txt b/BossHP/configs/MapBossHP/ze_Bathroom_v2_5.txt new file mode 100644 index 0000000..c0e531d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_Bathroom_v2_5.txt @@ -0,0 +1,23 @@ +"math_counter" +{ + "0" + { + "HP_counter" "krokodyl_counter" + "HPbar_counter" "krokodyl_hp_iterations" + "HPinit_counter" "krokodyl_hp_backup" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "kostka_hp" + "HPbar_counter" "kostka_hp_iterations" + "HPinit_counter" "kostka_hp_backup" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_FFXIV_Wanderers_Palace_v6.txt b/BossHP/configs/MapBossHP/ze_FFXIV_Wanderers_Palace_v6.txt new file mode 100644 index 0000000..e4c3701 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_FFXIV_Wanderers_Palace_v6.txt @@ -0,0 +1,29 @@ +"math_counter" +{ + "config" + { + "ForceEnable" "0" + "BossRewardMoney" "1" + } + "0" + { + "HP_counter" "Boss_HP_Counter" + } + "1" + { + "HP_counter" "Ultima_Weapon_HP_Counter" + "CustomText" "Ultima Weapon" + } + "2" + { + "Type" "breakable" + "BreakableName" "Aqua_Breakable" + "CustomText" "Ice Wall" + } + "3" + { + "Type" "breakable" + "BreakableName" "Final_Kain_Highwind_Hitbox" + "CustomText" "Final Kain Highwind" + } +} diff --git a/BossHP/configs/MapBossHP/ze_FFXIV_Wanderers_Palace_v6_beta.txt b/BossHP/configs/MapBossHP/ze_FFXIV_Wanderers_Palace_v6_beta.txt new file mode 100644 index 0000000..248ffd1 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_FFXIV_Wanderers_Palace_v6_beta.txt @@ -0,0 +1,33 @@ +"math_counter" +{ + "config" + { + "ForceEnable" "0" + "BossRewardMoney" "1" + } + "0" + { + "HP_counter" "Boss_HP_Counter" + } + "1" + { + "HP_counter" "Ultima_Weapon_HP_Counter" + "HP_Mode" "1" + "HPbar_counter" "Ultima_Weapon_HP_Phase_Counter" + "HPinit_counter" "Ultima_Weapon_HP_Initial" + "CustomText" "Ultima Weapon" + "HPbar_mode" "2" + } + "2" + { + "Type" "breakable" + "BreakableName" "Aqua_Breakable" + "CustomText" "Ice Wall" + } + "3" + { + "Type" "breakable" + "BreakableName" "Final_Kain_Highwind_Hitbox" + "CustomText" "Final Kain Highwind" + } +} diff --git a/BossHP/configs/MapBossHP/ze_Naruto_p.txt b/BossHP/configs/MapBossHP/ze_Naruto_p.txt new file mode 100644 index 0000000..f0757e9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_Naruto_p.txt @@ -0,0 +1,8 @@ +"math_counter" +{ + "0" + { + "HP_counter" "health_boss" + "CustomText" "Sasuke" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v7_6.txt b/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v7_6.txt new file mode 100644 index 0000000..0333cfa --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v7_6.txt @@ -0,0 +1,88 @@ +"math_counter" +{ + "0" + { + "HP_counter" "crobatcyrushp" + "CustomText" "Crobat" + } + "1" + { + "HP_counter" "crobatfinal2_health" + "CustomText" "Crobat" + } + "2" + { + "HP_counter" "finalkirlia_hp" + "CustomText" "Kirlia" + } + "3" + { + "HP_counter" "giratinahp" + "CustomText" "Giratina" + } + "4" + { + "HP_counter" "groudonhp" + "CustomText" "Groudon" + } + "5" + { + "HP_counter" "honchkrowhp" + "CustomText" "Honchkrow" + } + "6" + { + "HP_counter" "puruglyhealth" + "CustomText" "Purugly" + } + "7" + { + "HP_counter" "regicehealth" + "CustomText" "Regice" + } + "8" + { + "HP_counter" "skuntankhealth" + "CustomText" "Skuntank" + } + "9" + { + "HP_counter" "weavilefinalhp" + "CustomText" "Weavile" + } + "10" + { + "HP_counter" "weavilehp" + "CustomText" "Weavile" + } + "10" + { + "HP_counter" "PrimalGroudonHP" + "CustomText" "Primal Groudon" + } + "11" + { + "HP_counter" "ArceusHP_1" + "CustomText" "Arceus(1/4)" + } + "11" + { + "HP_counter" "ArceusHP_2" + "CustomText" "Arceus(2/4)" + } + "11" + { + "HP_counter" "ArceusHP_3" + "CustomText" "Arceus(3/4)" + } + "11" + { + "HP_counter" "ArceusHP_4" + "CustomText" "Arceus(4/4)" + } + "10" + { + "HP_counter" "Arceus_Judgement_HP" + "CustomText" "Judgement" + } +} diff --git a/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v8_2.txt b/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v8_2.txt new file mode 100644 index 0000000..0333cfa --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v8_2.txt @@ -0,0 +1,88 @@ +"math_counter" +{ + "0" + { + "HP_counter" "crobatcyrushp" + "CustomText" "Crobat" + } + "1" + { + "HP_counter" "crobatfinal2_health" + "CustomText" "Crobat" + } + "2" + { + "HP_counter" "finalkirlia_hp" + "CustomText" "Kirlia" + } + "3" + { + "HP_counter" "giratinahp" + "CustomText" "Giratina" + } + "4" + { + "HP_counter" "groudonhp" + "CustomText" "Groudon" + } + "5" + { + "HP_counter" "honchkrowhp" + "CustomText" "Honchkrow" + } + "6" + { + "HP_counter" "puruglyhealth" + "CustomText" "Purugly" + } + "7" + { + "HP_counter" "regicehealth" + "CustomText" "Regice" + } + "8" + { + "HP_counter" "skuntankhealth" + "CustomText" "Skuntank" + } + "9" + { + "HP_counter" "weavilefinalhp" + "CustomText" "Weavile" + } + "10" + { + "HP_counter" "weavilehp" + "CustomText" "Weavile" + } + "10" + { + "HP_counter" "PrimalGroudonHP" + "CustomText" "Primal Groudon" + } + "11" + { + "HP_counter" "ArceusHP_1" + "CustomText" "Arceus(1/4)" + } + "11" + { + "HP_counter" "ArceusHP_2" + "CustomText" "Arceus(2/4)" + } + "11" + { + "HP_counter" "ArceusHP_3" + "CustomText" "Arceus(3/4)" + } + "11" + { + "HP_counter" "ArceusHP_4" + "CustomText" "Arceus(4/4)" + } + "10" + { + "HP_counter" "Arceus_Judgement_HP" + "CustomText" "Judgement" + } +} diff --git a/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v9c.txt b/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v9c.txt new file mode 100644 index 0000000..0333cfa --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_PKMN_Adventure_v9c.txt @@ -0,0 +1,88 @@ +"math_counter" +{ + "0" + { + "HP_counter" "crobatcyrushp" + "CustomText" "Crobat" + } + "1" + { + "HP_counter" "crobatfinal2_health" + "CustomText" "Crobat" + } + "2" + { + "HP_counter" "finalkirlia_hp" + "CustomText" "Kirlia" + } + "3" + { + "HP_counter" "giratinahp" + "CustomText" "Giratina" + } + "4" + { + "HP_counter" "groudonhp" + "CustomText" "Groudon" + } + "5" + { + "HP_counter" "honchkrowhp" + "CustomText" "Honchkrow" + } + "6" + { + "HP_counter" "puruglyhealth" + "CustomText" "Purugly" + } + "7" + { + "HP_counter" "regicehealth" + "CustomText" "Regice" + } + "8" + { + "HP_counter" "skuntankhealth" + "CustomText" "Skuntank" + } + "9" + { + "HP_counter" "weavilefinalhp" + "CustomText" "Weavile" + } + "10" + { + "HP_counter" "weavilehp" + "CustomText" "Weavile" + } + "10" + { + "HP_counter" "PrimalGroudonHP" + "CustomText" "Primal Groudon" + } + "11" + { + "HP_counter" "ArceusHP_1" + "CustomText" "Arceus(1/4)" + } + "11" + { + "HP_counter" "ArceusHP_2" + "CustomText" "Arceus(2/4)" + } + "11" + { + "HP_counter" "ArceusHP_3" + "CustomText" "Arceus(3/4)" + } + "11" + { + "HP_counter" "ArceusHP_4" + "CustomText" "Arceus(4/4)" + } + "10" + { + "HP_counter" "Arceus_Judgement_HP" + "CustomText" "Judgement" + } +} diff --git a/BossHP/configs/MapBossHP/ze_S_A_M_a29.txt b/BossHP/configs/MapBossHP/ze_S_A_M_a29.txt new file mode 100644 index 0000000..f68bd12 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_S_A_M_a29.txt @@ -0,0 +1,92 @@ +"math_counter" +{ + "0" + { + "HP_counter" "lvl4_boss_hp" + "HPbar_counter" "lvl4_boss_hpbar_counter" + "HPinit_counter" "lvl4_boss_hp_init" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_boss_dark_hp" + "CustomText" "Dark" + } + "2" + { + "HP_counter" "lvl2_boss_fire_hp" + "CustomText" "Fire" + } + "3" + { + "HP_counter" "lvl2_boss_hp" + } + "4" + { + "HP_counter" "lvl2_boss_ice_hp" + "CustomText" "Ice" + } + "5" + { + "HP_counter" "lvl2_boss_light_hp" + "CustomText" "Light" + } + "6" + { + "HP_counter" "lvl3_boss_hp" + "HPbar_counter" "lvl3_boss_hpbar_counter" + "HPinit_counter" "lvl3_boss_hp_init" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "7" + { + "HP_counter" "lvl4_boss2_hp" + "CustomText" "Barlog" + } + "8" + { + "HP_counter" "Troll_health" + "HPbar_counter" "Troll_hpbar_counter" + "HPinit_counter" "Troll_hp_init" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "9" + { + "HP_counter" "lvl5_boss_hp_1" + "HPbar_counter" "lvl5_boss_hpbar_counter_1" + "HPinit_counter" "lvl5_boss_hp_init_1" + "CustomText" "Sword Castle" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "10" + { + "HP_counter" "lvl5_finalboss_hp" + } + "11" + { + "HP_counter" "lvl5_laserboss_hp" + } + "12" + { + "HP_counter" "darklaser_hp_1" + } + "13" + { + "HP_counter" "darklaser_hp_2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_S_A_M_a32.txt b/BossHP/configs/MapBossHP/ze_S_A_M_a32.txt new file mode 100644 index 0000000..87cdf09 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_S_A_M_a32.txt @@ -0,0 +1,65 @@ +"math_counter" +{ + "0" + { + "HP_counter" "lvl4_boss_hp" + "HPbar_counter" "lvl4_boss_hpbar_counter" + "HPinit_counter" "lvl4_boss_hp_init" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_boss_dark_hp" + "CustomText" "Dark" + } + "2" + { + "HP_counter" "lvl2_boss_fire_hp" + "CustomText" "Fire" + } + "3" + { + "HP_counter" "lvl2_boss_hp" + } + "4" + { + "HP_counter" "lvl2_boss_ice_hp" + "CustomText" "Ice" + } + "5" + { + "HP_counter" "lvl2_boss_light_hp" + "CustomText" "Light" + } + "6" + { + "HP_counter" "lvl3_boss_hp" + "HPbar_counter" "lvl3_boss_hpbar_counter" + "HPinit_counter" "lvl3_boss_hp_init" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "7" + { + "HP_counter" "lvl4_boss2_hp" + "CustomText" "Barlog" + } + "8" + { + "HP_counter" "Troll_health" + "HPbar_counter" "Troll_hpbar_counter" + "HPinit_counter" "Troll_hp_init" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_S_A_M_a33.txt b/BossHP/configs/MapBossHP/ze_S_A_M_a33.txt new file mode 100644 index 0000000..f68bd12 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_S_A_M_a33.txt @@ -0,0 +1,92 @@ +"math_counter" +{ + "0" + { + "HP_counter" "lvl4_boss_hp" + "HPbar_counter" "lvl4_boss_hpbar_counter" + "HPinit_counter" "lvl4_boss_hp_init" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_boss_dark_hp" + "CustomText" "Dark" + } + "2" + { + "HP_counter" "lvl2_boss_fire_hp" + "CustomText" "Fire" + } + "3" + { + "HP_counter" "lvl2_boss_hp" + } + "4" + { + "HP_counter" "lvl2_boss_ice_hp" + "CustomText" "Ice" + } + "5" + { + "HP_counter" "lvl2_boss_light_hp" + "CustomText" "Light" + } + "6" + { + "HP_counter" "lvl3_boss_hp" + "HPbar_counter" "lvl3_boss_hpbar_counter" + "HPinit_counter" "lvl3_boss_hp_init" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "7" + { + "HP_counter" "lvl4_boss2_hp" + "CustomText" "Barlog" + } + "8" + { + "HP_counter" "Troll_health" + "HPbar_counter" "Troll_hpbar_counter" + "HPinit_counter" "Troll_hp_init" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "9" + { + "HP_counter" "lvl5_boss_hp_1" + "HPbar_counter" "lvl5_boss_hpbar_counter_1" + "HPinit_counter" "lvl5_boss_hp_init_1" + "CustomText" "Sword Castle" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "10" + { + "HP_counter" "lvl5_finalboss_hp" + } + "11" + { + "HP_counter" "lvl5_laserboss_hp" + } + "12" + { + "HP_counter" "darklaser_hp_1" + } + "13" + { + "HP_counter" "darklaser_hp_2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_S_A_M_a39.txt b/BossHP/configs/MapBossHP/ze_S_A_M_a39.txt new file mode 100644 index 0000000..f68bd12 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_S_A_M_a39.txt @@ -0,0 +1,92 @@ +"math_counter" +{ + "0" + { + "HP_counter" "lvl4_boss_hp" + "HPbar_counter" "lvl4_boss_hpbar_counter" + "HPinit_counter" "lvl4_boss_hp_init" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_boss_dark_hp" + "CustomText" "Dark" + } + "2" + { + "HP_counter" "lvl2_boss_fire_hp" + "CustomText" "Fire" + } + "3" + { + "HP_counter" "lvl2_boss_hp" + } + "4" + { + "HP_counter" "lvl2_boss_ice_hp" + "CustomText" "Ice" + } + "5" + { + "HP_counter" "lvl2_boss_light_hp" + "CustomText" "Light" + } + "6" + { + "HP_counter" "lvl3_boss_hp" + "HPbar_counter" "lvl3_boss_hpbar_counter" + "HPinit_counter" "lvl3_boss_hp_init" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "7" + { + "HP_counter" "lvl4_boss2_hp" + "CustomText" "Barlog" + } + "8" + { + "HP_counter" "Troll_health" + "HPbar_counter" "Troll_hpbar_counter" + "HPinit_counter" "Troll_hp_init" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "9" + { + "HP_counter" "lvl5_boss_hp_1" + "HPbar_counter" "lvl5_boss_hpbar_counter_1" + "HPinit_counter" "lvl5_boss_hp_init_1" + "CustomText" "Sword Castle" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "10" + { + "HP_counter" "lvl5_finalboss_hp" + } + "11" + { + "HP_counter" "lvl5_laserboss_hp" + } + "12" + { + "HP_counter" "darklaser_hp_1" + } + "13" + { + "HP_counter" "darklaser_hp_2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_S_A_M_v1.txt b/BossHP/configs/MapBossHP/ze_S_A_M_v1.txt new file mode 100644 index 0000000..f68bd12 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_S_A_M_v1.txt @@ -0,0 +1,92 @@ +"math_counter" +{ + "0" + { + "HP_counter" "lvl4_boss_hp" + "HPbar_counter" "lvl4_boss_hpbar_counter" + "HPinit_counter" "lvl4_boss_hp_init" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_boss_dark_hp" + "CustomText" "Dark" + } + "2" + { + "HP_counter" "lvl2_boss_fire_hp" + "CustomText" "Fire" + } + "3" + { + "HP_counter" "lvl2_boss_hp" + } + "4" + { + "HP_counter" "lvl2_boss_ice_hp" + "CustomText" "Ice" + } + "5" + { + "HP_counter" "lvl2_boss_light_hp" + "CustomText" "Light" + } + "6" + { + "HP_counter" "lvl3_boss_hp" + "HPbar_counter" "lvl3_boss_hpbar_counter" + "HPinit_counter" "lvl3_boss_hp_init" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "7" + { + "HP_counter" "lvl4_boss2_hp" + "CustomText" "Barlog" + } + "8" + { + "HP_counter" "Troll_health" + "HPbar_counter" "Troll_hpbar_counter" + "HPinit_counter" "Troll_hp_init" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "9" + { + "HP_counter" "lvl5_boss_hp_1" + "HPbar_counter" "lvl5_boss_hpbar_counter_1" + "HPinit_counter" "lvl5_boss_hp_init_1" + "CustomText" "Sword Castle" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "10" + { + "HP_counter" "lvl5_finalboss_hp" + } + "11" + { + "HP_counter" "lvl5_laserboss_hp" + } + "12" + { + "HP_counter" "darklaser_hp_1" + } + "13" + { + "HP_counter" "darklaser_hp_2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_S_A_M_v1_4.txt b/BossHP/configs/MapBossHP/ze_S_A_M_v1_4.txt new file mode 100644 index 0000000..f68bd12 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_S_A_M_v1_4.txt @@ -0,0 +1,92 @@ +"math_counter" +{ + "0" + { + "HP_counter" "lvl4_boss_hp" + "HPbar_counter" "lvl4_boss_hpbar_counter" + "HPinit_counter" "lvl4_boss_hp_init" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_boss_dark_hp" + "CustomText" "Dark" + } + "2" + { + "HP_counter" "lvl2_boss_fire_hp" + "CustomText" "Fire" + } + "3" + { + "HP_counter" "lvl2_boss_hp" + } + "4" + { + "HP_counter" "lvl2_boss_ice_hp" + "CustomText" "Ice" + } + "5" + { + "HP_counter" "lvl2_boss_light_hp" + "CustomText" "Light" + } + "6" + { + "HP_counter" "lvl3_boss_hp" + "HPbar_counter" "lvl3_boss_hpbar_counter" + "HPinit_counter" "lvl3_boss_hp_init" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "7" + { + "HP_counter" "lvl4_boss2_hp" + "CustomText" "Barlog" + } + "8" + { + "HP_counter" "Troll_health" + "HPbar_counter" "Troll_hpbar_counter" + "HPinit_counter" "Troll_hp_init" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "9" + { + "HP_counter" "lvl5_boss_hp_1" + "HPbar_counter" "lvl5_boss_hpbar_counter_1" + "HPinit_counter" "lvl5_boss_hp_init_1" + "CustomText" "Sword Castle" + "HPbar_min" "" + "HPbar_max" "9" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "10" + { + "HP_counter" "lvl5_finalboss_hp" + } + "11" + { + "HP_counter" "lvl5_laserboss_hp" + } + "12" + { + "HP_counter" "darklaser_hp_1" + } + "13" + { + "HP_counter" "darklaser_hp_2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ShroomForest3_p.txt b/BossHP/configs/MapBossHP/ze_ShroomForest3_p.txt new file mode 100644 index 0000000..62ab514 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ShroomForest3_p.txt @@ -0,0 +1,106 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_HealthCount" + "HPinit_counter" "Boss_HealthInit" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "Type" "breakable" + "BreakableName" "Hitler_Boss_Tank1_Break" + "CustomText" "Tank1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Hitler_Boss_Tank2_Break" + "CustomText" "Tank2" + } + "1" + { + "Type" "breakable" + "BreakableName" "House_ClockBattery_Break1" + "CustomText" "Clock" + } + "1" + { + "Type" "breakable" + "BreakableName" "House_ClockBattery_Break2" + "CustomText" "Battery" + } + "1" + { + "Type" "breakable" + "BreakableName" "Spider_Break1" + "CustomText" "Spider1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Spider_Break2" + "CustomText" "Spider2" + } + "1" + { + "Type" "breakable" + "BreakableName" "Spider_Break3" + "CustomText" "Spider3" + } + "1" + { + "Type" "breakable" + "BreakableName" "Spider_Break4" + "CustomText" "Spider4" + } + "1" + { + "Type" "breakable" + "BreakableName" "Spider_Break5" + "CustomText" "Spider5" + } + "1" + { + "Type" "breakable" + "BreakableName" "Spider_Break6" + "CustomText" "Spider6" + } + "1" + { + "HP_counter" "Skeleton_Boss_Health1" + "HPbar_counter" "Skeleton_Boss_HealthCount1" + "HPinit_counter" "Skeleton_Boss_HealthInit1" + "CustomText" "Skeleton 1" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "Skeleton_Boss_Health2" + "HPbar_counter" "Skeleton_Boss_HealthCount2" + "HPinit_counter" "Skeleton_Boss_HealthInit2" + "CustomText" "Skeleton 2" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "Skeleton_Boss_Health3" + "HPbar_counter" "Skeleton_Boss_HealthCount3" + "HPinit_counter" "Skeleton_Boss_HealthInit3" + "CustomText" "Skeleton 3" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_Sit_Caelum_Paradisus_b7.txt b/BossHP/configs/MapBossHP/ze_Sit_Caelum_Paradisus_b7.txt new file mode 100644 index 0000000..e69de29 diff --git a/BossHP/configs/MapBossHP/ze_UT2004_Convoy_p2.txt b/BossHP/configs/MapBossHP/ze_UT2004_Convoy_p2.txt new file mode 100644 index 0000000..8bde1e7 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_UT2004_Convoy_p2.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_counter2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_alien_shooter_v7_ps4.txt b/BossHP/configs/MapBossHP/ze_alien_shooter_v7_ps4.txt new file mode 100644 index 0000000..7784595 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_alien_shooter_v7_ps4.txt @@ -0,0 +1,18 @@ +"math_counter" +{ + "0" + { + "HP_counter" "counter1" + "HP_Mode" "1" + "HPbar_counter" "counter3" + "HPbar_mode" "1" + "CustomText" "Alien" + } + "1" + { + "HP_counter" "roshi_counter" + "HP_Mode" "1" + "HPbar_mode" "1" + "CustomText" "Roshi" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ancient_wrath_r4.txt b/BossHP/configs/MapBossHP/ze_ancient_wrath_r4.txt new file mode 100644 index 0000000..ca34aeb --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ancient_wrath_r4.txt @@ -0,0 +1,29 @@ +"math_counter" +{ + "config" + { + "BossDieKeepTime" "5" + } + "0" + { + "HP_counter" "boss_3_knockback_1_counter" + } + "1" + { + "Type" "breakable" + "BreakableName" "boss_3_energy_1" + "CustomText" "Energy Tube(Left)" + } + "2" + { + "Type" "breakable" + "BreakableName" "boss_3_energy_2" + "CustomText" "Energy Tube(Right)" + } + "3" + { + "Type" "breakable" + "BreakableName" "boss_3_container" + "CustomText" "Core" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ancient_wrath_v1_2.txt b/BossHP/configs/MapBossHP/ze_ancient_wrath_v1_2.txt new file mode 100644 index 0000000..4e7d6d3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ancient_wrath_v1_2.txt @@ -0,0 +1,23 @@ +"math_counter" +{ + "config" + { + "BossDieKeepTime" "5" + } + "0" + { + "HP_counter" "boss_3_knockback_1_counter" + } + "1" + { + "Type" "breakable" + "BreakableName" "boss_3_energy_1" + "CustomText" "Energy1" + } + "2" + { + "Type" "breakable" + "BreakableName" "boss_3_energy_2" + "CustomText" "Energy2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_biohazard3_nemesis_b5_2.txt b/BossHP/configs/MapBossHP/ze_biohazard3_nemesis_b5_2.txt new file mode 100644 index 0000000..9606a04 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_biohazard3_nemesis_b5_2.txt @@ -0,0 +1,8 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HP_Mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_bioshock_v6_2_csgo4.txt b/BossHP/configs/MapBossHP/ze_bioshock_v6_2_csgo4.txt new file mode 100644 index 0000000..a20ef52 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_bioshock_v6_2_csgo4.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bouncer_Health_BarHP" + "HPbar_counter" "bouncer_Health_HP" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "cohen_Health_BarHP" + "HPbar_counter" "cohen_Health_HP" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "fontaine_Health_BarHP" + "HPbar_counter" "fontaine_Health_HP" + "HPbar_min" "" + "HPbar_max" "6" + "HPbar_default" "" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "songbird_finale_health" + } + "4" + { + "HP_counter" "songbird_Health_BarHP" + "HPbar_counter" "songbird_Health_HP" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_bioshock_v6_2_csgo6.txt b/BossHP/configs/MapBossHP/ze_bioshock_v6_2_csgo6.txt new file mode 100644 index 0000000..a20ef52 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_bioshock_v6_2_csgo6.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bouncer_Health_BarHP" + "HPbar_counter" "bouncer_Health_HP" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "cohen_Health_BarHP" + "HPbar_counter" "cohen_Health_HP" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "fontaine_Health_BarHP" + "HPbar_counter" "fontaine_Health_HP" + "HPbar_min" "" + "HPbar_max" "6" + "HPbar_default" "" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "songbird_finale_health" + } + "4" + { + "HP_counter" "songbird_Health_BarHP" + "HPbar_counter" "songbird_Health_HP" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_bisounours_party_go_v3fix.txt b/BossHP/configs/MapBossHP/ze_bisounours_party_go_v3fix.txt new file mode 100644 index 0000000..be069c5 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_bisounours_party_go_v3fix.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "Pedobear_lvl1" + "CustomText" "Pedobear" + } + "2" + { + "Type" "breakable" + "BreakableName" "Pedobear_lvl2" + "CustomText" "Pedobear" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_bisounours_party_p2.txt b/BossHP/configs/MapBossHP/ze_bisounours_party_p2.txt new file mode 100644 index 0000000..947a7bd --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_bisounours_party_p2.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "Statue" + "CustomText" "Statue" + } + "1" + { + "Type" "breakable" + "BreakableName" "Pedobear_lvl1" + "CustomText" "Pedobear" + } + "2" + { + "Type" "breakable" + "BreakableName" "Pedobear_lvl2" + "CustomText" "Pedobear" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_boatescape999_v4_4.txt b/BossHP/configs/MapBossHP/ze_boatescape999_v4_4.txt new file mode 100644 index 0000000..4ad4d26 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_boatescape999_v4_4.txt @@ -0,0 +1,33 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "Mode_8_Chaos_Box" + "CustomText" "Chaos - 轮回王" + } + "0" + { + "Type" "breakable" + "BreakableName" "Mode_7_Zodiark_Box" + "CustomText" "Zodiark - 戒律王" + } + "0" + { + "Type" "breakable" + "BreakableName" "Mode_5_Genesis_Box" + "CustomText" "Genesis - 杰内西斯" + } + "0" + { + "Type" "breakable" + "BreakableName" "Mode_2_Sephiroth_Box" + "CustomText" "Sephiroth - 萨菲罗斯" + } + "0" + { + "Type" "breakable" + "BreakableName" "Mode_1_Bahamut_Box" + "CustomText" "Bahamut - 巴哈姆特" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_boredom_p.txt b/BossHP/configs/MapBossHP/ze_boredom_p.txt new file mode 100644 index 0000000..9c0df4c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_boredom_p.txt @@ -0,0 +1,27 @@ +"math_counter" +{ + "0" + { + "HP_counter" "CacoDemonHP" + } + "1" + { + "HP_counter" "LostSoulHP" + } + "2" + { + "HP_counter" "DemonHP" + } + "3" + { + "HP_counter" "ImpHP" + } + "4" + { + "HP_counter" "PaperCounter" + } + "5" + { + "HP_counter" "AssaHP" + } +} diff --git a/BossHP/configs/MapBossHP/ze_castlevania_p1_7.txt b/BossHP/configs/MapBossHP/ze_castlevania_p1_7.txt new file mode 100644 index 0000000..5eaa5ba --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_castlevania_p1_7.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_counter" + "HPbar_counter" "boss_hpbar_overlaycounter" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_chaos_v7_4f.txt b/BossHP/configs/MapBossHP/ze_chaos_v7_4f.txt new file mode 100644 index 0000000..535b1f5 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_chaos_v7_4f.txt @@ -0,0 +1,27 @@ +"math_counter" +{ + //Stage 2 Boss HP + "0" + { + "HP_Group" + { + "0" "level2_counter_2" + "1" "level2_counter_2_1" + "2" "level2_counter_2_2" + "3" "level2_counter_2_3" + "4" "level2_counter_2_4" + } + } + //Stage 3 Boss HP + "1" + { + "HP_Group" + { + "0" "counter_2" + "1" "counter_2_1" + "2" "counter_2_2" + "3" "counter_2_3" + "4" "counter_2_4" + } + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_chemical_weapons_depot_v3_1.txt b/BossHP/configs/MapBossHP/ze_chemical_weapons_depot_v3_1.txt new file mode 100644 index 0000000..71d6e2d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_chemical_weapons_depot_v3_1.txt @@ -0,0 +1,13 @@ +"math_counter" +{ + "0" + { + "HP_counter" "HPCounter" + "HPbar_counter" "HPCounterIterator" + "HPinit_counter" "HPCounterBackUp" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_christmas_b2_1.txt b/BossHP/configs/MapBossHP/ze_christmas_b2_1.txt new file mode 100644 index 0000000..b4f8a1b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_christmas_b2_1.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HP_Mode" "1" + "HPbar_counter" "Boss_HealthCount" + "HPinit_counter" "Boss_HealthInit" + "HPbar_mode" "2" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_chroma_v0_4_csgo1.txt b/BossHP/configs/MapBossHP/ze_chroma_v0_4_csgo1.txt new file mode 100644 index 0000000..6f24cab --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_chroma_v0_4_csgo1.txt @@ -0,0 +1,57 @@ +"math_counter" +{ + "config" + { + "RoundEndShowTopDamage" "1" + "ShowTopDamageDuringBOSS" "0" + "ForceEnable" "1" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss7" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss6" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss5" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss4" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss3" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss2" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss1" + "CustomText" "Boss" + } + "1" //func_breakable or func_physbox + { + "Type" "breakable" + "BreakableName" "pbox_boss" + "CustomText" "Boss" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_copy_escape_a01_15.txt b/BossHP/configs/MapBossHP/ze_copy_escape_a01_15.txt new file mode 100644 index 0000000..b6a5828 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_copy_escape_a01_15.txt @@ -0,0 +1,31 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss2_hp_add" + "HPbar_counter" "boss2_hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "cube_hp" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "cube_hp_" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "hp_counter" + "HPbar_counter" "hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_copy_escape_a02_05.txt b/BossHP/configs/MapBossHP/ze_copy_escape_a02_05.txt new file mode 100644 index 0000000..602b6ca --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_copy_escape_a02_05.txt @@ -0,0 +1,35 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "boss2_hp_add" + "HPbar_counter" "boss2_hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "cube_hp" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "cube_hp_" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "hp_counter" + "HPbar_counter" "hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_copy_escape_a03_19.txt b/BossHP/configs/MapBossHP/ze_copy_escape_a03_19.txt new file mode 100644 index 0000000..602b6ca --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_copy_escape_a03_19.txt @@ -0,0 +1,35 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "boss2_hp_add" + "HPbar_counter" "boss2_hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "cube_hp" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "cube_hp_" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "hp_counter" + "HPbar_counter" "hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_copy_escape_v1_1.txt b/BossHP/configs/MapBossHP/ze_copy_escape_v1_1.txt new file mode 100644 index 0000000..602b6ca --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_copy_escape_v1_1.txt @@ -0,0 +1,35 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "boss2_hp_add" + "HPbar_counter" "boss2_hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "cube_hp" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "cube_hp_" + "CustomText" "Cube" + } + "1" + { + "HP_counter" "hp_counter" + "HPbar_counter" "hp" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "0" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_crazy_escape_v5_5_1.txt b/BossHP/configs/MapBossHP/ze_crazy_escape_v5_5_1.txt new file mode 100644 index 0000000..5f8019d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_crazy_escape_v5_5_1.txt @@ -0,0 +1,38 @@ +"math_counter" +{ + "0" + { + "HP_counter" "naraka_counter" + "HPbar_counter" "naraka_hp_iterations" + "HPinit_counter" "naraka_hp_backup" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "naraka_counter2" + "HPbar_counter" "naraka_hp_iterations2" + "HPinit_counter" "naraka_hp_backup2" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "naraka_ultimate_counter" + "HPbar_counter" "naraka_ultimate_hp_iterations" + "HPinit_counter" "naraka_ultimate_hp_backup" + "HPbar_min" "" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" + } + "1" + { + "Type" "breakable" + "BreakableName" "BossCage" + } +} diff --git a/BossHP/configs/MapBossHP/ze_dark_souls_ptd_v0_4_csgo.txt b/BossHP/configs/MapBossHP/ze_dark_souls_ptd_v0_4_csgo.txt new file mode 100644 index 0000000..a9abce3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dark_souls_ptd_v0_4_csgo.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Asylum_Demon_Counter" + "CustomText" "Asylum Demon" + } + "1" + { + "HP_counter" "Broadhead_Counter" + "CustomText" "Broad Head" + } + "2" + { + "HP_counter" "Golem_Counter" + "CustomText" "Golem" + } + "3" + { + "HP_counter" "Gwyndolin_Counter" + "CustomText" "Gwyndolin" + } + "4" + { + "HP_counter" "2Ornstein_Counter" + "CustomText" "Ornstein" + } + "5" + { + "HP_counter" "Phase2_Counter" + "CustomText" "Phase2" + } + "6" + { + "HP_counter" "Gargoyle_Counter" + "CustomText" "Gargoyle" + } + "7" + { + "HP_counter" "Gwyn_Counter" + "CustomText" "Gwyn" + } +} diff --git a/BossHP/configs/MapBossHP/ze_dark_souls_ptd_v0_4_csgo5.txt b/BossHP/configs/MapBossHP/ze_dark_souls_ptd_v0_4_csgo5.txt new file mode 100644 index 0000000..a9abce3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dark_souls_ptd_v0_4_csgo5.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Asylum_Demon_Counter" + "CustomText" "Asylum Demon" + } + "1" + { + "HP_counter" "Broadhead_Counter" + "CustomText" "Broad Head" + } + "2" + { + "HP_counter" "Golem_Counter" + "CustomText" "Golem" + } + "3" + { + "HP_counter" "Gwyndolin_Counter" + "CustomText" "Gwyndolin" + } + "4" + { + "HP_counter" "2Ornstein_Counter" + "CustomText" "Ornstein" + } + "5" + { + "HP_counter" "Phase2_Counter" + "CustomText" "Phase2" + } + "6" + { + "HP_counter" "Gargoyle_Counter" + "CustomText" "Gargoyle" + } + "7" + { + "HP_counter" "Gwyn_Counter" + "CustomText" "Gwyn" + } +} diff --git a/BossHP/configs/MapBossHP/ze_deadcore_c18.txt b/BossHP/configs/MapBossHP/ze_deadcore_c18.txt new file mode 100644 index 0000000..e926799 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_deadcore_c18.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "radiation_hpcounter" + "HPbar_counter" "radiation_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_deadcore_v1_3.txt b/BossHP/configs/MapBossHP/ze_deadcore_v1_3.txt new file mode 100644 index 0000000..e926799 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_deadcore_v1_3.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "radiation_hpcounter" + "HPbar_counter" "radiation_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_destruction_of_exorath_v4_lite.txt b/BossHP/configs/MapBossHP/ze_destruction_of_exorath_v4_lite.txt new file mode 100644 index 0000000..0fceaf2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_destruction_of_exorath_v4_lite.txt @@ -0,0 +1,30 @@ +"math_counter" +{ + "0" + { + "HP_counter" "healbar_counter_extreme" + "HPbar_counter" "healbar_count_total2" + "HPbar_min" "" + "HPbar_max" "12" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "healbar_counter_god" + "HPbar_counter" "healbar_count_total2" + "HPbar_min" "" + "HPbar_max" "12" + "HPbar_default" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "healbar_counter_impossible" + "HPbar_counter" "healbar_count_total2" + "HPbar_min" "" + "HPbar_max" "12" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_diddle_v3.txt b/BossHP/configs/MapBossHP/ze_diddle_v3.txt new file mode 100644 index 0000000..d479c8d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_diddle_v3.txt @@ -0,0 +1,69 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "Ord_lvl_02_boss_break" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "X69XTurtleBossHP1" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "X69XTurtleBosshp2" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "X69XOrd_main_large_diglett_break" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "X69XOrd_main_mid_diglett_break" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "Ord_lvl_01_boss_break" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "X69XOrd_main_little_diglett_break" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "X69Xluff_npc_phys2gg" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "i_npc_hp_1" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "seal" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "dd_hp" + "CustomText" "" + } +} diff --git a/BossHP/configs/MapBossHP/ze_doorhug_and_solo_v5_2_csgo.txt b/BossHP/configs/MapBossHP/ze_doorhug_and_solo_v5_2_csgo.txt new file mode 100644 index 0000000..b80850f --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_doorhug_and_solo_v5_2_csgo.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Counter" + } + "1" + { + "HP_counter" "Monster_ZM_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_dreamin_v1_9_1.txt b/BossHP/configs/MapBossHP/ze_dreamin_v1_9_1.txt new file mode 100644 index 0000000..6aa92ed --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dreamin_v1_9_1.txt @@ -0,0 +1,16 @@ +"math_counter" +{ + "0" + { + "HP_counter" "blade_hp" + } + "1" + { + "HP_counter" "HPCounter2" + "HPbar_counter" "HPCounterIterator2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_dreamin_v2_1.txt b/BossHP/configs/MapBossHP/ze_dreamin_v2_1.txt new file mode 100644 index 0000000..7c2ad6d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dreamin_v2_1.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "0" + { + "HP_counter" "blade_hp" + } + "0" + { + "HP_counter" "st3_hp" + } + "0" + { + "HP_counter" "final_hp" + } + "1" + { + "HP_counter" "HPCounter2" + "HPbar_counter" "HPCounterIterator2" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_dreamin_v2_3.txt b/BossHP/configs/MapBossHP/ze_dreamin_v2_3.txt new file mode 100644 index 0000000..0a28437 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dreamin_v2_3.txt @@ -0,0 +1,27 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "st2_hitbox" + } + "1" + { + "HP_counter" "final_hp" + } + "2" + { + "HP_counter" "st3_hp" + } + "3" + { + "HP_counter" "HPCounter2" + "HP_Mode" "1" + "HPbar_counter" "HPCounterIterator2" + "HPbar_mode" "1" + } + "4" + { + "HP_counter" "blade_hp" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_dreamin_v2b.txt b/BossHP/configs/MapBossHP/ze_dreamin_v2b.txt new file mode 100644 index 0000000..6aa92ed --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dreamin_v2b.txt @@ -0,0 +1,16 @@ +"math_counter" +{ + "0" + { + "HP_counter" "blade_hp" + } + "1" + { + "HP_counter" "HPCounter2" + "HPbar_counter" "HPCounterIterator2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_dreamin_v2c.txt b/BossHP/configs/MapBossHP/ze_dreamin_v2c.txt new file mode 100644 index 0000000..7c2ad6d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_dreamin_v2c.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "0" + { + "HP_counter" "blade_hp" + } + "0" + { + "HP_counter" "st3_hp" + } + "0" + { + "HP_counter" "final_hp" + } + "1" + { + "HP_counter" "HPCounter2" + "HPbar_counter" "HPCounterIterator2" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_eternal_grove_v3.txt b/BossHP/configs/MapBossHP/ze_eternal_grove_v3.txt new file mode 100644 index 0000000..87c07bb --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_eternal_grove_v3.txt @@ -0,0 +1,8 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "i_minotaurgod_hp" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_evernight_a2_4c.txt b/BossHP/configs/MapBossHP/ze_evernight_a2_4c.txt new file mode 100644 index 0000000..a720131 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_evernight_a2_4c.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "imagebasileusis_hp" + } + "1" + { + "HP_counter" "act2_boss_hp" + } +} diff --git a/BossHP/configs/MapBossHP/ze_evernight_a3_1c.txt b/BossHP/configs/MapBossHP/ze_evernight_a3_1c.txt new file mode 100644 index 0000000..c9d5b91 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_evernight_a3_1c.txt @@ -0,0 +1,64 @@ +"math_counter" +{ + "config" + { + "MultBoss" "1" + } + "0" + { + "HP_counter" "Boss_1_Hp" + "HPbar_counter" "Boss_1_Hp_Overlay" + "HPinit_counter" "Boss_1_Hp_Init" + "CustomText" "Basil" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + "0" + { + "HP_counter" "Boss_2_Hp" + "HPbar_counter" "Boss_2_Hp_Overlay" + "HPinit_counter" "Boss_2_Hp_Init" + "CustomText" "Watcher(BOSS)" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + "0" + { + "HP_counter" "Boss_3_Hp" + "HPbar_counter" "Boss_3_Hp_Overlay" + "HPinit_counter" "Boss_3_Hp_Init" + "CustomText" "Gradyrat" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + + "2" + { + "HP_counter" "Boss_2_Hp_Lava" + "CustomText" "Lava" + } + "3" + { + "HP_counter" "Boss_2_Hp_Blizzard" + "CustomText" "Blizzard" + } + "4" + { + "HP_counter" "Boss_2_Hp_Hurricane" + "CustomText" "Hurricane" + } + "5" + { + "HP_counter" "Boss_2_Hp_Arc" + "CustomText" "Arc" + } + "1" + { + "Type" "breakable" + "BreakableName" "Act_1_Crystal" + "CustomText" "Crystal" + } +} diff --git a/BossHP/configs/MapBossHP/ze_evernight_a3_1d.txt b/BossHP/configs/MapBossHP/ze_evernight_a3_1d.txt new file mode 100644 index 0000000..7352370 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_evernight_a3_1d.txt @@ -0,0 +1,64 @@ +"math_counter" +{ + "config" + { + "MultBoss" "1" + } + "0" + { + "HP_counter" "Boss_1_Hp" + "HPbar_counter" "Boss_1_Hp_Overlay" + "HPinit_counter" "Boss_1_Hp_Init" + "CustomText" "Basil" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + "0" + { + "HP_counter" "Boss_2_Hp" + "HPbar_counter" "Boss_2_Hp_Overlay" + "HPinit_counter" "Boss_2_Hp_Init" + "CustomText" "BOSS" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + "0" + { + "HP_counter" "Boss_3_Hp" + "HPbar_counter" "Boss_3_Hp_Overlay" + "HPinit_counter" "Boss_3_Hp_Init" + "CustomText" "Gradyrat" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + + "2" + { + "HP_counter" "Boss_2_Hp_Lava" + "CustomText" "Lava" + } + "3" + { + "HP_counter" "Boss_2_Hp_Blizzard" + "CustomText" "Blizzard" + } + "4" + { + "HP_counter" "Boss_2_Hp_Hurricane" + "CustomText" "Hurricane" + } + "5" + { + "HP_counter" "Boss_2_Hp_Arc" + "CustomText" "Arc" + } + "1" + { + "Type" "breakable" + "BreakableName" "Act_1_Crystal" + "CustomText" "Crystal" + } +} diff --git a/BossHP/configs/MapBossHP/ze_evernight_a3_4.txt b/BossHP/configs/MapBossHP/ze_evernight_a3_4.txt new file mode 100644 index 0000000..7352370 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_evernight_a3_4.txt @@ -0,0 +1,64 @@ +"math_counter" +{ + "config" + { + "MultBoss" "1" + } + "0" + { + "HP_counter" "Boss_1_Hp" + "HPbar_counter" "Boss_1_Hp_Overlay" + "HPinit_counter" "Boss_1_Hp_Init" + "CustomText" "Basil" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + "0" + { + "HP_counter" "Boss_2_Hp" + "HPbar_counter" "Boss_2_Hp_Overlay" + "HPinit_counter" "Boss_2_Hp_Init" + "CustomText" "BOSS" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + "0" + { + "HP_counter" "Boss_3_Hp" + "HPbar_counter" "Boss_3_Hp_Overlay" + "HPinit_counter" "Boss_3_Hp_Init" + "CustomText" "Gradyrat" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_mode" "1" + } + + "2" + { + "HP_counter" "Boss_2_Hp_Lava" + "CustomText" "Lava" + } + "3" + { + "HP_counter" "Boss_2_Hp_Blizzard" + "CustomText" "Blizzard" + } + "4" + { + "HP_counter" "Boss_2_Hp_Hurricane" + "CustomText" "Hurricane" + } + "5" + { + "HP_counter" "Boss_2_Hp_Arc" + "CustomText" "Arc" + } + "1" + { + "Type" "breakable" + "BreakableName" "Act_1_Crystal" + "CustomText" "Crystal" + } +} diff --git a/BossHP/configs/MapBossHP/ze_fapescape_p5.txt b/BossHP/configs/MapBossHP/ze_fapescape_p5.txt new file mode 100644 index 0000000..930f5e2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_fapescape_p5.txt @@ -0,0 +1,61 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + "HPbar_counter" "boss_hp_iterations" + "HPinit_counter" "boss_hp_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "boss_hp2" + "HPbar_counter" "boss_hp_iterations2" + "HPinit_counter" "boss_hp2_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "boss_hp3" + "HPbar_counter" "boss_hp_iterations3" + "HPinit_counter" "boss_hp3_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "boss_hp4" + "HPbar_counter" "mouth_counter" + "HPinit_counter" "boss_hp4_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "4" + { + "HP_counter" "boss_hp5" + "HPbar_counter" "boss_hp_iterations5" + "HPinit_counter" "boss_hp5_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "5" + { + "HP_counter" "lastboss_moving" + } + "6" + { + "HP_counter" "lastboss_static" + } +} diff --git a/BossHP/configs/MapBossHP/ze_fapescape_rote_v1_3.txt b/BossHP/configs/MapBossHP/ze_fapescape_rote_v1_3.txt new file mode 100644 index 0000000..fc2208a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_fapescape_rote_v1_3.txt @@ -0,0 +1,69 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + "HPbar_counter" "boss_hp_iterations" + "HPinit_counter" "boss_hp_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "boss_hp_ex1" + "HPbar_counter" "boss_hp_ex1_iterations" + "HPinit_counter" "boss_hp_ex1_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "boss_hp_ex2" + "HPbar_counter" "boss_hp_ex2_iterations" + "HPinit_counter" "boss_hp_ex2_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "boss_hp2" + "HPbar_counter" "boss_hp2_iterations" + "HPinit_counter" "boss_hp2_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "4" + { + "HP_counter" "boss_hp3" + "HPbar_counter" "boss_hp3_iterations" + "HPinit_counter" "boss_hp3_backup" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "5" + { + "HP_counter" "boss_hp4_phase1" + } + "6" + { + "HP_counter" "boss_hp4_phase2" + } + "7" + { + "HP_counter" "boss_hp4_phase3" + } + "8" + { + "HP_counter" "stage_2_ex_boss_37" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_go_t6.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_go_t6.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_go_t6.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v1_1_csgo.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v1_1_csgo.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v1_1_csgo.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v2_1_g12.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v2_1_g12.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v2_1_g12.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v4fix_ps4.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v4fix_ps4.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v4fix_ps4.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5fix_b10k.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5fix_b10k.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5fix_b10k.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5k.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5k.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5k.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5k_fix.txt b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5k_fix.txt new file mode 100644 index 0000000..fd777b2 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_cosmo_canyon_v5k_fix.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl2_Gi_Nattak_Counter" + } + "2" + { + "HP_counter" "Genesis_Counter" + "CustomText" "Genesis" + } + "3" + { + "HP_counter" "Shinra_Ifrit_Counter" + "CustomText" "Ifrit" + } + "4" + { + "HP_counter" "Shinra_Jenova_Counter" + "CustomText" "Jenova" + } + "5" + { + "HP_counter" "Shinra_Shiva_Counter" + "CustomText" "Shiva" + } + "6" + { + "HP_counter" "Gi_Nattak_Counter" + } + "8" + { + "HP_counter" "Ifrit_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_p8.txt b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_p8.txt new file mode 100644 index 0000000..56fe782 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_p8.txt @@ -0,0 +1,24 @@ +"math_counter" +{ + "config" + { + "ForceEnable" "0" + } + "0" + { + "HP_counter" "bahamut_vida" + "CustomText" "Bahamut" + } + "1" + { + "Type" "breakable" + "BreakableName" "Monstruo_Breakable" + "CustomText" "Monster" + } + "2" + { + "Type" "breakable" + "BreakableName" "bahamutend" + "CustomText" "Bahamut" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v5_3_v5.txt b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v5_3_v5.txt new file mode 100644 index 0000000..420b530 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v5_3_v5.txt @@ -0,0 +1,45 @@ +"math_counter" +{ + "config" + { + "ForceEnable" "0" + } + "0" + { + "HP_counter" "bahamut_vida" + "HPbar_counter" "" + "HPbar_min" "" + "HPbar_max" "" + "HPbar_default" "" + "HPbar_mode" "" // OnHitMin=1 OnHitMax=2 + "CustomText" "Bahamut" + } + + "1" + { + "Type" "breakable" + "BreakableName" "glassT" + "CustomText" "Sephiroth" + } + + "2" + { + "Type" "breakable" + "BreakableName" "bahamutend" + "CustomText" "Bahamut" + } + + "3" + { + "Type" "breakable" + "BreakableName" "bahamutend1" + "CustomText" "Sephiroth" + } + + "4" + { + "Type" "breakable" + "BreakableName" "Monstruo_Breakable" + "CustomText" "Guard Scorpion" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v6_b08_ps7.txt b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v6_b08_ps7.txt new file mode 100644 index 0000000..e6aa14b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v6_b08_ps7.txt @@ -0,0 +1,25 @@ +"math_counter" +{ + "0" + { + "HP_counter" "HPCounter" + "HPbar_counter" "HPCounterIterator" + "HPinit_counter" "HPCounterBackUp" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Final_Fulgor_Breakable" + "CustomText" "Bahamut" + } + "1" + { + "Type" "breakable" + "BreakableName" "Sephiroth_Final_Breakable" + "CustomText" "Sephiroth" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v6_b08_ps8.txt b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v6_b08_ps8.txt new file mode 100644 index 0000000..e6aa14b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_mako_reactor_v6_b08_ps8.txt @@ -0,0 +1,25 @@ +"math_counter" +{ + "0" + { + "HP_counter" "HPCounter" + "HPbar_counter" "HPCounterIterator" + "HPinit_counter" "HPCounterBackUp" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Final_Fulgor_Breakable" + "CustomText" "Bahamut" + } + "1" + { + "Type" "breakable" + "BreakableName" "Sephiroth_Final_Breakable" + "CustomText" "Sephiroth" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffvii_temple_ancient_v4_3.txt b/BossHP/configs/MapBossHP/ze_ffvii_temple_ancient_v4_3.txt new file mode 100644 index 0000000..a392de3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffvii_temple_ancient_v4_3.txt @@ -0,0 +1,55 @@ +"math_counter" +{ + "0" + { + "CustomText" "Dragon" + "HPbar_mode" "1" + "HPbar_counter" "counter_dragon_3" + "HP_counter" "counter_dragon_1" + "HPinit_counter" "counter_dragon_2" + } + "1" + { + "CustomText" "Dragon" + "HPbar_mode" "1" + "HPbar_counter" "counter_dragon_3_1" + "HP_counter" "counter_dragon_1" + "HPinit_counter" "counter_dragon_2" + } + "2" + { + "CustomText" "Gaulle" + "HP_counter" "counter_golem_3" + } + "3" + { + "CustomText" "Sephiroth" + "HPbar_mode" "1" + "HPbar_counter" "Edge_Health" + "HP_counter" "counter_edge_1" + "HPinit_counter" "counter_edge_2" + } + "4" + { + "CustomText" "Bomb" + "HP_counter" "bomb_1_counter" + } + "5" + { + "CustomText" "Bomb" + "HP_counter" "bomb_3_counter" + } + "6" + { + "CustomText" "Sephiroth" + "HP_counter" "counter_seph" + } + "7" + { + "CustomText" "Triface" + "HPbar_mode" "1" + "HPbar_counter" "counter_triface_3" + "HP_counter" "counter_triface_1" + "HPinit_counter" "counter_triface_2" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ffxii_feywood_b3_1_e2_2.txt b/BossHP/configs/MapBossHP/ze_ffxii_feywood_b3_1_e2_2.txt new file mode 100644 index 0000000..2a45dcf --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_feywood_b3_1_e2_2.txt @@ -0,0 +1,40 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_HP_2" + "HPbar_counter" "Boss_HPbar_Counter" + "HPbar_min" "1" + "HPbar_max" "7" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl5_Boss_HP_2" + "HPbar_counter" "lvl5_Boss_HPbar_Counter" + "CustomText" "Zodiark" + "HPbar_min" "1" + "HPbar_max" "7" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "Type" "breakable" + "BreakableName" "Glass_Ball_Break" + "CustomText" "Glass" + } + "1" + { + "Type" "breakable" + "BreakableName" "Tomb_01_Crystall_1" + "CustomText" "Crystall 1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Tomb_01_Crystall_2" + "CustomText" "Crystall 2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_feywood_b3_1_x4.txt b/BossHP/configs/MapBossHP/ze_ffxii_feywood_b3_1_x4.txt new file mode 100644 index 0000000..2a45dcf --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_feywood_b3_1_x4.txt @@ -0,0 +1,40 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_HP_2" + "HPbar_counter" "Boss_HPbar_Counter" + "HPbar_min" "1" + "HPbar_max" "7" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl5_Boss_HP_2" + "HPbar_counter" "lvl5_Boss_HPbar_Counter" + "CustomText" "Zodiark" + "HPbar_min" "1" + "HPbar_max" "7" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "Type" "breakable" + "BreakableName" "Glass_Ball_Break" + "CustomText" "Glass" + } + "1" + { + "Type" "breakable" + "BreakableName" "Tomb_01_Crystall_1" + "CustomText" "Crystall 1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Tomb_01_Crystall_2" + "CustomText" "Crystall 2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_feywood_b6_1k.txt b/BossHP/configs/MapBossHP/ze_ffxii_feywood_b6_1k.txt new file mode 100644 index 0000000..7330868 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_feywood_b6_1k.txt @@ -0,0 +1,47 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_HP_2" + "HPbar_counter" "Boss_HPbar_Counter" + "HPbar_min" "1" + "HPbar_max" "7" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "lvl5_Boss_HP_2" + "HPbar_counter" "lvl5_Boss_HPbar_Counter" + "CustomText" "Zodiark" + "HPbar_min" "1" + "HPbar_max" "7" + "HPbar_default" "1" + "HPbar_mode" "2" + } + "1" + { + "Type" "breakable" + "BreakableName" "Glass_Ball_Break" + "CustomText" "Glass" + } + "1" + { + "Type" "breakable" + "BreakableName" "Tomb_01_Crystall_1" + "CustomText" "Crystall 1" + } + "1" + { + "Type" "breakable" + "BreakableName" "Tomb_01_Crystall_2" + "CustomText" "Crystall 2" + } + "1" + { + "Type" "breakable" + "BreakableName" "boss_cactus_break" + "CustomText" "Cactus" + } + +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_csgo1.txt b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_csgo1.txt new file mode 100644 index 0000000..e5e79c3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_csgo1.txt @@ -0,0 +1,44 @@ +"math_counter" +{ + "config" + { + "MultBoss" "" + "IsDynamicHP" "" + "RoundEndShowTopDamage" "" + "ShowTopDamageDuringBOSS" "" + } + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Mateus" + "CustomText2" "" + } + "1" + { + "HP_counter" "Chaos_Health" + "HPbar_counter" "Chaos_Overlay_Counter" + "HPinit_counter" "Chaos_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Chaos" + "CustomText2" "" + } + "2" + { + "HP_counter" "Fin_Boss_Counter" + "CustomText" "Mateus" + } + "3" + { + "HP_counter" "Guardian_Counter" + "CustomText" "Guardian" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_csgo5.txt b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_csgo5.txt new file mode 100644 index 0000000..e5e79c3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_csgo5.txt @@ -0,0 +1,44 @@ +"math_counter" +{ + "config" + { + "MultBoss" "" + "IsDynamicHP" "" + "RoundEndShowTopDamage" "" + "ShowTopDamageDuringBOSS" "" + } + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Mateus" + "CustomText2" "" + } + "1" + { + "HP_counter" "Chaos_Health" + "HPbar_counter" "Chaos_Overlay_Counter" + "HPinit_counter" "Chaos_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Chaos" + "CustomText2" "" + } + "2" + { + "HP_counter" "Fin_Boss_Counter" + "CustomText" "Mateus" + } + "3" + { + "HP_counter" "Guardian_Counter" + "CustomText" "Guardian" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_r4.txt b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_r4.txt new file mode 100644 index 0000000..e5e79c3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_r4.txt @@ -0,0 +1,44 @@ +"math_counter" +{ + "config" + { + "MultBoss" "" + "IsDynamicHP" "" + "RoundEndShowTopDamage" "" + "ShowTopDamageDuringBOSS" "" + } + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Mateus" + "CustomText2" "" + } + "1" + { + "HP_counter" "Chaos_Health" + "HPbar_counter" "Chaos_Overlay_Counter" + "HPinit_counter" "Chaos_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Chaos" + "CustomText2" "" + } + "2" + { + "HP_counter" "Fin_Boss_Counter" + "CustomText" "Mateus" + } + "3" + { + "HP_counter" "Guardian_Counter" + "CustomText" "Guardian" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_v1_4_a7t.txt b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_v1_4_a7t.txt new file mode 100644 index 0000000..e5e79c3 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_paramina_rift_v1_4_a7t.txt @@ -0,0 +1,44 @@ +"math_counter" +{ + "config" + { + "MultBoss" "" + "IsDynamicHP" "" + "RoundEndShowTopDamage" "" + "ShowTopDamageDuringBOSS" "" + } + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Mateus" + "CustomText2" "" + } + "1" + { + "HP_counter" "Chaos_Health" + "HPbar_counter" "Chaos_Overlay_Counter" + "HPinit_counter" "Chaos_Health_Init" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "" + "HPbar_mode" "2" + "CustomText" "Chaos" + "CustomText2" "" + } + "2" + { + "HP_counter" "Fin_Boss_Counter" + "CustomText" "Mateus" + } + "3" + { + "HP_counter" "Guardian_Counter" + "CustomText" "Guardian" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_b3_ps9.txt b/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_b3_ps9.txt new file mode 100644 index 0000000..077af6c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_b3_ps9.txt @@ -0,0 +1,52 @@ +"math_counter" +{ + "config" + { + "MultBoss" "1" + } + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "Stage_13_End_Boss_Counter" + "CustomText" "Hydro" + } + "2" + { + "HP_counter" "Stage_2_End_Dragon_Counter" + "CustomText" "Hydro" + } + "3" + { + "HP_counter" "Stage_24_End_Famfrit_Counter" + "CustomText" "Famfrit" + } + "4" + { + "HP_counter" "Fat_Nigger_Counter" + "CustomText" "Cuchulainn" + } + "5" + { + "HP_counter" "Stage_4_End_Guard_Counter" + "CustomText" "Guardian" + } + "6" + { + "HP_counter" "Famfrit_Holy_Summon_Counter" + "CustomText" "Guardian(Holy)" + } + "7" + { + "HP_counter" "Famfrit_Fire_Summon_Counter" + "CustomText" "Guardian(Fire)" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_b4_ps8.txt b/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_b4_ps8.txt new file mode 100644 index 0000000..077af6c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_b4_ps8.txt @@ -0,0 +1,52 @@ +"math_counter" +{ + "config" + { + "MultBoss" "1" + } + "0" + { + "HP_counter" "Special_Health" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "Stage_13_End_Boss_Counter" + "CustomText" "Hydro" + } + "2" + { + "HP_counter" "Stage_2_End_Dragon_Counter" + "CustomText" "Hydro" + } + "3" + { + "HP_counter" "Stage_24_End_Famfrit_Counter" + "CustomText" "Famfrit" + } + "4" + { + "HP_counter" "Fat_Nigger_Counter" + "CustomText" "Cuchulainn" + } + "5" + { + "HP_counter" "Stage_4_End_Guard_Counter" + "CustomText" "Guardian" + } + "6" + { + "HP_counter" "Famfrit_Holy_Summon_Counter" + "CustomText" "Guardian(Holy)" + } + "7" + { + "HP_counter" "Famfrit_Fire_Summon_Counter" + "CustomText" "Guardian(Fire)" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_t4_ps3.txt b/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_t4_ps3.txt new file mode 100644 index 0000000..3df37ba --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_ridorana_cataract_t4_ps3.txt @@ -0,0 +1,72 @@ +"math_counter" +{ + "config" + { + "BossRewardMoney" "5" + } + "0" + { + "HP_counter" "Special_Health" + "HP_Mode" "1" + "HPbar_counter" "Special_HealthCount" + "HPinit_counter" "Special_HealthInit" + "HPbar_min" "" + "HPbar_max" "16" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "Stage_13_End_Boss_Counter" + "HP_Mode" "1" + "CustomText" "Hydro" + } + "2" + { + "HP_counter" "Stage_2_End_Dragon_Counter" + "HP_Mode" "1" + "CustomText" "Hydro" + } + "3" + { + "HP_counter" "Stage_24_End_Famfrit_Counter" + "HP_Mode" "1" + "CustomText" "Famfrit" + } + "4" + { + "HP_counter" "Fat_Nigger_Counter" + "HP_Mode" "1" + "CustomText" "Zalera" + } + "5" + { + "HP_counter" "Stage_4_End_Guard_Counter" + "HP_Mode" "1" + "CustomText" "Gabranth" + } + "6" + { + "HP_counter" "Famfrit_Holy_Summon_Counter" + "HP_Mode" "1" + "CustomText" "Zargabaath(Holy)" + } + "7" + { + "HP_counter" "Famfrit_Fire_Summon_Counter" + "HP_Mode" "1" + "CustomText" "Bergan(Fire)" + } + "8" + { + "Type" "breakable" + "BreakableName" "Stage_1_Hold_3_Crystall_1" + "CustomText" ""Crystal - 1" + } + "9" + { + "Type" "breakable" + "BreakableName" "Stage_1_Hold_3_Crystall_2" + "CustomText" ""Crystal - 2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_westersand_v7_z9.txt b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v7_z9.txt new file mode 100644 index 0000000..2e1110c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v7_z9.txt @@ -0,0 +1,38 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "0" + "HPbar_max" "6" + "HPbar_default" "6" + "HPbar_mode" "1" // 1=降序 =2升序 + } + "5" + { + "HP_counter" "Guard_2_Health" + "CustomText" "Healer" + } + "5" + { + "HP_counter" "Guard_3_Health" + "CustomText" "Battlemage" + } + "2" + { + "HP_counter" "Airship_Ending_Boss_Health" + "CustomText" "Chaos" + } + "3" + { + "HP_counter" "Ehe_Dragon_HP" + "CustomText" "Dragon" + } + "4" + { + "HP_counter" "Ehe_Guardian_HP" + "CustomText" "Guardian" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_b4k.txt b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_b4k.txt new file mode 100644 index 0000000..755328a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_b4k.txt @@ -0,0 +1,46 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "0" + "HPbar_max" "7" + "HPbar_default" "0" + "HPbar_mode" "2" // 1=降序 =2升序 + } + "1" + { + "HP_counter" "Espers_Mateus_Counter" + "CustomText" "Mateus" + } + "2" + { + "HP_counter" "Espers_Belias_Counter" + "CustomText" "Belias" + } + "3" + { + "HP_counter" "Espers_Chaos_Counter" + "CustomText" "Chaos" + } + "4" + { + "Type" "breakable" + "BreakableName" "Mateus_Summon_Physbox" + "CustomText" "Mateus" + } + "5" + { + "Type" "breakable" + "BreakableName" "Chaos_Summon_Physbox" + "CustomText" "Chaos" + } + "6" + { + "Type" "breakable" + "BreakableName" "Belias_Summon_Physbox" + "CustomText" "Belias" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_b5k.txt b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_b5k.txt new file mode 100644 index 0000000..755328a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_b5k.txt @@ -0,0 +1,46 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "0" + "HPbar_max" "7" + "HPbar_default" "0" + "HPbar_mode" "2" // 1=降序 =2升序 + } + "1" + { + "HP_counter" "Espers_Mateus_Counter" + "CustomText" "Mateus" + } + "2" + { + "HP_counter" "Espers_Belias_Counter" + "CustomText" "Belias" + } + "3" + { + "HP_counter" "Espers_Chaos_Counter" + "CustomText" "Chaos" + } + "4" + { + "Type" "breakable" + "BreakableName" "Mateus_Summon_Physbox" + "CustomText" "Mateus" + } + "5" + { + "Type" "breakable" + "BreakableName" "Chaos_Summon_Physbox" + "CustomText" "Chaos" + } + "6" + { + "Type" "breakable" + "BreakableName" "Belias_Summon_Physbox" + "CustomText" "Belias" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_x16.txt b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_x16.txt new file mode 100644 index 0000000..755328a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxii_westersand_v8zeta1_x16.txt @@ -0,0 +1,46 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Overlay_Counter" + "HPinit_counter" "Boss_Health_Init" + "HPbar_min" "0" + "HPbar_max" "7" + "HPbar_default" "0" + "HPbar_mode" "2" // 1=降序 =2升序 + } + "1" + { + "HP_counter" "Espers_Mateus_Counter" + "CustomText" "Mateus" + } + "2" + { + "HP_counter" "Espers_Belias_Counter" + "CustomText" "Belias" + } + "3" + { + "HP_counter" "Espers_Chaos_Counter" + "CustomText" "Chaos" + } + "4" + { + "Type" "breakable" + "BreakableName" "Mateus_Summon_Physbox" + "CustomText" "Mateus" + } + "5" + { + "Type" "breakable" + "BreakableName" "Chaos_Summon_Physbox" + "CustomText" "Chaos" + } + "6" + { + "Type" "breakable" + "BreakableName" "Belias_Summon_Physbox" + "CustomText" "Belias" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v2_8.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v2_8.txt new file mode 100644 index 0000000..d103a5a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v2_8.txt @@ -0,0 +1,34 @@ +"math_counter" +{ + "1" + { + "HP_Group" + { + "0" "bahamut_hp_100" + "1" "bahamut_hp_75" + "2" "bahamut_hp_50" + "3" "bahamut_hp_25" + } + "CustomText" "Bahamut" + } + "5" + { + "HP_counter" "nael_hp" + "CustomText" "Nael Van Darnus" + } + "6" + { + "HP_counter" "ads_atack1_hp" + "CustomText" "ADS(Right)" + } + "7" + { + "HP_counter" "ads_atack2_hp" + "CustomText" "ADS(Left)" + } + "0" + { + "HP_counter" "ADS_HP" + "CustomText" "ADS" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v2_va2.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v2_va2.txt new file mode 100644 index 0000000..d103a5a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v2_va2.txt @@ -0,0 +1,34 @@ +"math_counter" +{ + "1" + { + "HP_Group" + { + "0" "bahamut_hp_100" + "1" "bahamut_hp_75" + "2" "bahamut_hp_50" + "3" "bahamut_hp_25" + } + "CustomText" "Bahamut" + } + "5" + { + "HP_counter" "nael_hp" + "CustomText" "Nael Van Darnus" + } + "6" + { + "HP_counter" "ads_atack1_hp" + "CustomText" "ADS(Right)" + } + "7" + { + "HP_counter" "ads_atack2_hp" + "CustomText" "ADS(Left)" + } + "0" + { + "HP_counter" "ADS_HP" + "CustomText" "ADS" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v4_10.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v4_10.txt new file mode 100644 index 0000000..d41a012 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v4_10.txt @@ -0,0 +1,63 @@ +"math_counter" +{ + "0" + { + "CustomText" "Bahamut(true)" + "HP_Group" + { + "0" "true_bahamut_hp_100" + "1" "true_bahamut_hp_75" + "2" "true_bahamut_hp_50" + "3" "true_bahamut_hp_25" + } + } + "1" + { + "CustomText" "Bahamut" + "HP_Group" + { + "0" "bahamut_hp_100" + "1" "bahamut_hp_75" + "2" "bahamut_hp_50" + "3" "bahamut_hp_25" + } + } + "2" + { + "CustomText" "Dalamud" + "HP_Group" + { + "0" "dalamud_hp_100" + "1" "dalamud_hp_50" + } + } + "3" + { + "CustomText" "Behemoth" + "HP_Group" + { + "0" "behemoth_king_hp3_counter" + "1" "behemoth_king_hp2_counter" + "2" "behemoth_king_hp1_counter" + } + } + "4" + { + "HP_counter" "ads_hp" + "CustomText" "ADS" + } + "5" + { + "HP_counter" "ads_atack2_hp" + "CustomText" "ADS(Left)" + } + "6" + { + "HP_counter" "ads_atack1_hp" + "CustomText" "ADS(Right)" + } + "7" + { + "HP_counter" "lv3_nael_hp" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v4_va2.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v4_va2.txt new file mode 100644 index 0000000..d41a012 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v4_va2.txt @@ -0,0 +1,63 @@ +"math_counter" +{ + "0" + { + "CustomText" "Bahamut(true)" + "HP_Group" + { + "0" "true_bahamut_hp_100" + "1" "true_bahamut_hp_75" + "2" "true_bahamut_hp_50" + "3" "true_bahamut_hp_25" + } + } + "1" + { + "CustomText" "Bahamut" + "HP_Group" + { + "0" "bahamut_hp_100" + "1" "bahamut_hp_75" + "2" "bahamut_hp_50" + "3" "bahamut_hp_25" + } + } + "2" + { + "CustomText" "Dalamud" + "HP_Group" + { + "0" "dalamud_hp_100" + "1" "dalamud_hp_50" + } + } + "3" + { + "CustomText" "Behemoth" + "HP_Group" + { + "0" "behemoth_king_hp3_counter" + "1" "behemoth_king_hp2_counter" + "2" "behemoth_king_hp1_counter" + } + } + "4" + { + "HP_counter" "ads_hp" + "CustomText" "ADS" + } + "5" + { + "HP_counter" "ads_atack2_hp" + "CustomText" "ADS(Left)" + } + "6" + { + "HP_counter" "ads_atack1_hp" + "CustomText" "ADS(Right)" + } + "7" + { + "HP_counter" "lv3_nael_hp" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5.txt new file mode 100644 index 0000000..80a8e25 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5.txt @@ -0,0 +1,75 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Behemoth_HP_Counter" + "HPinit_counter" "Behemoth_HP_Initial" + "HPbar_counter" "Behemoth_HP_Breakable_Counter" + "HPbar_min" "0" + "HPbar_max" "4" + "HPbar_default" "0" + "HPbar_mode" "2" // 1=降序 =2升序 + "CustomText" "Behemoth" + } + "1" + { + "HP_counter" "Garland_HP_Counter" + "CustomText" "Garland" + } + "2" + { + "HP_counter" "KOH_HP_Counter" + "CustomText" "KOH" + } + "3" + { + "HP_counter" "Sanctuary_Keeper_HP_Counter" + "CustomText" "Sanctuary" + } + + "4" + { + "HP_counter" "Odin_HP_Counter" + "CustomText" "Odin" + } + "5" + { + "HP_counter" "Seymour_Natus_HP_Counter" + "CustomText" "Seymour" + } + "3" + { + "HP_counter" "Seymour_Natus_Break_HP_Counter" + "CustomText" "Break Attack" + } + "6" + { + "HP_counter" "Garuda_HP_Counter" + "CustomText" "Garuda" + } + "7" + { + "HP_counter" "Serpent_HP_Counter" + "CustomText" "Serpent" + } + "8" + { + "HP_counter" "Iron_Giant_HP_Counter" + "CustomText" "Iron Giant" + } + "9" + { + "HP_counter" "Alteci_HP_Counter" + "CustomText" "Alteci" + } + "10" + { + "HP_counter" "Knight_HP_Counter" + "CustomText" "Knight" + } + "11" + { + "HP_counter" "Adrammelech_HP_Counter" + "CustomText" "Adrammelech" + } +} diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5_1.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5_1.txt new file mode 100644 index 0000000..021f129 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5_1.txt @@ -0,0 +1,85 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Knight_HP_Counter" + "HP_Mode" "1" + "CustomText" "Knight" + } + "1" + { + "HP_counter" "KOH_HP_Counter" + "HP_Mode" "1" + "CustomText" "Keeper of Halidom" + } + "2" + { + "HP_counter" "Behemoth_HP_Counter" + "HP_Mode" "1" + "HPbar_counter" "Behemoth_HP_Breakable_Counter" + "HPbar_max" "4" + "HPinit_counter" "Behemoth_HP_Initial" + "CustomText" "Behemoth" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "Seymour_Natus_HP_Counter" + "HP_Mode" "1" + "CustomText" "Seymour Natus" + } + "4" + { + "HP_counter" "Seymour_Natus_Break_HP_Counter" + "HP_Mode" "1" + "CustomText" "Mortibody" + } + "5" + { + "HP_counter" "Sanctuary_Keeper_HP_Counter" + "HP_Mode" "1" + "CustomText" "Sanctuary Keeper" + } + "6" + { + "HP_counter" "Odin_HP_Counter" + "HP_Mode" "1" + "CustomText" "Odin" + } + "7" + { + "HP_counter" "Castle_Bahamut_HP_Counter" + "HP_Mode" "1" + "CustomText" "Bahamut" + } + "8" + { + "HP_counter" "Ultima_Weapon_HP_Counter" + "HP_Mode" "1" + "CustomText" "Ultima Weapon" + } + "9" + { + "HP_counter" "Final_Bahamut_HP_Counter" + "HP_Mode" "1" + "CustomText" "Bahamut" + } + "10" + { + "HP_counter" "Garland_HP_Counter" + "HP_Mode" "1" + "CustomText" "Garland" + } + "11" + { + "HP_counter" "Iron_Giant_HP_Counter" + "HP_Mode" "1" + "CustomText" "Iron Giant" + } + "12" + { + "HP_counter" "Serpent_HP_Counter" + "HP_Mode" "1" + "CustomText" "Serpent" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5_2.txt b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5_2.txt new file mode 100644 index 0000000..5f1b517 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_ffxiv_wanderers_palace_v5_2.txt @@ -0,0 +1,74 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Knight_HP_Counter" + "CustomText" "Knight" + } + "1" + { + "HP_counter" "KOH_HP_Counter" + "CustomText" "Keeper of Halidom" + } + "2" + { + "HP_counter" "Behemoth_HP_Counter" + "HP_Mode" "1" + "HPbar_counter" "Behemoth_HP_Breakable_Counter" + "HPinit_counter" "Behemoth_HP_Initial" + "CustomText" "Behemoth" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "Seymour_Natus_HP_Counter" + "CustomText" "Seymour Natus" + } + "4" + { + "HP_counter" "Mortibody_HP_Counter" + "CustomText" "Mortibody" + } + "5" + { + "HP_counter" "Sanctuary_Keeper_HP_Counter" + "CustomText" "Sanctuary Keeper" + } + "6" + { + "HP_counter" "Odin_HP_Counter" + "CustomText" "Odin" + } + "7" + { + "HP_counter" "Castle_Bahamut_HP_Counter" + "CustomText" "Castle Bahamut" + } + "8" + { + "HP_counter" "Ultima_Weapon_HP_Counter" + "CustomText" "Ultima Weapon" + } + "9" + { + "HP_counter" "Final_Bahamut_HP_Counter" + "CustomText" "Final Bahamut" + } + "10" + { + "HP_counter" "Garland_HP_Counter" + "CustomText" "Garland" + } + "11" + { + "Type" "breakable" + "BreakableName" "Iron_Giant_Breakable" + "CustomText" "Iron Giant" + } + "12" + { + "Type" "breakable" + "BreakableName" "Serpent_Breakable" + "CustomText" "Serpent" + } +} diff --git a/BossHP/configs/MapBossHP/ze_forbidden_repository_a1_3.txt b/BossHP/configs/MapBossHP/ze_forbidden_repository_a1_3.txt new file mode 100644 index 0000000..449f2be --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_forbidden_repository_a1_3.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } + "0" + { + "HP_counter" "boss2_hp" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_foreign_sand_mix_v2_fix.txt b/BossHP/configs/MapBossHP/ze_foreign_sand_mix_v2_fix.txt new file mode 100644 index 0000000..64a0389 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_foreign_sand_mix_v2_fix.txt @@ -0,0 +1,13 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "lolBoss" + } + "1" + { + "Type" "breakable" + "BreakableName" "Bahamut_clip" + } +} diff --git a/BossHP/configs/MapBossHP/ze_frostdrake_tower_v1.txt b/BossHP/configs/MapBossHP/ze_frostdrake_tower_v1.txt new file mode 100644 index 0000000..344c2fd --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_frostdrake_tower_v1.txt @@ -0,0 +1,9 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "drake_hp" + "CustomText" "" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_gameshow_v1_2_csgo1.txt b/BossHP/configs/MapBossHP/ze_gameshow_v1_2_csgo1.txt new file mode 100644 index 0000000..a8726f7 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_gameshow_v1_2_csgo1.txt @@ -0,0 +1,8 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "boss_break" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_grau_a03_1.txt b/BossHP/configs/MapBossHP/ze_grau_a03_1.txt new file mode 100644 index 0000000..12991ef --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_grau_a03_1.txt @@ -0,0 +1,16 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "counter_st3_boss_vida" + "HPbar_counter" "counter_st3_boss_vida_bar" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_grau_a03_2.txt b/BossHP/configs/MapBossHP/ze_grau_a03_2.txt new file mode 100644 index 0000000..12991ef --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_grau_a03_2.txt @@ -0,0 +1,16 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "counter_st3_boss_vida" + "HPbar_counter" "counter_st3_boss_vida_bar" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_grau_a03_4f.txt b/BossHP/configs/MapBossHP/ze_grau_a03_4f.txt new file mode 100644 index 0000000..12991ef --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_grau_a03_4f.txt @@ -0,0 +1,16 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "counter_st3_boss_vida" + "HPbar_counter" "counter_st3_boss_vida_bar" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_gris_v1_13.txt b/BossHP/configs/MapBossHP/ze_gris_v1_13.txt new file mode 100644 index 0000000..7fe7b11 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_gris_v1_13.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "config" + { + "IsDynamicHP" "1" + } + "0" + { + "HP_counter" "counter_rtv_boss_vida" + "HPbar_counter" "counter_rtv_hp_bar" + "HPbar_min" "" + "HPbar_max" "15" + "HPbar_default" "15" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "counter_st4_boss_vida" + "HPbar_counter" "counter_st4_boss_vida_bar" + "HPbar_min" "" + "HPbar_max" "11" + "HPbar_default" "4" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "l4_boss_vida" + "HPbar_counter" "l4_boss_life_counter" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "white_boss_vida" + "HPbar_counter" "white_boss_life_counter" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_harry_potter_v1_3.txt b/BossHP/configs/MapBossHP/ze_harry_potter_v1_3.txt new file mode 100644 index 0000000..d5ce46d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_harry_potter_v1_3.txt @@ -0,0 +1,50 @@ +"math_counter" +{ + "config" + { + "BossHpKeepTime" "60" + "MaxLegalMathCounterHP" "150000" + } + "0" + { + "HP_counter" "map_boss_counter2" + "HP_Mode" "1" + "HPbar_counter" "map_boss_counter3" + "HPinit_counter" "map_boss_counter1" + "CustomText" "" + "HPbar_mode" "2" + } + "0" + { + "HP_counter" "stage4_miniboss_counter2" + "HP_Mode" "1" + "HPbar_counter" "stage4_miniboss_counter3" + "HPinit_counter" "stage4_miniboss_counter1" + "CustomText" "Mini spider" + "HPbar_mode" "2" + } + "0" + { + "HP_counter" "stage5_boss_d1_counter2" + "HP_Mode" "1" + "HPbar_counter" "stage5_boss_d1_counter3" + "HPinit_counter" "stage5_boss_d1_counter" + "CustomText" "DeathEater1" + "HPbar_mode" "2" + } + "0" + { + "HP_counter" "stage5_ex_d2_counter2" + "HP_Mode" "1" + "HPbar_counter" "stage5_ex_d2_counter3" + "HPinit_counter" "stage5_ex_d2_counter" + "CustomText" "DeathEater2" + "HPbar_mode" "2" + } + "0" + { + "HP_counter" "stage5_ending_counter2" + "HP_Mode" "1" + "CustomText" "DEMENTOR" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_hold_em_v10_p.txt b/BossHP/configs/MapBossHP/ze_hold_em_v10_p.txt new file mode 100644 index 0000000..eaea9c9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_hold_em_v10_p.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } +} diff --git a/BossHP/configs/MapBossHP/ze_hold_em_v8.txt b/BossHP/configs/MapBossHP/ze_hold_em_v8.txt new file mode 100644 index 0000000..eaea9c9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_hold_em_v8.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } +} diff --git a/BossHP/configs/MapBossHP/ze_industrial_dejavu_v3_3_1_e2.txt b/BossHP/configs/MapBossHP/ze_industrial_dejavu_v3_3_1_e2.txt new file mode 100644 index 0000000..8e9708c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_industrial_dejavu_v3_3_1_e2.txt @@ -0,0 +1,18 @@ +"math_counter" +{ + "0" + { + "HP_counter" "BossHPBarPerPlayer" + "HP_Mode" "1" + "HPbar_counter" "BossHPBarPlayers" + "HPinit_counter" "BossHPBarInitial" + "CustomText" "" + "HPbar_mode" "1" + } + "1" + { + "Type" "breakable" + "BreakableName" "BossGlassWall" + "CustomText" "Glass" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_infected_sewers_v4_ps6.txt b/BossHP/configs/MapBossHP/ze_infected_sewers_v4_ps6.txt new file mode 100644 index 0000000..b510bde --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_infected_sewers_v4_ps6.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "hpcount2" + "HPbar_counter" "hpcount3" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_infected_sewers_v6_1_1.txt b/BossHP/configs/MapBossHP/ze_infected_sewers_v6_1_1.txt new file mode 100644 index 0000000..b510bde --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_infected_sewers_v6_1_1.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "hpcount2" + "HPbar_counter" "hpcount3" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_isla_nublar_p3.txt b/BossHP/configs/MapBossHP/ze_isla_nublar_p3.txt new file mode 100644 index 0000000..04cb5d0 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_isla_nublar_p3.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "trexhitbased1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo2.txt b/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo2.txt new file mode 100644 index 0000000..3dc1381 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo2.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Frog_Counter" + } + "1" + { + "HP_counter" "Frog2_Counter" + } + "2" + { + "HP_counter" "Solid_Paper_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo3.txt b/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo3.txt new file mode 100644 index 0000000..1a5ab3b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo3.txt @@ -0,0 +1,18 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Frog_Counter" + "CustomText" "Frog" + } + "1" + { + "HP_counter" "Frog2_Counter" + "CustomText" "Frog" + } + "2" + { + "HP_counter" "Solid_Paper_Counter" + "CustomText" "Solid Paper" + } +} diff --git a/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo4.txt b/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo4.txt new file mode 100644 index 0000000..1a5ab3b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_johnny_nukem_b8_1_csgo4.txt @@ -0,0 +1,18 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Frog_Counter" + "CustomText" "Frog" + } + "1" + { + "HP_counter" "Frog2_Counter" + "CustomText" "Frog" + } + "2" + { + "HP_counter" "Solid_Paper_Counter" + "CustomText" "Solid Paper" + } +} diff --git a/BossHP/configs/MapBossHP/ze_l0v0l_a7_csgo4.bak b/BossHP/configs/MapBossHP/ze_l0v0l_a7_csgo4.bak new file mode 100644 index 0000000..c58709d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_l0v0l_a7_csgo4.bak @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bosslvl3_counter1" + "HPbar_counter" "bosslvl3_counter3" + "HPinit_counter" "bosslvl3_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "bosslvl2_counter1" + "HPbar_counter" "bosslvl2_counter3" + "HPinit_counter" "bosslvl2_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "bosslvl3_end_counter1" + "HPbar_counter" "bosslvl3_end_counter3" + "HPinit_counter" "bosslvl3_end_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "bosslvl1_counter1" + "HPbar_counter" "bosslvl1_counter3" + "HPinit_counter" "bosslvl1_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_l0v0l_a7_csgo4.txt b/BossHP/configs/MapBossHP/ze_l0v0l_a7_csgo4.txt new file mode 100644 index 0000000..c58709d --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_l0v0l_a7_csgo4.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bosslvl3_counter1" + "HPbar_counter" "bosslvl3_counter3" + "HPinit_counter" "bosslvl3_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "bosslvl2_counter1" + "HPbar_counter" "bosslvl2_counter3" + "HPinit_counter" "bosslvl2_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "bosslvl3_end_counter1" + "HPbar_counter" "bosslvl3_end_counter3" + "HPinit_counter" "bosslvl3_end_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } + "3" + { + "HP_counter" "bosslvl1_counter1" + "HPbar_counter" "bosslvl1_counter3" + "HPinit_counter" "bosslvl1_counter2" + "HPbar_min" "" + "HPbar_max" "999999" + "HPbar_default" "10" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_l0v0l_csgo4_fix.txt b/BossHP/configs/MapBossHP/ze_l0v0l_csgo4_fix.txt new file mode 100644 index 0000000..5d38dc4 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_l0v0l_csgo4_fix.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bosslvl1_counter1" + "HPbar_counter" "bosslvl1_counter3" + "HPinit_counter" "bosslvl1_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "bosslvl2_counter1" + "HPbar_counter" "bosslvl2_counter3" + "HPinit_counter" "bosslvl2_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "bosslvl3_counter1" + "HPbar_counter" "bosslvl3_counter3" + "HPinit_counter" "bosslvl3_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "bosslvl3_end_counter1" + "HPbar_counter" "bosslvl3_end_counter3" + "HPinit_counter" "bosslvl3_end_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_l0v0l_v1_4_csgo1.txt b/BossHP/configs/MapBossHP/ze_l0v0l_v1_4_csgo1.txt new file mode 100644 index 0000000..5d38dc4 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_l0v0l_v1_4_csgo1.txt @@ -0,0 +1,43 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bosslvl1_counter1" + "HPbar_counter" "bosslvl1_counter3" + "HPinit_counter" "bosslvl1_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "bosslvl2_counter1" + "HPbar_counter" "bosslvl2_counter3" + "HPinit_counter" "bosslvl2_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "bosslvl3_counter1" + "HPbar_counter" "bosslvl3_counter3" + "HPinit_counter" "bosslvl3_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "bosslvl3_end_counter1" + "HPbar_counter" "bosslvl3_end_counter3" + "HPinit_counter" "bosslvl3_end_counter2" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_legoland_crackheads_p2.txt b/BossHP/configs/MapBossHP/ze_legoland_crackheads_p2.txt new file mode 100644 index 0000000..abe93d1 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_legoland_crackheads_p2.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "ColorCounter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_lila_panic_escape_v3.txt b/BossHP/configs/MapBossHP/ze_lila_panic_escape_v3.txt new file mode 100644 index 0000000..34090df --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_lila_panic_escape_v3.txt @@ -0,0 +1,13 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Heli_HP" + "CustomText" "Helicopter" + } + "1" + { + "HP_counter" "boss_hp" + "CustomText" "Bear" + } +} diff --git a/BossHP/configs/MapBossHP/ze_lila_panic_escape_v3_3.txt b/BossHP/configs/MapBossHP/ze_lila_panic_escape_v3_3.txt new file mode 100644 index 0000000..34090df --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_lila_panic_escape_v3_3.txt @@ -0,0 +1,13 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Heli_HP" + "CustomText" "Helicopter" + } + "1" + { + "HP_counter" "boss_hp" + "CustomText" "Bear" + } +} diff --git a/BossHP/configs/MapBossHP/ze_lotr_isengard_v2_3_x5.txt b/BossHP/configs/MapBossHP/ze_lotr_isengard_v2_3_x5.txt new file mode 100644 index 0000000..1ad71b9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_lotr_isengard_v2_3_x5.txt @@ -0,0 +1,30 @@ +"math_counter" +{ + "0" + { + "HP_counter" "hp_controller_spider" + "HP_Mode" "2" + "HPbar_counter" "hp_counter_spider" + "HPinit_counter" "" + "CustomText" "Spider" + "HPbar_mode" "2" + } + "0" + { + "HP_counter" "hp_controller_yruk" + "HP_Mode" "2" + "HPbar_counter" "hp_counter_yruk" + "HPinit_counter" "" + "CustomText" "Uruk-hai" + "HPbar_mode" "2" + } + "0" + { + "HP_counter" "hp_controller_saruman" + "HP_Mode" "2" + "HPbar_counter" "hp_counter_saruman" + "HPinit_counter" "" + "CustomText" "Saruman" + "HPbar_mode" "2" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_minecraft_adventure_v1_3d.txt b/BossHP/configs/MapBossHP/ze_minecraft_adventure_v1_3d.txt new file mode 100644 index 0000000..a678d2f --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_minecraft_adventure_v1_3d.txt @@ -0,0 +1,30 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + "HPbar_counter" "boss_hp_iterations" + "HPinit_counter" "boss_hp_backup" + "CustomText" "Ghast" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "boss2_hp" + "HPbar_counter" "boss2_hp_iterations" + "HPinit_counter" "boss2_hp_backup" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "stronghold_final_boss_hp" + "CustomText" "HIM" + } +} diff --git a/BossHP/configs/MapBossHP/ze_notredame_p3.txt b/BossHP/configs/MapBossHP/ze_notredame_p3.txt new file mode 100644 index 0000000..5415dc9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_notredame_p3.txt @@ -0,0 +1,8 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + "CustomText" "Jack" + } +} diff --git a/BossHP/configs/MapBossHP/ze_omen_076.txt b/BossHP/configs/MapBossHP/ze_omen_076.txt new file mode 100644 index 0000000..e18dd17 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_omen_076.txt @@ -0,0 +1,24 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "l3_boss_end_hitbox" + } + "1" + { + "Type" "breakable" + "BreakableName" "l3_boss_hitbox" + } + "2" + { + "Type" "breakable" + "BreakableName" "l2_end_hitbox" + } + "3" + { + "Type" "breakable" + "BreakableName" "l1_plant_hitbox" + "CustomText" "Plant" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_omen_090.txt b/BossHP/configs/MapBossHP/ze_omen_090.txt new file mode 100644 index 0000000..35ca890 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_omen_090.txt @@ -0,0 +1,615 @@ +;fix warmup in Possession & ZombieDen warmup plugin +filter: +{ + "targetname" "Warmup_Relay" + "classname" "logic_relay" + "hammerid" "599620" +} +add: +{ + "origin" "-11192 -13632 384" + "targetname" "Warmup_Relay" + "StartDisabled" "0" + "spawnflags" "0" + "classname" "logic_relay" + "hammerid" "599620" + "OnTrigger" "TitleMenuBGMPlaySound4-1" + "OnTrigger" "Level_mathSetValue130-1" + "OnTrigger" "No_damage_triggerEnable0-1" + "OnTrigger" "warmup_edict_killerTrigger0-1" + "OnTrigger" "!selfFireUser10-1" + "OnUser1" "PKMNCommandsay >>WARMUP<<0-1" + "OnUser1" "!selfFireUser15-1" +} + +;-------------- +;神器Command重置 +;-------------- +;entadm2 0 "精灵球";entadm2 1 "精灵球";entadm2 3 "精灵球";entadm2 4 "精灵球" +;---------- +;中文化技能 +;---------- +;----------------------------- +;博士选择宝可梦的旁边增添选择提示 +;----------------------------- +add: +{ + "classname" "vgui_world_text_panel" + "origin" "-10244 -14976 401" + "enabled" "1" + "displaytext" "小火龙 杰尼龟 妙蛙种子" + "targetname" "Doctor_choice" + "hammerid" "0" + "font" "DefaultLarge" + "width" "1024" + "height" "1024" + "angles" "0 90 0" + "textpanelwidth" "1024" + "textcolor" "0 255 255" +} +;-10244.5 +;------------------ +;人类的宝可梦技能提示 +;------------------ +add: +{ + "classname" "vgui_world_text_panel" + "origin" "1551.5 1151 3956" + "enabled" "1" + "displaytext" " " + "targetname" "Human_choice" + "hammerid" "4723658" + "font" "DefaultLarge" + "width" "2048" + "height" "2048" + "angles" "0 90 0" + "textpanelwidth" "2048" + "textcolor" "0 255 255" +} +; +;可达鸭 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Psyduck_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    抓    摇尾巴    念力01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Psyduck_main_relay1" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    抓    摇尾巴    念力01" + } +} +; +;烈雀 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Spearow_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     啄           瞪眼01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Spearow_main_relay1" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     啄           瞪眼01" + } +} +; +;小火龙 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "charmander_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    火花    利爪     叫声0.011" + } +} +; +;墨海马 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Horsea_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    水枪    泡沫     瞪眼01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Horsea_main_relay1" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    水枪    泡沫     瞪眼01" + } +} +;妙蛙种子 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "bulbasaur_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     藤鞭   撞击    寄生种子0.011" + } +} +; +;独角虫 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Weedle_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     毒针   撞击     虫咬 01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Weedle_main_relay1" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     毒针   撞击     虫咬 01" + } +} +; +;杰尼龟 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "squirtle_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     水枪     撞击     摇尾巴 0.011" + } +} +; +;地鼠 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "diglett_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    掷泥     抓      叫声01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "diglett_main_relay1" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    掷泥     抓      叫声01" + } +} +;腕力 +; +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Machop_main_relay" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    空手劈     踢倒     聚气 01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Machop_main_relay1" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    空手劈     踢倒     聚气 01" + } +} +;----------- +;色彩修正修复 +;----------- +add: +{ + "classname" "color_correction" + "spawnflags" "0" + "filename" "materials/correction/null.raw" + "StartDisabled" "0" + "minfalloff" "0" + "maxfalloff" "0" + "maxweight" "0.0" + "origin" "0 0 0" +} +;------------------ +;僵尸的宝可梦技能提示 +;------------------ +add: +{ + "classname" "vgui_world_text_panel" + "origin" "-1551.5 -1151 3936" + "enabled" "1" + "displaytext" " " + "targetname" "Zombie_choice" + "hammerid" "4723659" + "font" "DefaultLarge" + "width" "2048" + "height" "2048" + "angles" "0 -90 0" + "textpanelwidth" "2048" + "textcolor" "0 255 255" +} +; +;可达鸭 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Psyduck_main_relay2" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    抓    摇尾巴    念力01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Psyduck_main_relay3" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    抓    摇尾巴    念力01" + } +} +; +;烈雀 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Spearow_main_relay2" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     啄           瞪眼01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Spearow_main_relay3" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     啄           瞪眼01" + } +} +;小火龙 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "charmander_main_relay1" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    火花    利爪     叫声0.011" + } +} +; +;墨海马 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Horsea_main_relay2" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    水枪    泡沫     瞪眼01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Horsea_main_relay3" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    水枪    泡沫     瞪眼01" + } +} +;妙蛙种子 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "bulbasaur_main_relay1" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     藤鞭   撞击    寄生种子0.011" + } +} +; +;独角虫 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Weedle_main_relay2" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     毒针   撞击     虫咬 01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Weedle_main_relay3" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     毒针   撞击     虫咬 01" + } +} +; +;杰尼龟 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "squirtle_main_relay1" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     水枪     撞击     摇尾巴 0.011" + } +} +; +;地鼠 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "diglett_main_relay2" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    掷泥     抓      叫声01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "diglett_main_relay3" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    掷泥     抓      叫声01" + } +} +; +;海星 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "staryu_main_relay1" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     水枪   撞击     变硬 01" + } +} +; +;小拳石 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "geodude_main_relay1" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     落石   撞击     变硬 01" + } +} +; +;腕力 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Machop_main_relay2" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    空手劈     踢倒     聚气 01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "Machop_main_relay3" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    空手劈     踢倒     聚气 01" + } +} +; +;主宝可梦文本提示修复 +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "bulbasaur_noises" + "hammerid" "560409" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     藤鞭   撞击    寄生种子01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "bulbasaur_noises1" + "hammerid" "559603" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     藤鞭   撞击    寄生种子01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "charmander_noises" + "hammerid" "560405" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText    火花    利爪     叫声01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "charmander_noises1" + "hammerid" "559587" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText    火花    利爪     叫声01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "squirtle_noises" + "hammerid" "560407" + } + insert: + { + "OnTrigger" "Human_choiceSetDisplayText     水枪     撞击     摇尾巴 01" + } +} +modify: +{ + match: + { + "classname" "logic_relay" + "targetname" "squirtle_noises1" + "hammerid" "559601" + } + insert: + { + "OnTrigger" "Zombie_choiceSetDisplayText     水枪     撞击     摇尾巴 01" + } +}termination." + { + "chi" "小刚:' 岩石系宝可梦有着真正的勇气 '" + "AutoTranslate" "我的口袋妖怪都是岩石坚硬的,有着真正的勇气。" + "normaltext" "1" + } + "Fuhaha! You're going to challenge me knowing that you'll lose?" + { + "chi" "小刚:' fu哈哈哈哈,挑战我你会输知道吗? '" + "AutoTranslate" "Fuhaha!你会挑战我知道你会输吗?" + "normaltext" "1" + } + "Fine, then! Show me your best!" + { + "chi" "小刚:' 看来你不打算放弃..好吧 把 \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_omochix.txt b/BossHP/configs/MapBossHP/ze_omochix.txt new file mode 100644 index 0000000..d4bd40a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_omochix.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "normal_boss_counter" + } + "0" + { + "HP_counter" "Level2_blue_counter" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_omochix_v2.txt b/BossHP/configs/MapBossHP/ze_omochix_v2.txt new file mode 100644 index 0000000..3d39af7 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_omochix_v2.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_counter" "normal_boss_counter" + } + "0" + { + "HP_counter" "Level2_blue_counter" + } + "0" + { + "HP_counter" "level2_crystal_counter" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f.txt b/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f.txt new file mode 100644 index 0000000..8fc1acd --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f.txt @@ -0,0 +1,13 @@ +"math_counter" +{ + "0" + { + "HP_counter" "ts_hp" + "CustomText" "Tenshi" + } + "1" + { + "HP_counter" "lv3_ts_hp" + "CustomText" "Tenshi" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f3.txt b/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f3.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f3.txt @@ -0,0 +1 @@ + diff --git a/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f4.txt b/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f4.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_otakuroom_v5_6f4.txt @@ -0,0 +1 @@ + diff --git a/BossHP/configs/MapBossHP/ze_paper_escaper_p7.txt b/BossHP/configs/MapBossHP/ze_paper_escaper_p7.txt new file mode 100644 index 0000000..6f4b949 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_paper_escaper_p7.txt @@ -0,0 +1,19 @@ +"math_counter" +{ + "0" + { + "HP_counter" "BossExtHitP" + } + "1" + { + "HP_counter" "BossMassHitP" + } + "2" + { + "HP_counter" "BossRageHitP" + } + "3" + { + "HP_counter" "PanicHPCounter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_pidaras_v1_4fix3_e10.txt b/BossHP/configs/MapBossHP/ze_pidaras_v1_4fix3_e10.txt new file mode 100644 index 0000000..31bcdcc --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_pidaras_v1_4fix3_e10.txt @@ -0,0 +1,32 @@ +"math_counter" +{ + "0" + { + "HP_counter" "npc_bahamut_counter" + "CustomText" "Bahamut" + } + "1" + { + "HP_counter" "trigger_pyramid_counter" + "CustomText" "Pyramid Head" + } + "2" + { + "HP_Group" + { + "0" "space_counter1" + "1" "space_counter2" + } + "CustomText" "Space" + } + "4" + { + "HP_counter" "boss_hp" + "CustomText" "Ur" + } + "5" + { + "HP_counter" "roshi_counter" + "CustomText" "Roshi" + } +} diff --git a/BossHP/configs/MapBossHP/ze_pirates_port_royal_v5_6.txt b/BossHP/configs/MapBossHP/ze_pirates_port_royal_v5_6.txt new file mode 100644 index 0000000..b792bc0 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_pirates_port_royal_v5_6.txt @@ -0,0 +1,63 @@ +"math_counter" +{ + "0" + { + "HP_counter" "delo_counter" + "HPbar_counter" "boss_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Delo" + } + "1" + { + "HP_counter" "pirate_counter" + "HPbar_counter" "pirate_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Pirate" + } + "2" + { + "HP_counter" "barbossa_counter" + "HPbar_counter" "barbossa_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "5" + "HPbar_default" "5" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Barbossa" + } + "3" + { + "HP_counter" "kraken_counter" + "HPbar_counter" "kraken_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Kraken" + } + "4" + { + "HP_counter" "final_barbossa_counter2" + "HPbar_counter" "" + "HPbar_min" "" + "HPbar_max" "" + "HPbar_default" "" + "HPbar_mode" "" // OnHitMin=1 OnHitMax=2 + "CustomText" "Barbossa" + } + "5" + { + "HP_counter" "final_barbossa_counter" + "HPbar_counter" "" + "HPbar_min" "" + "HPbar_max" "" + "HPbar_default" "" + "HPbar_mode" "" // OnHitMin=1 OnHitMax=2 + "CustomText" "Barbossa" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_pizzatime_v9.txt b/BossHP/configs/MapBossHP/ze_pizzatime_v9.txt new file mode 100644 index 0000000..559cdf6 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_pizzatime_v9.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "Type" "breakable" + "BreakableName" "boss_ep_hp" + "CustomText" "Evil Pizza" + } + "1" + { + "Type" "breakable" + "BreakableName" "boss_jar_hp" + "CustomText" "JarJar Binks" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_pkmn_adventure_v9_3.txt b/BossHP/configs/MapBossHP/ze_pkmn_adventure_v9_3.txt new file mode 100644 index 0000000..f077abd --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_pkmn_adventure_v9_3.txt @@ -0,0 +1,127 @@ +"math_counter" +{ + // Stage 1 - Bosses + "21" + { + "HP_Group" + { + "0" "KirliaHP_1" + "1" "KirliaHP_2" + "2" "KirliaHP_3" + "3" "KirliaHP_4" + } + "CustomText" "Kirlia" + } + // Stage 2 - Bosses + "31" + { + "CustomText" "Noctowl" + "HP_counter" "NoctowlHP" + } + "32" + { + "CustomText" "Pidgeot" + "HP_counter" "PidgeotHP" + } + "33" + { + "CustomText" "Miltank" + "HP_counter" "MiltankHP" + } + // Stage 3 - Bosses + "2" + { + "CustomText" "Kirlia" + "Type" "breakable" + "BreakableName" "kirlia" + } + "3" + { + "CustomText" "Crobat" + "Type" "breakable" + "BreakableName" "Crobat" + } + "4" + { + "CustomText" "Groudon" + "HP_counter" "groudonhp" + } + "5" + { + "CustomText" "Kirlia" + "HP_counter" "finalkirlia_hp" + } + "45" + { + "CustomText" "Primal Groudon" + "HP_counter" "PrimalGroudonHP" + } + // Stage 4 - Bosses + "6" + { + "CustomText" "Skuntank" + "HP_counter" "skuntankhealth" + } + "7" + { + "CustomText" "Purugly" + "HP_counter" "puruglyhealth" + } + "8" + { + "CustomText" "Honchkrow" + "HP_counter" "honchkrowhp" + } + "9" + { + "CustomText" "Crobat" + "HP_counter" "crobatcyrushp" + } + "10" + { + "CustomText" "Weavile" + "HP_counter" "weavilehp" + } + "11" + { + "CustomText" "Giratina" + "HP_counter" "giratinahp" + } + "12" + { + "CustomText" "Crobat" + "HP_counter" "crobatfinal2_health" + } + "13" + { + "CustomText" "Weavile" + "HP_counter" "weavilefinalhp" + } + // Stage 5 - Bosses + "51" + { + "HP_Group" + { + "0" "ArceusHP_1" + "1" "ArceusHP_2" + "2" "ArceusHP_3" + "3" "ArceusHP_4" + } + "CustomText" "Arceus" + } + "52" + { + "CustomText" "Arceus Judgement" + "HP_counter" "Arceus_Judgement_HP" + } + "53" + { + "CustomText" "Darkrai" + "HP_counter" "DarkraiHP" + } + "54" + { + "CustomText" "Gallade" + "HP_counter" "Level5_GalladeHP" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_predator_ultimate_p7.txt b/BossHP/configs/MapBossHP/ze_predator_ultimate_p7.txt new file mode 100644 index 0000000..30a2f30 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_predator_ultimate_p7.txt @@ -0,0 +1,48 @@ +"math_counter" +{ + "config" + { + "BossHpKeepTime" "0" + "BossDieKeepTime" "0" + } + "0" + { + "HP_counter" "cboss_predatorhealth_counter" + "CustomText" "Predator" + } + "1" + { + "HP_counter" "bosshealth_endboss" + "CustomText" "Predator" + } + "2" + { + "HP_counter" "mob_grudge_math" + "CustomText" "Predator" + } + "3" + { + "HP_counter" "aztecboss_math_health" + "CustomText" "Predator" + } + "4" + { + "HP_counter" "aztecboss_math_health_2" + "CustomText" "Predator" + } + "5" + { + "HP_counter" "fboss_math_1" + "CustomText" "Predator(狂暴)" + } + "5" + { + "HP_counter" "fboss_math_2" + "CustomText" "Predator" + } + "6" + { + "HP_counter" "fboss_ee_math" + "CustomText" "Alien" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_project_alcaria_v3_2.txt b/BossHP/configs/MapBossHP/ze_project_alcaria_v3_2.txt new file mode 100644 index 0000000..3cc2597 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_project_alcaria_v3_2.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "0" //math_counter + { + "HP_counter" "naraka_counter2" + "HP_Mode" "1" + "HPbar_counter" "naraka_hp_iterations2" + "HPinit_counter" "naraka_hp_backup2" + "CustomText" "" + "HPbar_mode" "1" + } + "0" //math_counter + { + "HP_counter" "nrk2_naraka_counter" + "HP_Mode" "1" + "HPbar_counter" "nrk2_naraka_hp_iterations" + "HPinit_counter" "nrk2_naraka_hp_backup" + "CustomText" "" + "HPbar_mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_project_alcaria_x.txt b/BossHP/configs/MapBossHP/ze_project_alcaria_x.txt new file mode 100644 index 0000000..3cc2597 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_project_alcaria_x.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "0" //math_counter + { + "HP_counter" "naraka_counter2" + "HP_Mode" "1" + "HPbar_counter" "naraka_hp_iterations2" + "HPinit_counter" "naraka_hp_backup2" + "CustomText" "" + "HPbar_mode" "1" + } + "0" //math_counter + { + "HP_counter" "nrk2_naraka_counter" + "HP_Mode" "1" + "HPbar_counter" "nrk2_naraka_hp_iterations" + "HPinit_counter" "nrk2_naraka_hp_backup" + "CustomText" "" + "HPbar_mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_random_p2.txt b/BossHP/configs/MapBossHP/ze_random_p2.txt new file mode 100644 index 0000000..ee73225 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_random_p2.txt @@ -0,0 +1,9 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "FBK" + "CustomText" "" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_random_p2_fix.txt b/BossHP/configs/MapBossHP/ze_random_p2_fix.txt new file mode 100644 index 0000000..ee73225 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_random_p2_fix.txt @@ -0,0 +1,9 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "FBK" + "CustomText" "" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_reloaded_v1_e10.txt b/BossHP/configs/MapBossHP/ze_reloaded_v1_e10.txt new file mode 100644 index 0000000..42ac441 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_reloaded_v1_e10.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } + "1" + { + "HP_counter" "boss_hp_Immense" + } +} diff --git a/BossHP/configs/MapBossHP/ze_reloaded_v1_ez2.txt b/BossHP/configs/MapBossHP/ze_reloaded_v1_ez2.txt new file mode 100644 index 0000000..e9949af --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_reloaded_v1_ez2.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } + "1" + { + "HP_counter" "boss_hp_Immense" + "HP_Mode" "1" + "HPbar_counter" "hp_mat" + "HPinit_counter" "hp_backup" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_rizomata_a45.txt b/BossHP/configs/MapBossHP/ze_rizomata_a45.txt new file mode 100644 index 0000000..ed3eb93 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_rizomata_a45.txt @@ -0,0 +1,29 @@ +"math_counter" +{ + "config" + { + "MultBoss" "0" + "IsDynamicHP" "1" + "RoundEndShowTopDamage" "1" + "ShowTopDamageDuringBOSS" "0" + "ForceEnable" "1" + } + "0" + { + "HP_counter" "2_boss_hpcounter" + "HPbar_counter" "2_boss_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "3_boss_hpcounter" + "HPbar_counter" "3_boss_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } +} diff --git a/BossHP/configs/MapBossHP/ze_rizomata_b44.txt b/BossHP/configs/MapBossHP/ze_rizomata_b44.txt new file mode 100644 index 0000000..8262ed8 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_rizomata_b44.txt @@ -0,0 +1,34 @@ +"math_counter" +{ + "config" + { + "MultBoss" "0" + "IsDynamicHP" "1" + "RoundEndShowTopDamage" "1" + "ShowTopDamageDuringBOSS" "0" + "ForceEnable" "1" + } + "0" + { + "HP_counter" "4_boss_hpcounter" + "HPbar_counter" "4_boss_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "5_boss_hpcounter" + "HPbar_counter" "5_boss_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "5_boss_enragehpcounter" + "CustomText" "BOSS (ENRAGE)" + } +} diff --git a/BossHP/configs/MapBossHP/ze_rizomata_b45.txt b/BossHP/configs/MapBossHP/ze_rizomata_b45.txt new file mode 100644 index 0000000..8262ed8 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_rizomata_b45.txt @@ -0,0 +1,34 @@ +"math_counter" +{ + "config" + { + "MultBoss" "0" + "IsDynamicHP" "1" + "RoundEndShowTopDamage" "1" + "ShowTopDamageDuringBOSS" "0" + "ForceEnable" "1" + } + "0" + { + "HP_counter" "4_boss_hpcounter" + "HPbar_counter" "4_boss_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "5_boss_hpcounter" + "HPbar_counter" "5_boss_hpmasscounter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "5_boss_enragehpcounter" + "CustomText" "BOSS (ENRAGE)" + } +} diff --git a/BossHP/configs/MapBossHP/ze_roof_adventure_v8f.txt b/BossHP/configs/MapBossHP/ze_roof_adventure_v8f.txt new file mode 100644 index 0000000..7c6e85e --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_roof_adventure_v8f.txt @@ -0,0 +1,11 @@ +"math_counter" +{ + "0" + { + "HP_counter" "counter_bosshp" + "HP_Mode" "1" + "HPbar_counter" "boss_hpcounter" + "HPbar_mode" "1" + "CustomText" "" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_sandstorm_go_v1_3.txt b/BossHP/configs/MapBossHP/ze_sandstorm_go_v1_3.txt new file mode 100644 index 0000000..94a8417 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_sandstorm_go_v1_3.txt @@ -0,0 +1,48 @@ +"math_counter" +{ + "0" + { + "HP_counter" "mutant_counter1" + "HPbar_counter" "mutant_hp_iterations1" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "1" + { + "HP_counter" "grg_counter" + "HPbar_counter" "grg_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "2" + { + "HP_counter" "mutant_counter" + "HPbar_counter" "mutant_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "3" + { + "HP_counter" "garg_counter" + "HPbar_counter" "garg_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "4" + { + "HP_counter" "sss5_l_laser_counter" + "HPbar_counter" "sss5_l_laser_hp_interations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_sandstorm_go_v1_4.txt b/BossHP/configs/MapBossHP/ze_sandstorm_go_v1_4.txt new file mode 100644 index 0000000..94a8417 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_sandstorm_go_v1_4.txt @@ -0,0 +1,48 @@ +"math_counter" +{ + "0" + { + "HP_counter" "mutant_counter1" + "HPbar_counter" "mutant_hp_iterations1" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "1" + { + "HP_counter" "grg_counter" + "HPbar_counter" "grg_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "2" + { + "HP_counter" "mutant_counter" + "HPbar_counter" "mutant_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "3" + { + "HP_counter" "garg_counter" + "HPbar_counter" "garg_hp_iterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } + "4" + { + "HP_counter" "sss5_l_laser_counter" + "HPbar_counter" "sss5_l_laser_hp_interations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_santassination_v3.txt b/BossHP/configs/MapBossHP/ze_santassination_v3.txt new file mode 100644 index 0000000..3bb4fc4 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_santassination_v3.txt @@ -0,0 +1,21 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "bosss_hp" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "bosss_hp1" + "CustomText" "" + } + "1" + { + "Type" "breakable" + "BreakableName" "bosss_hp2" + "CustomText" "" + } +} diff --git a/BossHP/configs/MapBossHP/ze_serpentis_temple_csgo.txt b/BossHP/configs/MapBossHP/ze_serpentis_temple_csgo.txt new file mode 100644 index 0000000..a858716 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_serpentis_temple_csgo.txt @@ -0,0 +1,33 @@ +"math_counter" +{ + "0" + { + "HP_counter" "HPCounter" + "HPbar_counter" "HPCounterIterator" + "HPinit_counter" "HPCounterBackUp" + "HPbar_min" "" + "HPbar_max" "30" + "HPbar_default" "30" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "Room4_Boss_Hp" + "HPbar_counter" "Room4_Boss_HpCounter" + "HPinit_counter" "Room4_Boss_HpBackup" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "Room8_Boss_Hp" + "HPbar_counter" "Room8_Boss_HpCounter" + "HPinit_counter" "Room8_Boss_HpBackup" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_serpentis_temple_csgo1.txt b/BossHP/configs/MapBossHP/ze_serpentis_temple_csgo1.txt new file mode 100644 index 0000000..a858716 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_serpentis_temple_csgo1.txt @@ -0,0 +1,33 @@ +"math_counter" +{ + "0" + { + "HP_counter" "HPCounter" + "HPbar_counter" "HPCounterIterator" + "HPinit_counter" "HPCounterBackUp" + "HPbar_min" "" + "HPbar_max" "30" + "HPbar_default" "30" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "Room4_Boss_Hp" + "HPbar_counter" "Room4_Boss_HpCounter" + "HPinit_counter" "Room4_Boss_HpBackup" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "Room8_Boss_Hp" + "HPbar_counter" "Room8_Boss_HpCounter" + "HPinit_counter" "Room8_Boss_HpBackup" + "HPbar_min" "" + "HPbar_max" "40" + "HPbar_default" "40" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_shaurma_v3_b07_t5.txt b/BossHP/configs/MapBossHP/ze_shaurma_v3_b07_t5.txt new file mode 100644 index 0000000..475e3f7 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_shaurma_v3_b07_t5.txt @@ -0,0 +1,30 @@ +"math_counter" +{ + "0" + { + "HP_counter" "level1_boss_health" + "HP_Mode" "1" + "HPbar_counter" "level1_boss_health_counter" + "HPinit_counter" "level1_boss_health_backup" + "CustomText" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "level2_boss_health" + "HP_Mode" "1" + "HPbar_counter" "level2_boss_health_counter" + "HPinit_counter" "level2_boss_health_backup" + "CustomText" "" + "HPbar_mode" "2" + } + "2" + { + "HP_counter" "level3_boss_health" + "HP_Mode" "1" + "HPbar_counter" "level3_boss_health_counter" + "HPinit_counter" "level3_boss_health_backup" + "CustomText" "" + "HPbar_mode" "2" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_shroomforest2_p6.txt b/BossHP/configs/MapBossHP/ze_shroomforest2_p6.txt new file mode 100644 index 0000000..6ba7a5b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_shroomforest2_p6.txt @@ -0,0 +1,23 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_HealthCount" + "HPinit_counter" "Boss_HealthInit" + "HPbar_min" "0" + "HPbar_max" "5" + "HPbar_default" "0" + "HPbar_mode" "2" // OnHitMin=1 OnHitMax=2 + } + "1" + { + "HP_counter" "Satan_Health" + "HPbar_counter" "Satan_HealthCount" + "HPinit_counter" "Satan_HealthInit" + "HPbar_min" "0" + "HPbar_max" "5" + "HPbar_default" "0" + "HPbar_mode" "2" // OnHitMin=1 OnHitMax=2 + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_shroomforest_p6.txt b/BossHP/configs/MapBossHP/ze_shroomforest_p6.txt new file mode 100644 index 0000000..5badb80 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_shroomforest_p6.txt @@ -0,0 +1,8 @@ +"math_counter" +{ + "0" + { + "HP_counter" "dynamic_HP" + "HP_Mode" "2" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_sit_caelum_paradisus_b7_hdr.txt b/BossHP/configs/MapBossHP/ze_sit_caelum_paradisus_b7_hdr.txt new file mode 100644 index 0000000..e69de29 diff --git a/BossHP/configs/MapBossHP/ze_sky_athletic_adv_v9_10.txt b/BossHP/configs/MapBossHP/ze_sky_athletic_adv_v9_10.txt new file mode 100644 index 0000000..5514a5c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_sky_athletic_adv_v9_10.txt @@ -0,0 +1,53 @@ +"math_counter" +{ + "0" + { + "HP_counter" "betray_him_hp_count_now" + "HPbar_counter" "betray_him_hp_count" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "crasher hp" + } + "2" + { + "HP_counter" "cute cloud hp" + } + "3" + { + "HP_counter" "ex1_girl_cloud_hp1" + } + "4" + { + "HP_counter" "ex1_girl_cloud_hp2" + } + "5" + { + "HP_counter" "ex2_boss_real_hp" + } + "6" + { + "HP_counter" "final_boss_ittanstop_counter" + } + "7" + { + "HP_counter" "final_boss_truehp_counter" + } + "8" + { + "HP_counter" "final_crasher_finale_final_hp_counter" + } + "9" + { + "HP_counter" "final_crasher_hp_counter_total" + "HPbar_counter" "final_crasher_hp_counter_10" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_sky_athletic_adv_v9_12.txt b/BossHP/configs/MapBossHP/ze_sky_athletic_adv_v9_12.txt new file mode 100644 index 0000000..5514a5c --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_sky_athletic_adv_v9_12.txt @@ -0,0 +1,53 @@ +"math_counter" +{ + "0" + { + "HP_counter" "betray_him_hp_count_now" + "HPbar_counter" "betray_him_hp_count" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "crasher hp" + } + "2" + { + "HP_counter" "cute cloud hp" + } + "3" + { + "HP_counter" "ex1_girl_cloud_hp1" + } + "4" + { + "HP_counter" "ex1_girl_cloud_hp2" + } + "5" + { + "HP_counter" "ex2_boss_real_hp" + } + "6" + { + "HP_counter" "final_boss_ittanstop_counter" + } + "7" + { + "HP_counter" "final_boss_truehp_counter" + } + "8" + { + "HP_counter" "final_crasher_finale_final_hp_counter" + } + "9" + { + "HP_counter" "final_crasher_hp_counter_total" + "HPbar_counter" "final_crasher_hp_counter_10" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_stalker_ultimate_v1.txt b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v1.txt new file mode 100644 index 0000000..1137b0f --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v1.txt @@ -0,0 +1,28 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } + "0" + { + "HP_counter" "boss3_health" + } + "0" + { + "HP_counter" "mob_grudge_math1" + } + "0" + { + "HP_counter" "mob_grudge_math" + "CustomText" "BOSS (幻境)" + } + "0" + { + "HP_counter" "mob_grudge_math_1" + } + "0" + { + "HP_counter" "boss4_hp" + } +} diff --git a/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g3.txt b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g3.txt new file mode 100644 index 0000000..49b4ae9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g3.txt @@ -0,0 +1,28 @@ +"math_counter" +{ + "0" + { + "HP_counter" "mob_grudge_math1" + "CustomText" "Controller" + } + "1" + { + "HP_counter" "mob_grudge_math_1" + "CustomText" "Energy Ball" + } + "2" + { + "HP_counter" "boss3_health" + "CustomText" "Energy Ball" + } + "3" + { + "HP_counter" "boss_hp" + "CustomText" "Bloodsucker" + } + "4" + { + "HP_counter" "mob_grudge_math" + "CustomText" "Controller" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g5.txt b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g5.txt new file mode 100644 index 0000000..bbfc4d6 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g5.txt @@ -0,0 +1,42 @@ +"math_counter" +{ + "0" + { + "HP_counter" "mob_grudge_math1" + "CustomText" "Controller" + } + "1" + { + "HP_counter" "mob_grudge_math_1" + "CustomText" "Energy Ball" + } + "2" + { + "HP_counter" "boss3_health" + "CustomText" "Energy Ball" + } + "3" + { + "HP_counter" "boss_hp" + "CustomText" "Bloodsucker" + } + "4" + { + "HP_counter" "mob_grudge_math" + "CustomText" "Controller" + } + "4" + { + "HP_counter" "sphere_counter" + } + "4" + { + "HP_counter" "boss4_hp" + "CustomText" "Energy Ball" + } + "4" + { + "HP_counter" "boss5_hp" + "CustomText" "Energy Ball" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g6.txt b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g6.txt new file mode 100644 index 0000000..bbfc4d6 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v2_3_g6.txt @@ -0,0 +1,42 @@ +"math_counter" +{ + "0" + { + "HP_counter" "mob_grudge_math1" + "CustomText" "Controller" + } + "1" + { + "HP_counter" "mob_grudge_math_1" + "CustomText" "Energy Ball" + } + "2" + { + "HP_counter" "boss3_health" + "CustomText" "Energy Ball" + } + "3" + { + "HP_counter" "boss_hp" + "CustomText" "Bloodsucker" + } + "4" + { + "HP_counter" "mob_grudge_math" + "CustomText" "Controller" + } + "4" + { + "HP_counter" "sphere_counter" + } + "4" + { + "HP_counter" "boss4_hp" + "CustomText" "Energy Ball" + } + "4" + { + "HP_counter" "boss5_hp" + "CustomText" "Energy Ball" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_stalker_ultimate_v4_4.txt b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v4_4.txt new file mode 100644 index 0000000..1dd560b --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_stalker_ultimate_v4_4.txt @@ -0,0 +1,23 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } + "0" + { + "HP_counter" "boss3_health" + } + "0" + { + "HP_counter" "mob_grudge_math1" + } + "0" + { + "HP_counter" "mob_grudge_math" + } + "0" + { + "HP_counter" "mob_grudge_math_1" + } +} diff --git a/BossHP/configs/MapBossHP/ze_starwars_v1_5.txt b/BossHP/configs/MapBossHP/ze_starwars_v1_5.txt new file mode 100644 index 0000000..a844925 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_starwars_v1_5.txt @@ -0,0 +1,63 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_heal_tank" + } + "1" + { + "HP_counter" "Math_conter_station" + } + "2" + { + "HP_counter" "darth_vader_1" + "HPbar_counter" "darth_vader_3" + "HPinit_counter" "darth_vader_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "darth_sid_1" + "HPbar_counter" "darth_sid_3" + "HPinit_counter" "darth_sid_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "4" + { + "HP_counter" "darth_mol_1" + "HPbar_counter" "darth_mol_3" + "HPinit_counter" "darth_mol_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "5" + { + "HP_counter" "darth_sphere_1" + "HPbar_counter" "darth_sphere_3" + "HPinit_counter" "darth_sphere_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "6" + { + "HP_counter" "Boss_heal_dart_maul" + } + "7" + { + "HP_counter" "Lvl_hard_heal" + } + "8" + { + "HP_counter" "Boss_heal_dart_vader" + } +} diff --git a/BossHP/configs/MapBossHP/ze_starwars_v2fix_csgo2.txt b/BossHP/configs/MapBossHP/ze_starwars_v2fix_csgo2.txt new file mode 100644 index 0000000..a844925 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_starwars_v2fix_csgo2.txt @@ -0,0 +1,63 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_heal_tank" + } + "1" + { + "HP_counter" "Math_conter_station" + } + "2" + { + "HP_counter" "darth_vader_1" + "HPbar_counter" "darth_vader_3" + "HPinit_counter" "darth_vader_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "darth_sid_1" + "HPbar_counter" "darth_sid_3" + "HPinit_counter" "darth_sid_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "4" + { + "HP_counter" "darth_mol_1" + "HPbar_counter" "darth_mol_3" + "HPinit_counter" "darth_mol_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "5" + { + "HP_counter" "darth_sphere_1" + "HPbar_counter" "darth_sphere_3" + "HPinit_counter" "darth_sphere_2" + "HPbar_min" "" + "HPbar_max" "8" + "HPbar_default" "8" + "HPbar_mode" "1" + } + "6" + { + "HP_counter" "Boss_heal_dart_maul" + } + "7" + { + "HP_counter" "Lvl_hard_heal" + } + "8" + { + "HP_counter" "Boss_heal_dart_vader" + } +} diff --git a/BossHP/configs/MapBossHP/ze_sunkentemple_v3_1.txt b/BossHP/configs/MapBossHP/ze_sunkentemple_v3_1.txt new file mode 100644 index 0000000..408cef4 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_sunkentemple_v3_1.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Counter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_sunlight_v2_0_p8_ps1.txt b/BossHP/configs/MapBossHP/ze_sunlight_v2_0_p8_ps1.txt new file mode 100644 index 0000000..912a114 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_sunlight_v2_0_p8_ps1.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "counter_boss_health" + } + "1" + { + "HP_counter" "counter_dragonhealth" + "CustomText" "Dragon" + } +} diff --git a/BossHP/configs/MapBossHP/ze_surf_bona_a1_3.txt b/BossHP/configs/MapBossHP/ze_surf_bona_a1_3.txt new file mode 100644 index 0000000..eaea9c9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_surf_bona_a1_3.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } +} diff --git a/BossHP/configs/MapBossHP/ze_surf_froyo_v2_1.txt b/BossHP/configs/MapBossHP/ze_surf_froyo_v2_1.txt new file mode 100644 index 0000000..b0fd980 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_surf_froyo_v2_1.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_Group" + { + "0" "boss_star_hitcounter1" + "1" "boss_star_hitcounter2" + "2" "boss_star_hitcounter3" + "3" "boss_star_hitcounter4" + "4" "boss_star_hitcounter5" + } + "CustomText" "Star" + } +} diff --git a/BossHP/configs/MapBossHP/ze_surf_froyo_v2_2.txt b/BossHP/configs/MapBossHP/ze_surf_froyo_v2_2.txt new file mode 100644 index 0000000..b0fd980 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_surf_froyo_v2_2.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_Group" + { + "0" "boss_star_hitcounter1" + "1" "boss_star_hitcounter2" + "2" "boss_star_hitcounter3" + "3" "boss_star_hitcounter4" + "4" "boss_star_hitcounter5" + } + "CustomText" "Star" + } +} diff --git a/BossHP/configs/MapBossHP/ze_surf_gypt_v1_3_1f.txt b/BossHP/configs/MapBossHP/ze_surf_gypt_v1_3_1f.txt new file mode 100644 index 0000000..b0fd980 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_surf_gypt_v1_3_1f.txt @@ -0,0 +1,15 @@ +"math_counter" +{ + "0" + { + "HP_Group" + { + "0" "boss_star_hitcounter1" + "1" "boss_star_hitcounter2" + "2" "boss_star_hitcounter3" + "3" "boss_star_hitcounter4" + "4" "boss_star_hitcounter5" + } + "CustomText" "Star" + } +} diff --git a/BossHP/configs/MapBossHP/ze_surf_sahok_p3.txt b/BossHP/configs/MapBossHP/ze_surf_sahok_p3.txt new file mode 100644 index 0000000..3d9f666 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_surf_sahok_p3.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + } +} diff --git a/BossHP/configs/MapBossHP/ze_surf_virtus_b1_2.txt b/BossHP/configs/MapBossHP/ze_surf_virtus_b1_2.txt new file mode 100644 index 0000000..eaea9c9 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_surf_virtus_b1_2.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + } +} diff --git a/BossHP/configs/MapBossHP/ze_temple_raider_b4_ps6.txt b/BossHP/configs/MapBossHP/ze_temple_raider_b4_ps6.txt new file mode 100644 index 0000000..2ed79bf --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_temple_raider_b4_ps6.txt @@ -0,0 +1,16 @@ +"math_counter" +{ + "0" + { + "HP_counter" "Boss_Health" + "HPbar_counter" "Boss_Health_Overlay_Counter" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "" + "HPbar_mode" "2" + } + "1" + { + "HP_counter" "End_Boss_Health" + } +} diff --git a/BossHP/configs/MapBossHP/ze_tesv_skyrim_v5_6.txt b/BossHP/configs/MapBossHP/ze_tesv_skyrim_v5_6.txt new file mode 100644 index 0000000..fb5dd09 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_tesv_skyrim_v5_6.txt @@ -0,0 +1,53 @@ +"math_counter" +{ + "config" + { + "ForceEnable" "0" + } + "0" + { + "HP_counter" "counter_1" + "HPbar_counter" "BossHpIterations4" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Alduin" + } + "1" + { + "HP_counter" "counter_2" + "HPbar_counter" "BossHpIterations3" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Alduin" + } + "2" + { + "HP_counter" "dw_hp" + "HPbar_counter" "BossHpIterations2" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Dwarven Centurion" + } + "3" + { + "HP_counter" "counter_3" + "HPbar_counter" "BossHpIterations" + "HPbar_min" "0" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" // OnHitMin=1 OnHitMax=2 + "CustomText" "Alduin" + } + "4" + { + "BreakableName" "smalldwboss_physbox2" + "Type" "breakable" + "CustomText" "Small DC" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_timesplitters_p.txt b/BossHP/configs/MapBossHP/ze_timesplitters_p.txt new file mode 100644 index 0000000..d65966a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_timesplitters_p.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "BossHPCounter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_timesplitters_p2.txt b/BossHP/configs/MapBossHP/ze_timesplitters_p2.txt new file mode 100644 index 0000000..d65966a --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_timesplitters_p2.txt @@ -0,0 +1,7 @@ +"math_counter" +{ + "0" + { + "HP_counter" "BossHPCounter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_tloz_stone_tower_temple_v1_4.txt b/BossHP/configs/MapBossHP/ze_tloz_stone_tower_temple_v1_4.txt new file mode 100644 index 0000000..4b09a8e --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_tloz_stone_tower_temple_v1_4.txt @@ -0,0 +1,17 @@ +"math_counter" +{ + "0" + { + "HP_counter" "b_handhpcounter" + "HPinit_counter" "b_handhpbackup" + "CustomText" "Hand" + } + "1" + { + "HP_counter" "b_hpcounter" + } + "2" + { + "HP_counter" "garo_hpcounter" + } +} diff --git a/BossHP/configs/MapBossHP/ze_trials_v1_4.txt b/BossHP/configs/MapBossHP/ze_trials_v1_4.txt new file mode 100644 index 0000000..bc5bdf4 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_trials_v1_4.txt @@ -0,0 +1,39 @@ +"math_counter" +{ + "1" + { + "Type" "breakable" + "BreakableName" "Level_1_Boss_Breakable_3" + "CustomText" "BOSS (1/3)" + } + "1" + { + "Type" "breakable" + "BreakableName" "Level_1_Boss_Breakable_2" + "CustomText" "BOSS (2/3)" + } + "1" + { + "Type" "breakable" + "BreakableName" "Level_1_Boss_Breakable" + "CustomText" "BOSS (3/3)" + } + "1" + { + "Type" "breakable" + "BreakableName" "Level_3_Boss_Breakable_3" + "CustomText" "BOSS (1/3)" + } + "1" + { + "Type" "breakable" + "BreakableName" "Level_3_Boss_Breakable_2" + "CustomText" "BOSS (2/3)" + } + "1" + { + "Type" "breakable" + "BreakableName" "Level_3_Boss_Breakable" + "CustomText" "BOSS (3/3)" + } +} \ No newline at end of file diff --git a/BossHP/configs/MapBossHP/ze_tyranny_v5_go3.txt b/BossHP/configs/MapBossHP/ze_tyranny_v5_go3.txt new file mode 100644 index 0000000..8802bc6 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_tyranny_v5_go3.txt @@ -0,0 +1,112 @@ +"math_counter" +{ + "0" + { + "HP_counter" "bosslvl1_counter1" + "HPbar_counter" "bosslvl1_counter3" + "HPinit_counter" "bosslvl1_counter2" + "CustomText" "Spider" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "1" + { + "HP_counter" "bosslvl2_counter1" + "HPbar_counter" "bosslvl2_counter3" + "HPinit_counter" "bosslvl2_counter2" + "CustomText" "Troll" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "2" + { + "HP_counter" "bosslvl2_end_counter1" + "HPbar_counter" "bosslvl2_end_counter3" + "HPinit_counter" "bosslvl2_end_counter2" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "3" + { + "HP_counter" "bosslvl3_counter1" + "HPbar_counter" "bosslvl3_counter3" + "HPinit_counter" "bosslvl3_counter2" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "4" + { + "HP_counter" "bosslvl3_end_counter1" + "HPbar_counter" "bosslvl3_end_counter3" + "HPinit_counter" "bosslvl3_end_counter2" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "5" + { + "HP_counter" "bosslvl4_counter1" + "HPbar_counter" "bosslvl4_counter3" + "HPinit_counter" "bosslvl4_counter2" + "CustomText" "Titan" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "6" + { + "HP_counter" "bosslvl5_counter1" + "HPbar_counter" "bosslvl5_counter3" + "HPinit_counter" "bosslvl5_counter2" + "CustomText" "Barlog" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "7" + { + "HP_counter" "bosslvl5_end_counter1" + "HPbar_counter" "bosslvl5_end_counter3" + "HPinit_counter" "bosslvl5_end_counter2" + "CustomText" "Dragon" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "8" + { + "HP_counter" "bosslvl5_garg_counter1" + "HPbar_counter" "bosslvl5_garg_counter3" + "HPinit_counter" "bosslvl5_garg_counter2" + "CustomText" "Titan" + "HPbar_min" "" + "HPbar_max" "10" + "HPbar_default" "10" + "HPbar_mode" "1" + } + "9" + { + "HP_counter" "bosslvl5_laser_counter" + "CustomText" "Barlog" + } + "10" + { + "HP_counter" "bosslvl6_counter" + "CustomText" "Titan" + } +} diff --git a/BossHP/configs/MapBossHP/ze_zk_fapescape_p3.txt b/BossHP/configs/MapBossHP/ze_zk_fapescape_p3.txt new file mode 100644 index 0000000..120a299 --- /dev/null +++ b/BossHP/configs/MapBossHP/ze_zk_fapescape_p3.txt @@ -0,0 +1,12 @@ +"math_counter" +{ + "0" + { + "HP_counter" "boss_hp" + "HP_Mode" "1" + "HPbar_counter" "boss_hp_iterations" + "HPinit_counter" "boss_hp_backup" + "CustomText" "" + "HPbar_mode" "1" + } +} \ No newline at end of file diff --git a/BossHP/scripting/BossHP.sp b/BossHP/scripting/BossHP.sp new file mode 100644 index 0000000..7c13609 --- /dev/null +++ b/BossHP/scripting/BossHP.sp @@ -0,0 +1,1679 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include +#include +#include +#include +#include +#include +#undef REQUIRE_PLUGIN +#tryinclude +#define REQUIRE_PLUGIN + +//#define DEBUG + +#define LoopIngamePlayers(%1) for (int %1 = 1; %1 <= MaxClients; ++%1)\ + if (IsClientInGame(%1)) + +public Plugin myinfo = { + name = "[ZE] BOSS HP", + author = "CrazyKid @僵尸乐园 ZombiEden.CN + PŠΣ™ SHUFEN", + description = "", + version = "2.7 PŠΣ™", + url = "http://ZombiEden.CN + https://possession.jp" +}; + +#define MAX_BOSS_NAME_LENGTH 32 +#define MathCounterSize 32 +#define MathCounterBackupSize 10 +#define BreakableSize 32 +#define MAX_MATHCOUNTER_NAME_LENGTH 48 +#define MAX_BREAKABLE_NAME_LENGTH 48 +#define DEFAULT_BOSSNAME_CSS "BOSS HP" +#define DEFAULT_BOSSNAME_CSGO "BOSS" + +enum struct Array_MathCounter { + char BossName[MAX_BOSS_NAME_LENGTH]; + char sHP[MAX_MATHCOUNTER_NAME_LENGTH]; + char sHPBar[MAX_MATHCOUNTER_NAME_LENGTH]; + char sHPInit[MAX_MATHCOUNTER_NAME_LENGTH]; + int HP; + int HP_Mode; + int HP_Min; + int HP_Max; + int HP_StartValue; + int HPBar_Min; + int HPBar_Max; + int HPBar_StartValue; + int HPBar_Mode; + int HPInit; + int HPInit_Min; + int HPInit_Max; + int HPInit_StartValue; + int Multiply; + int LastHP; + int LastHPShow; + int LastValue; + float LastChangeTime; + float LastAddTime; + bool IsSetDefaultMultiply; + bool IsSetDefaultInit; + int BossHP; + int MaxBossHP; + int HpGroupCount; +} + +enum struct Array_MathCounter_Group { + char sName[MAX_MATHCOUNTER_NAME_LENGTH]; + int HP; + int Min; + int Max; + bool Stats; //true=enable, false=disabled + bool StartDisabled; + bool Kill; + bool RunOut; + int StartValue; + float LastAddTime; +} + +enum struct Array_ClientLastShootBreakable { + char sName[MAX_BREAKABLE_NAME_LENGTH]; + int EntityIndex; +} + +enum struct Array_Breakable { + char BossName[MAX_BOSS_NAME_LENGTH]; + char HitboxName[MAX_BREAKABLE_NAME_LENGTH]; + float LastChangeTime; + int BossHP; + int MaxBossHP; + int Index; +} + +enum struct Array_Count { + int Math_Counter; + int Breakable; +} + +enum struct Array_BossDisplay { + ArrayList BossName; + ArrayList HP; + ArrayList MaxHP; +} + +bool csgo = false; +char g_Mapname[128]; +char sConfig[PLATFORM_MAX_PATH]; +bool IsConfigLoad = false; + +ConVar hCVAR_ForceShowBossHP = null; +ConVar hCVAR_UpperCaseInForceEnable = null; +ConVar hCVAR_RoundEndShowBossDamageRank = null; +ConVar hCVAR_ShowTopDamageDuringBOSS = null; +ConVar hCVAR_BossBeatenShowBossDamageRank = null; +ConVar hCVAR_CrosshairChannel = null; +ConVar hCVAR_BossRewardMoney = null; +ConVar hCVAR_BossHpKeepTime = null; +ConVar hCVAR_BossDieKeepTime = null; +ConVar hCVAR_DisplayWhenHPAdded = null; +ConVar hCVAR_MaxLegalBreakableHP = null; +ConVar hCVAR_MaxLegalMathCounterHP = null; + +bool g_bForceShowBossHP; +bool g_bUpperCaseInForceEnable; +bool g_bRoundEndShowBossDamageRank; +bool g_bShowTopDamageDuringBOSS; +bool g_bBossBeatenShowBossDamageRank; +int g_CrosshairChannel; +int g_RewardMoney; +float g_BossHpKeepTime; +float g_BossDieKeepTime; +bool g_DisplayWhenHPAdded; +int g_MaxLegalBreakableHP; +int g_MaxLegalMathCounterHP; + +Array_MathCounter g_MathCounter[MathCounterSize]; +Array_MathCounter_Group g_MathCounter_HPgroup[MathCounterSize][MathCounterBackupSize]; +Array_ClientLastShootBreakable g_ClientLastShootBreakable[MAXPLAYERS+1]; +Array_Breakable g_Breakable[BreakableSize]; +Array_BossDisplay g_BossDisplay; +Array_Count g_Count; + +float LastShootHitbox[MAXPLAYERS+1]; +float LastShowBossHP; +float LastForceShowBossHP; +float LastShowTopDamage; +char HPcolor[7]; + +float g_LastShowCenterText[MAXPLAYERS+1]; + +//Dhook +Handle hAcceptInput = INVALID_HANDLE; + +Handle BossHP_Cookie = null; +int g_iStatus[MAXPLAYERS+1] = {0, ...}; + +int iLastBossEnt = -1; + +bool g_bUIManager = false; + +public void OnPluginStart() { + /* ====== CSGO Check ====== */ + char theFolder[40]; + GetGameFolderName(theFolder, sizeof(theFolder)); + if (StrEqual(theFolder, "csgo")) csgo = true; + + /* ====== BossDisplay Array ====== */ + g_BossDisplay.BossName = new ArrayList(MAX_BOSS_NAME_LENGTH); + g_BossDisplay.HP = new ArrayList(); + g_BossDisplay.MaxHP = new ArrayList(); + + /* ====== Hook ====== */ + HookEntityOutput("math_counter", "OutValue", MathCounterHook_OutValue); + HookEntityOutput("func_physbox_multiplayer", "OnDamaged", HitboxHook_Breakable); + HookEntityOutput("func_physbox", "OnHealthChanged", HitboxHook_Breakable); + HookEntityOutput("func_breakable", "OnHealthChanged", HitboxHook_Breakable); + HookEntityOutput("prop_dynamic", "OnHealthChanged", HitboxHook); + + HookUserMessage(GetUserMessageId("TextMsg"), UserMessageHook_TextMsg, true); + HookUserMessage(GetUserMessageId("HintText"), UserMessageHook_HintText, true); + + /* ====== CVAR ====== */ + hCVAR_ForceShowBossHP = CreateConVar("BossHP_ForceEnable", "1", "Force display math_counter and func_breakable's valve without BossHP config (bhud function)", _, true, 0.0, true, 1.0); + g_bForceShowBossHP = hCVAR_ForceShowBossHP.BoolValue; + hCVAR_ForceShowBossHP.AddChangeHook(OnConVarChange); + + hCVAR_UpperCaseInForceEnable = CreateConVar("BossHP_ForceEnableUpperCase", "1", "Covert boss name to upper case in force display (bhud function)", _, true, 0.0, true, 1.0); + g_bUpperCaseInForceEnable = hCVAR_UpperCaseInForceEnable.BoolValue; + hCVAR_UpperCaseInForceEnable.AddChangeHook(OnConVarChange); + + hCVAR_RoundEndShowBossDamageRank = CreateConVar("BossHP_RoundEndShowTopDamage", "0", "Show OnRoundEnd", _, true, 0.0, true, 1.0); + g_bRoundEndShowBossDamageRank = hCVAR_RoundEndShowBossDamageRank.BoolValue; + hCVAR_RoundEndShowBossDamageRank.AddChangeHook(OnConVarChange); + + hCVAR_ShowTopDamageDuringBOSS = CreateConVar("BossHP_ShowTopDamageDuringBOSS", "0", "Show during boss fight", _, true, 0.0, true, 1.0); + g_bShowTopDamageDuringBOSS = hCVAR_ShowTopDamageDuringBOSS.BoolValue; + hCVAR_ShowTopDamageDuringBOSS.AddChangeHook(OnConVarChange); + + hCVAR_BossBeatenShowBossDamageRank = CreateConVar("BossHP_BossBeatenShowTopDamage", "1", "Show when boss has beaten", _, true, 0.0, true, 1.0); + g_bBossBeatenShowBossDamageRank = hCVAR_BossBeatenShowBossDamageRank.BoolValue; + hCVAR_BossBeatenShowBossDamageRank.AddChangeHook(OnConVarChange); + + hCVAR_CrosshairChannel = CreateConVar("BossHP_CrosshairChannel", "4", "Crosshair marker display channel", _, true, 0.0, true, 6.0); + g_CrosshairChannel = hCVAR_CrosshairChannel.IntValue; + hCVAR_CrosshairChannel.AddChangeHook(OnConVarChange); + + hCVAR_BossRewardMoney = CreateConVar("BossHP_BossRewardMoney", "10", "Reward money to players when shoot the boss (per hit)", _, true, 0.0, false, _); + g_RewardMoney = hCVAR_BossRewardMoney.IntValue; + hCVAR_BossRewardMoney.AddChangeHook(OnConVarChange); + + hCVAR_BossHpKeepTime = CreateConVar("BossHP_KeepTime", "15.0", "Boss Alive HP keep time", _, true, 0.0, false, _); + g_BossHpKeepTime = hCVAR_BossHpKeepTime.FloatValue; + hCVAR_BossHpKeepTime.AddChangeHook(OnConVarChange); + + hCVAR_BossDieKeepTime = CreateConVar("BossHP_DieKeepTime", "1.0", "Boss Die HP keep time (must be under Alive KeepTime)", _, true, 0.0, false, _); + g_BossDieKeepTime = hCVAR_BossDieKeepTime.FloatValue; + hCVAR_BossDieKeepTime.AddChangeHook(OnConVarChange); + + hCVAR_DisplayWhenHPAdded = CreateConVar("BossHP_DisplayWhenHPAdded", "0", "Display Boss HP When Boss HP Added", _, true, 0.0, true, 1.0); + g_DisplayWhenHPAdded = hCVAR_DisplayWhenHPAdded.BoolValue; + hCVAR_DisplayWhenHPAdded.AddChangeHook(OnConVarChange); + + hCVAR_MaxLegalBreakableHP = CreateConVar("BossHP_MaxLegalBreakableHP", "500000", "", _, true, 0.0, false, _); + g_MaxLegalBreakableHP = hCVAR_MaxLegalBreakableHP.IntValue; + hCVAR_MaxLegalBreakableHP.AddChangeHook(OnConVarChange); + + hCVAR_MaxLegalMathCounterHP = CreateConVar("BossHP_MaxLegalMathCounterHP", "40000", "", _, true, 0.0, false, _); + g_MaxLegalMathCounterHP = hCVAR_MaxLegalMathCounterHP.IntValue; + hCVAR_MaxLegalMathCounterHP.AddChangeHook(OnConVarChange); + + /* ====== Commands ====== */ + RegAdminCmd("reloadbosshp", ReloadBossHP, ADMFLAG_RCON); + RegConsoleCmd("sm_bossdamage", Command_ShowTopDamage); //Allow client view boss damage rank + + RegAdminCmd("sm_shp", Command_SubtractHP, ADMFLAG_RCON, "Subtract Current Boss HP"); + RegAdminCmd("sm_subtracthp", Command_SubtractHP, ADMFLAG_RCON, "Subtract Current Boss HP"); + + /* ====== DEBUG Commands ====== */ + RegAdminCmd("debug_bosshp_saveconfig", DEBUG_SaveConfig, ADMFLAG_RCON); + RegAdminCmd("debug_bosshp_getarrayinfo", DEBUG_GetArrayInfo, ADMFLAG_RCON); + RegAdminCmd("debug_bosshp_printvar", DEBUG_PrintVar, ADMFLAG_RCON); + + /* ====== Events ====== */ + HookEvent("round_start", OnRoundStart); + HookEvent("round_end", OnRoundEnd); + + /* ====== Translate ====== */ + LoadTranslations("BossHP.phrases"); + + /* ====== DHook ====== */ + if (csgo) { + char tmpOffset[148]; + switch (GetEngineVersion()) { + case Engine_CSGO: + tmpOffset = "sdktools.games\\engine.csgo"; + case Engine_CSS: + tmpOffset = "sdktools.games\\engine.css"; + } + Handle temp = LoadGameConfigFile(tmpOffset); + if (temp == INVALID_HANDLE) { + SetFailState("Why you no has gamedata?"); + } else { + int offset = GameConfGetOffset(temp, "AcceptInput"); + hAcceptInput = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, AcceptInput); + DHookAddParam(hAcceptInput, HookParamType_CharPtr); + DHookAddParam(hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(hAcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); //varaint_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20 + DHookAddParam(hAcceptInput, HookParamType_Int); + } + CloseHandle(temp); + } + + /* ====== Clients ====== */ + RegConsoleCmd("sm_bhp", Command_BossHP, "Toggle BossHP"); + RegConsoleCmd("sm_bhud", Command_BossHP, "Toggle BossHP"); + RegConsoleCmd("sm_boss", Command_BossHP, "Toggle BossHP"); + RegConsoleCmd("sm_bosshp", Command_BossHP, "Toggle BossHP"); + RegConsoleCmd("sm_bosshud", Command_BossHP, "Toggle BossHP"); + + BossHP_Cookie = RegClientCookie("bosshp_status", "Status of BossHP", CookieAccess_Private); + SetCookieMenuItem(PrefMenu, 0, "Boss HP"); + + for(int i = 1; i <= MaxClients; i++) { + if (IsClientInGame(i)) { + if (!AreClientCookiesCached(i)) + continue; + OnClientCookiesCached(i); + } + } + + /* ====== Init ====== */ + //GetCurrentMap(g_Mapname, sizeof(g_Mapname)); + //LoadConfig(); +} + +public void OnAllPluginsLoaded() { + g_bUIManager = LibraryExists("UIManager"); +} + +public void OnLibraryAdded(const char[] name) { + if (StrEqual(name, "UIManager")) + g_bUIManager = true; +} + +public void OnLibraryRemoved(const char[] name) { + if (StrEqual(name, "UIManager")) + g_bUIManager = false; +} + +public void OnConVarChange(ConVar hCvar, const char[] oldValue, const char[] newValue) { + if (hCvar == hCVAR_ForceShowBossHP) + g_bForceShowBossHP = view_as(StringToInt(newValue)); + else if (hCvar == hCVAR_UpperCaseInForceEnable) + g_bUpperCaseInForceEnable = view_as(StringToInt(newValue)); + else if (hCvar == hCVAR_RoundEndShowBossDamageRank) + g_bRoundEndShowBossDamageRank = view_as(StringToInt(newValue)); + else if (hCvar == hCVAR_ShowTopDamageDuringBOSS) + g_bShowTopDamageDuringBOSS = view_as(StringToInt(newValue)); + else if (hCvar == hCVAR_BossBeatenShowBossDamageRank) + g_bBossBeatenShowBossDamageRank = view_as(StringToInt(newValue)); + else if (hCvar == hCVAR_CrosshairChannel) + g_CrosshairChannel = StringToInt(newValue); + else if (hCvar == hCVAR_BossRewardMoney) + g_RewardMoney = StringToInt(newValue); + else if (hCvar == hCVAR_DisplayWhenHPAdded) + g_DisplayWhenHPAdded = view_as(StringToInt(newValue)); + else if (hCvar == hCVAR_MaxLegalBreakableHP) + g_MaxLegalBreakableHP = StringToInt(newValue); + else if (hCvar == hCVAR_MaxLegalMathCounterHP) + g_MaxLegalMathCounterHP = StringToInt(newValue); + else if (hCvar == hCVAR_BossHpKeepTime) { + g_BossHpKeepTime = StringToFloat(newValue); + if (g_BossHpKeepTime == 0.0) + g_BossHpKeepTime = 0.01; + if (g_BossHpKeepTime < g_BossDieKeepTime) + g_BossDieKeepTime = g_BossHpKeepTime - 0.01; + } + else if (hCvar == hCVAR_BossDieKeepTime) { + g_BossDieKeepTime = StringToFloat(newValue); + if (g_BossHpKeepTime < g_BossDieKeepTime) + g_BossDieKeepTime = g_BossHpKeepTime - 0.01; + } +} + +public void OnClientConnected(int client) { + g_iStatus[client] = 0; +} + +public void OnClientCookiesCached(int client) { + char sValue[8]; + GetClientCookie(client, BossHP_Cookie, sValue, sizeof(sValue)); + if(sValue[0] == '\0') { + SetClientCookie(client, BossHP_Cookie, "0"); + strcopy(sValue, sizeof(sValue), "0"); + } + g_iStatus[client] = StringToInt(sValue); +} + +public void PrefMenu(int client, CookieMenuAction actions, any info, char[] buffer, int maxlen) { + if (actions == CookieMenuAction_DisplayOption) { + switch(g_iStatus[client]) { + case 0: FormatEx(buffer, maxlen, "%T: %T", "BossHP", client, "FullEnabled", client); + case 1: FormatEx(buffer, maxlen, "%T: %T", "BossHP", client, "OnlyHP", client); + case 2: FormatEx(buffer, maxlen, "%T: %T", "BossHP", client, "OnlyHitmarker", client); + case 3: FormatEx(buffer, maxlen, "%T: %T", "BossHP", client, "Disabled", client); + } + } + + if (actions == CookieMenuAction_SelectOption) { + ToggleBossHP(client); + ShowCookieMenu(client); + } +} + +public Action Command_BossHP(int client, int argc) { + if(client < 1 || client > MaxClients) + return Plugin_Handled; + + ToggleBossHP(client); + return Plugin_Handled; +} + +void ToggleBossHP(int client) { + switch(g_iStatus[client]) { + case 0: { + g_iStatus[client] = 1; + CPrintToChat(client, "%t", "OnlyHPMsg"); + } + case 1: { + g_iStatus[client] = 2; + CPrintToChat(client, "%t", "OnlyHitmarkerMsg"); + } + case 2: { + g_iStatus[client] = 3; + CPrintToChat(client, "%t", "DisabledMsg"); + } + case 3: { + g_iStatus[client] = 0; + CPrintToChat(client, "%t", "FullEnabledMsg"); + } + } + + char sCookieValue[2]; + IntToString(g_iStatus[client], sCookieValue, sizeof(sCookieValue)); + SetClientCookie(client, BossHP_Cookie, sCookieValue); +} + +public Action Command_SubtractHP(int client, int argc) { + if (!IsValidEntity(iLastBossEnt)) { + CReplyToCommand(client, "\x04[SM]\x01 Last boss entity is invalid (ID: %i)", iLastBossEnt); + return Plugin_Handled; + } + + if (argc < 1) { + CReplyToCommand(client, "\x04[SM]\x01 Usage: sm_subtracthp "); + return Plugin_Handled; + } + + char szName[64], szType[64], arg[8]; + int health; + + GetEntityClassname(iLastBossEnt, szType, sizeof(szType)); + GetEntPropString(iLastBossEnt, Prop_Data, "m_iName", szName, sizeof(szName)); + GetCmdArg(1, arg, sizeof(arg)); + SetVariantInt(StringToInt(arg)); + + if (StrEqual(szType, "math_counter", false)) { + static int offset = -1; + if (offset == -1) + offset = FindDataMapInfo(iLastBossEnt, "m_OutValue"); + + health = RoundFloat(GetEntDataFloat(iLastBossEnt, offset)); + AcceptEntityInput(iLastBossEnt, "Subtract", client, client); + CReplyToCommand(client, "\x04[SM]\x01 %i health subtracted from \"%s\" (%i HP to %i HP)", StringToInt(arg), szName, health, health - StringToInt(arg)); + } else { + health = GetEntProp(iLastBossEnt, Prop_Data, "m_iHealth"); + AcceptEntityInput(iLastBossEnt, "RemoveHealth", client, client); + CReplyToCommand(client, "\x04[SM]\x01 %i health subtracted from \"%s\" (%i HP to %i HP)", StringToInt(arg), szName, health, health - StringToInt(arg)); + } + + return Plugin_Handled; +} + +public void OnRoundStart(Event event, const char[] name, bool dontBroadcast) { + if (IsConfigLoad) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + g_MathCounter[i].BossHP = 0; + g_MathCounter[i].MaxBossHP = 0; + g_MathCounter[i].Multiply = 1; + g_MathCounter[i].HPInit = 0; + g_MathCounter[i].IsSetDefaultMultiply = false; + g_MathCounter[i].IsSetDefaultInit = false; + g_MathCounter[i].LastValue = 0; + g_MathCounter[i].LastHP = 0; + g_MathCounter[i].LastHPShow = 0; + + for (int x=0; x < g_MathCounter[i].HpGroupCount; x++) { + g_MathCounter_HPgroup[i][x].Kill = false; + g_MathCounter_HPgroup[i][x].RunOut = false; + + } + } + + for (int i = 0; i < g_Count.Breakable; i++) { + g_Breakable[i].MaxBossHP = 0; + g_Breakable[i].Index = -1; + } + + LoopIngamePlayers(i) { + if (csgo) + CS_SetClientAssists(i, 0); + else + SetEntProp(i, Prop_Data, "m_iDeaths", 0); + + g_ClientLastShootBreakable[i].EntityIndex = -1; + g_ClientLastShootBreakable[i].sName[0] = '\0'; + } + CreateTimer(5.0, Timer_OnRoundStartPost); + } +} + +public void OnRoundEnd(Event event, const char[] name, bool dontBroadcast) { + if (csgo && IsConfigLoad && g_bRoundEndShowBossDamageRank) + ShowBossTopDamage(0); +} + +public Action Timer_OnRoundStartPost(Handle timer) { + if (csgo) + CPrintToChatAll("%t", "RoundStart_Message_CSGO"); + else + CPrintToChatAll("%t", "RoundStart_Message_CSS"); + return Plugin_Stop; +} + +public void OnMapStart() { + GetCurrentMap(g_Mapname, sizeof(g_Mapname)); + LoadConfig(); +} + +public void OnMapEnd() { + iLastBossEnt = -1; +} + +public Action ReloadBossHP(int client, int args) { + LoadConfig(); + ReplyToCommand(client, "[Z-BossHP] Config Reloaded"); + return Plugin_Handled; +} + +public Action DEBUG_SaveConfig(int client, int args) { + SaveConfig(); + ReplyToCommand(client, "[Z-BossHP] Config Saved"); + return Plugin_Handled; +} + +public Action Command_ShowTopDamage(int client, int args) { + if (IsConfigLoad) + ShowBossTopDamage(client); + return Plugin_Handled; +} + +stock void LoadConfig() { + g_Count.Math_Counter = 0; + g_Count.Breakable = 0; + IsConfigLoad = false; + + char tempStr[16]; + int tempNum; + + BuildPath(Path_SM, sConfig, PLATFORM_MAX_PATH, "configs/MapBossHP/%s.txt", g_Mapname); + + KeyValues kv = new KeyValues("math_counter"); + kv.ImportFromFile(sConfig); + + if (kv.GotoFirstSubKey()) { + do { + kv.GetSectionName(tempStr, sizeof(tempStr)); + if (StrEqual(tempStr, "config", false)) { + tempNum = kv.GetNum("ForceEnable", 1); + hCVAR_ForceShowBossHP.SetBool(view_as(tempNum)); + + tempNum = kv.GetNum("ForceEnableUpperCase", 1); + hCVAR_UpperCaseInForceEnable.SetBool(view_as(tempNum)); + + tempNum = kv.GetNum("RoundEndShowTopDamage", 0); + hCVAR_RoundEndShowBossDamageRank.SetBool(view_as(tempNum)); + + tempNum = kv.GetNum("ShowTopDamageDuringBOSS", 0); + hCVAR_ShowTopDamageDuringBOSS.SetBool(view_as(tempNum)); + + tempNum = kv.GetNum("BossBeatenShowTopDamage", 1); + hCVAR_BossBeatenShowBossDamageRank.SetBool(view_as(tempNum)); + + tempNum = kv.GetNum("CrosshairChannel", 4); + if (tempNum >= 1 && tempNum <= 6) + hCVAR_CrosshairChannel.SetInt(tempNum); + + tempNum = kv.GetNum("BossRewardMoney", 10); + if (tempNum > 0) + hCVAR_BossRewardMoney.SetInt(tempNum); + + tempNum = kv.GetNum("BossHpKeepTime", 15); + hCVAR_BossHpKeepTime.SetInt(tempNum); + + tempNum = kv.GetNum("BossDieKeepTime", 1); + hCVAR_BossDieKeepTime.SetInt(tempNum); + + tempNum = kv.GetNum("DisplayWhenHPAdded", 0); + hCVAR_DisplayWhenHPAdded.SetBool(view_as(tempNum)); + + tempNum = kv.GetNum("MaxLegalBreakableHP", 500000); + hCVAR_MaxLegalBreakableHP.SetInt(tempNum); + + tempNum = kv.GetNum("MaxLegalMathCounterHP", 40000); + hCVAR_MaxLegalMathCounterHP.SetInt(tempNum); + + if (g_BossHpKeepTime == 0.0) + g_BossHpKeepTime = 0.01; + if (g_BossHpKeepTime < g_BossDieKeepTime) + g_BossDieKeepTime = g_BossHpKeepTime - 0.01; + } else { + kv.GetString("Type", tempStr, sizeof(tempStr)); + if (StrEqual(tempStr, "breakable", false)) { + kv.GetString("BreakableName", g_Breakable[g_Count.Breakable].HitboxName, sizeof(Array_Breakable::HitboxName)); + kv.GetString("CustomText", g_Breakable[g_Count.Breakable].BossName, sizeof(Array_Breakable::BossName)); + if (g_Breakable[g_Count.Breakable].BossName[0] == '\0') + Format(g_Breakable[g_Count.Breakable].BossName, sizeof(Array_Breakable::BossName), "%s", csgo ? DEFAULT_BOSSNAME_CSGO : DEFAULT_BOSSNAME_CSS); + + #if defined DEBUG + CPrintToChatAll("{pink}[BossHP-DEBUG] kv read: BreakableName:%s", g_Breakable[g_Count.Breakable].HitboxName); //debug + #endif + + ++g_Count.Breakable; + } else { + kv.GetString("HP_Counter", g_MathCounter[g_Count.Math_Counter].sHP, sizeof(Array_MathCounter::sHP)); + kv.GetString("HPinit_Counter", g_MathCounter[g_Count.Math_Counter].sHPInit, sizeof(Array_MathCounter::sHPInit)); + kv.GetString("HPbar_Counter", g_MathCounter[g_Count.Math_Counter].sHPBar, sizeof(Array_MathCounter::sHPBar)); + kv.GetString("CustomText", g_MathCounter[g_Count.Math_Counter].BossName, sizeof(Array_MathCounter::BossName)); + if (g_MathCounter[g_Count.Math_Counter].BossName[0] == '\0') + Format(g_MathCounter[g_Count.Math_Counter].BossName, sizeof(Array_MathCounter::BossName), "%s", csgo ? DEFAULT_BOSSNAME_CSGO : DEFAULT_BOSSNAME_CSS); + + #if defined DEBUG + CPrintToChatAll("{pink}[BossHP-DEBUG] kv read: HP_Counter:%s", g_MathCounter[g_Count.Math_Counter].sHP); //debug + #endif + + g_MathCounter[g_Count.Math_Counter].HpGroupCount = 0; + if (kv.JumpToKey("HP_Group", false)) { + for (int i = 0; i < sizeof(g_MathCounter_HPgroup[]); i++) { + IntToString(i, tempStr, sizeof(tempStr)); + kv.GetString(tempStr, g_MathCounter_HPgroup[g_Count.Math_Counter][i].sName, sizeof(Array_MathCounter_Group::sName)); + + if (g_MathCounter_HPgroup[g_Count.Math_Counter][i].sName[0] == '\0') + break; + + g_MathCounter[g_Count.Math_Counter].HpGroupCount++; + + #if defined DEBUG + CPrintToChatAll("{pink}[BossHP-DEBUG] kv read: BackupCounter(%d):%s", i, g_MathCounter_HPgroup[g_Count.Math_Counter][i].sName); //debug + #endif + } + kv.GoBack(); + } + ++g_Count.Math_Counter; + } + } + } while (kv.GotoNextKey()); + } + delete kv; + + if (g_Count.Math_Counter == 0 && g_Count.Breakable == 0) + IsConfigLoad = false; + else + IsConfigLoad = true; +} + +stock void SaveConfig() { + if (!IsConfigLoad) + return; + + KeyValues kv = new KeyValues("math_counter"); + int index = 0; + char tempStr[16]; + + kv.JumpToKey("config", true); + //kv.SetSectionName("config"); + if (!g_bForceShowBossHP) + kv.SetNum("ForceEnable", 0); + if (!g_bUpperCaseInForceEnable) + kv.SetNum("ForceEnableUpperCase", 0); + if (g_bRoundEndShowBossDamageRank) + kv.SetNum("RoundEndShowTopDamage", 1); + if (g_bShowTopDamageDuringBOSS) + kv.SetNum("ShowTopDamageDuringBOSS", 1); + if (!g_bBossBeatenShowBossDamageRank) + kv.SetNum("BossBeatenShowTopDamage", 0); + if (g_CrosshairChannel != 4 && g_CrosshairChannel >= 1 && g_CrosshairChannel <= 6) + kv.SetNum("CrosshairChannel", g_CrosshairChannel); + if (g_RewardMoney != 10 && g_RewardMoney > 0) + kv.SetNum("BossRewardMoney", g_RewardMoney); + if (g_BossHpKeepTime != 15.0) + kv.SetFloat("BossHpKeepTime", g_BossHpKeepTime); + if (g_BossDieKeepTime != 1.0) + kv.SetFloat("BossDieKeepTime", g_BossDieKeepTime); + if (g_DisplayWhenHPAdded) + kv.SetNum("DisplayWhenHPAdded", 1); + if (g_MaxLegalBreakableHP != 500000) + kv.SetNum("MaxLegalBreakableHP", g_MaxLegalBreakableHP); + if (g_MaxLegalMathCounterHP != 40000) + kv.SetNum("MaxLegalMathCounterHP", g_MaxLegalMathCounterHP); + kv.Rewind(); + + for (int i = 0; i < g_Count.Breakable; i++) { + IntToString(index, tempStr, sizeof(tempStr)); + kv.JumpToKey(tempStr, true); + //kv.SetSectionName(tempStr); + kv.SetString("BreakableName", g_Breakable[i].HitboxName); + if ( ( csgo && !StrEqual(g_Breakable[i].BossName, DEFAULT_BOSSNAME_CSGO) ) || (!csgo && !StrEqual(g_Breakable[i].BossName, DEFAULT_BOSSNAME_CSS) ) ) + kv.SetString("CustomText", g_Breakable[i].BossName); + kv.Rewind(); + index++; + } + + for (int i = 0; i < g_Count.Math_Counter; i++) { + IntToString(index, tempStr, sizeof(tempStr)); + kv.JumpToKey(tempStr, true); + //kv.SetSectionName(tempStr); + kv.SetString("HP_Counter", g_MathCounter[i].sHP); + kv.SetString("HPinit_Counter", g_MathCounter[i].sHPInit); + kv.SetString("HPbar_Counter", g_MathCounter[i].sHPBar); + if ( ( csgo && !StrEqual(g_MathCounter[i].BossName, DEFAULT_BOSSNAME_CSGO) ) || (!csgo && !StrEqual(g_MathCounter[i].BossName, DEFAULT_BOSSNAME_CSS) ) ) + kv.SetString("CustomText", g_MathCounter[i].BossName); + + if (g_MathCounter[i].HpGroupCount > 0) { + kv.JumpToKey("HP_Group", true); + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + IntToString(x, tempStr, sizeof(tempStr)); + kv.SetString(tempStr, g_MathCounter_HPgroup[i][x].sName); + } + kv.GoBack(); + } + + kv.Rewind(); + index++; + } + kv.ExportToFile(sConfig); + delete kv; +} + +public void HitboxHook(const char[] output, int entity, int activator, float delay) { + if (activator > 0 && activator < MAXPLAYERS) + LastShootHitbox[activator] = GetEngineTime(); +} + +public void HitboxHook_Breakable(const char[] output, int entity, int activator, float delay) { + if (activator < 1 || activator > MAXPLAYERS) + return; + + LastShootHitbox[activator] = GetEngineTime(); + + int CurrentValue; + CurrentValue = GetEntProp(entity, Prop_Data, "m_iHealth"); + + if (CurrentValue > g_MaxLegalBreakableHP) + return; + + if (IsConfigLoad) { + char targetname[MAX_BREAKABLE_NAME_LENGTH]; + GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); + + #if defined DEBUG + // CPrintToChatAll("{green}[BossHP-DEBUG] Breakable:%s Value:%d Activator:%d", g_Breakable[i].EntityName, CurrentValue, activator); //debug + #endif + + for (int i = 0; i < g_Count.Breakable; i++) { + if (g_Breakable[i].Index == entity || StrEqual(targetname, g_Breakable[i].HitboxName, false)) { + if (csgo) + CS_SetClientAssists(activator, CS_GetClientAssists(activator) + 1); + else + SetEntProp(activator, Prop_Data, "m_iDeaths", GetEntProp(activator, Prop_Data, "m_iDeaths") - 1); + + //Give cash to players, 1hit = $10 + SetEntProp(activator, Prop_Send, "m_iAccount", GetEntProp(activator, Prop_Send, "m_iAccount") + g_RewardMoney); + + //Show HitMarker when hit the boss + //if (g_iStatus[activator] == 0 || g_iStatus[activator] == 2) { + // if (g_bUIManager) + // SendHudText(activator, g_CrosshairChannel, 5, -1.0, -1.0, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1, 0.1, "◞ ◟\n◝ ◜"); + // else { + // SetHudTextParamsEx(-1.0, -1.0, 0.1, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1); + // ShowHudText(activator, g_CrosshairChannel, "◞ ◟\n◝ ◜"); + // } + //} + + //Show HitMarker to player who is spectating attacker + /* + for (new x = 1; x <= MaxClients; x++) { + if (IsClientInGame(x) && !IsPlayerAlive(x)) { + if (GetEntProp(x, Prop_Send, "m_iObserverMode") == 4) { + if (activator == GetEntPropEnt(x, Prop_Send, "m_hObserverTarget")) { + if (g_iStatus[x] == 0 || g_iStatus[x] == 2) { + SetHudTextParamsEx(-1.0, -1.0, 0.1, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1); + ShowHudText(x, g_CrosshairChannel, "◞ ◟\n◝ ◜"); + } + } + } + } + } + */ + + if (CurrentValue <= 0) { + iLastBossEnt = -1; + + CurrentValue = 0; //to prevent from display negative number + g_Breakable[i].LastChangeTime = GetEngineTime() - ( g_BossHpKeepTime - g_BossDieKeepTime ); + } else { + iLastBossEnt = entity; + + g_Breakable[i].LastChangeTime = GetEngineTime(); + } + + if (g_DisplayWhenHPAdded || CurrentValue < g_Breakable[i].BossHP) { //Only display when BossHP is subtracted. + g_Breakable[i].BossHP = CurrentValue; + PrintHP(activator, CurrentValue); + } else { + g_Breakable[i].BossHP = CurrentValue; + } + + g_Breakable[i].Index = entity; //Need to store Breakable's entity index, because if it's broke, we can't get his targetname. + + return; + } + } + } + + //bhud function + if (g_bForceShowBossHP && + LastShowBossHP + 3.0 < GetEngineTime() && // If BossHP was show in the past 3sec, block this function + LastShootHitbox[activator] > GetEngineTime() - 0.2) { //if players didn't shoot to breakable, block this function + if (g_ClientLastShootBreakable[activator].EntityIndex != entity) { + GetEntPropString(entity, Prop_Data, "m_iName", g_ClientLastShootBreakable[activator].sName, sizeof(Array_ClientLastShootBreakable::sName)); + if (g_ClientLastShootBreakable[activator].sName[0] == '\0') + Format(g_ClientLastShootBreakable[activator].sName, sizeof(Array_ClientLastShootBreakable::sName), "HP"); + g_ClientLastShootBreakable[activator].EntityIndex = entity; + } + if (CurrentValue <= 0) { //to prevent from display negative number + iLastBossEnt = -1; + + PrintHP_Force(activator, entity, g_ClientLastShootBreakable[activator].sName, 0); + } else { + iLastBossEnt = entity; + + PrintHP_Force(activator, entity, g_ClientLastShootBreakable[activator].sName, CurrentValue); + } + } +} + +public void MathCounterHook_OutValue(const char[] output, int entity, int activator, float delay) { + #if defined DEBUG + if (IsValidEntity(entity) || IsValidEdict(entity)) { + char targetname[32]; + int offset, CurrentValue; + GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); + offset = FindDataMapInfo(entity, "m_OutValue"); + if (offset == -1) + return; + CurrentValue = RoundFloat(GetEntDataFloat(entity, offset)); + CPrintToChatAll("%s I:%d N:%s V:%d A:%d O:%d", output, entity, targetname, CurrentValue, activator, offset); + } + #endif + + //if (activator < 0) return; + if (IsConfigLoad && (IsValidEntity(entity) || IsValidEdict(entity))) { + #if !defined DEBUG + char targetname[32]; + int offset, CurrentValue; + GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); + offset = FindDataMapInfo(entity, "m_OutValue"); + if (offset == -1) + return; + CurrentValue = RoundFloat(GetEntDataFloat(entity, offset)); + #endif + + #if defined DEBUG + CPrintToChatAll("{green}[BossHP-DEBUG] Counter:%s Value:%d Activator:%d", targetname, CurrentValue, activator); //debug + #endif + + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (StrEqual(targetname, g_MathCounter[i].sHP, false)) { + if (g_MathCounter[i].sHPBar[0] != '\0') { //If this boss has HPBar counter + //Need to give a default multiply value + if (!g_MathCounter[i].IsSetDefaultMultiply) { + g_MathCounter[i].IsSetDefaultMultiply = true; + if (g_MathCounter[i].HPBar_Mode == 1) { //Boss_end_relay is triggered by OnHitMin + g_MathCounter[i].Multiply = g_MathCounter[i].HPBar_StartValue - g_MathCounter[i].HPBar_Min; + } + else if (g_MathCounter[i].HPBar_Mode == 2) { //Boss_end_relay is triggered by OnHitMax + g_MathCounter[i].Multiply = g_MathCounter[i].HPBar_Max - g_MathCounter[i].HPBar_StartValue; + } + #if defined DEBUG + CPrintToChatAll("{red}[BossHP-DEBUG] Set default multiply: %d", g_MathCounter[i].Multiply); //debug + #endif + } + } + if (g_MathCounter[i].Multiply <= 0) { + g_MathCounter[i].BossHP = 0; //to prevent from display negative number + } else { + if (g_MathCounter[i].HP_Mode == 1) + g_MathCounter[i].BossHP = (g_MathCounter[i].Multiply - 1) * g_MathCounter[i].HPInit + (CurrentValue - g_MathCounter[i].HP_Min); + else + g_MathCounter[i].BossHP = (g_MathCounter[i].Multiply - 1) * g_MathCounter[i].HPInit + (g_MathCounter[i].HP_Max - CurrentValue); + } + + if (g_DisplayWhenHPAdded || g_MathCounter[i].BossHP < g_MathCounter[i].LastHP) { //Only display when BossHP is subtracted. + if ((g_MathCounter[i].HP_Mode == 1 && g_MathCounter[i].BossHP <= g_MathCounter[i].HP_Min) || g_MathCounter[i].BossHP <= 1) { //in some maps like ze_Predator_ultimate, boss's math_counter MinValve is not 0 but 1. + g_MathCounter[i].LastChangeTime = GetEngineTime() - ( g_BossHpKeepTime - g_BossDieKeepTime ) ; // when BossHP hit 0, keep more 1s display time.(default value) + + iLastBossEnt = -1; + + g_MathCounter[i].BossHP = 0; + g_MathCounter[i].IsSetDefaultInit = false; // When a Boss die, reset it to false + } else { + iLastBossEnt = entity; + + g_MathCounter[i].LastChangeTime = GetEngineTime(); + if (g_MathCounter[i].BossHP < g_MaxLegalMathCounterHP && activator > 0 && activator <= MAXPLAYERS) { + if (csgo) + CS_SetClientAssists(activator, CS_GetClientAssists(activator) + 1); + else + SetEntProp(activator, Prop_Data, "m_iDeaths", GetEntProp(activator, Prop_Data, "m_iDeaths") - 1); + + //Give cash to players, 1hit = $10 + SetEntProp(activator, Prop_Send, "m_iAccount", GetEntProp(activator, Prop_Send, "m_iAccount") + g_RewardMoney); + + ////Show HitMarker when hit the boss + //if (g_iStatus[activator] == 0 || g_iStatus[activator] == 2) { + // if (g_bUIManager) + // SendHudText(activator, g_CrosshairChannel, 5, -1.0, -1.0, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1, 0.1, "◞ ◟\n◝ ◜"); + // else { + // SetHudTextParamsEx(-1.0, -1.0, 0.1, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1); + // ShowHudText(activator, g_CrosshairChannel, "◞ ◟\n◝ ◜"); + // } + //} + + //Show HitMarker to player who is spectating attacker + /* + for (new z = 1; z <= MaxClients; z++) { + if (IsClientInGame(z) && !IsPlayerAlive(z)) { + if (GetEntProp(z, Prop_Send, "m_iObserverMode") == 4) { + if (activator == GetEntPropEnt(z, Prop_Send, "m_hObserverTarget")) { + if (g_iStatus[z] == 0 || g_iStatus[z] == 2) { + SetHudTextParamsEx(-1.0, -1.0, 0.1, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1); + ShowHudText(z, g_CrosshairChannel, "◞ ◟\n◝ ◜"); + } + } + } + } + } + */ + } + } + if (g_MathCounter[i].BossHP < g_MaxLegalMathCounterHP && activator > 0 && activator <= MAXPLAYERS) { + PrintHP(activator, g_MathCounter[i].BossHP); + } + g_MathCounter[i].LastHPShow = g_MathCounter[i].BossHP; + } + g_MathCounter[i].LastHP = g_MathCounter[i].BossHP; + + if (!g_MathCounter[i].IsSetDefaultInit && ( + (g_MathCounter[i].HP_Mode == 1 && CurrentValue > g_MathCounter[i].LastValue) || (g_MathCounter[i].HP_Mode != 1 && CurrentValue < g_MathCounter[i].LastValue) + )) { + if (g_MathCounter[i].LastHPShow < 2 /* Some maps do not have HP_Init counter, so need to transfer HPMaxValue to HPinit */ + || g_MathCounter[i].LastAddTime > GetEngineTime() - 0.1 ) { //Some maps will recalculate BossHP when HPbar is subtracted (ze_dreamin, ze_copy, ze_grau, ze_gris etc) + if (g_MathCounter[i].HP_Mode == 1 && CurrentValue - 5 >= g_MathCounter[i].LastValue) { + g_MathCounter[i].HPInit = CurrentValue; + + #if defined DEBUG + CPrintToChatAll("{blue}[BossHP-DEBUG] Set HP init: %d (HP Lastshow:%d Last HPadd interval:%.2f)", g_MathCounter[i].HPInit, g_MathCounter[i].LastHPShow, GetEngineTime() - g_MathCounter[i].LastAddTime); //debug + #endif + } + else if (g_MathCounter[i].HP_Mode != 1 && CurrentValue < g_MathCounter[i].LastValue) + { + g_MathCounter[i].HPInit = g_MathCounter[i].HP_Max; + + #if defined DEBUG + CPrintToChatAll("{blue}[BossHP-DEBUG] Set HP init: %d (HP Lastshow:%d Last HPadd interval:%.2f)", g_MathCounter[i].HPInit, g_MathCounter[i].LastHPShow, GetEngineTime() - g_MathCounter[i].LastAddTime); //debug + #endif + } + } + g_MathCounter[i].LastAddTime = GetEngineTime(); + } + g_MathCounter[i].LastValue = CurrentValue; + break; + } + + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(targetname, g_MathCounter_HPgroup[i][x].sName, false)) { + if (g_MathCounter_HPgroup[i][x].RunOut) + return; + + g_MathCounter_HPgroup[i][x].HP = CurrentValue; + + if (CurrentValue == 0) + g_MathCounter_HPgroup[i][x].RunOut = true; + + g_MathCounter[i].BossHP = 0; + for (int z = 0; z < g_MathCounter[i].HpGroupCount; z++) { + g_MathCounter[i].BossHP += g_MathCounter_HPgroup[i][z].HP; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] HPgroup(%s)'s CurrentValue:%d", g_MathCounter_HPgroup[i][z].sName, g_MathCounter_HPgroup[i][z].HP); //debug + #endif + } + + if (g_DisplayWhenHPAdded || g_MathCounter[i].BossHP < g_MathCounter[i].LastHP) { //Only display when BossHP is subtracted. + if (g_MathCounter[i].BossHP <= 1) { //in some maps like ze_Predator_ultimate, boss's math_counter MinValve is not 0 but 1. + g_MathCounter[i].LastChangeTime = GetEngineTime() - ( g_BossHpKeepTime - g_BossDieKeepTime ) ; // when BossHP hit 0, keep more 1s display time. + iLastBossEnt = -1; + + g_MathCounter[i].BossHP = 0; + } else { + iLastBossEnt = entity; + + g_MathCounter[i].LastChangeTime = GetEngineTime(); + if (g_MathCounter[i].BossHP < g_MaxLegalMathCounterHP && activator > 0 && activator <= MAXPLAYERS) { + if (csgo) + CS_SetClientAssists(activator, CS_GetClientAssists(activator) + 1); + else + SetEntProp(activator, Prop_Data, "m_iDeaths", GetEntProp(activator, Prop_Data, "m_iDeaths") - 1); + + //Give cash to players, 1hit = $10 + SetEntProp(activator, Prop_Send, "m_iAccount", GetEntProp(activator, Prop_Send, "m_iAccount") + g_RewardMoney); + + ////Crosshair when hit the boss + //if (g_iStatus[activator] == 0 || g_iStatus[activator] == 2) { + // if (g_bUIManager) + // SendHudText(activator, g_CrosshairChannel, 5, -1.0, -1.0, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1, 0.1, "◞ ◟\n◝ ◜"); + // else { + // SetHudTextParamsEx(-1.0, -1.0, 0.1, {255, 0, 0, 50}, {255, 0, 0, 50}, 0, 0.1, 0.1, 0.1); + // ShowHudText(activator, g_CrosshairChannel, "◞ ◟\n◝ ◜"); + // } + //} + } + } + if (g_MathCounter[i].BossHP < g_MaxLegalMathCounterHP && activator > 0 && activator <= MAXPLAYERS) { + PrintHP(activator, g_MathCounter[i].BossHP); + } + } + g_MathCounter[i].LastHP = g_MathCounter[i].BossHP; + break; + } + } + + if (StrEqual(targetname, g_MathCounter[i].sHPBar, false)) { + if (g_MathCounter[i].HPBar_Mode == 1) { + g_MathCounter[i].Multiply = CurrentValue - g_MathCounter[i].HPBar_Min; + } + else if (g_MathCounter[i].HPBar_Mode == 2) + { + g_MathCounter[i].Multiply = g_MathCounter[i].HPBar_Max - CurrentValue; + } + g_MathCounter[i].IsSetDefaultMultiply = true; + + #if defined DEBUG + CPrintToChatAll("{pink}[BossHP-DEBUG] Set multiply:%d (triggered by HPbar Counter:%s)", g_MathCounter[i].Multiply, targetname); //debug + #endif + } + if (StrEqual(targetname, g_MathCounter[i].sHPInit, false)) { + g_MathCounter[i].HPInit = CurrentValue; + g_MathCounter[i].IsSetDefaultInit = true; + + #if defined DEBUG + CPrintToChatAll("{blue}[BossHP-DEBUG] Set HP init:%d (triggered by HPinit Counter:%s)", g_MathCounter[i].HPInit, targetname); //debug + #endif + } + } + } + + //bhud function + if (g_bForceShowBossHP && + LastShowBossHP + 3.0 < GetEngineTime() && // If BossHP was show in the past 3sec, block this function + (IsValidEntity(entity) || IsValidEdict(entity)) && + activator > 0 && activator <= MAXPLAYERS) { + if (LastShootHitbox[activator] < GetEngineTime() - 0.1) + return; //if players didn't shoot to hitbox, block this function + + int CurrentValue = RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue"))); + GetEntPropString(entity, Prop_Data, "m_iName", g_ClientLastShootBreakable[activator].sName, sizeof(Array_ClientLastShootBreakable::sName)); + g_ClientLastShootBreakable[activator].EntityIndex = 0; + if (CurrentValue > 0) { + iLastBossEnt = entity; + + PrintHP_Force(activator, 0, g_ClientLastShootBreakable[activator].sName, CurrentValue); + } else{ + iLastBossEnt = -1; + + //PrintHP_Force(activator, 0, g_ClientLastShootBreakable[activator].sName, 0); + } + } +} + +public Action UserMessageHook_TextMsg(UserMsg msg_id, Handle pb, const int[] players, int playersNum, bool reliable, bool init) { + if (csgo) { + if (PbReadInt(pb, "msg_dst") != 4) + return Plugin_Continue; + + for (int i; i < playersNum; i++) { + g_LastShowCenterText[players[i]] = GetEngineTime() + 2.0; + } + + } + return Plugin_Continue; +} + +public Action UserMessageHook_HintText(UserMsg msg_id, Handle pb, const int[] players, int playersNum, bool reliable, bool init) { +// + if (csgo) { + char szBuffer[255]; + PbReadString(pb, "text", szBuffer, sizeof(szBuffer)); + if (StrContains(szBuffer, "HP") > -1) { + return Plugin_Continue; + } + for (int i; i< playersNum; i++) { + g_LastShowCenterText[players[i]] = GetEngineTime(); + } + } + return Plugin_Continue; +} + +char PrintText_Hint[255]; +//char PrintText_HUD[255]; +char CurrentBossName[32]; +float CurrentTime; +int CurrentHP; +int MaxHP; +int CTcount; +int count; + +//bhud function +void PrintHP_Force(int client, int entity, const char[] entityname, int hp) { + CurrentTime = GetEngineTime(); + if ( (LastShootHitbox[client] > CurrentTime - 3.0 && LastForceShowBossHP + 0.1 < CurrentTime) || hp == 0 ) { + count = 0; + CTcount = 0; + LoopIngamePlayers(i) { + if (GetClientTeam(i) == CS_TEAM_CT) { + CTcount++; + if (LastShootHitbox[i] > CurrentTime - 7.0 && g_ClientLastShootBreakable[i].EntityIndex == entity && StrEqual(g_ClientLastShootBreakable[i].sName, entityname)) { + count++; + } + } + } + + char sBuffer[MAX_BREAKABLE_NAME_LENGTH]; + strcopy(sBuffer, sizeof(sBuffer), entityname); + if (g_bUpperCaseInForceEnable) { + int and = -1; + if ((and = FindCharInString(sBuffer, '&', true)) > 0) + sBuffer[and] = '\0'; + ReplaceString(sBuffer, sizeof(sBuffer), "_", " "); + for (int x = 0; x < strlen(sBuffer); x++) { + if (IsCharLower(sBuffer[x])) + sBuffer[x] = CharToUpper(sBuffer[x]); + } + } + + if (count > CTcount / 2) { + if (csgo) { + LoopIngamePlayers(i) { + if (g_iStatus[i] == 0 || g_iStatus[i] == 1) + PrintHintText(i, "%s: %d", sBuffer, hp); + } + } else { + LoopIngamePlayers(i) { + if (g_iStatus[i] == 0 || g_iStatus[i] == 1) + PrintHintText(i, "%s: %d", sBuffer, hp); + } + } + } else { + LoopIngamePlayers(i) { + if (g_iStatus[i] == 0 || g_iStatus[i] == 1) { + if (LastShootHitbox[i] > CurrentTime - 7.0 && g_ClientLastShootBreakable[i].EntityIndex == entity && StrEqual(g_ClientLastShootBreakable[i].sName, entityname)) { + if (csgo) { + PrintHintText(i, "%s: %d", sBuffer, hp); + } else { + PrintHintText(i, "%s: %d", sBuffer, hp); + } + } + } + } + } + LastForceShowBossHP = CurrentTime; + } +} + +void PrintHP(int client, int hp) { + CurrentTime = GetEngineTime(); + + if ( (LastShootHitbox[client] > CurrentTime - 3.0 && LastShowBossHP + 0.1 < CurrentTime) || hp == 0 || g_DisplayWhenHPAdded ) { + g_BossDisplay.BossName.Clear(); + g_BossDisplay.HP.Clear(); + g_BossDisplay.MaxHP.Clear(); + PrintText_Hint = ""; + //PrintText_HUD = ""; + + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (g_MathCounter[i].BossHP > g_MathCounter[i].MaxBossHP) + g_MathCounter[i].MaxBossHP = g_MathCounter[i].BossHP; + if (g_MathCounter[i].LastChangeTime + g_BossHpKeepTime > CurrentTime) { + g_BossDisplay.BossName.PushString(g_MathCounter[i].BossName); + g_BossDisplay.HP.Push(g_MathCounter[i].BossHP); + g_BossDisplay.MaxHP.Push(g_MathCounter[i].MaxBossHP); + } + } + + for (int i = 0; i < g_Count.Breakable; i++) { + if (g_Breakable[i].BossHP > g_Breakable[i].MaxBossHP) + g_Breakable[i].MaxBossHP = g_Breakable[i].BossHP; + if (g_Breakable[i].LastChangeTime + g_BossHpKeepTime > CurrentTime) { + g_BossDisplay.BossName.PushString(g_Breakable[i].BossName); + g_BossDisplay.HP.Push(g_Breakable[i].BossHP); + g_BossDisplay.MaxHP.Push(g_Breakable[i].MaxBossHP); + } + } + + for (int i = 0; i < g_BossDisplay.HP.Length; i++) { + g_BossDisplay.BossName.GetString(i, CurrentBossName, sizeof(CurrentBossName)); + CurrentHP = g_BossDisplay.HP.Get(i); + MaxHP = g_BossDisplay.MaxHP.Get(i); + + if (csgo) { + float fCurrentRatio = float(CurrentHP) / float(MaxHP); + if (fCurrentRatio < 0.2) + HPcolor = "FF0000"; //red + else if (fCurrentRatio < 0.4) + HPcolor = "FFFF00"; //yellow + else + HPcolor = "00FF00"; //green + if (g_BossDisplay.HP.Length == 1) { //Single BossHP display style + int iBar = (hp == 0) ? 0 : RoundToCeil(fCurrentRatio * 20.0); + if (iBar > 20) iBar = 20; + char sBar[64], sBlank[64]; + for (int j = 0; j < iBar - 1; j++) + StrCat(sBar, sizeof(sBar), "█"); //░▒▓█ + for (int k = 0; k < 20 - iBar; k++) + StrCat(sBlank, sizeof(sBlank), "░"); + //%s + Format(PrintText_Hint, sizeof(PrintText_Hint), "►%s◄ HP: %d\n%s%s%s%s\n", CurrentBossName, HPcolor, CurrentHP, sBar, CurrentHP <= 0 ? "" : (CurrentHP < MaxHP ? "▒" : "█"), CurrentHP <= 0 ? "░" : "", sBlank); + } + else if (g_BossDisplay.HP.Length <= 3) { //Mult BossHP display style + Format(PrintText_Hint, sizeof(PrintText_Hint), "%s%s: %dHP\n", PrintText_Hint, CurrentBossName, HPcolor, CurrentHP); + } + else // more than 3 BossHP display style + { + Format(PrintText_Hint, sizeof(PrintText_Hint), "%s%s: %dHP%s", PrintText_Hint, CurrentBossName, HPcolor, CurrentHP, i % 2 ? "\n" : " "); + } + } else { + Format(PrintText_Hint, sizeof(PrintText_Hint), "%s%s: %d%s", PrintText_Hint, CurrentBossName, CurrentHP, i == g_BossDisplay.HP.Length - 1 ? "" : "\n"); + } + //Format(PrintText_HUD, sizeof(PrintText_HUD), "%s%s: %d%s", PrintText_HUD, CurrentBossName, CurrentHP, "HP\n"); + } + + LoopIngamePlayers(i) { + if (g_iStatus[i] == 0 || g_iStatus[i] == 1) { + if (CurrentTime - 3.0 < g_LastShowCenterText[i]) { + //SetHudTextParamsEx(-1.0, 0.66 - 0.03 * (g_BossDisplay.HP.Length - 1), 1.0, {255, 0, 255, 230}, {255, 0, 255, 230}, 1, 0.0, 0.1, 0.1); + //ShowHudText(i, 1, "%s", PrintText_HUD); + } else { + PrintHintText(i, "%s", PrintText_Hint); + } + } + } + + LastShowBossHP = CurrentTime; + + if (LastShowTopDamage + 1.0 < CurrentTime) { + if (g_bShowTopDamageDuringBOSS) { + ShowBossTopDamage(0, -1.0, 0.76); + LastShowTopDamage = CurrentTime; + } else if (g_bBossBeatenShowBossDamageRank && hp == 0) { + ShowBossTopDamage(0, -1.0, 0.76); + LastShowTopDamage = CurrentTime; + } + } + } +} + +public Action DEBUG_PrintVar(int client, int args) { + if (!client) + return Plugin_Handled; + + ReplyToCommand(client, "g_bForceShowBossHP: %b", g_bForceShowBossHP); + ReplyToCommand(client, "g_BossHpKeepTime: %.1f", g_BossHpKeepTime); + ReplyToCommand(client, "g_BossDieKeepTime: %.1f", g_BossDieKeepTime); + ReplyToCommand(client, "g_MaxLegalBreakableHP: %d", g_MaxLegalBreakableHP); + ReplyToCommand(client, "g_MaxLegalMathCounterHP: %d", g_MaxLegalMathCounterHP); + ReplyToCommand(client, "g_DisplayWhenHPAdded: %b", g_DisplayWhenHPAdded); + ReplyToCommand(client, "g_LastShowCenterText: %.1f", g_LastShowCenterText); + ReplyToCommand(client, "GetEngineTime(): %.2f", GetEngineTime()); + return Plugin_Handled; +} + +public void OnEntityCreated(int entity, const char[] classname) { + if (!IsConfigLoad) return; + + if (StrEqual(classname, "math_counter", false)) { + if (hAcceptInput != INVALID_HANDLE) + DHookEntity(hAcceptInput, false, entity); + RequestFrame(Timer_InitMathCounterInfo, entity); + } +} + +void Timer_InitMathCounterInfo(int entity) { + if (!IsValidEntity(entity) && !IsValidEdict(entity)) + return; + + char EntityName[64]; + GetEntPropString(entity, Prop_Data, "m_iName", EntityName, sizeof(EntityName)); + + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (StrEqual(EntityName, g_MathCounter[i].sHP, false)) { + g_MathCounter[i].HP_Min = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMin")); + g_MathCounter[i].HP_Max = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMax")); + g_MathCounter[i].HP_StartValue = RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue"))); + int iOnHitMinCount = GetOutputCount(entity, "m_OnHitMin"); + int iOnHitMaxCount = GetOutputCount(entity, "m_OnHitMax"); + g_MathCounter[i].HP_Mode = iOnHitMaxCount > iOnHitMinCount ? 2 : 1; + break; + } + + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(EntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + g_MathCounter_HPgroup[i][x].Min = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMin")); + g_MathCounter_HPgroup[i][x].Max = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMax")); + g_MathCounter_HPgroup[i][x].HP = RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue"))); + g_MathCounter_HPgroup[i][x].StartValue = RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue"))); + g_MathCounter_HPgroup[i][x].StartDisabled = view_as(GetEntProp(entity, Prop_Data, "m_bDisabled")); + g_MathCounter_HPgroup[i][x].Stats = !g_MathCounter_HPgroup[i][x].StartDisabled; + + break; + } + } + + if (StrEqual(EntityName, g_MathCounter[i].sHPBar, false)) { + g_MathCounter[i].HPBar_Min = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMin")); + g_MathCounter[i].HPBar_Max = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMax")); + g_MathCounter[i].HPBar_StartValue = RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue"))); + int iOnHitMinCount = GetOutputCount(entity, "m_OnHitMin"); + int iOnHitMaxCount = GetOutputCount(entity, "m_OnHitMax"); + g_MathCounter[i].HPBar_Mode = iOnHitMaxCount > iOnHitMinCount ? 2 : 1; + break; + } + + if (StrEqual(EntityName, g_MathCounter[i].sHPInit, false)) { + g_MathCounter[i].HPInit_Min = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMin")); + g_MathCounter[i].HPInit_Max = RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMax")); + g_MathCounter[i].HPInit_StartValue = RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue"))); + break; + } + } + + #if defined DEBUG + if (StrContains(EntityName, "behemoth_", false) > -1) { + PrintToChatAll("EntityName: %s", EntityName); + PrintToChatAll("m_bHitMax: %d", GetEntProp(entity, Prop_Data, "m_bHitMax")); + PrintToChatAll("m_bHitMin: %d", GetEntProp(entity, Prop_Data, "m_bHitMin")); + PrintToChatAll("m_flMin: %d", RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMin"))); + PrintToChatAll("m_flMax: %d", RoundFloat(GetEntPropFloat(entity, Prop_Data, "m_flMax"))); + PrintToChatAll("m_bDisabled: %d", GetEntProp(entity, Prop_Data, "m_bDisabled")); + PrintToChatAll("m_OutValue: %d", RoundFloat(GetEntDataFloat(entity, FindDataMapInfo(entity, "m_OutValue")))); + PrintToChatAll("--"); + } + #endif +} + +public Action DEBUG_GetArrayInfo(int client, int args) { + if (args != 1) + return Plugin_Handled; + char Arg1[32]; + GetCmdArg(1, Arg1, 32); + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (StrEqual(Arg1, g_MathCounter[i].sHP, false)) { + ReplyToCommand(client, "Type: g_MathCounter[i].sHP"); + ReplyToCommand(client, "Min:%d", g_MathCounter[i].HP_Min); + ReplyToCommand(client, "Max:%d", g_MathCounter[i].HP_Max); + ReplyToCommand(client, "StartValue:%d", g_MathCounter[i].HP_StartValue); + + break; + } + + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(Arg1, g_MathCounter_HPgroup[i][x].sName, false)) { + ReplyToCommand(client, "Type: g_MathCounter_HPgroup[i][x].sName"); + ReplyToCommand(client, "Min:%d", g_MathCounter_HPgroup[i][x].Min); + ReplyToCommand(client, "Max:%d", g_MathCounter_HPgroup[i][x].Max); + ReplyToCommand(client, "HP:%d", g_MathCounter_HPgroup[i][x].HP); + ReplyToCommand(client, "StartValue:%d", g_MathCounter_HPgroup[i][x].StartValue); + ReplyToCommand(client, "StartDisabled:%b", g_MathCounter_HPgroup[i][x].StartDisabled); + ReplyToCommand(client, "Stats:%d", g_MathCounter_HPgroup[i][x].Stats); + + break; + } + } + + if (StrEqual(Arg1, g_MathCounter[i].sHPBar, false)) { + ReplyToCommand(client, "Type: g_MathCounter[i].sHPBar"); + ReplyToCommand(client, "Min:%d", g_MathCounter[i].HPBar_Min); + ReplyToCommand(client, "Max:%d", g_MathCounter[i].HPBar_Max); + ReplyToCommand(client, "StartValue:%b", g_MathCounter[i].HPBar_StartValue); + + break; + } + + if (StrEqual(Arg1, g_MathCounter[i].sHPInit, false)) { + ReplyToCommand(client, "Type: g_MathCounter[i].sHPInit"); + ReplyToCommand(client, "Min:%d", g_MathCounter[i].HPInit_Min); + ReplyToCommand(client, "Max:%d", g_MathCounter[i].HPInit_Max); + ReplyToCommand(client, "StartValue:%b", g_MathCounter[i].HPInit_StartValue); + + break; + } + } + return Plugin_Handled; +} + +public MRESReturn AcceptInput(int entity, Handle hReturn, Handle hParams) { + if (!IsValidEntity(entity)) + return MRES_Ignored; + + char eEntityName[64], eCommand[16], eParam[16]; + GetEntPropString(entity, Prop_Data, "m_iName", eEntityName, 64); + + DHookGetParamString(hParams, 1, eCommand, 16); + int type = -1, Param = 0; + type = DHookGetParamObjectPtrVar(hParams, 4, 16, ObjectValueType_Int); + if (type == 1) + Param = DHookGetParamObjectPtrVar(hParams, 4, 0, ObjectValueType_Int); + else if (type == 2) { + DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, eParam, 16); + StringToIntEx(eParam, Param); + } + + if (StrEqual(eCommand, "SetValueNoFire", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (StrEqual(eEntityName, g_MathCounter[i].sHPBar, false)) { + if (g_MathCounter[i].HPBar_Mode == 1) + g_MathCounter[i].Multiply = Param - g_MathCounter[i].HPBar_Min; + else if (g_MathCounter[i].HPBar_Mode == 2) + g_MathCounter[i].Multiply = g_MathCounter[i].HPBar_Max - Param; + g_MathCounter[i].IsSetDefaultMultiply = true; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetValueNoFire)Set multiply:%d (triggered by HPbar Counter:%s)", g_MathCounter[i].Multiply, eEntityName); //debug + #endif + + break; + } + if (StrEqual(eEntityName, g_MathCounter[i].sHPInit, false)) { + g_MathCounter[i].HPInit = Param; + g_MathCounter[i].IsSetDefaultInit = true; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetValueNoFire)Set HP init:%d (triggered by HPinit Counter:%s)", g_MathCounter[i].HPInit, eEntityName); //debug + #endif + + break; + } + } + } + else if (StrEqual(eCommand, "Add", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(eEntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + if (g_MathCounter_HPgroup[i][x].Kill || g_MathCounter_HPgroup[i][x].RunOut || g_MathCounter_HPgroup[i][x].Stats) + return MRES_Ignored; + + if (g_MathCounter_HPgroup[i][x].LastAddTime < GetEngineTime() - 1.0) { + g_MathCounter_HPgroup[i][x].HP = g_MathCounter_HPgroup[i][x].StartValue; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] Set Default HPgroup(%s):%d CurrentValue:%d", eEntityName, g_MathCounter_HPgroup[i][x].StartValue, g_MathCounter_HPgroup[i][x].HP); //debug + #endif + } + + g_MathCounter_HPgroup[i][x].HP += Param; + g_MathCounter_HPgroup[i][x].LastAddTime = GetEngineTime(); + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] Add to HPgroup(%s):%d CurrentValue:%d", eEntityName, Param, g_MathCounter_HPgroup[i][x].HP); //debug + #endif + + break; + } + } + } + } + else if (StrEqual(eCommand, "SetHitMax", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (StrEqual(eEntityName, g_MathCounter[i].sHP, false)) { + g_MathCounter[i].HP_Max = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMax)Set HP Max:%d", Param); //debug + #endif + + break; + } + if (StrEqual(eEntityName, g_MathCounter[i].sHPBar, false)) { + g_MathCounter[i].HPBar_Max = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMax)Set HPbar Max:%d", Param); //debug + #endif + + break; + } + if (StrEqual(eEntityName, g_MathCounter[i].sHPInit, false)) { + g_MathCounter[i].HPInit_Max = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMax)Set HPInit Max:%d", Param); //debug + #endif + + break; + } + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(eEntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + g_MathCounter_HPgroup[i][x].Max = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMax)Set HPgroup(%d) Max:%d", x, Param); //debug + #endif + + break; + } + } + } + } + else if (StrEqual(eCommand, "SetHitMin", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + if (StrEqual(eEntityName, g_MathCounter[i].sHP, false)) { + g_MathCounter[i].HP_Min = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMin)Set HP Min:%d", Param); //debug + #endif + + break; + } + if (StrEqual(eEntityName, g_MathCounter[i].sHPBar, false)) { + g_MathCounter[i].HPBar_Min = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMin)Set HPbar Min:%d", Param); //debug + #endif + + break; + } + if (StrEqual(eEntityName, g_MathCounter[i].sHPInit, false)) { + g_MathCounter[i].HPInit_Min = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMin)Set HPInit Min:%d", Param); //debug + #endif + + break; + } + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(eEntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + g_MathCounter_HPgroup[i][x].Min = Param; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] (SetHitMin)Set HPgroup(%d) Min:%d", x, Param); //debug + #endif + + break; + } + } + } + } + //else if (StrEqual(eCommand, "SetMaxValueNoFire", false)) {} //(New with Portal 2, Unknown if it's exist in CSGO or CSS) + //else if (StrEqual(eCommand, "SetMinValueNoFire", false)) {} //(New with Portal 2, Unknown if it's exist in CSGO or CSS) + else if (StrEqual(eCommand, "Enable", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(eEntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + g_MathCounter_HPgroup[i][x].Stats = true; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] Enable HPgroup: %s", eEntityName); //debug + #endif + + break; + } + } + } + } + else if (StrEqual(eCommand, "Disable", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(eEntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + g_MathCounter_HPgroup[i][x].Stats = false; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] Disabled(kill) HPgroup: %s", eEntityName); //debug + #endif + + break; + } + } + } + } + else if (StrEqual(eCommand, "Kill", false)) { + for (int i = 0; i < g_Count.Math_Counter; i++) { + for (int x = 0; x < g_MathCounter[i].HpGroupCount; x++) { + if (StrEqual(eEntityName, g_MathCounter_HPgroup[i][x].sName, false)) { + g_MathCounter_HPgroup[i][x].Stats = false; + g_MathCounter_HPgroup[i][x].Kill = true; + + #if defined DEBUG + CPrintToChatAll("{purple}[BossHP-DEBUG] Disabled HPgroup: %s", eEntityName); //debug + #endif + + break; + } + } + } + } + + return MRES_Ignored; +} + +stock void q_sort(int[] numbers, int left, int right, int[] subarr) { + int pivot, l_hold, r_hold, subpiv; + + l_hold = left; + r_hold = right; + pivot = numbers[left]; + subpiv = subarr[left]; + while (left < right) { + while ((numbers[right] >= pivot) && (left < right)) + right--; + if (left != right) { + numbers[left] = numbers[right]; + subarr[left] = subarr[right]; + left++; + } + while ((numbers[left] <= pivot) && (left < right)) + left++; + if (left != right) { + numbers[right] = numbers[left]; + subarr[right] = subarr[left]; + right--; + } + } + numbers[left] = pivot; + subarr[left] = subpiv; + pivot = left; + left = l_hold; + right = r_hold; + if (left < pivot) + q_sort(numbers, left, pivot - 1, subarr); + if (right > pivot) + q_sort(numbers, pivot + 1, right, subarr); +} + +stock void QuickSort(int[] arr, int size, int[] subarr) { + q_sort(arr, 0, size - 1, subarr); +} + +void ShowBossTopDamage(int client, const float x = -1.0, const float y = 0.26) { + int[] damagelist = new int[MaxClients+1]; + int[] playerlist = new int[MaxClients+1]; + + for (int i = 1; i < MaxClients; i++) { + if (IsClientInGame(i)) { + damagelist[i] = csgo ? CS_GetClientAssists(i) : GetEntProp(i, Prop_Data, "m_iDeaths") * -1; + playerlist[i] = i; + } + } + + QuickSort(damagelist, MaxClients+1, playerlist); + + char damagelist_text[255]; + int rank = 1; + for (int j = MaxClients; j >= 0; j--, rank++) { + if (rank > 5 || damagelist[j] < 1 || playerlist[j] < 1) break; + Format(damagelist_text, sizeof(damagelist_text), "%s\n%i. %N: %d HITs", damagelist_text, rank, playerlist[j], damagelist[j]); + } + if (damagelist_text[0] != '\0') { + if (g_bUIManager) { + //if (client == 0) { + // SendHudTextToAll(3, 5, x, y, {255, 201, 14, 230}, _, 1, 0.3, 2.0, 10.0, 0.0, "%t%s", "DamageRank_Title", damagelist_text); + //} + //else { + // SendHudText(client, 3, 5, x, y, {255, 201, 14, 230}, _, 1, 0.3, 2.0, 10.0, 0.0, "%t%s", "DamageRank_Title", damagelist_text); + //} + } else { + SetHudTextParams(x, y, 10.0, 255, 201, 14, 230, 1, 0.0, 0.3, 2.0); + + char text[255]; + + if (client == 0) { + LoopIngamePlayers(i) { + FormatEx(text, sizeof(text), "%T", "DamageRank_Title", i); + Format(text, sizeof(text), "%s%s", text, damagelist_text); + ShowHudText(i, 3, "%s", text); + } + } + else { + FormatEx(text, sizeof(text), "%T", "DamageRank_Title", client); + Format(text, sizeof(text), "%s%s", text, damagelist_text); + ShowHudText(client, 3, "%s", text); + } + } + } +} diff --git a/BossHP/translations/BossHP.phrases.txt b/BossHP/translations/BossHP.phrases.txt new file mode 100644 index 0000000..015cafb --- /dev/null +++ b/BossHP/translations/BossHP.phrases.txt @@ -0,0 +1,87 @@ +"Phrases" +{ + "RoundStart_Message_CSGO" + { + "en" "{orange}[{yellow}BossHP{orange}]{olive} This map is supported by this plugin. Shoot the boss will add your [Assists] count." + "jp" "{orange}[{yellow}BossHP{orange}]{olive} このマップは BossHP プラグインが動作しています. ボスを撃つとあなたの [Assists] カウントが上昇します." + "chi" "{orange}[{yellow}BossHP{orange}]{olive} 当前地图支持 BOSS HP 显示. 助攻数为本局您对 BOSS 造成的伤害" + "zho" "{orange}[{yellow}BossHP{orange}]{olive} 當前地圖支援 BOSS HP 顯示. 助攻數為本局您對 BOSS 造成的傷害" + } + "RoundStart_Message_CSS" + { + "en" "{orange}[{yellow}BossHP{orange}]{olive} This map is supported by this plugin. Shoot the boss will add your [Death] count." + "jp" "{orange}[{yellow}BossHP{orange}]{olive} このマップは BossHP プラグインが動作しています. ボスを撃つとあなたの [Death] カウントが上昇します." + "chi" "{orange}[{yellow}BossHP{orange}]{olive} 当前地图支持 BOSS HP 显示. 死亡数为本局您对 BOSS 造成的伤害" + "zho" "{orange}[{yellow}BossHP{orange}]{olive} 當前地圖支援 BOSS HP 顯示. 死亡數為本局您對 BOSS 造成的傷害" + } + "DamageRank_Title" + { + "en" "*** Rank of Damage to BOSS ***" + "jp" "*** BOSS ダメージランキング ***" + "chi" "*** 本局对 BOSS 输出排行 ***" + "zho" "*** 本局對 BOSS 輸出排行 ***" + } + "BossHP" + { + "en" "Show Boss HP" + "jp" "Boss HP (ボスHP表示)" + "chi" "显示 Boss HP" + "zho" "顯示 Boss HP" + } + "Disabled" + { + "en" "Disabled" + "jp" "無効" + "chi" "停用" + "zho" "停用" + } + "OnlyHP" + { + "en" "Only HP Display" + "jp" "HP表示のみ" + "chi" "只有 HP 显示" + "zho" "只有 HP 顯示" + } + "FullEnabled" + { + "en" "Enabled (with Hitmarker)" + "jp" "有効 (ヒットマーカー付き)" + "chi" "启用 (附带击中标记)" + "zho" "啟用 (附帶擊中標記)" + } + "OnlyHitmarker" + { + "en" "Only Hitmarker" + "jp" "ヒットマーカーのみ" + "chi" "只有击中标记" + "zho" "只有擊中標記" + } + "DisabledMsg" + { + "en" "{green}[BossHP]{default} Show Boss HP has been {olive}Disabled{default}." + "jp" "{green}[BossHP]{default} ボスHP表示を {olive}無効{default} にしました." + "chi" "{green}[BossHP]{default} 显示Boss HP机能已被{olive}停用{default}。" + "zho" "{green}[BossHP]{default} 顯示Boss HP功能已被{olive}停用{default}。" + } + "OnlyHPMsg" + { + "en" "{green}[BossHP]{default} Show Boss HP has been {olive}Set to only HP display{default}." + "jp" "{green}[BossHP]{default} ボスHP表示を {olive}HP表示のみ{default} にしました." + "chi" "{green}[BossHP]{default} 显示Boss HP机能已设置为{olive}只有 HP 显示{default}。" + "zho" "{green}[BossHP]{default} 顯示Boss HP功能已設定為{olive}只有 HP 顯示{default}。" + } + "FullEnabledMsg" + { + "en" "{green}[BossHP]{default} Show Boss HP has been {olive}Enabled with Hitmarker{default}." + "jp" "{green}[BossHP]{default} ボスHP表示を {olive}有効 (ヒットマーカー付き){default} にしました." + "chi" "{green}[BossHP]{default} 显示Boss HP机能已被{olive}启用 (附带击中标记){default}。" + "zho" "{green}[BossHP]{default} 顯示Boss HP功能已被{olive}啟用 (附帶擊中標記){default}。" + } + "OnlyHitmarkerMsg" + { + "en" "{green}[BossHP]{default} Show Boss HP has been {olive}Set to only Hitmarker{default}." + "jp" "{green}[BossHP]{default} ボスHP表示を {olive}ヒットマーカーのみ{default} にしました." + "chi" "{green}[BossHP]{default} 显示Boss HP机能已设置为{olive}只有击中标记{default}。" + "zho" "{green}[BossHP]{default} 顯示Boss HP功能已設定為{olive}只有擊中標記{default}。" + } +} \ No newline at end of file diff --git a/BumpWeaponFix/gamedata/BumpWeaponFix.games.txt b/BumpWeaponFix/gamedata/BumpWeaponFix.games.txt new file mode 100644 index 0000000..3999a87 --- /dev/null +++ b/BumpWeaponFix/gamedata/BumpWeaponFix.games.txt @@ -0,0 +1,15 @@ +"Games" +{ + "csgo" + { + "Signatures" + { + "ValidateLineOfSight" + { + "library" "server" + "windows" "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\xF8\x00\x00\x00\x56\x8B\x75\x08" + "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x7C\x01\x00\x00\x8B\x5D\x0C\x8B\x75\x08" + } + } + } +} diff --git a/BumpWeaponFix/scripting/BumpWeaponFix.sp b/BumpWeaponFix/scripting/BumpWeaponFix.sp new file mode 100644 index 0000000..f140932 --- /dev/null +++ b/BumpWeaponFix/scripting/BumpWeaponFix.sp @@ -0,0 +1,47 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include + +Handle g_hValidateLineOfSight = null; + +public Plugin myinfo = { + name = "[CS:GO] BumpWeapon Fix", + author = "SHUFEN from POSSESSION.tokyo", + description = "Ignore to validate line-of-sight when pick up weapons", + version = "20190331", + url = "https://possession.tokyo" +}; + +public void OnPluginStart() { + Handle hGameConf = LoadGameConfigFile("BumpWeaponFix.games"); + if (hGameConf == null) { + SetFailState("Couldn't load BumpWeaponFix.games game config!"); + return; + } + + // ValidateLineOfSight + g_hValidateLineOfSight = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Bool, ThisPointer_CBaseEntity); + if (!g_hValidateLineOfSight) { + delete hGameConf; + SetFailState("Failed to setup detour for \"ValidateLineOfSight\""); + } + if (!DHookSetFromConf(g_hValidateLineOfSight, hGameConf, SDKConf_Signature, "ValidateLineOfSight")) { + delete hGameConf; + SetFailState("Failed to load \"ValidateLineOfSight\" signature from gamedata"); + } + DHookAddParam(g_hValidateLineOfSight, HookParamType_Int); + if (!DHookEnableDetour(g_hValidateLineOfSight, false, Detour_ValidateLineOfSight)) { + delete hGameConf; + SetFailState("Failed to detour \"ValidateLineOfSight\""); + } + + delete hGameConf; +} + +public MRESReturn Detour_ValidateLineOfSight(Address pThis, Handle hReturn, Handle hParams) { + DHookSetReturn(hReturn, true); + return MRES_ChangedOverride; +} \ No newline at end of file diff --git a/ButtonPresser/scripting/ButtonPresser.sp b/ButtonPresser/scripting/ButtonPresser.sp new file mode 100644 index 0000000..494ba75 --- /dev/null +++ b/ButtonPresser/scripting/ButtonPresser.sp @@ -0,0 +1,78 @@ +#include +#include +#include + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin:myinfo = +{ + name = "Button Presser", + description = "Notify admins when buttons are pressed", + author = "zaCade (lost the original source btw)", + version = "1.0", + url = "" +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + int iEntity = INVALID_ENT_REFERENCE; + + while ((iEntity = FindEntityByClassname(iEntity, "*")) != INVALID_ENT_REFERENCE) + { + OnEntityCreated(iEntity, ""); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnEntityCreated(int iEntity, const char[] sClassname) +{ + if (!IsValidEdict(iEntity)) + return; + + char sClassname2[64]; + GetEdictClassname(iEntity, sClassname2, sizeof(sClassname2)) + + if (StrEqual(sClassname2, "func_button") || StrEqual(sClassname2, "func_rot_button")) + SDKHook(iEntity, SDKHook_Use, OnPressed); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnPressed(int iEntity, int iActivator, int iCaller, UseType type, float fValue) +{ + if (IsValidClient(iActivator)) + { + char sSteamID[32]; + if (!GetClientAuthId(iActivator, AuthId_Steam2, sSteamID, sizeof(sSteamID))) + { + sSteamID = "UNKNOWN"; + } + char sTargetname[128]; + GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsValidClient(i) && CheckCommandAccess(i, "", ADMFLAG_GENERIC)) + PrintToConsole(i, "%N (%s) Pressed button: %s", iActivator, sSteamID, sTargetname); + } + } + return Plugin_Continue; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +stock int IsValidClient(int client, bool nobots = true) +{ + if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client))) + return false; + + return IsClientInGame(client); +} diff --git a/CSGOMovementUnlocker/gamedata/CSGOMovementUnlocker.games.txt b/CSGOMovementUnlocker/gamedata/CSGOMovementUnlocker.games.txt new file mode 100644 index 0000000..c811587 --- /dev/null +++ b/CSGOMovementUnlocker/gamedata/CSGOMovementUnlocker.games.txt @@ -0,0 +1,43 @@ +"Games" +{ + "csgo" + { + "Addresses" + { + "WalkMoveMaxSpeed" + { + "windows" + { + "signature" "CGameMovement::WalkMove" + } + "linux" + { + "signature" "CGameMovement::WalkMove" + } + } + } + + "Signatures" + { + "CGameMovement::WalkMove" + { + "library" "server" + "windows" "\xF3\x0F\x59\xC0\xF3\x0F\x59\xD2\xF3\x0F\x59\xC9\xF3\x0F\x58\xD0\xF3\x0F\x58\xD1\x0F\x28\xC2\xF3\x0F\x51\xC0\xF3\x0F\x5E\xD8" + "linux" "\xF3\x0F\x10\xF0\x0F\x28\xC6\xF3\x0F\x51\xC6\xF3\x0F\x5E\xE8" + } + } + "Offsets" + { + "CappingOffset" + { + "windows" "31" + "linux" "27" + } + "PatchBytes" + { + "windows" "42" + "linux" "15" + } + } + } +} diff --git a/CSGOMovementUnlocker/scripting/CSGOMovementUnlocker.sp b/CSGOMovementUnlocker/scripting/CSGOMovementUnlocker.sp new file mode 100644 index 0000000..fe0c578 --- /dev/null +++ b/CSGOMovementUnlocker/scripting/CSGOMovementUnlocker.sp @@ -0,0 +1,85 @@ +#pragma semicolon 1 +#include +#include + +new Address:g_iPatchAddress; +new g_iPatchRestore[100]; +new g_iPatchRestoreBytes; + +#define PLUGIN_VERSION "1.0" + +public Plugin:myinfo = +{ + name = "CS:GO Movement Unlocker", + author = "Peace-Maker", + description = "Removes max speed limitation from players on the ground. Feels like CS:S.", + version = PLUGIN_VERSION, + url = "http://www.wcfan.de/" +} + +public OnPluginStart() +{ + // Load the gamedata file. + new Handle:hGameConf = LoadGameConfigFile("CSGOMovementUnlocker.games"); + if(hGameConf == INVALID_HANDLE) + SetFailState("Can't find CSGOMovementUnlocker.games.txt gamedata."); + + // Get the address near our patch area inside CGameMovement::WalkMove + new Address:iAddr = GameConfGetAddress(hGameConf, "WalkMoveMaxSpeed"); + if(iAddr == Address_Null) + { + CloseHandle(hGameConf); + SetFailState("Can't find WalkMoveMaxSpeed address."); + } + + // Get the offset from the start of the signature to the start of our patch area. + new iCapOffset = GameConfGetOffset(hGameConf, "CappingOffset"); + if(iCapOffset == -1) + { + CloseHandle(hGameConf); + SetFailState("Can't find CappingOffset in gamedata."); + } + + // Move right in front of the instructions we want to NOP. + iAddr += Address:iCapOffset; + g_iPatchAddress = iAddr; + + // Get how many bytes we want to NOP. + g_iPatchRestoreBytes = GameConfGetOffset(hGameConf, "PatchBytes"); + if(g_iPatchRestoreBytes == -1) + { + CloseHandle(hGameConf); + SetFailState("Can't find PatchBytes in gamedata."); + } + CloseHandle(hGameConf); + + //PrintToServer("CGameMovement::WalkMove VectorScale(wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); ... at address %x", g_iPatchAddress); + + new iData; + for(new i=0;i +#include +#include +#include + +#include + +//#define Debug + +methodmap CPlayer < Basic { + public CPlayer(int client) { + Basic myclass = new Basic(); + myclass.SetInt("iClient", client); + myclass.SetHandle("hEntities", new ArrayList(1)); + myclass.SetBool("bForceRise", false); + return view_as(myclass); + } + + property int iClient { + public get() { + return this.GetInt("iClient"); + } + public set(int value) { + this.SetInt("iClient", value); + } + } + + property ArrayList hEntities { + public get() { + return view_as(this.GetHandle("hEntities")); + } + public set(ArrayList value) { + this.SetHandle("hEntities", value); + } + } + + property bool bForceRise { + public get() { + return this.GetBool("bForceRise"); + } + public set(bool value) { + this.SetBool("bForceRise", value); + } + } + + public void Dispose(bool disposemembers = true) { + if (disposemembers) { + ArrayList hEntities = this.hEntities; + delete hEntities; + } + delete this; + } +} + +enum { + SF_TRIGGER_ALLOW_CLIENTS = 0x01, // Players can fire this trigger + SF_TRIGGER_ALLOW_NPCS = 0x02, // NPCS can fire this trigger + SF_TRIGGER_ALLOW_PUSHABLES = 0x04, // Pushables can fire this trigger + SF_TRIGGER_ALLOW_PHYSICS = 0x08, // Physics objects can fire this trigger + SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS = 0x10, // *if* NPCs can fire this trigger, this flag means only player allies do so + SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES = 0x20, // *if* Players can fire this trigger, this flag means only players inside vehicles can + SF_TRIGGER_ALLOW_ALL = 0x40, // Everything can fire this trigger EXCEPT DEBRIS! + SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES = 0x200, // *if* Players can fire this trigger, this flag means only players outside vehicles can + SF_TRIG_PUSH_ONCE = 0x80, // trigger_push removes itself after firing once + SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER = 0x100, // if pushed object is player on a ladder, then this disengages them from the ladder (HL2only) + SF_TRIG_TOUCH_DEBRIS = 0x400, // Will touch physics debris objects + SF_TRIGGER_ONLY_NPCS_IN_VEHICLES = 0x800, // *if* NPCs can fire this trigger, only NPCs in vehicles do so (respects player ally flag too) + SF_TRIGGER_PUSH_USE_MASS = 0x1000, // Correctly account for an entity's mass (CTriggerPush::Touch used to assume 100Kg) +}; + +Handle hTriggerPushPassesTriggerFilters; + +ConVar g_hTriggerPushGround_Scale; +ConVar g_hTriggerPushRising_Scale; +ConVar g_hTriggerPushLagFixEnable; +float g_fTriggerPushGround_Scale; +float g_fTriggerPushRising_Scale; +bool g_bTriggerPushLagFixEnable; + +int iOffsVelocity; +int iOffsAbsVel; +int iOffsBaseVel; + +CPlayer g_aPlayers[MAXPLAYERS+1]; + +bool bLateLoad = false; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + RegPluginLibrary("CSGOPushFix"); + + bLateLoad = late; + return APLRes_Success; +} + +public Plugin myinfo = { + name = "Trigger_push Fix", + author = "uuz + PŠΣ™ SHUFEN + Based from Mev, George, & Blacky | Slidy & rio Edit", + description = "Removes lag from trigger_push and fixes of velocity bug", + version = "4.2", + url = "http://bbs.zombieden.cn/ & https://possession.jp & http://steamcommunity.com/id/mevv/ & http://steamcommunity.com/profiles/76561197975854215/ & http://steamcommunity.com/id/blaackyy/" +} + +public void OnPluginStart() { + iOffsVelocity = FindSendPropInfo("CBasePlayer", "m_vecVelocity[0]"); + if (iOffsVelocity == -1) { + SetFailState("[Trigger_push Fix] Could not find CBasePlayer::m_vecVelocity[0]"); + } + + Handle hGameConf = LoadGameConfigFile("CSGOPushFix.games"); + + StartPrepSDKCall(SDKCall_Entity); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "CBaseTrigger::PassesTriggerFilters"); + PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); + hTriggerPushPassesTriggerFilters = EndPrepSDKCall(); + + delete hGameConf; + + g_hTriggerPushGround_Scale = CreateConVar("triggerpushfix_ground_scale", "1.0", "Scale trigger_push Ground.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_hTriggerPushRising_Scale = CreateConVar("triggerpushfix_ground_rising", "1.0", "Scale trigger_push Rising.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_hTriggerPushLagFixEnable = CreateConVar("triggerpushlagfix_enable", "1", "Enables trigger push fix.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_hTriggerPushGround_Scale.AddChangeHook(OnConVarChanged); + g_hTriggerPushRising_Scale.AddChangeHook(OnConVarChanged); + g_hTriggerPushLagFixEnable.AddChangeHook(OnConVarChanged); + + if (bLateLoad) { + for (int client = 1; client <= MaxClients; client++) { + if (IsClientConnected(client)) { + if (IsClientInGame(client)) + OnClientPutInServer(client); + } + } + int entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, "trigger_push")) != INVALID_ENT_REFERENCE) { + OnEntityCreated(entity, "trigger_push"); + } + } +} + +public void OnConfigsExecuted() { + iOffsAbsVel = FindDataMapInfo(0, "m_vecAbsVelocity"); + if (iOffsAbsVel == -1) + SetFailState("[Trigger_push Fix] Could not find m_vecAbsVelocity"); + + iOffsBaseVel = FindDataMapInfo(0, "m_vecBaseVelocity"); + if (iOffsBaseVel == -1) + SetFailState("[Trigger_push Fix] Could not find m_vecBaseVelocity"); + + g_fTriggerPushGround_Scale = g_hTriggerPushGround_Scale.FloatValue; + g_fTriggerPushRising_Scale = g_hTriggerPushRising_Scale.FloatValue; + g_bTriggerPushLagFixEnable = g_hTriggerPushLagFixEnable.BoolValue; +} + +public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) { + if (convar == g_hTriggerPushGround_Scale) + g_fTriggerPushGround_Scale = StringToFloat(newValue); + else if (convar == g_hTriggerPushRising_Scale) + g_fTriggerPushRising_Scale = StringToFloat(newValue); + else if (convar == g_hTriggerPushLagFixEnable) + g_bTriggerPushLagFixEnable = view_as(StringToInt(newValue)); +} + +public void OnClientPutInServer(int client) { + if (!g_aPlayers[client]) + g_aPlayers[client] = new CPlayer(client); +} + +public void OnClientDisconnect(int client) { + if (g_aPlayers[client]) { + g_aPlayers[client].Dispose(); + g_aPlayers[client] = null; + } +} + +public void OnEntityCreated(int entity, const char[] classname) { + if (StrEqual(classname, "trigger_push", false)) { + SDKHook(entity, SDKHook_SpawnPost, OnEntitySpawn_Post); + } else { + for (int i = 1; i <= MaxClients; i++) + if (g_aPlayers[i]) { + ArrayList hEntities = g_aPlayers[i].hEntities; + int index = hEntities.FindValue(entity); + if (index > -1) + hEntities.Erase(index); + } + } +} + +public void OnEntityDestroyed(int entity) { + for (int i = 1; i <= MaxClients; i++) + if (g_aPlayers[i]) { + ArrayList hEntities = g_aPlayers[i].hEntities; + int index = hEntities.FindValue(entity); + if (index > -1) + hEntities.Erase(index); + } +} + +public void OnEntitySpawn_Post(int entity) { + float m_vecPushDir[3]; + GetEntPropVector(entity, Prop_Data, "m_vecPushDir", m_vecPushDir); + + float fPushSpeed = GetEntPropFloat(entity, Prop_Data, "m_flSpeed"); + if (fPushSpeed > 250) { + if (RoundToNearest(m_vecPushDir[2]) != 0) { + if (g_fTriggerPushRising_Scale != 1.0) + SetEntPropFloat(entity, Prop_Data, "m_flSpeed", fPushSpeed * g_fTriggerPushRising_Scale); + } else { + if (g_fTriggerPushGround_Scale != 1.0) + SetEntPropFloat(entity, Prop_Data, "m_flSpeed", fPushSpeed * g_fTriggerPushGround_Scale); + } + } + + SDKHook(entity, SDKHook_Touch, OnTouch); + SDKHook(entity, SDKHook_StartTouch, StartTouch); + SDKHook(entity, SDKHook_EndTouch, EndTouch); +} + +public Action OnTouch(int entity, int other) { + if (g_bTriggerPushLagFixEnable && 0 < other <= MaxClients) { + if (!PassesTriggerFilters(entity, other)) { + #if defined Debug + PrintToChatAll("push not pass: %i => Client %N", entity, other); + #endif + return Plugin_Handled; + } + + bool bDisabled = view_as(GetEntProp(entity, Prop_Data, "m_bDisabled")); + int iSpawnflags = GetEntProp(entity, Prop_Data, "m_spawnflags"); + + // dont move player if they're on ladder + if (GetEntityMoveType(other) == MOVETYPE_LADDER && !(iSpawnflags & SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER)) { + return Plugin_Handled; + } + + if (iSpawnflags == 1 && !bDisabled) { + #if defined Debug + float fGameTime = GetGameTime(); + PrintToChatAll("push pass: %i => Client %N @%.2f", entity, other, fGameTime); + #endif + DoPush(other, (iSpawnflags & SF_TRIG_PUSH_ONCE) ? entity : -1); + + return Plugin_Handled; + } + } + return Plugin_Continue; +} + +public Action StartTouch(int entity, int other) { + if (g_bTriggerPushLagFixEnable && 0 < other <= MaxClients && PassesTriggerFilters(entity, other)) { + if (g_aPlayers[other]) { + if (IsClientOnObject(other)) + g_aPlayers[other].bForceRise = true; + ArrayList hEntities = g_aPlayers[other].hEntities; + int index = hEntities.FindValue(entity); + if (index == -1) + hEntities.Push(entity); + } + #if defined Debug + PrintToChatAll("start touch: %i => Client %N [HammerID: %i]", entity, other, GetHammerIdOfEntity(entity)); + #endif + } + return Plugin_Continue; +} + +public Action EndTouch(int entity, int other) { + if (g_bTriggerPushLagFixEnable && 0 < other <= MaxClients && PassesTriggerFilters(entity, other)) { + if (g_aPlayers[other]) { + ArrayList hEntities = g_aPlayers[other].hEntities; + int index = hEntities.FindValue(entity); + if (index > -1) + hEntities.Erase(index); + } + #if defined Debug + PrintToChatAll("end touch: %i => Client %N [HammerID: %i]", entity, other, GetHammerIdOfEntity(entity)); + #endif + } + return Plugin_Continue; +} + +void DoPush(int other, int onceent = -1) { + float vecAbsDirTotal[3]; + + if (g_aPlayers[other]) { + ArrayList hEntities = g_aPlayers[other].hEntities; + for (int i = 0; i < hEntities.Length; i++) { + int entity = hEntities.Get(i); + + float m_vecPushDir[3], angRotation[3], fPushSpeed; + + fPushSpeed = GetEntPropFloat(entity, Prop_Data, "m_flSpeed"); + GetEntPropVector(entity, Prop_Data, "m_vecPushDir", m_vecPushDir); + + FindEntityParent_Main(entity, angRotation, false); + + // Rotate vector according to world + float sr, sp, sy, cr, cp, cy; + float matrix[3][4]; + + SinCos(DegToRad(angRotation[1]), sy, cy); + SinCos(DegToRad(angRotation[0]), sp, cp); + SinCos(DegToRad(angRotation[2]), sr, cr); + + matrix[0][0] = cp*cy; + matrix[1][0] = cp*sy; + matrix[2][0] = -sp; + + float crcy = cr*cy; + float crsy = cr*sy; + float srcy = sr*cy; + float srsy = sr*sy; + + matrix[0][1] = sp*srcy-crsy; + matrix[1][1] = sp*srsy+crcy; + matrix[2][1] = sr*cp; + + matrix[0][2] = (sp*crcy+srsy); + matrix[1][2] = (sp*crsy-srcy); + matrix[2][2] = cr*cp; + + matrix[0][3] = angRotation[0]; + matrix[1][3] = angRotation[1]; + matrix[2][3] = angRotation[2]; + + float vecAbsDir[3]; + vecAbsDir[0] = m_vecPushDir[0]*matrix[0][0] + m_vecPushDir[1]*matrix[0][1] + m_vecPushDir[2]*matrix[0][2]; + vecAbsDir[1] = m_vecPushDir[0]*matrix[1][0] + m_vecPushDir[1]*matrix[1][1] + m_vecPushDir[2]*matrix[1][2]; + vecAbsDir[2] = m_vecPushDir[0]*matrix[2][0] + m_vecPushDir[1]*matrix[2][1] + m_vecPushDir[2]*matrix[2][2]; + + ScaleVector(vecAbsDir, fPushSpeed); + + AddVectors(vecAbsDirTotal, vecAbsDir, vecAbsDirTotal); + } + } + + float newVelocity[3]; + + if (onceent != -1) { + for (int x = 0; x < 3; x++) { + newVelocity[x] = GetEntDataFloat(other, iOffsVelocity + (x * 4)); + } + AddVectors(newVelocity, vecAbsDirTotal, newVelocity); + + SetEntDataVector(other, iOffsAbsVel, newVelocity); + SetEntDataVector(other, iOffsBaseVel, NULL_VECTOR); + + if (vecAbsDirTotal[2] > 0.0) { + SetEntPropEnt(other, Prop_Data, "m_hGroundEntity", -1); + } + + AcceptEntityInput(onceent, "Kill"); // remove the trigger so it only applies once + + return; + } + + // Apply the base velocity directly to abs velocity + for (int x = 0; x < 3; x++) { + newVelocity[x] = GetEntDataFloat(other, iOffsVelocity + (x * 4)); + } + + newVelocity[2] += vecAbsDirTotal[2] * GetTickInterval() * GetEntPropFloat(other, Prop_Data, "m_flLaggedMovementValue"); // frametime = tick_interval * laggedmovementvalue + + if (g_aPlayers[other].bForceRise) { + if (IsClientOnObject(other) && 0.0 < newVelocity[2] <= 250.0) { + newVelocity[2] = 250.1; + #if defined Debug + PrintToChatAll("force rise: Client %N", other); + #endif + } + g_aPlayers[other].bForceRise = false; + } + + SetEntDataVector(other, iOffsAbsVel, newVelocity); + SetEntDataVector(other, iOffsBaseVel, NULL_VECTOR); + + // apply x, y as a base velocity so we travel at constant speed on conveyors + // Remove the base velocity z height so abs velocity can do it and add old base velocity if there is any + vecAbsDirTotal[2] = 0.0; + + if (GetEntityFlags(other) & FL_BASEVELOCITY) { + float vecBaseVel[3]; + GetEntDataVector(other, iOffsBaseVel, vecBaseVel); + AddVectors(vecAbsDirTotal, vecBaseVel, vecAbsDirTotal); + } + + SetEntDataVector(other, iOffsBaseVel, vecAbsDirTotal); + SetEntityFlags(other, GetEntityFlags(other) | FL_BASEVELOCITY); +} + +stock int FindEntityParent_Main(int entity, float Ang[3], bool inloop) { + int TempEntity; + float TempAng[3]; + + if (!inloop) + GetEntPropVector(entity, Prop_Data, "m_angRotation", Ang); + + TempEntity = GetEntPropEnt(entity, Prop_Data, "m_hParent"); + if (TempEntity == -1) { + if (!inloop) + return -1; + else + return entity; + } + + GetEntPropVector(TempEntity, Prop_Data, "m_angRotation", TempAng); + AddVectors(Ang, TempAng, Ang); + + if (Ang[1] > 360.0) + Ang[1] -= 360.0; + + return FindEntityParent_Main(TempEntity, Ang, true); +} + +stock void SinCos(float radians, float &sine, float &cosine) { + sine = Sine(radians); + cosine = Cosine(radians); +} + +/** + * Return whether a client is standing on an object + * + * @param Client index + * @return True if client is standing on an object. False otherwise. + */ +stock bool IsClientOnObject(int client) { + return GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") > -1 ? true : false; +} + +stock int GetHammerIdOfEntity(int entity) { + if (IsValidEntity(entity)) { + return GetEntProp(entity, Prop_Data, "m_iHammerID"); + } + return -1; +} + +stock bool PassesTriggerFilters(int entity, int client) { + return SDKCall(hTriggerPushPassesTriggerFilters, entity, client); +} \ No newline at end of file diff --git a/CSGO_ParticleSystemFix/gamedata/CSGO_ParticleSystemFix.games.txt b/CSGO_ParticleSystemFix/gamedata/CSGO_ParticleSystemFix.games.txt new file mode 100644 index 0000000..de9803c --- /dev/null +++ b/CSGO_ParticleSystemFix/gamedata/CSGO_ParticleSystemFix.games.txt @@ -0,0 +1,93 @@ +"Games" +{ + "#default" + { + "#supported" + { + "game" "csgo" + } + + "Keys" + { + "VEngineServerStringTable" "VEngineServerStringTable001" + } + + "Offsets" + { + "CServer::OS" + { + "windows" "1" + "linux" "2" + } + "CParticleSystemDictionary::Count" // Str: "PrecacheStandardParticleSystems()" + { + "windows" "38" + "linux" "38" + } + // @link https://github.com/VSES/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/particles/particles.cpp#L2659 + "CNetworkStringTableContainer::FindTable" + { + "windows" "3" + "linux" "4" + } + } + + // Sigs from the lib ( https://forums.alliedmods.net/showthread.php?t=309074 ) + // You can update them only by yourself using tutorial in the link + "Signatures" + { + "CreateInterface" + { + "library" "engine" + "windows" "@CreateInterface" + "linux" "@CreateInterface" + "mac" "@CreateInterface" + } + + /* + * Info: Every custom particle precached during a map is not removed from precache table on the map end. + * Then, if a lot of maps that uses custom particles are running in my server the precache table will be filled + * and the custom particles used on the next maps will not show right but erros will appear in their place. + */ + "CParticleSystemDictionary::~CParticleSystemDictionary" // Str: "CParticleSystemMgr::InitAttributeTable has an out-of-date attribute list! (element %d not set up)\n" | "CParticleSystemMgr::InitAttributeTable" -> "CParticleSystemMgr::CParticleSystemMgr" -> "CParticleSystemMgr::~CParticleSystemMgr" + { + "library" "server" + "windows" "\x55\x8B\xEC\x51\x56\x57\x8B\xF9\x33\xF6\x8B\x47\x58" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x2C\x8B\x7D\x08\x8B\x47\x58\x85\xC0" + } + "CParticleSystemDefinition::ParseChildren" // Str: "DmeParticleSystemDefinition" and "children" and "preventNameBasedLookup" + { + "library" "server" + "windows" "\x55\x8B\xEC\x83\xEC\x20\x53\x56\x57\x8B\xF9\x51" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x2C\x8B\x45\x0C\xC7\x44\x24\x04" + } + "CNetworkStringTable::DeleteAllStrings" // Str: "___clientsideitemsplaceholder0___" + { + "library" "engine" + "windows" "\x56\x8B\xF1\x57\x8B\x4E\x40" + "linux" "\x55\x89\xE5\x53\x83\xEC\x14\x8B\x5D\x08\x8B\x43\x40" + } + } + + // Addr from the lib ( https://forums.alliedmods.net/showthread.php?t=309074 ) + // You can update them only by yourself using tutorial in the link + "Addresses" + { + "m_pParticleSystemDictionary" + { + "signature" "CParticleSystemDefinition::ParseChildren" + "linux" + { + "read" "375" + "read" "0" + "read" "140" + } + "windows" + { + "read" "401" + "read" "0" + } + } + } + } +} \ No newline at end of file diff --git a/CSGO_ParticleSystemFix/scripting/CSGO_ParticleSystemFix.sp b/CSGO_ParticleSystemFix/scripting/CSGO_ParticleSystemFix.sp new file mode 100644 index 0000000..38ee0ec --- /dev/null +++ b/CSGO_ParticleSystemFix/scripting/CSGO_ParticleSystemFix.sp @@ -0,0 +1,468 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include + +#define PLATFORM_LINE_LENGTH 1024 +#define NORMAL_LINE_LENGTH 256 +#define PLUGIN_CONFIG "CSGO_ParticleSystemFix.games" +#define PLUGIN_LOGFILE "logs/CSGO_ParticleSystemFix.log" + +/** + * @section List of operation systems. + **/ +enum EngineOS { + OS_Unknown, + OS_Windows, + OS_Linux +}; + +/** + * @section Struct of operation types for server arrays. + **/ +enum struct ServerData +{ + /* Internal Particles */ + ArrayList Particles; + + /* OS */ + EngineOS Platform; + + /* Gamedata */ + Handle Config; +} +/** + * @endsection + **/ + +ServerData gServerData; + +/** + * Variables to store SDK calls handlers. + **/ +Handle hSDKCallDestructorParticleDictionary; +Handle hSDKCallTableDeleteAllStrings; + +/** + * Variables to store virtual SDK offsets. + **/ +Address pParticleSystemDictionary; +int ParticleSystem_Count; + +char g_sLogPath[PLATFORM_MAX_PATH]; + +public Plugin myinfo = { + name = "[CS:GO] Particle System Fix", + description = "", + author = "gubka && PŠΣ™ SHUFEN", + version = "1.0", + url = "https://forums.alliedmods.net/showthread.php?t=313951 && https://possession.jp" +}; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + RegPluginLibrary("CSGO_ParticleSystemFix"); + CreateNative("UncacheAllParticleSystems", Native_UncacheAllParticleSystems); + return APLRes_Success; +} + +public int Native_UncacheAllParticleSystems(Handle plugin, int numParams) { + ParticlesOnPurge(); +} + +/** + * @brief Particles module init function. + **/ +public void OnPluginStart() { + BuildPath(Path_SM, g_sLogPath, sizeof(g_sLogPath), PLUGIN_LOGFILE); + + if (GetEngineVersion() != Engine_CSGO) { + LogToFileEx(g_sLogPath, "[System Init] Engine error: This plugin only works on Counter-Strike: Global Offensive."); + return; + } + + gServerData.Config = LoadGameConfigFile(PLUGIN_CONFIG); + + /*_________________________________________________________________________________________________________________________________________*/ + + // Starts the preparation of an SDK call + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(gServerData.Config, SDKConf_Signature, "CParticleSystemDictionary::~CParticleSystemDictionary"); + + // Validate call + if ((hSDKCallDestructorParticleDictionary = EndPrepSDKCall()) == null) { + // Log failure + LogToFileEx(g_sLogPath, "[GameData Validation] Failed to load SDK call \"CParticleSystemDictionary::~CParticleSystemDictionary\". Update signature in \"%s\"", PLUGIN_CONFIG); + return; + } + + /*_________________________________________________________________________________________________________________________________________*/ + + // Starts the preparation of an SDK call + StartPrepSDKCall(SDKCall_Raw); + PrepSDKCall_SetFromConf(gServerData.Config, SDKConf_Signature, "CNetworkStringTable::DeleteAllStrings"); + + // Validate call + if ((hSDKCallTableDeleteAllStrings = EndPrepSDKCall()) == null) { + // Log failure + LogToFileEx(g_sLogPath, "[GameData Validation] Failed to load SDK call \"CNetworkStringTable::DeleteAllStrings\". Update signature in \"%s\"", PLUGIN_CONFIG); + return; + } + + /*_________________________________________________________________________________________________________________________________________*/ + + // Load other offsets + fnInitGameConfAddress(gServerData.Config, pParticleSystemDictionary, "m_pParticleSystemDictionary"); + fnInitGameConfOffset(gServerData.Config, ParticleSystem_Count, "CParticleSystemDictionary::Count"); + + /*_________________________________________________________________________________________________________________________________________*/ + + //fnInitGameConfOffset(gServerData.Config, view_as(gServerData.Platform), "CServer::OS"); + LogToFileEx(g_sLogPath, "[System Init] Loaded \"Particle System Fix\""); +} + +public void OnMapStart() { + // Precache CS:GO internal particles + ParticlesOnLoad(); + + // Load map particles + LoadMapExtraFiles(); +} + +public void OnMapEnd() { + ParticlesOnPurge(); +} + +/** + * @brief Particles module load function. + **/ +void ParticlesOnLoad() { + // Initialize buffer char + static char sBuffer[PLATFORM_LINE_LENGTH]; + + // Validate that particles wasn't precache yet + bool bSave = LockStringTables(false); + Address pTable = CNetworkStringTableContainer_FindTable("ParticleEffectNames"); + int iCount = LoadFromAddress(pParticleSystemDictionary + view_as
(ParticleSystem_Count), NumberType_Int16); + if (pTable != Address_Null && !iCount) { /// Validate that table is exist and it empty + // Opens the file + File hFile = OpenFile("particles/particles_manifest.txt", "rt", true); + + // If doesn't exist stop + if (hFile == null) { + LogToFileEx(g_sLogPath, "[Config Validation] Error opening file: \"particles/particles_manifest.txt\""); + return; + } + + // Read lines in the file + while (hFile.ReadLine(sBuffer, sizeof(sBuffer))) { + // Checks if string has correct quotes + int iQuotes = CountCharInString(sBuffer, '"'); + if (iQuotes == 4) { + // Trim string + TrimString(sBuffer); + + // Copy value string + strcopy(sBuffer, sizeof(sBuffer), sBuffer[strlen("\"file\"")]); + + // Trim string + TrimString(sBuffer); + + // Strips a quote pair off a string + StripQuotes(sBuffer); + + // Precache model + int i = 0; if (sBuffer[i] == '!') i++; + PrecacheGeneric(sBuffer[i], true); + SDKCall(hSDKCallTableDeleteAllStrings, pTable); /// HACK~HACK + /// Clear tables after each file because some of them contains + /// huge amount of particles and we work around the limit + } + } + + delete hFile; + } + + // Initialize the table index + static int tableIndex = INVALID_STRING_TABLE; + + // Validate table + if (tableIndex == INVALID_STRING_TABLE) { + // Searches for a string table + tableIndex = FindStringTable("ParticleEffectNames"); + } + + // If array hasn't been created, then create + if (gServerData.Particles == null) { + // Initialize a particle list array + gServerData.Particles = CreateArray(NORMAL_LINE_LENGTH); + + // i = table string + iCount = GetStringTableNumStrings(tableIndex); + for (int i = 0; i < iCount; i++) { + // Gets the string at a given index + ReadStringTable(tableIndex, i, sBuffer, sizeof(sBuffer)); + + // Push data into array + gServerData.Particles.PushString(sBuffer); + } + } else { + // i = particle name + iCount = gServerData.Particles.Length; + for (int i = 0; i < iCount; i++) { + // Gets the string at a given index + gServerData.Particles.GetString(i, sBuffer, sizeof(sBuffer)); + + // Push data into table + AddToStringTable(tableIndex, sBuffer); + } + } + + /* + // Refresh tables + pTable = CNetworkStringTableContainer_FindTable("ExtraParticleFilesTable"); + if (pTable != Address_Null) { + SDKCall(hSDKCallTableDeleteAllStrings, pTable); + } + + pTable = CNetworkStringTableContainer_FindTable("genericprecache"); + if (pTable != Address_Null) { + SDKCall(hSDKCallTableDeleteAllStrings, pTable); + } + */ + LockStringTables(bSave); +} + +void LoadMapExtraFiles() { + char sMapName[PLATFORM_MAX_PATH]; + GetCurrentMap(sMapName, sizeof(sMapName)); + + LoadPerMapParticleManifest(sMapName); +} + +void LoadPerMapParticleManifest(const char[] sMapName) { + KeyValues hKv = GetParticleManifestKv(sMapName); + if (hKv == null) { + return; + } + + ParseParticleManifestKv(hKv, true, true); + + delete hKv; +} + +char sDirExt[][][] = { { + "particles/", "maps/", "particles/", "maps/" + }, { + "_manifest_override.txt", "_particles_override.txt", "_manifest.txt", "_particles.txt" + } +}; + +KeyValues GetParticleManifestKv(const char[] sMapName) { + char sFileName[PLATFORM_MAX_PATH]; + + for (int i = 0; i < sizeof(sDirExt[]); i++) { + Format(sFileName, sizeof(sFileName), "%s%s%s", sDirExt[0][i], sMapName, sDirExt[1][i]); + if (FileExists(sFileName, true)) { + if (FileExists(sFileName, false)) + AddFileToDownloadsTable(sFileName); + + return CreateManifestKv(sFileName); + } + } + + return null; +} + +KeyValues CreateManifestKv(const char[] sPath) { + KeyValues hKv = CreateKeyValues("particles_manifest"); + + //hKv.SetEscapeSequences(true); + if (hKv.ImportFromFile(sPath)) { + return hKv; + } + + return null; +} + +void ParseParticleManifestKv(KeyValues hKv, bool perMap = false, bool awayPreloads = false) { + char sPCF[PLATFORM_MAX_PATH]; + if (hKv.GotoFirstSubKey(false)) + do { + hKv.GetString(NULL_STRING, sPCF, sizeof(sPCF)); + if (sPCF[0] == '\0') + continue; + + bool preload = FindCharInString(sPCF, '!') == 0; + if (preload) + strcopy(sPCF, sizeof(sPCF), sPCF[1]); + if (FileExists(sPCF, true)) { + if (perMap && FileExists(sPCF, false)) + AddFileToDownloadsTable(sPCF); + PrecacheGeneric(sPCF, awayPreloads || preload); + } + } while (hKv.GotoNextKey(false)); +} + +/** + * @brief Particles module purge function. + **/ +void ParticlesOnPurge() { + // @link https://github.com/VSES/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/particles/particles.cpp#L81 + SDKCall(hSDKCallDestructorParticleDictionary, pParticleSystemDictionary); + + /*_________________________________________________________________________________________________________________________________________*/ + + // Clear particles in the effect table + bool bSave = LockStringTables(false); + Address pTable = CNetworkStringTableContainer_FindTable("ParticleEffectNames"); + if (pTable != Address_Null) { + SDKCall(hSDKCallTableDeleteAllStrings, pTable); + } + + // Clear particles in the extra effect table + pTable = CNetworkStringTableContainer_FindTable("ExtraParticleFilesTable"); + if (pTable != Address_Null) { + SDKCall(hSDKCallTableDeleteAllStrings, pTable); + } + + // Clear particles in the generic precache table + pTable = CNetworkStringTableContainer_FindTable("genericprecache"); + if (pTable != Address_Null) { + SDKCall(hSDKCallTableDeleteAllStrings, pTable); + } + LockStringTables(bSave); +} + +stock Address CNetworkStringTableContainer_FindTable(const char[] tableName) { + Address pNetworkstringtable = GetNetworkStringTableAddr(); + if (pNetworkstringtable == Address_Null) + return Address_Null; + + static Handle hFindTable = INVALID_HANDLE; + if (hFindTable == INVALID_HANDLE) { + if (gServerData.Config == INVALID_HANDLE) + return Address_Null; + + StartPrepSDKCall(SDKCall_Raw); + if (!PrepSDKCall_SetFromConf(gServerData.Config, SDKConf_Virtual, "CNetworkStringTableContainer::FindTable")) { + LogToFileEx(g_sLogPath, "[Find Table] Cant find the method CNetworkStringTableContainer::FindTable."); + return Address_Null; + } + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); // tableName + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + hFindTable = EndPrepSDKCall(); + if (hFindTable == INVALID_HANDLE) { + LogToFileEx(g_sLogPath, "[Find Table] Method CNetworkStringTableContainer::FindTable was not loaded right."); + return Address_Null; + } + } + + return SDKCall(hFindTable, pNetworkstringtable, tableName); +} + +stock Address GetNetworkStringTableAddr() { + static Address pEngineServerStringTable = Address_Null; + if (pEngineServerStringTable == Address_Null) { + if (gServerData.Config == null) + return Address_Null; + + char sInterfaceName[64]; + if (!GameConfGetKeyValue(gServerData.Config, "VEngineServerStringTable", sInterfaceName, sizeof(sInterfaceName))) + strcopy(sInterfaceName, sizeof(sInterfaceName), "VEngineServerStringTable001"); + pEngineServerStringTable = CreateEngineInterface(sInterfaceName); + } + + return pEngineServerStringTable; +} + +stock Address CreateEngineInterface(const char[] sInterfaceKey, Address ptr = Address_Null) { + static Handle hCreateInterface = null; + if (hCreateInterface == null) { + if (gServerData.Config == null) + return Address_Null; + + StartPrepSDKCall(SDKCall_Static); + if (!PrepSDKCall_SetFromConf(gServerData.Config, SDKConf_Signature, "CreateInterface")) { + LogToFileEx(g_sLogPath, "[Create Engine Interface] Failed to get CreateInterface"); + return Address_Null; + } + + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain, VDECODE_FLAG_ALLOWNULL); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + + hCreateInterface = EndPrepSDKCall(); + if (hCreateInterface == null) { + LogToFileEx(g_sLogPath, "[Create Engine Interface] Function CreateInterface was not loaded right."); + return Address_Null; + } + } + + if (gServerData.Config == null) + return Address_Null; + + char sInterfaceName[64]; + if (!GameConfGetKeyValue(gServerData.Config, sInterfaceKey, sInterfaceName, sizeof(sInterfaceName))) + strcopy(sInterfaceName, sizeof(sInterfaceName), sInterfaceKey); + + Address addr = SDKCall(hCreateInterface, sInterfaceName, ptr); + if (addr == Address_Null) { + LogToFileEx(g_sLogPath, "[Create Engine Interface] Failed to get pointer to interface %s(%s)", sInterfaceKey, sInterfaceName); + return Address_Null; + } + + return addr; +} + +/** + * @brief Finds the amount of all occurrences of a character in a string. + * + * @param sBuffer Input string buffer. + * @param cSymbol The character to search for. + * @return The amount of characters in the string, or -1 if the characters were not found. + */ +int CountCharInString(char[] sBuffer, char cSymbol) { + // Initialize index + int iCount; + + // i = char index + int iLen = strlen(sBuffer); + for (int i = 0; i < iLen; i++) { + // Validate char + if (sBuffer[i] == cSymbol) { + // Increment amount + iCount++; + } + } + + // Return amount + return iCount ? iCount : -1; +} + +/** + * @brief Returns an offset value from a given config. + * + * @param gameConf The game config handle. + * @param iOffset An offset, or -1 on failure. + * @param sKey Key to retrieve from the offset section. + **/ +stock void fnInitGameConfOffset(Handle gameConf, int &iOffset, char[] sKey) { + // Validate offset + if ((iOffset = GameConfGetOffset(gameConf, sKey)) == -1) { + LogToFileEx(g_sLogPath, "[GameData Validation] Failed to get offset: \"%s\"", sKey); + } +} + +/** + * @brief Returns an address value from a given config. + * + * @param gameConf The game config handle. + * @param pAddress An address, or null on failure. + * @param sKey Key to retrieve from the address section. + **/ +stock void fnInitGameConfAddress(Handle gameConf, Address &pAddress, char[] sKey) { + // Validate address + if ((pAddress = GameConfGetAddress(gameConf, sKey)) == Address_Null) { + LogToFileEx(g_sLogPath, "[GameData Validation] Failed to get address: \"%s\"", sKey); + } +} \ No newline at end of file diff --git a/ChatTags/Instructions.txt b/ChatTags/Instructions.txt new file mode 100644 index 0000000..8919c48 --- /dev/null +++ b/ChatTags/Instructions.txt @@ -0,0 +1,14 @@ +A few explanations: +You will need to set up a database config in addons/sourcemod/configs/databases.cfg. There is a file (db info to add.txt) with example setups for either mysql or sqlite. The plugin supports both. + +ChatTags_blocked.cfg: Each line in this file is a separate sub-phrase that is blocked for all players who do not have the configured admin flag. e.g. if "owner" is a line, then "owner", "growner", or even "addwdaownercqdq" are blocked for anyone w/o the admin flag. (non-case sensitive). + +ChatTags_colors_csgo.cfg, ChatTags_colors_ins.cfg and ChatTags_colors.cfg: ChatTags_colors_csgo is for csgo, ChatTags_colors_ins.cfg is for insurgency, and the other is for all other games. No need to install the ones that dont apply. For ChatTags_colors.cfg, you can add as many hex colors as you want (http://www.colorpicker.com/ is a good site to make them). The cs:go one has no other colors available (well, there is ONE more, but it causes formatting issues, so it is hidden). The insurgency just has 7 colors at this time. + +Similar to the config files, there are translation files for several games. While it doesnt hurt to add them all, the only one that will be used is the one matching your game (e.g. scp.cstrike.phrases.txt for CS:S, or scp.csgo.phrases.txt for CS:GO). + +ChatTags_groups.cfg is the main "setup" config other than the cvar file that is generated. Be sure to read the info at the top for a full explanation of all the ways it can be used/configured. + +The web stuff is in its own folder and has further instructions inside. + +If you have more questions or any issues, let me know! \ No newline at end of file diff --git a/ChatTags/configs/ChatTags_blocked.cfg b/ChatTags/configs/ChatTags_blocked.cfg new file mode 100644 index 0000000..aa6b5a2 --- /dev/null +++ b/ChatTags/configs/ChatTags_blocked.cfg @@ -0,0 +1,4 @@ +Founder +Admin +console +Shop \ No newline at end of file diff --git a/ChatTags/configs/ChatTags_colors.cfg b/ChatTags/configs/ChatTags_colors.cfg new file mode 100644 index 0000000..c088c7c --- /dev/null +++ b/ChatTags/configs/ChatTags_colors.cfg @@ -0,0 +1,783 @@ +"Tag Colors" +{ + "1" + { + "name" "Alice Blue" + "color" "#F0F8FF" + } + "2" + { + "name" "Allies" + "color" "#4D7942" + } + "3" + { + "name" "Antique White" + "color" "#FAEBD7" + } + "4" + { + "name" "Aqua" + "color" "#00FFFF" + } + "5" + { + "name" "Aquamarine" + "color" "#7FFFD4" + } + "6" + { + "name" "Axis" + "color" "#FF4040" + } + "7" + { + "name" "Azure" + "color" "#007FFF" + } + "8" + { + "name" "Beige" + "color" "#F5F5DC" + } + "9" + { + "name" "Bisque" + "color" "#FFE4C4" + } + "10" + { + "name" "Black" + "color" "#000000" + } + "11" + { + "name" "Blanched Almond" + "color" "#FFEBCD" + } + "12" + { + "name" "Blue" + "color" "#99CCFF" + } + "13" + { + "name" "Blue Violet" + "color" "#8A2BE2" + } + "14" + { + "name" "Brown" + "color" "#A52A2A" + } + "15" + { + "name" "Burlywood" + "color" "#DEB887" + } + "16" + { + "name" "Cadet Blue" + "color" "#5F9EA0" + } + "17" + { + "name" "Chartreuse" + "color" "#7FFF00" + } + "18" + { + "name" "Chocolate" + "color" "#D2691E" + } + "19" + { + "name" "Community" + "color" "#70B04A" + } + "20" + { + "name" "Coral" + "color" "#FF7F50" + } + "21" + { + "name" "Cornflower Blue" + "color" "#6495ED" + } + "22" + { + "name" "Cornsilk" + "color" "#FFF8DC" + } + "23" + { + "name" "Crimson" + "color" "#DC143C" + } + "24" + { + "name" "Cyan" + "color" "#00FFFF" + } + "25" + { + "name" "Dark Blue" + "color" "#00008B" + } + "26" + { + "name" "Dark Cyan" + "color" "#008B8B" + } + "27" + { + "name" "Dark Golden Rod" + "color" "#B8860B" + } + "29" + { + "name" "Dark Grey" + "color" "#A9A9A9" + } + "30" + { + "name" "Dark Green" + "color" "#006400" + } + "31" + { + "name" "Dark Khaki" + "color" "#BDB76B" + } + "32" + { + "name" "Dark Magenta" + "color" "#8B008B" + } + "33" + { + "name" "Dark Olive Green" + "color" "#556B2F" + } + "34" + { + "name" "Dark Orange" + "color" "#FF8C00" + } + "35" + { + "name" "Dark Orchid" + "color" "#9932CC" + } + "36" + { + "name" "Dark Red" + "color" "#8B0000" + } + "37" + { + "name" "Dark Salmon" + "color" "#E9967A" + } + "38" + { + "name" "Dark Sea Green" + "color" "#8FBC8F" + } + "39" + { + "name" "Dark Slate Blue" + "color" "#483D8B" + } + "41" + { + "name" "Dark Slate Grey" + "color" "#2F4F4F" + } + "42" + { + "name" "Dark Turquoise" + "color" "#00CED1" + } + "43" + { + "name" "Dark Violet" + "color" "#9400D3" + } + "44" + { + "name" "Deep Pink" + "color" "#FF1493" + } + "45" + { + "name" "Deep Sky Blue" + "color" "#00BFFF" + } + "46" + { + "name" "Dim Grey" + "color" "#696969" + } + "48" + { + "name" "Dodger Blue" + "color" "#1E90FF" + } + "49" + { + "name" "Firebrick" + "color" "#B22222" + } + "50" + { + "name" "Floral White" + "color" "#FFFAF0" + } + "51" + { + "name" "Forest Green" + "color" "#228B22" + } + "52" + { + "name" "Fuchsia" + "color" "#FF00FF" + } + "53" + { + "name" "Full Blue" + "color" "#0000FF" + } + "54" + { + "name" "Full Red" + "color" "#FF0000" + } + "55" + { + "name" "Gainsboro" + "color" "#DCDCDC" + } + "56" + { + "name" "Genuine" + "color" "#4D7455" + } + "57" + { + "name" "Ghost White" + "color" "#F8F8FF" + } + "58" + { + "name" "Gold" + "color" "#FFD700" + } + "59" + { + "name" "Goldenrod" + "color" "#DAA520" + } + "60" + { + "name" "Gray" + "color" "#CCCCCC" + } + "61" + { + "name" "Green" + "color" "#3EFF3E" + } + "62" + { + "name" "Green Yellow" + "color" "#ADFF2F" + } + "63" + { + "name" "Haunted" + "color" "#38F3AB" + } + "64" + { + "name" "Honeydew" + "color" "#F0FFF0" + } + "65" + { + "name" "Hot Pink" + "color" "#FF69B4" + } + "66" + { + "name" "Indian Red" + "color" "#CD5C5C" + } + "67" + { + "name" "Indigo" + "color" "#4B0082" + } + "68" + { + "name" "Ivory" + "color" "#FFFFF0" + } + "69" + { + "name" "Khaki" + "color" "#F0E68C" + } + "70" + { + "name" "Lavender" + "color" "#E6E6FA" + } + "71" + { + "name" "Lavender Blush" + "color" "#FFF0F5" + } + "72" + { + "name" "Lawn Green" + "color" "#7CFC00" + } + "73" + { + "name" "Lemon Chiffon" + "color" "#FFFACD" + } + "74" + { + "name" "Light Blue" + "color" "#ADD8E6" + } + "75" + { + "name" "Light Coral" + "color" "#F08080" + } + "76" + { + "name" "Light Cyan" + "color" "#E0FFFF" + } + "77" + { + "name" "Light Goldenrod Yellow" + "color" "#FAFAD2" + } + "78" + { + "name" "Light Grey" + "color" "#D3D3D3" + } + "80" + { + "name" "Light Green" + "color" "#99FF99" + } + "81" + { + "name" "Light Pink" + "color" "#FFB6C1" + } + "82" + { + "name" "Light Salmon" + "color" "#FFA07A" + } + "83" + { + "name" "Light Sea Green" + "color" "#20B2AA" + } + "84" + { + "name" "Light Sky Blue" + "color" "#87CEFA" + } + "85" + { + "name" "Light Slate Grey" + "color" "#778899" + } + "86" + { + "name" "Light Slate Grey" + "color" "#778899" + } + "87" + { + "name" "Light Steel Blue" + "color" "#B0C4DE" + } + "88" + { + "name" "Light Yellow" + "color" "#FFFFE0" + } + "89" + { + "name" "Lime" + "color" "#00FF00" + } + "90" + { + "name" "Lime Green" + "color" "#32CD32" + } + "91" + { + "name" "Linen" + "color" "#FAF0E6" + } + "92" + { + "name" "Magenta" + "color" "#FF00FF" + } + "93" + { + "name" "Maroon" + "color" "#800000" + } + "94" + { + "name" "Medium Aquamarine" + "color" "#66CDAA" + } + "95" + { + "name" "Medium Blue" + "color" "#0000CD" + } + "96" + { + "name" "Medium Orchid" + "color" "#BA55D3" + } + "97" + { + "name" "Medium Purple" + "color" "#9370D8" + } + "98" + { + "name" "Mediumsea Green" + "color" "#3CB371" + } + "99" + { + "name" "Mediumslate Blue" + "color" "#7B68EE" + } + "100" + { + "name" "Medium Spring Green" + "color" "#00FA9A" + } + "101" + { + "name" "Medium Turquoise" + "color" "#48D1CC" + } + "102" + { + "name" "Medium Violet Red" + "color" "#C71585" + } + "103" + { + "name" "Midnight Blue" + "color" "#191970" + } + "104" + { + "name" "Mint Cream" + "color" "#F5FFFA" + } + "105" + { + "name" "Misty Rose" + "color" "#FFE4E1" + } + "106" + { + "name" "Moccasin" + "color" "#FFE4B5" + } + "107" + { + "name" "Navajo White" + "color" "#FFDEAD" + } + "108" + { + "name" "Navy" + "color" "#000080" + } + "109" + { + "name" "Normal" + "color" "#B2B2B2" + } + "110" + { + "name" "Old Lace" + "color" "#FDF5E6" + } + "111" + { + "name" "Olive" + "color" "#9EC34F" + } + "112" + { + "name" "Olive Drab" + "color" "#6B8E23" + } + "113" + { + "name" "Orange" + "color" "#FFA500" + } + "114" + { + "name" "Orange Red" + "color" "#FF4500" + } + "115" + { + "name" "Orchid" + "color" "#DA70D6" + } + "116" + { + "name" "Pale Goldenrod" + "color" "#EEE8AA" + } + "117" + { + "name" "Pale Green" + "color" "#98FB98" + } + "118" + { + "name" "Pale Turquoise" + "color" "#AFEEEE" + } + "119" + { + "name" "Pale Violet Red" + "color" "#D87093" + } + "120" + { + "name" "Papaya Whip" + "color" "#FFEFD5" + } + "121" + { + "name" "Peach Puff" + "color" "#FFDAB9" + } + "122" + { + "name" "Peru" + "color" "#CD853F" + } + "123" + { + "name" "Pink" + "color" "#FFC0CB" + } + "124" + { + "name" "Plum" + "color" "#DDA0DD" + } + "125" + { + "name" "Powder Blue" + "color" "#B0E0E6" + } + "126" + { + "name" "Purple" + "color" "#800080" + } + "127" + { + "name" "Red" + "color" "#FF4040" + } + "128" + { + "name" "Rosybrown" + "color" "#BC8F8F" + } + "129" + { + "name" "Royal Blue" + "color" "#4169E1" + } + "130" + { + "name" "Saddle Brown" + "color" "#8B4513" + } + "131" + { + "name" "Salmon" + "color" "#FA8072" + } + "132" + { + "name" "Sandy Brown" + "color" "#F4A460" + } + "133" + { + "name" "Sea Green" + "color" "#2E8B57" + } + "134" + { + "name" "Seashell" + "color" "#FFF5EE" + } + "135" + { + "name" "Selfmade" + "color" "#70B04A" + } + "136" + { + "name" "Sienna" + "color" "#A0522D" + } + "137" + { + "name" "Silver" + "color" "#C0C0C0" + } + "138" + { + "name" "Sky Blue" + "color" "#87CEEB" + } + "139" + { + "name" "Slate Blue" + "color" "#6A5ACD" + } + "140" + { + "name" "Slate Grey" + "color" "#708090" + } + "141" + { + "name" "Slate Grey" + "color" "#708090" + } + "142" + { + "name" "Snow" + "color" "#FFFAFA" + } + "143" + { + "name" "Spring Green" + "color" "#00FF7F" + } + "144" + { + "name" "Steel Blue" + "color" "#4682B4" + } + "145" + { + "name" "Strange" + "color" "#CF6A32" + } + "146" + { + "name" "Tan" + "color" "#D2B48C" + } + "147" + { + "name" "Teal" + "color" "#008080" + } + "148" + { + "name" "Thistle" + "color" "#D8BFD8" + } + "149" + { + "name" "Tomato" + "color" "#FF6347" + } + "150" + { + "name" "Turquoise" + "color" "#40E0D0" + } + "151" + { + "name" "Unique" + "color" "#FFD700" + } + "152" + { + "name" "Unusual" + "color" "#8650AC" + } + "153" + { + "name" "Valve" + "color" "#A50F79" + } + "154" + { + "name" "Vintage" + "color" "#476291" + } + "155" + { + "name" "Violet" + "color" "#EE82EE" + } + "156" + { + "name" "Wheat" + "color" "#F5DEB3" + } + "157" + { + "name" "White" + "color" "#FFFFFF" + } + "158" + { + "name" "Whitesmoke" + "color" "#F5F5F5" + } + "159" + { + "name" "Yellow" + "color" "#FFFF00" + } + "160" + { + "name" "Yellow Green" + "color" "#9ACD32" + } +} \ No newline at end of file diff --git a/ChatTags/configs/ChatTags_colors_csgo.cfg b/ChatTags/configs/ChatTags_colors_csgo.cfg new file mode 100644 index 0000000..776efc7 --- /dev/null +++ b/ChatTags/configs/ChatTags_colors_csgo.cfg @@ -0,0 +1,83 @@ +"Tag Colors" +{ + "1" + { + "name" "White" + "color" "1" + } + "2" + { + "name" "Dark Red (Disabled)" + "color" "-1" + } + "3" + { + "name" "Team Colors" + "color" "3" + } + "4" + { + "name" "Lime Green" + "color" "4" + } + "5" + { + "name" "Light Green" + "color" "5" + } + "6" + { + "name" "Olive" + "color" "6" + } + "7" + { + "name" "Light Red" + "color" "7" + } + "8" + { + "name" "Grey" + "color" "8" + } + "9" + { + "name" "Yellow" + "color" "9" + } + "10" + { + "name" "Light Blue" + "color" "10" + } + "11" + { + "name" "Blue" + "color" "11" + } + "12" + { + "name" "Dark Blue" + "color" "12" + } + "13" + { + "name" "Another Blue" + "color" "13" + } + "14" + { + "name" "Purple" + "color" "14" + } + "15" + { + "name" "Orange Red" + "color" "15" + } + "16" + { + "name" "Orange" + "color" "16" + } +} \ No newline at end of file diff --git a/ChatTags/configs/ChatTags_colors_ins.cfg b/ChatTags/configs/ChatTags_colors_ins.cfg new file mode 100644 index 0000000..f481961 --- /dev/null +++ b/ChatTags/configs/ChatTags_colors_ins.cfg @@ -0,0 +1,38 @@ +"Tag Colors" +{ + "1" + { + "name" "White" + "color" "1" + } + "2" + { + "name" "Team Colors" + "color" "2" + } + "3" + { + "name" "Lime Green" + "color" "3" + } + "4" + { + "name" "Light Green" + "color" "4" + } + "5" + { + "name" "Olive Black" + "color" "5" + } + "6" + { + "name" "Banana Yellow" + "color" "6" + } + "7" + { + "name" "Dark Yellow" + "color" "7" + } +} \ No newline at end of file diff --git a/ChatTags/configs/ChatTags_groups.cfg b/ChatTags/configs/ChatTags_groups.cfg new file mode 100644 index 0000000..a32341b --- /dev/null +++ b/ChatTags/configs/ChatTags_groups.cfg @@ -0,0 +1,131 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// EXPLANATION OF SETUPS: +// +// "Group Name" <<< See GROUP NAME EXPLANATION below. +// { +// "flags" "st" <<< Flags required to be considered a matching setup. See FLAGS EXPLANATION below. +// "tagstring" "[Donator]" <<< Define the tag text. +// "tagcolor" "9" <<< Color to use for the tag text, if applicable (see COLOR SETTINGS EXPLANATION below). +// "namecolor" "5" <<< Color to use for the players name (see COLOR SETTINGS EXPLANATION below). +// "chatcolor" "6" <<< Color to use for chat message text (see COLOR SETTINGS EXPLANATION below). +// } +// +////////////////////////////////////////////////////////////////////////////////// +// +// ORDER OF OPERATIONS: Players get the first matching setup. Order them accordingly! +// +// For all setups, be sure to make check which setup first applies. e.g. if admins have flags 'b' and 's' and donators have 's', +// make sure the admin setup comes first, else they will get the donator setup +// +////////////////////////////////////////////////////////////////////////////////// +// FLAGS EXPLANATION: Access can be set to be a single flag, a set of required flags, or even sets of sets. +// +// e.g. Setting the flag as "a" requires players to have the "a" flag to be considered a match. +// e.g. "at" requires players to have both the "a" AND "t" flags to be considered a match. +// e.g. "a;t" requires players to have either the "a" OR "t" flags to be considered a match. +// e.g. "at;b" requires players to have EITHER: (both the "a" AND "t" flags), OR the "b" flag. If either of the two conditions apply, they are considered a match. +// A few more inputs: "none" restricts access for all. "public" or empty quotes ("") make the access available to all (if the "flags" key-value is omitted in any non-steam ID group, "public" is assumed). +// +////////////////////////////////////////////////////////////////////////////////// +// GROUP NAME EXPLANATION: +// +// Names for the setups can be anything, but there are a few special names that can be used: +// * Set it to a player's Steam ID (STEAM_X:Y:ZZZZZZZZ (AuthId_Steam2) or [U:Y:ZZZZZZZZ] (AuthId_Steam3) or XXXXXXXXXXXXXXXXX (AuthId_SteamID64)) to make one specific for that player. +// Note: It will identify steam IDs as any section name with ":" in it (AuthId_Steam2 or AuthId_Steam3), or that is only numbers (AuthId_SteamID64). +// +// * Set it to TEAM_# (replace # with a team number) to make the setup apply to anyone on that team. +// e.g. TEAM_2 would be team 2, which in CS:S/CS:GO is the Terrorist team. TEAM_3 would be the Counter-Terrorist team. +// Note: If the "flags" key-value is set, then both are applied. e.g. A setup can specify Team 2 players with flag "a". +// +////////////////////////////////////////////////////////////////////////////////// +// COLOR SETTINGS EXPLANATION: +// +// Note: The same colors below are required for the plugin cvars that ask you to specify colors. Lavender +// +// INSURGENCY COLORS: +// 1 - White 2 - Team Colors 3 - Lime Green 4 - Light Green +// 5 - Olive Black 6 - Banana Yellow 7 - Dark Yellow 8 - Grey +// +// CS:GO COLORS: +// 1 - White 2 - Dark Red 3 - Team Colors 4 - Lime Green +// 5 - Light Green 6 - Olive 7 - Light Red 8 - Grey +// 9 - Yellow 10 - Light Blue 11 - Blue 12 - Dark Blue +// 13 - Another Blue 14 - Purple 15 - Orange Red +// +// ALL OTHER GAMES: +// Use hex code. e.g. Red = FF0000. To generate hex code, either look in ChatTags_colors.cfg for examples, or use a website like http://www.colorpicker.com/ +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +"Setups" +{ + "Root Admins" + { + "flags" "z" + "tagstring" "[Root Admin]" + "tagcolor" "7" + "namecolor" "6" + "chatcolor" "9" + } + + "Normal Admins" + { + "flags" "s" + "tagstring" "[Admin]" + "tagcolor" "7" + "namecolor" "6" + "chatcolor" "9" + } + + "Donators" + { + "flags" "o" + "tagstring" "[Donator]" + "tagcolor" "4" + "namecolor" "5" + "chatcolor" "1" + } + + "Vip" + { + "flags" "opq" + "tagstring" "[Vip]" + "tagcolor" "4" + "namecolor" "5" + "chatcolor" "1" + } + + "Mapper" + { + "flags" "opqr" + "tagstring" "[Mapper]" + "tagcolor" "16" + "namecolor" "9" + "chatcolor" "1" + } + + "Event Winner" + { + "flags" "t" + "tagstring" "[Event Winner]" + "tagcolor" "12" + "namecolor" "11" + "chatcolor" "1" + } + + //"TEAM_2" + //{ + // "tagstring" "[TERRORIST]" + // "tagcolor" "5" + // "namecolor" "1" + // "chatcolor" "9" + //} + + //"TEAM_3" + //{ + // "tagstring" "[CT]" + // "tagcolor" "5" + // "namecolor" "1" + // "chatcolor" "9" + //} +} \ No newline at end of file diff --git a/ChatTags/configs/db info to add.txt b/ChatTags/configs/db info to add.txt new file mode 100644 index 0000000..9cc6b25 --- /dev/null +++ b/ChatTags/configs/db info to add.txt @@ -0,0 +1,24 @@ +"Databases" +{ + "ChatTags" + { + "driver" "mysql" + "host" "YOUR HOST IP HERE" + "database" "YOUR DB NAME HERE" + "user" "DB USERNAME HERE" + "pass" "USERNAME PASSWORD HERE" + } + + //Use the above setup example if connecting to a MySQL DB. + //======== OR ======== + //Use the below setup example if connecting to a local SQLite DB. + + "ChatTags" + { + "driver" "sqlite" + "host" "localhost" + "database" "ChatTags" + "user" "root" + "pass" "" + } +} diff --git a/ChatTags/scripting/ChatTags.sp b/ChatTags/scripting/ChatTags.sp new file mode 100644 index 0000000..76e0bab --- /dev/null +++ b/ChatTags/scripting/ChatTags.sp @@ -0,0 +1,4911 @@ +/* +Potential To Do: + * Make cvar for if players with required flag are restricted by default or not. (i.e., they have access, but have to be unrestricted first). + * If color setting no longer exists (via config), set player setup to "" if that is their cookie setting. + * Add to color config file the ability to specify color access via flags or to disable/enable certain colors. + * Add natives to plugin to allow other plugins to specify client, and section name of a group setup to apply that setup to a player. Make a "flags" keyvalue that specifies that the setup isnt public or flag/ID based, but for external use (i.e. N/A). + * Re-add Admin see-all. + Add client indexes that are admin and are not part of msg recipients to datapack w/ msg (already formatted with names, etc) + and execute msg on next game frame (or 0.1s timer?). + * Make secondary tag usable through groups config with another kv. + * Natives for external plugins to set a clients tag/colors. + +Requests: + * Force bracket color cfg in groups txt file? + +To Do: + Fix !blockchat + Only using a 1-D array, should be 2D...need to fix + +=========================================== +=========== CHANGELOG AT BOTTOM =========== +=========================================== +*/ + +#pragma semicolon 1 +#pragma dynamic 131072 //increase stack space to from 4 kB to 131072 cells (or 512KB, a cell is 4 bytes). + +#define PLUGIN_VERSION "4.9.3" //CHANGELOG AT BOTTOM +#define MAXTAGSIZE 22 +#define INS_GREEN "\x01" +#define CSGO_RED "\x07" +#define CSS_RED "\x07FF0000" +#define CSGO_WHITE "\x01" + +#define LoopValidPlayers(%1) for (int %1 = 1; %1 <= MaxClients; ++%1) if (IsValidClient(%1)) + +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#undef REQUIRE_PLUGIN +#tryinclude +#define REQUIRE_PLUGIN + +#pragma newdecls required + +ConVar g_cAccessFlag = null; +char g_sAccessFlag[120]; +ConVar g_cAdminFlag = null; +char g_sAdminFlag[120]; +ConVar g_cSpamIgnoreFlag = null; +char g_sSpamIgnoreFlag[120]; +ConVar g_cAdminFlag_Force = null; +char g_sAdminFlag_Force[120]; +ConVar g_cAdminUnloadFlag = null; +char g_sAdminUnloadFlag[120]; +ConVar g_cBracketColors = null; +char g_sBracketColors[32]; +ConVar g_cDatabaseName = null; +char g_sDatabaseName[60]; +ConVar g_cDBTableName = null; +char g_sDBTableName[60]; +//console and team colors +ConVar g_cConsoleTag = null; +char g_sConsoleTag[50]; +ConVar g_cConsoleTagColor = null; +char g_sConsoleTagColor[32]; +ConVar g_cConsoleName = null; +char g_sConsoleName[50]; +ConVar g_cConsoleNameColor = null; +char g_sConsoleNameColor[32]; +ConVar g_cConsoleChatColor = null; +char g_sConsoleChatColor[32]; +ConVar g_cCTChatColor = null; +char g_sCTChatColor[32]; +ConVar g_cTChatColor = null; +char g_sTChatColor[32]; +ConVar g_cCTNameColor = null; +char g_sCTNameColor[32]; +ConVar g_cTNameColor = null; +char g_sTNameColor[32]; +ConVar g_cSpamDetectDuration = null; +ConVar g_cGrpsAfterRemove = null; +ConVar g_cSpamGagDetectDuration = null; +ConVar g_cSpamMsgCnt = null; +ConVar g_cSpamMsgGagCnt = null; +ConVar g_cConvertTriggerCases = null; +ConVar g_cEnableTags = null; +ConVar g_cEnableTagColors = null; +ConVar g_cEnableNameColors = null; +ConVar g_cEnableChatColors = null; +ConVar g_cHideChatTriggers = null; +ConVar g_cLog = null; +ConVar g_cEnableSpamBlock = null; +ConVar g_cEnableSpamGag = null; +ConVar g_cAllowBlockChat = null; +ConVar g_cExternalPos = null; +ConVar g_cForceBrackets = null; + +//player settings +int gaa_iAdminOvrd[MAXPLAYERS + 1][4]; +int ga_iTagVisible[MAXPLAYERS + 1] = {0, ...}; //0 = hidden, 1 = cfg file, 2 = custom setup, 3 = admin forced setup +int ga_iIsRestricted[MAXPLAYERS + 1] = {0, ...}; +int ga_iIsLoaded[MAXPLAYERS + 1] = {0, ...}; +int ga_iEntOverride[MAXPLAYERS + 1] = {0, ...}; +char gaa_sSteamID[MAXPLAYERS + 1][4][65]; +char ga_sName[MAXPLAYERS + 1][MAX_NAME_LENGTH]; +char ga_sEscapedName[MAXPLAYERS + 1][MAX_NAME_LENGTH*2 + 1]; +char ga_sTagColor[MAXPLAYERS + 1][32]; +char ga_sNameColor[MAXPLAYERS + 1][32]; +char ga_sChatColor[MAXPLAYERS + 1][32]; +char ga_sTag[MAXPLAYERS + 1][MAXTAGSIZE]; +char gaa_sCleanSetupText[MAXPLAYERS + 1][4][32]; +char ga_sExtTag[MAXPLAYERS + 1][30]; +char ga_sExtTagColor[MAXPLAYERS + 1][32]; +int ga_iTagColorAccess[MAXPLAYERS + 1] = {-1, ...}; +int ga_iNameColorAccess[MAXPLAYERS + 1] = {-1, ...}; +int ga_iChatColorAccess[MAXPLAYERS + 1] = {-1, ...}; +int ga_iSetTagAccess[MAXPLAYERS + 1] = {-1, ...}; +int ga_iGroupMatch[MAXPLAYERS + 1] = {0, ...}; +bool ga_bChatBlocked[MAXPLAYERS + 1] = {false, ...}; + +//chat spam blocker +int ga_iChatMsgCnt[MAXPLAYERS + 1] = {0, ...}; +int ga_iChatSpamCnt[MAXPLAYERS + 1] = {0, ...}; + +ArrayList g_aTranslationNames; +ArrayList g_aColorName; +ArrayList g_aColorCode; +ArrayList g_aBlockedTags; +ArrayList g_aMessages; + +Handle g_hLoadFwd = INVALID_HANDLE; +Handle g_hClientLoadFwd = INVALID_HANDLE; +Handle g_hClientReloadFwd = INVALID_HANDLE; +TopMenu g_oTopMenu; +Regex g_oRegexHex; +Database g_oDatabase; + +char g_sPath[PLATFORM_MAX_PATH]; //cfg file data +char g_sGroupPath[PLATFORM_MAX_PATH]; //cfg file data +char g_sChatLogPath[PLATFORM_MAX_PATH]; //chat logger +char g_sTag[MAXTAGSIZE]; +char g_sMapName[128] = ""; +char g_sServerIP[64] = ""; + +bool g_bLateLoad; +bool g_bCSGO = false; +bool g_bIns = false; +bool g_bMySQL = false; + +//chat ignoring +bool g_Ignored[(MAXPLAYERS + 1) * (MAXPLAYERS + 1)] = {false, ...}; + +bool b_InGroup[MAXPLAYERS+1] = {false, ...}; +ConVar g_cGroupTag = null; +ConVar g_cCTGroupTagColor = null; +char g_sCTGroupTagColor[32]; +ConVar g_cTGroupTagColor = null; +char g_sTGroupTagColor[32]; + +bool g_bPOSSESSION = false; + +public Plugin myinfo = +{ + name = "Chat Tags", + author = "That One Guy + PŠΣ™ SHUFEN", + description = "Gives players with designated flag the ability to set their own custom tags, and much much more.", + version = PLUGIN_VERSION, + url = "" +} + +public APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int err_max) +{ + g_bLateLoad = bLate; + + CreateNative("CT_SetExtTag", Native_SetExtTag); + CreateNative("CT_SetSetTagAccess", Native_SetSetTagAccess); + CreateNative("CT_SetTagColorAccess", Native_SetTagColorAccess); + CreateNative("CT_SetNameColorAccess", Native_SetNameColorAccess); + CreateNative("CT_SetChatColorAccess", Native_SetChatColorAccess); + CreateNative("CT_SetCompleteAccess", Native_SetCompleteAccess); + + //chat ignoring + CreateNative("CT_UpdateIgnoredArray", Native_UpdateIgnoredArray); + + RegPluginLibrary("ChatTags"); + + return APLRes_Success; +} + +public void OnPluginStart() +{ + LoadTranslations("ChatTags.phrases"); + + char sGameFolder[32], sTranslation[PLATFORM_MAX_PATH], sDescription[64]; + GetGameDescription(sDescription, sizeof(sDescription), true); + GetGameFolderName(sGameFolder, sizeof(sGameFolder)); + if ((StrContains(sGameFolder, "csgo", false) != -1) || (StrContains(sDescription, "Counter-Strike: Global Offensive", false) != -1)) + { + g_bCSGO = true; + } + else if ((StrContains(sGameFolder, "insurgency", false) != -1) || StrEqual(sGameFolder, "ins", false) || (StrContains(sDescription, "Insurgency", false) != -1)) + { + g_bIns = true; + } + + g_aTranslationNames = new ArrayList(64); + Format(sTranslation, sizeof(sTranslation), "scp.%s.phrases.txt", sGameFolder); + LoadTranslations(sTranslation); + BuildPath(Path_SM, sTranslation, sizeof(sTranslation), "translations/%s", sTranslation); + if (!FileExists(sTranslation)) + { + SetFailState("Translation file missing! %s", sTranslation); + } + GetTranslationNames(sTranslation); + + g_hLoadFwd = CreateGlobalForward("CTLoaded", ET_Ignore); + g_hClientLoadFwd = CreateGlobalForward("CTClientLoaded", ET_Event, Param_Cell); + g_hClientReloadFwd = CreateGlobalForward("CTClientReloaded", ET_Event, Param_Cell); + + AutoExecConfig_SetFile("ChatTags"); + AutoExecConfig_CreateConVar("ct_version", PLUGIN_VERSION, "Chat Tags: Version", FCVAR_NOTIFY|FCVAR_DONTRECORD); + + LoadTranslations("core.phrases"); + LoadTranslations("common.phrases"); + + g_cAccessFlag = AutoExecConfig_CreateConVar("ct_accessflag", "q", "If \"\", everyone can change their tags, \"none\" restricted access (other than external plugin use), otherwise, only players with the listed flag(s) can access plugin features.", _); + g_cAccessFlag.GetString(g_sAccessFlag, sizeof(g_sAccessFlag)); + g_cAccessFlag.AddChangeHook(OnCVarChange); + + g_cAdminFlag = AutoExecConfig_CreateConVar("ct_adminflag", "s", "Only players with this flag can restrict/remove tags of players.", _); + g_cAdminFlag.GetString(g_sAdminFlag, sizeof(g_sAdminFlag)); + g_cAdminFlag.AddChangeHook(OnCVarChange); + + g_cSpamIgnoreFlag = AutoExecConfig_CreateConVar("ct_spamignoreflag", "s", "Players with this flag will be ignored by the spam blocker.", _); + g_cSpamIgnoreFlag.GetString(g_sSpamIgnoreFlag, sizeof(g_sSpamIgnoreFlag)); + g_cSpamIgnoreFlag.AddChangeHook(OnCVarChange); + + g_cAdminFlag_Force = AutoExecConfig_CreateConVar("ct_adminflag_force", "s", "Only players with this flag can force tags/colors for other players.", _); + g_cAdminFlag_Force.GetString(g_sAdminFlag_Force, sizeof(g_sAdminFlag_Force)); + g_cAdminFlag_Force.AddChangeHook(OnCVarChange); + + g_cAdminUnloadFlag = AutoExecConfig_CreateConVar("ct_unloadflag", "z", "Only players with this flag can unload the entire plugin until map change.", _); + g_cAdminUnloadFlag.GetString(g_sAdminUnloadFlag, sizeof(g_sAdminUnloadFlag)); + g_cAdminUnloadFlag.AddChangeHook(OnCVarChange); + + g_cConsoleTag = AutoExecConfig_CreateConVar("ct_console_tag", "", "Tag to use for console.", FCVAR_NONE); + g_cConsoleTag.GetString(g_sConsoleTag, sizeof(g_sConsoleTag)); + g_cConsoleTag.AddChangeHook(OnCVarChange); + + g_cConsoleName = AutoExecConfig_CreateConVar("ct_console_name", ":::CONSOLE:::", "Name to use for console.", FCVAR_NONE); + g_cConsoleName.GetString(g_sConsoleName, sizeof(g_sConsoleName)); + g_cConsoleName.AddChangeHook(OnCVarChange); + + g_cDatabaseName = AutoExecConfig_CreateConVar("ct_dbname", "ChatTags", "Name of the database setup for the plugin."); + g_cDatabaseName.GetString(g_sDatabaseName, sizeof(g_sDatabaseName)); + g_cDatabaseName.AddChangeHook(OnCVarChange); + + g_cDBTableName = AutoExecConfig_CreateConVar("ct_dbtblname", "ChatTags", "Name of the database table for the plugin."); + g_cDBTableName.GetString(g_sDBTableName, sizeof(g_sDBTableName)); + g_cDBTableName.AddChangeHook(OnCVarChange); + + g_cConvertTriggerCases = AutoExecConfig_CreateConVar("ct_triggercase", "1", "Convert chat triggers to lowercase if theyre uppercase. (1 = enabled, 0 = disabled)", FCVAR_NONE, true, 0.0, true, 1.0); + g_cLog = AutoExecConfig_CreateConVar("ct_log", "1", "Enable chat logger. (1 = enabled, 0 = disabled)", FCVAR_NONE, true, 0.0, true, 1.0); + g_cExternalPos = AutoExecConfig_CreateConVar("ct_exttagspos", "0", "0 = external tags applied on left of normal tags, 1 = on right, 2 = Only one tag can show, ext tag is preferenced, 3 = Only one tag can show, personal is preferenced.", FCVAR_NONE, true, 0.0, true, 3.0); + g_cForceBrackets = AutoExecConfig_CreateConVar("ct_forcebrackets", "0", "0 = disabled, 1 = wrap personal tags in {}, 2 = wrap personal tags in []. Note: Tags from the cfg file are not forced to have brackets.", FCVAR_NONE, true, 0.0, true, 2.0); + //spam blocker + g_cEnableSpamBlock = AutoExecConfig_CreateConVar("ct_spam_enable", "1", "Enable blocking chat spam (0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cSpamDetectDuration = AutoExecConfig_CreateConVar("ct_spam_duration_short", "3", "Number of seconds used for the initial spam detection. If messages sent within this time frame exceed the count set by ct_spam_count_short, it blocks them and marks them as spam.", FCVAR_NONE, true, 1.0); + g_cSpamMsgCnt = AutoExecConfig_CreateConVar("ct_spam_count_short", "3", "Number of messages within the time interval set by ct_spam_duration_short for it to be considered spam.", FCVAR_NONE, true, 2.0); + g_cEnableSpamGag = AutoExecConfig_CreateConVar("ct_spam_enablegag", "0", "Enable muting chat spammers after spam detections exceed ct_spam_count_long within time set by ct_spam_duration_long? (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cSpamGagDetectDuration = AutoExecConfig_CreateConVar("ct_spam_duration_long", "60", "Number of spam detections within the time interval set by ct_spam_duration_long before auto-gag is issued.", FCVAR_NONE, true, 1.0); + g_cSpamMsgGagCnt = AutoExecConfig_CreateConVar("ct_spam_count_long", "30", "Number of spam detections within the time interval set by ct_spam_duration_long before auto-gag is issued.", FCVAR_NONE, true, 2.0); + g_cAllowBlockChat = AutoExecConfig_CreateConVar("ct_allowblockchat", "0", "Allow players to use !blockchat command (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cGrpsAfterRemove = AutoExecConfig_CreateConVar("ct_grps_after_remove", "1", "Apply group file configs to player after an admin removes their tags? (1 = enabled, 0 = disabled)", FCVAR_NONE, true, 0.0, true, 1.0); + g_cEnableTags = AutoExecConfig_CreateConVar("ct_enabletags", "1", "Enable being able to set a tags if you have access (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cEnableTagColors = AutoExecConfig_CreateConVar("ct_enabletagcolors", "1", "Enable being able to set tag colors (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cEnableNameColors = AutoExecConfig_CreateConVar("ct_enablenamecolors", "1", "Enable being able to set name colors (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cEnableChatColors = AutoExecConfig_CreateConVar("ct_enablechatcolors", "1", "Enable being able to set chat colors (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cHideChatTriggers = AutoExecConfig_CreateConVar("ct_hidechattriggers", "0", "Hides registered chat commands and messages starting with ! or / (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + + if (g_bCSGO) //CS:GO + { + Format(g_sTag, sizeof(g_sTag), " \x04[ChatTags] "); //includes space + g_cCTChatColor = AutoExecConfig_CreateConVar("ct_chatcolor_ct", "1", "Color to use as default chat color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTChatColor = AutoExecConfig_CreateConVar("ct_chatcolor_t", "1", "Color to use as default chat color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cCTNameColor = AutoExecConfig_CreateConVar("ct_namecolor_ct", "11", "Color to use as default name color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTNameColor = AutoExecConfig_CreateConVar("ct_namecolor_t", "15", "Color to use as default name color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleTagColor = AutoExecConfig_CreateConVar("ct_console_tagcolor", "2", "Color to use for console tag color (should be sourcepawn format).", FCVAR_NONE); + g_cConsoleNameColor = AutoExecConfig_CreateConVar("ct_console_namecolor", "2", "Color to use for console name color (should be sourcepawn format).", FCVAR_NONE); + g_cConsoleChatColor = AutoExecConfig_CreateConVar("ct_console_chatcolor", "2", "Color to use for console chat color (should be sourcepawn format).", FCVAR_NONE); + g_cBracketColors = AutoExecConfig_CreateConVar("ct_bracketcolor", "", "Color to use for brackets if ct_forcebrackets is enabled (check color cfg file for #s - Blank = match tag color).", FCVAR_NONE); + } + else if (g_bIns) //Insurgency + { + Format(g_sTag, sizeof(g_sTag), " \x04[ChatTags] "); //includes space + g_cCTChatColor = AutoExecConfig_CreateConVar("ct_chatcolor_ct", "1", "Color to use as default chat color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTChatColor = AutoExecConfig_CreateConVar("ct_chatcolor_t", "1", "Color to use as default chat color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cCTNameColor = AutoExecConfig_CreateConVar("ct_namecolor_ct", "2", "Color to use as default name color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTNameColor = AutoExecConfig_CreateConVar("ct_namecolor_t", "2", "Color to use as default name color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleTagColor = AutoExecConfig_CreateConVar("ct_console_tagcolor", "1", "Color to use for console tag color (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleNameColor = AutoExecConfig_CreateConVar("ct_console_namecolor", "3", "Color to use for console name color (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleChatColor = AutoExecConfig_CreateConVar("ct_console_chatcolor", "6", "Color to use for console chat color (check color cfg file for #s).", FCVAR_NONE); + g_cBracketColors = AutoExecConfig_CreateConVar("ct_bracketcolor", "", "Color to use for brackets if ct_forcebrackets is enabled (check color cfg file for #s - Blank = match tag color).", FCVAR_NONE); + } + else + { + Format(g_sTag, sizeof(g_sTag), "\x04[ChatTags] "); //no space added + g_cCTChatColor = AutoExecConfig_CreateConVar("ct_chatcolor_ct", "", "Color to use as default chat color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTChatColor = AutoExecConfig_CreateConVar("ct_chatcolor_t", "", "Color to use as default chat color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cCTNameColor = AutoExecConfig_CreateConVar("ct_namecolor_ct", "99CCFF", "Color to use as default name color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTNameColor = AutoExecConfig_CreateConVar("ct_namecolor_t", "FF4040", "Color to use as default name color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleTagColor = AutoExecConfig_CreateConVar("ct_console_tagcolor", "FFFFFF", "Color to use for console tag color (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleNameColor = AutoExecConfig_CreateConVar("ct_console_namecolor", "FF0000", "Color to use for console name color (check color cfg file for #s).", FCVAR_NONE); + g_cConsoleChatColor = AutoExecConfig_CreateConVar("ct_console_chatcolor", "FF0000", "Color to use for console chat color (check color cfg file for #s).", FCVAR_NONE); + g_cBracketColors = AutoExecConfig_CreateConVar("ct_bracketcolor", "", "Color to use for brackets if ct_forcebrackets is enabled (check color cfg file for #s - Blank = match tag color).", FCVAR_NONE); + } + + g_cGroupTag = AutoExecConfig_CreateConVar("ct_grouptag", "1", "Add symbol prefix for steam group member (1 = enabled, 0 = disabled).", FCVAR_NONE, true, 0.0, true, 1.0); + g_cCTGroupTagColor = AutoExecConfig_CreateConVar("ct_grouptagcolor_ct", "12", "Color to use as group tag color for CTs, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cTGroupTagColor = AutoExecConfig_CreateConVar("ct_grouptagcolor_t", "7", "Color to use as group tag color for Ts, if nothing else applies (check color cfg file for #s).", FCVAR_NONE); + g_cCTGroupTagColor.GetString(g_sCTGroupTagColor, sizeof(g_sCTGroupTagColor)); + g_cCTGroupTagColor.AddChangeHook(OnCVarChange); + g_cTGroupTagColor.GetString(g_sTGroupTagColor, sizeof(g_sTGroupTagColor)); + g_cTGroupTagColor.AddChangeHook(OnCVarChange); + + g_cCTChatColor.GetString(g_sCTChatColor, sizeof(g_sCTChatColor)); + g_cCTChatColor.AddChangeHook(OnCVarChange); + + g_cTChatColor.GetString(g_sTChatColor, sizeof(g_sTChatColor)); + g_cTChatColor.AddChangeHook(OnCVarChange); + + g_cCTNameColor.GetString(g_sCTNameColor, sizeof(g_sCTNameColor)); + g_cCTNameColor.AddChangeHook(OnCVarChange); + + g_cTNameColor.GetString(g_sTNameColor, sizeof(g_sTNameColor)); + g_cTNameColor.AddChangeHook(OnCVarChange); + + g_cBracketColors.GetString(g_sBracketColors, sizeof(g_sBracketColors)); + g_cBracketColors.AddChangeHook(OnCVarChange); + + g_cConsoleTagColor.GetString(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + g_cConsoleTagColor.AddChangeHook(OnCVarChange); + + g_cConsoleNameColor.GetString(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + g_cConsoleNameColor.AddChangeHook(OnCVarChange); + + g_cConsoleChatColor.GetString(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + g_cConsoleChatColor.AddChangeHook(OnCVarChange); + + AutoExecConfig_ExecuteFile(); + AutoExecConfig_CleanFile(); + + SetDBHandle(); + + //player commands + RegConsoleCmd("sm_tags", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_chatmenu", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_chatcolor", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_tagmenu", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_tagcolor", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_namemenu", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_namecolor", Command_Tag, "Opens Chat Tags Menu."); + RegConsoleCmd("sm_settag", Command_SetText, "Change tag text."); + RegConsoleCmd("sm_tag", Command_SetText, "Change tag text."); + RegConsoleCmd("sm_checktag", Command_CheckTag, "Check tag settings of another player."); + RegConsoleCmd("sm_blockchat", Command_BlockChat, "Block player from seeing chat from others (if command is allowed)."); + //non-CS:GO commands + RegConsoleCmd("sm_tagcolor", Command_TagColor, "Change tag color to a specified hexadecimal value."); + RegConsoleCmd("sm_namecolor", Command_NameColor, "Change name color to a specified hexadecimal value."); + RegConsoleCmd("sm_chatcolor", Command_ChatColor, "Change chat color to a specified hexadecimal value."); + + //admin commands - RegConsoleCmd used to allow setting access via cvar "ct_adminflag" + RegConsoleCmd("sm_reloadtagcolors", Cmd_Reload, "Reloads color cfg file for tags."); + RegConsoleCmd("sm_unrestricttag", Cmd_Unrestrict, "Unrestrict player from setting their chat tags."); + RegConsoleCmd("sm_restricttag", Cmd_Restrict, "Restrict player from setting their chat tags."); + RegConsoleCmd("sm_removetag", Cmd_RemoveTag, "Removes a players tag setup."); + RegConsoleCmd("sm_unloadtags", Cmd_Unload, "Unloads the entire plugin for the current map."); + //admin commands - RegConsoleCmd used to allow setting access via cvar "ct_adminflag_force" + RegConsoleCmd("sm_forcetag", Cmd_ForceTag, "Force tag setup on a player."); + RegConsoleCmd("sm_removeoverride", Cmd_RemoveOverride, "Remove admin overrides from a player."); + + AddCommandListener(Command_Say, "say"); + AddCommandListener(Command_SayTeam, "say_team"); + if (g_bIns) + { + AddCommandListener(Command_Say, "say2"); + } + + //hook admin chat commands for chat logging + AddCommandListener(Command_AdminChat, "sm_say"); + AddCommandListener(Command_AdminOnlyChat, "sm_chat"); + AddCommandListener(Command_CSay, "sm_csay"); + AddCommandListener(Command_TSay, "sm_tsay"); + AddCommandListener(Command_MSay, "sm_msay"); + AddCommandListener(Command_HSay, "sm_hsay"); + AddCommandListener(Command_PSay, "sm_psay"); + + HookEvent("player_changename", Event_NameChange); + UserMsg umSayText2 = GetUserMessageId("SayText2"); + if (umSayText2 != INVALID_MESSAGE_ID) + { + HookUserMessage(umSayText2, OnSayText2, true); + } + else + { + UserMsg umSayText = GetUserMessageId("SayText"); + if (umSayText != INVALID_MESSAGE_ID) + { + HookUserMessage(umSayText, OnSayText2, true); + } + else + { + LogError("Unable to hook either SayText2 or SayText. This game is not supported!"); + SetFailState("Unable to hook either SayText2 or SayText. This game is not supported!"); + } + } + + //client prefs and cookies + SetCookieMenuItem(Menu_ClientPrefs, 0, "Chat Tags"); + + //admin menu - Account for late loading + TopMenu hTopMenu; + if (LibraryExists("adminmenu") && ((hTopMenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(hTopMenu); + } + + //color file + g_aColorName = new ArrayList(32); + g_aColorCode = new ArrayList(32); + if (g_bCSGO) + { + BuildPath(Path_SM, g_sPath, sizeof(g_sPath), "configs/ChatTags_colors_csgo.cfg"); + } + else if (g_bIns) + { + BuildPath(Path_SM, g_sPath, sizeof(g_sPath), "configs/ChatTags_colors_ins.cfg"); + } + else + { + BuildPath(Path_SM, g_sPath, sizeof(g_sPath), "configs/ChatTags_colors.cfg"); + g_oRegexHex = new Regex("([A-Fa-f0-9]{6})"); + } + + BuildPath(Path_SM, g_sGroupPath, sizeof(g_sGroupPath), "configs/ChatTags_groups.cfg"); + + if (g_bCSGO) + { + ConvertConsoleColor(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + ConvertConsoleColor(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + ConvertConsoleColor(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + } + else + { + ConvertColor(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + ConvertColor(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + ConvertColor(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + } + ConvertColor(g_sCTChatColor, sizeof(g_sCTChatColor)); + ConvertColor(g_sTChatColor, sizeof(g_sTChatColor)); + ConvertColor(g_sCTNameColor, sizeof(g_sCTNameColor)); + ConvertColor(g_sTNameColor, sizeof(g_sTNameColor)); + ConvertColor(g_sBracketColors, sizeof(g_sBracketColors)); + + ConvertColor(g_sCTGroupTagColor, sizeof(g_sCTGroupTagColor)); + ConvertColor(g_sTGroupTagColor, sizeof(g_sTGroupTagColor)); + + //overwrite cookies if they are cached + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && AreClientCookiesCached(i) && IsClientAuthorized(i)) + { + SetDefaults(i); + LoadClientData(i); + } + } + + GetServerIP(); +} + +public void OnAllPluginsLoaded() { + g_bPOSSESSION = LibraryExists("POSSESSION"); +} + +public void OnLibraryAdded(const char[] name) { + if (StrEqual(name, "POSSESSION", false)) g_bPOSSESSION = true; +} + +public void OnLibraryRemoved(const char[] name) { + if (StrEqual(name, "POSSESSION", false)) g_bPOSSESSION = false; +} + +public void OnCVarChange(ConVar hCVar, const char[] sOldValue, const char[] sNewValue) +{ + if (hCVar == g_cDatabaseName) + { + g_cDatabaseName.GetString(g_sDatabaseName, sizeof(g_sDatabaseName)); + } + else if (hCVar == g_cDBTableName) + { + g_cDBTableName.GetString(g_sDBTableName, sizeof(g_sDBTableName)); + } + else if (hCVar == g_cAccessFlag) + { + g_cAccessFlag.GetString(g_sAccessFlag, sizeof(g_sAccessFlag)); + } + else if (hCVar == g_cAdminFlag) + { + g_cAdminFlag.GetString(g_sAdminFlag, sizeof(g_sAdminFlag)); + } + else if (hCVar == g_cSpamIgnoreFlag) + { + g_cSpamIgnoreFlag.GetString(g_sSpamIgnoreFlag, sizeof(g_sSpamIgnoreFlag)); + } + else if (hCVar == g_cAdminFlag_Force) + { + g_cAdminFlag_Force.GetString(g_sAdminFlag_Force, sizeof(g_sAdminFlag_Force)); + } + else if (hCVar == g_cAdminUnloadFlag) + { + g_cAdminUnloadFlag.GetString(g_sAdminUnloadFlag, sizeof(g_sAdminUnloadFlag)); + } + else if (hCVar == g_cConsoleTag) + { + g_cConsoleTag.GetString(g_sConsoleTag, sizeof(g_sConsoleTag)); + } + else if (hCVar == g_cConsoleTagColor) + { + GetConVarString(g_cConsoleTagColor, g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + if (g_bCSGO) + ConvertConsoleColor(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + else + ConvertColor(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + } + else if (hCVar == g_cConsoleName) + { + g_cConsoleName.GetString(g_sConsoleName, sizeof(g_sConsoleName)); + } + else if (hCVar == g_cConsoleNameColor) + { + GetConVarString(g_cConsoleNameColor, g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + if (g_bCSGO) + ConvertConsoleColor(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + else + ConvertColor(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + } + else if (hCVar == g_cConsoleChatColor) + { + GetConVarString(g_cConsoleChatColor, g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + if (g_bCSGO) + ConvertConsoleColor(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + else + ConvertColor(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + } + else if (hCVar == g_cCTChatColor) + { + g_cCTChatColor.GetString(g_sCTChatColor, sizeof(g_sCTChatColor)); + ConvertColor(g_sCTChatColor, sizeof(g_sCTChatColor)); + } + else if (hCVar == g_cTChatColor) + { + g_cTChatColor.GetString(g_sTChatColor, sizeof(g_sTChatColor)); + ConvertColor(g_sTChatColor, sizeof(g_sTChatColor)); + } + else if (hCVar == g_cCTNameColor) + { + g_cCTNameColor.GetString(g_sCTNameColor, sizeof(g_sCTNameColor)); + ConvertColor(g_sCTNameColor, sizeof(g_sCTNameColor)); + } + else if (hCVar == g_cTNameColor) + { + g_cTNameColor.GetString(g_sTNameColor, sizeof(g_sTNameColor)); + ConvertColor(g_sTNameColor, sizeof(g_sTNameColor)); + } + else if (hCVar == g_cBracketColors) + { + g_cBracketColors.GetString(g_sBracketColors, sizeof(g_sBracketColors)); + ConvertColor(g_sBracketColors, sizeof(g_sBracketColors)); + } + + else if (hCVar == g_cCTGroupTagColor) + { + g_cCTGroupTagColor.GetString(g_sCTGroupTagColor, sizeof(g_sCTGroupTagColor)); + ConvertColor(g_sCTGroupTagColor, sizeof(g_sCTGroupTagColor)); + } + else if (hCVar == g_cTGroupTagColor) + { + g_cTGroupTagColor.GetString(g_sTGroupTagColor, sizeof(g_sTGroupTagColor)); + ConvertColor(g_sTGroupTagColor, sizeof(g_sTGroupTagColor)); + } +} + +void SetDBHandle() +{ + if (g_oDatabase != null) + { + delete g_oDatabase; + g_oDatabase = null; + } + Database.Connect(SQLCallback_Connect, g_sDatabaseName); +} + +public void SQLCallback_Connect(Database oDB, const char[] sError, any data) +{ + if (oDB == null) + { + SetFailState("Error connecting to main database. %s", sError); + } + else + { + g_oDatabase = oDB; + char sDriver[64], sQuery[1000], sQueryBuffer[1000]; + DBDriver oDriver = g_oDatabase.Driver; + oDriver.GetIdentifier(sDriver, sizeof(sDriver)); + delete oDriver; + + Format(sQueryBuffer, sizeof(sQueryBuffer), " `steamid` VARCHAR(65) NOT NULL, \ + `tagtext` VARCHAR(32) NOT NULL, \ + `visible` INT(2) NOT NULL, \ + `restricted` INT(2) NOT NULL, \ + `tagcolor` VARCHAR(10) NOT NULL, \ + `namecolor` VARCHAR(10) NOT NULL, \ + `chatcolor` VARCHAR(10) NOT NULL, \ + `ovrd_ttext` INT(2) NOT NULL, \ + `ovrd_tcolor` INT(2) NOT NULL, \ + `ovrd_ncolor` INT(2) NOT NULL, \ + `ovrd_ccolor` INT(2) NOT NULL"); + FormatQueryByDriver(sDriver, g_sDBTableName, sQuery, sizeof(sQuery), sQueryBuffer); + g_oDatabase.Query(SQLCallback_Void, sQuery, 1); + + if (!StrEqual(sDriver, "sqlite")) + { + Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `ct_chatlogs` ( `id` INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, \ + `steamid` VARCHAR(65) NULL, \ + `mapname` VARCHAR(128) NULL, \ + `server` VARCHAR(32) NULL, \ + `playername` VARCHAR(65) NULL, \ + `playerip` VARCHAR(32) NULL, \ + `ip_country` VARCHAR(45) NULL, \ + `chatmsg` VARCHAR(300) NULL, \ + `chatgrp` VARCHAR(20) NULL, \ + `logdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() \ + )"); + g_oDatabase.Query(SQLCallback_Void, sQuery, 5); + g_bMySQL = true; + } + else //if sqlite, use flat files + { + char sBuffer[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sBuffer, sizeof(sBuffer), "logs/chatlogger/"); + if (!DirExists(sBuffer)) + { + CreateDirectory(sBuffer, 777); + } + FormatTime(sBuffer, sizeof(sBuffer), "%m%d%y"); + Format(sBuffer, sizeof(sBuffer), "logs/chatlogger/chatlogs_%s.log", sBuffer); + BuildPath(Path_SM, g_sChatLogPath, sizeof(g_sChatLogPath), sBuffer); + CreateTimer(60.0, Timer_UpdatePath, _, TIMER_REPEAT); + } + } + GetCurrentMap(g_sMapName, sizeof(g_sMapName)); //redundancy to make sure this has a value +} + +void FormatQueryByDriver(char[] sDriver, char[] sTblName, char[] sRtnQuery, int iSize, char[] sQuery) +{ + if (StrEqual(sDriver, "sqlite", false)) + { + Format(sRtnQuery, iSize, "CREATE TABLE IF NOT EXISTS `%s` (`id` int(20) PRIMARY KEY, %s, PRIMARY KEY (`id`)) DEFAULT AUTO_INCREMENT=1", sTblName, sQuery); + } + else + { + Format(sRtnQuery, iSize, "CREATE TABLE IF NOT EXISTS `%s` (`id` int(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, %s)", sTblName, sQuery); + } +} + +public void SQLCallback_Void(Database oDB, DBResultSet oHndl, const char[] sError, any iValue) +{ + if (oHndl == null) + { + //SetFailState("Error (%i): %s", iValue, sError); + LogMessage("Error (%i): %s", iValue, sError); + } +} + +public void OnMapStart() +{ + if (g_bCSGO) + { + ConvertConsoleColor(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + ConvertConsoleColor(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + ConvertConsoleColor(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + } + else + { + ConvertColor(g_sConsoleTagColor, sizeof(g_sConsoleTagColor)); + ConvertColor(g_sConsoleNameColor, sizeof(g_sConsoleNameColor)); + ConvertColor(g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + } + ConvertColor(g_sCTChatColor, sizeof(g_sCTChatColor)); + ConvertColor(g_sTChatColor, sizeof(g_sTChatColor)); + ConvertColor(g_sCTNameColor, sizeof(g_sCTNameColor)); + ConvertColor(g_sTNameColor, sizeof(g_sTNameColor)); + ConvertColor(g_sBracketColors, sizeof(g_sBracketColors)); + + ConvertColor(g_sCTGroupTagColor, sizeof(g_sCTGroupTagColor)); + ConvertColor(g_sTGroupTagColor, sizeof(g_sTGroupTagColor)); + + LoadColorCfg(); + LoadCustomConfigs(); + + GetCurrentMap(g_sMapName, sizeof(g_sMapName)); //redundancy to make sure this has a value + + if (g_cLog.BoolValue && (g_oDatabase != null)) + { + if (!g_bMySQL) //map is already noted in MySQL DB, so this logging is not necessary. + { + LogToFileEx(g_sChatLogPath, " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> New map started: %s <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", g_sMapName); + } + } +} + +void GetServerIP() +{ + int a_iArray[4]; + /*int iLongIP = GetConVarInt(FindConVar("hostip")); + a_iArray[0] = (iLongIP >> 24) & 0x000000FF; + a_iArray[1] = (iLongIP >> 16) & 0x000000FF; + a_iArray[2] = (iLongIP >> 8) & 0x000000FF; + a_iArray[3] = iLongIP & 0x000000FF;*/ + SteamWorks_GetPublicIP(a_iArray); + Format(g_sServerIP, sizeof(g_sServerIP), "%d.%d.%d.%d:%i", a_iArray[0], a_iArray[1], a_iArray[2], a_iArray[3], GetConVarInt(FindConVar("hostport"))); +} + +public Action Timer_UpdatePath(Handle hTimer) +{ + char sBuffer[256]; + FormatTime(sBuffer, sizeof(sBuffer), "%m%d%y"); + Format(sBuffer, sizeof(sBuffer), "logs/chatlogger/chatlogs_%s.log", sBuffer); + BuildPath(Path_SM, g_sChatLogPath, sizeof(g_sChatLogPath), sBuffer); +} + +public void OnConfigsExecuted() +{ + LoadColorCfg(); + LoadCustomConfigs(); + + if (g_bLateLoad) + { + Reload(); + /*if (g_cLog.BoolValue && !g_bMySQL) + { + CreateTimer(60.0, Timer_UpdatePath, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + }*/ + } + + GetServerIP(); +} + +public void LoadColorCfg() +{ + if (!FileExists(g_sPath)) + { + SetFailState("Configuration file %s not found!", g_sPath); + return; + } + + KeyValues oKeyValues = new KeyValues("Tag Colors"); + + if (!oKeyValues.ImportFromFile(g_sPath)) + { + SetFailState("Improper structure for configuration file %s!", g_sPath); + return; + } + + if (!oKeyValues.GotoFirstSubKey(true)) + { + SetFailState("Can't find configuration file %s!", g_sPath); + return; + } + + g_aColorName.Clear(); + g_aColorCode.Clear(); + + char sName[32], sCode[32]; + do + { + oKeyValues.GetString("name", sName, sizeof(sName)); + oKeyValues.GetString("color", sCode, sizeof(sCode)); + ReplaceString(sCode, sizeof(sCode), "#", "", false); + + if (!g_bCSGO && !g_bIns) + { + if (!IsValidHex(sCode)) + { + LogError("Invalid hexadecimal value for color %s: %s.", sName, sCode); + continue; + } + } + + g_aColorName.PushString(sName); + g_aColorCode.PushString(sCode); + } + while (oKeyValues.GotoNextKey(false)); + delete oKeyValues; +} + +public void LoadCustomConfigs() +{ + if (g_aBlockedTags != null) + delete g_aBlockedTags; + if (g_aMessages != null) + delete g_aMessages; + g_aBlockedTags = new ArrayList(64); + g_aMessages = new ArrayList(); + char sBuffer[PLATFORM_MAX_PATH], sFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/ChatTags_blocked.cfg"); + File oFile = OpenFile(sFile, "r"); + if (oFile != null) + { + while (oFile.ReadLine(sBuffer, sizeof(sBuffer))) + { + TrimString(sBuffer); //remove spaces and tabs at both ends of string + if ((StrContains(sBuffer, "//") == -1) && (!StrEqual(sBuffer, ""))) //filter out comments and blank lines + { + g_aBlockedTags.PushString(sBuffer); + } + } + } + else + { + LogError("File does not exist: \"%s\"", sFile); + } + + delete oFile; +} + +public int Native_SetExtTag(Handle hPlugin, int iNumParams) +{ + int client = GetNativeCell(1); + if (IsValidClient(client)) + { + GetNativeString(2, ga_sExtTag[client], sizeof(ga_sExtTag[])); + GetNativeString(3, ga_sExtTagColor[client], sizeof(ga_sExtTagColor[])); + FormatColors(client); + return true; + } + return false; +} + +public int Native_SetSetTagAccess(Handle hPlugin, int iNumParams) +{ + int client = GetNativeCell(1); + + if (IsValidClient(client)) + { + ga_iSetTagAccess[client] = GetNativeCell(2); + return true; + } + + return false; +} + +public int Native_SetTagColorAccess(Handle hPlugin, int iNumParams) +{ + int client = GetNativeCell(1); + + if (IsValidClient(client)) + { + ga_iTagColorAccess[client] = GetNativeCell(2); + return true; + } + + return false; +} + +public int Native_SetNameColorAccess(Handle hPlugin, int iNumParams) +{ + int client = GetNativeCell(1); + + if (IsValidClient(client)) + { + ga_iNameColorAccess[client] = GetNativeCell(2); + return true; + } + + return false; +} + +public int Native_SetChatColorAccess(Handle hPlugin, int iNumParams) +{ + int client = GetNativeCell(1); + + if (IsValidClient(client)) + { + ga_iChatColorAccess[client] = GetNativeCell(2); + return true; + } + + return false; +} + +public int Native_SetCompleteAccess(Handle hPlugin, int iNumParams) +{ + int client = GetNativeCell(1); + + if (IsValidClient(client)) + { + int iAccess = GetNativeCell(2); + ga_iTagColorAccess[client] = iAccess; + ga_iNameColorAccess[client] = iAccess; + ga_iChatColorAccess[client] = iAccess; + ga_iSetTagAccess[client] = iAccess; + return true; + } + + return false; +} + +//chat ignoring +public int Native_UpdateIgnoredArray(Handle plugin, int numParams) +{ + GetNativeArray(1, g_Ignored, sizeof(g_Ignored)); + + return 1; +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Client Connections ///////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void SetDefaults(int client) +{ + gaa_sSteamID[client][0] = ""; + gaa_sSteamID[client][1] = ""; + gaa_sSteamID[client][2] = ""; + gaa_sSteamID[client][3] = ""; + ga_sName[client] = ""; + ga_sEscapedName[client] = ""; + ga_bChatBlocked[client] = false; + ga_iTagVisible[client] = 0; + ga_iIsRestricted[client] = 0; + ga_iIsLoaded[client] = 0; + ga_iEntOverride[client] = 0; + gaa_iAdminOvrd[client][0] = 0; + gaa_iAdminOvrd[client][1] = 0; + gaa_iAdminOvrd[client][2] = 0; + gaa_iAdminOvrd[client][3] = 0; + gaa_sCleanSetupText[client][0] = ""; + gaa_sCleanSetupText[client][1] = ""; + gaa_sCleanSetupText[client][2] = ""; + gaa_sCleanSetupText[client][3] = ""; + ga_iChatMsgCnt[client] = 0; + ga_iChatSpamCnt[client] = 0; + + ga_sTagColor[client] = ""; + ga_sNameColor[client] = ""; + ga_sChatColor[client] = ""; + ga_sTag[client] = ""; + ga_sExtTag[client] = ""; + ga_sExtTagColor[client] = ""; + ga_iTagColorAccess[client] = -1; + ga_iNameColorAccess[client] = -1; + ga_iChatColorAccess[client] = -1; + ga_iSetTagAccess[client] = -1; + + b_InGroup[client] = false; +} + +public void OnClientConnected(int client) //get names as soon as they connect +{ + SetDefaults(client); +} + +public void OnClientDisconnect(int client) +{ + SetDefaults(client); +} + +public void OnClientPostAdminCheck(int client) +{ + if (IsFakeClient(client)) + { + Format(gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][]), "BOT"); + Format(gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][]), "BOT"); + Format(gaa_sSteamID[client][2], sizeof(gaa_sSteamID[][]), "BOT"); + Format(gaa_sSteamID[client][3], sizeof(gaa_sSteamID[][]), "BOT"); + return; + } + + GetClientAuthId(client, AuthId_Steam2, gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][])); + ReplaceString(gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][]), "STEAM_1", "STEAM_0", false); + GetClientAuthId(client, AuthId_Steam2, gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][])); + ReplaceString(gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][]), "STEAM_0", "STEAM_1", false); + GetClientAuthId(client, AuthId_Steam3, gaa_sSteamID[client][2], sizeof(gaa_sSteamID[][])); + GetClientAuthId(client, AuthId_SteamID64, gaa_sSteamID[client][3], sizeof(gaa_sSteamID[][])); + + if (StrContains(gaa_sSteamID[client][0], "STEAM_", true) == -1) //invalid - retry again + { + CreateTimer(10.0, RefreshSteamID, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + else + { + LoadClientData(client); + } +} + +public Action RefreshSteamID(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (!IsValidClient(client)) + { + return; + } + + GetClientAuthId(client, AuthId_Steam2, gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][])); + ReplaceString(gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][]), "STEAM_1", "STEAM_0", false); + GetClientAuthId(client, AuthId_Steam2, gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][])); + ReplaceString(gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][]), "STEAM_0", "STEAM_1", false); + GetClientAuthId(client, AuthId_Steam3, gaa_sSteamID[client][2], sizeof(gaa_sSteamID[][])); + GetClientAuthId(client, AuthId_SteamID64, gaa_sSteamID[client][3], sizeof(gaa_sSteamID[][])); + + if (StrContains(gaa_sSteamID[client][0], "STEAM_", true) == -1) //still invalid - retry again + { + CreateTimer(10.0, RefreshSteamID, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + else + { + LoadClientData(client); + } +} + +public Action RefreshSteamID_SendData(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (!IsValidClient(client)) + { + return; + } + + GetClientAuthId(client, AuthId_Steam2, gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][])); + ReplaceString(gaa_sSteamID[client][0], sizeof(gaa_sSteamID[][]), "STEAM_1", "STEAM_0", false); + GetClientAuthId(client, AuthId_Steam2, gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][])); + ReplaceString(gaa_sSteamID[client][1], sizeof(gaa_sSteamID[][]), "STEAM_0", "STEAM_1", false); + GetClientAuthId(client, AuthId_Steam3, gaa_sSteamID[client][2], sizeof(gaa_sSteamID[][])); + GetClientAuthId(client, AuthId_SteamID64, gaa_sSteamID[client][3], sizeof(gaa_sSteamID[][])); + + if (StrContains(gaa_sSteamID[client][0], "STEAM_", true) == -1) //still invalid - retry again + { + CreateTimer(10.0, RefreshSteamID_SendData, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + else + { + SendClientSetupToDB(client); + } +} + +public Action TimerCB_RetrySendData(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (!IsValidClient(client)) + { + return; + } + + if (g_oDatabase != null) //still invalid - retry again + { + CreateTimer(5.0, TimerCB_RetrySendData, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + else + { + SendClientSetupToDB(client); + } +} + +public Action RepeatCheck(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (IsValidClient(client)) + { + LoadClientData(client); + } +} + +bool HasFlags(int client, char[] sFlags) +{ + if (StrEqual(sFlags, "public", false) || StrEqual(sFlags, "", false)) + { + return true; + } + else if (StrEqual(sFlags, "none", false)) //useful for some plugins + { + return false; + } + else if (!client) //if rcon + { + return true; + } + else if (CheckCommandAccess(client, "sm_not_a_command", ADMFLAG_ROOT, true)) + { + return true; + } + + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID) + { + return false; + } + int flags, clientflags; + clientflags = GetUserFlagBits(client); + + if (StrContains(sFlags, ";", false) != -1) //check if multiple strings + { + int i = 0, iStrCount = 0; + while (sFlags[i] != '\0') + { + if (sFlags[i++] == ';') + { + iStrCount++; + } + } + iStrCount++; //add one more for stuff after last comma + + char[][] a_sTempArray = new char[iStrCount][30]; + ExplodeString(sFlags, ";", a_sTempArray, iStrCount, 30); + bool bMatching = true; + + for (i = 0; i < iStrCount; i++) + { + bMatching = true; + flags = ReadFlagString(a_sTempArray[i]); + for (int j = 0; j <= 20; j++) + { + if (bMatching) //if still matching, continue loop + { + if (flags & (1< g_cSpamMsgCnt.IntValue) + { + CreateTimer(0.1, TimerCB_MsgsTooFast, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + if (g_cEnableSpamGag.BoolValue) + { + ga_iChatSpamCnt[client]++; + CreateTimer(g_cSpamGagDetectDuration.FloatValue, TimerCB_ReduceSpamCnt, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + if (ga_iChatSpamCnt[client] > g_cSpamMsgGagCnt.IntValue) + { + BaseComm_SetClientGag(client, true); + + if (!BaseComm_IsClientGagged(client)) //check if not already gagged - you *shouldnt* be able to get here if gagged, but sometimes if the server sends SayText2 msgs enough, it might send you here + { + CreateTimer(0.1, TimerCB_Muted, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + } + } + + return 0; + } + } + } + + return 1; +} + +public Action TimerCB_ReduceMsgCnt(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (IsValidClient(client)) + { + ga_iChatMsgCnt[client]--; + } +} + +public Action TimerCB_ReduceSpamCnt(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (IsValidClient(client)) + { + ga_iChatSpamCnt[client]--; + } +} + +public Action TimerCB_Gagged(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (IsValidClient(client)) + { + PrintToChat(client, "================================="); + PrintToChat(client, " ƪ(ツ)ノ Nice Try! ( ͡° ͜ʖ ͡°)"); + PrintToChat(client, "--------- You're gagged and cannot speak. --------"); + PrintToChat(client, "(ノಠ益ಠ)ノ彡┻━┻ YOU MAD? ლ(ಠ益ಠლ)"); + PrintToChat(client, "================================="); + } +} + +public Action TimerCB_MsgsTooFast(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (IsValidClient(client)) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "TooFast"); + } +} + +public Action TimerCB_Muted(Handle hTimer, any iUserID) +{ + int client = GetClientOfUserId(iUserID); + if (IsValidClient(client)) + { + PrintToChatAll("%s%s%N %t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), client, "HasBeenGagged"); + } +} + +public Action Command_AdminChat(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), " ADMIN "); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "ADMIN ALL CHAT"); + } + } + return Plugin_Continue; +} + +public Action Command_AdminOnlyChat(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), "ADMIN ONLY"); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "ADMIN ONLY"); + } + } + return Plugin_Continue; +} + +public Action Command_CSay(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), " CSAY "); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "CENTER MSG"); + } + } + return Plugin_Continue; +} + +public Action Command_TSay(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), " TSAY "); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "TOP MSG"); + } + } + + return Plugin_Continue; +} + +public Action Command_MSay(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), " MSAY "); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "MENU MSG"); + } + } + + return Plugin_Continue; +} + +public Action Command_HSay(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), " HSAY "); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "HINT MSG"); + } + } + + return Plugin_Continue; +} + +public Action Command_PSay(int client, const char[] sCommand, int iArgC) +{ + if (!g_cLog.BoolValue) + { + return Plugin_Continue; + } + + char sBuffer[128], sName[MAX_NAME_LENGTH], sID[MAX_NAME_LENGTH]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (g_oDatabase != null) + { + if (!g_bMySQL) + { + if (IsValidClient(client)) + { + strcopy(sID, sizeof(sID), gaa_sSteamID[client][0]); + GetClientName(client, sName, sizeof(sName)); + } + else + { + Format(sID, sizeof(sID), "CONSOLE"); + Format(sName, sizeof(sName), "CONSOLE"); + } + + int iNamePadding = 33 - strlen(sName); + char[] sPad2 = new char[iNamePadding]; + char sLogMsg[256], sTeam[12]; + Format(sPad2, iNamePadding, " "); + Format(sLogMsg, sizeof(sLogMsg), "%s%s", sPad2, sName); + Format(sTeam, sizeof(sTeam), " PSAY "); + Format(sLogMsg, sizeof(sLogMsg), "[%20s][%s]%s: %s", sID, sTeam, sLogMsg, sBuffer); + LogToFileEx(g_sChatLogPath, sLogMsg); + } + else + { + SendChatToDB(client, sBuffer, sizeof(sBuffer), "PRIVATE MSG"); + } + } + + return Plugin_Continue; +} + +void SendChatToDB(int client, char[] sMsg, int iSize, char[] sChatGrp) +{ + StripQuotes(sMsg); + if (StrEqual(sMsg, "", false)) + { + return; + } + int iEscapeSize = 2*iSize + 1; + char[] sEscapedMsg = new char[iEscapeSize]; + g_oDatabase.Escape(sMsg, sEscapedMsg, iEscapeSize); + char sIP[MAX_NAME_LENGTH], sCountry[45], sName[MAX_NAME_LENGTH], sSteamID[65]; + char sQuery[1000]; + if (IsValidClient(client, true)) + { + if (IsFakeClient(client)) + { + sIP = "BOT"; + sCountry = "BOT"; + sSteamID = "BOT"; + } + else + { + GetClientIP(client, sIP, sizeof(sIP)); + GeoipCountry(sIP, sCountry, sizeof(sCountry)); + strcopy(sSteamID, sizeof(sSteamID), gaa_sSteamID[client][0]); + } + strcopy(sName, sizeof(sName), ga_sEscapedName[client]); + if (!StrEqual(sName, "", false)) + { + GetClientName(client, ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + CleanStringForSQL(ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + strcopy(sName, sizeof(sName), ga_sEscapedName[client]); + } + } + else + { + sIP = "CONSOLE"; + sCountry = "CONSOLE"; + sSteamID = "CONSOLE"; + sName = "CONSOLE"; + } + + CleanStringForSQL(sCountry, sizeof(sCountry)); + //Format(sQuery, sizeof(sQuery), "INSERT INTO `ct_chatlogs` (steamid, mapname, server, playername, playerip, ip_country, chatmsg, chatgrp) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');", sSteamID, g_sMapName, g_sServerIP, sName, sIP, sCountry, sEscapedMsg, sChatGrp); + Format(sQuery, sizeof(sQuery), "INSERT INTO `ct_chatlogs` (steamid, server, playername, playerip, ip_country, chatmsg, chatgrp) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s');", sSteamID, g_sServerIP, sName, sIP, sCountry, sEscapedMsg, sChatGrp); + g_oDatabase.Query(SQLCallback_Void, sQuery, 4); +} + +public Action Event_NameChange(Handle hEvent, const char[] sName, bool bDontBroadcast) +{ + int client = GetClientOfUserId(GetEventInt(hEvent, "userid")); + if (IsValidClient(client)) + { + if (g_oDatabase != null) + { + GetClientName(client, ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + strcopy(ga_sName[client], sizeof(ga_sName[]), ga_sEscapedName[client]); + CleanStringForSQL(ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + } + } + return Plugin_Continue; +} + +void Reload() +{ + LoadColorCfg(); + LoadCustomConfigs(); + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i) && AreClientCookiesCached(i) && IsClientAuthorized(i)) + { + OnClientConnected(i); + LoadClientData(i); + } + } + + Call_StartForward(g_hLoadFwd); + Call_Finish(); +} + +public void OnClientSettingsChanged(int client) +{ + if (IsValidClient(client)) + { + if (g_oDatabase != null) + { + GetClientName(client, ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + strcopy(ga_sName[client], sizeof(ga_sName[]), ga_sEscapedName[client]); + CleanStringForSQL(ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + CheckTag(client); + } + } +} + +////////////////////////////////////////////////////////////////////////// +///////////////////////////// Admin Commands ///////////////////////////// +////////////////////////////////////////////////////////////////////////// + +public Action Cmd_Reload(int client, int iArgs) +{ + if (client != 0) + { + if (!HasFlags(client, g_sAdminFlag)) + { + PrintToConsole(client, "%sYou do not have access to this command!", g_sTag); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + } + + Reload(); + + ReplyToCommand(client, "%s%sColors setups are now reloaded.", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + + return Plugin_Handled; +} + +public Action Cmd_Unload(int client, int iArgs) +{ + if (client != 0) + { + if (!HasFlags(client, g_sAdminFlag)) + { + PrintToConsole(client, "%sYou do not have access to this command!", g_sTag); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + } + + ReplyToCommand(client, "%s%sChat Tags is now unloaded until map change!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + + char sPluginName[128]; + GetPluginFilename(INVALID_HANDLE, sPluginName, sizeof(sPluginName)); + ServerCommand("sm plugins unload %s", sPluginName); + + return Plugin_Handled; +} + +public Action Cmd_RemoveOverride(int client, int iArgs) +{ + if (client != 0) + { + if (!HasFlags(client, g_sAdminFlag_Force)) + { + PrintToConsole(client, "%sYou do not have access to this command!", g_sTag); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + } + + if (iArgs != 1) + { + if (client != 0) + { + PrintToConsole(client, "%sUsage: sm_removeoverride ", g_sTag); + PrintToChat(client, "%s%sUsage: sm_removeoverride ", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + } + else + { + ReplyToCommand(client, "%s%sUsage: sm_removeoverride ", g_sTag, CSGO_WHITE); + } + return Plugin_Handled; + } + + char sTarget[65], sTargetName[MAX_TARGET_LENGTH]; + GetCmdArg(1, sTarget, sizeof(sTarget)); + int a_iTargets[MAXPLAYERS], iTargetCount; + bool bTN_ML; + if ((iTargetCount = ProcessTargetString(sTarget, client, a_iTargets, MAXPLAYERS, COMMAND_FILTER_NO_IMMUNITY, sTargetName, sizeof(sTargetName), bTN_ML)) <= 0) + { + ReplyToCommand(client, "%s%sNot found or invalid parameter.", g_sTag, CSGO_WHITE); + return Plugin_Handled; + } + + for (int i = 0; i < iTargetCount; i++) + { + int target = a_iTargets[i]; + if (IsValidClient(target)) + { + gaa_iAdminOvrd[target][0] = 0; + gaa_iAdminOvrd[target][1] = 0; + gaa_iAdminOvrd[target][2] = 0; + gaa_iAdminOvrd[target][3] = 0; + SendClientSetupToDB(target); + + LogMessage("%L has removed the admin overrides for player %L", client, target); + ReplyToCommand(client, "%s%sRemoved setup for player: %N", g_sTag, CSGO_WHITE); + } + } + + return Plugin_Handled; +} + +public Action Cmd_ForceTag(int client, int iArgs) +{ + if (client != 0) + { + if (!HasFlags(client, g_sAdminFlag_Force)) + { + PrintToConsole(client, "%sYou do not have access to this command!", g_sTag); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + } + + if (iArgs < 2) + { + if (client != 0) + { + PrintToConsole(client, "%sUsage: sm_forcetag (pass \"skip\" to skip overriding one, or \"remove\" to reset it - omitted args assume skip).", g_sTag); + PrintToChat(client, "%s%sUsage: sm_forcetag (pass \"skip\" to skip overriding one, or \"remove\" to reset it - omitted args assume skip).", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + else + { + ReplyToCommand(client, "%s%sUsage: sm_forcetag (pass \"skip\" to skip overriding one, or \"remove\" to reset it - omitted args assume skip).", g_sTag, CSGO_WHITE); + return Plugin_Handled; + } + } + + char sTarget[65], sTargetName[MAX_TARGET_LENGTH]; + GetCmdArg(1, sTarget, sizeof(sTarget)); + int a_iTargets[MAXPLAYERS], iTargetCount; + bool bTN_ML; + if ((iTargetCount = ProcessTargetString(sTarget, client, a_iTargets, MAXPLAYERS, COMMAND_FILTER_NO_IMMUNITY, sTargetName, sizeof(sTargetName), bTN_ML)) <= 0) + { + ReplyToCommand(client, "%s%sNot found or invalid parameter.", g_sTag, CSGO_WHITE); + return Plugin_Handled; + } + + int iSize = MAXTAGSIZE; + if (iSize < 30) + { + iSize = 30; + } + + char[] sTag = new char[iSize]; + char sTagColor[32], sNameColor[32], sChatColor[32]; + + GetCmdArg(2, sTag, iSize); + + if (iArgs > 2) + { + GetCmdArg(3, sTagColor, sizeof(sTagColor)); + } + else + { + Format(sTagColor, sizeof(sTagColor), "skip"); + } + + if (iArgs > 3) + { + GetCmdArg(4, sNameColor, sizeof(sNameColor)); + } + else + { + Format(sNameColor, sizeof(sNameColor), "skip"); + } + + if (iArgs > 4) + { + GetCmdArg(5, sChatColor, sizeof(sChatColor)); + } + else + { + Format(sChatColor, sizeof(sChatColor), "skip"); + } + + for (int i = 0; i < iTargetCount; i++) + { + int target = a_iTargets[i]; + if (IsValidClient(target)) + { + if (StrEqual(sTag, "remove", false)) + { + ga_iTagVisible[target] = 0; + ga_sTag[target] = ""; + gaa_iAdminOvrd[target][0] = 1; + gaa_sCleanSetupText[target][0] = ""; + } + else if (!StrEqual(sTag, "skip", false)) + { + ga_iTagVisible[target] = 3; + strcopy(ga_sTag[target], sizeof(ga_sTag[]), sTag); + strcopy(gaa_sCleanSetupText[target][0], sizeof(gaa_sCleanSetupText[][]), sTag); + gaa_iAdminOvrd[target][0] = 1; + gaa_sCleanSetupText[target][0] = ""; + } + + if (StrEqual(sTagColor, "remove", false)) + { + ga_sTagColor[target] = ""; + gaa_sCleanSetupText[target][1] = ""; + gaa_iAdminOvrd[target][1] = 1; + } + else if (!StrEqual(sTagColor, "skip", false)) + { + strcopy(ga_sTagColor[target], sizeof(ga_sTagColor[]), sTagColor); + strcopy(gaa_sCleanSetupText[target][1], sizeof(gaa_sCleanSetupText[][]), sTagColor); + gaa_iAdminOvrd[target][1] = 1; + } + + if (StrEqual(sNameColor, "remove", false)) + { + ga_sNameColor[target] = ""; + gaa_sCleanSetupText[target][2] = ""; + gaa_iAdminOvrd[target][2] = 1; + } + else if (!StrEqual(sNameColor, "skip", false)) + { + strcopy(ga_sNameColor[target], sizeof(ga_sNameColor[]), sNameColor); + strcopy(gaa_sCleanSetupText[target][2], sizeof(gaa_sCleanSetupText[][]), sNameColor); + gaa_iAdminOvrd[target][2] = 1; + } + + if (StrEqual(sChatColor, "remove", false)) + { + ga_sChatColor[target] = ""; + gaa_sCleanSetupText[target][3] = ""; + gaa_iAdminOvrd[target][3] = 1; + } + else if (!StrEqual(sChatColor, "skip", false)) + { + strcopy(ga_sChatColor[target], sizeof(ga_sChatColor[]), sChatColor); + strcopy(gaa_sCleanSetupText[target][3], sizeof(gaa_sCleanSetupText[][]), sChatColor); + gaa_iAdminOvrd[target][3] = 1; + } + + FormatColors(target); + SendClientSetupToDB(target); + + if (IsValidClient(client)) + { + PrintToChat(client, "%s%sTags/colors successfully set to: %s%s %s%N: %sChat colors...", g_sTag, CSGO_WHITE, ga_sTagColor[target], ga_sTag[target], ga_sNameColor[target], target, ga_sChatColor[target]); + } + + LogMessage("%L has set an admin overrides for player %L: %s%s %s%N: %sChat colors...", client, target, ga_sTagColor[target], ga_sTag[target], ga_sNameColor[target], target, ga_sChatColor[target]); + } + } + + return Plugin_Handled; +} + +public Action Cmd_RemoveTag(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%sMust be in the server to execute command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (iArgs != 1) + { + ReplyToCommand(client, "%s%sUsage: sm_removetag ", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAdminFlag)) + { + PrintToConsole(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + char sTargetArg[MAX_NAME_LENGTH]; + GetCmdArg(1,sTargetArg,sizeof(sTargetArg)); + + int iPlayers = SearchForPlayer(sTargetArg); + if (iPlayers == 0) + { + ReplyToCommand(client, "%s%sNo valid clients found!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + else if (iPlayers > 1) + { + ReplyToCommand(client, "%s%sMore than one matching player found!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + int target = 0; + + if (iPlayers == -1) + { + ReplaceString(sTargetArg, sizeof(sTargetArg), "#", "", false); + target = GetClientOfUserId(StringToInt(sTargetArg)); + } + else + { + target = FindTarget(client, sTargetArg, true); + } + + if (!IsValidClient(target)) + { + ReplyToCommand(client, "%s%sInvalid target!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (IsFakeClient(target)) + { + ReplyToCommand(client, "%s%sCannot target bots!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + RemoveSetup(target); + ReplyToCommand(client, "%s%sTag settings for player '%s' are now set to default.", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + SendClientSetupToDB(target); + + return Plugin_Handled; +} + +void RemoveSetup(int client) +{ + if (!IsValidClient(client)) + { + return; + } + + ga_iTagVisible[client] = 0; + //ga_iIsRestricted[client] = 0; + ga_sTagColor[client] = ""; + ga_sNameColor[client] = ""; + ga_sChatColor[client] = ""; + gaa_sCleanSetupText[client][0] = ""; + gaa_sCleanSetupText[client][1] = ""; + gaa_sCleanSetupText[client][2] = ""; + gaa_sCleanSetupText[client][3] = ""; + ga_sTag[client] = ""; + + if (g_cGrpsAfterRemove.BoolValue) + { + CheckForGroups(client); + } + + FormatColors(client); + SendClientSetupToDB(client); +} + +public Action Cmd_Restrict(int client, int iArgs) +{ + if (client == 0) + { + ReplyToCommand(client, "%s%sMust be in the server to execute command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (iArgs != 1) + { + ReplyToCommand(client, "%s%sUsage: sm_restricttag ", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAdminFlag)) + { + PrintToConsole(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + char sTargetArg[MAX_NAME_LENGTH]; + GetCmdArg(1,sTargetArg,sizeof(sTargetArg)); + + int iPlayers = SearchForPlayer(sTargetArg); + if (iPlayers == 0) + { + ReplyToCommand(client, "%s%sNo valid clients found!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + else if (iPlayers > 1) + { + ReplyToCommand(client, "%s%sMore than one matching player found!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + int target = 0; + + if (iPlayers == -1) + { + ReplaceString(sTargetArg, sizeof(sTargetArg), "#", "", false); + target = GetClientOfUserId(StringToInt(sTargetArg)); + } + else + { + target = FindTarget(client, sTargetArg, true); + } + + if (!IsValidClient(target)) + { + ReplyToCommand(client, "%s%sInvalid target!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (IsFakeClient(target)) + { + ReplyToCommand(client, "%s%sCannot target bots!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + RestrictPlayer(client,target); + SendClientSetupToDB(target); + + return Plugin_Handled; +} + +void RestrictPlayer(int client, int target) +{ + if (!IsValidClient(target)) + { + PrintToConsole(client, "%s%sTarget '%s' is either not in game, or is a bot!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + PrintToChat(client, "%s%sTarget '%s' is either not in game, or is a bot!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + return; + } + else if (ga_iIsRestricted[target] == 1) + { + PrintToConsole(client, "%s%sTarget '%s' is already restricted!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + PrintToChat(client, "%s%sTarget '%s' is already restricted!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + return; + } + else + { + PrintToConsole(client, "%s%s'%s' is now restricted from changing their chat tag!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + PrintToChatAll("%s%s%s%s has restricted \x01%s%s from changing their chat tag!", g_sTag, CSGO_WHITE, ga_sEscapedName[client], g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target], g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + + ga_iIsRestricted[target] = 1; + SendClientSetupToDB(target); + } +} + +public Action Cmd_Unrestrict(int client, int iArgs) +{ + if (client == 0) + { + ReplyToCommand(client, "%s%sMust be in the server to execute command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (iArgs != 1) + { + ReplyToCommand(client, "%s%sUsage: sm_unrestricttag ", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAdminFlag)) + { + PrintToConsole(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + PrintToChat(client, "%s%sYou do not have access to this command!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + char sTargetArg[MAX_NAME_LENGTH]; + GetCmdArg(1,sTargetArg,sizeof(sTargetArg)); + + int iPlayers = SearchForPlayer(sTargetArg); + if (iPlayers == 0) + { + ReplyToCommand(client, "%s%sNo valid clients found!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + else if (iPlayers > 1) + { + ReplyToCommand(client, "%s%sMore than one matching player found!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + int target = 0; + + if (iPlayers == -1) + { + ReplaceString(sTargetArg, sizeof(sTargetArg), "#", "", false); + target = GetClientOfUserId(StringToInt(sTargetArg)); + } + else + { + target = FindTarget(client, sTargetArg, true); + } + + if (!IsValidClient(target)) + { + ReplyToCommand(client, "%s%sInvalid target!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + if (IsFakeClient(target)) + { + ReplyToCommand(client, "%s%sCannot target bots!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + return Plugin_Handled; + } + + UnrestrictPlayer(client,target); + SendClientSetupToDB(target); + + return Plugin_Handled; +} + +void UnrestrictPlayer(int client, int target) +{ + if (!IsValidClient(target)) + { + PrintToConsole(client, "%s%sTarget '%s' is either not in game, or is a bot!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + PrintToChat(client, "%s%sTarget '%s' is either not in game, or is a bot!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + return; + } + else if (ga_iIsRestricted[target] == 0) + { + PrintToConsole(client, "%s%sTarget '%s' is not restricted!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + PrintToChat(client, "%s%sTarget '%s' is not restricted!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + return; + } + else + { + PrintToConsole(client, "%s%sTarget '%s' is now unrestricted from changing their chat tag!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target]); + PrintToChatAll("%s%s%s%s has unrestricted \x01%s%s from changing their chat tag!", g_sTag, CSGO_WHITE, ga_sEscapedName[client], g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), ga_sEscapedName[target], g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + + ga_iIsRestricted[target] = 0; + SendClientSetupToDB(client); + } +} + +//////////////////////////////////////////////////////////////////// +///////////////////////////// Commands ///////////////////////////// +//////////////////////////////////////////////////////////////////// + +public Action Command_Tag(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%s%t", g_sTag, CSGO_WHITE, "MustBeInGame"); + return Plugin_Handled; + } + + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "RestrictedChangingTags"); + return Plugin_Handled; + } + if (!ga_iIsLoaded[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledUntilCookiesCached"); + return Plugin_Handled; + } + + Menu_MainTag(client); + return Plugin_Handled; +} + +public Action Command_BlockChat(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%s%t", g_sTag, CSGO_WHITE, "MustBeInGame"); + return Plugin_Handled; + } + + if (!g_cAllowBlockChat.BoolValue) + { + ReplyToCommand(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledByServerManager"); + return Plugin_Handled; + } + + if (ga_bChatBlocked[client]) + { + ga_bChatBlocked[client] = false; + ReplyToCommand(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "EnabledChatBlocker"); + } + else + { + ga_bChatBlocked[client] = true; + ReplyToCommand(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledChatBlocker"); + } + + return Plugin_Handled; +} + +public Action Command_SetText(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%s%t", g_sTag, CSGO_WHITE, "MustBeInGame"); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAccessFlag) && (ga_iSetTagAccess[client] != 1)) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DoNotHaveAccess"); + return Plugin_Handled; + } + + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "RestrictedChangingTags"); + return Plugin_Handled; + } + + if (!ga_iSetTagAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeTagByExternalPlugin"); + return Plugin_Handled; + } + + if (!ga_iIsLoaded[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledUntilCookiesCached"); + return Plugin_Handled; + } + + if (!g_cEnableTags.BoolValue) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledForEveryoneByServerManager"); + return Plugin_Handled; + } + + //char sArg[MAXTAGSIZE]; + char sArg[128]; + GetCmdArgString(sArg, sizeof(sArg)); + + int iBlockedName = 0; + + for (int i = 0; i < g_aBlockedTags.Length; i++) + { + char sBuffer[75]; + g_aBlockedTags.GetString(i, sBuffer, sizeof(sBuffer)); + if (StrContains(sArg, sBuffer, false) != -1) + { + iBlockedName = 1; + } + } + + if (iBlockedName) + { + if (!HasFlags(client, g_sAdminFlag)) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "TagBlockedFromUse"); + return Plugin_Handled; + } + } + + if (CheckTag(client)) + { + return Plugin_Handled; + } + + //pS Mod + if (HasFlags(client, g_sAccessFlag) || (ga_iTagColorAccess[client] == 1)) + CFormatColor(sArg, sizeof(sArg), client, false); + + PrintToChat(client, "%s%s%t %s", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "TagSetTo", sArg); + strcopy(ga_sTag[client], sizeof(ga_sTag[]), sArg); + strcopy(gaa_sCleanSetupText[client][0], sizeof(gaa_sCleanSetupText[][]), sArg); + ga_iTagVisible[client] = 1; + FormatColors(client); + SendClientSetupToDB(client); + + return Plugin_Handled; +} + +public Action Command_TagColor(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%s%t", g_sTag, CSGO_WHITE, "MustBeInGame"); + return Plugin_Handled; + } + + if (g_bCSGO) + { + ReplyToCommand(client, "%s\x03%t", g_sTag, "NotAvailableForCSGO"); + return Plugin_Handled; + } + else if (g_bIns) + { + ReplyToCommand(client, "%s\x03%t", g_sTag, "NotAvailableForInsurgency"); + return Plugin_Handled; + } + + if (iArgs != 1) + { + ReplyToCommand(client, "%s\x03%t sm_tagcolor ", g_sTag, "Usage"); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAccessFlag) && (ga_iTagColorAccess[client] != 1)) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DoNotHaveAccess"); + return Plugin_Handled; + } + + if (!ga_iTagColorAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeTagColorByExternalPlugin"); + return Plugin_Handled; + } + + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s\x03%t", g_sTag, "RestrictedChangingTagColors"); + return Plugin_Handled; + } + + if (!ga_iIsLoaded[client]) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DisabledUntilCookiesCached"); + return Plugin_Handled; + } + + if (!g_cEnableTagColors.BoolValue || !g_cEnableTags.BoolValue) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DisabledForEveryoneByServerManager"); + return Plugin_Handled; + } + + char sArg[32]; + GetCmdArgString(sArg, sizeof(sArg)); + ReplaceString(sArg, sizeof(sArg), "#", "", false); + + if (!IsValidHex(sArg)) + { + ReplyToCommand(client, "%s\x03%t %t sm_tagcolor ", g_sTag, "InvalidHex", "Usage"); + return Plugin_Handled; + } + + + if (CheckTag(client)) + { + return Plugin_Handled; + } + + PrintToChat(client, "\x01%s\x03%t \x07%s %s", g_sTag, "TagColorSetTo", sArg, sArg); + strcopy(ga_sTagColor[client], sizeof(ga_sTagColor[]), sArg); + strcopy(gaa_sCleanSetupText[client][1], sizeof(gaa_sCleanSetupText[][]), sArg); + FormatColors(client); + SendClientSetupToDB(client); + + return Plugin_Handled; +} + +public Action Command_NameColor(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%s%t", g_sTag, CSGO_WHITE, "MustBeInGame"); + return Plugin_Handled; + } + + if (g_bCSGO) + { + ReplyToCommand(client, "%s\x03%t", g_sTag, "NotAvailableForCSGO"); + return Plugin_Handled; + } + else if (g_bIns) + { + ReplyToCommand(client, "%s\x03%t", g_sTag, "NotAvailableForInsurgency"); + return Plugin_Handled; + } + + if (iArgs != 1) + { + ReplyToCommand(client, "%s\x03%t sm_namecolor ", g_sTag, "Usage"); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAccessFlag) && (ga_iNameColorAccess[client] != 1)) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DoNotHaveAccess"); + return Plugin_Handled; + } + + if (!ga_iNameColorAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeNameColorByExternalPlugin"); + return Plugin_Handled; + } + + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s\x03%t", g_sTag, "RestrictedChangingNameColors"); + return Plugin_Handled; + } + + if (!ga_iIsLoaded[client]) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DisabledUntilCookiesCached"); + return Plugin_Handled; + } + + if (!g_cEnableNameColors.BoolValue) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DisabledForEveryoneByServerManager"); + return Plugin_Handled; + } + + char sArg[32]; + GetCmdArgString(sArg, sizeof(sArg)); + ReplaceString(sArg, sizeof(sArg), "#", "", false); + + if (!IsValidHex(sArg)) + { + ReplyToCommand(client, "%s\x03%t %t sm_tagcolor ", g_sTag, "InvalidHex", "Usage"); + return Plugin_Handled; + } + + if (CheckTag(client)) + { + return Plugin_Handled; + } + + PrintToChat(client, "\x01%s\x03%t \x07%s %s", g_sTag, "NameColorSetTo", sArg, sArg); + strcopy(ga_sNameColor[client], sizeof(ga_sNameColor[]), sArg); + strcopy(gaa_sCleanSetupText[client][2], sizeof(gaa_sCleanSetupText[][]), sArg); + FormatColors(client); + SendClientSetupToDB(client); + + return Plugin_Handled; +} + +public Action Command_ChatColor(int client, int iArgs) +{ + if (!IsValidClient(client)) + { + ReplyToCommand(client, "%s%s%t", g_sTag, CSGO_WHITE, "MustBeInGame"); + return Plugin_Handled; + } + + if (g_bCSGO) + { + ReplyToCommand(client, "%s\x03%t", g_sTag, "NotAvailableForCSGO"); + return Plugin_Handled; + } + else if (g_bIns) + { + ReplyToCommand(client, "%s\x03%t", g_sTag, "NotAvailableForInsurgency"); + return Plugin_Handled; + } + + if (iArgs != 1) + { + ReplyToCommand(client, "%s\x03%t sm_chatcolor ", g_sTag, "Usage"); + return Plugin_Handled; + } + + if (!HasFlags(client, g_sAccessFlag) && (ga_iChatColorAccess[client] != 1)) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DoNotHaveAccess"); + return Plugin_Handled; + } + + if (!ga_iChatColorAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeChatColorByExternalPlugin"); + return Plugin_Handled; + } + + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s\x03%t", g_sTag, "RestrictedChangingChatColors"); + return Plugin_Handled; + } + + if (!ga_iIsLoaded[client]) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DisabledUntilCookiesCached"); + return Plugin_Handled; + } + + if (!g_cEnableChatColors.BoolValue) + { + PrintToChat(client, "%s\x03%t", g_sTag, "DisabledForEveryoneByServerManager"); + return Plugin_Handled; + } + + char sArg[32]; + GetCmdArgString(sArg, sizeof(sArg)); + ReplaceString(sArg, sizeof(sArg), "#", "", false); + + if (!IsValidHex(sArg)) + { + ReplyToCommand(client, "%s\x03%t %t sm_tagcolor ", g_sTag, "InvalidHex", "Usage"); + return Plugin_Handled; + } + + if (CheckTag(client)) + { + return Plugin_Handled; + } + + PrintToChat(client, "\x01%s\x03%t \x07%s %s", g_sTag, "ChatColorSetTo", sArg, sArg); + strcopy(ga_sChatColor[client], sizeof(ga_sChatColor[]), sArg); + strcopy(gaa_sCleanSetupText[client][3], sizeof(gaa_sCleanSetupText[][]), ga_sChatColor[client]); + FormatColors(client); + SendClientSetupToDB(client); + + return Plugin_Handled; +} + +public Action Command_CheckTag(int client, int iArgs) +{ + if (iArgs != 1) + { + ReplyToCommand(client, "%s%s%t sm_checktag ", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "Usage"); + return Plugin_Handled; + } + + char sTarget[65], sTargetName[MAX_TARGET_LENGTH]; + GetCmdArg(1, sTarget, sizeof(sTarget)); + int a_iTargets[MAXPLAYERS], iTargetCount; + bool bTN_ML; + if ((iTargetCount = ProcessTargetString(sTarget, client, a_iTargets, MAXPLAYERS, COMMAND_FILTER_NO_IMMUNITY, sTargetName, sizeof(sTargetName), bTN_ML)) <= 0) + { + ReplyToCommand(client, "%t", "NotFoundOrInvalidParameter"); + return Plugin_Handled; + } + + for (int i = 0; i < iTargetCount; i++) + { + int target = a_iTargets[i]; + if (IsValidClient(target)) + { + char sHiddenTag[10], sRestricted[24], sSetTag[10], sTagColor[10], sNameColor[10], sChatColor[10]; + if (ga_iTagVisible[target]) + { + sHiddenTag = "Visible"; + } + else + { + sHiddenTag = "Hidden"; + } + + if (ga_iIsRestricted[target]) + { + sRestricted = "Restricted"; + } + else + { + sRestricted = "Not Restricted"; + } + + if (!ga_iSetTagAccess[target]) + { + sSetTag = "Denied"; + } + else if (ga_iSetTagAccess[target] == 1) + { + sSetTag = "Granted"; + } + else + { + sSetTag = "Default"; + } + + if (!ga_iTagColorAccess[target]) + { + sTagColor = "Denied"; + } + else if (ga_iTagColorAccess[target] == 1) + { + sTagColor = "Granted"; + } + else + { + sTagColor = "Default"; + } + + if (!ga_iNameColorAccess[target]) + { + sNameColor = "Denied"; + } + else if (ga_iNameColorAccess[target] == 1) + { + sNameColor = "Granted"; + } + else + { + sNameColor = "Default"; + } + + if (!ga_iChatColorAccess[target]) + { + sChatColor = "Denied"; + } + else if (ga_iChatColorAccess[target] == 1) + { + sChatColor = "Granted"; + } + else + { + sChatColor = "Default"; + } + + if (IsValidClient(client)) + { + PrintToConsole(client, "-------------------------- PLAYER TAG INFO --------------------------"); + PrintToConsole(client, "Player: %L, Status: \"%s\", Tag status: \"%s\"", target, sRestricted, sHiddenTag); + PrintToConsole(client, "Tag Color: \"%s\", Name Color: \"%s\", Chat Color: \"%s\"", gaa_sCleanSetupText[target][1], gaa_sCleanSetupText[target][2], gaa_sCleanSetupText[target][3]); + PrintToConsole(client, "Access overrides - Set Tag: %s, Tag color: %s", sSetTag, sTagColor); + PrintToConsole(client, "Access overrides - Name color: %s, Chat color: %s", sNameColor, sChatColor); + } + else + { + ReplyToCommand(client, "-------------------------- PLAYER TAG INFO --------------------------"); + ReplyToCommand(client, "Player: %L, Status: \"%s\", Tag status: \"%s\"", target, sRestricted, sHiddenTag); + ReplyToCommand(client, "Tag Color: \"%s\", Name Color: \"%s\", Chat Color: \"%s\"", gaa_sCleanSetupText[target][1], gaa_sCleanSetupText[target][2], gaa_sCleanSetupText[target][3]); + ReplyToCommand(client, "Access overrides - Set Tag: %s, Tag color: %s", sSetTag, sTagColor); + ReplyToCommand(client, "Access overrides - Name color: %s, Chat color: %s", sNameColor, sChatColor); + } + } + } + + if (IsValidClient(client)) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "CheckConsole"); + } + + return Plugin_Handled; +} + +int SearchForPlayer(const char[] sTarget) +{ + if (sTarget[0] == '#') + { + bool bUserID = true; + for (int i = 1; i < strlen(sTarget); i++) + { + if (!IsCharNumeric(sTarget[i])) + { + bUserID = false; + break; + } + } + + if (bUserID) + { + return -1; + } + } + + char sName[MAX_NAME_LENGTH]; + int iNumberFound = 0; + + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i)) + { + GetClientName(i, sName, sizeof(sName)); + + if (StrContains(sName, sTarget, false) != -1) + { + iNumberFound++; + } + } + } + return iNumberFound; +} + +//check if name/tag combo should be allowed - this section can be used to define setups that are not allowed +bool CheckTag(int client) +{ + //check if imitating console + char sName[MAX_NAME_LENGTH]; + GetClientName(client, sName, sizeof(sName)); + TrimString(sName); + if (StrEqual(sName, g_sConsoleName, false)) + { + RemoveSetup(client); + PrintToChat(client, "%s\x07%t", g_sTag, "MimickingCONSOLE"); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////// +///////////////////////////// Cookies ///////////////////////////// +/////////////////////////////////////////////////////////////////// + +void LoadClientData(int client) +{ + if (!IsValidClient(client)) + { + return; + } + + if (StrContains(gaa_sSteamID[client][0], "STEAM_", true) == -1) //invalid + { + CreateTimer(2.0, RefreshSteamID, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + return; + } + + if (g_oDatabase != null) + { + GetClientName(client, ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + strcopy(ga_sName[client], sizeof(ga_sName[]), ga_sEscapedName[client]); + CleanStringForSQL(ga_sEscapedName[client], sizeof(ga_sEscapedName[])); + + char sQuery[300]; + Format(sQuery, sizeof(sQuery), "SELECT `visible`, `restricted`, `tagtext`, `tagcolor`, `namecolor`, `chatcolor`, `ovrd_ttext`, `ovrd_tcolor`, `ovrd_ncolor`, `ovrd_ccolor` FROM `%s` WHERE (`steamid` = '%s') ORDER BY `id` LIMIT 1", g_sDBTableName, gaa_sSteamID[client][0]); + g_oDatabase.Query(SQLCallback_LoadPlayer, sQuery, GetClientUserId(client)); + } + else + { + CreateTimer(5.0, RepeatCheck, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } +} + +public void SQLCallback_LoadPlayer(Database oDB, DBResultSet oResults, const char[] sError, any iUserID) +{ + if (oResults == INVALID_HANDLE) + { + //SetFailState("Player load callback error: %s", sError); + LogMessage("Player load callback error: %s", sError); + return; + } + + int client = GetClientOfUserId(iUserID); + if (!IsValidClient(client)) + { + return; + } + else + { + if (oResults.RowCount == 1) + { + /* TABLE SETUP: + `id` int(20) PRIMARY KEY + `steamid` VARCHAR(32) NOT NULL + `tagtext` VARCHAR(32) NOT NULL + `visible` INT(2) NOT NULL + `restricted` INT(2) NOT NULL + `tagcolor` VARCHAR(10) NOT NULL + `namecolor` VARCHAR(10) NOT NULL + `chatcolor` VARCHAR(10) NOT NULL + `ovrd_ttext` INT(2) NOT NULL + `ovrd_tcolor` INT(2) NOT NULL + `ovrd_ncolor` INT(2) NOT NULL + `ovrd_ccolor` INT(2) NOT NULL + QUERY ORDER: + `visible`, `restricted`, `tagtext`, `tagcolor`, `namecolor`, `chatcolor`, `ovrd_ttext`, `ovrd_tcolor`, `ovrd_ncolor`, `ovrd_ccolor` + */ + char sBuffer[128]; + oResults.FetchRow(); + IntToString(oResults.FetchInt(0), sBuffer, sizeof(sBuffer)); + if (StrEqual(sBuffer, "", false) || (StrEqual(sBuffer, "0", false) && !ga_iTagVisible[client])) //if they dont have access and the DB has it saved as disabled + { + ga_iTagVisible[client] = 0; + } + else + { + ga_iTagVisible[client] = StringToInt(sBuffer); + } + + IntToString(oResults.FetchInt(1), sBuffer, sizeof(sBuffer)); + if (StrEqual(sBuffer, "", false)) + { + ga_iIsRestricted[client] = 0; + } + else + { + ga_iIsRestricted[client] = StringToInt(sBuffer); + } + oResults.FetchString(2, ga_sTag[client], sizeof(ga_sTag[])); + oResults.FetchString(3, ga_sTagColor[client], sizeof(ga_sTagColor[])); + oResults.FetchString(4, ga_sNameColor[client], sizeof(ga_sNameColor[])); + oResults.FetchString(5, ga_sChatColor[client], sizeof(ga_sChatColor[])); + gaa_iAdminOvrd[client][0] = oResults.FetchInt(6); + gaa_iAdminOvrd[client][1] = oResults.FetchInt(7); + gaa_iAdminOvrd[client][2] = oResults.FetchInt(8); + gaa_iAdminOvrd[client][3] = oResults.FetchInt(9); + + CheckTag(client); //this is put before group tags so that group ones can bypass this if the manager wants it (e.g. have root admins be tagged "CONSOLE"?) + //load group setups if they dont have access per defined access flags. + if (!HasFlags(client, g_sAccessFlag)) + { + CheckForGroups(client); + } + else if (!ga_iTagVisible[client]) //has flags, but "disabled" (if they dont want a tag, they can set the text to ""). + { //This isnt combined with the above check since they might not have the flag, but their cookie says "enabled". That would block the groups for such a situation. + CheckForGroups(client); + } + + FormatColors(client); + ga_iIsLoaded[client] = 1; + Call_StartForward(g_hClientLoadFwd); + Call_PushCell(client); + Call_Finish(); + } + else if (!oResults.RowCount) + { + SendClientSetupToDB(client, false, true); + //load group setups if they dont have access per defined access flags. + if (!HasFlags(client, g_sAccessFlag)) + { + CheckForGroups(client); + } + else if (!ga_iTagVisible[client]) //has flags, but "disabled" (if they dont want a tag, they can set the text to ""). + { //This isnt combined with the above check since they might not have the flag, but their cookie says "enabled". That would block the groups for such a situation. + CheckForGroups(client); + } + + FormatColors(client); + ga_iIsLoaded[client] = 1; + Call_StartForward(g_hClientLoadFwd); + Call_PushCell(client); + Call_Finish(); + } + else if (g_oDatabase == null) + { + CreateTimer(5.0, RepeatCheck, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } + } +} + +void SendClientSetupToDB(int client, bool bBlockAccess = false, bool bDefault = false) +{ + if (!IsValidClient(client)) + { + return; + } + + if (StrContains(gaa_sSteamID[client][0], "STEAM_", true) == -1) //invalid + { + CreateTimer(2.0, RefreshSteamID_SendData, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + return; + } + + if (g_oDatabase != null) + { + /* TABLE SETUP: + `id` INT(20) PRIMARY KEY + `steamid` VARCHAR(32) NOT NULL + `tagtext` VARCHAR(32) NOT NULL + `visible` INT(2) NOT NULL + `restricted` INT(2) NOT NULL + `tagcolor` VARCHAR(10) NOT NULL + `namecolor` VARCHAR(10) NOT NULL + `chatcolor` VARCHAR(10) NOT NULL + `ovrd_ttext` INT(2) NOT NULL + `ovrd_tcolor` INT(2) NOT NULL + `ovrd_ncolor` INT(2) NOT NULL + `ovrd_ccolor` INT(2) NOT NULL + */ + + char sQuery[750], sTagText[MAX_NAME_LENGTH * 2 + 1], sTagColor[MAX_NAME_LENGTH * 2 + 1], sNameColor[MAX_NAME_LENGTH * 2 + 1], sChatColor[MAX_NAME_LENGTH * 2 + 1]; + if (bDefault) + { + Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`steamid`, `tagtext`, `visible`, `restricted`, `tagcolor`, `namecolor`, `chatcolor`, `ovrd_ttext`, `ovrd_tcolor`, `ovrd_ncolor`, `ovrd_ccolor`) VALUES('%s', '', 0, 0, '', '', '', 0, 0, 0, 0)", g_sDBTableName, gaa_sSteamID[client][0]); + g_oDatabase.Query(SQLCallback_Void, sQuery, 2); + } + else + { + g_oDatabase.Escape(ga_sTag[client], sTagText, sizeof(sTagText)); + g_oDatabase.Escape(gaa_sCleanSetupText[client][1], sTagColor, sizeof(sTagColor)); + g_oDatabase.Escape(gaa_sCleanSetupText[client][2], sNameColor, sizeof(sNameColor)); + g_oDatabase.Escape(gaa_sCleanSetupText[client][3], sChatColor, sizeof(sChatColor)); + Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `tagtext`='%s', `visible`=%i, `restricted`=%i, `tagcolor`='%s', `namecolor`='%s', `chatcolor`='%s', `ovrd_ttext`=%i, `ovrd_tcolor`=%i, `ovrd_ncolor`=%i, `ovrd_ccolor`=%i WHERE `steamid` = '%s'", g_sDBTableName, sTagText, ((ga_iTagVisible[client] && !bBlockAccess) ? 1 : 0), ga_iIsRestricted[client], sTagColor, sNameColor, sChatColor, gaa_iAdminOvrd[client][0], gaa_iAdminOvrd[client][1], gaa_iAdminOvrd[client][2], gaa_iAdminOvrd[client][3], gaa_sSteamID[client][0]); + g_oDatabase.Query(SQLCallback_Void, sQuery, 3); + } + } + else + { + CreateTimer(5.0, TimerCB_RetrySendData, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE); + } +} + +////////////////////////////////////////////////////////////////// +///////////////////////////// Setups ///////////////////////////// +////////////////////////////////////////////////////////////////// + +void FormatColors(int client) +{ + ConvertColor(ga_sExtTagColor[client], sizeof(ga_sExtTagColor[])); + ConvertColor(ga_sTagColor[client], sizeof(ga_sTagColor[])/*, client*/); + ConvertColor(ga_sNameColor[client], sizeof(ga_sNameColor[])/*, client*/); + ConvertColor(ga_sChatColor[client], sizeof(ga_sChatColor[])/*, client*/); + + if (!gaa_iAdminOvrd[client][0] && (!g_cEnableTags.BoolValue || (!HasFlags(client, g_sAccessFlag) && (ga_iSetTagAccess[client] != 1) && !ga_iGroupMatch[client]) || (ga_iSetTagAccess[client] == 0) || !ga_iTagVisible[client])) //remove tag if no access or custom configs disabled by server manager or disabled by user + { + Format(ga_sTag[client], sizeof(ga_sTag[]), ""); + gaa_sCleanSetupText[client][0] = ""; + } + + if (!gaa_iAdminOvrd[client][1] && (!g_cEnableTagColors.BoolValue || (!HasFlags(client, g_sAccessFlag) && (ga_iTagColorAccess[client] != 1) && !ga_iGroupMatch[client]) || (ga_iTagColorAccess[client] == 0) || !ga_iTagVisible[client])) //remove tag color if no access or custom tag colors disabled by server manager or disabled by user + { + Format(ga_sTagColor[client], sizeof(ga_sTagColor[]), ""); + gaa_sCleanSetupText[client][1] = ""; + } + + if (!gaa_iAdminOvrd[client][2] && (!g_cEnableNameColors.BoolValue || (!HasFlags(client, g_sAccessFlag) && (ga_iNameColorAccess[client] != 1) && !ga_iGroupMatch[client]) || (ga_iNameColorAccess[client] == 0) || !ga_iTagVisible[client])) //remove name color if no access or custom name colors disabled by server manager or disabled by user + { + Format(ga_sNameColor[client], sizeof(ga_sNameColor[]), ""); + gaa_sCleanSetupText[client][2] = ""; + } + + if (!gaa_iAdminOvrd[client][3] && (!g_cEnableChatColors.BoolValue || (!HasFlags(client, g_sAccessFlag) && (ga_iChatColorAccess[client] != 1) && !ga_iGroupMatch[client]) || (ga_iChatColorAccess[client] == 0) || !ga_iTagVisible[client])) //remove chat color if no access or custom chat colors disabled by server manager or disabled by user + { + Format(ga_sChatColor[client], sizeof(ga_sChatColor[]), ""); + gaa_sCleanSetupText[client][3] = ""; + } +} + +void ConvertColor(char[] sString, int iSize/*, client = 0*/) +{ + if (StrEqual(sString, "", false)) + { + return; + } + + if (g_bCSGO) + { + bool bBuffer = true; //checking if numeric only + for (int i = 1; i < strlen(sString); i++) + { + if (!IsCharNumeric(sString[i])) + { + bBuffer = false; + break; + } + } + + if (!bBuffer) //convert text into numbers + { + char sBuffer[32]; + for (int i = 0; i < g_aColorName.Length; i++) + { + g_aColorName.GetString(i, sBuffer, sizeof(sBuffer)); + if (StrEqual(sBuffer, sString, false)) + { + g_aColorCode.GetString(i, sBuffer, sizeof(sBuffer)); + strcopy(sString, iSize, sBuffer); + bBuffer = true; + break; + } + } + } + + if (!bBuffer) + { + return; + } + + if ((strlen(sString) <= 3) && !StrEqual(sString, "", false)) + { + switch(StringToInt(sString)) + { + case 1: + { + Format(sString, iSize, "\x01"); + } + case 2: + { + Format(sString, iSize, "\x02"); + } + case 3: + { + Format(sString, iSize, "\x03"); + } + case 4: + { + Format(sString, iSize, "\x04"); + } + case 5: + { + Format(sString, iSize, "\x05"); + } + case 6: + { + Format(sString, iSize, "\x06"); + } + case 7: + { + Format(sString, iSize, "\x07"); + } + case 8: + { + Format(sString, iSize, "\x08"); + } + case 9: + { + Format(sString, iSize, "\x09"); + } + case 10: + { + Format(sString, iSize, "\x0A"); + } + case 11: + { + Format(sString, iSize, "\x0B"); + } + case 12: + { + Format(sString, iSize, "\x0C"); + } + case 13: + { + Format(sString, iSize, "\x0D"); + } + case 14: + { + Format(sString, iSize, "\x0E"); + } + case 15: + { + Format(sString, iSize, "\x0F"); + } + case 16: //not recommended - messes with formatting + { + Format(sString, iSize, "\x10"); + } + case -1: + { + Format(sString, iSize, "\x01"); + } + } + } + } + else if (g_bIns) + { + bool bBuffer = true; //checking if numeric only + for (int i = 1; i < strlen(sString); i++) + { + if (!IsCharNumeric(sString[i])) + { + bBuffer = false; + break; + } + } + + if (!bBuffer) //convert text into numbers + { + char sBuffer[32]; + for (int i = 0; i < g_aColorName.Length; i++) + { + g_aColorName.GetString(i, sBuffer, sizeof(sBuffer)); + if (StrEqual(sBuffer, sString, false)) + { + g_aColorCode.GetString(i, sBuffer, sizeof(sBuffer)); + strcopy(sString, iSize, sBuffer); + bBuffer = true; + break; + } + } + } + + if (!bBuffer) + { + return; + } + if ((strlen(sString) <= 3) && !StrEqual(sString, "", false)) + { + switch(StringToInt(sString)) + { + case 1: //white + { + Format(sString, iSize, "\x01"); + } + case 2: //team + { + Format(sString, iSize, "\x03"); + } + case 3: //lime + { + Format(sString, iSize, "\x04"); + } + case 4: //light green + { + Format(sString, iSize, "\x05"); + } + case 5: //olive + { + Format(sString, iSize, "\x06"); + } + case 6: //banana yellow + { + Format(sString, iSize, "\x11"); + } + case 7: //Dark yellow + { + Format(sString, iSize, "\x12"); + } + /*case 8: //light blue + { + Format(sString, iSize, "\x03"); + if (IsValidClient(client)) + { + ga_iEntOverride[client] = 1; + } + } + case 9: //attempt + { + Format(sString, iSize, "{redblue}"); + //Format(sString, iSize, "\x03"); + if (IsValidClient(client)) + { + ga_iEntOverride[client] = 3; + } + }*/ + case -1: + { + Format(sString, iSize, "\x01"); + } + } + } + } + else + { + if (strlen(sString) == 6) + { + if (IsValidHex(sString)) + { + Format(sString, iSize, "\x07%s", sString); + } + else + { + char sBuffer[32]; + for (int i = 0; i < g_aColorName.Length; i++) + { + g_aColorName.GetString(i, sBuffer, sizeof(sBuffer)); + if (StrEqual(sBuffer, sString, false)) + { + g_aColorCode.GetString(i, sBuffer, sizeof(sBuffer)); + Format(sString, iSize, "\x07%s", sBuffer); + break; + } + } + } + } + } +} + +void ConvertConsoleColor(char[] sString, int iSize) +{ + if (g_bCSGO) + { + bool bBuffer = true; //checking if numeric only + for (int i = 1; i < strlen(sString); i++) + { + if (!IsCharNumeric(sString[i])) + { + bBuffer = false; + break; + } + } + + if (!bBuffer) + { + return; + } + + if ((strlen(sString) <= 3) && !StrEqual(sString, "", false)) + { + switch(StringToInt(sString)) + { + case 1: + { + Format(sString, iSize, "\x01"); + } + case 2: + { + Format(sString, iSize, "\x02"); + } + case 3: + { + Format(sString, iSize, "\x03"); + } + case 4: + { + Format(sString, iSize, "\x04"); + } + case 5: + { + Format(sString, iSize, "\x05"); + } + case 6: + { + Format(sString, iSize, "\x06"); + } + case 7: + { + Format(sString, iSize, "\x07"); + } + case 8: + { + Format(sString, iSize, "\x08"); + } + case 9: + { + Format(sString, iSize, "\x09"); + } + case 10: + { + Format(sString, iSize, "\x0A"); + } + case 11: + { + Format(sString, iSize, "\x0B"); + } + case 12: + { + Format(sString, iSize, "\x0C"); + } + case 13: + { + Format(sString, iSize, "\x0D"); + } + case 14: + { + Format(sString, iSize, "\x0E"); + } + case 15: + { + Format(sString, iSize, "\x0F"); + } + case 16: //not recommended - messes with formatting + { + Format(sString, iSize, "\x10"); + } + } + } + } +} + +void CheckForGroups(int client) +{ + KeyValues oKeyValues = new KeyValues("Setups"); + + if (!FileExists(g_sGroupPath)) + { + SetFailState("Configuration file %s not found!", g_sGroupPath); + return; + } + + if (!oKeyValues.ImportFromFile(g_sGroupPath)) + { + SetFailState("Improper structure for configuration file %s!", g_sGroupPath); + return; + } + + if (oKeyValues.GotoFirstSubKey(true)) + { + do + { + char sFlags[30], sSectionName[100]; + oKeyValues.GetSectionName(sSectionName, sizeof(sSectionName)); + oKeyValues.GetString("flags", sFlags, sizeof(sFlags), "public"); + + if (!StrEqual(sSectionName, gaa_sSteamID[client][0], false) && !StrEqual(sSectionName, gaa_sSteamID[client][1], false) && !StrEqual(sSectionName, gaa_sSteamID[client][2], false) && !StrEqual(sSectionName, gaa_sSteamID[client][3], false)) + { + if (HasOnlyNumbers(sSectionName) || (StrContains(sSectionName, ":", false) != -1)) //if steam ID //if it's a steam ID, and not theirs, skip + { + continue; + } + + if (StrContains(sSectionName, "TEAM_", false) == 0) //if the string STARTS with TEAM_ , check if team matches - Note: need to check if at start, else "STEAM_" would trigger it + { + char sTeam[32]; + strcopy(sTeam, sizeof(sTeam), sSectionName); + ReplaceString(sTeam, sizeof(sTeam), "TEAM_", "", false); + if (IsNumeric(sTeam)) + { + int iTeam = StringToInt(sTeam); + if (GetClientTeam(client) != iTeam) + { + continue; + } + } + } + + if (!StrEqual(sFlags, "public", false) || !StrEqual(sFlags, "", false)) //not a steam ID or team setup, so check if public + { + if (!HasFlags(client, sFlags)) //not public - check flags required. This one is on a separate line in case HasFlags might error while checking "public" (since there is no 'u' flag) + { + continue; + } + } + } + //match found - get setup + oKeyValues.GetString("tagstring", ga_sTag[client], sizeof(ga_sTag[]), ""); + oKeyValues.GetString("tagcolor", ga_sTagColor[client], sizeof(ga_sTagColor[]), ""); + oKeyValues.GetString("namecolor", ga_sNameColor[client], sizeof(ga_sNameColor[]), ""); + oKeyValues.GetString("chatcolor", ga_sChatColor[client], sizeof(ga_sChatColor[]), ""); + strcopy(gaa_sCleanSetupText[client][0], sizeof(gaa_sCleanSetupText[][]), ga_sTag[client]); + strcopy(gaa_sCleanSetupText[client][1], sizeof(gaa_sCleanSetupText[][]), ga_sTagColor[client]); + strcopy(gaa_sCleanSetupText[client][2], sizeof(gaa_sCleanSetupText[][]), ga_sNameColor[client]); + strcopy(gaa_sCleanSetupText[client][3], sizeof(gaa_sCleanSetupText[][]), ga_sChatColor[client]); + ga_iGroupMatch[client] = 1; + ga_iTagVisible[client] = 2; + break; //break loop to avoid excess processing if a matching setup is found + } + while (oKeyValues.GotoNextKey(false)); + oKeyValues.GoBack(); //go back one level (to main level) to leave for other functions using kv tree - may not be needed here since we re-parse each time players connect, but is good practice. + } + else + { + //SetFailState("Can't find first subkey in configuration file %s!", g_sGroupPath); //commented out to allow zero setups + PrintToServer("Can't find first subkey in configuration file %s!", g_sGroupPath); + } + delete oKeyValues; +} + +bool HasOnlyNumbers(char[] sString) +{ + for (int i = 0; i < strlen(sString); i++) + { + if (!IsCharNumeric(sString[i])) + { + return false; + } + } + return true; +} + +bool IsNumeric(char[] sString) +{ + for (int i = 0; i < strlen(sString); i++) + { + if (!IsCharNumeric(sString[i])) + { + return false; + } + } + return true; +} + +bool IsValidClient(int client, bool bAllowBots = false, bool bAllowDead = true) +{ + if (!(1 <= client <= MaxClients) || !IsClientInGame(client) || (IsFakeClient(client) && !bAllowBots) || IsClientSourceTV(client) || IsClientReplay(client) || (!IsPlayerAlive(client) && !bAllowDead)) + { + return false; + } + return true; +} + +bool IsValidHex(const char[] sHex) +{ + if (g_oRegexHex.Match(sHex)) + { + return true; + } + return false; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////// Client Prefs Menu ///////////////////////////// +///////////////////////////////////////////////////////////////////////////// +stock void SetPanelTitleEx(Panel panel, bool onlyIfEmpty = false, const char[] text, any ...) +{ + char sBuffer[256]; + VFormat(sBuffer, sizeof(sBuffer), text, 4); + panel.SetTitle(sBuffer, onlyIfEmpty); +} + +stock int DrawPanelItemEx(Panel panel, int style = ITEMDRAW_DEFAULT, const char[] text, any ...) +{ + char sBuffer[256]; + VFormat(sBuffer, sizeof(sBuffer), text, 4); + return panel.DrawItem(sBuffer, style); +} + +stock bool AddMenuItemEx(Menu menu, int style = ITEMDRAW_DEFAULT, const char[] info, const char[] display, any ...) +{ + char sBuffer[256]; + VFormat(sBuffer, sizeof(sBuffer), display, 5); + return menu.AddItem(info, sBuffer, style); +} + +public void Menu_ClientPrefs(int client, CookieMenuAction action, any info, char[] sBuffer, int iMaxLen) +{ + if (action == CookieMenuAction_DisplayOption) + { + FormatEx(sBuffer, iMaxLen, "%T", "ClientPrefs Title", client); + } + if (action == CookieMenuAction_SelectOption) + { + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "RestrictedChangingTags"); + return; + } + else + { + Menu_MainTag(client); + } + } +} + +int Menu_MainTag(int client) +{ + Panel hPanel = CreatePanel(); + SetPanelTitleEx(hPanel, _, "%T", "Chat Tags", client); + if ((HasFlags(client, g_sAccessFlag) || (ga_iSetTagAccess[client] == 1)) && ga_iSetTagAccess[client] && (g_cEnableTags.BoolValue || (ga_iSetTagAccess[client] == 1))) + { + if (ga_iTagVisible[client]) + { + DrawPanelItemEx(hPanel, _, "%T", "Disable Tag", client); + } + else + { + DrawPanelItemEx(hPanel, _, "%T", "Enable Tag", client); + } + } + else + { + DrawPanelItemEx(hPanel, ITEMDRAW_DISABLED, "%T", "Enable Tag", client); + } + + if ((HasFlags(client, g_sAccessFlag) || (ga_iTagColorAccess[client] == 1)) && ga_iTagColorAccess[client] && (g_cEnableTagColors.BoolValue || (ga_iTagColorAccess[client] == 1))) + { + DrawPanelItemEx(hPanel, _, "%T", "Tag Colors", client); + } + else + { + DrawPanelItemEx(hPanel, ITEMDRAW_DISABLED, "%T", "Tag Colors", client); + } + + //name colors menu + if ((HasFlags(client, g_sAccessFlag) || (ga_iNameColorAccess[client] == 1)) && ga_iNameColorAccess[client] && (g_cEnableNameColors.BoolValue || (ga_iNameColorAccess[client] == 1))) + { + DrawPanelItemEx(hPanel, _, "%T", "Name Colors", client); + } + else + { + DrawPanelItemEx(hPanel, ITEMDRAW_DISABLED, "%T", "Name Colors", client); + } + //chat colors menu + if ((HasFlags(client, g_sAccessFlag) || (ga_iChatColorAccess[client] == 1)) && ga_iChatColorAccess[client] && (g_cEnableChatColors.BoolValue || (ga_iChatColorAccess[client] == 1))) + { + DrawPanelItemEx(hPanel, _, "%T", "Chat Colors", client); + } + else + { + DrawPanelItemEx(hPanel, ITEMDRAW_DISABLED, "%T", "Chat Colors", client); + } + + DrawPanelItemEx(hPanel, _, "%T", "Check Setup of Player", client); + hPanel.DrawItem("------------------------", ITEMDRAW_RAWLINE); + DrawPanelItemEx(hPanel, ITEMDRAW_RAWLINE, "%T", "Chat Command To Change Tag", client); + DrawPanelItemEx(hPanel, ITEMDRAW_RAWLINE, "%T", "Tag Text You Want", client); + DrawPanelItemEx(hPanel, ITEMDRAW_RAWLINE, "%T", "Characters Max", client, MAXTAGSIZE); + hPanel.DrawItem("", ITEMDRAW_SPACER); + hPanel.DrawItem("Back", ITEMDRAW_CONTROL); + hPanel.DrawItem("", ITEMDRAW_SPACER); + hPanel.DrawItem("Exit", ITEMDRAW_CONTROL); + + hPanel.Send(client, PanelHandler_MenuMainTag, MENU_TIME_FOREVER); + delete hPanel; + + return 2; +} + +public int PanelHandler_MenuMainTag(Menu oMenu, MenuAction action, int client, int param2) +{ + switch(action) + { + case MenuAction_End: + { + EmitSoundToClient(client, "buttons/combine_button7.wav"); + delete oMenu; + } + case MenuAction_Cancel: + { + if (param2 == MenuCancel_ExitBack) + { + ShowCookieMenu(client); + } + } + case MenuAction_Select: + { + switch(param2) + { + case 1: + { + if (HasFlags(client, g_sAccessFlag) || (ga_iSetTagAccess[client] == 1) || (ga_iTagColorAccess[client] == 1)) + { + EmitSoundToClient(client, "buttons/button14.wav"); + if (!ga_iTagVisible[client]) + { + ga_iTagVisible[client] = 1; + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "TagEnabled"); + RecheckSetup(client, true); + } + else + { + ga_iTagVisible[client] = 0; + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "TagDisabled"); + RecheckSetup(client, true); + } + Menu_MainTag(client); + } + else + { + EmitSoundToClient(client, "buttons/button14.wav"); + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DoNotHaveAccess"); + Menu_MainTag(client); + } + } + case 2: + { + if (HasFlags(client, g_sAccessFlag) || (ga_iTagColorAccess[client] == 1)) + { + EmitSoundToClient(client, "buttons/button14.wav"); + Menu_TagColor(client); + } + else + { + EmitSoundToClient(client, "buttons/button14.wav"); + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DoNotHaveAccess"); + Menu_MainTag(client); + } + } + case 3: + { + if (HasFlags(client, g_sAccessFlag) || (ga_iNameColorAccess[client] == 1)) + { + EmitSoundToClient(client, "buttons/button14.wav"); + Menu_NameColor(client); + } + else + { + EmitSoundToClient(client, "buttons/button14.wav"); + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DoNotHaveAccess"); + Menu_MainTag(client); + } + } + case 4: + { + if (HasFlags(client, g_sAccessFlag) || (ga_iChatColorAccess[client] == 1)) + { + EmitSoundToClient(client, "buttons/button14.wav"); + Menu_ChatColor(client); + } + else + { + EmitSoundToClient(client, "buttons/button14.wav"); + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DoNotHaveAccess"); + Menu_MainTag(client); + } + } + case 5: + { + if (HasFlags(client, g_sAdminFlag)) + { + EmitSoundToClient(client, "buttons/button14.wav"); + Displaymenu_Player(client, "sm_checktag", "%t", "Check Tag Setup For"); + } + else + { + EmitSoundToClient(client, "buttons/button14.wav"); + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DoNotHaveAccess"); + Menu_MainTag(client); + } + } + case 7: + { + EmitSoundToClient(client, "buttons/button14.wav"); + ShowCookieMenu(client); + } + case 9: + { + EmitSoundToClient(client, "buttons/combine_button7.wav"); + } + } + } + } + + return; +} + +void RecheckSetup(int client, bool bBlockAccess = false) +{ + CheckTag(client); //this is put before group tags so that group ones can bypass this if the manager wants it (e.g. have root admins be tagged "CONSOLE"?) + + //load group setups if they dont have access per defined access flags. + if (!(HasFlags(client, g_sAccessFlag) || StrEqual(g_sAccessFlag, "", false))) + { + CheckForGroups(client); + } + else if (ga_iTagVisible[client] != 1) //has flags, but "disabled" (if they dont want a tag, they can set the text to ""). + { //This isnt combined with the above check since they might not have the flag, but their cookie says "enabled". That would block the groups for such a situation. + CheckForGroups(client); + } + + FormatColors(client); + SendClientSetupToDB(client, bBlockAccess); + + Call_StartForward(g_hClientReloadFwd); + Call_PushCell(client); + Call_Finish(); +} + +public void Menu_TagColor(int client) +{ + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "RestrictedChangingTagColors"); + return; + } + + if (!ga_iChatColorAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeChatColorByExternalPlugin"); + return; + } + + if (!ga_iTagColorAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeTagColorByExternalPlugin"); + return; + } + + if (!g_cEnableTagColors.BoolValue || !g_cEnableTags.BoolValue) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledForEveryoneByServerManager"); + return; + } + + Menu oMenu = new Menu(MenuCallback_TagColor); + oMenu.SetTitle("%T", "Tag Colors", client); + oMenu.ExitBackButton = true; + oMenu.Pagination = 6; + + AddMenuItemEx(oMenu, _, "Reset", "%T", "Reset", client); + if (!g_bCSGO && !g_bIns) + { + AddMenuItemEx(oMenu, _, "SetManually", "%T", "Define Your Own Color", client); + } + + char sColorIndex[5], sColorName[32], sBuffer[32]; + for (int i = 0; i < g_aColorName.Length; i++) + { + IntToString(i, sColorIndex, sizeof(sColorIndex)); + g_aColorName.GetString(i, sColorName, sizeof(sColorName)); + g_aColorCode.GetString(i, sBuffer, sizeof(sBuffer)); + if (StringToInt(sBuffer) < 0) + oMenu.AddItem(sColorIndex, sColorName, ITEMDRAW_DISABLED); + else + oMenu.AddItem(sColorIndex, sColorName); + } + + oMenu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuCallback_TagColor(Menu oMenu, MenuAction action, int client, int param2) +{ + if (action == MenuAction_End) + { + delete oMenu; + return; + } + + if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) + { + Menu_MainTag(client); + return; + } + + if (action == MenuAction_Select) + { + char sBuffer[32]; + oMenu.GetItem(param2, sBuffer, sizeof(sBuffer)); + + if (StrEqual(sBuffer, "Reset")) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "ResetTagColor"); + ga_sTagColor[client] = ""; + gaa_sCleanSetupText[client][1] = ""; + FormatColors(client); + SendClientSetupToDB(client); + } + else if (StrEqual(sBuffer, "SetManually")) + { + PrintToChat(client, "%s\x03%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "ToDefineYourOwnTagColor"); + } + else + { + int iColorIndex = StringToInt(sBuffer); + g_aColorCode.GetString(iColorIndex, sBuffer, sizeof(sBuffer)); + strcopy(ga_sTagColor[client], sizeof(ga_sTagColor[]), sBuffer); + strcopy(gaa_sCleanSetupText[client][1], sizeof(gaa_sCleanSetupText[][]), ga_sTagColor[client]); + FormatColors(client); + g_aColorName.GetString(iColorIndex, sBuffer, sizeof(sBuffer)); + PrintToChat(client, "\x01%s%s%t %s%s", g_sTag, CSGO_WHITE, "TagColorSetTo", ga_sTagColor[client], sBuffer); + SendClientSetupToDB(client); + } + + Menu_MainTag(client); + } +} + +public void Menu_NameColor(int client) +{ + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "RestrictedChangingNameColors"); + return; + } + + if (!ga_iNameColorAccess[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DeniedChangeNameColorByExternalPlugin"); + return; + } + + if (!g_cEnableNameColors.BoolValue) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledForEveryoneByServerManager"); + return; + } + + Menu oMenu = new Menu(MenuCallback_NameColor); + oMenu.SetTitle("%T", "Name Colors", client); + oMenu.ExitBackButton = true; + oMenu.Pagination = 6; + + AddMenuItemEx(oMenu, _, "Reset", "%T", "Reset", client); + if (!g_bCSGO && !g_bIns) + { + AddMenuItemEx(oMenu, _, "SetManually", "%T", "Define Your Own Color", client); + } + + char sColorIndex[5], sColorName[32], sBuffer[32]; + for (int i = 0; i < g_aColorName.Length; i++) + { + IntToString(i, sColorIndex, sizeof(sColorIndex)); + g_aColorName.GetString(i, sColorName, sizeof(sColorName)); + g_aColorCode.GetString(i, sBuffer, sizeof(sBuffer)); + if (StringToInt(sBuffer) < 0) + oMenu.AddItem(sColorIndex, sColorName, ITEMDRAW_DISABLED); + else + oMenu.AddItem(sColorIndex, sColorName); + } + + oMenu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuCallback_NameColor(Menu oMenu, MenuAction action, int client, int param2) +{ + if (action == MenuAction_End) + { + delete oMenu; + return; + } + + if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) + { + Menu_MainTag(client); + return; + } + + if (action == MenuAction_Select) + { + char sBuffer[32]; + oMenu.GetItem(param2, sBuffer, sizeof(sBuffer)); + + if (StrEqual(sBuffer, "Reset")) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "ResetNameColor"); + ga_sNameColor[client] = ""; + gaa_sCleanSetupText[client][2] = ""; + FormatColors(client); + SendClientSetupToDB(client); + } + else if (StrEqual(sBuffer, "SetManually")) + { + PrintToChat(client, "%s\x03%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "ToDefineYourOwnNameColor"); + } + else + { + int iColorIndex = StringToInt(sBuffer); + g_aColorCode.GetString(iColorIndex, sBuffer, sizeof(sBuffer)); + strcopy(ga_sNameColor[client], sizeof(ga_sNameColor[]), sBuffer); + strcopy(gaa_sCleanSetupText[client][2], sizeof(gaa_sCleanSetupText[][]), ga_sNameColor[client]); + FormatColors(client); + g_aColorName.GetString(iColorIndex, sBuffer, sizeof(sBuffer)); + PrintToChat(client, "\x01%s%s%t %s%s", g_sTag, CSGO_WHITE, "NameColorSetTo", ga_sNameColor[client], sBuffer); + SendClientSetupToDB(client); + } + Menu_MainTag(client); + } +} + +public void Menu_ChatColor(int client) +{ + if (ga_iIsRestricted[client]) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "RestrictedChangingChatColors"); + return; + } + + if (!g_cEnableChatColors.BoolValue) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "DisabledForEveryoneByServerManager"); + return; + } + + Menu oMenu = new Menu(MenuCallback_ChatColor); + oMenu.SetTitle("%T", "Chat Colors", client); + oMenu.ExitBackButton = true; + oMenu.Pagination = 6; + + AddMenuItemEx(oMenu, _, "Reset", "%T", "Reset", client); + if (!g_bCSGO && !g_bIns) + { + AddMenuItemEx(oMenu, _, "SetManually", "%T", "Define Your Own Color", client); + } + + char sColorIndex[5], sColorName[32], sBuffer[32]; + for (int i = 0; i < g_aColorName.Length; i++) + { + IntToString(i, sColorIndex, sizeof(sColorIndex)); + g_aColorName.GetString(i, sColorName, sizeof(sColorName)); + g_aColorCode.GetString(i, sBuffer, sizeof(sBuffer)); + if (StringToInt(sBuffer) < 0) + oMenu.AddItem(sColorIndex, sColorName, ITEMDRAW_DISABLED); + else + oMenu.AddItem(sColorIndex, sColorName); + } + + oMenu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuCallback_ChatColor(Menu oMenu, MenuAction action, int client, int param2) +{ + if (action == MenuAction_End) + { + delete oMenu; + return; + } + + if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) + { + Menu_MainTag(client); + return; + } + + if (action == MenuAction_Select) + { + char sBuffer[32]; + oMenu.GetItem(param2, sBuffer, sizeof(sBuffer)); + + if (StrEqual(sBuffer, "Reset")) + { + PrintToChat(client, "%s%s%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "ResetChatColor"); + ga_sChatColor[client] = ""; + gaa_sCleanSetupText[client][3] = ""; + FormatColors(client); + SendClientSetupToDB(client); + } + else if (StrEqual(sBuffer, "SetManually")) + { + PrintToChat(client, "%s\x03%t", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED), "ToDefineYourOwnChatColor"); + } + else + { + int iColorIndex = StringToInt(sBuffer); + g_aColorCode.GetString(iColorIndex, sBuffer, sizeof(sBuffer)); + strcopy(ga_sChatColor[client], sizeof(ga_sChatColor[]), sBuffer); + strcopy(gaa_sCleanSetupText[client][3], sizeof(gaa_sCleanSetupText[][]), ga_sChatColor[client]); + FormatColors(client); + g_aColorName.GetString(iColorIndex, sBuffer, sizeof(sBuffer)); + PrintToChat(client, "\x01%s%s%t %s%s", g_sTag, CSGO_WHITE, "ChatColorSetTo", ga_sChatColor[client], sBuffer); + SendClientSetupToDB(client); + } + + Menu_MainTag(client); + } +} + +////////////////////////////////////////////////////////////////////// +///////////////////////////// Admin Menu ///////////////////////////// +////////////////////////////////////////////////////////////////////// + +public void OnAdminMenuReady(Handle hTopMenu) +{ + TopMenu oTopMenu = TopMenu.FromHandle(hTopMenu); + + /* Block us from being called twice */ + if (oTopMenu == g_oTopMenu) + { + return; + } + + /* Save the Handle */ + g_oTopMenu = oTopMenu; + + //TopMenuObject oMenuObject = AddToTopMenu(hTopMenu, "ChatTags", TopMenuObject_Category, Handle_Commands, INVALID_TOPMENUOBJECT); + TopMenuObject oMenuObject = g_oTopMenu.AddCategory("ChatTags", Handle_Commands, "sm_tagadmin", ReadFlagString(g_sAdminFlag), "mainmenu"); + if (oMenuObject == INVALID_TOPMENUOBJECT) + { + return; + } + + g_oTopMenu.AddItem("sm_reloadtags", AdminMenu_Command, oMenuObject, "sm_reloadtags", ReadFlagString(g_sAdminFlag)); + g_oTopMenu.AddItem("sm_restricttag", Adminmenu_Player, oMenuObject, "sm_restricttag", ReadFlagString(g_sAdminFlag), "Restrict Tags"); + g_oTopMenu.AddItem("sm_unrestricttag", Adminmenu_Player, oMenuObject, "sm_unrestricttag", ReadFlagString(g_sAdminFlag), "Unrestrict Tags"); + g_oTopMenu.AddItem("sm_removetag", Adminmenu_Player, oMenuObject, "sm_removetag", ReadFlagString(g_sAdminFlag), "Remove Tags"); + g_oTopMenu.AddItem("sm_unloadtags", AdminMenu_Unload, oMenuObject, "sm_unloadtags", ReadFlagString(g_sAdminUnloadFlag)); +} + +public void Handle_Commands(TopMenu oMenu, TopMenuAction action, TopMenuObject object_id, int client, char[] sBuffer, int iMaxLen) +{ + switch(action) + { + case TopMenuAction_DisplayOption: + { + Format(sBuffer, iMaxLen, "Chat Tags"); + } + case TopMenuAction_DisplayTitle: + { + Format(sBuffer, iMaxLen, "Chat Tags"); + } + } +} + +public void AdminMenu_Unload(TopMenu oMenu, TopMenuAction action, TopMenuObject object_id, int client, char[] sBuffer, int iMaxLen) //command via admin menu +{ + if (action == TopMenuAction_DisplayOption) + { + Format(sBuffer, iMaxLen, "Unload plugin until map change"); + } + else if (action == TopMenuAction_SelectOption) + { + PrintToChat(client, "%s%sChat Tags is now unloaded until map change!", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + char sPluginName[128]; + GetPluginFilename(INVALID_HANDLE, sPluginName, sizeof(sPluginName)); + ServerCommand("sm plugins unload %s", sPluginName); + } +} + +public void AdminMenu_Command(TopMenu oTopMenu, TopMenuAction action, TopMenuObject object_id, int client, char[] sBuffer, int iMaxLen) //command via admin menu +{ + if (action == TopMenuAction_DisplayOption) + { + Format(sBuffer, iMaxLen, "Reload Chat Tag Colors"); + } + else if (action == TopMenuAction_SelectOption) + { + Reload(); + + PrintToChat(client, "%s%sColors setups are now reloaded.", g_sTag, g_bCSGO ? CSGO_RED : (g_bIns ? INS_GREEN : CSS_RED)); + RedisplayAdminMenu(oTopMenu, client); + } +} + +public void Adminmenu_Player(TopMenu oTopMenu, TopMenuAction action, TopMenuObject object_id, int client, char[] sBuffer, int iMaxLength) +{ + char sCommand[MAX_NAME_LENGTH], sTitle[128]; + GetTopMenuObjName(oTopMenu, object_id, sCommand, sizeof(sCommand)); + GetTopMenuInfoString(oTopMenu, object_id, sTitle, sizeof(sTitle)); + + switch(action) + { + case(TopMenuAction_DisplayOption): + { + Format(sBuffer, iMaxLength, sTitle); + } + case(TopMenuAction_SelectOption): + { + Displaymenu_Player(client, sCommand, sTitle); + } + } +} + +public void Displaymenu_Player(int client, char[] sCommand, char[] sTitle, any ...) +{ + Menu oMenu = new Menu(Commandmenu_Player); + char sBuffer[256]; + SetGlobalTransTarget(client); + VFormat(sBuffer, sizeof(sBuffer), sTitle, 4); + oMenu.SetTitle(sBuffer); + oMenu.ExitBackButton = true; + + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i)) + { + char sInfoBuffer[256], sName[MAX_NAME_LENGTH], sUserID[MAX_NAME_LENGTH], sDisplay[128]; + IntToString(GetClientUserId(i), sUserID, sizeof(sUserID)); + GetClientName(i, sName, sizeof(sName)); + Format(sDisplay, sizeof(sDisplay), "%s (%s)", sName, sUserID); + Format(sInfoBuffer, sizeof(sInfoBuffer), "%s %s", sCommand, sUserID); + oMenu.AddItem(sInfoBuffer, sDisplay); + } + } + + oMenu.Display(client, MENU_TIME_FOREVER); +} + + +public int Commandmenu_Player(Menu oMenu, MenuAction Selection, int client, int param2) +{ + switch(Selection) + { + case(MenuAction_End): + { + delete oMenu; + } + case(MenuAction_Cancel): + { + char sInfo[64]; + oMenu.GetItem(param2, sInfo, sizeof(sInfo)); + char sTempArray[2][32]; + ExplodeString(sInfo, " ", sTempArray, sizeof(sTempArray), sizeof(sTempArray[])); + + if (StrEqual(sTempArray[0],"sm_checktag",false)) + { + Menu_MainTag(client); + } + else + { + DisplayTopMenu(g_oTopMenu, client, TopMenuPosition_LastCategory); + } + } + case(MenuAction_Select): + { + char sInfo[64]; + oMenu.GetItem(param2, sInfo, sizeof(sInfo)); + char sTempArray[2][32]; + ExplodeString(sInfo, " ", sTempArray, sizeof(sTempArray), sizeof(sTempArray[])); + + if (!IsValidClient(GetClientOfUserId(StringToInt(sTempArray[1])))) + { + ReplyToCommand(client, "%s%sTarget is now invalid.", g_sTag, CSGO_WHITE); + } + else + { + char sCommand[300]; + Format(sCommand, sizeof(sCommand), "%s #%i", sTempArray[0], StringToInt(sTempArray[1])); + FakeClientCommandEx(client, sCommand); + } + } + } +} + +void CleanStringForSQL(char[] sString, int iSize) +{ + int iEscapeSize = 2*iSize + 1; + char[] sEscapedText = new char[iEscapeSize]; + g_oDatabase.Escape(sString, sEscapedText, iEscapeSize); + strcopy(sString, iSize, sEscapedText); + + /*char[] sBuffer = new char[iSize]; + strcopy(sBuffer, iSize, sString); + ReplaceString(sBuffer, iSize, "}", ""); + ReplaceString(sBuffer, iSize, "{", ""); + ReplaceString(sBuffer, iSize, "|", ""); + ReplaceString(sBuffer, iSize, "'", ""); + ReplaceString(sBuffer, iSize, "\%27", ""); // ' + ReplaceString(sBuffer, iSize, "\"", ""); + ReplaceString(sBuffer, iSize, "\%22", ""); // " + ReplaceString(sBuffer, iSize, "`", ""); + ReplaceString(sBuffer, iSize, "\%60", ""); // ` + ReplaceString(sBuffer, iSize, "\\", ""); + ReplaceString(sBuffer, iSize, "\%5C", ""); // backslash + ReplaceString(sBuffer, iSize, "#", ""); + ReplaceString(sBuffer, iSize, "\%23", ""); // # + ReplaceString(sBuffer, iSize, "--", ""); + ReplaceString(sBuffer, iSize, "\%2D-", ""); // -- + ReplaceString(sBuffer, iSize, "-\%2D", ""); // -- + ReplaceString(sBuffer, iSize, "\%2D\%2D", ""); // -- + ReplaceString(sBuffer, iSize, "=", ""); + ReplaceString(sBuffer, iSize, "\%3D", ""); // = + ReplaceString(sBuffer, iSize, ";", ""); + ReplaceString(sBuffer, iSize, "\%3B", ""); // ; + ReplaceString(sBuffer, iSize, "^", ""); + ReplaceString(sBuffer, iSize, "\%5E", ""); // ^ + ReplaceString(sBuffer, iSize, "%", ""); + ReplaceString(sBuffer, iSize, "\%25", ""); // % + strcopy(sString, iSize, sBuffer); */ +} + +stock void Log(char[] sPath, const char[] sMsg, any ...) //logging function - path is relative to logs folder. +{ + char sLogFilePath[PLATFORM_MAX_PATH], sFormattedMsg[1500]; + BuildPath(Path_SM, sLogFilePath, sizeof(sLogFilePath), "logs/%s", sPath); + VFormat(sFormattedMsg, sizeof(sFormattedMsg), sMsg, 3); + LogToFileEx(sLogFilePath, "%s", sFormattedMsg); +} + +/* +CHANGELOG: + 2.1: + * Added spam blocker + * Added console color cvars + 2.2: + * Converted team names to read team name...was using CSS ones before. + 2.3: + * Added auto-gag after detected for spam 3x, with cvar for time span it all occurs, if it is even on, and added cvar to disable spam detection period. + 2.5: + * Moved return for ignored text after the spam blockers. + 2.6: + * Fixed the check tags command - now handles groups, and fixed that it was returning the setup of the person sending the command, not the target. Also made the command executable from rcon. + * Edited formatting for [ChatTags] on all replys. + * Added checks to make sure they arent mimicking console and have an easy place to add checks for bad names (and remove if they're found to be). + 2.7: + * Created CS:GO version (currently not public). + * Converted back to PrintToChat from morecolor's CPrintToChat, due to the ability to glitch their function and color text by using the tag codes in your message (e.g. "{fullred}Hey look! My text is red!") + * Added a function to format the team names so that the first letter is capitolized, and letters after hyphens, a space, or a first parenthesis are capitolized. All else are lower case. + * Fixed cvar names. + * Added cvars to disable any of the 4 main tag/color functions (tags, tag colors, name colors, and chat colors). + * Changed kv key from "hex" to "colors" to be more generic for use with CS:GO, and simplicity/similarity between the two versions. + * Removed g_iTagSetting, and replaced with conditional formatting. + * One cvar was missing a hook to it being change. Added in. + * Added code to return Plugin_Continue if message was blank. + 2.8: + * Added config setups for groups or individuals. + 3.0.3: + * Fixed setting individual setups in ftp cfg being applied as public. + * Made it so that if server managers put the wrong steam universe in IDs (STEAM_0), it will still work, despite their fail -.- . + 3.1: + * Added code to allow external plugins to set additional tags. + * Shortened cfg filenames. + * Added a few lines to help protect against multiple messages spamming chat when gagging a client. + 3.1.2: + * Added ability to set the access flag to "public" to make it available to all. + 3.1.3: + * Added line at top to increase stack space. + 3.1.4: + * Updated HasFlags function to allow more versatility/configurability. + 3.1.5: + * Added cvars for default CT and T colors for names and chat, when no other setups apply. + 4.0: + * Added natives for access overrides. + * Edited HasFlags to be able to pass "none" to block all access (leaving tags only available to external plugins). + * Added forward for plugin reload. + * Renamed a lot of variables to use my current nomenclatures, and made global integers into booleans where there's only 2 options. + * Combined the CS:S and CS:GO versions + * Redid some of the admin menu to combine functions using techniques/code I made for my other plugins. + 4.1 + * Stable version with all bugs worked out from the 4.0 conversion. + 4.1.1: + * Added more CheckClientLoad to combat the OnClientCookiesCached event not always firing (not sure if that's a sourcemod bug or what...). + 4.1.2: + * Created Fwd for when a client finishes loading. + 4.2: + * Added missing flag check for unload cmd. + * Added cvars and cmd for an admin (configurable flag) to set another player's tag/color settings. + * Removed resetting ga_iIsRestricted in RemoveSetup. + * Rebuilt cfg file to use ADT arrays for storing colors. Didnt realize I wasnt doing this (that piece was coded a long while back). + * Edited ConvertColor to also allow the names from the cfg file and to convert them accordingly. Increased all tag buffers to 32 to allow this. + * Added 4 more cookies to keep track of if settings for a client were an admin override. + * Added cmd to remove all admin overrides from a client. + * Changed default for ga_iTagVisible to 0. + 4.2.1: + * Edited g_iExternalPos to make it so they can have just one tag, if configured to be that, and preference one or the other. + * Removed FCVAR_PLUGIN due to it being depreciated. + 4.3.0: + * Added code from SCP to filter out translations. + 4.3.1: + * Scrapped everything added from SCP for 4.3.0, and wrote my own stuff that functions better and avoids the bad translation file parsing they use, etc. + 4.3.2: + * Edited using names to specify colors being forced. Still not tested, but noticed that it was still inside the check for string length < 3, and fixed that. Should be good now. + 4.3.3: + * Added a SetFailState for missing translation. + * Added CreateDirectory if they didnt make the logs/chatlogger/ folder. + 4.3.4: + * Edited cancel button when picking players players via Displaymenu_Player function. Since it is also used in admin menu, apparently the back button allows going to the admin menu. Now, it checks the command to determine if it should draw the tags menu or admin menu. + 4.4.0: + * Added code so that the group configs can specify team groups (e.g. TEAM_2 would be terrorists in CS:S/CS:GO). + 4.4.1: + * Added cvar ct_hidechattriggers and supporting code to hide chat triggers, per request. + 4.4.2: + * Added cvar ct_forcebrackets and supporting code, per request. + 4.4.3: + * Added cvar ct_bracketcolor and supporting code, per request. + * Removed include, since it wasnt being used (and hasnt been for a while). + 4.4.4: + * Minor edit to move the bracket adding inside the check for if they get tags. + 4.5.0: + * Added support for Insurgency. + * Removed "ct_enabled" cvar, since they should just unload/reload plugin to enable/disable. + * Changed player settings to store in SQL database instead of client cookies. + * Added forward for when clients reload (called every time they change their setup) and their info is sent to the database. + 4.5.1: + * Added a few colors to insurgency settings: Banana yellow, dark yellow. Added iEntIDOverride in SayText2 to add red color for insurgency. + * Added code to paginate the colors better. + 4.5.2: + * Flipped the ga_iTagHidden variable to ga_iTagVisible, and changed the DB column from `hidden` to `visible`. Flipped the logic on that variable throughout the plugin. The purpose of this was to fix the tags menu functionality around if the first option in the menu says "Disable tag", etc. + 4.5.3: + * Added code for !blockchat command to allow players to disable all chat from others. I havent tested this feature yet though. + 4.5.4: + * Added CleanStringForSQL(ga_sEscapedName[client], sizeof(ga_sEscapedName[])); in a couple locations where the name was being re-grabbed. + 4.5.5: + * Fixed an error that showed up in recent edits around the ga_iTagVisible (previously ga_iTagHidden) variable. It caused players to get cfg setup even when they set their own. + 4.6.0: + * Converted plugin to 1.8 syntax and made use of classes, etc. Plugin not tested. + 4.6.1: + * Upped buffer from 32 to 120 for cvar flag caches. + * Added cvar and code for admin flags for the spam blocker to ignore. + * Added cvar and code so that after an admin removes someones setup, it will check the groups config file for a default setup (if enabled). + * Made it so that the groups config file supports AuthId_Steam2, AuthId_Steam3, and AuthId_SteamID64. The database will use AuthId_Steam2 with steam universe = 0. + 4.7.0: + * Attempted rainbow chat after being harassed by several people to add it. Figured out that it truncates long messages due to character limit. Rolled back changes. + * Edited out SetFailState for key-values config for groups, allowing it to be blank inside setups. Replaced with PrintToServer msg. + * Moved chat logger to database table if using MySQL. For SQLite, it still uses flatfiles. + * Modified CleanStringForSQL to just use the Escape. Should change into prepared statements in the future... EDIT: Looked into prepared statements, and they still arent available threaded :( . + * Added separate cache for escaped and unescaped player names (unescaped is used in SayText2). + * Added g_cHideChatTriggers check in Say_Team hook (it was only in regular say hook before). + * Moved timer to update chat logs flatfile path to be inside DB connect when it decides it is sqlite (part of migration to MySQL logs). + * Created web panel for displaying chat logs, grouped by server. Web page is all relative urls and the only edit needed is the database info! + 4.7.1: + * Minor edit to incorporate the 1.8 syntax Database class fully. This does not effect the plugin in any way. + * Made it so that CleanStringForSQL cant be called when the database is null. + 4.7.2 + * Minor edit to enforce some boundaries on some cvars. + 4.8.0 + * Made the name of the database table into a cvar so that a single database can be used with separate tables for separate servers (per request). + * Restructured the table creation code for the database so that the bulk of the query is only written in the code once, and is more readable. + 4.8.1 + * Missed some of the queries being updated to use the new cvar tablename from 4.8.0. Edited. + * Changed the unload cmds to auto-detect the plugin name. + 4.9.0 + * Revamped spam protection to make it more configurable. All cvars renamed accordingly. + * Renamed some variables and functions. + * Converted the last of the timers to pass userid instead of client index. The only ones that were still there were 0.1 second timers, so I didnt bother it using the extra resources to be safe on such a short timer, but now I changed it. + 4.9.1 + * Removed include - wasnt being used. + 4.9.2 + * Edit so that cfg sections with numbers wouldnt be skipped by being falsely identified as steam ids (64). + 4.9.3 + * Minor edit in SQLCallback_Connect that has no change to functionality. It is simply a cleanup of code that I want to use in the future for database connections where sqlite is allowed. +*/ \ No newline at end of file diff --git a/ChatTags/translations/ChatTags.phrases.txt b/ChatTags/translations/ChatTags.phrases.txt new file mode 100644 index 0000000..d3db4ae --- /dev/null +++ b/ChatTags/translations/ChatTags.phrases.txt @@ -0,0 +1,361 @@ +"Phrases" +{ + "ClientPrefs Title" + { + "en" "[Donator] Chat Tags" + "jp" "[ドネーター] Chat Tags (チャットタグ)" + "chi" "[捐助者] Chat Tags (聊天标签)" + "zho" "[捐助者] Chat Tags (聊天標籤)" + } + "CheckConsole" + { + "en" "Check console for output!" + "jp" "コンソールの出力を確認してください!" + //"chi" "" + //"zho" "" + } + "NotFoundOrInvalidParameter" + { + "en" "Not found or invalid parameter." + "jp" "見つからなかったか無効なパラメータです." + //"chi" "" + //"zho" "" + } + "InvalidHex" + { + "en" "Invalid hex." + "jp" "無効なHEX." + //"chi" "" + //"zho" "" + } + "Usage" + { + "en" "Usage:" + "jp" "使用方法:" + //"chi" "" + //"zho" "" + } + "NotAvailableForCSGO" + { + "en" "This command is not available for CS:GO!" + "jp" "このコマンドは CS:GO では使用できません!" + //"chi" "" + //"zho" "" + } + "NotAvailableForInsurgency" + { + "en" "This command is not available for Insurgency!" + "jp" "このコマンドは Insurgency では使用できません!" + //"chi" "" + //"zho" "" + } + "DisabledByServerManager" + { + "en" "This feature has been disabled by the server manager!" + "jp" "この機能はサーバー管理者によって無効化されています!" + //"chi" "" + //"zho" "" + } + "DisabledForEveryoneByServerManager" + { + "en" "This feature has been disabled for everyone by the server manager!" + "jp" "この機能はサーバー管理者によってプレイヤー全体で無効化されています!" + //"chi" "" + //"zho" "" + } + "MustBeInGame" + { + "en" "You must be in game to use this command!" + "jp" "このコマンドを使用するにはゲーム内である必要があります!" + //"chi" "" + //"zho" "" + } + "DoNotHaveAccess" + { + "en" "You do not have access to this feature!" + "jp" "あなたはこの機能にアクセスできません!" + //"chi" "" + //"zho" "" + } + "DisabledUntilCookiesCached" + { + "en" "This feature is disabled until your cookies have cached!" + "jp" "この機能はあなたのクッキーがキャッシュされるまで使用できません!" + //"chi" "" + //"zho" "" + } + "TagEnabled" + { + "en" "Your tag is now enabled!" + "jp" "あなたのタグが有効になりました!" + //"chi" "" + //"zho" "" + } + "TagDisabled" + { + "en" "Your tag is now disabled!" + "jp" "あなたのタグが無効になりました!" + //"chi" "" + //"zho" "" + } + "RestrictedChangingTags" + { + "en" "You are currently restricted from changing your tags!" + "jp" "あなたは現在タグの変更が制限されています!" + //"chi" "" + //"zho" "" + } + "RestrictedChangingTagColors" + { + "en" "You are currently restricted from changing your tag colors!" + "jp" "あなたは現在タグの色の変更が制限されています!" + //"chi" "" + //"zho" "" + } + "RestrictedChangingNameColors" + { + "en" "You are currently restricted from changing your name colors!" + "jp" "あなたは現在名前の色の変更が制限されています!" + //"chi" "" + //"zho" "" + } + "RestrictedChangingChatColors" + { + "en" "You are currently restricted from changing your chat colors!" + "jp" "あなたは現在チャットの色の変更が制限されています!" + //"chi" "" + //"zho" "" + } + "EnabledChatBlocker" + { + "en" "You have enabled chat blocker! You will no longer see chat from other players. To re-enable it, type !blockchat." + "jp" "チャットブロックを有効にしました! 他のプレイヤーのチャットが見えなくなります. 再度 !blockchar を入力で元に戻します." + //"chi" "" + //"zho" "" + } + "DisabledChatBlocker" + { + "en" "You have disabled chat blocker! You can now see chat. To disable chat again, type !blockchat." + "jp" "チャットブロックを無効にしました! チャットが見えるようになります. 再度 !blockchar を入力でチャットを無効化します." + //"chi" "" + //"zho" "" + } + "DeniedChangeTagByExternalPlugin" + { + "en" "Your access to change your tag text has been denied by override by an external plugin!" + "jp" "他のプラグインが優先制御している為, タグテキストの変更が拒否されました!" + //"chi" "" + //"zho" "" + } + "DeniedChangeTagColorByExternalPlugin" + { + "en" "Your access to change your tag colors has been denied by override by an external plugin!" + "jp" "他のプラグインが優先制御している為, タグの色の変更が拒否されました!" + //"chi" "" + //"zho" "" + } + "DeniedChangeNameColorByExternalPlugin" + { + "en" "Your access to change your name colors has been denied by override by an external plugin!" + "jp" "他のプラグインが優先制御している為, 名前の色の変更が拒否されました!" + //"chi" "" + //"zho" "" + } + "DeniedChangeChatColorByExternalPlugin" + { + "en" "Your access to change your chat colors has been denied by override by an external plugin!" + "jp" "他のプラグインが優先制御している為, チャットの色の変更が拒否されました!" + //"chi" "" + //"zho" "" + } + "TagBlockedFromUse" + { + "en" "Nice try! This tag is blocked from use." + "jp" "いい試みですね! このタグの使用はブロックされています." + //"chi" "" + //"zho" "" + } + "TagSetTo" + { + "en" "Your tag is now visible and is set to:" + "jp" "あなたのタグが設定されました:" + //"chi" "" + //"zho" "" + } + "TagColorSetTo" + { + "en" "Your tag color is now set to:" + "jp" "あなたのタグの色が設定されました:" + //"chi" "" + //"zho" "" + } + "NameColorSetTo" + { + "en" "Your name color is now set to:" + "jp" "あなたの名前の色が設定されました:" + //"chi" "" + //"zho" "" + } + "ChatColorSetTo" + { + "en" "Your chat color is now set to:" + "jp" "あなたのチャットの色が設定されました:" + //"chi" "" + //"zho" "" + } + "ResetTagColor" + { + "en" "Your tag color is now reset to default." + "jp" "あなたのタグの色が初期値にリセットされました." + //"chi" "" + //"zho" "" + } + "ResetNameColor" + { + "en" "Your name color is now reset to default." + "jp" "あなたの名前の色が初期値にリセットされました." + //"chi" "" + //"zho" "" + } + "ResetChatColor" + { + "en" "Your chat color is now reset to default." + "jp" "あなたのチャットの色が初期値にリセットされました." + //"chi" "" + //"zho" "" + } + "TooFast" + { + "en" "You are sending messages too fast!" + "jp" "メッセージの間隔が短かすぎます!" + //"chi" "" + //"zho" "" + } + "HasBeenGagged" + { + "en" "has been gagged for spamming chat." + "jp" "がチャットスパムによってギャグされました." + //"chi" "" + //"zho" "" + } + "MimickingCONSOLE" + { + "en" "Your chat tags setup has been removed due to mimicking CONSOLE (change your name)!" + "jp" "あなたはCONSOLE表示を真似しようとしたためタグの設定が消去されました (名前を変更してください)!" + //"chi" "" + //"zho" "" + } + "ToDefineYourOwnTagColor" + { + "en" "To define your own tag color, type !tagcolor (e.g. !tagcolor FFFFFF)." + "jp" "自分の好みのタグの色を定義するには, !tagcolor を入力してください (例. !tagcolor FFFFFF)." + //"chi" "" + //"zho" "" + } + "ToDefineYourOwnNameColor" + { + "en" "To define your own name color, type !namecolor (e.g. !namecolor FFFFFF)." + "jp" "自分の好みの名前の色を定義するには, !namecolor を入力してください (例. !namecolor FFFFFF)." + //"chi" "" + //"zho" "" + } + "ToDefineYourOwnChatColor" + { + "en" "To define your own chat color, type !chatcolor (e.g. !chatcolor FFFFFF)." + "jp" "自分の好みのチャットの色を定義するには, !chatcolor を入力してください (例. !chatcolor FFFFFF)." + //"chi" "" + //"zho" "" + } + "Chat Tags" + { + "en" "Chat Tags" + "jp" "チャットタグ" + //"chi" "" + //"zho" "" + } + "Disable Tag" + { + "en" "Disable Tag" + "jp" "タグを無効化" + //"chi" "" + //"zho" "" + } + "Enable Tag" + { + "en" "Enable Tag" + "jp" "タグを有効化" + //"chi" "" + //"zho" "" + } + "Tag Colors" + { + "en" "Tag Colors" + "jp" "タグの色" + //"chi" "" + //"zho" "" + } + "Name Colors" + { + "en" "Name Colors" + "jp" "名前の色" + //"chi" "" + //"zho" "" + } + "Chat Colors" + { + "en" "Chat Colors" + "jp" "チャットの色" + //"chi" "" + //"zho" "" + } + "Check Setup of Player" + { + "en" "Check Setup of Player" + "jp" "プレイヤーの設定値を確認" + //"chi" "" + //"zho" "" + } + "Chat Command To Change Tag" + { + "en" "Chat command to change tag:" + "jp" "タグを変更するコマンド:" + //"chi" "" + //"zho" "" + } + "Tag Text You Want" + { + "en" "!tag " + "jp" "!tag <設定したいテキスト>" + //"chi" "" + //"zho" "" + } + "Characters Max" + { + "#format" "{1:i}" + "en" "({1} Characters Max)" + "jp" "(最大 {1} 文字)" + //"chi" "" + //"zho" "" + } + "Check Tag Setup For" + { + "en" "Check Tag Setup for:" + "jp" "タグの設定値を確認:" + //"chi" "" + //"zho" "" + } + "Reset" + { + "en" "Reset" + "jp" "リセット" + //"chi" "" + //"zho" "" + } + "Define Your Own Color" + { + "en" "Define Your Own Color" + "jp" "自分の好みの色を定義" + //"chi" "" + //"zho" "" + } +} \ No newline at end of file diff --git a/ChatTags/translations/scp.csgo.phrases.txt b/ChatTags/translations/scp.csgo.phrases.txt new file mode 100644 index 0000000..27a8398 --- /dev/null +++ b/ChatTags/translations/scp.csgo.phrases.txt @@ -0,0 +1,53 @@ +"Phrases" +{ + "Cstrike_Chat_CT_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(Counter-Terrorist) {1} : {2}" + } + "Cstrike_Chat_CT" + { + "#format" "{1:s},{2:s}" + "en" "(Counter-Terrorist) {1} : {2}" + } + "Cstrike_Chat_T_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(Terrorist) {1} : {2}" + } + "Cstrike_Chat_T" + { + "#format" "{1:s},{2:s}" + "en" "(Terrorist) {1} : {2}" + } + "Cstrike_Chat_CT_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Counter-Terrorist) {1} : {2}" + } + "Cstrike_Chat_T_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Terrorist) {1} : {2}" + } + "Cstrike_Chat_Spec" + { + "#format" "{1:s},{2:s}" + "en" "(Spectator) {1} : {2}" + } + "Cstrike_Chat_All" + { + "#format" "{1:s},{2:s}" + "en" "{1} : {2}" + } + "Cstrike_Chat_AllDead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD* {1} : {2}" + } + "Cstrike_Chat_AllSpec" + { + "#format" "{1:s},{2:s}" + "en" "*SPEC* {1} : {2}" + } +} \ No newline at end of file diff --git a/ChatTags/translations/scp.cstrike.phrases.txt b/ChatTags/translations/scp.cstrike.phrases.txt new file mode 100644 index 0000000..27a8398 --- /dev/null +++ b/ChatTags/translations/scp.cstrike.phrases.txt @@ -0,0 +1,53 @@ +"Phrases" +{ + "Cstrike_Chat_CT_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(Counter-Terrorist) {1} : {2}" + } + "Cstrike_Chat_CT" + { + "#format" "{1:s},{2:s}" + "en" "(Counter-Terrorist) {1} : {2}" + } + "Cstrike_Chat_T_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(Terrorist) {1} : {2}" + } + "Cstrike_Chat_T" + { + "#format" "{1:s},{2:s}" + "en" "(Terrorist) {1} : {2}" + } + "Cstrike_Chat_CT_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Counter-Terrorist) {1} : {2}" + } + "Cstrike_Chat_T_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Terrorist) {1} : {2}" + } + "Cstrike_Chat_Spec" + { + "#format" "{1:s},{2:s}" + "en" "(Spectator) {1} : {2}" + } + "Cstrike_Chat_All" + { + "#format" "{1:s},{2:s}" + "en" "{1} : {2}" + } + "Cstrike_Chat_AllDead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD* {1} : {2}" + } + "Cstrike_Chat_AllSpec" + { + "#format" "{1:s},{2:s}" + "en" "*SPEC* {1} : {2}" + } +} \ No newline at end of file diff --git a/ChatTags/translations/scp.insurgency.phrases.txt b/ChatTags/translations/scp.insurgency.phrases.txt new file mode 100644 index 0000000..700be47 --- /dev/null +++ b/ChatTags/translations/scp.insurgency.phrases.txt @@ -0,0 +1,68 @@ +"Phrases" +{ + "Game_radio" + { + "#format" "{1:s},{2:s}" + "en" " {1} (RADIO): {2}" + } + "Game_radio_location" + { + "#format" "{1:s},{2:s}" + "en" " {1} (RADIO): {2}" + } + "INS_Chat_T1_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(Security) {1} : {2}" + } + "INS_Chat_T1" + { + "#format" "{1:s},{2:s}" + "en" "(Security) {1}: {2}" + } + "INS_Chat_T2_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(Insurgents) {1}: {2}" + } + "INS_Chat_T2" + { + "#format" "{1:s},{2:s}" + "en" "(Insurgents) {1}: {2}" + } + "INS_Chat_T1_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Security) {1}: {2}" + } + "INS_Chat_T2_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Insurgents) {1} : {2}" + } + "INS_Chat_Spec" + { + "#format" "{1:s},{2:s}" + "en" "(Spectator) {1} : {2}" + } + "INS_Chat_All" + { + "#format" "{1:s},{2:s}" + "en" " {1} : {2}" + } + "INS_Chat_AllDead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD* {1} : {2}" + } + "INS_Chat_AllSpec" + { + "#format" "{1:s},{2:s}" + "en" "*SPEC* {1} : {2}" + } + "INS_Chat_Party" + { + "#format" "{1:s},{2:s}" + "en" "(Party) {1}" + } +} \ No newline at end of file diff --git a/ChatTags/translations/scp.l4d.phrases.txt b/ChatTags/translations/scp.l4d.phrases.txt new file mode 100644 index 0000000..41d70d1 --- /dev/null +++ b/ChatTags/translations/scp.l4d.phrases.txt @@ -0,0 +1,43 @@ +"Phrases" +{ + "L4D_Chat_Infected" + { + "#format" "{1:s},{2:s}" + "en" "(Infected) {1} : {2}" + } + "L4D_Chat_Survivor" + { + "#format" "{1:s},{2:s}" + "en" "(Survivor) {1} : {2}" + } + "L4D_Chat_Infected_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Infected) {1} : {2}" + } + "L4D_Chat_Survivor_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Survivor) {1} : {2}" + } + "L4D_Chat_Spec" + { + "#format" "{1:s},{2:s}" + "en" "(Spectator) {1} : {2}" + } + "L4D_Chat_All" + { + "#format" "{1:s},{2:s}" + "en" "{1} : {2}" + } + "L4D_Chat_AllDead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD* {1} : {2}" + } + "L4D_Chat_AllSpec" + { + "#format" "{1:s},{2:s}" + "en" "*SPEC* {1} : {2}" + } +} \ No newline at end of file diff --git a/ChatTags/translations/scp.l4d2.phrases.txt b/ChatTags/translations/scp.l4d2.phrases.txt new file mode 100644 index 0000000..41d70d1 --- /dev/null +++ b/ChatTags/translations/scp.l4d2.phrases.txt @@ -0,0 +1,43 @@ +"Phrases" +{ + "L4D_Chat_Infected" + { + "#format" "{1:s},{2:s}" + "en" "(Infected) {1} : {2}" + } + "L4D_Chat_Survivor" + { + "#format" "{1:s},{2:s}" + "en" "(Survivor) {1} : {2}" + } + "L4D_Chat_Infected_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Infected) {1} : {2}" + } + "L4D_Chat_Survivor_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(Survivor) {1} : {2}" + } + "L4D_Chat_Spec" + { + "#format" "{1:s},{2:s}" + "en" "(Spectator) {1} : {2}" + } + "L4D_Chat_All" + { + "#format" "{1:s},{2:s}" + "en" "{1} : {2}" + } + "L4D_Chat_AllDead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD* {1} : {2}" + } + "L4D_Chat_AllSpec" + { + "#format" "{1:s},{2:s}" + "en" "*SPEC* {1} : {2}" + } +} \ No newline at end of file diff --git a/ChatTags/translations/scp.tf.phrases.txt b/ChatTags/translations/scp.tf.phrases.txt new file mode 100644 index 0000000..6ecc614 --- /dev/null +++ b/ChatTags/translations/scp.tf.phrases.txt @@ -0,0 +1,43 @@ +"Phrases" +{ + "TF_Chat_Team_Loc" + { + "#format" "{1:s},{2:s}" + "en" "(TEAM) {1} : {2}" + } + "TF_Chat_Team" + { + "#format" "{1:s},{2:s}" + "en" "(TEAM) {1} : {2}" + } + "TF_Chat_Team_Dead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD*(TEAM) {1} : {2}" + } + "TF_Chat_Spec" + { + "#format" "{1:s},{2:s}" + "en" "(Spectator) {1} : {2}" + } + "TF_Chat_All" + { + "#format" "{1:s},{2:s}" + "en" "{1} : {2}" + } + "TF_Chat_AllDead" + { + "#format" "{1:s},{2:s}" + "en" "*DEAD* {1} : {2}" + } + "TF_Chat_AllSpec" + { + "#format" "{1:s},{2:s}" + "en" "*SPEC* {1} : {2}" + } + "TF_Chat_Coach" + { + "#format" "{1:s},{2:s}" + "en" "(Coach) {1} : {2}" + } +} diff --git a/ChatTags/web/Read Me.txt b/ChatTags/web/Read Me.txt new file mode 100644 index 0000000..4e15637 --- /dev/null +++ b/ChatTags/web/Read Me.txt @@ -0,0 +1,30 @@ +Drop the "session" and "chattags" folders onto your website in the public section. +Drop the "steamauth" folder in your root directory. +The paths in the plugin assume: +//ROOT: + * steamauth/ + * public/ + * chatlogs/ + * sessions/ + +If your structure differs from this, you may want to edit the following lines: + In "chatlogs/index.php", lines 11-13 may need to be adjusted. Here's some explanation of the existing code: + * $_SERVER['DOCUMENT_ROOT'] [line 11] gets the path to the public folder. + * str_replace("/public", "", $_SERVER['DOCUMENT_ROOT']) [line 11] removes "/public" from the above path. Given the structure shown above, that puts us at the ROOT folder. + If yours is different, or if your public folder is named differently, this would need to be edited. + * define('ROOT_FOLDER', str_replace("/public", "", $_SERVER['DOCUMENT_ROOT'])); [line 11] defines the above path as a named constant 'ROOT_FOLDER'. + * define('STEAM_AUTH_FOLDER', ROOT_FOLDER.'/steamauth/'); [line 12] defines 'STEAM_AUTH_FOLDER' as the root path with "/steamauth/" appended. + * session_save_path($_SERVER['DOCUMENT_ROOT'].'/sessions'); [line 13] says that the session data for a visitor are saved in the public folder in the "sessions" subfolder. + +In "chatlogs/index.php", add admins by adding them as text (inside quotes) into the array on line 15. This will give access to IP searches and seeing IP info. + +In "steamauth/SteamConfig.php", fill in line 3 and 4 (follow the comments to the right of the lines). + +In "chatlogs/runquery.php", + * Set your database info on lines 2-5. + * Line 6 sets the number of rows from the database to show per page. + * Around line 300, you will see where you can manipulate the names of the tabs from being server IPs, to whatever else you want it to be (names presumably). + +Fin. Make edits to banners, backgrounds, logos, and/or text as your see fit. + +Note: The server validation file is only included if you wish to set up server validation via a database. It is a bit complex to set up, so see me if you wish to enable this. \ No newline at end of file diff --git a/ChatTags/web/chatlogs/formatting.css b/ChatTags/web/chatlogs/formatting.css new file mode 100644 index 0000000..5f055bb --- /dev/null +++ b/ChatTags/web/chatlogs/formatting.css @@ -0,0 +1,221 @@ +body +{ + background:#1b1b1b url(img/bg.jpg) top center no-repeat; + color:#bbb; + font-size:12px; + font-family:Verdana, Geneva, sans-serif; + margin:0px 0px 0px 0px; + line-height:18px; + overflow:auto; +} + +#wrap +{ + width:auto; + margin:0 auto; + min-height:100px; + padding:20px 20px 20px 20px; + position:relative; + overflow:auto; +} + +.infobox +{ + width:auto; + height:auto; + position:relative; + background:url(img/tranbg2.png); + border-radius:10px; + padding:10px 15px 10px 15px; + box-shadow: 5px 5px 10px #121212; + overflow:auto; + margin-left: auto; + margin-right: auto; +} + +.infobox2 +{ + width:auto; + height:auto; + /*position:relative;*/ + background:url(img/tranbg2.png); + border-radius:10px; + padding:10px 15px 10px 15px; + box-shadow: 5px 5px 10px #121212; + overflow:auto; + margin-left: auto; + margin-right: auto; + float:right; +} + +.headpoint1 +{ + font-size:16px; + font-weight:bold; + color:#ddd; +} + +.headpoint2 +{ + font-size:30px; + font-weight:bold; + color:#ddd; +} + +.headpoint3 +{ + font-size:18px; + font-weight:bold; + color:#ddd; +} + +.headpoint4 +{ + font-size:14px; + color:#ddd; +} + +.headpoint5 +{ + font-size:12px; + color:#ddd; +} + +ul.rules li +{ + padding-top:1px; + padding-bottom:1px; +} + + + +.cf:before, .cf:after{ + content:""; + display:table; +} + +.cf:after{ + clear:both; +} + +.cf{ + zoom:1; +} +/* Form wrapper styling */ +.form-wrapper { + /*width: 295px;*/ + width: auto; + height: auto; + margin: 2px auto 2px auto; + background: #444; + background: rgba(0,0,0,.2); + border-radius: 10px; + box-shadow: 0 1px 1px rgba(0,0,0,.4) inset, 0 1px 0 rgba(255,255,255,.2); +} + +/* Form wrapper styling */ +.form-wrapper { + /*width: 295px;*/ + width: auto; + height: auto; + margin: 2px auto 2px auto; + background: #444; + background: rgba(0,0,0,.2); + border-radius: 10px; + box-shadow: 0 1px 1px rgba(0,0,0,.4) inset, 0 1px 0 rgba(255,255,255,.2); +} + +/* Form text input */ + +.form-wrapper input { + width: 200px; + height: 16px; + padding: 5px 1px; + float: left; + font: bold 15px 'lucida sans', 'trebuchet MS', 'Tahoma'; + border: 0; + background: #eee; + border-radius: 0; +} + +.form-wrapper input:focus { + outline: 0; + background: #fff; + box-shadow: 0 0 2px rgba(0,0,0,.8) inset; +} + +.form-wrapper input::-webkit-input-placeholder { + color: #999; + font-weight: normal; + font-style: italic; +} + +.form-wrapper input:-moz-placeholder { + color: #999; + font-weight: normal; + font-style: italic; +} + +.form-wrapper input:-ms-input-placeholder { + color: #999; + font-weight: normal; + font-style: italic; +} + +/* Form submit button */ +.form-wrapper button { + overflow: visible; + position: relative; + float: right; + border: 0; + padding: 0; + cursor: pointer; + height: 26px; + width: 75px; + font: bold 12px/20px 'lucida sans', 'trebuchet MS', 'Tahoma'; + color: #fff; + text-transform: uppercase; + background: #d83c3c; + border-radius: 0 3px 3px 0; + text-shadow: 0 -1px 0 rgba(0, 0 ,0, .3); +} + +.form-wrapper button:hover{ + background: #e54040; +} + +.form-wrapper button:active, +.form-wrapper button:focus{ + background: #c42f2f; + outline: 0; +} + +.form-wrapper button:before { /* left arrow */ + content: ''; + position: absolute; + border-width: 8px 8px 8px 0; + border-style: solid solid solid none; + border-color: transparent #d83c3c transparent; + top: 5px; + left: -6px; +} + +.form-wrapper button:hover:before{ + border-right-color: #e54040; +} + +.form-wrapper button:focus:before, +.form-wrapper button:active:before{ + border-right-color: #c42f2f; +} + +.form-wrapper button::-moz-focus-inner { /* remove extra button spacing for Mozilla Firefox */ + border: 0; + padding: 0; +} \ No newline at end of file diff --git a/ChatTags/web/chatlogs/img/YOUR_LOGO_HERE.png b/ChatTags/web/chatlogs/img/YOUR_LOGO_HERE.png new file mode 100644 index 0000000..4ca2269 Binary files /dev/null and b/ChatTags/web/chatlogs/img/YOUR_LOGO_HERE.png differ diff --git a/ChatTags/web/chatlogs/img/bg.jpg b/ChatTags/web/chatlogs/img/bg.jpg new file mode 100644 index 0000000..630f081 Binary files /dev/null and b/ChatTags/web/chatlogs/img/bg.jpg differ diff --git a/ChatTags/web/chatlogs/img/bg.png b/ChatTags/web/chatlogs/img/bg.png new file mode 100644 index 0000000..4b3b087 Binary files /dev/null and b/ChatTags/web/chatlogs/img/bg.png differ diff --git a/ChatTags/web/chatlogs/img/example.gif b/ChatTags/web/chatlogs/img/example.gif new file mode 100644 index 0000000..0e3f80d Binary files /dev/null and b/ChatTags/web/chatlogs/img/example.gif differ diff --git a/ChatTags/web/chatlogs/img/fire.gif b/ChatTags/web/chatlogs/img/fire.gif new file mode 100644 index 0000000..004e171 Binary files /dev/null and b/ChatTags/web/chatlogs/img/fire.gif differ diff --git a/ChatTags/web/chatlogs/img/logout.png b/ChatTags/web/chatlogs/img/logout.png new file mode 100644 index 0000000..5b1b88a Binary files /dev/null and b/ChatTags/web/chatlogs/img/logout.png differ diff --git a/ChatTags/web/chatlogs/img/tranbg2.png b/ChatTags/web/chatlogs/img/tranbg2.png new file mode 100644 index 0000000..18b05f4 Binary files /dev/null and b/ChatTags/web/chatlogs/img/tranbg2.png differ diff --git a/ChatTags/web/chatlogs/index.html b/ChatTags/web/chatlogs/index.html new file mode 100644 index 0000000..39c2ad5 --- /dev/null +++ b/ChatTags/web/chatlogs/index.html @@ -0,0 +1,10 @@ + + + Chat Tags: Chat Logs + + +
+ +
+ + \ No newline at end of file diff --git a/ChatTags/web/chatlogs/index.php b/ChatTags/web/chatlogs/index.php new file mode 100644 index 0000000..5a5c535 --- /dev/null +++ b/ChatTags/web/chatlogs/index.php @@ -0,0 +1,486 @@ + + + + + Chat Tags - Chat Logs + + + + + = 16) + { + $z = bcdiv(bcsub($id, '76561197960265728'), '2'); + } + elseif (is_numeric($id)) + { + $z = bcdiv($id, '2'); // Actually new User ID format + } + else + { + return $id; // We have no idea what this is, so just return it. + } + $y = bcmod($id, '2'); + return 'STEAM_0:' . $y . ':' . floor($z); + } + } + + function LogConnectedSteamID($sSteamID, $sSteamName, $servername, $username, $password, $dbname) + { + try + { + $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $stmt = $conn->prepare("CREATE TABLE IF NOT EXISTS `chatlogs_connections` (`id` int(20) PRIMARY KEY AUTO_INCREMENT, `steamid` VARCHAR(65) NOT NULL, `steamname` VARCHAR(65) NOT NULL) DEFAULT CHARSET=latin1"); + $stmt->execute(); + + $stmt = $conn->prepare("INSERT INTO chatlogs_connections (steamid,steamname) VALUES (:steamid,:steamname)"); + $stmt->bindValue(":steamid", $sSteamID, PDO::PARAM_STR); + $stmt->bindValue(":steamname", $sSteamName, PDO::PARAM_STR); + $stmt->execute(); + + $conn = null; + } + catch(PDOException $e) + { + echo "Error: " . $e->getMessage(); + } + } + ?> + + + + + + + + + + +
+ +
+     [Log In for Additional Options]'; + if(defined(REQUIRE_AUTH_FOR_LOGS) == true) + { + $_SESSION['chatlogs_admin'] = "NOT AUTHORIZED"; + } + } + else + { + include_once (STEAM_AUTH_FOLDER.'userInfo.php'); //To access the $steamprofile array + $_SESSION['steamid2'] = toSteamID($steamprofile['steamid']); + echo 'Logged in as: '.$steamprofile['personaname'].'
('.$_SESSION['steamid2'].')'; + $_SESSION['chatlogs_admin'] = "NOT ADMIN"; + if(in_array($_SESSION['steamid2'], $admins)) + { + $_SESSION['chatlogs_admin'] = "ADMIN"; + } + else if(defined(REQUIRE_AUTH_FOR_LOGS) == true) + { + $_SESSION['chatlogs_admin'] = "NOT AUTHORIZED"; + } + echo ' ['.$_SESSION['chatlogs_admin'].']'; + echo '
'; //logoutbutton(); + LogConnectedSteamID(toSteamID($steamprofile['steamid']), $steamprofile['personaname'], $servername, $username, $password, $dbname); + } + ?> +
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + "; + echo ''; + echo '
'; + } + ?> + + + + +
+ +
+

Select Server

+
+ + + + '; + include_once('runquery.php'); + echo ''; + ?> + +
+ +
+

Page created by That One Guy.

+
+ + \ No newline at end of file diff --git a/ChatTags/web/chatlogs/runquery.php b/ChatTags/web/chatlogs/runquery.php new file mode 100644 index 0000000..e3180a6 --- /dev/null +++ b/ChatTags/web/chatlogs/runquery.php @@ -0,0 +1,620 @@ +You must be logged in as an admin to view the chat logs!

'; + die; + } + + function noHTML($input, $encoding = 'UTF-8') + { + return htmlentities($input, ENT_QUOTES | ENT_HTML5, $encoding); + } + + function GetSteamID64($steamid) + { + if(preg_match('/^STEAM_[0-9]:[0-9]:[0-9]{1,}/i', $steamid)) // Valid? + { + // Convert + $steamid = str_replace("_", ":", $steamid); + list($part_one, $part_two, $part_three, $part_four) = explode(':', $steamid); + $result = bcadd('76561197960265728', $part_four * 2); + return bcadd($result, $part_three); + } + else + { + return false; + } + } + + if(!function_exists('toSteamID')) + { + function toSteamID($id) //https://gist.github.com/rannmann/49c1321b3239e00f442c + { + if(is_numeric($id) && strlen($id) >= 16) + { + $z = bcdiv(bcsub($id, '76561197960265728'), '2'); + } + elseif (is_numeric($id)) + { + $z = bcdiv($id, '2'); // Actually new User ID format + } + else + { + return $id; // We have no idea what this is, so just return it. + } + $y = bcmod($id, '2'); + return 'STEAM_0:' . $y . ':' . floor($z); + } + } + + $a_sColNames; + $a_sColNames = array(); + $a_sServers; + $a_sServers = array(); + + define('ROOT_FOLDER', str_replace("/public", "", $_SERVER['DOCUMENT_ROOT'])); + define('STEAM_AUTH_FOLDER', ROOT_FOLDER.'/steamauth/'); + + /*if(isset($_SESSION['steamid'])) + { + include_once (STEAM_AUTH_FOLDER.'userInfo.php'); //To access the $steamprofile array + LogConnectedSteamID(toSteamID($steamprofile['steamid']), $steamprofile['personaname'], $servername, $username, $password, $dbname); + }*/ + //echo 'session id: '.$_SESSION['steamid2'].'
'; + //include_once STEAM_AUTH_FOLDER.'steamauth.php'; + //include_once (STEAM_AUTH_FOLDER.'userInfo.php'); //To access the $steamprofile array + + //$_SESSION['steamid2'] = toSteamID($steamprofile['steamid']); + //$GLOBALS['steamid2'] + //echo ""; + + if(isset($_GET["page"])) + { + $currentpage = urldecode($_GET["page"]); + } + else + { + $currentpage = 1; + }; + + if(isset($_GET["server"])) + { + $server = urldecode($_GET["server"]); + } + else + { + $server = 1; + }; + + if(isset($_GET["input"]) && isset($_GET["inputcol"])) + { + $input = urldecode($_GET["input"]); + $inputcol = urldecode($_GET["inputcol"]); + if($_SESSION['chatlogs_admin'] != "ADMIN") + { + if(($inputcol == "playerip")) + { + $input = ""; + $inputcol = ""; + } + } + } + else + { + $input = ""; + $inputcol = ""; + //echo ""; + }; + + if(isset($_GET["ordercol"])) + { + $ordercol = urldecode($_GET["ordercol"]); + } + else + { + $ordercol = "id"; + }; + + if(isset($_GET["reverse"])) + { + $reverse = urldecode($_GET["reverse"]); + } + else + { + $reverse = ""; + }; + + class TableHeaders extends RecursiveIteratorIterator + { + function __construct($it) + { + parent::__construct($it, self::LEAVES_ONLY); + } + + function current() + { + $colname = parent::current(); + if(($colname == "playername")) + { + $minwidth = 200; + } + else if(($colname == "ip_country") || ($colname == "logdate")) + { + $minwidth = 110; + } + else if(($colname == "chatmsg")) + { + $minwidth = 250; + } + else if(($colname == "chatgrp")) + { + $minwidth = 75; + } + else if(($colname == "mapname") || ($colname == "server") || ($colname == "steamid")) + { + $minwidth = 140; + } + else + { + $minwidth = 0; + } + + $colname = str_replace("_", " ", $colname); + if($colname == "mapname") + { + $colname = "Map Name"; + } + else if($colname == "steamid") + { + $colname = "Steam ID"; + } + else if($colname == "server") + { + $colname = "Server IP"; + } + else if($colname == "playername") + { + $colname = "Player Name"; + } + else if($colname == "playerip") + { + $colname = "Player IP"; + } + else if($colname == "chatmsg") + { + $colname = "Chat Message"; + } + else if($colname == "ip country") + { + $colname = "Country"; + } + else if($colname == "ip_country") + { + $colname = "Country"; + } + else if($colname == "chatgrp") + { + $colname = "Message Type"; + } + else if($colname == "logdate") + { + $colname = "Message Date"; + } + + if(isset($_GET["ordercol"])) //if set + { + $ordercol = urldecode($_GET["ordercol"]); + if(!empty($ordercol)) //if not set to blank + { + if($ordercol == parent::current()) //if the current one we're setting, check for reverse + { + if(!isset($_GET["reverse"])) + { + //return "".$colname. " ▼"; + return "".$colname. " ▼"; + } + else + { + $reverse = urldecode($_GET["reverse"]); + if(($reverse == "false") || empty($reverse)) + { + //return "".$colname. " ▼"; + return "".$colname. " ▼"; + } + else + { + //return "".$colname. " ▲"; + return "".$colname. " ▼"; + } + } + } + } + } + //return "".$colname. ""; + return "".$colname. ""; + } + + function beginChildren() + { + echo ""; + } + + function endChildren() + { + echo ""; + } + } + + class TableRows extends RecursiveIteratorIterator + { + function __construct($it) + { + parent::__construct($it, self::LEAVES_ONLY); + } + + function current() + { + if(parent::current() == "") + { + $value = "-"; + } + else + { + $value = noHTML(parent::current()); + } + //return "".$value. ""; + if(strpos($value, "STEAM_") === false) + { + return "".$value. ""; + } + else + { + return '' .$value. ''; + } + } + + function beginChildren() + { + echo ""; + } + + function endChildren() + { + echo "" . "\n"; + } + } + + try + { + $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $stmt = $conn->prepare("SELECT DISTINCT server FROM ".$tblname); + $stmt->execute(); + $result = $stmt->setFetchMode(PDO::FETCH_ASSOC); + $result = $stmt->fetchAll(); + foreach ($result as $key => $value) + { + $a_sServers[] = $value['server']; + } + + //create tabs + echo '
'; + echo '
'; + echo '
    '; + foreach($a_sServers as $sServer) + { + $sServerName = $sServer; + if($sServerName == "13.73.0.133:27019") + { + $sServerName = "Test Server
    (".$sServerName.")"; + } + else if($sServerName == "13.73.0.133:27017") + { + $sServerName = "Some Fake Server
    (".$sServerName.")"; + } + echo '
  • '.$sServerName.'
  • '; + } + echo '
'; + echo '
'; + echo '
'; + + foreach($a_sServers as $sServer) + { + $sServerDiv = str_replace(".", "_", str_replace(":", "_", $sServer)); + $server = $sServer; + + echo '
'; + /*echo ' '; + echo ''; + echo ''; + echo ''; + */ + //$stmt = $conn->prepare("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '".$tblname."'"); + $sQuery = "SELECT 'server','mapname','steamid','playername'"; + if($_SESSION['chatlogs_admin'] == "ADMIN") + { + $sQuery .=",'playerip'"; + } + $sQuery .=",'ip_country','chatgrp','chatmsg','logdate'"; + $stmt = $conn->prepare($sQuery); + $stmt->execute(); + // set the resulting array to associative + $result = $stmt->setFetchMode(PDO::FETCH_ASSOC); + $result = $stmt->fetchAll(); + foreach ($result as $key => $value) + { + //$a_sColNames[] = $value['COLUMN_NAME']; + $a_sColNames[] = "server"; + $a_sColNames[] = "mapname"; + $a_sColNames[] = "steamid"; + $a_sColNames[] = "playername"; + $a_sColNames[] = "playerip"; + $a_sColNames[] = "ip_country"; + $a_sColNames[] = "chatgrp"; + $a_sColNames[] = "chatmsg"; + $a_sColNames[] = "logdate"; + } + $headers = ''; + foreach(new TableHeaders(new RecursiveArrayIterator($result)) as $key=>$value) + { + $headers = $headers.$value; + } + //echo ""; + echo "
Server IPMap NameSteam IDPlayer NameCountryMessage TypeMessageMessage Date
"; + echo $headers; + + $start_from = ($currentpage - 1) * $num_rec_per_page; + if(empty($reverse) || ($reverse == "false")) + { + $orderdir = "DESC"; + } + else + { + $orderdir = "ASC"; + } + if($_SESSION['chatlogs_admin'] == "ADMIN") + { + //echo ""; + $query = "SELECT server,mapname,steamid,playername,playerip,ip_country,chatgrp,chatmsg,logdate FROM ".$tblname." "; + } + else + { + //echo ""; + $query = "SELECT server,mapname,steamid,playername,ip_country,chatgrp,chatmsg,logdate FROM ".$tblname." "; + } + $bWhere = false; + + if(!empty($inputcol)) + { + $bValid = false; + foreach($a_sColNames as $sColName) + { + if($sColName == $inputcol) + { + $bValid = true; + break; + } + } + + if($bValid == false) + { + $inputcol = ""; + } + } + + $bValid = false; + foreach($a_sColNames as $sColName) + { + if($sColName == $ordercol) + { + $bValid = true; + break; + } + } + if($bValid == false) + { + $ordercol = "id"; + } + + if(!(empty($input) || empty($inputcol))) + { + $bWhere = true; + if(($inputcol == "playername") || ($inputcol == "playerip") || ($inputcol == "steamid") || ($inputcol == "chatmsg")) + { + $query = $query."WHERE (".$inputcol." LIKE :inputstr) "; + $inputtemp = '%'.$input.'%'; + } + else + { + $query = $query."WHERE (".$inputcol." = :inputstr) "; + } + } + + if(!empty($server)) + { + if($bWhere === false) + { + $query = $query."WHERE (server = :server) "; + } + else + { + $query = $query."AND (server = :server) "; + } + $bWhere = true; + } + + $query = $query." ORDER BY ".$ordercol." ".$orderdir." LIMIT :startpgnum, ".$num_rec_per_page; + //echo $query; + $stmt = $conn->prepare($query); + if(!(empty($input) || empty($inputcol))) + { + $stmt->bindValue(":inputstr", $inputtemp, PDO::PARAM_STR); + } + if(!empty($server)) + { + $stmt->bindValue(":server", $server, PDO::PARAM_STR); + } + $stmt->bindValue(":startpgnum", $start_from, PDO::PARAM_INT); + $stmt->execute(); + + // set the resulting array to associative + $result = $stmt->setFetchMode(PDO::FETCH_ASSOC); + + foreach(new TableRows(new RecursiveArrayIterator($stmt->fetchAll())) as $k=>$v) + { + echo $v; + } + echo "
"; + + //page numbers + if(empty($input) || empty($inputcol)) + { + if(!empty($server)) + { + $query = "SELECT * FROM ".$tblname." WHERE server = :server"; + } + else + { + $query = "SELECT * FROM ".$tblname; + } + } + else + { + if(($inputcol == "playername") || ($inputcol == "playerip") || ($inputcol == "steamid") || ($inputcol == "chatmsg")) + { + $query = "SELECT * FROM ".$tblname." WHERE ".$inputcol." LIKE :inputstr"; + $inputtemp = '%'.$input.'%'; + } + else + { + $query = "SELECT * FROM ".$tblname." WHERE ".$inputcol." = :inputstr"; + } + if(!empty($server)) + { + $query = $query." AND server = :server"; + } + } + $stmt = $conn->prepare($query); + if(!(empty($input) || empty($inputcol))) + { + $stmt->bindValue(":inputstr", $inputtemp, PDO::PARAM_STR); + } + if(!empty($server)) + { + $stmt->bindValue(":server", $server, PDO::PARAM_STR); + } + $stmt->execute(); + $i_count = $stmt->rowCount(); + $total_pages = ceil($i_count / $num_rec_per_page); + + echo "Page: "; + if($total_pages <= 10) + { + for($i = 1; $i <= $total_pages; $i++) + { + if($i == $currentpage) + { + echo "".$i.""; + } + else + { + echo ''.$i.''; + } + echo " "; + }; + } + else + { + if($currentpage < 8) //7 or less, then list first 10 + { + for($i = 1; $i <= 9; $i++) + { + if($i == $currentpage) + { + echo "".$i.""; + } + else + { + echo ''.$i.''; + } + echo " "; + }; + echo '>'; // Goto next page at end of list + echo " ... "; + echo ''.$total_pages.''; // Goto last page + } + else + { + echo '1'; // Goto 1st page + echo ' 2'; + echo ' 3'; + echo ' ... '; + $i_temp = $currentpage - 2; + echo ''.$i_temp.' '; //2 less + $i_temp++; + echo ''.$i_temp.' '; //1 less + $i_temp++; + echo "".$i_temp.""; //current + echo " "; + $i_temp++; + if($i_temp <= $total_pages) //if not past last, continue + { + echo ''.$i_temp.' '; //1 past current + $i_temp++; + if($i_temp <= $total_pages) //if not past last, continue + { + echo ''.$i_temp.' '; //2 past current + $i_temp++; + if($i_temp <= $total_pages) //if not past last, show 'next' + { + if(($i_temp + 1 == $total_pages) || ($i_temp == $total_pages)) //show number if within last 2 + { + echo ''.$i_temp.''; + } + else //show symbol for next instead of number + { + echo '>'; + } + $i_temp++; + + if($i_temp <= $total_pages) //if not past last, show last + { + if($i_temp < $total_pages) + { + echo " ... "; + } + else + { + echo " "; + } + + echo ''.$total_pages.''; // Goto last page + } + } + } + } + } + } + echo '
'; + } + } + catch(PDOException $e) + { + echo "Error: " . $e->getMessage(); + } + $conn = null; +?> \ No newline at end of file diff --git a/ChatTags/web/servervalidation/Instructions.txt b/ChatTags/web/servervalidation/Instructions.txt new file mode 100644 index 0000000..394454b --- /dev/null +++ b/ChatTags/web/servervalidation/Instructions.txt @@ -0,0 +1,54 @@ +1. Create a database, username, and password for the plugin validation table. + +//////////////////////////////////////////////////////////////////// +2. Run the query to create the validation table. It is stored in Validation Database Table Creation Query.sql + You can certainly add additional columns for keeping notes, as required. The notes column isnt actually used by the validation, so you can leave that out if you wish as well. + +//////////////////////////////////////////////////////////////////// +3. Edit lines 2-5 of validationdatabase_web.php to be the database information set up in step 1. + +//////////////////////////////////////////////////////////////////// +4. Edit the include for the plugin: servervalidation.inc + Change the line: #define VALIDATION_PHP_URL "http://www.domain.com/servervalidation/validationdatabase_web.php" + Set it to the path for wherever you installed the servervalidation folder. + +//////////////////////////////////////////////////////////////////// +5. Look for the following lines near the top of the plugin that is to be validated. If they dont exist, add them: + +#if defined(VALIDATION_TIME) || defined(VALIDATION_IP) || defined(VALIDATION_HOSTNAME) || defined(VALIDATION_DATABASE) + #include +#endif + +Directly above these lines, add this line: + +#define VALIDATION_DATABASE + +//////////////////////////////////////////////////////////////////// +6. Search in the plugin for: public void OnPluginStart(). + Note: The word void may or may not be included in the line if it is old syntax. + Inside of this function, add the following code (if it doesnt already exist): + +#if defined(VALIDATION_TIME) || defined(VALIDATION_IP) || defined(VALIDATION_HOSTNAME) || defined(VALIDATION_DATABASE) + ValidateServer("SOME PLUGIN KEY HERE"); +#endif + + Note: Replace "SOME PLUGIN KEY HERE" with some custom key for further validation that is harder to spoof. + Leave as empty quotes to exclude the additional check (though there is no reason not to use it). + +//////////////////////////////////////////////////////////////////// +7. Recompile the plugin. + +//////////////////////////////////////////////////////////////////// +8. In the database, insert a row with the server IP under `serveraddr`, the plugin file name under `pluginname`, and the key you made in step 6 under `pluginkey`. + At this time, the port is not checked in the validation. + For the plugin name, you dont need to include .smx, but it wont break the validation if you do. + The validaiton query will look for the pluginname with wildcards on either side, so if the database has .smx included, it will still find a match. + + Note: The whole PHP page is coded using prepared statements to protect against SQL injection. +//////////////////////////////////////////////////////////////////// +9. Install socket extension on the game server. This extension is used to communicate with the web page. You can find the extension here: +https://forums.alliedmods.net/showthread.php?t=67640 + +//////////////////////////////////////////////////////////////////// +10. Put the plugin on the server and reload it via RCon (sm plugins reload YOUR_PLUGINS_NAME_HERE). + If you changed anything in your databases.cfg file for the plugin, you will need to restart the server. diff --git a/ChatTags/web/servervalidation/Validation Database Table Creation Query.sql b/ChatTags/web/servervalidation/Validation Database Table Creation Query.sql new file mode 100644 index 0000000..c78b72a --- /dev/null +++ b/ChatTags/web/servervalidation/Validation Database Table Creation Query.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `validatedplugins` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `serveraddr` varchar(60) NOT NULL, + `pluginname` varchar(200) NOT NULL, + `pluginkey` varchar(200) DEFAULT NULL, + `notes` varchar(200) DEFAULT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/ChatTags/web/servervalidation/validationdatabase_web.php b/ChatTags/web/servervalidation/validationdatabase_web.php new file mode 100644 index 0000000..f350489 --- /dev/null +++ b/ChatTags/web/servervalidation/validationdatabase_web.php @@ -0,0 +1,62 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + if(isset($_GET["pluginkey"])) + { + $pluginkey = $_GET["pluginkey"]; + $stmt = $conn->prepare("SELECT serveraddr,pluginname,pluginkey FROM validatedplugins WHERE (serveraddr LIKE :serveraddr) AND (pluginname LIKE :pluginname) AND (pluginkey = :pluginkey);"); + $stmt->bindValue(':serveraddr', "%{$serveraddr}%"); + $stmt->bindValue(':pluginname', "%{$pluginname}%"); + $stmt->bindValue(':pluginkey', $pluginkey); + } + else + { + $stmt = $conn->prepare("SELECT serveraddr,pluginname FROM validatedplugins WHERE (serveraddr LIKE :serveraddr) AND (pluginname LIKE :pluginname);"); + $stmt->bindValue(':serveraddr', "%{$serveraddr}%"); + $stmt->bindValue(':pluginname', "%{$pluginname}%"); + } + + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); //$row = $stmt->fetch(); + $pos = strpos($row['pluginname'], $pluginname); + if(!($pos === false) && ($row['serveraddr'] == $serveraddr)) + { + echo "PLUGIN IS VALID"; + } + else + { + echo "PLUGIN IS INVALID!!!"; + } + //var_dump($stmt); + //var_dump($row); + //$result = $stmt->setFetchMode(PDO::FETCH_ASSOC); // set the resulting array to associative + //echo "Plugin Valid: $result['pluginname']"; + //echo $result['pluginname']; + //echo ""; + } + catch(PDOException $e) + { + echo "Error: " . $e->getMessage(); + } + + $conn = null; + $stmt = null; +?> \ No newline at end of file diff --git a/ChatTags/web/steamauth/SteamConfig.php b/ChatTags/web/steamauth/SteamConfig.php new file mode 100644 index 0000000..561ff81 --- /dev/null +++ b/ChatTags/web/steamauth/SteamConfig.php @@ -0,0 +1,13 @@ +SteamAuth:
Please supply an API-Key!");} +if (empty($steamauth['domainname'])) {$steamauth['domainname'] = $_SERVER['SERVER_NAME'];} +if (empty($steamauth['logoutpage'])) {$steamauth['logoutpage'] = $_SERVER['PHP_SELF'];} +if (empty($steamauth['loginpage'])) {$steamauth['loginpage'] = $_SERVER['PHP_SELF'];} +?> diff --git a/ChatTags/web/steamauth/openid.php b/ChatTags/web/steamauth/openid.php new file mode 100644 index 0000000..d160236 --- /dev/null +++ b/ChatTags/web/steamauth/openid.php @@ -0,0 +1,1076 @@ += 5.1.2 with cURL or HTTP/HTTPS stream wrappers enabled. + * + * @version v1.3.1 (2016-03-04) + * @link https://code.google.com/p/lightopenid/ Project URL + * @link https://github.com/iignatov/LightOpenID GitHub Repo + * @author Mewp + * @copyright Copyright (c) 2013 Mewp + * @license http://opensource.org/licenses/mit-license.php MIT License + */ +class LightOpenID +{ + public $returnUrl + , $required = array() + , $optional = array() + , $verify_peer = null + , $capath = null + , $cainfo = null + , $cnmatch = null + , $data + , $oauth = array() + , $curl_time_out = 30 // in seconds + , $curl_connect_time_out = 30; // in seconds + private $identity, $claimed_id; + protected $server, $version, $trustRoot, $aliases, $identifier_select = false + , $ax = false, $sreg = false, $setup_url = null, $headers = array() + , $proxy = null, $user_agent = 'LightOpenID' + , $xrds_override_pattern = null, $xrds_override_replacement = null; + static protected $ax_to_sreg = array( + 'namePerson/friendly' => 'nickname', + 'contact/email' => 'email', + 'namePerson' => 'fullname', + 'birthDate' => 'dob', + 'person/gender' => 'gender', + 'contact/postalCode/home' => 'postcode', + 'contact/country/home' => 'country', + 'pref/language' => 'language', + 'pref/timezone' => 'timezone', + ); + + function __construct($host, $proxy = null) + { + $this->set_realm($host); + $this->set_proxy($proxy); + + $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?'); + $this->returnUrl = $this->trustRoot . $uri; + + $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET; + + if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) { + throw new ErrorException('You must have either https wrappers or curl enabled.'); + } + } + + function __isset($name) + { + return in_array($name, array('identity', 'trustRoot', 'realm', 'xrdsOverride', 'mode')); + } + + function __set($name, $value) + { + switch ($name) { + case 'identity': + if (strlen($value = trim((String) $value))) { + if (preg_match('#^xri:/*#i', $value, $m)) { + $value = substr($value, strlen($m[0])); + } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) { + $value = "http://$value"; + } + if (preg_match('#^https?://[^/]+$#i', $value, $m)) { + $value .= '/'; + } + } + $this->$name = $this->claimed_id = $value; + break; + case 'trustRoot': + case 'realm': + $this->trustRoot = trim($value); + break; + case 'xrdsOverride': + if (is_array($value)) { + list($pattern, $replacement) = $value; + $this->xrds_override_pattern = $pattern; + $this->xrds_override_replacement = $replacement; + } else { + trigger_error('Invalid value specified for "xrdsOverride".', E_USER_ERROR); + } + break; + } + } + + function __get($name) + { + switch ($name) { + case 'identity': + # We return claimed_id instead of identity, + # because the developer should see the claimed identifier, + # i.e. what he set as identity, not the op-local identifier (which is what we verify) + return $this->claimed_id; + case 'trustRoot': + case 'realm': + return $this->trustRoot; + case 'mode': + return empty($this->data['openid_mode']) ? null : $this->data['openid_mode']; + } + } + + function set_proxy($proxy) + { + if (!empty($proxy)) { + // When the proxy is a string - try to parse it. + if (!is_array($proxy)) { + $proxy = parse_url($proxy); + } + + // Check if $proxy is valid after the parsing. + if ($proxy && !empty($proxy['host'])) { + // Make sure that a valid port number is specified. + if (array_key_exists('port', $proxy)) { + if (!is_int($proxy['port'])) { + $proxy['port'] = is_numeric($proxy['port']) ? intval($proxy['port']) : 0; + } + + if ($proxy['port'] <= 0) { + throw new ErrorException('The specified proxy port number is invalid.'); + } + } + + $this->proxy = $proxy; + } + } + } + + /** + * Checks if the server specified in the url exists. + * + * @param $url url to check + * @return true, if the server exists; false otherwise + */ + function hostExists($url) + { + if (strpos($url, '/') === false) { + $server = $url; + } else { + $server = @parse_url($url, PHP_URL_HOST); + } + + if (!$server) { + return false; + } + + return !!gethostbynamel($server); + } + + protected function set_realm($uri) + { + $realm = ''; + + # Set a protocol, if not specified. + $realm .= (($offset = strpos($uri, '://')) === false) ? $this->get_realm_protocol() : ''; + + # Set the offset properly. + $offset = (($offset !== false) ? $offset + 3 : 0); + + # Get only the root, without the path. + $realm .= (($end = strpos($uri, '/', $offset)) === false) ? $uri : substr($uri, 0, $end); + + $this->trustRoot = $realm; + } + + protected function get_realm_protocol() + { + if (!empty($_SERVER['HTTPS'])) { + $use_secure_protocol = ($_SERVER['HTTPS'] != 'off'); + } else if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { + $use_secure_protocol = ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'); + } else if (isset($_SERVER['HTTP__WSSC'])) { + $use_secure_protocol = ($_SERVER['HTTP__WSSC'] == 'https'); + } else { + $use_secure_protocol = false; + } + + return $use_secure_protocol ? 'https://' : 'http://'; + } + + protected function request_curl($url, $method='GET', $params=array(), $update_claimed_id) + { + $params = http_build_query($params, '', '&'); + $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : '')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + + if ($method == 'POST') { + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); + } else { + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*')); + } + + curl_setopt($curl, CURLOPT_TIMEOUT, $this->curl_time_out); // defaults to infinite + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out); // defaults to 300s + + if (!empty($this->proxy)) { + curl_setopt($curl, CURLOPT_PROXY, $this->proxy['host']); + + if (!empty($this->proxy['port'])) { + curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxy['port']); + } + + if (!empty($this->proxy['user'])) { + curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); + } + } + + if($this->verify_peer !== null) { + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); + if($this->capath) { + curl_setopt($curl, CURLOPT_CAPATH, $this->capath); + } + + if($this->cainfo) { + curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo); + } + } + + if ($method == 'POST') { + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, $params); + } elseif ($method == 'HEAD') { + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_NOBODY, true); + } else { + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_HTTPGET, true); + } + $response = curl_exec($curl); + + if($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) { + curl_setopt($curl, CURLOPT_HTTPGET, true); + $response = curl_exec($curl); + $response = substr($response, 0, strpos($response, "\r\n\r\n")); + } + + if($method == 'HEAD' || $method == 'GET') { + $header_response = $response; + + # If it's a GET request, we want to only parse the header part. + if($method == 'GET') { + $header_response = substr($response, 0, strpos($response, "\r\n\r\n")); + } + + $headers = array(); + foreach(explode("\n", $header_response) as $header) { + $pos = strpos($header,':'); + if ($pos !== false) { + $name = strtolower(trim(substr($header, 0, $pos))); + $headers[$name] = trim(substr($header, $pos+1)); + } + } + + if($update_claimed_id) { + # Update the claimed_id value in case of redirections. + $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); + # Ignore the fragment (some cURL versions don't handle it well). + if (strtok($effective_url, '#') != strtok($url, '#')) { + $this->identity = $this->claimed_id = $effective_url; + } + } + + if($method == 'HEAD') { + return $headers; + } else { + $this->headers = $headers; + } + } + + if (curl_errno($curl)) { + throw new ErrorException(curl_error($curl), curl_errno($curl)); + } + + return $response; + } + + protected function parse_header_array($array, $update_claimed_id) + { + $headers = array(); + foreach($array as $header) { + $pos = strpos($header,':'); + if ($pos !== false) { + $name = strtolower(trim(substr($header, 0, $pos))); + $headers[$name] = trim(substr($header, $pos+1)); + + # Following possible redirections. The point is just to have + # claimed_id change with them, because the redirections + # are followed automatically. + # We ignore redirections with relative paths. + # If any known provider uses them, file a bug report. + if($name == 'location' && $update_claimed_id) { + if(strpos($headers[$name], 'http') === 0) { + $this->identity = $this->claimed_id = $headers[$name]; + } elseif($headers[$name][0] == '/') { + $parsed_url = parse_url($this->claimed_id); + $this->identity = + $this->claimed_id = $parsed_url['scheme'] . '://' + . $parsed_url['host'] + . $headers[$name]; + } + } + } + } + return $headers; + } + + protected function request_streams($url, $method='GET', $params=array(), $update_claimed_id) + { + if(!$this->hostExists($url)) { + throw new ErrorException("Could not connect to $url.", 404); + } + + if (empty($this->cnmatch)) { + $this->cnmatch = parse_url($url, PHP_URL_HOST); + } + + $params = http_build_query($params, '', '&'); + switch($method) { + case 'GET': + $opts = array( + 'http' => array( + 'method' => 'GET', + 'header' => 'Accept: application/xrds+xml, */*', + 'user_agent' => $this->user_agent, + 'ignore_errors' => true, + ), + 'ssl' => array( + 'CN_match' => $this->cnmatch + ) + ); + $url = $url . ($params ? '?' . $params : ''); + if (!empty($this->proxy)) { + $opts['http']['proxy'] = $this->proxy_url(); + } + break; + case 'POST': + $opts = array( + 'http' => array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'user_agent' => $this->user_agent, + 'content' => $params, + 'ignore_errors' => true, + ), + 'ssl' => array( + 'CN_match' => $this->cnmatch + ) + ); + if (!empty($this->proxy)) { + $opts['http']['proxy'] = $this->proxy_url(); + } + break; + case 'HEAD': + // We want to send a HEAD request, but since get_headers() doesn't + // accept $context parameter, we have to change the defaults. + $default = stream_context_get_options(stream_context_get_default()); + + // PHP does not reset all options. Instead, it just sets the options + // available in the passed array, therefore set the defaults manually. + $default += array( + 'http' => array(), + 'ssl' => array() + ); + $default['http'] += array( + 'method' => 'GET', + 'header' => '', + 'user_agent' => '', + 'ignore_errors' => false + ); + $default['ssl'] += array( + 'CN_match' => '' + ); + + $opts = array( + 'http' => array( + 'method' => 'HEAD', + 'header' => 'Accept: application/xrds+xml, */*', + 'user_agent' => $this->user_agent, + 'ignore_errors' => true, + ), + 'ssl' => array( + 'CN_match' => $this->cnmatch + ) + ); + + // Enable validation of the SSL certificates. + if ($this->verify_peer) { + $default['ssl'] += array( + 'verify_peer' => false, + 'capath' => '', + 'cafile' => '' + ); + $opts['ssl'] += array( + 'verify_peer' => true, + 'capath' => $this->capath, + 'cafile' => $this->cainfo + ); + } + + // Change the stream context options. + stream_context_get_default($opts); + + $headers = get_headers($url . ($params ? '?' . $params : '')); + + // Restore the stream context options. + stream_context_get_default($default); + + if (!empty($headers)) { + if (intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) { + // The server doesn't support HEAD - emulate it with a GET. + $args = func_get_args(); + $args[1] = 'GET'; + call_user_func_array(array($this, 'request_streams'), $args); + $headers = $this->headers; + } else { + $headers = $this->parse_header_array($headers, $update_claimed_id); + } + } else { + $headers = array(); + } + + return $headers; + } + + if ($this->verify_peer) { + $opts['ssl'] += array( + 'verify_peer' => true, + 'capath' => $this->capath, + 'cafile' => $this->cainfo + ); + } + + $context = stream_context_create ($opts); + $data = file_get_contents($url, false, $context); + # This is a hack for providers who don't support HEAD requests. + # It just creates the headers array for the last request in $this->headers. + if(isset($http_response_header)) { + $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id); + } + + return $data; + } + + protected function request($url, $method='GET', $params=array(), $update_claimed_id=false) + { + $use_curl = false; + + if (function_exists('curl_init')) { + if (!$use_curl) { + # When allow_url_fopen is disabled, PHP streams will not work. + $use_curl = !ini_get('allow_url_fopen'); + } + + if (!$use_curl) { + # When there is no HTTPS wrapper, PHP streams cannott be used. + $use_curl = !in_array('https', stream_get_wrappers()); + } + + if (!$use_curl) { + # With open_basedir or safe_mode set, cURL can't follow redirects. + $use_curl = !(ini_get('safe_mode') || ini_get('open_basedir')); + } + } + + return + $use_curl + ? $this->request_curl($url, $method, $params, $update_claimed_id) + : $this->request_streams($url, $method, $params, $update_claimed_id); + } + + protected function proxy_url() + { + $result = ''; + + if (!empty($this->proxy)) { + $result = $this->proxy['host']; + + if (!empty($this->proxy['port'])) { + $result = $result . ':' . $this->proxy['port']; + } + + if (!empty($this->proxy['user'])) { + $result = $this->proxy['user'] . ':' . $this->proxy['pass'] . '@' . $result; + } + + $result = 'http://' . $result; + } + + return $result; + } + + protected function build_url($url, $parts) + { + if (isset($url['query'], $parts['query'])) { + $parts['query'] = $url['query'] . '&' . $parts['query']; + } + + $url = $parts + $url; + $url = $url['scheme'] . '://' + . (empty($url['username'])?'' + :(empty($url['password'])? "{$url['username']}@" + :"{$url['username']}:{$url['password']}@")) + . $url['host'] + . (empty($url['port'])?'':":{$url['port']}") + . (empty($url['path'])?'':$url['path']) + . (empty($url['query'])?'':"?{$url['query']}") + . (empty($url['fragment'])?'':"#{$url['fragment']}"); + return $url; + } + + /** + * Helper function used to scan for / tags and extract information + * from them + */ + protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName) + { + preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1); + preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2); + + $result = array_merge($matches1[1], $matches2[1]); + return empty($result)?false:$result[0]; + } + + /** + * Performs Yadis and HTML discovery. Normally not used. + * @param $url Identity URL. + * @return String OP Endpoint (i.e. OpenID provider address). + * @throws ErrorException + */ + function discover($url) + { + if (!$url) throw new ErrorException('No identity supplied.'); + # Use xri.net proxy to resolve i-name identities + if (!preg_match('#^https?:#', $url)) { + $url = "https://xri.net/$url"; + } + + # We save the original url in case of Yadis discovery failure. + # It can happen when we'll be lead to an XRDS document + # which does not have any OpenID2 services. + $originalUrl = $url; + + # A flag to disable yadis discovery in case of failure in headers. + $yadis = true; + + # Allows optional regex replacement of the URL, e.g. to use Google Apps + # as an OpenID provider without setting up XRDS on the domain hosting. + if (!is_null($this->xrds_override_pattern) && !is_null($this->xrds_override_replacement)) { + $url = preg_replace($this->xrds_override_pattern, $this->xrds_override_replacement, $url); + } + + # We'll jump a maximum of 5 times, to avoid endless redirections. + for ($i = 0; $i < 5; $i ++) { + if ($yadis) { + $headers = $this->request($url, 'HEAD', array(), true); + + $next = false; + if (isset($headers['x-xrds-location'])) { + $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location']))); + $next = true; + } + + if (isset($headers['content-type']) && $this->is_allowed_type($headers['content-type'])) { + # Found an XRDS document, now let's find the server, and optionally delegate. + $content = $this->request($url, 'GET'); + + preg_match_all('#(.*?)#s', $content, $m); + foreach($m[1] as $content) { + $content = ' ' . $content; # The space is added, so that strpos doesn't return 0. + + # OpenID 2 + $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#'); + if(preg_match('#\s*'.$ns.'(server|signon)\s*#s', $content, $type)) { + if ($type[1] == 'server') $this->identifier_select = true; + + preg_match('#(.*)#', $content, $server); + preg_match('#<(Local|Canonical)ID>(.*)#', $content, $delegate); + if (empty($server)) { + return false; + } + # Does the server advertise support for either AX or SREG? + $this->ax = (bool) strpos($content, 'http://openid.net/srv/ax/1.0'); + $this->sreg = strpos($content, 'http://openid.net/sreg/1.0') + || strpos($content, 'http://openid.net/extensions/sreg/1.1'); + + $server = $server[1]; + if (isset($delegate[2])) $this->identity = trim($delegate[2]); + $this->version = 2; + + $this->server = $server; + return $server; + } + + # OpenID 1.1 + $ns = preg_quote('http://openid.net/signon/1.1', '#'); + if (preg_match('#\s*'.$ns.'\s*#s', $content)) { + + preg_match('#(.*)#', $content, $server); + preg_match('#<.*?Delegate>(.*)#', $content, $delegate); + if (empty($server)) { + return false; + } + # AX can be used only with OpenID 2.0, so checking only SREG + $this->sreg = strpos($content, 'http://openid.net/sreg/1.0') + || strpos($content, 'http://openid.net/extensions/sreg/1.1'); + + $server = $server[1]; + if (isset($delegate[1])) $this->identity = $delegate[1]; + $this->version = 1; + + $this->server = $server; + return $server; + } + } + + $next = true; + $yadis = false; + $url = $originalUrl; + $content = null; + break; + } + if ($next) continue; + + # There are no relevant information in headers, so we search the body. + $content = $this->request($url, 'GET', array(), true); + + if (isset($this->headers['x-xrds-location'])) { + $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location']))); + continue; + } + + $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content'); + if ($location) { + $url = $this->build_url(parse_url($url), parse_url($location)); + continue; + } + } + + if (!$content) $content = $this->request($url, 'GET'); + + # At this point, the YADIS Discovery has failed, so we'll switch + # to openid2 HTML discovery, then fallback to openid 1.1 discovery. + $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href'); + $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href'); + $this->version = 2; + + if (!$server) { + # The same with openid 1.1 + $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href'); + $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href'); + $this->version = 1; + } + + if ($server) { + # We found an OpenID2 OP Endpoint + if ($delegate) { + # We have also found an OP-Local ID. + $this->identity = $delegate; + } + $this->server = $server; + return $server; + } + + throw new ErrorException("No OpenID Server found at $url", 404); + } + throw new ErrorException('Endless redirection!', 500); + } + + protected function is_allowed_type($content_type) { + # Apparently, some providers return XRDS documents as text/html. + # While it is against the spec, allowing this here shouldn't break + # compatibility with anything. + $allowed_types = array('application/xrds+xml', 'text/xml'); + + # Only allow text/html content type for the Yahoo logins, since + # it might cause an endless redirection for the other providers. + if ($this->get_provider_name($this->claimed_id) == 'yahoo') { + $allowed_types[] = 'text/html'; + } + + foreach ($allowed_types as $type) { + if (strpos($content_type, $type) !== false) { + return true; + } + } + + return false; + } + + protected function get_provider_name($provider_url) { + $result = ''; + + if (!empty($provider_url)) { + $tokens = array_reverse( + explode('.', parse_url($provider_url, PHP_URL_HOST)) + ); + $result = strtolower( + (count($tokens) > 1 && strlen($tokens[1]) > 3) + ? $tokens[1] + : (count($tokens) > 2 ? $tokens[2] : '') + ); + } + + return $result; + } + + protected function sregParams() + { + $params = array(); + # We always use SREG 1.1, even if the server is advertising only support for 1.0. + # That's because it's fully backwards compatible with 1.0, and some providers + # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com + $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1'; + if ($this->required) { + $params['openid.sreg.required'] = array(); + foreach ($this->required as $required) { + if (!isset(self::$ax_to_sreg[$required])) continue; + $params['openid.sreg.required'][] = self::$ax_to_sreg[$required]; + } + $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']); + } + + if ($this->optional) { + $params['openid.sreg.optional'] = array(); + foreach ($this->optional as $optional) { + if (!isset(self::$ax_to_sreg[$optional])) continue; + $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional]; + } + $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']); + } + return $params; + } + + protected function axParams() + { + $params = array(); + if ($this->required || $this->optional) { + $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; + $params['openid.ax.mode'] = 'fetch_request'; + $this->aliases = array(); + $counts = array(); + $required = array(); + $optional = array(); + foreach (array('required','optional') as $type) { + foreach ($this->$type as $alias => $field) { + if (is_int($alias)) $alias = strtr($field, '/', '_'); + $this->aliases[$alias] = 'http://axschema.org/' . $field; + if (empty($counts[$alias])) $counts[$alias] = 0; + $counts[$alias] += 1; + ${$type}[] = $alias; + } + } + foreach ($this->aliases as $alias => $ns) { + $params['openid.ax.type.' . $alias] = $ns; + } + foreach ($counts as $alias => $count) { + if ($count == 1) continue; + $params['openid.ax.count.' . $alias] = $count; + } + + # Don't send empty ax.required and ax.if_available. + # Google and possibly other providers refuse to support ax when one of these is empty. + if($required) { + $params['openid.ax.required'] = implode(',', $required); + } + if($optional) { + $params['openid.ax.if_available'] = implode(',', $optional); + } + } + return $params; + } + + protected function authUrl_v1($immediate) + { + $returnUrl = $this->returnUrl; + # If we have an openid.delegate that is different from our claimed id, + # we need to somehow preserve the claimed id between requests. + # The simplest way is to just send it along with the return_to url. + if($this->identity != $this->claimed_id) { + $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id; + } + + $params = array( + 'openid.return_to' => $returnUrl, + 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', + 'openid.identity' => $this->identity, + 'openid.trust_root' => $this->trustRoot, + ) + $this->sregParams(); + + return $this->build_url(parse_url($this->server) + , array('query' => http_build_query($params, '', '&'))); + } + + protected function authUrl_v2($immediate) + { + $params = array( + 'openid.ns' => 'http://specs.openid.net/auth/2.0', + 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', + 'openid.return_to' => $this->returnUrl, + 'openid.realm' => $this->trustRoot, + ); + + if ($this->ax) { + $params += $this->axParams(); + } + + if ($this->sreg) { + $params += $this->sregParams(); + } + + if (!$this->ax && !$this->sreg) { + # If OP doesn't advertise either SREG, nor AX, let's send them both + # in worst case we don't get anything in return. + $params += $this->axParams() + $this->sregParams(); + } + + if (!empty($this->oauth) && is_array($this->oauth)) { + $params['openid.ns.oauth'] = 'http://specs.openid.net/extensions/oauth/1.0'; + $params['openid.oauth.consumer'] = str_replace(array('http://', 'https://'), '', $this->trustRoot); + $params['openid.oauth.scope'] = implode(' ', $this->oauth); + } + + if ($this->identifier_select) { + $params['openid.identity'] = $params['openid.claimed_id'] + = 'http://specs.openid.net/auth/2.0/identifier_select'; + } else { + $params['openid.identity'] = $this->identity; + $params['openid.claimed_id'] = $this->claimed_id; + } + + return $this->build_url(parse_url($this->server) + , array('query' => http_build_query($params, '', '&'))); + } + + /** + * Returns authentication url. Usually, you want to redirect your user to it. + * @return String The authentication url. + * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1. + * @throws ErrorException + */ + function authUrl($immediate = false) + { + if ($this->setup_url && !$immediate) return $this->setup_url; + if (!$this->server) $this->discover($this->identity); + + if ($this->version == 2) { + return $this->authUrl_v2($immediate); + } + return $this->authUrl_v1($immediate); + } + + /** + * Performs OpenID verification with the OP. + * @return Bool Whether the verification was successful. + * @throws ErrorException + */ + function validate() + { + # If the request was using immediate mode, a failure may be reported + # by presenting user_setup_url (for 1.1) or reporting + # mode 'setup_needed' (for 2.0). Also catching all modes other than + # id_res, in order to avoid throwing errors. + if(isset($this->data['openid_user_setup_url'])) { + $this->setup_url = $this->data['openid_user_setup_url']; + return false; + } + if($this->mode != 'id_res') { + return false; + } + + $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity']; + $params = array( + 'openid.assoc_handle' => $this->data['openid_assoc_handle'], + 'openid.signed' => $this->data['openid_signed'], + 'openid.sig' => $this->data['openid_sig'], + ); + + if (isset($this->data['openid_ns'])) { + # We're dealing with an OpenID 2.0 server, so let's set an ns + # Even though we should know location of the endpoint, + # we still need to verify it by discovery, so $server is not set here + $params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; + } elseif (isset($this->data['openid_claimed_id']) + && $this->data['openid_claimed_id'] != $this->data['openid_identity'] + ) { + # If it's an OpenID 1 provider, and we've got claimed_id, + # we have to append it to the returnUrl, like authUrl_v1 does. + $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') + . 'openid.claimed_id=' . $this->claimed_id; + } + + if ($this->data['openid_return_to'] != $this->returnUrl) { + # The return_to url must match the url of current request. + # I'm assuming that no one will set the returnUrl to something that doesn't make sense. + return false; + } + + $server = $this->discover($this->claimed_id); + + foreach (explode(',', $this->data['openid_signed']) as $item) { + # Checking whether magic_quotes_gpc is turned on, because + # the function may fail if it is. For example, when fetching + # AX namePerson, it might contain an apostrophe, which will be escaped. + # In such case, validation would fail, since we'd send different data than OP + # wants to verify. stripslashes() should solve that problem, but we can't + # use it when magic_quotes is off. + $value = $this->data['openid_' . str_replace('.','_',$item)]; + $params['openid.' . $item] = function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ? stripslashes($value) : $value; + + } + + $params['openid.mode'] = 'check_authentication'; + + $response = $this->request($server, 'POST', $params); + + return preg_match('/is_valid\s*:\s*true/i', $response); + } + + protected function getAxAttributes() + { + $result = array(); + + if ($alias = $this->getNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')) { + $prefix = 'openid_' . $alias; + $length = strlen('http://axschema.org/'); + + foreach (explode(',', $this->data['openid_signed']) as $key) { + $keyMatch = $alias . '.type.'; + + if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) { + continue; + } + + $key = substr($key, strlen($keyMatch)); + $idv = $prefix . '_value_' . $key; + $idc = $prefix . '_count_' . $key; + $key = substr($this->getItem($prefix . '_type_' . $key), $length); + + if (!empty($key)) { + if (($count = intval($this->getItem($idc))) > 0) { + $value = array(); + + for ($i = 1; $i <= $count; $i++) { + $value[] = $this->getItem($idv . '_' . $i); + } + + $value = ($count == 1) ? reset($value) : $value; + } else { + $value = $this->getItem($idv); + } + + if (!is_null($value)) { + $result[$key] = $value; + } + } + } + } else { + // No alias for the AX schema has been found, + // so there is no AX data in the OP's response. + } + + return $result; + } + + protected function getSregAttributes() + { + $attributes = array(); + $sreg_to_ax = array_flip(self::$ax_to_sreg); + foreach (explode(',', $this->data['openid_signed']) as $key) { + $keyMatch = 'sreg.'; + if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) { + continue; + } + $key = substr($key, strlen($keyMatch)); + if (!isset($sreg_to_ax[$key])) { + # The field name isn't part of the SREG spec, so we ignore it. + continue; + } + $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key]; + } + return $attributes; + } + + /** + * Gets AX/SREG attributes provided by OP. should be used only after successful validation. + * Note that it does not guarantee that any of the required/optional parameters will be present, + * or that there will be no other attributes besides those specified. + * In other words. OP may provide whatever information it wants to. + * * SREG names will be mapped to AX names. + * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email' + * @see http://www.axschema.org/types/ + */ + function getAttributes() + { + if (isset($this->data['openid_ns']) + && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0' + ) { # OpenID 2.0 + # We search for both AX and SREG attributes, with AX taking precedence. + return $this->getAxAttributes() + $this->getSregAttributes(); + } + return $this->getSregAttributes(); + } + + /** + * Gets an OAuth request token if the OpenID+OAuth hybrid protocol has been used. + * + * In order to use the OpenID+OAuth hybrid protocol, you need to add at least one + * scope to the $openid->oauth array before you get the call to getAuthUrl(), e.g.: + * $openid->oauth[] = 'https://www.googleapis.com/auth/plus.me'; + * + * Furthermore the registered consumer name must fit the OpenID realm. + * To register an OpenID consumer at Google use: https://www.google.com/accounts/ManageDomains + * + * @return string|bool OAuth request token on success, FALSE if no token was provided. + */ + function getOAuthRequestToken() + { + $alias = $this->getNamespaceAlias('http://specs.openid.net/extensions/oauth/1.0'); + + return !empty($alias) ? $this->data['openid_' . $alias . '_request_token'] : false; + } + + /** + * Gets the alias for the specified namespace, if it's present. + * + * @param string $namespace The namespace for which an alias is needed. + * @param string $hint Common alias of this namespace, used for optimization. + * @return string|null The namespace alias if found, otherwise - NULL. + */ + private function getNamespaceAlias($namespace, $hint = null) + { + $result = null; + + if (empty($hint) || $this->getItem('openid_ns_' . $hint) != $namespace) { + // The common alias is either undefined or points to + // some other extension - search for another alias.. + $prefix = 'openid_ns_'; + $length = strlen($prefix); + + foreach ($this->data as $key => $val) { + if (strncmp($key, $prefix, $length) === 0 && $val === $namespace) { + $result = trim(substr($key, $length)); + break; + } + } + } else { + $result = $hint; + } + + return $result; + } + + /** + * Gets an item from the $data array by the specified id. + * + * @param string $id The id of the desired item. + * @return string|null The item if found, otherwise - NULL. + */ + private function getItem($id) + { + return isset($this->data[$id]) ? $this->data[$id] : null; + } +} \ No newline at end of file diff --git a/ChatTags/web/steamauth/steamauth.php b/ChatTags/web/steamauth/steamauth.php new file mode 100644 index 0000000..b820527 --- /dev/null +++ b/ChatTags/web/steamauth/steamauth.php @@ -0,0 +1,77 @@ +"; //logout button +} + +function loginbutton($buttonstyle = "square") +{ + $button['rectangle'] = "01"; + $button['square'] = "02"; + $button = ""; + + echo $button; +} + +if (isset($_GET['login'])){ + require 'openid.php'; + try { + require 'SteamConfig.php'; + $openid = new LightOpenID($steamauth['domainname']); + + if(!$openid->mode) { + $openid->identity = 'http://steamcommunity.com/openid'; + header('Location: ' . $openid->authUrl()); + } elseif ($openid->mode == 'cancel') { + echo 'User has canceled authentication!'; + } else { + if($openid->validate()) { + $id = $openid->identity; + $ptn = "/^http:\/\/steamcommunity\.com\/openid\/id\/(7[0-9]{15,25}+)$/"; + preg_match($ptn, $id, $matches); + + $_SESSION['steamid'] = $matches[1]; + if (!headers_sent()) { + header('Location: '.$steamauth['loginpage']); + exit; + } else { + ?> + + + getMessage(); + } +} + +if (isset($_GET['logout'])){ + require 'SteamConfig.php'; + session_unset(); + session_destroy(); + header('Location: '.$steamauth['logoutpage']); + exit; +} + +if (isset($_GET['update']) || !empty($_SESSION['steam_uptodate']) && $_SESSION['steam_uptodate']+(24*60*60) < time()){ + unset($_SESSION['steam_uptodate']); + require 'userInfo.php'; + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + +// Version 3.2 + +?> diff --git a/ChatTags/web/steamauth/userInfo.php b/ChatTags/web/steamauth/userInfo.php new file mode 100644 index 0000000..5003a81 --- /dev/null +++ b/ChatTags/web/steamauth/userInfo.php @@ -0,0 +1,43 @@ + + diff --git a/Extend/gamedata/Extend.games.txt b/Extend/gamedata/Extend.games.txt new file mode 100644 index 0000000..9c9ecf7 --- /dev/null +++ b/Extend/gamedata/Extend.games.txt @@ -0,0 +1,48 @@ +"Games" +{ + "cstrike" + { + "Addresses" + { + "GameOver" + { + "linux" + { + "signature" "g_fGameOver" + } + } + } + + "Signatures" + { + "g_fGameOver" + { + "library" "server" + "linux" "@g_fGameOver" + } + } + } + "csgo" + { + "Addresses" + { + "GameOver" + { + "linux" + { + "signature" "g_fGameOver" + "read" "16" + } + } + } + + "Signatures" + { + "g_fGameOver" + { + "library" "server" + "linux" "\x55\x89\xE5\x57\x56\x53\x31\xDB\x81\xEC\x2A\x2A\x2A\x2A\x80\x3D\x2A\x2A\x2A\x2A\x2A" + } + } + } +} diff --git a/Extend/scripting/Extend.sp b/Extend/scripting/Extend.sp new file mode 100644 index 0000000..6ea08cf --- /dev/null +++ b/Extend/scripting/Extend.sp @@ -0,0 +1,389 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include + +#define VOTE_NO "###no###" +#define VOTE_YES "###yes###" + +ConVar g_cvarExtendVoteTime = null; +ConVar g_cvarExtendVotePercent = null; +ConVar g_cvarMpMaxRounds = null; +ConVar g_cvarMpFragLimit = null; +ConVar g_cvarMpWinLimit = null; +ConVar g_cvarMpTimeLimit = null; + +bool g_bGameOver = false; +Address g_pGameOver; + +public Plugin myinfo = +{ + name = "Map extend tools", + author = "Obus + BotoX", + description = "Adds map extension commands.", + version = "1.0", + url = "" +}; + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("basevotes.phrases"); + + g_cvarMpMaxRounds = FindConVar("mp_maxrounds"); + g_cvarMpFragLimit = FindConVar("mp_fraglimit"); + g_cvarMpWinLimit = FindConVar("mp_winlimit"); + g_cvarMpTimeLimit = FindConVar("mp_timelimit"); + + g_cvarExtendVoteTime = CreateConVar("sm_extendvote_time", "15", "Time that will be added to mp_timelimit shall the extend vote succeed", FCVAR_NONE, true, 1.0); + g_cvarExtendVotePercent = CreateConVar("sm_extendvote_percent", "0.6", "Percentage of \"yes\" votes required to consider the vote successful", FCVAR_NONE, true, 0.05, true, 1.0); + + AutoExecConfig(true, "plugin.Extend"); + + if (g_cvarMpMaxRounds != null) + RegAdminCmd("sm_extend_rounds", Command_Extend_Rounds, ADMFLAG_GENERIC, "Add more rounds to mp_maxrounds"); + else + LogMessage("Failed to find \"mp_maxrounds\" console variable, related commands will be disabled."); + + if (g_cvarMpFragLimit != null) + RegAdminCmd("sm_extend_frags", Command_Extend_Frags, ADMFLAG_GENERIC, "Add more frags to mp_fraglimit"); + else + LogMessage("Failed to find \"mp_fraglimit\" console variable, related commands will be disabled."); + + if (g_cvarMpWinLimit != null) + RegAdminCmd("sm_extend_wins", Command_Extend_Wins, ADMFLAG_GENERIC, "Add more wins to mp_winlimit"); + else + LogMessage("Failed to find \"mp_winlimit\" console variable, related commands will be disabled."); + + if (g_cvarMpTimeLimit != null) + { + RegAdminCmd("sm_extend", Command_Extend, ADMFLAG_GENERIC, "Add more time to mp_timelimit"); + RegAdminCmd("sm_extend_time", Command_Extend, ADMFLAG_GENERIC, "Add more time to mp_timelimit"); + RegAdminCmd("sm_extendvote", Command_ExtendVote, ADMFLAG_GENERIC, "sm_extendvote [time] - Start an extendvote"); + RegAdminCmd("sm_voteextend", Command_ExtendVote, ADMFLAG_GENERIC, "sm_voteextend [time] - Start an extendvote"); + RegAdminCmd("sm_extend_vote", Command_ExtendVote, ADMFLAG_GENERIC, "sm_extend_vote [time] - Start an extendvote"); + } + else + { + LogMessage("Failed to find \"mp_timelimit\" console variable, related commands will be disabled."); + } + + Handle hGameConf = LoadGameConfigFile("Extend.games"); + if(hGameConf == INVALID_HANDLE) + { + g_bGameOver = false; + LogError("Couldn't load Extend.games game config! GameOver cancel disabled."); + return; + } + + if(!(g_pGameOver = GameConfGetAddress(hGameConf, "GameOver"))) + { + g_bGameOver = false; + delete hGameConf; + LogError("Couldn't get GameOver address from game config! GameOver cancel disabled."); + return; + } + + g_bGameOver = true; + delete hGameConf; +} + +public Action Command_Extend_Rounds(int client, int argc) +{ + if (argc < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_extend_rounds "); + return Plugin_Handled; + } + + char sArgs[16]; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + + if (sArgs[0] == '-') + { + int iRoundsToDeduct; + + if (!StringToIntEx(sArgs[1], iRoundsToDeduct)) + { + ReplyToCommand(client, "[SM] Invalid value"); + return Plugin_Handled; + } + + g_cvarMpMaxRounds.IntValue -= iRoundsToDeduct; + + LogAction(client, -1, "\"%L\" deducted \"%d\" rounds from \"mp_maxrounds\"", client, iRoundsToDeduct); + + return Plugin_Handled; + } + + int iRoundsToAdd; + + if (!StringToIntEx(sArgs, iRoundsToAdd)) + { + ReplyToCommand(client, "[SM] Invalid value"); + return Plugin_Handled; + } + + g_cvarMpMaxRounds.IntValue += iRoundsToAdd; + CancelGameOver(); + + LogAction(client, -1, "\"%L\" added \"%d\" rounds to \"mp_maxrounds\"", client, iRoundsToAdd); + + return Plugin_Handled; +} + +public Action Command_Extend_Frags(int client, int argc) +{ + if (argc < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_extend_frags "); + return Plugin_Handled; + } + + char sArgs[16]; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + + if (sArgs[0] == '-') + { + int iFragsToDeduct; + + if (!StringToIntEx(sArgs[1], iFragsToDeduct)) + { + ReplyToCommand(client, "[SM] Invalid value"); + return Plugin_Handled; + } + + g_cvarMpFragLimit.IntValue -= iFragsToDeduct; + + LogAction(client, -1, "\"%L\" deducted \"%d\" frags from \"mp_fraglimit\"", client, iFragsToDeduct); + + return Plugin_Handled; + } + + int iFragsToAdd; + + if (!StringToIntEx(sArgs, iFragsToAdd)) + { + ReplyToCommand(client, "[SM] Invalid value"); + return Plugin_Handled; + } + + g_cvarMpFragLimit.IntValue += iFragsToAdd; + CancelGameOver(); + + LogAction(client, -1, "\"%L\" added \"%d\" frags to \"mp_fraglimit\"", client, iFragsToAdd); + + return Plugin_Handled; +} + +public Action Command_Extend_Wins(int client, int argc) +{ + if (argc < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_extend_wins "); + return Plugin_Handled; + } + + char sArgs[16]; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + + if (sArgs[0] == '-') + { + int iWinsToDeduct; + + if (!StringToIntEx(sArgs[1], iWinsToDeduct)) + { + ReplyToCommand(client, "[SM] Invalid value"); + return Plugin_Handled; + } + + g_cvarMpWinLimit.IntValue -= iWinsToDeduct; + + LogAction(client, -1, "\"%L\" deducted \"%d\" wins from \"mp_winlimit\"", client, iWinsToDeduct); + + return Plugin_Handled; + } + + int iWinsToAdd; + + if (!StringToIntEx(sArgs, iWinsToAdd)) + { + ReplyToCommand(client, "[SM] Invalid value"); + return Plugin_Handled; + } + + g_cvarMpWinLimit.IntValue += iWinsToAdd; + CancelGameOver(); + + LogAction(client, -1, "\"%L\" added \"%d\" wins to \"mp_winlimit\"", client, iWinsToAdd); + + return Plugin_Handled; +} + +public Action Command_Extend(int client, int argc) +{ + if (argc < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_extend
%s%s", sBuf, g_sSpace); + hMessage.AddString("params", sBuf); + + hMessage.AddString("params", NULL_STRING); + hMessage.AddString("params", NULL_STRING); + hMessage.AddString("params", NULL_STRING); + hMessage.AddString("params", NULL_STRING); + + EndMessage(); + } +} diff --git a/FixLagCompensation/gamedata/FixLagCompensation.games.txt b/FixLagCompensation/gamedata/FixLagCompensation.games.txt new file mode 100644 index 0000000..9aae7d2 --- /dev/null +++ b/FixLagCompensation/gamedata/FixLagCompensation.games.txt @@ -0,0 +1,104 @@ +"Games" +{ + "cstrike" + { + "Offsets" + { + "IsMoving" + { + "windows" "74" + "linux" "75" + } + } + + "Signatures" + { + "CLagCompensationManager::BacktrackPlayer" + { + "library" "server" + "linux" "@_ZN23CLagCompensationManager15BacktrackPlayerEP11CBasePlayerf" + } + } + + "Functions" + { + "CLagCompensationManager__BacktrackPlayer" + { + "signature" "CLagCompensationManager::BacktrackPlayer" + "callconv" "thiscall" + "return" "void" + "this" "ignore" + "arguments" + { + "pPlayer" + { + "type" "cbaseentity" + } + "flTargetTime" + { + "type" "float" + } + } + } + } + } + "csgo" + { + "Offsets" + { + "IsMoving" + { + "windows" "80" + "linux" "81" + } + } + + "Signatures" + { + "CLagCompensationManager::BacktrackEntity" + { + "library" "server" + "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x6C\x01\x00\x00\x8B\x0D\x2A\x2A\x2A\x2A\x0F\xB6\x45\x20" + "windows" "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\xC4\x00\x00\x00\x89\x4C\x24\x34" + } + } + + "Functions" + { + "CLagCompensationManager__BacktrackEntity" + { + "signature" "CLagCompensationManager::BacktrackEntity" + "callconv" "thiscall" + "return" "bool" + "this" "ignore" + "arguments" + { + "entity" + { + "type" "cbaseentity" + } + "flTargetTime" + { + "type" "float" + } + "track" + { + "type" "objectptr" + } + "restore" + { + "type" "objectptr" + } + "change" + { + "type" "objectptr" + } + "wantsAnims" + { + "type" "bool" + } + } + } + } + } +} diff --git a/FixLagCompensation/scripting/FixLagCompensation.sp b/FixLagCompensation/scripting/FixLagCompensation.sp new file mode 100644 index 0000000..39ee47e --- /dev/null +++ b/FixLagCompensation/scripting/FixLagCompensation.sp @@ -0,0 +1,119 @@ +#include +#include +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +public Plugin myinfo = +{ + name = "FixLagCompensation", + author = "BotoX + xen(CS:GO support)", + description = "Disable player hitbox lag compensation when on moving objects", + version = "1.0", + url = "" +}; + +Handle g_hBacktrackPlayer; +Handle g_hBacktrackEntity; +Handle g_hIsMoving; + +public void OnPluginStart() +{ + Handle hGameData = LoadGameConfigFile("FixLagCompensation.games"); + if(!hGameData) + SetFailState("Failed to load FixLagCompensation gamedata."); + + if (GetEngineVersion() != Engine_CSGO) + { + // void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, float flTargetTime ) + g_hBacktrackPlayer = DHookCreateFromConf(hGameData, "CLagCompensationManager__BacktrackPlayer"); + if(!g_hBacktrackPlayer) + { + delete hGameData; + SetFailState("Failed to setup detour for CLagCompensationManager__BacktrackPlayer"); + } + + if(!DHookEnableDetour(g_hBacktrackPlayer, false, Detour_OnBacktrackPlayer)) + { + delete hGameData; + SetFailState("Failed to detour CLagCompensationManager__BacktrackPlayer."); + } + } + else + { + // bool CLagCompensationManager::BacktrackEntity( CBaseEntity *entity, float flTargetTime, LagRecordList *track, LagRecord *restore, LagRecord *change, bool wantsAnims ) + g_hBacktrackEntity = DHookCreateFromConf(hGameData, "CLagCompensationManager__BacktrackEntity"); + if(!g_hBacktrackEntity) + { + delete hGameData; + SetFailState("Failed to setup detour for CLagCompensationManager__BacktrackEntity"); + } + + if(!DHookEnableDetour(g_hBacktrackEntity, false, Detour_OnBacktrackEntity)) + { + delete hGameData; + SetFailState("Failed to detour CLagCompensationManager__BacktrackEntity."); + } + } + + // CBaseEntity::IsMoving + StartPrepSDKCall(SDKCall_Entity); + if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "IsMoving")) + { + delete hGameData; + SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, \"IsMoving\") failed!"); + } + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + g_hIsMoving = EndPrepSDKCall(); + + delete hGameData; +} + +// CS:S +public MRESReturn Detour_OnBacktrackPlayer(Handle hParams) +{ + if(DHookIsNullParam(hParams, 1)) + return MRES_Ignored; + + int client = DHookGetParam(hParams, 1); + if(client < 1 || client > MaxClients) + return MRES_Ignored; + + int GroundEntity = GetEntPropEnt(client, Prop_Data, "m_hGroundEntity"); + if(GroundEntity <= 0) + return MRES_Ignored; + + bool bIsMoving = SDKCall(g_hIsMoving, GroundEntity); + + if(bIsMoving) + return MRES_Supercede; + + return MRES_Ignored; +} + +// CS:GO +public MRESReturn Detour_OnBacktrackEntity(Handle hReturn, Handle hParams) +{ + if(DHookIsNullParam(hParams, 1)) + return MRES_Ignored; + + int client = DHookGetParam(hParams, 1); + if(client < 1 || client > MaxClients) + return MRES_Ignored; + + int GroundEntity = GetEntPropEnt(client, Prop_Data, "m_hGroundEntity"); + if(GroundEntity <= 0) + return MRES_Ignored; + + bool bIsMoving = SDKCall(g_hIsMoving, GroundEntity); + + if(bIsMoving) + { + DHookSetReturn(hReturn, false); + return MRES_Supercede; + } + + return MRES_Ignored; +} diff --git a/FixPhysboxMultiplayerCollision/gamedata/FixPhysBoxMultiplayer.games.txt b/FixPhysboxMultiplayerCollision/gamedata/FixPhysBoxMultiplayer.games.txt new file mode 100644 index 0000000..1832ba7 --- /dev/null +++ b/FixPhysboxMultiplayerCollision/gamedata/FixPhysBoxMultiplayer.games.txt @@ -0,0 +1,14 @@ +"Games" +{ + "csgo" + { + "Offsets" + { + "CBaseEntity::SetParent" + { + "windows" "39" + "linux" "40" + } + } + } +} diff --git a/FixPhysboxMultiplayerCollision/scripting/FixPhysboxMultiplayerCollision.sp b/FixPhysboxMultiplayerCollision/scripting/FixPhysboxMultiplayerCollision.sp new file mode 100644 index 0000000..55dff86 --- /dev/null +++ b/FixPhysboxMultiplayerCollision/scripting/FixPhysboxMultiplayerCollision.sp @@ -0,0 +1,105 @@ +#include +#include +#include +#pragma semicolon 1 +#pragma newdecls required + +#define PLUGIN_VERSION "1.0" + +public Plugin myinfo = +{ + name = "Fix func_physbox_multiplayer collision", + author = "xen", + description = "Sets the collision to weapons only whenever a func_physbox_multiplayer gets parented", + version = PLUGIN_VERSION, + url = "" +} + +Handle g_hSetParent; + +// Entity collision groups +enum +{ + COLLISION_GROUP_NONE = 0, // 0 + COLLISION_GROUP_DEBRIS, // 1 - Collides with nothing but world and static stuff + COLLISION_GROUP_DEBRIS_TRIGGER, // 2 - Same as debris, but hits triggers + COLLISION_GROUP_INTERACTIVE_DEBRIS, // 3 - Collides with everything except other interactive debris or debris + COLLISION_GROUP_INTERACTIVE, // 4 - Collides with everything except interactive debris or debris + COLLISION_GROUP_PLAYER, // 5 + COLLISION_GROUP_BREAKABLE_GLASS, // 6 + COLLISION_GROUP_VEHICLE, // 7 + COLLISION_GROUP_PLAYER_MOVEMENT, // 8 - For HL2, same as Collision_Group_Player, for + // TF2, this filters out other players and CBaseObjects + COLLISION_GROUP_NPC, // 9 - Generic NPC group + COLLISION_GROUP_IN_VEHICLE, // 10 - for any entity inside a vehicle + COLLISION_GROUP_WEAPON, // 11 - for any weapons that need collision detection + COLLISION_GROUP_VEHICLE_CLIP, // 12 - vehicle clip brush to restrict vehicle movement + COLLISION_GROUP_PROJECTILE, // 13 - Projectiles! + COLLISION_GROUP_DOOR_BLOCKER, // 14 - Blocks entities not permitted to get near moving doors + COLLISION_GROUP_PASSABLE_DOOR, // 15 - Doors that the player shouldn't collide with + COLLISION_GROUP_DISSOLVING, // 16 - Things that are dissolving are in this group + COLLISION_GROUP_PUSHAWAY, // 17 - Nonsolid on client and server, pushaway in player code + + COLLISION_GROUP_NPC_ACTOR, // 18 - Used so NPCs in scripts ignore the player. + COLLISION_GROUP_NPC_SCRIPTED, // 19 - USed for NPCs in scripts that should not collide with each other + + LAST_SHARED_COLLISION_GROUP +}; + +// Physbox spawnflags +enum +{ + SF_PHYSBOX_ASLEEP = 0x01000, + SF_PHYSBOX_IGNOREUSE = 0x02000, + SF_PHYSBOX_DEBRIS = 0x04000, + SF_PHYSBOX_MOTIONDISABLED = 0x08000, + SF_PHYSBOX_USEPREFERRED = 0x10000, + SF_PHYSBOX_ENABLE_ON_PHYSCANNON = 0x20000, + SF_PHYSBOX_NO_ROTORWASH_PUSH = 0x40000, // The rotorwash doesn't push these + SF_PHYSBOX_ENABLE_PICKUP_OUTPUT = 0x80000, + SF_PHYSBOX_ALWAYS_PICK_UP = 0x100000, // Physcannon can always pick this up, no matter what mass or constraints may apply. + SF_PHYSBOX_NEVER_PICK_UP = 0x200000, // Physcannon will never be able to pick this up. + SF_PHYSBOX_NEVER_PUNT = 0x400000, // Physcannon will never be able to punt this object. + SF_PHYSBOX_PREVENT_PLAYER_TOUCH_ENABLE = 0x800000 // If set, the player will not cause the object to enable its motion when bumped into +}; + +public void OnPluginStart() +{ + Handle hGameConf = LoadGameConfigFile("FixPhysboxMultiplayer.games"); + if(!hGameConf) + { + SetFailState("Can't find FixPhysBoxMultiplayers.games.txt gamedata."); + return; + } + + int offset = GameConfGetOffset(hGameConf, "CBaseEntity::SetParent"); + if (offset == -1) + SetFailState("Failed to find CBaseEntity::SetParent offset"); + + // DHooks + g_hSetParent = DHookCreate(offset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, Hook_SetParent); + DHookAddParam(g_hSetParent, HookParamType_CBaseEntity); + DHookAddParam(g_hSetParent, HookParamType_Int); + + CloseHandle(hGameConf); +} + +public void OnEntityCreated(int iEntity, const char[] sClassname) +{ + if(StrEqual(sClassname, "func_physbox_multiplayer", false)) + DHookEntity(g_hSetParent, true, iEntity); +} + +public MRESReturn Hook_SetParent(int iEntity) +{ + SetDebrisCollisionGroup(iEntity); + return MRES_Ignored; +} + +public void SetDebrisCollisionGroup(int iEntity) +{ + // Set collisiongroup to WEAPON to replicate CS:S behavior when parented + char parent[64]; + if(GetEntPropString(iEntity, Prop_Data, "m_iParent", parent, sizeof(parent))) + SetEntProp(iEntity, Prop_Data, "m_CollisionGroup", COLLISION_GROUP_WEAPON); +} diff --git a/ForceConvars/scripting/ForceConvars.sp b/ForceConvars/scripting/ForceConvars.sp new file mode 100644 index 0000000..c7357dd --- /dev/null +++ b/ForceConvars/scripting/ForceConvars.sp @@ -0,0 +1,57 @@ +#pragma newdecls required + +#include + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "Force ConVars", + author = "zaCade", + description = "Force ConVars to specific values.", + version = "1.0.0" +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + if(GetEngineVersion() != Engine_CSGO) + { + strcopy(error, err_max, "This plugin is only required on CS:GO!"); + return APLRes_Failure; + } + + return APLRes_Success; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + RegServerCmd("sm_forcevar", Command_ForceCVar); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_ForceCVar(int args) +{ + char sArguments[2][128]; + GetCmdArg(1, sArguments[0], sizeof(sArguments[])); + GetCmdArg(2, sArguments[1], sizeof(sArguments[])); + + ConVar CVar; + if ((CVar = FindConVar(sArguments[0])) != null) + { + float fValue = StringToFloat(sArguments[1]); + + CVar.SetBounds(ConVarBound_Lower, true, fValue); + CVar.SetBounds(ConVarBound_Upper, true, fValue); + + CVar.SetFloat(fValue, true, false); + } +} \ No newline at end of file diff --git a/ForceInputs/scripting/ForceInputs.sp b/ForceInputs/scripting/ForceInputs.sp new file mode 100644 index 0000000..2173515 --- /dev/null +++ b/ForceInputs/scripting/ForceInputs.sp @@ -0,0 +1,206 @@ +//==================================================================================================== +// +// Name: ForceInput +// Author: zaCade + BotoX +// Description: Allows admins to force inputs on entities. (ent_fire) +// +//==================================================================================================== +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "ForceInput", + author = "zaCade + BotoX", + description = "Allows admins to force inputs on entities. (ent_fire)", + version = "2.1.1", + url = "" +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + + RegAdminCmd("sm_forceinput", Command_ForceInput, ADMFLAG_RCON); + RegAdminCmd("sm_forceinputplayer", Command_ForceInputPlayer, ADMFLAG_RCON); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_ForceInputPlayer(int client, int args) +{ + if(GetCmdArgs() < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_forceinputplayer [parameter]"); + return Plugin_Handled; + } + + char sArguments[3][256]; + GetCmdArg(1, sArguments[0], sizeof(sArguments[])); + GetCmdArg(2, sArguments[1], sizeof(sArguments[])); + GetCmdArg(3, sArguments[2], sizeof(sArguments[])); + + char sTargetName[MAX_TARGET_LENGTH]; + int aTargetList[MAXPLAYERS]; + int TargetCount; + bool TnIsMl; + + if((TargetCount = ProcessTargetString( + sArguments[0], + client, + aTargetList, + MAXPLAYERS, + COMMAND_FILTER_CONNECTED|COMMAND_FILTER_NO_IMMUNITY, + sTargetName, + sizeof(sTargetName), + TnIsMl)) <= 0) + { + ReplyToTargetError(client, TargetCount); + return Plugin_Handled; + } + + for(int i = 0; i < TargetCount; i++) + { + if(!IsValidEntity(aTargetList[i])) + continue; + + if(sArguments[2][0]) + SetVariantString(sArguments[2]); + + AcceptEntityInput(aTargetList[i], sArguments[1], aTargetList[i], aTargetList[i]); + ReplyToCommand(client, "[SM] Input successful."); + LogAction(client, -1, "\"%L\" used ForceInputPlayer on \"%L\": \"%s %s\"", client, aTargetList[i], sArguments[1], sArguments[2]); + } + + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_ForceInput(int client, int args) +{ + if(GetCmdArgs() < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_forceinput [parameter]"); + return Plugin_Handled; + } + + char sArguments[3][256]; + GetCmdArg(1, sArguments[0], sizeof(sArguments[])); + GetCmdArg(2, sArguments[1], sizeof(sArguments[])); + GetCmdArg(3, sArguments[2], sizeof(sArguments[])); + + if(StrEqual(sArguments[0], "!self")) + { + if(sArguments[2][0]) + SetVariantString(sArguments[2]); + + AcceptEntityInput(client, sArguments[1], client, client); + ReplyToCommand(client, "[SM] Input successful."); + LogAction(client, -1, "\"%L\" used ForceInput on himself: \"%s %s\"", client, sArguments[1], sArguments[2]); + } + else if(StrEqual(sArguments[0], "!target")) + { + float fPosition[3]; + float fAngles[3]; + GetClientEyePosition(client, fPosition); + GetClientEyeAngles(client, fAngles); + + Handle hTrace = TR_TraceRayFilterEx(fPosition, fAngles, MASK_SOLID, RayType_Infinite, TraceRayFilter, client); + + if(TR_DidHit(hTrace)) + { + int entity = TR_GetEntityIndex(hTrace); + + if(entity <= 1 || !IsValidEntity(entity)) + { + CloseHandle(hTrace); + return Plugin_Handled; + } + + if(sArguments[2][0]) + SetVariantString(sArguments[2]); + + AcceptEntityInput(entity, sArguments[1], client, client); + ReplyToCommand(client, "[SM] Input successful."); + + char sClassname[64]; + char sTargetname[64]; + GetEntPropString(entity, Prop_Data, "m_iClassname", sClassname, sizeof(sClassname)); + GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + LogAction(client, -1, "\"%L\" used ForceInput on Entity \"%d\" - \"%s\" - \"%s\": \"%s %s\"", client, entity, sClassname, sTargetname, sArguments[1], sArguments[2]); + } + CloseHandle(hTrace); + } + else if(sArguments[0][0] == '#') // HammerID + { + int HammerID = StringToInt(sArguments[0][1]); + + int entity = INVALID_ENT_REFERENCE; + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) + { + if(GetEntProp(entity, Prop_Data, "m_iHammerID") == HammerID) + { + if(sArguments[2][0]) + SetVariantString(sArguments[2]); + + AcceptEntityInput(entity, sArguments[1], client, client); + ReplyToCommand(client, "[SM] Input successful."); + + char sClassname[64]; + char sTargetname[64]; + GetEntPropString(entity, Prop_Data, "m_iClassname", sClassname, sizeof(sClassname)); + GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + LogAction(client, -1, "\"%L\" used ForceInput on Entity \"%d\" - \"%s\" - \"%s\": \"%s %s\"", client, entity, sClassname, sTargetname, sArguments[1], sArguments[2]); + } + } + } + else + { + int Wildcard = FindCharInString(sArguments[0], '*'); + + int entity = INVALID_ENT_REFERENCE; + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) + { + char sClassname[64]; + char sTargetname[64]; + GetEntPropString(entity, Prop_Data, "m_iClassname", sClassname, sizeof(sClassname)); + GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + if(strncmp(sClassname, sArguments[0], Wildcard, false) == 0 + || strncmp(sTargetname, sArguments[0], Wildcard, false) == 0) + { + if(sArguments[2][0]) + SetVariantString(sArguments[2]); + + AcceptEntityInput(entity, sArguments[1], client, client); + ReplyToCommand(client, "[SM] Input successful."); + LogAction(client, -1, "\"%L\" used ForceInput on Entity \"%d\" - \"%s\" - \"%s\": \"%s %s\"", client, entity, sClassname, sTargetname, sArguments[1], sArguments[2]); + } + } + } + + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public bool TraceRayFilter(int entity, int mask, any client) +{ + if(entity == client) + return false; + + return true; +} \ No newline at end of file diff --git a/FullUpdate/gamedata/FullUpdate.games.txt b/FullUpdate/gamedata/FullUpdate.games.txt new file mode 100644 index 0000000..1df9b02 --- /dev/null +++ b/FullUpdate/gamedata/FullUpdate.games.txt @@ -0,0 +1,31 @@ +"Games" +{ + "#default" + { + "#supported" + { + "engine" "orangebox" + "engine" "orangebox_valve" + "engine" "css" + } + "Offsets" + { + "CBaseClient::UpdateAcknowledgedFramecount" + { + "library" "engine" + "linux" "44" + } + } + } + "csgo" + { + "Offsets" + { + "CBaseClient::UpdateAcknowledgedFramecount" + { + "library" "engine" + "linux" "55" + } + } + } +} diff --git a/FullUpdate/scripting/FullUpdate.sp b/FullUpdate/scripting/FullUpdate.sp new file mode 100644 index 0000000..cc078fe --- /dev/null +++ b/FullUpdate/scripting/FullUpdate.sp @@ -0,0 +1,106 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include + +Handle g_hCBaseClient_UpdateAcknowledgedFramecount; +int g_iLastFullUpdate[MAXPLAYERS + 1] = { 0, ... }; + +public Plugin myinfo = +{ + name = "FullUpdate", + author = "BotoX", + description = "Serverside cl_fullupdate", + version = "1.0" +} + +public void OnPluginStart() +{ + Handle hGameConf = LoadGameConfigFile("FullUpdate.games"); + if(hGameConf == INVALID_HANDLE) + { + SetFailState("Couldn't load FullUpdate.games game config!"); + return; + } + + // void CBaseClient::UpdateAcknowledgedFramecount() + StartPrepSDKCall(SDKCall_Raw); + + if(!PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "CBaseClient::UpdateAcknowledgedFramecount")) + { + CloseHandle(hGameConf); + SetFailState("PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, \"CBaseClient::UpdateAcknowledgedFramecount\" failed!"); + return; + } + CloseHandle(hGameConf); + + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + + g_hCBaseClient_UpdateAcknowledgedFramecount = EndPrepSDKCall(); + + RegConsoleCmd("sm_fullupdate", Command_FullUpdate); + RegConsoleCmd("cl_fullupdate", Command_FullUpdate); + RegConsoleCmd("fullupdate", Command_FullUpdate); +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + CreateNative("ClientFullUpdate", Native_FullUpdate); + RegPluginLibrary("FullUpdate"); + + return APLRes_Success; +} + +public void OnClientConnected(int client) +{ + g_iLastFullUpdate[client] = 0; +} + +bool FullUpdate(int client) +{ + if(g_iLastFullUpdate[client] + 1 > GetTime()) + return false; + + // The IClient vtable is +4 from the IGameEventListener2 (CBaseClient) vtable due to multiple inheritance. + Address pIClient = GetClientIClient(client) - view_as
(4); + if(!pIClient) + return false; + + SDKCall(g_hCBaseClient_UpdateAcknowledgedFramecount, pIClient, -1); + + g_iLastFullUpdate[client] = GetTime(); + return true; +} + +public int Native_FullUpdate(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if(client > MaxClients || client <= 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); + return 0; + } + + if(!IsClientInGame(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); + return 0; + } + + if(IsFakeClient(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is fake-client."); + return 0; + } + + return FullUpdate(client); +} + +public Action Command_FullUpdate(int client, int args) +{ + FullUpdate(client); + return Plugin_Handled; +} diff --git a/FullUpdate/scripting/include/FullUpdate.inc b/FullUpdate/scripting/include/FullUpdate.inc new file mode 100644 index 0000000..0b026c8 --- /dev/null +++ b/FullUpdate/scripting/include/FullUpdate.inc @@ -0,0 +1,24 @@ +#if defined _FullUpdate_Included + #endinput +#endif +#define _FullUpdate_Included + +native bool ClientFullUpdate(int client); + +public SharedPlugin __pl_FullUpdate = +{ + name = "FullUpdate", + file = "FullUpdate.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_FullUpdate_SetNTVOptional() +{ + MarkNativeAsOptional("ClientFullUpdate"); +} +#endif diff --git a/Hitmarker/configs/Hitmarkers.cfg b/Hitmarker/configs/Hitmarkers.cfg new file mode 100644 index 0000000..607692c --- /dev/null +++ b/Hitmarker/configs/Hitmarkers.cfg @@ -0,0 +1,18 @@ +"Hitmarkers" +{ + "0" //default one + { + "Path" "unloze/hitmarker/hitmarker_nano" + "Name" "Skin 1" + } + "1" + { + "Path" "unloze/hitmarker/hitmarker_guc" + "Name" "Skin 2" + } + "2" + { + "Path" "unloze/hitmarker/hitmarker_es" + "Name" "Skin 3" + } +} diff --git a/Hitmarker/content/materials/unloze/hitmarker/hitmarker_es.vmt b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_es.vmt new file mode 100644 index 0000000..b5ece8b --- /dev/null +++ b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_es.vmt @@ -0,0 +1,7 @@ +"MonitorScreen" +{ + "$basetexture" "unloze/hitmarker/hitmarker_es" + "$alphatest" 1 + "$translucent" 1 + "$vertexalpha" 1 +} \ No newline at end of file diff --git a/Hitmarker/content/materials/unloze/hitmarker/hitmarker_es.vtf b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_es.vtf new file mode 100644 index 0000000..d13dd41 Binary files /dev/null and b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_es.vtf differ diff --git a/Hitmarker/content/materials/unloze/hitmarker/hitmarker_guc.vmt b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_guc.vmt new file mode 100644 index 0000000..87d73c2 --- /dev/null +++ b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_guc.vmt @@ -0,0 +1,7 @@ +"MonitorScreen" +{ + "$basetexture" "unloze/hitmarker/hitmarker_guc" + "$alphatest" 1 + "$translucent" 1 + "$vertexalpha" 1 +} diff --git a/Hitmarker/content/materials/unloze/hitmarker/hitmarker_guc.vtf b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_guc.vtf new file mode 100644 index 0000000..10cdd43 Binary files /dev/null and b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_guc.vtf differ diff --git a/Hitmarker/content/materials/unloze/hitmarker/hitmarker_nano.vmt b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_nano.vmt new file mode 100644 index 0000000..d88ca02 --- /dev/null +++ b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_nano.vmt @@ -0,0 +1,5 @@ +"UnlitGeneric" +{ + "$basetexture" "unloze/hitmarker/hitmarker_nano" + "$translucent" 1 +} \ No newline at end of file diff --git a/Hitmarker/content/materials/unloze/hitmarker/hitmarker_nano.vtf b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_nano.vtf new file mode 100644 index 0000000..3bd9ddd Binary files /dev/null and b/Hitmarker/content/materials/unloze/hitmarker/hitmarker_nano.vtf differ diff --git a/Hitmarker/content/sound/unloze/hm_v2.mp3 b/Hitmarker/content/sound/unloze/hm_v2.mp3 new file mode 100644 index 0000000..cda2a08 Binary files /dev/null and b/Hitmarker/content/sound/unloze/hm_v2.mp3 differ diff --git a/Hitmarker/gamedata/Hitmarker.games.txt b/Hitmarker/gamedata/Hitmarker.games.txt new file mode 100644 index 0000000..c11d393 --- /dev/null +++ b/Hitmarker/gamedata/Hitmarker.games.txt @@ -0,0 +1,45 @@ +"Games" +{ + "cstrike" + { + "Signatures" + { + "CBaseEntity::OnTakeDamage" + { + "library" "server" + "linux" "@_ZN11CBaseEntity12OnTakeDamageERK15CTakeDamageInfo" + } + } + } + "csgo" + { + "Signatures" + { + "CBaseEntity::OnTakeDamage" + { + "library" "server" + "linux" "\x55\x31\xC0\x89\xE5\x57\x56\x53\x83\xEC\x6C\x8B\x5D\x08\x8B\x75\x0C\x8B\x53\x24" + } + } + } + "#default" + { + "Functions" + { + "CBaseEntity__OnTakeDamage" + { + "signature" "CBaseEntity::OnTakeDamage" + "callconv" "thiscall" + "return" "int" + "this" "entity" + "arguments" + { + "info" + { + "type" "objectptr" + } + } + } + } + } +} diff --git a/Hitmarker/scripting/Hitmarker.sp b/Hitmarker/scripting/Hitmarker.sp new file mode 100644 index 0000000..b933b36 --- /dev/null +++ b/Hitmarker/scripting/Hitmarker.sp @@ -0,0 +1,597 @@ +#include +#include +//#include +#include +#include +#include +#include +#include + +#pragma newdecls required +#pragma semicolon 1 + +#define SPECMODE_NONE 0 +#define SPECMODE_FIRSTPERSON 4 +#define SPECMODE_THIRDPERSON 5 +#define SPECMODE_FREELOOK 6 + +#define MAX_EDICTS 2048 +bool g_bHasOutputs[MAX_EDICTS]; + +bool g_bShowBossHitmarker[MAXPLAYERS+1]; +bool g_bShowZombieHitmarker[MAXPLAYERS+1]; +bool g_bHitmarkerSound[MAXPLAYERS+1]; + +int g_iHitmarkerSoundVolume[MAXPLAYERS+1] = {100,...}; +int g_iHitmarkerSkin[MAXPLAYERS+1]; + +#define MAXIMUMSKINS 6 +char g_sHitmarkerSkins[MAXIMUMSKINS][PLATFORM_MAX_PATH]; +char g_sHitmarkerSkinsNames[MAXIMUMSKINS][PLATFORM_MAX_PATH]; + +Handle g_hTimer[MAXPLAYERS+1] = {null,...}; + +Handle g_hCookie_ShowBossHitmarker; +Handle g_hCookie_ShowZombieHitmarker; +Handle g_hCookie_HitmarkerSound; +Handle g_hCookie_HitmarkerSoundVolume; +Handle g_hCookie_HitmarkerSkin; + +ConVar g_hCVar_Debug; + +bool g_bLate; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "Hitmarker", + author = "Neon", + description = "Players can enable or disable their hitmarkers while shooting zombies or bosses", + version = "3.1", +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int errorSize) +{ + g_bLate = bLate; + return APLRes_Success; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + Handle g_hOnTakeDamageDetour; + Handle hGameData = LoadGameConfigFile("Hitmarker.games"); + if(!hGameData) + SetFailState("Failed to load Hitmarker gamedata."); + + g_hOnTakeDamageDetour = DHookCreateFromConf(hGameData, "CBaseEntity__OnTakeDamage"); + if(!g_hOnTakeDamageDetour) + SetFailState("Failed to setup detour for CBaseEntity__OnTakeDamage"); + + delete hGameData; + + if(!DHookEnableDetour(g_hOnTakeDamageDetour, true, Detour_OnTakeDamage)) + SetFailState("Failed to detour CBaseEntity__OnTakeDamage."); + + g_hCookie_ShowBossHitmarker = RegClientCookie("hitmarker_boss", "", CookieAccess_Private); + g_hCookie_ShowZombieHitmarker = RegClientCookie("hitmarker_zombie", "", CookieAccess_Private); + g_hCookie_HitmarkerSound = RegClientCookie("hitmarker_sound", "", CookieAccess_Private); + g_hCookie_HitmarkerSoundVolume = RegClientCookie("hitmarker_sound_volume", "", CookieAccess_Private); + g_hCookie_HitmarkerSkin = RegClientCookie("hitmarker_skin", "", CookieAccess_Private); + + g_hCVar_Debug = CreateConVar("sm_hitmarker_debug", "0", "", FCVAR_NONE, true, 0.0, true, 1.0); + AutoExecConfig(); + + RegConsoleCmd("sm_hm", OnHitmarkerSettings); + RegConsoleCmd("sm_hitmarker", OnHitmarkerSettings); + RegConsoleCmd("sm_bhm", OnToggleBossHitmarker); + RegConsoleCmd("sm_zhm", OnToggleZombieHitmarker); + RegConsoleCmd("sm_hmsound", OnToggleHitmarkerSound); + + SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Hitmarker"); + + HookEvent("player_hurt", OnClientHurt); + + if (g_bLate) + { + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i) && AreClientCookiesCached(i)) + OnClientCookiesCached(i); + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnMapStart() +{ + for (int i = 0; i < MAXIMUMSKINS; i++) + { + g_sHitmarkerSkins[i] = ""; + g_sHitmarkerSkinsNames[i] = ""; + } + + char sConfigFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/Hitmarkers.cfg"); + + if (!FileExists(sConfigFile)) + SetFailState("Could not find: \"%s\"", sConfigFile); + + KeyValues Hitmarkers = new KeyValues("Hitmarkers"); + + if (!Hitmarkers.ImportFromFile(sConfigFile)) + { + delete Hitmarkers; + SetFailState("ImportFromFile() failed!"); + } + + if (!Hitmarkers.GotoFirstSubKey()) + { + delete Hitmarkers; + SetFailState("Unable to goto first sub key in: \"%s\"", sConfigFile); + } + + int i = 0; + do + { + char sPath[PLATFORM_MAX_PATH]; + Hitmarkers.GetString("Path", sPath, sizeof(sPath), "error"); + if (StrEqual(sPath, "error")) + { + delete Hitmarkers; + SetFailState("Unable to read Path"); + } + + char sName[32]; + Hitmarkers.GetString("Name", sName, sizeof(sName), "error"); + if (StrEqual(sName, "error")) + { + delete Hitmarkers; + SetFailState("Unable to read Name"); + } + + char sBuffer[PLATFORM_MAX_PATH]; + + Format(sBuffer, sizeof(sBuffer), "materials/%s.vmt", sPath); + PrecacheGeneric(sBuffer, true); + AddFileToDownloadsTable(sBuffer); + + Format(sBuffer, sizeof(sBuffer), "materials/%s.vtf", sPath); + PrecacheGeneric(sBuffer, true); + AddFileToDownloadsTable(sBuffer); + + g_sHitmarkerSkins[i] = sPath; + g_sHitmarkerSkinsNames[i] = sName; + i++; + } while(Hitmarkers.GotoNextKey()); + delete Hitmarkers; + + PrecacheSound("unloze/hm_v3.mp3"); + AddFileToDownloadsTable("sound/unloze/hm_v3.mp3"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientCookiesCached(int client) +{ + char sBuffer[PLATFORM_MAX_PATH]; + + GetClientCookie(client, g_hCookie_ShowBossHitmarker, sBuffer, sizeof(sBuffer)); + if (sBuffer[0]) + g_bShowBossHitmarker[client] = true; + else + g_bShowBossHitmarker[client] = false; + + GetClientCookie(client, g_hCookie_ShowZombieHitmarker, sBuffer, sizeof(sBuffer)); + if (sBuffer[0]) + g_bShowZombieHitmarker[client] = true; + else + g_bShowZombieHitmarker[client] = false; + + GetClientCookie(client, g_hCookie_HitmarkerSound, sBuffer, sizeof(sBuffer)); + if (sBuffer[0]) + g_bHitmarkerSound[client] = true; + else + g_bHitmarkerSound[client] = false; + + GetClientCookie(client, g_hCookie_HitmarkerSoundVolume, sBuffer, sizeof(sBuffer)); + if (sBuffer[0]) + g_iHitmarkerSoundVolume[client] = StringToInt(sBuffer); + else + g_iHitmarkerSoundVolume[client] = 100; + + GetClientCookie(client, g_hCookie_HitmarkerSkin, sBuffer, sizeof(sBuffer)); + if (sBuffer[0]) + { + for (int i = 0; i < MAXIMUMSKINS; i++) + { + if (StrEqual(g_sHitmarkerSkins[i], sBuffer)) + { + g_iHitmarkerSkin[client] = i; + break; + } + } + } + else + g_iHitmarkerSkin[client] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientDisconnect(int client) +{ + g_bShowBossHitmarker[client] = false; + g_bShowZombieHitmarker[client] = false; + g_bHitmarkerSound[client] = false; + g_iHitmarkerSoundVolume[client] = 100; + g_iHitmarkerSkin[client] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnHitmarkerSettings(int client, int args) +{ + ShowSettingsMenu(client); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnToggleBossHitmarker(int client, int args) +{ + ToggleBossHitmarker(client); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ToggleBossHitmarker(int client) +{ + g_bShowBossHitmarker[client] = !g_bShowBossHitmarker[client]; + SetClientCookie(client, g_hCookie_ShowBossHitmarker, g_bShowBossHitmarker[client] ? "1" : ""); + + CPrintToChat(client, "{lightblue}[Hitmarker] {default}%s.", g_bShowBossHitmarker[client] ? "Boss Hitmarker Enabled" : "Boss Hitmarker Disabled"); + + if (g_bShowBossHitmarker[client]) + ShowOverlay(client, 2.0); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnToggleZombieHitmarker(int client, int args) +{ + ToggleZombieHitmarker(client); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ToggleZombieHitmarker(int client) +{ + g_bShowZombieHitmarker[client] = !g_bShowZombieHitmarker[client]; + SetClientCookie(client, g_hCookie_ShowZombieHitmarker, g_bShowZombieHitmarker[client] ? "1" : ""); + + CPrintToChat(client, "{lightblue}[Hitmarker] {default}%s.", g_bShowZombieHitmarker[client] ? "Zombie Hitmarker Enabled" : "Zombie Hitmarker Disabled"); + + if (g_bShowZombieHitmarker[client]) + ShowOverlay(client, 2.0); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnToggleHitmarkerSound(int client, int args) +{ + ToggleHitmarkerSound(client); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ToggleHitmarkerSound(int client) +{ + g_bHitmarkerSound[client] = !g_bHitmarkerSound[client]; + SetClientCookie(client, g_hCookie_HitmarkerSound, g_bHitmarkerSound[client] ? "1" : ""); + + CPrintToChat(client, "{lightblue}[Hitmarker] {default}%s.", g_bHitmarkerSound[client] ? "Hitmarker Sound Enabled" : "Hitmarker Sound Disabled"); + + if (g_bHitmarkerSound[client]) + ShowOverlay(client, 2.0); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ToggleHitmarkerSoundVolume(int client) +{ + g_iHitmarkerSoundVolume[client] += 25; + + if(g_iHitmarkerSoundVolume[client] > 100) + g_iHitmarkerSoundVolume[client] = 25; + + char sBuffer[16]; + IntToString(g_iHitmarkerSoundVolume[client], sBuffer, sizeof(sBuffer)); + + SetClientCookie(client, g_hCookie_HitmarkerSoundVolume, sBuffer); + + ShowOverlay(client, 2.0); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ToggleHitmarkerSkin(int client) +{ + g_iHitmarkerSkin[client]++; + + if (StrEqual(g_sHitmarkerSkins[g_iHitmarkerSkin[client]], "")) + g_iHitmarkerSkin[client] = 0; + + SetClientCookie(client, g_hCookie_HitmarkerSkin, g_sHitmarkerSkins[g_iHitmarkerSkin[client]]); + + ShowOverlay(client, 2.0); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ShowSettingsMenu(int client) +{ + Menu menu = new Menu(MenuHandler_MainMenu); + + menu.SetTitle("Hitmarker Settings", client); + + char sBuffer[128]; + + Format(sBuffer, sizeof(sBuffer), "Boss Hitmarker: %s", g_bShowBossHitmarker[client] ? "Enabled" : "Disabled"); + menu.AddItem("0", sBuffer); + + Format(sBuffer, sizeof(sBuffer), "Zombie Hitmarker: %s", g_bShowZombieHitmarker[client] ? "Enabled" : "Disabled"); + menu.AddItem("1", sBuffer); + + Format(sBuffer, sizeof(sBuffer), "Hitmarker Sound: %s", g_bHitmarkerSound[client] ? "Enabled" : "Disabled"); + menu.AddItem("2", sBuffer); + + Format(sBuffer, sizeof(sBuffer), "Hitmarker Sound Volume: %d\%", g_iHitmarkerSoundVolume[client]); + menu.AddItem("3", sBuffer); + + Format(sBuffer, sizeof(sBuffer), "Hitmarker Skin: %s", g_sHitmarkerSkinsNames[g_iHitmarkerSkin[client]]); + menu.AddItem("4", sBuffer); + + menu.ExitBackButton = true; + + menu.Display(client, MENU_TIME_FOREVER); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) +{ + switch(action) + { + case(CookieMenuAction_DisplayOption): + { + Format(buffer, maxlen, "Hitmarker", client); + } + case(CookieMenuAction_SelectOption): + { + ShowSettingsMenu(client); + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection) +{ + switch(action) + { + case(MenuAction_Select): + { + switch(selection) + { + case(0): ToggleBossHitmarker(client); + case(1): ToggleZombieHitmarker(client); + case(2): ToggleHitmarkerSound(client); + case(3): ToggleHitmarkerSoundVolume(client); + case(4): ToggleHitmarkerSkin(client); + } + + ShowSettingsMenu(client); + } + case(MenuAction_Cancel): + { + ShowCookieMenu(client); + } + case(MenuAction_End): + { + delete menu; + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnEntitySpawned(int Entity, const char[] sClassname) +{ + int ent = EntRefToEntIndex(Entity); + + if (0 < ent < MAX_EDICTS) + { + if ((GetOutputCount(ent, "m_OnDamaged") > 0) || (GetOutputCount(ent, "m_OnHealthChanged") > 0)) + g_bHasOutputs[ent] = true; + else + g_bHasOutputs[ent] = false; + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +/* +public void OnBossDamaged(CBoss Boss, CConfig Config, int client, float damage) +{ + if (!IsValidClient(client)) + return; + + if (g_bShowBossHitmarker[client]) + ShowOverlay(client); + + for (int spec = 1; spec <= MaxClients; spec++) + { + if (!IsClientInGame(spec) || !IsClientObserver(spec) || !g_bShowBossHitmarker[spec]) + continue; + + int specMode = GetClientSpectatorMode(spec); + int specTarget = GetClientSpectatorTarget(spec); + + if ((specMode == SPECMODE_FIRSTPERSON) && specTarget == client) + ShowOverlay(spec); + } +} +*/ +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public MRESReturn Detour_OnTakeDamage(int entity, Handle hReturn, Handle hParams) +{ + //https://github.com/alliedmodders/hl2sdk/blob/css/game/shared/takedamageinfo.h#L115 + int iHealth = GetEntProp(entity, Prop_Data, "m_iHealth"); + int client = DHookGetParamObjectPtrVar(hParams, 1, 3*(3*4) + 0, ObjectValueType_Ehandle); + + if (g_hCVar_Debug.BoolValue) + { + float dmg = DHookGetParamObjectPtrVar(hParams, 1, 48, ObjectValueType_Float); + PrintToChatAll("Detour_OnTakeDamage: ent: %d; client: %d; m_iHealth: %d; output: %d; dmg: %f", entity, client, iHealth, g_bHasOutputs[entity], dmg); + } + + if (!iHealth && !g_bHasOutputs[entity]) + return MRES_Ignored; + + if (!IsValidClient(client)) + return MRES_Ignored; + + if (g_bShowBossHitmarker[client]) + ShowOverlay(client); + + for (int spec = 1; spec <= MaxClients; spec++) + { + if (!IsClientInGame(spec) || !IsClientObserver(spec) || !g_bShowBossHitmarker[spec]) + continue; + + int specMode = GetClientSpectatorMode(spec); + int specTarget = GetClientSpectatorTarget(spec); + + if ((specMode == SPECMODE_FIRSTPERSON) && specTarget == client) + ShowOverlay(spec); + } + + return MRES_Ignored; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientHurt(Event hEvent, const char[] sEvent, bool bDontBroadcast) +{ + int client = GetClientOfUserId(hEvent.GetInt("attacker")); + int victim = GetClientOfUserId(hEvent.GetInt("userid")); + + if (client < 1 || client > MaxClients || victim < 1 || victim > MaxClients) + return; + + if (client == victim || (IsPlayerAlive(client) && ZR_IsClientZombie(client))) + return; + + if (g_bShowZombieHitmarker[client]) + ShowOverlay(client); + + for (int spec = 1; spec <= MaxClients; spec++) + { + if (!IsClientInGame(spec) || !IsClientObserver(spec) || !g_bShowZombieHitmarker[spec]) + continue; + + int specMode = GetClientSpectatorMode(spec); + int specTarget = GetClientSpectatorTarget(spec); + + if (specMode == SPECMODE_FIRSTPERSON && specTarget == client) + ShowOverlay(spec); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +void ShowOverlay(int client, float fTime = 0.3) +{ + if (g_bHitmarkerSound[client]) + { + float fVolume = g_iHitmarkerSoundVolume[client] / 100.0; + EmitSoundToClient(client, "unloze/hm_v3.mp3", .volume=fVolume); + } + + if (g_hTimer[client] != null) + { + delete g_hTimer[client]; + g_hTimer[client] = null; + } + ClientCommand(client, "r_screenoverlay \"%s\"", g_sHitmarkerSkins[g_iHitmarkerSkin[client]]); + g_hTimer[client] = CreateTimer(fTime, ClearOverlay, client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action ClearOverlay(Handle timer, int client) +{ + g_hTimer[client] = null; + if (IsClientConnected(client)) + ClientCommand(client, "r_screenoverlay \"\""); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +stock int IsValidClient(int client, bool nobots = true) +{ + if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client))) + return false; + + return IsClientInGame(client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +int GetClientSpectatorMode(int client) +{ + return GetEntProp(client, Prop_Send, "m_iObserverMode"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +int GetClientSpectatorTarget(int client) +{ + return GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); +} diff --git a/KnifeAlert/scripting/KnifeAlert.sp b/KnifeAlert/scripting/KnifeAlert.sp new file mode 100644 index 0000000..199a4fe --- /dev/null +++ b/KnifeAlert/scripting/KnifeAlert.sp @@ -0,0 +1,112 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include + +Handle g_hCVar_NotificationTime = INVALID_HANDLE; +char g_sAttackerSID[MAXPLAYERS + 1][32]; +int g_iNotificationTime[MAXPLAYERS + 1]; + +public Plugin myinfo = +{ + name = "Knife Notifications", + author = "Obus + BotoX", + description = "Notify administrators when zombies have been knifed by humans.", + version = "2.2", + url = "" +}; + +public void OnPluginStart() +{ + g_hCVar_NotificationTime = CreateConVar("sm_knifenotifytime", "5", "Amount of time to pass before a knifed zombie is considered \"not knifed\" anymore.", 0, true, 0.0, true, 60.0); + + if(!HookEventEx("player_hurt", Event_PlayerHurt, EventHookMode_Pre)) + SetFailState("[Knife-Notifications] Failed to hook \"player_hurt\" event."); +} + +public int GetClientFromSteamID(const char[] auth) +{ + char clientAuth[32]; + + for(int client = 1; client <= MaxClients; client++) + { + if(!IsClientAuthorized(client)) + continue; + + GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + + if(StrEqual(auth, clientAuth)) + return client; + } + + return -1; +} + +public Action Event_PlayerHurt(Handle hEvent, const char[] name, bool dontBroadcast) +{ + int victim; + int attacker; + char sWepName[64]; + char sAtkSID[32]; + char sVictSID[32]; + GetEventString(hEvent, "weapon", sWepName, sizeof(sWepName)); + + if((victim = GetClientOfUserId(GetEventInt(hEvent, "userid"))) == 0) + return; + + if((attacker = GetClientOfUserId(GetEventInt(hEvent, "attacker"))) == 0) + return; + + if(!IsClientInGame(victim) || !IsPlayerAlive(victim)) + return; + + if(!IsClientInGame(attacker) || !IsPlayerAlive(attacker)) + return; + + if(victim != attacker && GetClientTeam(victim) == 2 && GetClientTeam(attacker) == 3) + { + if(StrEqual(sWepName, "knife")) + { + int damage = GetEventInt(hEvent, "dmg_health"); + + if(damage < 35) + return; + + GetClientAuthId(attacker, AuthId_Steam2, sAtkSID, sizeof(sAtkSID)); + GetClientAuthId(attacker, AuthId_Steam2, g_sAttackerSID[victim], sizeof(g_sAttackerSID[])); + GetClientAuthId(victim, AuthId_Steam2, sVictSID, sizeof(sVictSID)); + LogMessage("%L knifed %L", attacker, victim); + + g_iNotificationTime[victim] = (GetTime() + GetConVarInt(g_hCVar_NotificationTime)); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientConnected(i) && IsClientInGame(i) && (IsClientSourceTV(i) || GetAdminFlag(GetUserAdmin(i), Admin_Generic))) + CPrintToChat(i, "{green}[SM] {lightblue}%N {default}knifed {lightred}%N", attacker, victim); + } + } + } + else if(victim != attacker && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 3) + { + int pOldKnifer; + pOldKnifer = GetClientFromSteamID(g_sAttackerSID[attacker]); + + if(g_iNotificationTime[attacker] > GetTime() && (victim != pOldKnifer)) + { + char sAtkAttackerName[MAX_NAME_LENGTH]; + GetClientAuthId(attacker, AuthId_Steam2, sAtkSID, sizeof(sAtkSID)); + + if(pOldKnifer != -1) + { + GetClientName(pOldKnifer, sAtkAttackerName, sizeof(sAtkAttackerName)); + LogMessage("%L killed %L (Recently knifed by %L)", attacker, victim, pOldKnifer); + } + else + LogMessage("%L killed %L (Recently knifed by a disconnected player [%s])", attacker, victim, g_sAttackerSID[attacker]); + + CPrintToChatAll("{green}[SM] {red}%N {green}(%s){default} killed {lightblue}%N{default} - knifed by {lightblue}%s {green}(%s)", + attacker, sAtkSID, victim, (pOldKnifer != -1) ? sAtkAttackerName : "a disconnected player", g_sAttackerSID[attacker]); + } + } +} diff --git a/KnifeRevert/scripting/KnifeRevert.sp b/KnifeRevert/scripting/KnifeRevert.sp new file mode 100644 index 0000000..8f94559 --- /dev/null +++ b/KnifeRevert/scripting/KnifeRevert.sp @@ -0,0 +1,180 @@ +#pragma semicolon 1 + +#include +#include +#include +#include + +bool g_bKnifeRevert; +bool g_bSlayKnifer; +float g_fMaxDistance; +float g_fMaxTime; + +int g_iClientIndexKnifer; +int g_iClientIndexKnifedZombie; +bool g_bKnife; +bool g_bInfectedInRadius[MAXPLAYERS + 1]; + +public Plugin myinfo = +{ + name = "KnifeRevert", + author = "Dogan", + description = "Tool for Admins to prevent teamkilling cause of zombie knifing (ZE)", + version = "1.1.0", + url = "" +} + +public void OnPluginStart() +{ + ConVar cvar; + HookConVarChange((cvar = CreateConVar("sm_kr_enabled", "1", "1 = sm_kniferevert is available, 0 = sm_kniferevert is not available", FCVAR_NONE, true, 0.0, true, 1.0)), g_cvZAbuse); + g_bKnifeRevert = cvar.BoolValue; + HookConVarChange((cvar = CreateConVar("sm_kr_slay", "0", "1 = slays the knifer automatically when sm_kniferevert is used, 0 = don't do anything to the knifer", FCVAR_NONE, true, 0.0, true, 1.0)), g_cvSlayKnifer); + g_bSlayKnifer = cvar.BoolValue; + HookConVarChange((cvar = CreateConVar("sm_kr_maxdistance", "200.0", "max distance from the zombie that got knifed forward where infected humans will be rezurrected")), g_cvMaxDistance); + g_fMaxDistance = cvar.FloatValue; + HookConVarChange((cvar = CreateConVar("sm_kr_time", "6.0", "max time until sm_kniferevert is available after a knife")), g_cvMaxTime); + g_fMaxTime = cvar.FloatValue; + delete cvar; + + HookEvent("player_hurt", OnPlayerHurt); + + RegAdminCmd("sm_kr", Command_KnifeRevert, ADMFLAG_GENERIC, "Undoes the damage caused by a zombie after being knifed forward"); + RegAdminCmd("sm_kniferevert", Command_KnifeRevert, ADMFLAG_GENERIC, "Undoes the damage caused by a zombie after being knifed forward"); + + AutoExecConfig(true, "plugin.KnifeRevert"); + + g_iClientIndexKnifer = -1; + g_iClientIndexKnifedZombie = -1; +} + +public void g_cvZAbuse(ConVar convar, const char[] oldValue, const char[] newValue) +{ + g_bKnifeRevert = convar.BoolValue; +} + +public void g_cvSlayKnifer(ConVar convar, const char[] oldValue, const char[] newValue) +{ + g_bSlayKnifer = convar.BoolValue; +} + +public void g_cvMaxDistance(ConVar convar, const char[] oldValue, const char[] newValue) +{ + g_fMaxDistance = convar.FloatValue; +} + +public void g_cvMaxTime(ConVar convar, const char[] oldValue, const char[] newValue) +{ + g_fMaxTime = convar.FloatValue; +} + +public void OnPlayerHurt(Handle hEvent, const char[] name, bool dontBroadcast) +{ + if(!g_bKnifeRevert) + return; + + int attacker; + int victim; + char sWepName[64]; + GetEventString(hEvent, "weapon", sWepName, sizeof(sWepName)); + + if((attacker = GetClientOfUserId(GetEventInt(hEvent, "attacker"))) == 0) + return; + + if((victim = GetClientOfUserId(GetEventInt(hEvent, "userid"))) == 0) + return; + + if(!IsClientInGame(attacker) || !IsPlayerAlive(attacker)) + return; + + if(!IsClientInGame(victim) || !IsPlayerAlive(victim)) + return; + + if(victim != attacker && GetClientTeam(victim) == CS_TEAM_T && GetClientTeam(attacker) == CS_TEAM_CT) + { + if(StrEqual(sWepName, "knife")) + { + int damage = GetEventInt(hEvent, "dmg_health"); + + if(damage < 35) + return; + + g_iClientIndexKnifer = attacker; + g_iClientIndexKnifedZombie = victim; + g_bKnife = true; + + CreateTimer(g_fMaxTime, OnClientKnife, _, TIMER_FLAG_NO_MAPCHANGE); + } + } +} + +public Action OnClientKnife(Handle timer) +{ + g_bKnife = false; + g_iClientIndexKnifer = -1; + g_iClientIndexKnifedZombie = -1; + + for(int i = 1; i <= MaxClients; i++) + { + if(g_bInfectedInRadius[i]) + { + g_bInfectedInRadius[i] = false; + } + } +} + +public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn) +{ + if(!g_bKnifeRevert || !g_bKnife) + return; + + static float fVec1[3]; + static float fVec2[3]; + GetClientAbsOrigin(g_iClientIndexKnifedZombie, fVec1); + GetClientAbsOrigin(client, fVec2); + + float fDistance = GetVectorDistance(fVec1, fVec2, false); + + if(fDistance <= g_fMaxDistance) + g_bInfectedInRadius[client] = true; +} + +public Action Command_KnifeRevert(int client, int argc) +{ + if(!g_bKnifeRevert) + { + ReplyToCommand(client, "[SM] Disabled on this Map."); + return Plugin_Handled; + } + + if(!g_bKnife) + { + ReplyToCommand(client, "[SM] Not available right now because there was no active Zombie Knifing the past %d Seconds.", RoundFloat(g_fMaxTime)); + return Plugin_Handled; + } + + ReplyToCommand(client, "[SM] You undid the damage from the previous Knife!"); + PrintToChatAll("[SM] %N undid the damage from the previous Zombie Knife.", client); + LogAction(client, -1, "\"%L\" undid the damage from the previous Zombie Knife.", client); + + if(g_bSlayKnifer) + { + ForcePlayerSuicide(g_iClientIndexKnifer); + PrintToChat(g_iClientIndexKnifer, "[SM] You got slayed for knifing a Zombie forward!"); + } + + int IdKnifedZombie = GetClientUserId(g_iClientIndexKnifedZombie); + ServerCommand("zr_ztele_force #%d", IdKnifedZombie); + PrintToChat(g_iClientIndexKnifedZombie, "[SM] You got teleported back because you were knifed forward."); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && IsPlayerAlive(i) && g_bInfectedInRadius[i]) + { + ZR_HumanClient(i, false, false); + PrintToChat(i, "[SM] You were rezurrected because you died after a Zombie was knifed forward."); + } + } + + return Plugin_Handled; +} \ No newline at end of file diff --git a/LagCompensation/gamedata/LagCompensation.games.txt b/LagCompensation/gamedata/LagCompensation.games.txt new file mode 100644 index 0000000..6ba768a --- /dev/null +++ b/LagCompensation/gamedata/LagCompensation.games.txt @@ -0,0 +1,249 @@ +"Games" +{ + "cstrike" + { + "Signatures" + { + "::UTIL_Remove" + { + "library" "server" + "linux" "@_Z11UTIL_RemoveP18IServerNetworkable" + } + + "CCSGameRules::RestartRound" + { + "library" "server" + "linux" "@_ZN12CCSGameRules12RestartRoundEv" + } + + "CLogicMeasureMovement::SetTarget" + { + "library" "server" + "linux" "@_ZN21CLogicMeasureMovement9SetTargetEPKc" + } + + "CEntityTouchManager::FrameUpdatePostEntityThink" + { + "library" "server" + "linux" "@_ZN19CEntityTouchManager26FrameUpdatePostEntityThinkEv" + } + + "CalcAbsolutePosition" + { + "library" "server" + "linux" "@_ZN11CBaseEntity20CalcAbsolutePositionEv" + } + + "MarkPartitionHandleDirty" + { + "library" "server" + "linux" "@_ZN18CCollisionProperty24MarkPartitionHandleDirtyEv" + } + } + + "Functions" + { + "UTIL_Remove" + { + "signature" "::UTIL_Remove" + "return" "void" + "callconv" "cdecl" + "arguments" + { + "oldObj" + { + "type" "objectptr" + } + } + } + + "CCSGameRules__RestartRound" + { + "signature" "CCSGameRules::RestartRound" + "callconv" "thiscall" + "return" "void" + "this" "ignore" + } + + "CEntityTouchManager__FrameUpdatePostEntityThink" + { + "signature" "CEntityTouchManager::FrameUpdatePostEntityThink" + "callconv" "cdecl" + "return" "void" + } + + "CLogicMeasureMovement__SetTarget" + { + "signature" "CLogicMeasureMovement::SetTarget" + "callconv" "thiscall" + "return" "void" + "this" "entity" + "arguments" + { + "pName" + { + "type" "charptr" + } + } + } + + "CLogicMeasureMovement__SetTarget_post" + { + "signature" "CLogicMeasureMovement::SetTarget" + "callconv" "thiscall" + "return" "void" + "this" "ignore" + "arguments" + { + "pName" + { + "type" "charptr" + } + } + } + } + + "Offsets" + { + "CServerNetworkableProperty::m_pOuter" + { + "linux" "8" + "windows" "8" + } + + "CGameRules::EndGameFrame" + { + "linux" "49" + "windows" "48" + } + } + } + + "csgo" + { + "Signatures" + { + "::UTIL_Remove" + { + "library" "server" + "linux" "\x55\x89\xE5\x83\xEC\x48\x89\x5D\xF4\x8B\x5D\x08\x89\x75\xF8\x89\x7D\xFC\x85\xDB" + "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x85\xF6\x0F\x84\x2A\x2A\x2A\x2A" + } + + "CCSGameRules::RestartRound" + { + "library" "server" + "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x3C\x03\x00\x00" + "windows" "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\xD8\x01\x00\x00" + } + + "CLogicMeasureMovement::SetTarget" + { + "library" "server" + "linux" "\x55\x89\xE5\x56\x53\x83\xEC\x20\x8B\x5D\x0C\xC7\x44\x24\x18\x00\x00\x00\x00\x8B\x75\x08\xC7\x44\x24\x14\x00\x00\x00\x00\xC7\x44\x24\x10\x00\x00\x00\x00\xC7\x44\x24\x0C\x00\x00\x00\x00\x89\x5C\x24\x08\xC7\x44\x24\x04\x00\x00\x00\x00\xC7\x04\x24\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x2A\x8B\x10\x89\x04\x24\xFF\x52\x0C\x8B\x0D\x2A\x2A\x2A\x2A\x8B\x00\x83\xF8\xFF\x89\x86\xBC\x03\x00\x00" + "windows" "\x55\x8B\xEC\x51\x53\x8B\x5D\x08\x56\x8B\xF1\x89\x75\xFC\x57\x85\xDB\x74\x2A\x8A\x03\x84\xC0\x74\x2A\x3C\x21\x75\x2A\x6A\x00\x6A\x00\x6A\x00\x53\xE8\x2A\x2A\x2A\x2A\x8B\xF8\xEB\x2A\x8B\x35\x2A\x2A\x2A\x2A\x85\xF6\x74\x2A\x8B\x3E\x85\xFF\x75\x2A\x68\x2A\x2A\x2A\x2A\xFF\x15\x2A\x2A\x2A\x2A\x83\xC4\x04\xEB\x2A\x8B\x56\x10\x85\xD2\x74\x2A\x3B\xD3\x74\x2A\x8B\xCB\xE8\x2A\x2A\x2A\x2A\x84\xC0\x75\x2A\x8B\x76\x0C\x85\xF6\x75\x2A\x8B\x75\xFC\x33\xFF\x85\xFF\x74\x2A\x8B\x07\x8B\xCF\xFF\x50\x08\x8B\x00\x89\x86\xA4\x03\x00\x00" + } + + "CEntityTouchManager::FrameUpdatePostEntityThink" + { + "library" "server" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x3C\xA1\x2A\x2A\x2A\x2A\x8B\x5D\x08\x85\xC0" + "windows" "\x55\x8B\xEC\x53\x56\x57\x8B\x3D\x2A\x2A\x2A\x2A\x85\xFF" + } + + "CalcAbsolutePosition" + { + "library" "server" + "linux" "\x55\x89\xE5\x81\xEC\x98\x00\x00\x00\x89\x5D\xF4\x8B\x5D\x08\x89\x75\xF8\x89\x7D\xFC\x8B\x83\xDC\x00\x00\x00" + "windows" "\x55\x8B\xEC\x83\xE4\xF0\x83\xEC\x68\x56\x8B\xF1" + } + + "MarkPartitionHandleDirty" + { + "library" "server" + "linux" "\x55\x89\xE5\x53\x83\xEC\x14\x8B\x5D\x08\x8B\x43\x04\xF6\x80\xDD\x00\x00\x00\x80" + "windows" "\x56\x8B\xF1\x8B\x4E\x04\x8B\x51\x1C" + } + } + + "Functions" + { + "UTIL_Remove" + { + "signature" "::UTIL_Remove" + "return" "void" + "callconv" "cdecl" + "arguments" + { + "oldObj" + { + "type" "objectptr" + } + } + "windows" + { + "callconv" "fastcall" + } + } + + "CCSGameRules__RestartRound" + { + "signature" "CCSGameRules::RestartRound" + "callconv" "thiscall" + "return" "void" + } + + "CEntityTouchManager__FrameUpdatePostEntityThink" + { + "signature" "CEntityTouchManager::FrameUpdatePostEntityThink" + "callconv" "cdecl" + "return" "void" + } + + "CLogicMeasureMovement__SetTarget" + { + "signature" "CLogicMeasureMovement::SetTarget" + "callconv" "thiscall" + "return" "void" + "this" "entity" + "arguments" + { + "pName" + { + "type" "charptr" + } + } + } + + "CLogicMeasureMovement__SetTarget_post" + { + "signature" "CLogicMeasureMovement::SetTarget" + "callconv" "thiscall" + "return" "void" + "this" "ignore" + "arguments" + { + "pName" + { + "type" "charptr" + } + } + } + } + + "Offsets" + { + "CServerNetworkableProperty::m_pOuter" + { + "linux" "8" + "windows" "8" + } + + "CGameRules::EndGameFrame" + { + "linux" "48" + "windows" "47" + } + } + } +} diff --git a/LagCompensation/scripting/LagCompensation.sp b/LagCompensation/scripting/LagCompensation.sp new file mode 100644 index 0000000..e1e2bb6 --- /dev/null +++ b/LagCompensation/scripting/LagCompensation.sp @@ -0,0 +1,1436 @@ +#include +#include +#include +#include +#include +#include + +#define PLUGIN_VERSION "1.0.4" + +#define SetBit(%1,%2) ((%1)[(%2) >> 5] |= (1 << ((%2) & 31))) +#define ClearBit(%1,%2) ((%1)[(%2) >> 5] &= ~(1 << ((%2) & 31))) +#define CheckBit(%1,%2) !!((%1)[(%2) >> 5] & (1 << ((%2) & 31))) + +#pragma semicolon 1 +#pragma newdecls required + +public Plugin myinfo = +{ + name = "LagCompensation", + author = "BotoX", + description = "", + version = PLUGIN_VERSION, + url = "" +}; + +bool g_bLateLoad = false; +bool g_bHasPhysHooks = true; +bool g_bHasOnEntitySpawned = false; + +// Don't change this. +#define MAX_EDICTS 2048 +#define FSOLID_FORCE_WORLD_ALIGNED 0x0040 +#define FSOLID_ROOT_PARENT_ALIGNED 0x0100 +#define EFL_DIRTY_ABSTRANSFORM (1<<11) +#define EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS (1<<14) +#define EFL_CHECK_UNTOUCH (1<<24) +#define COORDINATE_FRAME_SIZE 14 + +enum +{ + USE_OBB_COLLISION_BOUNDS = 0, + USE_BEST_COLLISION_BOUNDS, + USE_HITBOXES, + USE_SPECIFIED_BOUNDS, + USE_GAME_CODE, + USE_ROTATION_EXPANDED_BOUNDS, + USE_COLLISION_BOUNDS_NEVER_VPHYSICS, +} + +enum +{ + SOLID_NONE = 0, // no solid model + SOLID_BSP = 1, // a BSP tree + SOLID_BBOX = 2, // an AABB + SOLID_OBB = 3, // an OBB (not implemented yet) + SOLID_OBB_YAW = 4, // an OBB, constrained so that it can only yaw + SOLID_CUSTOM = 5, // Always call into the entity for tests + SOLID_VPHYSICS = 6, // solid vphysics object, get vcollide from the model and collide with that + SOLID_LAST, +}; + +enum +{ + SF_TRIGGER_ALLOW_CLIENTS = 0x01, // Players can fire this trigger + SF_TRIGGER_ALLOW_NPCS = 0x02, // NPCS can fire this trigger + SF_TRIGGER_ALLOW_PUSHABLES = 0x04, // Pushables can fire this trigger + SF_TRIGGER_ALLOW_PHYSICS = 0x08, // Physics objects can fire this trigger + SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS = 0x10, // *if* NPCs can fire this trigger, this flag means only player allies do so + SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES = 0x20, // *if* Players can fire this trigger, this flag means only players inside vehicles can + SF_TRIGGER_ALLOW_ALL = 0x40, // Everything can fire this trigger EXCEPT DEBRIS! + SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES = 0x200, // *if* Players can fire this trigger, this flag means only players outside vehicles can + SF_TRIG_PUSH_ONCE = 0x80, // trigger_push removes itself after firing once + SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER = 0x100, // if pushed object is player on a ladder, then this disengages them from the ladder (HL2only) + SF_TRIG_TOUCH_DEBRIS = 0x400, // Will touch physics debris objects + SF_TRIGGER_ONLY_NPCS_IN_VEHICLES = 0x800, // *if* NPCs can fire this trigger, only NPCs in vehicles do so (respects player ally flag too) +}; + +#define SF_LAGCOMP_DISABLE (1 << 30) + +#define MAX_RECORDS 32 +#define MAX_ENTITIES 256 + +enum struct LagRecord +{ + float vecOrigin[3]; + float vecAbsOrigin[3]; + float angRotation[3]; + float angAbsRotation[3]; + float vecMins[3]; + float vecMaxs[3]; + float flSimulationTime; + float rgflCoordinateFrame[COORDINATE_FRAME_SIZE]; +} + +enum struct EntityLagData +{ + int iEntity; + int iRecordIndex; + int iNumRecords; + int iRecordsValid; + int iSpawned; + int iDeleted; + int iNotMoving; + bool bRestore; + bool bLateKill; +} + +LagRecord g_aaLagRecords[MAX_ENTITIES][MAX_RECORDS]; +EntityLagData g_aEntityLagData[MAX_ENTITIES]; +int g_iNumEntities = 0; +bool g_bCleaningUp = true; + +// Cache +int g_iGameTick; +float g_fTickInterval; +int g_aLerpTicks[MAXPLAYERS + 1]; + +// SDKCall +Handle g_hCalcAbsolutePosition; +Handle g_hMarkPartitionHandleDirty; + +// DHooks Detour +Handle g_hUTIL_Remove; +Handle g_hRestartRound; +Handle g_hSetTarget; +Handle g_hSetTargetPost; +Handle g_hFrameUpdatePostEntityThink; + +// DHooks Virtual +Handle g_hActivate; +Handle g_hAcceptInput; +Handle g_hEndGameFrame; + +int g_iNetworkableOuter; +int g_iParent; +int g_iSpawnFlags; +int g_iCollision; +int g_iSolidFlags; +int g_iSolidType; +int g_iSurroundType; +int g_iEFlags; + +int g_iVecOrigin; +int g_iVecAbsOrigin; +int g_iAngRotation; +int g_iAngAbsRotation; +int g_iVecMins; +int g_iVecMaxs; +int g_iSimulationTime; +int g_iCoordinateFrame; + +int g_aLagCompensated[MAX_EDICTS] = {-1, ...}; +int g_aBlockTriggerTouchPlayers[MAX_EDICTS / 32]; +int g_aaFilterClientSolidTouch[((MAXPLAYERS + 1) * MAX_EDICTS) / 32]; +int g_aBlockTriggerMoved[MAX_EDICTS / 32]; +int g_aBlacklisted[MAX_EDICTS / 32]; + +//Handle g_hCookie_DisableLagComp; +//bool g_bDisableLagComp[MAXPLAYERS + 1]; +//int g_iDisableLagComp[MAXPLAYERS + 1]; + +ConVar g_bLagCompTriggers; +ConVar g_bLagCompPhysboxes; + +public void OnPluginStart() +{ + CreateConVar("sm_lagcomp_version", PLUGIN_VERSION, "LagCompensation Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD).SetString(PLUGIN_VERSION); + + g_bLagCompTriggers = CreateConVar("sm_lagcomp_triggers", "1", "Lag compensate triggers", FCVAR_NONE, true, 0.0, true, 1.0); + g_bLagCompPhysboxes = CreateConVar("sm_lagcomp_physboxes", "1", "Lag compensate physboxes", FCVAR_NONE, true, 0.0, true, 1.0); + + AutoExecConfig(true); + + Handle hGameData = LoadGameConfigFile("LagCompensation.games"); + if(!hGameData) + SetFailState("Failed to load LagCompensation gamedata."); + + // CBaseEntity::CalcAbsolutePosition + StartPrepSDKCall(SDKCall_Entity); + if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CalcAbsolutePosition")) + { + delete hGameData; + SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, \"CalcAbsolutePosition\") failed!"); + } + g_hCalcAbsolutePosition = EndPrepSDKCall(); + + // CCollisionProperty::MarkPartitionHandleDirty + StartPrepSDKCall(SDKCall_Raw); + if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "MarkPartitionHandleDirty")) + { + delete hGameData; + SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, \"MarkPartitionHandleDirty\") failed!"); + } + g_hMarkPartitionHandleDirty = EndPrepSDKCall(); + + + // ::UTIL_Remove + g_hUTIL_Remove = DHookCreateFromConf(hGameData, "UTIL_Remove"); + if(!g_hUTIL_Remove) + { + delete hGameData; + SetFailState("Failed to setup detour for UTIL_Remove"); + } + + if(!DHookEnableDetour(g_hUTIL_Remove, false, Detour_OnUTIL_Remove)) + { + delete hGameData; + SetFailState("Failed to detour UTIL_Remove."); + } + + // CCSGameRules::RestartRound + g_hRestartRound = DHookCreateFromConf(hGameData, "CCSGameRules__RestartRound"); + if(!g_hRestartRound) + { + delete hGameData; + SetFailState("Failed to setup detour for CCSGameRules__RestartRound"); + } + + if(!DHookEnableDetour(g_hRestartRound, false, Detour_OnRestartRound)) + { + delete hGameData; + SetFailState("Failed to detour CCSGameRules__RestartRound."); + } + + // CLogicMeasureMovement::SetTarget + g_hSetTarget = DHookCreateFromConf(hGameData, "CLogicMeasureMovement__SetTarget"); + if(!g_hSetTarget) + { + delete hGameData; + SetFailState("Failed to setup detour for CLogicMeasureMovement__SetTarget"); + } + + if(!DHookEnableDetour(g_hSetTarget, false, Detour_OnSetTargetPre)) + { + delete hGameData; + SetFailState("Failed to detour CLogicMeasureMovement__SetTarget."); + } + + // CLogicMeasureMovement::SetTarget (fix post hook crashing due to this pointer being overwritten) + g_hSetTargetPost = DHookCreateFromConf(hGameData, "CLogicMeasureMovement__SetTarget_post"); + if(!g_hSetTargetPost) + { + delete hGameData; + SetFailState("Failed to setup detour for CLogicMeasureMovement__SetTarget_post"); + } + + if(!DHookEnableDetour(g_hSetTargetPost, true, Detour_OnSetTargetPost)) + { + delete hGameData; + SetFailState("Failed to detour CLogicMeasureMovement__SetTarget_post."); + } + + // CEntityTouchManager::FrameUpdatePostEntityThink + g_hFrameUpdatePostEntityThink = DHookCreateFromConf(hGameData, "CEntityTouchManager__FrameUpdatePostEntityThink"); + if(!g_hFrameUpdatePostEntityThink) + { + delete hGameData; + SetFailState("Failed to setup detour for CEntityTouchManager__FrameUpdatePostEntityThink"); + } + + if(!DHookEnableDetour(g_hFrameUpdatePostEntityThink, false, Detour_OnFrameUpdatePostEntityThink)) + { + delete hGameData; + SetFailState("Failed to detour CEntityTouchManager__FrameUpdatePostEntityThink."); + } + + + g_iNetworkableOuter = GameConfGetOffset(hGameData, "CServerNetworkableProperty::m_pOuter"); + if(g_iNetworkableOuter == -1) + { + delete hGameData; + SetFailState("GameConfGetOffset(hGameData, \"CServerNetworkableProperty::m_pOuter\") failed!"); + } + + + int offset = GameConfGetOffset(hGameData, "CGameRules::EndGameFrame"); + if(offset == -1) + { + delete hGameData; + SetFailState("Failed to find CGameRules::EndGameFrame offset."); + } + + // CGameRules::EndGameFrame + g_hEndGameFrame = DHookCreate(offset, HookType_GameRules, ReturnType_Void, ThisPointer_Ignore, Hook_EndGameFrame); + if(g_hEndGameFrame == INVALID_HANDLE) + { + delete hGameData; + SetFailState("Failed to DHook CGameRules::EndGameFrame."); + } + delete hGameData; + + + hGameData = LoadGameConfigFile("sdktools.games"); + if(!hGameData) + SetFailState("Failed to load sdktools gamedata."); + + offset = GameConfGetOffset(hGameData, "Activate"); + if(offset == -1) + { + delete hGameData; + SetFailState("Failed to find Activate offset"); + } + + // CPhysForce::Activate + g_hActivate = DHookCreate(offset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, Hook_CPhysForce_Activate); + if(g_hActivate == INVALID_HANDLE) + { + delete hGameData; + SetFailState("Failed to DHookCreate Activate"); + } + + offset = GameConfGetOffset(hGameData, "AcceptInput"); + if(offset == -1) + { + delete hGameData; + SetFailState("Failed to find AcceptInput offset."); + } + + // CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ) + g_hAcceptInput = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, Hook_AcceptInput); + if(g_hAcceptInput == INVALID_HANDLE) + { + delete hGameData; + SetFailState("Failed to DHook AcceptInput."); + } + + DHookAddParam(g_hAcceptInput, HookParamType_CharPtr); + DHookAddParam(g_hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(g_hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(g_hAcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); // variant_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20 + DHookAddParam(g_hAcceptInput, HookParamType_Int); + + delete hGameData; + + // Capability provider from https://github.com/alliedmodders/sourcemod/pull/1078 + g_bHasOnEntitySpawned = GetFeatureStatus(FeatureType_Capability, "SDKHook_OnEntitySpawned") == FeatureStatus_Available; + + //g_hCookie_DisableLagComp = RegClientCookie("disable_lagcomp", "", CookieAccess_Private); + //RegConsoleCmd("sm_lagcomp", OnToggleLagCompSettings); + //RegConsoleCmd("sm_0ping", OnToggleLagCompSettings); + //SetCookieMenuItem(MenuHandler_CookieMenu, 0, "LagCompensation"); + + //CreateTimer(0.1, DisableLagCompTimer, _, TIMER_REPEAT); + + RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_RCON, "sm_unlag "); + RegAdminCmd("sm_lagged", Command_CheckLagCompensated, ADMFLAG_GENERIC, "sm_lagged"); + + FilterClientSolidTouch(g_aaFilterClientSolidTouch, true); + BlockTriggerMoved(g_aBlockTriggerMoved, true); +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_bLateLoad = late; + return APLRes_Success; +} + +public void OnLibraryRemoved(const char[] name) +{ + if(StrEqual(name, "PhysHooks")) + g_bHasPhysHooks = false; +} + +public void OnPluginEnd() +{ + g_bCleaningUp = true; + if(g_bHasPhysHooks) + { + FilterClientSolidTouch(g_aaFilterClientSolidTouch, false); + BlockTriggerMoved(g_aBlockTriggerMoved, false); + BlockTriggerTouchPlayers(g_aBlockTriggerTouchPlayers, false); + } + + DHookDisableDetour(g_hUTIL_Remove, false, Detour_OnUTIL_Remove); + + for(int i = 0; i < g_iNumEntities; i++) + { + if(!IsValidEntity(g_aEntityLagData[i].iEntity)) + continue; + + if(g_aEntityLagData[i].iDeleted) + { + RemoveEdict(g_aEntityLagData[i].iEntity); + } + } +} + +public void OnMapStart() +{ + bool bLate = g_bLateLoad; + + DHookGamerules(g_hEndGameFrame, true); + + g_bCleaningUp = false; + + g_fTickInterval = GetTickInterval(); + + g_iParent = FindDataMapInfo(0, "m_pParent"); + g_iSpawnFlags = FindDataMapInfo(0, "m_spawnflags"); + g_iCollision = FindDataMapInfo(0, "m_Collision"); + g_iSolidFlags = FindDataMapInfo(0, "m_usSolidFlags"); + g_iSolidType = FindDataMapInfo(0, "m_nSolidType"); + g_iSurroundType = FindDataMapInfo(0, "m_nSurroundType"); + g_iEFlags = FindDataMapInfo(0, "m_iEFlags"); + + g_iVecOrigin = FindDataMapInfo(0, "m_vecOrigin"); + g_iVecAbsOrigin = FindDataMapInfo(0, "m_vecAbsOrigin"); + g_iAngRotation = FindDataMapInfo(0, "m_angRotation"); + g_iAngAbsRotation = FindDataMapInfo(0, "m_angAbsRotation"); + g_iVecMins = FindDataMapInfo(0, "m_vecMins"); + g_iVecMaxs = FindDataMapInfo(0, "m_vecMaxs"); + g_iSimulationTime = FindDataMapInfo(0, "m_flSimulationTime"); + g_iCoordinateFrame = FindDataMapInfo(0, "m_rgflCoordinateFrame"); + + /* Late Load */ + if(bLate) + { + for (int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + { + //OnClientConnected(client); + //if(AreClientCookiesCached(client)) + // OnClientCookiesCached(client); + OnClientSettingsChanged(client); + } + } + + int entity = INVALID_ENT_REFERENCE; + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) + { + char sClassname[64]; + if(GetEntityClassname(entity, sClassname, sizeof(sClassname))) + { + OnEntityCreated(entity, sClassname); + OnEntitySpawned(entity, sClassname); + + if(StrEqual(sClassname, "phys_thruster", false)) + { + Hook_CPhysForce_Activate(entity); + } + } + } + } + + g_bLateLoad = false; +} + +public void OnMapEnd() +{ + Detour_OnRestartRound(); + g_bCleaningUp = true; +} + +//public void OnClientConnected(int client) +//{ +// g_bDisableLagComp[client] = false; +// g_iDisableLagComp[client] = 0; +//} +// +//public void OnClientCookiesCached(int client) +//{ +// char sBuffer[16]; +// GetClientCookie(client, g_hCookie_DisableLagComp, sBuffer, sizeof(sBuffer)); +// if(sBuffer[0]) +// g_bDisableLagComp[client] = true; +// else +// g_bDisableLagComp[client] = false; +//} + +public void OnClientSettingsChanged(int client) +{ + if(!IsClientInGame(client)) + return; + + float fLerpTime = GetEntPropFloat(client, Prop_Data, "m_fLerpTime"); + g_aLerpTicks[client] = RoundToNearest(fLerpTime / g_fTickInterval); +} + +//public void OnClientDisconnect(int client) +//{ +// g_bDisableLagComp[client] = false; +// g_iDisableLagComp[client] = 0; +//} + +public void OnEntityCreated(int entity, const char[] classname) +{ + if(g_bCleaningUp) + return; + + if(StrEqual(classname, "phys_thruster", false)) + { + DHookEntity(g_hActivate, true, entity); + } + else if(StrEqual(classname, "game_ui", false)) + { + DHookEntity(g_hAcceptInput, true, entity); + } + + if(!g_bHasOnEntitySpawned) + { + SDKHook(entity, SDKHook_SpawnPost, OnSDKHookEntitySpawnPost); + } +} + +public void OnSDKHookEntitySpawnPost(int entity) +{ + char classname[64]; + GetEntityClassname(entity, classname, sizeof(classname)); + + OnEntitySpawned(entity, classname); +} + +public void OnEntitySpawned(int entity, const char[] classname) +{ + if(g_bCleaningUp) + return; + + CheckEntityForLagComp(entity, classname); +} + +public MRESReturn Hook_AcceptInput(int entity, Handle hReturn, Handle hParams) +{ + if(!IsValidEntity(entity)) + return MRES_Ignored; + + char sCommand[128]; + DHookGetParamString(hParams, 1, sCommand, sizeof(sCommand)); + + if(!StrEqual(sCommand, "Activate", false)) + return MRES_Ignored; + + if(DHookIsNullParam(hParams, 3)) + return MRES_Ignored; + + int iCaller = DHookGetParam(hParams, 3); + if(iCaller <= 0 || iCaller >= MAX_EDICTS) + return MRES_Ignored; + + // Don't lagcompensate anything that has a game_ui button in their hierarchy. + BlacklistFamily(iCaller); + + return MRES_Ignored; +} + +void BlacklistFamily(int entity) +{ + if(entity > 0 && entity < MAX_EDICTS) + { + SetBit(g_aBlacklisted, entity); + RemoveEntityFromLagCompensation(entity); + } + + // Blacklist children of this entity + BlacklistChildren(entity); + + // And blacklist all parents and their children + for(;;) + { + entity = GetEntDataEnt2(entity, g_iParent); + if(entity == INVALID_ENT_REFERENCE) + break; + + if(entity > 0 && entity < MAX_EDICTS) + { + SetBit(g_aBlacklisted, entity); + RemoveEntityFromLagCompensation(entity); + } + + // And their children + BlacklistChildren(entity); + } +} + +void BlacklistChildren(int parent) +{ + int entity = INVALID_ENT_REFERENCE; + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) + { + if(GetEntDataEnt2(entity, g_iParent) != parent) + continue; + + if(entity > 0 && entity < MAX_EDICTS) + { + SetBit(g_aBlacklisted, entity); + RemoveEntityFromLagCompensation(entity); + } + } +} + +bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=false, bool bGoodParents=false) +{ + if(entity < 0 || entity > MAX_EDICTS) + return false; + + if(!IsValidEntity(entity)) + return false; + + if(g_aLagCompensated[entity] != -1) + return false; + + int SpawnFlags = GetEntData(entity, g_iSpawnFlags); + if(SpawnFlags & SF_LAGCOMP_DISABLE) + return false; + + bool bTrigger = StrEqual(classname, "trigger_hurt", false) || + StrEqual(classname, "trigger_push", false) || + StrEqual(classname, "trigger_teleport", false); + + if(bTrigger && !g_bLagCompTriggers.BoolValue) + return false; + + bool bPhysbox = !strncmp(classname, "func_physbox", 12, false); + + if(bPhysbox && !g_bLagCompPhysboxes.BoolValue) + return false; + + bool bBlacklisted = CheckBit(g_aBlacklisted, entity); + + if(!bTrigger && !bPhysbox || bBlacklisted) + return false; + + // Don't lag compensate anything that could be parented to a player + // The player simulation would usually move the entity, + // but we would overwrite that position change by restoring the entity to its previous state. + int iParent = INVALID_ENT_REFERENCE; + char sParentClassname[64]; + for(int iTmp = entity;;) + { + iTmp = GetEntDataEnt2(iTmp, g_iParent); + if(iTmp == INVALID_ENT_REFERENCE) + break; + + iParent = iTmp; + GetEntityClassname(iParent, sParentClassname, sizeof(sParentClassname)); + + if((iParent >= 1 && iParent <= MaxClients) || + !strncmp(sParentClassname, "weapon_", 7)) + { + return false; + } + + if(CheckBit(g_aBlacklisted, entity)) + { + return false; + } + + if(g_aLagCompensated[iParent] != -1) + { + bGoodParents = true; + break; + } + + if(strncmp(sParentClassname, "func_", 5)) + continue; + + if(StrEqual(sParentClassname[5], "movelinear") || + StrEqual(sParentClassname[5], "door") || + StrEqual(sParentClassname[5], "rotating") || + StrEqual(sParentClassname[5], "tracktrain")) + { + bGoodParents = true; + break; + } + } + + if(!bGoodParents) + return false; + + if(AddEntityForLagCompensation(entity, bTrigger)) + { + if(bTrigger) + { + if(!(SpawnFlags & (SF_TRIGGER_ALLOW_PUSHABLES | SF_TRIGGER_ALLOW_PHYSICS | SF_TRIGGER_ALLOW_ALL | SF_TRIG_TOUCH_DEBRIS))) + SetBit(g_aBlockTriggerMoved, entity); + } + + if(bRecursive) + { + CheckEntityChildrenForLagComp(entity); + } + + return true; + } + + return false; +} + +void CheckEntityChildrenForLagComp(int parent) +{ + int entity = INVALID_ENT_REFERENCE; + while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) + { + if(GetEntDataEnt2(entity, g_iParent) != parent) + continue; + + char sClassname[64]; + if(GetEntityClassname(entity, sClassname, sizeof(sClassname))) + { + CheckEntityForLagComp(entity, sClassname, _, true); + CheckEntityChildrenForLagComp(entity); + } + } +} + +public void OnEntityDestroyed(int entity) +{ + if(g_bCleaningUp) + return; + + if(entity < 0 || entity > MAX_EDICTS) + return; + + ClearBit(g_aBlacklisted, entity); + + int iIndex = g_aLagCompensated[entity]; + if(iIndex == -1) + return; + + RemoveRecord(iIndex); +} + + +public MRESReturn Detour_OnUTIL_Remove(Handle hParams) +{ + if(g_bCleaningUp) + return MRES_Ignored; + + if(DHookIsNullParam(hParams, 1)) + return MRES_Ignored; + + int entity = DHookGetParamObjectPtrVar(hParams, 1, g_iNetworkableOuter, ObjectValueType_CBaseEntityPtr); + if(entity < 0 || entity > MAX_EDICTS) + return MRES_Ignored; + + int iIndex = g_aLagCompensated[entity]; + if(iIndex == -1) + return MRES_Ignored; + + // let it die + if(!g_aEntityLagData[iIndex].bLateKill) + return MRES_Ignored; + + // ignore sleeping entities + if(g_aEntityLagData[iIndex].iNotMoving >= MAX_RECORDS) + return MRES_Ignored; + + if(!g_aEntityLagData[iIndex].iDeleted) + { + g_aEntityLagData[iIndex].iDeleted = GetGameTickCount(); + } + + return MRES_Supercede; +} + +public MRESReturn Detour_OnRestartRound() +{ + g_bCleaningUp = true; + + for(int i = 0; i < g_iNumEntities; i++) + { + int iEntity = g_aEntityLagData[i].iEntity; + + g_aLagCompensated[iEntity] = -1; + ClearBit(g_aBlockTriggerTouchPlayers, iEntity); + ClearBit(g_aBlockTriggerMoved, iEntity); + + for(int client = 1; client <= MaxClients; client++) + { + ClearBit(g_aaFilterClientSolidTouch, client * MAX_EDICTS + iEntity); + } + + if(g_aEntityLagData[i].iDeleted) + { + if(IsValidEntity(iEntity)) + RemoveEdict(iEntity); + } + + g_aEntityLagData[i].iEntity = INVALID_ENT_REFERENCE; + } + + for(int i = 0; i < sizeof(g_aBlacklisted); i++) + g_aBlacklisted[i] = 0; + + g_iNumEntities = 0; + + g_bCleaningUp = false; + return MRES_Ignored; +} + +// https://developer.valvesoftware.com/wiki/Logic_measure_movement +int g_OnSetTarget_pThis; +public MRESReturn Detour_OnSetTargetPre(int pThis, Handle hParams) +{ + g_OnSetTarget_pThis = pThis; + return MRES_Ignored; +} +public MRESReturn Detour_OnSetTargetPost(Handle hParams) +{ + int entity = GetEntPropEnt(g_OnSetTarget_pThis, Prop_Data, "m_hTarget"); + if(!IsValidEntity(entity)) + return MRES_Ignored; + + char sClassname[64]; + if(!GetEntityClassname(entity, sClassname, sizeof(sClassname))) + return MRES_Ignored; + + CheckEntityForLagComp(entity, sClassname, true, true); + + return MRES_Ignored; +} + +public MRESReturn Hook_CPhysForce_Activate(int entity) +{ + int attachedObject = GetEntPropEnt(entity, Prop_Data, "m_attachedObject"); + if(!IsValidEntity(attachedObject)) + return MRES_Ignored; + + char sClassname[64]; + if(!GetEntityClassname(attachedObject, sClassname, sizeof(sClassname))) + return MRES_Ignored; + + CheckEntityForLagComp(attachedObject, sClassname, true, true); + + return MRES_Ignored; +} + +public MRESReturn Detour_OnFrameUpdatePostEntityThink() +{ + for(int i = 0; i < g_iNumEntities; i++) + { + // Don't make the entity check untouch in FrameUpdatePostEntityThink. + // If the player didn't get simulated in the current frame then + // they didn't have a chance to touch this entity. + // Hence the touchlink could be broken and we only let the player check untouch. + int EFlags = GetEntData(g_aEntityLagData[i].iEntity, g_iEFlags); + EFlags &= ~EFL_CHECK_UNTOUCH; + SetEntData(g_aEntityLagData[i].iEntity, g_iEFlags, EFlags); + } +} + + +public void OnRunThinkFunctions(bool simulating) +{ + g_iGameTick = GetGameTickCount(); + BlockTriggerTouchPlayers(g_aBlockTriggerTouchPlayers, false); +} + +public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) +{ + if(!IsPlayerAlive(client) || IsFakeClient(client)) + return Plugin_Continue; + + int iTargetTick = tickcount - g_aLerpTicks[client]; + + // -1 because the newest record in the list is one tick old + // this is because we simulate players first + // hence no new entity record was inserted on the current tick + int iDelta = g_iGameTick - iTargetTick - 1; + + // The player is stupid and doesn't want lag compensation. + // To get the original behavior back lets assume they actually have 0 latency. + // To avoid abusing toggling lagcomp we increase/decrease this var by 1 every 100ms. + // This is so we only skip single ticks at a time. Fully ON = 0, Fully OFF = MAX_RECORDS + //iDelta -= g_iDisableLagComp[client]; + + if(iDelta < 0) + iDelta = 0; + if(iDelta > MAX_RECORDS) + iDelta = MAX_RECORDS; + + int iPlayerSimTick = g_iGameTick - iDelta; + + for(int i = 0; i < g_iNumEntities; i++) + { + int iEntity = g_aEntityLagData[i].iEntity; + + // Entity too new, the client couldn't even see it yet. + if(g_aEntityLagData[i].iSpawned > iPlayerSimTick) + { + SetBit(g_aaFilterClientSolidTouch, client * MAX_EDICTS + iEntity); + continue; + } + + if(g_aEntityLagData[i].iDeleted) + { + if(g_aEntityLagData[i].iDeleted <= iPlayerSimTick) + { + SetBit(g_aaFilterClientSolidTouch, client * MAX_EDICTS + iEntity); + continue; + } + } + else + { + ClearBit(g_aaFilterClientSolidTouch, client * MAX_EDICTS + iEntity); + } + + if(g_aEntityLagData[i].iNotMoving >= MAX_RECORDS) + continue; + + int iRecord = iDelta; + if(iRecord >= g_aEntityLagData[i].iNumRecords) + iRecord = g_aEntityLagData[i].iNumRecords - 1; + + int iRecordIndex = g_aEntityLagData[i].iRecordIndex - iRecord; + if(iRecordIndex < 0) + iRecordIndex += MAX_RECORDS; + + RestoreEntityFromRecord(iEntity, g_aaLagRecords[i][iRecordIndex]); + g_aEntityLagData[i].bRestore = !g_aEntityLagData[i].iDeleted; + } + + return Plugin_Continue; +} + +public void OnPostPlayerThinkFunctions() +{ + for(int i = 0; i < g_iNumEntities; i++) + { + if(!g_aEntityLagData[i].bRestore) + continue; + + RestoreEntityFromRecord(g_aEntityLagData[i].iEntity, g_aaLagRecords[i][g_aEntityLagData[i].iRecordIndex]); + g_aEntityLagData[i].bRestore = false; + } + + BlockTriggerTouchPlayers(g_aBlockTriggerTouchPlayers, true); +} + +public MRESReturn Hook_EndGameFrame() +{ + for(int i = 0; i < g_iNumEntities; i++) + { + if(g_aEntityLagData[i].iDeleted) + { + if(g_aEntityLagData[i].iDeleted + MAX_RECORDS <= g_iGameTick) + { + // calls OnEntityDestroyed right away + // which calls RemoveRecord + // which moves the next element to our current position + RemoveEdict(g_aEntityLagData[i].iEntity); + i--; continue; + } + + if(g_aEntityLagData[i].iRecordsValid) + { + g_aEntityLagData[i].iRecordIndex++; + + if(g_aEntityLagData[i].iRecordIndex >= MAX_RECORDS) + g_aEntityLagData[i].iRecordIndex = 0; + + g_aEntityLagData[i].iRecordsValid--; + } + + continue; + } + + LagRecord TmpRecord; + RecordDataIntoRecord(g_aEntityLagData[i].iEntity, TmpRecord); + + // sleep detection + { + int iOldRecord = g_aEntityLagData[i].iRecordIndex; + + if(CompareVectors(g_aaLagRecords[i][iOldRecord].vecAbsOrigin, TmpRecord.vecAbsOrigin) && + CompareVectors(g_aaLagRecords[i][iOldRecord].angAbsRotation, TmpRecord.angAbsRotation)) + { + g_aEntityLagData[i].iNotMoving++; + } + else + { + g_aEntityLagData[i].iNotMoving = 0; + } + + if(g_aEntityLagData[i].iNotMoving >= MAX_RECORDS) + continue; + } + + g_aEntityLagData[i].iRecordIndex++; + + if(g_aEntityLagData[i].iRecordIndex >= MAX_RECORDS) + g_aEntityLagData[i].iRecordIndex = 0; + + if(g_aEntityLagData[i].iNumRecords < MAX_RECORDS) + g_aEntityLagData[i].iRecordsValid = ++g_aEntityLagData[i].iNumRecords; + + LagRecord_Copy(g_aaLagRecords[i][g_aEntityLagData[i].iRecordIndex], TmpRecord); + } + + return MRES_Ignored; +} + + +void RecordDataIntoRecord(int iEntity, LagRecord Record) +{ + // Force recalculation of all values + int EFlags = GetEntData(iEntity, g_iEFlags); + EFlags |= EFL_DIRTY_ABSTRANSFORM; + SetEntData(iEntity, g_iEFlags, EFlags); + + SDKCall(g_hCalcAbsolutePosition, iEntity); + + GetEntDataVector(iEntity, g_iVecOrigin, Record.vecOrigin); + GetEntDataVector(iEntity, g_iVecAbsOrigin, Record.vecAbsOrigin); + GetEntDataVector(iEntity, g_iAngRotation, Record.angRotation); + GetEntDataVector(iEntity, g_iAngAbsRotation, Record.angAbsRotation); + GetEntDataVector(iEntity, g_iVecMins, Record.vecMins); + GetEntDataVector(iEntity, g_iVecMaxs, Record.vecMaxs); + GetEntDataArray(iEntity, g_iCoordinateFrame, view_as(Record.rgflCoordinateFrame), COORDINATE_FRAME_SIZE); + Record.flSimulationTime = GetEntDataFloat(iEntity, g_iSimulationTime); +} + +bool DoesRotationInvalidateSurroundingBox(int iEntity) +{ + int SolidFlags = GetEntData(iEntity, g_iSolidFlags); + if(SolidFlags & FSOLID_ROOT_PARENT_ALIGNED) + return true; + + int SurroundType = GetEntData(iEntity, g_iSurroundType); + switch(SurroundType) + { + case USE_COLLISION_BOUNDS_NEVER_VPHYSICS, + USE_OBB_COLLISION_BOUNDS, + USE_BEST_COLLISION_BOUNDS: + { + // IsBoundsDefinedInEntitySpace() + int SolidType = GetEntData(iEntity, g_iSolidType); + return ((SolidFlags & FSOLID_FORCE_WORLD_ALIGNED) == 0) && + (SolidType != SOLID_BBOX) && (SolidType != SOLID_NONE); + } + + case USE_HITBOXES, + USE_GAME_CODE: + { + return true; + } + + case USE_ROTATION_EXPANDED_BOUNDS, + USE_SPECIFIED_BOUNDS: + { + return false; + } + + default: + { + return true; + } + } +} + +void InvalidatePhysicsRecursive(int iEntity) +{ + // CollisionProp()->MarkPartitionHandleDirty(); + Address CollisionProp = GetEntityAddress(iEntity) + view_as
(g_iCollision); + SDKCall(g_hMarkPartitionHandleDirty, CollisionProp); + + if(DoesRotationInvalidateSurroundingBox(iEntity)) + { + // CollisionProp()->MarkSurroundingBoundsDirty(); + int EFlags = GetEntData(iEntity, g_iEFlags); + EFlags |= EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS; + SetEntData(iEntity, g_iEFlags, EFlags); + } +} + +void RestoreEntityFromRecord(int iEntity, LagRecord Record) +{ + SetEntDataVector(iEntity, g_iVecOrigin, Record.vecOrigin); + SetEntDataVector(iEntity, g_iVecAbsOrigin, Record.vecAbsOrigin); + SetEntDataVector(iEntity, g_iAngRotation, Record.angRotation); + SetEntDataVector(iEntity, g_iAngAbsRotation, Record.angAbsRotation); + SetEntDataVector(iEntity, g_iVecMins, Record.vecMins); + SetEntDataVector(iEntity, g_iVecMaxs, Record.vecMaxs); + SetEntDataArray(iEntity, g_iCoordinateFrame, view_as(Record.rgflCoordinateFrame), COORDINATE_FRAME_SIZE); + SetEntDataFloat(iEntity, g_iSimulationTime, Record.flSimulationTime); + + InvalidatePhysicsRecursive(iEntity); +} + + +bool AddEntityForLagCompensation(int iEntity, bool bLateKill) +{ + if(g_bCleaningUp) + return false; + + if(g_iNumEntities == MAX_ENTITIES) + { + char sClassname[64]; + GetEntityClassname(iEntity, sClassname, sizeof(sClassname)); + + char sTargetname[64]; + GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + int iHammerID = GetEntProp(iEntity, Prop_Data, "m_iHammerID"); + + PrintToBoth("[%d] OUT OF LAGCOMP SLOTS entity %d (%s)\"%s\"(#%d)", GetGameTickCount(), iEntity, sClassname, sTargetname, iHammerID); + return false; + } + + if(g_aLagCompensated[iEntity] != -1) + return false; + + int i = g_iNumEntities; + g_iNumEntities++; + + g_aLagCompensated[iEntity] = i; + + g_aEntityLagData[i].iEntity = iEntity; + g_aEntityLagData[i].iRecordIndex = 0; + g_aEntityLagData[i].iNumRecords = 1; + g_aEntityLagData[i].iRecordsValid = 1; + g_aEntityLagData[i].iSpawned = GetGameTickCount(); + g_aEntityLagData[i].iDeleted = 0; + g_aEntityLagData[i].iNotMoving = MAX_RECORDS; + g_aEntityLagData[i].bRestore = false; + g_aEntityLagData[i].bLateKill = bLateKill; + + if(bLateKill) + { + SetBit(g_aBlockTriggerTouchPlayers, iEntity); + } + + RecordDataIntoRecord(iEntity, g_aaLagRecords[i][0]); + + { + char sClassname[64]; + GetEntityClassname(iEntity, sClassname, sizeof(sClassname)); + + char sTargetname[64]; + GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + int iHammerID = GetEntProp(iEntity, Prop_Data, "m_iHammerID"); + + PrintToBoth("[%d] added entity %d (%s)\"%s\"(#%d) under index %d", GetGameTickCount(), iEntity, sClassname, sTargetname, iHammerID, i); + } + + return true; +} + +bool RemoveEntityFromLagCompensation(int iEntity) +{ + int index = g_aLagCompensated[iEntity]; + if(index == -1) + return false; + + RemoveRecord(index); + return true; +} + +void RemoveRecord(int index) +{ + if(g_bCleaningUp) + return; + + int iEntity = g_aEntityLagData[index].iEntity; + + if(IsValidEntity(iEntity)) + { + char sClassname[64]; + GetEntityClassname(g_aEntityLagData[index].iEntity, sClassname, sizeof(sClassname)); + + char sTargetname[64]; + GetEntPropString(g_aEntityLagData[index].iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + int iHammerID = GetEntProp(g_aEntityLagData[index].iEntity, Prop_Data, "m_iHammerID"); + + PrintToBoth("[%d] RemoveRecord %d / %d (%s)\"%s\"(#%d), num: %d", GetGameTickCount(), index, g_aEntityLagData[index].iEntity, sClassname, sTargetname, iHammerID, g_iNumEntities); + } + + g_aLagCompensated[iEntity] = -1; + ClearBit(g_aBlockTriggerTouchPlayers, iEntity); + ClearBit(g_aBlockTriggerMoved, iEntity); + + for(int client = 1; client <= MaxClients; client++) + { + ClearBit(g_aaFilterClientSolidTouch, client * MAX_EDICTS + iEntity); + } + + g_aEntityLagData[index].iEntity = INVALID_ENT_REFERENCE; + + for(int src = index + 1; src < g_iNumEntities; src++) + { + int dest = src - 1; + + EntityLagData_Copy(g_aEntityLagData[dest], g_aEntityLagData[src]); + g_aEntityLagData[src].iEntity = INVALID_ENT_REFERENCE; + g_aLagCompensated[g_aEntityLagData[dest].iEntity] = dest; + + int iNumRecords = g_aEntityLagData[dest].iNumRecords; + for(int i = 0; i < iNumRecords; i++) + { + LagRecord_Copy(g_aaLagRecords[dest][i], g_aaLagRecords[src][i]); + } + } + + g_iNumEntities--; +} + +void EntityLagData_Copy(EntityLagData obj, const EntityLagData other) +{ + obj.iEntity = other.iEntity; + obj.iRecordIndex = other.iRecordIndex; + obj.iNumRecords = other.iNumRecords; + obj.iRecordsValid = other.iRecordsValid; + obj.iSpawned = other.iSpawned; + obj.iDeleted = other.iDeleted; + obj.iNotMoving = other.iNotMoving; + obj.bRestore = other.bRestore; + obj.bLateKill = other.bLateKill; +} + +void LagRecord_Copy(LagRecord obj, const LagRecord other) +{ + for(int i = 0; i < 3; i++) + { + obj.vecOrigin[i] = other.vecOrigin[i]; + obj.vecAbsOrigin[i] = other.vecAbsOrigin[i]; + obj.angRotation[i] = other.angRotation[i]; + obj.angAbsRotation[i] = other.angAbsRotation[i]; + obj.vecMins[i] = other.vecMins[i]; + obj.vecMaxs[i] = other.vecMaxs[i]; + } + + obj.flSimulationTime = other.flSimulationTime; + + for(int i = 0; i < COORDINATE_FRAME_SIZE; i++) + { + obj.rgflCoordinateFrame[i] = other.rgflCoordinateFrame[i]; + } +} + +bool CompareVectors(const float vec1[3], const float vec2[3]) +{ + return vec1[0] == vec2[0] && vec1[1] == vec2[1] && vec1[2] == vec2[2]; +} + + +public Action Command_AddLagCompensation(int client, int argc) +{ + if(argc < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_unlag [late]"); + return Plugin_Handled; + } + + char sArgs[32]; + GetCmdArg(1, sArgs, sizeof(sArgs)); + + int entity = StringToInt(sArgs); + + bool late = false; + if(argc >= 2) + { + GetCmdArg(2, sArgs, sizeof(sArgs)); + late = view_as(StringToInt(sArgs)); + } + + AddEntityForLagCompensation(entity, late); + + return Plugin_Handled; +} + +public Action Command_CheckLagCompensated(int client, int argc) +{ + if(argc == 0) + { + for(int i = 0; i < g_iNumEntities; i++) + { + char sClassname[64]; + GetEntityClassname(g_aEntityLagData[i].iEntity, sClassname, sizeof(sClassname)); + + char sTargetname[64]; + GetEntPropString(g_aEntityLagData[i].iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + int iHammerID = GetEntProp(g_aEntityLagData[i].iEntity, Prop_Data, "m_iHammerID"); + + PrintToConsole(client, "%2d. #%d %s \"%s\" (#%d)", i, g_aEntityLagData[i].iEntity, sClassname, sTargetname, iHammerID); + } + + return Plugin_Handled; + } + + for(int iEntity = 0; iEntity < MAX_EDICTS; iEntity++) + { + bool bDeleted = false; + for(int j = 1; j <= MaxClients; j++) + { + if(CheckBit(g_aaFilterClientSolidTouch, j * MAX_EDICTS + iEntity)) + { + bDeleted = true; + break; + } + } + + bool bBlockPhysics = CheckBit(g_aBlockTriggerTouchPlayers, iEntity); + bool bBlockTriggerMoved = CheckBit(g_aBlockTriggerMoved, iEntity); + bool bBlacklisted = CheckBit(g_aBlacklisted, iEntity); + + if(bDeleted || bBlockPhysics || bBlockTriggerMoved || bBlacklisted) + { + int index = -1; + for(int j = 0; j < g_iNumEntities; j++) + { + if(g_aEntityLagData[j].iEntity == iEntity) + { + index = j; + break; + } + } + + char sClassname[64] = "INVALID"; + char sTargetname[64] = "INVALID"; + int iHammerID = -1; + + if(IsValidEntity(iEntity)) + { + GetEntityClassname(iEntity, sClassname, sizeof(sClassname)); + GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + iHammerID = GetEntProp(iEntity, Prop_Data, "m_iHammerID"); + } + + PrintToConsole(client, "%2d. #%d %s \"%s\" (#%d) -> Phys: %d / TrigMov: %d / Deleted: %d / Black: %d", + index, iEntity, sClassname, sTargetname, iHammerID, bBlockPhysics, bBlockTriggerMoved, bDeleted, bBlacklisted); + } + } + + return Plugin_Handled; +} + + +stock void PrintToBoth(const char[] format, any ...) +{ + char buffer[254]; + VFormat(buffer, sizeof(buffer), format, 2); + LogMessage(buffer); + + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + { + PrintToConsole(client, "%s", buffer); + } + } +} + +//public Action DisableLagCompTimer(Handle timer) +//{ +// for(int client = 1; client <= MaxClients; client++) +// { +// if(g_bDisableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS) +// { +// g_iDisableLagComp[client]++; +// } +// else if(!g_bDisableLagComp[client] && g_iDisableLagComp[client] > 0) +// { +// g_iDisableLagComp[client]--; +// } +// } +// +// return Plugin_Continue; +//} +// +//public Action OnLagCompSettings(int client, int args) +//{ +// ShowSettingsMenu(client); +// return Plugin_Handled; +//} + +//public Action OnToggleLagCompSettings(int client, int args) +//{ +// ToggleLagCompSettings(client); +// return Plugin_Handled; +//} +// +//public void ToggleLagCompSettings(int client) +//{ +// if(!client) +// return; +// +// g_bDisableLagComp[client] = !g_bDisableLagComp[client]; +// SetClientCookie(client, g_hCookie_DisableLagComp, g_bDisableLagComp[client] ? "1" : ""); +// +// PrintToChat(client, "\x04[LagCompensation]\x01 LagCompensation has been %s.", g_bDisableLagComp[client] ? "disabled" : "enabled"); +//} +// +//public void ShowSettingsMenu(int client) +//{ +// Menu menu = new Menu(MenuHandler_MainMenu); +// menu.SetTitle("LagCompensation Settings", client); +// menu.ExitBackButton = true; +// +// char sBuffer[128]; +// Format(sBuffer, sizeof(sBuffer), "LagCompensation: %s", g_bDisableLagComp[client] ? "Disabled" : "Enabled"); +// menu.AddItem("0", sBuffer); +// +// menu.Display(client, MENU_TIME_FOREVER); +//} +// +//public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) +//{ +// switch(action) +// { +// case(CookieMenuAction_DisplayOption): +// { +// Format(buffer, maxlen, "LagCompensation", client); +// } +// case(CookieMenuAction_SelectOption): +// { +// ShowSettingsMenu(client); +// } +// } +//} +// +//public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection) +//{ +// switch(action) +// { +// case(MenuAction_Select): +// { +// switch(selection) +// { +// case(0): ToggleLagCompSettings(client); +// } +// +// ShowSettingsMenu(client); +// } +// case(MenuAction_Cancel): +// { +// ShowCookieMenu(client); +// } +// case(MenuAction_End): +// { +// delete menu; +// } +// } +//} diff --git a/MakoV5System/scripting/MakoV5System.sp b/MakoV5System/scripting/MakoV5System.sp new file mode 100644 index 0000000..e303d28 --- /dev/null +++ b/MakoV5System/scripting/MakoV5System.sp @@ -0,0 +1,670 @@ +#include +#include +#include +#include +#include +#include + +#pragma semicolon 1 + +#define NUMBEROFSTAGES 6 + +bool g_bVoteFinished = true; +bool g_bIsRevote = false; +bool g_bStartVoteNextRound = false; +bool g_bPlayedZM = false; +bool g_bRaceEnabled; +bool g_bRaceAutoBhop; +bool g_bRaceBlockInfect; +bool g_bRaceBlockRespawn; + +bool g_bOnCooldown[NUMBEROFSTAGES]; +static char g_sStageName[NUMBEROFSTAGES][32] = {"Extreme 2", "Extreme 2 (Heal + Ultima)", "Extreme 3 (ZED)", "Extreme 3 (Hellz)", "Race Mode", "Zombie Mode"}; +int g_Winnerstage; + +Handle g_VoteMenu = INVALID_HANDLE; +Handle g_StageList = INVALID_HANDLE; +Handle g_CountdownTimer = INVALID_HANDLE; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "MakoV5 System CS:GO", + author = "Neon + xen", + description = "MakoV5 System CS:GO", + version = "1.0", + url = "https://steamcommunity.com/id/n3ontm" +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + RegServerCmd("sm_makovote", Command_StartVote); + + RegAdminCmd("sm_racebhop", Command_RaceBhop, ADMFLAG_GENERIC, "Toggle race-mode bhop"); + + RegConsoleCmd("sm_cancelrace", Command_CancelRace, "Cancel the race"); + RegConsoleCmd("sm_startrace", Command_StartRace, "Start the race"); + RegConsoleCmd("sm_endrace", Command_EndRace, "End the race"); + + HookEvent("round_start", OnRoundStart); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnMapStart() +{ + VerifyMap(); + + g_bStartVoteNextRound = false; + g_bPlayedZM = false; + + for (int i = 0; i < NUMBEROFSTAGES; i++) + g_bOnCooldown[i] = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action VerifyMap() +{ + char currentMap[64]; + GetCurrentMap(currentMap, sizeof(currentMap)); + + if (!StrEqual(currentMap, "ze_ffvii_mako_reactor_v5_3_v5", false)) + { + char sFilename[256]; + GetPluginFilename(INVALID_HANDLE, sFilename, sizeof(sFilename)); + + ServerCommand("sm plugins unload %s", sFilename); + } + else + { + LoadContent(); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnEntitySpawned(int iEntity, const char[] sClassname) +{ + if (!IsValidEntity(iEntity) || g_bVoteFinished) + return; + + char sTargetname[128]; + GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + if ((strcmp(sTargetname, "espad") != 0) && (strcmp(sTargetname, "ss_slow") != 0) && (strcmp(sClassname, "ambient_generic") == 0)) + { + AcceptEntityInput(iEntity, "Kill"); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast) +{ + if (g_bStartVoteNextRound) + { + g_bVoteFinished = false; + if (!g_bPlayedZM) + { + CPrintToChatAll("{green}[Mako Vote] {default}ZM has not been played yet. Rolling the dice..."); + if (GetRandomInt(1, 100) <= 35) + { + CPrintToChatAll("{green}[Mako Vote] {default}Result: ZM, restarting round."); + ServerCommand("sm_stage zm"); + g_bVoteFinished = true; + g_bStartVoteNextRound = false; + g_bPlayedZM = true; + CS_TerminateRound(1.0, CSRoundEnd_GameStart, false); + return; + } + CPrintToChatAll("{green}[Mako Vote] {default}Result: Normal vote"); + } + else + CPrintToChatAll("{green}[Mako Vote] {default}ZM already has been played. Starting normal vote."); + + g_CountdownTimer = CreateTimer(1.0, StartVote, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + g_bStartVoteNextRound = false; + } + + if (!(g_bVoteFinished)) + { + int iStrip = FindEntityByTargetname(INVALID_ENT_REFERENCE, "RaceZone", "game_zone_player"); + if (iStrip != INVALID_ENT_REFERENCE) + AcceptEntityInput(iStrip, "FireUser1"); + + int iButton1 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "boton", "func_button"); + if (iButton1 != INVALID_ENT_REFERENCE) + AcceptEntityInput(iButton1, "Lock"); + + int iButton2 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "RaceMapButton1", "func_button"); + if (iButton2 != INVALID_ENT_REFERENCE) + AcceptEntityInput(iButton2, "Lock"); + + int iButton3 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "RaceMapButton2", "func_button"); + if (iButton3 != INVALID_ENT_REFERENCE) + AcceptEntityInput(iButton3, "Lock"); + + int iButton4 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "RaceMapButton3", "func_button"); + if (iButton4 != INVALID_ENT_REFERENCE) + AcceptEntityInput(iButton4, "Lock"); + + int iButton5 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "RaceMapButton4", "func_button"); + if (iButton5 != INVALID_ENT_REFERENCE) + AcceptEntityInput(iButton5, "Lock"); + + int iCounter = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "math_counter"); + if (iCounter != INVALID_ENT_REFERENCE) + AcceptEntityInput(iCounter, "Kill"); + + int iFilter = FindEntityByTargetname(INVALID_ENT_REFERENCE, "humanos", "filter_activator_team"); + if (iFilter != INVALID_ENT_REFERENCE) + AcceptEntityInput(iFilter, "Kill"); + + int iBarrerasfinal = FindEntityByTargetname(INVALID_ENT_REFERENCE, "barrerasfinal", "prop_dynamic"); + if (iBarrerasfinal != INVALID_ENT_REFERENCE) + AcceptEntityInput(iBarrerasfinal, "Kill"); + + int iBarrerasfinal2 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "barrerasfinal2", "func_breakable"); + if (iBarrerasfinal2 != INVALID_ENT_REFERENCE) + AcceptEntityInput(iBarrerasfinal2, "Break"); + + int iLevelText = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelText", "game_text"); + if (iLevelText != INVALID_ENT_REFERENCE) + { + SetVariantString("message > INTERMISSION ROUND <"); + AcceptEntityInput(iLevelText, "AddOutput"); + } + + int iDestination = FindEntityByTargetname(INVALID_ENT_REFERENCE, "arriba2ex", "info_teleport_destination"); + if (iDestination != INVALID_ENT_REFERENCE) + { + SetVariantString("origin -9350 4550 100"); + AcceptEntityInput(iDestination, "AddOutput"); + + SetVariantString("angles 0 -90 0"); + AcceptEntityInput(iDestination, "AddOutput"); + } + + int iTemplate1 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "EX2Laser1Temp", "point_template"); + if (iTemplate1 != INVALID_ENT_REFERENCE) + { + DispatchKeyValue(iTemplate1, "OnEntitySpawned", "EX2Laser1Hurt,SetDamage,0,0,-1"); + DispatchKeyValue(iTemplate1, "OnEntitySpawned", "EX2Laser1Hurt,AddOutput,OnStartTouch !activator:AddOutput:origin -7000 -1000 100:0:-1,0,-1"); + } + + int iTemplate2 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "EX2Laser2Temp", "point_template"); + if (iTemplate2 != INVALID_ENT_REFERENCE) + { + DispatchKeyValue(iTemplate2, "OnEntitySpawned", "EX2Laser2Hurt,SetDamage,0,0,-1"); + DispatchKeyValue(iTemplate2, "OnEntitySpawned", "EX2Laser2Hurt,AddOutput,OnStartTouch !activator:AddOutput:origin -7000 -1000 100:0:-1,0,-1"); + } + + int iTemplate3 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "EX2Laser3Temp", "point_template"); + if (iTemplate3 != INVALID_ENT_REFERENCE) + { + DispatchKeyValue(iTemplate3, "OnEntitySpawned", "EX2Laser3Hurt,SetDamage,0,0,-1"); + DispatchKeyValue(iTemplate3, "OnEntitySpawned", "EX2Laser3Hurt,AddOutput,OnStartTouch !activator:AddOutput:origin -7000 -1000 100:0:-1,0,-1"); + } + + int iTemplate4 = FindEntityByTargetname(INVALID_ENT_REFERENCE, "EX2Laser4Temp", "point_template"); + if (iTemplate4 != INVALID_ENT_REFERENCE) + { + DispatchKeyValue(iTemplate4, "OnEntitySpawned", "EX2Laser4Hurt,SetDamage,0,0,-1"); + DispatchKeyValue(iTemplate4, "OnEntitySpawned", "EX2Laser4Hurt,AddOutput,OnStartTouch !activator:AddOutput:origin -7000 -1000 100:0:-1,0,-1"); + } + + int iMusic = FindEntityByTargetname(INVALID_ENT_REFERENCE, "ss_slow", "ambient_generic"); + if (iMusic != INVALID_ENT_REFERENCE) + { + SetVariantString("message #music/unloze/Pendulum_Witchcraft.mp3"); + AcceptEntityInput(iMusic, "AddOutput"); + + AcceptEntityInput(iMusic, "PlaySound"); + } + // CS:GO doesn't kill entities when they spawn I guess, so double down on that + iMusic = FindEntityByTargetname(INVALID_ENT_REFERENCE, "cancion_1", "ambient_generic"); + if (iMusic != INVALID_ENT_REFERENCE) + { + AcceptEntityInput(iMusic, "Kill"); + } + + int iTeleport = FindEntityByTargetname(INVALID_ENT_REFERENCE, "teleporte_extreme", "trigger_teleport"); + if (iTeleport != INVALID_ENT_REFERENCE) + AcceptEntityInput(iTeleport, "Enable"); + + int iTimer = FindEntityByTargetname(INVALID_ENT_REFERENCE, "cortes2", "logic_timer"); + if (iTimer != INVALID_ENT_REFERENCE) + AcceptEntityInput(iTimer, "Enable"); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void GenerateArray() +{ + int iBlockSize = ByteCountToCells(PLATFORM_MAX_PATH); + g_StageList = CreateArray(iBlockSize); + + for (int i = 0; i < NUMBEROFSTAGES; i++) + PushArrayString(g_StageList, g_sStageName[i]); + + int iArraySize = GetArraySize(g_StageList); + + for (int i = 0; i < iArraySize; i++) + { + int iRandom = GetRandomInt(0, iArraySize - 1); + char sTemp1[128]; + GetArrayString(g_StageList, iRandom, sTemp1, sizeof(sTemp1)); + char sTemp2[128]; + GetArrayString(g_StageList, i, sTemp2, sizeof(sTemp2)); + SetArrayString(g_StageList, i, sTemp1); + SetArrayString(g_StageList, iRandom, sTemp2); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_StartVote(int args) +{ + int iCurrentStage = GetCurrentStage(); + + if (iCurrentStage > 0) + g_bOnCooldown[iCurrentStage] = true; + + if (iCurrentStage == 5) + g_bPlayedZM = true; + + int iOnCD = 0; + for (int i = 0; i < NUMBEROFSTAGES; i++) + { + if (g_bOnCooldown[i]) + iOnCD += 1; + } + + if (iOnCD >= 4) + { + for (int i = 0; i < NUMBEROFSTAGES; i++) + g_bOnCooldown[i] = false; + } + + GenerateArray(); + g_bStartVoteNextRound = true; + + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action StartVote(Handle timer) +{ + static int iCountDown = 5; + PrintCenterTextAll("[MakoVote] Starting Vote in %ds", iCountDown); + + if (iCountDown-- <= 0) + { + iCountDown = 5; + KillTimer(g_CountdownTimer); + g_CountdownTimer = INVALID_HANDLE; + InitiateVote(); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void InitiateVote() +{ + if(IsVoteInProgress()) + { + CPrintToChatAll("{green}[Mako Vote] {default}Another vote is currently in progress, retrying again in 5s."); + g_CountdownTimer = CreateTimer(1.0, StartVote, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + return; + } + + Handle menuStyle = GetMenuStyleHandle(view_as(0)); + g_VoteMenu = CreateMenuEx(menuStyle, Handler_MakoVoteMenu, MenuAction_End | MenuAction_Display | MenuAction_DisplayItem | MenuAction_VoteCancel); + + int iArraySize = GetArraySize(g_StageList); + for (int i = 0; i < iArraySize; i++) + { + char sBuffer[128]; + GetArrayString(g_StageList, i, sBuffer, sizeof(sBuffer)); + + for (int j = 0; j < NUMBEROFSTAGES; j++) + { + if (strcmp(sBuffer, g_sStageName[j]) == 0) + { + if (g_bOnCooldown[j]) + AddMenuItem(g_VoteMenu, sBuffer, sBuffer, ITEMDRAW_DISABLED); + else + AddMenuItem(g_VoteMenu, sBuffer, sBuffer); + } + } + } + + SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE); + SetMenuTitle(g_VoteMenu, "What stage to play next?"); + SetVoteResultCallback(g_VoteMenu, Handler_SettingsVoteFinished); + VoteMenuToAll(g_VoteMenu, 20); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int Handler_MakoVoteMenu(Handle menu, MenuAction action, int param1, int param2) +{ + switch(action) + { + case MenuAction_End: + { + delete menu; + + if (param1 != -1) + { + g_bVoteFinished = true; + float fDelay = 3.0; + CS_TerminateRound(fDelay, CSRoundEnd_GameStart, false); + } + } + } + return 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int MenuHandler_NotifyPanel(Menu hMenu, MenuAction iAction, int iParam1, int iParam2) +{ + switch (iAction) + { + case MenuAction_Select, MenuAction_Cancel: + delete hMenu; + } +} + +public void Handler_SettingsVoteFinished(Handle menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) +{ + int highest_votes = item_info[0][VOTEINFO_ITEM_VOTES]; + int required_percent = 60; + int required_votes = RoundToCeil(float(num_votes) * float(required_percent) / 100); + + if ((highest_votes < required_votes) && (!g_bIsRevote)) + { + CPrintToChatAll("{green}[MakoVote] {default}A revote is needed!"); + char sFirst[128]; + char sSecond[128]; + GetMenuItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], sFirst, sizeof(sFirst)); + GetMenuItem(menu, item_info[1][VOTEINFO_ITEM_INDEX], sSecond, sizeof(sSecond)); + ClearArray(g_StageList); + PushArrayString(g_StageList, sFirst); + PushArrayString(g_StageList, sSecond); + g_bIsRevote = true; + g_CountdownTimer = CreateTimer(1.0, StartVote, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + + return; + } + + // No revote needed, continue as normal. + g_bIsRevote = false; + Handler_VoteFinishedGeneric(menu, num_votes, num_clients, client_info, num_items, item_info); +} + +public void Handler_VoteFinishedGeneric(Handle menu, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) +{ + g_bVoteFinished = true; + char sWinner[128]; + GetMenuItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], sWinner, sizeof(sWinner)); + float fPercentage = float(item_info[0][VOTEINFO_ITEM_VOTES] * 100) / float(num_votes); + + CPrintToChatAll("{green}[MakoVote] {default}Vote Finished! Winner: {red}%s{default} with %d%% of %d votes!", sWinner, RoundToFloor(fPercentage), num_votes); + + for (int i = 0; i < NUMBEROFSTAGES; i++) + { + if (strcmp(sWinner, g_sStageName[i]) == 0) + g_Winnerstage = i; + } + + ServerCommand("sm_stage %d", (g_Winnerstage + 4)); + + float fDelay = 3.0; + CS_TerminateRound(fDelay, CSRoundEnd_GameStart, false); +} + +public int GetCurrentStage() +{ + int iLevelCounterEnt = FindEntityByTargetname(INVALID_ENT_REFERENCE, "LevelCounter", "math_counter"); + + int offset = FindDataMapInfo(iLevelCounterEnt, "m_OutValue"); + int iCounterVal = RoundFloat(GetEntDataFloat(iLevelCounterEnt, offset)); + + int iCurrentStage; + if (iCounterVal == 5) + iCurrentStage = 0; + else if (iCounterVal == 6) + iCurrentStage = 5; + else if (iCounterVal == 7) + iCurrentStage = 1; + else if (iCounterVal == 9) + iCurrentStage = 3; + else if (iCounterVal == 10) + iCurrentStage = 2; + else if (iCounterVal == 11) + iCurrentStage = 4; + else + iCurrentStage = 0; + + return iCurrentStage; +} + +public int FindEntityByTargetname(int entity, const char[] sTargetname, const char[] sClassname) +{ + if(sTargetname[0] == '#') // HammerID + { + int HammerID = StringToInt(sTargetname[1]); + + while((entity = FindEntityByClassname(entity, sClassname)) != INVALID_ENT_REFERENCE) + { + if(GetEntProp(entity, Prop_Data, "m_iHammerID") == HammerID) + return entity; + } + } + else // Targetname + { + int Wildcard = FindCharInString(sTargetname, '*'); + char sTargetnameBuf[64]; + + while((entity = FindEntityByClassname(entity, sClassname)) != INVALID_ENT_REFERENCE) + { + if(GetEntPropString(entity, Prop_Data, "m_iName", sTargetnameBuf, sizeof(sTargetnameBuf)) <= 0) + continue; + + if(strncmp(sTargetnameBuf, sTargetname, Wildcard) == 0) + return entity; + } + } + return INVALID_ENT_REFERENCE; +} + +public Action Command_RaceBhop(int client, int args) +{ + g_bRaceAutoBhop = !g_bRaceAutoBhop; + + if(g_bRaceAutoBhop) + { + ServerCommand("sm plugins unload togsjumpstats"); + ServerCommand("sm plugins reload adminmenu"); + ServerCommand("sv_autobunnyhopping 1"); + ServerCommand("sv_airaccelerate 150"); + } + else + { + ServerCommand("sm plugins load togsjumpstats"); + ServerCommand("sm plugins reload adminmenu"); + ServerCommand("sv_autobunnyhopping 0"); + ServerCommand("sv_airaccelerate 12"); + } + + return Plugin_Handled; +} + +public Action Command_CancelRace(int client, int args) +{ + if (g_bRaceAutoBhop) + { + ServerCommand("sm plugins load togsjumpstats"); + ServerCommand("sm plugins reload adminmenu"); + ServerCommand("sv_autobunnyhopping 0"); + ServerCommand("sv_airaccelerate 12"); + } + g_bRaceEnabled = false; + g_bRaceAutoBhop = false; + g_bRaceBlockInfect = false; + g_bRaceBlockRespawn = false; + + return Plugin_Handled; +} + +public Action Command_StartRace(int client, int args) +{ + g_bRaceEnabled = true; + g_bRaceBlockInfect = true; + g_bRaceBlockRespawn = true; + + return Plugin_Handled; +} + +public Action Command_EndRace(int client, int args) +{ + g_bRaceBlockInfect = false; + + char sTargetname[128]; + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && IsPlayerAlive(i)) + { + GetEntPropString(i, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + if(!StrEqual(sTargetname, "player_racewinner", false)) + { + ZR_InfectClient(i); + } + } + } + + CreateTimer(5.0, SlayAll); + + int entity; + + while((entity = FindEntityByClassname(entity, "*")) != -1) + { + if(IsValidEntity(entity)) + { + GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + + if(StrEqual(sTargetname, "RaceRelayEnd", false)) + AcceptEntityInput(entity, "CancelPending"); + } + } + + return Plugin_Handled; +} + +public Action:ZR_OnClientInfect(&client, &attacker, &bool:motherInfect, &bool:respawnOverride, &bool:respawn) +{ + if(g_bRaceEnabled && g_bRaceBlockInfect) + return Plugin_Handled; + + return Plugin_Continue; +} + +public Action:ZR_OnClientRespawn(&client, &ZR_RespawnCondition:condition) +{ + if(g_bRaceEnabled && g_bRaceBlockRespawn) + return Plugin_Handled; + + return Plugin_Continue; +} + +public Action SlayAll(Handle timer) +{ + for(int i = 1; i <= MaxClients; i++) + if(IsClientInGame(i) && IsPlayerAlive(i)) + ForcePlayerSuicide(i); +} + +public void LoadContent() +{ + AddFileToDownloadsTable("models/player/uuz/sephiroth/sephiroth.mdl"); + AddFileToDownloadsTable("models/player/uuz/sephiroth/sephiroth.phy"); + AddFileToDownloadsTable("models/player/uuz/sephiroth/sephiroth.vvd"); + AddFileToDownloadsTable("models/player/uuz/sephiroth/sephiroth.dx90.vtx"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part1.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part1.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part2.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part2.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part3.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part3.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part4.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part4.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part5.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part5.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part6.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part6.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part7.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part7.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part8.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part8.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part9.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part9.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part10.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part10.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part11.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part11.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part12.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part12.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part13.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part13.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part14.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part14.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part15.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part15.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part16.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part16.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part17.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part17.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part18.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part18.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part19.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part19.vtf"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part20.vmt"); + AddFileToDownloadsTable("materials/models/player/uuz/sephiroth/part20.vtf"); + AddFileToDownloadsTable("sound/hl1/fvox/beep.wav"); + AddFileToDownloadsTable("sound/zombieden/custommusic/advent2.mp3"); + AddFileToDownloadsTable("sound/zombieden/custommusic/m2fix.mp3"); + AddFileToDownloadsTable("sound/zombieden/custommusic/m3fix.mp3"); + AddFileToDownloadsTable("sound/zombieden/custommusic/m4fix.mp3"); + AddFileToDownloadsTable("sound/zombieden/custommusic/m5fix.mp3"); + AddFileToDownloadsTable("sound/zombieden/custommusic/m6.mp3"); + AddFileToDownloadsTable("sound/music/unloze/Pendulum_Witchcraft.mp3"); + PrecacheSound("#zombieden/custommusic/advent2.mp3", true); + PrecacheSound("#zombieden/custommusic/m2fix.mp3", true); + PrecacheSound("#zombieden/custommusic/m3fix.mp3", true); + PrecacheSound("#zombieden/custommusic/m4fix.mp3", true); + PrecacheSound("#zombieden/custommusic/m5fix.mp3", true); + PrecacheSound("#zombieden/custommusic/m6.mp3", true); + PrecacheSound("#music/unloze/Pendulum_Witchcraft.mp3", true); +} \ No newline at end of file diff --git a/MapMusic/scripting/MapMusic_interface.sp b/MapMusic/scripting/MapMusic_interface.sp new file mode 100644 index 0000000..1b2c620 --- /dev/null +++ b/MapMusic/scripting/MapMusic_interface.sp @@ -0,0 +1,392 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +bool g_bBlockAll = false; +bool g_bDisabled[MAXPLAYERS+1] = false; +float g_fSoundVolume[MAXPLAYERS+1] = {1.0, ...}; +float g_fMusicVolume[MAXPLAYERS+1] = {1.0, ...}; + +Handle hOnStartSoundForward = INVALID_HANDLE; + +Basic g_aSounds; + + +//---------------------------------------------------------------------------------------------------- +// Purpose: MyInfo +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = { + name = "Map Music Control Interface", + author = "Mitch & SHUFEN from POSSESSION.tokyo & New Methods by PerfectLaugh from POSSESSION.tokyo & Testing by CrazyKid", + description = "Allows clients to adjust ambient sounds played by the map", + version = "4.0", + url = "http://www.sourcemod.net/" +}; + + +//---------------------------------------------------------------------------------------------------- +// Purpose: API +//---------------------------------------------------------------------------------------------------- +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + RegPluginLibrary("mapmusic_interface"); + + CreateNative("MapMusicInterface_GetSoundVolume", Native_GetSoundVolume); + CreateNative("MapMusicInterface_GetMusicVolume", Native_GetMusicVolume); + CreateNative("MapMusicInterface_SetSoundVolume", Native_SetSoundVolume); + CreateNative("MapMusicInterface_SetMusicVolume", Native_SetMusicVolume); + + CreateNative("MapMusicInterface_IsDisabled", Native_IsDisabled); + CreateNative("MapMusicInterface_SetDisabled", Native_SetDisabled); + + CreateNative("MapMusicInterface_GetBlockAll", Native_GetBlockAll); + CreateNative("MapMusicInterface_SetBlockAll", Native_SetBlockAll); + + CreateNative("MapMusicInterface_HasExceedingLengthSound", Native_HasExceedingLengthSound); + + return APLRes_Success; +} + +public any Native_GetSoundVolume(Handle myself, int numParams) { + return GetSoundVolume(GetNativeCell(1)); +} + +public any Native_GetMusicVolume(Handle myself, int numParams) { + return GetMusicVolume(GetNativeCell(1)); +} + +public any Native_SetSoundVolume(Handle myself, int numParams) { + SetSoundVolume(GetNativeCell(1), GetNativeCell(2)); +} + +public any Native_SetMusicVolume(Handle myself, int numParams) { + SetMusicVolume(GetNativeCell(1), GetNativeCell(2)); +} + +public any Native_IsDisabled(Handle myself, int numParams) { + return IsClientDisabled(GetNativeCell(1)); +} + +public any Native_SetDisabled(Handle myself, int numParams) { + SetClientDisabled(GetNativeCell(1), GetNativeCell(2)); +} + +public any Native_GetBlockAll(Handle myself, int numParams) +{ + return g_bBlockAll; +} + +public any Native_SetBlockAll(Handle myself, int numParams) +{ + g_bBlockAll = GetNativeCell(1); +} + +public any Native_HasExceedingLengthSound(Handle myself, int numParams) { + return HasExceedingLengthSound(GetNativeCell(1)); +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: General +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + hOnStartSoundForward = CreateGlobalForward("MapMusicInterface_OnStartSound", ET_Hook, Param_String, Param_Float, Param_CellByRef, Param_CellByRef); + + g_aSounds = new Basic(); + for (int client = 1; client <= MaxClients; client++) { + g_bDisabled[client] = false; + g_fSoundVolume[client] = 1.0; + g_fMusicVolume[client] = 1.0; + } +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: Clients +//---------------------------------------------------------------------------------------------------- +public void OnClientConnected(int client) { + g_bDisabled[client] = false; + g_fSoundVolume[client] = 1.0; + g_fMusicVolume[client] = 1.0; +} + +public void OnClientDisconnect_Post(int client) { + g_bDisabled[client] = false; + g_fSoundVolume[client] = 1.0; + g_fMusicVolume[client] = 1.0; +} + +CSound GetCSound(int entity, int soundnum, bool is_ambient) +{ + char key[64]; + FormatEx(key, sizeof(key), "%x_%x_%d", entity, soundnum, is_ambient? 1 : 0); + CSound soundmap = view_as(g_aSounds.GetHandle(key)); + if (soundmap == null) { + soundmap = new CSound(); + g_aSounds.SetHandle(key, soundmap); + } + + return soundmap; +} + +CSound GetCSoundFromEntity(int entity) +{ + char prefix[64]; + FormatEx(prefix, sizeof(prefix), "%x_", entity); + + CSound soundmap = null; + StringMapSnapshot snap = g_aSounds.Snapshot(); + char elem_key[64]; + for (int i = 0; i < snap.Length; i++) { + snap.GetKey(i, elem_key, sizeof(elem_key)); + if (strncmp(prefix, elem_key, strlen(prefix)) == 0) { + soundmap = view_as(g_aSounds.GetHandle(elem_key)); + break; + } + } + + delete snap; + return soundmap; +} + +#define EMIT_SND_NOFLAGS 0 +#define EMIT_SND_CHANGE_VOL (1 << 0) +#define EMIT_SND_STOP (1 << 2) +#define EMIT_SND_STOP_LOOPING (1 << 5) + +//---------------------------------------------------------------------------------------------------- +// Purpose: HookSoundMsg +//---------------------------------------------------------------------------------------------------- +public Action OnSoundEmittingToClient(int client, int origin[3], int& volume, float& delay, int& seq, int& entity, int& channel, int& pitch, int& flags, int& soundnum, int& soundnum_handle, int& speaker_entity, int& sound_level, bool is_sentence, bool is_ambient) { + if (is_sentence || soundnum == -1) { + return Plugin_Continue; + } + + CSound soundmap = GetCSound(entity, soundnum, is_ambient); + + if (flags & EMIT_SND_STOP || flags & EMIT_SND_STOP_LOOPING) { + volume = 0; + } else if (volume == -1) { + volume = 100; + } + + if (soundmap.iPassTickCount != GetGameTickCount()) { + char sample[256]; + char soundfile[256]; + ReadStringTable(FindStringTable("soundprecache"), soundnum, sample, sizeof(sample)); + if (sample[0] == '*' || sample[0] == '#') + strcopy(soundfile, sizeof(soundfile), sample[1]); + else + strcopy(soundfile, sizeof(soundfile), sample); + + if (soundmap.fLength <= 0.0) { + soundmap.fLength = GetSoundLengthFloat(soundfile); + } + + if (flags == EMIT_SND_NOFLAGS || + ((flags & EMIT_SND_CHANGE_VOL) && soundmap.fEndTime > 0.0 && soundmap.fEndTime <= GetTickedTime() && volume > 0)) { + bool isMusic = false; + Action res = APIOnStartSoundForward(soundfile, soundmap.fLength, isMusic, volume); + if (res != Plugin_Continue) { + return Plugin_Handled; + } + + if (isMusic && g_bBlockAll) { + return Plugin_Handled; + } + + soundmap.fEndTime = GetTickedTime() + soundmap.fLength; + soundmap.bIsMusic = isMusic; + } + else if (flags & EMIT_SND_STOP || flags & EMIT_SND_STOP_LOOPING) { + soundmap.fEndTime = GetTickedTime(); + } + // Volumes should be binded on entity, rather than sound file path. + // This is the reason of why sounds were bugged on ze_deadcore. + // Airvulpes uses multiple entities with same sound file to play with huge volume. + soundmap.iVolume = volume; + } + + if ((flags & EMIT_SND_CHANGE_VOL) && soundmap.iPassTickCount == GetGameTickCount()) { + if (soundmap.iPassClient != client) { + // When called AcceptEntityInput(entity, "Volume"); via void UpdateAllGenericSound(int client), + // that should be ignored because it was called even though needless except caller client. + return Plugin_Handled; + } + } + + volume = GetAdjustedVolumeOfClient(client, volume, soundmap.bIsMusic); + return Plugin_Changed; +} + +int GetAdjustedVolumeOfClient(int client, int sourcevol, bool isMusic) { + if (isMusic && IsClientDisabled(client)) { + return 0; + } + + float fvolume; + if (isMusic) { + fvolume = GetMusicVolume(client); + } + else { + fvolume = GetSoundVolume(client); + } + + if (fvolume <= 0.0) { + return 0; + } + return RoundToCeil(float(sourcevol) * fvolume); +} + +Action APIOnStartSoundForward(const char[] sample, float length, bool& isMusic, int& volume) { + Call_StartForward(hOnStartSoundForward); + Call_PushString(sample); + Call_PushFloat(length); + Call_PushCellRef(isMusic); + Call_PushCellRef(volume); + + Action res = Plugin_Continue; + Call_Finish(res); + return res; +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: Internal +//---------------------------------------------------------------------------------------------------- +float GetSoundVolume(int client) +{ + return g_fSoundVolume[client]; +} + +float GetMusicVolume(int client) +{ + return g_fMusicVolume[client]; +} + +void SetSoundVolume(int client, float volume) { + if (volume < 0.0) { + volume = 0.0; + } + else if (volume > 1.0) { + volume = 1.0; + } + + g_fSoundVolume[client] = volume; + + if (IsClientInGame(client)) { + UpdateAllGenericSound(client); + } +} + +void SetMusicVolume(int client, float volume) { + SetClientDisabled(client, false); + + if (volume < 0.0) { + volume = 0.0; + } + else if (volume > 1.0) { + volume = 1.0; + } + + g_fMusicVolume[client] = volume; + + if (IsClientInGame(client)) { + UpdateAllGenericSound(client); + } +} + +bool IsClientDisabled(int client) { + return g_bDisabled[client]; +} + +void SetClientDisabled(int client, bool disabled) { + g_bDisabled[client] = disabled; + + if (IsClientInGame(client)) { + UpdateAllGenericSound(client); + } +} + +bool HasExceedingLengthSound(float searchlength) { + int entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, "ambient_generic")) != INVALID_ENT_REFERENCE) { + char soundfile[256]; + GetEntPropString(entity, Prop_Data, "m_iszSound", soundfile, sizeof(soundfile)); + if (soundfile[0] == '*' || soundfile[0] == '#') + strcopy(soundfile, sizeof(soundfile), soundfile[1]); + + float length = GetSoundLengthFloat(soundfile); + if (searchlength > 0.0 && length >= searchlength) { + return true; + } + } + + return false; +} + + +float GetSoundLengthFloat(const char[] soundFile) { + char path[PLATFORM_MAX_PATH]; + FormatEx(path, sizeof(path), "sound/%s", soundFile); + + if (!FileExists(path, true)) { + return 0.0; + } + + SoundFile hSoundFile = new SoundFile(path, true); + if (hSoundFile == null) + return 0.0; + + float result = float(hSoundFile.LengthInMilliseconds) / 1000.0; + delete hSoundFile; + return result; +} + +public void OnClientDisconnect(int client) +{ + OnEntityDestroyed(client); +} + +public void OnEntityDestroyed(int entity) { + char prefix[32]; + FormatEx(prefix, sizeof(prefix), "%x_", entity); + + StringMapSnapshot snap = g_aSounds.Snapshot(); + char elem_key[32]; + for (int i = 0; i < snap.Length; i++) { + snap.GetKey(i, elem_key, sizeof(elem_key)); + if (strncmp(prefix, elem_key, strlen(prefix)) == 0) { + delete g_aSounds.GetHandle(elem_key); + g_aSounds.Remove(elem_key); + } + } + delete snap; +} + +void UpdateAllGenericSound(int client) { + int entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, "ambient_generic")) != INVALID_ENT_REFERENCE) { + CSound soundmap = GetCSoundFromEntity(entity); + if (soundmap == null) { + continue; + } + + soundmap.iPassTickCount = GetGameTickCount(); + soundmap.iPassClient = client; + + SetVariantInt(soundmap.iVolume / 10); + AcceptEntityInput(entity, "Volume"); + } +} diff --git a/MapMusic/scripting/MapMusic_new.sp b/MapMusic/scripting/MapMusic_new.sp new file mode 100644 index 0000000..f4ba647 --- /dev/null +++ b/MapMusic/scripting/MapMusic_new.sp @@ -0,0 +1,272 @@ +#include +#include + +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +//---------------------------------------------------------------------------------------------------- +// Purpose: MyInfo +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = { + name = "Map Music Control", + author = "Mitch & SHUFEN from POSSESSION.tokyo & Multi lang by Yuna & New Methods by PerfectLaugh from POSSESSION.tokyo & Testing by CrazyKid", + description = "Allows clients to adjust ambient sounds played by the map", + version = "4.0", + url = "http://www.sourcemod.net/ + https://possession.tokyo" +}; + +Handle cDisableSounds; +Handle cSoundVolume; + +ConVar sm_mapmusic_length; + +bool bLateLoad; + +//---------------------------------------------------------------------------------------------------- +// Purpose: General +//---------------------------------------------------------------------------------------------------- +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + bLateLoad = late; + return APLRes_Success; +} + +public void OnPluginStart() { + RegConsoleCmd("sm_music", Command_Music, "Brings up the music menu"); + RegConsoleCmd("sm_mapmusic", Command_Music, "Brings up the music menu"); + RegConsoleCmd("sm_stopmusic", Command_StopMusic, "Toggles map music"); + RegConsoleCmd("sm_startmusic", Command_StartMusic, "Start map music"); + RegConsoleCmd("sm_playmusic", Command_StartMusic, "Start map music"); + + sm_mapmusic_length = CreateConVar("sm_mapmusic_length", "10.0", "How long required length for it will be music files.", _, true, 0.0); + + LoadTranslations("mapmusic.phrases"); + + cDisableSounds = RegClientCookie("mapmusic_disable", "Disable Map Music", CookieAccess_Private); + cSoundVolume = RegClientCookie("mapmusic_volume_int", "Map Music Volume", CookieAccess_Private); + + SetCookieMenuItem(PrefMenu, 0, "Map Music"); + + if (bLateLoad) { + for (int i = 1; i <= MaxClients; i++) { + if (AreClientCookiesCached(i)) + OnClientCookiesCached(i); + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Clients +//---------------------------------------------------------------------------------------------------- +public void OnClientCookiesCached(int client) { + char sValue[8]; + + sValue[0] = '\0'; + GetClientCookie(client, cSoundVolume, sValue, sizeof(sValue)); + if (sValue[0] == '\0') { + SetClientCookie(client, cSoundVolume, "100"); + strcopy(sValue, sizeof(sValue), "100"); + } + MapMusicInterface_SetMusicVolume(client, StringToInt(sValue) / 100.0); + + sValue[0] = '\0'; + GetClientCookie(client, cDisableSounds, sValue, sizeof(sValue)); + if (sValue[0] == '\0') { + SetClientCookie(client, cDisableSounds, "0"); + strcopy(sValue, sizeof(sValue), "0"); + } + MapMusicInterface_SetDisabled(client, view_as(StringToInt(sValue))); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Commands +//---------------------------------------------------------------------------------------------------- +public Action Command_Music(int client, int args) { + if (client < 1 || client > MaxClients) return Plugin_Handled; + + if (GetCmdArgs() < 1) { + DisplaySettingsMenu(client); + return Plugin_Handled; + } + + char sArguments[256]; + GetCmdArg(1, sArguments, sizeof(sArguments)); + + if (StrContains(sArguments, "off", false) > -1 || StrContains(sArguments, "disable", false) > -1 || StrContains(sArguments, "stop", false) > -1 || (strncmp(sArguments, "0", 1) == 0 && sArguments[1] == '\0')) { + SetStatus(client, true); + return Plugin_Handled; + } else if (StrContains(sArguments, "on", false) > -1 || StrContains(sArguments, "enable", false) > -1 || StrContains(sArguments, "play", false) > -1) { + SetStatus(client, false); + return Plugin_Handled; + } + + SetVolume(client, StringToInt(sArguments)); + + return Plugin_Handled; +} + +public Action Command_StopMusic(int client, int args) { + if (client < 1 || client > MaxClients) return Plugin_Handled; + + if (GetStatus(client)) { + SetStatus(client, false); + return Plugin_Handled; + } + + SetStatus(client, true); + + return Plugin_Handled; +} + +public Action Command_StartMusic(int client, int args) { + if (client < 1 || client > MaxClients) return Plugin_Handled; + + SetStatus(client, false); + + return Plugin_Handled; +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: Menu +//---------------------------------------------------------------------------------------------------- +public void PrefMenu(int client, CookieMenuAction actions, any info, char[] buffer, int maxlen){ + if (actions == CookieMenuAction_DisplayOption) { + Format(buffer, maxlen, "%T", "Cookie_Menu", client); + } + + if (actions == CookieMenuAction_SelectOption) { + DisplaySettingsMenu(client); + } +} + +void DisplaySettingsMenu(int client) { + Menu prefmenu = CreateMenu(PrefMenuHandler, MENU_ACTIONS_DEFAULT); + + char szMenuTitle[64]; + Format(szMenuTitle, sizeof(szMenuTitle), "%T", "Menu_Title", client); + prefmenu.SetTitle(szMenuTitle); + + char szEnable[128]; + Format(szEnable, sizeof(szEnable), "%T\n \n%T", "Menu_Music", client, GetStatus(client) ? "Disabled" : "Enabled", client, "Menu_AdjustDesc", client); + prefmenu.AddItem(GetStatus(client) ? "enable" : "disable", szEnable); + + char szItem[32]; + int iVolume = GetVolume(client); + Format(szItem, sizeof(szItem), "%T", "Menu_Vol", client, iVolume); + switch (iVolume) { + case 100: { + prefmenu.AddItem("vol_80", szItem); + } + case 80: { + prefmenu.AddItem("vol_60", szItem); + } + case 60: { + prefmenu.AddItem("vol_40", szItem); + } + case 40: { + prefmenu.AddItem("vol_20", szItem); + } + case 20: { + prefmenu.AddItem("vol_15", szItem); + } + case 15: { + prefmenu.AddItem("vol_10", szItem); + } + case 10: { + prefmenu.AddItem("vol_5", szItem); + } + case 5: { + prefmenu.AddItem("vol_100", szItem); + } + default: { + prefmenu.AddItem("vol_100", szItem); + } + } + + prefmenu.ExitBackButton = true; + + prefmenu.Display(client, MENU_TIME_FOREVER); +} + +public int PrefMenuHandler(Menu prefmenu, MenuAction actions, int client, int item){ + if (actions == MenuAction_Select) { + char preference[8]; + GetMenuItem(prefmenu, item, preference, sizeof(preference)); + + if (StrEqual(preference, "disable")) { + SetStatus(client, true); + } else if (StrEqual(preference, "enable")) { + SetStatus(client, false); + } + + if (strncmp(preference, "vol_", 4) == 0) { + SetVolume(client, StringToInt(preference[4])); + } + + DisplaySettingsMenu(client); + } + else if (actions == MenuAction_Cancel) { + if (item == MenuCancel_ExitBack) { + ShowCookieMenu(client); + } + } + else if (actions == MenuAction_End) { + delete prefmenu; + } +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: Internal +//---------------------------------------------------------------------------------------------------- +int GetVolume(int client) { + return RoundToFloor(MapMusicInterface_GetMusicVolume(client) * 100.0); +} + +void SetVolume(int client, int volume) { + if (!IsClientInGame(client)) { + return; + } + + if (volume < 0) { + volume = 0; + } + else if (volume > 100) { + volume = 100; + } + + MapMusicInterface_SetMusicVolume(client, volume / 100.0); + + char sValue[8]; + IntToString(volume, sValue, sizeof(sValue)); + SetClientCookie(client, cSoundVolume, sValue); + + CPrintToChat(client, "\x04[MapMusic] \x01%t", "Text_MapMusicVolume", volume); +} + +bool GetStatus(int client) { + return MapMusicInterface_IsDisabled(client); +} + +void SetStatus(int client, bool bBlockMapMusic) { + if (!IsClientInGame(client)) { + return; + } + MapMusicInterface_SetDisabled(client, bBlockMapMusic); + + char sValue[8]; + IntToString(view_as(bBlockMapMusic), sValue, sizeof(sValue)); + SetClientCookie(client, cDisableSounds, sValue); + + CPrintToChat(client, "\x04[MapMusic] \x01%t", bBlockMapMusic ? "Text_MapMusicDisable" : "Text_MapMusicEnable"); +} + +public Action MapMusicInterface_OnStartSound(const char[] sample, float length, bool& isMusic, int& volume) +{ + if (length >= sm_mapmusic_length.FloatValue) { + isMusic = true; + } +} diff --git a/NavGenerateBlocker/gamedata/NavGenerateBlocker.games.txt b/NavGenerateBlocker/gamedata/NavGenerateBlocker.games.txt new file mode 100644 index 0000000..5447a56 --- /dev/null +++ b/NavGenerateBlocker/gamedata/NavGenerateBlocker.games.txt @@ -0,0 +1,21 @@ +"Games" +{ + "csgo" + { + "Signatures" + { + "CNavMesh::BeginGeneration" // #STR: "nav_generate" + { + "library" "server" + "windows" "\x55\x8B\xEC\x51\x8B\x0D\x2A\x2A\x2A\x2A\x56\x8B\x35\x2A\x2A\x2A\x2A\x6A\x00" + "linux" "\x55\x89\xE5\x56\x53\x83\xEC\x20\xA1\x2A\x2A\x2A\x2A\x8B\x5D\x08\x0F\xB6\x75\x0C" + } + "CNavMesh::Load" // #STR: "Error reading sub-version number.\n" + { + "library" "server" + "windows" "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\x74\x01\x00\x00\x53\x56\x8B\x35\x2A\x2A\x2A\x2A" + "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\xAC\x01\x00\x00\x8B\x3D\x2A\x2A\x2A\x2A" + } + } + } +} \ No newline at end of file diff --git a/NavGenerateBlocker/scripting/NavGenerateBlocker.sp b/NavGenerateBlocker/scripting/NavGenerateBlocker.sp new file mode 100644 index 0000000..ee94d7a --- /dev/null +++ b/NavGenerateBlocker/scripting/NavGenerateBlocker.sp @@ -0,0 +1,155 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include + +#define NAV_CANT_ACCESS_FILE 0 + +Handle hLoad; +Handle hBeginGeneration; + +bool g_bEnabledAutoNavGenerate = false; + +public Plugin myinfo = { + name = "Nav Generate Blocker", + author = "SHUFEN from POSSESSION.tokyo", + description = "", + version = "0.1", + url = "https://possession.tokyo" +}; + +public void OnPluginStart() { + Handle hGameData = LoadGameConfigFile("NavGenerateBlocker.games"); + if (hGameData == null) { + SetFailState("Couldn't find NavGenerateBlocker.games gamedata."); + } + + hLoad = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Int, ThisPointer_Address); + if (!hLoad) { + delete hGameData; + SetFailState("Failed to setup detour for CNavMesh::Load."); + } + if (!DHookSetFromConf(hLoad, hGameData, SDKConf_Signature, "CNavMesh::Load")) { + delete hGameData; + SetFailState("Failed to load CNavMesh::Load signature from gamedata."); + } + if (!DHookEnableDetour(hLoad, false, Detour_OnLoad)) { + delete hGameData; + SetFailState("Failed to detour CNavMesh::Load."); + } + + hBeginGeneration = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_Address); + if (!hBeginGeneration) { + delete hGameData; + SetFailState("Failed to setup detour for CNavMesh::BeginGeneration."); + } + if (!DHookSetFromConf(hBeginGeneration, hGameData, SDKConf_Signature, "CNavMesh::BeginGeneration")) { + delete hGameData; + SetFailState("Failed to load CNavMesh::BeginGeneration signature from gamedata."); + } + DHookAddParam(hBeginGeneration, HookParamType_Bool); + if (!DHookEnableDetour(hBeginGeneration, false, Detour_OnBeginGeneration)) { + delete hGameData; + SetFailState("Failed to detour CNavMesh::BeginGeneration."); + } + delete hGameData; + + RegAdminCmd("sm_navgenerate", Command_NavGenerate, ADMFLAG_ROOT, "Generate a blank .nav file for current map"); + RegAdminCmd("sm_navgeneratenextmap", Command_NavGenerateNextMap, ADMFLAG_ROOT, "Generate a blank .nav file for next map"); + + ConVar hConVar; + + (hConVar = CreateConVar("sm_navgenerate_autogenerate", "1", "Enable/Disable auto generate a blank .nav file for next map", _, true, 0.0, true, 1.0)).AddChangeHook(OnAutoGenerateChanged); + g_bEnabledAutoNavGenerate = hConVar.BoolValue; + + delete hConVar; +} + +public void OnAutoGenerateChanged(ConVar cvar, const char[] oldVal, const char[] newVal) { + g_bEnabledAutoNavGenerate = cvar.BoolValue; +} + +public void OnMapEnd() { + if (g_bEnabledAutoNavGenerate) { + char szNextMap[PLATFORM_MAX_PATH]; + if (GetNextMap(szNextMap, sizeof(szNextMap))) { + GenerateBlankNavFile(szNextMap); + } + } +} + +public Action OnLogAction(Handle source, Identity ident, int client, int target, const char[] message) { + if (g_bEnabledAutoNavGenerate) { + if (StrContains(message, "changed map to") != -1) { + char buffer[PLATFORM_MAX_PATH]; + strcopy(buffer, sizeof(buffer), message); + buffer[FindCharInString(buffer, '\"', true)] = '\0'; + GenerateBlankNavFile(buffer[FindCharInString(buffer, '\"', true) + 1]); + } + } +} + +public MRESReturn Detour_OnLoad(Address pThis, Handle hReturn, Handle hParams) { + char szMapName[PLATFORM_MAX_PATH], szNavFilePath[PLATFORM_MAX_PATH]; + GetCurrentMap(szMapName, sizeof(szMapName)); + FormatEx(szNavFilePath, sizeof(szNavFilePath), "maps/%s.nav", szMapName); + if (FileExists(szNavFilePath, false)) + return MRES_Ignored; + + LogMessage("NavGenerateBlocker -> Superseded \"CNavMesh::Load\" Function: return NAV_CANT_ACCESS_FILE;"); + DHookSetReturn(hReturn, NAV_CANT_ACCESS_FILE); + return MRES_Supercede; +} + +public MRESReturn Detour_OnBeginGeneration(Address pThis, Handle hReturn, Handle hParams) { + LogMessage("NavGenerateBlocker -> Skipped \"CNavMesh::BeginGeneration\" Function"); + return MRES_Supercede; +} + +public Action Command_NavGenerate(int client, int args) { + char szMapName[PLATFORM_MAX_PATH]; + GetCurrentMap(szMapName, sizeof(szMapName)); + + GenerateBlankNavFile(szMapName); + return Plugin_Handled; +} + +public Action Command_NavGenerateNextMap(int client, int args) { + char szNextMap[PLATFORM_MAX_PATH]; + if (GetNextMap(szNextMap, sizeof(szNextMap))) { + GenerateBlankNavFile(szNextMap); + } + return Plugin_Handled; +} + +int NAV_BLANKFILE_DATA[] = {206, 250, 237, 254, 16, 0, 0, 0, 1, 0, 0, 0, 14, 238, 118, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 168, 119, 198, 0, 160, 240, 68, 0, 254, 63, 196, 0, 204, 113, 198, 0, 0, 22, 69, 0, 254, 63, 196, 0, 254, 63, 196, 0, 254, 63, 196, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 118, 119, 198, 0, 48, 242, 68, 0, 254, 63, + 196, 8, 1, 0, 0, 0, 0, 254, 113, 198, 0, 48, 242, 68, 0, 254, 63, 196, 8, 2, 0, 0, 0, 0, 254, 113, 198, 0, 56, 21, 69, 0, + 254, 63, 196, 8, 3, 0, 0, 0, 0, 118, 119, 198, 0, 56, 21, 69, 0, 254, 63, 196, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 139, 187, 111, 61, 212, 233, 139, 62, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + +bool GenerateBlankNavFile(const char[] map) { + char szNavFilePath[PLATFORM_MAX_PATH]; + FormatEx(szNavFilePath, sizeof(szNavFilePath), "maps/%s.nav", map); + + if (FileExists(szNavFilePath, false)) { + LogMessage("File \"%s\" has already exist.", szNavFilePath); + return false; + } + + File hFile = OpenFile(szNavFilePath, "wb"); + if (hFile == null) { + LogMessage("Couldn't create file \"%s\" for writing.", szNavFilePath); + return false; + } + + for (int x = 0; x < sizeof(NAV_BLANKFILE_DATA); x++) + hFile.WriteInt8(NAV_BLANKFILE_DATA[x]); + + hFile.Close(); + return true; +} diff --git a/PlayerVisibility/scripting/PlayerVisibility.sp b/PlayerVisibility/scripting/PlayerVisibility.sp new file mode 100644 index 0000000..b2b4f9f --- /dev/null +++ b/PlayerVisibility/scripting/PlayerVisibility.sp @@ -0,0 +1,376 @@ +#include +#include +#include +#undef REQUIRE_PLUGIN +#include +#define REQUIRE_PLUGIN + +#pragma semicolon 1 +#pragma newdecls required + +public Plugin myinfo = +{ + name = "PlayerVisibility", + author = "BotoX", + description = "Fades players away when you get close to them.", + version = "1.2", + url = "" +}; + +// bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ) +Handle g_hAcceptInput; +Handle g_hThinkTimer; + +ConVar g_CVar_MaxDistance; +ConVar g_CVar_MinFactor; +ConVar g_CVar_MinAlpha; +ConVar g_CVar_MinPlayers; + +float g_fMaxDistance; +float g_fMinFactor; +float g_fMinAlpha; +int g_iMinPlayers; + +int g_Client_Alpha[MAXPLAYERS + 1] = {255, ...}; +bool g_Client_bEnabled[MAXPLAYERS + 1] = {false, ...}; + +bool g_Plugin_zombiereloaded = false; + +public void OnPluginStart() +{ + Handle hGameConf = LoadGameConfigFile("sdktools.games"); + if(hGameConf == INVALID_HANDLE) + { + SetFailState("Couldn't load sdktools game config!"); + return; + } + + int Offset = GameConfGetOffset(hGameConf, "AcceptInput"); + g_hAcceptInput = DHookCreate(Offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, AcceptInput); + DHookAddParam(g_hAcceptInput, HookParamType_CharPtr); + DHookAddParam(g_hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(g_hAcceptInput, HookParamType_CBaseEntity); + DHookAddParam(g_hAcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); //varaint_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20 + DHookAddParam(g_hAcceptInput, HookParamType_Int); + + CloseHandle(hGameConf); + + g_CVar_MaxDistance = CreateConVar("sm_pvis_maxdistance", "100.0", "Distance at which models stop fading.", 0, true, 0.0); + g_fMaxDistance = g_CVar_MaxDistance.FloatValue; + g_CVar_MaxDistance.AddChangeHook(OnConVarChanged); + + g_CVar_MinFactor = CreateConVar("sm_pvis_minfactor", "0.75", "Smallest allowed alpha factor per client.", 0, true, 0.0, true, 1.0); + g_fMinFactor = g_CVar_MinFactor.FloatValue; + g_CVar_MinFactor.AddChangeHook(OnConVarChanged); + + g_CVar_MinAlpha = CreateConVar("sm_pvis_minalpha", "75.0", "Minimum allowed alpha value.", 0, true, 0.0, true, 255.0); + g_fMinAlpha = g_CVar_MinAlpha.FloatValue; + g_CVar_MinAlpha.AddChangeHook(OnConVarChanged); + + g_CVar_MinPlayers = CreateConVar("sm_pvis_minplayers", "3.0", "Minimum players within distance to enable fading.", 0, true, 0.0, true, 255.0); + g_iMinPlayers = g_CVar_MinPlayers.IntValue; + g_CVar_MinPlayers.AddChangeHook(OnConVarChanged); + + AutoExecConfig(true, "plugin.PlayerVisibility"); + + HookEvent("player_spawn", Event_Spawn, EventHookMode_Post); + HookEvent("player_death", Event_Death, EventHookMode_Post); + + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + OnClientPutInServer(client); + } + + g_hThinkTimer = CreateTimer(0.2, Timer_Think, _, TIMER_REPEAT); +} + +public Action Timer_Think(Handle timer) +{ + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + DoClientThink(client); + } + return Plugin_Continue; +} + +public void OnAllPluginsLoaded() +{ + g_Plugin_zombiereloaded = LibraryExists("zombiereloaded"); +} + +public void OnLibraryAdded(const char[] sName) +{ + if(strcmp(sName, "zombiereloaded", false) == 0) + g_Plugin_zombiereloaded = true; +} + +public void OnLibraryRemoved(const char[] sName) +{ + if(strcmp(sName, "zombiereloaded", false) == 0) + g_Plugin_zombiereloaded = false; +} + +public void OnPluginEnd() +{ + KillTimer(g_hThinkTimer); + + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + { + if(g_Client_bEnabled[client] && g_Client_Alpha[client] != 255.0) + SetEntityRenderMode(client, RENDER_NORMAL); + } + } +} + +public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + if(convar == g_CVar_MaxDistance) + g_fMaxDistance = g_CVar_MaxDistance.FloatValue; + + else if(convar == g_CVar_MinFactor) + g_fMinFactor = g_CVar_MinFactor.FloatValue; + + else if(convar == g_CVar_MinAlpha) + g_fMinAlpha = g_CVar_MinAlpha.FloatValue; + + else if(convar == g_CVar_MinPlayers) + g_iMinPlayers = g_CVar_MinPlayers.IntValue; +} + +public void OnClientPutInServer(int client) +{ + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = true; + + DHookEntity(g_hAcceptInput, false, client); +} + +// bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID ) +public MRESReturn AcceptInput(int pThis, Handle hReturn, Handle hParams) +{ + // Should not happen? + if(DHookIsNullParam(hParams, 2)) + return MRES_Ignored; + + int client = EntRefToEntIndex(DHookGetParam(hParams, 2)); + if(client < 1 || client > MAXPLAYERS) + return MRES_Ignored; + + if(!g_Client_bEnabled[client]) + return MRES_Ignored; + + char szInputName[32]; + DHookGetParamString(hParams, 1, szInputName, sizeof(szInputName)); + + if(!StrEqual(szInputName, "addoutput", false)) + return MRES_Ignored; + + char sValue[128]; + DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, sValue, sizeof(sValue)); + int iValueLen = strlen(sValue); + + int aArgs[4] = {0, ...}; + int iArgs = 0; + bool bFound = false; + + for(int i = 0; i < iValueLen; i++) + { + if(sValue[i] == ' ') + { + if(bFound) + { + sValue[i] = '\0'; + bFound = false; + + if(iArgs >= sizeof(aArgs)) + break; + } + continue; + } + + if(!bFound) + { + aArgs[iArgs++] = i; + bFound = true; + } + } + + if(StrEqual(szInputName, "addoutput", false)) + { + if(StrEqual(sValue[aArgs[0]], "rendermode", false)) + { + RenderMode renderMode = view_as(StringToInt(sValue[aArgs[1]]) & 0xFF); + if(renderMode == RENDER_ENVIRONMENTAL) + { + ToolsSetEntityAlpha(client, 255); + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = false; + } + else + g_Client_bEnabled[client] = true; + } + else if(StrEqual(sValue[aArgs[0]], "renderfx", false)) + { + RenderFx renderFx = view_as(StringToInt(sValue[aArgs[1]]) & 0xFF); + if(renderFx != RENDERFX_NONE) + { + ToolsSetEntityAlpha(client, 255); + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = false; + } + else + g_Client_bEnabled[client] = true; + } + } + else if(StrEqual(szInputName, "alpha", false)) + { + int iAlpha = StringToInt(sValue[aArgs[0]]) & 0xFF; + if(iAlpha == 0) + { + ToolsSetEntityAlpha(client, 255); + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = false; + } + else + { + g_Client_bEnabled[client] = true; + return MRES_Supercede; + } + } + + return MRES_Ignored; +} + +public void Event_Spawn(Event event, const char[] name, bool dontBroadcast) +{ + int userid = GetEventInt(event, "userid"); + CreateTimer(0.1, Timer_SpawnPost, userid); +} + +public void Event_Death(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(GetEventInt(event, "userid")); + if(!client) + return; + + g_Client_bEnabled[client] = false; +} + +public Action Timer_SpawnPost(Handle timer, int userid) +{ + int client = GetClientOfUserId(userid); + if(!client) + return Plugin_Stop; + + if(!IsClientInGame(client) || !IsPlayerAlive(client)) + return Plugin_Stop; + + ToolsSetEntityAlpha(client, 255); + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = true; + return Plugin_Stop; +} + +public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn) +{ + ToolsSetEntityAlpha(client, 255); + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = false; +} + +public void ZR_OnClientHumanPost(int client, bool respawn, bool protect) +{ + ToolsSetEntityAlpha(client, 255); + g_Client_Alpha[client] = 255; + g_Client_bEnabled[client] = true; +} + +public void DoClientThink(int client) +{ + if(!g_Client_bEnabled[client]) + return; + + int PlayersInRange = 0; + float fAlpha = 255.0; + for(int i = 1; i <= MaxClients; i++) + { + if(i == client || !IsClientInGame(i) || !IsPlayerAlive(i)) + continue; + + if(g_Plugin_zombiereloaded && !ZR_IsClientHuman(i)) + continue; + + static float fVec1[3]; + static float fVec2[3]; + GetClientAbsOrigin(client, fVec1); + GetClientAbsOrigin(i, fVec2); + + float fDistance = GetVectorDistance(fVec1, fVec2, false); + if(fDistance <= g_fMaxDistance) + { + PlayersInRange++; + + float fFactor = fDistance / g_fMaxDistance; + if(fFactor < g_fMinFactor) + fFactor = g_fMinFactor; + + fAlpha *= fFactor; + } + } + + if(fAlpha < g_fMinAlpha) + fAlpha = g_fMinAlpha; + + if(PlayersInRange < g_iMinPlayers) + fAlpha = 255.0; + + int Alpha = RoundToNearest(fAlpha); + + if(Alpha == g_Client_Alpha[client]) + return; + + g_Client_Alpha[client] = Alpha; + ToolsSetEntityAlpha(client, Alpha); +} + +stock void ToolsSetEntityAlpha(int client, int Alpha) +{ + if(Alpha == 255) + { + SetEntityRenderMode(client, RENDER_NORMAL); + return; + } + + int aColor[4]; + ToolsGetEntityColor(client, aColor); + + SetEntityRenderMode(client, RENDER_TRANSCOLOR); + SetEntityRenderColor(client, aColor[0], aColor[1], aColor[2], Alpha); +} + +stock void ToolsGetEntityColor(int entity, int aColor[4]) +{ + static bool s_GotConfig = false; + static char s_sProp[32]; + + if(!s_GotConfig) + { + Handle GameConf = LoadGameConfigFile("core.games"); + bool Exists = GameConfGetKeyValue(GameConf, "m_clrRender", s_sProp, sizeof(s_sProp)); + CloseHandle(GameConf); + + if(!Exists) + strcopy(s_sProp, sizeof(s_sProp), "m_clrRender"); + + s_GotConfig = true; + } + + int Offset = GetEntSendPropOffs(entity, s_sProp); + + for(int i = 0; i < 4; i++) + aColor[i] = GetEntData(entity, Offset + i, 1) & 0xFF; +} diff --git a/RNGFix/gamedata/rngfix.games.txt b/RNGFix/gamedata/rngfix.games.txt new file mode 100644 index 0000000..34a962f --- /dev/null +++ b/RNGFix/gamedata/rngfix.games.txt @@ -0,0 +1,83 @@ +"Games" +{ + "#default" + { + "Keys" + { + "IGameMovement" "GameMovement001" + + "IServerGameEnts" "ServerGameEnts001" + } + + "Signatures" + { + "CreateInterface" + { + "library" "server" + "windows" "@CreateInterface" + "linux" "@CreateInterface" + } + } + + "Offsets" + { + "ProcessMovement" + { + "windows" "1" + "linux" "2" + } + } + } + + "csgo" + { + "Offsets" + { + // applies to trigger_vphysics_motion and trigger_wind + "CBaseVPhysicsTrigger::PassesTriggerFilters" + { + "windows" "199" + "linux" "200" + } + + // applies to all other triggers + "CBaseTrigger::PassesTriggerFilters" + { + "windows" "209" + "linux" "210" + } + + "IServerGameEnts::MarkEntitiesAsTouching" + { + "windows" "1" + "linux" "2" + } + } + } + + "cstrike" + { + "Offsets" + { + // applies to trigger_vphysics_motion and trigger_wind + "CBaseVPhysicsTrigger::PassesTriggerFilters" + { + "windows" "188" + "linux" "189" + } + + // applies to all other triggers + "CBaseTrigger::PassesTriggerFilters" + { + "windows" "197" + "linux" "198" + } + + "IServerGameEnts::MarkEntitiesAsTouching" + { + "windows" "2" + "linux" "3" + } + } + } +} diff --git a/RNGFix/scripting/rngfix.sp b/RNGFix/scripting/rngfix.sp new file mode 100644 index 0000000..7fe2412 --- /dev/null +++ b/RNGFix/scripting/rngfix.sp @@ -0,0 +1,1290 @@ +#include +#include + +#include + +#pragma semicolon 1 +#pragma newdecls required + +#define PLUGIN_VERSION "1.1.2" + +public Plugin myinfo = +{ + name = "RNGFix", + author = "rio", + description = "Fixes physics bugs in movement game modes", + version = PLUGIN_VERSION, + url = "https://github.com/jason-e/rngfix" +} + +// Engine constants, NOT settings (do not change) +#define LAND_HEIGHT 2.0 // Maximum height above ground at which you can "land" +#define NON_JUMP_VELOCITY 140.0 // Maximum Z velocity you are allowed to have and still land +#define MIN_STANDABLE_ZNRM 0.7 // Minimum surface normal Z component of a walkable surface +#define AIR_SPEED_CAP 30.0 // Constant used to limit air acceleration +#define DUCK_MIN_DUCKSPEED 1.5 // Minimum duckspeed to start ducking +#define DEFAULT_JUMP_IMPULSE 301.99337741 // sqrt(2 * 57.0 units * 800.0 u/s^2) + +float g_vecMins[3]; +float g_vecMaxsUnducked[3]; +float g_vecMaxsDucked[3]; +float g_flDuckDelta; + +int g_iTick[MAXPLAYERS+1]; +float g_flFrameTime[MAXPLAYERS+1]; + +bool g_bTouchingTrigger[MAXPLAYERS+1][2048]; + +int g_iButtons[MAXPLAYERS+1]; +float g_vVel[MAXPLAYERS+1][3]; +float g_vAngles[MAXPLAYERS+1][3]; +int g_iOldButtons[MAXPLAYERS+1]; + +int g_iLastTickPredicted[MAXPLAYERS+1]; + +float g_vPreCollisionVelocity[MAXPLAYERS+1][3]; +float g_vLastBaseVelocity[MAXPLAYERS+1][3]; +int g_iLastGroundEnt[MAXPLAYERS+1]; +int g_iLastLandTick[MAXPLAYERS+1]; +int g_iLastCollisionTick[MAXPLAYERS+1]; +int g_iLastMapTeleportTick[MAXPLAYERS+1]; +bool g_bMapTeleportedSequentialTicks[MAXPLAYERS+1]; +float g_vCollisionPoint[MAXPLAYERS+1][3]; +float g_vCollisionNormal[MAXPLAYERS+1][3]; + +enum +{ + UPHILL_LOSS = -1, // Force a jump, AND negatively affect speed as if a collision occurred (fix RNG not in player's favor) + UPHILL_DEFAULT = 0, // Do nothing (retain RNG) + UPHILL_NEUTRAL = 1 // Force a jump (respecting NON_JUMP_VELOCITY) (fix RNG in player's favor) +} + +// Plugin settings +ConVar g_cvDownhill; +ConVar g_cvUphill; +ConVar g_cvEdge; +ConVar g_cvTriggerjump; +ConVar g_cvTelehop; +ConVar g_cvStairs; +ConVar g_cvUseOldSlopefixLogic; +ConVar g_cvDebug; + +// Core physics ConVars +ConVar g_cvMaxVelocity; +ConVar g_cvGravity; +ConVar g_cvAirAccelerate; + +// In CSS and CSGO but apparently not used in CSS +ConVar g_cvTimeBetweenDucks; + +// CSGO-only +ConVar g_cvJumpImpulse; +ConVar g_cvAutoBunnyHopping; + +Handle g_hPassesTriggerFilters; +Handle g_hProcessMovementHookPre; +Address g_IServerGameEnts; +Handle g_hMarkEntitiesAsTouching; + +bool g_bIsSurfMap; + +bool g_bLateLoad; + +int g_iLaserIndex; +int g_color1[] = {0, 100, 255, 255}; +int g_color2[] = {0, 255, 0, 255}; + +void DebugMsg(int client, const char[] fmt, any ...) +{ + if (!g_cvDebug.BoolValue) return; + + char output[1024]; + VFormat(output, sizeof(output), fmt, 3); + PrintToConsole(client, "[%i] %s", g_iTick[client], output); +} + +void DebugLaser(int client, const float p1[3], const float p2[3], float life, float width, const int color[4]) +{ + if (g_cvDebug.IntValue < 2) return; + + TE_SetupBeamPoints(p2, p1, g_iLaserIndex, 0, 0, 0, life, width, width, 10, 0.0, color, 0); + TE_SendToClient(client); +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_bLateLoad = late; + return APLRes_Success; +} + +public void OnPluginStart() +{ + EngineVersion engine = GetEngineVersion(); + + if (engine != Engine_CSGO && engine != Engine_CSS) + { + SetFailState("Game is not supported"); + } + + g_vecMins = view_as({-16.0, -16.0, 0.0}); + g_vecMaxsUnducked = view_as({16.0, 16.0, 0.0}); + g_vecMaxsDucked = view_as({16.0, 16.0, 0.0}); + + switch (engine) + { + case Engine_CSGO: + { + g_vecMaxsUnducked[2] = 72.0; + g_vecMaxsDucked[2] = 64.0; + } + case Engine_CSS: + { + g_vecMaxsUnducked[2] = 62.0; + g_vecMaxsDucked[2] = 45.0; + } + } + + g_flDuckDelta = (g_vecMaxsUnducked[2]-g_vecMaxsDucked[2]) / 2; + + g_cvDownhill = CreateConVar("rngfix_downhill", "1", "Enable downhill incline fix.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_cvUphill = CreateConVar("rngfix_uphill", "1", "Enable uphill incline fix. Set to -1 to normalize effects not in the player's favor (not recommended).", FCVAR_NOTIFY, true, -1.0, true, 1.0); + g_cvEdge = CreateConVar("rngfix_edge", "1", "Enable edgebug fix.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_cvTriggerjump = CreateConVar("rngfix_triggerjump", "1", "Enable trigger jump fix.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_cvTelehop = CreateConVar("rngfix_telehop", "1", "Enable telehop fix.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + g_cvStairs = CreateConVar("rngfix_stairs", "1", "Enable stair slide fix (surf only). You must have Movement Unlocker for sliding to work on CSGO.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + + g_cvUseOldSlopefixLogic = CreateConVar("rngfix_useoldslopefixlogic", "0", "Old Slopefix had some logic errors that could cause double boosts. Enable this on a per-map basis to retain old behavior. (NOT RECOMMENDED)", FCVAR_NOTIFY, true, 0.0, true, 1.0); + + g_cvDebug = CreateConVar("rngfix_debug", "0", "1 = Enable debug messages. 2 = Enable debug messages and lasers.", _, true, 0.0, true, 2.0); + + AutoExecConfig(); + + g_cvMaxVelocity = FindConVar("sv_maxvelocity"); + g_cvGravity = FindConVar("sv_gravity"); + g_cvAirAccelerate = FindConVar("sv_airaccelerate"); + + if (g_cvMaxVelocity == null || g_cvGravity == null || g_cvAirAccelerate == null) + { + SetFailState("Could not find all ConVars"); + } + + // Not required + g_cvTimeBetweenDucks = FindConVar("sv_timebetweenducks"); + g_cvJumpImpulse = FindConVar("sv_jump_impulse"); + g_cvAutoBunnyHopping = FindConVar("sv_autobunnyhopping"); + + Handle gamedataConf = LoadGameConfigFile("rngfix.games"); + if (gamedataConf == null) SetFailState("Failed to load rngfix gamedata"); + + // PassesTriggerFilters + StartPrepSDKCall(SDKCall_Entity); + if (!PrepSDKCall_SetFromConf(gamedataConf, SDKConf_Virtual, "CBaseTrigger::PassesTriggerFilters")) + { + SetFailState("Failed to get CBaseTrigger::PassesTriggerFilters offset"); + } + PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); + g_hPassesTriggerFilters = EndPrepSDKCall(); + + if (g_hPassesTriggerFilters == null) SetFailState("Unable to prepare SDKCall for CBaseTrigger::PassesTriggerFilters"); + + // CreateInterface + // Thanks SlidyBat and ici + StartPrepSDKCall(SDKCall_Static); + if (!PrepSDKCall_SetFromConf(gamedataConf, SDKConf_Signature, "CreateInterface")) + { + SetFailState("Failed to get CreateInterface"); + } + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Pointer, VDECODE_FLAG_ALLOWNULL); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + Handle CreateInterface = EndPrepSDKCall(); + + if (CreateInterface == null) SetFailState("Unable to prepare SDKCall for CreateInterface"); + + char interfaceName[64]; + + // ProcessMovement + if (!GameConfGetKeyValue(gamedataConf, "IGameMovement", interfaceName, sizeof(interfaceName))) + { + SetFailState("Failed to get IGameMovement interface name"); + } + Address IGameMovement = SDKCall(CreateInterface, interfaceName, 0); + if (!IGameMovement) + { + SetFailState("Failed to get IGameMovement pointer"); + } + + int offset = GameConfGetOffset(gamedataConf, "ProcessMovement"); + if (offset == -1) SetFailState("Failed to get ProcessMovement offset"); + + g_hProcessMovementHookPre = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, DHook_ProcessMovementPre); + DHookAddParam(g_hProcessMovementHookPre, HookParamType_CBaseEntity); + DHookAddParam(g_hProcessMovementHookPre, HookParamType_ObjectPtr); + DHookRaw(g_hProcessMovementHookPre, false, IGameMovement); + + // MarkEntitiesAsTouching + if (!GameConfGetKeyValue(gamedataConf, "IServerGameEnts", interfaceName, sizeof(interfaceName))) + { + SetFailState("Failed to get IServerGameEnts interface name"); + } + g_IServerGameEnts = SDKCall(CreateInterface, interfaceName, 0); + if (!g_IServerGameEnts) + { + SetFailState("Failed to get IServerGameEnts pointer"); + } + + StartPrepSDKCall(SDKCall_Raw); + if (!PrepSDKCall_SetFromConf(gamedataConf, SDKConf_Virtual, "IServerGameEnts::MarkEntitiesAsTouching")) + { + SetFailState("Failed to get IServerGameEnts::MarkEntitiesAsTouching offset"); + } + PrepSDKCall_AddParameter(SDKType_Edict, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_Edict, SDKPass_Pointer); + g_hMarkEntitiesAsTouching = EndPrepSDKCall(); + + if (g_hMarkEntitiesAsTouching == null) SetFailState("Unable to prepare SDKCall for IServerGameEnts::MarkEntitiesAsTouching"); + + delete CreateInterface; + delete gamedataConf; + + if (g_bLateLoad) + { + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client)) OnClientPutInServer(client); + } + + char classname[64]; + for (int entity = MaxClients+1; entity < sizeof(g_bTouchingTrigger[]); entity++) + { + if (!IsValidEntity(entity)) continue; + GetEntPropString(entity, Prop_Data, "m_iClassname", classname, sizeof(classname)); + HookTrigger(entity, classname); + } + } +} + +public void OnMapStart() +{ + g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt", true); + + char map[PLATFORM_MAX_PATH]; + GetCurrentMap(map, sizeof(map)); + g_bIsSurfMap = StrContains(map, "surf_", false) == 0; +} + +public void OnEntityCreated(int entity, const char[] classname) +{ + if (entity >= sizeof(g_bTouchingTrigger[])) return; + HookTrigger(entity, classname); +} + +void HookTrigger(int entity, const char[] classname) +{ + if (StrContains(classname, "trigger_") != -1) + { + SDKHook(entity, SDKHook_StartTouchPost, Hook_TriggerStartTouch); + SDKHook(entity, SDKHook_EndTouchPost, Hook_TriggerEndTouch); + } + + if (StrContains(classname, "trigger_teleport") != -1) + { + SDKHook(entity, SDKHook_TouchPost, Hook_TriggerTeleportTouchPost); + } +} + +public void OnClientConnected(int client) +{ + g_iTick[client] = 0; + for (int i = 0; i < sizeof(g_bTouchingTrigger[]); i++) g_bTouchingTrigger[client][i] = false; +} + +public void OnClientPutInServer(int client) +{ + SDKHook(client, SDKHook_GroundEntChangedPost, Hook_PlayerGroundEntChanged); + SDKHook(client, SDKHook_PostThink, Hook_PlayerPostThink); +} + +public Action Hook_TriggerStartTouch(int entity, int other) +{ + if (1 <= other <= MaxClients) + { + g_bTouchingTrigger[other][entity] = true; + DebugMsg(other, "StartTouch %i", entity); + } + + return Plugin_Continue; +} + +// TODO Would be nice to have IServerTools::FindEntityByName / CGlobalEntityList::FindEntityByName +bool NameExists(const char[] targetname) +{ + // Assume special types exist + if (targetname[0] == '!') return true; + + char targetname2[128]; + + int max = GetMaxEntities(); + for (int entity = 1; entity < max; entity++) + { + if (!IsValidEntity(entity)) continue; + if (GetEntPropString(entity, Prop_Data, "m_iName", targetname2, sizeof(targetname2)) == 0) continue; + + if (StrEqual(targetname, targetname2)) return true; + } + + return false; +} + +public void Hook_TriggerTeleportTouchPost(int entity, int other) +{ + if (!(1 <= other <= MaxClients)) return; + + if (!SDKCall(g_hPassesTriggerFilters, entity, other)) return; + + char targetstring[128]; + if (GetEntPropString(entity, Prop_Data, "m_target", targetstring, sizeof(targetstring)) == 0) return; + + if (!NameExists(targetstring)) return; + + if (g_iLastMapTeleportTick[other] == g_iTick[other]-1) + { + g_bMapTeleportedSequentialTicks[other] = true; + } + + g_iLastMapTeleportTick[other] = g_iTick[other]; + + DebugMsg(other, "Triggered teleport %i", entity); +} + +public Action Hook_TriggerEndTouch(int entity, int other) +{ + if (1 <= other <= MaxClients) + { + g_bTouchingTrigger[other][entity] = false; + DebugMsg(other, "EndTouch %i", entity); + } + return Plugin_Continue; +} + +public bool PlayerFilter(int entity, int mask) +{ + return !(1 <= entity <= MaxClients); +} + +float GetJumpImpulse() +{ + if (g_cvJumpImpulse != null) + { + return g_cvJumpImpulse.FloatValue; + } + else + { + return DEFAULT_JUMP_IMPULSE; + } +} + +bool IsDuckCoolingDown(int client) +{ + // TODO Is this stuff in MoveData? + + // Ducking is prevented if the last switch to a ducked state from an unducked state is sooner than sv_timebetweenducks ago. + // Note: This cooldown is based on client's curtime (GetGameTime() in this context) and thus is unaffected by m_flLaggedMovementValue. + if (g_cvTimeBetweenDucks != null && HasEntProp(client, Prop_Data, "m_flLastDuckTime")) + { + if (GetGameTime() - GetEntPropFloat(client, Prop_Data, "m_flLastDuckTime") < g_cvTimeBetweenDucks.FloatValue) return true; + } + + // m_flDuckSpeed is decreased by 2.0 to a minimum of 0.0 every time the duck key is pressed OR released. + // It recovers at a rate of 3.0 * m_flLaggedMovementValue per second and caps at 8.0. + // Switching to a ducked state from an unducked state is prevented if it is less than 1.5. + if (HasEntProp(client, Prop_Data, "m_flDuckSpeed")) + { + if (GetEntPropFloat(client, Prop_Data, "m_flDuckSpeed") < DUCK_MIN_DUCKSPEED) return true; + } + + return false; +} + +void Duck(int client, float origin[3], float mins[3], float maxs[3]) +{ + bool ducking = GetEntityFlags(client) & FL_DUCKING != 0; + + bool nextDucking = ducking; + + if (g_iButtons[client] & IN_DUCK != 0 && !ducking) + { + if (!IsDuckCoolingDown(client)) + { + origin[2] += g_flDuckDelta; + nextDucking = true; + } + } + else if (g_iButtons[client] & IN_DUCK == 0 && ducking) + { + origin[2] -= g_flDuckDelta; + + TR_TraceHullFilter(origin, origin, g_vecMins, g_vecMaxsUnducked, MASK_PLAYERSOLID, PlayerFilter); + + // Cannot unduck in air, not enough room + if (TR_DidHit()) origin[2] += g_flDuckDelta; + else nextDucking = false; + } + + mins = g_vecMins; + maxs = nextDucking ? g_vecMaxsDucked : g_vecMaxsUnducked; +} + +bool CanJump(int client) +{ + if (g_iButtons[client] & IN_JUMP == 0) return false; + if (g_iOldButtons[client] & IN_JUMP != 0 && !(g_cvAutoBunnyHopping != null && g_cvAutoBunnyHopping.BoolValue)) return false; + + return true; +} + +void CheckJumpButton(int client, float velocity[3]) +{ + // Skip dead and water checks since we already did them. + + // We need to check for ground somewhere so stick it here. + if (GetEntityFlags(client) & FL_ONGROUND == 0) return; + + if (!CanJump(client)) return; + + // TODO Incorporate surfacedata jump factor + + // This conditional is why jumping while crouched jumps higher! Bad! + if (GetEntProp(client, Prop_Data, "m_bDucking") != 0 || GetEntityFlags(client) & FL_DUCKING != 0) + { + velocity[2] = GetJumpImpulse(); + } + else + { + velocity[2] += GetJumpImpulse(); + } + + // Jumping does an extra half tick of gravity! Bad! + FinishGravity(client, velocity); +} + +void AirAccelerate(int client, float velocity[3], Handle hParams) +{ + // This also includes the initial parts of AirMove() + + float fore[3], side[3]; + float wishvel[3], wishdir[3]; + + GetAngleVectors(g_vAngles[client], fore, side, NULL_VECTOR); + + fore[2] = 0.0; + side[2] = 0.0; + NormalizeVector(fore, fore); + NormalizeVector(side, side); + + for (int i = 0; i < 2; i++) wishvel[i] = fore[i] * g_vVel[client][0] + side[i] * g_vVel[client][1]; + + float wishspeed = NormalizeVector(wishvel, wishdir); + float m_flMaxSpeed = DHookGetParamObjectPtrVar(hParams, 2, 56, ObjectValueType_Float); + if (wishspeed > m_flMaxSpeed && m_flMaxSpeed != 0.0) wishspeed = m_flMaxSpeed; + + if (wishspeed) + { + float wishspd = wishspeed; + if (wishspd > AIR_SPEED_CAP) wishspd = AIR_SPEED_CAP; + + float currentspeed = GetVectorDotProduct(velocity, wishdir); + float addspeed = wishspd - currentspeed; + + if (addspeed > 0) + { + float accelspeed = g_cvAirAccelerate.FloatValue * wishspeed * g_flFrameTime[client]; + if (accelspeed > addspeed) accelspeed = addspeed; + + for (int i = 0; i < 2; i++) velocity[i] += accelspeed * wishdir[i]; + } + } +} + +void CheckVelocity(float velocity[3]) +{ + for (int i = 0; i < 3; i++) + { + if (velocity[i] > g_cvMaxVelocity.FloatValue) velocity[i] = g_cvMaxVelocity.FloatValue; + else if (velocity[i] < -g_cvMaxVelocity.FloatValue) velocity[i] = -g_cvMaxVelocity.FloatValue; + } +} + +void StartGravity(int client, float velocity[3]) +{ + float localGravity = GetEntPropFloat(client, Prop_Data, "m_flGravity"); + if (localGravity == 0.0) localGravity = 1.0; + + velocity[2] -= localGravity * g_cvGravity.FloatValue * 0.5 * g_flFrameTime[client]; + + float baseVelocity[3]; + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity); + velocity[2] += baseVelocity[2] * g_flFrameTime[client]; + + // baseVelocity[2] would get cleared here but we shouldn't do that since this is just a prediction. + + CheckVelocity(velocity); +} + +void FinishGravity(int client, float velocity[3]) +{ + float localGravity = GetEntPropFloat(client, Prop_Data, "m_flGravity"); + if (localGravity == 0.0) localGravity = 1.0; + + velocity[2] -= localGravity * g_cvGravity.FloatValue * 0.5 * g_flFrameTime[client]; + + CheckVelocity(velocity); +} + +bool CheckWater(int client) +{ + // The cached water level is updated multiple times per tick, including after movement happens, + // so we can just check the cached value here. + return GetEntProp(client, Prop_Data, "m_nWaterLevel") > 1; +} + +void PreventCollision(int client, Handle hParams, const float origin[3], const float collisionPoint[3], const float velocity_tick[3]) +{ + DebugLaser(client, origin, collisionPoint, 15.0, 0.5, g_color1); + + // Rewind part of a tick so at the end of this tick we will end up close to the ground without colliding with it. + // This effectively simulates a mid-tick jump (we lose part of a tick but its a miniscule trade-off). + // This is also only an approximation of a partial tick rewind but it's good enough. + float newOrigin[3]; + SubtractVectors(collisionPoint, velocity_tick, newOrigin); + + // Add a little space between us and the ground so we don't accidentally hit it anyway, maybe due to floating point error or something. + // I don't know if this is necessary but I would rather be safe. + newOrigin[2] += 0.1; + + // Since the MoveData for this tick has already been filled and is about to be used, we need + // to modify it directly instead of changing the player entity's actual position (such as with TeleportEntity). + DHookSetParamObjectPtrVarVector(hParams, 2, GetEngineVersion() == Engine_CSGO ? 172 : 152, ObjectValueType_Vector, newOrigin); + + DebugLaser(client, origin, newOrigin, 15.0, 0.5, g_color2); + + float adjustment[3]; + SubtractVectors(newOrigin, origin, adjustment); + DebugMsg(client, "Moved: %.2f %.2f %.2f", adjustment[0], adjustment[1], adjustment[2]); + + // No longer colliding this tick, clear our prediction flag + g_iLastCollisionTick[client] = 0; +} + +void ClipVelocity(const float velocity[3], const float nrm[3], float out[3]) +{ + float backoff = GetVectorDotProduct(velocity, nrm); + + for (int i = 0; i < 3; i++) + { + out[i] = velocity[i] - nrm[i]*backoff; + } + + // The adjust step only matters with overbounce which doesnt apply to walkable surfaces. +} + +void SetVelocity(int client, float velocity[3], bool dontUseTeleportEntity = false) +{ + // Pull out basevelocity from desired true velocity + // Use the pre-tick basevelocity because that is what influenced this tick's movement and the desired new velocity. + SubtractVectors(velocity, g_vLastBaseVelocity[client], velocity); + + if (dontUseTeleportEntity && GetEntPropEnt(client, Prop_Data, "m_hMoveParent") == -1) + { + SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", velocity); + SetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity); + } + else + { + float baseVelocity[3]; + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity); + + TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, velocity); + + // TeleportEntity with non-null velocity wipes out basevelocity, so restore it after. + // Since we didn't change position, nothing should change regarding influences on basevelocity. + SetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity); + } +} + +public MRESReturn DHook_ProcessMovementPre(Handle hParams) +{ + int client = DHookGetParam(hParams, 1); + + g_iTick[client]++; + g_flFrameTime[client] = GetTickInterval() * GetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue"); + g_bMapTeleportedSequentialTicks[client] = false; + + // If we are actually not doing ANY of the fixes that rely on pre-tick collision prediction, skip all this. + if (!g_cvUphill.BoolValue && !g_cvEdge.BoolValue && !g_cvStairs.BoolValue && !g_cvTelehop.BoolValue && !g_cvDownhill.BoolValue) + { + return MRES_Ignored; + } + + RunPreTickChecks(client, hParams); + + return MRES_Ignored; +} + +void RunPreTickChecks(int client, Handle hParams) +{ + // Recreate enough of CGameMovement::ProcessMovement to predict if fixes are needed. + // We only really care about a limited set of scenarios (less than waist-deep in water, MOVETYPE_WALK, air movement). + + if (!IsPlayerAlive(client)) return; + if (GetEntityMoveType(client) != MOVETYPE_WALK) return; + if (CheckWater(client)) return; + + g_iLastGroundEnt[client] = GetEntPropEnt(client, Prop_Data, "m_hGroundEntity"); + + // If we are definitely staying on the ground this tick, don't predict it. + if (g_iLastGroundEnt[client] != -1 && !CanJump(client)) return; + + g_iLastTickPredicted[client] = g_iTick[client]; + + g_iButtons[client] = DHookGetParamObjectPtrVar(hParams, 2, 36, ObjectValueType_Int); + g_iOldButtons[client] = DHookGetParamObjectPtrVar(hParams, 2, 40, ObjectValueType_Int); + DHookGetParamObjectPtrVarVector(hParams, 2, 44, ObjectValueType_Vector, g_vVel[client]); + DHookGetParamObjectPtrVarVector(hParams, 2, 12, ObjectValueType_Vector, g_vAngles[client]); + + float velocity[3]; + DHookGetParamObjectPtrVarVector(hParams, 2, 64, ObjectValueType_Vector, velocity); + + float baseVelocity[3]; + // basevelocity is not stored in MoveData + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity); + + float origin[3]; + DHookGetParamObjectPtrVarVector(hParams, 2, GetEngineVersion() == Engine_CSGO ? 172 : 152, ObjectValueType_Vector, origin); + + float nextOrigin[3], mins[3], maxs[3]; + + nextOrigin = origin; + + // These roughly replicate the behavior of their equivalent CGameMovement functions. + + Duck(client, nextOrigin, mins, maxs); + + StartGravity(client, velocity); + + CheckJumpButton(client, velocity); + + CheckVelocity(velocity); + + AirAccelerate(client, velocity, hParams); + + // StartGravity dealt with Z basevelocity. + baseVelocity[2] = 0.0; + g_vLastBaseVelocity[client] = baseVelocity; + AddVectors(velocity, baseVelocity, velocity); + + // Store this for later in case we need to undo the effects of a collision. + g_vPreCollisionVelocity[client] = velocity; + + // This is basically where TryPlayerMove happens. + // We don't really care about anything after TryPlayerMove either. + + float velocity_tick[3]; + velocity_tick = velocity; + ScaleVector(velocity_tick, g_flFrameTime[client]); + + AddVectors(nextOrigin, velocity_tick, nextOrigin); + + // Check if we will hit something this tick. + TR_TraceHullFilter(origin, nextOrigin, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + float nrm[3]; + TR_GetPlaneNormal(null, nrm); + + if (g_iLastCollisionTick[client] < g_iTick[client]-1) + { + DebugMsg(client, "Collision predicted! (normal: %.3f %.3f %.3f)", nrm[0], nrm[1], nrm[2]); + } + + float collisionPoint[3]; + TR_GetEndPosition(collisionPoint); + + // Store this result for post-tick fixes. + g_iLastCollisionTick[client] = g_iTick[client]; + g_vCollisionPoint[client] = collisionPoint; + g_vCollisionNormal[client] = nrm; + + // If we are moving up too fast, we can't land anyway so these fixes aren't needed. + if (velocity[2] > NON_JUMP_VELOCITY) return; + + // Landing also requires a walkable surface. + // This will give false negatives if the surface initially collided + // is too steep but the final one isn't (rare and unlikely to matter). + if (nrm[2] < MIN_STANDABLE_ZNRM) return; + + // Check uphill incline fix first since it's more common and faster. + if (g_cvUphill.IntValue == UPHILL_NEUTRAL) + { + // Make sure it's not flat, and that we are actually going uphill (X/Y dot product < 0.0) + if (nrm[2] < 1.0 && nrm[0]*velocity[0] + nrm[1]*velocity[1] < 0.0) + { + bool shouldDoDownhillFixInstead = false; + + if (g_cvDownhill.BoolValue) + { + // We also want to make sure this isn't a case where it's actually more beneficial to do the downhill fix. + float newVelocity[3]; + ClipVelocity(velocity, nrm, newVelocity); + + if (newVelocity[0]*newVelocity[0] + newVelocity[1]*newVelocity[1] > velocity[0]*velocity[0] + velocity[1]*velocity[1]) + { + shouldDoDownhillFixInstead = true; + } + } + + if (!shouldDoDownhillFixInstead) + { + DebugMsg(client, "DO FIX: Uphill Incline"); + PreventCollision(client, hParams, origin, collisionPoint, velocity_tick); + + // This naturally prevents any edge bugs so we can skip the edge fix. + return; + } + } + } + + if (g_cvEdge.BoolValue) + { + // Do a rough estimate of where we will be at the end of the tick after colliding. + // This method assumes no more collisions will take place after the first. + // There are some very extreme circumstances where this will give false positives (unlikely to come into play). + + float tickEnd[3]; + float fraction_left = 1.0 - TR_GetFraction(); + + if (nrm[2] == 1.0) + { + // If the ground is level, all that changes is Z velocity becomes zero. + tickEnd[0] = collisionPoint[0] + velocity_tick[0]*fraction_left; + tickEnd[1] = collisionPoint[1] + velocity_tick[1]*fraction_left; + tickEnd[2] = collisionPoint[2]; + } + else + { + float velocity2[3]; + ClipVelocity(velocity, nrm, velocity2); + + if (velocity2[2] > NON_JUMP_VELOCITY) + { + // This would be an "edge bug" (slide without landing at the end of the tick) + // 100% of the time due to the Z velocity restriction. + return; + } + else + { + ScaleVector(velocity2, g_flFrameTime[client]*fraction_left); + AddVectors(collisionPoint, velocity2, tickEnd); + } + } + + // Check if there's something close enough to land on below the player at the end of this tick. + float tickEndBelow[3]; + tickEndBelow[0] = tickEnd[0]; + tickEndBelow[1] = tickEnd[1]; + tickEndBelow[2] = tickEnd[2] - LAND_HEIGHT; + + TR_TraceHullFilter(tickEnd, tickEndBelow, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + // There's something there, can we land on it? + float nrm2[3]; + TR_GetPlaneNormal(null, nrm2); + + // Yes, it's not too steep. + if (nrm2[2] >= MIN_STANDABLE_ZNRM) return; + // Yes, the quadrant check finds ground that isn't too steep. + if (TracePlayerBBoxForGround(tickEnd, tickEndBelow, mins, maxs)) return; + } + + DebugMsg(client, "DO FIX: Edge Bug"); + DebugLaser(client, collisionPoint, tickEnd, 15.0, 0.5, g_color1); + + PreventCollision(client, hParams, origin, collisionPoint, velocity_tick); + } + } +} + +public void Hook_PlayerGroundEntChanged(int client) +{ + // We cannot get the new ground entity at this point, + // but if the previous value was -1, it must be something else now, so we landed. + if (GetEntPropEnt(client, Prop_Data, "m_hGroundEntity") == -1) + { + g_iLastLandTick[client] = g_iTick[client]; + DebugMsg(client, "Landed"); + } +} + +bool DoTriggerjumpFix(int client, const float landingPoint[3], const float landingMins[3], const float landingMaxs[3]) +{ + if (!g_cvTriggerjump.BoolValue) return false; + + // It's possible to land above a trigger but also in another trigger_teleport, have the teleport move you to + // another location, and then the trigger jumping fix wouldn't fire the other trigger you technically landed above, + // but I can't imagine a mapper would ever actually stack triggers like that. + + float origin[3]; + GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin); + + float landingMaxsBelow[3]; + landingMaxsBelow[0] = landingMaxs[0]; + landingMaxsBelow[1] = landingMaxs[1]; + landingMaxsBelow[2] = origin[2] - landingPoint[2]; + + ArrayList triggers = new ArrayList(); + + // Find triggers that are between us and the ground (using the bounding box quadrant we landed with if applicable). + TR_EnumerateEntitiesHull(landingPoint, landingPoint, landingMins, landingMaxsBelow, true, AddTrigger, triggers); + + bool didSomething = false; + + for (int i = 0; i < triggers.Length; i++) + { + int trigger = triggers.Get(i); + + // MarkEntitiesAsTouching always fires the Touch function even if it was already fired this tick. + // In case that could cause side-effects, manually keep track of triggers we are actually touching + // and don't re-touch them. + if (g_bTouchingTrigger[client][trigger]) continue; + + DebugMsg(client, "DO FIX: Trigger Jumping (entity %i)", trigger); + + SDKCall(g_hMarkEntitiesAsTouching, g_IServerGameEnts, client, trigger); + didSomething = true; + } + + delete triggers; + + return didSomething; +} + +bool DoStairsFix(int client) +{ + if (!g_cvStairs.BoolValue) return false; + if (g_iLastTickPredicted[client] != g_iTick[client]) return false; + + // This fix has undesirable side-effects on bhop. It is also very unlikely to help on bhop. + if (!g_bIsSurfMap) return false; + + // Let teleports take precedence (including teleports activated by the trigger jumping fix). + if (g_iLastMapTeleportTick[client] == g_iTick[client]) return false; + + // If moving upward, the player would never be able to slide up with any current position. + if (g_vPreCollisionVelocity[client][2] > 0.0) return false; + + // Stair step faces don't necessarily have to be completely vertical, but, if they are not, + // sliding up them at high speed -- or even just walking up -- usually doesn't work. + // Plus, it's really unlikely that there are actual stairs shaped like that. + if (g_iLastCollisionTick[client] == g_iTick[client] && g_vCollisionNormal[client][2] == 0.0) + { + // Do this first and stop if we are moving slowly (less than 1 unit per tick). + float velocity_dir[3]; + velocity_dir = g_vPreCollisionVelocity[client]; + velocity_dir[2] = 0.0; + if (NormalizeVector(velocity_dir, velocity_dir) * g_flFrameTime[client] < 1.0) return false; + + float mins[3], maxs[3]; + GetEntPropVector(client, Prop_Data, "m_vecMins", mins); + GetEntPropVector(client, Prop_Data, "m_vecMaxs", maxs); + + // We seem to have collided with a "wall", now figure out if it's a stair step. + + // Look for ground below us + float stepsize = GetEntPropFloat(client, Prop_Data, "m_flStepSize"); + + float end[3]; + end = g_vCollisionPoint[client]; + end[2] -= stepsize; + + TR_TraceHullFilter(g_vCollisionPoint[client], end, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + float nrm[3]; + TR_GetPlaneNormal(null, nrm); + + // Ground below is not walkable, not stairs + if (nrm[2] < MIN_STANDABLE_ZNRM) return false; + + float start[3]; + TR_GetEndPosition(start); + + // Find triggers that we would trigger if we did touch the ground here. + ArrayList triggers = new ArrayList(); + + TR_EnumerateEntitiesHull(start, start, mins, maxs, true, AddTrigger, triggers); + + for (int i = 0; i < triggers.Length; i++) + { + int trigger = triggers.Get(i); + + if (SDKCall(g_hPassesTriggerFilters, trigger, client)) + { + // We would have triggered something on the ground here, so we cant be sure the stairs fix is safe to do. + // The most likely scenario here is this isn't stairs, but just a short ledge with a fail teleport in front. + delete triggers; + return false; + } + } + + delete triggers; + + // Now follow CGameMovement::StepMove behavior. + + // Trace up + end = start; + end[2] += stepsize; + TR_TraceHullFilter(start, end, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) TR_GetEndPosition(end); + + // Trace over (only 1 unit, just to find a stair step) + start = end; + AddVectors(start, velocity_dir, end); + + TR_TraceHullFilter(start, end, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + // The plane we collided with is too tall to be a stair step (i.e. it's a wall, not stairs). + // Or possibly: the ceiling is too low to get on top of it. + return false; + } + else + { + // Trace downward + start = end; + end[2] -= stepsize; + + TR_TraceHullFilter(start, end, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (!TR_DidHit()) return false; // Shouldn't happen + + TR_GetPlaneNormal(null, nrm); + + // Ground atop "stair" is not walkable, not stairs + if (nrm[2] < MIN_STANDABLE_ZNRM) return false; + + // It looks like we actually collided with a stair step. + // Put the player just barely on top of the stair step we found and restore their speed + TR_GetEndPosition(end); + + DebugMsg(client, "DO FIX: Stair Sliding"); + + TeleportEntity(client, end, NULL_VECTOR, NULL_VECTOR); + SetVelocity(client, g_vPreCollisionVelocity[client]); + + return true; + } + } + } + + return false; +} + +bool DoInclineCollisionFixes(int client, const float nrm[3]) +{ + if (!g_cvDownhill.BoolValue && g_cvUphill.IntValue != UPHILL_LOSS) return false; + if (g_iLastTickPredicted[client] != g_iTick[client]) return false; + + // There's no point in checking for fix if we were moving up, unless we want to do an uphill collision + if (g_vPreCollisionVelocity[client][2] > 0.0 && g_cvUphill.IntValue != UPHILL_LOSS) return false; + + // If a collision was predicted this tick (and wasn't prevented by another fix alrady), no fix is needed. + // It's possible we actually have to run the edge bug fix and an incline fix in the same tick. + // If using the old Slopefix logic, do the fix regardless of necessity just like Slopefix + // so we can be sure to trigger a double boost if applicable. + if (g_iLastCollisionTick[client] == g_iTick[client] && !g_cvUseOldSlopefixLogic.BoolValue) return false; + + // Make sure the ground is not level, otherwise a collision would do nothing important anyway. + if (nrm[2] == 1.0) return false; + + // This velocity includes changes from player input this tick as well as + // the half tick of gravity applied before collision would occur. + float velocity[3]; + velocity = g_vPreCollisionVelocity[client]; + + if (g_cvUseOldSlopefixLogic.BoolValue) + { + // The old slopefix did not consider basevelocity when calculating deflected velocity + SubtractVectors(velocity, g_vLastBaseVelocity[client], velocity); + } + + float dot = nrm[0]*velocity[0] + nrm[1]*velocity[1]; + + if (dot >= 0) + { + // If going downhill, only adjust velocity if the downhill incline fix is on. + if (!g_cvDownhill.BoolValue) return false; + } + + bool downhillFixIsBeneficial = false; + + float newVelocity[3]; + ClipVelocity(velocity, nrm, newVelocity); + + if (newVelocity[0]*newVelocity[0] + newVelocity[1]*newVelocity[1] > velocity[0]*velocity[0] + velocity[1]*velocity[1]) + { + downhillFixIsBeneficial = true; + } + + if (dot < 0) + { + // If going uphill, only adjust velocity if uphill incline fix is set to loss mode + // OR if this is actually a case where the downhill incline fix is better. + if (!((downhillFixIsBeneficial && g_cvDownhill.BoolValue) || g_cvUphill.IntValue == UPHILL_LOSS)) return false; + } + + DebugMsg(client, "DO FIX: Incline Collision (%s) (z-normal: %.3f)", downhillFixIsBeneficial ? "Downhill" : "Uphill", nrm[2]); + + // Make sure Z velocity is zero since we are on the ground. + newVelocity[2] = 0.0; + + // Since we are on the ground, we also don't need to FinishGravity(). + + if (g_cvUseOldSlopefixLogic.BoolValue) + { + // The old slopefix immediately moves basevelocity into local velocity to keep it from getting cleared. + // This results in double boosts as the player is likely still being influenced by the source of the basevelocity. + if (GetEntityFlags(client) & FL_BASEVELOCITY != 0) + { + float baseVelocity[3]; + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity); + AddVectors(newVelocity, baseVelocity, newVelocity); + } + + TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, newVelocity); + } + else + { + SetVelocity(client, newVelocity); + } + + return true; +} + +bool DoTelehopFix(int client) +{ + if (!g_cvTelehop.BoolValue) return false; + if (g_iLastTickPredicted[client] != g_iTick[client]) return false; + + if (g_iLastMapTeleportTick[client] != g_iTick[client]) return false; + + // If the player was teleported two ticks in a row, don't do this fix because the player likely just passed + // through a speed-stopping teleport hub, and the map really did want to stop the player this way. + if (g_bMapTeleportedSequentialTicks[client]) return false; + + // Check if we either collided this tick OR landed during this tick. + // Note that we could have landed this tick, lost Z velocity, then gotten teleported, making us no longer on the ground. + // This is why we need to remember if we landed mid-tick rather than just check ground state now. + if (!(g_iLastCollisionTick[client] == g_iTick[client] || g_iLastLandTick[client] == g_iTick[client])) return false; + + // At this point, ideally we should check if the teleport would have triggered "after" the collision (within the tick duration), + // and, if so, not restore speed, but properly doing that would involve completely duplicating TryPlayerMove but with + // multiple intermediate trigger checks which is probably a bad idea... better to just give people the benefit of the doubt sometimes. + + // Restore the velocity we would have had if we didn't collide or land. + float newVelocity[3]; + newVelocity = g_vPreCollisionVelocity[client]; + + // Don't forget to add the second half-tick of gravity ourselves. + FinishGravity(client, newVelocity); + + float origin[3]; + GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin); + + float mins[3], maxs[3]; + GetEntPropVector(client, Prop_Data, "m_vecMins", mins); + GetEntPropVector(client, Prop_Data, "m_vecMaxs", maxs); + + TR_TraceHullFilter(origin, origin, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + // If we appear to be "stuck" after teleporting (likely because the teleport destination + // was exactly on the ground), set velocity directly to avoid side-effects of + // TeleportEntity that can cause the player to really get stuck in the ground. + // This might only be an issue in CSS, but do it on CSGO too just to be safe. + bool dontUseTeleportEntity = TR_DidHit(); + + DebugMsg(client, "DO FIX: Telehop%s", dontUseTeleportEntity ? " (no TeleportEntity)" : ""); + + SetVelocity(client, newVelocity, dontUseTeleportEntity); + + return true; +} + +// PostThink works a little better than a ProcessMovement post hook because we need to wait for ProcessImpacts (trigger activation) +public void Hook_PlayerPostThink(int client) +{ + if (!IsPlayerAlive(client)) return; + if (GetEntityMoveType(client) != MOVETYPE_WALK) return; + if (CheckWater(client)) return; + + bool landed = GetEntPropEnt(client, Prop_Data, "m_hGroundEntity") != -1 && g_iLastGroundEnt[client] == -1; + + float origin[3], landingMins[3], landingMaxs[3], nrm[3], landingPoint[3]; + + // Get info about the ground we landed on (if we need to do landing fixes). + if (landed && (g_cvTriggerjump.BoolValue || g_cvDownhill.BoolValue || g_cvUphill.IntValue == UPHILL_LOSS)) + { + GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin); + + GetEntPropVector(client, Prop_Data, "m_vecMins", landingMins); + GetEntPropVector(client, Prop_Data, "m_vecMaxs", landingMaxs); + + float originBelow[3]; + originBelow[0] = origin[0]; + originBelow[1] = origin[1]; + originBelow[2] = origin[2] - LAND_HEIGHT; + + TR_TraceHullFilter(origin, originBelow, landingMins, landingMaxs, MASK_PLAYERSOLID, PlayerFilter); + + if (!TR_DidHit()) + { + // This should never happen, since we know we are on the ground. + landed = false; + } + else + { + TR_GetPlaneNormal(null, nrm); + + if (nrm[2] < MIN_STANDABLE_ZNRM) + { + // This is rare, and how the incline fix should behave isn't entirely clear because maybe we should + // collide with multiple faces at once in this case, but let's just get the ground we officially + // landed on and use that for our ground normal. + + // landingMins and landingMaxs will contain the final values used to find the ground after returning. + if (TracePlayerBBoxForGround(origin, originBelow, landingMins, landingMaxs)) + { + TR_GetPlaneNormal(null, nrm); + } + else + { + // This should also never happen. + landed = false; + } + + DebugMsg(client, "Used bounding box quadrant to find ground (z-normal: %.3f)", nrm[2]); + } + + TR_GetEndPosition(landingPoint); + } + } + + if (landed && TR_GetFraction() > 0.0) + { + DoTriggerjumpFix(client, landingPoint, landingMins, landingMaxs); + + // Check if a trigger we just touched put us in the air (probably due to a teleport). + if (GetEntityFlags(client) & FL_ONGROUND == 0) landed = false; + } + + // The stair sliding fix changes the outcome of this tick more significantly, so it doesn't really make sense to do incline fixes too. + if (DoStairsFix(client)) return; + + if (landed) + { + DoInclineCollisionFixes(client, nrm); + } + + DoTelehopFix(client); +} + +public bool AddTrigger(int entity, ArrayList triggers) +{ + TR_ClipCurrentRayToEntity(MASK_ALL, entity); + if (TR_DidHit()) triggers.Push(entity); + + return true; +} + +bool TracePlayerBBoxForGround(const float origin[3], const float originBelow[3], float mins[3], float maxs[3]) +{ + // See CGameMovement::TracePlayerBBoxForGround() + + float origMins[3], origMaxs[3]; + origMins = mins; + origMaxs = maxs; + + float nrm[3]; + + mins = origMins; + + // -x -y + maxs[0] = origMaxs[0] > 0.0 ? 0.0 : origMaxs[0]; + maxs[1] = origMaxs[1] > 0.0 ? 0.0 : origMaxs[1]; + maxs[2] = origMaxs[2]; + + TR_TraceHullFilter(origin, originBelow, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + TR_GetPlaneNormal(null, nrm); + if (nrm[2] >= MIN_STANDABLE_ZNRM) return true; + } + + // +x +y + mins[0] = origMins[0] < 0.0 ? 0.0 : origMins[0]; + mins[1] = origMins[1] < 0.0 ? 0.0 : origMins[1]; + mins[2] = origMins[2]; + + maxs = origMaxs; + + TR_TraceHullFilter(origin, originBelow, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + TR_GetPlaneNormal(null, nrm); + if (nrm[2] >= MIN_STANDABLE_ZNRM) return true; + } + + // -x +y + mins[0] = origMins[0]; + mins[1] = origMins[1] < 0.0 ? 0.0 : origMins[1]; + mins[2] = origMins[2]; + + maxs[0] = origMaxs[0] > 0.0 ? 0.0 : origMaxs[0]; + maxs[1] = origMaxs[1]; + maxs[2] = origMaxs[2]; + + TR_TraceHullFilter(origin, originBelow, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + TR_GetPlaneNormal(null, nrm); + if (nrm[2] >= MIN_STANDABLE_ZNRM) return true; + } + + // +x -y + mins[0] = origMins[0] < 0.0 ? 0.0 : origMins[0]; + mins[1] = origMins[1]; + mins[2] = origMins[2]; + + maxs[0] = origMaxs[0]; + maxs[1] = origMaxs[1] > 0.0 ? 0.0 : origMaxs[1]; + maxs[2] = origMaxs[2]; + + TR_TraceHullFilter(origin, originBelow, mins, maxs, MASK_PLAYERSOLID, PlayerFilter); + + if (TR_DidHit()) + { + TR_GetPlaneNormal(null, nrm); + if (nrm[2] >= MIN_STANDABLE_ZNRM) return true; + } + + return false; +} diff --git a/RepeatKiller/scripting/RepeatKiller.sp b/RepeatKiller/scripting/RepeatKiller.sp new file mode 100644 index 0000000..96fb162 --- /dev/null +++ b/RepeatKiller/scripting/RepeatKiller.sp @@ -0,0 +1,97 @@ +#include +#include + +/* BOOLS */ +bool g_bBlockRespawn[MAXPLAYERS+1]; + +/* FLOATS */ +float g_fDeathTime[MAXPLAYERS+1]; + +/* CONVARS */ +ConVar g_hRespawnDelay; +ConVar g_hRespawnTreshold; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "Repeat kill detector", + author = "zaCade", + description = "Disables respawning on maps with repeat killers", + version = "2.0.0", + url = "http://www.sourcemod.net/" +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + if((g_hRespawnDelay = FindConVar("zr_respawn_delay")) == INVALID_HANDLE) + SetFailState("Failed to find zr_respawn_delay cvar."); + + g_hRespawnTreshold = CreateConVar("zr_repeatkill_threshold", "1.0", "Zombie Reloaded Repeat Kill Detector Threshold", 0, true, 0.0, true, 10.0); + + HookEvent("round_start", OnRoundStart); + HookEvent("player_death", OnClientDeath); + + AutoExecConfig(true, "plugin.RepeatKillDetector"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientDisconnect(int client) +{ + g_bBlockRespawn[client] = false; + g_fDeathTime[client] = 0.0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast) +{ + for (int client; client < MaxClients; client++) + g_bBlockRespawn[client] = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientDeath(Event hEvent, const char[] sEvent, bool bDontBroadcast) +{ + char sWeapon[32]; + hEvent.GetString("weapon", sWeapon, sizeof(sWeapon)) + + int victim = GetClientOfUserId(hEvent.GetInt("userid")); + int client = GetClientOfUserId(hEvent.GetInt("attacker")); + + if (g_bBlockRespawn[victim]) + return; + + if (victim && !client && StrEqual(sWeapon, "trigger_hurt")) + { + float fGameTime = GetGameTime(); + + if (fGameTime - g_fDeathTime[victim] - g_hRespawnDelay.FloatValue <= g_hRespawnTreshold.FloatValue) + { + PrintToChat(victim, " \x01\x0B\x04[ZR]\x01 Repeat killer detected. Disabling your respawn for this round."); + g_bBlockRespawn[victim] = true; + } + + g_fDeathTime[victim] = fGameTime; + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action ZR_OnClientRespawn(&client, &ZR_RespawnCondition:condition) +{ + if (g_bBlockRespawn[client]) + return Plugin_Handled; + + return Plugin_Continue; +} \ No newline at end of file diff --git a/SaveLevel/configs/savelevel/missing.sh b/SaveLevel/configs/savelevel/missing.sh new file mode 100644 index 0000000..d7ae33e --- /dev/null +++ b/SaveLevel/configs/savelevel/missing.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +for map in ./*.cfg; do + mapname=$(basename -s .cfg "${map}") + + if [ ! -f ../../../../maps/$mapname.bsp ]; then + echo "$map" + fi +done diff --git a/SaveLevel/configs/savelevel/ze_FFXII_Paramina_Rift_v1_4_a8t.cfg b/SaveLevel/configs/savelevel/ze_FFXII_Paramina_Rift_v1_4_a8t.cfg new file mode 100644 index 0000000..0ea7473 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_FFXII_Paramina_Rift_v1_4_a8t.cfg @@ -0,0 +1,92 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "m_iName" "" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "props" + { + "m_iName" "Level_1" + } + } + "restore" + { + "m_iName" "Level_1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "props" + { + "m_iName" "Level_2" + } + } + "restore" + { + "m_iName" "Level_2" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "props" + { + "m_iName" "Level_3" + } + } + "restore" + { + "m_iName" "Level_3" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "props" + { + "m_iName" "Level_4" + } + } + "restore" + { + "m_iName" "Level_4" + "m_iFrags" "400" + } + } + "5" + { + "name" "Level 5" + "match" + { + "props" + { + "m_iName" "Level_5" + } + } + "restore" + { + "m_iName" "Level_5" + "m_iFrags" "500" + } + } +} diff --git a/SaveLevel/configs/savelevel/ze_ffvii_mako_reactor_v6_b09k2.cfg b/SaveLevel/configs/savelevel/ze_ffvii_mako_reactor_v6_b09k2.cfg new file mode 100644 index 0000000..17cb4b7 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_ffvii_mako_reactor_v6_b09k2.cfg @@ -0,0 +1,67 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "DeleteOutput" "m_OnUser1 leveling_counter" + "DeleteOutput" "m_OnUser4 score10,ApplyScore" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "math" + { + "m_OnUser1" "leveling_counter,Add,1" + } + } + "restore" + { + "AddOutput" "OnUser1 leveling_counter,Add,1,0,-1" + "AddOutput" "OnUser4 score10,ApplyScore,,0,-1" + "m_iFrags" "10" + } + } + "2" + { + "name" "Level 2" + "match" + { + "math" + { + "m_OnUser1" "leveling_counter,Add,2" + } + } + "restore" + { + "AddOutput" "OnUser1 leveling_counter,Add,2,0,-1" + "AddOutput" "OnUser4 score10,ApplyScore,,0,-1" + "AddOutput" "OnUser4 score10,ApplyScore,,0,-1" + "m_iFrags" "20" + } + } + "3" + { + "name" "Level 3" + "match" + { + "math" + { + "m_OnUser1" "leveling_counter,Add,3" + } + } + "restore" + { + "AddOutput" "OnUser1 leveling_counter,Add,3,0,-1" + "AddOutput" "OnUser4 score10,ApplyScore,,0,-1" + "AddOutput" "OnUser4 score10,ApplyScore,,0,-1" + "AddOutput" "OnUser4 score10,ApplyScore,,0,-1" + "m_iFrags" "30" + } + } +} diff --git a/SaveLevel/configs/savelevel/ze_ffxii_feywood_b6_1k.cfg b/SaveLevel/configs/savelevel/ze_ffxii_feywood_b6_1k.cfg new file mode 100644 index 0000000..e43f9e7 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_ffxii_feywood_b6_1k.cfg @@ -0,0 +1,60 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "DeleteOutput" "m_OnUser4 Map_Level_Check" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "math" + { + "m_OnUser4" "Map_Level_Check,Add,1" + } + } + "restore" + { + "AddOutput" "OnUser4 Map_Level_Check,Add,1,0,-1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "math" + { + "m_OnUser4" "Map_Level_Check,Add,2" + } + } + "restore" + { + "AddOutput" "OnUser4 Map_Level_Check,Add,2,0,-1" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "math" + { + "m_OnUser4" "Map_Level_Check,Add,3" + } + } + "restore" + { + "AddOutput" "OnUser4 Map_Level_Check,Add,3,0,-1" + "m_iFrags" "300" + } + } +} diff --git a/SaveLevel/configs/savelevel/ze_harry_potter_v2_1_csgo.cfg b/SaveLevel/configs/savelevel/ze_harry_potter_v2_1_csgo.cfg new file mode 100644 index 0000000..7acf0b1 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_harry_potter_v2_1_csgo.cfg @@ -0,0 +1,85 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "DeleteOutput" "m_OnUser3 fake_SaveLevel_Entity_math" + "DeleteOutput" "m_OnUser3 !self" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL=0,0,1" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "math" + { + "m_OnUser3" "fake_SaveLevel_Entity_math,Add,1" + } + } + "restore" + { + "AddOutput" "OnUser3 fake_SaveLevel_Entity_math,Add,1,0,-1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL++,1,1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "math" + { + "m_OnUser3" "fake_SaveLevel_Entity_math,Add,2" + } + } + "restore" + { + "AddOutput" "OnUser3 fake_SaveLevel_Entity_math,Add,2,0,-1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL++,1,1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL++,1,1" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "math" + { + "m_OnUser3" "fake_SaveLevel_Entity_math,Add,3" + } + } + "restore" + { + "AddOutput" "OnUser3 fake_SaveLevel_Entity_math,Add,3,0,-1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL++,1,1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL++,1,1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL++,1,1" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "math" + { + "m_OnUser3" "fake_SaveLevel_Entity_math,Add,4" + } + } + "restore" + { + "AddOutput" "OnUser3 fake_SaveLevel_Entity_math,Add,4,0,-1" + "AddOutput" "OnUser3 !self,RunScriptCode,ITEMLEVEL=4,1,1" + "m_iFrags" "400" + } + } +} \ No newline at end of file diff --git a/SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg b/SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg new file mode 100644 index 0000000..afc586a --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg @@ -0,0 +1,92 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "m_iName" "" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "props" + { + "m_iName" "1" + } + } + "restore" + { + "m_iName" "1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "props" + { + "m_iName" "2" + } + } + "restore" + { + "m_iName" "2" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "props" + { + "m_iName" "3" + } + } + "restore" + { + "m_iName" "3" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "props" + { + "m_iName" "4" + } + } + "restore" + { + "m_iName" "4" + "m_iFrags" "400" + } + } + "5" + { + "name" "Level 5" + "match" + { + "props" + { + "m_iName" "5" + } + } + "restore" + { + "m_iName" "5" + "m_iFrags" "500" + } + } +} diff --git a/SaveLevel/configs/savelevel/ze_lotr_minas_tirith_p4.cfg b/SaveLevel/configs/savelevel/ze_lotr_minas_tirith_p4.cfg new file mode 100644 index 0000000..261a6c7 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_lotr_minas_tirith_p4.cfg @@ -0,0 +1,92 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "m_iName" "" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "props" + { + "m_iName" "1" + } + } + "restore" + { + "m_iName" "1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "props" + { + "m_iName" "2" + } + } + "restore" + { + "m_iName" "2" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "props" + { + "m_iName" "3" + } + } + "restore" + { + "m_iName" "3" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "props" + { + "m_iName" "4" + } + } + "restore" + { + "m_iName" "4" + "m_iFrags" "400" + } + } + "5" + { + "name" "Level 5" + "match" + { + "props" + { + "m_iName" "5" + } + } + "restore" + { + "m_iName" "5" + "m_iFrags" "500" + } + } +} \ No newline at end of file diff --git a/SaveLevel/configs/savelevel/ze_lotr_minas_tirith_p5.cfg b/SaveLevel/configs/savelevel/ze_lotr_minas_tirith_p5.cfg new file mode 100644 index 0000000..261a6c7 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_lotr_minas_tirith_p5.cfg @@ -0,0 +1,92 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "m_iName" "" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "props" + { + "m_iName" "1" + } + } + "restore" + { + "m_iName" "1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "props" + { + "m_iName" "2" + } + } + "restore" + { + "m_iName" "2" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "props" + { + "m_iName" "3" + } + } + "restore" + { + "m_iName" "3" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "props" + { + "m_iName" "4" + } + } + "restore" + { + "m_iName" "4" + "m_iFrags" "400" + } + } + "5" + { + "name" "Level 5" + "match" + { + "props" + { + "m_iName" "5" + } + } + "restore" + { + "m_iName" "5" + "m_iFrags" "500" + } + } +} \ No newline at end of file diff --git a/SaveLevel/configs/savelevel/ze_tesv_skyrim_v5_6.cfg b/SaveLevel/configs/savelevel/ze_tesv_skyrim_v5_6.cfg new file mode 100644 index 0000000..94c8433 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_tesv_skyrim_v5_6.cfg @@ -0,0 +1,76 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "m_iName" "" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "props" + { + "m_iName" "1" + } + } + "restore" + { + "m_iName" "1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "props" + { + "m_iName" "2" + } + } + "restore" + { + "m_iName" "2" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "props" + { + "m_iName" "3" + } + } + "restore" + { + "m_iName" "3" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "props" + { + "m_iName" "4" + } + } + "restore" + { + "m_iName" "4" + "m_iFrags" "400" + } + } +} diff --git a/SaveLevel/scripting/SaveLevel.sp b/SaveLevel/scripting/SaveLevel.sp new file mode 100644 index 0000000..d5d5ff0 --- /dev/null +++ b/SaveLevel/scripting/SaveLevel.sp @@ -0,0 +1,521 @@ +#pragma semicolon 1 + +#include +#include +#include + +#pragma newdecls required + +StringMap g_PlayerLevels; +KeyValues g_Config; +KeyValues g_PropAltNames; + +#define PLUGIN_VERSION "2.0" +public Plugin myinfo = +{ + name = "SaveLevel", + author = "BotoX", + description = "Saves players level on maps when they disconnect and restore them on connect.", + version = PLUGIN_VERSION, + url = "" +}; + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + + g_PropAltNames = new KeyValues("PropAltNames"); + g_PropAltNames.SetString("m_iName", "targetname"); + + RegAdminCmd("sm_level", Command_Level, ADMFLAG_GENERIC, "Set a players map level."); +} + +public void OnPluginEnd() +{ + if(g_Config) + delete g_Config; + if(g_PlayerLevels) + delete g_PlayerLevels; + delete g_PropAltNames; +} + +public void OnMapStart() +{ + if(g_Config) + delete g_Config; + if(g_PlayerLevels) + delete g_PlayerLevels; + + char sMapName[PLATFORM_MAX_PATH]; + GetCurrentMap(sMapName, sizeof(sMapName)); + + char sConfigFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/savelevel/%s.cfg", sMapName); + if(!FileExists(sConfigFile)) + { + LogMessage("Could not find mapconfig: \"%s\"", sConfigFile); + return; + } + LogMessage("Found mapconfig: \"%s\"", sConfigFile); + + g_Config = new KeyValues("levels"); + if(!g_Config.ImportFromFile(sConfigFile)) + { + delete g_Config; + LogMessage("ImportFromFile() failed!"); + return; + } + g_Config.Rewind(); + + if(!g_Config.GotoFirstSubKey()) + { + delete g_Config; + LogMessage("GotoFirstSubKey() failed!"); + return; + } + + g_PlayerLevels = new StringMap(); +} + +public void OnClientPostAdminCheck(int client) +{ + if(!g_Config) + return; + + char sSteamID[32]; + GetClientAuthId(client, AuthId_Steam3, sSteamID, sizeof(sSteamID)); + + static char sTargets[128]; + if(g_PlayerLevels.GetString(sSteamID, sTargets, sizeof(sTargets))) + { + g_PlayerLevels.Remove(sSteamID); + + char sNames[128]; + static char asTargets[4][32]; + int Split = ExplodeString(sTargets, ";", asTargets, sizeof(asTargets), sizeof(asTargets[])); + + int Found = 0; + for(int i = 0; i < Split; i++) + { + static char sName[32]; + if(RestoreLevel(client, asTargets[i], sName, sizeof(sName))) + { + if(Found) + StrCat(sNames, sizeof(sNames), ", "); + Found++; + + StrCat(sNames, sizeof(sNames), sName); + } + } + + if(Found) + PrintToChatAll(" \x01\x0B\x03[SaveLevel]\x01 \x04%N\x01 has been restored to: \x04%s", client, sNames); + } +} + +public void OnClientDisconnect(int client) +{ + if(!g_Config || !g_PlayerLevels || !IsClientInGame(client)) + return; + + char sTargets[128]; + if(GetLevel(client, sTargets, sizeof(sTargets))) + { + char sSteamID[32]; + GetClientAuthId(client, AuthId_Steam3, sSteamID, sizeof(sSteamID)); + g_PlayerLevels.SetString(sSteamID, sTargets, true); + } +} + +bool RestoreLevel(int client, const char[] sTarget, char[] sName = NULL_STRING, int NameLen = 0) +{ + g_Config.Rewind(); + + if(!g_Config.JumpToKey(sTarget)) + return false; + + if(NameLen) + g_Config.GetString("name", sName, NameLen); + + static char sKey[32]; + static char sValue[1024]; + + if(!g_Config.JumpToKey("restore")) + return false; + + if(!g_Config.GotoFirstSubKey(false)) + return false; + + do + { + g_Config.GetSectionName(sKey, sizeof(sKey)); + g_Config.GetString(NULL_STRING, sValue, sizeof(sValue)); + if(StrEqual(sKey, "AddOutput", false)) + { + SetVariantString(sValue); + AcceptEntityInput(client, sKey, client, client); + } + else if(StrEqual(sKey, "DeleteOutput", false)) + { + int Index; + // Output (e.g. m_OnUser1) + int Target = FindCharInString(sValue, ' '); + if(Target == -1) + { + while((Index = FindOutput(client, sValue, 0)) != -1) + DeleteOutput(client, sValue, Index); + + continue; + } + sValue[Target] = 0; Target++; + while(IsCharSpace(sValue[Target])) + Target++; + + // Target (e.g. leveling_counter) + int Input = FindCharInString(sValue[Target], ','); + if(Input == -1) + { + while((Index = FindOutput(client, sValue, 0, sValue[Target])) != -1) + DeleteOutput(client, sValue, Index); + + continue; + } + sValue[Input] = 0; Input++; + + // Input (e.g. add) + int Parameter = Input + FindCharInString(sValue[Input], ','); + if(Input == -1) + { + while((Index = FindOutput(client, sValue, 0, sValue[Target], sValue[Input])) != -1) + DeleteOutput(client, sValue, Index); + + continue; + } + sValue[Parameter] = 0; Parameter++; + + // Parameter (e.g. 1) + while((Index = FindOutput(client, sValue, 0, sValue[Target], sValue[Input], sValue[Parameter])) != -1) + DeleteOutput(client, sValue, Index); + } + else + { + PropFieldType Type; + int NumBits; + int Offset = FindDataMapInfo(client, sKey, Type, NumBits); + if(Offset != -1) + { + if(Type == PropField_Integer) + { + int Value = StringToInt(sValue); + SetEntData(client, Offset, Value, NumBits / 8, false); + } + else if(Type == PropField_Float) + { + float Value = StringToFloat(sValue); + SetEntDataFloat(client, Offset, Value, false); + } + else if(Type == PropField_String) + { + SetEntDataString(client, Offset, sValue, strlen(sValue) + 1, false); + } + else if(Type == PropField_String_T) + { + static char sAltKey[32]; + g_PropAltNames.GetString(sKey, sAltKey, sizeof(sAltKey), NULL_STRING); + if(sAltKey[0]) + DispatchKeyValue(client, sAltKey, sValue); + } + } + } + } + while(g_Config.GotoNextKey(false)); + + g_Config.Rewind(); + return true; +} + +bool GetLevel(int client, char[] sTargets, int TargetsLen, char[] sNames = NULL_STRING, int NamesLen = 0) +{ + if(!g_Config || !g_PlayerLevels || !IsClientInGame(client)) + return false; + + g_Config.Rewind(); + g_Config.GotoFirstSubKey(); + + static char sTarget[32]; + static char sName[32]; + static char sKey[32]; + static char sValue[1024]; + static char sOutput[1024]; + bool Found = false; + do + { + g_Config.GetSectionName(sTarget, sizeof(sTarget)); + g_Config.GetString("name", sName, sizeof(sName)); + + if(!g_Config.JumpToKey("match")) + continue; + + int Matches = 0; + int ExactMatches = g_Config.GetNum("ExactMatches", -1); + int MinMatches = g_Config.GetNum("MinMatches", -1); + int MaxMatches = g_Config.GetNum("MaxMatches", -1); + + if(!g_Config.GotoFirstSubKey(false)) + continue; + + do + { + static char sSection[32]; + g_Config.GetSectionName(sSection, sizeof(sSection)); + + if(StrEqual(sSection, "outputs")) + { + int _Matches = 0; + int _ExactMatches = g_Config.GetNum("ExactMatches", -1); + int _MinMatches = g_Config.GetNum("MinMatches", -1); + int _MaxMatches = g_Config.GetNum("MaxMatches", -1); + + if(g_Config.GotoFirstSubKey(false)) + { + do + { + g_Config.GetSectionName(sKey, sizeof(sKey)); + g_Config.GetString(NULL_STRING, sValue, sizeof(sValue)); + + int Count = GetOutputCount(client, sKey); + for(int i = 0; i < Count; i++) + { + GetOutputFormatted(client, sKey, i, sOutput, sizeof(sOutput)); + sOutput[FindCharInString(sOutput, ',', true)] = 0; + sOutput[FindCharInString(sOutput, ',', true)] = 0; + + if(StrEqual(sValue, sOutput)) + _Matches++; + } + } + while(g_Config.GotoNextKey(false)); + + g_Config.GoBack(); + } + g_Config.GoBack(); + + Matches += CalcMatches(_Matches, _ExactMatches, _MinMatches, _MaxMatches); + } + else if(StrEqual(sSection, "props")) + { + int _Matches = 0; + int _ExactMatches = g_Config.GetNum("ExactMatches", -1); + int _MinMatches = g_Config.GetNum("MinMatches", -1); + int _MaxMatches = g_Config.GetNum("MaxMatches", -1); + + if(g_Config.GotoFirstSubKey(false)) + { + do + { + g_Config.GetSectionName(sKey, sizeof(sKey)); + g_Config.GetString(NULL_STRING, sValue, sizeof(sValue)); + + GetEntPropString(client, Prop_Data, sKey, sOutput, sizeof(sOutput)); + + if(StrEqual(sValue, sOutput)) + _Matches++; + } + while(g_Config.GotoNextKey(false)); + + g_Config.GoBack(); + } + g_Config.GoBack(); + + Matches += CalcMatches(_Matches, _ExactMatches, _MinMatches, _MaxMatches); + } + else if(StrEqual(sSection, "math")) + { + if(g_Config.GotoFirstSubKey(false)) + { + do + { + g_Config.GetSectionName(sKey, sizeof(sKey)); + g_Config.GetString(NULL_STRING, sValue, sizeof(sValue)); + + int Target = 0; + int Input; + int Parameter; + + Input = FindCharInString(sValue[Target], ','); + sValue[Input] = 0; Input++; + + Parameter = Input + FindCharInString(sValue[Input], ','); + sValue[Parameter] = 0; Parameter++; + + int Value = 0; + int Count = GetOutputCount(client, sKey); + for(int i = 0; i < Count; i++) + { + int _Target = 0; + int _Input; + int _Parameter; + + _Input = GetOutputTarget(client, sKey, i, sOutput[_Target], sizeof(sOutput) - _Target); + sOutput[_Input] = 0; _Input++; + + _Parameter = _Input + GetOutputTargetInput(client, sKey, i, sOutput[_Input], sizeof(sOutput) - _Input); + sOutput[_Parameter] = 0; _Parameter++; + + GetOutputParameter(client, sKey, i, sOutput[_Parameter], sizeof(sOutput) - _Parameter); + + if(!StrEqual(sOutput[_Target], sValue[Target])) + continue; + + int _Value = StringToInt(sOutput[_Parameter]); + + if(StrEqual(sOutput[_Input], "add", false)) + Value += _Value; + else if(StrEqual(sOutput[_Input], "subtract", false)) + Value -= _Value; + } + + int Result = StringToInt(sValue[Parameter]); + if(StrEqual(sValue[Input], "subtract", false)) + Result *= -1; + + if(Value == Result) + Matches += 1; + } + while(g_Config.GotoNextKey(false)); + + g_Config.GoBack(); + } + g_Config.GoBack(); + } + } + while(g_Config.GotoNextKey(false)); + + g_Config.GoBack(); + + if(CalcMatches(Matches, ExactMatches, MinMatches, MaxMatches)) + { + if(Found) + { + if(TargetsLen) + StrCat(sTargets, TargetsLen, ";"); + if(NamesLen) + StrCat(sNames, NamesLen, ", "); + } + + Found = true; + if(TargetsLen) + StrCat(sTargets, TargetsLen, sTarget); + if(NamesLen) + StrCat(sNames, NamesLen, sName); + } + } + while(g_Config.GotoNextKey()); + + g_Config.Rewind(); + if(!Found) + return false; + return true; +} + +public Action Command_Level(int client, int args) +{ + if(!g_Config) + { + ReplyToCommand(client, "[SM] The current map is not supported."); + return Plugin_Handled; + } + + if(args < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_level "); + return Plugin_Handled; + } + + char sTarget[MAX_TARGET_LENGTH]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + + GetCmdArg(1, sTarget, sizeof(sTarget)); + if((iTargetCount = ProcessTargetString(sTarget, client, iTargets, MAXPLAYERS, 0, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + char sLevel[32]; + GetCmdArg(2, sLevel, sizeof(sLevel)); + + int Level; + if(!StringToIntEx(sLevel, Level)) + { + ReplyToCommand(client, "[SM] Level has to be a number."); + return Plugin_Handled; + } + IntToString(Level, sLevel, sizeof(sLevel)); + + g_Config.Rewind(); + if(!g_Config.JumpToKey("0")) + { + ReplyToCommand(client, "[SM] Setting levels on the current map is not supported."); + return Plugin_Handled; + } + g_Config.GoBack(); + + if(Level && !g_Config.JumpToKey(sLevel)) + { + ReplyToCommand(client, "[SM] Level %s could not be found.", sLevel); + return Plugin_Handled; + } + g_Config.Rewind(); + + char sPrevNames[128]; + if(iTargetCount == 1) + GetLevel(iTargets[0], sPrevNames, 0, sPrevNames, sizeof(sPrevNames)); + + char sName[32]; + for(int i = 0; i < iTargetCount; i++) + { + // Reset level first + if(Level) + { + if(!RestoreLevel(iTargets[i], "0")) + { + ReplyToCommand(client, "[SM] Failed resetting level on %L.", iTargets[i]); + return Plugin_Handled; + } + } + + if(!RestoreLevel(iTargets[i], sLevel, sName, sizeof(sName))) + { + ReplyToCommand(client, "[SM] Failed setting level to %s on %L.", sLevel, iTargets[i]); + return Plugin_Handled; + } + + LogAction(client, iTargets[i], "Set %L to %s", iTargets[i], sName); + } + + if(sPrevNames[0]) + ShowActivity2(client, " \x01\x0B\x03[SaveLevel]\x01 ", "Set \x04%s\x01 from \x04%s\x01 to \x04%s\x01", sTargetName, sPrevNames, sName); + else + ShowActivity2(client, " \x01\x0B\x03[SaveLevel]\x01 ", "Set \x04%s\x01 to \x04%s\x01", sTargetName, sName); + + return Plugin_Handled; +} + +stock int CalcMatches(int Matches, int ExactMatches, int MinMatches, int MaxMatches) +{ + int Value = 0; + if((ExactMatches == -1 && MinMatches == -1 && MaxMatches == -1 && Matches) || + Matches == ExactMatches || + (MinMatches != -1 && MaxMatches == -1 && Matches >= MinMatches) || + (MaxMatches != -1 && MinMatches == -1 && Matches <= MaxMatches) || + (MinMatches != -1 && MaxMatches != -1 && Matches >= MinMatches && Matches <= MaxMatches)) + { + Value++; + } + + return Value; +} diff --git a/SelectiveBhop/scripting/SelectiveBhop.sp b/SelectiveBhop/scripting/SelectiveBhop.sp new file mode 100644 index 0000000..d2f5f8b --- /dev/null +++ b/SelectiveBhop/scripting/SelectiveBhop.sp @@ -0,0 +1,421 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include +#undef REQUIRE_PLUGIN +#include +#define REQUIRE_PLUGIN +#include + +ConVar g_CVar_sv_enablebunnyhopping; +ConVar g_CVar_zr_disablebunnyhopping; + +enum +{ + LIMITED_NONE = 0, + LIMITED_GENERAL = 1, + LIMITED_PLUGIN = 2, + + // Temp + LIMITED_ZOMBIE = 4 +} + +bool g_bEnabled = false; +bool g_bZombieEnabled = false; +bool g_bInOnPlayerRunCmd = false; + +int g_ClientLimited[MAXPLAYERS + 1] = {LIMITED_NONE, ...}; +int g_ActiveLimitedFlags = LIMITED_GENERAL | LIMITED_PLUGIN; + +StringMap g_ClientLimitedCache; + +public Plugin myinfo = +{ + name = "Selective Bunnyhop", + author = "BotoX", + description = "Disables bunnyhop on certain players/groups", + version = "0.1" +} + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + + g_CVar_sv_enablebunnyhopping = FindConVar("sv_enablebunnyhopping"); + g_CVar_sv_enablebunnyhopping.Flags &= ~FCVAR_REPLICATED; + g_CVar_sv_enablebunnyhopping.AddChangeHook(OnConVarChanged); + g_bEnabled = g_CVar_sv_enablebunnyhopping.BoolValue; + + g_CVar_zr_disablebunnyhopping = CreateConVar("zr_disablebunnyhopping", "0", "Disable bhop for zombies.", FCVAR_NOTIFY); + g_CVar_zr_disablebunnyhopping.AddChangeHook(OnConVarChanged); + g_bZombieEnabled = g_CVar_zr_disablebunnyhopping.BoolValue; + + g_ClientLimitedCache = new StringMap(); + + HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); + + RegAdminCmd("sm_bhop", Command_Bhop, ADMFLAG_GENERIC, "sm_bhop <#userid|name> <0|1>"); + + RegConsoleCmd("sm_bhopstatus", Command_Status, "sm_bhopstatus [#userid|name]"); + + /* Late load */ + for(int i = 1; i <= MaxClients; i++) + { + if(!IsClientInGame(i) || IsFakeClient(i)) + return; + + if(ZR_IsClientZombie(i)) + AddLimitedFlag(i, LIMITED_ZOMBIE); + } + + UpdateLimitedFlags(); + UpdateClients(); +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + CreateNative("LimitBhop", Native_LimitBhop); + CreateNative("IsBhopLimited", Native_IsBhopLimited); + RegPluginLibrary("SelectiveBhop"); + + return APLRes_Success; +} + +public void OnPluginEnd() +{ + g_CVar_sv_enablebunnyhopping.BoolValue = g_bEnabled; + g_CVar_sv_enablebunnyhopping.Flags |= FCVAR_REPLICATED|FCVAR_NOTIFY; + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i)) + { + if(g_CVar_sv_enablebunnyhopping.BoolValue) + g_CVar_sv_enablebunnyhopping.ReplicateToClient(i, "1"); + else + g_CVar_sv_enablebunnyhopping.ReplicateToClient(i, "0"); + } + } +} + +public void OnMapEnd() +{ + g_ClientLimitedCache.Clear(); +} + +public void OnClientPutInServer(int client) +{ + TransmitConVar(client); +} + +public void OnClientDisconnect(int client) +{ + // Remove temp + int LimitedFlag = g_ClientLimited[client] & ~(LIMITED_ZOMBIE); + + if(LimitedFlag != LIMITED_NONE) + { + char sSteamID[64]; + if(GetClientAuthId(client, AuthId_Engine, sSteamID, sizeof(sSteamID))) + g_ClientLimitedCache.SetValue(sSteamID, LimitedFlag, true); + } + + g_ClientLimited[client] = LIMITED_NONE; +} + +public void OnClientPostAdminCheck(int client) +{ + char sSteamID[64]; + if(GetClientAuthId(client, AuthId_Engine, sSteamID, sizeof(sSteamID))) + { + int LimitedFlag; + if(g_ClientLimitedCache.GetValue(sSteamID, LimitedFlag)) + { + AddLimitedFlag(client, LimitedFlag); + g_ClientLimitedCache.Remove(sSteamID); + } + } +} + +public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + if(convar == g_CVar_sv_enablebunnyhopping) + { + if(g_bInOnPlayerRunCmd) + return; + + g_bEnabled = convar.BoolValue; + UpdateClients(); + } + else if(convar == g_CVar_zr_disablebunnyhopping) + { + g_bZombieEnabled = convar.BoolValue; + UpdateLimitedFlags(); + } +} + +public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) +{ + if(!g_bEnabled) + return Plugin_Continue; + + bool bEnableBunnyhopping = !(g_ClientLimited[client] & g_ActiveLimitedFlags); + if(bEnableBunnyhopping == g_CVar_sv_enablebunnyhopping.BoolValue) + return Plugin_Continue; + + if(!g_bInOnPlayerRunCmd) + { + g_CVar_sv_enablebunnyhopping.Flags &= ~FCVAR_NOTIFY; + g_bInOnPlayerRunCmd = true; + } + + g_CVar_sv_enablebunnyhopping.BoolValue = bEnableBunnyhopping; + + return Plugin_Continue; +} + +public void OnRunThinkFunctionsPost(bool simulating) +{ + if(g_bInOnPlayerRunCmd) + { + g_CVar_sv_enablebunnyhopping.BoolValue = g_bEnabled; + g_CVar_sv_enablebunnyhopping.Flags |= FCVAR_NOTIFY; + g_bInOnPlayerRunCmd = false; + } +} + +public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn) +{ + AddLimitedFlag(client, LIMITED_ZOMBIE); +} + +public void ZR_OnClientHumanPost(int client, bool respawn, bool protect) +{ + RemoveLimitedFlag(client, LIMITED_ZOMBIE); +} + +public void ZR_OnClientRespawned(int client, ZR_RespawnCondition condition) +{ + if(condition == ZR_Respawn_Human) + RemoveLimitedFlag(client, LIMITED_ZOMBIE); + else + AddLimitedFlag(client, LIMITED_ZOMBIE); +} + +public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + RemoveLimitedFlag(-1, LIMITED_ZOMBIE); +} + +void UpdateLimitedFlags() +{ + int Flags = LIMITED_GENERAL; + + if(g_bZombieEnabled) + Flags |= LIMITED_ZOMBIE; + + if(g_ActiveLimitedFlags != Flags) + { + g_ActiveLimitedFlags = Flags; + UpdateClients(); + } + g_ActiveLimitedFlags = Flags; +} + +stock void AddLimitedFlag(int client, int Flag) +{ + if(client == -1) + { + for(int i = 1; i <= MaxClients; i++) + _AddLimitedFlag(i, Flag); + } + else + _AddLimitedFlag(client, Flag); +} + +stock void _AddLimitedFlag(int client, int Flag) +{ + bool bWasLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); + g_ClientLimited[client] |= Flag; + bool bIsLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); + + if(bIsLimited != bWasLimited) + TransmitConVar(client); +} + +stock void RemoveLimitedFlag(int client, int Flag) +{ + if(client == -1) + { + for(int i = 1; i <= MaxClients; i++) + _RemoveLimitedFlag(i, Flag); + } + else + _RemoveLimitedFlag(client, Flag); +} + +stock void _RemoveLimitedFlag(int client, int Flag) +{ + bool bWasLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); + g_ClientLimited[client] &= ~Flag; + bool bIsLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); + + if(bIsLimited != bWasLimited) + TransmitConVar(client); +} + +stock void UpdateClients() +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i)) + TransmitConVar(i); + } +} + +stock void TransmitConVar(int client) +{ + if(!IsClientInGame(client) || IsFakeClient(client)) + return; + + bool bIsLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); + + if(g_bEnabled && !bIsLimited) + g_CVar_sv_enablebunnyhopping.ReplicateToClient(client, "1"); + else + g_CVar_sv_enablebunnyhopping.ReplicateToClient(client, "0"); +} + +public Action Command_Bhop(int client, int argc) +{ + if(argc < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_bhop <#userid|name> <0|1>"); + return Plugin_Handled; + } + + char sArg[64]; + char sArg2[2]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + bool bValue; + + GetCmdArg(1, sArg, sizeof(sArg)); + GetCmdArg(2, sArg2, sizeof(sArg2)); + + bValue = sArg2[0] == '1' ? true : false; + + if((iTargetCount = ProcessTargetString(sArg, client, iTargets, MAXPLAYERS, COMMAND_FILTER_NO_MULTI, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + for(int i = 0; i < iTargetCount; i++) + { + if(bValue) + RemoveLimitedFlag(iTargets[i], LIMITED_GENERAL | LIMITED_PLUGIN); + else + AddLimitedFlag(iTargets[i], LIMITED_GENERAL); + } + + ShowActivity2(client, "\x01[SM] \x04", "\x01\x04%s\x01 bunnyhop on target \x04%s", bValue ? "Un-limited" : "Limited", sTargetName); + + if(iTargetCount > 1) + LogAction(client, -1, "\"%L\" %s bunnyhop on target \"%s\"", client, bValue ? "Un-limited" : "Limited", sTargetName); + else + LogAction(client, iTargets[0], "\"%L\" %s bunnyhop on target \"%L\"", client, bValue ? "Un-limited" : "Limited", iTargets[0]); + + return Plugin_Handled; +} + +public Action Command_Status(int client, int argc) +{ + if (argc && CheckCommandAccess(client, "", ADMFLAG_BAN, true)) + { + char sArgument[64]; + GetCmdArg(1, sArgument, sizeof(sArgument)); + + int target = -1; + if((target = FindTarget(client, sArgument, true, false)) == -1) + return Plugin_Handled; + + bool bLimited = view_as(g_ClientLimited[client] & (LIMITED_GENERAL | LIMITED_PLUGIN)); + + if(bLimited) + { + ReplyToCommand(client, "[SM] %N their bhop is currently: limited", target); + return Plugin_Handled; + } + else + { + ReplyToCommand(client, "[SM] %N their bhop is currently: unlimited", target); + return Plugin_Handled; + } + } + else + { + bool bLimited = view_as(g_ClientLimited[client] & (LIMITED_GENERAL | LIMITED_PLUGIN)); + + if(bLimited) + { + ReplyToCommand(client, "[SM] your bhop is currently: limited"); + return Plugin_Handled; + } + else + { + ReplyToCommand(client, "[SM] your bhop is currently: unlimited"); + return Plugin_Handled; + } + } +} + +public int Native_LimitBhop(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + bool bLimited = view_as(GetNativeCell(2)); + + if(client > MaxClients || client <= 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); + return -1; + } + + if(!IsClientInGame(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); + return -1; + } + + if(bLimited) + AddLimitedFlag(client, LIMITED_PLUGIN); + else + RemoveLimitedFlag(client, LIMITED_PLUGIN); + + return 0; +} + +public int Native_IsBhopLimited(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + + if(client > MaxClients || client <= 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); + return -1; + } + + if(!IsClientInGame(client)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); + return -1; + } + + int LimitedFlag = g_ClientLimited[client] & LIMITED_PLUGIN; + + return LimitedFlag != LIMITED_NONE; +} diff --git a/SelectiveBhop/scripting/include/SelectiveBhop.inc b/SelectiveBhop/scripting/include/SelectiveBhop.inc new file mode 100644 index 0000000..7da0abf --- /dev/null +++ b/SelectiveBhop/scripting/include/SelectiveBhop.inc @@ -0,0 +1,26 @@ +#if defined _SelectiveBhop_Included + #endinput +#endif +#define _SelectiveBhop_Included + +native int LimitBhop(int client, bool bLimited); +native int IsBhopLimited(int client); + +public SharedPlugin __pl_SelectiveBhop = +{ + name = "SelectiveBhop", + file = "SelectiveBhop.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_SelectiveBhop_SetNTVOptional() +{ + MarkNativeAsOptional("LimitBhop"); + MarkNativeAsOptional("IsBhopLimited"); +} +#endif diff --git a/SelfMute/scripting/SelfMute.sp b/SelfMute/scripting/SelfMute.sp new file mode 100644 index 0000000..03cd9a5 --- /dev/null +++ b/SelfMute/scripting/SelfMute.sp @@ -0,0 +1,1358 @@ +#pragma semicolon 1 + +#include +#include +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include +#include +#tryinclude +#include +#define REQUIRE_PLUGIN + +#undef REQUIRE_EXTENSIONS +#tryinclude +#define REQUIRE_EXTENSIONS + +#pragma newdecls required + +bool g_Plugin_ccc = false; +bool g_Plugin_zombiereloaded = false; +bool g_Plugin_voiceannounce_ex = false; +bool g_Plugin_AdvancedTargeting = false; +bool g_Extension_Voice = false; +bool g_bIsProtoBuf = false; + +Handle g_hCookieTorchMuted = null; + +#define PLUGIN_VERSION "2.4" + +public Plugin myinfo = +{ + name = "SelfMute", + author = "BotoX", + description = "Ignore other players in text and voicechat.", + version = PLUGIN_VERSION, + url = "" +}; + +enum +{ + MUTE_NONE = 0, + MUTE_SPEC = 1, + MUTE_CT = 2, + MUTE_T = 4, + MUTE_DEAD = 8, + MUTE_ALIVE = 16, + MUTE_NOTFRIENDS = 32, + MUTE_ALL = 64, + MUTE_LAST = 64 +}; + +bool g_Ignored[(MAXPLAYERS + 1) * (MAXPLAYERS + 1)]; +bool g_Exempt[MAXPLAYERS + 1][MAXPLAYERS + 1]; +int g_SpecialMutes[MAXPLAYERS + 1]; + +char g_PlayerNames[MAXPLAYERS+1][MAX_NAME_LENGTH]; + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + + CreateConVar("sm_selfmute_version", PLUGIN_VERSION, "Version of Self-Mute", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + RegConsoleCmd("sm_sm", Command_SelfMute, "Mute player by typing !sm [playername]"); + RegConsoleCmd("sm_su", Command_SelfUnMute, "Unmute player by typing !su [playername]"); + RegConsoleCmd("sm_cm", Command_CheckMutes, "Check who you have self-muted"); + RegAdminCmd("sm_debugtorch", Command_CheckPermaTorchMutes, ADMFLAG_GENERIC, "Check who has permanently self-muted Torch"); + + HookEvent("round_start", Event_Round); + HookEvent("round_end", Event_Round); + HookEvent("player_team", Event_TeamChange); + + if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) + g_bIsProtoBuf = true; + + UserMsg RadioText = GetUserMessageId("RadioText"); + if(RadioText == INVALID_MESSAGE_ID) + SetFailState("This game doesn't support RadioText user messages."); + + HookUserMessage(RadioText, Hook_UserMessageRadioText, true); + + UserMsg SendAudio = GetUserMessageId("SendAudio"); + if(SendAudio == INVALID_MESSAGE_ID) + SetFailState("This game doesn't support SendAudio user messages."); + + HookUserMessage(SendAudio, Hook_UserMessageSendAudio, true); + + g_hCookieTorchMuted = RegClientCookie("torch_muted", "is torch muted", CookieAccess_Protected); +} + +public void OnAllPluginsLoaded() +{ + g_Plugin_ccc = LibraryExists("ccc"); + g_Plugin_zombiereloaded = LibraryExists("zombiereloaded"); + g_Plugin_voiceannounce_ex = LibraryExists("voiceannounce_ex"); + g_Plugin_AdvancedTargeting = LibraryExists("AdvancedTargeting"); + g_Extension_Voice = LibraryExists("Voice"); + + LogMessage("SelfMute capabilities:\nProtoBuf: %s\nCCC: %s\nZombieReloaded: %s\nVoiceAnnounce: %s\nAdvancedTargeting: %s\nVoice: %s", + (g_bIsProtoBuf ? "yes" : "no"), + (g_Plugin_ccc ? "loaded" : "not loaded"), + (g_Plugin_zombiereloaded ? "loaded" : "not loaded"), + (g_Plugin_voiceannounce_ex ? "loaded" : "not loaded"), + (g_Plugin_AdvancedTargeting ? "loaded" : "not loaded"), + (g_Extension_Voice ? "loaded" : "not loaded")); +} + +void OnLibrary(const char[] name, bool added) +{ + if(StrEqual(name, "ccc")) + g_Plugin_ccc = added; + else if(StrEqual(name, "zombiereloaded")) + g_Plugin_zombiereloaded = added; + else if(StrEqual(name, "voiceannounce_ex")) + g_Plugin_voiceannounce_ex = added; + else if(StrEqual(name, "AdvancedTargeting")) + g_Plugin_AdvancedTargeting = added; + else if(StrEqual(name, "Voice")) + g_Extension_Voice = added; + +} + +public void OnLibraryAdded(const char[] name) { OnLibrary(name, true); } +public void OnLibraryRemoved(const char[] name) { OnLibrary(name, false); } + +public void OnClientPutInServer(int client) +{ + g_SpecialMutes[client] = MUTE_NONE; + for(int i = 1; i < MAXPLAYERS; i++) + { + SetIgnored(client, i, false); + SetExempt(client, i, false); + + SetIgnored(i, client, false); + SetExempt(i, client, false); + } + + UpdateSpecialMutesOtherClients(client); + UpdateIgnored(); + + GetCookiesForTorch(client); +} + +public void OnClientDisconnect(int client) +{ + g_SpecialMutes[client] = MUTE_NONE; + for(int i = 1; i < MAXPLAYERS; i++) + { + SetIgnored(client, i, false); + SetExempt(client, i, false); + + SetIgnored(i, client, false); + SetExempt(i, client, false); + + if(IsClientInGame(i) && i != client) + SetListenOverride(i, client, Listen_Yes); + } + + UpdateIgnored(); +} + +public void GetCookiesForTorch(int client) +{ + char sBuffer[2]; + + GetClientCookie(client, g_hCookieTorchMuted, sBuffer, sizeof(sBuffer)); + if(sBuffer[0] != '\0') + { + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && IsClientSourceTV(i)) + Ignore(client, i); + } + } + + UpdateIgnored(); +} + +public void Event_Round(Handle event, const char[] name, bool dontBroadcast) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i)) + UpdateSpecialMutesThisClient(i); + } +} + +public void Event_TeamChange(Handle event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(GetEventInt(event, "userid")); + + UpdateSpecialMutesOtherClients(client); +} + +public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn) +{ + UpdateSpecialMutesOtherClients(client); +} + +public void ZR_OnClientHumanPost(int client, bool respawn, bool protect) +{ + UpdateSpecialMutesOtherClients(client); +} + +/* + * Mutes this client on other players +*/ +void UpdateSpecialMutesOtherClients(int client) +{ + bool Alive = IsPlayerAlive(client); + int Team = GetClientTeam(client); + + for(int i = 1; i <= MaxClients; i++) + { + if(i == client || !IsClientInGame(i)) + continue; + + int Flags = MUTE_NONE; + + if(g_SpecialMutes[i] & MUTE_SPEC && Team == CS_TEAM_SPECTATOR) + Flags |= MUTE_SPEC; + + else if(g_SpecialMutes[i] & MUTE_CT && Alive && + ((g_Plugin_zombiereloaded && ZR_IsClientHuman(client)) || (!g_Plugin_zombiereloaded && Team == CS_TEAM_CT))) + Flags |= MUTE_CT; + + else if(g_SpecialMutes[i] & MUTE_T && Alive && + ((g_Plugin_zombiereloaded && ZR_IsClientZombie(client)) || (!g_Plugin_zombiereloaded && Team == CS_TEAM_T))) + Flags |= MUTE_T; + + else if(g_SpecialMutes[i] & MUTE_DEAD && !Alive) + Flags |= MUTE_DEAD; + + else if(g_SpecialMutes[i] & MUTE_ALIVE && Alive) + Flags |= MUTE_ALIVE; + + else if(g_SpecialMutes[i] & MUTE_NOTFRIENDS && + g_Plugin_AdvancedTargeting && IsClientFriend(i, client) == 0) + Flags |= MUTE_NOTFRIENDS; + + else if(g_SpecialMutes[i] & MUTE_ALL) + Flags |= MUTE_ALL; + + if(Flags && !GetExempt(i, client)) + SetListenOverride(i, client, Listen_No); + else if(!GetIgnored(i, client)) + SetListenOverride(i, client, Listen_Yes); + } +} + +/* + * Mutes other players on this client +*/ +void UpdateSpecialMutesThisClient(int client) +{ + for(int i = 1; i <= MaxClients; i++) + { + if(i == client || !IsClientInGame(i)) + continue; + + bool Alive = IsPlayerAlive(i); + int Team = GetClientTeam(i); + + int Flags = MUTE_NONE; + + if(g_SpecialMutes[client] & MUTE_SPEC && Team == CS_TEAM_SPECTATOR) + Flags |= MUTE_SPEC; + + else if(g_SpecialMutes[client] & MUTE_CT && Alive && + ((g_Plugin_zombiereloaded && ZR_IsClientHuman(i) || (!g_Plugin_zombiereloaded) && Team == CS_TEAM_CT))) + Flags |= MUTE_CT; + + else if(g_SpecialMutes[client] & MUTE_T && Alive && + ((g_Plugin_zombiereloaded && ZR_IsClientZombie(i) || (!g_Plugin_zombiereloaded) && Team == CS_TEAM_T))) + Flags |= MUTE_T; + + else if(g_SpecialMutes[client] & MUTE_DEAD && !Alive) + Flags |= MUTE_DEAD; + + else if(g_SpecialMutes[client] & MUTE_ALIVE && Alive) + Flags |= MUTE_ALIVE; + + else if(g_SpecialMutes[client] & MUTE_NOTFRIENDS && + g_Plugin_AdvancedTargeting && IsClientFriend(client, i) == 0) + Flags |= MUTE_NOTFRIENDS; + + else if(g_SpecialMutes[client] & MUTE_ALL) + Flags |= MUTE_ALL; + + if(Flags && !GetExempt(client, i)) + SetListenOverride(client, i, Listen_No); + else if(!GetIgnored(client, i)) + SetListenOverride(client, i, Listen_Yes); + } +} + +int GetSpecialMutesFlags(char[] Argument) +{ + int SpecialMute = MUTE_NONE; + if(StrEqual(Argument, "@spec", false) || StrEqual(Argument, "@!ct", false) || StrEqual(Argument, "@!t", false)) + SpecialMute |= MUTE_SPEC; + if(StrEqual(Argument, "@ct", false) || StrEqual(Argument, "@!t", false) || StrEqual(Argument, "@!spec", false)) + SpecialMute |= MUTE_CT; + if(StrEqual(Argument, "@t", false) || StrEqual(Argument, "@!ct", false) || StrEqual(Argument, "@!spec", false)) + SpecialMute |= MUTE_T; + if(StrEqual(Argument, "@dead", false) || StrEqual(Argument, "@!alive", false)) + SpecialMute |= MUTE_DEAD; + if(StrEqual(Argument, "@alive", false) || StrEqual(Argument, "@!dead", false)) + SpecialMute |= MUTE_ALIVE; + if(g_Plugin_AdvancedTargeting && StrEqual(Argument, "@!friends", false)) + SpecialMute |= MUTE_NOTFRIENDS; + if(StrEqual(Argument, "@all", false)) + SpecialMute |= MUTE_ALL; + + return SpecialMute; +} + +void FormatSpecialMutes(int SpecialMute, char[] aBuf, int BufLen) +{ + if(!SpecialMute) + { + StrCat(aBuf, BufLen, "none"); + return; + } + + bool Status = false; + if(SpecialMute & MUTE_ALL) + { + StrCat(aBuf, BufLen, "Everyone, "); + Status = true; + } + if(SpecialMute & MUTE_SPEC) + { + StrCat(aBuf, BufLen, "Spectators, "); + Status = true; + } + if(SpecialMute & MUTE_CT) + { + StrCat(aBuf, BufLen, "Counter-Terrorists, "); + Status = true; + } + if(SpecialMute & MUTE_T) + { + StrCat(aBuf, BufLen, "Terrorists, "); + Status = true; + } + if(SpecialMute & MUTE_DEAD) + { + StrCat(aBuf, BufLen, "Dead players, "); + Status = true; + } + if(SpecialMute & MUTE_ALIVE) + { + StrCat(aBuf, BufLen, "Alive players, "); + Status = true; + } + if(SpecialMute & MUTE_NOTFRIENDS) + { + StrCat(aBuf, BufLen, "Not Steam friends, "); + Status = true; + } + + // Cut off last ', ' + if(Status) + aBuf[strlen(aBuf) - 2] = 0; +} + +bool MuteSpecial(int client, char[] Argument) +{ + bool RetValue = false; + int SpecialMute = GetSpecialMutesFlags(Argument); + + if(SpecialMute & MUTE_NOTFRIENDS && g_Plugin_AdvancedTargeting && ReadClientFriends(client) != 1) + { + PrintToChat(client, "\x04[Self-Mute]\x01 Could not read your friendslist, your profile must be set to public!"); + SpecialMute &= ~MUTE_NOTFRIENDS; + RetValue = true; + } + + if(SpecialMute) + { + if(SpecialMute & MUTE_ALL || g_SpecialMutes[client] & MUTE_ALL) + { + g_SpecialMutes[client] = MUTE_ALL; + SpecialMute = MUTE_ALL; + } + else + g_SpecialMutes[client] |= SpecialMute; + + UpdateSpecialMutesThisClient(client); + + char aBuf[128]; + FormatSpecialMutes(SpecialMute, aBuf, sizeof(aBuf)); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-muted group:\x04 %s", aBuf); + RetValue = true; + } + + return RetValue; +} + +bool UnMuteSpecial(int client, char[] Argument) +{ + int SpecialMute = GetSpecialMutesFlags(Argument); + + if(SpecialMute) + { + if(SpecialMute & MUTE_ALL) + { + if(g_SpecialMutes[client]) + { + SpecialMute = g_SpecialMutes[client]; + g_SpecialMutes[client] = MUTE_NONE; + } + else + { + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsClientSourceTV(i)) + UnIgnore(client, i); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-unmuted:\x04 all players"); + return true; + } + } + } + else + g_SpecialMutes[client] &= ~SpecialMute; + + UpdateSpecialMutesThisClient(client); + + char aBuf[256]; + FormatSpecialMutes(SpecialMute, aBuf, sizeof(aBuf)); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-unmuted group:\x04 %s", aBuf); + return true; + } + + return false; +} + +void Ignore(int client, int target) +{ + SetIgnored(client, target, true); + SetListenOverride(client, target, Listen_No); +} + +void UnIgnore(int client, int target) +{ + SetIgnored(client, target, false); + SetListenOverride(client, target, Listen_Yes); +} + +void Exempt(int client, int target) +{ + SetExempt(client, target, true); + UpdateSpecialMutesThisClient(client); +} + +void UnExempt(int client, int target) +{ + SetExempt(client, target, false); + UpdateSpecialMutesThisClient(client); +} + +/* + * CHAT COMMANDS +*/ + +public Action Command_CheckPermaTorchMutes(int client, int args) +{ + int iTorchPermMuted; + int iPlayers; + char sBuffer[2]; + + for(int i = 1; i <= MaxClients; i++) + { + if(!IsClientInGame(i) || IsFakeClient(i)) + continue; + + GetClientCookie(i, g_hCookieTorchMuted, sBuffer, sizeof(sBuffer)); + + if(sBuffer[0] != '\0') + iTorchPermMuted++; + + iPlayers++; + } + + ReplyToCommand(client, "[SM] There are currently %d out of %d Players who've got Torch permanently self-muted.", iTorchPermMuted, iPlayers); + + return Plugin_Handled; +} + +public Action Command_SelfMute(int client, int args) +{ + if(client == 0) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + if(args < 1) + { + DisplayMuteMenu(client); + return Plugin_Handled; + } + + char Argument[65]; + GetCmdArg(1, Argument, sizeof(Argument)); + + char Filtered[65]; + strcopy(Filtered, sizeof(Filtered), Argument); + StripQuotes(Filtered); + TrimString(Filtered); + + if(MuteSpecial(client, Filtered)) + return Plugin_Handled; + + char sTargetName[MAX_TARGET_LENGTH]; + int aTargetList[MAXPLAYERS]; + int TargetCount; + bool TnIsMl; + + if((TargetCount = ProcessTargetString( + Argument, + client, + aTargetList, + MAXPLAYERS, + COMMAND_FILTER_CONNECTED|COMMAND_FILTER_NO_IMMUNITY, + sTargetName, + sizeof(sTargetName), + TnIsMl)) <= 0) + { + ReplyToTargetError(client, TargetCount); + return Plugin_Handled; + } + + if(TargetCount == 1) + { + if(aTargetList[0] == client) + { + PrintToChat(client, "\x04[Self-Mute]\x01 You can't mute yourself, don't be silly."); + return Plugin_Handled; + } + + if(IsClientSourceTV(aTargetList[0])) + { + Ignore(client, aTargetList[0]); + SetClientCookie(client, g_hCookieTorchMuted, "1"); + PrintToChat(client, "\x04[Self-Mute]\x01 You have permanently self-muted:\x04 %s", sTargetName); + return Plugin_Handled; + } + + if(GetExempt(client, aTargetList[0])) + { + UnExempt(client, aTargetList[0]); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have removed exempt from self-mute:\x04 %s", sTargetName); + + return Plugin_Handled; + } + } + + for(int i = 0; i < TargetCount; i++) + { + if(aTargetList[i] == client) + continue; + + Ignore(client, aTargetList[i]); + } + UpdateIgnored(); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-muted:\x04 %s", sTargetName); + + return Plugin_Handled; +} + +public Action Command_SelfUnMute(int client, int args) +{ + if(client == 0) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + if(args < 1) + { + DisplayUnMuteMenu(client); + return Plugin_Handled; + } + + char Argument[65]; + GetCmdArg(1, Argument, sizeof(Argument)); + + char Filtered[65]; + strcopy(Filtered, sizeof(Filtered), Argument); + StripQuotes(Filtered); + TrimString(Filtered); + + if(UnMuteSpecial(client, Filtered)) + return Plugin_Handled; + + char sTargetName[MAX_TARGET_LENGTH]; + int aTargetList[MAXPLAYERS]; + int TargetCount; + bool TnIsMl; + + if((TargetCount = ProcessTargetString( + Argument, + client, + aTargetList, + MAXPLAYERS, + COMMAND_FILTER_CONNECTED|COMMAND_FILTER_NO_IMMUNITY, + sTargetName, + sizeof(sTargetName), + TnIsMl)) <= 0) + { + ReplyToTargetError(client, TargetCount); + return Plugin_Handled; + } + + if(TargetCount == 1) + { + if(aTargetList[0] == client) + { + PrintToChat(client, "\x04[Self-Mute]\x01 Unmuting won't work either."); + return Plugin_Handled; + } + + if(IsClientSourceTV(aTargetList[0])) + { + UnIgnore(client, aTargetList[0]); + SetClientCookie(client, g_hCookieTorchMuted, ""); + PrintToChat(client, "\x04[Self-Mute]\x01 You have permanently self-unmuted:\x04 %s", sTargetName); + return Plugin_Handled; + } + + if(!GetIgnored(client, aTargetList[0])) + { + Exempt(client, aTargetList[0]); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have exempted from self-mute:\x04 %s", sTargetName); + + return Plugin_Handled; + } + } + + for(int i = 0; i < TargetCount; i++) + { + if(aTargetList[i] == client) + continue; + + UnIgnore(client, aTargetList[i]); + } + UpdateIgnored(); + + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-unmuted:\x04 %s", sTargetName); + + return Plugin_Handled; +} + +public Action Command_CheckMutes(int client, int args) +{ + if(client == 0) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + char aMuted[1024]; + char aExempted[1024]; + char aName[MAX_NAME_LENGTH]; + for(int i = 1; i <= MaxClients; i++) + { + if(!IsClientInGame(i)) + continue; + + GetClientName(i, aName, sizeof(aName)); + + if(GetIgnored(client, i)) + { + StrCat(aMuted, sizeof(aMuted), aName); + StrCat(aMuted, sizeof(aMuted), ", "); + } + + if(GetExempt(client, i)) + { + StrCat(aExempted, sizeof(aExempted), aName); + StrCat(aExempted, sizeof(aExempted), ", "); + } + } + + if(strlen(aMuted)) + { + aMuted[strlen(aMuted) - 2] = 0; + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-muted:\x04 %s", aMuted); + } + + if(g_SpecialMutes[client] != MUTE_NONE) + { + aMuted[0] = 0; + FormatSpecialMutes(g_SpecialMutes[client], aMuted, sizeof(aMuted)); + PrintToChat(client, "\x04[Self-Mute]\x01 You have self-muted group:\x04 %s", aMuted); + } + else if(!strlen(aMuted) && !strlen(aExempted)) + PrintToChat(client, "\x04[Self-Mute]\x01 You have not self-muted anyone!"); + + if(strlen(aExempted)) + { + aExempted[strlen(aExempted) - 2] = 0; + PrintToChat(client, "\x04[Self-Mute]\x01 You have exempted from self-mute:\x04 %s", aExempted); + } + + return Plugin_Handled; +} + +stock bool _IsClientSpeaking(int client) +{ + #if defined _voiceannounceex_included_ + if(g_Plugin_voiceannounce_ex) + return IsClientSpeaking(client); + #endif + + #if defined _voice_included + if(g_Extension_Voice) + return IsClientTalking(client); + #endif + + return false; +} + +/* + * MENU +*/ +void DisplayMuteMenu(int client) +{ + Menu menu = new Menu(MenuHandler_MuteMenu, MenuAction_Select|MenuAction_Cancel|MenuAction_End|MenuAction_DrawItem|MenuAction_DisplayItem); + menu.ExitButton = true; + + int[] aClients = new int[MaxClients + 1]; + + { + // Count talking players and insert id's into aClients array + int CurrentlyTalking = 0; + for(int i = 1; i <= MaxClients; i++) + { + if(i != client && IsClientInGame(i) && _IsClientSpeaking(i)) + aClients[CurrentlyTalking++] = i; + } + + if(CurrentlyTalking > 0) + { + // insert player names into g_PlayerNames array + for(int i = 0; i < CurrentlyTalking; i++) + GetClientName(aClients[i], g_PlayerNames[aClients[i]], sizeof(g_PlayerNames[])); + + // sort aClients array by player name + SortCustom1D(aClients, CurrentlyTalking, SortByPlayerName); + + // insert players sorted + char aBuf[12]; + for(int i = 0; i < CurrentlyTalking; i++) + { + IntToString(GetClientUserId(aClients[i]), aBuf, sizeof(aBuf)); + menu.AddItem(aBuf, g_PlayerNames[aClients[i]]); + } + + // insert spacers + int Entries = 7 - CurrentlyTalking % 7; + while(Entries--) + menu.AddItem("", "", ITEMDRAW_RAWLINE); + } + } + + menu.AddItem("@all", "Everyone"); + menu.AddItem("@spec", "Spectators"); + menu.AddItem("@ct", "Counter-Terrorists"); + menu.AddItem("@t", "Terrorists"); + menu.AddItem("@dead", "Dead players"); + menu.AddItem("@alive", "Alive players"); + if(g_Plugin_AdvancedTargeting) + menu.AddItem("@!friends", "Not Steam friends"); + else + menu.AddItem("", "", ITEMDRAW_RAWLINE); + + // Count valid players and insert id's into aClients array + int Players = 0; + for(int i = 1; i <= MaxClients; i++) + { + if(i != client && IsClientInGame(i)) + aClients[Players++] = i; + } + + // insert player names into g_PlayerNames array + for(int i = 0; i < Players; i++) + GetClientName(aClients[i], g_PlayerNames[aClients[i]], sizeof(g_PlayerNames[])); + + // sort aClients array by player name + SortCustom1D(aClients, Players, SortByPlayerName); + + // insert players sorted + char aBuf[12]; + for(int i = 0; i < Players; i++) + { + IntToString(GetClientUserId(aClients[i]), aBuf, sizeof(aBuf)); + menu.AddItem(aBuf, g_PlayerNames[aClients[i]]); + } + + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_MuteMenu(Menu menu, MenuAction action, int param1, int param2) +{ + switch(action) + { + case MenuAction_End: + { + if(param1 != MenuEnd_Selected) + CloseHandle(menu); + } + case MenuAction_Select: + { + int Style; + char aItem[32]; + char aDisp[MAX_NAME_LENGTH + 4]; + menu.GetItem(param2, aItem, sizeof(aItem), Style, aDisp, sizeof(aDisp)); + + if(Style != ITEMDRAW_DEFAULT || !aItem[0]) + { + PrintToChat(param1, "Internal error: aItem[0] -> %d | Style -> %d", aItem[0], Style); + return 0; + } + + if(aItem[0] == '@') + { + int Flag = GetSpecialMutesFlags(aItem); + if(Flag && g_SpecialMutes[param1] & Flag) + UnMuteSpecial(param1, aItem); + else + MuteSpecial(param1, aItem); + + menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER); + return 0; + } + + int UserId = StringToInt(aItem); + int client = GetClientOfUserId(UserId); + if(!client) + { + PrintToChat(param1, "\x04[Self-Mute]\x01 Player no longer available."); + menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER); + return 0; + } + + if(GetIgnored(param1, client)) + { + if(IsClientSourceTV(client)) + { + UnIgnore(param1, client); + SetClientCookie(param1, g_hCookieTorchMuted, ""); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have permanently self-unmuted:\x04 %N", client); + } + else + { + UnIgnore(param1, client); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have self-unmuted:\x04 %N", client); + } + } + else if(GetExempt(param1, client)) + { + UnExempt(param1, client); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have removed exempt from self-mute:\x04 %N", client); + } + else + { + if(IsClientSourceTV(client)) + { + Ignore(param1, client); + SetClientCookie(param1, g_hCookieTorchMuted, "1"); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have permanently self-muted:\x04 %N", client); + } + else + { + Ignore(param1, client); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have self-muted:\x04 %N", client); + } + } + menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER); + return 0; + } + case MenuAction_DrawItem: + { + int Style; + char aItem[32]; + menu.GetItem(param2, aItem, sizeof(aItem), Style); + + if(!aItem[0]) + return ITEMDRAW_DISABLED; + + if(aItem[0] == '@') + { + int Flag = GetSpecialMutesFlags(aItem); + if(Flag & MUTE_ALL) + return Style; + else if(g_SpecialMutes[param1] & MUTE_ALL) + return ITEMDRAW_DISABLED; + + return Style; + } + + int UserId = StringToInt(aItem); + int client = GetClientOfUserId(UserId); + if(!client) // Player disconnected + return ITEMDRAW_DISABLED; + + return Style; + } + case MenuAction_DisplayItem: + { + int Style; + char aItem[32]; + char aDisp[MAX_NAME_LENGTH + 4]; + menu.GetItem(param2, aItem, sizeof(aItem), Style, aDisp, sizeof(aDisp)); + + // Start of current page + if((param2 + 1) % 7 == 1) + { + if(aItem[0] == '@') + menu.SetTitle("[Self-Mute] Groups"); + else if(param2 == 0) + menu.SetTitle("[Self-Mute] Talking players"); + else + menu.SetTitle("[Self-Mute] All players"); + } + + if(!aItem[0]) + return 0; + + if(aItem[0] == '@') + { + int Flag = GetSpecialMutesFlags(aItem); + if(Flag && g_SpecialMutes[param1] & Flag) + { + char aBuf[32] = "[M] "; + FormatSpecialMutes(Flag, aBuf, sizeof(aBuf)); + if(!StrEqual(aDisp, aBuf)) + return RedrawMenuItem(aBuf); + } + + return 0; + } + + int UserId = StringToInt(aItem); + int client = GetClientOfUserId(UserId); + if(!client) // Player disconnected + { + char aBuf[MAX_NAME_LENGTH + 4] = "[D] "; + StrCat(aBuf, sizeof(aBuf), aDisp); + if(!StrEqual(aDisp, aBuf)) + return RedrawMenuItem(aBuf); + } + + if(GetIgnored(param1, client)) + { + char aBuf[MAX_NAME_LENGTH + 4] = "[M] "; + GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[])); + StrCat(aBuf, sizeof(aBuf), g_PlayerNames[client]); + if(!StrEqual(aDisp, aBuf)) + return RedrawMenuItem(aBuf); + } + else if(GetExempt(param1, client)) + { + char aBuf[MAX_NAME_LENGTH + 4] = "[E] "; + GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[])); + StrCat(aBuf, sizeof(aBuf), g_PlayerNames[client]); + if(!StrEqual(aDisp, aBuf)) + return RedrawMenuItem(aBuf); + } + else + { + GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[])); + if(!StrEqual(aDisp, g_PlayerNames[client])) + return RedrawMenuItem(g_PlayerNames[client]); + } + + return 0; + } + } + + return 0; +} + +void DisplayUnMuteMenu(int client) +{ + Menu menu = new Menu(MenuHandler_UnMuteMenu, MenuAction_Select|MenuAction_Cancel|MenuAction_End|MenuAction_DrawItem|MenuAction_DisplayItem); + menu.SetTitle("[Self-UnMute]"); + menu.ExitButton = true; + + if(g_SpecialMutes[client] & MUTE_ALL) + menu.AddItem("@all", "Everyone"); + if(g_SpecialMutes[client] & MUTE_SPEC) + menu.AddItem("@spec", "Spectators"); + if(g_SpecialMutes[client] & MUTE_CT) + menu.AddItem("@ct", "Counter-Terrorists"); + if(g_SpecialMutes[client] & MUTE_T) + menu.AddItem("@t", "Terrorists"); + if(g_SpecialMutes[client] & MUTE_DEAD) + menu.AddItem("@dead", "Dead players"); + if(g_SpecialMutes[client] & MUTE_ALIVE) + menu.AddItem("@alive", "Alive players"); + if(g_SpecialMutes[client] & MUTE_NOTFRIENDS) + menu.AddItem("@!friends", "Not Steam friends"); + if(g_SpecialMutes[client]) + menu.AddItem("", "", ITEMDRAW_RAWLINE); + + int[] aClients = new int[MaxClients + 1]; + + // Count valid players and insert id's into aClients array + int Players = 0; + for(int i = 1; i <= MaxClients; i++) + { + if(i != client && IsClientInGame(i) && (GetIgnored(client, i) || GetExempt(client, i))) + aClients[Players++] = i; + } + + // insert player names into g_PlayerNames array + for(int i = 0; i < Players; i++) + { + GetClientName(aClients[i], g_PlayerNames[aClients[i]], sizeof(g_PlayerNames[])); + } + + // sort aClients array by player name + SortCustom1D(aClients, Players, SortByPlayerName); + + // insert players sorted + char aBuf[12]; + for(int i = 0; i < Players; i++) + { + IntToString(GetClientUserId(aClients[i]), aBuf, sizeof(aBuf)); + menu.AddItem(aBuf, g_PlayerNames[aClients[i]]); + } + + if(!menu.ItemCount) + { + delete menu; + PrintToChat(client, "\x04[Self-Mute]\x01 You haven't muted or exempted anyone."); + return; + } + + menu.Display(client, MENU_TIME_FOREVER); +} + + +public int MenuHandler_UnMuteMenu(Menu menu, MenuAction action, int param1, int param2) +{ + switch(action) + { + case MenuAction_End: + { + if(param1 != MenuEnd_Selected) + CloseHandle(menu); + } + case MenuAction_Select: + { + int Style; + char aItem[32]; + char aDisp[MAX_NAME_LENGTH + 4]; + menu.GetItem(param2, aItem, sizeof(aItem), Style, aDisp, sizeof(aDisp)); + + if(Style != ITEMDRAW_DEFAULT || !aItem[0]) + { + PrintToChat(param1, "Internal error: aItem[0] -> %d | Style -> %d", aItem[0], Style); + return 0; + } + + if(aItem[0] == '@') + { + int Flag = GetSpecialMutesFlags(aItem); + if(Flag && g_SpecialMutes[param1] & Flag) + UnMuteSpecial(param1, aItem); + + menu.RemoveItem(GetMenuSelectionPosition()); + menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER); + return 0; + } + + int UserId = StringToInt(aItem); + int client = GetClientOfUserId(UserId); + if(!client) + { + PrintToChat(param1, "\x04[Self-Mute]\x01 Player no longer available."); + menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER); + return 0; + } + + if(GetIgnored(param1, client)) + { + if(IsClientSourceTV(client)) + { + UnIgnore(param1, client); + SetClientCookie(param1, g_hCookieTorchMuted, ""); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have permanently self-unmuted:\x04 %N", client); + } + else + { + UnIgnore(param1, client); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have self-unmuted:\x04 %N", client); + } + } + else if(GetExempt(param1, client)) + { + UnExempt(param1, client); + PrintToChat(param1, "\x04[Self-Mute]\x01 You have removed exempt from self-mute:\x04 %N", client); + } + + menu.RemoveItem(GetMenuSelectionPosition()); + menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER); + return 0; + } + case MenuAction_DrawItem: + { + int Style; + char aItem[32]; + menu.GetItem(param2, aItem, sizeof(aItem), Style); + + if(!aItem[0]) + return ITEMDRAW_DISABLED; + + if(aItem[0] == '@') + return Style; + + int UserId = StringToInt(aItem); + int client = GetClientOfUserId(UserId); + if(!client) // Player disconnected + return ITEMDRAW_DISABLED; + + return Style; + } + case MenuAction_DisplayItem: + { + int Style; + char aItem[32]; + char aDisp[MAX_NAME_LENGTH + 4]; + menu.GetItem(param2, aItem, sizeof(aItem), Style, aDisp, sizeof(aDisp)); + + if(!aItem[0]) + return 0; + + int UserId = StringToInt(aItem); + int client = GetClientOfUserId(UserId); + if(!client) // Player disconnected + { + char aBuf[MAX_NAME_LENGTH + 4] = "[D] "; + StrCat(aBuf, sizeof(aBuf), aDisp); + if(!StrEqual(aDisp, aBuf)) + return RedrawMenuItem(aBuf); + } + + if(GetExempt(param1, client)) + { + char aBuf[MAX_NAME_LENGTH + 4] = "[E] "; + GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[])); + StrCat(aBuf, sizeof(aBuf), g_PlayerNames[client]); + if(!StrEqual(aDisp, aBuf)) + return RedrawMenuItem(aBuf); + } + + return 0; + } + } + + return 0; +} + +/* + * HOOKS +*/ +int g_MsgDest; +int g_MsgClient; +char g_MsgName[256]; +char g_MsgParam1[256]; +char g_MsgParam2[256]; +char g_MsgParam3[256]; +char g_MsgParam4[256]; +char g_MsgRadioSound[256]; +int g_MsgPlayersNum; +int g_MsgPlayers[MAXPLAYERS + 1]; + +public Action Hook_UserMessageRadioText(UserMsg msg_id, Handle bf, const int[] players, int playersNum, bool reliable, bool init) +{ + if(g_bIsProtoBuf) + { + g_MsgDest = PbReadInt(bf, "msg_dst"); + g_MsgClient = PbReadInt(bf, "client"); + PbReadString(bf, "msg_name", g_MsgName, sizeof(g_MsgName)); + PbReadString(bf, "params", g_MsgParam1, sizeof(g_MsgParam1), 0); + PbReadString(bf, "params", g_MsgParam2, sizeof(g_MsgParam2), 1); + PbReadString(bf, "params", g_MsgParam3, sizeof(g_MsgParam3), 2); + PbReadString(bf, "params", g_MsgParam4, sizeof(g_MsgParam4), 3); + } + else + { + g_MsgDest = BfReadByte(bf); + g_MsgClient = BfReadByte(bf); + BfReadString(bf, g_MsgName, sizeof(g_MsgName), false); + BfReadString(bf, g_MsgParam1, sizeof(g_MsgParam1), false); + BfReadString(bf, g_MsgParam2, sizeof(g_MsgParam2), false); + BfReadString(bf, g_MsgParam3, sizeof(g_MsgParam3), false); + BfReadString(bf, g_MsgParam4, sizeof(g_MsgParam4), false); + } + + // Check which clients need to be excluded. + g_MsgPlayersNum = 0; + for(int i = 0; i < playersNum; i++) + { + int client = players[i]; + if(!GetIgnored(client, g_MsgClient)) + g_MsgPlayers[g_MsgPlayersNum++] = client; + } + + // No clients were excluded. + if(g_MsgPlayersNum == playersNum) + { + g_MsgClient = -1; + return Plugin_Continue; + } + else if(g_MsgPlayersNum == 0) // All clients were excluded and there is no need to broadcast. + { + g_MsgClient = -2; + return Plugin_Handled; + } + + return Plugin_Handled; +} + +public Action Hook_UserMessageSendAudio(UserMsg msg_id, Handle bf, const int[] players, int playersNum, bool reliable, bool init) +{ + if(g_MsgClient == -1) + return Plugin_Continue; + else if(g_MsgClient == -2) + return Plugin_Handled; + + if(g_bIsProtoBuf) + PbReadString(bf, "radio_sound", g_MsgRadioSound, sizeof(g_MsgRadioSound)); + else + BfReadString(bf, g_MsgRadioSound, sizeof(g_MsgRadioSound), false); + + if(StrEqual(g_MsgRadioSound, "radio.locknload")) + return Plugin_Continue; + + DataPack pack = new DataPack(); + pack.WriteCell(g_MsgDest); + pack.WriteCell(g_MsgClient); + pack.WriteString(g_MsgName); + pack.WriteString(g_MsgParam1); + pack.WriteString(g_MsgParam2); + pack.WriteString(g_MsgParam3); + pack.WriteString(g_MsgParam4); + pack.WriteString(g_MsgRadioSound); + pack.WriteCell(g_MsgPlayersNum); + + for(int i = 0; i < g_MsgPlayersNum; i++) + pack.WriteCell(g_MsgPlayers[i]); + + RequestFrame(OnPlayerRadio, pack); + + return Plugin_Handled; +} + +public void OnPlayerRadio(DataPack pack) +{ + pack.Reset(); + g_MsgDest = pack.ReadCell(); + g_MsgClient = pack.ReadCell(); + pack.ReadString(g_MsgName, sizeof(g_MsgName)); + pack.ReadString(g_MsgParam1, sizeof(g_MsgParam1)); + pack.ReadString(g_MsgParam2, sizeof(g_MsgParam2)); + pack.ReadString(g_MsgParam3, sizeof(g_MsgParam3)); + pack.ReadString(g_MsgParam4, sizeof(g_MsgParam4)); + pack.ReadString(g_MsgRadioSound, sizeof(g_MsgRadioSound)); + g_MsgPlayersNum = pack.ReadCell(); + + int playersNum = 0; + for(int i = 0; i < g_MsgPlayersNum; i++) + { + int client_ = pack.ReadCell(); + if(IsClientInGame(client_)) + g_MsgPlayers[playersNum++] = client_; + } + CloseHandle(pack); + + Handle RadioText = StartMessage("RadioText", g_MsgPlayers, playersNum, USERMSG_RELIABLE); + if(g_bIsProtoBuf) + { + PbSetInt(RadioText, "msg_dst", g_MsgDest); + PbSetInt(RadioText, "client", g_MsgClient); + PbSetString(RadioText, "msg_name", g_MsgName); + PbSetString(RadioText, "params", g_MsgParam1, 0); + PbSetString(RadioText, "params", g_MsgParam2, 1); + PbSetString(RadioText, "params", g_MsgParam3, 2); + PbSetString(RadioText, "params", g_MsgParam4, 3); + } + else + { + BfWriteByte(RadioText, g_MsgDest); + BfWriteByte(RadioText, g_MsgClient); + BfWriteString(RadioText, g_MsgName); + BfWriteString(RadioText, g_MsgParam1); + BfWriteString(RadioText, g_MsgParam2); + BfWriteString(RadioText, g_MsgParam3); + BfWriteString(RadioText, g_MsgParam4); + } + EndMessage(); + + Handle SendAudio = StartMessage("SendAudio", g_MsgPlayers, playersNum, USERMSG_RELIABLE); + if(g_bIsProtoBuf) + PbSetString(SendAudio, "radio_sound", g_MsgRadioSound); + else + BfWriteString(SendAudio, g_MsgRadioSound); + EndMessage(); +} + +/* + * HELPERS +*/ +void UpdateIgnored() +{ + if(g_Plugin_ccc) + CCC_UpdateIgnoredArray(g_Ignored); +} + +public int SortByPlayerName(int elem1, int elem2, const int[] array, Handle hndl) +{ + return strcmp(g_PlayerNames[elem1], g_PlayerNames[elem2], false); +} + +bool GetIgnored(int client, int target) +{ + return g_Ignored[(client * (MAXPLAYERS + 1) + target)]; +} + +void SetIgnored(int client, int target, bool ignored) +{ + g_Ignored[(client * (MAXPLAYERS + 1) + target)] = ignored; +} + +bool GetExempt(int client, int target) +{ + return g_Exempt[client][target]; +} + +void SetExempt(int client, int target, bool exempt) +{ + g_Exempt[client][target] = exempt; +} diff --git a/SelfMute/scripting/SelfMuteRadio.sp b/SelfMute/scripting/SelfMuteRadio.sp new file mode 100644 index 0000000..3248b40 --- /dev/null +++ b/SelfMute/scripting/SelfMuteRadio.sp @@ -0,0 +1,378 @@ +#pragma semicolon 1 + +#include +#include +#include +#include +#include + +#define MESSAGE_RADIOTEXT 1 +#define MESSAGE_SENDAUDIO 2 + +bool g_bStopRadioSounds[MAXPLAYERS+1] = { false, ... }; +bool g_bStopNadeSounds[MAXPLAYERS+1] = { false, ...}; +bool g_bStopRadioSoundsHooked = false; + +Handle g_hCookieStopRadio = null; +Handle g_hCookieStopNade = null; + +public Plugin myinfo = +{ + name = "SelfMuteRadio", + author = "Dogan + zaCade", + description = "Make it possible to self mute the radio (aka ignoread via command)", + version = "1.0.0", + url = "" +}; + +public void OnPluginStart() +{ + UserMsg RadioText = GetUserMessageId("RadioText"); + + if (RadioText == INVALID_MESSAGE_ID) + SetFailState("This game does not support the \"RadioText\" UserMessage."); + + UserMsg SendAudio = GetUserMessageId("SendAudio"); + + if (SendAudio == INVALID_MESSAGE_ID) + SetFailState("This game does not support the \"SendAudio\" UserMessage."); + + RegConsoleCmd("sm_smradio", OnToggleSelfMuteRadio, "Toggle Radio Self Mute"); + //RegConsoleCmd("sm_radio", OnToggleSelfMuteRadio, "Toggle Radio Self Mute"); //GFL only + RegConsoleCmd("sm_smradio_nades", OnToggleSelfMuteNade, "Toggle only Radio 'Fire in the hole' Self Mute"); + + g_hCookieStopRadio = RegClientCookie("radio_blocked", "is the radio blocked", CookieAccess_Protected); + g_hCookieStopNade = RegClientCookie("nades_blocked", "is the 'fire in the hole' radio blocked", CookieAccess_Protected); + + SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Radio Self Mute"); + + HookUserMessage(RadioText, Hook_RadioText, true); + HookUserMessage(SendAudio, Hook_SendAudio, true); +} + +public Action Hook_RadioText(UserMsg msg_id, Handle bf, const int[] players, int playersNum, bool reliable, bool init) +{ + if(!g_bStopRadioSoundsHooked) + return Plugin_Continue; + + int dest = BfReadByte(bf); + int client = BfReadByte(bf); + + char sSoundType[128]; + BfReadString(bf, sSoundType, sizeof(sSoundType), false); + + char sSoundName[128]; + BfReadString(bf, sSoundName, sizeof(sSoundName), false); + + char sSoundFile[128]; + BfReadString(bf, sSoundFile, sizeof(sSoundFile), false); + + // Check which clients need to be excluded. + int[] newPlayers = new int[playersNum]; + int newPlayersNum = 0; + + for(int i = 0; i < playersNum; i++) + { + int player = players[i]; + + if(IsClientInGame(player) && !g_bStopRadioSounds[player] && !(g_bStopNadeSounds[player] && StrContains(sSoundFile, "hole", false) != -1)) + { + newPlayers[newPlayersNum++] = player; + } + } + + if (newPlayersNum == playersNum) + { + // No clients where excluded. + return Plugin_Continue; + } + else if (newPlayersNum == 0) + { + // All clients were excluded and there is no need to broadcast. + return Plugin_Handled; + } + + DataPack pack = new DataPack(); + pack.WriteString(sSoundType); + pack.WriteString(sSoundName); + pack.WriteString(sSoundFile); + pack.WriteCell(dest); + pack.WriteCell(client); + pack.WriteCell(newPlayersNum); + + for(int i = 0; i < newPlayersNum; i++) + { + pack.WriteCell(newPlayers[i]); + } + + RequestFrame(OnRadioText, pack); + + return Plugin_Handled; +} + +public void OnRadioText(DataPack pack) +{ + pack.Reset(); + + char sSoundType[128]; + pack.ReadString(sSoundType, sizeof(sSoundType)); + + char sSoundName[128]; + pack.ReadString(sSoundName, sizeof(sSoundName)); + + char sSoundFile[128]; + pack.ReadString(sSoundFile, sizeof(sSoundFile)); + + int dest = pack.ReadCell(); + int client = pack.ReadCell(); + int newPlayersNum = pack.ReadCell(); + + int[] players = new int[newPlayersNum]; + int playersNum = 0; + + for(int i = 0; i < newPlayersNum; i++) + { + int player = pack.ReadCell(); + + if(IsClientInGame(player)) + { + players[playersNum++] = player; + } + } + + CloseHandle(pack); + + Handle RadioText = StartMessage("RadioText", players, playersNum, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); + + if (RadioText != INVALID_HANDLE) + { + BfWriteByte(RadioText, dest); + BfWriteByte(RadioText, client); + BfWriteString(RadioText, sSoundType); + BfWriteString(RadioText, sSoundName); + BfWriteString(RadioText, sSoundFile); + } + + EndMessage(); +} + +public Action Hook_SendAudio(UserMsg msg_id, Handle bf, const int[] players, int playersNum, bool reliable, bool init) +{ + if(!g_bStopRadioSoundsHooked) + return Plugin_Continue; + + char sSoundFile[128]; + BfReadString(bf, sSoundFile, sizeof(sSoundFile), false); + + // Check which clients need to be excluded. + int[] newPlayers = new int[playersNum]; + int newPlayersNum = 0; + + for(int i = 0; i < playersNum; i++) + { + int player = players[i]; + + if(IsClientInGame(player) && !g_bStopRadioSounds[player] && !(g_bStopNadeSounds[player] && StrContains(sSoundFile, "hole", false) != -1)) + { + newPlayers[newPlayersNum++] = player; + } + } + + if (newPlayersNum == playersNum) + { + // No clients where excluded. + return Plugin_Continue; + } + else if (newPlayersNum == 0) + { + // All clients were excluded and there is no need to broadcast. + return Plugin_Handled; + } + + DataPack pack = new DataPack(); + pack.WriteString(sSoundFile); + pack.WriteCell(newPlayersNum); + + for(int i = 0; i < newPlayersNum; i++) + { + pack.WriteCell(newPlayers[i]); + } + + RequestFrame(OnSendAudio, pack); + + return Plugin_Handled; +} + +public void OnSendAudio(DataPack pack) +{ + pack.Reset(); + + char sSoundFile[128]; + pack.ReadString(sSoundFile, sizeof(sSoundFile)); + + int newPlayersNum = pack.ReadCell(); + + int[] players = new int[newPlayersNum]; + int playersNum = 0; + + for(int i = 0; i < newPlayersNum; i++) + { + int player = pack.ReadCell(); + + if(IsClientInGame(player)) + { + players[playersNum++] = player; + } + } + + CloseHandle(pack); + + Handle SendAudio = StartMessage("SendAudio", players, playersNum, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); + + if (SendAudio != INVALID_HANDLE) + { + BfWriteString(SendAudio, sSoundFile); + } + + EndMessage(); +} + +public Action OnToggleSelfMuteRadio(int client, int args) +{ + ToggleSelfMuteRadio(client); + return Plugin_Handled; +} + +public Action OnToggleSelfMuteNade(int client, int args) +{ + ToggleSelfMuteNade(client); + return Plugin_Handled; +} + +public Action ToggleSelfMuteRadio(int client) +{ + g_bStopRadioSounds[client] = !g_bStopRadioSounds[client]; + CheckHooks(); + + SetClientCookie(client, g_hCookieStopRadio, g_bStopRadioSounds[client] ? "1" : ""); + CPrintToChat(client, "{cyan}[SelfMuteRadio] {white}%s", g_bStopRadioSounds[client] ? "You self-muted all Radio Messages and Sounds." : "You self-unmuted all Radio Messages and Sounds."); +} + +public Action ToggleSelfMuteNade(int client) +{ + g_bStopNadeSounds[client] = !g_bStopNadeSounds[client]; + CheckHooks(); + + SetClientCookie(client, g_hCookieStopNade, g_bStopNadeSounds[client] ? "1" : ""); + CPrintToChat(client, "{cyan}[SelfMuteRadio] {white}%s", g_bStopNadeSounds[client] ? "You self-muted 'Fire in the Hole' Radio Messages and Sounds." : "You self-unmuted 'Fire in the Hole' Radio Messages and Sounds."); +} + +public void OnClientCookiesCached(int client) +{ + char sBuffer[2]; + + GetClientCookie(client, g_hCookieStopRadio, sBuffer, sizeof(sBuffer)); + + if(sBuffer[0] != '\0') + { + g_bStopRadioSounds[client] = true; + g_bStopRadioSoundsHooked = true; + } + else + { + g_bStopRadioSounds[client] = false; + } + + GetClientCookie(client, g_hCookieStopRadio, sBuffer, sizeof(sBuffer)); + + if(sBuffer[0] != '\0') + { + g_bStopNadeSounds[client] = true; + g_bStopRadioSoundsHooked = true; + } + else + { + g_bStopNadeSounds[client] = false; + } +} + +public void OnClientDisconnect(int client) +{ + g_bStopRadioSounds[client] = false; + g_bStopNadeSounds[client] = false; + CheckHooks(); +} + +public void CheckHooks() +{ + bool bShouldHook = false; + + for(int i = 1; i <= MaxClients; i++) + { + if(g_bStopRadioSounds[i] || g_bStopNadeSounds[i]) + { + bShouldHook = true; + break; + } + } + + g_bStopRadioSoundsHooked = bShouldHook; +} + +public void ShowSettingsMenu(int client) +{ + Menu menu = new Menu(MenuHandler_MainMenu); + + menu.SetTitle("SelfMuteRadio Settings", client); + + char sBuffer[128]; + Format(sBuffer, sizeof(sBuffer), "Self-Muting all Radio: %s", g_bStopRadioSounds[client] ? "Enabled" : "Disabled"); + menu.AddItem("0", sBuffer); + + Format(sBuffer, sizeof(sBuffer), "Self-Muting Nades Radio only: %s", g_bStopNadeSounds[client] ? "Enabled" : "Disabled"); + menu.AddItem("1", sBuffer); + + menu.ExitBackButton = true; + + menu.Display(client, MENU_TIME_FOREVER); +} + +public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) +{ + switch(action) + { + case(CookieMenuAction_DisplayOption): + { + Format(buffer, maxlen, "SelfMuteRadio", client); + } + case(CookieMenuAction_SelectOption): + { + ShowSettingsMenu(client); + } + } +} + +public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection) +{ + switch(action) + { + case(MenuAction_Select): + { + switch(selection) + { + case(0): ToggleSelfMuteRadio(client); + case(1): ToggleSelfMuteNade(client); + } + + ShowSettingsMenu(client); + } + case(MenuAction_Cancel): + { + ShowCookieMenu(client); + } + case(MenuAction_End): + { + delete menu; + } + } +} \ No newline at end of file diff --git a/Spectate/scripting/Spectate.sp b/Spectate/scripting/Spectate.sp new file mode 100644 index 0000000..391bdf0 --- /dev/null +++ b/Spectate/scripting/Spectate.sp @@ -0,0 +1,265 @@ +#pragma semicolon 1 + +#include +#include +#include + +#undef REQUIRE_PLUGIN +#tryinclude +#define REQUIRE_PLUGIN +#include "loghelper.inc" + +#pragma newdecls required + +bool g_bZRLoaded; +Handle g_hFwd_SpectateByCommand; + +bool g_bRoundEnd; + +public Plugin myinfo = +{ + name = "Spectate", + description = "Adds a command to spectate specific players and removes broken spectate mode.", + author = "Obus + BotoX", + version = "1.1", + url = "" +} + +// Spectator Movement modes (from smlib) +enum Obs_Mode +{ + OBS_MODE_NONE = 0, // not in spectator mode + OBS_MODE_DEATHCAM, // special mode for death cam animation + OBS_MODE_FREEZECAM, // zooms to a target, and freeze-frames on them + OBS_MODE_FIXED, // view from a fixed camera position + OBS_MODE_IN_EYE, // follow a player in first person view + OBS_MODE_CHASE, // follow a player in third person view + OBS_MODE_POI, // PASSTIME point of interest - game objective, big fight, anything interesting; added in the middle of the enum due to tons of hard-coded "(StringToInt(sSpecMode)); + + // Skip broken OBS_MODE_POI, but not on CS:GO + if (GetEngineVersion() != Engine_CSGO && iObserverMode == OBS_MODE_POI) + { + ClientCommand(client, "cl_spec_mode %d", OBS_MODE_ROAMING); + if(IsClientInGame(client) && !IsPlayerAlive(client)) + SetEntProp(client, Prop_Send, "m_iObserverMode", OBS_MODE_ROAMING); + } +} + +public Action Command_Spectate(int client, int argc) +{ + if (!client) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + +#if defined _zr_included + if (g_bZRLoaded && IsPlayerAlive(client) && ZR_IsClientZombie(client)) + { + bool bOnlyZombie = true; + for (int i = 1; i <= MaxClients; i++) + { + if (i != client && IsClientInGame(i) && IsPlayerAlive(i) && ZR_IsClientZombie(i)) + { + bOnlyZombie = false; + break; + } + } + + if (bOnlyZombie) + { + PrintToChat(client, "[SM] Can not switch to spectate as the last zombie!"); + return Plugin_Handled; + } + } +#endif + + if (!argc) + { + if (GetClientTeam(client) != CS_TEAM_SPECTATOR) + { +#if defined _zr_included + if (g_bZRLoaded && IsPlayerAlive(client) && ZR_IsClientHuman(client) && GetTeamAliveClientCount(CS_TEAM_T) > 0 && !g_bRoundEnd) + LogPlayerEvent(client, "triggered", "switch_to_spec"); +#endif + + Call_StartForward(g_hFwd_SpectateByCommand); + Call_PushCell(client); + Call_Finish(); + + StripPlayerKnifes(client); + ForcePlayerSuicide(client); + ChangeClientTeam(client, CS_TEAM_SPECTATOR); + } + + return Plugin_Handled; + } + + char sTarget[MAX_TARGET_LENGTH]; + GetCmdArg(1, sTarget, sizeof(sTarget)); + + int iTarget; + if ((iTarget = FindTarget(client, sTarget, false, false)) <= 0) + return Plugin_Handled; + + if (!IsPlayerAlive(iTarget)) + { + ReplyToCommand(client, "[SM] %t", "Target must be alive"); + return Plugin_Handled; + } + + if (GetClientTeam(client) != CS_TEAM_SPECTATOR) + { +#if defined _zr_included + if (g_bZRLoaded && IsPlayerAlive(client) && ZR_IsClientHuman(client) && GetTeamAliveClientCount(CS_TEAM_T) > 0 && !g_bRoundEnd) + LogPlayerEvent(client, "triggered", "switch_to_spec"); +#endif + + Call_StartForward(g_hFwd_SpectateByCommand); + Call_PushCell(client); + Call_Finish(); + + StripPlayerKnifes(client); + ForcePlayerSuicide(client); + ChangeClientTeam(client, CS_TEAM_SPECTATOR); + } + + SetEntPropEnt(client, Prop_Send, "m_hObserverTarget", iTarget); + + Obs_Mode iObserverMode = view_as(GetEntProp(client, Prop_Send, "m_iObserverMode")); + + // If the client is currently in free roaming then switch them to first person view + if (iObserverMode >= OBS_MODE_POI) + { + ClientCommand(client, "cl_spec_mode %d", OBS_MODE_IN_EYE); + SetEntProp(client, Prop_Send, "m_iObserverMode", OBS_MODE_IN_EYE); + } + + PrintToChat(client, "\x01[SM] Spectating \x04%N\x01.", iTarget); + + return Plugin_Handled; +} + +public Action Command_Suicide(int client, char[] command, int args) +{ +#if defined _zr_included + if (g_bZRLoaded && IsPlayerAlive(client) && ZR_IsClientHuman(client) && GetTeamAliveClientCount(CS_TEAM_T) > 0 && !g_bRoundEnd) + LogPlayerEvent(client, "triggered", "switch_to_spec"); +#endif + + return Plugin_Continue; +} + +// Fix spec_goto crash exploit +public Action Command_Goto(int client, const char[] command, int argc) +{ + if(argc == 5) + { + for(int i = 1; i <= 3; i++) + { + char sArg[64]; + GetCmdArg(i, sArg, 64); + float fArg = StringToFloat(sArg); + + if(FloatAbs(fArg) > 5000000000.0) + { + PrintToServer("%d -> %f > %f", i, FloatAbs(fArg), 5000000000.0); + return Plugin_Handled; + } + } + } + + return Plugin_Continue; +} + +stock int GetTeamAliveClientCount(int iTeam) +{ + int ret = 0; + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || GetClientTeam(i) != iTeam) + continue; + + if (!IsPlayerAlive(i)) + continue; + + ret++; + } + + return ret; +} + +stock void StripPlayerKnifes(int client) +{ + int w = -1; + + while ((w = GetPlayerWeaponSlot(client, CS_SLOT_KNIFE)) != -1) + { + if(IsValidEntity(w) && IsValidEdict(w)) + { + RemovePlayerItem(client, w); + AcceptEntityInput(w, "Kill"); + } + } +} diff --git a/Spectate/scripting/include/Spectate.inc b/Spectate/scripting/include/Spectate.inc new file mode 100644 index 0000000..c041831 --- /dev/null +++ b/Spectate/scripting/include/Spectate.inc @@ -0,0 +1,10 @@ +#if defined _Spectate_included + #endinput +#endif +#define _Spectate_included +/** + * Called when a client switches to spec by sm_spectate. + * + * @param client Client index of the caller. + */ +forward void OnPlayerSwitchedToSpectateByCommand(int client); \ No newline at end of file diff --git a/Status/gamedata/serverfps.games.txt b/Status/gamedata/serverfps.games.txt new file mode 100644 index 0000000..dc433aa --- /dev/null +++ b/Status/gamedata/serverfps.games.txt @@ -0,0 +1,74 @@ +"Games" +{ + "#default" + { + "#supported" + { + "engine" "orangebox" + "engine" "orangebox_valve" + "engine" "css" + } + "Addresses" + { + "HostTimeFrame" + { + "windows" + { + "signature" "GetStatsString" + "read" "79" + // ALTERNATIVE 1: -4 + } + "linux" + { + "signature" "host_frametime" + } + "mac" + { + "signature" "host_frametime" + } + } + } + + "Signatures" + { + "GetStatsString" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x83\xEC\x0C\xD9\xEE\x8D\x45\xFC\x56\x57\x50\x8D\x45\xF8" + /* 55 8B EC 83 EC 0C D9 EE 8D 45 FC 56 57 50 8D 45 F8 */ + /* ALTERNATIVE 1: 2B F0 D9 E8 8D 47 FF DE F1 56 83 EC 08 DD 1C 24 50 */ + } + + "host_frametime" + { + "library" "engine" + "linux" "@host_frametime" + "mac" "@host_frametime" + } + } + } + "csgo" + { + "Addresses" + { + "HostTimeFrame" + { + "linux" + { + "signature" "host_frametime" + "read" "4" + } + } + } + + "Signatures" + { + "host_frametime" + { + "library" "engine" + "linux" "\xF3\x0F\x10\x0D\x2A\x2A\x2A\x2A\x0F\x2F\x0D\x2A\x2A\x2A\x2A\x76\x28\xF3\x0F\x10\x05\x2A\x2A\x2A\x2A\xF3\x0F\x5E\xC1" + } + } + } +} + diff --git a/Status/scripting/Status.sp b/Status/scripting/Status.sp new file mode 100644 index 0000000..b5b011b --- /dev/null +++ b/Status/scripting/Status.sp @@ -0,0 +1,226 @@ +#pragma semicolon 1 + +#include +#include + +#tryinclude "serverfps.inc" + +#pragma newdecls required + +ConVar g_Cvar_HostIP; +ConVar g_Cvar_HostPort; +ConVar g_Cvar_HostName; +ConVar g_Cvar_HostTags; + +#if !defined _serverfps_included +int g_iTickRate; +#endif + +public Plugin myinfo = +{ + name = "Status Fixer", + author = "zaCade + BotoX + Obus", + description = "Fixes the \"status\" command", + version = "2.0", + url = "https://github.com/CSSZombieEscape/sm-plugins/tree/master/Status/" +}; + +public void OnPluginStart() +{ + g_Cvar_HostIP = FindConVar("hostip"); + g_Cvar_HostPort = FindConVar("hostport"); + g_Cvar_HostName = FindConVar("hostname"); + g_Cvar_HostTags = FindConVar("sv_tags"); + + // For some reason, a client doing "status" on CS:GO is never passed properly into the callback, so I settled for "_status" + AddCommandListener(Command_Status, "_status"); +} + +public Action Command_Status(int client, const char[] command, int args) +{ + if(!client || !IsClientInGame(client)) + return Plugin_Continue; + + static char sServerName[128]; + static char sServerTags[128]; + static char sServerAdress[128]; + + int iServerIP = g_Cvar_HostIP.IntValue; + int iServerPort = g_Cvar_HostPort.IntValue; + + g_Cvar_HostName.GetString(sServerName, sizeof(sServerName)); + g_Cvar_HostTags.GetString(sServerTags, sizeof(sServerTags)); + + Format(sServerAdress, sizeof(sServerAdress), "%d.%d.%d.%d:%d", iServerIP >>> 24 & 255, iServerIP >>> 16 & 255, iServerIP >>> 8 & 255, iServerIP & 255, iServerPort); + + static char sMapName[128]; + GetCurrentMap(sMapName, sizeof(sMapName)); + + float fPosition[3]; + GetClientAbsOrigin(client, fPosition); + + float fClientDataIn = GetClientAvgData(client, NetFlow_Incoming); + float fClientDataOut = GetClientAvgData(client, NetFlow_Outgoing); + float fServerDataIn; + float fServerDataOut; + + GetServerNetStats(fServerDataIn, fServerDataOut); + + int iRealClients; + int iFakeClients; + int iTotalClients; + + for(int player = 1; player <= MaxClients; player++) + { + if(IsClientConnected(player)) + { + iTotalClients++; + + if(IsClientSourceTV(player)) + iFakeClients++; + else + iRealClients++; + } + } + +#if defined _serverfps_included + float fServerTickRate = 1.0 / GetTickInterval(); + float fServerFPS = GetServerFPS(); + + fServerFPS = fServerFPS <= fServerTickRate ? fServerFPS : fServerTickRate; +#else + int iServerTickRate = RoundToZero(1.0 / GetTickInterval()); + int iTickRate = g_iTickRate; + + iTickRate = iTickRate <= iServerTickRate ? iTickRate : iServerTickRate; +#endif + + PrintToConsole(client, "hostname: %s", + sServerName); + +#if defined _serverfps_included + PrintToConsole(client, "tickrate: %.2f/%.2f (%d%%)", + fServerFPS, fServerTickRate, RoundToNearest((fServerFPS / fServerTickRate) * 100)); +#else + PrintToConsole(client, "tickrate: %d/%d (%d%%)", + iTickRate, iServerTickRate, RoundToNearest((float(iTickRate) / float(iServerTickRate)) * 100)); +#endif + + PrintToConsole(client, "udp/ip : %s", + sServerAdress); + + PrintToConsole(client, "net I/O : %.2f/%.2f KiB/s (You: %.2f/%.2f KiB/s)", + fServerDataIn / 1024, fServerDataOut / 1024, fClientDataIn / 1024, fClientDataOut / 1024); + + PrintToConsole(client, "map : %s at: %.0f x, %.0f y, %.0f z", + sMapName, fPosition[0], fPosition[1], fPosition[2]); + + PrintToConsole(client, "tags : %s", + sServerTags); + + PrintToConsole(client, "edicts : %d/%d/%d (used/max/free)", + GetEntityCount(), GetMaxEntities(), GetMaxEntities() - GetEntityCount()); + + PrintToConsole(client, "players : %d %s | %d %s (%d/%d)", + iRealClients, Multiple(iRealClients) ? "humans" : "human", iFakeClients, Multiple(iFakeClients) ? "bots" : "bot", iTotalClients, MaxClients); + + PrintToConsole(client, "# %8s %40s %24s %12s %4s %4s %10s %16s %s", + "userid", "name", "uniqueid", "connected", "ping", "loss", "state", "addr", "type"); + + for(int player = 1; player <= MaxClients; player++) + { + if(!IsClientConnected(player)) + continue; + + static char sPlayerID[8]; + static char sPlayerName[MAX_NAME_LENGTH + 2]; + static char sPlayerAuth[24]; + char sPlayerTime[12]; + char sPlayerPing[4]; + char sPlayerLoss[4]; + static char sPlayerState[16]; + char sPlayerAddr[16]; + char sPlayerType[64]; + + FormatEx(sPlayerID, sizeof(sPlayerID), "%d", GetClientUserId(player)); + FormatEx(sPlayerName, sizeof(sPlayerName), "\"%N\"", player); + + if(!GetClientAuthId(player, AuthId_Steam2, sPlayerAuth, sizeof(sPlayerAuth))) + FormatEx(sPlayerAuth, sizeof(sPlayerAuth), "STEAM_ID_PENDING"); + + if(IsFakeClient(player) && !IsClientSourceTV(player)) + FormatEx(sPlayerAuth, sizeof(sPlayerAuth), "STEAM_0:%d:%d", GetRandomInt(0, 1), GetRandomInt(10000000, 100000000)); + + if(!IsFakeClient(player)) + { + int iHours = RoundToFloor((GetClientTime(player) / 3600)); + int iMinutes = RoundToFloor((GetClientTime(player) - (iHours * 3600)) / 60); + int iSeconds = RoundToFloor((GetClientTime(player) - (iHours * 3600)) - (iMinutes * 60)); + + if (iHours) + FormatEx(sPlayerTime, sizeof(sPlayerTime), "%d:%02d:%02d", iHours, iMinutes, iSeconds); + else + FormatEx(sPlayerTime, sizeof(sPlayerTime), "%d:%02d", iMinutes, iSeconds); + + FormatEx(sPlayerPing, sizeof(sPlayerPing), "%d", RoundFloat(GetClientLatency(player, NetFlow_Outgoing) * 800)); + FormatEx(sPlayerLoss, sizeof(sPlayerLoss), "%d", RoundFloat(GetClientAvgLoss(player, NetFlow_Outgoing) * 100)); + } + else if(!IsClientSourceTV(player)) + { + FormatEx(sPlayerTime, sizeof(sPlayerTime), "%d:%d", GetRandomInt(0, 59), GetRandomInt(10, 59)); + FormatEx(sPlayerPing, sizeof(sPlayerPing), "%d", GetRandomInt(15, 200)); + FormatEx(sPlayerLoss, sizeof(sPlayerLoss), "0"); + } + + if(IsClientInGame(player)) + FormatEx(sPlayerState, sizeof(sPlayerState), "active"); + else + FormatEx(sPlayerState, sizeof(sPlayerState), "spawning"); + + if(GetAdminFlag(GetUserAdmin(client), Admin_RCON)) + { + if(!IsFakeClient(player)) + GetClientIP(player, sPlayerAddr, sizeof(sPlayerAddr)); + else if(!IsClientSourceTV(player)) + FormatEx(sPlayerAddr, sizeof(sPlayerAddr), "%d.%d.%d.%d", GetRandomInt(20, 200), GetRandomInt(0, 200), GetRandomInt(0, 200), GetRandomInt(0, 200)); + + if(IsClientSourceTV(player)) + FormatEx(sPlayerType, sizeof(sPlayerType), "FakeClient"); + else if(IsFakeClient(player)) + FormatEx(sPlayerType, sizeof(sPlayerType), "SteamLegit"); + //else + // PM_GetPlayerType(player, sPlayerType, sizeof(sPlayerType)); + + PrintToConsole(client, "# %8s %40s %24s %12s %4s %4s %10s %16s %s", + sPlayerID, sPlayerName, sPlayerAuth, sPlayerTime, sPlayerPing, sPlayerLoss, sPlayerState, sPlayerAddr, sPlayerType); + } + else + PrintToConsole(client, "# %8s %40s %24s %12s %4s %4s %s", + sPlayerID, sPlayerName, sPlayerAuth, sPlayerTime, sPlayerPing, sPlayerLoss, sPlayerState); + } + + return Plugin_Handled; +} + +public void OnGameFrame() +{ +#if !defined _serverfps_included //Inaccurate fallback + static float fLastEngineTime; + static int iTicks; + float fCurEngineTime = GetEngineTime(); //GetEngineTime() will become less and less accurate as server uptime goes up! + + iTicks++; + + if (fCurEngineTime - fLastEngineTime >= 1.0) + { + g_iTickRate = iTicks; + iTicks = 0; + fLastEngineTime = fCurEngineTime; + } +#endif +} + +stock bool Multiple(int num) +{ + return (!num || num > 1); +} diff --git a/Status/scripting/include/serverfps.inc b/Status/scripting/include/serverfps.inc new file mode 100644 index 0000000..165b365 --- /dev/null +++ b/Status/scripting/include/serverfps.inc @@ -0,0 +1,49 @@ +#if defined _serverfps_included + #endinput +#endif +#define _serverfps_included + +#include +#include + +stock float GetServerFPS() +{ + return 1.0 / view_as(LoadFromAddress(GetHostTimeFrame(), NumberType_Int32)); +} + +/* +* Internal Functions +*/ +stock Handle GetServerFPSConf() +{ + static Handle hGameConf = null; + + if (hGameConf == null) + { + hGameConf = LoadGameConfigFile("serverfps.games"); + + if (hGameConf == null) + { + SetFailState("Couldn't find \"serverfps.games\" configuration file"); + } + } + + return hGameConf; +} + +stock Address GetHostTimeFrame() +{ + static Address pHostTimeFrame = Address_Null; + + if (pHostTimeFrame == Address_Null) + { + pHostTimeFrame = GameConfGetAddress(GetServerFPSConf(), "HostTimeFrame"); + + if (pHostTimeFrame == Address_Null) + { + SetFailState("Failed to find time frame address"); + } + } + + return pHostTimeFrame; +} diff --git a/StopSound/scripting/StopSound.sp b/StopSound/scripting/StopSound.sp new file mode 100644 index 0000000..9851852 --- /dev/null +++ b/StopSound/scripting/StopSound.sp @@ -0,0 +1,512 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include +#include +#include + +#define PLUGIN_VERSION "3.1.0" + +bool g_bStopWeaponSounds[MAXPLAYERS+1] = { false, ... }; + +bool g_bStopWeaponSoundsHooked = false; + +Handle g_hCookieStopSound = null; + +public Plugin myinfo = +{ + name = "Toggle Game Sounds", + author = "GoD-Tony, edit by Obus + BotoX, Oleg Tsvetkov", + description = "Allows clients to stop hearing weapon sounds and map music", + version = PLUGIN_VERSION, + url = "http://www.sourcemod.net/" +}; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + if(GetEngineVersion() != Engine_CSGO && GetEngineVersion() != Engine_CSS) + { + strcopy(error, err_max, "This plugin supports only CS:GO and CS:S!"); + return APLRes_Failure; + } + + return APLRes_Success; +} + +public void OnPluginStart() +{ + // Load translations + LoadTranslations("plugin.stopsound.phrases"); + LoadTranslations("common.phrases"); // For On/Off buttons in Cookies Menu + + // Detect game and hook appropriate tempent. + AddTempEntHook("Shotgun Shot", Hook_ShotgunShot); + + CreateConVar("sm_stopsound_version", PLUGIN_VERSION, "Toggle Weapon Sounds", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_REPLICATED); + RegConsoleCmd("sm_stopsound", Command_StopSound, "Toggle hearing weapon sounds"); + RegConsoleCmd("sm_sound", Command_StopSound, "Toggle hearing weapon sounds"); + + // Cookies + g_hCookieStopSound = RegClientCookie("weaponsound_blocked", "Are weapon sounds enabled", CookieAccess_Protected); + + SetCookieMenuItem(CookieMenuHandler_StopSounds, 0, "Stop sounds"); + + // Suppress reload sound effects + UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); + + // Game-specific setup + if(GetEngineVersion() == Engine_CSGO) + { + // Weapon sounds will be caught here. + AddNormalSoundHook(Hook_NormalSound_CSGO); + + if(ReloadEffect != INVALID_MESSAGE_ID) + { + HookUserMessage(ReloadEffect, Hook_ReloadEffect_CSGO, true); + } + } + else // CS:S + { + // Weapon sounds will be caught here. + AddNormalSoundHook(Hook_NormalSound_CSS); + + if(ReloadEffect != INVALID_MESSAGE_ID) + { + HookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); + } + } + + // Late load + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client) && AreClientCookiesCached(client)) + { + OnClientCookiesCached(client); + } + } +} + +public void OnPluginEnd() +{ + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + { + OnClientDisconnect(client); + } + } + + // Remove tempent hook + RemoveTempEntHook("Shotgun Shot", Hook_ShotgunShot); + + // Find ReloadEffect + UserMsg ReloadEffect = GetUserMessageId("ReloadEffect"); + + // Remove game-specific + if(GetEngineVersion() == Engine_CSGO) + { + RemoveNormalSoundHook(Hook_NormalSound_CSGO); + + if(ReloadEffect != INVALID_MESSAGE_ID) + UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSGO, true); + } + else + { + RemoveNormalSoundHook(Hook_NormalSound_CSS); + + if(ReloadEffect != INVALID_MESSAGE_ID) + UnhookUserMessage(ReloadEffect, Hook_ReloadEffect_CSS, true); + } +} + +public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(event.GetInt("userid")); + + if(!IsClientInGame(client) || GetClientTeam(client) <= CS_TEAM_SPECTATOR) + return; + + if(g_bStopWeaponSounds[client]) + CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); +} + +public Action Command_StopSound(int client, int args) +{ + if(client == 0) + { + ReplyToCommand(client, "[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + g_bStopWeaponSounds[client] = !g_bStopWeaponSounds[client]; + CheckWeaponSoundsHooks(); + + if(g_bStopWeaponSounds[client]) + { + SetClientCookie(client, g_hCookieStopSound, "1"); + CReplyToCommand(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); + } + else + { + SetClientCookie(client, g_hCookieStopSound, ""); + CReplyToCommand(client, "%t %t", "Chat Prefix", "Weapon sounds enabled"); + } + + return Plugin_Handled; +} + + +public void OnClientCookiesCached(int client) +{ + char sBuffer[10]; + + // Weapon Sounds cookie + GetClientCookie(client, g_hCookieStopSound, sBuffer, sizeof(sBuffer)); + + if(sBuffer[0] != '\0') + { + g_bStopWeaponSounds[client] = true; + g_bStopWeaponSoundsHooked = true; + } + else + g_bStopWeaponSounds[client] = false; +} + +public void OnClientDisconnect(int client) +{ + g_bStopWeaponSounds[client] = false; + + CheckWeaponSoundsHooks(); +} + +void CheckWeaponSoundsHooks() +{ + bool bShouldHook = false; + + for(int i = 1; i <= MaxClients; i++) + { + if(g_bStopWeaponSounds[i]) + { + bShouldHook = true; + break; + } + } + + // Fake (un)hook because toggling actual hooks will cause server instability. + g_bStopWeaponSoundsHooked = bShouldHook; +} + +public void CookieMenuHandler_StopSounds(int client, CookieMenuAction action, any info, char[] buffer, int maxlen) +{ + if(action == CookieMenuAction_DisplayOption) + { + Format(buffer, maxlen, "%T", "Cookie Menu Stop Sounds", client); + } + else if(action == CookieMenuAction_SelectOption) + { + ShowStopSoundsSettingsMenu(client); + } +} + +void ShowStopSoundsSettingsMenu(int client) +{ + Menu menu = new Menu(MenuHandler_StopSoundsSettings); + + menu.SetTitle("%T", "Cookie Menu Stop Sounds Title", client); + + char sBuffer[128]; + + Format(sBuffer, sizeof(sBuffer), "%T%T", "Weapon Sounds", client, g_bStopWeaponSounds[client] ? "Disabled" : "Enabled", client); + menu.AddItem("0", sBuffer); + + menu.ExitBackButton = true; + menu.Display(client, MENU_TIME_FOREVER); +} + +public int MenuHandler_StopSoundsSettings(Menu menu, MenuAction action, int client, int selection) +{ + if(action == MenuAction_Cancel) + { + ShowCookieMenu(client); + } + else if(action == MenuAction_Select) + { + if(selection == 0) + { + g_bStopWeaponSounds[client] = !g_bStopWeaponSounds[client]; + CheckWeaponSoundsHooks(); + + if(g_bStopWeaponSounds[client]) + { + SetClientCookie(client, g_hCookieStopSound, "1"); + CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds disabled"); + } + else + { + SetClientCookie(client, g_hCookieStopSound, ""); + CPrintToChat(client, "%t %t", "Chat Prefix", "Weapon sounds enabled"); + } + + } + + ShowStopSoundsSettingsMenu(client); + } + else if(action == MenuAction_End) + { + delete menu; + } +} + +public Action Hook_NormalSound_CSS(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], + int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, + char soundEntry[PLATFORM_MAX_PATH], int &seed) +{ + if(!g_bStopWeaponSoundsHooked) + return Plugin_Continue; + + // Ignore non-weapon sounds. + if(channel != SNDCHAN_WEAPON && + !(channel == SNDCHAN_AUTO && strncmp(sample, "physics/flesh", 13) == 0) && + !(channel == SNDCHAN_VOICE && StrContains(sample, "player/headshot", true) != -1)) + { + return Plugin_Continue; + } + + int j = 0; + for(int i = 0; i < numClients; i++) + { + int client = clients[i]; + if(!g_bStopWeaponSounds[client] && IsClientInGame(client)) + { + // Keep client. + clients[j] = clients[i]; + j++; + } + } + + numClients = j; + + return (numClients > 0) ? Plugin_Changed : Plugin_Stop; +} + +public Action Hook_NormalSound_CSGO(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], + int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, + char soundEntry[PLATFORM_MAX_PATH], int &seed) +{ + if(!g_bStopWeaponSoundsHooked) + return Plugin_Continue; + + // Ignore non-weapon sounds. + if(channel != SNDCHAN_WEAPON && + !(channel == SNDCHAN_AUTO && strncmp(sample, "physics/flesh", 13) == 0) && + !(channel == SNDCHAN_STATIC && StrContains(sample, "player/headshot", true) != -1)) + { + return Plugin_Continue; + } + + int j = 0; + for(int i = 0; i < numClients; i++) + { + int client = clients[i]; + if(!g_bStopWeaponSounds[client] && IsClientInGame(client)) + { + // Keep client. + clients[j] = clients[i]; + j++; + } + } + + numClients = j; + + return (numClients > 0) ? Plugin_Changed : Plugin_Stop; +} + +public Action Hook_ShotgunShot(const char[] te_name, const int[] Players, int numClients, float delay) +{ + if(!g_bStopWeaponSoundsHooked) + return Plugin_Continue; + + // Check which clients need to be excluded. + int[] newClients = new int[numClients]; + int newTotal = 0; + + for(int i = 0; i < numClients; i++) + { + if(!g_bStopWeaponSounds[Players[i]]) + { + newClients[newTotal++] = Players[i]; + } + } + + if(newTotal == numClients) + { + // No clients were excluded. + return Plugin_Continue; + } + else if(newTotal == 0) + { + // All clients were excluded and there is no need to broadcast. + return Plugin_Stop; + } + + // Re-broadcast to clients that still need it. + if(GetEngineVersion() == Engine_CSGO) + { + float vTemp[3]; + TE_Start("Shotgun Shot"); + TE_ReadVector("m_vecOrigin", vTemp); + TE_WriteVector("m_vecOrigin", vTemp); + TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); + TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); + TE_WriteNum("m_weapon", TE_ReadNum("m_weapon")); + TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); + TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); + TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); + TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); + TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); + TE_Send(newClients, newTotal, delay); + } + else + { + float vTemp[3]; + TE_Start("Shotgun Shot"); + TE_ReadVector("m_vecOrigin", vTemp); + TE_WriteVector("m_vecOrigin", vTemp); + TE_WriteFloat("m_vecAngles[0]", TE_ReadFloat("m_vecAngles[0]")); + TE_WriteFloat("m_vecAngles[1]", TE_ReadFloat("m_vecAngles[1]")); + TE_WriteNum("m_iWeaponID", TE_ReadNum("m_iWeaponID")); + TE_WriteNum("m_iMode", TE_ReadNum("m_iMode")); + TE_WriteNum("m_iSeed", TE_ReadNum("m_iSeed")); + TE_WriteNum("m_iPlayer", TE_ReadNum("m_iPlayer")); + TE_WriteFloat("m_fInaccuracy", TE_ReadFloat("m_fInaccuracy")); + TE_WriteFloat("m_fSpread", TE_ReadFloat("m_fSpread")); + TE_Send(newClients, newTotal, delay); + } + + return Plugin_Stop; +} + +public Action Hook_ReloadEffect_CSS(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init) +{ + if(!g_bStopWeaponSoundsHooked) + return Plugin_Continue; + + int client = msg.ReadShort(); + + // Check which clients need to be excluded. + int[] newClients = new int[playersNum]; + int newTotal = 0; + + for(int i = 0; i < playersNum; i++) + { + int client_ = players[i]; + if(IsClientInGame(client_) && !g_bStopWeaponSounds[client_]) + { + newClients[newTotal++] = client_; + } + } + + if(newTotal == playersNum) + { + // No clients were excluded. + return Plugin_Continue; + } + else if(newTotal == 0) + { + // All clients were excluded and there is no need to broadcast. + return Plugin_Handled; + } + + DataPack pack = new DataPack(); + pack.WriteCell(client); + pack.WriteCell(newTotal); + + for(int i = 0; i < newTotal; i++) + { + pack.WriteCell(newClients[i]); + } + + RequestFrame(OnReloadEffect, pack); + + return Plugin_Handled; +} + +public Action Hook_ReloadEffect_CSGO(UserMsg msg_id, Protobuf msg, const int[] players, int playersNum, bool reliable, bool init) +{ + if(!g_bStopWeaponSoundsHooked) + return Plugin_Continue; + + int client = PbReadInt(msg, "entidx"); + + // Check which clients need to be excluded. + int[] newClients = new int[playersNum]; + int newTotal = 0; + + for(int i = 0; i < playersNum; i++) + { + int client_ = players[i]; + if(IsClientInGame(client_) && !g_bStopWeaponSounds[client_]) + { + newClients[newTotal++] = client_; + } + } + + if(newTotal == playersNum) + { + // No clients were excluded. + return Plugin_Continue; + } + else if(newTotal == 0) + { + // All clients were excluded and there is no need to broadcast. + return Plugin_Handled; + } + + DataPack pack = new DataPack(); + pack.WriteCell(client); + pack.WriteCell(newTotal); + + for(int i = 0; i < newTotal; i++) + { + pack.WriteCell(newClients[i]); + } + + RequestFrame(OnReloadEffect, pack); + + return Plugin_Handled; +} + +public void OnReloadEffect(DataPack pack) +{ + pack.Reset(); + int client = pack.ReadCell(); + int newTotal = pack.ReadCell(); + + int[] players = new int[newTotal]; + int playersNum = 0; + + for(int i = 0; i < newTotal; i++) + { + int client_ = pack.ReadCell(); + if(IsClientInGame(client_)) + { + players[playersNum++] = client_; + } + } + + delete pack; + + Handle ReloadEffect = StartMessage("ReloadEffect", players, playersNum, USERMSG_RELIABLE | USERMSG_BLOCKHOOKS); + if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) + { + PbSetInt(ReloadEffect, "entidx", client); + } + else + { + BfWriteShort(ReloadEffect, client); + } + + EndMessage(); +} diff --git a/StopSound/translations/plugin.stopsound.phrases.txt b/StopSound/translations/plugin.stopsound.phrases.txt new file mode 100644 index 0000000..970a37a --- /dev/null +++ b/StopSound/translations/plugin.stopsound.phrases.txt @@ -0,0 +1,50 @@ +"Phrases" +{ + "Chat Prefix" + { + "en" "{green}[StopSound]{default}" + "ru" "{green}[StopSound]{default}" + } + + "Weapon sounds enabled" + { + "en" "Weapon sounds {green}enabled{default}!" + "ru" "Звуки стрельбы {green}включены{default}!" + } + + "Weapon sounds disabled" + { + "en" "Weapon sounds {darkred}disabled{default}!" + "ru" "Звуки стрельбы {darkred}отключены{default}!" + } + + "Cookie Menu Stop Sounds" + { + "en" "Stop Sounds" + "ru" "Отключение звуков" + } + + "Cookie Menu Stop Sounds Title" + { + "en" "Stop Sounds Configuration" + "ru" "Настройки Отключения Звуков" + } + + "Weapon Sounds" + { + "en" "Weapon sounds" + "ru" "Звуки стрельбы" + } + + "Disabled" + { + "en" ": Disabled" + "ru" " [Выключено]" + } + + "Enabled" + { + "en" ": Enabled" + "ru" " [Включено]" + } +} \ No newline at end of file diff --git a/TOGConsoleColors/scripting/TOGConsoleColors.sp b/TOGConsoleColors/scripting/TOGConsoleColors.sp new file mode 100644 index 0000000..e78a95c --- /dev/null +++ b/TOGConsoleColors/scripting/TOGConsoleColors.sp @@ -0,0 +1,80 @@ +#pragma semicolon 1 +#define PLUGIN_VERSION "1.3" + +#include +#include + +new Handle:g_hConsoleName = INVALID_HANDLE; +new String:g_sConsoleName[64]; +new Handle:g_hConsoleChatColor = INVALID_HANDLE; +new String:g_sConsoleChatColor[32]; + +char C_Tag[][] = {"{default}", "{red}", "{lightpurple}", "{green}", "{lime}", "{lightgreen}", "{lightred}", "{gray}", "{lightolive}", "{olive}", "{purple}", "{lightblue}", "{blue}"}; +char C_Code[][]= {"\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x10", "\x0E", "\x0B", "\x0C"}; + +#define MAX_COLORS 13 + +public Plugin:myinfo = +{ + name = "Console Colors", + author = "That One Guy + xen", + description = "Colors chat output from CONSOLE", + version = PLUGIN_VERSION, + url = "https://forums.alliedmods.net" +} + +public OnPluginStart() +{ + g_hConsoleName = CreateConVar("togcc_consolename", "CONSOLE", "Name to use for console.", FCVAR_NONE); + GetConVarString(g_hConsoleName, g_sConsoleName, sizeof(g_sConsoleName)); + HookConVarChange(g_hConsoleName, OnCVarChange); + + g_hConsoleChatColor = CreateConVar("togcc_chatcolor", "\x04", "Hexadecimal value to use for console chat color (do not include #)", FCVAR_NONE); + GetConVarString(g_hConsoleChatColor, g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + HookConVarChange(g_hConsoleChatColor, OnCVarChange); + + AddCommandListener(Command_Say, "say"); + AddCommandListener(Command_Say, "say_team"); + + AutoExecConfig(true, "TOGConsoleColors"); +} + +public Action:Command_Say(client, String:Command[], ArgC) +{ + if(client) + { + return Plugin_Continue; + } + + decl String:sMessage[512]; + GetCmdArgString(sMessage, 512); + StripQuotes(sMessage); + TrimString(sMessage); + ConPrintToChatAll("\x02\x0E%s %s%s", g_sConsoleName, g_sConsoleChatColor, sMessage); + PrintToServer("%s: %s", g_sConsoleName, sMessage); + return Plugin_Handled; +} + +public OnCVarChange(Handle:cvar, const String:oldvalue[], const String:newvalue[]) +{ + if(cvar == g_hConsoleName) + { + GetConVarString(g_hConsoleName, g_sConsoleName, sizeof(g_sConsoleName)); + } + if(cvar == g_hConsoleChatColor) + { + GetConVarString(g_hConsoleChatColor, g_sConsoleChatColor, sizeof(g_sConsoleChatColor)); + } +} + +stock ConPrintToChatAll(const String:message[], any:...) +{ + decl String:text0[512], String:text[512]; + FormatEx(text0, sizeof(text0), " %s", message); + VFormat(text, sizeof(text), text0, 2); + + for (int i = 0; i < MAX_COLORS; i++) + ReplaceString(text, sizeof(text), C_Tag[i], C_Code[i]); + + PrintToChatAll(text); +} diff --git a/TeamManager/scripting/TeamManager.sp b/TeamManager/scripting/TeamManager.sp new file mode 100644 index 0000000..05de71f --- /dev/null +++ b/TeamManager/scripting/TeamManager.sp @@ -0,0 +1,259 @@ +#include +#include +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +#define MIN_PLAYERS 2 + +int g_iWarmup = 0; +int g_iMaxWarmup = 0; +bool g_bWarmup = false; +ConVar g_CVar_sm_warmuptime; +ConVar g_CVar_sm_warmupratio; +ConVar g_CVar_sm_warmupmaxtime; +bool g_bLateLoad = false; + +bool g_bRoundEnded = false; +bool g_bZombieSpawned = false; +int g_TeamChangeQueue[MAXPLAYERS + 1] = { -1, ... }; + +public Plugin myinfo = +{ + name = "TeamManager", + author = "BotoX", + description = "", + version = "1.0", + url = "https://github.com/CSSZombieEscape/sm-plugins/tree/master/TeamManager" +}; + +public void OnPluginStart() +{ + if (GetEngineVersion() == Engine_CSGO) + AddCommandListener(OnJoinTeamCommand, "joingame"); + + AddCommandListener(OnJoinTeamCommand, "jointeam"); + HookEvent("round_start", OnRoundStart, EventHookMode_Pre); + HookEvent("round_end", OnRoundEnd, EventHookMode_PostNoCopy); + + g_CVar_sm_warmuptime = CreateConVar("sm_warmuptime", "10", "Warmup timer.", 0, true, 0.0, true, 60.0); + g_CVar_sm_warmupratio = CreateConVar("sm_warmupratio", "0.60", "Ratio of connected players that need to be in game to start warmup timer.", 0, true, 0.0, true, 1.0); + g_CVar_sm_warmupmaxtime = CreateConVar("sm_warmupmaxtime", "45", "Max Warmup timer.", 0, true, 0.0, true, 120.0); + + AutoExecConfig(true, "plugin.TeamManager"); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && IsPlayerAlive(i) && ZR_IsClientZombie(i)) + { + g_bZombieSpawned = true; + break; + } + } +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_bLateLoad = late; + return APLRes_Success; +} + +public void OnConfigsExecuted() +{ + if(g_bLateLoad) + { + g_bLateLoad = false; + return; + } + + g_iWarmup = 0; + g_iMaxWarmup = 0; + g_bWarmup = false; + g_bRoundEnded = false; + g_bZombieSpawned = false; + + if(g_CVar_sm_warmuptime.IntValue > 0 || g_CVar_sm_warmupratio.FloatValue > 0.0) + { + g_bWarmup = true; + CreateTimer(1.0, OnWarmupTimer, 0, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } +} + +public Action OnWarmupTimer(Handle timer) +{ + if(g_iMaxWarmup >= g_CVar_sm_warmupmaxtime.IntValue) + { + g_iMaxWarmup = 0; + g_bWarmup = false; + CS_TerminateRound(3.0, CSRoundEnd_GameStart, false); + return Plugin_Stop; + } + + if(g_iWarmup >= g_CVar_sm_warmuptime.IntValue) + { + g_iWarmup = 0; + g_bWarmup = false; + CS_TerminateRound(3.0, CSRoundEnd_GameStart, false); + return Plugin_Stop; + } + + if(g_CVar_sm_warmupratio.FloatValue > 0.0) + { + int ClientsConnected = GetClientCount(false); + int ClientsInGame = GetClientCount(true); + int ClientsNeeded = RoundToCeil(float(ClientsConnected) * g_CVar_sm_warmupratio.FloatValue); + ClientsNeeded = ClientsNeeded > MIN_PLAYERS ? ClientsNeeded : MIN_PLAYERS; + + if(ClientsInGame < ClientsNeeded) + { + g_iMaxWarmup++; + PrintCenterTextAll("Warmup: Waiting for %d more players to join or %d seconds.", ClientsNeeded - ClientsInGame, g_CVar_sm_warmupmaxtime.IntValue - g_iMaxWarmup); + return Plugin_Continue; + } + else + { + g_iWarmup++; + PrintCenterTextAll("Warmup: Enough players joined. %d seconds left. ", g_CVar_sm_warmuptime.IntValue - g_iWarmup); + return Plugin_Continue; + } + } + + return Plugin_Continue; +} + +public void OnClientDisconnect(int client) +{ + g_TeamChangeQueue[client] = -1; +} + +public Action OnJoinTeamCommand(int client, const char[] command, int argc) +{ + if(client < 1 || client >= MaxClients || !IsClientInGame(client)) + return Plugin_Continue; + + if(StrEqual(command, "joingame", false)) + { + if(GetClientTeam(client) != CS_TEAM_NONE) + return Plugin_Continue; + + ShowVGUIPanel(client, "team"); + return Plugin_Handled; + } + + char sArg[8]; + GetCmdArg(1, sArg, sizeof(sArg)); + + int CurrentTeam = GetClientTeam(client); + int NewTeam = StringToInt(sArg); + + if(NewTeam < CS_TEAM_NONE || NewTeam > CS_TEAM_CT) + return Plugin_Handled; + + // prevent accidental suicide + if(g_bZombieSpawned && IsPlayerAlive(client) && NewTeam != CS_TEAM_SPECTATOR) + return Plugin_Handled; + + if(g_bRoundEnded) + { + if(NewTeam == CS_TEAM_T || NewTeam == CS_TEAM_NONE) + NewTeam = CS_TEAM_CT; + + if(NewTeam == CurrentTeam) + { + if(g_TeamChangeQueue[client] != -1) + { + g_TeamChangeQueue[client] = -1; + PrintCenterText(client, "Team change request canceled."); + } + return Plugin_Handled; + } + + g_TeamChangeQueue[client] = NewTeam; + PrintCenterText(client, "You will be placed in the selected team shortly."); + return Plugin_Handled; + } + + if(!g_bZombieSpawned) + { + if(NewTeam == CS_TEAM_T || NewTeam == CS_TEAM_NONE) + NewTeam = CS_TEAM_CT; + } + else if(NewTeam == CS_TEAM_CT || NewTeam == CS_TEAM_NONE) + NewTeam = CS_TEAM_T; + + if(g_bZombieSpawned && NewTeam == CurrentTeam) + return Plugin_Handled; + + ForcePlayerSuicide(client); + ChangeClientTeam(client, CS_TEAM_NONE); + ChangeClientTeam(client, NewTeam); + return Plugin_Handled; +} + +public void OnRoundStart(Event event, const char[] name, bool dontBroadcast) +{ + g_bRoundEnded = false; + g_bZombieSpawned = false; + + for(int client = 1; client <= MaxClients; client++) + { + if(!IsClientInGame(client)) + continue; + + int CurrentTeam = GetClientTeam(client); + int NewTeam = CS_TEAM_CT; + + if(g_TeamChangeQueue[client] != -1) + { + NewTeam = g_TeamChangeQueue[client]; + g_TeamChangeQueue[client] = -1; + } + else if(CurrentTeam <= CS_TEAM_SPECTATOR) + continue; + + if(NewTeam == CurrentTeam) + continue; + + if(NewTeam >= CS_TEAM_T) + CS_SwitchTeam(client, NewTeam); + else + ChangeClientTeam(client, NewTeam); + + if(NewTeam >= CS_TEAM_T && !IsPlayerAlive(client)) + CS_RespawnPlayer(client); + } +} + +public void OnRoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + g_bRoundEnded = true; + g_bZombieSpawned = false; +} + +public Action CS_OnTerminateRound(float &delay, CSRoundEndReason &reason) +{ + if(g_bWarmup) + return Plugin_Handled; + + return Plugin_Continue; +} + +public Action ZR_OnClientInfect(int &client, int &attacker, bool &motherInfect, bool &respawnOverride, bool &respawn) +{ + if(g_bWarmup) + return Plugin_Handled; + + g_bZombieSpawned = true; + + return Plugin_Continue; +} + +public Action ZR_OnInfectCountdown() +{ + if(g_bWarmup) + return Plugin_Handled; + + return Plugin_Continue; +} diff --git a/TeamManager/scripting/include/TeamManager.inc b/TeamManager/scripting/include/TeamManager.inc new file mode 100644 index 0000000..ab800c7 --- /dev/null +++ b/TeamManager/scripting/include/TeamManager.inc @@ -0,0 +1,36 @@ +#if defined _TeamManager_include + #endinput +#endif +#define _TeamManager_include + +/** + * Called when warmup ends + * + * @return None + */ +forward void TeamManager_WarmupEnd(); + +/** + * Returns the status of warmup + * + * @return bool inwarmup + */ +native bool TeamManager_InWarmup(); + +public SharedPlugin __pl_TeamManager = +{ + name = "TeamManager", + file = "TeamManager.smx", +#if defined REQUIRE_PLUGIN + required = 1 +#else + required = 0 +#endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_TeamManager_SetNTVOptional() +{ + MarkNativeAsOptional("TeamManager_InWarmup"); +} +#endif diff --git a/Teleport/scripting/Teleport.sp b/Teleport/scripting/Teleport.sp new file mode 100644 index 0000000..c263a7f --- /dev/null +++ b/Teleport/scripting/Teleport.sp @@ -0,0 +1,330 @@ +#pragma semicolon 1 + +#include +#include + +#pragma newdecls required + +public Plugin myinfo = +{ + name = "Teleport Commands", + author = "Obus", + description = "Adds commands to teleport clients.", + version = "1.3.1", + url = "https://github.com/CSSZombieEscape/sm-plugins/blob/master/Teleport/" +} + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + + RegAdminCmd("sm_bring", Command_Bring, ADMFLAG_GENERIC, "Brings a client to your position."); + RegAdminCmd("sm_goto", Command_Goto, ADMFLAG_GENERIC, "Teleport to a client."); + RegAdminCmd("sm_send", Command_Send, ADMFLAG_GENERIC, "Send a client to another client."); + RegAdminCmd("sm_tpaim", Command_TpAim, ADMFLAG_GENERIC, "Teleport a client to your aimpoint."); +} + +public Action Command_Bring(int client, int argc) +{ + if (!client) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + if (argc < 1) + { + PrintToChat(client, "[SM] Usage: sm_bring "); + return Plugin_Handled; + } + + char sArgs[64]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + + if ((iTargetCount = ProcessTargetString(sArgs, client, iTargets, MAXPLAYERS, COMMAND_FILTER_ALIVE, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + float vecClientPos[3]; + + GetClientAbsOrigin(client, vecClientPos); + + for (int i = 0; i < iTargetCount; i++) + { + TeleportEntity(iTargets[i], vecClientPos, NULL_VECTOR, NULL_VECTOR); + } + + ShowActivity2(client, "\x01[SM] \x04", "\x01Brought \x04%s\x01", sTargetName); + + if (iTargetCount > 1) + LogAction(client, -1, "\"%L\" brought \"%s\"", client, sTargetName); + else + LogAction(client, iTargets[0], "\"%L\" brought \"%L\"", client, iTargets[0]); + + return Plugin_Handled; +} + +public Action Command_Goto(int client, int argc) +{ + if (!client) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + if (argc < 1) + { + PrintToChat(client, "[SM] Usage: sm_goto "); + return Plugin_Handled; + } + + int iTarget; + char sTarget[32]; + + GetCmdArg(1, sTarget, sizeof(sTarget)); + + if (strcmp(sTarget, "@aim") == 0) + { + if (argc > 1) + { + char sOption[2]; + + GetCmdArg(2, sOption, sizeof(sOption)); + + if (StringToInt(sOption) <= 0) + { + float vecAimPoint[3]; + + if (!TracePlayerAngles(client, vecAimPoint)) + { + PrintToChat(client, "[SM] Couldn't perform trace to your aimpoint."); + return Plugin_Handled; + } + + TeleportEntity(client, vecAimPoint, NULL_VECTOR, NULL_VECTOR); + + ShowActivity3(client, "\x01[SM] \x04", "\x01Teleported to their aimpoint."); + ReplyToCommand(client, "[SM] Teleported you to your aimpoint."); + LogAction(client, -1, "\"%L\" teleported to their aimpoint", client); + + return Plugin_Handled; + } + } + + int iAimTarget = GetClientAimTarget(client, true); + + if (iAimTarget == -1) + { + float vecAimPoint[3]; + + if (!TracePlayerAngles(client, vecAimPoint)) + { + PrintToChat(client, "[SM] Couldn't perform trace to your aimpoint."); + return Plugin_Handled; + } + + TeleportEntity(client, vecAimPoint, NULL_VECTOR, NULL_VECTOR); + + ShowActivity3(client, "\x01[SM] \x04", "\x01Teleported to their aimpoint."); + ReplyToCommand(client, "[SM] Teleported you to your aimpoint."); + LogAction(client, -1, "\"%L\" teleported to their aimpoint", client); + + return Plugin_Handled; + } + } + + if ((iTarget = FindTarget(client, sTarget)) <= 0) + return Plugin_Handled; + + float vecTargetPos[3]; + + GetClientAbsOrigin(iTarget, vecTargetPos); + + TeleportEntity(client, vecTargetPos, NULL_VECTOR, NULL_VECTOR); + + ShowActivity2(client, "\x01[SM] \x04", "\x01Teleported to \x04%N\x01.", iTarget); + LogAction(client, iTarget, "\"%L\" teleported to \"%L\"", client, iTarget); + + return Plugin_Handled; +} + +public Action Command_Send(int client, int argc) +{ + if (argc < 2) + { + PrintToChat(client, "[SM] Usage: sm_send "); + return Plugin_Handled; + } + + int iTarget; + char sArgs[32]; + char sTarget[32]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + GetCmdArg(2, sTarget, sizeof(sTarget)); + + if ((iTargetCount = ProcessTargetString(sArgs, client, iTargets, MAXPLAYERS, COMMAND_FILTER_ALIVE, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + if (strcmp(sTarget, "@aim") == 0) + { + if (!client) + { + ReplyToCommand(client, "[SM] Cannot use \"sm_send @aim\" from server console."); + return Plugin_Handled; + } + + float vecAimPoint[3]; + + if (!TracePlayerAngles(client, vecAimPoint)) + { + PrintToChat(client, "[SM] Couldn't perform trace to your aimpoint."); + return Plugin_Handled; + } + + for (int i = 0; i < iTargetCount; i++) + { + TeleportEntity(iTargets[i], vecAimPoint, NULL_VECTOR, NULL_VECTOR); + } + + ShowActivity3(client, "\x01[SM] \x04", "\x01Teleported \x04%s\x01 to their aimpoint.", sTargetName); + ReplyToCommand(client, "\x01[SM] Teleported \x04%s\x01 to your aimpoint.", sTargetName); + + if (iTargetCount > 1) + LogAction(client, -1, "\"%L\" teleported target \"%s\" to their aimpoint", client, sTargetName); + else + LogAction(client, iTargets[0], "\"%L\" teleported target \"%L\" to their aimpoint", client, iTargets[0]); + + return Plugin_Handled; + } + + if ((iTarget = FindTarget(client, sTarget)) <= 0) + return Plugin_Handled; + + float vecTargetPos[3]; + + GetClientAbsOrigin(iTarget, vecTargetPos); + + for (int i = 0; i < iTargetCount; i++) + { + TeleportEntity(iTargets[i], vecTargetPos, NULL_VECTOR, NULL_VECTOR); + } + + ShowActivity2(client, "\x01[SM] \x04", "\x01Teleported \x04%s\x01 to \x04%N\x01.", sTargetName, iTarget); + + if (iTargetCount > 1) + LogAction(client, -1, "\"%L\" teleported target \"%s\" to \"%L\"", client, sTargetName, iTarget); + else + LogAction(client, iTargets[0], "\"%L\" teleported target \"%L\" to \"%L\"", client, iTargets[0], iTarget); + + return Plugin_Handled; +} + +public Action Command_TpAim(int client, int argc) +{ + if (!client) + { + PrintToServer("[SM] Cannot use command from server console."); + return Plugin_Handled; + } + + char sArgs[32]; + char sTargetName[MAX_TARGET_LENGTH]; + int iTargets[MAXPLAYERS]; + int iTargetCount; + bool bIsML; + + GetCmdArg(1, sArgs, sizeof(sArgs)); + + if ((iTargetCount = ProcessTargetString(sArgs, client, iTargets, MAXPLAYERS, COMMAND_FILTER_ALIVE, sTargetName, sizeof(sTargetName), bIsML)) <= 0) + { + ReplyToTargetError(client, iTargetCount); + return Plugin_Handled; + } + + float vecAimPoint[3]; + + TracePlayerAngles(client, vecAimPoint); + + for (int i = 0; i < iTargetCount; i++) + { + TeleportEntity(iTargets[i], vecAimPoint, NULL_VECTOR, NULL_VECTOR); + } + + ShowActivity3(client, "\x01[SM] \x04", "\x01Teleported \x04%s\x01 to their aimpoint.", sTargetName); + ReplyToCommand(client, "\x01[SM] Teleported \x04%s\x01 to your aimpoint.", sTargetName); + + if (iTargetCount > 1) + LogAction(client, -1, "\"%L\" teleported \"%s\" to their aimpoint", client, sTargetName); + else + LogAction(client, iTargets[0], "\"%L\" teleported \"%L\" to their aimpoint", client, iTargets[0]); + + return Plugin_Handled; +} + +bool TracePlayerAngles(int client, float vecResult[3]) +{ + if (!IsClientInGame(client)) + return false; + + float vecEyeAngles[3]; + float vecEyeOrigin[3]; + + GetClientEyeAngles(client, vecEyeAngles); + GetClientEyePosition(client, vecEyeOrigin); + + Handle hTraceRay = TR_TraceRayFilterEx(vecEyeOrigin, vecEyeAngles, MASK_SHOT_HULL, RayType_Infinite, TraceEntityFilter_FilterPlayers); + + if (TR_DidHit(hTraceRay)) + { + TR_GetEndPosition(vecResult, hTraceRay); + + delete hTraceRay; + + return true; + } + + delete hTraceRay; + + return false; +} + +stock bool TraceEntityFilter_FilterPlayers(int entity, int contentsMask) +{ + return entity > MaxClients; +} + +stock void ShowActivity3(int client, const char[] tag, const char[] fmt, any ...) +{ + char sFinal[255]; + char sFormatted[255]; + char sActivitySource[MAX_NAME_LENGTH]; + + FormatActivitySource(client, client, sActivitySource, sizeof(sActivitySource)); + + VFormat(sFormatted, sizeof(sFormatted), fmt, 4); + + Format(sFinal, sizeof(sFinal), "%s%s: %s", tag, sActivitySource, sFormatted); + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i) || i == client) + continue; + + PrintToChat(i, sFinal); + } +} diff --git a/UNLOZE_BanDetector/scripting/UNLOZE_BanDetector.sp b/UNLOZE_BanDetector/scripting/UNLOZE_BanDetector.sp new file mode 100644 index 0000000..01659f4 --- /dev/null +++ b/UNLOZE_BanDetector/scripting/UNLOZE_BanDetector.sp @@ -0,0 +1,189 @@ +//==================================================================================================== +// +// Name: i3D.net Ban Detector. +// Author: zaCade +// Description: Detect potential ban evasions. +// +//==================================================================================================== +#undef REQUIRE_PLUGIN +#include +#include +#include + +new G_iParentAuthID[MAXPLAYERS+1]; +new G_iClientAuthID[MAXPLAYERS+1]; + +new bool:G_bSourcebansAvailable; +new Handle:G_hDatabase; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin:myinfo = +{ + name = "UNLOZE Ban Detector", + author = "zaCade", + description = "Detect potential ban evasions", + version = "1.0", +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnPluginStart() +{ + SQL_TConnect(SQL_OnConnected, "sourcebans"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnAllPluginsLoaded() +{ + if (LibraryExists("sourcebans")) + G_bSourcebansAvailable = true; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnLibraryAdded(const String:name[]) +{ + if (StrEqual("sourcebans", name)) + G_bSourcebansAvailable = true; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnLibraryRemoved(const String:name[]) +{ + if (StrEqual("sourcebans", name)) + G_bSourcebansAvailable = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnClientConnected(client) +{ + G_iParentAuthID[client] = -1; + G_iClientAuthID[client] = -1; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnClientDisconnect(client) +{ + G_iParentAuthID[client] = -1; + G_iClientAuthID[client] = -1; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public OnClientPostAdminCheck(client) +{ + if (G_bSourcebansAvailable && !IsFakeClient(client)) + { + if (G_iParentAuthID[client] != -1 && G_iClientAuthID[client] != -1) + CheckBans_SteamID(client, G_iParentAuthID[client], G_iClientAuthID[client]); + + CheckBans_IPAdress(client); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public SteamWorks_OnValidateClient(parentAuthID, clientAuthID) +{ + if (G_bSourcebansAvailable && parentAuthID != clientAuthID) + { + for (new client = 1; client <= MaxClients; client++) + { + if (IsClientConnected(client) && GetSteamAccountID(client) == clientAuthID) + { + if (!IsClientInGame(client)) + { + G_iParentAuthID[client] = parentAuthID; + G_iClientAuthID[client] = clientAuthID; + break; + } + else + { + CheckBans_SteamID(client, parentAuthID, clientAuthID); + break; + } + } + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public CheckBans_SteamID(client, parentAuthID, clientAuthID) +{ + new String:authid[32]; + Format(authid, sizeof(authid), "STEAM_0:%d:%d", (parentAuthID & 1), (parentAuthID >> 1)); + + new String:query[512]; + Format(query, sizeof(query), "SELECT * FROM sb_bans WHERE authid = '%s' AND ((length = 0 OR ends > UNIX_TIMESTAMP()) AND removetype IS NULL)", authid); +// Format(query, sizeof(query), "SELECT * FROM sb_bans WHERE authid = '%s' AND ((ends > '%d' AND length != '0') OR length = '0')", authid, GetTime()); + + LogMessage("[BanDetector] Checking family sharing user %L (AUTH: %s, PAUTH: %d, CAUTH: %d)", client, authid, parentAuthID, clientAuthID); + + if (G_hDatabase != INVALID_HANDLE) + SQL_TQuery(G_hDatabase, SQL_OnCheckClientSteamID, query, client); + else + LogMessage("[BanDetector] Unable to check user %L (No database)", client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public CheckBans_IPAdress(client) +{ + new String:adress[32]; + GetClientIP(client, adress, sizeof(adress)); + + new String:query[512]; + Format(query, sizeof(query), "SELECT * FROM sb_bans WHERE ip = '%s' AND ((length = 0 OR ends > UNIX_TIMESTAMP()) AND removetype IS NULL AND (aid != 0 OR reason != 'Ban evasion (IP)')", adress); +// Format(query, sizeof(query), "SELECT * FROM sb_bans WHERE ip = '%s' AND ((ends > '%d' AND length != '0') OR length = '0') AND ((reason != 'Ban evasion (IP)' AND aid = '0') OR aid != '0')", adress, GetTime()); + + LogMessage("[BanDetector] Checking user %L (IP: %s)", client, adress); + + if (G_hDatabase != INVALID_HANDLE) + SQL_TQuery(G_hDatabase, SQL_OnCheckClientIP, query, client); + else + LogMessage("[BanDetector] Unable to check user %L (No database)", client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public SQL_OnConnected(Handle:owner, Handle:handle, const String:error[], any:data) +{ + if (handle != INVALID_HANDLE) + G_hDatabase = handle; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public SQL_OnCheckClientSteamID(Handle:owner, Handle:handle, const String:error[], any:client) +{ + if (handle != INVALID_HANDLE && SQL_FetchRow(handle)) + SBBanPlayer(0, client, 0, "Ban evasion (FS)"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public SQL_OnCheckClientIP(Handle:owner, Handle:handle, const String:error[], any:client) +{ + if (handle != INVALID_HANDLE && SQL_FetchRow(handle)) + SBBanPlayer(0, client, 0, "Ban evasion (IP)"); +} \ No newline at end of file diff --git a/UNLOZE_Clantag/scripting/UNLOZE_Clantag.sp b/UNLOZE_Clantag/scripting/UNLOZE_Clantag.sp new file mode 100644 index 0000000..64ed672 --- /dev/null +++ b/UNLOZE_Clantag/scripting/UNLOZE_Clantag.sp @@ -0,0 +1,69 @@ +#include +#include + +#define CLANID "9110211" +#define GROUP "Game-Member" + +public Plugin myinfo = +{ + name = "UNLOZE Clantag", + author = "BotoX", + description = "Assign group to people wearing the UNLOZE clantag", + version = "1.0", + url = "" +}; + +public void OnPluginStart() +{ + /* Handle late load */ + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client) && IsClientAuthorized(client)) + { + OnClientPostAdminFilter(client); + } + } +} + +public void OnClientPostAdminFilter(int client) +{ + CheckClantag(client); +} + +public void OnClientSettingsChanged(int client) +{ + CheckClantag(client); +} + +void CheckClantag(int client) +{ + if(!IsClientAuthorized(client)) + return; + + char sClanID[32]; + GetClientInfo(client, "cl_clanid", sClanID, sizeof(sClanID)); + + if(!StrEqual(sClanID, CLANID)) + return; + + AdminId AdmID; + GroupId GrpID; + + if ((AdmID = GetUserAdmin(client)) == INVALID_ADMIN_ID) + { + LogMessage("Creating new admin for %L", client); + + AdmID = CreateAdmin(""); + SetUserAdmin(client, AdmID, true); + } + + if ((GrpID = FindAdmGroup(GROUP)) != INVALID_GROUP_ID) + { + if (AdminInheritGroup(AdmID, GrpID)) + LogMessage("%L added to group %s", client, GROUP); + } + else + { + LogMessage("%L group not found %s", client, GROUP); + } +} diff --git a/UNLOZE_Donation_Alert/configs/data.cfg b/UNLOZE_Donation_Alert/configs/data.cfg new file mode 100644 index 0000000..132e968 --- /dev/null +++ b/UNLOZE_Donation_Alert/configs/data.cfg @@ -0,0 +1,7 @@ +"unloze_donation_alert" +{ + "last_processed_donation" + { + "id" "1189" + } +} diff --git a/UNLOZE_Donation_Alert/scripting/UNLOZE_Donation_Alert.sp b/UNLOZE_Donation_Alert/scripting/UNLOZE_Donation_Alert.sp new file mode 100644 index 0000000..671cf44 --- /dev/null +++ b/UNLOZE_Donation_Alert/scripting/UNLOZE_Donation_Alert.sp @@ -0,0 +1,226 @@ +#pragma semicolon 1 + +#include +#include + +#pragma newdecls required + +/* CONVARS */ +ConVar g_cvInterval; + +/* DATABASE */ +Database g_hDatabase; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "UNLOZE Donation Alert", + author = "Neon", + description = "", + version = "1.0", + url = "https://steamcommunity.com/id/n3ontm" +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + g_cvInterval = CreateConVar("sm_unloze_donation_alert_interval", "60", "", FCVAR_NONE); + + AutoExecConfig(); + + char sError[256]; + if (SQL_CheckConfig("xenforo")) + g_hDatabase = SQL_Connect("xenforo", true, sError, sizeof(sError)); + + if (g_hDatabase == null) + LogError("Could not connect to database: %s", sError); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnMapStart() +{ + CreateTimer(g_cvInterval.FloatValue, CheckForNewDonations, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action CheckForNewDonations(Handle timer) +{ + char sDataFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sDataFile, sizeof(sDataFile), "configs/unloze_donation_alert/data.cfg"); + + if(!FileExists(sDataFile)) + { + LogError("Could not find data file: \"%s\"", sDataFile); + return Plugin_Stop; + } + + KeyValues Data = new KeyValues("unloze_donation_alert"); + + if(!Data.ImportFromFile(sDataFile)) + { + LogError("Unable to load data file: \"%s\"", sDataFile); + delete Data; + return Plugin_Stop; + } + + if(!Data.JumpToKey("last_processed_donation", false)) + { + LogError("Unable to find section last_processed_donation: \"%s\"", sDataFile); + delete Data; + return Plugin_Stop; + } + + int iID; + iID = Data.GetNum("id", -1); + if (iID == -1) + { + LogError("Unable to ID of last_processed_donation: \"%s\"", sDataFile); + delete Data; + return Plugin_Stop; + } + + char sQuery[255]; + Format(sQuery, sizeof(sQuery), "SELECT * FROM xf_bdpaygate_log WHERE log_id = %d", iID + 1); + g_hDatabase.Query(TQueryCB, sQuery, iID + 1); + + delete Data; + return Plugin_Continue; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void TQueryCB(Database db, DBResultSet results, const char[] error, any data) +{ + int iID = data; + + if (results.RowCount > 0) + { + int iField; + char sLogType[64]; + char sLogDetails[4096]; + results.FetchRow(); + results.FieldNameToNum("log_type", iField); + results.FetchString(iField, sLogType, sizeof(sLogType)); + + if(!StrEqual(sLogType, "accepted")) + { + HandleDonation(iID); + char sQuery[255]; + Format(sQuery, sizeof(sQuery), "SELECT * FROM xf_bdpaygate_log WHERE log_id = %d", iID + 1); + g_hDatabase.Query(TQueryCB, sQuery, iID + 1); + return; + } + + results.FieldNameToNum("log_details", iField); + results.FetchString(iField, sLogDetails, sizeof(sLogDetails)); + static char sStrings[256][256]; + char sMessage[256]; + char sMessageWhite[256]; + int iStrings = ExplodeString(sLogDetails, "\"", sStrings, 256, 256); + + if (StrContains(sLogDetails, "user_upgrade") != -1) + { + for (int i = 0; i < iStrings; i++) + { + if (StrContains(sStrings[i], "Account Upgrade:") != -1) + { + char sBuffer[256]; + char sUpgrade[256]; + char sName[256]; + strcopy(sBuffer, sizeof(sBuffer), sStrings[i]); + + int iStart = FindCharInString(sBuffer, ':', false); + strcopy(sBuffer, sizeof(sBuffer), sBuffer[iStart + 2]); + iStart = FindCharInString(sBuffer, '(', false); + strcopy(sName, sizeof(sName), sBuffer[iStart + 1]); + int iLength = strlen(sName); + sName[iLength-1] = 0; + SplitString(sBuffer, "(", sUpgrade, sizeof(sUpgrade)); + Format(sMessage, sizeof(sMessage), "{yellow}[UNLOZE] {lime}%s {default}just bought {yellow}%s{default}! Thank you for supporting our servers!!!", sName, sUpgrade); + Format(sMessageWhite, sizeof(sMessageWhite), "%s just bought %s! Thank you for supporting our servers!!!", sName, sUpgrade); + + } + } + } + else if (StrContains(sLogDetails, "gift_upgrade") != -1) + { + for (int i = 0; i < iStrings; i++) + { + if (StrContains(sStrings[i], "Account Upgrade:") != -1) + { + char sBuffer[256]; + char sUpgrade[256]; + char sName[256]; + strcopy(sBuffer, sizeof(sBuffer), sStrings[i]); + + int iStart = FindCharInString(sBuffer, ':', false); + strcopy(sBuffer, sizeof(sBuffer), sBuffer[iStart + 2]); + iStart = FindCharInString(sBuffer, '(', false); + strcopy(sName, sizeof(sName), sBuffer[iStart + 1]); + int iLength = strlen(sName); + sName[iLength-1] = 0; + SplitString(sBuffer, "(", sUpgrade, sizeof(sUpgrade)); + + Format(sMessage, sizeof(sMessage), "{yellow}[UNLOZE] {lime}%s {default}just recieved {yellow}%s {default}as a gift! Thank you for supporting our servers!!!", sName, sUpgrade); + Format(sMessageWhite, sizeof(sMessageWhite), "%s just recieved %s as a gift! Thank you for supporting our servers!!!", sName, sUpgrade); + } + } + } + + CPrintToChatAll(sMessage); + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + { + PrintHintText(client, sMessageWhite); + } + } + + HandleDonation(iID); + char sQuery[255]; + Format(sQuery, sizeof(sQuery), "SELECT * FROM xf_bdpaygate_log WHERE log_id = %d", iID + 1); + g_hDatabase.Query(TQueryCB, sQuery, iID + 1); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void HandleDonation(int iID) +{ + char sDataFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sDataFile, sizeof(sDataFile), "configs/unloze_donation_alert/data.cfg"); + + if(!FileExists(sDataFile)) + { + LogError("Could not find data file: \"%s\"", sDataFile); + return; + } + + KeyValues Data = new KeyValues("unloze_donation_alert"); + + if(!Data.JumpToKey("last_processed_donation", true)) + { + LogError("Unable to create section last_processed_donation: \"%s\"", sDataFile); + delete Data; + return; + } + Data.SetNum("id", iID); + Data.Rewind(); + if(!Data.ExportToFile(sDataFile)) + { + LogError("Unable to export data file: \"%s\"", sDataFile); + delete Data; + return; + } + delete Data; +} \ No newline at end of file diff --git a/UNLOZE_ForumIntegration/scripting/UNLOZE_ForumIntegration.sp b/UNLOZE_ForumIntegration/scripting/UNLOZE_ForumIntegration.sp new file mode 100644 index 0000000..de060b1 --- /dev/null +++ b/UNLOZE_ForumIntegration/scripting/UNLOZE_ForumIntegration.sp @@ -0,0 +1,359 @@ +//==================================================================================================== +// +// Name: UNLOZE Forum Integration. +// Author: .George & zaCade (Original by Botox) +// Description: Handles forum access ingame. +// +//==================================================================================================== +#include +#include +#include +#include //#define UNLOZE_APIKEY here + +#pragma newdecls required + +/* STRINGS */ +char G_sGroup[MAXPLAYERS+1][64]; + +/* BOOLS */ +bool G_bPreAdminChecked[MAXPLAYERS+1]; +bool G_bResponseFailed[MAXPLAYERS+1]; +bool G_bResponsePassed[MAXPLAYERS+1]; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "UNLOZE Forum Integration", + author = ".George & zaCade (Original by Botox)", + description = "Handles forum access ingame", + version = "1.2.1" +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: Late load +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + /* Late load */ + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientConnected(client)) + OnClientConnected(client); + + if(IsClientAuthorized(client) && !IsFakeClient(client)) + { + char sSteamID32[32]; + if(GetClientAuthId(client, AuthId_Steam2, sSteamID32, sizeof(sSteamID32))) + OnClientAuthorized(client, sSteamID32); + } + } +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + CreateNative("AsyncHasSteamIDReservedSlot", Native_AsyncHasSteamIDReservedSlot); + + RegPluginLibrary("UNLOZE_ForumIntegration"); + + return APLRes_Success; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnRebuildAdminCache(AdminCachePart part) +{ + if (part != AdminCache_Admins) + return; + + CreateTimer(1.0, OnRebuildAdminCachePost, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnRebuildAdminCachePost(Handle hTimer) +{ + for (int client = 1; client <= MaxClients; client++) + { + if(G_bResponsePassed[client] && G_bPreAdminChecked[client]) + ApplyGroupFlags(client); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientConnected(int client) +{ + G_bPreAdminChecked[client] = false; + G_bResponseFailed[client] = false; + G_bResponsePassed[client] = false; + + G_sGroup[client][0] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientDisconnect(int client) +{ + G_bPreAdminChecked[client] = false; + G_bResponseFailed[client] = false; + G_bResponsePassed[client] = false; + + G_sGroup[client][0] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientAuthorized(int client, const char[] sSteamID32) +{ + if (IsFakeClient(client)) + return; + + char sSteamID64[32]; + SteamID32toSteamID64(sSteamID32, sSteamID64, sizeof(sSteamID64)); + + int iSerial = GetClientSerial(client); + + char sRequest[256]; + FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64); + + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); + if (!hRequest || + !SteamWorks_SetHTTPCallbacks(hRequest, OnClientAuthorized_OnTransferComplete) || + !SteamWorks_SetHTTPRequestContextValue(hRequest, iSerial) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + delete hRequest; + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int OnClientAuthorized_OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, int iSerial) +{ + int client = GetClientFromSerial(iSerial); + + if (!client) //Player disconnected. + { + delete hRequest; + return; + } + + if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK) + { + G_bResponseFailed[client] = true; + + if (G_bPreAdminChecked[client]) + NotifyPostAdminCheck(client); + + delete hRequest; + return; + } + + SteamWorks_GetHTTPResponseBodyCallback(hRequest, OnClientAuthorized_OnTransferResponse, iSerial); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int OnClientAuthorized_OnTransferResponse(char[] sData, int iSerial) +{ + int client = GetClientFromSerial(iSerial); + + if (!client) //Player disconnected. + return; + + TrimString(sData); + StripQuotes(sData); + + strcopy(G_sGroup[client], sizeof(G_sGroup[]), sData); + + G_bResponsePassed[client] = true; + + if (G_bPreAdminChecked[client]) + NotifyPostAdminCheck(client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnClientPreAdminCheck(int client) +{ + G_bPreAdminChecked[client] = true; + + if (G_bResponsePassed[client] || G_bResponseFailed[client]) + return Plugin_Continue; + + RunAdminCacheChecks(client); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientPostAdminFilter(int client) +{ + ApplyGroupFlags(client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +stock void ApplyGroupFlags(int client) +{ + if (!G_bResponsePassed[client]) + return; + + AdminId AdmID; + GroupId GrpID; + + if ((AdmID = GetUserAdmin(client)) == INVALID_ADMIN_ID) + { + LogMessage("Creating new admin for %L", client); + + AdmID = CreateAdmin(); + SetUserAdmin(client, AdmID, true); + } + + if ((GrpID = FindAdmGroup(G_sGroup[client])) != INVALID_GROUP_ID) + { + if (AdminInheritGroup(AdmID, GrpID)) + { + LogMessage("%L added to group %s", client, G_sGroup[client]); + } + } + else + { + LogMessage("%L group not found %s", client, G_sGroup[client]); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int Native_AsyncHasSteamIDReservedSlot(Handle plugin, int numParams) +{ + char sSteamID32[32]; + GetNativeString(1, sSteamID32, sizeof(sSteamID32)); + + AsyncHasSteamIDReservedSlotCallbackFunc callback; + callback = GetNativeCell(2); + + any data; + data = GetNativeCell(3); + + char sSteamID64[32]; + SteamID32toSteamID64(sSteamID32, sSteamID64, sizeof(sSteamID64)); + + char sRequest[256]; + FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64); + + DataPack hDataPack = new DataPack(); + hDataPack.WriteString(sSteamID32); + hDataPack.WriteFunction(callback); + hDataPack.WriteCell(plugin); + hDataPack.WriteCell(data); + + Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); + if (!hRequest || + !SteamWorks_SetHTTPCallbacks(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferComplete) || + !SteamWorks_SetHTTPRequestContextValue(hRequest, hDataPack) || + !SteamWorks_SendHTTPRequest(hRequest)) + { + delete hRequest; + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int Native_AsyncHasSteamIDReservedSlot_OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, DataPack hDataPack) +{ + if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK) + { + char sData[32] = "NOGROUP"; + Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(sData, hDataPack); + + delete hRequest; + return; + } + + SteamWorks_GetHTTPResponseBodyCallback(hRequest, Native_AsyncHasSteamIDReservedSlot_OnTransferResponse, hDataPack); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public int Native_AsyncHasSteamIDReservedSlot_OnTransferResponse(char[] sData, DataPack hDataPack) +{ + hDataPack.Reset(); + + char sSteamID32[32]; + hDataPack.ReadString(sSteamID32, sizeof(sSteamID32)); + + AsyncHasSteamIDReservedSlotCallbackFunc callback; + callback = view_as(hDataPack.ReadFunction()); + + Handle plugin; + plugin = hDataPack.ReadCell(); + + any data; + data = hDataPack.ReadCell(); + + TrimString(sData); + StripQuotes(sData); + + int result; + if (StrEqual(sData, "Game-Donator", false)) + result = 1; + else + result = 0; + + Call_StartFunction(plugin, callback); + Call_PushString(sSteamID32); + Call_PushCell(result); + Call_PushCell(data); + Call_Finish(); + + delete hDataPack; + return; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +stock bool SteamID32toSteamID64(const char[] sSteamID32, char[] sSteamID64, int iSize) +{ + if (strlen(sSteamID32) < 11 || strncmp(sSteamID32[0], "STEAM_", 6)) + { + sSteamID64[0] = 0; + return false; + } + + int iUpper = 765611979; + int isSteam64ID = StringToInt(sSteamID32[10]) * 2 + 60265728 + sSteamID32[8] - 48; + + int iDiv = isSteam64ID / 100000000; + int iIdx = 9 - (iDiv ? (iDiv / 10 + 1) : 0); + + iUpper += iDiv; + + IntToString(isSteam64ID, sSteamID64[iIdx], iSize - iIdx); + iIdx = sSteamID64[9]; + + IntToString(iUpper, sSteamID64, iSize); + sSteamID64[9] = iIdx; + + return true; +} \ No newline at end of file diff --git a/VIP_Test/scripting/VIP_Test.sp b/VIP_Test/scripting/VIP_Test.sp new file mode 100644 index 0000000..0fb48ce --- /dev/null +++ b/VIP_Test/scripting/VIP_Test.sp @@ -0,0 +1,298 @@ +#pragma semicolon 1 + +#include + +#pragma newdecls required + +/* STRINGS */ +char g_sAdmGroup[32] = "Game-Donator"; +char g_sGroup[MAXPLAYERS+1][64]; + +/* CONVARS */ +ConVar g_cvFreeVIPDuration; + +/* DATABASE */ +Database g_hDatabase; + +/* BOOLS */ +bool g_bPreAdminChecked[MAXPLAYERS+1]; +bool g_bResponseFailed[MAXPLAYERS+1]; +bool g_bResponsePassed[MAXPLAYERS+1]; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "UNLOZE_VIP_Test", + author = "Neon", + description = "", + version = "2.0", + url = "https://steamcommunity.com/id/n3ontm" +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + g_cvFreeVIPDuration = CreateConVar("sm_unloze_vip_test_duration", "1440", "", FCVAR_NONE); + + RegConsoleCmd("sm_viptest", Command_VIP, "Activate free VIP period"); + RegConsoleCmd("sm_testvip", Command_VIP, "Activate free VIP period"); + + AutoExecConfig(); + + char sError[256]; + if (SQL_CheckConfig("testvip")) + g_hDatabase = SQL_Connect("testvip", true, sError, sizeof(sError)); + + if (g_hDatabase == null) + LogError("Could not connect to database: %s", sError); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnRebuildAdminCache(AdminCachePart part) +{ + if (part != AdminCache_Admins) + return; + + CreateTimer(1.0, OnRebuildAdminCachePost, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnRebuildAdminCachePost(Handle hTimer) +{ + for (int client = 1; client <= MaxClients; client++) + { + if(g_bResponsePassed[client] && g_bPreAdminChecked[client]) + ApplyGroupFlags(client); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientConnected(int client) +{ + g_bPreAdminChecked[client] = false; + g_bResponseFailed[client] = false; + g_bResponsePassed[client] = false; + + g_sGroup[client][0] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientDisconnect(int client) +{ + g_bPreAdminChecked[client] = false; + g_bResponseFailed[client] = false; + g_bResponsePassed[client] = false; + + g_sGroup[client][0] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientAuthorized(int client, const char[] sSteamID32) +{ + if (IsFakeClient(client)) + return; + + char sSteamID[32]; + GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID)); + + char sQuery[256]; + Format(sQuery, sizeof(sQuery), "SELECT * FROM testvip_table WHERE steam_auth = '%s'", sSteamID); + SQL_TQuery(g_hDatabase, TQueryCBConnect, sQuery, GetClientUserId(client)); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void TQueryCBConnect(Handle owner, Handle rs, const char[] error, any data) +{ + int client = 0; + + if ((client = GetClientOfUserId(data)) == 0) + return; + + if (SQL_GetRowCount(rs) > 0) + { + int iField; + SQL_FetchRow(rs); + SQL_FieldNameToNum(rs, "activated", iField); + int iTimestampActivated = SQL_FetchInt(rs, iField); + int iTimestamp = GetTime(); + delete rs; + + if ((iTimestamp - iTimestampActivated) < g_cvFreeVIPDuration.IntValue *60) + { + strcopy(g_sGroup[client], sizeof(g_sGroup[]), g_sAdmGroup); + } + } + + g_bResponsePassed[client] = true; + if (g_bPreAdminChecked[client]) + NotifyPostAdminCheck(client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnClientPreAdminCheck(int client) +{ + g_bPreAdminChecked[client] = true; + + if (g_bResponsePassed[client] || g_bResponseFailed[client]) + return Plugin_Continue; + + RunAdminCacheChecks(client); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientPostAdminFilter(int client) +{ + ApplyGroupFlags(client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_VIP(int client, int iArgs) +{ + if (!IsValidClient(client)) + return Plugin_Handled; + + char sSteamID[32]; + GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID)); + + char sQuery[255]; + Format(sQuery, sizeof(sQuery), "SELECT * FROM testvip_table WHERE steam_auth = '%s'", sSteamID); + SQL_TQuery(g_hDatabase, TQueryCBCommand, sQuery, GetClientUserId(client)); + + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void TQueryCBCommand(Handle owner, Handle rs, const char[] error, any data) +{ + int client = 0; + + if ((client = GetClientOfUserId(data)) == 0) + return; + + int iTimestamp = GetTime(); + + if (SQL_GetRowCount(rs) > 0) + { + int iField; + SQL_FetchRow(rs); + SQL_FieldNameToNum(rs, "activated", iField); + int iTimestampActivated = SQL_FetchInt(rs, iField); + delete rs; + + if ((iTimestamp - iTimestampActivated) < g_cvFreeVIPDuration.IntValue *60) + PrintToChat(client, "[UNLOZE] Your TEST VIP will expire in %d minutes!", g_cvFreeVIPDuration.IntValue - (iTimestamp - iTimestampActivated) / 60); + else + PrintToChat(client, "[UNLOZE] Your TEST VIP expired already!"); + } + else + { + if (IsVIP(client)) + { + PrintToChat(client, "[UNLOZE] You already have VIP activated!"); + return; + } + + char sSteamID[32]; + GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID)); + + char sQuery[255]; + Format(sQuery, sizeof(sQuery), "INSERT INTO testvip_table (steam_auth, activated) VALUES ('%s', '%d')", sSteamID, iTimestamp); + SQL_FastQuery(g_hDatabase, sQuery); + strcopy(g_sGroup[client], sizeof(g_sGroup[]), g_sAdmGroup); + ApplyGroupFlags(client); + PrintToChat(client, "[UNLOZE] You have now access to !zclass, !tag and !glow and other VIP-Perks."); + PrintToChat(client, "[UNLOZE] Your TEST VIP will expire in %d minutes!", g_cvFreeVIPDuration.IntValue); + FakeClientCommandEx(client, "sm_vip"); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +stock void ApplyGroupFlags(int client) +{ + if (!g_bResponsePassed[client]) + return; + + if (g_sGroup[client][0] == 0) + return; + + AdminId AdmID; + GroupId GrpID; + + if ((AdmID = GetUserAdmin(client)) == INVALID_ADMIN_ID) + { + LogMessage("Creating new admin for %L", client); + + AdmID = CreateAdmin(""); + SetUserAdmin(client, AdmID, true); + } + + if ((GrpID = FindAdmGroup(g_sGroup[client])) != INVALID_GROUP_ID) + { + if (AdminInheritGroup(AdmID, GrpID)) + LogMessage("%L added to group %s", client, g_sGroup[client]); + } + else + LogMessage("%L group not found %s", client, g_sGroup[client]); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +stock int IsValidClient(int client, bool nobots = true) +{ + if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client))) + return false; + + return IsClientInGame(client); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public bool IsVIP(int client) +{ + AdminId AdmID; + + if ((AdmID = GetUserAdmin(client)) == INVALID_ADMIN_ID) + return false; + else + { + for (int i = 0; i <= GetAdminGroupCount(AdmID); i++) + { + char sGroup[32]; + if ((GetAdminGroup(AdmID, i, sGroup, sizeof(sGroup)) != INVALID_GROUP_ID)) + { + if (StrEqual(sGroup, g_sAdmGroup)) + return true; + } + } + } + return false; +} \ No newline at end of file diff --git a/WeaponCleaner/scripting/WeaponCleaner.sp b/WeaponCleaner/scripting/WeaponCleaner.sp new file mode 100644 index 0000000..3d209b4 --- /dev/null +++ b/WeaponCleaner/scripting/WeaponCleaner.sp @@ -0,0 +1,306 @@ +#include +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +#define TIMER_INTERVAL 1.0 +Handle g_hTimer = INVALID_HANDLE; + +ConVar g_CVar_MaxWeapons; +ConVar g_CVar_WeaponLifetime; + +bool g_bHasOnEntitySpawned; + +int g_RealRoundStartedTime; +int g_MaxWeapons; +int g_MaxWeaponLifetime; + +#define MAX_WEAPONS MAXPLAYERS +int G_WeaponArray[MAX_WEAPONS][2]; + +public Plugin myinfo = +{ + name = "WeaponCleaner", + author = "BotoX", + description = "Clean unneeded weapons", + version = "2.2.1", + url = "" +}; + +public void OnPluginStart() +{ + g_CVar_MaxWeapons = CreateConVar("sm_weaponcleaner_max", "5", "The maximum amount of weapons allowed in the game.", 0, true, 0.0, true, MAX_WEAPONS - 1.0); + g_MaxWeapons = g_CVar_MaxWeapons.IntValue; + g_CVar_MaxWeapons.AddChangeHook(OnConVarChanged); + + g_CVar_WeaponLifetime = CreateConVar("sm_weaponcleaner_lifetime", "15", "The maximum number of seconds a weapon is allowed in the game.", 0, true, 0.0); + g_MaxWeaponLifetime = g_CVar_WeaponLifetime.IntValue; + g_CVar_WeaponLifetime.AddChangeHook(OnConVarChanged); + + // Capability provider from https://github.com/alliedmodders/sourcemod/pull/1078 + g_bHasOnEntitySpawned = GetFeatureStatus(FeatureType_Capability, "SDKHook_OnEntitySpawned") == FeatureStatus_Available; + + HookEvent("round_start", Event_RoundStart); + + AutoExecConfig(true, "plugin.WeaponCleaner"); + + for(int client = 1; client <= MaxClients; client++) + { + if(IsClientInGame(client)) + OnClientPutInServer(client); + } +} + +public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + if(convar == g_CVar_MaxWeapons) + { + if(StringToInt(newValue) < StringToInt(oldValue)) + { + // Need to shrink list and kill items + int d = StringToInt(oldValue) - StringToInt(newValue); + + // Kill items that don't have space anymore + for(int i = 0; d && i < g_MaxWeapons; i++) + { + if(!G_WeaponArray[i][0]) + continue; + + // Kill it + if(KillWeapon(G_WeaponArray[i][0])) + { + // Move index backwards (since the list was modified by removing it) + i--; + d--; + } + } + } + g_MaxWeapons = StringToInt(newValue); + } + else if(convar == g_CVar_WeaponLifetime) + { + g_MaxWeaponLifetime = StringToInt(newValue); + CheckWeapons(); + } +} + +public void OnMapStart() +{ + if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer)) + g_hTimer = INVALID_HANDLE; + + g_hTimer = CreateTimer(TIMER_INTERVAL, Timer_CleanupWeapons, INVALID_HANDLE, TIMER_REPEAT); +} + +public void OnMapEnd() +{ + if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer)) + g_hTimer = INVALID_HANDLE; +} + +public void OnClientPutInServer(int client) +{ + SDKHook(client, SDKHook_WeaponDropPost, OnWeaponDrop); + SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponEquip); +} + +public void OnClientDisconnect(int client) +{ + if(!IsClientInGame(client)) + return; + + // Simulate dropping all equipped weapons + for(int i = 0; i < 5; i++) + { + int weapon = GetPlayerWeaponSlot(client, i); + if(weapon != -1) + OnWeaponDrop(client, weapon); + } +} + +public void OnEntityDestroyed(int entity) +{ + // wtf sourcemod? + if(entity == -1) + return; + + RemoveWeapon(EntIndexToEntRef(EntRefToEntIndex(entity))); +} + +public void OnEntityCreated(int entity, const char[] classname) +{ + if(strncmp(classname, "weapon_", 7) == 0) + { + if(!g_bHasOnEntitySpawned) + { + SDKHook(entity, SDKHook_SpawnPost, OnSDKHookEntitySpawnPost); + } + } +} + +public void OnSDKHookEntitySpawnPost(int entity) +{ + RequestFrame(OnWeaponSpawnedPost, entity); +} + +public void OnEntitySpawned(int entity, const char[] classname) +{ + if(strncmp(classname, "weapon_", 7) == 0) + { + OnWeaponSpawnedPost(entity); + } +} + +public void OnWeaponSpawnedPost(int entity) +{ + if(!IsValidEntity(entity)) + return; + + int HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); + // Should not be cleaned since it's a map spawned weapon + if(HammerID) + return; + + // Weapon doesn't belong to any player + if(GetEntPropEnt(entity, Prop_Data, "m_hOwnerEntity") == -1) + InsertWeapon(entity); +} + +public Action OnWeaponEquip(int client, int entity) +{ + if(!IsValidEntity(entity)) + return; + + int HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); + // Should not be cleaned since it's a map spawned weapon + if(HammerID) + return; + + // Weapon should not be cleaned anymore + RemoveWeapon(EntIndexToEntRef(entity)); +} + +public Action OnWeaponDrop(int client, int entity) +{ + if(!IsValidEntity(entity)) + return; + + int HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); + // Should not be cleaned since it's a map spawned weapon + if(HammerID) + return; + + // Kill all dropped weapons during mp_freezetime + // or if no weapons are allowed at all + if(GetTime() < g_RealRoundStartedTime || !g_MaxWeapons) + { + // Kill it + AcceptEntityInput(entity, "Kill"); + return; + } + + // Weapon should be cleaned again + InsertWeapon(entity); +} + +bool InsertWeapon(int entity) +{ + if(!g_MaxWeapons) + return false; + + int entref = EntIndexToEntRef(entity); + + // Try to find a free slot + for(int i = 0; i < g_MaxWeapons; i++) + { + if(G_WeaponArray[i][0]) + continue; + + // Found a free slot, add it here + G_WeaponArray[i][0] = entref; + G_WeaponArray[i][1] = GetTime(); + return true; + } + + // No free slot found + // Kill the first (oldest) item in the list + KillWeapon(G_WeaponArray[0][0]); + + // Add new weapon to the end of the list + G_WeaponArray[g_MaxWeapons - 1][0] = entref; + G_WeaponArray[g_MaxWeapons - 1][1] = GetTime(); + return true; +} + +bool RemoveWeapon(int entref) +{ + // Find the Weapon + for(int i = 0; i < g_MaxWeapons; i++) + { + if(G_WeaponArray[i][0] == entref) + { + G_WeaponArray[i][0] = 0; G_WeaponArray[i][1] = 0; + + // Move list items in front of this index back by one + for(int j = i + 1; j < g_MaxWeapons; j++) + { + G_WeaponArray[j - 1][0] = G_WeaponArray[j][0]; + G_WeaponArray[j - 1][1] = G_WeaponArray[j][1]; + } + + // Reset last list item + G_WeaponArray[g_MaxWeapons - 1][0] = 0; + G_WeaponArray[g_MaxWeapons - 1][1] = 0; + + return true; + } + } + return false; +} + +bool CheckWeapons() +{ + for(int i = 0; i < g_MaxWeapons; i++) + { + if(!G_WeaponArray[i][0]) + continue; + + if(g_MaxWeaponLifetime && GetTime() - G_WeaponArray[i][1] >= g_MaxWeaponLifetime) + { + // Kill it + if(KillWeapon(G_WeaponArray[i][0])) + { + // Move index backwards (since the list was modified by removing it) + i--; + } + } + } + return true; +} + +bool KillWeapon(int entref) +{ + if(!IsValidEntity(entref)) + return RemoveWeapon(entref); + + AcceptEntityInput(entref, "Kill"); + + return RemoveWeapon(entref); +} + +public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + for(int i = 0; i < MAX_WEAPONS; i++) + { + G_WeaponArray[i][0] = 0; + G_WeaponArray[i][1] = 0; + } + g_RealRoundStartedTime = GetTime() + GetConVarInt(FindConVar("mp_freezetime")); +} + +public Action Timer_CleanupWeapons(Handle timer) +{ + CheckWeapons(); +} diff --git a/ZSkills/content/sound/unloze/extinguish.wav b/ZSkills/content/sound/unloze/extinguish.wav new file mode 100644 index 0000000..9e8c2e6 Binary files /dev/null and b/ZSkills/content/sound/unloze/extinguish.wav differ diff --git a/ZSkills/gamedata/ZSkills.games.txt b/ZSkills/gamedata/ZSkills.games.txt new file mode 100644 index 0000000..6b8c4da --- /dev/null +++ b/ZSkills/gamedata/ZSkills.games.txt @@ -0,0 +1,15 @@ +"Games" +{ + "csgo" + { + "Signatures" + { + "CCSPlayer::SetProgressBarTime" + { + "library" "server" + "windows" "\x55\x8B\xEC\x51\x53\x8B\xD9\x56\x57\x8B\x7D\x08\x39\xBB\xE8\x27\x00\x00" + "linux" "\x55\x89\xE5\x83\xEC\x48\x89\x5D\xF4\x8B\x5D\x08\x89\x75\xF8\x8B\x75\x0C\x89\x7D\xFC\x39\xB3\x00\x28\x00\x00" + } + } + } +} \ No newline at end of file diff --git a/ZSkills/scripting/ZSkills.sp b/ZSkills/scripting/ZSkills.sp new file mode 100644 index 0000000..5d63ba0 --- /dev/null +++ b/ZSkills/scripting/ZSkills.sp @@ -0,0 +1,399 @@ +#include +#include +#include + +/* BOOLS */ +bool g_bZAmmo_Enabled; +bool g_bZAmmo_Active[MAXPLAYERS+1]; + +bool g_bZCleanse_Enabled; +bool g_bZCleanse_Active[MAXPLAYERS+1]; +bool g_bLastButtonReload[MAXPLAYERS+1]; + +/* CONVARS */ +ConVar g_hCVar_ZAmmo_Enabled; +ConVar g_hCVar_ZAmmo_Duration; +ConVar g_hCVar_ZAmmo_Cost; + +ConVar g_hCVar_ZCleanse_Enabled; +ConVar g_hCVar_ZCleanse_Duration; +ConVar g_hCVar_ZCleanse_Uses; + +/* INTEGERS */ +int g_bZCleanse_Uses[MAXPLAYERS+1]; + +Handle g_hSetProgressBarTime = null; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "ZSkills", + author = "Neon", + description = "Skills?!", + version = "1.0.0" +}; + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + g_hCVar_ZAmmo_Enabled = CreateConVar("zr_zammo_enabled", "1", "", FCVAR_NONE, true, 0.0, true, 1.0); + g_hCVar_ZAmmo_Duration = CreateConVar("zr_zammo_duration", "6", "", FCVAR_NONE, true, 1.0); + g_hCVar_ZAmmo_Cost = CreateConVar("zr_zammo_cost", "4500", "", FCVAR_NONE, true, 1.0); + g_bZAmmo_Enabled = g_hCVar_ZAmmo_Enabled.BoolValue; + if (g_bZAmmo_Enabled) + HookEvent("weapon_fire", Event_WeaponFire); + g_hCVar_ZAmmo_Enabled.AddChangeHook(ConVarChanged); + + g_hCVar_ZCleanse_Enabled = CreateConVar("zr_zcleanse_enabled", "1", "", FCVAR_NONE, true, 0.0, true, 1.0); + g_hCVar_ZCleanse_Duration = CreateConVar("zr_zcleanse_duration", "5", "", FCVAR_NONE, true, 1.0); + g_hCVar_ZCleanse_Uses = CreateConVar("zr_zcleanse_uses", "5", "", FCVAR_NONE, true, 1.0); + g_bZCleanse_Enabled = g_hCVar_ZCleanse_Enabled.BoolValue; + g_hCVar_ZCleanse_Enabled.AddChangeHook(ConVarChanged); + + + RegConsoleCmd("sm_zammo", Command_ZAmmo); + RegConsoleCmd("sm_zcleanse", Command_ZCleanse); + + if (GetEngineVersion() == Engine_CSGO) + { + Handle hGameConf = LoadGameConfigFile("ZSkills.games"); + + if (hGameConf == null) + { + SetFailState("Game config was not loaded right."); + return; + } + + // Starts the preparation of an SDK call + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "CCSPlayer::SetProgressBarTime"); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + + if (!(g_hSetProgressBarTime = EndPrepSDKCall())) + { + SetFailState("Failed to find \"CCSPlayer::SetProgressBarTime\"."); + return; + } + + delete hGameConf; + } + + HookEvent("round_start", Event_RoundStart); + AutoExecConfig(); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnMapStart() +{ + PrecacheSound("items/pickup_ammo_01.wav"); + PrecacheSound("unloze/extinguish.wav"); + AddFileToDownloadsTable("sound/unloze/extinguish.wav"); +} + + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + g_bZAmmo_Enabled = g_hCVar_ZAmmo_Enabled.BoolValue; + g_bZCleanse_Enabled = g_hCVar_ZCleanse_Enabled.BoolValue; + + if (g_bZAmmo_Enabled) + HookEvent("weapon_fire", Event_WeaponFire); + else + UnhookEvent("weapon_fire", Event_WeaponFire); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_ZAmmo(int client, int args) +{ + if (client == 0) + { + ReplyToCommand(client, "[ZAmmo] Can't use this from console."); + return Plugin_Handled; + } + if (!g_bZAmmo_Enabled) + { + PrintToChat(client, "[ZAmmo] is currently disabled."); + return Plugin_Handled; + } + if (!IsPlayerAlive(client)) + { + PrintToChat(client, "[ZAmmo] This feature requires you to be alive."); + return Plugin_Handled; + } + if (!ZR_IsClientHuman(client)) + { + PrintToChat(client, "[ZAmmo] This feature requires you to be Human."); + return Plugin_Handled; + } + if(g_bZAmmo_Active[client]) + { + PrintToChat(client, "[ZAmmo] is already active on you."); + return Plugin_Handled; + } + int iCost = g_hCVar_ZAmmo_Cost.IntValue; + if (GetEntProp(client, Prop_Send, "m_iAccount") < iCost) + { + PrintToChat(client, "[ZAmmo] Insufficent funds (%d).", iCost); + return Plugin_Handled; + } + int iDuration = g_hCVar_ZAmmo_Duration.IntValue; + + SetEntProp(client, Prop_Send, "m_iAccount", GetEntProp(client, Prop_Send, "m_iAccount") - iCost); + + if(GetEngineVersion() == Engine_CSGO) + { + SDKCall(g_hSetProgressBarTime, client, iDuration); + } + else + { + SetEntPropFloat(client, Prop_Send, "m_flProgressBarStartTime", GetGameTime()); + SetEntProp(client, Prop_Send, "m_iProgressBarDuration", iDuration); + } + + CreateTimer(float(iDuration), Timer_Disable_ZAmmo, GetClientSerial(client), TIMER_FLAG_NO_MAPCHANGE); + + g_bZAmmo_Active[client] = true; + PrintToChat(client, "[ZAmmo] Enabling Infinite Ammo for %d seconds in exchange for %d$.", iDuration, iCost); + EmitSoundToAll("items/pickup_ammo_01.wav", .entity=client, .volume=0.4); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Command_ZCleanse(int client, int args) +{ + if (client == 0) + { + ReplyToCommand(client, "[ZCleanse] Can't use this from console."); + return Plugin_Handled; + } + if (!g_bZCleanse_Enabled) + { + PrintToChat(client, "[ZCleanse] is currently disabled."); + return Plugin_Handled; + } + if (!IsPlayerAlive(client)) + { + PrintToChat(client, "[ZCleanse] This feature requires you to be alive."); + return Plugin_Handled; + } + if (!ZR_IsClientZombie(client)) + { + PrintToChat(client, "[ZCleanse] This feature requires you to be Zombie."); + return Plugin_Handled; + } + if(g_bZCleanse_Active[client]) + { + PrintToChat(client, "[ZCleanse] is already active on you."); + return Plugin_Handled; + } + int iMaxUses = g_hCVar_ZCleanse_Uses.IntValue; + if (g_bZCleanse_Uses[client] >= iMaxUses) + { + PrintToChat(client, "[ZCleanse] Already used (%d/%d) times this round.", g_bZCleanse_Uses[client], iMaxUses); + return Plugin_Handled; + } + int iDuration = g_hCVar_ZCleanse_Duration.IntValue; + + if(GetEngineVersion() == Engine_CSGO) + { + SDKCall(g_hSetProgressBarTime, client, iDuration); + } + else + { + SetEntPropFloat(client, Prop_Send, "m_flProgressBarStartTime", GetGameTime()); + SetEntProp(client, Prop_Send, "m_iProgressBarDuration", iDuration); + } + + NapalmExtinguishEntity(client); + CreateTimer(float(iDuration), Timer_Disable_ZCleanse, GetClientSerial(client), TIMER_FLAG_NO_MAPCHANGE); + + g_bZCleanse_Active[client] = true; + g_bZCleanse_Uses[client]++; + PrintToChat(client, "[ZCleanse] You are immune against napalm for the next %d seconds. (%d/%d) uses.", iDuration, g_bZCleanse_Uses[client], iMaxUses); + EmitSoundToAll("unloze/extinguish.wav", .entity=client, .volume=0.4); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void OnClientDisconnect(int client) +{ + g_bZAmmo_Active[client] = false; + g_bZCleanse_Active[client] = false; + g_bZCleanse_Uses[client] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void Event_RoundStart(Handle hEvent, char[] name, bool dontBroadcast) +{ + for (int client = 1; client <= MaxClients; client++) + { + g_bZAmmo_Active[client] = false; + g_bZCleanse_Active[client] = false; + g_bZCleanse_Uses[client] = 0; + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void Event_WeaponFire(Handle hEvent, char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(GetEventInt(hEvent, "userid")); + if(!g_bZAmmo_Active[client]) + return; + + int weapon = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon", 0); + if(IsValidEntity(weapon)) + { + if(weapon == GetPlayerWeaponSlot(client, 0) || weapon == GetPlayerWeaponSlot(client, 1)) + { + if(GetEntProp(weapon, Prop_Send, "m_iState", 4, 0) == 2 && GetEntProp(weapon, Prop_Send, "m_iClip1", 4, 0)) + { + int toAdd = 1; + char weaponClassname[128]; + GetEntityClassname(weapon, weaponClassname, sizeof(weaponClassname)); + + if(StrEqual(weaponClassname, "weapon_glock", true) || StrEqual(weaponClassname, "weapon_famas", true)) + { + if(GetEntProp(weapon, Prop_Send, "m_bBurstMode")) + { + switch (GetEntProp(weapon, Prop_Send, "m_iClip1")) + { + case 1: + { + toAdd = 1; + } + case 2: + { + toAdd = 2; + } + default: + { + toAdd = 3; + } + } + } + } + SetEntProp(weapon, Prop_Send, "m_iClip1", GetEntProp(weapon, Prop_Send, "m_iClip1", 4, 0) + toAdd, 4, 0); + } + } + } + + return; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn) +{ + if (!g_bZAmmo_Active[client]) + return; + + SetEntPropFloat(client, Prop_Send, "m_flProgressBarStartTime", GetGameTime()-1); + SetEntProp(client, Prop_Send, "m_iProgressBarDuration", 0); + + g_bZAmmo_Active[client] = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public void NapalmExtinguishEntity(int client) +{ + int iFire = GetEntPropEnt(client, Prop_Data, "m_hEffectEntity"); + if (IsValidEntity(iFire)) + { + char sClassName[64]; + GetEdictClassname(iFire, sClassName, sizeof(sClassName)); + if (StrEqual(sClassName, "entityflame", false)) + SetEntPropFloat(iFire, Prop_Data, "m_flLifetime", 0.0); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action ZR_OnClientIgnite(int &client, float &duration) +{ + if(g_bZCleanse_Active[client]) + return Plugin_Handled; + + return Plugin_Continue; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Timer_Disable_ZAmmo(Handle timer, int iSerial) +{ + int client; + if ((client = GetClientFromSerial(iSerial)) == 0) + return; + + SetEntPropFloat(client, Prop_Send, "m_flProgressBarStartTime", GetGameTime()-1); + SetEntProp(client, Prop_Send, "m_iProgressBarDuration", 0); + + g_bZAmmo_Active[client] = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action Timer_Disable_ZCleanse(Handle timer, int iSerial) +{ + int client; + if ((client = GetClientFromSerial(iSerial)) == 0) + return; + + SetEntPropFloat(client, Prop_Send, "m_flProgressBarStartTime", GetGameTime()-1); + SetEntProp(client, Prop_Send, "m_iProgressBarDuration", 0); + + g_bZCleanse_Active[client] = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action OnPlayerRunCmd(int client, int &buttons) +{ + if(!IsPlayerAlive(client)) + return Plugin_Continue; + + if(!ZR_IsClientZombie(client)) + return Plugin_Continue; + + if (g_bZCleanse_Active[client]) + return Plugin_Continue; + + bool bPressingReload = view_as(buttons & IN_RELOAD); + if (!bPressingReload) + { + g_bLastButtonReload[client] = false; + return Plugin_Continue; + } + + if (!g_bLastButtonReload[client]) + { + g_bLastButtonReload[client] = true; + Command_ZCleanse(client, 0); + } + return Plugin_Continue; +} \ No newline at end of file diff --git a/_CSGOFixes/gamedata/CSGOFixes.games.txt b/_CSGOFixes/gamedata/CSGOFixes.games.txt new file mode 100644 index 0000000..4506a48 --- /dev/null +++ b/_CSGOFixes/gamedata/CSGOFixes.games.txt @@ -0,0 +1,67 @@ +"Games" +{ + "csgo" + { + "Addresses" + { + "GameUILag" + { + "signature" "CGameUI::Think" + } + "SpeedModFL" + { + "signature" "CMovementSpeedMod::InputSpeedMod" + } + } + "Signatures" + { + "CGameUI::Think" + { + "library" "server" + "windows" "\x8B\x83\x2A\x2A\x2A\x2A\x8D\x8B\x2A\x2A\x2A\x2A\x53\xFF\x90\x2A\x2A\x2A\x2A\x8B\x45\xF4\x89\x03" + "linux" "\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x89\x34\x24\xE8\x2A\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x8B\x40\x10" + } + "CMovementSpeedMod::InputSpeedMod" + { + "library" "server" + //"windows" "\x74\x0C\x8B\x06\x8B\xCE\x6A\x00\xFF\x90\x2A\x2A\x2A\x2A\x8B\x06" // Crashes on windows atm + "linux" "\xFF\x90\x2A\x2A\x2A\x2A\x85\xC0\x0F\x85\x2A\x2A\x2A\x2A\x8B\x03\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x89\x1C\x24" + } + } + "Offsets" + { + "CBaseEntity::SetParent" + { + "windows" "39" + "linux" "40" + } + "GameUILag_Offset" + { + "windows" "0" + "linux" "0" + } + "GameUILag_PatchSize" + { + "windows" "24" + "linux" "16" + } + "SpeedModFL_Offset" + { + "windows" "0" + "linux" "0" + } + "SpeedModFL_PatchSize" + { + "windows" "12" + "linux" "14" + } + } + "Keys" + { + "SpeedModFL_Patch" + { + "windows" "\xEB" // Replace jz with jmp to always skip over the flashlight disable function + } + } + } +} diff --git a/_CSGOFixes/scripting/CSGOFixes.sp b/_CSGOFixes/scripting/CSGOFixes.sp new file mode 100644 index 0000000..8a403e7 --- /dev/null +++ b/_CSGOFixes/scripting/CSGOFixes.sp @@ -0,0 +1,242 @@ +#include +#include +#include +#pragma semicolon 1 +#pragma newdecls required + +#define PLUGIN_VERSION "1.0" + +public Plugin myinfo = +{ + name = "CS:GO Fixes", + author = "xen", + description = "Fix some CS:GO entity issues", + version = PLUGIN_VERSION, + url = "" +} + +Handle g_hSetParent; + +// Entity solid types +enum +{ + SOLID_NONE = 0, // no solid model + SOLID_BSP = 1, // a BSP tree + SOLID_BBOX = 2, // an AABB + SOLID_OBB = 3, // an OBB (not implemented yet) + SOLID_OBB_YAW = 4, // an OBB, constrained so that it can only yaw + SOLID_CUSTOM = 5, // Always call into the entity for tests + SOLID_VPHYSICS = 6, // solid vphysics object, get vcollide from the model and collide with that + SOLID_LAST, +}; + +// Entity collision groups +enum +{ + COLLISION_GROUP_NONE = 0, // 0 + COLLISION_GROUP_DEBRIS, // 1 - Collides with nothing but world and static stuff + COLLISION_GROUP_DEBRIS_TRIGGER, // 2 - Same as debris, but hits triggers + COLLISION_GROUP_INTERACTIVE_DEBRIS, // 3 - Collides with everything except other interactive debris or debris + COLLISION_GROUP_INTERACTIVE, // 4 - Collides with everything except interactive debris or debris + COLLISION_GROUP_PLAYER, // 5 + COLLISION_GROUP_BREAKABLE_GLASS, // 6 + COLLISION_GROUP_VEHICLE, // 7 + COLLISION_GROUP_PLAYER_MOVEMENT, // 8 - For HL2, same as Collision_Group_Player, for + // TF2, this filters out other players and CBaseObjects + COLLISION_GROUP_NPC, // 9 - Generic NPC group + COLLISION_GROUP_IN_VEHICLE, // 10 - for any entity inside a vehicle + COLLISION_GROUP_WEAPON, // 11 - for any weapons that need collision detection + COLLISION_GROUP_VEHICLE_CLIP, // 12 - vehicle clip brush to restrict vehicle movement + COLLISION_GROUP_PROJECTILE, // 13 - Projectiles! + COLLISION_GROUP_DOOR_BLOCKER, // 14 - Blocks entities not permitted to get near moving doors + COLLISION_GROUP_PASSABLE_DOOR, // 15 - Doors that the player shouldn't collide with + COLLISION_GROUP_DISSOLVING, // 16 - Things that are dissolving are in this group + COLLISION_GROUP_PUSHAWAY, // 17 - Nonsolid on client and server, pushaway in player code + + COLLISION_GROUP_NPC_ACTOR, // 18 - Used so NPCs in scripts ignore the player. + COLLISION_GROUP_NPC_SCRIPTED, // 19 - USed for NPCs in scripts that should not collide with each other + + LAST_SHARED_COLLISION_GROUP +}; + +// Physbox spawnflags +enum +{ + SF_PHYSBOX_ASLEEP = 0x01000, + SF_PHYSBOX_IGNOREUSE = 0x02000, + SF_PHYSBOX_DEBRIS = 0x04000, + SF_PHYSBOX_MOTIONDISABLED = 0x08000, + SF_PHYSBOX_USEPREFERRED = 0x10000, + SF_PHYSBOX_ENABLE_ON_PHYSCANNON = 0x20000, + SF_PHYSBOX_NO_ROTORWASH_PUSH = 0x40000, // The rotorwash doesn't push these + SF_PHYSBOX_ENABLE_PICKUP_OUTPUT = 0x80000, + SF_PHYSBOX_ALWAYS_PICK_UP = 0x100000, // Physcannon can always pick this up, no matter what mass or constraints may apply. + SF_PHYSBOX_NEVER_PICK_UP = 0x200000, // Physcannon will never be able to pick this up. + SF_PHYSBOX_NEVER_PUNT = 0x400000, // Physcannon will never be able to punt this object. + SF_PHYSBOX_PREVENT_PLAYER_TOUCH_ENABLE = 0x800000 // If set, the player will not cause the object to enable its motion when bumped into +}; + +enum struct Patch +{ + char sPatchName[64]; // Signature name to lookup in gamedata + bool bNOP; // Whether it's a full-on NOP patch + Address iPatchAddress; // Patch address to be later filled in from gamedata + char aPatch[128]; // The patch itself + char aPatchRestore[128]; // Buffer to store the original bytes + int iPatchSize; // Length of the patch +} + +Patch g_Patches[] = +{ + {"GameUILag"}, // Prevent game_ui from setting FL_ONTRAIN flag which disables prediction + //{"SpeedModFL"} // Prevent player_speedmod from disabling flashlight +}; + +#define NOP 0x90 + +public void OnPluginStart() +{ + Handle hGameConf = LoadGameConfigFile("CSGOFixes.games"); + if(!hGameConf) + { + SetFailState("Can't find CSGOFixes.games.txt gamedata."); + return; + } + + int offset = GameConfGetOffset(hGameConf, "CBaseEntity::SetParent"); + if (offset == -1) + SetFailState("Failed to find CBaseEntity::SetParent offset"); + + // DHooks. + g_hSetParent = DHookCreate(offset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, Hook_SetParent); + DHookAddParam(g_hSetParent, HookParamType_CBaseEntity); + DHookAddParam(g_hSetParent, HookParamType_Int); + + for (int i = 0; i < sizeof(g_Patches); i++) + { + char sPatchName[128], sPatchOffset[128], sPatchSize[128], sPatch[128]; + strcopy(sPatchName, 128, g_Patches[i].sPatchName); + strcopy(sPatchOffset, 128, g_Patches[i].sPatchName); + strcopy(sPatchSize, 128, g_Patches[i].sPatchName); + strcopy(sPatch, 128, g_Patches[i].sPatchName); + + Address iAddr = GameConfGetAddress(hGameConf, sPatchName); + if(iAddr == Address_Null) + { + CloseHandle(hGameConf); + PrintToServer("Can't find %s address.", sPatchName); + return; + } + + StrCat(sPatchOffset, 128, "_Offset"); + // Get the offset from the start of the signature to the start of our patch area. + int iOffset = GameConfGetOffset(hGameConf, sPatchOffset); + if(iOffset == -1) + { + CloseHandle(hGameConf); + PrintToServer("Can't find Offset for %s in gamedata.", sPatchName); + return; + } + + // Move right in front of the instructions we want to NOP. + iAddr += view_as
(iOffset); + g_Patches[i].iPatchAddress = iAddr; + + StrCat(sPatchSize, 128, "_PatchSize"); + // Get how many bytes we want to NOP. + int iPatchSize = GameConfGetOffset(hGameConf, sPatchSize); + if(iPatchSize == -1) + { + CloseHandle(hGameConf); + PrintToServer("Can't find PatchBytes for %s in gamedata.", sPatchName); + return; + } + g_Patches[i].iPatchSize = iPatchSize; + + StrCat(sPatch, 128, "_Patch"); + // Assume it's a NOP patch unless we get an actual patch array + if(GameConfGetKeyValue(hGameConf, sPatch, g_Patches[i].aPatch, 128)) + { + PrintToServer("%s isn't a NOP patch.", sPatchName); + g_Patches[i].bNOP = true; + } + else + { + PrintToServer("%s is a NOP patch.", sPatchName); + g_Patches[i].bNOP = false; + } + } + CloseHandle(hGameConf); + + ApplyPatches(); +} + +public void OnPluginEnd() +{ + RevertPatches(); +} + +public void ApplyPatches() +{ + for (int i = 0; i < sizeof(g_Patches); i++) + { + Address iAddr = g_Patches[i].iPatchAddress; + + for(int j = 0; j < g_Patches[i].iPatchSize; j++) + { + // Save the current instructions, so we can restore them on unload. + g_Patches[i].aPatchRestore[j] = LoadFromAddress(iAddr, NumberType_Int8); + + if (g_Patches[i].bNOP) + StoreToAddress(iAddr, NOP, NumberType_Int8); + else + StoreToAddress(iAddr, g_Patches[i].aPatch[j], NumberType_Int8); + + iAddr++; + } + } +} + +public void RevertPatches() +{ + for (int i = 0; i < sizeof(g_Patches); i++) + { + Address iAddr = g_Patches[i].iPatchAddress; + + // Restore the original instructions only if we actually patched them + if (iAddr != Address_Null) + { + for(int j = 0; j < g_Patches[i].iPatchSize; j++) + { + StoreToAddress(iAddr, g_Patches[i].aPatchRestore[j], NumberType_Int8); + iAddr++; + } + } + } +} + +public void OnEntityCreated(int iEntity, const char[] sClassname) +{ + if(StrEqual(sClassname, "func_physbox_multiplayer", false)) + { + RequestFrame(SetDebrisCollisionGroup, iEntity); + DHookEntity(g_hSetParent, false, iEntity); + } +} + +public MRESReturn Hook_SetParent(int iEntity, Handle hReturn, Handle hParams) +{ + RequestFrame(SetDebrisCollisionGroup, iEntity); + return MRES_Ignored; +} + +public void SetDebrisCollisionGroup(int iEntity) +{ + if (IsValidEntity(iEntity)) + { + // Set collisiongroup to WEAPON to replicate CS:S behavior when parented + char parent[64]; + if(GetEntPropString(iEntity, Prop_Data, "m_iParent", parent, sizeof(parent))) + SetEntProp(iEntity, Prop_Data, "m_CollisionGroup", COLLISION_GROUP_WEAPON); + } +} diff --git a/_FixGameUILag/gamedata/FixGameUILag.games.txt b/_FixGameUILag/gamedata/FixGameUILag.games.txt new file mode 100644 index 0000000..bae8644 --- /dev/null +++ b/_FixGameUILag/gamedata/FixGameUILag.games.txt @@ -0,0 +1,35 @@ +"Games" +{ + "csgo" + { + "Addresses" + { + "GameUILag" + { + "signature" "CGameUI::Think" + } + } + "Signatures" + { + "CGameUI::Think" + { + "library" "server" + "windows" "\x8B\x83\x2A\x2A\x2A\x2A\x8D\x8B\x2A\x2A\x2A\x2A\x53\xFF\x90\x2A\x2A\x2A\x2A\x8B\x45\xF4\x89\x03" + "linux" "\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x89\x34\x24\xE8\x2A\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x8B\x40\x10" + } + } + "Offsets" + { + "GameUILag_Offset" + { + "windows" "0" + "linux" "0" + } + "GameUILag_PatchSize" + { + "windows" "24" + "linux" "16" + } + } + } +} diff --git a/_FixGameUILag/scripting/FixGameUILag.sp b/_FixGameUILag/scripting/FixGameUILag.sp new file mode 100644 index 0000000..9aee3a3 --- /dev/null +++ b/_FixGameUILag/scripting/FixGameUILag.sp @@ -0,0 +1,85 @@ +#include +#include +#include +#pragma semicolon 1 +#pragma newdecls required + +#define PLUGIN_VERSION "1.0" + +public Plugin myinfo = +{ + name = "Fix game_ui lag", + author = "xen", + description = "Patches out game_ui code that turns off client prediction.", + version = PLUGIN_VERSION, + url = "" +} + +#define NOP 0x90 + +Address g_iPatchAddress; +char g_aPatchRestore[32]; +int g_iPatchSize; + +public void OnPluginStart() +{ + Handle hGameConf = LoadGameConfigFile("FixGameUILag.games"); + if(!hGameConf) + { + SetFailState("Can't find FixGameUILag.games.txt gamedata."); + return; + } + + // Get the start address for the signature + g_iPatchAddress = GameConfGetAddress(hGameConf, "GameUILag"); + if(g_iPatchAddress == Address_Null) + { + CloseHandle(hGameConf); + SetFailState("Invalid or outdated signature for GameUILag in gamedata."); + } + + // Get the offset from our address to the patch area + int iOffset = GameConfGetOffset(hGameConf, "GameUILag_Offset"); + if(iOffset == -1) + { + CloseHandle(hGameConf); + SetFailState("Can't find Offset in gamedata."); + } + + g_iPatchAddress += view_as
(iOffset); + + // Get how many bytes we want to NOP + g_iPatchSize = GameConfGetOffset(hGameConf, "GameUILag_PatchSize"); + if(g_iPatchSize == -1) + { + CloseHandle(hGameConf); + SetFailState("Can't find PatchSize for GameUILag in gamedata."); + } + + CloseHandle(hGameConf); + + Address iAddr = g_iPatchAddress; + + for(int i = 0; i < g_iPatchSize; i++) + { + // Save the current instructions so we can restore them on unload + g_aPatchRestore[i] = LoadFromAddress(iAddr, NumberType_Int8); + StoreToAddress(iAddr, NOP, NumberType_Int8); + iAddr++; + } +} + +public void OnPluginEnd() +{ + Address iAddr = g_iPatchAddress; + + // Sanity check, because you never know + if (iAddr != Address_Null) + { + for(int i = 0; i < g_iPatchSize; i++) + { + StoreToAddress(iAddr, g_aPatchRestore[i], NumberType_Int8); + iAddr++; + } + } +} diff --git a/ammo_giver/scripting/ammo_giver.sp b/ammo_giver/scripting/ammo_giver.sp new file mode 100644 index 0000000..8d67c18 --- /dev/null +++ b/ammo_giver/scripting/ammo_giver.sp @@ -0,0 +1,88 @@ +/* + Why hook weapon_fire? weapon_reload does not fire appropriately if ammo is depleted entirely. SDK Hook'ing can be + expensive and we're trying to keep it as simple as possible without gamedata or other messes. This is the route to take. + + Known bug: Burst fire is not checked for weapons (famas & glock) and does not apply appropriate ammo as the event + fires only once. For us, we don't really care for it. It is worth noting and fixing at a later appropriate time... (when?) +*/ + +ConVar AmmoFilter = null; + +char AmmoToFilter[32][64]; + +int AmmoFilterTotal; + +bool ShouldProceed; + +public Plugin myinfo = +{ + name = "Ammo Giver", + author = "Mapeadores", + description = "Refills ammo on fire.", + version = "0.2", + url = "" +}; + +public void OnPluginStart() +{ + AmmoFilter = CreateConVar("sm_ammogiver_filter", "none", "Ammo to filter by ent. e.g. none, all, or decoy,flashbang,grenade,..."); + + HookEvent("weapon_fire", Event_WeaponFire, EventHookMode_Post); +} + +public void OnMapStart() +{ + char AmmoFilterList[256]; + AmmoFilter.GetString(AmmoFilterList, sizeof(AmmoFilterList)); + + AmmoFilterTotal = ExplodeString(AmmoFilterList, ",", AmmoToFilter, sizeof(AmmoToFilter), sizeof(AmmoToFilter[])) - 1; + + if (strlen(AmmoToFilter[0]) == 0 || StrEqual(AmmoToFilter[0], "all", false)) + ShouldProceed = false; + else + ShouldProceed = true; +} + +public void Event_WeaponFire(Handle event, char[] name, bool dontBroadcast) +{ + if (ShouldProceed){ + int PlayerID = GetClientOfUserId(GetEventInt(event, "userid")); + if (IsClientConnected(PlayerID)){ + char WeaponName[256]; + GetEventString(event, "weapon", WeaponName, sizeof(WeaponName)); + + bool DontGiveAmmo; + for (int i = 0; i <= AmmoFilterTotal; i++) + if (!DontGiveAmmo && StrContains(WeaponName, AmmoToFilter[i], false) != -1) + DontGiveAmmo = true; + + if (!DontGiveAmmo) SetReserveAmmo(PlayerID, GetReserveAmmo(PlayerID) + 1); + } + } +} + +stock int GetReserveAmmo(int iClient) +{ + int iWep = GetEntPropEnt(iClient, Prop_Data, "m_hActiveWeapon"); + + if(iWep < 1) return -1; + + int iAmmoType = GetEntProp(iWep, Prop_Send, "m_iPrimaryAmmoType"); + + if (iAmmoType < 0) return -1; + + return GetEntProp(iClient, Prop_Send, "m_iAmmo", _, iAmmoType); +} + +stock void SetReserveAmmo(int iClient, int iAmmo) +{ + int iWep = GetEntPropEnt(iClient, Prop_Data, "m_hActiveWeapon"); + + if(iWep < 1) return; + + int iAmmoType = GetEntProp(iWep, Prop_Send, "m_iPrimaryAmmoType"); + + if(iAmmoType == -1) return; + + SetEntProp(iClient, Prop_Send, "m_iAmmo", iAmmo, _, iAmmoType); +} \ No newline at end of file diff --git a/compile-all.py b/compile-all.py new file mode 100644 index 0000000..3efd608 --- /dev/null +++ b/compile-all.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +import os +import sys +import subprocess + +c_null = "\x1b[00;00m" +c_red = "\x1b[31;01m" +c_blue = "\x1b[34;01m" +c_green = "\x1b[32;01m" + +SM_INCLUDES = "includes" +SPCOMP = "../spcomp" + +if __name__ == "__main__": + Plugins = [] + Errors = [] + Path, Directories, Files = next(os.walk(".")) + for Directory in Directories: + if not Directory.startswith(".") and not Directory.startswith("_") and \ + Directory != "include" and Directory != "includes" and Directory != "plugins" and Directory != "secrets": + Plugins.append(Directory) + + for Plugin in Plugins: + print(c_red + "### Compiling {0}".format(Plugin) + c_null) + + SourcePath = os.path.join(Plugin, "scripting") + Path, Directories, Files = next(os.walk(SourcePath)) + for File in Files: + if File.endswith(".sp"): + print(c_green + "# Compiling {0} ({1})".format(os.path.basename(File), Plugin) + c_null) + SourcePath = os.path.join(Path, File) + IncludePath = os.path.join(Path, "include") + OutDir = "plugins" + OutPath = os.path.join(OutDir, os.path.splitext(os.path.basename(SourcePath))[0] + ".smx") + + Compiler = [SPCOMP, "-i" + SM_INCLUDES, "-i" + "include", "-i" + "secrets"] + if os.path.isdir(IncludePath): + Compiler.append("-i" + IncludePath) + Compiler.append(SourcePath) + Compiler.append("-o" + OutPath) + + try: + err = subprocess.call(Compiler) + if err: + raise Exception() + except Exception: + Errors.append(Plugin) + + for Plugin in Errors: + print(c_red + "### ERROR compiling " + Plugin + c_null) + diff --git a/compile.py b/compile.py new file mode 100644 index 0000000..7c370fb --- /dev/null +++ b/compile.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +import os +import sys +import subprocess + +c_null = "\x1b[00;00m" +c_red = "\x1b[31;01m" +c_blue = "\x1b[34;01m" +c_green = "\x1b[32;01m" + +SM_INCLUDES = "includes" +SPCOMP = "../spcomp" + +if __name__ == "__main__": + Plugins = [] + for Directory in sys.argv[1:]: + if Directory != ".git" and Directory != "include" and Directory != "includes" and Directory != "plugins" and Directory != "secrets": + Plugins.append(Directory) + + for Plugin in Plugins: + SourcePath = os.path.join(Plugin, "scripting") + Path, Directories, Files = next(os.walk(SourcePath)) + for File in Files: + if File.endswith(".sp"): + print(c_green + "# Compiling {0} ({1})".format(os.path.basename(File), Plugin) + c_null) + SourcePath = os.path.join(Path, File) + IncludePath = os.path.join(Path, "include") + OutDir = "plugins" + OutPath = os.path.join(OutDir, os.path.splitext(os.path.basename(SourcePath))[0] + ".smx") + + Compiler = [SPCOMP, "-i" + SM_INCLUDES, "-i" + "include", "-i" + "secrets"] + if os.path.isdir(IncludePath): + Compiler.append("-i" + IncludePath) + Compiler.append(SourcePath) + Compiler.append("-o" + OutPath) + + try: + subprocess.call(Compiler) + except Exception: + sys.exit(1) + + print("") diff --git a/dzspriterendermode_fix/scripting/dzspriterendermode_fix.sp b/dzspriterendermode_fix/scripting/dzspriterendermode_fix.sp new file mode 100644 index 0000000..1b50da6 --- /dev/null +++ b/dzspriterendermode_fix/scripting/dzspriterendermode_fix.sp @@ -0,0 +1,59 @@ +/* + Note: Map weapons/items will look like ass. For each update, ensure this is necessary. + Bug: Render modes of anything parented onto weapons will act the same way onto players. + Temp Fix: Change render mode to something flat. + + Idea by Darnias. +*/ +public Plugin myinfo = +{ + name = "Weapon Sprite Temp-Fix", + author = "Mapeadores", + description = "Changes render mode for parented sprites post-DZ Sirocco.", + version = "0.2", + url = "" +}; + +public OnEntityCreated(int entity, char[] classname) +{ + if (IsValidEntity(entity) && strcmp(classname, "env_sprite", false) == 0) RequestFrame(CheckSprite, entity); +} + +public CheckSprite(int entity) +{ + if (IsValidEntity(entity)){ + int ParentEntity = GetEntPropEnt(entity, Prop_Data, "m_pParent"); + + if (IsValidEntity(ParentEntity)){ + ParentEntity = GetRootMoveParent(ParentEntity); + + char ParentClassName[32]; + GetEntityClassname(ParentEntity, ParentClassName, sizeof(ParentClassName)); + + if (StrContains(ParentClassName, "weapon_", false) != -1){ + if (GetEntityRenderMode(entity) == RENDER_GLOW || GetEntityRenderMode(entity) == RENDER_WORLDGLOW){ + //If a glow sprite, reduce inflated scale so it isn't massive for the player. + if (GetEntityRenderMode(entity) == RENDER_GLOW && GetEntPropFloat(entity, Prop_Data, "m_flSpriteScale") > 0.35) SetEntPropFloat(entity, Prop_Data, "m_flSpriteScale", 0.35); + + SetEntityRenderMode(entity, RENDER_TRANSADD); + } + } + } + } +} + +int GetRootMoveParent(iEntity) { + int iCurrentEntity = iEntity; + + while (IsValidEntity(iCurrentEntity)) { + int iCurrentParent = GetEntPropEnt(iCurrentEntity, Prop_Data, "m_pParent"); + + if (!IsValidEntity(iCurrentParent)) { + return iCurrentEntity; + } + + iCurrentEntity = iCurrentParent; + } + + return iCurrentEntity; +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_classic.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_classic.cfg new file mode 100644 index 0000000..aa6d01b --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_classic.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "{green}" + "color_name" "{default}" + "color_steamid" "{grey}" + "color_use" "{lightblue}" + "color_pickup" "{lime}" + "color_drop" "{pink}" + "color_disconnect" "{orange}" + "color_death" "{orange}" + "color_warning" "{orange}" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_crayon.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_crayon.cfg new file mode 100644 index 0000000..41febfc --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_crayon.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "E01B5D" + "color_name" "1A8599" + "color_steamid" "5FBDCE" + "color_use" "FF7C00" + "color_pickup" "FFDF00" + "color_drop" "CB0077" + "color_disconnect" "04859D" + "color_death" "4F10AD" + "color_warning" "4F10AD" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_css.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_css.cfg new file mode 100644 index 0000000..7ac00bc --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_css.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "32FF32" + "color_name" "9AFF9A" + "color_steamid" "D2C898" + "color_use" "9EC34F" + "color_pickup" "9EC34F" + "color_drop" "9EC34F" + "color_disconnect" "9EC34F" + "color_death" "9EC34F" + "color_warning" "9EC34F" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_fairyfloss.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_fairyfloss.cfg new file mode 100644 index 0000000..78fa6ed --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_fairyfloss.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "E01B5D" + "color_name" "EDEDED" + "color_steamid" "B2B2B2" + "color_use" "67ADDF" + "color_pickup" "C9EF66" + "color_drop" "E562BA" + "color_disconnect" "F1B567" + "color_death" "F1B567" + "color_warning" "F16767" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_gfl.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_gfl.cfg new file mode 100644 index 0000000..648837a --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_gfl.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "8A0808" + "color_name" "BDBDBD" + "color_steamid" "4E4E4E" + "color_use" "BDBDBD" + "color_pickup" "BDBDBD" + "color_drop" "BDBDBD" + "color_disconnect" "BDBDBD" + "color_death" "BDBDBD" + "color_warning" "BDBDBD" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_hikka.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_hikka.cfg new file mode 100644 index 0000000..6f5cb86 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_hikka.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "{green}" + "color_name" "\x01" + "color_steamid" "{olive}" + "color_use" "{purple}" + "color_pickup" "{purple}" + "color_drop" "{red}" + "color_disconnect" "{red}" + "color_death" "{red}" + "color_warning" "{lightgreen}" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_midnight.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_midnight.cfg new file mode 100644 index 0000000..0141d1c --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_midnight.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "E01B5D" + "color_name" "E7EB75" + "color_steamid" "8A714A" + "color_use" "4C46EB" + "color_pickup" "46EB54" + "color_drop" "BF3939" + "color_disconnect" "7B4F9E" + "color_death" "732C34" + "color_warning" "732C34" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_noctali.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_noctali.cfg new file mode 100644 index 0000000..a1dc974 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/color_noctali.cfg @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "800000" + "color_name" "807E7E" + "color_steamid" "4A4A4A" + "color_use" "807E7E" + "color_pickup" "807E7E" + "color_drop" "AB0000" + "color_disconnect" "AB0000" + "color_death" "AB0000" + "color_warning" "807E7E" + "color_hud_red" "255" + "color_hud_green" "255" + "color_hud_blue" "255" + "color_hud_posx" "0.0" + "color_hud_posy" "0.4" +} diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/colors/template.txt b/entWatch_csgo/cfg/sourcemod/entwatch/colors/template.txt new file mode 100644 index 0000000..869c7cb --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/colors/template.txt @@ -0,0 +1,17 @@ +"colors" +{ + "color_tag" "" + "color_name" "" + "color_steamid" "" + "color_use" "" + "color_pickup" "" + "color_drop" "" + "color_disconnect" "" + "color_death" "" + "color_warning" "" + "color_hud_red" "" + "color_hud_green" "" + "color_hud_blue" "" + "color_hud_posx" "" + "color_hud_posy" "" +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/maps/template.txt b/entWatch_csgo/cfg/sourcemod/entwatch/maps/template.txt new file mode 100644 index 0000000..16b9d73 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/maps/template.txt @@ -0,0 +1,22 @@ +"entities" +{ + "0" + { + "name" "" + "shortname" "" + "color" "" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "false" + "hud" "false" + "hammerid" "0" + "mode" "0" + "maxuses" "0" + "cooldown" "0" + "maxamount" "0" + } +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_football_go2.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_football_go2.cfg new file mode 100644 index 0000000..cf2da03 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_football_go2.cfg @@ -0,0 +1,117 @@ +"entities" +{ + "0" + { + "name" "Dark Repulser(Wind)" + "shortname" "Wind" + "color" "{lightblue}" + "buttonclass" "func_button" + "filtername" "f_m_wind_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "39002" + "mode" "2" + "maxuses" "0" + "cooldown" "20" + "maxamount" "1" + } + "1" + { + "name" "Dark Repulser(ZWind)" + "shortname" "ZWind" + "color" "{blue}" + "buttonclass" "func_button" + "filtername" "f_m_wind_zombie" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "39885" + "mode" "2" + "maxuses" "0" + "cooldown" "20" + "maxamount" "1" + } + "2" + { + "name" "Katana(Speed)" + "shortname" "Speed" + "color" "{olive}" + "buttonclass" "func_button" + "filtername" "f_m_speed_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "40110" + "mode" "2" + "maxuses" "0" + "cooldown" "20" + "maxamount" "1" + } + "3" + { + "name" "Katana(ZSpeed)" + "shortname" "ZSpeed" + "color" "{green}" + "buttonclass" "func_button" + "filtername" "f_m_speed_zombie" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "40141" + "mode" "2" + "maxuses" "0" + "cooldown" "20" + "maxamount" "1" + } + "4" + { + "name" "Elucidator(Gravity)" + "shortname" "Gravity" + "color" "{pink}" + "buttonclass" "func_button" + "filtername" "f_m_gravity_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "41391" + "mode" "2" + "maxuses" "0" + "cooldown" "20" + "maxamount" "1" + } + "5" + { + "name" "Elucidator(ZGravity)" + "shortname" "ZGravity" + "color" "{purple}" + "buttonclass" "func_button" + "filtername" "f_m_gravity_zombie" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "42566" + "mode" "2" + "maxuses" "0" + "cooldown" "20" + "maxamount" "1" + } +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_harry_potter_v2_1_csgo.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_harry_potter_v2_1_csgo.cfg new file mode 100644 index 0000000..927b146 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_harry_potter_v2_1_csgo.cfg @@ -0,0 +1,420 @@ +"entities" +{ + "0" + { + "name" "Incendio (fire)" + "shortname" "Incendio" + "color" "{red}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "8974097" + "mode" "2" + "maxuses" "0" + "cooldown" "55" + "maxamount" "1" + "trigger" "59863086" + } + "1" + { + "name" "Impedimenta (ice)" + "shortname" "Impedimenta" + "color" "{lightblue}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "8975848" + "mode" "2" + "maxuses" "0" + "cooldown" "70" + "maxamount" "1" + "trigger" "59863090" + } + "2" + { + "name" "Flipendo (wind)" + "shortname" "Flipendo" + "color" "{default}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "8977321" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + "trigger" "59863094" + } + "3" + { + "name" "Avada kedavra" + "shortname" "Avada kedavra" + "color" "{purple}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "8977961" + "mode" "2" + "maxuses" "0" + "cooldown" "140" + "maxamount" "1" + "trigger" "59863102" + } + "4" + { + "name" "Reparo (heal)" + "shortname" "Reparo" + "color" "{green}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "8978959" + "mode" "2" + "maxuses" "0" + "cooldown" "50" + "maxamount" "1" + "trigger" "59863110" + } + "5" + { + "name" "Protego (wall)" + "shortname" "Protego" + "color" "{darkorange}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "8979602" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + "trigger" "59863098" + } + "6" + { + "name" "Accio (ammo)" + "shortname" "Accio" + "color" "{grey}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "17845448" + "mode" "2" + "maxuses" "0" + "cooldown" "50" + "maxamount" "1" + "trigger" "59863114" + } + "7" + { + "name" "Crucio (poison)" + "shortname" "Crucio" + "color" "{orange}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "17845738" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + "trigger" "59863106" + } + "8" + { + "name" "Aguamenti (slowmo)" + "shortname" "Aguamenti" + "color" "{lightblue}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "17845811" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + "trigger" "59863118" + } + "9" + { + "name" "Incendio (zfire)" + "shortname" "ZIncendio" + "color" "{darkred}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "9125676" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + "trigger" "9005859" + } + "10" + { + "name" "Emendo (zheal)" + "shortname" "Emendo" + "color" "{purple}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "9127617" + "mode" "2" + "maxuses" "0" + "cooldown" "50" + "maxamount" "1" + "trigger" "9007824" + } + "11" + { + "name" "Confundus (zconfusing)" + "shortname" "Confundus" + "color" "{lime}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "9128126" + "mode" "2" + "maxuses" "0" + "cooldown" "40" + "maxamount" "1" + "trigger" "9007828" + } + "12" + { + "name" "Deprimo (zslowmo)" + "shortname" "Deprimo" + "color" "{blue}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "9128865" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + "trigger" "9007832" + } + "13" + { + "name" "Expulso (znuker)" + "shortname" "Expulso" + "color" "{green}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "9129450" + "mode" "2" + "maxuses" "0" + "cooldown" "80" + "maxamount" "1" + "trigger" "9007836" + } + "14" + { + "name" "Disillusionment (zinvisible)" + "shortname" "Disillusionment" + "color" "{grey}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "17845958" + "mode" "2" + "maxuses" "0" + "cooldown" "50" + "maxamount" "1" + "trigger" "17955655" + } + "15" + { + "name" "Conjunctivitus (zpoison)" + "shortname" "Conjunctivitus" + "color" "{orange}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "17846183" + "mode" "2" + "maxuses" "0" + "cooldown" "40" + "maxamount" "1" + "trigger" "17955634" + } + "16" + { + "name" "Deletrius (zwand breaker)" + "shortname" "Deletrius" + "color" "{lightblue}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "17846257" + "mode" "1" + "maxuses" "0" + "cooldown" "0" + "maxamount" "1" + "trigger" "17955652" + } + "17" + { + "name" "Reducio (zminimizer)" + "shortname" "Reducio" + "color" "{pink}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "false" + "chat" "true" + "hud" "true" + "hammerid" "20869434" + "mode" "2" + "maxuses" "0" + "cooldown" "105" + "maxamount" "1" + "trigger" "20840257" + } + "18" + { + "name" "Builder" + "shortname" "Builder" + "color" "{olive}" + "buttonclass" "func_button" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "37642786" + "mode" "4" + "maxuses" "8" + "cooldown" "1" + "maxamount" "1" + } + "19" + { + "name" "Hat" + "shortname" "Hat" + "color" "{blue}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "41919111" + "mode" "1" + "maxuses" "0" + "cooldown" "0" + "maxamount" "1" + } + "20" + { + "name" "Owl" + "shortname" "Owl" + "color" "{blue}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "20485274" + "mode" "1" + "maxuses" "0" + "cooldown" "0" + "maxamount" "1" + } +} \ No newline at end of file diff --git a/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_surf_dark_fantasy_v1go3.cfg b/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_surf_dark_fantasy_v1go3.cfg new file mode 100644 index 0000000..9fcce17 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/entwatch/maps/ze_surf_dark_fantasy_v1go3.cfg @@ -0,0 +1,250 @@ +"entities" +{ + "0" + { + "name" "Collector Shield" + "shortname" "Shield" + "color" "{darkorange}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "985181" + "mode" "0" + "maxuses" "0" + "cooldown" "0" + "maxamount" "20" + } + "1" + { + "name" "Collector Sword" + "shortname" "Sword" + "color" "{lightblue}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "987821" + "mode" "0" + "maxuses" "0" + "cooldown" "0" + "maxamount" "20" + } + "2" + { + "name" "Collector Spear" + "shortname" "Spear" + "color" "{red}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "987871" + "mode" "0" + "maxuses" "0" + "cooldown" "0" + "maxamount" "20" + } + "3" + { + "name" "Collector Bow" + "shortname" "Bow" + "color" "{green}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "987911" + "mode" "0" + "maxuses" "0" + "cooldown" "0" + "maxamount" "20" + } + "4" + { + "name" "Seal" + "shortname" "Seal" + "color" "{pink}" + "buttonclass" "" + "filtername" "" + "hasfiltername" "false" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "false" + "hammerid" "1296712" + "mode" "0" + "maxuses" "0" + "cooldown" "0" + "maxamount" "1" + } + "5" + { + "name" "Wind" + "shortname" "Wind" + "color" "{default}" + "buttonclass" "func_button" + "filtername" "f_wind_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1451331" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + } + "6" + { + "name" "Gravity" + "shortname" "Gravity" + "color" "{purple}" + "buttonclass" "func_button" + "filtername" "f_gravity_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1451409" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + } + "7" + { + "name" "Electro" + "shortname" "Electro" + "color" "{olive}" + "buttonclass" "func_button" + "filtername" "f_electro_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1451632" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + } + "8" + { + "name" "Heal" + "shortname" "Heal" + "color" "{green}" + "buttonclass" "func_button" + "filtername" "f_heal_human" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "true" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1451852" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + } + "9" + { + "name" "ZHeal" + "shortname" "ZHeal" + "color" "{red}" + "buttonclass" "func_button" + "filtername" "f_heal_zombie" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1452000" + "mode" "2" + "maxuses" "0" + "cooldown" "30" + "maxamount" "1" + } + "10" + { + "name" "ZDroper" + "shortname" "ZDroper" + "color" "{blue}" + "buttonclass" "func_button" + "filtername" "f_droper_zombie" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1452075" + "mode" "2" + "maxuses" "0" + "cooldown" "60" + "maxamount" "1" + } + "11" + { + "name" "ZFire" + "shortname" "ZFire" + "color" "{orange}" + "buttonclass" "func_button" + "filtername" "f_fire_zombie" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1452310" + "mode" "2" + "maxuses" "0" + "cooldown" "30" + "maxamount" "2" + } + "12" + { + "name" "Shia Trophy" + "shortname" "Shia Trophy" + "color" "{lightblue}" + "buttonclass" "func_button" + "filtername" "f_trophy_shea" + "hasfiltername" "true" + "blockpickup" "false" + "allowtransfer" "false" + "forcedrop" "true" + "chat" "true" + "hud" "true" + "hammerid" "1452391" + "mode" "3" + "maxuses" "1" + "cooldown" "0" + "maxamount" "1" + } +} diff --git a/entWatch_csgo/cfg/sourcemod/plugin.entWatch.cfg b/entWatch_csgo/cfg/sourcemod/plugin.entWatch.cfg new file mode 100644 index 0000000..34a3bf3 --- /dev/null +++ b/entWatch_csgo/cfg/sourcemod/plugin.entWatch.cfg @@ -0,0 +1,31 @@ +// This file was auto-generated by SourceMod (v1.9.0.6281) +// ConVars for plugin "entWatch.smx" + + +// The name of the color config. +// - +// Default: "color_classic" +entwatch_config_color "color_classic" + +// Show/Hide the cooldowns on the display. +// - +// Default: "1" +// Minimum: "0.000000" +// Maximum: "1.000000" +entwatch_display_cooldowns "1" + +// Enable/Disable the display. +// - +// Default: "1" +// Minimum: "0.000000" +// Maximum: "1.000000" +entwatch_display_enable "1" + +// Enable/Disable team only mode. +// - +// Default: "1" +// Minimum: "0.000000" +// Maximum: "1.000000" +entwatch_mode_teamonly "1" + + diff --git a/entWatch_csgo/scripting/entWatch_csgo.sp b/entWatch_csgo/scripting/entWatch_csgo.sp new file mode 100644 index 0000000..ccc1a2e --- /dev/null +++ b/entWatch_csgo/scripting/entWatch_csgo.sp @@ -0,0 +1,2850 @@ +//==================================================================================================== +// +// Name: entWatch +// Author: Prometheum & zaCade +// Description: Monitor entity interactions. +// +//==================================================================================================== +#pragma semicolon 1 +#include +#include +#include +#include +#include +#tryinclude +#tryinclude +#pragma newdecls required + +#define PLUGIN_VERSION "3.8.143" + +//---------------------------------------------------------------------------------------------------- +// Purpose: Entity data +//---------------------------------------------------------------------------------------------------- +enum entities +{ + String:ent_name[32], + String:ent_shortname[32], + String:ent_color[32], + String:ent_buttonclass[32], + String:ent_filtername[32], + bool:ent_hasfiltername, + bool:ent_blockpickup, + bool:ent_allowtransfer, + bool:ent_forcedrop, + bool:ent_chat, + bool:ent_hud, + ent_hammerid, + ent_weaponid, + ent_buttonid, + ent_ownerid, + ent_mode, // 0 = No iButton, 1 = Spam protection only, 2 = Cooldowns, 3 = Limited uses, 4 = Limited uses with cooldowns, 5 = Cooldowns after multiple uses. + ent_uses, + ent_maxuses, + ent_cooldown, + ent_cooldowntime, + ent_glowent, + ent_glow_r, + ent_glow_g, + ent_glow_b, +}; + +int entArray[512][entities], + entArraySize = 512, + triggerArray[512], + triggerSize = 512; + +//---------------------------------------------------------------------------------------------------- +// Purpose: Color settings +//---------------------------------------------------------------------------------------------------- +char color_tag[16] = "{green}", + color_name[16] = "{default}", + color_steamid[16] = "{grey}", + color_use[16] = "{lightblue}", + color_pickup[16] = "{lime}", + color_drop[16] = "{pink}", + color_disconnect[16] = "{orange}", + color_death[16] = "{orange}", + color_warning[16] = "{orange}"; + +//---------------------------------------------------------------------------------------------------- +// Purpose: Client settings +//---------------------------------------------------------------------------------------------------- +Handle g_hCookie_Restricted = null, + g_hCookie_RestrictedLength = null, + g_hCookie_RestrictedIssued = null, + g_hCookie_RestrictedBy = null, + g_hCookie_Display = null, + g_hCookie_HudPos = null, + g_hCookie_HudColor = null; + +bool g_bRestricted[MAXPLAYERS + 1] = false; +char g_sRestrictedBy[MAXPLAYERS + 1][64]; +int g_iRestrictedLength[MAXPLAYERS + 1], + g_iRestrictedIssued[MAXPLAYERS + 1], + g_iAdminMenuTarget[MAXPLAYERS + 1]; + +//---------------------------------------------------------------------------------------------------- +// Purpose: Plugin settings +//---------------------------------------------------------------------------------------------------- +ConVar g_hCvar_DisplayEnabled, + g_hCvar_DisplayCooldowns, + g_hCvar_ModeTeamOnly, + g_hCvar_ConfigColor; + +Handle g_hAdminMenu, + g_hOnBanForward, + g_hOnUnbanForward; + +bool g_bRoundTransition = false, + g_bConfigLoaded = false, + g_bLateLoad = false; + +bool isMapRunning; + +bool g_bDisplay[MAXPLAYERS + 1] = false; +bool g_bDisplay2[MAXPLAYERS + 1] = false; +float g_DefaultHudPos[2]; +int g_DefaultHudColor[3]; +int ItemIdx=1; +char ShowCools[64][512]; +char ShowCoolsPlayerName[64][512]; +float HudPosition[MAXPLAYERS+1][2]; +int HudColor[MAXPLAYERS+1][3]; +//---------------------------------------------------------------------------------------------------- +// Purpose: Plugin information +//---------------------------------------------------------------------------------------------------- +public Plugin myinfo = +{ + name = "entWatch", + author = "Prometheum & zaCade. Edits: George & Obus & BotoX & Hikka & DarkerZ[RUS]", + description = "Notify players about entity interactions.", + version = PLUGIN_VERSION, + url = "https://github.com/darkerz7/CSGO-Plugins/tree/master/EntWatch%20Hud" +}; + +public APLRes AskPluginLoad2(Handle hThis, bool bLate, char[] sError, int iErr_max) +{ + CreateNative("entWatch_IsClientBanned", Native_IsClientBanned); + CreateNative("entWatch_BanClient", Native_BanClient); + CreateNative("entWatch_UnbanClient", Native_UnbanClient); + CreateNative("entWatch_IsSpecialItem", Native_IsSpecialItem); + CreateNative("entWatch_HasSpecialItem", Native_HasSpecialItem); + + RegPluginLibrary("entWatch"); + + g_bLateLoad = bLate; + + return APLRes_Success; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Plugin initialization +//---------------------------------------------------------------------------------------------------- +public void OnPluginStart() +{ + CreateConVar("entwatch_version", PLUGIN_VERSION, "Current version of entWatch", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); + + g_hCvar_DisplayEnabled = CreateConVar("entwatch_display_enable", "1", "Enable/Disable the display.", _, true, 0.0, true, 1.0); + g_hCvar_DisplayCooldowns = CreateConVar("entwatch_display_cooldowns", "1", "Show/Hide the cooldowns on the display.", _, true, 0.0, true, 1.0); + g_hCvar_ModeTeamOnly = CreateConVar("entwatch_mode_teamonly", "1", "Enable/Disable team only mode.", _, true, 0.0, true, 1.0); + g_hCvar_ConfigColor = CreateConVar("entwatch_config_color", "color_classic", "The name of the color config.", _); + + g_hCookie_Display = RegClientCookie("entwatch_display", "", CookieAccess_Private); + g_hCookie_Restricted = RegClientCookie("entwatch_restricted", "", CookieAccess_Private); + g_hCookie_RestrictedLength = RegClientCookie("entwatch_restrictedlength", "", CookieAccess_Private); + g_hCookie_RestrictedIssued = RegClientCookie("entwatch_restrictedissued", "", CookieAccess_Private); + g_hCookie_RestrictedBy = RegClientCookie("entwatch_restrictedby", "", CookieAccess_Private); + g_hCookie_HudPos = RegClientCookie("entwatch_hudpos", "", CookieAccess_Private); + g_hCookie_HudColor = RegClientCookie("entwatch_hudcolor", "", CookieAccess_Private); + + Handle hTopMenu; + + if (LibraryExists("adminmenu") && ((hTopMenu = GetAdminTopMenu()) != INVALID_HANDLE)) OnAdminMenuReady(hTopMenu); + + RegConsoleCmd("sm_status", Command_Status); + + RegAdminCmd("sm_eban", Command_Restrict, ADMFLAG_BAN); + RegAdminCmd("sm_ebanlist", Command_EBanlist, ADMFLAG_BAN); + RegAdminCmd("sm_eunban", Command_Unrestrict, ADMFLAG_BAN); + RegAdminCmd("sm_etransfer", Command_Transfer, ADMFLAG_BAN); + RegAdminCmd("sm_setcooldown", Command_Cooldown, ADMFLAG_BAN); + RegAdminCmd("sm_ew_reloadconfig", Command_ReloadConfig, ADMFLAG_CONFIG); + RegAdminCmd("sm_ewdebugarray", Command_DebugArray, ADMFLAG_CONFIG); + + RegConsoleCmd("sm_hud", Command_ToggleHUD); + RegConsoleCmd("sm_hudpos", Command_Hudpos); + RegConsoleCmd("sm_hudcolor", Command_HudColor); + + HookEventEx("round_start", Event_RoundStart, EventHookMode_Pre); + HookEventEx("round_end", Event_RoundEnd, EventHookMode_Pre); + HookEventEx("player_death", Event_PlayerDeath, EventHookMode_Pre); + + CreateTimer(1.0, Timer_DisplayHUD, _, TIMER_REPEAT); + CreateTimer(1.0, Timer_Cooldowns, _, TIMER_REPEAT); + + LoadTranslations("entWatch.phrases"); + LoadTranslations("common.phrases"); + + AutoExecConfig(true, "plugin.entWatch"); + + g_hOnBanForward = CreateGlobalForward("entWatch_OnClientBanned", ET_Ignore, Param_Cell, Param_Cell, Param_Cell); + g_hOnUnbanForward = CreateGlobalForward("entWatch_OnClientUnbanned", ET_Ignore, Param_Cell, Param_Cell); + + if (g_bLateLoad) { + for (int i = 1; i <= MaxClients; i++) { + if (!IsClientInGame(i) || IsFakeClient(i)) continue; + OnClientPutInServer(i); + OnClientCookiesCached(i); + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Main ban function +//---------------------------------------------------------------------------------------------------- +void EBanClient(int iClient, const char[] sLength, int iAdmin) +{ + int iBanLen = StringToInt(sLength); + int iBanDuration = (iBanLen - GetTime()) / 60; + + if (iAdmin != 0) + { + char sAdminSID[64]; + GetClientAuthId(iAdmin, AuthId_Steam2, sAdminSID, sizeof(sAdminSID)); + FormatEx(g_sRestrictedBy[iClient], sizeof(g_sRestrictedBy[]), "%s (%N)", sAdminSID, iAdmin); + + SetClientCookie(iClient, g_hCookie_RestrictedBy, sAdminSID); + } else { + FormatEx(g_sRestrictedBy[iClient], sizeof(g_sRestrictedBy[]), "Console"); + SetClientCookie(iClient, g_hCookie_RestrictedBy, "Console"); + } + + switch (iBanLen) { + case 0: { + iBanDuration = 0; + g_bRestricted[iClient] = true; + LogAction(iAdmin, iClient, "\"%L\" restricted \"%L\"", iAdmin, iClient); + } + } + if (iBanLen != 1) + { + g_iRestrictedLength[iClient] = iBanLen; + SetClientCookie(iClient, g_hCookie_RestrictedLength, sLength); + LogAction(iAdmin, iClient, "\"%L\" restricted \"%L\" for %d Minutes", iAdmin, iClient, iBanDuration); + } else { + iBanDuration = -1; + g_iRestrictedLength[iClient] = 1; + SetClientCookie(iClient, g_hCookie_RestrictedLength, "1"); + LogAction(iAdmin, iClient, "\"%L\" restricted \"%L\" permanently", iAdmin, iClient); + } + + char sIssueTime[64]; + FormatEx(sIssueTime, sizeof(sIssueTime), "%d", GetTime()); + + g_iRestrictedIssued[iClient] = GetTime(); + SetClientCookie(iClient, g_hCookie_RestrictedIssued, sIssueTime); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iAdmin, color_warning, "restricted", color_name, iClient); + + Call_StartForward(g_hOnBanForward); + Call_PushCell(iAdmin); + Call_PushCell(iBanDuration); + Call_PushCell(iClient); + Call_Finish(); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Main unban function +//---------------------------------------------------------------------------------------------------- +void EUnbanClient(int iClient, int iAdmin) +{ + g_bRestricted[iClient] = false; + g_iRestrictedLength[iClient] = 0; + g_iRestrictedIssued[iClient] = 0; + g_sRestrictedBy[iClient][0] = '\0'; + SetClientCookie(iClient, g_hCookie_RestrictedLength, "0"); + SetClientCookie(iClient, g_hCookie_RestrictedBy, ""); + SetClientCookie(iClient, g_hCookie_RestrictedIssued, ""); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iAdmin, color_warning, "unrestricted", color_name, iClient); + LogAction(iAdmin, iClient, "\"%L\" unrestricted \"%L\"", iAdmin, iClient); + + Call_StartForward(g_hOnUnbanForward); + Call_PushCell(iAdmin); + Call_PushCell(iClient); + Call_Finish(); +} +//---------------------------------------------------------------------------------------------------- +// Purpose: Safeguard against adminmenu unloading +//---------------------------------------------------------------------------------------------------- +public void OnLibraryRemoved(const char[] sName) { + if (strcmp(sName, "adminmenu") == 0) g_hAdminMenu = INVALID_HANDLE; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Add our entries to the main admin menu +//---------------------------------------------------------------------------------------------------- +public void OnAdminMenuReady(Handle hAdminMenu) +{ + if (hAdminMenu == g_hAdminMenu) return; + + g_hAdminMenu = hAdminMenu; + + TopMenuObject hMenuObj = AddToTopMenu(g_hAdminMenu, "entWatch_commands", TopMenuObject_Category, AdminMenu_Commands_Handler, INVALID_TOPMENUOBJECT); + + switch (hMenuObj) { + case INVALID_TOPMENUOBJECT: return; + } + + AddToTopMenu(g_hAdminMenu, "entWatch_banlist", TopMenuObject_Item, Handler_EBanList, hMenuObj, "sm_ebanlist", ADMFLAG_BAN); + AddToTopMenu(g_hAdminMenu, "entWatch_ban", TopMenuObject_Item, Handler_EBan, hMenuObj, "sm_eban", ADMFLAG_BAN); + AddToTopMenu(g_hAdminMenu, "entWatch_transfer", TopMenuObject_Item, Handler_Transfer, hMenuObj, "sm_etransfer", ADMFLAG_BAN); + AddToTopMenu(g_hAdminMenu, "entWatch_give", TopMenuObject_Item, Handler_Give, hMenuObj, "sm_egive", ADMFLAG_BAN); + AddToTopMenu(g_hAdminMenu, "entWatch_unban", TopMenuObject_Item, Handler_EUnban, hMenuObj, "sm_eunban", ADMFLAG_BAN); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Menu Stuff +//---------------------------------------------------------------------------------------------------- +public void AdminMenu_Commands_Handler(Handle hMenu, TopMenuAction hAction, TopMenuObject hObjID, int iParam1, char[] sBuffer, int iMaxlen) { + switch (hAction) { + case TopMenuAction_DisplayOption: FormatEx(sBuffer, iMaxlen, "%T", "entWatch Commands", iParam1); + case TopMenuAction_DisplayTitle: FormatEx(sBuffer, iMaxlen, "%T:", "entWatch Commands", iParam1); + } +} + +public void Handler_EBanList(Handle hMenu, TopMenuAction hAction, TopMenuObject hObjID, int iParam1, char[] sBuffer, int iMaxlen) { + switch (hAction) { + case TopMenuAction_DisplayOption: FormatEx(sBuffer, iMaxlen, "%T", "List Banned Clients", iParam1); + case TopMenuAction_SelectOption: Menu_List(iParam1); + } +} + +public void Handler_EBan(Handle hMenu, TopMenuAction hAction, TopMenuObject hObjID, int iParam1, char[] sBuffer, int iMaxlen) { + switch (hAction) { + case TopMenuAction_DisplayOption: FormatEx(sBuffer, iMaxlen, "%T", "Ban a Client", iParam1); + case TopMenuAction_SelectOption: Menu_EBan(iParam1); + } +} + +public void Handler_Transfer(Handle hMenu, TopMenuAction hAction, TopMenuObject hObjID, int iParam1, char[] sBuffer, int iMaxlen) { + switch (hAction) { + case TopMenuAction_DisplayOption: FormatEx(sBuffer, iMaxlen, "%T", "Transfer an item", iParam1); + case TopMenuAction_SelectOption: Menu_Transfer(iParam1); + } +} + +public void Handler_Give(Handle hMenu, TopMenuAction hAction, TopMenuObject hObjID, int iParam1, char[] sBuffer, int iMaxlen) { + switch (hAction) { + case TopMenuAction_DisplayOption: FormatEx(sBuffer, iMaxlen, "%T", "Give an Item", iParam1); + case TopMenuAction_SelectOption: Menu_Give(iParam1); + } +} + +public void Handler_EUnban(Handle hMenu, TopMenuAction hAction, TopMenuObject hObjID, int iParam1, char[] sBuffer, int iMaxlen) { + switch (hAction) { + case TopMenuAction_DisplayOption: FormatEx(sBuffer, iMaxlen, "%T", "Unban a Client", iParam1); + case TopMenuAction_SelectOption: Menu_EUnban(iParam1); + } +} + +void Menu_List(int iClient) { + int iBannedClients; + + Menu hListMenu = CreateMenu(MenuHandler_Menu_List); + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Banned Clients", iClient); + hListMenu.SetTitle(sMenuTranslate); + hListMenu.ExitBackButton = true; + + for (int i = 1; i < MaxClients + 1; i++) + { + if (IsClientInGame(i) && AreClientCookiesCached(i)) + { + char sBanLen[32]; + GetClientCookie(i, g_hCookie_RestrictedLength, sBanLen, sizeof(sBanLen)); + int iBanLen = StringToInt(sBanLen); + + if ((iBanLen != 0 && iBanLen >= GetTime()) || iBanLen == 1 || g_bRestricted[i]) + { + int iUserID = GetClientUserId(i); + char sUserID[12], sBuff[64]; + FormatEx(sBuff, sizeof(sBuff), "%N (#%i)", i, iUserID); + FormatEx(sUserID, sizeof(sUserID), "%d", iUserID); + + hListMenu.AddItem(sUserID, sBuff); + iBannedClients++; + } + } + } + + if (!iBannedClients) {char sMenuItem[128];Format(sMenuItem, sizeof(sMenuItem), "%T.", "No Banned Clients", iClient);hListMenu.AddItem("", sMenuItem, ITEMDRAW_DISABLED);} + + hListMenu.Display(iClient, MENU_TIME_FOREVER); +} + +void Menu_EBan(int iClient) { + Menu hEBanMenu = CreateMenu(MenuHandler_Menu_EBan); + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Ban a Client", iClient); + hEBanMenu.SetTitle(sMenuTranslate); + hEBanMenu.ExitBackButton = true; + AddTargetsToMenu2(hEBanMenu, iClient, COMMAND_FILTER_NO_BOTS|COMMAND_FILTER_CONNECTED); + + hEBanMenu.Display(iClient, MENU_TIME_FOREVER); +} + +void Menu_Transfer(int iClient) { + Menu hTransferMenu = CreateMenu(MenuHandler_Menu_Transfer); + char sMenuTemp[64], sIndexTemp[16]; + int iHeldCount = 0; + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Transfer an item", iClient); + hTransferMenu.SetTitle(sMenuTranslate); + hTransferMenu.ExitBackButton = true; + + for (int i = 0; i < entArraySize; i++) + { + if (entArray[i][ent_allowtransfer]) + { + if (entArray[i][ent_ownerid] != -1) + { + IntToString(i, sIndexTemp, sizeof(sIndexTemp)); + FormatEx(sMenuTemp, sizeof(sMenuTemp), "%s | %N (#%i)", entArray[i][ent_name], entArray[i][ent_ownerid], GetClientUserId(entArray[i][ent_ownerid])); + hTransferMenu.AddItem(sIndexTemp, sMenuTemp, ITEMDRAW_DEFAULT); + iHeldCount++; + } + } + } + + if (!iHeldCount) {char sMenuItem[128];Format(sMenuItem, sizeof(sMenuItem), "%T.", "No transferable items currently held", iClient);hTransferMenu.AddItem("", sMenuItem, ITEMDRAW_DISABLED);} + + hTransferMenu.Display(iClient, MENU_TIME_FOREVER); +} + +void Menu_Give(int iClient) { + Menu hTransferMenu = CreateMenu(MenuHandler_Menu_Give); + char sMenuTemp[64], sIndexTemp[16]; + int iHeldCount = 0; + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Give an Item", iClient); + hTransferMenu.SetTitle(sMenuTranslate); + hTransferMenu.ExitBackButton = true; + + for (int i = 0; i < entArraySize; i++) + { + if (entArray[i][ent_allowtransfer]) + { + if (entArray[i][ent_ownerid] == -1) + { + if (IsValidEdict(entArray[i][ent_weaponid])) + { + IntToString(i, sIndexTemp, sizeof(sIndexTemp)); + FormatEx(sMenuTemp, sizeof(sMenuTemp), "%s | #%i", entArray[i][ent_name], entArray[i][ent_hammerid]); + hTransferMenu.AddItem(sIndexTemp, sMenuTemp, ITEMDRAW_DEFAULT); + iHeldCount++; + } + } + } + } + + if (!iHeldCount) {char sMenuItem[128];Format(sMenuItem, sizeof(sMenuItem), "%T.", "No Give items currently held", iClient);hTransferMenu.AddItem("", sMenuItem, ITEMDRAW_DISABLED);} + + hTransferMenu.Display(iClient, MENU_TIME_FOREVER); +} + +void Menu_EUnban(int iClient) +{ + int iBannedClients; + + Menu hEUnbanMenu = CreateMenu(MenuHandler_Menu_EUnban); + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Unban a Client", iClient); + hEUnbanMenu.SetTitle(sMenuTranslate); + hEUnbanMenu.ExitBackButton = true; + + for (int i = 1; i < MaxClients + 1; i++) + { + if (IsClientInGame(i) && AreClientCookiesCached(i)) + { + char sBanLen[32]; + GetClientCookie(i, g_hCookie_RestrictedLength, sBanLen, sizeof(sBanLen)); + int iBanLen = StringToInt(sBanLen); + + if ((iBanLen != 0 && iBanLen >= GetTime()) || iBanLen == 1 || g_bRestricted[i]) + { + int iUserID = GetClientUserId(i); + char sUserID[12], sBuff[64]; + FormatEx(sBuff, sizeof(sBuff), "%N (#%i)", i, iUserID); + FormatEx(sUserID, sizeof(sUserID), "%d", iUserID); + + hEUnbanMenu.AddItem(sUserID, sBuff); + iBannedClients++; + } + } + } + + if (!iBannedClients) {char sMenuItem[128];Format(sMenuItem, sizeof(sMenuItem), "%T.", "No Banned Clients", iClient);hEUnbanMenu.AddItem("", sMenuItem, ITEMDRAW_DISABLED);} + + hEUnbanMenu.Display(iClient, MENU_TIME_FOREVER); +} + +public int MenuHandler_Menu_List(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: { + if (iParam2 == MenuCancel_ExitBack && g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + } + + case MenuAction_Select: + { + char sOption[32]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iTarget = GetClientOfUserId(StringToInt(sOption)); + + if (iTarget != 0) Menu_ListTarget(iParam1, iTarget); + else { + CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning,"Player no longer available"); + + if (g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + else delete hMenu; + } + } + } +} + +public int MenuHandler_Menu_EBan(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: { + if (iParam2 == MenuCancel_ExitBack && g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + } + + case MenuAction_Select: + { + char sOption[32]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iTarget = GetClientOfUserId(StringToInt(sOption)); + + if (iTarget != 0) Menu_EBanTime(iParam1, iTarget); + else { + CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning,"Player no longer available"); + + if (g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + else delete hMenu; + } + } + } +} + +public int MenuHandler_Menu_Transfer(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: { + if (iParam2 == MenuCancel_ExitBack && g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + } + + case MenuAction_Select: + { + char sOption[32]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iEntityIndex = StringToInt(sOption); + + if (entArray[iEntityIndex][ent_ownerid] != -1) Menu_TransferTarget(iParam1, iEntityIndex); + else CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning, "Item no longer available"); + } + } +} + +public int MenuHandler_Menu_Give(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: { + if (iParam2 == MenuCancel_ExitBack && g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + } + + case MenuAction_Select: + { + char sOption[32]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iEntityIndex = StringToInt(sOption); + + if (entArray[iEntityIndex][ent_ownerid] == -1) Menu_GiveTarget(iParam1, iEntityIndex); + else CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning, "Item is already picked up"); + } + } +} + +public int MenuHandler_Menu_EUnban(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: { + if (iParam2 == MenuCancel_ExitBack && g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + } + + case MenuAction_Select: + { + char sOption[32]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iTarget = GetClientOfUserId(StringToInt(sOption)); + + if (iTarget != 0) EUnbanClient(iTarget, iParam1); + else { + CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning, "Player no longer available"); + + if (g_hAdminMenu != INVALID_HANDLE) DisplayTopMenu(g_hAdminMenu, iParam1, TopMenuPosition_LastCategory); + else delete hMenu; + } + } + } +} + +void Menu_TransferTarget(int iClient, int iEntityIndex) +{ + Menu hTransferTarget = CreateMenu(MenuHandler_Menu_TransferTarget); + char sMenuTemp[64], sIndexTemp[32]; + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Transfer iTarget", iClient); + hTransferTarget.SetTitle(sMenuTranslate); + hTransferTarget.ExitBackButton = true; + + g_iAdminMenuTarget[iClient] = iEntityIndex; + Format(sIndexTemp, sizeof(sIndexTemp), "%i", GetClientUserId(iClient)); + Format(sMenuTemp, sizeof(sMenuTemp), "%N (#%s)", iClient, sIndexTemp); + hTransferTarget.AddItem(sIndexTemp, sMenuTemp, ITEMDRAW_DEFAULT); + + for (int i = 1; i < MAXPLAYERS; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i) || GetClientTeam(i) != GetClientTeam(entArray[iEntityIndex][ent_ownerid]) || i == iClient) continue; + + FormatEx(sIndexTemp, sizeof(sIndexTemp), "%i", GetClientUserId(i)); + FormatEx(sMenuTemp, sizeof(sMenuTemp), "%N (#%s)", i, sIndexTemp); + hTransferTarget.AddItem(sIndexTemp, sMenuTemp, ITEMDRAW_DEFAULT); + } + + hTransferTarget.Display(iClient, MENU_TIME_FOREVER); +} + +void Menu_GiveTarget(int iClient, int iEntityIndex) +{ + Menu hTransferTarget = CreateMenu(MenuHandler_Menu_GiveTarget); + char sMenuTemp[64], sIndexTemp[32]; + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Give iTarget", iClient); + hTransferTarget.SetTitle(sMenuTranslate); + hTransferTarget.ExitBackButton = true; + + + g_iAdminMenuTarget[iClient] = iEntityIndex; + + for (int i = 1; i < MAXPLAYERS; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i) || GetClientTeam(i)<2) continue; + + FormatEx(sIndexTemp, sizeof(sIndexTemp), "%i", GetClientUserId(i)); + FormatEx(sMenuTemp, sizeof(sMenuTemp), "%N (#%s)", i, sIndexTemp); + hTransferTarget.AddItem(sIndexTemp, sMenuTemp, ITEMDRAW_DEFAULT); + } + + hTransferTarget.Display(iClient, MENU_TIME_FOREVER); +} + +public int MenuHandler_Menu_TransferTarget(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch (hAction) + { + case MenuAction_End:delete hMenu; + case MenuAction_Cancel: { + switch (iParam2) { + case MenuCancel_ExitBack: Menu_Transfer(iParam1); + } + } + + case MenuAction_Select: + { + char sOption[64]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iEntityIndex = g_iAdminMenuTarget[iParam1]; + int iReceiver = GetClientOfUserId(StringToInt(sOption)); + + switch(iReceiver) { + case 0: { + CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning,"Receiver is not valid anymore"); + return; + } + } + + if (entArray[iEntityIndex][ent_allowtransfer]) + { + if (entArray[iEntityIndex][ent_ownerid] != -1) + { + if (IsValidEdict(entArray[iEntityIndex][ent_weaponid])) + { + int iCurOwner = entArray[iEntityIndex][ent_ownerid]; + + if (GetClientTeam(iReceiver) != GetClientTeam(iCurOwner)) + { + CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning,"The receivers team differs from the targets team"); + return; + } + + char ssBuffer_classname[64]; + GetEdictClassname(entArray[iEntityIndex][ent_weaponid], ssBuffer_classname, sizeof(ssBuffer_classname)); + + CS_DropWeapon(iCurOwner, entArray[iEntityIndex][ent_weaponid], false); + GivePlayerItem(iCurOwner, ssBuffer_classname); + + if (entArray[iEntityIndex][ent_chat]) + { + entArray[iEntityIndex][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + entArray[iEntityIndex][ent_chat] = true; + } + else FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iParam1, color_warning, "transfered all items", color_name, iCurOwner, color_warning, "tai to", color_name, iReceiver); + + LogAction(iParam1, iCurOwner, "\"%L\" transfered all items from \"%L\" to \"%L\"", iParam1, iCurOwner, iReceiver); + } + } + else CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning,"Item is not valid anymore"); + } + } + } +} + +public int MenuHandler_Menu_GiveTarget(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch (hAction) + { + case MenuAction_End:delete hMenu; + case MenuAction_Cancel: { + switch (iParam2) { + case MenuCancel_ExitBack: Menu_Transfer(iParam1); + } + } + + case MenuAction_Select: + { + char sOption[64]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iEntityIndex = g_iAdminMenuTarget[iParam1]; + int iReceiver = GetClientOfUserId(StringToInt(sOption)); + + switch(iReceiver) { + case 0: { + CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Receiver is not valid anymore"); + return; + } + } + + if (entArray[iEntityIndex][ent_allowtransfer]) + { + if (entArray[iEntityIndex][ent_ownerid] == -1) + { + if (IsValidEdict(entArray[iEntityIndex][ent_weaponid])) + { + if ((GetClientTeam(iReceiver) != 2)&&(GetClientTeam(iReceiver) != 3)) + { + CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning,"The receivers team is not CT or TER"); + return; + } + + if (entArray[iEntityIndex][ent_chat]) + { + entArray[iEntityIndex][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + entArray[iEntityIndex][ent_chat] = true; + } + else FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%s \x07%s%t \x07%s%N", color_tag, color_name, iParam1, color_warning, "give item", entArray[iEntityIndex][ent_color], entArray[iEntityIndex][ent_name], color_warning, "tai to", color_name, iReceiver); + + LogAction(iParam1, iReceiver, "\"%L\" gived %s item to \"%L\"", iParam1,entArray[iEntityIndex][ent_name], iReceiver); + } + else CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Item dont pickable"); + } + else CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Item is already picked up"); + } + } + } +} + +void Menu_EBanTime(int iClient, int iTarget) +{ + Menu hEBanMenuTime = CreateMenu(MenuHandler_Menu_EBanTime); + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T %N:", "Ban Time for", iClient, iTarget); + hEBanMenuTime.SetTitle(sMenuTranslate); + hEBanMenuTime.ExitBackButton = true; + + g_iAdminMenuTarget[iClient] = iTarget; + char sMenuItem[128]; + Format(sMenuItem, sizeof(sMenuItem), "%T", "Temporary", iClient); + hEBanMenuTime.AddItem("0", sMenuItem); + Format(sMenuItem, sizeof(sMenuItem), "10 %T", "Minutes", iClient); + hEBanMenuTime.AddItem("10", sMenuItem); + Format(sMenuItem, sizeof(sMenuItem), "1 %T", "Hour", iClient); + hEBanMenuTime.AddItem("60", sMenuItem); + Format(sMenuItem, sizeof(sMenuItem), "1 %T", "Day", iClient); + hEBanMenuTime.AddItem("1440", sMenuItem); + Format(sMenuItem, sizeof(sMenuItem), "1 %T", "Week", iClient); + hEBanMenuTime.AddItem("10080", sMenuItem); + Format(sMenuItem, sizeof(sMenuItem), "1 %T", "Month", iClient); + hEBanMenuTime.AddItem("40320", sMenuItem); + Format(sMenuItem, sizeof(sMenuItem), "%T", "Permanent", iClient); + hEBanMenuTime.AddItem("1", sMenuItem); + + hEBanMenuTime.Display(iClient, MENU_TIME_FOREVER); +} + +public int MenuHandler_Menu_EBanTime(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End:delete hMenu; + case MenuAction_Cancel: + { + switch(iParam2){ + case MenuCancel_ExitBack: Menu_EBan(iParam1); + } + } + + case MenuAction_Select: + { + char sOption[64]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iTarget = g_iAdminMenuTarget[iParam1]; + + if (iTarget != 0) + { + if (strcmp(sOption, "0") == 0) EBanClient(iTarget, "0", iParam1); + else if (strcmp(sOption, "1") == 0) EBanClient(iTarget, "1", iParam1); + else { + char sBanLen[64]; + Format(sBanLen, sizeof(sBanLen), "%d", GetTime() + (StringToInt(sOption) * 60)); + + EBanClient(iTarget, sBanLen, iParam1); + } + } else { + CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning, "Player no longer available"); + Menu_EBan(iParam1); + } + } + } +} + +void Menu_ListTarget(int iClient, int iTarget) +{ + Menu hListTargetMenu = CreateMenu(MenuHandler_Menu_ListTarget); + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T: %N", "Banned Client", iClient, iTarget); + hListTargetMenu.SetTitle(sMenuTranslate); + hListTargetMenu.ExitBackButton = true; + + char sBanExpiryDate[64], sBanIssuedDate[64], sBanDuration[64], sBannedBy[64], sUserID[15]; + int iBanExpiryDate = g_iRestrictedLength[iTarget]; + int iBanIssuedDate = g_iRestrictedIssued[iTarget]; + int iBanDuration = (iBanExpiryDate - iBanIssuedDate) / 60; + int iUserID = GetClientUserId(iTarget); + + char sBanExpiryDateBuf[64],sBanIssuedDateBuf[64]; + FormatTime(sBanExpiryDateBuf, sizeof(sBanExpiryDateBuf), NULL_STRING, iBanExpiryDate); + FormatTime(sBanIssuedDateBuf, sizeof(sBanIssuedDateBuf), NULL_STRING, iBanIssuedDate); + FormatEx(sUserID, sizeof(sUserID), "%d", iUserID); + + if (!g_bRestricted[iTarget]) + { + if (iBanExpiryDate != 1) + { + FormatEx(sBanDuration, sizeof(sBanDuration), "%T: %d %s", "Duration", iClient, iBanDuration, SingularOrMultiple(iBanDuration)?"Minutes":"Minute"); + FormatEx(sBanExpiryDate, sizeof(sBanExpiryDate), "%T: %s", "Expires", iClient, sBanExpiryDateBuf); + } else { + FormatEx(sBanDuration, sizeof(sBanDuration), "%T: %T", "Duration", iClient, "Permanent", iClient); + FormatEx(sBanExpiryDate, sizeof(sBanExpiryDate), "%T: %T", "Expires", iClient, "Never", iClient); + } + } else { + FormatEx(sBanDuration, sizeof(sBanDuration), "%T: %T", "Duration", iClient, "Temporary", iClient); + FormatEx(sBanExpiryDate, sizeof(sBanExpiryDate), "%T: %T", "Expires", iClient, "On Map Change", iClient); + } + + FormatEx(sBanIssuedDate, sizeof(sBanIssuedDate), "%T: %s", "Issued on", iClient, !(iBanIssuedDate == 0) ? sBanIssuedDateBuf:"Unknown"); + FormatEx(sBannedBy, sizeof(sBannedBy), "%T: %s", "Admin SID", iClient, g_sRestrictedBy[iTarget][0] ? g_sRestrictedBy[iTarget]:"Unknown"); + + hListTargetMenu.AddItem("", sBannedBy, ITEMDRAW_DISABLED); + hListTargetMenu.AddItem("", sBanIssuedDate, ITEMDRAW_DISABLED); + hListTargetMenu.AddItem("", sBanExpiryDate, ITEMDRAW_DISABLED); + hListTargetMenu.AddItem("", sBanDuration, ITEMDRAW_DISABLED); + hListTargetMenu.AddItem("", "", ITEMDRAW_SPACER); + char sMenuItem[128]; + Format(sMenuItem, sizeof(sMenuItem), "%T", "Unban", iClient); + hListTargetMenu.AddItem(sUserID, sMenuItem); + + hListTargetMenu.Display(iClient, MENU_TIME_FOREVER); +} + +public int MenuHandler_Menu_ListTarget(Menu hMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch(hAction) + { + case MenuAction_End:delete hMenu; + case MenuAction_Cancel: + { + switch (iParam2) { + case MenuCancel_ExitBack: Menu_List(iParam1); + } + } + + case MenuAction_Select: + { + char sOption[32]; + hMenu.GetItem(iParam2, sOption, sizeof(sOption)); + int iTarget = GetClientOfUserId(StringToInt(sOption)); + + if (iTarget != 0) EUnbanClient(iTarget, iParam1); + else { + CPrintToChat(iParam1, "\x07%s[entWatch]\x07%s %t", color_tag, color_warning, "Player no longer available"); + Menu_List(iParam1); + } + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Set variables +//---------------------------------------------------------------------------------------------------- +public void OnMapStart() +{ + CleanData(); + LoadColors(); + LoadConfig(); + + isMapRunning = true; +} + +public void OnMapEnd() +{ + isMapRunning = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Hook RoundStart event +//---------------------------------------------------------------------------------------------------- +public Action Event_RoundStart(Event hEvent, const char[] sName, bool bDontBroadcast) +{ + if (g_bConfigLoaded && g_bRoundTransition) CPrintToChatAll("\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "welcome"); + + g_bRoundTransition = false; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Hook RoundEnd event +//---------------------------------------------------------------------------------------------------- +public Action Event_RoundEnd(Event hEvent, const char[] sName, bool bDontBroadcast) +{ + if (g_bConfigLoaded && !g_bRoundTransition) + { + for (int index = 0; index < entArraySize; index++) + { + SDKUnhook(entArray[index][ent_buttonid], SDKHook_Use, OnButtonUse); + entArray[index][ent_weaponid] = -1; + entArray[index][ent_buttonid] = -1; + entArray[index][ent_ownerid] = -1; + entArray[index][ent_cooldowntime] = -1; + entArray[index][ent_uses] = 0; + if (IsValidEdict(entArray[index][ent_glowent])) AcceptEntityInput(entArray[index][ent_glowent], "Kill"); + entArray[index][ent_glowent] = -1; + } + } + + g_bRoundTransition = true; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Set client cookies once cached +//---------------------------------------------------------------------------------------------------- +public void OnClientCookiesCached(int iClient) +{ + char sBuffer_cookie[32]; + + GetClientCookie(iClient, g_hCookie_Display, sBuffer_cookie, sizeof(sBuffer_cookie)); + g_bDisplay[iClient] = view_as(StringToInt(sBuffer_cookie)); + + GetClientCookie(iClient, g_hCookie_RestrictedLength, sBuffer_cookie, sizeof(sBuffer_cookie)); + + if (StringToInt(sBuffer_cookie) != 1 && StringToInt(sBuffer_cookie) <= GetTime()) { + g_iRestrictedLength[iClient] = 0; + SetClientCookie(iClient, g_hCookie_RestrictedLength, "0"); + } + else g_iRestrictedLength[iClient] = StringToInt(sBuffer_cookie); + + GetClientCookie(iClient, g_hCookie_RestrictedIssued, sBuffer_cookie, sizeof(sBuffer_cookie)); + g_iRestrictedIssued[iClient] = StringToInt(sBuffer_cookie); + + GetClientCookie(iClient, g_hCookie_RestrictedBy, sBuffer_cookie, sizeof(sBuffer_cookie)); + FormatEx(g_sRestrictedBy[iClient], sizeof(g_sRestrictedBy[]), "%s", sBuffer_cookie); + + GetClientCookie(iClient, g_hCookie_HudPos, sBuffer_cookie, sizeof(sBuffer_cookie)); + if (StrEqual(sBuffer_cookie,"")){ + Format(sBuffer_cookie, sizeof(sBuffer_cookie), "%f/%f", g_DefaultHudPos[0], g_DefaultHudPos[1]); + SetClientCookie(iClient, g_hCookie_HudPos, sBuffer_cookie); + HudPosition[iClient][0] = g_DefaultHudPos[0]; + HudPosition[iClient][1] = g_DefaultHudPos[1]; + }else + { + char Explode_HudPosition[2][32]; + ExplodeString(sBuffer_cookie, "/", Explode_HudPosition, 2, 32); + HudPosition[iClient][0] = StringToFloat(Explode_HudPosition[0]); + HudPosition[iClient][1] = StringToFloat(Explode_HudPosition[1]); + } + + GetClientCookie(iClient, g_hCookie_HudColor, sBuffer_cookie, sizeof(sBuffer_cookie)); + if (StrEqual(sBuffer_cookie,"")){ + Format(sBuffer_cookie, sizeof(sBuffer_cookie), "%i/%i/%i", g_DefaultHudColor[0], g_DefaultHudColor[1], g_DefaultHudColor[2]); + SetClientCookie(iClient, g_hCookie_HudColor, sBuffer_cookie); + HudColor[iClient][0] = g_DefaultHudColor[0]; + HudColor[iClient][1] = g_DefaultHudColor[1]; + HudColor[iClient][2] = g_DefaultHudColor[2]; + }else + { + char Explode_HudColor[3][8]; + ExplodeString(sBuffer_cookie, "/", Explode_HudColor, 3, 8); + HudColor[iClient][0] = StringToInt(Explode_HudColor[0]); + HudColor[iClient][1] = StringToInt(Explode_HudColor[1]); + HudColor[iClient][2] = StringToInt(Explode_HudColor[2]); + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Hook weapons and update banned clients to int method +//---------------------------------------------------------------------------------------------------- +public void OnClientPutInServer(int iClient) +{ + SDKHookEx(iClient, SDKHook_WeaponDropPost, OnWeaponDrop); + SDKHookEx(iClient, SDKHook_WeaponEquipPost, OnWeaponEquip); + SDKHookEx(iClient, SDKHook_WeaponCanUse, OnWeaponCanUse); + + g_bDisplay2[iClient] = true; + if (!AreClientCookiesCached(iClient)) + { + HudPosition[iClient][0] = g_DefaultHudPos[0]; + HudPosition[iClient][1] = g_DefaultHudPos[1]; + HudColor[iClient][0] = g_DefaultHudColor[0]; + HudColor[iClient][1] = g_DefaultHudColor[1]; + HudColor[iClient][2] = g_DefaultHudColor[2]; + g_bDisplay2[iClient] = true; + g_bDisplay[iClient] = false; + } + + g_bRestricted[iClient] = false; + + if (!AreClientCookiesCached(iClient)) g_iRestrictedLength[iClient] = 0; + else + { + char sRestricted[32]; + GetClientCookie(iClient, g_hCookie_Restricted, sRestricted, sizeof(sRestricted)); + + switch(StringToInt(sRestricted)) { + case 1: { + SetClientCookie(iClient, g_hCookie_RestrictedLength, "1"); + SetClientCookie(iClient, g_hCookie_Restricted, "0"); + } + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Notify of Disconnect if they had a special weapon and unhook weapons +//---------------------------------------------------------------------------------------------------- +public void OnClientDisconnect(int iClient) +{ + if (g_bConfigLoaded && !g_bRoundTransition) + { + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_ownerid] != -1 && entArray[index][ent_ownerid] == iClient) + { + entArray[index][ent_ownerid] = -1; + + //if (entArray[index][ent_forcedrop] && IsValidEdict(entArray[index][ent_weaponid])) CS_DropWeapon(iClient, entArray[index][ent_weaponid], false); // Add glow function on drop + + //else if (entArray[index][ent_chat]) + if (entArray[index][ent_chat]) + { + char sBuffer_steamid[32]; + GetClientAuthId(iClient, AuthId_Steam2, sBuffer_steamid, sizeof(sBuffer_steamid)); + ReplaceString(sBuffer_steamid, sizeof(sBuffer_steamid), "STEAM_", "", true); + + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iClient) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iClient, color_disconnect, color_steamid, sBuffer_steamid, color_disconnect, color_disconnect, "disconnect", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + } + } + } + } + + SDKUnhook(iClient, SDKHook_WeaponDropPost, OnWeaponDrop); + SDKUnhook(iClient, SDKHook_WeaponEquipPost, OnWeaponEquip); + SDKUnhook(iClient, SDKHook_WeaponCanUse, OnWeaponCanUse); + + //g_bDisplay[iClient] = false; + g_bDisplay[iClient] = false; + g_bDisplay2[iClient] = true; + g_bRestricted[iClient] = false; + g_iRestrictedLength[iClient] = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Notify of Death if they had a special weapon +//---------------------------------------------------------------------------------------------------- +public Action Event_PlayerDeath(Event hEvent, const char[] sName, bool bDontBroadcast) +{ + int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid")); + + if (g_bConfigLoaded && !g_bRoundTransition) + { + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_ownerid] != -1 && entArray[index][ent_ownerid] == iClient) + { + entArray[index][ent_ownerid] = -1; + + if (entArray[index][ent_forcedrop] && IsValidEdict(entArray[index][ent_weaponid])) CS_DropWeapon(iClient, entArray[index][ent_weaponid], false); + + else if (entArray[index][ent_chat]) + { + char sBuffer_steamid[32]; + GetClientAuthId(iClient, AuthId_Steam2, sBuffer_steamid, sizeof(sBuffer_steamid)); + ReplaceString(sBuffer_steamid, sizeof(sBuffer_steamid), "STEAM_", "", true); + + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iClient) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iClient, color_death, color_steamid, sBuffer_steamid, color_death, color_death, "death", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + } + } + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Notify when they pick up a special weapon +//---------------------------------------------------------------------------------------------------- +public Action OnWeaponEquip(int iClient, int iWeapon) +{ + if (g_bConfigLoaded && !g_bRoundTransition && IsValidEdict(iWeapon)) + { + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_hammerid] == Entity_GetHammerID(iWeapon)) + { + if (entArray[index][ent_weaponid] != -1 && entArray[index][ent_weaponid] == iWeapon) + { + entArray[index][ent_ownerid] = iClient; + DisableGlow(index); + + if (entArray[index][ent_chat]) + { + char sBuffer_steamid[32]; + GetClientAuthId(iClient, AuthId_Steam2, sBuffer_steamid, sizeof(sBuffer_steamid)); + ReplaceString(sBuffer_steamid, sizeof(sBuffer_steamid), "STEAM_", "", true); + + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iClient) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) + { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iClient, color_pickup, color_steamid, sBuffer_steamid, color_pickup, color_pickup, "pickup", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + } + + break; + } + } + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Notify when they drop a special weapon +//---------------------------------------------------------------------------------------------------- +public Action OnWeaponDrop(int iClient, int iWeapon) +{ + if (g_bConfigLoaded && !g_bRoundTransition && IsValidEdict(iWeapon)) + { + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_hammerid] == Entity_GetHammerID(iWeapon)) + { + if (entArray[index][ent_weaponid] != -1 && entArray[index][ent_weaponid] == iWeapon) + { + entArray[index][ent_ownerid] = -1; + GlowWeapon(index); + + if (entArray[index][ent_chat]) + { + char sBuffer_steamid[32]; + GetClientAuthId(iClient, AuthId_Steam2, sBuffer_steamid, sizeof(sBuffer_steamid)); + ReplaceString(sBuffer_steamid, sizeof(sBuffer_steamid), "STEAM_", "", true); + + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iClient) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) + { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iClient, color_drop, color_steamid, sBuffer_steamid, color_drop, color_drop, "drop", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + } + + break; + } + } + } + } +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Prevent banned players from picking up special weapons +//---------------------------------------------------------------------------------------------------- +public Action OnWeaponCanUse(int iClient, int iWeapon) +{ + if (IsFakeClient(iClient)) return Plugin_Handled; + + if (g_bConfigLoaded && !g_bRoundTransition && IsValidEdict(iWeapon)) + { + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_hammerid] == Entity_GetHammerID(iWeapon)) + { + if (entArray[index][ent_weaponid] == -1) + { + entArray[index][ent_weaponid] = iWeapon; + + if (entArray[index][ent_buttonid] == -1 && entArray[index][ent_mode] != 0) + { + char sBuffer_targetname[32]; + Entity_GetTargetName(iWeapon, sBuffer_targetname, sizeof(sBuffer_targetname)); + + int iButton = -1; + while ((iButton = FindEntityByClassname(iButton, entArray[index][ent_buttonclass])) != -1) + { + if (IsValidEdict(iButton)) + { + char sBuffer_parentname[32]; + Entity_GetParentName(iButton, sBuffer_parentname, sizeof(sBuffer_parentname)); + + if (StrEqual(sBuffer_targetname, sBuffer_parentname)) + { + SDKHookEx(iButton, SDKHook_Use, OnButtonUse); + entArray[index][ent_buttonid] = iButton; + break; + } + } + } + } + } + + else if (entArray[index][ent_weaponid] == iWeapon) + { + if (entArray[index][ent_blockpickup] || g_bRestricted[iClient] || (GetClientButtons(iClient) & IN_USE)) return Plugin_Handled; + + if (g_iRestrictedLength[iClient] != 1 && g_iRestrictedLength[iClient] != 0 && g_iRestrictedLength[iClient] <= GetTime()) + { + g_iRestrictedLength[iClient] = 0; + SetClientCookie(iClient, g_hCookie_RestrictedLength, "0"); + return Plugin_Continue; + } + + if (g_iRestrictedLength[iClient] > GetTime() || g_iRestrictedLength[iClient] == 1) return Plugin_Handled; + return Plugin_Continue; + } + } + } + } + return Plugin_Continue; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Notify when they use a special weapon +//---------------------------------------------------------------------------------------------------- +public Action OnButtonUse(int iButton, int iActivator, int iCaller, UseType uType, float fvalue) +{ + if (g_bConfigLoaded && !g_bRoundTransition && IsValidEdict(iButton)) + { + int iOffset = FindDataMapInfo(iButton, "m_bLocked"); + if (iOffset != -1 && GetEntData(iButton, iOffset, 1)) return Plugin_Handled; + + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_buttonid] != -1 && entArray[index][ent_buttonid] == iButton) + { + if (entArray[index][ent_ownerid] != iActivator && entArray[index][ent_ownerid] != iCaller) return Plugin_Handled; + else if (entArray[index][ent_hasfiltername]) DispatchKeyValue(iActivator, "targetname", entArray[index][ent_filtername]); + + char sBuffer_steamid[32]; + GetClientAuthId(iActivator, AuthId_Steam2, sBuffer_steamid, sizeof(sBuffer_steamid)); + ReplaceString(sBuffer_steamid, sizeof(sBuffer_steamid), "STEAM_", "", true); + + switch (entArray[index][ent_mode]) { + case 1: return Plugin_Changed; + case 2: { + if (entArray[index][ent_cooldowntime] <= -1) + { + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iActivator) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) + { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iActivator, color_use, color_steamid, sBuffer_steamid, color_use, color_use, "use", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + + entArray[index][ent_cooldowntime] = entArray[index][ent_cooldown]; + return Plugin_Changed; + } + } + case 3: { + if (entArray[index][ent_uses] < entArray[index][ent_maxuses]) + { + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iActivator) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) + { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iActivator, color_use, color_steamid, sBuffer_steamid, color_use, color_use, "use", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + + entArray[index][ent_uses]++; + return Plugin_Changed; + } + } + + case 4: { + if (entArray[index][ent_uses] < entArray[index][ent_maxuses] && entArray[index][ent_cooldowntime] <= -1) + { + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iActivator) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) + { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iActivator, color_use, color_steamid, sBuffer_steamid, color_use, color_use, "use", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + + entArray[index][ent_cooldowntime] = entArray[index][ent_cooldown]; + entArray[index][ent_uses]++; + return Plugin_Changed; + } + } + case 5: { + if (entArray[index][ent_cooldowntime] <= -1) + { + for (int iPly = 1; iPly <= MaxClients; iPly++) + { + if (IsClientConnected(iPly) && IsClientInGame(iPly)) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(iPly) == GetClientTeam(iActivator) || !IsPlayerAlive(iPly) || CheckCommandAccess(iPly, "entWatch_chat", ADMFLAG_CHAT))) + { + CPrintToChat(iPly, "\x07%s[entWatch] \x07%s%N \x07%s(\x07%s%s\x07%s) \x07%s%t \x07%s%s", color_tag, color_name, iActivator, color_use, color_steamid, sBuffer_steamid, color_use, color_use, "use", entArray[index][ent_color], entArray[index][ent_name]); + } + } + } + + entArray[index][ent_uses]++; + if (entArray[index][ent_uses] >= entArray[index][ent_maxuses]) + { + entArray[index][ent_cooldowntime] = entArray[index][ent_cooldown]; + entArray[index][ent_uses] = 0; + } + + return Plugin_Changed; + } + } + } + return Plugin_Handled; + } + } + } + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Calculate cooldown time +//---------------------------------------------------------------------------------------------------- + +public Action Timer_Cooldowns(Handle timer) +{ + if (GameRules_GetProp("m_bWarmupPeriod") == 1) + return Plugin_Continue; + + if (g_bConfigLoaded && !g_bRoundTransition) + { + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_cooldowntime] >= 0) + { + entArray[index][ent_cooldowntime]--; + } + } + } + return Plugin_Continue; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Check status +//---------------------------------------------------------------------------------------------------- +public Action Command_Status(int iClient, int iArgs) +{ + if (iArgs != 0 && CheckCommandAccess(iClient, "", ADMFLAG_BAN, true)) + { + char sArguments[64]; + char CStatus[64]; + int iTarget = -1; + GetCmdArg(1, sArguments, sizeof(sArguments)); + iTarget = FindTarget(iClient, sArguments); + + switch(iTarget){ + case -1: return Plugin_Handled; + } + + if (AreClientCookiesCached(iTarget)) + { + GetClientCookie(iTarget, g_hCookie_RestrictedLength, CStatus, sizeof(CStatus)); + + if (g_bRestricted[iTarget]) + { + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s \x07%s%N\x07%s %t", color_tag, color_warning, color_name, iTarget, color_warning, "is temporarily restricted"); + return Plugin_Handled; + } + + switch(StringToInt(CStatus)) { + case 0: { + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s \x07%s%N\x07%s %t", color_tag, color_warning, color_name, iTarget, color_warning, "is not restricted"); + return Plugin_Handled; + } + + case 1: { + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s \x07%s%N\x07%s %t", color_tag, color_warning, color_name, iTarget, color_warning, "is permanently restricted"); + return Plugin_Handled; + } + } + if (StringToInt(CStatus) <= GetTime()) + { + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s \x07%s%N\x07%s %t", color_tag, color_warning, color_name, iTarget, color_warning, "is not restricted"); + g_iRestrictedLength[iTarget] = 0; + SetClientCookie(iTarget, g_hCookie_RestrictedLength, "0"); + + return Plugin_Handled; + } + + char sRemainingTime[128]; + char sFRemainingTime[128]; + GetClientCookie(iTarget, g_hCookie_RestrictedLength, sRemainingTime, sizeof(sRemainingTime)); + int iTstamp = (StringToInt(sRemainingTime) - GetTime()); + + int iDays = (iTstamp / 86400); + int iHours = ((iTstamp / 3600) % 24); + int iMinutes = ((iTstamp / 60) % 60); + int iSeconds = (iTstamp % 60); + + if (iTstamp > 86400) + FormatEx(sFRemainingTime, sizeof(sFRemainingTime), "%d %s, %d %s, %d %s, %d %s", iDays, SingularOrMultiple(iDays)?"Days":"Day", iHours, SingularOrMultiple(iHours)?"Hours":"Hour", iMinutes, SingularOrMultiple(iMinutes)?"Minutes":"Minute", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + else if (iTstamp > 3600) + FormatEx(sFRemainingTime, sizeof(sFRemainingTime), "%d %s, %d %s, %d %s", iHours, SingularOrMultiple(iHours)?"Hours":"Hour", iMinutes, SingularOrMultiple(iMinutes)?"Minutes":"Minute", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + else if (iTstamp > 60) + FormatEx(sFRemainingTime, sizeof(sFRemainingTime), "%d %s, %d %s", iMinutes, SingularOrMultiple(iMinutes)?"Minutes":"Minute", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + else FormatEx(sFRemainingTime, sizeof(sFRemainingTime), "%d %s", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s \x07%s%N\x07%s %t: \x04%s", color_tag, color_warning, color_name, iTarget, color_warning, "is restricted for another", sFRemainingTime); + + return Plugin_Handled; + } else { + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s \x07%s%N\x07%s%t", color_tag, color_warning, color_name, iTarget, color_warning, "cookies havent loaded yet"); + return Plugin_Handled; + } + } + + if (g_bRestricted[iClient]) + { + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "status restricted"); + return Plugin_Handled; + } + + if (AreClientCookiesCached(iClient)) + { + if (g_iRestrictedLength[iClient] >= 1) + { + if (g_iRestrictedLength[iClient] != 1 && g_iRestrictedLength[iClient] != 0 && g_iRestrictedLength[iClient] <= GetTime()) + { + g_iRestrictedLength[iClient] = 0; + SetClientCookie(iClient, g_hCookie_RestrictedLength, "0"); + + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "status unrestricted"); + return Plugin_Handled; + } + + switch (g_iRestrictedLength[iClient]) { + case 1: { + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t \x04(permanent)", color_tag, color_warning, "status restricted"); + return Plugin_Handled; + } + } + + if (g_iRestrictedLength[iClient] > 1) + { + char sRemainingTime[128]; + char sFRemainingTime[128]; + GetClientCookie(iClient, g_hCookie_RestrictedLength, sRemainingTime, sizeof(sRemainingTime)); + int iTstamp = (StringToInt(sRemainingTime) - GetTime()); + + int iDays = (iTstamp / 86400); + int iHours = ((iTstamp / 3600) % 24); + int iMinutes = ((iTstamp / 60) % 60); + int iSeconds = (iTstamp % 60); + + if (iTstamp > 86400) + Format(sFRemainingTime, sizeof(sFRemainingTime), "%d %s, %d %s, %d %s, %d %s", iDays, SingularOrMultiple(iDays)?"Days":"Day", iHours, SingularOrMultiple(iHours)?"Hours":"Hour", iMinutes, SingularOrMultiple(iMinutes)?"Minutes":"Minute", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + else if (iTstamp > 3600) + Format(sFRemainingTime, sizeof(sFRemainingTime), "%d %s, %d %s, %d %s", iHours, SingularOrMultiple(iHours)?"Hours":"Hour", iMinutes, SingularOrMultiple(iMinutes)?"Minutes":"Minute", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + else if (iTstamp > 60) + Format(sFRemainingTime, sizeof(sFRemainingTime), "%d %s, %d %s", iMinutes, SingularOrMultiple(iMinutes)?"Minutes":"Minute", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + else + Format(sFRemainingTime, sizeof(sFRemainingTime), "%d %s", iSeconds, SingularOrMultiple(iSeconds)?"Seconds":"Second"); + + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t \x04(%s)", color_tag, color_warning, "status restricted", sFRemainingTime); + + return Plugin_Handled; + } + + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "status restricted"); + } else CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "status unrestricted"); + } else CReplyToCommand(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "cookies loading"); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Ban a client +//---------------------------------------------------------------------------------------------------- +public Action Command_Restrict(int iClient, int iArgs) +{ + if (GetCmdArgs() < 1) + { + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%sUsage: sm_eban ", color_tag, color_warning); + return Plugin_Handled; + } + + char sTarget_argument[64]; + GetCmdArg(1, sTarget_argument, sizeof(sTarget_argument)); + + int iTarget = -1; + if ((iTarget = FindTarget(iClient, sTarget_argument, true)) == -1) return Plugin_Handled; + + if (GetCmdArgs() > 1) + { + char sLen[64], sFlength[64]; + GetCmdArg(2, sLen, sizeof(sLen)); + + FormatEx(sFlength, sizeof(sFlength), "%d", GetTime() + (StringToInt(sLen) * 60)); + + if (StringToInt(sLen) != 0) EBanClient(iTarget, sFlength, iClient); + else { + EBanClient(iTarget, "1", iClient); + return Plugin_Handled; + } + return Plugin_Handled; + } + + EBanClient(iTarget, "0", iClient); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Lists the clients that are currently on the server and banned +//---------------------------------------------------------------------------------------------------- +public Action Command_EBanlist(int iClient, int iArgs) +{ + char sBuff[1024]; + bool bFirst = true; + Format(sBuff, sizeof(sBuff), "%T", "No players found", iClient); + + for (int i = 1; i < MaxClients + 1; i++) + { + if(!IsClientInGame(i)) continue; + + if (AreClientCookiesCached(i)) + { + char sBanLen[32]; + GetClientCookie(i, g_hCookie_RestrictedLength, sBanLen, sizeof(sBanLen)); + int iBanLen = StringToInt(sBanLen); + + if ((iBanLen != 0 && iBanLen >= GetTime()) || iBanLen == 1) + { + if (bFirst) + { + bFirst = false; + FormatEx(sBuff, sizeof(sBuff), ""); + } else FormatEx(sBuff, sizeof(sBuff), "%s, ", sBuff); + + FormatEx(sBuff, sizeof(sBuff), "%s%N (#%i)", sBuff, i, GetClientUserId(i)); + } + } + else if (g_bRestricted[i]) + { + if (bFirst) + { + bFirst = false; + FormatEx(sBuff, sizeof(sBuff), ""); + } else FormatEx(sBuff, sizeof(sBuff), "%s, ", sBuff); + + FormatEx(sBuff, sizeof(sBuff), "%s%N (#%i)", sBuff, i, GetClientUserId(i)); + } + } + + CReplyToCommand(iClient, "\x07%s[entWatch]\x07%s %t: \x07%s%s", color_tag, color_warning, "Currently e-banned", color_name, sBuff); + FormatEx(sBuff, sizeof(sBuff), ""); + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Unban a client +//---------------------------------------------------------------------------------------------------- +public Action Command_Unrestrict(int iClient, int iArgs) +{ + if (iArgs != 1) + { + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%sUsage: sm_eunban ", color_tag, color_warning); + return Plugin_Handled; + } + + char sTarget_argument[64]; + GetCmdArg(1, sTarget_argument, sizeof(sTarget_argument)); + + int iTarget = -1; + if ((iTarget = FindTarget(iClient, sTarget_argument, true)) == -1) return Plugin_Handled; + + EUnbanClient(iTarget, iClient); + + return Plugin_Handled; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Transfer a special weapon from a iClient to another +//---------------------------------------------------------------------------------------------------- +public Action Command_Transfer(int iClient, int iArgs) +{ + if (iArgs != 2) + { + CReplyToCommand(iClient, "\x07%s[entWatch] \x07%sUsage: sm_etransfer ", color_tag, color_warning); + return Plugin_Handled; + } + + bool bFoundWeapon = false; + int iEntityIndex = -1, + iWeaponCount = 0, + iTarget = -1, + iReceiver = -1; + + char sTarget_argument[64], sReceiver_argument[64]; + GetCmdArg(1, sTarget_argument, sizeof(sTarget_argument)); + GetCmdArg(2, sReceiver_argument, sizeof(sReceiver_argument)); + + if ((iReceiver = FindTarget(iClient, sReceiver_argument, false)) == -1) return Plugin_Handled; + + if (g_bConfigLoaded && !g_bRoundTransition) + { + if (sTarget_argument[0] == '$') + { + strcopy(sTarget_argument, sizeof(sTarget_argument), sTarget_argument[1]); + + for (int i = 0; i < entArraySize; i++) + { + if (strcmp(sTarget_argument, entArray[i][ent_name], false) == 0 || strcmp(sTarget_argument, entArray[i][ent_shortname], false) == 0) + { + iWeaponCount++; + bFoundWeapon = true; + iEntityIndex = i; + } + } + } else { + iTarget = FindTarget(iClient, sTarget_argument, false); + + if (iTarget != -1) + { + if (GetClientTeam(iTarget) != GetClientTeam(iReceiver)) + { + CPrintToChat(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "The receivers team differs from the targets team"); + return Plugin_Handled; + } + + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_ownerid] != -1 + && entArray[index][ent_ownerid] == iTarget + && entArray[index][ent_allowtransfer] + && IsValidEdict(entArray[index][ent_weaponid])) + { + char sBuffer_classname[64]; + GetEdictClassname(entArray[index][ent_weaponid], sBuffer_classname, sizeof(sBuffer_classname)); + + CS_DropWeapon(iTarget, entArray[index][ent_weaponid], false); + GivePlayerItem(iTarget, sBuffer_classname); + + if (entArray[index][ent_chat]) + { + entArray[index][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, entArray[index][ent_weaponid]); + entArray[index][ent_chat] = true; + } else FixedEquipPlayerWeapon(iReceiver, entArray[index][ent_weaponid]); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iClient, color_warning, "transfered all items", color_name, iTarget, color_warning, "tai to", color_name, iReceiver); + LogAction(iClient, iTarget, "\"%L\" transfered all items from \"%L\" to \"%L\"", iClient, iTarget, iReceiver); + return Plugin_Handled; + } + } + } else return Plugin_Handled; + } + } + + if (iWeaponCount > 1) + { + Menu hEdictMenu = CreateMenu(EdictMenu_Handler); + char sMenuTemp[64], sIndexTemp[16]; + int iHeldCount = 0; + char sMenuTranslate[128]; + Format(sMenuTranslate, sizeof(sMenuTranslate), "[entWatch] %T:", "Edict targets", iClient); + hEdictMenu.SetTitle(sMenuTranslate); + + for (int i = 0; i < entArraySize; i++) + { + if (strcmp(sTarget_argument, entArray[i][ent_name], false) == 0 || strcmp(sTarget_argument, entArray[i][ent_shortname], false) == 0) + { + if (entArray[i][ent_allowtransfer] && entArray[i][ent_ownerid] != -1) + { + IntToString(i, sIndexTemp, sizeof(sIndexTemp)); + Format(sMenuTemp, sizeof(sMenuTemp), "%s | %N (#%i)", entArray[i][ent_name], entArray[i][ent_ownerid], GetClientUserId(entArray[i][ent_ownerid])); + hEdictMenu.AddItem(sIndexTemp, sMenuTemp, ITEMDRAW_DEFAULT); + iHeldCount++; + } + } + } + + switch(iHeldCount) { + case 1: { + iEntityIndex = StringToInt(sIndexTemp); + + if (entArray[iEntityIndex][ent_allowtransfer]) + { + if (entArray[iEntityIndex][ent_ownerid] != -1) + { + if (IsValidEdict(entArray[iEntityIndex][ent_weaponid])) + { + int iCurOwner = entArray[iEntityIndex][ent_ownerid]; + + if (GetClientTeam(iReceiver) != GetClientTeam(iCurOwner)) + { + CPrintToChat(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "The receivers team differs from the targets team"); + delete hEdictMenu; + return Plugin_Handled; + } + + char sBuffer_classname[64]; + GetEdictClassname(entArray[iEntityIndex][ent_weaponid], sBuffer_classname, sizeof(sBuffer_classname)); + + CS_DropWeapon(iCurOwner, entArray[iEntityIndex][ent_weaponid], false); + GivePlayerItem(iCurOwner, sBuffer_classname); + + if (entArray[iEntityIndex][ent_chat]) + { + entArray[iEntityIndex][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + entArray[iEntityIndex][ent_chat] = true; + } else FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iClient, color_warning, "transfered all items", color_name, iCurOwner, color_warning, "tai to", color_name, iReceiver); + + LogAction(iClient, iCurOwner, "\"%L\" transfered all items from \"%L\" to \"%L\"", iClient, iCurOwner, iReceiver); + } + } else CPrintToChat(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Target is not valid"); + } + + delete(hEdictMenu); + } + } + if (iHeldCount >= 2) + { + g_iAdminMenuTarget[iClient] = iReceiver; + hEdictMenu.Display(iClient, MENU_TIME_FOREVER); + } else { + CPrintToChat(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "No one is currently holding that item"); + delete hEdictMenu; + } + } + else + { + if (entArray[iEntityIndex][ent_allowtransfer]) + { + if (entArray[iEntityIndex][ent_ownerid] != -1) + { + if (IsValidEdict(entArray[iEntityIndex][ent_weaponid])) + { + int iCurOwner = entArray[iEntityIndex][ent_ownerid]; + + char sBuffer_classname[64]; + GetEdictClassname(entArray[iEntityIndex][ent_weaponid], sBuffer_classname, sizeof(sBuffer_classname)); + + CS_DropWeapon(iCurOwner, entArray[iEntityIndex][ent_weaponid], false); + GivePlayerItem(iCurOwner, sBuffer_classname); + + if (entArray[iEntityIndex][ent_chat]) + { + entArray[iEntityIndex][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + entArray[iEntityIndex][ent_chat] = true; + } else FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + + bFoundWeapon = true; + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iClient, color_warning, "transfered all items", color_name, iCurOwner, color_warning, "tai to", color_name, iReceiver); + LogAction(iClient, iCurOwner, "\"%L\" transfered all items from \"%L\" to \"%L\"", iClient, iCurOwner, iReceiver); + } + } + else + { + int iEntity = Entity_GetEntityFromHammerID(entArray[iEntityIndex][ent_hammerid]); + + if (entArray[iEntityIndex][ent_chat]) + { + entArray[iEntityIndex][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, iEntity); + entArray[iEntityIndex][ent_chat] = true; + } else FixedEquipPlayerWeapon(iReceiver, iEntity); + + bFoundWeapon = true; + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%s \x07%s%t \x07%s%N", color_tag, color_name, iClient, color_warning, "transfered", entArray[iEntityIndex][ent_color], entArray[iEntityIndex][ent_name], color_warning, "tai to", color_name, iReceiver); + LogAction(iClient, -1, "\"%L\" transfered \"%s\" to \"%L\"", iClient, entArray[iEntityIndex][ent_name], iReceiver); + } + } + } + if (!bFoundWeapon) CPrintToChat(iClient, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Invalid item name"); + return Plugin_Handled; +} + +public int EdictMenu_Handler(Menu hEdictMenu, MenuAction hAction, int iParam1, int iParam2) +{ + switch (hAction) + { + case MenuAction_End:delete hEdictMenu; + case MenuAction_Select: + { + char sSelected[32]; + GetMenuItem(hEdictMenu, iParam2, sSelected, sizeof(sSelected)); + int iEntityIndex = StringToInt(sSelected); + int iReceiver = g_iAdminMenuTarget[iParam1]; + + switch(iReceiver){ + case 0: { + CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Receiver is not valid anymore"); + return; + } + } + + if (entArray[iEntityIndex][ent_allowtransfer]) + { + if (entArray[iEntityIndex][ent_ownerid] != -1) + { + if (IsValidEdict(entArray[iEntityIndex][ent_weaponid])) + { + int iCurOwner = entArray[iEntityIndex][ent_ownerid]; + + if (GetClientTeam(iReceiver) != GetClientTeam(iCurOwner)) + { + CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "The receivers team differs from the targets team"); + return; + } + + char sBuffer_classname[64]; + GetEdictClassname(entArray[iEntityIndex][ent_weaponid], sBuffer_classname, sizeof(sBuffer_classname)); + + CS_DropWeapon(iCurOwner, entArray[iEntityIndex][ent_weaponid], false); + GivePlayerItem(iCurOwner, sBuffer_classname); + + if (entArray[iEntityIndex][ent_chat]) + { + entArray[iEntityIndex][ent_chat] = false; + FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + entArray[iEntityIndex][ent_chat] = true; + } else FixedEquipPlayerWeapon(iReceiver, entArray[iEntityIndex][ent_weaponid]); + + CPrintToChatAll("\x07%s[entWatch] \x07%s%N \x07%s%t \x07%s%N \x07%s%t \x07%s%N", color_tag, color_name, iParam1, color_warning, "transfered all items", color_name, iCurOwner, color_warning, "tai to", color_name, iReceiver); + LogAction(iParam1, iCurOwner, "\"%L\" transfered all items from \"%L\" to \"%L\"", iParam1, iCurOwner, iReceiver); + } + } else CPrintToChat(iParam1, "\x07%s[entWatch] \x07%s%t", color_tag, color_warning, "Item is not valid anymore"); + } + } + } +} + +public Action Command_DebugArray(int iClient, int iArgs) +{ + if (g_bConfigLoaded && !g_bRoundTransition) + { + for (int i = 0; i < entArraySize; i++) { + CPrintToChat(iClient, "\x07%s[entWatch] \x07%sInfo at \x07%sindex \x04%i\x07%s: \x07%sWeaponID \x04%i\x07%s | \x07%sOwnerID \x04%i\x07%s | \x07%sHammerID \x04%i\x07%s | \x07%sName\x07%s \"\x04%s\x07%s\" | \x07%sShortName\x07%s \"\x04%s\x07%s\"", color_tag, color_warning, color_pickup, i, color_warning, color_pickup, entArray[i][ent_weaponid], color_warning, color_pickup, entArray[i][ent_ownerid], color_warning, color_pickup, entArray[i][ent_hammerid], color_warning, color_pickup, color_warning, entArray[i][ent_name], color_warning, color_pickup, color_warning, entArray[i][ent_shortname], color_warning); + } + } else CPrintToChat(iClient, "\x07%s[entWatch] \x07%sConfig file has not yet loaded or the round is transitioning.", color_tag, color_warning); + + return Plugin_Handled; +} + + +void CleanData() +{ + for (int index = 0; index < entArraySize; index++) + { + FormatEx(entArray[index][ent_name], 32, ""); + FormatEx(entArray[index][ent_shortname], 32, ""); + FormatEx(entArray[index][ent_color], 32, ""); + FormatEx(entArray[index][ent_buttonclass], 32, ""); + FormatEx(entArray[index][ent_filtername], 32, ""); + entArray[index][ent_hasfiltername] = false; + entArray[index][ent_blockpickup] = false; + entArray[index][ent_allowtransfer] = false; + entArray[index][ent_forcedrop] = false; + entArray[index][ent_chat] = false; + entArray[index][ent_hud] = false; + entArray[index][ent_hammerid] = -1; + entArray[index][ent_weaponid] = -1; + entArray[index][ent_buttonid] = -1; + entArray[index][ent_ownerid] = -1; + entArray[index][ent_mode] = 0; + entArray[index][ent_uses] = 0; + entArray[index][ent_maxuses] = 0; + entArray[index][ent_cooldown] = 0; + entArray[index][ent_cooldowntime] = -1; + entArray[index][ent_glowent] = -1; + entArray[index][ent_glow_r] = 255; + entArray[index][ent_glow_g] = 255; + entArray[index][ent_glow_b] = 255; + } + + for (int index = 0; index < triggerSize; index++) triggerArray[index] = 0; + + entArraySize = 0; + triggerSize = 0; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Load color settings +//---------------------------------------------------------------------------------------------------- +stock void LoadColors() +{ + Handle hKeyValues = CreateKeyValues("colors"); + char sBuffer_config[128]; + char sBuffer_path[PLATFORM_MAX_PATH]; + char sBuffer_temp[16]; + + GetConVarString(g_hCvar_ConfigColor, sBuffer_config, sizeof(sBuffer_config)); + FormatEx(sBuffer_path, sizeof(sBuffer_path), "cfg/sourcemod/entwatch/colors/%s.cfg", sBuffer_config); + FileToKeyValues(hKeyValues, sBuffer_path); + + KvRewind(hKeyValues); + + KvGetString(hKeyValues, "color_tag", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_tag, sizeof(color_tag), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_name", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_name, sizeof(color_name), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_steamid", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_steamid, sizeof(color_steamid), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_use", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_use, sizeof(color_use), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_pickup", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_pickup, sizeof(color_pickup), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_drop", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_drop, sizeof(color_drop), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_disconnect", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_disconnect, sizeof(color_disconnect), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_death", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_death, sizeof(color_death), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_warning", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(color_warning, sizeof(color_warning), "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color_hud_red", sBuffer_temp, sizeof(sBuffer_temp), "255"); + g_DefaultHudColor[0] = StringToInt(sBuffer_temp); + + KvGetString(hKeyValues, "color_hud_green", sBuffer_temp, sizeof(sBuffer_temp), "255"); + g_DefaultHudColor[1] = StringToInt(sBuffer_temp); + + KvGetString(hKeyValues, "color_hud_blue", sBuffer_temp, sizeof(sBuffer_temp), "255"); + g_DefaultHudColor[2] = StringToInt(sBuffer_temp); + + PrintToServer("Default hud colors: %i %i %i", g_DefaultHudColor[0], g_DefaultHudColor[1], g_DefaultHudColor[2]); + + KvGetString(hKeyValues, "color_hud_posx", sBuffer_temp, sizeof(sBuffer_temp), "0.0"); + g_DefaultHudPos[0] = StringToFloat(sBuffer_temp); + + KvGetString(hKeyValues, "color_hud_posy", sBuffer_temp, sizeof(sBuffer_temp), "0.4"); + g_DefaultHudPos[1] = StringToFloat(sBuffer_temp); + + delete hKeyValues; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: Load configurations +//---------------------------------------------------------------------------------------------------- +stock void LoadConfig() +{ + Handle hKeyValues = CreateKeyValues("entities"); + char sBuffer_map[128], sBuffer_path[PLATFORM_MAX_PATH], sBuffer_path_override[PLATFORM_MAX_PATH], sBuffer_temp[32]; + int sBuffer_amount; + + GetCurrentMap(sBuffer_map, sizeof(sBuffer_map)); + FormatEx(sBuffer_path, sizeof(sBuffer_path), "cfg/sourcemod/entwatch/maps/%s.cfg", sBuffer_map); + FormatEx(sBuffer_path_override, sizeof(sBuffer_path_override), "cfg/sourcemod/entwatch/maps/%s_override.cfg", sBuffer_map); + if (FileExists(sBuffer_path_override)) { + FileToKeyValues(hKeyValues, sBuffer_path_override); + LogMessage("Loading %s", sBuffer_path_override); + } else { + FileToKeyValues(hKeyValues, sBuffer_path); + LogMessage("Loading %s", sBuffer_path); + } + + KvRewind(hKeyValues); + if (KvGotoFirstSubKey(hKeyValues)) + { + g_bConfigLoaded = true; + entArraySize = 0; + triggerSize = 0; + + do + { + KvGetString(hKeyValues, "maxamount", sBuffer_temp, sizeof(sBuffer_temp)); + sBuffer_amount = StringToInt(sBuffer_temp); + + for (int i = 0; i < sBuffer_amount; i++) + { + KvGetString(hKeyValues, "name", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(entArray[entArraySize][ent_name], 32, "%s", sBuffer_temp); + + KvGetString(hKeyValues, "shortname", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(entArray[entArraySize][ent_shortname], 32, "%s", sBuffer_temp); + + KvGetString(hKeyValues, "color", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(entArray[entArraySize][ent_color], 32, "%s", sBuffer_temp); + entArray[entArraySize][ent_glow_r]=255; + entArray[entArraySize][ent_glow_g]=255; + entArray[entArraySize][ent_glow_b]=255; + if(StrEqual(sBuffer_temp,"{green}",false)){entArray[entArraySize][ent_glow_r]=0;entArray[entArraySize][ent_glow_g]=255;entArray[entArraySize][ent_glow_b]=0;} + if(StrEqual(sBuffer_temp,"{default}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=255;entArray[entArraySize][ent_glow_b]=255;} + if(StrEqual(sBuffer_temp,"{darkred}",false)){entArray[entArraySize][ent_glow_r]=140;entArray[entArraySize][ent_glow_g]=0;entArray[entArraySize][ent_glow_b]=0;} + if(StrEqual(sBuffer_temp,"{purple}",false)){entArray[entArraySize][ent_glow_r]=128;entArray[entArraySize][ent_glow_g]=0;entArray[entArraySize][ent_glow_b]=128;} + if(StrEqual(sBuffer_temp,"{lightgreen}",false)){entArray[entArraySize][ent_glow_r]=144;entArray[entArraySize][ent_glow_g]=238;entArray[entArraySize][ent_glow_b]=144;} + if(StrEqual(sBuffer_temp,"{lime}",false)){entArray[entArraySize][ent_glow_r]=199;entArray[entArraySize][ent_glow_g]=234;entArray[entArraySize][ent_glow_b]=7;} + if(StrEqual(sBuffer_temp,"{red}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=0;entArray[entArraySize][ent_glow_b]=0;} + if(StrEqual(sBuffer_temp,"{grey}",false)){entArray[entArraySize][ent_glow_r]=128;entArray[entArraySize][ent_glow_g]=128;entArray[entArraySize][ent_glow_b]=128;} + if(StrEqual(sBuffer_temp,"{olive}",false)){entArray[entArraySize][ent_glow_r]=112;entArray[entArraySize][ent_glow_g]=130;entArray[entArraySize][ent_glow_b]=56;} + if(StrEqual(sBuffer_temp,"{a}",false)){entArray[entArraySize][ent_glow_r]=192;entArray[entArraySize][ent_glow_g]=192;entArray[entArraySize][ent_glow_b]=192;} + if(StrEqual(sBuffer_temp,"{lightblue}",false)){entArray[entArraySize][ent_glow_r]=173;entArray[entArraySize][ent_glow_g]=216;entArray[entArraySize][ent_glow_b]=230;} + if(StrEqual(sBuffer_temp,"{blue}",false)){entArray[entArraySize][ent_glow_r]=0;entArray[entArraySize][ent_glow_g]=0;entArray[entArraySize][ent_glow_b]=255;} + if(StrEqual(sBuffer_temp,"{d}",false)){entArray[entArraySize][ent_glow_r]=102;entArray[entArraySize][ent_glow_g]=153;entArray[entArraySize][ent_glow_b]=204;} + if(StrEqual(sBuffer_temp,"{pink}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=105;entArray[entArraySize][ent_glow_b]=180;} + if(StrEqual(sBuffer_temp,"{darkorange}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=140;entArray[entArraySize][ent_glow_b]=0;} + if(StrEqual(sBuffer_temp,"{orange}",false)){entArray[entArraySize][ent_glow_r]=240;entArray[entArraySize][ent_glow_g]=94;entArray[entArraySize][ent_glow_b]=35;} + if(StrEqual(sBuffer_temp,"{white}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=255;entArray[entArraySize][ent_glow_b]=255;} + if(StrEqual(sBuffer_temp,"{yellow}",false)){entArray[entArraySize][ent_glow_r]=112;entArray[entArraySize][ent_glow_g]=130;entArray[entArraySize][ent_glow_b]=56;} + if(StrEqual(sBuffer_temp,"{magenta}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=105;entArray[entArraySize][ent_glow_b]=180;} + if(StrEqual(sBuffer_temp,"{silver}",false)){entArray[entArraySize][ent_glow_r]=192;entArray[entArraySize][ent_glow_g]=192;entArray[entArraySize][ent_glow_b]=192;} + if(StrEqual(sBuffer_temp,"{bluegrey}",false)){entArray[entArraySize][ent_glow_r]=102;entArray[entArraySize][ent_glow_g]=153;entArray[entArraySize][ent_glow_b]=204;} + if(StrEqual(sBuffer_temp,"{lightred}",false)){entArray[entArraySize][ent_glow_r]=255;entArray[entArraySize][ent_glow_g]=90;entArray[entArraySize][ent_glow_b]=0;} + if(StrEqual(sBuffer_temp,"{cyan}",false)){entArray[entArraySize][ent_glow_r]=0;entArray[entArraySize][ent_glow_g]=150;entArray[entArraySize][ent_glow_b]=220;} + if(StrEqual(sBuffer_temp,"{gray}",false)){entArray[entArraySize][ent_glow_r]=128;entArray[entArraySize][ent_glow_g]=128;entArray[entArraySize][ent_glow_b]=128;} + + + KvGetString(hKeyValues, "buttonclass", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(entArray[entArraySize][ent_buttonclass], 32, "%s", sBuffer_temp); + + KvGetString(hKeyValues, "filtername", sBuffer_temp, sizeof(sBuffer_temp)); + FormatEx(entArray[entArraySize][ent_filtername], 32, "%s", sBuffer_temp); + + KvGetString(hKeyValues, "hasfiltername", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_hasfiltername] = StrEqual(sBuffer_temp, "true", false); + + KvGetString(hKeyValues, "blockpickup", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_blockpickup] = StrEqual(sBuffer_temp, "true", false); + + KvGetString(hKeyValues, "allowtransfer", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_allowtransfer] = StrEqual(sBuffer_temp, "true", false); + + KvGetString(hKeyValues, "forcedrop", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_forcedrop] = StrEqual(sBuffer_temp, "true", false); + + KvGetString(hKeyValues, "chat", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_chat] = StrEqual(sBuffer_temp, "true", false); + + KvGetString(hKeyValues, "hud", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_hud] = StrEqual(sBuffer_temp, "true", false); + + KvGetString(hKeyValues, "hammerid", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_hammerid] = StringToInt(sBuffer_temp); + + KvGetString(hKeyValues, "mode", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_mode] = StringToInt(sBuffer_temp); + + KvGetString(hKeyValues, "maxuses", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_maxuses] = StringToInt(sBuffer_temp); + + KvGetString(hKeyValues, "cooldown", sBuffer_temp, sizeof(sBuffer_temp)); + entArray[entArraySize][ent_cooldown] = StringToInt(sBuffer_temp); + + KvGetString(hKeyValues, "trigger", sBuffer_temp, sizeof(sBuffer_temp)); + + int tindex = StringToInt(sBuffer_temp); + if(tindex) + { + triggerArray[triggerSize] = tindex; + triggerSize++; + } + + entArraySize++; + } + } + while (KvGotoNextKey(hKeyValues)); + } else { + g_bConfigLoaded = false; + LogMessage("Could not load %s", sBuffer_path); + } + + delete hKeyValues; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: init Glow weapon on Drop weapons +//---------------------------------------------------------------------------------------------------- +stock void GlowWeapon(int index) +{ + if (!IsValidEdict(entArray[index][ent_glowent])) + { + char sModelPath[PLATFORM_MAX_PATH]; + float fWOrigin[3], fWAngle[3]; + + // Get the original model path + GetEntPropString(entArray[index][ent_weaponid], Prop_Data, "m_ModelName", sModelPath, sizeof(sModelPath)); + ReplaceString(sModelPath, sizeof(sModelPath), "_dropped", "", false); + + // Find the location of the weapon + GetEntPropVector(entArray[index][ent_weaponid], Prop_Send, "m_vecOrigin", fWOrigin); + GetEntPropVector(entArray[index][ent_weaponid], Prop_Send, "m_angRotation", fWAngle); + + // Create & set dynamic glow entity and give properties + entArray[index][ent_glowent] = CreateEntityByName("prop_dynamic_glow"); + + DispatchKeyValue(entArray[index][ent_glowent], "model", sModelPath); + DispatchKeyValue(entArray[index][ent_glowent], "disablereceiveshadows", "1"); + DispatchKeyValue(entArray[index][ent_glowent], "disableshadows", "1"); + DispatchKeyValue(entArray[index][ent_glowent], "solid", "0"); + DispatchKeyValue(entArray[index][ent_glowent], "spawnflags", "256"); + SetEntProp(entArray[index][ent_glowent], Prop_Send, "m_CollisionGroup", 11); + + //Fix Origin + fWOrigin[0]-=Cosine(DegToRad(fWAngle[1]))*5; + fWOrigin[1]-=Cosine(DegToRad(fWAngle[1]-90))*5; + + // Spawn and teleport the entity + DispatchSpawn(entArray[index][ent_glowent]); + TeleportEntity(entArray[index][ent_glowent], fWOrigin, fWAngle, NULL_VECTOR); + + // Give glowing effect to the entity + SetEntProp(entArray[index][ent_glowent], Prop_Send, "m_bShouldGlow", true, true); + SetEntPropFloat(entArray[index][ent_glowent], Prop_Send, "m_flGlowMaxDist", 10000000.0); + + // Set glowing color + int color_glow[4]; + color_glow[0]=entArray[index][ent_glow_r]; + color_glow[1]=entArray[index][ent_glow_g]; + color_glow[2]=entArray[index][ent_glow_b]; + color_glow[3]=200; + SetVariantColor(color_glow); + AcceptEntityInput(entArray[index][ent_glowent], "SetGlowColor"); + + // Set the activator and group the entity + SetVariantString("!activator"); + AcceptEntityInput(entArray[index][ent_glowent], "SetParent", entArray[index][ent_weaponid]); + + AcceptEntityInput(entArray[index][ent_glowent], "TurnOn"); + AcceptEntityInput(entArray[index][ent_glowent], "SetGlowEnabled"); + } else + { + AcceptEntityInput(entArray[index][ent_glowent], "TurnOn"); + AcceptEntityInput(entArray[index][ent_glowent], "SetGlowEnabled"); + } +} +//---------------------------------------------------------------------------------------------------- +// Purpose: Disable glow +//---------------------------------------------------------------------------------------------------- +stock void DisableGlow(int index) { + if (IsValidEdict(entArray[index][ent_glowent])) + { + AcceptEntityInput(entArray[index][ent_glowent], "SetGlowDisabled"); + AcceptEntityInput(entArray[index][ent_glowent], "TurnOff"); + } +} + + +public Action Command_ReloadConfig(int iClient, int iArgs) +{ + CleanData(); + LoadColors(); + LoadConfig(); + + return Plugin_Handled; +} + +#if SOURCEMOD_V_MAJOR >= 1 && (SOURCEMOD_V_MINOR >= 8 || SOURCEMOD_V_MINOR >= 7 && SOURCEMOD_V_RELEASE >= 2) +public void OnEntityCreated(int iEntity, const char[] sClassname) +#else +public int OnEntityCreated(int iEntity, const char[] sClassname) +#endif +{ + if (triggerSize > 0 && StrContains(sClassname, "trigger_", false) != -1 && IsValidEntity(iEntity)) SDKHookEx(iEntity, SDKHook_Spawn, OnEntitySpawned); + if (StrContains(sClassname, "weapon_", false) != -1 && IsValidEntity(iEntity) && isMapRunning) SDKHook(iEntity, SDKHook_Spawn, OnEntitySpawned2); +} + +public void OnEntitySpawned2(int iEntity) +{ + int iHammerID = GetEntProp(iEntity, Prop_Data, "m_iHammerID"); + for (int index = 0; index < entArraySize; index++) + { + if ((entArray[index][ent_hammerid] == iHammerID) && (entArray[index][ent_weaponid] == -1)) + { + //LogMessage("Found Item HammerID:%i EntityID:%i", iHammerID, iEntity); + entArray[index][ent_weaponid] = iEntity; + entArray[index][ent_glowent] = -1; + GlowWeapon(index); + if (entArray[index][ent_buttonid] == -1 && entArray[index][ent_mode] != 0) + { + char sBuffer_targetname[32]; + Entity_GetTargetName(iEntity, sBuffer_targetname, sizeof(sBuffer_targetname)); + + int iButton = -1; + while ((iButton = FindEntityByClassname(iButton, entArray[index][ent_buttonclass])) != -1) + { + if (IsValidEdict(iButton)) + { + char sBuffer_parentname[32]; + Entity_GetParentName(iButton, sBuffer_parentname, sizeof(sBuffer_parentname)); + + if (StrEqual(sBuffer_targetname, sBuffer_parentname)) + { + SDKHookEx(iButton, SDKHook_Use, OnButtonUse); + entArray[index][ent_buttonid] = iButton; + break; + } + } + } + } + break; + } + } +} + +public void OnEntitySpawned(int iEntity) +{ + char sClassname[32]; + if(Entity_GetClassName(iEntity, sClassname, sizeof(sClassname))) + { + if (IsValidEntity(iEntity) && StrContains(sClassname, "trigger_", false) != -1) + { + int iHid = Entity_GetHammerID(iEntity); + for (int index = 0; index < triggerSize; index++) + { + if (iHid == triggerArray[index]) { + SDKHookEx(iEntity, SDKHook_Touch, OnTrigger); + SDKHookEx(iEntity, SDKHook_EndTouch, OnTrigger); + SDKHookEx(iEntity, SDKHook_StartTouch, OnTrigger); + } + } + } + } +} + +public Action Command_Cooldown(int iClient, int iArgs) +{ + if (iArgs != 2) + { + ReplyToCommand(iClient, "[SM] Usage: sm_setcooldown "); + return Plugin_Handled; + } + + char sHid[32], sCooldown[10]; + + GetCmdArg(1, sHid, sizeof(sHid)); + GetCmdArg(2, sCooldown, sizeof(sCooldown)); + + int iHammerid = StringToInt(sHid); + + for (int index = 0; index < entArraySize; index++) { + if (entArray[index][ent_hammerid] == iHammerid) entArray[index][ent_cooldown] = StringToInt(sCooldown); + } + + return Plugin_Handled; +} + +public Action OnTrigger(int iEntity, int iOther) +{ + if (MaxClients >= iOther && 0 < iOther) { + if (IsClientConnected(iOther)) { + if (g_bRestricted[iOther]) return Plugin_Handled; + + else if (g_iRestrictedLength[iOther] != 1 && g_iRestrictedLength[iOther] != 0 && g_iRestrictedLength[iOther] <= GetTime()) + { + g_iRestrictedLength[iOther] = 0; + SetClientCookie(iOther, g_hCookie_RestrictedLength, "0"); + return Plugin_Continue; + } + + else if (g_iRestrictedLength[iOther] > GetTime() || g_iRestrictedLength[iOther] == 1) return Plugin_Handled; + } + } + + return Plugin_Continue; +} + +bool SingularOrMultiple(int iNum) { + if (iNum > 1 || iNum == 0) return true; + return false; +} + +public int Native_IsClientBanned(Handle hPlugin, int iArgC) +{ + int iClient = GetNativeCell(1); + + if (iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient) || !AreClientCookiesCached(iClient)) + { + ThrowNativeError(SP_ERROR_PARAM, "Invalid client/client is not in game or client cookies are not yet loaded"); + return false; + } + + char sBuff[64]; + GetClientCookie(iClient, g_hCookie_RestrictedLength, sBuff, sizeof(sBuff)); + int iBanLen = StringToInt(sBuff); + + if ((iBanLen != 0 && iBanLen >= GetTime()) || iBanLen == 1) + { + SetNativeCellRef(2, iBanLen); + return true; + } + + return true; +} + +public int Native_BanClient(Handle hPlugin, int iArgC) +{ + int iClient = GetNativeCell(1); + bool bIsTemporary = GetNativeCell(2); + int iBanLen = GetNativeCell(3); + + if (iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient) || !AreClientCookiesCached(iClient)) + { + ThrowNativeError(SP_ERROR_PARAM, "Invalid iClient/iClient is not in game or client cookies are not yet loaded"); + return false; + } + + if (bIsTemporary) + { + EBanClient(iClient, "0", 0); + return true; + } + + if (iBanLen != 0) + { + iBanLen = GetTime() + (iBanLen * 60); + if (iBanLen <= GetTime()) + { + ThrowNativeError(SP_ERROR_PARAM, "Invalid ban length given"); + return false; + } + } else { + EBanClient(iClient, "1", 0); + return true; + } + + char sBanLen[64]; + FormatEx(sBanLen, sizeof(sBanLen), "%d", iBanLen); + + EBanClient(iClient, sBanLen, 0); + return true; +} + +public int Native_UnbanClient(Handle hPlugin, int iArgC) +{ + int iClient = GetNativeCell(1); + + if (iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient) || !AreClientCookiesCached(iClient)) + { + ThrowNativeError(SP_ERROR_PARAM, "Invalid client/client is not in game or client cookies are not yet loaded"); + return false; + } + + EUnbanClient(iClient, 0); + + return true; +} + +public int Native_IsSpecialItem(Handle hPlugin, int iArgC) +{ + int entity = GetNativeCell(1); + if (!g_bConfigLoaded || entity < MaxClients || !IsValidEdict(entity) || !IsValidEntity(entity)) return false; + + for (int index = 0; index < entArraySize; index++) { + if (entArray[index][ent_buttonid] == entity) return true; + } + + return false; +} + +public int Native_HasSpecialItem(Handle hPlugin, int iArgC) +{ + if (!g_bConfigLoaded) return false; + int iClient = GetNativeCell(1); + + if (iClient < 1 || iClient > MaxClients || !IsClientInGame(iClient)) + { + ThrowNativeError(SP_ERROR_PARAM, "Invalid iClient/iClient is not in game"); + return false; + } + + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_ownerid] == iClient) return true; + } + + return false; +} + +stock void FixedEquipPlayerWeapon(int iClient, int iWeapon) +{ + int wSlot = GetSlotCSGO(iWeapon); + if((wSlot>=0) && (wSlot<=2)) + { + int TargetWInSlot = GetPlayerWeaponSlot(iClient, wSlot); + if(TargetWInSlot != -1) CS_DropWeapon(iClient, TargetWInSlot, false); + float iClientOrigin[3]; + GetClientAbsOrigin(iClient,iClientOrigin); + TeleportEntity(iWeapon, iClientOrigin, NULL_VECTOR, NULL_VECTOR); + } +} + +stock bool IsValidPlayer(int client){ + if (0 < client <= MaxClients && IsClientInGame(client)) return true; + else return false; +} + +public Action Timer_DisplayHUD(Handle timer, int client) +{ + if (GameRules_GetProp("m_bWarmupPeriod") == 1) + return Plugin_Continue; + + if (GetConVarBool(g_hCvar_DisplayEnabled)) + { + if (g_bConfigLoaded && !g_bRoundTransition) + { + char buffer_teamtext[10][512]; + ItemIdx = 1 ; + char buffer_hud[512]; + for (int index = 0; index < entArraySize; index++) + { + if (entArray[index][ent_hud] && entArray[index][ent_ownerid] != -1) + { + //128 + char buffer_temp[512]; + //13 + char buffer_name[64]; + if (GetConVarBool(g_hCvar_DisplayCooldowns)) + { + if (entArray[index][ent_mode] == 2) + { + if (entArray[index][ent_cooldowntime] > 0) + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d]: ",entArray[index][ent_shortname], entArray[index][ent_cooldowntime]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + + } + else + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%s]: ", entArray[index][ent_shortname],"R"); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + } + else if (entArray[index][ent_mode] == 3) + { + if (entArray[index][ent_uses] < entArray[index][ent_maxuses]) + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d/%d]: ", entArray[index][ent_shortname], entArray[index][ent_uses], entArray[index][ent_maxuses]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + else + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d/%d]: ", entArray[index][ent_shortname], entArray[index][ent_uses], entArray[index][ent_maxuses]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + } + else if (entArray[index][ent_mode] == 4) + { + if (entArray[index][ent_cooldowntime] > 0) + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d]: ", entArray[index][ent_shortname], entArray[index][ent_cooldowntime]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + else + { + if (entArray[index][ent_uses] < entArray[index][ent_maxuses]) + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d/%d]: ", entArray[index][ent_shortname], entArray[index][ent_uses], entArray[index][ent_maxuses]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + else + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%s]: ", entArray[index][ent_shortname], "D"); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + } + } + else if (entArray[index][ent_mode] == 5) + { + if (entArray[index][ent_cooldowntime] > 0) + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d]: ", entArray[index][ent_shortname], entArray[index][ent_cooldowntime]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + else + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%d/%d]: ", entArray[index][ent_shortname], entArray[index][ent_uses], entArray[index][ent_maxuses]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + } + else + { + Format(buffer_temp, sizeof(buffer_temp), "%s[%s]: ", entArray[index][ent_shortname], "N/A"); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + } + else + { + Format(buffer_temp, sizeof(buffer_temp), "%s: ", entArray[index][ent_shortname]); + Format(buffer_name, sizeof(buffer_name), "%N",entArray[index][ent_ownerid]); + if(GetClientTeam(entArray[index][ent_ownerid])==3) + { + ShowCools[ItemIdx] = buffer_temp; + ShowCoolsPlayerName[ItemIdx] = buffer_name; + ItemIdx++; + } + } + if (strlen(buffer_temp) + strlen(buffer_teamtext[GetClientTeam(entArray[index][ent_ownerid])]) <= sizeof(buffer_teamtext[])) + { + StrCat(buffer_teamtext[GetClientTeam(entArray[index][ent_ownerid])], sizeof(buffer_teamtext[]), buffer_temp); + } + } + } + + for( int idx=1 ; idx = 2) + { + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsFakeClient(i)) + { + if (g_bDisplay2[i]) + { + if((HudPosition[i][0] < 0.0) || (HudPosition[i][1] < 0.0)) + { + if((HudColor[i][0] < 0) || (HudColor[i][0] > 255) || (HudColor[i][1] < 0) || (HudColor[i][1] > 255) || (HudColor[i][2] < 0) || (HudColor[i][2] > 255)) + { + SetHudTextParams(g_DefaultHudPos[0], g_DefaultHudPos[1], 1.1, g_DefaultHudColor[0], g_DefaultHudColor[1], g_DefaultHudColor[2], 255, 0, 0.0, 0.0, 0.0); + } else + { + SetHudTextParams(g_DefaultHudPos[0], g_DefaultHudPos[1], 1.1, HudColor[i][0], HudColor[i][1], HudColor[i][2], 255, 0, 0.0, 0.0, 0.0); + } + } + else + { + if((HudColor[i][0] < 0) || (HudColor[i][0] > 255) || (HudColor[i][1] < 0) || (HudColor[i][1] > 255) || (HudColor[i][2] < 0) || (HudColor[i][2] > 255)) + { + SetHudTextParams(HudPosition[i][0], HudPosition[i][1], 1.1, g_DefaultHudColor[0], g_DefaultHudColor[1], g_DefaultHudColor[2], 255, 0, 0.0, 0.0, 0.0); + } else + { + SetHudTextParams(HudPosition[i][0], HudPosition[i][1], 1.1, HudColor[i][0], HudColor[i][1], HudColor[i][2], 255, 0, 0.0, 0.0, 0.0); + } + } + ShowHudText(i, 5, buffer_hud); + } + } + } + } + else if(ItemIdx <= 1) + { + return Plugin_Continue; + } + for (int ply = 1; ply <= MaxClients; ply++) + { + if (IsClientConnected(ply) && IsClientInGame(ply)) + { + if (g_bDisplay[ply]) + { + char buffer_text[512]; + + for (int teamid = 0; teamid < sizeof(buffer_teamtext); teamid++) + { + if (!GetConVarBool(g_hCvar_ModeTeamOnly) || (GetConVarBool(g_hCvar_ModeTeamOnly) && GetClientTeam(ply) == teamid || !IsPlayerAlive(ply) || CheckCommandAccess(ply, "entWatch_chat", ADMFLAG_CHAT))) + { + if (strlen(buffer_teamtext[teamid]) + strlen(buffer_text) <= sizeof(buffer_text)) + { + StrCat(buffer_text, sizeof(buffer_text), buffer_teamtext[teamid]); + } + } + } + } + } + } + } + } + return Plugin_Continue; +} + +public Action Command_Hudpos(int client, int args) +{ + if (GetCmdArgs() < 2) + { + CReplyToCommand(client, "\x07%s[entWatch] \x07%sUsage: sm_hudpos ", color_tag, color_warning); + return Plugin_Handled; + } + char buffer[128]; + float HudPosX_validate; + float HudPosY_validate; + GetCmdArg(1, buffer, sizeof(buffer)); + HudPosX_validate = StringToFloat(buffer); + + GetCmdArg(2, buffer, sizeof(buffer)); + HudPosY_validate = StringToFloat(buffer); + + if(HudPosX_validate >= 0.0 && HudPosY_validate >= 0.0) + { + HudPosition[client][0]=HudPosX_validate; + HudPosition[client][1]=HudPosY_validate; + char sBuffer_cookie[32]; + Format(sBuffer_cookie, sizeof(sBuffer_cookie), "%f/%f", HudPosition[client][0], HudPosition[client][1]); + SetClientCookie(client, g_hCookie_HudPos, sBuffer_cookie); + } else + { + CPrintToChat(client, "\x07%s[entWatch] \x03%t", color_tag, "hudpos_wrong"); + return Plugin_Handled; + } + + CPrintToChat(client, "\x07%s[entWatch] \x03%t", color_tag, "hudpos"); + + return Plugin_Handled; +} + +public Action Command_HudColor(int client, int args) +{ + if (GetCmdArgs() < 3) + { + CReplyToCommand(client, "\x07%s[entWatch] \x07%sUsage: sm_hudcolor ", color_tag, color_warning); + return Plugin_Handled; + } + char buffer[128]; + int color_validate[3]; + GetCmdArg(1, buffer, sizeof(buffer)); + color_validate[0] = StringToInt(buffer); + + GetCmdArg(2, buffer, sizeof(buffer)); + color_validate[1] = StringToInt(buffer); + + GetCmdArg(3, buffer, sizeof(buffer)); + color_validate[2] = StringToInt(buffer); + + if((color_validate[0] >= 0) && (color_validate[0] <= 255) && (color_validate[1] >= 0) && (color_validate[1] <= 255) && (color_validate[2] >= 0) && (color_validate[2] <= 255)) + { + HudColor[client][0]=color_validate[0]; + HudColor[client][1]=color_validate[1]; + HudColor[client][2]=color_validate[2]; + char sBuffer_cookie[32]; + Format(sBuffer_cookie, sizeof(sBuffer_cookie), "%i/%i/%i", HudColor[client][0], HudColor[client][1], HudColor[client][2]); + SetClientCookie(client, g_hCookie_HudColor, sBuffer_cookie); + } else + { + CPrintToChat(client, "\x07%s[entWatch] \x03%t", color_tag, "hudcolor_wrong"); + return Plugin_Handled; + } + + CPrintToChat(client, "\x07%s[entWatch] \x03%t", color_tag, "hudcolor"); + + return Plugin_Handled; +} + +public Action Command_ToggleHUD(int client, int args) +{ + if (g_bDisplay2[client]) + { + CPrintToChat(client, "\x07%s[entWatch] \x0b%t", color_tag, "display disabled"); + g_bDisplay2[client] = false; + } + else + { + CPrintToChat(client, "\x07%s[entWatch] \x07%t", color_tag, "display enabled"); + g_bDisplay2[client] = true; + } + return Plugin_Handled; +} + +stock int GetSlotCSGO(int eWeapon) +{ + /*Slots: + Invalid/Unknown: -1 + Primary: 0 + Secondary: 1 + Melee(Knife): 2 + Projectile(Grenades): 3 + Explosive(c4): 4 + */ + char wClassName[64]; + GetEntityClassname(eWeapon,wClassName,sizeof(wClassName)); + + if((StrEqual(wClassName,"weapon_elite",false))||(StrEqual(wClassName,"weapon_deagle",false))||(StrEqual(wClassName,"weapon_fiveseven",false))|| + (StrEqual(wClassName,"weapon_glock",false))||(StrEqual(wClassName,"weapon_revolver",false))||(StrEqual(wClassName,"weapon_usp_silencer",false))|| + (StrEqual(wClassName,"weapon_cz75a",false))||(StrEqual(wClassName,"weapon_hkp2000",false))||(StrEqual(wClassName,"weapon_p250",false))|| + (StrEqual(wClassName,"weapon_tec9",false))) + { + return 1; + } else if((StrEqual(wClassName,"weapon_knife",false))||(StrEqual(wClassName,"weapon_knifegg",false))||(StrEqual(wClassName,"weapon_taser",false))|| + (StrEqual(wClassName,"weapon_axe",false))||(StrEqual(wClassName,"weapon_hammer",false))||(StrEqual(wClassName,"weapon_spanner",false))) + { + return 2; + } else if((StrEqual(wClassName,"weapon_decoy",false))||(StrEqual(wClassName,"weapon_flashbang",false))||(StrEqual(wClassName,"weapon_hegrenade",false))|| + (StrEqual(wClassName,"weapon_incgrenade",false))||(StrEqual(wClassName,"weapon_molotov",false))||(StrEqual(wClassName,"weapon_smokegrenade",false))|| + (StrEqual(wClassName,"weapon_tagrenade",false))||(StrEqual(wClassName,"weapon_diversion",false))||(StrEqual(wClassName,"weapon_snowball",false))) + { + return 3; + } else if((StrEqual(wClassName,"weapon_ak47",false))||(StrEqual(wClassName,"weapon_aug",false))||(StrEqual(wClassName,"weapon_awp",false))|| + (StrEqual(wClassName,"weapon_bizon",false))||(StrEqual(wClassName,"weapon_famas",false))||(StrEqual(wClassName,"weapon_g3sg1",false))|| + (StrEqual(wClassName,"weapon_galilar",false))||(StrEqual(wClassName,"weapon_m4a1",false))||(StrEqual(wClassName,"weapon_m4a1_silencer",false))|| + (StrEqual(wClassName,"weapon_m249",false))||(StrEqual(wClassName,"weapon_negev",false))||(StrEqual(wClassName,"weapon_p90",false))|| + (StrEqual(wClassName,"weapon_scar20",false))||(StrEqual(wClassName,"weapon_ssg08",false))||(StrEqual(wClassName,"weapon_ump45",false))|| + (StrEqual(wClassName,"weapon_xm1014",false))||(StrEqual(wClassName,"weapon_mac10",false))||(StrEqual(wClassName,"weapon_mag7",false))|| + (StrEqual(wClassName,"weapon_mp7",false))||(StrEqual(wClassName,"weapon_mp9",false))||(StrEqual(wClassName,"weapon_nova",false))|| + (StrEqual(wClassName,"weapon_sawedoff",false))||(StrEqual(wClassName,"weapon_sg556",false))) + { + return 0; + } else if((StrEqual(wClassName,"weapon_c4",false))||(StrEqual(wClassName,"weapon_breachcharge",false))||(StrEqual(wClassName,"weapon_bumpmine",false))|| + (StrEqual(wClassName,"weapon_healthshot",false))||(StrEqual(wClassName,"weapon_shield",false))) + { + return 4; + } else return -1; +} \ No newline at end of file diff --git a/entWatch_csgo/scripting/include/csgocolors_fix.inc b/entWatch_csgo/scripting/include/csgocolors_fix.inc new file mode 100644 index 0000000..e0e9ad8 --- /dev/null +++ b/entWatch_csgo/scripting/include/csgocolors_fix.inc @@ -0,0 +1,874 @@ +/***************************************************************************** + * * + * Colored Chat Functions for CS:GO * + * Author: exvel, Editor: Popoklopsi, Powerlord, Zipcore, Bara, DarkerZ[RUS] * + * Version: 2.0.1fix * + * * + *****************************************************************************/ + +#if defined _csgocolors_included + #endinput +#endif + +#define _csgocolors_included + +#define MAX_MESSAGE_LENGTH 250 + +#define SERVER_INDEX 0 +#define NO_INDEX -1 +#define NO_PLAYER -2 +#define MAX_COLORS 24 + +enum CColors +{ + Color_Default = 0, + Color_Darkred, + Color_Purple, + Color_Green, + Color_Lightgreen, + Color_Lime, + Color_Red, + Color_Grey, + Color_Olive, + Color_A, + Color_Lightblue, + Color_Blue, + Color_D, + Color_Pink, + Color_Darkrange, + Color_Orange, + Color_White, + Color_Yellow, + Color_Magenta, + Color_Silver, + Color_Bluegrey, + Color_Lightred, + Color_Cyan, + Color_Gray +} + +/* CColors' properties */ +char CTag[][] = { + "{default}", + "{darkred}", + "{purple}", + "{green}", + "{lightgreen}", + "{lime}", + "{red}", + "{grey}", + "{olive}", + "{a}", + "{lightblue}", + "{blue}", + "{d}", + "{pink}", + "{darkorange}", + "{orange}", + "{white}", + "{yellow}", + "{magenta}", + "{silver}", + "{bluegrey}", + "{lightred}", + "{cyan}", + "{gray}" +}; +char CTagCode[][] = { + "\x01", + "\x02", + "\x03", + "\x04", + "\x05", + "\x06", + "\x07", + "\x08", + "\x09", + "\x0A", + "\x0B", + "\x0C", + "\x0D", + "\x0E", + "\x0F", + "\x10", + "\x01", + "\x09", + "\x0E", + "\x0A", + "\x0D", + "\x0F", + "\x03", + "\x08" +}; +bool CTagReqSayText2[] = { + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false +}; + +bool CEventIsHooked = false; +bool CSkipList[MAXPLAYERS+1] = {false,...}; + +int CProfile_TeamIndex[MAX_COLORS] = { + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + NO_INDEX, + 3, + NO_INDEX +}; +bool CProfile_SayText2 = true; + +static Handle sm_show_activity = null; + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client is not connected an error will be thrown. + */ + +stock void CPrintToChat(int client, const char[] szMessage, any ...) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (!IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + char szBuffer[MAX_MESSAGE_LENGTH]; + char szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3); + + int index = CFormat(szCMessage, sizeof(szCMessage)); + + if (index == NO_INDEX) + PrintToChat(client, "%s", szCMessage); + else CSayText2(client, index, szCMessage); +} + +/** + * Reples to a message in a command. A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. If the command was from chat, CPrintToChat() is used. + * Supports color tags. + * + * @param client Client index, or 0 for server. + * @param szMessage Formatting rules. + * @param ... Variable number of format parameters. + * @return No return + * + * On error/Errors: If the client is not connected or invalid. + */ +stock void CReplyToCommand(int client, const char[] szMessage, any ...) +{ + char szCMessage[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(szCMessage, sizeof(szCMessage), szMessage, 3); + + if (client == 0 || GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + CRemoveTags(szCMessage, sizeof(szCMessage)); + + if (client == 0) + PrintToServer("%s", szCMessage); + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + PrintToConsole(client, "%s", szCMessage); + else CPrintToChat(client, "%s", szCMessage); +} + +/** + * Reples to a message in a command. A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. If the command was from chat, CPrintToChat() is used. + * Supports color tags. + * + * @param client Client index, or 0 for server. + * @param author Author index whose color will be used for teamcolor tag. + * @param szMessage Formatting rules. + * @param ... Variable number of format parameters. + * @return No return + * + * On error/Errors: If the client is not connected or invalid. + */ +stock void CReplyToCommandEx(int client, int author, const char[] szMessage, any ...) +{ + char szCMessage[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(szCMessage, sizeof(szCMessage), szMessage, 4); + + if (client == 0 || GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + CRemoveTags(szCMessage, sizeof(szCMessage)); + + if (client == 0) + PrintToServer("%s", szCMessage); + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + PrintToConsole(client, "%s", szCMessage); + else CPrintToChatEx(client, author, "%s", szCMessage); +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param szMessage Message (formatting rules) + * @return No return + */ +stock void CPrintToChatAll(const char[] szMessage, any ...) +{ + char szBuffer[MAX_MESSAGE_LENGTH]; + + for(int i = 1; i <= MaxClients; i++) + { + if (i > 0 && IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) + { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 2); + + CPrintToChat(i, "%s", szBuffer); + } + + CSkipList[i] = false; + } +} + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags and teamcolor tag. + * + * @param client Client index. + * @param author Author index whose color will be used for teamcolor tag. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client or author are not connected an error will be thrown. + */ +stock void CPrintToChatEx(int client, int author, const char[] szMessage, any ...) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (author < 0 || author > MaxClients) + ThrowError("Invalid author index %d", author); + + if (!IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + char szBuffer[MAX_MESSAGE_LENGTH]; + char szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4); + + int index = CFormat(szCMessage, sizeof(szCMessage), author); + + if (index == NO_INDEX) + PrintToChat(client, "%s", szCMessage); + else CSayText2(client, author, szCMessage); +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags and teamcolor tag. + * + * @param author Author index whos color will be used for teamcolor tag. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the author is not connected an error will be thrown. + */ + +stock void CPrintToChatAllEx(int author, const char[] szMessage, any ...) +{ + if (author < 0 || author > MaxClients) + ThrowError("Invalid author index %d", author); + + if (!IsClientInGame(author)) + ThrowError("Client %d is not in game", author); + + char szBuffer[MAX_MESSAGE_LENGTH]; + + for(int i = 1; i <= MaxClients; i++) + { + if (i > 0 && IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) + { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 3); + + CPrintToChatEx(i, author, "%s", szBuffer); + } + + CSkipList[i] = false; + } +} + +/** + * Removes color tags from the string. + * + * @param szMessage String. + * @return No return + */ + +stock void CRemoveTags(char[] szMessage, int maxlength) +{ + for (int i = 0; i < MAX_COLORS; i++) + ReplaceString(szMessage, maxlength, CTag[i], "", false); + + ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); +} + +/** + * Replace the color with another color + * Handle with care! + * + * @param color color to replace. + * @param newColor color to replace with. + * @noreturn + */ + +stock void CReplaceColor(CColors color, CColors newColor) +{ + if (!CEventIsHooked) + { + //CSetupProfile(); + + CEventIsHooked = true; + } + + CProfile_TeamIndex[color] = CProfile_TeamIndex[newColor]; + + CTagReqSayText2[color] = CTagReqSayText2[newColor]; + Format(CTagCode[color], sizeof(CTagCode[]), CTagCode[newColor]); +} + +/** + * This function should only be used right in front of + * CPrintToChatAll or CPrintToChatAllEx and it tells + * to those funcions to skip specified client when printing + * message to all clients. After message is printed client will + * no more be skipped. + * + * @param client Client index + * @return No return + */ + +stock void CSkipNextClient(int client) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + CSkipList[client] = true; +} + +/** + * Replaces color tags in a string with color codes + * + * @param szMessage String. + * @param maxlength Maximum length of the string buffer. + * @return Client index that can be used for SayText2 author index + * + * On error/Errors: If there is more then one team color is used an error will be thrown. + */ + +stock int CFormat(char[] szMessage, int maxlength, int author = NO_INDEX) +{ + /* Hook event for auto profile setup on map start */ + if (!CEventIsHooked) + { + HookEvent("server_spawn", CEvent_MapStart, EventHookMode_PostNoCopy); + + CEventIsHooked = true; + } + + int iRandomPlayer = NO_INDEX; + + // On CS:GO set invisible precolor + Format(szMessage, maxlength, " %s", szMessage); + + /* If author was specified replace {teamcolor} tag */ + if (author != NO_INDEX) + { + if (CProfile_SayText2) + { + ReplaceString(szMessage, maxlength, "{teamcolor}", "\x03", false); + + iRandomPlayer = author; + } + /* If saytext2 is not supported by game replace {teamcolor} with green tag */ + else ReplaceString(szMessage, maxlength, "{teamcolor}", CTagCode[Color_Green], false); + } + else ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); + + /* For other color tags we need a loop */ + for (int i = 0; i < MAX_COLORS; i++) + { + /* If tag not found - skip */ + if (StrContains(szMessage, CTag[i], false) == -1) + continue; + + /* If tag doesn't need saytext2 simply replace */ + else if (!CTagReqSayText2[i]) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); + + /* Tag needs saytext2 */ + else + { + /* If saytext2 is not supported by game replace tag with green tag */ + if (!CProfile_SayText2) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* Game supports saytext2 */ + else + { + /* If random player for tag wasn't specified replace tag and find player */ + if (iRandomPlayer == NO_INDEX) + { + /* Searching for valid client for tag */ + iRandomPlayer = CFindRandomPlayerByTeam(CProfile_TeamIndex[i]); + + /* If player not found replace tag with green color tag */ + if (iRandomPlayer == NO_PLAYER) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* If player was found simply replace */ + else ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); + + } + /* If found another team color tag throw error */ + else + { + //ReplaceString(szMessage, maxlength, CTag[i], ""); + ThrowError("Using two team colors in one message is not allowed"); + } + } + + } + } + + return iRandomPlayer; +} + +/** + * Founds a random player with specified team + * + * @param color_team Client team. + * @return Client index or NO_PLAYER if no player found + */ + +stock int CFindRandomPlayerByTeam(int color_team) +{ + if (color_team == SERVER_INDEX) + return 0; + + else for(int i = 1; i <= MaxClients; i++) + { + if (i > 0 && IsClientInGame(i) && GetClientTeam(i) == color_team) + return i; + } + + return NO_PLAYER; +} + +/** + * Sends a SayText2 usermessage to a client + * + * @param szMessage Client index + * @param maxlength Author index + * @param szMessage Message + * @return No return. + */ + +stock void CSayText2(int client, int author, const char[] szMessage) +{ + Handle hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + + PbSetInt(hBuffer, "ent_idx", author); + PbSetBool(hBuffer, "chat", true); + PbSetString(hBuffer, "msg_name", szMessage); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + + EndMessage(); +} + +public Action CEvent_MapStart(Event event, const char[] name, bool dontBroadcast) +{ + for(int i = 1; i <= MaxClients; i++) + CSkipList[i] = false; +} + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. + * + * This version does not display a message to the originating client + * if used from chat triggers or menus. If manual replies are used + * for these cases, then this function will suffice. Otherwise, + * CShowActivity2() is slightly more useful. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ + +stock int CShowActivity(int client, const char[] format, any ...) +{ + if (sm_show_activity == null) + sm_show_activity = FindConVar("sm_show_activity"); + + char tag[] = "[SM] "; + + char szBuffer[MAX_MESSAGE_LENGTH]; + //char szCMessage[MAX_MESSAGE_LENGTH]; + int value = GetConVarInt(sm_show_activity); + ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + bool display_in_chat = false; + if (client != 0) + { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + sign = "PLAYER"; + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) + { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else + { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) + { + return 1; + } + + for(int i = 1; i <= MaxClients; i++) + { + if (i == 0 + || !IsClientInGame(i) + || IsFakeClient(i) + || (display_in_chat && i == client)) + { + continue; + } + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) + { + char newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 2) || (i == client)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else + { + /* Treat this as an admin user */ + bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) + { + char newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 8) || ((value & 16) && is_root) || (i == client)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Same as CShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to display with. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ + +stock int CShowActivityEx(int client, const char[] tag, const char[] format, any ...) +{ + if (sm_show_activity == null) + sm_show_activity = FindConVar("sm_show_activity"); + + char szBuffer[MAX_MESSAGE_LENGTH]; + //char szCMessage[MAX_MESSAGE_LENGTH]; + int value = GetConVarInt(sm_show_activity); + ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + bool display_in_chat = false; + if (client != 0) + { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + sign = "PLAYER"; + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) + { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else + { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) + return 1; + + for(int i = 1; i <= MaxClients; i++) + { + if (i == 0 || !IsClientInGame(i) || IsFakeClient(i) || (display_in_chat && i == client)) + continue; + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) + { + char newsign[MAX_NAME_LENGTH]; + newsign = sign; + + if ((value & 2) || (i == client)) + newsign = name; + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else + { + /* Treat this as an admin user */ + bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) + { + char newsign[MAX_NAME_LENGTH]; + newsign = sign; + + if ((value & 8) || ((value & 16) && is_root) || (i == client)) + newsign = name; + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. + * All users receive a message in their chat text, except for the originating client, + * who receives the message based on the current ReplySource. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to prepend to the message. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ + +stock int CShowActivity2(int client, const char[] tag, const char[] format, any ...) +{ + if (sm_show_activity == null) + sm_show_activity = FindConVar("sm_show_activity"); + + char szBuffer[MAX_MESSAGE_LENGTH]; + //char szCMessage[MAX_MESSAGE_LENGTH]; + int value = GetConVarInt(sm_show_activity); + // ReplySource replyto = GetCmdReplySource(); + + char name[MAX_NAME_LENGTH] = "Console"; + char sign[MAX_NAME_LENGTH] = "ADMIN"; + if (client != 0) + { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + AdminId id = GetUserAdmin(client); + + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + sign = "PLAYER"; + + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + /* We don't display directly to the console because the chat text + * simply gets added to the console, so we don't want it to print + * twice. + */ + CPrintToChatEx(client, client, "%s%s", tag, szBuffer); + } + else + { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", "[SM]", szBuffer); + } + + if (!value) + return 1; + + for(int i = 1; i <= MaxClients; i++) + { + if (i == 0 || !IsClientInGame(i) || IsFakeClient(i) || i == client) + continue; + + AdminId id = GetUserAdmin(i); + SetGlobalTransTarget(i); + + if (id == INVALID_ADMIN_ID || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) + { + char newsign[MAX_NAME_LENGTH]; + newsign = sign; + + if ((value & 2)) + newsign = name; + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else + { + /* Treat this as an admin user */ + bool is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + + if ((value & 4) || (value & 8) || ((value & 16) && is_root)) + { + char newsign[MAX_NAME_LENGTH]; + newsign = sign; + + if ((value & 8) || ((value & 16) && is_root)) + newsign = name; + + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} \ No newline at end of file diff --git a/entWatch_csgo/scripting/include/entWatch.inc b/entWatch_csgo/scripting/include/entWatch.inc new file mode 100644 index 0000000..0e6a2f2 --- /dev/null +++ b/entWatch_csgo/scripting/include/entWatch.inc @@ -0,0 +1,150 @@ +#if defined _entWatch_include + #endinput +#endif +#define _entWatch_include + +/** + * Checks if a client is currently banned, if an integer variable is referenced the time of unban will be assigned to it. + * + * @param client Client index to check for ban + * @param iTimeStamp Pass an integer variable by reference and it will contain the UNIX timestamp when the player will be unbanned + * @return True if user is banned, false otherwise + * + * On error/errors: Invalid client index/client is not in game or client cookies are not yet loaded + */ +native bool:entWatch_IsClientBanned(client, &iTimeStamp); + +/** + * Bans a client from using special items. + * + * @param client Client index to ban + * @param IsTemporary If the ban should be temporary pass true here + * @param iLength Length of ban in minutes, pass 0 here for a permanent ban + * @return True on success, false otherwsie + * + * On error/errors: Invalid client index/client is not in game or client cookies are not yet loaded + */ +native bool:entWatch_BanClient(client, bool:bIsTemporary=false, iLength=0); + +/** + * Unbans a previously ebanned Client. + * + * @param client Client index to unban + * @return True on success, false otherwsie + * + * On error/errors: Invalid client index/client is not in game or client cookies are not yet loaded + */ +native bool:entWatch_UnbanClient(client); + +/** + * Checks if an entity is a special item. + * + * @param entity Entity index to check + * @return True if entity is a special item, false otherwsie + */ +native bool:entWatch_IsSpecialItem(entity); + +/** + * Checks if a client has a special item. + * + * @param client Client index to check + * @return True if client has a special item, false otherwsie + */ +native bool:entWatch_HasSpecialItem(client); + +/** + * Called when a client is e-banned by any means + * + * @param admin Admin index that issued the ban + * @param iLength Length of the ban in UNIX time + * @param client Client index that was banned + * + * @return None + */ +forward entWatch_OnClientBanned(admin, iLenght, client); + +/** + * Called when a client is e-unbanned by any means + * + * @param admin Admin index that removed the ban + * @param client Client index that was unbanned + * @return None + */ +forward entWatch_OnClientUnbanned(admin, client); + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetTargetName(entity, String:buffer[], size) +{ + return GetEntPropString(entity, Prop_Data, "m_iName", buffer, size); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetParentName(entity, String:buffer[], size) +{ + return GetEntPropString(entity, Prop_Data, "m_iParent", buffer, size); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetHammerID(entity) +{ + return GetEntProp(entity, Prop_Data, "m_iHammerID"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetClassName(entity, String:buffer[], size) +{ + GetEntPropString(entity, Prop_Data, "m_iClassname", buffer, size); + + if (buffer[0] == '\0') { + return false; + } + + return true; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetEntityFromHammerID(hammerID) +{ + for (new i = 0; i < 4096; i++) + { + if (IsValidEntity(i) && Entity_GetHammerID(i) == hammerID) + { + if (IsValidEntity(i)) + return i; + } + } + + return -1; +} + +public SharedPlugin:__pl_entWatch = +{ + name = "entWatch", + file = "entWatch.smx", +#if defined REQUIRE_PLUGIN + required = 1 +#else + required = 0 +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_entWatch_SetNTVOptional() +{ + MarkNativeAsOptional("entWatch_IsClientBanned"); + MarkNativeAsOptional("entWatch_BanClient"); + MarkNativeAsOptional("entWatch_UnbanClient"); + MarkNativeAsOptional("entWatch_IsSpecialItem"); + MarkNativeAsOptional("entWatch_HasSpecialItem"); +} +#endif diff --git a/entWatch_csgo/translations/entWatch.phrases.txt b/entWatch_csgo/translations/entWatch.phrases.txt new file mode 100644 index 0000000..6febdeb --- /dev/null +++ b/entWatch_csgo/translations/entWatch.phrases.txt @@ -0,0 +1,271 @@ +"Phrases" +{ + "welcome" + { + "en" "The current map is supported by this plugin." + } + "use" + { + "en" "used" + } + "pickup" + { + "en" "has picked up" + } + "drop" + { + "en" "has dropped" + } + "death" + { + "en" "has died with" + } + "disconnect" + { + "en" "disconnected with" + } + "cookies loading" + { + "en" "Please wait. Your settings are still loading." + } + "status restricted" + { + "en" "You are currently restricted." + } + "status unrestricted" + { + "en" "You are currently unrestricted." + } + "display enabled" + { + "en" "The hud is now enabled." + } + "display disabled" + { + "en" "The hud is now disabled." + } + "hudpos" + { + "en" "hud position is saved." + } + "hudpos_wrong" + { + "en" "hud position is wrong." + } + "hudcolor" + { + "en" "hud color is saved." + } + "hudcolor_wrong" + { + "en" "hud color is wrong." + } + "restricted" + { + "en" "restricted" + } + "unrestricted" + { + "en" "unrestricted" + } + "entWatch Commands" + { + "en" "entWatch Commands" + } + "List Banned Clients" + { + "en" "List Banned Clients" + } + "Ban a Client" + { + "en" "Ban a Client" + } + "Transfer an item" + { + "en" "Transfer an item" + } + "Give an Item" + { + "en" "Give an Item" + } + "Unban a Client" + { + "en" "Unban a Client" + } + "Banned Clients" + { + "en" "Banned Clients" + } + "No Banned Clients" + { + "en" "No Banned Clients" + } + "No transferable items currently held" + { + "en" "No transferable items currently held" + } + "No Give items currently held" + { + "en" "No Give items currently held" + } + "Item no longer available" + { + "en" "Item no longer available" + } + "Item is already picked up" + { + "en" "Item is already picked up" + } + "Transfer iTarget" + { + "en" "Transfer iTarget" + } + "Give iTarget" + { + "en" "Give iTarget" + } + "Receiver is not valid anymore" + { + "en" "Receiver is not valid anymore" + } + "The receivers team differs from the targets team" + { + "en" "The receivers team differs from the targets team" + } + "transfered all items" + { + "en" "transfered all items from" + } + "transfered" + { + "en" "transfered" + } + "tai to" + { + "en" "to" + } + "Item is not valid anymore" + { + "en" "Item is not valid anymore" + } + "The receivers team is not CT or TER" + { + "en" "The receivers team is not CT or TER" + } + "give item" + { + "en" "give item" + } + "Item dont pickable" + { + "en" "Item dont pickable" + } + "Ban Time for" + { + "en" "Ban Time for" + } + "Temporary" + { + "en" "Temporary" + } + "Minutes" + { + "en" "Minutes" + } + "Hour" + { + "en" "Hour" + } + "Day" + { + "en" "Day" + } + "Week" + { + "en" "Week" + } + "Month" + { + "en" "Month" + } + "Permanent" + { + "en" "Permanent" + } + "Banned Client" + { + "en" "Banned Client" + } + "Duration" + { + "en" "Duration" + } + "Expires" + { + "en" "Expires" + } + "Never" + { + "en" "Never" + } + "On Map Change" + { + "en" "On Map Change" + } + "Issued on" + { + "en" "Issued on" + } + "Admin SID" + { + "en" "Admin SID" + } + "Unban" + { + "en" "Unban" + } + "is temporarily restricted" + { + "en" "is temporarily restricted" + } + "is not restricted" + { + "en" "is not restricted" + } + "is permanently restricted" + { + "en" "is permanently restricted" + } + "is restricted for another" + { + "en" "is restricted for another" + } + "cookies havent loaded yet" + { + "en" "s cookies havent loaded yet" + } + "No players found" + { + "en" "No players found" + } + "Currently e-banned" + { + "en" "Currently e-banned" + } + "Edict targets" + { + "en" "Edict targets" + } + "Target is not valid" + { + "en" "Target is not valid" + } + "No one is currently holding that item" + { + "en" "No one is currently holding that item" + } + "Invalid item name" + { + "en" "Invalid item name" + } +} diff --git a/entWatch_csgo/translations/ru/entWatch.phrases.txt b/entWatch_csgo/translations/ru/entWatch.phrases.txt new file mode 100644 index 0000000..be156dd --- /dev/null +++ b/entWatch_csgo/translations/ru/entWatch.phrases.txt @@ -0,0 +1,271 @@ +"Phrases" +{ + "welcome" + { + "ru" "entwatch работает на этой карте." + } + "use" + { + "ru" "использовал" + } + "pickup" + { + "ru" "подобрал" + } + "drop" + { + "ru" "выбросил" + } + "death" + { + "ru" "умер и сбросил" + } + "disconnect" + { + "ru" "отключился из игры с" + } + "cookies loading" + { + "ru" "Пожалуйста ждите. Ваши куки еще загружаются." + } + "status restricted" + { + "ru" "Вам запрещено использовать специальное оружие." + } + "status unrestricted" + { + "ru" "Вам разрешено использовать специальное оружие." + } + "display enabled" + { + "ru" "Информация ХУД теперь включена." + } + "display disabled" + { + "ru" "Информация ХУД теперь выключена." + } + "hudpos" + { + "ru" "Позиция ХУД сохранена." + } + "hudpos_wrong" + { + "ru" "Позиция ХУД ошибочна." + } + "hudcolor" + { + "ru" "Цвет ХУД сохранён." + } + "hudcolor_wrong" + { + "ru" "Цвет ХУД ошибочен." + } + "restricted" + { + "ru" "запретил подбор итемов игроку" + } + "unrestricted" + { + "ru" "разрешил подбор итемов игроку" + } + "entWatch Commands" + { + "ru" "entWatch Команды" + } + "List Banned Clients" + { + "ru" "Список Забаненных" + } + "Ban a Client" + { + "ru" "Забанить Игрока" + } + "Transfer an item" + { + "ru" "Передать Итем" + } + "Give an Item" + { + "ru" "Выдать Итем" + } + "Unban a Client" + { + "ru" "Разбанить Игрока" + } + "Banned Clients" + { + "ru" "Забаненные Игроки" + } + "No Banned Clients" + { + "ru" "Нет Забаненных Игроков" + } + "No transferable items currently held" + { + "ru" "В настоящий момент нет передаваемых итемов" + } + "No Give items currently held" + { + "ru" "В настоящий момент нет выдаваемых итемов" + } + "Item no longer available" + { + "ru" "Итем больше не доступен" + } + "Item is already picked up" + { + "ru" "Итем уже был подобран" + } + "Transfer iTarget" + { + "ru" "Кому Передать" + } + "Give iTarget" + { + "ru" "Кому Выдать" + } + "Receiver is not valid anymore" + { + "ru" "Получатель больше не доступен" + } + "The receivers team differs from the targets team" + { + "ru" "Получатель и Отправитель находятся в разных командах" + } + "transfered all items" + { + "ru" "передал все итемы от" + } + "transfered" + { + "ru" "передал" + } + "tai to" + { + "ru" "игроку" + } + "Item is not valid anymore" + { + "ru" "Итем больше не доступен" + } + "The receivers team is not CT or TER" + { + "ru" "Получатель играет ни за CT, ни за TER" + } + "give item" + { + "ru" "выдал итем" + } + "Item dont pickable" + { + "ru" "Предмет не подбирается" + } + "Ban Time for" + { + "ru" "Время Бана для" + } + "Temporary" + { + "ru" "Временно" + } + "Minutes" + { + "ru" "Минут" + } + "Hour" + { + "ru" "Час" + } + "Day" + { + "ru" "День" + } + "Week" + { + "ru" "Неделя" + } + "Month" + { + "ru" "Месяц" + } + "Permanent" + { + "ru" "Навсегда" + } + "Banned Client" + { + "ru" "Забаненный Игрок" + } + "Duration" + { + "ru" "Продолжительность" + } + "Expires" + { + "ru" "Истекает" + } + "Never" + { + "ru" "Никогда" + } + "On Map Change" + { + "ru" "По окончанию карты" + } + "Issued on" + { + "ru" "Выдано" + } + "Admin SID" + { + "ru" "SID Админа" + } + "Unban" + { + "ru" "Разбанить" + } + "is temporarily restricted" + { + "ru" "запрещено подбирать итемы временно" + } + "is not restricted" + { + "ru" "не имеет блокировки по поднятию итемов" + } + "is permanently restricted" + { + "ru" "запрещено подбирать итемы навсегда" + } + "is restricted for another" + { + "ru" "запрещено подбирать итемы на" + } + "cookies havent loaded yet" + { + "ru" "куки не были загружены" + } + "No players found" + { + "ru" "Игроки не найдены" + } + "Currently e-banned" + { + "ru" "Текущие ЕБаны" + } + "Edict targets" + { + "ru" "Итемы" + } + "Target is not valid" + { + "ru" "Цель не доступна" + } + "No one is currently holding that item" + { + "ru" "Никто в данный момент не владеет этим итемом" + } + "Invalid item name" + { + "ru" "Неправильное имя итема" + } +} diff --git a/hlstatsx/scripting/hlstatsx.sp b/hlstatsx/scripting/hlstatsx.sp new file mode 100644 index 0000000..8c14638 --- /dev/null +++ b/hlstatsx/scripting/hlstatsx.sp @@ -0,0 +1,2222 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to display ingame messages + * http://www.hlxcommunity.com/ + * Copyright (C) 2008-2009 Nicholas Hastings + * Copyright (C) 2007-2009 TTS Oetzel & Goerz GmbH + * Modified by Nicholas Hastings (psychonic) for use with HLstatsX Community Edition + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#define REQUIRE_EXTENSIONS +#include +#include +#include +#undef REQUIRE_EXTENSIONS +#include +#include + +#define VERSION "1.6.19" +#define HLXTAG "HLstatsX:CE" + +enum GameType { + Game_Unknown = -1, + Game_CSS, + Game_DODS, + Game_L4D, + Game_TF, + Game_HL2MP, + Game_INSMOD, + Game_FF, + Game_ZPS, + Game_AOC, + Game_FOF, + Game_GES, + Game_PVKII, + Game_CSP, + Game_ND, + Game_DDD, + Game_CSGO, +}; + +new GameType:gamemod = Game_Unknown; + +new Handle: hlx_block_chat_commands; +new Handle: hlx_message_prefix; +new Handle: hlx_protect_address; +new Handle: hlx_server_tag; +new Handle: sv_tags; +new Handle: message_recipients; +new const String: blocked_commands[][] = { "rank", "skill", "points", "place", "session", "session_data", + "kpd", "kdratio", "kdeath", "next", "load", "status", "servers", + "top20", "top10", "top5", "clans", "bans", "cheaters", "statsme", "weapons", + "weapon", "action", "actions", "accuracy", "targets", "target", "kills", + "kill", "player_kills", "cmd", "cmds", "command", "hlx_display 0", + "hlx_display 1", "hlx_teams 0", "hlx_teams 1", "hlx_hideranking", + "hlx_chat 0", "hlx_chat 1", "hlx_menu", "servers 1", "servers 2", + "servers 3", "hlx", "hlstatsx", "help" }; + +new Handle:HLstatsXMenuMain; +new Handle:HLstatsXMenuAuto; +new Handle:HLstatsXMenuEvents; + +new Handle: PlayerColorArray; +new ColorSlotArray[] = { -1, -1, -1, -1, -1, -1 }; + +new const String:ct_models[][] = { + "models/player/ct_urban.mdl", + "models/player/ct_gsg9.mdl", + "models/player/ct_sas.mdl", + "models/player/ct_gign.mdl" +}; + +new const String:ts_models[][] = { + "models/player/t_phoenix.mdl", + "models/player/t_leet.mdl", + "models/player/t_arctic.mdl", + "models/player/t_guerilla.mdl" +}; + +new const String: modnamelist[][] = { + "Counter-Strike: Source", + "Day of Defeat: Source", + "Left 4 Dead (1 or 2)", + "Team Fortress 2", + "Half-Life 2 Deathmatch", + "Insurgency", + "Fortress Forever", + "Zombie Panic: Source", + "Age of Chivalry", + "Fistful of Frags", + "GoldenEye: Source", + "Pirates, Vikings, and Knights", + "CSPromod", + "Nuclear Dawn", + "Dino D-Day", + "Counter-Strike: Global Offensive" +}; + +new String: message_prefix[32]; +new bool:g_bPlyrCanDoMotd[MAXPLAYERS+1]; +new bool:g_bGameCanDoMotd = true; +new bool:g_bTrackColors4Chat; +new g_iSDKVersion = SOURCE_SDK_UNKNOWN; +new Handle:g_cvarTeamPlay = INVALID_HANDLE; +new bool:g_bTeamPlay; +new bool:g_bLateLoad = false; +new bool:g_bIgnoreNextTagChange = false; +new Handle:g_hCustomTags; + +#define SVTAGSIZE 128 + +public Plugin:myinfo = { + name = "HLstatsX CE Ingame Plugin", + author = "psychonic", + description = "Provides ingame functionality for interaction from an HLstatsX CE installation", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + g_bLateLoad = late; + MarkNativeAsOptional("CS_SwitchTeam"); + MarkNativeAsOptional("CS_RespawnPlayer"); + MarkNativeAsOptional("SetCookieMenuItem"); + + return APLRes_Success; +} + + +public OnPluginStart() +{ + get_server_mod(); + + CreateHLstatsXMenuMain(HLstatsXMenuMain); + CreateHLstatsXMenuAuto(HLstatsXMenuAuto); + CreateHLstatsXMenuEvents(HLstatsXMenuEvents); + + RegServerCmd("hlx_sm_psay", hlx_sm_psay); + RegServerCmd("hlx_sm_psay2", hlx_sm_psay2); + RegServerCmd("hlx_sm_bulkpsay", hlx_sm_psay); + RegServerCmd("hlx_sm_csay", hlx_sm_csay); + RegServerCmd("hlx_sm_msay", hlx_sm_msay); + RegServerCmd("hlx_sm_tsay", hlx_sm_tsay); + RegServerCmd("hlx_sm_hint", hlx_sm_hint); + RegServerCmd("hlx_sm_browse", hlx_sm_browse); + RegServerCmd("hlx_sm_swap", hlx_sm_swap); + RegServerCmd("hlx_sm_redirect", hlx_sm_redirect); + RegServerCmd("hlx_sm_player_action", hlx_sm_player_action); + RegServerCmd("hlx_sm_team_action", hlx_sm_team_action); + RegServerCmd("hlx_sm_world_action", hlx_sm_world_action); + + if (gamemod == Game_INSMOD) + { + AddCommandListener(hlx_block_commands, "say2"); + } + else if (gamemod == Game_ND) + { + AddCommandListener(hlx_block_commands, "say_squad"); + } + + AddCommandListener(hlx_block_commands, "say"); + AddCommandListener(hlx_block_commands, "say_team"); + + switch (gamemod) + { + case Game_CSS, Game_L4D, Game_TF, Game_HL2MP, Game_AOC, Game_FOF, Game_PVKII, Game_ND, Game_DDD, Game_CSGO: + { + g_bTrackColors4Chat = true; + HookEvent("player_team", HLstatsX_Event_PlyTeamChange, EventHookMode_Post); + } + } + + switch (gamemod) + { + case Game_L4D, Game_INSMOD, Game_GES, Game_CSGO: + { + g_bGameCanDoMotd = false; + } + } + + if (gamemod == Game_HL2MP) + { + g_cvarTeamPlay = FindConVar("mp_teamplay"); + if (g_cvarTeamPlay != INVALID_HANDLE) + { + g_bTeamPlay = GetConVarBool(g_cvarTeamPlay); + HookConVarChange(g_cvarTeamPlay, OnTeamPlayChange); + } + } + + CreateConVar("hlxce_plugin_version", VERSION, "HLstatsX:CE Ingame Plugin", FCVAR_NOTIFY); + CreateConVar("hlxce_version", "", "HLstatsX:CE", FCVAR_NOTIFY); + CreateConVar("hlxce_webpage", "http://www.hlxcommunity.com", "http://www.hlxcommunity.com", FCVAR_NOTIFY); + + hlx_block_chat_commands = CreateConVar("hlx_block_commands", "1", "If activated HLstatsX commands are blocked from the chat area", 0); + hlx_message_prefix = CreateConVar("hlx_message_prefix", "", "Define the prefix displayed on every HLstatsX ingame message", 0); + hlx_protect_address = CreateConVar("hlx_protect_address", "", "Address to be protected for logging/forwarding", 0); + hlx_server_tag = CreateConVar("hlx_server_tag", "1", "If enabled, adds \"HLstatsX:CE\" to server tags on supported games. 1 = Enabled (default), 0 = Disabled", + 0, true, 0.0, true, 1.0); + + g_hCustomTags = CreateArray(SVTAGSIZE); + sv_tags = FindConVar("sv_tags"); + g_iSDKVersion = GuessSDKVersion(); + + if (g_bLateLoad) + { + GetConVarString(hlx_message_prefix, message_prefix, sizeof(message_prefix)); + decl String:protaddr[24]; + GetConVarString(hlx_protect_address, protaddr, sizeof(protaddr)); + OnProtectAddressChange(hlx_protect_address, "", protaddr); + } + + MyAddServerTag(HLXTAG); + + HookConVarChange(hlx_message_prefix, OnMessagePrefixChange); + HookConVarChange(hlx_protect_address, OnProtectAddressChange); + HookConVarChange(hlx_server_tag, OnServerTagChange); + if (sv_tags != INVALID_HANDLE) + { + HookConVarChange(sv_tags, OnSVTagsChange); + } + + RegServerCmd("log", ProtectLoggingChange); + RegServerCmd("logaddress_del", ProtectForwardingChange); + RegServerCmd("logaddress_delall", ProtectForwardingDelallChange); + RegServerCmd("hlx_message_prefix_clear", MessagePrefixClear); + + PlayerColorArray = CreateArray(); + message_recipients = CreateStack(); + + GetTeams(gamemod == Game_INSMOD); +} + + +public OnAllPluginsLoaded() +{ + if (LibraryExists("clientprefs")) + { + SetCookieMenuItem(HLXSettingsMenu, 0, "HLstatsX:CE Settings"); + } +} + +public HLXSettingsMenu(client, CookieMenuAction:action, any:info, String:buffer[], maxlen) +{ + if (action == CookieMenuAction_SelectOption) + { + DisplayMenu(HLstatsXMenuMain, client, MENU_TIME_FOREVER); + } +} + + +public OnMapStart() +{ + GetTeams(gamemod == Game_INSMOD); + + if (g_bTrackColors4Chat) + { + find_player_team_slot(2); + find_player_team_slot(3); + if (gamemod == Game_PVKII) + { + find_player_team_slot(4); + } + } +} + +bool:BTagsSupported() +{ + return (sv_tags != INVALID_HANDLE && (g_iSDKVersion == SOURCE_SDK_EPISODE2 || g_iSDKVersion == SOURCE_SDK_EPISODE2VALVE || gamemod == Game_ND)); +} + +stock MyAddServerTag(const String:tag[]) +{ + if (!BTagsSupported()) + { + // game doesn't support sv_tags + return; + } + + if (!GetConVarBool(hlx_server_tag)) + { + return; + } + + if (FindStringInArray(g_hCustomTags, tag) == -1) + { + PushArrayString(g_hCustomTags, tag); + } + + decl String:current_tags[SVTAGSIZE]; + GetConVarString(sv_tags, current_tags, sizeof(current_tags)); + if (StrContains(current_tags, tag) > -1) + { + // already have tag + return; + } + + decl String:new_tags[SVTAGSIZE]; + Format(new_tags, sizeof(new_tags), "%s%s%s", current_tags, (current_tags[0]!=0)?",":"", tag); + + new flags = GetConVarFlags(sv_tags); + SetConVarFlags(sv_tags, flags & ~FCVAR_NOTIFY); + g_bIgnoreNextTagChange = true; + SetConVarString(sv_tags, new_tags); + g_bIgnoreNextTagChange = false; + SetConVarFlags(sv_tags, flags); +} + +stock MyRemoveServerTag(const String:tag[]) +{ + if (!BTagsSupported()) + { + // game doesn't support sv_tags + return; + } + + new idx = FindStringInArray(g_hCustomTags, tag); + if (idx > -1) + { + RemoveFromArray(g_hCustomTags, idx); + } + + decl String:current_tags[SVTAGSIZE]; + GetConVarString(sv_tags, current_tags, sizeof(current_tags)); + if (StrContains(current_tags, tag) == -1) + { + // tag isn't on here, just bug out + return; + } + + ReplaceString(current_tags, sizeof(current_tags), tag, ""); + ReplaceString(current_tags, sizeof(current_tags), ",,", ""); + + new flags = GetConVarFlags(sv_tags); + SetConVarFlags(sv_tags, flags & ~FCVAR_NOTIFY); + g_bIgnoreNextTagChange = true; + SetConVarString(sv_tags, current_tags); + g_bIgnoreNextTagChange = false; + SetConVarFlags(sv_tags, flags); +} + +get_server_mod() +{ + new String: game_description[64]; + GetGameDescription(game_description, sizeof(game_description), true); + + if (StrContains(game_description, "Counter-Strike: Source", false) != -1) + { + gamemod = Game_CSS; + } + if (StrContains(game_description, "Counter-Strike: Global Offensive", false) != -1) + { + gamemod = Game_CSGO; + } + else if (StrContains(game_description, "Day of Defeat", false) != -1) + { + gamemod = Game_DODS; + } + else if (StrContains(game_description, "Half-Life 2 Deathmatch", false) != -1) + { + gamemod = Game_HL2MP; + } + else if (StrContains(game_description, "Team Fortress", false) != -1) + { + gamemod = Game_TF; + } + else if (StrContains(game_description, "L4D", false) != -1 || StrContains(game_description, "Left 4 D", false) != -1) + { + gamemod = Game_L4D; + } + else if (StrContains(game_description, "Insurgency", false) != -1) + { + gamemod = Game_INSMOD; + //psychonic - added detection for more supported games + } + else if (StrContains(game_description, "Fortress Forever", false) != -1) + { + gamemod = Game_FF; + } + else if (StrContains(game_description, "ZPS", false) != -1) + { + gamemod = Game_ZPS; + } + else if (StrContains(game_description, "Age of Chivalry", false) != -1) + { + gamemod = Game_AOC; + } + else if (StrContains(game_description, "PVKII", false) != -1) + { + gamemod = Game_PVKII; + } + else if (StrContains(game_description, "CSPromod", false) != -1) + { + gamemod = Game_CSP; + } + else if (StrContains(game_description, "Nuclear Dawn", false) != -1) + { + gamemod = Game_ND; + } + + // game mod could not detected, try further + if (gamemod == Game_Unknown) + { + new String: game_folder[64]; + GetGameFolderName(game_folder, sizeof(game_folder)); + if (StrContains(game_folder, "cstrike", false) != -1) + { + gamemod = Game_CSS; + } + else if (strncmp(game_folder, "dod", 3, false) == 0) + { + gamemod = Game_DODS; + } + else if (StrContains(game_folder, "hl2mp", false) != -1 || StrContains(game_folder, "hl2ctf", false) != -1) + { + gamemod = Game_HL2MP; + } + else if (StrContains(game_folder, "fistful_of_frags", false) != -1) + { + gamemod = Game_FOF; + } + else if (strncmp(game_folder, "tf", 2, false) == 0) + { + gamemod = Game_TF; + } + else if (StrContains(game_folder, "left4dead", false) != -1) + { + gamemod = Game_L4D; + } + else if (StrContains(game_folder, "insurgency", false) != -1) + { + gamemod = Game_INSMOD; + //psychonic - added detection for more supported games + } + else if (StrContains(game_folder, "FortressForever", false) != -1) + { + gamemod = Game_FF; + } + else if (StrContains(game_folder, "zps", false) != -1) + { + gamemod = Game_ZPS; + } + else if (StrContains(game_folder, "ageofchivalry", false) != -1) + { + gamemod = Game_AOC; + } + else if (StrContains(game_folder, "gesource", false) != -1) + { + gamemod = Game_GES; + } + else if (StrContains(game_folder, "pvkii", false) != -1) + { + gamemod = Game_PVKII; + } + else if (StrContains(game_folder, "cspromod", false) != -1) + { + gamemod = Game_CSP; + } + else if (StrContains(game_folder, "nucleardawn", false) != -1) + { + gamemod = Game_ND; + } + else if (StrContains(game_folder, "dinodday", false) != -1) + { + gamemod = Game_DDD; + } + else if (StrContains(game_folder, "csgo", false) != -1) + { + gamemod = Game_CSGO; + } + else + { + LogToGame("HLX:CE Mod Not In Detected List, Using Defaults (%s, %s)", game_description, game_folder); + LogToGame("HLX:CE If this is incorrect, please file a bug at hlxcommunity.com"); + } + } + if (gamemod > Game_Unknown) + { + LogToGame("HLX:CE Mod Detection: %s", modnamelist[_:gamemod]); + LogToGame("HLX:CE If this is incorrect, please file a bug at hlxcommunity.com"); + } +} + +public OnClientPostAdminCheck(client) +{ + if (g_bGameCanDoMotd && !IsFakeClient(client)) + { + QueryClientConVar(client, "cl_disablehtmlmotd", motdQuery); + } +} + + +public motdQuery(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[]) +{ + if (result == ConVarQuery_Okay && StringToInt(cvarValue) == 0 || result != ConVarQuery_Okay) + { + g_bPlyrCanDoMotd[client] = true; + } +} + + +public OnServerTagChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + if (GetConVarBool(hlx_server_tag)) + { + MyAddServerTag(HLXTAG); + } + else + { + MyRemoveServerTag(HLXTAG); + } +} + +public OnSVTagsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + if (g_bIgnoreNextTagChange) + { + // we fired this callback, no need to reapply tags + return; + } + + // reapply each custom tag + new cnt = GetArraySize(g_hCustomTags); + for (new i = 0; i < cnt; i++) + { + decl String:tag[SVTAGSIZE]; + GetArrayString(g_hCustomTags, i, tag, sizeof(tag)); + MyAddServerTag(tag); + } +} + + +public OnProtectAddressChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + if (newVal[0] > 0) + { + decl String: log_command[192]; + Format(log_command, sizeof(log_command), "logaddress_add %s", newVal); + LogToGame("Command: %s", log_command); + ServerCommand(log_command); + } +} + +public OnTeamPlayChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + g_bTeamPlay = GetConVarBool(g_cvarTeamPlay); +} + +public Action:ProtectLoggingChange(args) +{ + if (hlx_protect_address != INVALID_HANDLE) + { + decl String: protect_address[192]; + GetConVarString(hlx_protect_address, protect_address, sizeof(protect_address)); + if (strcmp(protect_address, "") != 0) + { + if (args >= 1) + { + decl String: log_action[192]; + GetCmdArg(1, log_action, sizeof(log_action)); + if ((strcmp(log_action, "off") == 0) || (strcmp(log_action, "0") == 0)) + { + LogToGame("HLstatsX address protection active, logging reenabled!"); + ServerCommand("log 1"); + } + } + } + } + return Plugin_Continue; +} + + +public Action:ProtectForwardingChange(args) +{ + if (hlx_protect_address != INVALID_HANDLE) + { + decl String: protect_address[192]; + GetConVarString(hlx_protect_address, protect_address, sizeof(protect_address)); + if (strcmp(protect_address, "") != 0) + { + if (args == 1) + { + decl String: log_action[192]; + GetCmdArg(1, log_action, sizeof(log_action)); + if (strcmp(log_action, protect_address) == 0) + { + decl String: log_command[192]; + Format(log_command, sizeof(log_command), "logaddress_add %s", protect_address); + LogToGame("HLstatsX address protection active, logaddress readded!"); + ServerCommand(log_command); + } + } + else if (args > 1) + { + new String: log_action[192]; + for(new i = 1; i <= args; i++) + { + decl String: temp_argument[192]; + GetCmdArg(i, temp_argument, sizeof(temp_argument)); + strcopy(log_action[strlen(log_action)], sizeof(log_action), temp_argument); + } + if (strcmp(log_action, protect_address) == 0) + { + decl String: log_command[192]; + Format(log_command, sizeof(log_command), "logaddress_add %s", protect_address); + LogToGame("HLstatsX address protection active, logaddress readded!"); + ServerCommand(log_command); + } + + } + } + } + return Plugin_Continue; +} + + + +public Action:ProtectForwardingDelallChange(args) +{ + if (hlx_protect_address != INVALID_HANDLE) + { + decl String: protect_address[192]; + GetConVarString(hlx_protect_address, protect_address, sizeof(protect_address)); + if (strcmp(protect_address, "") != 0) + { + decl String: log_command[192]; + Format(log_command, sizeof(log_command), "logaddress_add %s", protect_address); + LogToGame("HLstatsX address protection active, logaddress readded!"); + ServerCommand(log_command); + } + } + return Plugin_Continue; +} + + +public OnMessagePrefixChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + strcopy(message_prefix, sizeof(message_prefix), newVal); +} + + +public Action:MessagePrefixClear(args) +{ + message_prefix = ""; +} + + +find_player_team_slot(team_index) +{ + if (team_index > -1) + { + ColorSlotArray[team_index] = -1; + for(new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && GetClientTeam(i) == team_index) + { + ColorSlotArray[team_index] = i; + break; + } + } + } +} + + +stock validate_team_colors() +{ + for (new i = 0; i < sizeof(ColorSlotArray); i++) + { + new color_client = ColorSlotArray[i]; + if (color_client > 0) + { + if (IsClientInGame(color_client) && GetClientTeam(color_client) != color_client) + { + find_player_team_slot(i); + } + } + else + { + if (i == 2 || i == 3 || (i == 4 && gamemod == Game_PVKII)) + { + find_player_team_slot(i); + } + } + } +} + +public OnClientDisconnect(client) +{ + if (g_bTrackColors4Chat && client > 0 && IsClientInGame(client)) + { + new team_index = GetClientTeam(client); + if (client == ColorSlotArray[team_index]) + { + ColorSlotArray[team_index] = -1; + } + } + + g_bPlyrCanDoMotd[client] = false; +} + +color_player(color_type, player_index, String: client_message[192]) +{ + new color_player_index = -1; + if (g_bTrackColors4Chat || (gamemod == Game_DODS) || (gamemod == Game_ZPS) || (gamemod == Game_GES) || (gamemod == Game_CSP)) + { + decl String: client_name[192]; + GetClientName(player_index, client_name, sizeof(client_name)); + if ((strcmp(client_message, "") != 0) && (strcmp(client_name, "") != 0)) + { + if (color_type == 1) + { + decl String: search_client_name[192]; + Format(search_client_name, sizeof(search_client_name), "%s ", client_name); + decl String: colored_player_name[192]; + switch (gamemod) + { + case Game_DODS, Game_GES, Game_CSP: + Format(colored_player_name, sizeof(colored_player_name), "\x04%s\x01 ", client_name); + case Game_HL2MP: + Format(colored_player_name, sizeof(colored_player_name), "%c%s\x01 ", g_bTeamPlay?3:4, client_name); + case Game_ZPS: + Format(colored_player_name, sizeof(colored_player_name), "\x05%s\x01 ", client_name); + default: + Format(colored_player_name, sizeof(colored_player_name), "\x03%s\x01 ", client_name); + } + if (ReplaceString(client_message, sizeof(client_message), search_client_name, colored_player_name) > 0) + { + return player_index; + } + } + else + { + decl String: search_client_name[192]; + Format(search_client_name, sizeof(search_client_name), " %s ", client_name); + decl String: colored_player_name[192]; + switch (gamemod) + { + case Game_ZPS: + Format(colored_player_name, sizeof(colored_player_name), " \x05%s\x01 ", client_name); + case Game_GES: + Format(colored_player_name, sizeof(colored_player_name), " \x05%s\x01 ", client_name); + default: + Format(colored_player_name, sizeof(colored_player_name), " \x04%s\x01 ", client_name); + } + ReplaceString(client_message, sizeof(client_message), search_client_name, colored_player_name); + } + } + } + else if (gamemod == Game_FF) + { + decl String: client_name[192]; + GetClientName(player_index, client_name, sizeof(client_name)); + + new team = GetClientTeam(player_index); + if (team > 1 && team < 6) + { + decl String: colored_player_name[192]; + Format(colored_player_name, sizeof(colored_player_name), "^%d%s^0", (team-1), client_name); + if (ReplaceString(client_message, sizeof(client_message), client_name, colored_player_name) > 0) + { + return player_index; + } + } + } + return color_player_index; +} + + + +color_all_players(String: message[192]) +{ + new color_index = -1; + if ((g_bTrackColors4Chat || (gamemod == Game_DODS) || (gamemod == Game_ZPS) || (gamemod == Game_FF) || (gamemod == Game_GES) || (gamemod == Game_CSP)) && (PlayerColorArray != INVALID_HANDLE)) + { + if (strcmp(message, "") != 0) + { + ClearArray(PlayerColorArray); + + new lowest_matching_pos = 192; + new lowest_matching_pos_client = -1; + + for(new i = 1; i <= MaxClients; i++) + { + new client = i; + if (IsClientInGame(client)) + { + decl String: client_name[32]; + GetClientName(client, client_name, sizeof(client_name)); + + if (strcmp(client_name, "") != 0) + { + new message_pos = StrContains(message, client_name); + if (message_pos > -1) + { + if (lowest_matching_pos > message_pos) + { + lowest_matching_pos = message_pos; + lowest_matching_pos_client = client; + } + new TempPlayerColorArray[1]; + TempPlayerColorArray[0] = client; + PushArrayArray(PlayerColorArray, TempPlayerColorArray); + } + } + } + } + new size = GetArraySize(PlayerColorArray); + for (new i = 0; i < size; i++) + { + new temp_player_array[1]; + GetArrayArray(PlayerColorArray, i, temp_player_array); + new temp_client = temp_player_array[0]; + if (temp_client == lowest_matching_pos_client) + { + new temp_color_index = color_player(1, temp_client, message); + color_index = temp_color_index; + } + else + { + color_player(0, temp_client, message); + } + } + ClearArray(PlayerColorArray); + } + } + + return color_index; +} + + + +color_team_entities(String:message[192]) +{ + switch(gamemod) + { + case Game_CSS, Game_CSGO: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "TERRORIST ", "\x03TERRORIST\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "CT ", "\x03CT\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_L4D: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "Survivors ", "\x03Survivors\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "Infected ", "\x03Infected\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_TF: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "Red ", "\x03Red\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "Blue ", "\x03Blue\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_FF: + { + if (strcmp(message, "") != 0) + { + if (ReplaceString(message, sizeof(message), "Red Team", "^2Red Team^0") > 0) + { + return 0; + } + if (ReplaceString(message, sizeof(message), "Blue Team", "^1Blue Team^0") > 0) + { + return 0; + } + if (ReplaceString(message, sizeof(message), "Yellow Team", "^3Yellow Team^0") > 0) + { + return 0; + } + if (ReplaceString(message, sizeof(message), "Green Team", "^4Green Team^0") > 0) + { + return 0; + } + } + } + case Game_AOC: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "Agathia Knights ", "\x03Agathia Knights\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "The Mason Order ", "\x03The Mason Order\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_FOF: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "Desperados ", "\x03Desperados\x01 ") > 0 + || ReplaceString(message, sizeof(message), "Desparados ", "\x03Desperados\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "Vigilantes ", "\x03Vigilantes\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_HL2MP: + { + if (g_bTeamPlay && strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "The Combine ", "\x03The Combine\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "Rebel Forces ", "\x03Rebel Forces\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_PVKII: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "Pirates ", "\x03Pirates\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "Vikings ", "\x03Vikings\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + if (ColorSlotArray[4] > -1) + { + if (ReplaceString(message, sizeof(message), "Knights ", "\x03Knights\x01 ") > 0) + { + return ColorSlotArray[4]; + } + } + } + } + case Game_ND: + { + if (strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "EMPIRE ", "\x03Empire\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "CONSORTIUM ", "\x03Consortium\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + case Game_DDD: + { + if (g_bTeamPlay && strcmp(message, "") != 0) + { + if (ColorSlotArray[2] > -1) + { + if (ReplaceString(message, sizeof(message), "Allies ", "\x03Allies\x01 ") > 0) + { + return ColorSlotArray[2]; + } + } + if (ColorSlotArray[3] > -1) + { + if (ReplaceString(message, sizeof(message), "Axis ", "\x03Axis\x01 ") > 0) + { + return ColorSlotArray[3]; + } + } + } + } + } + + return -1; +} + + +display_menu(player_index, time, String: full_message[1024], need_handler = 0) +{ + ReplaceString(full_message, sizeof(full_message), "\\n", "\10"); + if (need_handler == 0) + { + InternalShowMenu(player_index, full_message, time); + } + else + { + InternalShowMenu(player_index, full_message, time, (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9), InternalMenuHandler); + } +} + + +public InternalMenuHandler(Handle:menu, MenuAction:action, param1, param2) +{ + new client = param1; + if (IsClientInGame(client)) + { + if (action == MenuAction_Select) + { + decl String: player_event[192]; + IntToString(param2, player_event, sizeof(player_event)); + LogPlayerEvent(client, "selected", player_event); + } + else if (action == MenuAction_Cancel) + { + LogPlayerEvent(client, "selected", "cancel"); + } + } +} + + +public Action:hlx_sm_psay(args) +{ + if (args < 2) + { + PrintToServer("Usage: hlx_sm_psay - sends private message"); + return Plugin_Handled; + } + + decl String: client_list[192]; + GetCmdArg(1, client_list, sizeof(client_list)); + BuildClientList(client_list); + + decl String: colored_param[32]; + GetCmdArg(2, colored_param, sizeof(colored_param)); + new is_colored = 0; + new ignore_param = 0; + + if (strcmp(colored_param, "1") == 0) + { + is_colored = 1; + ignore_param = 1; + } + else if (strcmp(colored_param, "2") == 0) + { + is_colored = 2; + ignore_param = 1; + } + else if (strcmp(colored_param, "0") == 0) + { + ignore_param = 1; + } + + new String: client_message[192]; + GetCmdArg((ignore_param + 2), client_message, sizeof(client_message)); + + if (IsStackEmpty(message_recipients)) + { + return Plugin_Handled; + } + + new color_index = -1; + decl String: display_message[192]; + + switch (gamemod) + { + case Game_CSS, Game_DODS, Game_L4D, Game_TF, Game_HL2MP, Game_ZPS, Game_AOC, Game_FOF, Game_GES, Game_PVKII, Game_CSP, Game_ND, Game_DDD, Game_CSGO: + { + if (is_colored > 0) + { + if (is_colored == 1) + { + new player_color_index = color_all_players(client_message); + if (player_color_index > -1) + { + color_index = player_color_index; + } + else + { + if (g_bTrackColors4Chat) + { + validate_team_colors(); + } + color_index = color_team_entities(client_message); + } + } + } + if (strcmp(message_prefix, "") == 0) + { + Format(display_message, sizeof(display_message), "\x01\x0B\x01%s", client_message); + } + else + { + Format(display_message, sizeof(display_message), "\x01\x0B%c%s\x01 %s", ((gamemod == Game_ZPS || gamemod == Game_GES)?5:4), message_prefix, client_message); + } + + new bool: setupColorForRecipients = false; + if (color_index == -1) + { + setupColorForRecipients = true; + } + + if (g_bTrackColors4Chat && is_colored != 2) + { + while (IsStackEmpty(message_recipients) == false) + { + new recipient_client = -1; + PopStackCell(message_recipients, recipient_client); + + new player_index = GetClientOfUserId(recipient_client); + if (player_index > 0 && !IsFakeClient(player_index) && IsClientInGame(player_index)) + { + if (setupColorForRecipients == true) + { + color_index = player_index; + } + new Handle:hBf; + hBf = StartMessageOne("SayText2", player_index, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + + if (hBf != INVALID_HANDLE) + { + if(GetUserMessageType() == UM_Protobuf) + { + PbSetInt(hBf, "ent_idx", 0); + PbSetBool(hBf, "chat", false); + PbSetString(hBf, "msg_name", display_message); + PbAddString(hBf, "params", ""); + PbAddString(hBf, "params", ""); + PbAddString(hBf, "params", ""); + PbAddString(hBf, "params", ""); + } + else + { + BfWriteByte(hBf, color_index); + BfWriteByte(hBf, 0); + + BfWriteString(hBf, display_message); + } + + EndMessage(); + } + } + } + } + else + { + PrintToChatRecipients(display_message); + } + } + case Game_FF: + { + // thanks to hlstriker for help with this + + decl String: client_message_backup[192]; + strcopy(client_message_backup, sizeof(client_message_backup), client_message); + + if (is_colored == 1) + { + color_index = color_all_players(client_message); + if (color_index == -1) + { + color_team_entities(client_message); + } + } + + if (strcmp(message_prefix, "") == 0) + { + Format(display_message, sizeof(display_message), "Console: %s%s\n", ((is_colored == 2)?"^4":""), client_message); + } + else + { + Format(display_message, sizeof(display_message), "Console: ^4%s:%s %s\n", message_prefix, ((is_colored == 2)?"":"^"), client_message); + } + + PrintToChatRecipientsFF(display_message); + } + default: + { + if (strcmp(message_prefix, "") != 0) + { + Format(display_message, sizeof(display_message), "%s %s", message_prefix, client_message); + PrintToChatRecipients(display_message); + return Plugin_Handled; + } + PrintToChatRecipients(client_message); + } + } + return Plugin_Handled; +} + + +public Action:hlx_sm_psay2(args) +{ + if (args < 2) + { + PrintToServer("Usage: hlx_sm_psay2 - sends green colored private message"); + return Plugin_Handled; + } + + decl String: client_list[192]; + GetCmdArg(1, client_list, sizeof(client_list)); + BuildClientList(client_list); + + decl String: colored_param[32]; + GetCmdArg(2, colored_param, sizeof(colored_param)); + + new ignore_param = 0; + if (strcmp(colored_param, "2") == 0 || strcmp(colored_param, "1") == 0 || strcmp(colored_param, "0") == 0) + { + ignore_param = 1; + } + + new String: client_message[192]; + GetCmdArg((ignore_param + 2), client_message, sizeof(client_message)); + + if (IsStackEmpty(message_recipients)) { + return Plugin_Handled; + } + + // Strip color control codes + decl String:buffer_message[192]; + new j = 0; + for (new i = 0; i < sizeof(client_message); i++) + { + new c = client_message[i]; + if (c < 5 && c > 0) + { + continue; + } + buffer_message[j] = client_message[i]; + if (c == 0) + { + break; + } + j++; + } + + switch(gamemod) + { + case Game_INSMOD: + { + new prefix = 0; + if (strcmp(message_prefix, "") != 0) + { + prefix = 1; + Format(client_message, sizeof(client_message), "%s: %s", message_prefix, buffer_message); + } + + while (IsStackEmpty(message_recipients) == false) + { + new recipient_client = -1; + PopStackCell(message_recipients, recipient_client); + + new player_index = GetClientOfUserId(recipient_client); + if (player_index > 0 && !IsFakeClient(player_index) && IsClientInGame(player_index)) + { + // thanks to Fyren and IceMatrix for help with this + new Handle:hBf; + hBf = StartMessageOne("SayText", player_index, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + if (hBf != INVALID_HANDLE) + { + if(GetUserMessageType() == UM_Protobuf) + { + PbSetInt(hBf, "ent_idx", player_index); + PbSetBool(hBf, "chat", true); + + if (prefix == 0) + { + PbSetString(hBf, "text", buffer_message); + } + else + { + PbSetString(hBf, "text", client_message); + } + } + else + { + BfWriteByte(hBf, 1); + BfWriteBool(hBf, true); + BfWriteByte(hBf, player_index); + + if (prefix == 0) + { + BfWriteString(hBf, buffer_message); + } + else + { + BfWriteString(hBf, client_message); + } + } + + EndMessage(); + } + } + } + } + case Game_FF: + { + if (strcmp(message_prefix, "") == 0) + { + Format(client_message, sizeof(client_message), "Console: \x02^4%s\n", buffer_message); + } + else + { + Format(client_message, sizeof(client_message), "Console: \x02^4%s: %s\n", message_prefix, buffer_message); + } + + PrintToChatRecipientsFF(client_message); + } + case Game_ZPS, Game_GES: + { + if (strcmp(message_prefix, "") == 0) + { + Format(client_message, sizeof(client_message), "\x05%s", buffer_message); + } + else + { + Format(client_message, sizeof(client_message), "\x05%s %s", message_prefix, buffer_message); + } + PrintToChatRecipients(client_message); + } + default: + { + if (strcmp(message_prefix, "") == 0) + { + Format(client_message, sizeof(client_message), "\x04%s", buffer_message); + } + else + { + Format(client_message, sizeof(client_message), "\x04%s %s", message_prefix, buffer_message); + } + PrintToChatRecipients(client_message); + } + } + return Plugin_Handled; +} + + +public Action:hlx_sm_csay(args) +{ + if (args < 1) + { + PrintToServer("Usage: hlx_sm_csay - display center message"); + return Plugin_Handled; + } + + new String: display_message[192]; + GetCmdArg(1, display_message, sizeof(display_message)); + + if (strcmp(display_message, "") != 0) + { + if (gamemod == Game_L4D) + { + PrintToChatAll("\x03%s", display_message); + } + else + { + PrintCenterTextAll("%s", display_message); + } + } + + return Plugin_Handled; +} + + +public Action:hlx_sm_msay(args) +{ + if (args < 3) + { + PrintToServer("Usage: hlx_sm_msay