#include <sourcemod>
#include <sdktools>
#include <cstrike>

#pragma semicolon 1
#pragma newdecls required

#define NUMBEROFNAMES 116
char g_cName[NUMBEROFNAMES][] = {"Dwelitram", "Gwyri", "Caredus", "Arerawia", "Vilali", "Astiwiel", "Vardonydd", "Ybaossa", "Umyk", "Nico50Pax", "Onand", "Thelian", "Nydaleth", "Chomarin", "Traedien", "Miev", "Kaaede", "Koamond", "TheRottenBenson", "BigLegend2017", "TRIGGEREDHarambexXx", "InPepe2016", "xXxMaster2012", "InBoixXx", "TheKopsing", "Cornelius", "Gustavo", "Bryant", "Winfred", "Nicolas", "Mitchel", "Dana", "Carrol", "Darell", "Ruben", "Jeromy", "Wade", "Scotty", "Salvatore", "Kory", "Don", "Morgan", "Kurtis", "Federico", "Darin", "css-ru", "aimbot", "lastkraftwagenfahrzeug", "edger", "clownface", "слово", "счастливый", "kara", "puta", "meow", "uncle sam", "FunBun", "Counter-Strike.Com.Ua", "For-css.Ru", "BOBO", "", ".", "Z3r0", "ZeTo", "Sakharov", "Sache11", "Mr. Dogenberg", "Maus", "Magikarpet", "Miles", "magick", "James Lebron", "jiZZ", "Dobel", "THE POPE OF DOPE", "The Joker", "that guy over there", "Be happy :)", "KingKong", "Figger Nucker", "noname", "alexdu63", "I put babies in the microwave", "Tango!!", "faggot killer", "admin pidaras", "MESSI>RONALDO", "Gabe Newell", "acetylsalicylic acid", "Country-Steak: Sauce", "chlamydia harvester", "kyle_69", "Shaggy's dog", "rEVERSE", "Piment d'espelette", "FireHawk", "Sgt. Pepper", "krix.", "imPulse_^_", "Lardon", "CAPS LOCK", "$pussymoneyweed$", "Low5", "Bonerfart", "Shiny Thanos", "MARK NUTT", "Legless Runner", "Banana is a berry", "Gabriel", "El Moustachio", "James Bong", "Disco Fever", "Acacia", "Sugared", "ronald goddamn macdonald", "pizza is life"};

bool g_bFakePopulation[MAXPLAYERS + 1];
bool g_bMapEnded;

int g_iBaseLatency[MAXPLAYERS + 1];
int g_iLatency[MAXPLAYERS + 1];

int g_iAdminFakes;

bool g_bCheckRequested;

