#include <sourcemod>
#include <sdktools>
#include <multicolors>
#include <zombiereloaded>
#include <loghelper>

#pragma semicolon 1
#pragma newdecls required

/* CONVARS */
ConVar g_hCVar_Delay;

/* BOOLS */
bool g_bClientKnifed[MAXPLAYERS+1];
bool g_bSupressDeath[MAXPLAYERS+1];

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name        = "UNLOZE Knife Madness",
	author      = "Neon",
	description = "UNLOZE Knife Madness",
	version     = "1.1",
	url         = "https://steamcommunity.com/id/n3ontm"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	g_hCVar_Delay = CreateConVar("sm_knife_madness_kill_delay", "3", "Delay before ZMs die after being knifed by a human.", 0, true, 0.0);

	HookEvent("player_spawn",   OnClientSpawn,  EventHookMode_Post);
	HookEvent("player_death",   OnClientDeath,  EventHookMode_Pre);
	HookEvent("player_hurt",    OnClientHurt,   EventHookMode_Post);
	HookEvent("player_team", 	OnClientTeam,   EventHookMode_Pre);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientSpawn(Event hEvent, const char[] sEvent, bool bDontBroadcast)
{
	int client = GetClientOfUserId(hEvent.GetInt("userid"));

	g_bClientKnifed[client] = false;
	g_bSupressDeath[client] = false;

	if (!IsFakeClient(client))
		QueryClient(client);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action OnClientDeath(Event hEvent, const char[] sEvent, bool bDontBroadcast)
{
	int client = GetClientOfUserId(hEvent.GetInt("userid"));

	if (g_bSupressDeath[client])
	{
		g_bSupressDeath[client] = false;
		return Plugin_Handled;
	}

	return Plugin_Continue;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientHurt(Event hEvent, const char[] sEvent, bool bDontBroadcast)
{
	int attacker = GetClientOfUserId(hEvent.GetInt("attacker"));
	int victim = GetClientOfUserId(hEvent.GetInt("userid"));

	if (!IsValidClient(attacker, false))
		return;

	if (!(IsPlayerAlive(attacker) && IsPlayerAlive(victim) && ZR_IsClientHuman(attacker) && ZR_IsClientZombie(victim)))
		return;

	char sWeapon[32];
	GetEventString(hEvent, "weapon", sWeapon, sizeof(sWeapon));

	if(!StrEqual(sWeapon, "knife", false))
		return;

	g_bClientKnifed[victim] = true;
	CPrintToChat(attacker, "{unique}[Knife Madness] {white}You have knifed {lime}%N{white}. He will die in %ds if he doesnt infect a human.", victim, g_hCVar_Delay.IntValue);
	CPrintToChat(victim, "{unique}[Knife Madness] {white}You have been knifed by {lime}%N{white}. You will die in %ds if you do not infect a human.", attacker, g_hCVar_Delay.IntValue);

	SetEntPropFloat(victim, Prop_Send, "m_flProgressBarStartTime", GetGameTime());
	SetEntProp(victim, Prop_Send, "m_iProgressBarDuration", g_hCVar_Delay.IntValue);

	DataPack pack;
	CreateDataTimer(g_hCVar_Delay.FloatValue, KillZM, pack, TIMER_FLAG_NO_MAPCHANGE);
	pack.WriteCell(GetClientUserId(attacker));
	pack.WriteCell(GetClientUserId(victim));
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientTeam(Event hEvent, const char[] sEvent, bool bDontBroadcast)
{
	int client = GetClientOfUserId(hEvent.GetInt("userid"));

	if (g_bClientKnifed[client])
	{
		g_bClientKnifed[client] = false;
		LogPlayerEvent(client, "triggered", "switch_to_spec");
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn)
{
	if (!IsValidClient(attacker))
		return;

	if(g_bClientKnifed[attacker])
	{
		g_bClientKnifed[attacker] = false;
		SetEntProp(attacker, Prop_Send, "m_iProgressBarDuration", 0);
		CPrintToChat(attacker, "{unique}[Knife Madness] {white}You have successfully infected a human and prevented your death.");
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action KillZM(Handle timer, DataPack pack)
{
	int attacker = 0;
	int client = 0;

	pack.Reset();
	attacker = GetClientOfUserId(pack.ReadCell());
	client = GetClientOfUserId(pack.ReadCell());

	if (client == 0)
		return;

	if (!(IsValidClient(client, false) && IsPlayerAlive(client) && ZR_IsClientZombie(client) && g_bClientKnifed[client]))
		return;

	g_bSupressDeath[client] = true;
	ForcePlayerSuicide(client);
	g_bClientKnifed[client] = false;

	if (!(IsValidClient(attacker, false)))
		return;

	Event hEvent = CreateEvent("player_death");
	if (hEvent == null)
		return;

	hEvent.SetInt("userid", GetClientUserId(client));
	hEvent.SetInt("attacker", GetClientUserId(attacker));
	hEvent.SetString("weapon", "knife");
	hEvent.SetBool("headshot", false);
	hEvent.SetInt("dominated", 0);
	hEvent.SetInt("revenge", 0);
	hEvent.Fire();
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void QueryClient(int client)
{
	if (QueryClientConVar(client, "cl_updaterate", OnConVarQueryFinished, GetClientSerial(client)) == QUERYCOOKIE_FAILED)
		LogError("Could not query ConVar \"cl_updaterate\" for %N", client);

	if (QueryClientConVar(client, "cl_cmdrate", OnConVarQueryFinished, GetClientSerial(client)) == QUERYCOOKIE_FAILED)
		LogError("Could not query ConVar \"cl_cmdrate\" for %N", client);

	if (QueryClientConVar(client, "cl_lagcompensation", OnConVarQueryFinished, GetClientSerial(client)) == QUERYCOOKIE_FAILED)
		LogError("Could not query ConVar \"cl_lagcompensation\" for %N", client);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnConVarQueryFinished(QueryCookie cookie, int client, ConVarQueryResult result, const char[] cvarName, const char[] cvarValue, int iSerial)
{
	if (GetClientFromSerial(iSerial) != client)
		return;

	if (result == ConVarQuery_NotFound)
	{
		LogError("Could not find ConVar \"cvarName\" for %N", client);
		return;
	}

	if (result == ConVarQuery_Okay)
    {
		float fValue = StringToFloat(cvarValue);

		if (StrEqual(cvarName, "cl_updaterate", false))
		{
			if (!(20.0 <= fValue <= 130.0))
			{
				LogAction(client, -1, "\"%L\" ConVar Violation: cl_updaterate (Value: %f)", client, fValue);
				KickClient(client, "ConVar Violation: cl_updaterate must be set between 20 and 130");
				return;
			}
		}
		else if (StrEqual(cvarName, "cl_cmdrate", false))
		{
			if (!(20.0 <= fValue <= 130.0))
			{
				LogAction(client, -1, "\"%L\" ConVar Violation: cl_cmdrate (Value: %f)", client, fValue);
				KickClient(client, "ConVar Violation: cl_cmdrate must be set between 20 and 130");
				return;
			}
		}
		else if (StrEqual(cvarName, "cl_lagcompensation", false))
		{
			if (!fValue)
			{
				LogAction(client, -1, "\"%L\" ConVar Violation: cl_lagcompensation (Value: %f)", client, fValue);
				KickClient(client, "ConVar Violation: cl_lagcompensation must be set to 1");
				return;
			}
		}
		else if (StrEqual(cvarName, "cl_interp", false))
		{
			if ((!(fValue >= 0.1)) && (fValue != 0.0))
			{
				LogAction(client, -1, "\"%L\" ConVar Violation: cl_interp (Value: %f)", client, fValue);
				KickClient(client, "ConVar Violation: cl_interp must not be lower than 0.1");
				return;
			}
		}
	}
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock int IsValidClient(int client, bool nobots = true)
{
	if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client)))
		return false;

	return IsClientInGame(client);
}