public bool isValidClient(int client) {
	if (client <= 0 
		|| client > MaxClients 
		|| !IsValidEntity(client)
		|| !IsClientConnected(client) 
		|| IsFakeClient(client) 
		|| !IsClientInGame(client)
		|| !PM_IsPlayerSteam(client)){
			return false;  
		}
	return true;  
}

public int GetTimerSteamId(int client)
{
	char steamid[32];
	GetClientAuthId(client, AuthId_Steam3, steamid, sizeof(steamid));
	ReplaceString(steamid, sizeof(steamid), "[U:1:", "\0");
	ReplaceString(steamid, sizeof(steamid), "]", "\0");
	return StringToInt(steamid);
}

public void ClearPlayerCache(int client)
{
	g_iActivity[client] = -1;
	g_fStartTime[client] = -1.0;
	g_fMapTime[client] = 0.0;
	if (g_arrayRun[client] != INVALID_HANDLE)
		g_arrayRun[client].Clear();
	else
		g_arrayRun[client] = new ArrayList(RUNDATA_MAX);
}

public void LowerString(char[] sString, int len)
{
	char sTemp[PLATFORM_MAX_PATH];
	Format(sTemp, sizeof(sTemp), "%s", sString);
	for (int i = 0; i < len; i++)
	{
		if (!IsCharLower(sTemp[i]))
		{
			sTemp[i] = CharToLower(sTemp[i]);
		}
	}
	Format(sString, len, "%s", sTemp);
}

