#pragma semicolon 1

#include <sourcemod>
#include <cstrike>
#include <sdktools>
#include <sdkhooks>
#include <multicolors>
#include <zombiereloaded>
#include <AFKManager>
#include <clientprefs>
#tryinclude <entWatch_core>

bool g_bTestRound;
bool g_bAdminTestRound;
bool g_bMotherZM[MAXPLAYERS + 1] = { false, ...};
bool g_bMotherZMSpawned;
char g_cOriginalMotherZMNames[256];

/*bool g_bZHP[MAXPLAYERS + 1] = { false, ... };
Handle g_hCookieZHP = null;*/
bool g_bZombieSpawnSound[MAXPLAYERS + 1] = { false, ... };
Handle g_hZombieSpawnSound = null;
int g_iZHPMax[MAXPLAYERS + 1];
int g_iZShield[MAXPLAYERS + 1];
bool g_bShield;
bool g_bZombieDrown;

bool g_Plugin_entWatch;

int g_iAFKTime;

public Plugin myinfo =
{
	name = "Zombie Manager",
	author = "Dogan",
	description = "Tools to manage testround and zombies",
	version = "3.2.0",
	url = ""
};

public void OnPluginStart()
{
	g_bTestRound = false;
	g_bAdminTestRound = false;

	RegAdminCmd("sm_testround", Command_Testround, ADMFLAG_GENERIC, "Toggle to enable/disable a test round.");
	RegAdminCmd("sm_shield", Command_ZShield, ADMFLAG_GENERIC, "Toggle to enable/disable zombie shield.");

	CreateTimer(20.0, Timer_MessageTestRound, _, TIMER_REPEAT);
	CreateTimer(5.0, Timer_UpdateHintMessage, _, TIMER_REPEAT);
	CreateTimer(0.2, Timer_HandleHPShield, _, TIMER_REPEAT); // health_regen_interval = 0.2

	HookEvent("round_start", OnRoundStart);
	HookEvent("player_spawn", OnClientSpawn);
	HookEvent("player_hurt", OnPlayerHurt, EventHookMode_Pre);

	ConVar cvar;
	HookConVarChange((cvar = CreateConVar("sm_player_afk_time", "120", "AFK Time in seconds after which a player won't turn into a motherzombie")), Cvar_AFKTime);
	g_iAFKTime = cvar.IntValue;
	HookConVarChange((cvar = CreateConVar("sm_zombieshield", "1", "1 = Zombie Shield activated, 0 = Zombie Shield deactivated", FCVAR_NONE, true, 0.0, true, 1.0)), Cvar_ZombieShield);
	g_bShield = cvar.BoolValue;
	HookConVarChange((cvar = CreateConVar("sm_zombiedrown", "1", "1 = Zombie Drown Protection active, 0 = Zombie Drown Protection not active", FCVAR_NONE, true, 0.0, true, 1.0)), Cvar_ZombieDrown);
	g_bZombieDrown = cvar.BoolValue;
	delete cvar;

	/*RegConsoleCmd("sm_zhp", OnToggleZHP, "Toggle blocking Zombie HP and Shield display");
	g_hCookieZHP = RegClientCookie("zhp_blocked", "are zombie hp and shield display blocked", CookieAccess_Protected);
	SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Zombie HP Shield Display");*/

	RegConsoleCmd("sm_zombiespawnsound", OnToggleZombieSpawnSound, "Toggle blocking the Sound when Mother Zombies spawn");
	RegConsoleCmd("sm_zss", OnToggleZombieSpawnSound, "Toggle blocking the Sound when Mother Zombies spawn");
	g_hZombieSpawnSound = RegClientCookie("zss_blocked", "is mother zombie spawn sound blocked", CookieAccess_Protected);
	SetCookieMenuItem(MenuHandler_CookieMenu, 0, "MotherZombieSpawnSound");

	AutoExecConfig(true, "plugin.ZombieManager");

	AddMultiTargetFilter("@mzombie", Filter_Motherzombies, "Current Mother Zombies", false);
	RegConsoleCmd("sm_mzombie", Command_DisplayMotherzombies, "Original + Current Mother Zombies");
	RegConsoleCmd("sm_mzombies", Command_DisplayMotherzombies, "Original + Current Mother Zombies");

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

public void OnAllPluginsLoaded()
{
	g_Plugin_entWatch = LibraryExists("entWatch-core");
}

public void OnLibraryAdded(const char[] name)
{
	if(StrEqual(name, "entWatch-core"))
		g_Plugin_entWatch = true;
}

public void OnLibraryRemoved(const char[] name)
{
	if(StrEqual(name, "entWatch-core"))
		g_Plugin_entWatch = false;
}

public void OnPluginEnd()
{
	RemoveMultiTargetFilter("@mzombie", Filter_Motherzombies);
}

public void OnMapStart()
{
	g_bShield = GetConVarBool(FindConVar("sm_zombieshield"));

	PrecacheSound("unloze/not-not-so-infected-rush.mp3");
	AddFileToDownloadsTable("sound/unloze/not-not-so-infected-rush.mp3");
}

/*public Action OnToggleZHP(int client, int args)
{
	ToggleZHP(client);
	return Plugin_Handled;
}

public void ToggleZHP(int client)
{
	g_bZHP[client] = !g_bZHP[client];

	SetClientCookie(client, g_hCookieZHP, g_bZHP[client] ? "1" : "");
	CPrintToChat(client, "{green}[ZR] {yellow}%s", g_bZHP[client] ? "Zombie HP and Shield display disabled." : "Zombie HP and Shield display enabled.");
}*/

/*public void OnClientCookiesCached(int client)
{
	char sBuffer[2];

	GetClientCookie(client, g_hCookieZHP, sBuffer, sizeof(sBuffer));
	if(sBuffer[0] != '\0')
		g_bZHP[client] = true;
	else
		g_bZHP[client] = false;
}*/

public Action OnToggleZombieSpawnSound(int client, int args)
{
	ToggleZombieSpawnSound(client);
	return Plugin_Handled;
}

public void ToggleZombieSpawnSound(int client)
{
	g_bZombieSpawnSound[client] = !g_bZombieSpawnSound[client];

	SetClientCookie(client, g_hZombieSpawnSound, g_bZombieSpawnSound[client] ? "1" : "");
	CPrintToChat(client, "{cyan}[MotherZombieSpawnSound] {white}%s", g_bZombieSpawnSound[client] ? "Sound disabled." : "Sound enabled.");
}

public void OnClientCookiesCached(int client)
{
	char sBuffer[2];

	GetClientCookie(client, g_hZombieSpawnSound, sBuffer, sizeof(sBuffer));
	if(sBuffer[0] != '\0')
		g_bZombieSpawnSound[client] = true;
	else
		g_bZombieSpawnSound[client] = false;
}

public void OnClientPutInServer(int client)
{
	SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
}

public void OnClientDisconnect(int client)
{
	//g_bZHP[client] = false;
	g_bZombieSpawnSound[client] = false;
	g_iZHPMax[client] = 0;
	g_iZShield[client] = 0;
	g_bMotherZM[client] = false;
}

/*public void ShowSettingsMenu(int client)
{
	Menu menu = new Menu(MenuHandler_MainMenu);

	menu.SetTitle("Zombie HP Shield Display Settings", client);

	char sBuffer[128];

	Format(sBuffer, sizeof(sBuffer), "Zombie HP and Shield Display: %s", g_bZHP[client] ? "Disabled" : "Enabled");
	menu.AddItem("0", 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, "Zombie HP Shield Display", 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): ToggleZHP(client);
			}

			ShowSettingsMenu(client);
		}
		case(MenuAction_Cancel):
		{
			ShowCookieMenu(client);
		}
		case(MenuAction_End):
		{
			delete menu;
		}
	}
}*/

public void ShowSettingsMenu(int client)
{
	Menu menu = new Menu(MenuHandler_MainMenu);

	menu.SetTitle("MotherZombieSpawnSound Settings", client);

	char sBuffer[128];

	Format(sBuffer, sizeof(sBuffer), "Sound: %s", g_bZombieSpawnSound[client] ? "Disabled" : "Enabled");
	menu.AddItem("0", 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, "MotherZombieSpawnSound", 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): ToggleZombieSpawnSound(client);
			}

			ShowSettingsMenu(client);
		}
		case(MenuAction_Cancel):
		{
			ShowCookieMenu(client);
		}
		case(MenuAction_End):
		{
			delete menu;
		}
	}
}

