#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <hamsandwich>
 
#define VERSION "0.2.7"
 
#define MAX_PLAYERS 32
 
enum _:BombSites_Datas {
	iBsIndex,
	szBsModel[5]
}
 
enum _:BombSites_States {
	AllSites,
	MainSite,
	NoSite
}
 
#define StatusIconDontShow 0
#define StatusIconShow 1
 
new const func_bomb_target[] = "func_bomb_target"
new const info_bomb_target[] = "info_bomb_target"
 
new const classname[] = "classname"
 
#define FIND_ENT_BY_CLASSNAME(%1,%2)	engfunc(EngFunc_FindEntityByString, %1, classname, %2)
 
#define XTRA_OFS_PLAYER 5
#define m_iTeam 114
#define cs_get_user_team_index(%1)		get_pdata_int(%1, m_iTeam, XTRA_OFS_PLAYER)
#define CS_TEAM_CT 2
 
new g_iMaxPlayers
 
new g_pCvarMinCts, g_pCvarMidCts
new g_pCvarStartRoundText, g_pCvarPlanterText
 
new bool:g_bMainSiteSet
 
new szConfigFile[64], g_szMapName[32]
new g_mMainBombSite[BombSites_Datas]
new g_szBombSiteName[16]
new Array:g_aBombSites
 
new Float:g_flCountUntilGameTime
new bool:g_bCountNewSpawns
 
new g_iSyncHud
 
new mp_buytime
 
new gmsgStatusIcon
 
new g_iBombSites_State
 
new g_iC4Carrier
 
public plugin_init()
{
	new iEnt, mDatas[BombSites_Datas]
	while( (iEnt = FIND_ENT_BY_CLASSNAME(iEnt, func_bomb_target)) )
	{
		if( g_aBombSites == Invalid_Array )
		{
			g_aBombSites = ArrayCreate(BombSites_Datas)
		}
 
		mDatas[iBsIndex] = iEnt
		pev(iEnt, pev_model, mDatas[szBsModel], charsmax(mDatas[szBsModel]))
		ArrayPushString(g_aBombSites, mDatas)
	}
	while( (iEnt = FIND_ENT_BY_CLASSNAME(iEnt, info_bomb_target)) )
	{
		if( g_aBombSites == Invalid_Array )
		{
			g_aBombSites = ArrayCreate(BombSites_Datas)
		}
 
		mDatas[iBsIndex] = iEnt
		pev(iEnt, pev_model, mDatas[szBsModel], charsmax(mDatas[szBsModel]))
		ArrayPushString(g_aBombSites, mDatas)
	}
 
	if( g_aBombSites == Invalid_Array )
	{
		register_plugin("c4 management", "NotBombMap", "ConnorMcLeod")
		pause("ad")
		return
	}
 
	register_plugin("c4 management", VERSION, "ConnorMcLeod")
	register_dictionary("c4management.txt")
 
	g_pCvarMinCts = register_cvar("bm_minct", "0")
	g_pCvarMidCts = register_cvar("bm_midct", "5")
	g_pCvarStartRoundText = register_cvar("bm_roundstart_text", "2")
	g_pCvarPlanterText = register_cvar("bm_planter_text", "1")
 
	get_mapname(g_szMapName, charsmax(g_szMapName))
 
	get_localinfo("amxx_configsdir", szConfigFile, charsmax(szConfigFile))
	format(szConfigFile, charsmax(szConfigFile), "%s/bombsites.ini", szConfigFile)
	if( !file_exists(szConfigFile) )
	{
		new iFile = fopen(szConfigFile, "at")
		fprintf(iFile, "; c4 management configuration file^n")
		fclose(iFile)
	}
 
	g_iMaxPlayers = get_maxplayers()
 
	GetMainBombSiteByFile()
 
	register_event("HLTV", "Event_HLTV_New_Round", "a", "1=0", "2=0")
	register_logevent("LogEvent_RoundStart", 2, "1=Round_Start")
	register_logevent("Logevent_Round_End", 2, "1=Round_End")
	RegisterHam(Ham_Spawn, "player", "Player_Spawn_Post", 1)
 
	register_event("WeapPickup", "Event_WeapPickup_c4", "be", "1=6")
	register_message(get_user_msgid("StatusIcon"), "Message_StatusIcon")
 
	register_event("BombDrop", "Event_BombDrop", "bc")
 
	register_event("TextMsg", "Event_TextMsg_C4PlantAtBombSpot", "be", "1=4", "2=#C4_Plant_At_Bomb_Spot")
 
	register_clcmd("bm_mark_bs", "AdminCommand_MarkBombSite", ADMIN_CFG, " <BombSite Name>")
 
	g_iSyncHud = CreateHudSyncObj()
	mp_buytime = get_cvar_pointer("mp_buytime")
	gmsgStatusIcon = get_user_msgid("StatusIcon")
}
 