int g_iBlockInstantFakeConnects;
bool g_bBlockInstantFakeConnects;

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name        = "ImprovedHitboxes", //camouflage
	author      = "Neon + Dogan + Botox",
	description = "Handle Hitboxes via Plugin",
	version     = "5.1.0",
	url         = "https://steamcommunity.com/id/n3ontm"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	RegAdminCmd("sm_debugfakes", Command_DebugFakes, ADMFLAG_GENERIC, "Shows the amount of fake-clients on server");
	RegAdminCmd("sm_fakes", Command_Fakes, ADMFLAG_GENERIC, "Shows the fake-clients on server");
	RegAdminCmd("sm_setfakes", Command_SetFakes, ADMFLAG_RCON, "Manually sets the amount of fake-clients");

	g_iAdminFakes = -1;
	g_bMapEnded = false;
	g_bBlockInstantFakeConnects = false;
	g_iBlockInstantFakeConnects = 0;
	CreateTimer(4.0, BlockInstantFakeConnects, _, TIMER_REPEAT);
	CreateTimer(3.0, RandomizePing, _, TIMER_REPEAT);
	CreateTimer(150.0, RandomizeNames, _, TIMER_REPEAT);

	HookUserMessage(GetUserMessageId("SayText2"), UserMessage_SayText2, true);

	RequestFrame(CheckPopulation);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginEnd()
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(g_bFakePopulation[i])
		{
			g_bFakePopulation[i] = false;
			g_iLatency[i] = 0;
			KickClient(i);
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnMapStart()
{
	g_bMapEnded = false;

	g_bBlockInstantFakeConnects = false;
	g_iBlockInstantFakeConnects = 0;
	CreateTimer(4.0, BlockInstantFakeConnects, _, TIMER_REPEAT);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnMapEnd()
{
	g_bMapEnded = true;
	g_iAdminFakes = -1;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action BlockInstantFakeConnects(Handle timer)
{
	g_iBlockInstantFakeConnects++;
	g_bBlockInstantFakeConnects = false;
	RequestFrame(CheckPopulation);

	if(g_iBlockInstantFakeConnects > 4)
		return Plugin_Stop;

	return Plugin_Continue;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action RandomizePing(Handle timer)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(g_bFakePopulation[i])
			g_iLatency[i] = g_iBaseLatency[i] + GetRandomInt(-3, 3);
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action RandomizeNames(Handle timer)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(g_bFakePopulation[i])
		{
			int RandomName = GetRandomInt(0, NUMBEROFNAMES - 1);
			SetClientName(i, g_cName[RandomName]);
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action UserMessage_SayText2(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init)
{
	if(!reliable)
		return Plugin_Continue;

	int client;
	char sMessage[32];

	if(GetUserMessageType() == UM_Protobuf) //fuck cs go but "ClEaN CoDe"
	{
		PbReadString(msg, "msg_name", sMessage, sizeof(sMessage));

		if(!(sMessage[0] == '#' && StrContains(sMessage, "Name_Change")))
			return Plugin_Continue;

		client = PbReadInt(msg, "ent_idx");
	}
	else
	{
		client = BfReadByte(msg);
		BfReadByte(msg);
		BfReadString(msg, sMessage, sizeof(sMessage));

		if(!(sMessage[0] == '#' && StrContains(sMessage, "Name_Change")))
			return Plugin_Continue;
	}

	if(g_bFakePopulation[client])
		return Plugin_Handled;

	return Plugin_Continue;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action Command_DebugFakes(int client, int argc)
{
	int iFakes = 0;
	int iFakesInTeam = 0;

	for(int i = 1; i <= MaxClients; i++)
	{
		if (g_bFakePopulation[i])
			iFakes++;

		if (g_bFakePopulation[i] && GetClientTeam(i) > 0)
			iFakesInTeam++;
	}

	ReplyToCommand(client, "[SM] There are currently %d Fake-Clients, from which %d are in Spectate.", iFakes, iFakes - iFakesInTeam);

	return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action Command_SetFakes(int client, int argc)
{
	if(g_iBlockInstantFakeConnects <= 4)
	{
		ReplyToCommand(client, "[SM] Not available right now, because the Plugin is still loading. Try again in couple seconds.");
		return Plugin_Handled;
	}

	if (argc < 1)
	{
		ReplyToCommand(client, "[SM] Usage: sm_setfakes <amount of fakes>");
		return Plugin_Handled;
	}

	char sArgs[16];
	GetCmdArg(1, sArgs, sizeof(sArgs));

	if (!StringToIntEx(sArgs, g_iAdminFakes))
	{
		ReplyToCommand(client, "[SM] Invalid value");
		return Plugin_Handled;
	}

	ReplyToCommand(client, "[SM] You set the amount of Fake-Clients to %d.", g_iAdminFakes);
	CheckPopulation();
	return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action Command_Fakes(int client, int args)
{
	char aBuf[1024];
	char aBuf2[MAX_NAME_LENGTH];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i))
		{
			if(g_bFakePopulation[i])
			{
				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] Fake-Clients online: %s", aBuf);
	}
	else
		ReplyToCommand(client, "[SM] Fake-Clients online: none");

	return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientConnected(int client)
{
	if (!g_bCheckRequested && !IsFakeClient(client))
	{
		RequestFrame(CheckPopulation);
		g_bCheckRequested = true;
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientDisconnect(int client)
{
	if (client > 0)
	{
		if(g_bFakePopulation[client])
		{
			g_bFakePopulation[client] = false;
			g_iLatency[client] = 0;
		}

		if (!g_bCheckRequested && !IsFakeClient(client))
		{
			RequestFrame(CheckPopulation);
			g_bCheckRequested = true;
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void CheckPopulation()
{
	g_bCheckRequested = false;

	if(g_bMapEnded)
		return;

	int iPlayers = GetClientCount(false);

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientConnected(i) && IsFakeClient(i))
			iPlayers--;
	}

	int iFakes = 0;
	int iFakesInTeam = 0;

	for(int i = 1; i <= MaxClients; i++)
	{
		if (g_bFakePopulation[i])
			iFakes++;
	}

	int iFakesNeeded = 0;
	int iFakesInTeamNeeded = 0;

	if (iPlayers > 61)
	{
		iFakesNeeded = 0;
		iFakesInTeamNeeded = 0;
	}
	else if(iPlayers > 59)
	{
		iFakesNeeded = 1;
		iFakesInTeamNeeded = 0;
	}
	else if(iPlayers > 57)
	{
		iFakesNeeded = 2;
		iFakesInTeamNeeded = 1;
	}
	else if(iPlayers > 55)
	{
		iFakesNeeded = 3;
		iFakesInTeamNeeded = 1;
	}
	else if (iPlayers > 20)
	{
		iFakesNeeded = 4;
		iFakesInTeamNeeded = 2;
	}
	else if (iPlayers > 10)
	{
		iFakesNeeded = 4;
		iFakesInTeamNeeded = 1;
	}
	else if (iPlayers > 5)
	{
		iFakesNeeded = 4;
		iFakesInTeamNeeded = 0;
	}
	else
	{
		iFakesNeeded = 4;
		iFakesInTeamNeeded = 0;
	}

	if(g_iAdminFakes != -1)
		iFakesNeeded = g_iAdminFakes;

	if (iFakes != iFakesNeeded)
	{
		while (iFakes < iFakesNeeded && !g_bBlockInstantFakeConnects)
		{
			int RandomName = GetRandomInt(0, NUMBEROFNAMES - 1);
			int iIndex = CreateFakeClient(g_cName[RandomName]);

			if(iIndex < 1 || iIndex > MaxClients)
				return;

			SetEntityFlags(iIndex, FL_CLIENT);
			DispatchKeyValue(iIndex, "classname", "player");
			DispatchSpawn(iIndex);

			g_bFakePopulation[iIndex] = true;
			g_iBaseLatency[iIndex] = GetRandomInt(20, 110);
			g_iLatency[iIndex] = g_iBaseLatency[iIndex];

			AdminId FakeAdmin = CreateAdmin();
			SetAdminFlag(FakeAdmin, Admin_Custom6, true);
			SetUserAdmin(iIndex, FakeAdmin, true);
			//CS_SetClientClanTag(iIndex, "UNLOZE");
			iFakes++;

			if(g_iBlockInstantFakeConnects <= 4)
				g_bBlockInstantFakeConnects = true;
		}

		while (iFakes > iFakesNeeded)
		{
			for(int i = 1; i <= MaxClients; i++)
			{
				if(g_bFakePopulation[i])
				{
					g_bFakePopulation[i] = false;
					g_iLatency[i] = 0;
					KickClient(i);
					iFakes--;
					break;
				}
			}
		}
	}

	for(int i = 1; i <= MaxClients; i++)
	{
		if (g_bFakePopulation[i] && GetClientTeam(i) >= CS_TEAM_T)
			iFakesInTeam++;
	}

	if (iFakes == iFakesNeeded && iFakesInTeam != iFakesInTeamNeeded && g_iAdminFakes == -1)
	{
		while (iFakesInTeam < iFakesInTeamNeeded)
		{
			for(int i = 1; i <= MaxClients; i++)
			{
				if(g_bFakePopulation[i] && GetClientTeam(i) <= CS_TEAM_SPECTATOR)
				{
					ChangeClientTeam(i, CS_TEAM_CT);
					FakeClientCommandEx(i, "joinclass");
					iFakesInTeam++;
					break;
				}
			}
		}

		while (iFakesInTeam > iFakesInTeamNeeded)
		{
			for(int i = 1; i <= MaxClients; i++)
			{
				if(g_bFakePopulation[i] && GetClientTeam(i) >= CS_TEAM_T)
				{
					ChangeClientTeam(i, CS_TEAM_SPECTATOR);
					iFakesInTeam--;
					break;
				}
			}
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnGameFrame()
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(g_bFakePopulation[i])
		{
			int iResEnt = GetPlayerResourceEntity();

			if(iResEnt == -1)
				return;

			SetEntProp(iResEnt, Prop_Send, "m_iPing", g_iLatency[i], _, i);
		}
	}
}