#include <amxmodx>
#include <amxmisc>
#include <cstrike>
#include <fakemeta>
#include <fun>
#include <hamsandwich>
#include <zombieplague>

#define TASK_AURA	85927
#define TASK_MODEL	59849

#define ID_AURA		(taskid - TASK_AURA)
#define ID_MODEL	(taskid - TASK_MODEL)

#define zp_get_grenade_type(%1)		(pev(%1, pev_flTimeStepSound))

// Plugin version
new const VERSION[] = "2.1"

new const MAXBPAMMO[] = { -1, 52, -1, 90, 1, 32, 1, 100, 90, 1, 120, 100, 100, 90, 90, 90, 100, 120,
			30, 120, 200, 32, 90, 120, 90, 2, 35, 90, 90, -1, 100 }

new const MAXCLIP[] = { -1, 13, -1, 10, -1, 7, -1, 30, 30, -1, 30, 20, 25, 30, 35, 25, 12, 20,
			10, 30, 100, 8, 30, 30, 20, -1, 7, 30, 30, -1, 50 }

// NADE_TYPE_INFECTION
const NADE_TYPE_INFECTION = 1111

const OFFSET_ACTIVE_ITEM = 373
const OFFSET_LINUX = 5

// Format: models/player/YOUR_CUSTOM_MODEL/YOUR_CUSTOM_MODEL.mdl
// 1. You DO NOT NEED to add "models/player"
// 2. You DO NOT NEED to add ".mdl"
new const VIP_MODEL[] = "vip"

// PCvars
new pCvarToggle, pCvarMode, pCvarRewardZ, pCvarRewardH, pCvarHealth, pCvarArmor, pCvarGlow, pCvarUClip, pCvarPickup, pCvarRespawnZombie, pCvarRespawnDelay, pCvarAura, pCvarAuraRadius

// Cached PCvars
new cToggle, cMode, cRewardZ, cRewardH, cHealth, cArmor, cUClip, cPickup, cRespawnZombie, Float:cRespawnDelay, cAuraRadius

// Player variables
new gVIP[33], gCurWeapon[33], g_szPlayerModel[33][32], bool:g_bCustomModel[33]

// Global variables
new gSwarmPlague

// Message IDs
new gMsgDeathMsg, gMsgScoreInfo

// Hooks
new MaxPlayers

// Forwards
new gFwVIPround, gFwDummyResult

public plugin_init()
{
	register_plugin("[ZP] Addon: BodyGuard Mode", VERSION, "eXcalibur.007")
	
	pCvarToggle = register_cvar("zp_enable_bodyguard", "1")
	pCvarMode = register_cvar("zp_bodyguard_swarm", "1")
	pCvarRewardZ = register_cvar("zp_bodyguard_zombie_rewards", "5")
	pCvarRewardH = register_cvar("zp_bodyguard_human_rewards", "5")
	pCvarHealth = register_cvar("zp_vip_health", "255")
	pCvarArmor = register_cvar("zp_vip_armor", "255")
	pCvarGlow = register_cvar("zp_vip_glow", "255 255 255")
	pCvarUClip = register_cvar("zp_vip_unlimited_ammo", "0")
	pCvarPickup = register_cvar("zp_vip_allow_pick_up", "1")
	pCvarRespawnZombie = register_cvar("zp_zombie_inf_respawn", "1")
	pCvarRespawnDelay = register_cvar("zp_zombie_respawn_delay", "5.0")
	pCvarAura = register_cvar("zp_vip_aura", "255 255 255")
	pCvarAuraRadius = register_cvar("zp_vip_aura_radius", "20")
	
	register_message(get_user_msgid("CurWeapon"), "message_CurWeapon")
	
	register_event("HLTV", "event_new_round", "a", "1=0", "2=0")
	register_event("CurWeapon", "event_CurWeapon", "be", "1=1")
	
	RegisterHam(Ham_Killed, "player", "fw_Killed_Post", 1)
	RegisterHam(Ham_Touch, "weaponbox", "fw_TouchWeapon")
	RegisterHam(Ham_Touch, "armoury_entity", "fw_TouchWeapon")
	RegisterHam(Ham_Touch, "weapon_shield", "fw_TouchWeapon")
	
	gMsgDeathMsg = get_user_msgid("DeathMsg")
	gMsgScoreInfo = get_user_msgid("ScoreInfo")
	
	MaxPlayers = get_maxplayers()
	
	gFwVIPround = CreateMultiForward("zp_round_is_vip", ET_IGNORE)
}

public plugin_precache()
{
	RegisterHam(Ham_TakeDamage, "player", "fw_TakeDamage_Pre")
	
	// If you know scripting, you may want to look at here. If you used the default vip.mdl, you need not to precache it since it's already precache by CS
	new buffer[100]
	formatex(buffer, 99, "models/player/%s/%s.mdl", VIP_MODEL, VIP_MODEL)
	precache_model(buffer)
}

