#pragma newdecls required

#include <sourcemod>
#include <sdktools>
#include <dhooks>

Handle hSelectSpawnPoint;

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name         = "RandomizeSpawns",
	author       = "zaCade",
	description  = "Randomize player spawns.",
	version      = "1.0.0"
};

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnPluginStart()
{
	Handle hGameConf;
	if ((hGameConf = LoadGameConfigFile("RandomizeSpawns.games")) == INVALID_HANDLE)
	{
		SetFailState("Couldn't load \"RandomizeSpawns.games\" game config!");
		return;
	}

	//
	int iOffset;
	if ((iOffset = GameConfGetOffset(hGameConf, "SelectSpawnPoint")) == -1)
	{
		CloseHandle(hGameConf);
		SetFailState("GameConfGetOffset(hGameConf, \"SelectSpawnPoint\") failed!");
		return;
	}

	if ((hSelectSpawnPoint = DHookCreate(iOffset, HookType_Entity, ReturnType_CBaseEntity, ThisPointer_CBaseEntity, OnPlayerSelectSpawnPoint)) == INVALID_HANDLE)
	{
		CloseHandle(hGameConf);
		SetFailState("DHookCreate(iOffset, HookType_Entity, ReturnType_CBaseEntity, ThisPointer_CBaseEntity, OnPlayerSelectSpawnPoint) failed!");
		return;
	}

	// Late load.
	for (int client = 1; client <= MaxClients; client++)
	{
		if (IsClientInGame(client))
		{
			OnClientPutInServer(client);
		}
	}

	CloseHandle(hGameConf);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnClientPutInServer(int client)
{
	DHookEntity(hSelectSpawnPoint, false, client);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public int RandomizeSpawnPoints(int elem1, int elem2, int[] array, Handle hndl)
{
	return GetRandomInt(-1, 1);
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public MRESReturn OnPlayerSelectSpawnPoint(int client, Handle hReturn)
{
	if (IsValidEntity(client) && !IsClientInGame(client))
		return MRES_Ignored;

	static int spawnPoints[128];
	static int spawnCount;

	if (!spawnCount)
	{
		int spawnPoint = INVALID_ENT_REFERENCE;
		while ((spawnPoint = FindEntityByClassname(spawnPoint, "info_player_*")) != INVALID_ENT_REFERENCE)
		{
			char sClassname[64];
			GetEntityClassname(spawnPoint, sClassname, sizeof(sClassname));

			if (StrEqual(sClassname, "info_player_terrorist", true) || StrEqual(sClassname, "info_player_counterterrorist", true))
			{
				spawnPoints[spawnCount++] = spawnPoint;
			}
		}
	}

	if (spawnCount)
	{
		int spawnCountAttempts;
		while (spawnCountAttempts >= spawnCount)
		{
			SortCustom1D(spawnPoints, spawnCount, RandomizeSpawnPoints);

			int spawnPoint = spawnPoints[0];
			if (IsValidEntity(spawnPoint) && IsValidPlayerSpawn(client, spawnPoint))
			{
				DHookSetReturn(hReturn, spawnPoint);
				return MRES_Supercede;
			}

			spawnCountAttempts++;
		}
	}

	return MRES_Ignored;
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
stock bool IsValidPlayerSpawn(int client, int spawnPoint)
{
	float clientMins[3];
	float clientMaxs[3];
	GetClientMins(client, clientMins);
	GetClientMaxs(client, clientMaxs);

	float spawnPointOrigin[3];
	GetEntPropVector(spawnPoint, Prop_Send, "m_vecOrigin", spawnPointOrigin);

	TR_TraceHullFilter(spawnPointOrigin, spawnPointOrigin, clientMins, clientMaxs, MASK_PLAYERSOLID, IsValidPlayerSpawnFilter);

	return !TR_DidHit();
}

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public bool IsValidPlayerSpawnFilter(int entity, int contentsMask)
{
	return view_as<bool>(entity);
}