GetMainBombSiteByFile()
{
	new iFile = fopen(szConfigFile, "rt")
	if( iFile )
	{
		new szData[64], szMapname[32], szModel[5], szBombSiteName[16]
		while( fgets(iFile, szData, charsmax(szData)) )
		{
			if( !szData[0] || szData[0] == '/' || szData[0] == ';' || szData[0] == '#' )
			{
				continue
			}
 
			trim(szData)
			parse(szData, szMapname, charsmax(szMapname), 
						szModel, charsmax(szModel), 
						szBombSiteName, charsmax(szBombSiteName))
 
			if( equali(szMapname, g_szMapName) )
			{
				new iEnt
				// less efficient than engine but used only at map start
				iEnt = FIND_ENT_BY_MODELNAME(g_iMaxPlayers, func_bomb_target, szModel)
 
				if( !iEnt )
				{
					iEnt = FIND_ENT_BY_MODELNAME(g_iMaxPlayers, info_bomb_target, szModel)
				}
 
				if( iEnt )
				{
					g_mMainBombSite[iBsIndex] = iEnt
					g_mMainBombSite[szBsModel] = szModel
					remove_quotes(szBombSiteName)
					g_szBombSiteName = szBombSiteName
 
					g_bMainSiteSet = true
				}				
				break
			}
		}
		fclose(iFile)
	}
}
 
public plugin_pause()
{
	new iBombSiteNum = ArraySize(g_aBombSites)
	new mBombSiteDatas[BombSites_Datas]
 
	for(new i; i<iBombSiteNum; i++)
	{
		ArrayGetArray(g_aBombSites, i, mBombSiteDatas)
		SetBombSite(mBombSiteDatas[iBsIndex], true)
	}
}
 
public plugin_end()
{
	if( g_aBombSites != Invalid_Array )
	{
		ArrayDestroy(g_aBombSites)
	}
}
 
public Event_HLTV_New_Round()
{
	g_iC4Carrier = 0
	g_bCountNewSpawns = false
	g_iBombSites_State = AllSites
}
 
public Event_BombDrop()
{
	g_iC4Carrier = 0
 
	if( read_data(4) ) // Bomb Planted
	{
		g_bCountNewSpawns = false
	}
}
 
public Logevent_Round_End()
{
	g_bCountNewSpawns = false
}
 
public LogEvent_RoundStart()
{
	g_bCountNewSpawns = true
	g_flCountUntilGameTime = get_gametime() + get_pcvar_float(mp_buytime) * 60.0
 
	if(!g_bMainSiteSet)
	{
		return
	}
 
	CountCounterTerrorists()
}
 
public AdminCommand_MarkBombSite(id, level, cid)
{
	if( !cmd_access(id, level, cid, 2) )
	{
		return PLUGIN_HANDLED
	}
 
	if(g_bMainSiteSet)
	{
		client_print(id, print_console, "%L", id, "BM_ALREADY")
		client_cmd(id, "spk buttons/latchlocked1")
		return PLUGIN_HANDLED
	}
 
	new iBombSiteNum = ArraySize(g_aBombSites)
 
	new mBombSiteDatas[BombSites_Datas], iEnt
	new Float:fVecOrigin[3], Float:fVecAbsMin[3], Float:fVecAbsMax[3]
 
	for(new i=0; i<iBombSiteNum; i++)
	{
		ArrayGetArray(g_aBombSites, i, mBombSiteDatas)
		iEnt = mBombSiteDatas[iBsIndex]
 
		pev(id, pev_origin, fVecOrigin)
		pev(iEnt, pev_absmin, fVecAbsMin)
		pev(iEnt, pev_absmax, fVecAbsMax)
 
		if(	(fVecAbsMin[0] <= fVecOrigin[0] <= fVecAbsMax[0])
		&&	(fVecAbsMin[1] <= fVecOrigin[1] <= fVecAbsMax[1])
		&&	(-72.0 <= (((fVecAbsMax[2] + fVecAbsMin[2]) / 2.0) - fVecOrigin[2]) <= 72.0)	)
		{
			read_argv(1, g_szBombSiteName, charsmax(g_szBombSiteName))
			new iFile = fopen(szConfigFile, "at")
			fprintf(iFile, "^n%s %s ^"%s^"", g_szMapName, mBombSiteDatas[szBsModel], g_szBombSiteName)
			fclose(iFile)
 
			g_mMainBombSite = mBombSiteDatas
			g_bMainSiteSet = true
 
			client_print(id, print_console, "%L", id, "BM_DEFINED")
			client_cmd(id, "spk events/task_complete")
			return PLUGIN_HANDLED
		}
	}
 
	client_print(id, print_console, "%L", id, "BM_NOTBOMBSITE")
	client_cmd(id, "spk events/friend_died")
	return PLUGIN_HANDLED
}
 