public void Cvar_AFKTime(ConVar convar, const char[] oldValue, const char[] newValue)
{
	g_iAFKTime = convar.IntValue;
}

public void Cvar_ZombieShield(ConVar convar, const char[] oldValue, const char[] newValue)
{
	g_bShield = convar.BoolValue;
}

public void Cvar_ZombieDrown(ConVar convar, const char[] oldValue, const char[] newValue)
{
	g_bZombieDrown = convar.BoolValue;
}

public void OnRoundStart(Event hEvent, const char[] sName, bool bDontBroadcast)
{
	g_bTestRound = false;
	g_bAdminTestRound = false;
	g_bMotherZMSpawned = false;
	g_cOriginalMotherZMNames = "";

	for(int i = 1; i <= MaxClients; i++)
	{
		g_bMotherZM[i] = false;
		g_iZHPMax[i] = 0;
		g_iZShield[i] = 0;
	}
}

public Action ZR_OnClientInfect(int &client, int &attacker, bool &motherInfect, bool &respawnOverride, bool &respawn)
{
	if(g_bTestRound)
		return Plugin_Handled;

	return Plugin_Continue;
}

public Action ZR_OnClientMotherZombieEligible(int client)
{
    bool bHasItem;
    #if defined entWatch_core_included
    if(g_Plugin_entWatch)
        bHasItem = EW_ClientHasItem(client);
    #endif

	char sAuthID[32];
	GetClientAuthId(client, AuthId_Steam2, sAuthID, sizeof(sAuthID)); //autism bot

	if(GetClientIdleTime(client) > g_iAFKTime || IsFakeClient(client) || bHasItem || StrEqual(sAuthID, "STEAM_0:1:60189040"))
        return Plugin_Handled;

	return Plugin_Continue;
}