public plugin_cfg()
{
	cToggle = get_pcvar_num(pCvarToggle)
	cMode = get_pcvar_num(pCvarMode)
	cRewardZ = get_pcvar_num(pCvarRewardZ)
	cRewardH = get_pcvar_num(pCvarRewardH)
	cHealth = get_pcvar_num(pCvarHealth)
	cArmor = get_pcvar_num(pCvarArmor)
	cUClip = get_pcvar_num(pCvarUClip)
	cPickup = get_pcvar_num(pCvarPickup)
	cRespawnZombie = get_pcvar_num(pCvarRespawnZombie)
	cRespawnDelay = get_pcvar_float(pCvarRespawnDelay)
	cAuraRadius = get_pcvar_num(pCvarAuraRadius)
	
	new cfgdir[32]
	get_configsdir(cfgdir, charsmax(cfgdir))
	
	server_cmd("exec %s/bodyguard.cfg", cfgdir)
	server_exec()
}

public plugin_natives()
{
	register_native("zp_is_user_vip", "_zp_is_user_vip", 0)
}

public client_disconnect(id)
{
	if(!cToggle)
		return PLUGIN_HANDLED
	
	if(gVIP[id] && gSwarmPlague)
	{
		gVIP[id] = false
		
		new health = get_user_health(id)
		new armor = get_user_armor(id)
		new szNameOldVIP[32]; get_user_name(id, szNameOldVIP, charsmax(szNameOldVIP))
		
		static iPlayersNum
		iPlayersNum = fnGetAliveCTs()
		
		id = fnGetRandomAliveCTs(random_num(1, iPlayersNum))
		
		new szNameNewVIP[32]
		
		gVIP[id] = true
		
		copy(g_szPlayerModel[id], 31, VIP_MODEL)
			
		new szCurrentModel[32]
		fm_get_user_model(id, szCurrentModel, charsmax(szCurrentModel))
		
		if(!equal(szCurrentModel, g_szPlayerModel[id]))
		{
			set_task(0.5, "task_set_model", id + TASK_MODEL)
		}
		
		get_user_name(id, szNameNewVIP, charsmax(szNameNewVIP))
			
		set_hudmessage(255, 0, 0, 0.0, -1.0, 0, 6.0, 5.0)
		show_hudmessage(0, "%s is the new VIP!!", szNameNewVIP)
		
		client_print(0, print_chat, "[ZP] VIP %s has disconnected, %s is chosen as the new VIP.", szNameOldVIP, szNameNewVIP)
		
		set_user_health(id, health)
		set_user_armor(id, armor) // cs_set_user_armor is useless for ZP
		
		set_task(0.1, "vip_aura", id + TASK_AURA, _, _, "b")
		
		new szColor[12], szRed[4], szGreen[4], szBlue[4]
		get_pcvar_string(pCvarGlow, szColor, 11)
		parse(szColor, szRed, 3, szGreen, 3, szBlue, 4)

		new iRed = clamp(str_to_num(szRed), 0, 255)
		new iGreen = clamp(str_to_num(szGreen), 0, 255)
		new iBlue = clamp(str_to_num(szBlue) , 0, 255)
		
		if(iRed == -1 || iGreen == - 1 || iBlue == -1)
			return PLUGIN_HANDLED
			
		set_user_rendering(id, kRenderFxGlowShell, iRed, iGreen, iBlue, kRenderNormal, 5)
	}
	return PLUGIN_HANDLED
}

public event_new_round()
{
	if(gSwarmPlague)
	{
		set_msg_block(gMsgDeathMsg, BLOCK_NOT)
		set_msg_block(gMsgScoreInfo, BLOCK_NOT)
		
		gSwarmPlague = false
	}
	
	static id
	
	for(id = 1; id <= MaxPlayers; id++)
	{
		gVIP[id] = false
	}
}

public event_CurWeapon(id)
{
	if(!is_user_connected(id) || !gVIP[id] || !cToggle || cPickup)
		return PLUGIN_HANDLED
		
	gCurWeapon[id] = read_data(2)
	
	if(gCurWeapon[id] != CSW_KNIFE)
	{
		engclient_cmd(id, "weapon_knife")
	}
	return PLUGIN_HANDLED
}

public fw_TakeDamage_Pre(victim, inflictor, attacker, Float:damage, damagetype)
{
	if(!is_user_connected(attacker) || !cToggle || !gSwarmPlague)
		return HAM_IGNORED
		
	if(zp_get_user_zombie(attacker) && gVIP[victim])
	{
		ExecuteHam(Ham_TakeDamage, victim, inflictor, attacker, Float:damage, damagetype)
		 
		return HAM_SUPERCEDE
	}
	return HAM_IGNORED
}