public Event_WeapPickup_c4( id )
{
	g_iC4Carrier = id
}
 
public Message_StatusIcon(/*iMsgId, iMsgDest, id*/)
{
	if( g_iBombSites_State != AllSites && get_msg_arg_int(1) == StatusIconShow )
	{
		new szIcon[4]
		get_msg_arg_string(2, szIcon, charsmax(szIcon))
		if(	get_msg_arg_string(2, szIcon, charsmax(szIcon)) == 2 // check "c4" length first
		&&	equal(szIcon, "c4")	)
		{
			switch( g_iBombSites_State )
			{
				case NoSite:
				{
					set_msg_arg_int(1, ARG_BYTE, 0)
				}
				case MainSite:
				{
					set_msg_arg_int(3, ARG_BYTE, 200)
					set_msg_arg_int(4, ARG_BYTE, 100)
				}
			}
		}
	}
}
 
public Event_TextMsg_C4PlantAtBombSpot(id)
{
	if( !get_pcvar_num(g_pCvarPlanterText) )
	{
		return
	}
 
	new iBombSiteNum = ArraySize(g_aBombSites)
 
	new mBombSiteDatas[BombSites_Datas], iEnt
	new Float:fVecOrigin[3], Float:fVecAbsMin[3], Float:fVecAbsMax[3]
 
	for(new i=0; i<iBombSiteNum; i++)
	{
		ArrayGetArray(g_aBombSites, i, mBombSiteDatas)
		iEnt = mBombSiteDatas[iBsIndex]
 
		pev(id, pev_origin, fVecOrigin)
		pev(iEnt, pev_absmin, fVecAbsMin)
		pev(iEnt, pev_absmax, fVecAbsMax)
 
		if(	(fVecAbsMin[0] <= fVecOrigin[0] <= fVecAbsMax[0])
		&&	(fVecAbsMin[1] <= fVecOrigin[1] <= fVecAbsMax[1])
		&&	(-72.0 <= (((fVecAbsMax[2] + fVecAbsMin[2]) / 2.0) - fVecOrigin[2]) <= 72.0)	)
		{
			switch( g_iBombSites_State )
			{
				case NoSite:
				{
					client_print(id, print_center, "%L", id, "BM_AD_NOPLANT", get_pcvar_num(g_pCvarMinCts))
				}
				case MainSite:
				{
					client_print(id, print_center, "%L", id, "BM_AD_ONLYMAIN", 
											get_pcvar_num(g_pCvarMidCts), g_szBombSiteName)
				}
			}
 
			break
		}
	}
}
 
public Player_Spawn_Post(id)
{
	if(	g_bMainSiteSet
	&&	g_bCountNewSpawns
	&&	is_user_alive(id)
	&&	cs_get_user_team_index(id) == CS_TEAM_CT	)
	{
		if( g_flCountUntilGameTime < get_gametime() )
		{
			g_bCountNewSpawns = false
			return
		}
		CountCounterTerrorists()
	}
}
 
