/* Plugin generated by AMXX-Studio */

#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <xs>
#include <hamsandwich>

#define PLUGIN "Barnacle"
#define VERSION "0.1.1"
#define AUTHOR "PomanoB"

#define HEALTH_OFFSET 500
#define THINK_DELAY 0.1

#define	MAX_BARNACLES 64

//#define BIOHAZARD

#if defined BIOHAZARD
	#include <biohazard.inc>
	#define TEST_ZOMBIE(%1) is_user_zombie(%1)
	#define INFECT(%1) infect_user(%1, 0)
	#define NO_INFECT(%1) false
#else
	#include <zombieplague.inc>
	#define TEST_ZOMBIE(%1) zp_get_user_zombie(%1)
	#define INFECT(%1) zp_infect_user(%1)
	#define NO_INFECT(%1) (zp_get_user_last_human(%1) || zp_get_user_survivor(%1) || zp_is_survivor_round() || zp_is_swarm_round())
#endif

enum
{
	idle1,
	idle2,
	idle3,
	flinch_sm,
	attack1,
	chewcycle,
	death
}

new const barnacle_class[] = "barnacle"
new g_mdl_gib_head

new g_CvarSpeed, g_CvarDamage, g_CvarHP, g_CvarInfect

new g_TotalBarnacles
new g_Spawns[MAX_BARNACLES][3]

new g_Menu

new g_MapFile[64]

public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR)
		
	register_event("HLTV", "eventFreezeTimeStart", "a", "1=0", "2=0")
	
	register_forward(FM_Think, "fwdThink")
	
	g_CvarSpeed = register_cvar("barnacle_speed", "20.0")
	g_CvarDamage = register_cvar("barnacle_damage", "70.0")
	g_CvarHP = register_cvar("barnacle_hp", "500.0")
	g_CvarInfect = register_cvar("barnacle_infect", "1")
		
	register_clcmd("say /barnacle", "display_menu", ADMIN_CFG, " - display barnacle's menu")
		
	g_Menu = menu_create("Barnacle's Menu","menu_barnacle")
	
	build_menu()
}

public plugin_precache()
{
	precache_model("models/barnacle.mdl")
	precache_model("models/barnaclet.mdl")
	
	precache_sound("barnacle/bcl_alert2.wav")
	precache_sound("barnacle/bcl_chew1.wav")
	precache_sound("barnacle/bcl_chew2.wav")
	precache_sound("barnacle/bcl_chew3.wav")
	precache_sound("barnacle/bcl_die1.wav" )
	precache_sound("barnacle/bcl_die3.wav" )
	
	g_mdl_gib_head = precache_model("models/GIB_Skull.mdl")
	
}

public plugin_cfg()
{
	new map[32], config[32]
	
	get_mapname(map, 31)
	get_configsdir(config, 31)
	format(g_MapFile, 63, "%s/barnacle/%s.cfg",config, map)
	
	loadSpawns()
}

public display_menu(id, level, cid)
{
	if (cmd_access(id, level, cid, 0))
		menu_display(id, g_Menu, 0)
	return PLUGIN_HANDLED
}

public build_menu()
{
	menu_additem(g_Menu, "Add Barnacle", "1")
	menu_additem(g_Menu, "Delete Barnacle", "2")
	menu_additem(g_Menu, "Delete All Barnacle's", "3")
	menu_additem(g_Menu, "Show Statistic", "4")
	menu_additem(g_Menu, "Exit", "0")
	menu_setprop(g_Menu, MPROP_PERPAGE, 0)
}

public menu_barnacle(id, menu, item)
{
	if( item < 0 ) 
		return PLUGIN_CONTINUE
 
	new cmd[3]
	new maccess, callback
 
	menu_item_getinfo(menu, item, maccess, cmd,2,_,_, callback)
	new iChoice = str_to_num(cmd)
	
	switch(iChoice)
	{
		case 1:
		{
			if (g_TotalBarnacles >= MAX_BARNACLES - 1)
				client_print(id, print_chat, "Max Barnacle's for Map!")
			else
			{
				new origin[3]
				get_user_origin(id, origin, 3)
				add_barnacle(origin, id)
			}
		}
		case 2:
			delete_barnacle(id)
		case 3:
			delete_all_barnacles()
		case 4:
			client_print(id, print_chat, "Current Barnacle's Count: %d", g_TotalBarnacles)
	}
	if (iChoice)
		menu_display(id, g_Menu, 0)
	
	return PLUGIN_CONTINUE
}