public fw_Killed_Post(victim, attacker)
{
	if(!cToggle)
		return PLUGIN_HANDLED
	
	if(cRespawnZombie && zp_get_user_zombie(victim) && !zp_get_user_nemesis(victim) && gSwarmPlague)
	{
		set_task(cRespawnDelay, "respawn_zombie", victim)
	}
	
	if(gVIP[victim] && gSwarmPlague)
	{
		gVIP[victim] = false
		
		new szName[32]
		
		get_user_name(attacker, szName, charsmax(szName))
		
		set_hudmessage(255, 0, 0, 0.0, -1.0, 0, 6.0, 5.0)
		show_hudmessage(0, "The VIP was killed by %s!!", szName)
		
		client_print(0, print_chat, "[ZP] The VIP was killed by %s!!", szName)
		
		for(new i = 1; i <= MaxPlayers; i++)
		{	
			set_user_rendering(victim)
			
			if(zp_get_user_zombie(i))
			{
				zp_set_user_ammo_packs(i, zp_get_user_ammo_packs(i) + cRewardZ)
				client_print(i, print_chat, "You've gained %d ammopacks for winning the round", cRewardZ)
			}
			else
			{
				client_print(i, print_chat, "[ZP] You've failed to protect the VIP")
				
				set_msg_block(gMsgDeathMsg, BLOCK_ONCE)
				set_msg_block(gMsgScoreInfo, BLOCK_ONCE)
				
				user_silentkill(i)
			}
		}
	}
	return HAM_IGNORED
}

public fw_TouchWeapon(weapon, id)
{
	if(!is_user_connected(id) || !is_user_alive(id) || cPickup || !cToggle)
		return HAM_IGNORED
	
	if(gVIP[id])
		return HAM_SUPERCEDE
	
	return HAM_IGNORED
}

public vip_aura(taskid)
{
	if(!gVIP[ID_AURA] || !is_user_alive(ID_AURA) || !is_user_connected(ID_AURA))
	{
		remove_task(taskid)
		return
	}
	
	new szColor[12], szRed[4], szGreen[4], szBlue[4]
	get_pcvar_string(pCvarAura, szColor, 11)
	parse(szColor, szRed, 3, szGreen, 3, szBlue, 4)

	new iRed = clamp(str_to_num(szRed), 0, 255)
	new iGreen = clamp(str_to_num(szGreen), 0, 255)
	new iBlue = clamp(str_to_num(szBlue) , 0, 255)
		
	if(iRed == -1 || iGreen == - 1 || iBlue == -1)
	{
		remove_task(taskid)
		return
	}
	
	static origin[3]
	get_user_origin(ID_AURA, origin)
	
	message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
	write_byte(TE_DLIGHT)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2]) 
	write_byte(cAuraRadius)
	write_byte(iRed)
	write_byte(iGreen)
	write_byte(iBlue)
	write_byte(2)
	write_byte(0)
	message_end()
}

public message_CurWeapon(msg_id, msg_dest, msg_entity)
{
	if(!is_user_alive(msg_entity) || zp_get_user_zombie(msg_entity) || !gVIP[msg_entity])
		return
	
	if(get_msg_arg_int(1) != 1)
		return
	
	if(!cUClip)
		return
	
	static weapon
	weapon = get_msg_arg_int(2)
	
	if(MAXBPAMMO[weapon] > 2)
	{
		cs_set_weapon_ammo(get_pdata_cbase(msg_entity, OFFSET_ACTIVE_ITEM, OFFSET_LINUX), MAXCLIP[weapon])
		
		set_msg_arg_int(3, get_msg_argtype(3), MAXCLIP[weapon])
	}
}

public zp_round_started(gamemode, id)
{
	if(!cToggle)
		return PLUGIN_HANDLED
		
	if(gamemode == MODE_SWARM && cMode || gamemode == MODE_PLAGUE && !cMode)
	{
		gSwarmPlague = true
		
		ExecuteForward(gFwVIPround, gFwDummyResult)
		
		new szName[32]
		
		static iPlayersNum
		iPlayersNum = fnGetAliveCTs()
		
		id = fnGetRandomAliveCTs(random_num(1, iPlayersNum))
		
		gVIP[id] = true
		
		copy(g_szPlayerModel[id], 31, VIP_MODEL)
			
		new szCurrentModel[32]
		fm_get_user_model(id, szCurrentModel, charsmax(szCurrentModel))
		
		if(!equal(szCurrentModel, g_szPlayerModel[id]))
		{
			set_task(0.5, "task_set_model", id + TASK_MODEL)
		}
		
		get_user_name(id, szName, charsmax(szName))
		
		set_hudmessage(255, 0, 0, 0.0, -1.0, 0, 6.0, 5.0)
		show_hudmessage(0, "%s is the VIP!!", szName)
		
		client_print(0, print_chat, "[ZP] Protect the VIP")
		
		set_user_health(id, cHealth)
		set_user_armor(id, cArmor) // cs_set_user_armor is useless for ZP
		
		set_task(0.1, "vip_aura", id + TASK_AURA, _, _, "b")
			
		new szColor[12], szRed[4], szGreen[4], szBlue[4]
		get_pcvar_string(pCvarGlow, szColor, 11)
		parse(szColor, szRed, 3, szGreen, 3, szBlue, 4)

		new iRed = clamp(str_to_num(szRed), 0, 255)
		new iGreen = clamp(str_to_num(szGreen), 0, 255)
		new iBlue = clamp(str_to_num(szBlue) , 0, 255)
		
		if(iRed == -1 || iGreen == - 1 || iBlue == -1)
			return PLUGIN_HANDLED
			
		set_user_rendering(id, kRenderFxGlowShell, iRed, iGreen, iBlue, kRenderNormal, 5)
	}
	return PLUGIN_HANDLED
}

