#include <amxmodx>
#include <engine>
#include <fakemeta>
#include <zombie_mode_ai>
#include <hamsandwich>
#include <xs>

#define TASK_DO 423423

#define DISTANCE 100.0
#define TIME_FROZEN 3.0
#define TASK_REMOVE 2323423

new g_zclass_froster, modelindex
new const grenade_frost_sound[] = "zombie_mode_ai/impalehit.wav"
new const grenade_frost_break_sound[] = "zombie_mode_ai/impalelaunch1.wav"
new const zb_model[] = "models/zombie_mode_ai/npc_froster_zombie.mdl"
new g_did_skill[200]
new frostsprite, g_glassSpr
new g_frozen[33]
new g_frozen_gravity[33], Float:g_frozen_speed[33]

public plugin_init()
{
	register_plugin("[ZMAI] Zombie Class: Froster", "1.0", "Dias")
	
	register_event("HLTV", "remove_ent", "a", "1=0", "2=0")
	register_logevent("remove_ent", 2, "1=Round_End")	
}

public plugin_precache()
{
	modelindex = precache_model(zb_model)
	precache_sound(grenade_frost_sound)
	precache_sound(grenade_frost_break_sound)
	
	frostsprite = precache_model("sprites/nhth1.spr")
	g_glassSpr = engfunc(EngFunc_PrecacheModel, "models/glassgibs.mdl")
	
	g_zclass_froster = zmai_register_class(100.0, 200.0, zb_model, modelindex)
}

public remove_ent(ent)
{
	if(g_frozen[ent])
	{
		set_user_frozen(ent, false, 0.0)
		remove_task(ent+TASK_REMOVE)
	}
	
	if(task_exists(TASK_DO)) remove_task(TASK_DO)
}

public zmai_zombie_create(ent)
{
	if (zmai_is_zombie(ent) && zmai_is_valid_zombie(ent) && zmai_get_class(ent) == g_zclass_froster)
	{
		new Ent[1]
		Ent[0] = ent
		set_task(random_float(5.0, 10.0), "find_enemy", TASK_DO, Ent, sizeof(Ent), "b")
	}
}

public find_enemy(Ent[])
{
	new ent = Ent[0]
	new Float:distance
	
	if(!is_valid_ent(ent))
		return PLUGIN_HANDLED
	
	static Victim, Float:Origin[3]
	Victim = zmai_get_current_target(ent)
	pev(ent, pev_origin, Origin)
	
	if(!is_user_alive(Victim) || g_frozen[Victim])
		return PLUGIN_HANDLED
	
	if(zmai_get_class(ent) != g_zclass_froster)
		return PLUGIN_HANDLED
	
	if(!g_did_skill[ent])
	{
		distance = entity_range(ent, Victim)
		if(distance <= DISTANCE)
		{
			g_did_skill[ent] = false
			sprite_control(ent)
			
			set_user_frozen(Victim, true, TIME_FROZEN)
		}
	}
	
	return PLUGIN_CONTINUE
}

public sprite_control(ent)
{
	new Float:temp_vec[3], Float:temp_aimvec[3]
	new vec[3]
	new aimvec[3]
	new velocityvec[3]
	new length
	new speed = 10
	
	pev(ent, pev_origin, temp_vec)
	fm_get_aim_origin(ent, temp_aimvec)
	
	vec[0] = floatround(temp_vec[0])
	vec[1] = floatround(temp_vec[1])
	vec[2] = floatround(temp_vec[2])
	
	aimvec[0] = floatround(temp_aimvec[0])
	aimvec[1] = floatround(temp_aimvec[1])
	aimvec[2] = floatround(temp_aimvec[2])
	
	velocityvec[0] = aimvec[0] - vec[0]
	velocityvec[1] = aimvec[1] - vec[1]
	velocityvec[2] = aimvec[2] - vec[2]
	length = sqrt( velocityvec[0] * velocityvec[0] + velocityvec[1] * velocityvec[1] + velocityvec[2] * velocityvec[2] )
	velocityvec[0] = velocityvec[0] * speed / length
	velocityvec[1] = velocityvec[1] * speed / length
	velocityvec[2] = velocityvec[2] * speed / length
	
	new args[8]
	args[0] = vec[0]
	args[1] = vec[1]
	args[2] = vec[2]
	args[3] = velocityvec[0]
	args[4] = velocityvec[1]
	args[5] = velocityvec[2]
	
	set_task(0.1, "te_spray", 0, args, 8, "a", 2)
	
}

public te_spray(args[])
{
	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(120) // Throws a shower of sprites or models
	write_coord(args[0]) // start pos
	write_coord(args[1])
	write_coord(args[2])
	write_coord(args[3]) // velocity
	write_coord(args[4])
	write_coord(args[5])
	write_short(frostsprite) // spr
	write_byte(8) // count
	write_byte(70) // speed
	write_byte(100) //(noise)
	write_byte(5) // (rendermode)
	message_end()
	
	return PLUGIN_CONTINUE
}