public add_barnacle(origin[3], id)
{
	
	new Float:fOrigin[3]
	IVecFVec(origin, fOrigin)
	
	fOrigin[2] -= 2.0
	new const Float:direction[4][3] = {{5.0, 0.0, 0.0}, {-5.0, 0.0, 0.0}, {0.0, 5.0, 0.0}, {0.0, -5.0, 0.0}}
	new Float:temp[3]
	xs_vec_copy(fOrigin, temp)
	new i
	for (i = 0; i < 4; i++)
	{
		xs_vec_add(fOrigin, direction[i], temp)
		if (engfunc(EngFunc_PointContents, temp) != -1)
			break
	}
	
	if (i != 4)
		client_print(id, print_center, "Invalid location!")
	else
	{
		new line[54]
		format(line, 53, "%d %d %d",origin[0], origin[1], origin[2])
		write_file(g_MapFile, line, -1)
		
		g_Spawns[g_TotalBarnacles][0] = origin[0]
		g_Spawns[g_TotalBarnacles][1] = origin[1]
		g_Spawns[g_TotalBarnacles][2] = origin[2]
		
		place_barnacle(g_TotalBarnacles)
		
		g_TotalBarnacles++
	}
}

public loadSpawns()
{
	if (file_exists(g_MapFile)) 
	{
		new data[124], len
		new line = 0
		new coord[3][8]
				
		while((g_TotalBarnacles < MAX_BARNACLES) && (line = read_file(g_MapFile , line , data , 123 , len))) 
		{
			if (strlen(data)<2) 
				continue
			
			parse(data, coord[0], 7, coord[1], 7, coord[2], 7)
			
			g_Spawns[g_TotalBarnacles][0] = str_to_num(coord[0])
			g_Spawns[g_TotalBarnacles][1] = str_to_num(coord[1])
			g_Spawns[g_TotalBarnacles][2] = str_to_num(coord[2])
						
			place_barnacle(g_TotalBarnacles)
			
			g_TotalBarnacles++			
		}
	}
	else
		log_amx("Can't find cfg file for this map")
	
	
}

public place_barnacle(barnacle)
{
	new Float:fOrigin[3]
	IVecFVec(g_Spawns[barnacle], fOrigin)
	new ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"info_target"))

	set_pev(ent,pev_classname, barnacle_class)
	engfunc(EngFunc_SetModel, ent, "models/barnacle.mdl")
 
	set_pev(ent, pev_mins, Float:{-16.0,-16.0, -36.0})
	set_pev(ent, pev_maxs, Float:{16.0,16.0,0.0})
	set_pev(ent, pev_size, Float:{-16.0,-16.0, -36.0,16.0,16.0,0.0})
	engfunc(EngFunc_SetSize, ent, Float:{-16.0,-16.0,-36.0}, Float:{16.0,16.0,0.0})
	
	set_pev(ent, pev_solid, SOLID_BBOX)
	set_pev(ent, pev_movetype, MOVETYPE_FLY)

	set_pev(ent, pev_health, get_pcvar_float(g_CvarHP) + HEALTH_OFFSET)
	set_pev(ent, pev_takedamage, 1.0)

	set_pev(ent, pev_dmg, 1.0)

	set_pev(ent, pev_maxspeed, 20.0)

	set_pev(ent,pev_flags, FL_MONSTER|FL_MONSTERCLIP)

	set_pev(ent, pev_origin, fOrigin)
	
	set_pev(ent, pev_animtime, 10.0)
	set_pev(ent, pev_framerate, 0.5)
	set_pev(ent, pev_sequence, idle3)
	set_pev(ent, pev_gaitsequence, idle3)
	
	set_pev(ent, pev_nextthink,get_gametime() + THINK_DELAY)
}

public delete_barnacle(id)
{
	new ent, a_body
	get_user_aiming(id, ent, a_body)
	if (!pev_valid(ent))
		return PLUGIN_CONTINUE
	new class[32]
	pev(ent, pev_classname, class, 31)
	if (!equal(class, barnacle_class))
		return PLUGIN_CONTINUE
		
	engfunc(EngFunc_RemoveEntity, ent)
		
	rewriteBarnacles()
		
	return PLUGIN_CONTINUE
}