public zp_round_ended(winteam)
{
	if(winteam == WIN_HUMANS && cToggle && gSwarmPlague)
	{
		for(new i = 1; i <= MaxPlayers; i++)
		{
			if(!zp_get_user_zombie(i))
			{
				zp_set_user_ammo_packs(i, zp_get_user_ammo_packs(i) + cRewardH)
				client_print(i, print_chat, "You've gained %d ammopacks for winning the round", cRewardH)
			}
			else
			{
				set_msg_block(gMsgDeathMsg, BLOCK_SET)
				set_msg_block(gMsgScoreInfo, BLOCK_SET)
				
				user_silentkill(i)
				
				client_print(i, print_chat, "[ZP] You've failed to kill the VIP")
			}
		}
	}
	return PLUGIN_HANDLED
}

public zp_user_infect_attempt(victim, infector, nemesis)
{
	// Non-player infection or turned into a nemesis
	if(!infector || nemesis || !cToggle)	
		return PLUGIN_CONTINUE
		
	if(gVIP[victim])
	{
		if(zp_get_user_infection_nade(infector) > 0)
			return ZP_PLUGIN_HANDLED
	}
	return PLUGIN_CONTINUE
}

public respawn_zombie(id)
{
	if(cToggle && cRespawnZombie && is_user_connected(id) && !is_user_alive(id))
	{
		zp_respawn_user(id, ZP_TEAM_ZOMBIE)
	}
}

public task_set_model(taskid)
{
	fm_set_user_model(ID_MODEL, g_szPlayerModel[ID_MODEL])
}

fnGetAliveCTs()
{
	static iAlive, id
	iAlive = 0
	
	for(id = 1; id <= MaxPlayers; id++)
	{
		if(is_user_alive(id) && !zp_get_user_zombie(id))
			iAlive++
	}
	return iAlive
}

fnGetRandomAliveCTs(n)
{
	static iAlive, id
	iAlive = 0
	
	for(id = 1; id <= MaxPlayers; id++)
	{
		if(is_user_alive(id) && !zp_get_user_zombie(id))
			iAlive++
		
		if(iAlive == n)
			return id
	}
	return -1
}

// Native: zp_is_user_vip(id)
public _zp_is_user_vip(id)
{
	if(!cToggle)
		return -1
	
	return gVIP[id]
}

// zp_get_user_infection_nade credits: meTaLiCroSS & me(changing to FM)
stock zp_get_user_infection_nade(id)
{
	static iNade
	iNade = fm_get_grenade_id(id, "", 0)
	
	if(iNade > 0 && pev_valid(iNade) 
	&& zp_get_grenade_type(iNade) == NADE_TYPE_INFECTION)	
		return iNade
	
	return 0
}

stock fm_set_user_model(player, modelname[])
{   
	engfunc(EngFunc_SetClientKeyValue, player, engfunc(EngFunc_GetInfoKeyBuffer, player), "model", modelname)
	
	g_bCustomModel[player] = true
}

stock fm_get_user_model(player, model[], len)
{   
	engfunc(EngFunc_InfoKeyValue, engfunc(EngFunc_GetInfoKeyBuffer, player), "model", model, len)
}

stock fm_find_ent_by_owner(index, const classname[], owner, jghgtype = 0)
{
	new strtype[11] = "classname", ent = index
	
	switch (jghgtype)
	{
		case 1: strtype = "target"
		case 2: strtype = "targetname"
	}
	
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, strtype, classname)) && pev(ent, pev_owner) != owner) {}
	
	return ent
}

stock fm_get_grenade_id(id, model[], len, grenadeid = 0)
{
	new ent = fm_find_ent_by_owner(grenadeid, "grenade", id)
	if (ent && len > 0)
		pev(ent, pev_model, model, len)

	return ent
}