public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn)
{
	g_iZHPMax[client] = GetClientHealth(client);

	if(g_bMotherZM[client]) //Motherzombies that die and respawn
	{
		g_bMotherZM[client] = true;
		return;
	}

	g_bMotherZM[client] = motherInfect;

	if(motherInfect && !g_bMotherZMSpawned)
	{
		g_bMotherZMSpawned = true;
		CreateTimer(1.0, Timer_LockMotherZMNames, _, TIMER_FLAG_NO_MAPCHANGE);
		for(int i = 1; i <= MaxClients; i++)
		{
			if(!IsClientInGame(i))
				continue;

			if(g_bZombieSpawnSound[i])
				continue;

			EmitSoundToClient(i, "unloze/not-not-so-infected-rush.mp3");
		}
	}

	char aBuf2[MAX_NAME_LENGTH];

	if(motherInfect)
	{
		GetClientName(client, aBuf2, sizeof(aBuf2));
		StrCat(g_cOriginalMotherZMNames, sizeof(g_cOriginalMotherZMNames), aBuf2);
		StrCat(g_cOriginalMotherZMNames, sizeof(g_cOriginalMotherZMNames), ", ");
	}
}

public Action ZR_OnInfectCountdown()
{
	if(g_bTestRound)
		return Plugin_Handled;

	return Plugin_Continue;
}

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

	g_iZShield[client] = 0;
}