public rewriteBarnacles()
{
	delete_file(g_MapFile)
	
	g_TotalBarnacles = 0
	new i
	for (i = 0; i < g_TotalBarnacles; i++)
	{
		arrayset(g_Spawns[i], 0, 3)
	}
	new origin[3], Float:fOrigin[3], line[64]
	new ent = FM_NULLENT
	static string_class[] = "classname"
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, string_class, barnacle_class))) 
	{
		pev(ent, pev_origin, fOrigin)
		FVecIVec(fOrigin, origin)
		g_Spawns[g_TotalBarnacles][0] = origin[0]
		g_Spawns[g_TotalBarnacles][1] = origin[1]
		g_Spawns[g_TotalBarnacles][2] = origin[2]
		g_TotalBarnacles++
		format(line, 53, "%d %d %d", origin[0], origin[1], origin[2])
		write_file(g_MapFile, line, -1)
	}
}

public delete_all_barnacles()
{
	new i
	for (i = 0; i < g_TotalBarnacles; i++)
	{
		arrayset(g_Spawns[i], 0, 3)
	}
	new ent = FM_NULLENT
	static string_class[] = "classname"
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, string_class, barnacle_class))) 
		engfunc(EngFunc_RemoveEntity, ent)
	
	delete_file(g_MapFile)
}

public fwdThink(ent)
{
	if (!pev_valid(ent))
		return
	static class[32]
	pev(ent, pev_classname, class, 31)
	if (equal(barnacle_class, class))
	{
		
		static Float:takedamage, Float:origin[3], Float:endPoint[3], Float:gametime, tr, hit
		pev(ent, pev_takedamage, takedamage)
		if (pev(ent, pev_health) - HEALTH_OFFSET <= 0 && takedamage == DAMAGE_YES)
		{
			set_pev(ent, pev_animtime, 6.6)
			set_pev(ent,pev_framerate, 1.0)
			set_pev(ent,pev_sequence, death)
			
			set_pev(ent, pev_takedamage, DAMAGE_NO)
			set_pev(ent, pev_controller_0, 255)
			set_pev(ent, pev_nextthink, get_gametime() + 99999.0)
			
			set_pev(ent, pev_solid, SOLID_NOT)
			
			switch(random_num(0, 1))
			{
				case 0:
					engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, "barnacle/bcl_die1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
				case 1:
					engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, "barnacle/bcl_die3.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
			}
			
			pev(ent, pev_origin, origin)
			createGibs(origin)
			return			
		}
		else
		if (!pev(ent,pev_enemy) && takedamage == DAMAGE_YES)
		{
			
			pev(ent, pev_origin, origin)
			xs_vec_copy(origin, endPoint)
			endPoint[2]-=2048.0
			engfunc(EngFunc_TraceLine, origin, endPoint, DONT_IGNORE_MONSTERS, ent, tr)
			hit = get_tr2(tr, TR_pHit)
			if (is_user_alive(hit)  && !TEST_ZOMBIE(hit))
			{
				set_pev(ent, pev_enemy, hit)
				set_pev(ent, pev_sequence, chewcycle)
				set_pev(ent, pev_gaitsequence, chewcycle)
				engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, "barnacle/bcl_alert2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
			}
			else
			if (!pev(ent, pev_controller_0))
			{
				get_tr2(tr, TR_vecEndPos, endPoint)
				set_pev(ent, pev_controller_0, 281 - floatround(255.0 / 1024.0 * (origin[2] - endPoint[2])))
			}
			free_tr2(tr)
							
		}
		else
		if (takedamage == DAMAGE_YES)
		{
			static id, Float:distance, Float:flNextAttackTime
			id = pev(ent,pev_enemy)
			if (is_user_alive(id))
			{
				pev(ent, pev_origin, origin)
				pev(id, pev_origin, endPoint)
				distance = origin[2] - endPoint[2]
				if (distance > 100.0)
				{
					endPoint[0] = origin[0]
					endPoint[1] = origin[1]
					endPoint[2] += get_pcvar_float(g_CvarSpeed)
					set_pev(id, pev_origin, endPoint)
					set_pev(ent, pev_controller_0, 261 - floatround(255.0 / 1024.0 * distance))
				}
				else
				{
					if (get_pcvar_num(g_CvarInfect) && !NO_INFECT(id))
					{
						INFECT(id)
						static name[32]
						get_user_name(id, name, 31)
						client_print(0, print_console, "%s infected by Barnacle", name)
						release(ent)
						
					}
					else
					{
						pev(ent, pev_fuser1, flNextAttackTime)
						gametime = get_gametime()
						if (flNextAttackTime <= gametime)
						{
							ExecuteHamB(Ham_TakeDamage, id, ent, id, get_pcvar_float(g_CvarDamage), DMG_GENERIC)
							set_pev(ent, pev_fuser1, gametime + 1.0)
							
							switch(random_num(0, 2))
							{
								case 0:
									engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
								case 1:
									engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
								case 2:
									engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)	
							}
						}
						if (pev(ent,pev_sequence) != attack1)
						{
							set_pev(ent,pev_sequence, attack1)
							set_pev(ent,pev_gaitsequence, attack1)
							set_pev(ent, pev_controller_0, 255)
						}
					}
				}
				set_pev(id, pev_velocity, Float:{0.0, 0.0, 0.0})
			}
			else
				release(ent)
			
		}
		set_pev(ent, pev_nextthink, get_gametime() + THINK_DELAY)
	}
}

