#include <multicolors>
#include <sourcemod>
#include <sdkhooks>
#include <zombiereloaded>

/* BOOLS */
bool g_bHideEnabled;
bool g_bHidePlayers[MAXPLAYERS+1][MAXPLAYERS+1];

/* INTEGERS */
int g_iHideDistance[MAXPLAYERS+1];

/* CONVARS */
ConVar g_hCVar_HideEnabled;
ConVar g_hCVar_HideMinimumDistance;
ConVar g_hCVar_HideMaximumDistance;
ConVar g_hCVar_HideDefaultDistance;

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name         = "Hide Teammates",
	author       = "Neon",
	description  = "A plugin that can !hide teammates with individual distances",
	version      = "1.0.0",
	url 		= "https://steamcommunity.com/id/n3ontm"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{

	g_hCVar_HideEnabled         	= CreateConVar("sm_hide_enabled", "1", "", FCVAR_NONE, true, 0.0, true, 1.0);
	g_hCVar_HideMinimumDistance 	= CreateConVar("sm_hide_minimum_distance", "10", "", FCVAR_NONE, true, 1.0);
	g_hCVar_HideMaximumDistance 	= CreateConVar("sm_hide_maximum_distance", "50000", "", FCVAR_NONE, true, 1.0);
	g_hCVar_HideDefaultDistance 	= CreateConVar("sm_hide_default_distance", "10000", "", FCVAR_NONE, true, 1.0);
	g_bHideEnabled = g_hCVar_HideEnabled.BoolValue;
	g_hCVar_HideEnabled.AddChangeHook(OnConVarChanged);


	RegAdminCmd("sm_hide", Command_Hide, ADMFLAG_RCON);

	for(int client = 1; client <= MaxClients; client++)
	{
		if(IsClientInGame(client))
			OnClientPutInServer(client);
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnMapStart()
{
	CreateTimer(0.3, UpdateHide, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
	g_bHideEnabled = convar.BoolValue;

	for(int client = 1; client <= MaxClients; client++)
	{
		for(int target = 1; target <= MaxClients; target++)
			g_bHidePlayers[client][target] = false;

		if(IsClientInGame(client))
		{
			if(g_bHideEnabled)
				SDKHook(client, SDKHook_SetTransmit, Hook_SetTransmit);
			else
				SDKUnhook(client, SDKHook_SetTransmit, Hook_SetTransmit);
		}
	}

	if(g_bHideEnabled)
		CPrintToChatAll("{cyan}[Hide] {white}has been allowed.");
	else
		CPrintToChatAll("{cyan}[Hide] {white}has been disabled.");
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientPutInServer(int client)
{
	if(!g_bHideEnabled)
		return;

	SDKHook(client, SDKHook_SetTransmit, Hook_SetTransmit);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientDisconnect(int client)
{
	g_iHideDistance[client] = 0;
	for(int target = 1; target <= MaxClients; target++)
	{
		g_bHidePlayers[client][target] = false;
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action Command_Hide(int client, int args)
{
	if(!g_bHideEnabled)
	{
		ReplyToCommand(client, "[Hide] is currently not allowed.");
		return Plugin_Handled;
	}

	int iDistance;

	if(args == 0)
	{
		if(g_iHideDistance[client])
		{
			g_iHideDistance[client] = 0;
			ReplyToCommand(client, "[Hide] is now disabled.");
			return Plugin_Handled;
		}
		else
			iDistance = g_hCVar_HideDefaultDistance.IntValue;
	}
	else
	{
		char sArgs[8];
		GetCmdArg(1, sArgs, sizeof(sArgs));
		iDistance = StringToInt(sArgs);
	}

	if((iDistance == 0) || (iDistance < g_hCVar_HideMinimumDistance.IntValue) || (iDistance > g_hCVar_HideMaximumDistance.IntValue))
	{
		ReplyToCommand(client, "[Hide] Wrong input! Allowed range: %d-%d", g_hCVar_HideMinimumDistance.IntValue, g_hCVar_HideMaximumDistance.IntValue);
		return Plugin_Handled;
	}

	ReplyToCommand(client, "[Hide] Humans within range %d are now hidden.", iDistance);
	g_iHideDistance[client] = iDistance * iDistance;
	return Plugin_Handled;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action UpdateHide(Handle timer)
{
	if(!g_bHideEnabled)
		return Plugin_Continue;

	for(int client = 1; client <= MaxClients; client++)
	{
		if(!g_iHideDistance[client])
			continue;

		if(!IsClientInGame(client) || !IsPlayerAlive(client) || !ZR_IsClientHuman(client))
			continue;

		float fOriginClient[3];
		float fOriginTarget[3];

		for(int target = 1; target <= MaxClients; target++)
		{
			if(target != client && IsClientInGame(target) && IsPlayerAlive(target) && ZR_IsClientHuman(target))
			{
				GetClientAbsOrigin(target, fOriginTarget);
				GetClientAbsOrigin(client, fOriginClient);
				if(GetVectorDistance(fOriginTarget, fOriginClient, true) < float(g_iHideDistance[client]))
					g_bHidePlayers[client][target] = true;
				else
					g_bHidePlayers[client][target] = false;
			}
			else
				g_bHidePlayers[client][target] = false;
		}
	}
	return Plugin_Continue;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action Hook_SetTransmit(int target, int client)
{
	if(!g_bHideEnabled)
		return Plugin_Continue;

	if(g_bHidePlayers[client][target])
		return Plugin_Handled;

	return Plugin_Continue;
}