public Action OnPlayerHurt(Event event, const char[] name, bool dontBroadcast)
{
	int client = GetClientOfUserId(GetEventInt(event, "userid"));
	int attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
	int damage = GetEventInt(event, "dmg_health");

	if(!IsClientInGame(client) || !IsPlayerAlive(client) || !ZR_IsClientZombie(client))
		return Plugin_Continue;

	if(g_iZShield[client] > 0 && attacker != 0)
	{
		g_iZShield[client] = g_iZShield[client] - damage;
		SetEntityHealth(client, g_iZHPMax[client]);
	}

	return Plugin_Continue;
}

public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
	if(!g_bZombieDrown || !IsPlayerAlive(victim))
		return Plugin_Continue;

	if(!ZR_IsClientZombie(victim) || damagetype != DMG_DROWN)//Zombie taking damage from drowning
		return Plugin_Continue;

	damage = 0.0;
	return Plugin_Handled;
}

public bool Filter_Motherzombies(const char[] sPattern, Handle hClients, int client)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == CS_TEAM_T)
		{
			if(g_bMotherZM[i])
				PushArrayCell(hClients, i);
		}
	}

	return true;
}

public Action Command_DisplayMotherzombies(int client, int args)
{
	if(CheckCommandAccess(client, "", ADMFLAG_GENERIC))
	{
		if(strlen(g_cOriginalMotherZMNames))
		{
			char cOriginalMotherZM[512] = "[SM] Original Mother Zombies: ";
			StrCat(cOriginalMotherZM, sizeof(cOriginalMotherZM), g_cOriginalMotherZMNames);
			ReplyToCommand(client, "%s", cOriginalMotherZM);
		}
		else
			ReplyToCommand(client, "[SM] Original Mother Zombies: none");
	}

	char aBuf[1024];
	char aBuf2[MAX_NAME_LENGTH];

	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == CS_TEAM_T)
		{
			if(g_bMotherZM[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] Current Alive Mother Zombies: %s", aBuf);
	}
	else
		ReplyToCommand(client, "[SM] Current Alive Mother Zombies: none");

	return Plugin_Handled;
}

public Action Command_Testround(int client, int args)
{
	/*if(GetClientTeam(client) == CS_TEAM_SPECTATOR)
	{
		ReplyToCommand(client, "[SM] Please join a Team first.");
		return Plugin_Handled;
	}

	if(!IsPlayerAlive(client))
	{
		ReplyToCommand(client, "[SM] Please respawn yourself first.");
		return Plugin_Handled;
	}*/

	ToggleTestRound(client);
	return Plugin_Handled;
}

public Action Command_ZShield(int client, int args)
{
	char sNemesis[32];
	GetConVarString(FindConVar("sm_info_message_file"), sNemesis, sizeof(sNemesis));
	bool bNemesis = StrEqual(sNemesis, "nemesis");

	if(bNemesis)
	{
		ReplyToCommand(client, "[SM] Can't modify zombie shields on nemesis maps!");
		return Plugin_Handled;
	}

	if(g_bShield)
	{
		g_bShield = false;

		ReplyToCommand(client, "[SM] Deactivated zombie shields.");
		CPrintToChatAll("[SM] %N deactivated zombie shields!", client);
		LogAction(client, -1, "\"%L\" deactivated zombie shields.", client);
	}
	else
	{
		g_bShield = true;

		ReplyToCommand(client, "[SM] Activated zombie shields.");
		CPrintToChatAll("[SM] %N activated zombie shields!", client);
		LogAction(client, -1, "\"%L\" activated zombie shields.", client);
	}

	return Plugin_Handled;
}

