#include <sourcemod>
#include <sdktools>
#include <zombiereloaded>
int g_bBlockRespawn[MAXPLAYERS+1];
int client_target[MAXPLAYERS + 1];
ConVar g_hRespawnTreshold;
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name = "zh stop zm respawn",
	author = "jenz",
	description = "Disables respawning on zombie hunting maps after some deaths",
	version = "1.0.0",
	url = "www.unloze.com"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	g_hRespawnTreshold = CreateConVar("zh_respawn_count", "5.0", "zombie hunting respawn count", 0, true, 0.0, true, 100.0);
	for (int client; client < MaxClients; client++)
		g_bBlockRespawn[client] = 0;
	
	HookEvent("round_start",  OnRoundStart);
	HookEvent("player_death", OnClientDeath);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientDisconnect(int client)
{
    client_target[client] = 0;
    g_bBlockRespawn[client] = 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnRoundStart(Event hEvent, const char[] sEvent, bool bDontBroadcast)
{
	for (int client; client < MaxClients; client++)
		g_bBlockRespawn[client] = 0;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientDeath(Event hEvent, const char[] sEvent, bool bDontBroadcast)
{
    int victim = GetClientOfUserId(hEvent.GetInt("userid"));
    if (g_bBlockRespawn[victim] > g_hRespawnTreshold.FloatValue)
        return;
    PrintToChat(victim, "\x04[ZR]\x01 You have %f respawns left for this round.", g_hRespawnTreshold.FloatValue - g_bBlockRespawn[victim]);
    g_bBlockRespawn[victim]++;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------

stock bool IsValidClient(int client)
{
	if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
		return true;
	return false;
}

public float get_power_distance(int target_player, float [3]pos)
{
	float vec[3];
	GetClientAbsOrigin(target_player, vec);
	return GetVectorDistance(vec, pos);
}

public Action ZR_OnClientRespawn(&client, &ZR_RespawnCondition:condition)
{
    if (g_bBlockRespawn[client] > g_hRespawnTreshold.FloatValue)
        return Plugin_Handled;

    //teleport player to team members with farthest distance to enemy just.
    //checking all alive clients to determine which client has highest dist_target to its closest enemy
    float total_furthest_distance = -1.0;
    int total_nearest = -1;
    for (int i = 1; i <= MaxClients; i++)
    {
        //if player would get stuck being teleported dont teleport them to this player
        if (!IsValidClient(i) || !IsPlayerAlive(i)) 
        {
            continue;
        }
        if (IsPlayerStuck(i) != -1)
        {
            continue;
        }
        float nearestdistance = -1.0;
        int nearest = -1;
        for (int j = 1; j <= MaxClients; j++)
        if (IsValidClient(j) && IsPlayerAlive(j) && GetClientTeam(i) != GetClientTeam(j) && GetClientTeam(i) == 3)
        {
            float pos[3];
            GetEntPropVector(i, Prop_Send, "m_vecOrigin", pos);
            float dist_target = get_power_distance(j, pos);
            if (nearestdistance < 0 || dist_target < nearestdistance)
            {
                nearest = i;
                nearestdistance = dist_target;
            }
        }
        //the closest enemy to this player is further away than previous players closest enemy
        if (nearestdistance > total_furthest_distance)
        {
            total_furthest_distance = nearestdistance;
            total_nearest = nearest;
        }
    }
    if (IsValidClient(total_nearest))
    {
        client_target[client] = total_nearest;
        CreateTimer(0.0, tp_client, client);
    }
    return Plugin_Continue;
}

stock int IsPlayerStuck(int client) {
    float vecMin[3]; 
    float vecMax[3]; 
    float vecOrigin[3];
    
    GetClientMins(client, vecMin);
    GetClientMaxs(client, vecMax);
    vecMax[2] = 63.0;
    vecMin[2] += 1.0;
    GetClientAbsOrigin(client, vecOrigin);
    
    TR_TraceHullFilter(vecOrigin, vecOrigin, vecMin, vecMax, MASK_PLAYERSOLID, TraceRayDontHitPlayerAndWorld);
    return TR_GetEntityIndex();
}

public bool TraceRayDontHitPlayerAndWorld(int entityhit, int mask) {
    return entityhit>MaxClients
}
public bool TraceRayHitOnlyEnt(int entityhit, int mask, any data) {
    return entityhit==data;
}

public void OnClientPostAdminCheck(int client)
{
    client_target[client] = 0;
}

public Action tp_client(Handle timer, int client)
{
    if (IsValidClient(client) && IsValidClient(client_target[client]))
    {
        float posd[3];
        GetEntPropVector(client_target[client], Prop_Send, "m_vecOrigin", posd);
        TeleportEntity(client, posd, NULL_VECTOR, NULL_VECTOR);
        PrintToChat(client, "Respawned you at player: %N", client_target[client]);
        client_target[client] = 0;
    }
}