#pragma semicolon 1
#define DEBUG
#define PLUGIN_AUTHOR "jenz, thx to zuff for help"
#define PLUGIN_VERSION "2.1"
#define MAXZONES 66
#define ZONENAMELENGTH 256
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#pragma newdecls required
//----------------------------------------------------------------------------------------------------
// Purpose: Global variables
//----------------------------------------------------------------------------------------------------
static Handle g_hForwardedZonesEnter;
static Handle g_hForwardedZonesLeaver;
static Handle g_hForwardedZonesCreated;
static char g_cConfigZones[PLATFORM_MAX_PATH];
char g_cZones[MAXZONES][ZONENAMELENGTH];
float g_fStartPos[MAXZONES][3];
float g_fEndPos[MAXZONES][3];
int g_iAdminDrawing[MAXZONES];
int g_iBeam = -1;
int g_iZoneRefference[MAXZONES];
int g_iZoneCount;
int g_iAdminSelectOpion[MAXZONES];
int g_iZoneEntRefference[MAXZONES];
int g_iViewingZones[MAXZONES];
int g_iKeyChecker;
bool g_bZoneNaming[MAXZONES];
bool g_bAdminNewZone;
//----------------------------------------------------------------------------------------------------
// Purpose: description
//----------------------------------------------------------------------------------------------------
public Plugin myinfo = 
{
	name = "Unloze zones",
	author = PLUGIN_AUTHOR,
	description = "Zones to use for zonerewards",
	version = PLUGIN_VERSION,
	url = "www.unloze.com"
};
//----------------------------------------------------------------------------------------------------
// Purpose: pluginstart/end/askpluginload
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	//hooks
	HookEvent("round_start", Event_roundStart, EventHookMode_Post);
	//commands
	RegConsoleCmd("say", Cmd_Say);
	RegAdminCmd("sm_zones", Cmd_Zones, ADMFLAG_GENERIC);
	RegAdminCmd("sm_zone", Cmd_Zones, ADMFLAG_GENERIC);
	RegAdminCmd("sm_zonetest", Cmd_Zonetest, ADMFLAG_CHEATS);
	RegAdminCmd("sm_showzones", Cmd_ZoneDisplay, ADMFLAG_CUSTOM1);
	RegAdminCmd("sm_showzone", Cmd_ZoneDisplay, ADMFLAG_CUSTOM1);
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void OnPluginEnd()
{
	CloseHandle(g_hForwardedZonesEnter);
	CloseHandle(g_hForwardedZonesLeaver);
	CloseHandle(g_hForwardedZonesCreated);
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public APLRes AskPluginLoad2(Handle myself, bool late, char [] error, int err_max)
{
	//global forwards
	g_hForwardedZonesEnter = CreateGlobalForward("unloze_zoneEntry", ET_Ignore, Param_Cell, Param_String);
	g_hForwardedZonesLeaver = CreateGlobalForward("unloze_zoneLeave", ET_Ignore, Param_Cell, Param_String);
	g_hForwardedZonesCreated = CreateGlobalForward("unloze_zoneCreated", ET_Ignore, Param_Cell, Param_String);
	CreateNative("unloze_zoneCount", Native_zoneCount);
	CreateNative("ZoneNameBasedOnIndex", Native_ZoneNameBasedOnIndex);
	return APLRes_Success;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int Native_zoneCount(Handle handler, int numParams)
{
	return g_iZoneCount;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int Native_ZoneNameBasedOnIndex(Handle handler, int numParams)
{
	int i = GetNativeCell(1);
	SetNativeString(2, g_cZones[i], sizeof(g_cZones), true);
    return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose: Displayes zones
//----------------------------------------------------------------------------------------------------
public Action Cmd_ZoneDisplay(int client, int args)
{	
	if (g_iZoneCount < 1)
		PrintToChat(client, "no active zones on the map");
	else
	{
		g_iAdminSelectOpion[client] = 2;
		MenuZoneCounter(client);
	}
	return Plugin_Handled;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void DisplayZones(int client, int selection)
{
	if (g_iAdminDrawing[client] == 0)
	{
		g_iViewingZones[client] = selection;
		g_iAdminDrawing[client] = -1;
		PrintToChat(client, "Activated zone display");
	}
	else if (g_iAdminDrawing[client] <= -1)
	{
		g_iAdminDrawing[client] = 0;
		PrintToChat(client, "De-Activated zone display");
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose: commands && menus
//----------------------------------------------------------------------------------------------------
public Action Cmd_Zonetest(int client, int args)
{
	ZoneResetting(0, 4, 0, "");
	return Plugin_Handled;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public Action Cmd_Zones(int client, int args)
{
	ZonesMenu(client);
	return Plugin_Handled;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void ZonesMenu(int client)
{
	if (!IsValidClient(client))
		return;
	Menu menu = new Menu(MenuHandler1);
	menu.SetTitle("UNLOZE Zones menu!");
	menu.AddItem("", "Create new Zone");
	menu.AddItem("", "Rename Zones");
	menu.AddItem("", "Show/hide Zones");
	menu.AddItem("", "Delete Zone");
	menu.AddItem("", "Goto Zone");
	menu.AddItem("", "Editing Zone");
	menu.ExitButton = true;
	menu.Display(client, 0);
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int MenuHandler1(Menu menu, MenuAction action, int param1, int choice)
{
	if (action == MenuAction_Select && choice > -1)
	{
		g_iAdminSelectOpion[param1] = choice;
		if (!!choice)
			MenuZoneCounter(param1);
		else
		{
			PrintToChat(param1, "Creating new zone");
			cmd_menuFinishZone(param1);
		}
	}
	else if (EndMenu(action))
	{
		delete menu;
	}
    return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public Action MenuZoneCounter(int client)
{
	Menu menu = CreateMenu(MenuHandler2);
	menu.SetTitle("Select which zone to Rename, Display, Delete, Edit or Goto ");
	if (g_iZoneCount < 1)
	{
		PrintToChat(client, "No active zones!");
		ZonesMenu(client);
		return Plugin_Continue;
	}
	for (int i = 0; i <= g_iZoneCount; i++)
		if (strlen(g_cZones[i]) > 0)
			menu.AddItem("", g_cZones[i]);
	menu.ExitButton = true;
	menu.Display(client, 0);
	return Plugin_Continue;
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int MenuHandler2(Menu menu, MenuAction action, int param1, int choice)
{
	if (!IsValidClient(param1))
		return 0;
	float l_fMiddle[MAXZONES][3];
	float l_fMin1[MAXZONES][3], l_fMax1[MAXZONES][3];
	if (action == MenuAction_Select)
	{
		g_iZoneRefference[param1] = choice;
		switch(g_iAdminSelectOpion[param1])
		{
			//rename zone
			case 1:
			{
				g_bZoneNaming[param1] = true;
				PrintToChat(param1, "Enter the name of the zone into the chat");
			}
			//display zones
			case 2:
			{
				DisplayZones(param1, choice);
				MenuZoneCounter(param1);
			}
			//delete zones
			case 3:
			{
				g_iKeyChecker = choice;
				if (deleteFromFile())
				{
					PrintToChat(param1, "Deleting %s ", g_cZones[choice]);
					ZoneResetting(0, 0, param1, "");
				}
				MenuZoneCounter(param1);
			}
			//goto zone
			case 4:
			{
				PrintToChat(param1, "Teleported to: %s", g_cZones[choice]);
				l_fMin1[choice][0] = g_fStartPos[choice][0];
				l_fMin1[choice][1] = g_fStartPos[choice][1];
				l_fMin1[choice][2] = g_fStartPos[choice][2];
				l_fMax1[choice][0] = g_fEndPos[choice][0];
				l_fMax1[choice][1] = g_fEndPos[choice][1];
				l_fMax1[choice][2] = g_fEndPos[choice][2];
				GetMiddleOfABox(l_fMin1[choice], l_fMax1[choice], l_fMiddle[choice]);
				TeleportEntity(param1, l_fMiddle[choice], NULL_VECTOR, NULL_VECTOR);
				MenuZoneCounter(param1);
			}
			//edit zone
			case 5:
			{
				SubMenuForEditing(param1);
			}
		}
	}
	else if (EndMenu(action))
	{
		if (!g_iZoneCount && IsValidClient(param1))
			PrintToChat(param1, "No zones available");
		delete menu;
		//experimental
		g_iAdminSelectOpion[param1] = 0;
	}
    return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void SubMenuForEditing(int client)
{
	Menu menu1 = CreateMenu(MenuHandler3);
	menu1.SetTitle("UNLOZE Editing zone: %s", g_cZones[g_iZoneRefference[client]]);
	menu1.AddItem("", "Redo Zone");
	menu1.AddItem("", "move Zone down");
	menu1.AddItem("", "move Zone up");
	menu1.AddItem("", "move Zone left");
	menu1.AddItem("", "move Zone right");
	menu1.AddItem("", "move Zone backwards");
	menu1.AddItem("", "move Zone forwards");
	menu1.ExitButton = true;
	menu1.Display(client, 0);
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public bool EndMenu(MenuAction action)
{
	if (action == MenuAction_End)
	{
		return true;
	}
	return false;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int MenuHandler3(Menu menu, MenuAction action, int param1, int choice)
{
	float l_fMoveStartPos[3], l_fMoveEndPos[3];
	l_fMoveStartPos = g_fStartPos[g_iZoneRefference[param1]];
	l_fMoveEndPos = g_fEndPos[g_iZoneRefference[param1]];
	if (EndMenu(action))
	{
		if (!g_iZoneCount && IsValidClient(param1))
			PrintToChat(param1, "No zones available");
		delete menu;
	}
	else if (action == MenuAction_Select)
	{
		switch(choice)
		{
			//redo zone
			case 0:
			{
				g_iAdminDrawing[param1] = 1;
				cmd_redoZone(param1);
			}
			//move zone down
			case 1:
			{
				l_fMoveStartPos[2] += -15.0;
			}
			//move zone up
			case 2:
			{
				l_fMoveEndPos[2] -= -15.0;
			}
			//move zone left
			case 3:
			{
				l_fMoveStartPos[1] += 15.0;
			}
			//move zone right
			case 4:
			{
				l_fMoveEndPos[1] -= 15.0;
			}
			//move zone backwards
			case 5:
			{
				l_fMoveStartPos[0] += -15.0;
			}
			//move zone forwards
			case 6:
			{
				l_fMoveEndPos[0] -= -15.0;
			}
		}
		if (!!choice)
		{
			g_iAdminSelectOpion[param1] = 5;
			g_fStartPos[g_iZoneRefference[param1]] = l_fMoveStartPos;
			g_fEndPos[g_iZoneRefference[param1]] = l_fMoveEndPos;
			ZoneResetting(g_iZoneRefference[param1], 2, param1, "");
			SubMenuForEditing(param1);
		}
	}
	return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void ZoneResetting(int index, int state, int client, char[] zoneRenaming)
{
	KillAllMultiples();
	if (state > 0 && state < 4)
		g_iKeyChecker = index;
	switch(state)
	{
		//deleting zone
		case 0:
		{
			g_iZoneCount--;
		}
		//adding new zone
		case 1:
		{
			g_iZoneCount++;
			writetofile(g_fStartPos[index], g_fEndPos[index], g_cZones[index], client, 1);
		}
		//editing zone
		case 2:
		{
			writetofile(g_fStartPos[index], g_fEndPos[index], g_cZones[index], client, 0);
		}
		//renaming zone
		case 3:
		{
			writetofile(g_fStartPos[index], g_fEndPos[index], zoneRenaming, client, 0);
		}
		//else do nothing here
	}
	resetZones();
	ReadZoneFile();
	for (int i = 0; i <= g_iZoneCount; i++)
	{
		CreateZones(g_fStartPos[i], g_fEndPos[i], g_cZones[i], i);
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void cmd_redoZone(int client)
{
	GetClientAbsOrigin(client, g_fStartPos[g_iZoneRefference[client]]);
	Menu menu1 = CreateMenu(MenuEditExistingZones);
	g_bAdminNewZone = false;
	menu1.SetTitle("UNLOZE Editing zone: %s", g_cZones[g_iZoneRefference[client]]);
	menu1.AddItem("", "Finish Zone");
	menu1.AddItem("", "restart Zone");
	menu1.ExitButton = true;
	menu1.Display(client, 0);
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int MenuEditExistingZones(Menu menu, MenuAction action, int param1, int choice)
{
	if (EndMenu(action))
	{
		if (!g_iZoneCount && IsValidClient(param1))
			PrintToChat(param1, "No zones available");
		g_iAdminDrawing[param1] = 0;
		delete menu;
	}
	else if (action == MenuAction_Select)
	{
		switch(choice)
		{
			case 0:
			{
				ZoneResetting(g_iZoneRefference[param1], 2, param1, "");
				g_iAdminDrawing[param1] = 0;
				SubMenuForEditing(param1);
			}
			case 1:
			{
				cmd_redoZone(param1);
			}
		}
	}
    return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void ResetClient(int client)
{	
	g_iAdminSelectOpion[client] = 0;
	g_iAdminDrawing[client] = 0;
	g_iViewingZones[client] = 0;
	g_bZoneNaming[client] = false;
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Action cmd_menuFinishZone(int client)
{
	g_iAdminDrawing[client] = 1;
	g_bAdminNewZone = true;
	GetClientAbsOrigin(client, g_fStartPos[g_iZoneCount]);
	Menu menu = new Menu(MenuHandlerfinishzone);
	menu.SetTitle("UNLOZE Zones menu!");
	menu.AddItem("", "Finish new Zone");
	menu.AddItem("", "Restart new Zone");
	menu.ExitButton = true;
	menu.Display(client, 0);
    return Plugin_Handled;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int MenuHandlerfinishzone(Menu menu, MenuAction action, int param1, int choice)
{
	if (action == MenuAction_End)
	{
		g_iAdminSelectOpion[param1] = 0;
		delete menu;
		g_bAdminNewZone = false;
		g_iAdminDrawing[param1] = 0;
	}
	else if (action == MenuAction_Select)
	{
		if (!choice)
		{
			PrintToChat(param1, "Finished creating zone!");
			Format(g_cZones[g_iZoneCount], sizeof(g_cZones), "UNASSIGNED_NAME");	
			ZoneResetting(g_iZoneCount, 1, param1, "");
			g_iAdminDrawing[param1] = 0;
			g_bAdminNewZone = false;
			ZonesMenu(param1);
		}
		else
		{
			cmd_menuFinishZone(param1);
		}
	}
    return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose: simple say hook
//----------------------------------------------------------------------------------------------------
public Action Cmd_Say(int client, int args)
{
	if (g_bZoneNaming[client])
	{
		char l_cZoneRenaming[ZONENAMELENGTH];
		for (int i = 1; i <= MaxClients; i++)
			if (IsValidClient(i) && i != client && g_bZoneNaming[i])
			{
				PrintToChat(client, "Other admin is already renaming zones");
				g_bZoneNaming[client] = false;
				return Plugin_Continue;
			}
		GetCmdArgString(l_cZoneRenaming, sizeof(l_cZoneRenaming));
		chattrim(l_cZoneRenaming);
		ZoneResetting(g_iZoneRefference[client], 3, client, l_cZoneRenaming);
		g_iZoneRefference[client] = 0;
		g_bZoneNaming[client] = false;
		g_iAdminSelectOpion[client] = 1;
		MenuZoneCounter(client);
	}
	return Plugin_Continue;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void chattrim(char[] message)
{
	for (int i = 0; i < strlen(message); i++)
	{
		if (StrContains(message[i], "}", false) >= 0)
		{
			strcopy(message, strlen(message), message[i]);
		}
	}
	ReplaceStrings(message, "xxx");
}
//----------------------------------------------------------------------------------------------------
// Purpose: Creatingzonepoints and displaying them: https://forums.alliedmods.net/showthread.php?p=2030793
//----------------------------------------------------------------------------------------------------
public void CreateZonePoints(float point[8][3])
{
    for (int i = 1; i < 7; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            point[i][j] = point[((i >> (2 - j)) & 1) * 7][j];
        }
    }
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public Action DrawZone(int client, float array[8][3], int beamsprite, int halosprite, int color[4], float life)
{
	//shavit
	static int pairs[][] =
	{
		{ 0, 2 },
		{ 2, 6 },
		{ 6, 4 },
		{ 4, 0 },
		{ 0, 1 },
		{ 3, 1 },
		{ 3, 2 },
		{ 3, 7 },
		{ 5, 1 },
		{ 5, 4 },
		{ 6, 7 },
		{ 7, 5 }
	};	
	for (int i = 0; i < 12; i++)
	{
		TE_SetupBeamPoints(array[pairs[i][0]], array[pairs[i][1]], beamsprite, halosprite, 0, 0, life, 5.0, 5.0, 1, 1.1, color, 1);
		TE_SendToClient(client, 0.0);
	}
	return Plugin_Handled;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int CheckNextEntryKey()
{
	int i_counter[MAXPLAYERS];
	int l_iZoneTempCount;
	int i_Upper;
	Handle l_hZoneFile = INVALID_HANDLE;
	char l_cLine[ZONENAMELENGTH];
	l_hZoneFile = OpenFile(g_cConfigZones, "r");
	while (!IsEndOfFile(l_hZoneFile) && ReadFileLine(l_hZoneFile, l_cLine, sizeof(l_cLine)))
	{
		ReplaceStrings(l_cLine, "xxx");
		if (!!StringToInt(l_cLine))
		{
			i_counter[l_iZoneTempCount] = StringToInt(l_cLine);
			++l_iZoneTempCount;
		}
	}
	for (int i = 0; i < l_iZoneTempCount; i++)
	{
		if (i_counter[i] > i_Upper)
		{
			i_Upper = i_counter[i];
		}
	}
	i_Upper++;
	CloseHandle(l_hZoneFile);
	return i_Upper;
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int Keychecker()
{
	int l_iZoneTempCount[MAXZONES];
	int l_iCounter;
	Handle l_hZoneFile = INVALID_HANDLE;
	char l_cLine[ZONENAMELENGTH];
	l_hZoneFile = OpenFile(g_cConfigZones, "r");
	while (!IsEndOfFile(l_hZoneFile) && ReadFileLine(l_hZoneFile, l_cLine, sizeof(l_cLine)))
	{
		ReplaceStrings(l_cLine, "name");
		if (!!StringToInt(l_cLine))
		{
			l_iZoneTempCount[l_iCounter++] = StringToInt(l_cLine);
		}
		if (StrEqual(l_cLine, g_cZones[g_iKeyChecker]))
		{
			CloseHandle(l_hZoneFile);
			return l_iZoneTempCount[l_iCounter - 1];
		}
	}
	CloseHandle(l_hZoneFile);
	return 0;
}
//----------------------------------------------------------------------------------------------------
// Purpose: Writing to File
//----------------------------------------------------------------------------------------------------
public void writetofile(float startpos[3], float endpos[3], char[] zones, int client, int state)
{
	char l_cEntryCount[MAXZONES];
	char l_cTransfer[MAXZONES];
	KeyValues l_kvZoneEntries = CreateKeyValues("Zones");
	int l_iZoneKey;
	float l_fMin1[3], l_fMax1[3];
	if (!state)
		l_iZoneKey = Keychecker();
	else
		l_iZoneKey = CheckNextEntryKey();
	if (!l_iZoneKey)
		return;
	IntToString(l_iZoneKey, l_cEntryCount, sizeof(l_cEntryCount));
	l_fMin1[0] = startpos[0];
	l_fMin1[1] = startpos[1];
	l_fMin1[2] = startpos[2];
	l_fMax1[0] = endpos[0];
	l_fMax1[1] = endpos[1];
	l_fMax1[2] = endpos[2];
	if (!l_kvZoneEntries.ImportFromFile(g_cConfigZones))
	{
		delete l_kvZoneEntries;
		g_iAdminSelectOpion[client] = 0;
		return;
	}
	if (RetrieveZoneIndex(zones) < 1)
	{
		IntToString(l_iZoneKey, l_cTransfer, sizeof(l_cTransfer));
		Format(l_cTransfer, sizeof(l_cTransfer), "_%s", l_cTransfer);
		StrCat(zones, sizeof(g_cZones), l_cTransfer);
	}
	PrintToChat(client, "Updated zone name to: %s", zones);
	l_kvZoneEntries.Rewind();
	l_kvZoneEntries.JumpToKey(l_cEntryCount, true);
	KvSetString(l_kvZoneEntries, "name", zones);
	KvSetVector(l_kvZoneEntries, "cordinate_a", l_fMin1);
	KvSetVector(l_kvZoneEntries, "cordinate_b", l_fMax1);
	l_kvZoneEntries.Rewind();
	l_kvZoneEntries.ExportToFile(g_cConfigZones);
	delete l_kvZoneEntries;
	//forward
	Call_StartForward(g_hForwardedZonesCreated);
	Call_Finish();
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public int RetrieveZoneIndex(char[] zone)
{
	int i = strlen(zone) - 1;
	return StringToInt(zone[i]);
}
//----------------------------------------------------------------------------------------------------
// Purpose: Deleting entries
//----------------------------------------------------------------------------------------------------
public bool deleteFromFile()
{	
	int l_iZoneKey;
	l_iZoneKey = Keychecker();
	if (!l_iZoneKey)
		return false;
	char l_cEntryCount[ZONENAMELENGTH];
	KeyValues l_kvZoneEntries = CreateKeyValues("Zones");
	IntToString(l_iZoneKey, l_cEntryCount, sizeof(l_cEntryCount));
	if (!l_kvZoneEntries.ImportFromFile(g_cConfigZones))
	{
		delete l_kvZoneEntries;
		return false;
	}
	l_kvZoneEntries.Rewind();
	while (!l_kvZoneEntries.JumpToKey(l_cEntryCount, false))
	{
		l_iZoneKey++;
		IntToString(l_iZoneKey, l_cEntryCount, sizeof(l_cEntryCount));
		if (l_iZoneKey > 64)
			break;
	}
	l_kvZoneEntries.DeleteThis();
	l_kvZoneEntries.Rewind();
	l_kvZoneEntries.ExportToFile(g_cConfigZones);
	delete l_kvZoneEntries;
	return true;
}
//----------------------------------------------------------------------------------------------------
// Purpose: PlayerRun
//----------------------------------------------------------------------------------------------------
//int& buttons int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]
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])
{
	float points[8][3];
	//admin is drawing a new zone
	if (IsValidClient(client) && client < 64 && g_iAdminDrawing[client] >= 1)
	{
		g_iAdminDrawing[client]++;
		if (g_iAdminDrawing[client] >= 15)
		{
			g_iAdminDrawing[client] = 1;
			if (g_bAdminNewZone)
			{
				g_fEndPos[g_iZoneCount] = GetAimPosition(client);
				points[0] = g_fStartPos[g_iZoneCount];
				points[7] = g_fEndPos[g_iZoneCount];
			}
			else
			{
				g_fEndPos[g_iZoneRefference[client]] = GetAimPosition(client);
				points[0] = g_fStartPos[g_iZoneRefference[client]];
				points[7] = g_fEndPos[g_iZoneRefference[client]];
			}
			CreateZonePoints(points);
			DrawZone(client, points, g_iBeam, 0, { 255, 255, 255, 255 }, 0.5);
		}
	}
	//people with custom flag 1 that want to see zones or admins
	else if (IsValidClient(client) && client < 64 && g_iAdminDrawing[client] <= -1 && g_iZoneCount > 0)
	{
		g_iAdminDrawing[client] -= 1;
		if (g_iAdminDrawing[client] <= -15)
		{
			g_iAdminDrawing[client] = -1;
			points[0] = g_fStartPos[g_iViewingZones[client]];
			points[7] = g_fEndPos[g_iViewingZones[client]];
			//fills all other points than [0] and [7]
			CreateZonePoints(points);
			DrawZone(client, points, g_iBeam, 0, { 255, 255, 255, 255 }, 1.0);
		}
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose: GetAimPosition for zone end drawing
//----------------------------------------------------------------------------------------------------
float[] GetAimPosition(int client)
{
	float pos[3];
	float angles[3];
	float end[3];
	GetClientEyePosition(client, pos);
	GetClientEyeAngles(client, angles);
	TR_TraceRayFilter(pos, angles, MASK_SHOT, RayType_Infinite, TraceFilter_NoClients, client);
	if (TR_DidHit())
	{
		TR_GetEndPosition(end);
		return end;
	}
	return pos;
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public bool TraceFilter_NoClients(int entity, int contentsMask, any data)
{
	return (entity != data && 1 <= data <= MaxClients || !IsClientInGame(data));
}
//----------------------------------------------------------------------------------------------------
// Purpose: Mapstart
//----------------------------------------------------------------------------------------------------
public void OnMapInit()
{
	resetZones();
	ReadZoneFile(); //loading zones a bit earlier for the natives sake.
}

public void OnMapStart()
{
	g_bAdminNewZone = false;
	PrecacheModel("models/props/cs_office/vending_machine.mdl");
	g_iBeam = PrecacheModel("materials/unloze_tracers/xbeam.vmt");
	AddFileToDownloadsTable("materials/unloze_tracers/xbeam.vmt");
	AddFileToDownloadsTable("materials/unloze_tracers/xbeam.vtf");
	ZoneResetting(0, 4, 0, "");
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void resetZones()
{
	for (int i = 0; i < MAXZONES; i++)
	{
		Format(g_cZones[i], sizeof(g_cZones), "");
		g_fStartPos[i][0] = 0.0;
		g_fStartPos[i][1] = 0.0;
		g_fStartPos[i][2] = 0.0;
		g_fEndPos[i][0] = 0.0;
		g_fEndPos[i][1] = 0.0;
		g_fEndPos[i][2] = 0.0;
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose: Reads from file
//----------------------------------------------------------------------------------------------------
public Action ReadZoneFile()
{
	int i_zoneTemp;
	char l_cMapName[MAXZONES];
	GetCurrentMap(l_cMapName, sizeof(l_cMapName));
	Handle l_hZoneFile = INVALID_HANDLE;
	char l_cLine[ZONENAMELENGTH];
	char sPart[4][32];
	BuildPath(Path_SM, g_cConfigZones, sizeof(g_cConfigZones), "configs/unloze_zones/%s.zones.txt", l_cMapName);
	l_hZoneFile = OpenFile(g_cConfigZones, "r");
	if (l_hZoneFile == INVALID_HANDLE)
	{
		Handle kv = CreateKeyValues("Zones");
		KeyValuesToFile(kv, g_cConfigZones);
		CloseHandle(kv);
		delete l_hZoneFile;
		return Plugin_Handled;
	}
	while (!IsEndOfFile(l_hZoneFile) && ReadFileLine(l_hZoneFile, l_cLine, sizeof(l_cLine)))
	{
		if (StrContains(l_cLine, "name", false) > -1)
		{
			ReplaceStrings(l_cLine, "name");
			Format(g_cZones[i_zoneTemp], sizeof(g_cZones), l_cLine);
		}
		if (StrContains(l_cLine, "cordinate_a", false) >= 0)
		{
			ReplaceString(l_cLine, sizeof(l_cLine), "\"", "", true);
			ReplaceString(l_cLine, sizeof(l_cLine), "cordinate_a", "", true);
			ExplodeString(l_cLine, " ", sPart, 4, 32);
			g_fStartPos[i_zoneTemp][0] = StringToFloat(sPart[0]);//reading first vector
			g_fStartPos[i_zoneTemp][1] = StringToFloat(sPart[1]);//reading second vector
			g_fStartPos[i_zoneTemp][2] = StringToFloat(sPart[2]);//reading third vector
		}
		if (StrContains(l_cLine, "cordinate_b", false) >= 0)
		{
			ReplaceString(l_cLine, sizeof(l_cLine), "\"", "", true);
			ReplaceString(l_cLine, sizeof(l_cLine), "cordinate_b", "", true);
			ExplodeString(l_cLine, " ", sPart, 4, 32);
			g_fEndPos[i_zoneTemp][0] = StringToFloat(sPart[0]);
			g_fEndPos[i_zoneTemp][1] = StringToFloat(sPart[1]);
			g_fEndPos[i_zoneTemp][2] = StringToFloat(sPart[2]);
			i_zoneTemp++;
		}
	}
	g_iZoneCount = i_zoneTemp;
	delete l_hZoneFile;
	return Plugin_Handled;
}
//----------------------------------------------------------------------------------------------------
// Purpose: OnClientPostAdminCheck && disconnect
//----------------------------------------------------------------------------------------------------
public void OnClientPostAdminCheck(int client)
{
	ResetClient(client);
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void OnClientDisconnect(int client)
{
	ResetClient(client);
}
//----------------------------------------------------------------------------------------------------
// Purpose: Hook roundstart
//----------------------------------------------------------------------------------------------------
public void Event_roundStart(Handle event, const char[] name, bool dontBroadcast)
{
	ZoneResetting(0, 4, 0, "");
}
//----------------------------------------------------------------------------------------------------
// Purpose: Destroys trigger_multiple´s in current round
//----------------------------------------------------------------------------------------------------
public void KillAllMultiples()
{
	int l_iZoneEntity;
	for (int i = 0; i <= g_iZoneCount; i++)
	{
		l_iZoneEntity = EntRefToEntIndex(g_iZoneEntRefference[i]);
		if (IsValidEntRef(l_iZoneEntity))
		{
			SDKUnhook(l_iZoneEntity, SDKHook_StartTouchPost, OnStartTouch);
			SDKUnhook(l_iZoneEntity, SDKHook_EndTouchPost, OnEndTouch);
			AcceptEntityInput(l_iZoneEntity, "Kill");
		}
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose: Zone loading https://forums.alliedmods.net/showthread.php?p=2585082
//----------------------------------------------------------------------------------------------------
public void CreateZones(float f_min[3], float f_max[3], char[] sZoneName, int zoneCount)
{
	float l_fMiddle[3], l_fMin1[3], l_fMax1[3];
	char l_cZoneName[ZONENAMELENGTH];
	Format(l_cZoneName, sizeof(l_cZoneName), sZoneName);
	int entindex = CreateEntityByName("trigger_multiple");
	if (entindex != -1)
	{	
		/*
		* According to a mysterious source:
		* Spawnflags:	
		* 1 - only a player can trigger this by touch, makes it so a NPC cannot fire a trigger_multiple
		* 2 - Won't fire unless triggering ent's view angles are within 45 degrees of trigger's angles (in addition to any other conditions), so if you want the player to only be able to fire the entity at a 90 degree angle you would do ",angles,0 90 0," into your spawnstring.
		* 4 - Won't fire unless player is in it and pressing use button (in addition to any other conditions), you must make a bounding box,(max\mins) for this to work.
		* 8 - Wont fire unless player/NPC is in it and pressing fire button, you must make a bounding box,(max\mins) for this to work.
		* 16 - only non-player NPCs can trigger this by touch
		* 128 - Start off, has to be activated by a target_activate to be touchable/usable
		* 256 - multiple players can trigger the entity at the same time
		*/
		DispatchKeyValue(entindex, "targetname", l_cZoneName); 
		DispatchKeyValue(entindex, "spawnflags", "257");
		SetEntityModel(entindex, "models/props/cs_office/vending_machine.mdl");
	}
	else
		return;
	DispatchSpawn(entindex);
	ActivateEntity(entindex);
	//we need to copy it over to local floats or else parameter gets updated and the origin is fucked
	l_fMin1[0] = f_min[0];
	l_fMin1[1] = f_min[1];
	l_fMin1[2] = f_min[2];
	l_fMax1[0] = f_max[0];
	l_fMax1[1] = f_max[1];
	l_fMax1[2] = f_max[2];
	GetMiddleOfABox(l_fMin1, l_fMax1, l_fMiddle);
	TeleportEntity(entindex, l_fMiddle, NULL_VECTOR, NULL_VECTOR);
	// Have the mins always be negative
	for (int i = 0; i < 3; i++)
	{
        l_fMin1[i] = l_fMin1[i] - l_fMiddle[i];
        if (l_fMin1[i] > 0.0)
        {
            l_fMin1[i] *= -1.0;
        }
    }
	// And the maxs always be positive
	for (int i = 0; i < 3; i++)
	{
        l_fMax1[i] = l_fMax1[i] - l_fMiddle[i];
        if (l_fMax1[i] < 0.0)
        {
            l_fMax1[i] *= -1.0;
        }
	}
	SetEntPropVector(entindex, Prop_Send, "m_vecMins", l_fMin1);
	SetEntPropVector(entindex, Prop_Send, "m_vecMaxs", l_fMax1);
	SetEntProp(entindex, Prop_Send, "m_nSolidType", 2);
	int enteffects = GetEntProp(entindex, Prop_Send, "m_fEffects");
	enteffects |= 32;
	SetEntProp(entindex, Prop_Send, "m_fEffects", enteffects);
	SDKHook(entindex, SDKHook_StartTouchPost, OnStartTouch);
	SDKHook(entindex, SDKHook_EndTouchPost, OnEndTouch);
	g_iZoneEntRefference[zoneCount] = EntIndexToEntRef(entindex);
}
//----------------------------------------------------------------------------------------------------
// Purpose: Entity Output Handlers
//----------------------------------------------------------------------------------------------------
public void OnStartTouch(int entindex, int other)
{
	char l_cZoneName[MAXZONES];
	if (IsValidClient1(other) && IsPlayerAlive(other))
	{
		GetEntPropString(entindex, Prop_Data, "m_iName", l_cZoneName, sizeof(l_cZoneName));
		//forward
		Call_StartForward(g_hForwardedZonesEnter);
		Call_PushCell(other);
		Call_PushString(l_cZoneName);
		Call_Finish();
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
public void OnEndTouch(int entindex, int other)
{
	char l_cZoneName[MAXZONES];
	if (IsValidClient1(other) && IsPlayerAlive(other))
	{
		GetEntPropString(entindex, Prop_Data, "m_iName", l_cZoneName, sizeof(l_cZoneName)); 
		//forward
		Call_StartForward(g_hForwardedZonesLeaver);
		Call_PushCell(other);
		Call_PushString(l_cZoneName);
		Call_Finish();
	}
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock int IsValidClient(int client, bool nobots = true)
{
	if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client)))
		return false;
	return IsClientInGame(client);
}

stock int IsValidClient1(int client, bool nobots = true)
{
	if (client <= 0 || client > MaxClients || !IsClientConnected(client))
		return false;
	return IsClientInGame(client);
}
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
stock void GetMiddleOfABox(const float vec1[3], const float vec2[3], float buffer[3])
{
    float l_fMid[3];
    MakeVectorFromPoints(vec1, vec2, l_fMid);
    l_fMid[0] = l_fMid[0] / 2.0;
    l_fMid[1] = l_fMid[1] / 2.0;
    l_fMid[2] = l_fMid[2] / 2.0;
    AddVectors(vec1, l_fMid, buffer);
} 
//----------------------------------------------------------------------------------------------------
// Purpose: 
//----------------------------------------------------------------------------------------------------
stock bool IsValidEntRef(int entity)
{
	if (entity && EntRefToEntIndex(entity) != INVALID_ENT_REFERENCE)
	{
		return true;
	}
	return false;
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock void ReplaceStrings(char[] str, char[] strReplace)
{
	char l_cstrFix[MAXZONES];
	Format(l_cstrFix, sizeof(l_cstrFix), str);
	ReplaceString(l_cstrFix, sizeof(l_cstrFix), "\"", "");
	ReplaceString(l_cstrFix, sizeof(l_cstrFix), strReplace, "");
	TrimString(l_cstrFix);
	Format(str, sizeof(l_cstrFix), l_cstrFix);
}