public void ToggleTestRound(int client)
{
	g_bAdminTestRound = !g_bAdminTestRound;

	if(g_bTestRound)
	{
		g_bTestRound = false;

		ReplyToCommand(client, "[SM] Deactivated this Test Round.");
		CPrintToChatAll("[SM] %N deactivated this Test Round!", client);
		LogAction(client, -1, "\"%L\" deactivated this Test Round.", client);
	}
	else
	{
		g_bTestRound = true;

		ReplyToCommand(client, "[SM] Activated a Test Round.");
		CPrintToChatAll("[SM] %N activated a Test Round!", client);
		LogAction(client, -1, "\"%L\" activated a Test Round.", client);

		for(int i = 1; i <= MaxClients; i++)
		{
			if(IsClientInGame(i) && IsPlayerAlive(i) && ZR_IsClientZombie(i))
			{
				ZR_HumanClient(i, false, false);
			}
		}
	}
}

public Action CS_OnTerminateRound(float &delay, CSRoundEndReason &reason)
{
	if(g_bTestRound)
		return Plugin_Handled;

	return Plugin_Continue;
}

public Action ZR_OnClientRespawn(int &client, ZR_RespawnCondition &condition)
{
    if(g_bTestRound)
        condition = ZR_Respawn_Human;

    return Plugin_Changed;
}

public Action Timer_MessageTestRound(Handle timer)
{
	if(g_bTestRound)
	{
		CPrintToChatAll("{cyan}[UNLOZE] {red}This is a Test Round!");
		CPrintToChatAll("{cyan}[UNLOZE] {red}This is a Test Round!");
		CPrintToChatAll("{cyan}[UNLOZE] {red}This is a Test Round!");
	}

	return Plugin_Continue;
}

public Action Timer_LockMotherZMNames(Handle timer)
{
	if(strlen(g_cOriginalMotherZMNames))
		g_cOriginalMotherZMNames[strlen(g_cOriginalMotherZMNames) - 2] = 0;

	PrintCenterTextAll("Zombie Infection has been spread!");

	return Plugin_Handled;
}

public Action Timer_HandleHPShield(Handle timer)
{
	char sNemesis[32];
	GetConVarString(FindConVar("sm_info_message_file"), sNemesis, sizeof(sNemesis));
	bool bNemesis = StrEqual(sNemesis, "nemesis");

	for(int i = 1; i <= MaxClients; i++)
	{
		if(!IsClientInGame(i) || !IsPlayerAlive(i) || !ZR_IsClientZombie(i))
			continue;

		if(GetClientHealth(i) > g_iZHPMax[i])
			g_iZHPMax[i] = GetClientHealth(i);

		bool bHasItem;
		#if defined entWatch_core_included
		if(g_Plugin_entWatch)
			bHasItem = EW_ClientHasItem(i);
		#endif

		if(!g_bShield)
			g_iZShield[i] = 0; //disabled by convar
		else if(bHasItem)
			g_iZShield[i] = 0; //zombies with items
		else if(GetClientHealth(i) < g_iZHPMax[i]) //zombies with no max hp
			g_iZShield[i] = 0;
		else if(GetClientHealth(i) >= g_iZHPMax[i] && bNemesis)
			g_iZShield[i] = 0; //disabled for now on nemesis
			//g_iZShield[i] = g_iZShield[i] + 12; // health_regen_amount = 12
		else if(GetClientHealth(i) >= g_iZHPMax[i] && !bNemesis)
			g_iZShield[i] = g_iZShield[i] + 6; // health_regen_amount = 6
	}

	return Plugin_Continue;
}

public Action Timer_UpdateHintMessage(Handle timer)
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(!IsClientInGame(i) || !IsPlayerAlive(i) || !ZR_IsClientZombie(i))
			continue;

		bool bVote = IsVoteInProgress();

		if(/*g_bZHP[i] || */bVote)
			continue;

		if(g_iZShield[i] <= 0)
			PrintHintText(i, "HP: %d", GetClientHealth(i));
		else
			PrintHintText(i, "HP: %d - Shield: %d", GetClientHealth(i), g_iZShield[i]);
	}

	return Plugin_Continue;
}