CountCounterTerrorists()
{
	new iPlayers[MAX_PLAYERS], iNum, iCtNum
	get_players(iPlayers, iNum, "h")
 
	for(new i; i<iNum; i++)
	{
		if( cs_get_user_team_index(iPlayers[i]) == CS_TEAM_CT )
		{
			iCtNum++
		}
	}
 
	new iMinCts = get_pcvar_num(g_pCvarMinCts)
	new iMidCts = get_pcvar_num(g_pCvarMidCts)
 
	new const szTag[] = "C4 Management"
 
	new iBombSiteNum = ArraySize(g_aBombSites)
	new mBombSiteDatas[BombSites_Datas]
 
	if( iCtNum < iMinCts)
	{
		switch( get_pcvar_num(g_pCvarStartRoundText) )
		{
			case 1:
			{
				client_print(0, print_chat, "* [%s] %L", szTag, LANG_PLAYER, "BM_AD_NOPLANT", iMinCts)
			}
			case 2:
			{
				set_hudmessage(200, 50, 50, -1.0, 0.15, .holdtime=8.0, .channel=-1)
				ShowSyncHudMsg(0, g_iSyncHud, "%s : %L", szTag, LANG_PLAYER, "BM_AD_NOPLANT", iMinCts)
			}
		}
 
		for(new i; i<iBombSiteNum; i++)
		{
			ArrayGetArray(g_aBombSites, i, mBombSiteDatas)
			SetBombSite(mBombSiteDatas[iBsIndex], false)
		}
 
		g_iBombSites_State = NoSite
		if( g_iC4Carrier )
		{
			Send_StatusIcon_c4(g_iC4Carrier)
		}
	}
	else if( iCtNum < iMidCts)
	{
		switch( get_pcvar_num(g_pCvarStartRoundText) )
		{
			case 1:
			{
				client_print(0, print_chat, "* [%s] %L", szTag, LANG_PLAYER, "BM_AD_ONLYMAIN", iMidCts, g_szBombSiteName)
			}
			case 2:
			{
				set_hudmessage(0, 255, 0, -1.0, 0.15, .holdtime=8.0, .channel=-1)
				ShowSyncHudMsg(0, g_iSyncHud, "%s : %L", szTag, LANG_PLAYER, "BM_AD_ONLYMAIN", iMidCts, g_szBombSiteName)
			}
		}
 
		new iEnt
		for(new i; i<iBombSiteNum; i++)
		{
			ArrayGetArray(g_aBombSites, i, mBombSiteDatas)
			iEnt = mBombSiteDatas[iBsIndex]
			SetBombSite(iEnt, iEnt == g_mMainBombSite[iBsIndex] ? true : false)
		}
 
		g_iBombSites_State = MainSite
		if( g_iC4Carrier )
		{
			Send_StatusIcon_c4(g_iC4Carrier, StatusIconShow, 200, 100, 0)
		}
	}
	else
	{
		switch( get_pcvar_num(g_pCvarStartRoundText) )
		{
			case 1:
			{
				client_print(0, print_chat, "* [%s] %L", szTag, LANG_PLAYER, "BM_AD_ALLSITES")
			}
			case 2:
			{
				set_hudmessage(50, 200, 50, -1.0, 0.15, .holdtime=8.0, .channel=-1)
				ShowSyncHudMsg(0, g_iSyncHud, "%s : %L", szTag, LANG_PLAYER, "BM_AD_ALLSITES")
			}
		}
 
		for(new i; i<iBombSiteNum; i++)
		{
			ArrayGetArray(g_aBombSites, i, mBombSiteDatas)
			SetBombSite(mBombSiteDatas[iBsIndex], true)
		}
 
		g_iBombSites_State = AllSites
		if( g_iC4Carrier )
		{
			Send_StatusIcon_c4(g_iC4Carrier, StatusIconShow, 0, 160, 0)
		}
	}
}
 
SetBombSite(iEnt, bool:bActive)
{
	set_pev(iEnt, pev_solid, bActive ? SOLID_TRIGGER : SOLID_NOT)
}
 
Send_StatusIcon_c4(id, Mode=StatusIconDontShow, r=0, g=0, b=0)
{
	message_begin(MSG_ONE_UNRELIABLE, gmsgStatusIcon, .player=id)
	write_byte(Mode)
	write_string("c4")
	if( Mode )
	{
		write_byte(r)
		write_byte(g)
		write_byte(b)
	}
	message_end()
}
 
FIND_ENT_BY_MODELNAME(const iStartEnt, const szClassName[], const szModel[])
{
	new iEnt = iStartEnt
	new szMdl[72]
	while( (iEnt = FIND_ENT_BY_CLASSNAME(iEnt, szClassName)) )
	{
		pev(iEnt, pev_model, szMdl, charsmax(szMdl))
		if( equal(szMdl, szModel) )
		{
			return iEnt
		}
	}
	return 0
}