public sqrt(num)
{
	new div = num
	new result = 1
	while(div > result)
	{
		div = (div + result) / 2
		result = num / div
	}
	return div
}

stock fm_get_aim_origin(index, Float:origin[3])
{
	static Float:start[3], Float:view_ofs[3]
	pev(index, pev_origin, start)
	pev(index, pev_view_ofs, view_ofs)
	xs_vec_add(start, view_ofs, start)
	
	static Float:dest[3]
	pev(index, pev_v_angle, dest)
	engfunc(EngFunc_MakeVectors, dest)
	global_get(glb_v_forward, dest)
	xs_vec_mul_scalar(dest, 9999.0, dest)
	xs_vec_add(start, dest, dest)
	
	engfunc(EngFunc_TraceLine, start, dest, 0, index, 0)
	get_tr2(0, TR_vecEndPos, origin)
	
	return 1
}

public set_user_frozen(id, bool:IsFrozen, Float:duration)
{		
	g_frozen[id] = true
	
	if(IsFrozen)
	{
		message_begin(MSG_ONE_UNRELIABLE, get_user_msgid("Damage"), _, id)
		write_byte(0) // damage save
		write_byte(0) // damage take
		write_long(DMG_DROWN) // damage type - DMG_FREEZE
		write_coord(0) // x
		write_coord(0) // y
		write_coord(0) // z
		message_end()
		
		fm_set_rendering(id, kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 25)
		
		emit_sound(id, CHAN_BODY, grenade_frost_sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
		
		message_begin(MSG_ONE, get_user_msgid("ScreenFade"), _, id)
		write_short(0) // duration
		write_short(0) // hold time
		write_short(0x0004) // fade type
		write_byte(0) // red
		write_byte(50) // green
		write_byte(200) // blue
		write_byte(100) // alpha
		message_end()
		
		g_frozen[id] = true
		
		pev(id, pev_gravity, g_frozen_gravity[id])
		pev(id, pev_maxspeed, g_frozen_speed[id])
		
		if (pev(id, pev_flags) & FL_ONGROUND)
			set_pev(id, pev_gravity, 999999.9) // set really high
		else
			set_pev(id, pev_gravity, 0.000001) // no gravity
		
		set_pev(id, pev_maxspeed, 1.0)
		set_task(duration, "remove_freeze", id+TASK_REMOVE)
	}
	else
	{
		g_frozen[id] = false
		
		set_pev(id, pev_gravity, g_frozen_gravity[id])
		set_pev(id, pev_maxspeed, g_frozen_speed[id])
		
		fm_set_rendering(id)
		
		message_begin(MSG_ONE, get_user_msgid("ScreenFade"), _, id)
		write_short((1<<12)) // duration
		write_short(0) // hold time
		write_short(0x0000) // fade type
		write_byte(0) // red
		write_byte(50) // green
		write_byte(200) // blue
		write_byte(100) // alpha
		message_end()
		
		emit_sound(id, CHAN_BODY, grenade_frost_break_sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
		
		static origin2[3]
		get_user_origin(id, origin2)
		
		message_begin(MSG_PVS, SVC_TEMPENTITY, origin2)
		write_byte(TE_BREAKMODEL) // TE id
		write_coord(origin2[0]) // x
		write_coord(origin2[1]) // y
		write_coord(origin2[2]+24) // z
		write_coord(16) // size x
		write_coord(16) // size y
		write_coord(16) // size z
		write_coord(random_num(-50, 50)) // velocity x
		write_coord(random_num(-50, 50)) // velocity y
		write_coord(25) // velocity z
		write_byte(10) // random velocity
		write_short(g_glassSpr) // model
		write_byte(10) // count
		write_byte(25) // life
		write_byte(0x01) // flags
		message_end()
	}
}

// Remove freeze task
public remove_freeze(id)
{
	id -= TASK_REMOVE
	
	// Not alive or not frozen anymore
	if (!is_user_alive(id) || !g_frozen[id])
		return;
	
	set_user_frozen(id, false, 0.0)
}

// Set entity's rendering type (from fakemeta_util)
stock fm_set_rendering(entity, fx = kRenderFxNone, r = 255, g = 255, b = 255, render = kRenderNormal, amount = 16)
{
	static Float:color[3]
	color[0] = float(r)
	color[1] = float(g)
	color[2] = float(b)
	
	set_pev(entity, pev_renderfx, fx)
	set_pev(entity, pev_rendercolor, color)
	set_pev(entity, pev_rendermode, render)
	set_pev(entity, pev_renderamt, float(amount))
}