public void DrawFullZone(float fMin[3], float fMax[3], int color[4], float life)
{
	float point[8][3];
	float size = 3.0;

	point[0][0] = fMin[0];
	point[0][1] = fMin[1];
	point[0][2] = fMax[2];

	point[1][0] = fMax[0];
	point[1][1] = fMin[1];
	point[1][2] = fMax[2];

	point[2][0] = fMin[0];
	point[2][1] = fMax[1];
	point[2][2] = fMax[2];

	point[3][0] = fMax[0];
	point[3][1] = fMax[1];
	point[3][2] = fMax[2];

	point[4][0] = fMin[0];
	point[4][1] = fMin[1];
	point[4][2] = fMin[2]+100;

	point[5][0] = fMax[0];
	point[5][1] = fMin[1];
	point[5][2] = fMin[2]+100;

	point[6][0] = fMin[0];
	point[6][1] = fMax[1];
	point[6][2] = fMin[2]+100;

	point[7][0] = fMax[0];
	point[7][1] = fMax[1];
	point[7][2] = fMin[2]+100;
	
	if (g_iEditor == -1)
		return;

	TE_SetupBeamPoints(point[4],point[5],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[4],point[6],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[7],point[6],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[7],point[5],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[0],point[1],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[0],point[2],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[0],point[4],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[3],point[2],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[3],point[1],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[3],point[7],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[1],point[5],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
	TE_SetupBeamPoints(point[2],point[6],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_SendToClient(g_iEditor, 0.0);
}

public void DrawZone(int[] clients, int numClients, float fMin[3], float fMax[3], int color[4], float life)
{
	float point[4][3];
	float size = 6.0;
	
	point[0][0] = fMin[0];
	point[0][1] = fMin[1];
	
	point[1][0] = fMax[0];
	point[1][1] = fMin[1];
	
	point[2][0] = fMin[0];
	point[2][1] = fMax[1];
	
	point[3][0] = fMax[0];
	point[3][1] = fMax[1];
	
	if (fMax[2] <= fMin[2] + 100)
	{
		point[0][2] = fMax[2] + 2;
		point[1][2] = fMax[2] + 2;
		point[2][2] = fMax[2] + 2;
		point[3][2] = fMax[2] + 2;
	}
	else
	{
		point[0][2] = fMin[2] + 102;
		point[1][2] = fMin[2] + 102;
		point[2][2] = fMin[2] + 102;
		point[3][2] = fMin[2] + 102;
	}

	TE_SetupBeamPoints(point[0],point[1],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_Send(clients, numClients);
	TE_SetupBeamPoints(point[0],point[2],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_Send(clients, numClients);
	TE_SetupBeamPoints(point[3],point[2],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_Send(clients, numClients);
	TE_SetupBeamPoints(point[3],point[1],g_iBeam, 0, 0, 30, life, size, size, 1, 0.0, color, 0);TE_Send(clients, numClients);
}

public void VectorToString(char[] buffer, int maxlength, float vector[3])
{
	Format(buffer, maxlength, "%f|%f|%f", vector[0], vector[1], vector[2]);
}

public float StringToVector(float vector[3], char[] buffer){
	char cords[3][24];

	ExplodeString(buffer, "|", cords, sizeof cords, sizeof cords[]);

	for (int i = 0; i < 3; i++)
		vector[i] = StringToFloat(cords[i]);
}

public void TimerFormat(float fTime, char[] sBuffer, int len, bool showMS, bool addsymbol)
{
	Format(sBuffer, len, "");
	
	int Min, Sec;
	float Ms;
	
	if(fTime < 0)
	{
		fTime *= -1;
		if (addsymbol)
			Format(sBuffer, len, "-");
	}
	else
		if (addsymbol)
			Format(sBuffer, len, "+");
	
	Min = RoundToFloor(fTime) / 60;
	Sec = RoundToFloor(fTime) - (Min * 60);
	if (showMS) 
		Ms = fTime - ((Min * 60) + Sec);
	
	char Mins[3], Secs[3], MiliSecond[16];
	if (Min < 10)
		Format(Mins, sizeof(Mins), "0%i", Min);
	else
		Format(Mins, sizeof(Mins), "%i", Min);
	
	if (Sec < 10)
		Format(Secs, sizeof(Secs), "0%i", Sec);
	else
		Format(Secs, sizeof(Secs), "%i", Sec);
	
	if (Ms < 10 && showMS)
		Format(MiliSecond, sizeof(MiliSecond), "0%.2f", Ms);
	else
		Format(MiliSecond, sizeof(MiliSecond), "%.2f", Ms);
	
	if (!showMS)
		Format(sBuffer, len, "%s%s:%s", sBuffer, Mins, Secs);
	else
		Format(sBuffer, len, "%s%s:%s.%s", sBuffer, Mins, Secs, MiliSecond[3]);
}

public void GetTimerTimeLeft(char[] buffer, int maxlength)
{
	int timeleft;

	GetMapTimeLeft(timeleft);
	
	if (timeleft <= 60)
	{
		if (timeleft < 1)
			Format(buffer, maxlength, "Map ending", timeleft);
		else if (timeleft == 1)
			Format(buffer, maxlength, "1 second", timeleft);
		else
			Format(buffer, maxlength, "%i seconds", timeleft);
	}
		
	
	else
	{
		int iMinutes = (timeleft / 60);
		
		Format(buffer, maxlength, "%i", iMinutes);
		
		if (iMinutes == 1)
			Format(buffer, maxlength, "%s minute", buffer);
		else
			Format(buffer, maxlength, "%s minutes", buffer);
		
	}
}

public void TimerPrintToChat(int client, bool toall, char[] sText, any:...)
{
	int[] targets = new int[MaxClients];
	int numTargets;
	if (toall)
	{
		for (int i = 1; i <= MaxClients; i++)
		{
			if (IsClientInGame(i))
			{
				targets[numTargets] = i;
				numTargets++;
			}
		}
	}
	else
	{
		targets[0] = client;
		numTargets = 1;
	}
	
	char finalmessage[MAXLENGTH_MESSAGE], cBuffer[MAXLENGTH_MESSAGE];
	strcopy(cBuffer, sizeof(cBuffer), sText);
	VFormat(finalmessage, MAXLENGTH_MESSAGE, cBuffer, 4);
	Format(cBuffer, MAXLENGTH_MESSAGE, "%T", "Tag", LANG_SERVER);
	CFormat(finalmessage, MAXLENGTH_MESSAGE, "%s%s", cBuffer, finalmessage);
	
	SayText2(targets, numTargets, client, finalmessage);
	
}

//forums.alliedmods.net/showpost.php?p=1709517&postcount=35?p=1709517&postcount=35
public void CFormat(char[] buffer, int maxlength, char[] sText, any:...)
{
	char cBuffer[MAXLENGTH_MESSAGE];
	
	strcopy(cBuffer, sizeof(cBuffer), sText);
	VFormat(buffer, maxlength, cBuffer, 4);
	
	ReplaceString(buffer, maxlength, "{default}", "\x01", false);
	
	int iStart, iEnd, iTotal;
	char sHex[9], sCodeBefore[12], sCodeAfter[10];
	
	while ((iStart = StrContains(buffer[(iTotal)], "{#")) != -1) 
	{
	    if ((iEnd = StrContains(buffer[iTotal+iStart+2], "}")) != -1) 
	    {
	        if (iEnd == 6 || iEnd == 8) 
	        {
	            strcopy(sHex, iEnd+1, buffer[iTotal+iStart+2]);
	            Format(sCodeBefore, sizeof(sCodeBefore), "{#%s}", sHex);
	            Format(sCodeAfter, sizeof(sCodeAfter), (iEnd == 6 ? "\x07%s" : "\x08%s"), sHex);
	            ReplaceString(buffer, maxlength, sCodeBefore, sCodeAfter);
	            iTotal += iStart + iEnd + 1;
	        }
	        else {
	            iTotal += iStart + iEnd + 3;
	        }
	    }
	    else {
	        break;
	    }
	}
}

public void SayText2(int[] targets, int numTargets, int author, char[] szMessage)
{
	Handle hBuffer = StartMessage("SayText2", targets, numTargets, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
	
	if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) 
	{
		PbSetInt(hBuffer, "ent_idx", author);
		PbSetBool(hBuffer, "chat", true);
		PbSetString(hBuffer, "msg_name", szMessage);
		PbAddString(hBuffer, "params", "");
		PbAddString(hBuffer, "params", "");
		PbAddString(hBuffer, "params", "");
		PbAddString(hBuffer, "params", "");
	}
	else
	{
		BfWriteByte(hBuffer, author);
		BfWriteByte(hBuffer, true);
		BfWriteString(hBuffer, szMessage);
	}
	
	EndMessage();
}

public void PrintKeyHintText(int client, char[] format, any:...)
{
    Handle userMessage = StartMessageOne("KeyHintText", client);
   
    if (userMessage == INVALID_HANDLE) {
        return;
    }
 
    char buffer[254];
 
    SetGlobalTransTarget(client);
    VFormat(buffer, sizeof(buffer), format, 3);
 
    if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available
        && GetUserMessageType() == UM_Protobuf) {
 
        PbSetString(userMessage, "hints", buffer);
    }
    else {
        BfWriteByte(userMessage, 1);
        BfWriteString(userMessage, buffer);
    }
 
    EndMessage();
}

public int Get2VecVelocity(int client)
{
	float vel[3];
	GetEntPropVector(client, Prop_Data, "m_vecVelocity", vel);

	for(int i = 0; i <= 2; i++)
		vel[i] *= vel[i];

	return RoundToFloor(SquareRoot(vel[0] + vel[1]));
}

//https://github.com/InfluxTimer/sm-timer/blob/master/addons/sourcemod/scripting/influx_prespeed.sp
public void CheckSpeed(int client)
{
	float vel[3];
	GetEntPropVector(client, Prop_Data, "m_vecVelocity", vel);
	

	/*for(int i = 0; i <= 2; i++)
		vel[i] *= vel[i];*/
		
	float speed = SquareRoot(vel[0] * vel[0] + vel[1] * vel[1]);
	
	//PrintToChatAll("Exit Vel: %.2f", speed);
	
	if (speed > MAXVELOCITY)
	{	
		float m = speed / MAXVELOCITY;
		
		//PrintToChatAll("Max Velocity. Factor: %.2f", m);
		
		vel[0] /= m;
		vel[1] /= m;
		vel[2] /= m;
		
		TimerPrintToChat(client, false, "%T", "MaxVelocityWarning", LANG_SERVER);
		
		//PrintToChatAll("Velocity reduced to: %i", RoundToFloor(SquareRoot(vel[0] * vel[0] + vel[1] * vel[1])));
		
		TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vel);
   }
}

public int GetSpecCount(int client)
{
	int count;
	for (int i = 1; i <= MaxClients; i++)
	{
		if (isValidClient(i) && IsClientObserver(i))
		{
			int mode = GetEntProp( i, Prop_Send, "m_iObserverMode" );
			if ( mode == 4 || mode == 5 ) 
			{
				int target = GetEntPropEnt( i, Prop_Send, "m_hObserverTarget");
				if (target == client)
					count++;
			}
		}
	}
	return count;
}

public void GetMiddleOfABox(float vec1[3], float vec2[3], float buffer[3])
{
	float mid[3];
	MakeVectorFromPoints(vec1, vec2, mid);
	mid[0] = mid[0] / 2.0;
	mid[1] = mid[1] / 2.0;
	mid[2] = mid[2] / 2.0;
	AddVectors(vec1, mid, buffer);
}

CSWeaponID GetWeaponID(int client)
{
	CSWeaponID weaponID = CSWeapon_NONE;
	
	int weaponIndex = GetEntDataEnt2(client, m_hActiveWeapon);
	if (weaponIndex != -1)
	{
		static char classname[64];
		GetEdictClassname(weaponIndex, classname, sizeof(classname));
		ReplaceString(classname, sizeof(classname), "weapon_", "");
		
		static char wepAlias[64];
		CS_GetTranslatedWeaponAlias(classname, wepAlias, sizeof(wepAlias));
		weaponID = CS_AliasToWeaponID(wepAlias);
	}
	return weaponID;
}