public eventFreezeTimeStart()
{
	static string_class[] = "classname"
	new ent = FM_NULLENT
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, string_class, barnacle_class))) 
	{
		set_pev(ent, pev_takedamage, DAMAGE_YES)
		set_pev(ent, pev_health, HEALTH_OFFSET + get_pcvar_float(g_CvarHP))
		set_pev(ent, pev_nextthink, get_gametime() + THINK_DELAY)
		set_pev(ent, pev_solid, SOLID_BBOX)

		release(ent)
	}
}

public createGibs(Float:origin[3])
{
	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_MODEL)
	engfunc(EngFunc_WriteCoord, origin[0])
	engfunc(EngFunc_WriteCoord, origin[1])
	engfunc(EngFunc_WriteCoord, origin[2]-40.0)
	write_coord(random_num(-20, 20))
	write_coord(random_num(-20, 20))
	write_coord(random_num(-20, 0))
	write_angle(random_num(0,360))
	write_short(g_mdl_gib_head)
	write_byte(0)
	write_byte(400)
	message_end()
}

public release(ent)
{
	new Float:origin[3], Float:endPoint[3], tr
	set_pev(ent, pev_enemy, 0)
				
	pev(ent, pev_origin, origin)
	xs_vec_copy(origin, endPoint)
	endPoint[2]-=2048.0
	engfunc(EngFunc_TraceLine, origin, endPoint, IGNORE_MONSTERS, ent, tr)
	
	get_tr2(tr, TR_vecEndPos, endPoint)
	free_tr2(tr)
	set_pev(ent, pev_controller_0, 281 - floatround(255.0 / 1024.0 * (origin[2] - endPoint[2])))
	
	set_pev(ent, pev_animtime, 10.0)
	set_pev(ent, pev_framerate, 0.5)
	set_pev(ent, pev_sequence, idle3)
	set_pev(ent, pev_gaitsequence, idle3)
	
	free_tr2(tr)
}

public event_infect(victim, attacker)
{
	static string_class[] = "classname"
	new ent = FM_NULLENT
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, string_class, barnacle_class))) 
	{
		if (pev(ent, pev_enemy) == victim)
			release(ent)
	}	
}

public zp_user_infected_post(id, infector, nemesis)
{
	static string_class[] = "classname"
	new ent = FM_NULLENT
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, string_class, barnacle_class))) 
	{
		if (pev(ent, pev_enemy) == id)
			release(ent)
	}	
}
/* AMXX-Studio Notes - DO NOT MODIFY BELOW HERE
*{\\ rtf1\\ ansi\\ deff0{\\ fonttbl{\\ f0\\ fnil Tahoma;}}\n\\ viewkind4\\ uc1\\ pard\\ lang1049\\ f0\\ fs16 \n\\ par }
*/
